164ab3302SCarolineConcatto //===-- lib/Parser/preprocessor.cpp ---------------------------------------===// 264ab3302SCarolineConcatto // 364ab3302SCarolineConcatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 464ab3302SCarolineConcatto // See https://llvm.org/LICENSE.txt for license information. 564ab3302SCarolineConcatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 664ab3302SCarolineConcatto // 764ab3302SCarolineConcatto //===----------------------------------------------------------------------===// 864ab3302SCarolineConcatto 97d60232bSKrzysztof Parzyszek #include "flang/Parser/preprocessor.h" 107d60232bSKrzysztof Parzyszek 1164ab3302SCarolineConcatto #include "prescan.h" 1264ab3302SCarolineConcatto #include "flang/Common/idioms.h" 1364ab3302SCarolineConcatto #include "flang/Parser/characters.h" 1464ab3302SCarolineConcatto #include "flang/Parser/message.h" 155024a6ecSPeter Klausler #include "llvm/Support/FileSystem.h" 168670e499SCaroline Concatto #include "llvm/Support/raw_ostream.h" 1764ab3302SCarolineConcatto #include <algorithm> 1864ab3302SCarolineConcatto #include <cinttypes> 1964ab3302SCarolineConcatto #include <cstddef> 2064ab3302SCarolineConcatto #include <ctime> 2164ab3302SCarolineConcatto #include <map> 2264ab3302SCarolineConcatto #include <memory> 2364ab3302SCarolineConcatto #include <optional> 2464ab3302SCarolineConcatto #include <set> 2564ab3302SCarolineConcatto #include <utility> 267d60232bSKrzysztof Parzyszek #include <vector> 2764ab3302SCarolineConcatto 2864ab3302SCarolineConcatto namespace Fortran::parser { 2964ab3302SCarolineConcatto 3064ab3302SCarolineConcatto Definition::Definition( 3164ab3302SCarolineConcatto const TokenSequence &repl, std::size_t firstToken, std::size_t tokens) 3264ab3302SCarolineConcatto : replacement_{Tokenize({}, repl, firstToken, tokens)} {} 3364ab3302SCarolineConcatto 3464ab3302SCarolineConcatto Definition::Definition(const std::vector<std::string> &argNames, 3564ab3302SCarolineConcatto const TokenSequence &repl, std::size_t firstToken, std::size_t tokens, 3664ab3302SCarolineConcatto bool isVariadic) 377d60232bSKrzysztof Parzyszek : isFunctionLike_{true}, isVariadic_{isVariadic}, argNames_{argNames}, 3864ab3302SCarolineConcatto replacement_{Tokenize(argNames, repl, firstToken, tokens)} {} 3964ab3302SCarolineConcatto 4064ab3302SCarolineConcatto Definition::Definition(const std::string &predefined, AllSources &sources) 411f879005STim Keith : isPredefined_{true}, 421f879005STim Keith replacement_{ 431f879005STim Keith predefined, sources.AddCompilerInsertion(predefined).start()} {} 4464ab3302SCarolineConcatto 4564ab3302SCarolineConcatto bool Definition::set_isDisabled(bool disable) { 4664ab3302SCarolineConcatto bool was{isDisabled_}; 4764ab3302SCarolineConcatto isDisabled_ = disable; 4864ab3302SCarolineConcatto return was; 4964ab3302SCarolineConcatto } 5064ab3302SCarolineConcatto 517d60232bSKrzysztof Parzyszek void Definition::Print(llvm::raw_ostream &out, const char *macroName) const { 527d60232bSKrzysztof Parzyszek if (!isFunctionLike_) { 537d60232bSKrzysztof Parzyszek // If it's not a function-like macro, then just print the replacement. 547d60232bSKrzysztof Parzyszek out << ' ' << replacement_.ToString(); 557d60232bSKrzysztof Parzyszek return; 567d60232bSKrzysztof Parzyszek } 577d60232bSKrzysztof Parzyszek 587d60232bSKrzysztof Parzyszek size_t argCount{argumentCount()}; 597d60232bSKrzysztof Parzyszek 607d60232bSKrzysztof Parzyszek out << '('; 617d60232bSKrzysztof Parzyszek for (size_t i{0}; i != argCount; ++i) { 627d60232bSKrzysztof Parzyszek if (i != 0) { 637d60232bSKrzysztof Parzyszek out << ", "; 647d60232bSKrzysztof Parzyszek } 657d60232bSKrzysztof Parzyszek out << argNames_[i]; 667d60232bSKrzysztof Parzyszek } 677d60232bSKrzysztof Parzyszek if (isVariadic_) { 687d60232bSKrzysztof Parzyszek out << ", ..."; 697d60232bSKrzysztof Parzyszek } 707d60232bSKrzysztof Parzyszek out << ") "; 717d60232bSKrzysztof Parzyszek 727d60232bSKrzysztof Parzyszek for (size_t i{0}, e{replacement_.SizeInTokens()}; i != e; ++i) { 737d60232bSKrzysztof Parzyszek std::string tok{replacement_.TokenAt(i).ToString()}; 747d60232bSKrzysztof Parzyszek if (size_t idx{GetArgumentIndex(tok)}; idx < argCount) { 757d60232bSKrzysztof Parzyszek out << argNames_[idx]; 767d60232bSKrzysztof Parzyszek } else { 777d60232bSKrzysztof Parzyszek out << tok; 787d60232bSKrzysztof Parzyszek } 797d60232bSKrzysztof Parzyszek } 807d60232bSKrzysztof Parzyszek } 817d60232bSKrzysztof Parzyszek 8264ab3302SCarolineConcatto static bool IsLegalIdentifierStart(const CharBlock &cpl) { 8364ab3302SCarolineConcatto return cpl.size() > 0 && IsLegalIdentifierStart(cpl[0]); 8464ab3302SCarolineConcatto } 8564ab3302SCarolineConcatto 8664ab3302SCarolineConcatto TokenSequence Definition::Tokenize(const std::vector<std::string> &argNames, 8764ab3302SCarolineConcatto const TokenSequence &token, std::size_t firstToken, std::size_t tokens) { 8864ab3302SCarolineConcatto std::map<std::string, std::string> args; 8964ab3302SCarolineConcatto char argIndex{'A'}; 9064ab3302SCarolineConcatto for (const std::string &arg : argNames) { 9164ab3302SCarolineConcatto CHECK(args.find(arg) == args.end()); 9264ab3302SCarolineConcatto args[arg] = "~"s + argIndex++; 9364ab3302SCarolineConcatto } 9464ab3302SCarolineConcatto TokenSequence result; 9564ab3302SCarolineConcatto for (std::size_t j{0}; j < tokens; ++j) { 9664ab3302SCarolineConcatto CharBlock tok{token.TokenAt(firstToken + j)}; 9764ab3302SCarolineConcatto if (IsLegalIdentifierStart(tok)) { 9864ab3302SCarolineConcatto auto it{args.find(tok.ToString())}; 9964ab3302SCarolineConcatto if (it != args.end()) { 10064ab3302SCarolineConcatto result.Put(it->second, token.GetTokenProvenance(j)); 10164ab3302SCarolineConcatto continue; 10264ab3302SCarolineConcatto } 10364ab3302SCarolineConcatto } 10464ab3302SCarolineConcatto result.Put(token, firstToken + j, 1); 10564ab3302SCarolineConcatto } 10664ab3302SCarolineConcatto return result; 10764ab3302SCarolineConcatto } 10864ab3302SCarolineConcatto 1097d60232bSKrzysztof Parzyszek std::size_t Definition::GetArgumentIndex(const CharBlock &token) const { 1107d60232bSKrzysztof Parzyszek if (token.size() >= 2 && token[0] == '~') { 1117d60232bSKrzysztof Parzyszek return static_cast<size_t>(token[1] - 'A'); 1127d60232bSKrzysztof Parzyszek } 1137d60232bSKrzysztof Parzyszek return argumentCount(); 1147d60232bSKrzysztof Parzyszek } 1157d60232bSKrzysztof Parzyszek 11664ab3302SCarolineConcatto static TokenSequence Stringify( 11764ab3302SCarolineConcatto const TokenSequence &tokens, AllSources &allSources) { 11864ab3302SCarolineConcatto TokenSequence result; 11964ab3302SCarolineConcatto Provenance quoteProvenance{allSources.CompilerInsertionProvenance('"')}; 12064ab3302SCarolineConcatto result.PutNextTokenChar('"', quoteProvenance); 12164ab3302SCarolineConcatto for (std::size_t j{0}; j < tokens.SizeInTokens(); ++j) { 12264ab3302SCarolineConcatto const CharBlock &token{tokens.TokenAt(j)}; 12364ab3302SCarolineConcatto std::size_t bytes{token.size()}; 12464ab3302SCarolineConcatto for (std::size_t k{0}; k < bytes; ++k) { 12564ab3302SCarolineConcatto char ch{token[k]}; 12664ab3302SCarolineConcatto Provenance from{tokens.GetTokenProvenance(j, k)}; 12764ab3302SCarolineConcatto if (ch == '"' || ch == '\\') { 12864ab3302SCarolineConcatto result.PutNextTokenChar(ch, from); 12964ab3302SCarolineConcatto } 13064ab3302SCarolineConcatto result.PutNextTokenChar(ch, from); 13164ab3302SCarolineConcatto } 13264ab3302SCarolineConcatto } 13364ab3302SCarolineConcatto result.PutNextTokenChar('"', quoteProvenance); 13464ab3302SCarolineConcatto result.CloseToken(); 13564ab3302SCarolineConcatto return result; 13664ab3302SCarolineConcatto } 13764ab3302SCarolineConcatto 13801def7f7Speter klausler constexpr bool IsTokenPasting(CharBlock opr) { 13901def7f7Speter klausler return opr.size() == 2 && opr[0] == '#' && opr[1] == '#'; 14001def7f7Speter klausler } 14101def7f7Speter klausler 14201def7f7Speter klausler static bool AnyTokenPasting(const TokenSequence &text) { 14301def7f7Speter klausler std::size_t tokens{text.SizeInTokens()}; 14401def7f7Speter klausler for (std::size_t j{0}; j < tokens; ++j) { 14501def7f7Speter klausler if (IsTokenPasting(text.TokenAt(j))) { 14601def7f7Speter klausler return true; 14701def7f7Speter klausler } 14801def7f7Speter klausler } 14901def7f7Speter klausler return false; 15001def7f7Speter klausler } 15101def7f7Speter klausler 15201def7f7Speter klausler static TokenSequence TokenPasting(TokenSequence &&text) { 15301def7f7Speter klausler if (!AnyTokenPasting(text)) { 15401def7f7Speter klausler return std::move(text); 15501def7f7Speter klausler } 15664ab3302SCarolineConcatto TokenSequence result; 15701def7f7Speter klausler std::size_t tokens{text.SizeInTokens()}; 15864ab3302SCarolineConcatto bool pasting{false}; 15901def7f7Speter klausler for (std::size_t j{0}; j < tokens; ++j) { 16001def7f7Speter klausler if (IsTokenPasting(text.TokenAt(j))) { 16101def7f7Speter klausler if (!pasting) { 16201def7f7Speter klausler while (!result.empty() && 16301def7f7Speter klausler result.TokenAt(result.SizeInTokens() - 1).IsBlank()) { 16401def7f7Speter klausler result.pop_back(); 16501def7f7Speter klausler } 16601def7f7Speter klausler if (!result.empty()) { 16701def7f7Speter klausler result.ReopenLastToken(); 16801def7f7Speter klausler pasting = true; 16901def7f7Speter klausler } 17001def7f7Speter klausler } 17101def7f7Speter klausler } else if (pasting && text.TokenAt(j).IsBlank()) { 17201def7f7Speter klausler } else { 17301def7f7Speter klausler result.Put(text, j, 1); 17401def7f7Speter klausler pasting = false; 17501def7f7Speter klausler } 17601def7f7Speter klausler } 17701def7f7Speter klausler return result; 17801def7f7Speter klausler } 17901def7f7Speter klausler 180*850d42fbSPeter Klausler constexpr bool IsDefinedKeyword(CharBlock token) { 181*850d42fbSPeter Klausler return token.size() == 7 && (token[0] == 'd' || token[0] == 'D') && 182*850d42fbSPeter Klausler ToLowerCaseLetters(token.ToString()) == "defined"; 183*850d42fbSPeter Klausler } 184*850d42fbSPeter Klausler 185*850d42fbSPeter Klausler TokenSequence Definition::Apply(const std::vector<TokenSequence> &args, 186*850d42fbSPeter Klausler Prescanner &prescanner, bool inIfExpression) { 18701def7f7Speter klausler TokenSequence result; 18864ab3302SCarolineConcatto bool skipping{false}; 18964ab3302SCarolineConcatto int parenthesesNesting{0}; 19064ab3302SCarolineConcatto std::size_t tokens{replacement_.SizeInTokens()}; 19164ab3302SCarolineConcatto for (std::size_t j{0}; j < tokens; ++j) { 19201def7f7Speter klausler CharBlock token{replacement_.TokenAt(j)}; 19364ab3302SCarolineConcatto std::size_t bytes{token.size()}; 19464ab3302SCarolineConcatto if (skipping) { 1956fac3f7bSPeter Klausler char ch{token.OnlyNonBlank()}; 1966fac3f7bSPeter Klausler if (ch == '(') { 19764ab3302SCarolineConcatto ++parenthesesNesting; 1986fac3f7bSPeter Klausler } else if (ch == ')') { 1996fac3f7bSPeter Klausler if (parenthesesNesting > 0) { 2006fac3f7bSPeter Klausler --parenthesesNesting; 20164ab3302SCarolineConcatto } 2026fac3f7bSPeter Klausler skipping = parenthesesNesting > 0; 20364ab3302SCarolineConcatto } 20464ab3302SCarolineConcatto continue; 20564ab3302SCarolineConcatto } 20601def7f7Speter klausler if (bytes == 2 && token[0] == '~') { // argument substitution 2077d60232bSKrzysztof Parzyszek std::size_t index{GetArgumentIndex(token)}; 20864ab3302SCarolineConcatto if (index >= args.size()) { 20964ab3302SCarolineConcatto continue; 21064ab3302SCarolineConcatto } 21101def7f7Speter klausler std::size_t prev{j}; 21201def7f7Speter klausler while (prev > 0 && replacement_.TokenAt(prev - 1).IsBlank()) { 21301def7f7Speter klausler --prev; 21401def7f7Speter klausler } 21501def7f7Speter klausler if (prev > 0 && replacement_.TokenAt(prev - 1).size() == 1 && 21601def7f7Speter klausler replacement_.TokenAt(prev - 1)[0] == 21701def7f7Speter klausler '#') { // stringify argument without macro replacement 21801def7f7Speter klausler std::size_t resultSize{result.SizeInTokens()}; 2190e811d3bSPeter Klausler while (resultSize > 0 && result.TokenAt(resultSize - 1).IsBlank()) { 22064ab3302SCarolineConcatto result.pop_back(); 2210e811d3bSPeter Klausler --resultSize; 22264ab3302SCarolineConcatto } 22301def7f7Speter klausler CHECK(resultSize > 0 && 22401def7f7Speter klausler result.TokenAt(resultSize - 1) == replacement_.TokenAt(prev - 1)); 22501def7f7Speter klausler result.pop_back(); 22601def7f7Speter klausler result.Put(Stringify(args[index], prescanner.allSources())); 22764ab3302SCarolineConcatto } else { 22801def7f7Speter klausler const TokenSequence *arg{&args[index]}; 22901def7f7Speter klausler std::optional<TokenSequence> replaced; 23001def7f7Speter klausler // Don't replace macros in the actual argument if it is preceded or 231*850d42fbSPeter Klausler // followed by the token-pasting operator ## in the replacement text, 232*850d42fbSPeter Klausler // or if we have to worry about "defined(X)"/"defined X" in an 233*850d42fbSPeter Klausler // #if/#elif expression. 234*850d42fbSPeter Klausler if (!inIfExpression && 235*850d42fbSPeter Klausler (prev == 0 || !IsTokenPasting(replacement_.TokenAt(prev - 1)))) { 23601def7f7Speter klausler auto next{replacement_.SkipBlanks(j + 1)}; 23701def7f7Speter klausler if (next >= tokens || !IsTokenPasting(replacement_.TokenAt(next))) { 23801def7f7Speter klausler // Apply macro replacement to the actual argument 239*850d42fbSPeter Klausler replaced = prescanner.preprocessor().MacroReplacement( 240*850d42fbSPeter Klausler *arg, prescanner, nullptr, inIfExpression); 24101def7f7Speter klausler if (replaced) { 24201def7f7Speter klausler arg = &*replaced; 24364ab3302SCarolineConcatto } 24464ab3302SCarolineConcatto } 24564ab3302SCarolineConcatto } 24601def7f7Speter klausler result.Put(DEREF(arg)); 24764ab3302SCarolineConcatto } 24864ab3302SCarolineConcatto } else if (bytes == 11 && isVariadic_ && 24964ab3302SCarolineConcatto token.ToString() == "__VA_ARGS__") { 25001def7f7Speter klausler Provenance commaProvenance{ 25101def7f7Speter klausler prescanner.preprocessor().allSources().CompilerInsertionProvenance( 25201def7f7Speter klausler ',')}; 2537d60232bSKrzysztof Parzyszek for (std::size_t k{argumentCount()}; k < args.size(); ++k) { 2547d60232bSKrzysztof Parzyszek if (k > argumentCount()) { 25564ab3302SCarolineConcatto result.Put(","s, commaProvenance); 25664ab3302SCarolineConcatto } 25764ab3302SCarolineConcatto result.Put(args[k]); 25864ab3302SCarolineConcatto } 25964ab3302SCarolineConcatto } else if (bytes == 10 && isVariadic_ && token.ToString() == "__VA_OPT__" && 2606fac3f7bSPeter Klausler j + 2 < tokens && replacement_.TokenAt(j + 1).OnlyNonBlank() == '(' && 26164ab3302SCarolineConcatto parenthesesNesting == 0) { 26264ab3302SCarolineConcatto parenthesesNesting = 1; 2637d60232bSKrzysztof Parzyszek skipping = args.size() == argumentCount(); 26464ab3302SCarolineConcatto ++j; 26564ab3302SCarolineConcatto } else { 2666fac3f7bSPeter Klausler if (parenthesesNesting > 0) { 2676fac3f7bSPeter Klausler char ch{token.OnlyNonBlank()}; 2686fac3f7bSPeter Klausler if (ch == '(') { 26964ab3302SCarolineConcatto ++parenthesesNesting; 2706fac3f7bSPeter Klausler } else if (ch == ')') { 27164ab3302SCarolineConcatto if (--parenthesesNesting == 0) { 27264ab3302SCarolineConcatto skipping = false; 27364ab3302SCarolineConcatto continue; 27464ab3302SCarolineConcatto } 27564ab3302SCarolineConcatto } 2766fac3f7bSPeter Klausler } 27764ab3302SCarolineConcatto result.Put(replacement_, j); 27864ab3302SCarolineConcatto } 27964ab3302SCarolineConcatto } 28001def7f7Speter klausler return TokenPasting(std::move(result)); 28164ab3302SCarolineConcatto } 28264ab3302SCarolineConcatto 28364ab3302SCarolineConcatto static std::string FormatTime(const std::time_t &now, const char *format) { 28464ab3302SCarolineConcatto char buffer[16]; 28564ab3302SCarolineConcatto return {buffer, 28664ab3302SCarolineConcatto std::strftime(buffer, sizeof buffer, format, std::localtime(&now))}; 28764ab3302SCarolineConcatto } 28864ab3302SCarolineConcatto 2898880a63aSpeter klausler Preprocessor::Preprocessor(AllSources &allSources) : allSources_{allSources} {} 2908880a63aSpeter klausler 2918880a63aSpeter klausler void Preprocessor::DefineStandardMacros() { 29264ab3302SCarolineConcatto // Capture current local date & time once now to avoid having the values 29364ab3302SCarolineConcatto // of __DATE__ or __TIME__ change during compilation. 29464ab3302SCarolineConcatto std::time_t now; 29564ab3302SCarolineConcatto std::time(&now); 2968880a63aSpeter klausler Define("__DATE__"s, FormatTime(now, "\"%h %e %Y\"")); // e.g., "Jun 16 1904" 2978880a63aSpeter klausler Define("__TIME__"s, FormatTime(now, "\"%T\"")); // e.g., "23:59:60" 29864ab3302SCarolineConcatto // The values of these predefined macros depend on their invocation sites. 2998880a63aSpeter klausler Define("__FILE__"s, "__FILE__"s); 3008880a63aSpeter klausler Define("__LINE__"s, "__LINE__"s); 3015024a6ecSPeter Klausler Define("__TIMESTAMP__"s, "__TIMESTAMP__"s); 30264ab3302SCarolineConcatto } 30364ab3302SCarolineConcatto 3045a20a208SPeter Klausler void Preprocessor::Define(const std::string ¯o, const std::string &value) { 30551cfad3aSpeter klausler definitions_.emplace(SaveTokenAsName(macro), Definition{value, allSources_}); 30664ab3302SCarolineConcatto } 30764ab3302SCarolineConcatto 30864ab3302SCarolineConcatto void Preprocessor::Undefine(std::string macro) { definitions_.erase(macro); } 30964ab3302SCarolineConcatto 31064ab3302SCarolineConcatto std::optional<TokenSequence> Preprocessor::MacroReplacement( 311d3e5c20aSPeter Klausler const TokenSequence &input, Prescanner &prescanner, 312*850d42fbSPeter Klausler std::optional<std::size_t> *partialFunctionLikeMacro, bool inIfExpression) { 31364ab3302SCarolineConcatto // Do quick scan for any use of a defined name. 3148880a63aSpeter klausler if (definitions_.empty()) { 3158880a63aSpeter klausler return std::nullopt; 3168880a63aSpeter klausler } 31764ab3302SCarolineConcatto std::size_t tokens{input.SizeInTokens()}; 318d3e5c20aSPeter Klausler std::size_t j{0}; 319d3e5c20aSPeter Klausler for (; j < tokens; ++j) { 32064ab3302SCarolineConcatto CharBlock token{input.TokenAt(j)}; 32164ab3302SCarolineConcatto if (!token.empty() && IsLegalIdentifierStart(token[0]) && 322*850d42fbSPeter Klausler (IsNameDefined(token) || (inIfExpression && IsDefinedKeyword(token)))) { 32364ab3302SCarolineConcatto break; 32464ab3302SCarolineConcatto } 32564ab3302SCarolineConcatto } 32664ab3302SCarolineConcatto if (j == tokens) { 32764ab3302SCarolineConcatto return std::nullopt; // input contains nothing that would be replaced 32864ab3302SCarolineConcatto } 32964ab3302SCarolineConcatto TokenSequence result{input, 0, j}; 330d3e5c20aSPeter Klausler 331d3e5c20aSPeter Klausler // After rescanning after macro replacement has failed due to an unclosed 332d3e5c20aSPeter Klausler // function-like macro call (no left parenthesis yet, or no closing 333d3e5c20aSPeter Klausler // parenthesis), if tokens remain in the input, append them to the 334d3e5c20aSPeter Klausler // replacement text and attempt to proceed. Otherwise, return, so that 335d3e5c20aSPeter Klausler // the caller may try again with remaining tokens in its input. 336d3e5c20aSPeter Klausler auto CompleteFunctionLikeMacro{ 337*850d42fbSPeter Klausler [this, &input, &prescanner, &result, &partialFunctionLikeMacro, 338*850d42fbSPeter Klausler inIfExpression](std::size_t after, const TokenSequence &replacement, 339d3e5c20aSPeter Klausler std::size_t pFLMOffset) { 340d3e5c20aSPeter Klausler if (after < input.SizeInTokens()) { 341d3e5c20aSPeter Klausler result.Put(replacement, 0, pFLMOffset); 342d3e5c20aSPeter Klausler TokenSequence suffix; 343d3e5c20aSPeter Klausler suffix.Put( 344d3e5c20aSPeter Klausler replacement, pFLMOffset, replacement.SizeInTokens() - pFLMOffset); 345d3e5c20aSPeter Klausler suffix.Put(input, after, input.SizeInTokens() - after); 346*850d42fbSPeter Klausler auto further{ReplaceMacros( 347*850d42fbSPeter Klausler suffix, prescanner, partialFunctionLikeMacro, inIfExpression)}; 348d3e5c20aSPeter Klausler if (partialFunctionLikeMacro && *partialFunctionLikeMacro) { 349d3e5c20aSPeter Klausler // still not closed 350d3e5c20aSPeter Klausler **partialFunctionLikeMacro += result.SizeInTokens(); 351d3e5c20aSPeter Klausler } 352d3e5c20aSPeter Klausler result.Put(further); 353d3e5c20aSPeter Klausler return true; 354d3e5c20aSPeter Klausler } else { 355d3e5c20aSPeter Klausler if (partialFunctionLikeMacro) { 356d3e5c20aSPeter Klausler *partialFunctionLikeMacro = pFLMOffset + result.SizeInTokens(); 357d3e5c20aSPeter Klausler } 358d3e5c20aSPeter Klausler return false; 359d3e5c20aSPeter Klausler } 360d3e5c20aSPeter Klausler }}; 361d3e5c20aSPeter Klausler 36264ab3302SCarolineConcatto for (; j < tokens; ++j) { 3639efe1581SPeter Klausler CharBlock token{input.TokenAt(j)}; 36464ab3302SCarolineConcatto if (token.IsBlank() || !IsLegalIdentifierStart(token[0])) { 36564ab3302SCarolineConcatto result.Put(input, j); 36664ab3302SCarolineConcatto continue; 36764ab3302SCarolineConcatto } 368*850d42fbSPeter Klausler // Process identifier in replacement text. 36964ab3302SCarolineConcatto auto it{definitions_.find(token)}; 370*850d42fbSPeter Klausler // Is in the X in "defined(X)" or "defined X" in an #if/#elif expression? 371*850d42fbSPeter Klausler if (inIfExpression) { 372*850d42fbSPeter Klausler if (auto prev{result.SkipBlanksBackwards(result.SizeInTokens())}) { 373*850d42fbSPeter Klausler bool ok{true}; 374*850d42fbSPeter Klausler std::optional<std::size_t> rightParenthesis; 375*850d42fbSPeter Klausler if (result.TokenAt(*prev).OnlyNonBlank() == '(') { 376*850d42fbSPeter Klausler prev = result.SkipBlanksBackwards(*prev); 377*850d42fbSPeter Klausler rightParenthesis = input.SkipBlanks(j + 1); 378*850d42fbSPeter Klausler ok = *rightParenthesis < tokens && 379*850d42fbSPeter Klausler input.TokenAt(*rightParenthesis).OnlyNonBlank() == ')'; 380*850d42fbSPeter Klausler } 381*850d42fbSPeter Klausler if (ok && prev && IsDefinedKeyword(result.TokenAt(*prev))) { 382*850d42fbSPeter Klausler result = TokenSequence{result, 0, *prev}; // trims off "defined (" 383*850d42fbSPeter Klausler char truth{it != definitions_.end() ? '1' : '0'}; 384*850d42fbSPeter Klausler result.Put(&truth, 1, allSources_.CompilerInsertionProvenance(truth)); 385*850d42fbSPeter Klausler j = rightParenthesis.value_or(j); 386*850d42fbSPeter Klausler continue; 387*850d42fbSPeter Klausler } 388*850d42fbSPeter Klausler } 389*850d42fbSPeter Klausler } 39064ab3302SCarolineConcatto if (it == definitions_.end()) { 39164ab3302SCarolineConcatto result.Put(input, j); 39264ab3302SCarolineConcatto continue; 39364ab3302SCarolineConcatto } 3949efe1581SPeter Klausler Definition *def{&it->second}; 3959efe1581SPeter Klausler if (def->isDisabled()) { 39664ab3302SCarolineConcatto result.Put(input, j); 39764ab3302SCarolineConcatto continue; 39864ab3302SCarolineConcatto } 3999efe1581SPeter Klausler if (!def->isFunctionLike()) { 400d3e5c20aSPeter Klausler if (def->isPredefined() && !def->replacement().empty()) { 40164ab3302SCarolineConcatto std::string repl; 402d40e6005SPeter Klausler std::string name{def->replacement().TokenAt(0).ToString()}; 40364ab3302SCarolineConcatto if (name == "__FILE__") { 40464ab3302SCarolineConcatto repl = "\""s + 40564ab3302SCarolineConcatto allSources_.GetPath(prescanner.GetCurrentProvenance()) + '"'; 40664ab3302SCarolineConcatto } else if (name == "__LINE__") { 4078670e499SCaroline Concatto std::string buf; 4088670e499SCaroline Concatto llvm::raw_string_ostream ss{buf}; 40964ab3302SCarolineConcatto ss << allSources_.GetLineNumber(prescanner.GetCurrentProvenance()); 41064ab3302SCarolineConcatto repl = ss.str(); 4115024a6ecSPeter Klausler } else if (name == "__TIMESTAMP__") { 4125024a6ecSPeter Klausler auto path{allSources_.GetPath( 4135024a6ecSPeter Klausler prescanner.GetCurrentProvenance(), /*topLevel=*/true)}; 4145024a6ecSPeter Klausler llvm::sys::fs::file_status status; 4155024a6ecSPeter Klausler repl = "??? ??? ?? ??:??:?? ????"; 4165024a6ecSPeter Klausler if (!llvm::sys::fs::status(path, status)) { 4175024a6ecSPeter Klausler auto modTime{llvm::sys::toTimeT(status.getLastModificationTime())}; 4185024a6ecSPeter Klausler if (std::string time{std::asctime(std::localtime(&modTime))}; 4195024a6ecSPeter Klausler time.size() > 1 && time[time.size() - 1] == '\n') { 4205024a6ecSPeter Klausler time.erase(time.size() - 1); // clip terminal '\n' 4215024a6ecSPeter Klausler repl = "\""s + time + '"'; 4225024a6ecSPeter Klausler } 4235024a6ecSPeter Klausler } 42464ab3302SCarolineConcatto } 42564ab3302SCarolineConcatto if (!repl.empty()) { 42664ab3302SCarolineConcatto ProvenanceRange insert{allSources_.AddCompilerInsertion(repl)}; 42764ab3302SCarolineConcatto ProvenanceRange call{allSources_.AddMacroCall( 42864ab3302SCarolineConcatto insert, input.GetTokenProvenanceRange(j), repl)}; 42964ab3302SCarolineConcatto result.Put(repl, call.start()); 43064ab3302SCarolineConcatto continue; 43164ab3302SCarolineConcatto } 43264ab3302SCarolineConcatto } 433d3e5c20aSPeter Klausler std::optional<std::size_t> partialFLM; 4349efe1581SPeter Klausler def->set_isDisabled(true); 435*850d42fbSPeter Klausler TokenSequence replaced{TokenPasting(ReplaceMacros( 436*850d42fbSPeter Klausler def->replacement(), prescanner, &partialFLM, inIfExpression))}; 4379efe1581SPeter Klausler def->set_isDisabled(false); 438d3e5c20aSPeter Klausler if (partialFLM && 439d3e5c20aSPeter Klausler CompleteFunctionLikeMacro(j + 1, replaced, *partialFLM)) { 440d3e5c20aSPeter Klausler return result; 4419efe1581SPeter Klausler } 44264ab3302SCarolineConcatto if (!replaced.empty()) { 4439efe1581SPeter Klausler ProvenanceRange from{def->replacement().GetProvenanceRange()}; 44464ab3302SCarolineConcatto ProvenanceRange use{input.GetTokenProvenanceRange(j)}; 44564ab3302SCarolineConcatto ProvenanceRange newRange{ 44664ab3302SCarolineConcatto allSources_.AddMacroCall(from, use, replaced.ToString())}; 44764ab3302SCarolineConcatto result.Put(replaced, newRange); 44864ab3302SCarolineConcatto } 449d3e5c20aSPeter Klausler } else { 45064ab3302SCarolineConcatto // Possible function-like macro call. Skip spaces and newlines to see 45164ab3302SCarolineConcatto // whether '(' is next. 45264ab3302SCarolineConcatto std::size_t k{j}; 45364ab3302SCarolineConcatto bool leftParen{false}; 45464ab3302SCarolineConcatto while (++k < tokens) { 45564ab3302SCarolineConcatto const CharBlock &lookAhead{input.TokenAt(k)}; 45664ab3302SCarolineConcatto if (!lookAhead.IsBlank() && lookAhead[0] != '\n') { 45764ab3302SCarolineConcatto leftParen = lookAhead[0] == '(' && lookAhead.size() == 1; 45864ab3302SCarolineConcatto break; 45964ab3302SCarolineConcatto } 46064ab3302SCarolineConcatto } 46164ab3302SCarolineConcatto if (!leftParen) { 462d3e5c20aSPeter Klausler if (partialFunctionLikeMacro) { 463d3e5c20aSPeter Klausler *partialFunctionLikeMacro = result.SizeInTokens(); 464d3e5c20aSPeter Klausler result.Put(input, j, tokens - j); 465d3e5c20aSPeter Klausler return result; 466d3e5c20aSPeter Klausler } else { 46764ab3302SCarolineConcatto result.Put(input, j); 46864ab3302SCarolineConcatto continue; 46964ab3302SCarolineConcatto } 470d3e5c20aSPeter Klausler } 47164ab3302SCarolineConcatto std::vector<std::size_t> argStart{++k}; 47264ab3302SCarolineConcatto for (int nesting{0}; k < tokens; ++k) { 47364ab3302SCarolineConcatto CharBlock token{input.TokenAt(k)}; 4746fac3f7bSPeter Klausler char ch{token.OnlyNonBlank()}; 47564ab3302SCarolineConcatto if (ch == '(') { 47664ab3302SCarolineConcatto ++nesting; 47764ab3302SCarolineConcatto } else if (ch == ')') { 47864ab3302SCarolineConcatto if (nesting == 0) { 47964ab3302SCarolineConcatto break; 48064ab3302SCarolineConcatto } 48164ab3302SCarolineConcatto --nesting; 48264ab3302SCarolineConcatto } else if (ch == ',' && nesting == 0) { 48364ab3302SCarolineConcatto argStart.push_back(k + 1); 48464ab3302SCarolineConcatto } 48564ab3302SCarolineConcatto } 486d3e5c20aSPeter Klausler if (argStart.size() == 1 && k == argStart[0] && 487d3e5c20aSPeter Klausler def->argumentCount() == 0) { 48864ab3302SCarolineConcatto // Subtle: () is zero arguments, not one empty argument, 48964ab3302SCarolineConcatto // unless one argument was expected. 49064ab3302SCarolineConcatto argStart.clear(); 49164ab3302SCarolineConcatto } 492d3e5c20aSPeter Klausler if (k >= tokens && partialFunctionLikeMacro) { 493d3e5c20aSPeter Klausler *partialFunctionLikeMacro = result.SizeInTokens(); 494d3e5c20aSPeter Klausler result.Put(input, j, tokens - j); 495d3e5c20aSPeter Klausler return result; 496d3e5c20aSPeter Klausler } else if (k >= tokens || argStart.size() < def->argumentCount() || 4979efe1581SPeter Klausler (argStart.size() > def->argumentCount() && !def->isVariadic())) { 49864ab3302SCarolineConcatto result.Put(input, j); 49964ab3302SCarolineConcatto continue; 50064ab3302SCarolineConcatto } 50164ab3302SCarolineConcatto std::vector<TokenSequence> args; 50264ab3302SCarolineConcatto for (std::size_t n{0}; n < argStart.size(); ++n) { 50364ab3302SCarolineConcatto std::size_t at{argStart[n]}; 50464ab3302SCarolineConcatto std::size_t count{ 50564ab3302SCarolineConcatto (n + 1 == argStart.size() ? k : argStart[n + 1] - 1) - at}; 50664ab3302SCarolineConcatto args.emplace_back(TokenSequence(input, at, count)); 50764ab3302SCarolineConcatto } 508*850d42fbSPeter Klausler TokenSequence applied{def->Apply(args, prescanner, inIfExpression)}; 509d3e5c20aSPeter Klausler std::optional<std::size_t> partialFLM; 5109efe1581SPeter Klausler def->set_isDisabled(true); 511*850d42fbSPeter Klausler TokenSequence replaced{ReplaceMacros( 512*850d42fbSPeter Klausler std::move(applied), prescanner, &partialFLM, inIfExpression)}; 5139efe1581SPeter Klausler def->set_isDisabled(false); 514d3e5c20aSPeter Klausler if (partialFLM && 515d3e5c20aSPeter Klausler CompleteFunctionLikeMacro(k + 1, replaced, *partialFLM)) { 516d3e5c20aSPeter Klausler return result; 517d3e5c20aSPeter Klausler } 51864ab3302SCarolineConcatto if (!replaced.empty()) { 5199efe1581SPeter Klausler ProvenanceRange from{def->replacement().GetProvenanceRange()}; 52064ab3302SCarolineConcatto ProvenanceRange use{input.GetIntervalProvenanceRange(j, k - j)}; 52164ab3302SCarolineConcatto ProvenanceRange newRange{ 52264ab3302SCarolineConcatto allSources_.AddMacroCall(from, use, replaced.ToString())}; 52364ab3302SCarolineConcatto result.Put(replaced, newRange); 52464ab3302SCarolineConcatto } 52564ab3302SCarolineConcatto j = k; // advance to the terminal ')' 52664ab3302SCarolineConcatto } 527d3e5c20aSPeter Klausler } 52864ab3302SCarolineConcatto return result; 52964ab3302SCarolineConcatto } 53064ab3302SCarolineConcatto 531d3e5c20aSPeter Klausler TokenSequence Preprocessor::ReplaceMacros(const TokenSequence &tokens, 532d3e5c20aSPeter Klausler Prescanner &prescanner, 533*850d42fbSPeter Klausler std::optional<std::size_t> *partialFunctionLikeMacro, bool inIfExpression) { 534*850d42fbSPeter Klausler if (std::optional<TokenSequence> repl{MacroReplacement( 535*850d42fbSPeter Klausler tokens, prescanner, partialFunctionLikeMacro, inIfExpression)}) { 53664ab3302SCarolineConcatto return std::move(*repl); 53764ab3302SCarolineConcatto } 53864ab3302SCarolineConcatto return tokens; 53964ab3302SCarolineConcatto } 54064ab3302SCarolineConcatto 541f411be0dSpeter klausler void Preprocessor::Directive(const TokenSequence &dir, Prescanner &prescanner) { 54264ab3302SCarolineConcatto std::size_t tokens{dir.SizeInTokens()}; 54364ab3302SCarolineConcatto std::size_t j{dir.SkipBlanks(0)}; 54464ab3302SCarolineConcatto if (j == tokens) { 54564ab3302SCarolineConcatto return; 54664ab3302SCarolineConcatto } 54764ab3302SCarolineConcatto if (dir.TokenAt(j).ToString() != "#") { 548f411be0dSpeter klausler prescanner.Say(dir.GetTokenProvenanceRange(j), "missing '#'"_err_en_US); 54964ab3302SCarolineConcatto return; 55064ab3302SCarolineConcatto } 55164ab3302SCarolineConcatto j = dir.SkipBlanks(j + 1); 55264ab3302SCarolineConcatto while (tokens > 0 && dir.TokenAt(tokens - 1).IsBlank()) { 55364ab3302SCarolineConcatto --tokens; 55464ab3302SCarolineConcatto } 55564ab3302SCarolineConcatto if (j == tokens) { 55664ab3302SCarolineConcatto return; 55764ab3302SCarolineConcatto } 55864ab3302SCarolineConcatto if (IsDecimalDigit(dir.TokenAt(j)[0]) || dir.TokenAt(j)[0] == '"') { 559e12ffe6aSPeter Klausler LineDirective(dir, j, prescanner); 560e12ffe6aSPeter Klausler return; 56164ab3302SCarolineConcatto } 56264ab3302SCarolineConcatto std::size_t dirOffset{j}; 56364ab3302SCarolineConcatto std::string dirName{ToLowerCaseLetters(dir.TokenAt(dirOffset).ToString())}; 56464ab3302SCarolineConcatto j = dir.SkipBlanks(j + 1); 56564ab3302SCarolineConcatto CharBlock nameToken; 56664ab3302SCarolineConcatto if (j < tokens && IsLegalIdentifierStart(dir.TokenAt(j)[0])) { 56764ab3302SCarolineConcatto nameToken = dir.TokenAt(j); 56864ab3302SCarolineConcatto } 56964ab3302SCarolineConcatto if (dirName == "line") { 570e12ffe6aSPeter Klausler LineDirective(dir, j, prescanner); 57164ab3302SCarolineConcatto } else if (dirName == "define") { 57264ab3302SCarolineConcatto if (nameToken.empty()) { 573f411be0dSpeter klausler prescanner.Say(dir.GetTokenProvenanceRange(j < tokens ? j : tokens - 1), 57464ab3302SCarolineConcatto "#define: missing or invalid name"_err_en_US); 57564ab3302SCarolineConcatto return; 57664ab3302SCarolineConcatto } 57764ab3302SCarolineConcatto nameToken = SaveTokenAsName(nameToken); 57864ab3302SCarolineConcatto definitions_.erase(nameToken); 5796fac3f7bSPeter Klausler if (++j < tokens && dir.TokenAt(j).OnlyNonBlank() == '(') { 58064ab3302SCarolineConcatto j = dir.SkipBlanks(j + 1); 58164ab3302SCarolineConcatto std::vector<std::string> argName; 58264ab3302SCarolineConcatto bool isVariadic{false}; 5836fac3f7bSPeter Klausler if (dir.TokenAt(j).OnlyNonBlank() != ')') { 58464ab3302SCarolineConcatto while (true) { 58564ab3302SCarolineConcatto std::string an{dir.TokenAt(j).ToString()}; 58664ab3302SCarolineConcatto if (an == "...") { 58764ab3302SCarolineConcatto isVariadic = true; 58864ab3302SCarolineConcatto } else { 58964ab3302SCarolineConcatto if (an.empty() || !IsLegalIdentifierStart(an[0])) { 590f411be0dSpeter klausler prescanner.Say(dir.GetTokenProvenanceRange(j), 59164ab3302SCarolineConcatto "#define: missing or invalid argument name"_err_en_US); 59264ab3302SCarolineConcatto return; 59364ab3302SCarolineConcatto } 59464ab3302SCarolineConcatto argName.push_back(an); 59564ab3302SCarolineConcatto } 59664ab3302SCarolineConcatto j = dir.SkipBlanks(j + 1); 59764ab3302SCarolineConcatto if (j == tokens) { 598f411be0dSpeter klausler prescanner.Say(dir.GetTokenProvenanceRange(tokens - 1), 59964ab3302SCarolineConcatto "#define: malformed argument list"_err_en_US); 60064ab3302SCarolineConcatto return; 60164ab3302SCarolineConcatto } 6026fac3f7bSPeter Klausler char punc{dir.TokenAt(j).OnlyNonBlank()}; 6036fac3f7bSPeter Klausler if (punc == ')') { 60464ab3302SCarolineConcatto break; 60564ab3302SCarolineConcatto } 6066fac3f7bSPeter Klausler if (isVariadic || punc != ',') { 607f411be0dSpeter klausler prescanner.Say(dir.GetTokenProvenanceRange(j), 60864ab3302SCarolineConcatto "#define: malformed argument list"_err_en_US); 60964ab3302SCarolineConcatto return; 61064ab3302SCarolineConcatto } 61164ab3302SCarolineConcatto j = dir.SkipBlanks(j + 1); 61264ab3302SCarolineConcatto if (j == tokens) { 613f411be0dSpeter klausler prescanner.Say(dir.GetTokenProvenanceRange(tokens - 1), 61464ab3302SCarolineConcatto "#define: malformed argument list"_err_en_US); 61564ab3302SCarolineConcatto return; 61664ab3302SCarolineConcatto } 61764ab3302SCarolineConcatto } 61864ab3302SCarolineConcatto if (std::set<std::string>(argName.begin(), argName.end()).size() != 61964ab3302SCarolineConcatto argName.size()) { 620f411be0dSpeter klausler prescanner.Say(dir.GetTokenProvenance(dirOffset), 62164ab3302SCarolineConcatto "#define: argument names are not distinct"_err_en_US); 62264ab3302SCarolineConcatto return; 62364ab3302SCarolineConcatto } 62464ab3302SCarolineConcatto } 62564ab3302SCarolineConcatto j = dir.SkipBlanks(j + 1); 62664ab3302SCarolineConcatto definitions_.emplace(std::make_pair( 62764ab3302SCarolineConcatto nameToken, Definition{argName, dir, j, tokens - j, isVariadic})); 62864ab3302SCarolineConcatto } else { 62964ab3302SCarolineConcatto j = dir.SkipBlanks(j + 1); 63064ab3302SCarolineConcatto definitions_.emplace( 63164ab3302SCarolineConcatto std::make_pair(nameToken, Definition{dir, j, tokens - j})); 63264ab3302SCarolineConcatto } 63364ab3302SCarolineConcatto } else if (dirName == "undef") { 63464ab3302SCarolineConcatto if (nameToken.empty()) { 635f411be0dSpeter klausler prescanner.Say( 63664ab3302SCarolineConcatto dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset), 63764ab3302SCarolineConcatto "# missing or invalid name"_err_en_US); 63864ab3302SCarolineConcatto } else { 639cf2274b7Speter klausler if (dir.IsAnythingLeft(++j)) { 640505f6da1SPeter Klausler if (prescanner.features().ShouldWarn( 641505f6da1SPeter Klausler common::UsageWarning::Portability)) { 6420f973ac7SPeter Klausler prescanner.Say(common::UsageWarning::Portability, 6430f973ac7SPeter Klausler dir.GetIntervalProvenanceRange(j, tokens - j), 644a53967cdSPeter Klausler "#undef: excess tokens at end of directive"_port_en_US); 645505f6da1SPeter Klausler } 64664ab3302SCarolineConcatto } else { 64764ab3302SCarolineConcatto definitions_.erase(nameToken); 64864ab3302SCarolineConcatto } 64964ab3302SCarolineConcatto } 65064ab3302SCarolineConcatto } else if (dirName == "ifdef" || dirName == "ifndef") { 65164ab3302SCarolineConcatto bool doThen{false}; 65264ab3302SCarolineConcatto if (nameToken.empty()) { 653f411be0dSpeter klausler prescanner.Say( 65464ab3302SCarolineConcatto dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset), 65564ab3302SCarolineConcatto "#%s: missing name"_err_en_US, dirName); 65664ab3302SCarolineConcatto } else { 657cf2274b7Speter klausler if (dir.IsAnythingLeft(++j)) { 658505f6da1SPeter Klausler if (prescanner.features().ShouldWarn( 659505f6da1SPeter Klausler common::UsageWarning::Portability)) { 6600f973ac7SPeter Klausler prescanner.Say(common::UsageWarning::Portability, 6610f973ac7SPeter Klausler dir.GetIntervalProvenanceRange(j, tokens - j), 662a53967cdSPeter Klausler "#%s: excess tokens at end of directive"_port_en_US, dirName); 66364ab3302SCarolineConcatto } 664505f6da1SPeter Klausler } 66564ab3302SCarolineConcatto doThen = IsNameDefined(nameToken) == (dirName == "ifdef"); 66664ab3302SCarolineConcatto } 66764ab3302SCarolineConcatto if (doThen) { 66864ab3302SCarolineConcatto ifStack_.push(CanDeadElseAppear::Yes); 66964ab3302SCarolineConcatto } else { 67064ab3302SCarolineConcatto SkipDisabledConditionalCode(dirName, IsElseActive::Yes, prescanner, 67164ab3302SCarolineConcatto dir.GetTokenProvenance(dirOffset)); 67264ab3302SCarolineConcatto } 67364ab3302SCarolineConcatto } else if (dirName == "if") { 67464ab3302SCarolineConcatto if (IsIfPredicateTrue(dir, j, tokens - j, prescanner)) { 67564ab3302SCarolineConcatto ifStack_.push(CanDeadElseAppear::Yes); 67664ab3302SCarolineConcatto } else { 67764ab3302SCarolineConcatto SkipDisabledConditionalCode(dirName, IsElseActive::Yes, prescanner, 67864ab3302SCarolineConcatto dir.GetTokenProvenanceRange(dirOffset)); 67964ab3302SCarolineConcatto } 68064ab3302SCarolineConcatto } else if (dirName == "else") { 681cf2274b7Speter klausler if (dir.IsAnythingLeft(j)) { 682505f6da1SPeter Klausler if (prescanner.features().ShouldWarn(common::UsageWarning::Portability)) { 6830f973ac7SPeter Klausler prescanner.Say(common::UsageWarning::Portability, 6840f973ac7SPeter Klausler dir.GetIntervalProvenanceRange(j, tokens - j), 685a53967cdSPeter Klausler "#else: excess tokens at end of directive"_port_en_US); 686505f6da1SPeter Klausler } 68764ab3302SCarolineConcatto } else if (ifStack_.empty()) { 688f411be0dSpeter klausler prescanner.Say(dir.GetTokenProvenanceRange(dirOffset), 68964ab3302SCarolineConcatto "#else: not nested within #if, #ifdef, or #ifndef"_err_en_US); 69064ab3302SCarolineConcatto } else if (ifStack_.top() != CanDeadElseAppear::Yes) { 691f411be0dSpeter klausler prescanner.Say(dir.GetTokenProvenanceRange(dirOffset), 69264ab3302SCarolineConcatto "#else: already appeared within this #if, #ifdef, or #ifndef"_err_en_US); 69364ab3302SCarolineConcatto } else { 69464ab3302SCarolineConcatto ifStack_.pop(); 69564ab3302SCarolineConcatto SkipDisabledConditionalCode("else", IsElseActive::No, prescanner, 69664ab3302SCarolineConcatto dir.GetTokenProvenanceRange(dirOffset)); 69764ab3302SCarolineConcatto } 69864ab3302SCarolineConcatto } else if (dirName == "elif") { 69964ab3302SCarolineConcatto if (ifStack_.empty()) { 700f411be0dSpeter klausler prescanner.Say(dir.GetTokenProvenanceRange(dirOffset), 70164ab3302SCarolineConcatto "#elif: not nested within #if, #ifdef, or #ifndef"_err_en_US); 70264ab3302SCarolineConcatto } else if (ifStack_.top() != CanDeadElseAppear::Yes) { 703f411be0dSpeter klausler prescanner.Say(dir.GetTokenProvenanceRange(dirOffset), 70464ab3302SCarolineConcatto "#elif: #else previously appeared within this #if, #ifdef, or #ifndef"_err_en_US); 70564ab3302SCarolineConcatto } else { 70664ab3302SCarolineConcatto ifStack_.pop(); 70764ab3302SCarolineConcatto SkipDisabledConditionalCode("elif", IsElseActive::No, prescanner, 70864ab3302SCarolineConcatto dir.GetTokenProvenanceRange(dirOffset)); 70964ab3302SCarolineConcatto } 71064ab3302SCarolineConcatto } else if (dirName == "endif") { 711cf2274b7Speter klausler if (dir.IsAnythingLeft(j)) { 712505f6da1SPeter Klausler if (prescanner.features().ShouldWarn(common::UsageWarning::Portability)) { 7130f973ac7SPeter Klausler prescanner.Say(common::UsageWarning::Portability, 7140f973ac7SPeter Klausler dir.GetIntervalProvenanceRange(j, tokens - j), 715a53967cdSPeter Klausler "#endif: excess tokens at end of directive"_port_en_US); 716505f6da1SPeter Klausler } 71764ab3302SCarolineConcatto } else if (ifStack_.empty()) { 718f411be0dSpeter klausler prescanner.Say(dir.GetTokenProvenanceRange(dirOffset), 71964ab3302SCarolineConcatto "#endif: no #if, #ifdef, or #ifndef"_err_en_US); 72064ab3302SCarolineConcatto } else { 72164ab3302SCarolineConcatto ifStack_.pop(); 72264ab3302SCarolineConcatto } 72364ab3302SCarolineConcatto } else if (dirName == "error") { 724f411be0dSpeter klausler prescanner.Say( 72564ab3302SCarolineConcatto dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset), 72664ab3302SCarolineConcatto "%s"_err_en_US, dir.ToString()); 727a53967cdSPeter Klausler } else if (dirName == "warning") { 728a53967cdSPeter Klausler prescanner.Say( 729a53967cdSPeter Klausler dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset), 730a53967cdSPeter Klausler "%s"_warn_en_US, dir.ToString()); 731a53967cdSPeter Klausler } else if (dirName == "comment" || dirName == "note") { 732f411be0dSpeter klausler prescanner.Say( 73364ab3302SCarolineConcatto dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset), 73464ab3302SCarolineConcatto "%s"_en_US, dir.ToString()); 73564ab3302SCarolineConcatto } else if (dirName == "include") { 73664ab3302SCarolineConcatto if (j == tokens) { 737f411be0dSpeter klausler prescanner.Say( 73864ab3302SCarolineConcatto dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset), 73964ab3302SCarolineConcatto "#include: missing name of file to include"_err_en_US); 74064ab3302SCarolineConcatto return; 74164ab3302SCarolineConcatto } 7426110e771Speter klausler std::optional<std::string> prependPath; 74360860079SPeter Klausler TokenSequence path{dir, j, tokens - j}; 74460860079SPeter Klausler std::string include{path.TokenAt(0).ToString()}; 74560860079SPeter Klausler if (include != "<" && include.substr(0, 1) != "\"" && 74660860079SPeter Klausler include.substr(0, 1) != "'") { 74760860079SPeter Klausler path = ReplaceMacros(path, prescanner); 74860860079SPeter Klausler include = path.empty() ? ""s : path.TokenAt(0).ToString(); 74960860079SPeter Klausler } 75060860079SPeter Klausler auto pathTokens{path.SizeInTokens()}; 75160860079SPeter Klausler std::size_t k{0}; 75260860079SPeter Klausler if (include == "<") { // #include <foo> 75360860079SPeter Klausler k = 1; 75460860079SPeter Klausler if (k >= pathTokens) { 75560860079SPeter Klausler prescanner.Say(dir.GetIntervalProvenanceRange(j, pathTokens), 75664ab3302SCarolineConcatto "#include: file name missing"_err_en_US); 75764ab3302SCarolineConcatto return; 75864ab3302SCarolineConcatto } 75960860079SPeter Klausler while (k < pathTokens && path.TokenAt(k) != ">") { 76064ab3302SCarolineConcatto ++k; 76164ab3302SCarolineConcatto } 76260860079SPeter Klausler if (k >= pathTokens) { 763505f6da1SPeter Klausler if (prescanner.features().ShouldWarn( 764505f6da1SPeter Klausler common::UsageWarning::Portability)) { 7650f973ac7SPeter Klausler prescanner.Say(common::UsageWarning::Portability, 7660f973ac7SPeter Klausler dir.GetIntervalProvenanceRange(j, tokens - j), 767a53967cdSPeter Klausler "#include: expected '>' at end of included file"_port_en_US); 76864ab3302SCarolineConcatto } 769505f6da1SPeter Klausler } 77060860079SPeter Klausler TokenSequence braced{path, 1, k - 1}; 7719ce8eb0fSEthan Luis McDonough include = braced.ToString(); 77260860079SPeter Klausler } else if ((include.substr(0, 1) == "\"" || include.substr(0, 1) == "'") && 773ab9a6987SKazu Hirata include.front() == include.back()) { 7744ad72793SPeter Klausler // #include "foo" and #include 'foo' 77564ab3302SCarolineConcatto include = include.substr(1, include.size() - 2); 7764ad72793SPeter Klausler // Start search in directory of file containing the directive 7776110e771Speter klausler auto prov{dir.GetTokenProvenanceRange(dirOffset).start()}; 7786110e771Speter klausler if (const auto *currentFile{allSources_.GetSourceFile(prov)}) { 7796110e771Speter klausler prependPath = DirectoryName(currentFile->path()); 7806110e771Speter klausler } 78164ab3302SCarolineConcatto } else { 782f411be0dSpeter klausler prescanner.Say(dir.GetTokenProvenanceRange(j < tokens ? j : tokens - 1), 78360860079SPeter Klausler "#include %s: expected name of file to include"_err_en_US, 78460860079SPeter Klausler path.ToString()); 78564ab3302SCarolineConcatto return; 78664ab3302SCarolineConcatto } 78764ab3302SCarolineConcatto if (include.empty()) { 788f411be0dSpeter klausler prescanner.Say(dir.GetTokenProvenanceRange(dirOffset), 78960860079SPeter Klausler "#include %s: empty include file name"_err_en_US, path.ToString()); 79064ab3302SCarolineConcatto return; 79164ab3302SCarolineConcatto } 79260860079SPeter Klausler k = path.SkipBlanks(k + 1); 79360860079SPeter Klausler if (k < pathTokens && path.TokenAt(k).ToString() != "!") { 794505f6da1SPeter Klausler if (prescanner.features().ShouldWarn(common::UsageWarning::Portability)) { 7950f973ac7SPeter Klausler prescanner.Say(common::UsageWarning::Portability, 7960f973ac7SPeter Klausler dir.GetIntervalProvenanceRange(j, tokens - j), 797a53967cdSPeter Klausler "#include: extra stuff ignored after file name"_port_en_US); 7984706880fSpeter klausler } 799505f6da1SPeter Klausler } 8008670e499SCaroline Concatto std::string buf; 8018670e499SCaroline Concatto llvm::raw_string_ostream error{buf}; 8020525c201SPeter Klausler if (const SourceFile * 8030525c201SPeter Klausler included{allSources_.Open(include, error, std::move(prependPath))}) { 8040525c201SPeter Klausler if (included->bytes() > 0) { 80564ab3302SCarolineConcatto ProvenanceRange fileRange{ 80664ab3302SCarolineConcatto allSources_.AddIncludedFile(*included, dir.GetProvenanceRange())}; 807fc1c481cSPeter Klausler Prescanner{prescanner, *this, /*isNestedInIncludeDirective=*/true} 80864ab3302SCarolineConcatto .set_encoding(included->encoding()) 80964ab3302SCarolineConcatto .Prescan(fileRange); 81064ab3302SCarolineConcatto } 81164ab3302SCarolineConcatto } else { 8120525c201SPeter Klausler prescanner.Say(dir.GetTokenProvenanceRange(j), "#include: %s"_err_en_US, 8130525c201SPeter Klausler error.str()); 8140525c201SPeter Klausler } 8150525c201SPeter Klausler } else { 816f411be0dSpeter klausler prescanner.Say(dir.GetTokenProvenanceRange(dirOffset), 81764ab3302SCarolineConcatto "#%s: unknown or unimplemented directive"_err_en_US, dirName); 81864ab3302SCarolineConcatto } 81964ab3302SCarolineConcatto } 82064ab3302SCarolineConcatto 8217d60232bSKrzysztof Parzyszek void Preprocessor::PrintMacros(llvm::raw_ostream &out) const { 8227d60232bSKrzysztof Parzyszek // std::set is ordered. Use that to print the macros in an 8237d60232bSKrzysztof Parzyszek // alphabetical order. 8247d60232bSKrzysztof Parzyszek std::set<std::string> macroNames; 8257d60232bSKrzysztof Parzyszek for (const auto &[name, _] : definitions_) { 8267d60232bSKrzysztof Parzyszek macroNames.insert(name.ToString()); 8277d60232bSKrzysztof Parzyszek } 8287d60232bSKrzysztof Parzyszek 8297d60232bSKrzysztof Parzyszek for (const std::string &name : macroNames) { 8307d60232bSKrzysztof Parzyszek out << "#define " << name; 8317d60232bSKrzysztof Parzyszek definitions_.at(name).Print(out, name.c_str()); 8327d60232bSKrzysztof Parzyszek out << '\n'; 8337d60232bSKrzysztof Parzyszek } 8347d60232bSKrzysztof Parzyszek } 8357d60232bSKrzysztof Parzyszek 83664ab3302SCarolineConcatto CharBlock Preprocessor::SaveTokenAsName(const CharBlock &t) { 83764ab3302SCarolineConcatto names_.push_back(t.ToString()); 83864ab3302SCarolineConcatto return {names_.back().data(), names_.back().size()}; 83964ab3302SCarolineConcatto } 84064ab3302SCarolineConcatto 84164ab3302SCarolineConcatto bool Preprocessor::IsNameDefined(const CharBlock &token) { 84264ab3302SCarolineConcatto return definitions_.find(token) != definitions_.end(); 84364ab3302SCarolineConcatto } 84464ab3302SCarolineConcatto 84550e1ad6eSRoger Ferrer Ibanez bool Preprocessor::IsFunctionLikeDefinition(const CharBlock &token) { 84650e1ad6eSRoger Ferrer Ibanez auto it{definitions_.find(token)}; 84750e1ad6eSRoger Ferrer Ibanez return it != definitions_.end() && it->second.isFunctionLike(); 84850e1ad6eSRoger Ferrer Ibanez } 84950e1ad6eSRoger Ferrer Ibanez 85064ab3302SCarolineConcatto static std::string GetDirectiveName( 85164ab3302SCarolineConcatto const TokenSequence &line, std::size_t *rest) { 85264ab3302SCarolineConcatto std::size_t tokens{line.SizeInTokens()}; 85364ab3302SCarolineConcatto std::size_t j{line.SkipBlanks(0)}; 85464ab3302SCarolineConcatto if (j == tokens || line.TokenAt(j).ToString() != "#") { 85564ab3302SCarolineConcatto *rest = tokens; 85664ab3302SCarolineConcatto return ""; 85764ab3302SCarolineConcatto } 85864ab3302SCarolineConcatto j = line.SkipBlanks(j + 1); 85964ab3302SCarolineConcatto if (j == tokens) { 86064ab3302SCarolineConcatto *rest = tokens; 86164ab3302SCarolineConcatto return ""; 86264ab3302SCarolineConcatto } 86364ab3302SCarolineConcatto *rest = line.SkipBlanks(j + 1); 86464ab3302SCarolineConcatto return ToLowerCaseLetters(line.TokenAt(j).ToString()); 86564ab3302SCarolineConcatto } 86664ab3302SCarolineConcatto 86764ab3302SCarolineConcatto void Preprocessor::SkipDisabledConditionalCode(const std::string &dirName, 868f411be0dSpeter klausler IsElseActive isElseActive, Prescanner &prescanner, 86964ab3302SCarolineConcatto ProvenanceRange provenanceRange) { 87064ab3302SCarolineConcatto int nesting{0}; 871f411be0dSpeter klausler while (!prescanner.IsAtEnd()) { 872f411be0dSpeter klausler if (!prescanner.IsNextLinePreprocessorDirective()) { 873f411be0dSpeter klausler prescanner.NextLine(); 87464ab3302SCarolineConcatto continue; 87564ab3302SCarolineConcatto } 876f411be0dSpeter klausler TokenSequence line{prescanner.TokenizePreprocessorDirective()}; 87764ab3302SCarolineConcatto std::size_t rest{0}; 87864ab3302SCarolineConcatto std::string dn{GetDirectiveName(line, &rest)}; 87964ab3302SCarolineConcatto if (dn == "ifdef" || dn == "ifndef" || dn == "if") { 88064ab3302SCarolineConcatto ++nesting; 88164ab3302SCarolineConcatto } else if (dn == "endif") { 88264ab3302SCarolineConcatto if (nesting-- == 0) { 88364ab3302SCarolineConcatto return; 88464ab3302SCarolineConcatto } 88564ab3302SCarolineConcatto } else if (isElseActive == IsElseActive::Yes && nesting == 0) { 88664ab3302SCarolineConcatto if (dn == "else") { 88764ab3302SCarolineConcatto ifStack_.push(CanDeadElseAppear::No); 88864ab3302SCarolineConcatto return; 88964ab3302SCarolineConcatto } 89064ab3302SCarolineConcatto if (dn == "elif" && 89164ab3302SCarolineConcatto IsIfPredicateTrue( 89264ab3302SCarolineConcatto line, rest, line.SizeInTokens() - rest, prescanner)) { 89364ab3302SCarolineConcatto ifStack_.push(CanDeadElseAppear::Yes); 89464ab3302SCarolineConcatto return; 89564ab3302SCarolineConcatto } 89664ab3302SCarolineConcatto } 89764ab3302SCarolineConcatto } 898f411be0dSpeter klausler prescanner.Say(provenanceRange, "#%s: missing #endif"_err_en_US, dirName); 89964ab3302SCarolineConcatto } 90064ab3302SCarolineConcatto 90164ab3302SCarolineConcatto // Precedence level codes used here to accommodate mixed Fortran and C: 90264ab3302SCarolineConcatto // 15: parentheses and constants, logical !, bitwise ~ 90364ab3302SCarolineConcatto // 14: unary + and - 90464ab3302SCarolineConcatto // 13: ** 90564ab3302SCarolineConcatto // 12: *, /, % (modulus) 90664ab3302SCarolineConcatto // 11: + and - 90764ab3302SCarolineConcatto // 10: << and >> 90864ab3302SCarolineConcatto // 9: bitwise & 90964ab3302SCarolineConcatto // 8: bitwise ^ 91064ab3302SCarolineConcatto // 7: bitwise | 91164ab3302SCarolineConcatto // 6: relations (.EQ., ==, &c.) 91264ab3302SCarolineConcatto // 5: .NOT. 91364ab3302SCarolineConcatto // 4: .AND., && 91464ab3302SCarolineConcatto // 3: .OR., || 91564ab3302SCarolineConcatto // 2: .EQV. and .NEQV. / .XOR. 91664ab3302SCarolineConcatto // 1: ? : 91764ab3302SCarolineConcatto // 0: , 91864ab3302SCarolineConcatto static std::int64_t ExpressionValue(const TokenSequence &token, 91964ab3302SCarolineConcatto int minimumPrecedence, std::size_t *atToken, 92064ab3302SCarolineConcatto std::optional<Message> *error) { 92164ab3302SCarolineConcatto enum Operator { 92264ab3302SCarolineConcatto PARENS, 92364ab3302SCarolineConcatto CONST, 92464ab3302SCarolineConcatto NOTZERO, // ! 92564ab3302SCarolineConcatto COMPLEMENT, // ~ 92664ab3302SCarolineConcatto UPLUS, 92764ab3302SCarolineConcatto UMINUS, 92864ab3302SCarolineConcatto POWER, 92964ab3302SCarolineConcatto TIMES, 93064ab3302SCarolineConcatto DIVIDE, 93164ab3302SCarolineConcatto MODULUS, 93264ab3302SCarolineConcatto ADD, 93364ab3302SCarolineConcatto SUBTRACT, 93464ab3302SCarolineConcatto LEFTSHIFT, 93564ab3302SCarolineConcatto RIGHTSHIFT, 93664ab3302SCarolineConcatto BITAND, 93764ab3302SCarolineConcatto BITXOR, 93864ab3302SCarolineConcatto BITOR, 93964ab3302SCarolineConcatto LT, 94064ab3302SCarolineConcatto LE, 94164ab3302SCarolineConcatto EQ, 94264ab3302SCarolineConcatto NE, 94364ab3302SCarolineConcatto GE, 94464ab3302SCarolineConcatto GT, 94564ab3302SCarolineConcatto NOT, 94664ab3302SCarolineConcatto AND, 94764ab3302SCarolineConcatto OR, 94864ab3302SCarolineConcatto EQV, 94964ab3302SCarolineConcatto NEQV, 95064ab3302SCarolineConcatto SELECT, 95164ab3302SCarolineConcatto COMMA 95264ab3302SCarolineConcatto }; 95364ab3302SCarolineConcatto static const int precedence[]{ 95464ab3302SCarolineConcatto 15, 15, 15, 15, // (), 6, !, ~ 95564ab3302SCarolineConcatto 14, 14, // unary +, - 95664ab3302SCarolineConcatto 13, 12, 12, 12, 11, 11, 10, 10, // **, *, /, %, +, -, <<, >> 95764ab3302SCarolineConcatto 9, 8, 7, // &, ^, | 95864ab3302SCarolineConcatto 6, 6, 6, 6, 6, 6, // relations .LT. to .GT. 95964ab3302SCarolineConcatto 5, 4, 3, 2, 2, // .NOT., .AND., .OR., .EQV., .NEQV. 96064ab3302SCarolineConcatto 1, 0 // ?: and , 96164ab3302SCarolineConcatto }; 96264ab3302SCarolineConcatto static const int operandPrecedence[]{0, -1, 15, 15, 15, 15, 13, 12, 12, 12, 96364ab3302SCarolineConcatto 11, 11, 11, 11, 9, 8, 7, 7, 7, 7, 7, 7, 7, 6, 4, 3, 3, 3, 1, 0}; 96464ab3302SCarolineConcatto 96564ab3302SCarolineConcatto static std::map<std::string, enum Operator> opNameMap; 96664ab3302SCarolineConcatto if (opNameMap.empty()) { 96764ab3302SCarolineConcatto opNameMap["("] = PARENS; 96864ab3302SCarolineConcatto opNameMap["!"] = NOTZERO; 96964ab3302SCarolineConcatto opNameMap["~"] = COMPLEMENT; 97064ab3302SCarolineConcatto opNameMap["**"] = POWER; 97164ab3302SCarolineConcatto opNameMap["*"] = TIMES; 97264ab3302SCarolineConcatto opNameMap["/"] = DIVIDE; 97364ab3302SCarolineConcatto opNameMap["%"] = MODULUS; 97464ab3302SCarolineConcatto opNameMap["+"] = ADD; 97564ab3302SCarolineConcatto opNameMap["-"] = SUBTRACT; 97664ab3302SCarolineConcatto opNameMap["<<"] = LEFTSHIFT; 97764ab3302SCarolineConcatto opNameMap[">>"] = RIGHTSHIFT; 97864ab3302SCarolineConcatto opNameMap["&"] = BITAND; 97964ab3302SCarolineConcatto opNameMap["^"] = BITXOR; 98064ab3302SCarolineConcatto opNameMap["|"] = BITOR; 98164ab3302SCarolineConcatto opNameMap[".lt."] = opNameMap["<"] = LT; 98264ab3302SCarolineConcatto opNameMap[".le."] = opNameMap["<="] = LE; 98364ab3302SCarolineConcatto opNameMap[".eq."] = opNameMap["=="] = EQ; 98464ab3302SCarolineConcatto opNameMap[".ne."] = opNameMap["/="] = opNameMap["!="] = NE; 98564ab3302SCarolineConcatto opNameMap[".ge."] = opNameMap[">="] = GE; 98664ab3302SCarolineConcatto opNameMap[".gt."] = opNameMap[">"] = GT; 98764ab3302SCarolineConcatto opNameMap[".not."] = NOT; 98864ab3302SCarolineConcatto opNameMap[".and."] = opNameMap[".a."] = opNameMap["&&"] = AND; 98964ab3302SCarolineConcatto opNameMap[".or."] = opNameMap[".o."] = opNameMap["||"] = OR; 99064ab3302SCarolineConcatto opNameMap[".eqv."] = EQV; 99164ab3302SCarolineConcatto opNameMap[".neqv."] = opNameMap[".xor."] = opNameMap[".x."] = NEQV; 99264ab3302SCarolineConcatto opNameMap["?"] = SELECT; 99364ab3302SCarolineConcatto opNameMap[","] = COMMA; 99464ab3302SCarolineConcatto } 99564ab3302SCarolineConcatto 99664ab3302SCarolineConcatto std::size_t tokens{token.SizeInTokens()}; 99764ab3302SCarolineConcatto CHECK(tokens > 0); 99864ab3302SCarolineConcatto if (*atToken >= tokens) { 99964ab3302SCarolineConcatto *error = 100064ab3302SCarolineConcatto Message{token.GetProvenanceRange(), "incomplete expression"_err_en_US}; 100164ab3302SCarolineConcatto return 0; 100264ab3302SCarolineConcatto } 100364ab3302SCarolineConcatto 100464ab3302SCarolineConcatto // Parse and evaluate a primary or a unary operator and its operand. 100564ab3302SCarolineConcatto std::size_t opAt{*atToken}; 100664ab3302SCarolineConcatto std::string t{token.TokenAt(opAt).ToString()}; 100764ab3302SCarolineConcatto enum Operator op; 100864ab3302SCarolineConcatto std::int64_t left{0}; 100964ab3302SCarolineConcatto if (t == "(") { 101064ab3302SCarolineConcatto op = PARENS; 101164ab3302SCarolineConcatto } else if (IsDecimalDigit(t[0])) { 101264ab3302SCarolineConcatto op = CONST; 101364ab3302SCarolineConcatto std::size_t consumed{0}; 101464ab3302SCarolineConcatto left = std::stoll(t, &consumed, 0 /*base to be detected*/); 101564ab3302SCarolineConcatto if (consumed < t.size()) { 101664ab3302SCarolineConcatto *error = Message{token.GetTokenProvenanceRange(opAt), 101764ab3302SCarolineConcatto "Uninterpretable numeric constant '%s'"_err_en_US, t}; 101864ab3302SCarolineConcatto return 0; 101964ab3302SCarolineConcatto } 102064ab3302SCarolineConcatto } else if (IsLegalIdentifierStart(t[0])) { 102164ab3302SCarolineConcatto // undefined macro name -> zero 102264ab3302SCarolineConcatto // TODO: BOZ constants? 102364ab3302SCarolineConcatto op = CONST; 102464ab3302SCarolineConcatto } else if (t == "+") { 102564ab3302SCarolineConcatto op = UPLUS; 102664ab3302SCarolineConcatto } else if (t == "-") { 102764ab3302SCarolineConcatto op = UMINUS; 102864ab3302SCarolineConcatto } else if (t == "." && *atToken + 2 < tokens && 102964ab3302SCarolineConcatto ToLowerCaseLetters(token.TokenAt(*atToken + 1).ToString()) == "not" && 103064ab3302SCarolineConcatto token.TokenAt(*atToken + 2).ToString() == ".") { 103164ab3302SCarolineConcatto op = NOT; 103264ab3302SCarolineConcatto *atToken += 2; 103364ab3302SCarolineConcatto } else { 103464ab3302SCarolineConcatto auto it{opNameMap.find(t)}; 103564ab3302SCarolineConcatto if (it != opNameMap.end()) { 103664ab3302SCarolineConcatto op = it->second; 103764ab3302SCarolineConcatto } else { 103864ab3302SCarolineConcatto *error = Message{token.GetTokenProvenanceRange(opAt), 103964ab3302SCarolineConcatto "operand expected in expression"_err_en_US}; 104064ab3302SCarolineConcatto return 0; 104164ab3302SCarolineConcatto } 104264ab3302SCarolineConcatto } 104364ab3302SCarolineConcatto if (precedence[op] < minimumPrecedence) { 104464ab3302SCarolineConcatto *error = Message{token.GetTokenProvenanceRange(opAt), 104564ab3302SCarolineConcatto "operator precedence error"_err_en_US}; 104664ab3302SCarolineConcatto return 0; 104764ab3302SCarolineConcatto } 104864ab3302SCarolineConcatto ++*atToken; 104964ab3302SCarolineConcatto if (op != CONST) { 105064ab3302SCarolineConcatto left = ExpressionValue(token, operandPrecedence[op], atToken, error); 105164ab3302SCarolineConcatto if (*error) { 105264ab3302SCarolineConcatto return 0; 105364ab3302SCarolineConcatto } 105464ab3302SCarolineConcatto switch (op) { 105564ab3302SCarolineConcatto case PARENS: 10566fac3f7bSPeter Klausler if (*atToken < tokens && token.TokenAt(*atToken).OnlyNonBlank() == ')') { 105764ab3302SCarolineConcatto ++*atToken; 105864ab3302SCarolineConcatto break; 105964ab3302SCarolineConcatto } 106064ab3302SCarolineConcatto if (*atToken >= tokens) { 106164ab3302SCarolineConcatto *error = Message{token.GetProvenanceRange(), 106264ab3302SCarolineConcatto "')' missing from expression"_err_en_US}; 106364ab3302SCarolineConcatto } else { 106464ab3302SCarolineConcatto *error = Message{ 106564ab3302SCarolineConcatto token.GetTokenProvenanceRange(*atToken), "expected ')'"_err_en_US}; 106664ab3302SCarolineConcatto } 106764ab3302SCarolineConcatto return 0; 10681f879005STim Keith case NOTZERO: 10691f879005STim Keith left = !left; 10701f879005STim Keith break; 10711f879005STim Keith case COMPLEMENT: 10721f879005STim Keith left = ~left; 10731f879005STim Keith break; 10741f879005STim Keith case UPLUS: 10751f879005STim Keith break; 10761f879005STim Keith case UMINUS: 10771f879005STim Keith left = -left; 10781f879005STim Keith break; 10791f879005STim Keith case NOT: 10801f879005STim Keith left = -!left; 10811f879005STim Keith break; 10821f879005STim Keith default: 10831f879005STim Keith CRASH_NO_CASE; 108464ab3302SCarolineConcatto } 108564ab3302SCarolineConcatto } 108664ab3302SCarolineConcatto 108764ab3302SCarolineConcatto // Parse and evaluate binary operators and their second operands, if present. 108864ab3302SCarolineConcatto while (*atToken < tokens) { 108964ab3302SCarolineConcatto int advance{1}; 109064ab3302SCarolineConcatto t = token.TokenAt(*atToken).ToString(); 109164ab3302SCarolineConcatto if (t == "." && *atToken + 2 < tokens && 109264ab3302SCarolineConcatto token.TokenAt(*atToken + 2).ToString() == ".") { 109364ab3302SCarolineConcatto t += ToLowerCaseLetters(token.TokenAt(*atToken + 1).ToString()) + '.'; 109464ab3302SCarolineConcatto advance = 3; 109564ab3302SCarolineConcatto } 109664ab3302SCarolineConcatto auto it{opNameMap.find(t)}; 109764ab3302SCarolineConcatto if (it == opNameMap.end()) { 109864ab3302SCarolineConcatto break; 109964ab3302SCarolineConcatto } 110064ab3302SCarolineConcatto op = it->second; 110164ab3302SCarolineConcatto if (op < POWER || precedence[op] < minimumPrecedence) { 110264ab3302SCarolineConcatto break; 110364ab3302SCarolineConcatto } 110464ab3302SCarolineConcatto opAt = *atToken; 110564ab3302SCarolineConcatto *atToken += advance; 110664ab3302SCarolineConcatto 110764ab3302SCarolineConcatto std::int64_t right{ 110864ab3302SCarolineConcatto ExpressionValue(token, operandPrecedence[op], atToken, error)}; 110964ab3302SCarolineConcatto if (*error) { 111064ab3302SCarolineConcatto return 0; 111164ab3302SCarolineConcatto } 111264ab3302SCarolineConcatto 111364ab3302SCarolineConcatto switch (op) { 111464ab3302SCarolineConcatto case POWER: 111564ab3302SCarolineConcatto if (left == 0) { 111664ab3302SCarolineConcatto if (right < 0) { 111764ab3302SCarolineConcatto *error = Message{token.GetTokenProvenanceRange(opAt), 111864ab3302SCarolineConcatto "0 ** negative power"_err_en_US}; 111964ab3302SCarolineConcatto } 112064ab3302SCarolineConcatto } else if (left != 1 && right != 1) { 112164ab3302SCarolineConcatto if (right <= 0) { 112264ab3302SCarolineConcatto left = !right; 112364ab3302SCarolineConcatto } else { 112464ab3302SCarolineConcatto std::int64_t power{1}; 112564ab3302SCarolineConcatto for (; right > 0; --right) { 112664ab3302SCarolineConcatto if ((power * left) / left != power) { 112764ab3302SCarolineConcatto *error = Message{token.GetTokenProvenanceRange(opAt), 112864ab3302SCarolineConcatto "overflow in exponentation"_err_en_US}; 112964ab3302SCarolineConcatto left = 1; 113064ab3302SCarolineConcatto } 113164ab3302SCarolineConcatto power *= left; 113264ab3302SCarolineConcatto } 113364ab3302SCarolineConcatto left = power; 113464ab3302SCarolineConcatto } 113564ab3302SCarolineConcatto } 113664ab3302SCarolineConcatto break; 113764ab3302SCarolineConcatto case TIMES: 113864ab3302SCarolineConcatto if (left != 0 && right != 0 && ((left * right) / left) != right) { 113964ab3302SCarolineConcatto *error = Message{token.GetTokenProvenanceRange(opAt), 114064ab3302SCarolineConcatto "overflow in multiplication"_err_en_US}; 114164ab3302SCarolineConcatto } 114264ab3302SCarolineConcatto left = left * right; 114364ab3302SCarolineConcatto break; 114464ab3302SCarolineConcatto case DIVIDE: 114564ab3302SCarolineConcatto if (right == 0) { 114664ab3302SCarolineConcatto *error = Message{ 114764ab3302SCarolineConcatto token.GetTokenProvenanceRange(opAt), "division by zero"_err_en_US}; 114864ab3302SCarolineConcatto left = 0; 114964ab3302SCarolineConcatto } else { 115064ab3302SCarolineConcatto left = left / right; 115164ab3302SCarolineConcatto } 115264ab3302SCarolineConcatto break; 115364ab3302SCarolineConcatto case MODULUS: 115464ab3302SCarolineConcatto if (right == 0) { 115564ab3302SCarolineConcatto *error = Message{ 115664ab3302SCarolineConcatto token.GetTokenProvenanceRange(opAt), "modulus by zero"_err_en_US}; 115764ab3302SCarolineConcatto left = 0; 115864ab3302SCarolineConcatto } else { 115964ab3302SCarolineConcatto left = left % right; 116064ab3302SCarolineConcatto } 116164ab3302SCarolineConcatto break; 116264ab3302SCarolineConcatto case ADD: 116364ab3302SCarolineConcatto if ((left < 0) == (right < 0) && (left < 0) != (left + right < 0)) { 116464ab3302SCarolineConcatto *error = Message{token.GetTokenProvenanceRange(opAt), 116564ab3302SCarolineConcatto "overflow in addition"_err_en_US}; 116664ab3302SCarolineConcatto } 116764ab3302SCarolineConcatto left = left + right; 116864ab3302SCarolineConcatto break; 116964ab3302SCarolineConcatto case SUBTRACT: 117064ab3302SCarolineConcatto if ((left < 0) != (right < 0) && (left < 0) == (left - right < 0)) { 117164ab3302SCarolineConcatto *error = Message{token.GetTokenProvenanceRange(opAt), 117264ab3302SCarolineConcatto "overflow in subtraction"_err_en_US}; 117364ab3302SCarolineConcatto } 117464ab3302SCarolineConcatto left = left - right; 117564ab3302SCarolineConcatto break; 117664ab3302SCarolineConcatto case LEFTSHIFT: 117764ab3302SCarolineConcatto if (right < 0 || right > 64) { 117864ab3302SCarolineConcatto *error = Message{token.GetTokenProvenanceRange(opAt), 117964ab3302SCarolineConcatto "bad left shift count"_err_en_US}; 118064ab3302SCarolineConcatto } 118164ab3302SCarolineConcatto left = right >= 64 ? 0 : left << right; 118264ab3302SCarolineConcatto break; 118364ab3302SCarolineConcatto case RIGHTSHIFT: 118464ab3302SCarolineConcatto if (right < 0 || right > 64) { 118564ab3302SCarolineConcatto *error = Message{token.GetTokenProvenanceRange(opAt), 118664ab3302SCarolineConcatto "bad right shift count"_err_en_US}; 118764ab3302SCarolineConcatto } 118864ab3302SCarolineConcatto left = right >= 64 ? 0 : left >> right; 118964ab3302SCarolineConcatto break; 119064ab3302SCarolineConcatto case BITAND: 11911f879005STim Keith case AND: 11921f879005STim Keith left = left & right; 11931f879005STim Keith break; 11941f879005STim Keith case BITXOR: 11951f879005STim Keith left = left ^ right; 11961f879005STim Keith break; 119764ab3302SCarolineConcatto case BITOR: 11981f879005STim Keith case OR: 11991f879005STim Keith left = left | right; 12001f879005STim Keith break; 12011f879005STim Keith case LT: 12021f879005STim Keith left = -(left < right); 12031f879005STim Keith break; 12041f879005STim Keith case LE: 12051f879005STim Keith left = -(left <= right); 12061f879005STim Keith break; 12071f879005STim Keith case EQ: 12081f879005STim Keith left = -(left == right); 12091f879005STim Keith break; 12101f879005STim Keith case NE: 12111f879005STim Keith left = -(left != right); 12121f879005STim Keith break; 12131f879005STim Keith case GE: 12141f879005STim Keith left = -(left >= right); 12151f879005STim Keith break; 12161f879005STim Keith case GT: 12171f879005STim Keith left = -(left > right); 12181f879005STim Keith break; 12191f879005STim Keith case EQV: 12201f879005STim Keith left = -(!left == !right); 12211f879005STim Keith break; 12221f879005STim Keith case NEQV: 12231f879005STim Keith left = -(!left != !right); 12241f879005STim Keith break; 122564ab3302SCarolineConcatto case SELECT: 122664ab3302SCarolineConcatto if (*atToken >= tokens || token.TokenAt(*atToken).ToString() != ":") { 122764ab3302SCarolineConcatto *error = Message{token.GetTokenProvenanceRange(opAt), 122864ab3302SCarolineConcatto "':' required in selection expression"_err_en_US}; 122964ab3302SCarolineConcatto return 0; 123064ab3302SCarolineConcatto } else { 123164ab3302SCarolineConcatto ++*atToken; 123264ab3302SCarolineConcatto std::int64_t third{ 123364ab3302SCarolineConcatto ExpressionValue(token, operandPrecedence[op], atToken, error)}; 123464ab3302SCarolineConcatto left = left != 0 ? right : third; 123564ab3302SCarolineConcatto } 123664ab3302SCarolineConcatto break; 12371f879005STim Keith case COMMA: 12381f879005STim Keith left = right; 12391f879005STim Keith break; 12401f879005STim Keith default: 12411f879005STim Keith CRASH_NO_CASE; 124264ab3302SCarolineConcatto } 124364ab3302SCarolineConcatto } 124464ab3302SCarolineConcatto return left; 124564ab3302SCarolineConcatto } 124664ab3302SCarolineConcatto 1247*850d42fbSPeter Klausler bool Preprocessor::IsIfPredicateTrue(const TokenSequence &directive, 1248f411be0dSpeter klausler std::size_t first, std::size_t exprTokens, Prescanner &prescanner) { 1249*850d42fbSPeter Klausler TokenSequence expr{directive, first, exprTokens}; 1250*850d42fbSPeter Klausler TokenSequence replaced{ 1251*850d42fbSPeter Klausler ReplaceMacros(expr, prescanner, nullptr, /*inIfExpression=*/true)}; 1252*850d42fbSPeter Klausler if (replaced.HasBlanks()) { 1253*850d42fbSPeter Klausler replaced.RemoveBlanks(); 125464ab3302SCarolineConcatto } 1255*850d42fbSPeter Klausler if (replaced.empty()) { 1256f411be0dSpeter klausler prescanner.Say(expr.GetProvenanceRange(), "empty expression"_err_en_US); 125764ab3302SCarolineConcatto return false; 125864ab3302SCarolineConcatto } 125964ab3302SCarolineConcatto std::size_t atToken{0}; 126064ab3302SCarolineConcatto std::optional<Message> error; 1261*850d42fbSPeter Klausler bool result{ExpressionValue(replaced, 0, &atToken, &error) != 0}; 126264ab3302SCarolineConcatto if (error) { 1263f411be0dSpeter klausler prescanner.Say(std::move(*error)); 1264*850d42fbSPeter Klausler } else if (atToken < replaced.SizeInTokens() && 1265*850d42fbSPeter Klausler replaced.TokenAt(atToken).ToString() != "!") { 1266*850d42fbSPeter Klausler prescanner.Say(replaced.GetIntervalProvenanceRange( 1267*850d42fbSPeter Klausler atToken, replaced.SizeInTokens() - atToken), 126864ab3302SCarolineConcatto atToken == 0 ? "could not parse any expression"_err_en_US 126964ab3302SCarolineConcatto : "excess characters after expression"_err_en_US); 127064ab3302SCarolineConcatto } 127164ab3302SCarolineConcatto return result; 127264ab3302SCarolineConcatto } 1273e12ffe6aSPeter Klausler 1274e12ffe6aSPeter Klausler void Preprocessor::LineDirective( 1275e12ffe6aSPeter Klausler const TokenSequence &dir, std::size_t j, Prescanner &prescanner) { 1276e12ffe6aSPeter Klausler std::size_t tokens{dir.SizeInTokens()}; 1277e12ffe6aSPeter Klausler const std::string *linePath{nullptr}; 1278e12ffe6aSPeter Klausler std::optional<int> lineNumber; 1279e12ffe6aSPeter Klausler SourceFile *sourceFile{nullptr}; 1280e12ffe6aSPeter Klausler std::optional<SourcePosition> pos; 1281e12ffe6aSPeter Klausler for (; j < tokens; j = dir.SkipBlanks(j + 1)) { 1282e12ffe6aSPeter Klausler std::string tstr{dir.TokenAt(j).ToString()}; 1283e12ffe6aSPeter Klausler Provenance provenance{dir.GetTokenProvenance(j)}; 1284e12ffe6aSPeter Klausler if (!pos) { 1285e12ffe6aSPeter Klausler pos = allSources_.GetSourcePosition(provenance); 1286e12ffe6aSPeter Klausler } 1287e12ffe6aSPeter Klausler if (!sourceFile && pos) { 1288e12ffe6aSPeter Klausler sourceFile = const_cast<SourceFile *>(&*pos->sourceFile); 1289e12ffe6aSPeter Klausler } 1290e12ffe6aSPeter Klausler if (tstr.front() == '"' && tstr.back() == '"') { 1291e12ffe6aSPeter Klausler tstr = tstr.substr(1, tstr.size() - 2); 1292e12ffe6aSPeter Klausler if (!tstr.empty() && sourceFile) { 1293e12ffe6aSPeter Klausler linePath = &sourceFile->SavePath(std::move(tstr)); 1294e12ffe6aSPeter Klausler } 1295e12ffe6aSPeter Klausler } else if (IsDecimalDigit(tstr[0])) { 1296e12ffe6aSPeter Klausler if (!lineNumber) { // ignore later column number 1297e12ffe6aSPeter Klausler int ln{0}; 1298e12ffe6aSPeter Klausler for (char c : tstr) { 1299e12ffe6aSPeter Klausler if (IsDecimalDigit(c)) { 1300e12ffe6aSPeter Klausler int nln{10 * ln + c - '0'}; 1301e12ffe6aSPeter Klausler if (nln / 10 == ln && nln % 10 == c - '0') { 1302e12ffe6aSPeter Klausler ln = nln; 1303e12ffe6aSPeter Klausler continue; 1304e12ffe6aSPeter Klausler } 1305e12ffe6aSPeter Klausler } 1306e12ffe6aSPeter Klausler prescanner.Say(provenance, 1307e12ffe6aSPeter Klausler "bad line number '%s' in #line directive"_err_en_US, tstr); 1308e12ffe6aSPeter Klausler return; 1309e12ffe6aSPeter Klausler } 1310e12ffe6aSPeter Klausler lineNumber = ln; 1311e12ffe6aSPeter Klausler } 1312e12ffe6aSPeter Klausler } else { 1313e12ffe6aSPeter Klausler prescanner.Say( 1314e12ffe6aSPeter Klausler provenance, "bad token '%s' in #line directive"_err_en_US, tstr); 1315e12ffe6aSPeter Klausler return; 1316e12ffe6aSPeter Klausler } 1317e12ffe6aSPeter Klausler } 1318e12ffe6aSPeter Klausler if (lineNumber && sourceFile) { 1319e12ffe6aSPeter Klausler CHECK(pos); 1320e12ffe6aSPeter Klausler if (!linePath) { 1321e12ffe6aSPeter Klausler linePath = &*pos->path; 1322e12ffe6aSPeter Klausler } 1323e12ffe6aSPeter Klausler sourceFile->LineDirective(pos->trueLineNumber + 1, *linePath, *lineNumber); 1324e12ffe6aSPeter Klausler } 1325e12ffe6aSPeter Klausler } 13266fac3f7bSPeter Klausler 13271f879005STim Keith } // namespace Fortran::parser 1328