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