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 
964ab3302SCarolineConcatto #include "preprocessor.h"
1064ab3302SCarolineConcatto #include "prescan.h"
1164ab3302SCarolineConcatto #include "flang/Common/idioms.h"
1264ab3302SCarolineConcatto #include "flang/Parser/characters.h"
1364ab3302SCarolineConcatto #include "flang/Parser/message.h"
148670e499SCaroline Concatto #include "llvm/Support/raw_ostream.h"
1564ab3302SCarolineConcatto #include <algorithm>
1664ab3302SCarolineConcatto #include <cinttypes>
1764ab3302SCarolineConcatto #include <cstddef>
1864ab3302SCarolineConcatto #include <ctime>
1964ab3302SCarolineConcatto #include <map>
2064ab3302SCarolineConcatto #include <memory>
2164ab3302SCarolineConcatto #include <optional>
2264ab3302SCarolineConcatto #include <set>
2364ab3302SCarolineConcatto #include <utility>
2464ab3302SCarolineConcatto 
2564ab3302SCarolineConcatto namespace Fortran::parser {
2664ab3302SCarolineConcatto 
Definition(const TokenSequence & repl,std::size_t firstToken,std::size_t tokens)2764ab3302SCarolineConcatto Definition::Definition(
2864ab3302SCarolineConcatto     const TokenSequence &repl, std::size_t firstToken, std::size_t tokens)
2964ab3302SCarolineConcatto     : replacement_{Tokenize({}, repl, firstToken, tokens)} {}
3064ab3302SCarolineConcatto 
Definition(const std::vector<std::string> & argNames,const TokenSequence & repl,std::size_t firstToken,std::size_t tokens,bool isVariadic)3164ab3302SCarolineConcatto Definition::Definition(const std::vector<std::string> &argNames,
3264ab3302SCarolineConcatto     const TokenSequence &repl, std::size_t firstToken, std::size_t tokens,
3364ab3302SCarolineConcatto     bool isVariadic)
3464ab3302SCarolineConcatto     : isFunctionLike_{true},
3564ab3302SCarolineConcatto       argumentCount_(argNames.size()), isVariadic_{isVariadic},
3664ab3302SCarolineConcatto       replacement_{Tokenize(argNames, repl, firstToken, tokens)} {}
3764ab3302SCarolineConcatto 
Definition(const std::string & predefined,AllSources & sources)3864ab3302SCarolineConcatto Definition::Definition(const std::string &predefined, AllSources &sources)
391f879005STim Keith     : isPredefined_{true},
401f879005STim Keith       replacement_{
411f879005STim Keith           predefined, sources.AddCompilerInsertion(predefined).start()} {}
4264ab3302SCarolineConcatto 
set_isDisabled(bool disable)4364ab3302SCarolineConcatto bool Definition::set_isDisabled(bool disable) {
4464ab3302SCarolineConcatto   bool was{isDisabled_};
4564ab3302SCarolineConcatto   isDisabled_ = disable;
4664ab3302SCarolineConcatto   return was;
4764ab3302SCarolineConcatto }
4864ab3302SCarolineConcatto 
IsLegalIdentifierStart(const CharBlock & cpl)4964ab3302SCarolineConcatto static bool IsLegalIdentifierStart(const CharBlock &cpl) {
5064ab3302SCarolineConcatto   return cpl.size() > 0 && IsLegalIdentifierStart(cpl[0]);
5164ab3302SCarolineConcatto }
5264ab3302SCarolineConcatto 
Tokenize(const std::vector<std::string> & argNames,const TokenSequence & token,std::size_t firstToken,std::size_t tokens)5364ab3302SCarolineConcatto TokenSequence Definition::Tokenize(const std::vector<std::string> &argNames,
5464ab3302SCarolineConcatto     const TokenSequence &token, std::size_t firstToken, std::size_t tokens) {
5564ab3302SCarolineConcatto   std::map<std::string, std::string> args;
5664ab3302SCarolineConcatto   char argIndex{'A'};
5764ab3302SCarolineConcatto   for (const std::string &arg : argNames) {
5864ab3302SCarolineConcatto     CHECK(args.find(arg) == args.end());
5964ab3302SCarolineConcatto     args[arg] = "~"s + argIndex++;
6064ab3302SCarolineConcatto   }
6164ab3302SCarolineConcatto   TokenSequence result;
6264ab3302SCarolineConcatto   for (std::size_t j{0}; j < tokens; ++j) {
6364ab3302SCarolineConcatto     CharBlock tok{token.TokenAt(firstToken + j)};
6464ab3302SCarolineConcatto     if (IsLegalIdentifierStart(tok)) {
6564ab3302SCarolineConcatto       auto it{args.find(tok.ToString())};
6664ab3302SCarolineConcatto       if (it != args.end()) {
6764ab3302SCarolineConcatto         result.Put(it->second, token.GetTokenProvenance(j));
6864ab3302SCarolineConcatto         continue;
6964ab3302SCarolineConcatto       }
7064ab3302SCarolineConcatto     }
7164ab3302SCarolineConcatto     result.Put(token, firstToken + j, 1);
7264ab3302SCarolineConcatto   }
7364ab3302SCarolineConcatto   return result;
7464ab3302SCarolineConcatto }
7564ab3302SCarolineConcatto 
Stringify(const TokenSequence & tokens,AllSources & allSources)7664ab3302SCarolineConcatto static TokenSequence Stringify(
7764ab3302SCarolineConcatto     const TokenSequence &tokens, AllSources &allSources) {
7864ab3302SCarolineConcatto   TokenSequence result;
7964ab3302SCarolineConcatto   Provenance quoteProvenance{allSources.CompilerInsertionProvenance('"')};
8064ab3302SCarolineConcatto   result.PutNextTokenChar('"', quoteProvenance);
8164ab3302SCarolineConcatto   for (std::size_t j{0}; j < tokens.SizeInTokens(); ++j) {
8264ab3302SCarolineConcatto     const CharBlock &token{tokens.TokenAt(j)};
8364ab3302SCarolineConcatto     std::size_t bytes{token.size()};
8464ab3302SCarolineConcatto     for (std::size_t k{0}; k < bytes; ++k) {
8564ab3302SCarolineConcatto       char ch{token[k]};
8664ab3302SCarolineConcatto       Provenance from{tokens.GetTokenProvenance(j, k)};
8764ab3302SCarolineConcatto       if (ch == '"' || ch == '\\') {
8864ab3302SCarolineConcatto         result.PutNextTokenChar(ch, from);
8964ab3302SCarolineConcatto       }
9064ab3302SCarolineConcatto       result.PutNextTokenChar(ch, from);
9164ab3302SCarolineConcatto     }
9264ab3302SCarolineConcatto   }
9364ab3302SCarolineConcatto   result.PutNextTokenChar('"', quoteProvenance);
9464ab3302SCarolineConcatto   result.CloseToken();
9564ab3302SCarolineConcatto   return result;
9664ab3302SCarolineConcatto }
9764ab3302SCarolineConcatto 
IsTokenPasting(CharBlock opr)9801def7f7Speter klausler constexpr bool IsTokenPasting(CharBlock opr) {
9901def7f7Speter klausler   return opr.size() == 2 && opr[0] == '#' && opr[1] == '#';
10001def7f7Speter klausler }
10101def7f7Speter klausler 
AnyTokenPasting(const TokenSequence & text)10201def7f7Speter klausler static bool AnyTokenPasting(const TokenSequence &text) {
10301def7f7Speter klausler   std::size_t tokens{text.SizeInTokens()};
10401def7f7Speter klausler   for (std::size_t j{0}; j < tokens; ++j) {
10501def7f7Speter klausler     if (IsTokenPasting(text.TokenAt(j))) {
10601def7f7Speter klausler       return true;
10701def7f7Speter klausler     }
10801def7f7Speter klausler   }
10901def7f7Speter klausler   return false;
11001def7f7Speter klausler }
11101def7f7Speter klausler 
TokenPasting(TokenSequence && text)11201def7f7Speter klausler static TokenSequence TokenPasting(TokenSequence &&text) {
11301def7f7Speter klausler   if (!AnyTokenPasting(text)) {
11401def7f7Speter klausler     return std::move(text);
11501def7f7Speter klausler   }
11664ab3302SCarolineConcatto   TokenSequence result;
11701def7f7Speter klausler   std::size_t tokens{text.SizeInTokens()};
11864ab3302SCarolineConcatto   bool pasting{false};
11901def7f7Speter klausler   for (std::size_t j{0}; j < tokens; ++j) {
12001def7f7Speter klausler     if (IsTokenPasting(text.TokenAt(j))) {
12101def7f7Speter klausler       if (!pasting) {
12201def7f7Speter klausler         while (!result.empty() &&
12301def7f7Speter klausler             result.TokenAt(result.SizeInTokens() - 1).IsBlank()) {
12401def7f7Speter klausler           result.pop_back();
12501def7f7Speter klausler         }
12601def7f7Speter klausler         if (!result.empty()) {
12701def7f7Speter klausler           result.ReopenLastToken();
12801def7f7Speter klausler           pasting = true;
12901def7f7Speter klausler         }
13001def7f7Speter klausler       }
13101def7f7Speter klausler     } else if (pasting && text.TokenAt(j).IsBlank()) {
13201def7f7Speter klausler     } else {
13301def7f7Speter klausler       result.Put(text, j, 1);
13401def7f7Speter klausler       pasting = false;
13501def7f7Speter klausler     }
13601def7f7Speter klausler   }
13701def7f7Speter klausler   return result;
13801def7f7Speter klausler }
13901def7f7Speter klausler 
Apply(const std::vector<TokenSequence> & args,Prescanner & prescanner)14001def7f7Speter klausler TokenSequence Definition::Apply(
14101def7f7Speter klausler     const std::vector<TokenSequence> &args, Prescanner &prescanner) {
14201def7f7Speter klausler   TokenSequence result;
14364ab3302SCarolineConcatto   bool skipping{false};
14464ab3302SCarolineConcatto   int parenthesesNesting{0};
14564ab3302SCarolineConcatto   std::size_t tokens{replacement_.SizeInTokens()};
14664ab3302SCarolineConcatto   for (std::size_t j{0}; j < tokens; ++j) {
14701def7f7Speter klausler     CharBlock token{replacement_.TokenAt(j)};
14864ab3302SCarolineConcatto     std::size_t bytes{token.size()};
14964ab3302SCarolineConcatto     if (skipping) {
15064ab3302SCarolineConcatto       if (bytes == 1) {
15164ab3302SCarolineConcatto         if (token[0] == '(') {
15264ab3302SCarolineConcatto           ++parenthesesNesting;
15364ab3302SCarolineConcatto         } else if (token[0] == ')') {
15464ab3302SCarolineConcatto           skipping = --parenthesesNesting > 0;
15564ab3302SCarolineConcatto         }
15664ab3302SCarolineConcatto       }
15764ab3302SCarolineConcatto       continue;
15864ab3302SCarolineConcatto     }
15901def7f7Speter klausler     if (bytes == 2 && token[0] == '~') { // argument substitution
16064ab3302SCarolineConcatto       std::size_t index = token[1] - 'A';
16164ab3302SCarolineConcatto       if (index >= args.size()) {
16264ab3302SCarolineConcatto         continue;
16364ab3302SCarolineConcatto       }
16401def7f7Speter klausler       std::size_t prev{j};
16501def7f7Speter klausler       while (prev > 0 && replacement_.TokenAt(prev - 1).IsBlank()) {
16601def7f7Speter klausler         --prev;
16701def7f7Speter klausler       }
16801def7f7Speter klausler       if (prev > 0 && replacement_.TokenAt(prev - 1).size() == 1 &&
16901def7f7Speter klausler           replacement_.TokenAt(prev - 1)[0] ==
17001def7f7Speter klausler               '#') { // stringify argument without macro replacement
17101def7f7Speter klausler         std::size_t resultSize{result.SizeInTokens()};
1720e811d3bSPeter Klausler         while (resultSize > 0 && result.TokenAt(resultSize - 1).IsBlank()) {
17364ab3302SCarolineConcatto           result.pop_back();
1740e811d3bSPeter Klausler           --resultSize;
17564ab3302SCarolineConcatto         }
17601def7f7Speter klausler         CHECK(resultSize > 0 &&
17701def7f7Speter klausler             result.TokenAt(resultSize - 1) == replacement_.TokenAt(prev - 1));
17801def7f7Speter klausler         result.pop_back();
17901def7f7Speter klausler         result.Put(Stringify(args[index], prescanner.allSources()));
18064ab3302SCarolineConcatto       } else {
18101def7f7Speter klausler         const TokenSequence *arg{&args[index]};
18201def7f7Speter klausler         std::optional<TokenSequence> replaced;
18301def7f7Speter klausler         // Don't replace macros in the actual argument if it is preceded or
18401def7f7Speter klausler         // followed by the token-pasting operator ## in the replacement text.
18501def7f7Speter klausler         if (prev == 0 || !IsTokenPasting(replacement_.TokenAt(prev - 1))) {
18601def7f7Speter klausler           auto next{replacement_.SkipBlanks(j + 1)};
18701def7f7Speter klausler           if (next >= tokens || !IsTokenPasting(replacement_.TokenAt(next))) {
18801def7f7Speter klausler             // Apply macro replacement to the actual argument
18901def7f7Speter klausler             replaced =
19001def7f7Speter klausler                 prescanner.preprocessor().MacroReplacement(*arg, prescanner);
19101def7f7Speter klausler             if (replaced) {
19201def7f7Speter klausler               arg = &*replaced;
19364ab3302SCarolineConcatto             }
19464ab3302SCarolineConcatto           }
19564ab3302SCarolineConcatto         }
19601def7f7Speter klausler         result.Put(DEREF(arg));
19764ab3302SCarolineConcatto       }
19864ab3302SCarolineConcatto     } else if (bytes == 11 && isVariadic_ &&
19964ab3302SCarolineConcatto         token.ToString() == "__VA_ARGS__") {
20001def7f7Speter klausler       Provenance commaProvenance{
20101def7f7Speter klausler           prescanner.preprocessor().allSources().CompilerInsertionProvenance(
20201def7f7Speter klausler               ',')};
20364ab3302SCarolineConcatto       for (std::size_t k{argumentCount_}; k < args.size(); ++k) {
20464ab3302SCarolineConcatto         if (k > argumentCount_) {
20564ab3302SCarolineConcatto           result.Put(","s, commaProvenance);
20664ab3302SCarolineConcatto         }
20764ab3302SCarolineConcatto         result.Put(args[k]);
20864ab3302SCarolineConcatto       }
20964ab3302SCarolineConcatto     } else if (bytes == 10 && isVariadic_ && token.ToString() == "__VA_OPT__" &&
21064ab3302SCarolineConcatto         j + 2 < tokens && replacement_.TokenAt(j + 1).ToString() == "(" &&
21164ab3302SCarolineConcatto         parenthesesNesting == 0) {
21264ab3302SCarolineConcatto       parenthesesNesting = 1;
21364ab3302SCarolineConcatto       skipping = args.size() == argumentCount_;
21464ab3302SCarolineConcatto       ++j;
21564ab3302SCarolineConcatto     } else {
21664ab3302SCarolineConcatto       if (bytes == 1 && parenthesesNesting > 0 && token[0] == '(') {
21764ab3302SCarolineConcatto         ++parenthesesNesting;
21864ab3302SCarolineConcatto       } else if (bytes == 1 && parenthesesNesting > 0 && token[0] == ')') {
21964ab3302SCarolineConcatto         if (--parenthesesNesting == 0) {
22064ab3302SCarolineConcatto           skipping = false;
22164ab3302SCarolineConcatto           continue;
22264ab3302SCarolineConcatto         }
22364ab3302SCarolineConcatto       }
22464ab3302SCarolineConcatto       result.Put(replacement_, j);
22564ab3302SCarolineConcatto     }
22664ab3302SCarolineConcatto   }
22701def7f7Speter klausler   return TokenPasting(std::move(result));
22864ab3302SCarolineConcatto }
22964ab3302SCarolineConcatto 
FormatTime(const std::time_t & now,const char * format)23064ab3302SCarolineConcatto static std::string FormatTime(const std::time_t &now, const char *format) {
23164ab3302SCarolineConcatto   char buffer[16];
23264ab3302SCarolineConcatto   return {buffer,
23364ab3302SCarolineConcatto       std::strftime(buffer, sizeof buffer, format, std::localtime(&now))};
23464ab3302SCarolineConcatto }
23564ab3302SCarolineConcatto 
Preprocessor(AllSources & allSources)2368880a63aSpeter klausler Preprocessor::Preprocessor(AllSources &allSources) : allSources_{allSources} {}
2378880a63aSpeter klausler 
DefineStandardMacros()2388880a63aSpeter klausler void Preprocessor::DefineStandardMacros() {
23964ab3302SCarolineConcatto   // Capture current local date & time once now to avoid having the values
24064ab3302SCarolineConcatto   // of __DATE__ or __TIME__ change during compilation.
24164ab3302SCarolineConcatto   std::time_t now;
24264ab3302SCarolineConcatto   std::time(&now);
2438880a63aSpeter klausler   Define("__DATE__"s, FormatTime(now, "\"%h %e %Y\"")); // e.g., "Jun 16 1904"
2448880a63aSpeter klausler   Define("__TIME__"s, FormatTime(now, "\"%T\"")); // e.g., "23:59:60"
24564ab3302SCarolineConcatto   // The values of these predefined macros depend on their invocation sites.
2468880a63aSpeter klausler   Define("__FILE__"s, "__FILE__"s);
2478880a63aSpeter klausler   Define("__LINE__"s, "__LINE__"s);
24864ab3302SCarolineConcatto }
24964ab3302SCarolineConcatto 
Define(std::string macro,std::string value)25064ab3302SCarolineConcatto void Preprocessor::Define(std::string macro, std::string value) {
25151cfad3aSpeter klausler   definitions_.emplace(SaveTokenAsName(macro), Definition{value, allSources_});
25264ab3302SCarolineConcatto }
25364ab3302SCarolineConcatto 
Undefine(std::string macro)25464ab3302SCarolineConcatto void Preprocessor::Undefine(std::string macro) { definitions_.erase(macro); }
25564ab3302SCarolineConcatto 
MacroReplacement(const TokenSequence & input,Prescanner & prescanner)25664ab3302SCarolineConcatto std::optional<TokenSequence> Preprocessor::MacroReplacement(
25701def7f7Speter klausler     const TokenSequence &input, Prescanner &prescanner) {
25864ab3302SCarolineConcatto   // Do quick scan for any use of a defined name.
2598880a63aSpeter klausler   if (definitions_.empty()) {
2608880a63aSpeter klausler     return std::nullopt;
2618880a63aSpeter klausler   }
26264ab3302SCarolineConcatto   std::size_t tokens{input.SizeInTokens()};
26364ab3302SCarolineConcatto   std::size_t j;
26464ab3302SCarolineConcatto   for (j = 0; j < tokens; ++j) {
26564ab3302SCarolineConcatto     CharBlock token{input.TokenAt(j)};
26664ab3302SCarolineConcatto     if (!token.empty() && IsLegalIdentifierStart(token[0]) &&
26764ab3302SCarolineConcatto         IsNameDefined(token)) {
26864ab3302SCarolineConcatto       break;
26964ab3302SCarolineConcatto     }
27064ab3302SCarolineConcatto   }
27164ab3302SCarolineConcatto   if (j == tokens) {
27264ab3302SCarolineConcatto     return std::nullopt; // input contains nothing that would be replaced
27364ab3302SCarolineConcatto   }
27464ab3302SCarolineConcatto   TokenSequence result{input, 0, j};
27564ab3302SCarolineConcatto   for (; j < tokens; ++j) {
27664ab3302SCarolineConcatto     const CharBlock &token{input.TokenAt(j)};
27764ab3302SCarolineConcatto     if (token.IsBlank() || !IsLegalIdentifierStart(token[0])) {
27864ab3302SCarolineConcatto       result.Put(input, j);
27964ab3302SCarolineConcatto       continue;
28064ab3302SCarolineConcatto     }
28164ab3302SCarolineConcatto     auto it{definitions_.find(token)};
28264ab3302SCarolineConcatto     if (it == definitions_.end()) {
28364ab3302SCarolineConcatto       result.Put(input, j);
28464ab3302SCarolineConcatto       continue;
28564ab3302SCarolineConcatto     }
28664ab3302SCarolineConcatto     Definition &def{it->second};
28764ab3302SCarolineConcatto     if (def.isDisabled()) {
28864ab3302SCarolineConcatto       result.Put(input, j);
28964ab3302SCarolineConcatto       continue;
29064ab3302SCarolineConcatto     }
29164ab3302SCarolineConcatto     if (!def.isFunctionLike()) {
29264ab3302SCarolineConcatto       if (def.isPredefined()) {
29364ab3302SCarolineConcatto         std::string name{def.replacement().TokenAt(0).ToString()};
29464ab3302SCarolineConcatto         std::string repl;
29564ab3302SCarolineConcatto         if (name == "__FILE__") {
29664ab3302SCarolineConcatto           repl = "\""s +
29764ab3302SCarolineConcatto               allSources_.GetPath(prescanner.GetCurrentProvenance()) + '"';
29864ab3302SCarolineConcatto         } else if (name == "__LINE__") {
2998670e499SCaroline Concatto           std::string buf;
3008670e499SCaroline Concatto           llvm::raw_string_ostream ss{buf};
30164ab3302SCarolineConcatto           ss << allSources_.GetLineNumber(prescanner.GetCurrentProvenance());
30264ab3302SCarolineConcatto           repl = ss.str();
30364ab3302SCarolineConcatto         }
30464ab3302SCarolineConcatto         if (!repl.empty()) {
30564ab3302SCarolineConcatto           ProvenanceRange insert{allSources_.AddCompilerInsertion(repl)};
30664ab3302SCarolineConcatto           ProvenanceRange call{allSources_.AddMacroCall(
30764ab3302SCarolineConcatto               insert, input.GetTokenProvenanceRange(j), repl)};
30864ab3302SCarolineConcatto           result.Put(repl, call.start());
30964ab3302SCarolineConcatto           continue;
31064ab3302SCarolineConcatto         }
31164ab3302SCarolineConcatto       }
31264ab3302SCarolineConcatto       def.set_isDisabled(true);
31301def7f7Speter klausler       TokenSequence replaced{
31401def7f7Speter klausler           TokenPasting(ReplaceMacros(def.replacement(), prescanner))};
31564ab3302SCarolineConcatto       def.set_isDisabled(false);
31664ab3302SCarolineConcatto       if (!replaced.empty()) {
31764ab3302SCarolineConcatto         ProvenanceRange from{def.replacement().GetProvenanceRange()};
31864ab3302SCarolineConcatto         ProvenanceRange use{input.GetTokenProvenanceRange(j)};
31964ab3302SCarolineConcatto         ProvenanceRange newRange{
32064ab3302SCarolineConcatto             allSources_.AddMacroCall(from, use, replaced.ToString())};
32164ab3302SCarolineConcatto         result.Put(replaced, newRange);
32264ab3302SCarolineConcatto       }
32364ab3302SCarolineConcatto       continue;
32464ab3302SCarolineConcatto     }
32564ab3302SCarolineConcatto     // Possible function-like macro call.  Skip spaces and newlines to see
32664ab3302SCarolineConcatto     // whether '(' is next.
32764ab3302SCarolineConcatto     std::size_t k{j};
32864ab3302SCarolineConcatto     bool leftParen{false};
32964ab3302SCarolineConcatto     while (++k < tokens) {
33064ab3302SCarolineConcatto       const CharBlock &lookAhead{input.TokenAt(k)};
33164ab3302SCarolineConcatto       if (!lookAhead.IsBlank() && lookAhead[0] != '\n') {
33264ab3302SCarolineConcatto         leftParen = lookAhead[0] == '(' && lookAhead.size() == 1;
33364ab3302SCarolineConcatto         break;
33464ab3302SCarolineConcatto       }
33564ab3302SCarolineConcatto     }
33664ab3302SCarolineConcatto     if (!leftParen) {
33764ab3302SCarolineConcatto       result.Put(input, j);
33864ab3302SCarolineConcatto       continue;
33964ab3302SCarolineConcatto     }
34064ab3302SCarolineConcatto     std::vector<std::size_t> argStart{++k};
34164ab3302SCarolineConcatto     for (int nesting{0}; k < tokens; ++k) {
34264ab3302SCarolineConcatto       CharBlock token{input.TokenAt(k)};
34364ab3302SCarolineConcatto       if (token.size() == 1) {
34464ab3302SCarolineConcatto         char ch{token[0]};
34564ab3302SCarolineConcatto         if (ch == '(') {
34664ab3302SCarolineConcatto           ++nesting;
34764ab3302SCarolineConcatto         } else if (ch == ')') {
34864ab3302SCarolineConcatto           if (nesting == 0) {
34964ab3302SCarolineConcatto             break;
35064ab3302SCarolineConcatto           }
35164ab3302SCarolineConcatto           --nesting;
35264ab3302SCarolineConcatto         } else if (ch == ',' && nesting == 0) {
35364ab3302SCarolineConcatto           argStart.push_back(k + 1);
35464ab3302SCarolineConcatto         }
35564ab3302SCarolineConcatto       }
35664ab3302SCarolineConcatto     }
35764ab3302SCarolineConcatto     if (argStart.size() == 1 && k == argStart[0] && def.argumentCount() == 0) {
35864ab3302SCarolineConcatto       // Subtle: () is zero arguments, not one empty argument,
35964ab3302SCarolineConcatto       // unless one argument was expected.
36064ab3302SCarolineConcatto       argStart.clear();
36164ab3302SCarolineConcatto     }
36264ab3302SCarolineConcatto     if (k >= tokens || argStart.size() < def.argumentCount() ||
36364ab3302SCarolineConcatto         (argStart.size() > def.argumentCount() && !def.isVariadic())) {
36464ab3302SCarolineConcatto       result.Put(input, j);
36564ab3302SCarolineConcatto       continue;
36664ab3302SCarolineConcatto     }
36764ab3302SCarolineConcatto     std::vector<TokenSequence> args;
36864ab3302SCarolineConcatto     for (std::size_t n{0}; n < argStart.size(); ++n) {
36964ab3302SCarolineConcatto       std::size_t at{argStart[n]};
37064ab3302SCarolineConcatto       std::size_t count{
37164ab3302SCarolineConcatto           (n + 1 == argStart.size() ? k : argStart[n + 1] - 1) - at};
37264ab3302SCarolineConcatto       args.emplace_back(TokenSequence(input, at, count));
37364ab3302SCarolineConcatto     }
37464ab3302SCarolineConcatto     def.set_isDisabled(true);
37564ab3302SCarolineConcatto     TokenSequence replaced{
37601def7f7Speter klausler         ReplaceMacros(def.Apply(args, prescanner), prescanner)};
37764ab3302SCarolineConcatto     def.set_isDisabled(false);
37864ab3302SCarolineConcatto     if (!replaced.empty()) {
37964ab3302SCarolineConcatto       ProvenanceRange from{def.replacement().GetProvenanceRange()};
38064ab3302SCarolineConcatto       ProvenanceRange use{input.GetIntervalProvenanceRange(j, k - j)};
38164ab3302SCarolineConcatto       ProvenanceRange newRange{
38264ab3302SCarolineConcatto           allSources_.AddMacroCall(from, use, replaced.ToString())};
38364ab3302SCarolineConcatto       result.Put(replaced, newRange);
38464ab3302SCarolineConcatto     }
38564ab3302SCarolineConcatto     j = k; // advance to the terminal ')'
38664ab3302SCarolineConcatto   }
38764ab3302SCarolineConcatto   return result;
38864ab3302SCarolineConcatto }
38964ab3302SCarolineConcatto 
ReplaceMacros(const TokenSequence & tokens,Prescanner & prescanner)39064ab3302SCarolineConcatto TokenSequence Preprocessor::ReplaceMacros(
39101def7f7Speter klausler     const TokenSequence &tokens, Prescanner &prescanner) {
39264ab3302SCarolineConcatto   if (std::optional<TokenSequence> repl{MacroReplacement(tokens, prescanner)}) {
39364ab3302SCarolineConcatto     return std::move(*repl);
39464ab3302SCarolineConcatto   }
39564ab3302SCarolineConcatto   return tokens;
39664ab3302SCarolineConcatto }
39764ab3302SCarolineConcatto 
Directive(const TokenSequence & dir,Prescanner & prescanner)398f411be0dSpeter klausler void Preprocessor::Directive(const TokenSequence &dir, Prescanner &prescanner) {
39964ab3302SCarolineConcatto   std::size_t tokens{dir.SizeInTokens()};
40064ab3302SCarolineConcatto   std::size_t j{dir.SkipBlanks(0)};
40164ab3302SCarolineConcatto   if (j == tokens) {
40264ab3302SCarolineConcatto     return;
40364ab3302SCarolineConcatto   }
40464ab3302SCarolineConcatto   if (dir.TokenAt(j).ToString() != "#") {
405f411be0dSpeter klausler     prescanner.Say(dir.GetTokenProvenanceRange(j), "missing '#'"_err_en_US);
40664ab3302SCarolineConcatto     return;
40764ab3302SCarolineConcatto   }
40864ab3302SCarolineConcatto   j = dir.SkipBlanks(j + 1);
40964ab3302SCarolineConcatto   while (tokens > 0 && dir.TokenAt(tokens - 1).IsBlank()) {
41064ab3302SCarolineConcatto     --tokens;
41164ab3302SCarolineConcatto   }
41264ab3302SCarolineConcatto   if (j == tokens) {
41364ab3302SCarolineConcatto     return;
41464ab3302SCarolineConcatto   }
41564ab3302SCarolineConcatto   if (IsDecimalDigit(dir.TokenAt(j)[0]) || dir.TokenAt(j)[0] == '"') {
41664ab3302SCarolineConcatto     return; // treat like #line, ignore it
41764ab3302SCarolineConcatto   }
41864ab3302SCarolineConcatto   std::size_t dirOffset{j};
41964ab3302SCarolineConcatto   std::string dirName{ToLowerCaseLetters(dir.TokenAt(dirOffset).ToString())};
42064ab3302SCarolineConcatto   j = dir.SkipBlanks(j + 1);
42164ab3302SCarolineConcatto   CharBlock nameToken;
42264ab3302SCarolineConcatto   if (j < tokens && IsLegalIdentifierStart(dir.TokenAt(j)[0])) {
42364ab3302SCarolineConcatto     nameToken = dir.TokenAt(j);
42464ab3302SCarolineConcatto   }
42564ab3302SCarolineConcatto   if (dirName == "line") {
42664ab3302SCarolineConcatto     // #line is ignored
42764ab3302SCarolineConcatto   } else if (dirName == "define") {
42864ab3302SCarolineConcatto     if (nameToken.empty()) {
429f411be0dSpeter klausler       prescanner.Say(dir.GetTokenProvenanceRange(j < tokens ? j : tokens - 1),
43064ab3302SCarolineConcatto           "#define: missing or invalid name"_err_en_US);
43164ab3302SCarolineConcatto       return;
43264ab3302SCarolineConcatto     }
43364ab3302SCarolineConcatto     nameToken = SaveTokenAsName(nameToken);
43464ab3302SCarolineConcatto     definitions_.erase(nameToken);
43564ab3302SCarolineConcatto     if (++j < tokens && dir.TokenAt(j).size() == 1 &&
43664ab3302SCarolineConcatto         dir.TokenAt(j)[0] == '(') {
43764ab3302SCarolineConcatto       j = dir.SkipBlanks(j + 1);
43864ab3302SCarolineConcatto       std::vector<std::string> argName;
43964ab3302SCarolineConcatto       bool isVariadic{false};
44064ab3302SCarolineConcatto       if (dir.TokenAt(j).ToString() != ")") {
44164ab3302SCarolineConcatto         while (true) {
44264ab3302SCarolineConcatto           std::string an{dir.TokenAt(j).ToString()};
44364ab3302SCarolineConcatto           if (an == "...") {
44464ab3302SCarolineConcatto             isVariadic = true;
44564ab3302SCarolineConcatto           } else {
44664ab3302SCarolineConcatto             if (an.empty() || !IsLegalIdentifierStart(an[0])) {
447f411be0dSpeter klausler               prescanner.Say(dir.GetTokenProvenanceRange(j),
44864ab3302SCarolineConcatto                   "#define: missing or invalid argument name"_err_en_US);
44964ab3302SCarolineConcatto               return;
45064ab3302SCarolineConcatto             }
45164ab3302SCarolineConcatto             argName.push_back(an);
45264ab3302SCarolineConcatto           }
45364ab3302SCarolineConcatto           j = dir.SkipBlanks(j + 1);
45464ab3302SCarolineConcatto           if (j == tokens) {
455f411be0dSpeter klausler             prescanner.Say(dir.GetTokenProvenanceRange(tokens - 1),
45664ab3302SCarolineConcatto                 "#define: malformed argument list"_err_en_US);
45764ab3302SCarolineConcatto             return;
45864ab3302SCarolineConcatto           }
45964ab3302SCarolineConcatto           std::string punc{dir.TokenAt(j).ToString()};
46064ab3302SCarolineConcatto           if (punc == ")") {
46164ab3302SCarolineConcatto             break;
46264ab3302SCarolineConcatto           }
46364ab3302SCarolineConcatto           if (isVariadic || punc != ",") {
464f411be0dSpeter klausler             prescanner.Say(dir.GetTokenProvenanceRange(j),
46564ab3302SCarolineConcatto                 "#define: malformed argument list"_err_en_US);
46664ab3302SCarolineConcatto             return;
46764ab3302SCarolineConcatto           }
46864ab3302SCarolineConcatto           j = dir.SkipBlanks(j + 1);
46964ab3302SCarolineConcatto           if (j == tokens) {
470f411be0dSpeter klausler             prescanner.Say(dir.GetTokenProvenanceRange(tokens - 1),
47164ab3302SCarolineConcatto                 "#define: malformed argument list"_err_en_US);
47264ab3302SCarolineConcatto             return;
47364ab3302SCarolineConcatto           }
47464ab3302SCarolineConcatto         }
47564ab3302SCarolineConcatto         if (std::set<std::string>(argName.begin(), argName.end()).size() !=
47664ab3302SCarolineConcatto             argName.size()) {
477f411be0dSpeter klausler           prescanner.Say(dir.GetTokenProvenance(dirOffset),
47864ab3302SCarolineConcatto               "#define: argument names are not distinct"_err_en_US);
47964ab3302SCarolineConcatto           return;
48064ab3302SCarolineConcatto         }
48164ab3302SCarolineConcatto       }
48264ab3302SCarolineConcatto       j = dir.SkipBlanks(j + 1);
48364ab3302SCarolineConcatto       definitions_.emplace(std::make_pair(
48464ab3302SCarolineConcatto           nameToken, Definition{argName, dir, j, tokens - j, isVariadic}));
48564ab3302SCarolineConcatto     } else {
48664ab3302SCarolineConcatto       j = dir.SkipBlanks(j + 1);
48764ab3302SCarolineConcatto       definitions_.emplace(
48864ab3302SCarolineConcatto           std::make_pair(nameToken, Definition{dir, j, tokens - j}));
48964ab3302SCarolineConcatto     }
49064ab3302SCarolineConcatto   } else if (dirName == "undef") {
49164ab3302SCarolineConcatto     if (nameToken.empty()) {
492f411be0dSpeter klausler       prescanner.Say(
49364ab3302SCarolineConcatto           dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset),
49464ab3302SCarolineConcatto           "# missing or invalid name"_err_en_US);
49564ab3302SCarolineConcatto     } else {
496cf2274b7Speter klausler       if (dir.IsAnythingLeft(++j)) {
497f411be0dSpeter klausler         prescanner.Say(dir.GetIntervalProvenanceRange(j, tokens - j),
498*a53967cdSPeter Klausler             "#undef: excess tokens at end of directive"_port_en_US);
49964ab3302SCarolineConcatto       } else {
50064ab3302SCarolineConcatto         definitions_.erase(nameToken);
50164ab3302SCarolineConcatto       }
50264ab3302SCarolineConcatto     }
50364ab3302SCarolineConcatto   } else if (dirName == "ifdef" || dirName == "ifndef") {
50464ab3302SCarolineConcatto     bool doThen{false};
50564ab3302SCarolineConcatto     if (nameToken.empty()) {
506f411be0dSpeter klausler       prescanner.Say(
50764ab3302SCarolineConcatto           dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset),
50864ab3302SCarolineConcatto           "#%s: missing name"_err_en_US, dirName);
50964ab3302SCarolineConcatto     } else {
510cf2274b7Speter klausler       if (dir.IsAnythingLeft(++j)) {
511f411be0dSpeter klausler         prescanner.Say(dir.GetIntervalProvenanceRange(j, tokens - j),
512*a53967cdSPeter Klausler             "#%s: excess tokens at end of directive"_port_en_US, dirName);
51364ab3302SCarolineConcatto       }
51464ab3302SCarolineConcatto       doThen = IsNameDefined(nameToken) == (dirName == "ifdef");
51564ab3302SCarolineConcatto     }
51664ab3302SCarolineConcatto     if (doThen) {
51764ab3302SCarolineConcatto       ifStack_.push(CanDeadElseAppear::Yes);
51864ab3302SCarolineConcatto     } else {
51964ab3302SCarolineConcatto       SkipDisabledConditionalCode(dirName, IsElseActive::Yes, prescanner,
52064ab3302SCarolineConcatto           dir.GetTokenProvenance(dirOffset));
52164ab3302SCarolineConcatto     }
52264ab3302SCarolineConcatto   } else if (dirName == "if") {
52364ab3302SCarolineConcatto     if (IsIfPredicateTrue(dir, j, tokens - j, prescanner)) {
52464ab3302SCarolineConcatto       ifStack_.push(CanDeadElseAppear::Yes);
52564ab3302SCarolineConcatto     } else {
52664ab3302SCarolineConcatto       SkipDisabledConditionalCode(dirName, IsElseActive::Yes, prescanner,
52764ab3302SCarolineConcatto           dir.GetTokenProvenanceRange(dirOffset));
52864ab3302SCarolineConcatto     }
52964ab3302SCarolineConcatto   } else if (dirName == "else") {
530cf2274b7Speter klausler     if (dir.IsAnythingLeft(j)) {
531f411be0dSpeter klausler       prescanner.Say(dir.GetIntervalProvenanceRange(j, tokens - j),
532*a53967cdSPeter Klausler           "#else: excess tokens at end of directive"_port_en_US);
53364ab3302SCarolineConcatto     } else if (ifStack_.empty()) {
534f411be0dSpeter klausler       prescanner.Say(dir.GetTokenProvenanceRange(dirOffset),
53564ab3302SCarolineConcatto           "#else: not nested within #if, #ifdef, or #ifndef"_err_en_US);
53664ab3302SCarolineConcatto     } else if (ifStack_.top() != CanDeadElseAppear::Yes) {
537f411be0dSpeter klausler       prescanner.Say(dir.GetTokenProvenanceRange(dirOffset),
53864ab3302SCarolineConcatto           "#else: already appeared within this #if, #ifdef, or #ifndef"_err_en_US);
53964ab3302SCarolineConcatto     } else {
54064ab3302SCarolineConcatto       ifStack_.pop();
54164ab3302SCarolineConcatto       SkipDisabledConditionalCode("else", IsElseActive::No, prescanner,
54264ab3302SCarolineConcatto           dir.GetTokenProvenanceRange(dirOffset));
54364ab3302SCarolineConcatto     }
54464ab3302SCarolineConcatto   } else if (dirName == "elif") {
54564ab3302SCarolineConcatto     if (ifStack_.empty()) {
546f411be0dSpeter klausler       prescanner.Say(dir.GetTokenProvenanceRange(dirOffset),
54764ab3302SCarolineConcatto           "#elif: not nested within #if, #ifdef, or #ifndef"_err_en_US);
54864ab3302SCarolineConcatto     } else if (ifStack_.top() != CanDeadElseAppear::Yes) {
549f411be0dSpeter klausler       prescanner.Say(dir.GetTokenProvenanceRange(dirOffset),
55064ab3302SCarolineConcatto           "#elif: #else previously appeared within this #if, #ifdef, or #ifndef"_err_en_US);
55164ab3302SCarolineConcatto     } else {
55264ab3302SCarolineConcatto       ifStack_.pop();
55364ab3302SCarolineConcatto       SkipDisabledConditionalCode("elif", IsElseActive::No, prescanner,
55464ab3302SCarolineConcatto           dir.GetTokenProvenanceRange(dirOffset));
55564ab3302SCarolineConcatto     }
55664ab3302SCarolineConcatto   } else if (dirName == "endif") {
557cf2274b7Speter klausler     if (dir.IsAnythingLeft(j)) {
558f411be0dSpeter klausler       prescanner.Say(dir.GetIntervalProvenanceRange(j, tokens - j),
559*a53967cdSPeter Klausler           "#endif: excess tokens at end of directive"_port_en_US);
56064ab3302SCarolineConcatto     } else if (ifStack_.empty()) {
561f411be0dSpeter klausler       prescanner.Say(dir.GetTokenProvenanceRange(dirOffset),
56264ab3302SCarolineConcatto           "#endif: no #if, #ifdef, or #ifndef"_err_en_US);
56364ab3302SCarolineConcatto     } else {
56464ab3302SCarolineConcatto       ifStack_.pop();
56564ab3302SCarolineConcatto     }
56664ab3302SCarolineConcatto   } else if (dirName == "error") {
567f411be0dSpeter klausler     prescanner.Say(
56864ab3302SCarolineConcatto         dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset),
56964ab3302SCarolineConcatto         "%s"_err_en_US, dir.ToString());
570*a53967cdSPeter Klausler   } else if (dirName == "warning") {
571*a53967cdSPeter Klausler     prescanner.Say(
572*a53967cdSPeter Klausler         dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset),
573*a53967cdSPeter Klausler         "%s"_warn_en_US, dir.ToString());
574*a53967cdSPeter Klausler   } else if (dirName == "comment" || dirName == "note") {
575f411be0dSpeter klausler     prescanner.Say(
57664ab3302SCarolineConcatto         dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset),
57764ab3302SCarolineConcatto         "%s"_en_US, dir.ToString());
57864ab3302SCarolineConcatto   } else if (dirName == "include") {
57964ab3302SCarolineConcatto     if (j == tokens) {
580f411be0dSpeter klausler       prescanner.Say(
58164ab3302SCarolineConcatto           dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset),
58264ab3302SCarolineConcatto           "#include: missing name of file to include"_err_en_US);
58364ab3302SCarolineConcatto       return;
58464ab3302SCarolineConcatto     }
58564ab3302SCarolineConcatto     std::string include;
5866110e771Speter klausler     std::optional<std::string> prependPath;
5874706880fSpeter klausler     if (dir.TokenAt(j).ToString() == "<") { // #include <foo>
58864ab3302SCarolineConcatto       std::size_t k{j + 1};
58964ab3302SCarolineConcatto       if (k >= tokens) {
590f411be0dSpeter klausler         prescanner.Say(dir.GetIntervalProvenanceRange(j, tokens - j),
59164ab3302SCarolineConcatto             "#include: file name missing"_err_en_US);
59264ab3302SCarolineConcatto         return;
59364ab3302SCarolineConcatto       }
59464ab3302SCarolineConcatto       while (k < tokens && dir.TokenAt(k) != ">") {
59564ab3302SCarolineConcatto         ++k;
59664ab3302SCarolineConcatto       }
59764ab3302SCarolineConcatto       if (k >= tokens) {
598f411be0dSpeter klausler         prescanner.Say(dir.GetIntervalProvenanceRange(j, tokens - j),
599*a53967cdSPeter Klausler             "#include: expected '>' at end of included file"_port_en_US);
60064ab3302SCarolineConcatto       }
60164ab3302SCarolineConcatto       TokenSequence braced{dir, j + 1, k - j - 1};
602f411be0dSpeter klausler       include = ReplaceMacros(braced, prescanner).ToString();
6034706880fSpeter klausler       j = k;
6044706880fSpeter klausler     } else if ((include = dir.TokenAt(j).ToString()).substr(0, 1) == "\"" &&
6054706880fSpeter klausler         include.substr(include.size() - 1, 1) == "\"") { // #include "foo"
60664ab3302SCarolineConcatto       include = include.substr(1, include.size() - 2);
6076110e771Speter klausler       // #include "foo" starts search in directory of file containing
6086110e771Speter klausler       // the directive
6096110e771Speter klausler       auto prov{dir.GetTokenProvenanceRange(dirOffset).start()};
6106110e771Speter klausler       if (const auto *currentFile{allSources_.GetSourceFile(prov)}) {
6116110e771Speter klausler         prependPath = DirectoryName(currentFile->path());
6126110e771Speter klausler       }
61364ab3302SCarolineConcatto     } else {
614f411be0dSpeter klausler       prescanner.Say(dir.GetTokenProvenanceRange(j < tokens ? j : tokens - 1),
61564ab3302SCarolineConcatto           "#include: expected name of file to include"_err_en_US);
61664ab3302SCarolineConcatto       return;
61764ab3302SCarolineConcatto     }
61864ab3302SCarolineConcatto     if (include.empty()) {
619f411be0dSpeter klausler       prescanner.Say(dir.GetTokenProvenanceRange(dirOffset),
62064ab3302SCarolineConcatto           "#include: empty include file name"_err_en_US);
62164ab3302SCarolineConcatto       return;
62264ab3302SCarolineConcatto     }
6234706880fSpeter klausler     j = dir.SkipBlanks(j + 1);
6244706880fSpeter klausler     if (j < tokens && dir.TokenAt(j).ToString() != "!") {
625f411be0dSpeter klausler       prescanner.Say(dir.GetIntervalProvenanceRange(j, tokens - j),
626*a53967cdSPeter Klausler           "#include: extra stuff ignored after file name"_port_en_US);
6274706880fSpeter klausler     }
6288670e499SCaroline Concatto     std::string buf;
6298670e499SCaroline Concatto     llvm::raw_string_ostream error{buf};
6306110e771Speter klausler     const SourceFile *included{
6316110e771Speter klausler         allSources_.Open(include, error, std::move(prependPath))};
63264ab3302SCarolineConcatto     if (!included) {
633f411be0dSpeter klausler       prescanner.Say(dir.GetTokenProvenanceRange(dirOffset),
63464ab3302SCarolineConcatto           "#include: %s"_err_en_US, error.str());
63564ab3302SCarolineConcatto     } else if (included->bytes() > 0) {
63664ab3302SCarolineConcatto       ProvenanceRange fileRange{
63764ab3302SCarolineConcatto           allSources_.AddIncludedFile(*included, dir.GetProvenanceRange())};
638f411be0dSpeter klausler       Prescanner{prescanner}
63964ab3302SCarolineConcatto           .set_encoding(included->encoding())
64064ab3302SCarolineConcatto           .Prescan(fileRange);
64164ab3302SCarolineConcatto     }
64264ab3302SCarolineConcatto   } else {
643f411be0dSpeter klausler     prescanner.Say(dir.GetTokenProvenanceRange(dirOffset),
64464ab3302SCarolineConcatto         "#%s: unknown or unimplemented directive"_err_en_US, dirName);
64564ab3302SCarolineConcatto   }
64664ab3302SCarolineConcatto }
64764ab3302SCarolineConcatto 
SaveTokenAsName(const CharBlock & t)64864ab3302SCarolineConcatto CharBlock Preprocessor::SaveTokenAsName(const CharBlock &t) {
64964ab3302SCarolineConcatto   names_.push_back(t.ToString());
65064ab3302SCarolineConcatto   return {names_.back().data(), names_.back().size()};
65164ab3302SCarolineConcatto }
65264ab3302SCarolineConcatto 
IsNameDefined(const CharBlock & token)65364ab3302SCarolineConcatto bool Preprocessor::IsNameDefined(const CharBlock &token) {
65464ab3302SCarolineConcatto   return definitions_.find(token) != definitions_.end();
65564ab3302SCarolineConcatto }
65664ab3302SCarolineConcatto 
GetDirectiveName(const TokenSequence & line,std::size_t * rest)65764ab3302SCarolineConcatto static std::string GetDirectiveName(
65864ab3302SCarolineConcatto     const TokenSequence &line, std::size_t *rest) {
65964ab3302SCarolineConcatto   std::size_t tokens{line.SizeInTokens()};
66064ab3302SCarolineConcatto   std::size_t j{line.SkipBlanks(0)};
66164ab3302SCarolineConcatto   if (j == tokens || line.TokenAt(j).ToString() != "#") {
66264ab3302SCarolineConcatto     *rest = tokens;
66364ab3302SCarolineConcatto     return "";
66464ab3302SCarolineConcatto   }
66564ab3302SCarolineConcatto   j = line.SkipBlanks(j + 1);
66664ab3302SCarolineConcatto   if (j == tokens) {
66764ab3302SCarolineConcatto     *rest = tokens;
66864ab3302SCarolineConcatto     return "";
66964ab3302SCarolineConcatto   }
67064ab3302SCarolineConcatto   *rest = line.SkipBlanks(j + 1);
67164ab3302SCarolineConcatto   return ToLowerCaseLetters(line.TokenAt(j).ToString());
67264ab3302SCarolineConcatto }
67364ab3302SCarolineConcatto 
SkipDisabledConditionalCode(const std::string & dirName,IsElseActive isElseActive,Prescanner & prescanner,ProvenanceRange provenanceRange)67464ab3302SCarolineConcatto void Preprocessor::SkipDisabledConditionalCode(const std::string &dirName,
675f411be0dSpeter klausler     IsElseActive isElseActive, Prescanner &prescanner,
67664ab3302SCarolineConcatto     ProvenanceRange provenanceRange) {
67764ab3302SCarolineConcatto   int nesting{0};
678f411be0dSpeter klausler   while (!prescanner.IsAtEnd()) {
679f411be0dSpeter klausler     if (!prescanner.IsNextLinePreprocessorDirective()) {
680f411be0dSpeter klausler       prescanner.NextLine();
68164ab3302SCarolineConcatto       continue;
68264ab3302SCarolineConcatto     }
683f411be0dSpeter klausler     TokenSequence line{prescanner.TokenizePreprocessorDirective()};
68464ab3302SCarolineConcatto     std::size_t rest{0};
68564ab3302SCarolineConcatto     std::string dn{GetDirectiveName(line, &rest)};
68664ab3302SCarolineConcatto     if (dn == "ifdef" || dn == "ifndef" || dn == "if") {
68764ab3302SCarolineConcatto       ++nesting;
68864ab3302SCarolineConcatto     } else if (dn == "endif") {
68964ab3302SCarolineConcatto       if (nesting-- == 0) {
69064ab3302SCarolineConcatto         return;
69164ab3302SCarolineConcatto       }
69264ab3302SCarolineConcatto     } else if (isElseActive == IsElseActive::Yes && nesting == 0) {
69364ab3302SCarolineConcatto       if (dn == "else") {
69464ab3302SCarolineConcatto         ifStack_.push(CanDeadElseAppear::No);
69564ab3302SCarolineConcatto         return;
69664ab3302SCarolineConcatto       }
69764ab3302SCarolineConcatto       if (dn == "elif" &&
69864ab3302SCarolineConcatto           IsIfPredicateTrue(
69964ab3302SCarolineConcatto               line, rest, line.SizeInTokens() - rest, prescanner)) {
70064ab3302SCarolineConcatto         ifStack_.push(CanDeadElseAppear::Yes);
70164ab3302SCarolineConcatto         return;
70264ab3302SCarolineConcatto       }
70364ab3302SCarolineConcatto     }
70464ab3302SCarolineConcatto   }
705f411be0dSpeter klausler   prescanner.Say(provenanceRange, "#%s: missing #endif"_err_en_US, dirName);
70664ab3302SCarolineConcatto }
70764ab3302SCarolineConcatto 
70864ab3302SCarolineConcatto // Precedence level codes used here to accommodate mixed Fortran and C:
70964ab3302SCarolineConcatto // 15: parentheses and constants, logical !, bitwise ~
71064ab3302SCarolineConcatto // 14: unary + and -
71164ab3302SCarolineConcatto // 13: **
71264ab3302SCarolineConcatto // 12: *, /, % (modulus)
71364ab3302SCarolineConcatto // 11: + and -
71464ab3302SCarolineConcatto // 10: << and >>
71564ab3302SCarolineConcatto //  9: bitwise &
71664ab3302SCarolineConcatto //  8: bitwise ^
71764ab3302SCarolineConcatto //  7: bitwise |
71864ab3302SCarolineConcatto //  6: relations (.EQ., ==, &c.)
71964ab3302SCarolineConcatto //  5: .NOT.
72064ab3302SCarolineConcatto //  4: .AND., &&
72164ab3302SCarolineConcatto //  3: .OR., ||
72264ab3302SCarolineConcatto //  2: .EQV. and .NEQV. / .XOR.
72364ab3302SCarolineConcatto //  1: ? :
72464ab3302SCarolineConcatto //  0: ,
ExpressionValue(const TokenSequence & token,int minimumPrecedence,std::size_t * atToken,std::optional<Message> * error)72564ab3302SCarolineConcatto static std::int64_t ExpressionValue(const TokenSequence &token,
72664ab3302SCarolineConcatto     int minimumPrecedence, std::size_t *atToken,
72764ab3302SCarolineConcatto     std::optional<Message> *error) {
72864ab3302SCarolineConcatto   enum Operator {
72964ab3302SCarolineConcatto     PARENS,
73064ab3302SCarolineConcatto     CONST,
73164ab3302SCarolineConcatto     NOTZERO, // !
73264ab3302SCarolineConcatto     COMPLEMENT, // ~
73364ab3302SCarolineConcatto     UPLUS,
73464ab3302SCarolineConcatto     UMINUS,
73564ab3302SCarolineConcatto     POWER,
73664ab3302SCarolineConcatto     TIMES,
73764ab3302SCarolineConcatto     DIVIDE,
73864ab3302SCarolineConcatto     MODULUS,
73964ab3302SCarolineConcatto     ADD,
74064ab3302SCarolineConcatto     SUBTRACT,
74164ab3302SCarolineConcatto     LEFTSHIFT,
74264ab3302SCarolineConcatto     RIGHTSHIFT,
74364ab3302SCarolineConcatto     BITAND,
74464ab3302SCarolineConcatto     BITXOR,
74564ab3302SCarolineConcatto     BITOR,
74664ab3302SCarolineConcatto     LT,
74764ab3302SCarolineConcatto     LE,
74864ab3302SCarolineConcatto     EQ,
74964ab3302SCarolineConcatto     NE,
75064ab3302SCarolineConcatto     GE,
75164ab3302SCarolineConcatto     GT,
75264ab3302SCarolineConcatto     NOT,
75364ab3302SCarolineConcatto     AND,
75464ab3302SCarolineConcatto     OR,
75564ab3302SCarolineConcatto     EQV,
75664ab3302SCarolineConcatto     NEQV,
75764ab3302SCarolineConcatto     SELECT,
75864ab3302SCarolineConcatto     COMMA
75964ab3302SCarolineConcatto   };
76064ab3302SCarolineConcatto   static const int precedence[]{
76164ab3302SCarolineConcatto       15, 15, 15, 15, // (), 6, !, ~
76264ab3302SCarolineConcatto       14, 14, // unary +, -
76364ab3302SCarolineConcatto       13, 12, 12, 12, 11, 11, 10, 10, // **, *, /, %, +, -, <<, >>
76464ab3302SCarolineConcatto       9, 8, 7, // &, ^, |
76564ab3302SCarolineConcatto       6, 6, 6, 6, 6, 6, // relations .LT. to .GT.
76664ab3302SCarolineConcatto       5, 4, 3, 2, 2, // .NOT., .AND., .OR., .EQV., .NEQV.
76764ab3302SCarolineConcatto       1, 0 // ?: and ,
76864ab3302SCarolineConcatto   };
76964ab3302SCarolineConcatto   static const int operandPrecedence[]{0, -1, 15, 15, 15, 15, 13, 12, 12, 12,
77064ab3302SCarolineConcatto       11, 11, 11, 11, 9, 8, 7, 7, 7, 7, 7, 7, 7, 6, 4, 3, 3, 3, 1, 0};
77164ab3302SCarolineConcatto 
77264ab3302SCarolineConcatto   static std::map<std::string, enum Operator> opNameMap;
77364ab3302SCarolineConcatto   if (opNameMap.empty()) {
77464ab3302SCarolineConcatto     opNameMap["("] = PARENS;
77564ab3302SCarolineConcatto     opNameMap["!"] = NOTZERO;
77664ab3302SCarolineConcatto     opNameMap["~"] = COMPLEMENT;
77764ab3302SCarolineConcatto     opNameMap["**"] = POWER;
77864ab3302SCarolineConcatto     opNameMap["*"] = TIMES;
77964ab3302SCarolineConcatto     opNameMap["/"] = DIVIDE;
78064ab3302SCarolineConcatto     opNameMap["%"] = MODULUS;
78164ab3302SCarolineConcatto     opNameMap["+"] = ADD;
78264ab3302SCarolineConcatto     opNameMap["-"] = SUBTRACT;
78364ab3302SCarolineConcatto     opNameMap["<<"] = LEFTSHIFT;
78464ab3302SCarolineConcatto     opNameMap[">>"] = RIGHTSHIFT;
78564ab3302SCarolineConcatto     opNameMap["&"] = BITAND;
78664ab3302SCarolineConcatto     opNameMap["^"] = BITXOR;
78764ab3302SCarolineConcatto     opNameMap["|"] = BITOR;
78864ab3302SCarolineConcatto     opNameMap[".lt."] = opNameMap["<"] = LT;
78964ab3302SCarolineConcatto     opNameMap[".le."] = opNameMap["<="] = LE;
79064ab3302SCarolineConcatto     opNameMap[".eq."] = opNameMap["=="] = EQ;
79164ab3302SCarolineConcatto     opNameMap[".ne."] = opNameMap["/="] = opNameMap["!="] = NE;
79264ab3302SCarolineConcatto     opNameMap[".ge."] = opNameMap[">="] = GE;
79364ab3302SCarolineConcatto     opNameMap[".gt."] = opNameMap[">"] = GT;
79464ab3302SCarolineConcatto     opNameMap[".not."] = NOT;
79564ab3302SCarolineConcatto     opNameMap[".and."] = opNameMap[".a."] = opNameMap["&&"] = AND;
79664ab3302SCarolineConcatto     opNameMap[".or."] = opNameMap[".o."] = opNameMap["||"] = OR;
79764ab3302SCarolineConcatto     opNameMap[".eqv."] = EQV;
79864ab3302SCarolineConcatto     opNameMap[".neqv."] = opNameMap[".xor."] = opNameMap[".x."] = NEQV;
79964ab3302SCarolineConcatto     opNameMap["?"] = SELECT;
80064ab3302SCarolineConcatto     opNameMap[","] = COMMA;
80164ab3302SCarolineConcatto   }
80264ab3302SCarolineConcatto 
80364ab3302SCarolineConcatto   std::size_t tokens{token.SizeInTokens()};
80464ab3302SCarolineConcatto   CHECK(tokens > 0);
80564ab3302SCarolineConcatto   if (*atToken >= tokens) {
80664ab3302SCarolineConcatto     *error =
80764ab3302SCarolineConcatto         Message{token.GetProvenanceRange(), "incomplete expression"_err_en_US};
80864ab3302SCarolineConcatto     return 0;
80964ab3302SCarolineConcatto   }
81064ab3302SCarolineConcatto 
81164ab3302SCarolineConcatto   // Parse and evaluate a primary or a unary operator and its operand.
81264ab3302SCarolineConcatto   std::size_t opAt{*atToken};
81364ab3302SCarolineConcatto   std::string t{token.TokenAt(opAt).ToString()};
81464ab3302SCarolineConcatto   enum Operator op;
81564ab3302SCarolineConcatto   std::int64_t left{0};
81664ab3302SCarolineConcatto   if (t == "(") {
81764ab3302SCarolineConcatto     op = PARENS;
81864ab3302SCarolineConcatto   } else if (IsDecimalDigit(t[0])) {
81964ab3302SCarolineConcatto     op = CONST;
82064ab3302SCarolineConcatto     std::size_t consumed{0};
82164ab3302SCarolineConcatto     left = std::stoll(t, &consumed, 0 /*base to be detected*/);
82264ab3302SCarolineConcatto     if (consumed < t.size()) {
82364ab3302SCarolineConcatto       *error = Message{token.GetTokenProvenanceRange(opAt),
82464ab3302SCarolineConcatto           "Uninterpretable numeric constant '%s'"_err_en_US, t};
82564ab3302SCarolineConcatto       return 0;
82664ab3302SCarolineConcatto     }
82764ab3302SCarolineConcatto   } else if (IsLegalIdentifierStart(t[0])) {
82864ab3302SCarolineConcatto     // undefined macro name -> zero
82964ab3302SCarolineConcatto     // TODO: BOZ constants?
83064ab3302SCarolineConcatto     op = CONST;
83164ab3302SCarolineConcatto   } else if (t == "+") {
83264ab3302SCarolineConcatto     op = UPLUS;
83364ab3302SCarolineConcatto   } else if (t == "-") {
83464ab3302SCarolineConcatto     op = UMINUS;
83564ab3302SCarolineConcatto   } else if (t == "." && *atToken + 2 < tokens &&
83664ab3302SCarolineConcatto       ToLowerCaseLetters(token.TokenAt(*atToken + 1).ToString()) == "not" &&
83764ab3302SCarolineConcatto       token.TokenAt(*atToken + 2).ToString() == ".") {
83864ab3302SCarolineConcatto     op = NOT;
83964ab3302SCarolineConcatto     *atToken += 2;
84064ab3302SCarolineConcatto   } else {
84164ab3302SCarolineConcatto     auto it{opNameMap.find(t)};
84264ab3302SCarolineConcatto     if (it != opNameMap.end()) {
84364ab3302SCarolineConcatto       op = it->second;
84464ab3302SCarolineConcatto     } else {
84564ab3302SCarolineConcatto       *error = Message{token.GetTokenProvenanceRange(opAt),
84664ab3302SCarolineConcatto           "operand expected in expression"_err_en_US};
84764ab3302SCarolineConcatto       return 0;
84864ab3302SCarolineConcatto     }
84964ab3302SCarolineConcatto   }
85064ab3302SCarolineConcatto   if (precedence[op] < minimumPrecedence) {
85164ab3302SCarolineConcatto     *error = Message{token.GetTokenProvenanceRange(opAt),
85264ab3302SCarolineConcatto         "operator precedence error"_err_en_US};
85364ab3302SCarolineConcatto     return 0;
85464ab3302SCarolineConcatto   }
85564ab3302SCarolineConcatto   ++*atToken;
85664ab3302SCarolineConcatto   if (op != CONST) {
85764ab3302SCarolineConcatto     left = ExpressionValue(token, operandPrecedence[op], atToken, error);
85864ab3302SCarolineConcatto     if (*error) {
85964ab3302SCarolineConcatto       return 0;
86064ab3302SCarolineConcatto     }
86164ab3302SCarolineConcatto     switch (op) {
86264ab3302SCarolineConcatto     case PARENS:
86364ab3302SCarolineConcatto       if (*atToken < tokens && token.TokenAt(*atToken).ToString() == ")") {
86464ab3302SCarolineConcatto         ++*atToken;
86564ab3302SCarolineConcatto         break;
86664ab3302SCarolineConcatto       }
86764ab3302SCarolineConcatto       if (*atToken >= tokens) {
86864ab3302SCarolineConcatto         *error = Message{token.GetProvenanceRange(),
86964ab3302SCarolineConcatto             "')' missing from expression"_err_en_US};
87064ab3302SCarolineConcatto       } else {
87164ab3302SCarolineConcatto         *error = Message{
87264ab3302SCarolineConcatto             token.GetTokenProvenanceRange(*atToken), "expected ')'"_err_en_US};
87364ab3302SCarolineConcatto       }
87464ab3302SCarolineConcatto       return 0;
8751f879005STim Keith     case NOTZERO:
8761f879005STim Keith       left = !left;
8771f879005STim Keith       break;
8781f879005STim Keith     case COMPLEMENT:
8791f879005STim Keith       left = ~left;
8801f879005STim Keith       break;
8811f879005STim Keith     case UPLUS:
8821f879005STim Keith       break;
8831f879005STim Keith     case UMINUS:
8841f879005STim Keith       left = -left;
8851f879005STim Keith       break;
8861f879005STim Keith     case NOT:
8871f879005STim Keith       left = -!left;
8881f879005STim Keith       break;
8891f879005STim Keith     default:
8901f879005STim Keith       CRASH_NO_CASE;
89164ab3302SCarolineConcatto     }
89264ab3302SCarolineConcatto   }
89364ab3302SCarolineConcatto 
89464ab3302SCarolineConcatto   // Parse and evaluate binary operators and their second operands, if present.
89564ab3302SCarolineConcatto   while (*atToken < tokens) {
89664ab3302SCarolineConcatto     int advance{1};
89764ab3302SCarolineConcatto     t = token.TokenAt(*atToken).ToString();
89864ab3302SCarolineConcatto     if (t == "." && *atToken + 2 < tokens &&
89964ab3302SCarolineConcatto         token.TokenAt(*atToken + 2).ToString() == ".") {
90064ab3302SCarolineConcatto       t += ToLowerCaseLetters(token.TokenAt(*atToken + 1).ToString()) + '.';
90164ab3302SCarolineConcatto       advance = 3;
90264ab3302SCarolineConcatto     }
90364ab3302SCarolineConcatto     auto it{opNameMap.find(t)};
90464ab3302SCarolineConcatto     if (it == opNameMap.end()) {
90564ab3302SCarolineConcatto       break;
90664ab3302SCarolineConcatto     }
90764ab3302SCarolineConcatto     op = it->second;
90864ab3302SCarolineConcatto     if (op < POWER || precedence[op] < minimumPrecedence) {
90964ab3302SCarolineConcatto       break;
91064ab3302SCarolineConcatto     }
91164ab3302SCarolineConcatto     opAt = *atToken;
91264ab3302SCarolineConcatto     *atToken += advance;
91364ab3302SCarolineConcatto 
91464ab3302SCarolineConcatto     std::int64_t right{
91564ab3302SCarolineConcatto         ExpressionValue(token, operandPrecedence[op], atToken, error)};
91664ab3302SCarolineConcatto     if (*error) {
91764ab3302SCarolineConcatto       return 0;
91864ab3302SCarolineConcatto     }
91964ab3302SCarolineConcatto 
92064ab3302SCarolineConcatto     switch (op) {
92164ab3302SCarolineConcatto     case POWER:
92264ab3302SCarolineConcatto       if (left == 0) {
92364ab3302SCarolineConcatto         if (right < 0) {
92464ab3302SCarolineConcatto           *error = Message{token.GetTokenProvenanceRange(opAt),
92564ab3302SCarolineConcatto               "0 ** negative power"_err_en_US};
92664ab3302SCarolineConcatto         }
92764ab3302SCarolineConcatto       } else if (left != 1 && right != 1) {
92864ab3302SCarolineConcatto         if (right <= 0) {
92964ab3302SCarolineConcatto           left = !right;
93064ab3302SCarolineConcatto         } else {
93164ab3302SCarolineConcatto           std::int64_t power{1};
93264ab3302SCarolineConcatto           for (; right > 0; --right) {
93364ab3302SCarolineConcatto             if ((power * left) / left != power) {
93464ab3302SCarolineConcatto               *error = Message{token.GetTokenProvenanceRange(opAt),
93564ab3302SCarolineConcatto                   "overflow in exponentation"_err_en_US};
93664ab3302SCarolineConcatto               left = 1;
93764ab3302SCarolineConcatto             }
93864ab3302SCarolineConcatto             power *= left;
93964ab3302SCarolineConcatto           }
94064ab3302SCarolineConcatto           left = power;
94164ab3302SCarolineConcatto         }
94264ab3302SCarolineConcatto       }
94364ab3302SCarolineConcatto       break;
94464ab3302SCarolineConcatto     case TIMES:
94564ab3302SCarolineConcatto       if (left != 0 && right != 0 && ((left * right) / left) != right) {
94664ab3302SCarolineConcatto         *error = Message{token.GetTokenProvenanceRange(opAt),
94764ab3302SCarolineConcatto             "overflow in multiplication"_err_en_US};
94864ab3302SCarolineConcatto       }
94964ab3302SCarolineConcatto       left = left * right;
95064ab3302SCarolineConcatto       break;
95164ab3302SCarolineConcatto     case DIVIDE:
95264ab3302SCarolineConcatto       if (right == 0) {
95364ab3302SCarolineConcatto         *error = Message{
95464ab3302SCarolineConcatto             token.GetTokenProvenanceRange(opAt), "division by zero"_err_en_US};
95564ab3302SCarolineConcatto         left = 0;
95664ab3302SCarolineConcatto       } else {
95764ab3302SCarolineConcatto         left = left / right;
95864ab3302SCarolineConcatto       }
95964ab3302SCarolineConcatto       break;
96064ab3302SCarolineConcatto     case MODULUS:
96164ab3302SCarolineConcatto       if (right == 0) {
96264ab3302SCarolineConcatto         *error = Message{
96364ab3302SCarolineConcatto             token.GetTokenProvenanceRange(opAt), "modulus by zero"_err_en_US};
96464ab3302SCarolineConcatto         left = 0;
96564ab3302SCarolineConcatto       } else {
96664ab3302SCarolineConcatto         left = left % right;
96764ab3302SCarolineConcatto       }
96864ab3302SCarolineConcatto       break;
96964ab3302SCarolineConcatto     case ADD:
97064ab3302SCarolineConcatto       if ((left < 0) == (right < 0) && (left < 0) != (left + right < 0)) {
97164ab3302SCarolineConcatto         *error = Message{token.GetTokenProvenanceRange(opAt),
97264ab3302SCarolineConcatto             "overflow in addition"_err_en_US};
97364ab3302SCarolineConcatto       }
97464ab3302SCarolineConcatto       left = left + right;
97564ab3302SCarolineConcatto       break;
97664ab3302SCarolineConcatto     case SUBTRACT:
97764ab3302SCarolineConcatto       if ((left < 0) != (right < 0) && (left < 0) == (left - right < 0)) {
97864ab3302SCarolineConcatto         *error = Message{token.GetTokenProvenanceRange(opAt),
97964ab3302SCarolineConcatto             "overflow in subtraction"_err_en_US};
98064ab3302SCarolineConcatto       }
98164ab3302SCarolineConcatto       left = left - right;
98264ab3302SCarolineConcatto       break;
98364ab3302SCarolineConcatto     case LEFTSHIFT:
98464ab3302SCarolineConcatto       if (right < 0 || right > 64) {
98564ab3302SCarolineConcatto         *error = Message{token.GetTokenProvenanceRange(opAt),
98664ab3302SCarolineConcatto             "bad left shift count"_err_en_US};
98764ab3302SCarolineConcatto       }
98864ab3302SCarolineConcatto       left = right >= 64 ? 0 : left << right;
98964ab3302SCarolineConcatto       break;
99064ab3302SCarolineConcatto     case RIGHTSHIFT:
99164ab3302SCarolineConcatto       if (right < 0 || right > 64) {
99264ab3302SCarolineConcatto         *error = Message{token.GetTokenProvenanceRange(opAt),
99364ab3302SCarolineConcatto             "bad right shift count"_err_en_US};
99464ab3302SCarolineConcatto       }
99564ab3302SCarolineConcatto       left = right >= 64 ? 0 : left >> right;
99664ab3302SCarolineConcatto       break;
99764ab3302SCarolineConcatto     case BITAND:
9981f879005STim Keith     case AND:
9991f879005STim Keith       left = left & right;
10001f879005STim Keith       break;
10011f879005STim Keith     case BITXOR:
10021f879005STim Keith       left = left ^ right;
10031f879005STim Keith       break;
100464ab3302SCarolineConcatto     case BITOR:
10051f879005STim Keith     case OR:
10061f879005STim Keith       left = left | right;
10071f879005STim Keith       break;
10081f879005STim Keith     case LT:
10091f879005STim Keith       left = -(left < right);
10101f879005STim Keith       break;
10111f879005STim Keith     case LE:
10121f879005STim Keith       left = -(left <= right);
10131f879005STim Keith       break;
10141f879005STim Keith     case EQ:
10151f879005STim Keith       left = -(left == right);
10161f879005STim Keith       break;
10171f879005STim Keith     case NE:
10181f879005STim Keith       left = -(left != right);
10191f879005STim Keith       break;
10201f879005STim Keith     case GE:
10211f879005STim Keith       left = -(left >= right);
10221f879005STim Keith       break;
10231f879005STim Keith     case GT:
10241f879005STim Keith       left = -(left > right);
10251f879005STim Keith       break;
10261f879005STim Keith     case EQV:
10271f879005STim Keith       left = -(!left == !right);
10281f879005STim Keith       break;
10291f879005STim Keith     case NEQV:
10301f879005STim Keith       left = -(!left != !right);
10311f879005STim Keith       break;
103264ab3302SCarolineConcatto     case SELECT:
103364ab3302SCarolineConcatto       if (*atToken >= tokens || token.TokenAt(*atToken).ToString() != ":") {
103464ab3302SCarolineConcatto         *error = Message{token.GetTokenProvenanceRange(opAt),
103564ab3302SCarolineConcatto             "':' required in selection expression"_err_en_US};
103664ab3302SCarolineConcatto         return 0;
103764ab3302SCarolineConcatto       } else {
103864ab3302SCarolineConcatto         ++*atToken;
103964ab3302SCarolineConcatto         std::int64_t third{
104064ab3302SCarolineConcatto             ExpressionValue(token, operandPrecedence[op], atToken, error)};
104164ab3302SCarolineConcatto         left = left != 0 ? right : third;
104264ab3302SCarolineConcatto       }
104364ab3302SCarolineConcatto       break;
10441f879005STim Keith     case COMMA:
10451f879005STim Keith       left = right;
10461f879005STim Keith       break;
10471f879005STim Keith     default:
10481f879005STim Keith       CRASH_NO_CASE;
104964ab3302SCarolineConcatto     }
105064ab3302SCarolineConcatto   }
105164ab3302SCarolineConcatto   return left;
105264ab3302SCarolineConcatto }
105364ab3302SCarolineConcatto 
IsIfPredicateTrue(const TokenSequence & expr,std::size_t first,std::size_t exprTokens,Prescanner & prescanner)105464ab3302SCarolineConcatto bool Preprocessor::IsIfPredicateTrue(const TokenSequence &expr,
1055f411be0dSpeter klausler     std::size_t first, std::size_t exprTokens, Prescanner &prescanner) {
105664ab3302SCarolineConcatto   TokenSequence expr1{expr, first, exprTokens};
105764ab3302SCarolineConcatto   if (expr1.HasBlanks()) {
105864ab3302SCarolineConcatto     expr1.RemoveBlanks();
105964ab3302SCarolineConcatto   }
106064ab3302SCarolineConcatto   TokenSequence expr2;
106164ab3302SCarolineConcatto   for (std::size_t j{0}; j < expr1.SizeInTokens(); ++j) {
106264ab3302SCarolineConcatto     if (ToLowerCaseLetters(expr1.TokenAt(j).ToString()) == "defined") {
106364ab3302SCarolineConcatto       CharBlock name;
106464ab3302SCarolineConcatto       if (j + 3 < expr1.SizeInTokens() &&
106564ab3302SCarolineConcatto           expr1.TokenAt(j + 1).ToString() == "(" &&
106664ab3302SCarolineConcatto           expr1.TokenAt(j + 3).ToString() == ")") {
106764ab3302SCarolineConcatto         name = expr1.TokenAt(j + 2);
106864ab3302SCarolineConcatto         j += 3;
106964ab3302SCarolineConcatto       } else if (j + 1 < expr1.SizeInTokens() &&
107064ab3302SCarolineConcatto           IsLegalIdentifierStart(expr1.TokenAt(j + 1))) {
107164ab3302SCarolineConcatto         name = expr1.TokenAt(++j);
107264ab3302SCarolineConcatto       }
107364ab3302SCarolineConcatto       if (!name.empty()) {
107464ab3302SCarolineConcatto         char truth{IsNameDefined(name) ? '1' : '0'};
107564ab3302SCarolineConcatto         expr2.Put(&truth, 1, allSources_.CompilerInsertionProvenance(truth));
107664ab3302SCarolineConcatto         continue;
107764ab3302SCarolineConcatto       }
107864ab3302SCarolineConcatto     }
107964ab3302SCarolineConcatto     expr2.Put(expr1, j);
108064ab3302SCarolineConcatto   }
1081f411be0dSpeter klausler   TokenSequence expr3{ReplaceMacros(expr2, prescanner)};
108264ab3302SCarolineConcatto   if (expr3.HasBlanks()) {
108364ab3302SCarolineConcatto     expr3.RemoveBlanks();
108464ab3302SCarolineConcatto   }
108564ab3302SCarolineConcatto   if (expr3.empty()) {
1086f411be0dSpeter klausler     prescanner.Say(expr.GetProvenanceRange(), "empty expression"_err_en_US);
108764ab3302SCarolineConcatto     return false;
108864ab3302SCarolineConcatto   }
108964ab3302SCarolineConcatto   std::size_t atToken{0};
109064ab3302SCarolineConcatto   std::optional<Message> error;
109164ab3302SCarolineConcatto   bool result{ExpressionValue(expr3, 0, &atToken, &error) != 0};
109264ab3302SCarolineConcatto   if (error) {
1093f411be0dSpeter klausler     prescanner.Say(std::move(*error));
109464ab3302SCarolineConcatto   } else if (atToken < expr3.SizeInTokens() &&
109564ab3302SCarolineConcatto       expr3.TokenAt(atToken).ToString() != "!") {
1096f411be0dSpeter klausler     prescanner.Say(expr3.GetIntervalProvenanceRange(
109764ab3302SCarolineConcatto                        atToken, expr3.SizeInTokens() - atToken),
109864ab3302SCarolineConcatto         atToken == 0 ? "could not parse any expression"_err_en_US
109964ab3302SCarolineConcatto                      : "excess characters after expression"_err_en_US);
110064ab3302SCarolineConcatto   }
110164ab3302SCarolineConcatto   return result;
110264ab3302SCarolineConcatto }
11031f879005STim Keith } // namespace Fortran::parser
1104