xref: /llvm-project/flang/lib/Parser/preprocessor.cpp (revision 850d42fb145c636a3b56a7616c3e3c5c188c1916)
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 &macro, 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