brintos

brintos / llvm-project-archived public Read only

0
0
Text · 47.6 KiB · 529d2c3 Raw
1461 lines · cpp
1//===-- lib/Parser/preprocessor.cpp ---------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "flang/Parser/preprocessor.h"10 11#include "prescan.h"12#include "flang/Common/idioms.h"13#include "flang/Parser/characters.h"14#include "flang/Parser/message.h"15#include "llvm/Support/FileSystem.h"16#include "llvm/Support/raw_ostream.h"17#include <algorithm>18#include <cinttypes>19#include <cstddef>20#include <ctime>21#include <map>22#include <memory>23#include <optional>24#include <set>25#include <string>26#include <utility>27#include <vector>28 29namespace Fortran::parser {30 31Definition::Definition(32    const TokenSequence &repl, std::size_t firstToken, std::size_t tokens)33    : replacement_{Tokenize({}, repl, firstToken, tokens)} {}34 35Definition::Definition(const std::vector<std::string> &argNames,36    const TokenSequence &repl, std::size_t firstToken, std::size_t tokens,37    bool isVariadic)38    : isFunctionLike_{true}, isVariadic_{isVariadic}, argNames_{argNames},39      replacement_{Tokenize(argNames, repl, firstToken, tokens)} {}40 41Definition::Definition(const std::string &predefined, AllSources &sources)42    : isPredefined_{true},43      replacement_{44          predefined, sources.AddCompilerInsertion(predefined).start()} {}45 46Definition::Definition(const TokenSequence &repl)47    : isPredefined_{true}, replacement_{repl} {}48 49bool Definition::set_isDisabled(bool disable) {50  bool was{isDisabled_};51  isDisabled_ = disable;52  return was;53}54 55void Definition::Print(llvm::raw_ostream &out, const char *macroName) const {56  if (!isFunctionLike_) {57    // If it's not a function-like macro, then just print the replacement.58    out << ' ' << replacement_.ToString();59    return;60  }61 62  size_t argCount{argumentCount()};63 64  out << '(';65  for (size_t i{0}; i != argCount; ++i) {66    if (i != 0) {67      out << ", ";68    }69    out << argNames_[i];70  }71  if (isVariadic_) {72    out << ", ...";73  }74  out << ") ";75 76  for (size_t i{0}, e{replacement_.SizeInTokens()}; i != e; ++i) {77    std::string tok{replacement_.TokenAt(i).ToString()};78    if (size_t idx{GetArgumentIndex(tok)}; idx < argCount) {79      out << argNames_[idx];80    } else {81      out << tok;82    }83  }84}85 86static bool IsLegalIdentifierStart(const CharBlock &cpl) {87  return cpl.size() > 0 && IsLegalIdentifierStart(cpl[0]);88}89 90TokenSequence Definition::Tokenize(const std::vector<std::string> &argNames,91    const TokenSequence &token, std::size_t firstToken, std::size_t tokens) {92  std::map<std::string, std::string> args;93  char argIndex{'A'};94  for (const std::string &arg : argNames) {95    CHECK(args.find(arg) == args.end());96    args[arg] = "~"s + argIndex++;97  }98  TokenSequence result;99  for (std::size_t j{0}; j < tokens; ++j) {100    CharBlock tok{token.TokenAt(firstToken + j)};101    if (IsLegalIdentifierStart(tok)) {102      auto it{args.find(tok.ToString())};103      if (it != args.end()) {104        result.Put(it->second, token.GetTokenProvenance(j));105        continue;106      }107    }108    result.AppendRange(token, firstToken + j, 1);109  }110  return result;111}112 113std::size_t Definition::GetArgumentIndex(const CharBlock &token) const {114  if (token.size() >= 2 && token[0] == '~') {115    return static_cast<size_t>(token[1] - 'A');116  }117  return argumentCount();118}119 120static TokenSequence Stringify(121    const TokenSequence &tokens, AllSources &allSources) {122  TokenSequence result;123  Provenance quoteProvenance{allSources.CompilerInsertionProvenance('"')};124  result.PutNextTokenChar('"', quoteProvenance);125  for (std::size_t j{0}; j < tokens.SizeInTokens(); ++j) {126    const CharBlock &token{tokens.TokenAt(j)};127    std::size_t bytes{token.size()};128    for (std::size_t k{0}; k < bytes; ++k) {129      char ch{token[k]};130      Provenance from{tokens.GetTokenProvenance(j, k)};131      if (ch == '"' || ch == '\\') {132        result.PutNextTokenChar(ch, from);133      }134      result.PutNextTokenChar(ch, from);135    }136  }137  result.PutNextTokenChar('"', quoteProvenance);138  result.CloseToken();139  return result;140}141 142constexpr bool IsTokenPasting(CharBlock opr) {143  return opr.size() == 2 && opr[0] == '#' && opr[1] == '#';144}145 146static bool AnyTokenPasting(const TokenSequence &text) {147  std::size_t tokens{text.SizeInTokens()};148  for (std::size_t j{0}; j < tokens; ++j) {149    if (IsTokenPasting(text.TokenAt(j))) {150      return true;151    }152  }153  return false;154}155 156static TokenSequence TokenPasting(TokenSequence &&text) {157  if (!AnyTokenPasting(text)) {158    return std::move(text);159  }160  TokenSequence result;161  std::size_t tokens{text.SizeInTokens()};162  std::optional<CharBlock> before; // last non-blank token before ##163  for (std::size_t j{0}; j < tokens; ++j) {164    CharBlock after{text.TokenAt(j)};165    if (!before) {166      if (IsTokenPasting(after)) {167        while (!result.empty() &&168            result.TokenAt(result.SizeInTokens() - 1).IsBlank()) {169          result.pop_back();170        }171        if (!result.empty()) {172          before = result.TokenAt(result.SizeInTokens() - 1);173        }174      } else {175        result.AppendRange(text, j, 1);176      }177    } else if (after.IsBlank() || IsTokenPasting(after)) {178      // drop it179    } else { // pasting before ## after180      bool doPaste{false};181      char last{before->back()};182      char first{after.front()};183      // Apply basic sanity checking to pasting so avoid constructing a bogus184      // token that might cause macro replacement to fail, like "macro(".185      if (IsLegalInIdentifier(last) && IsLegalInIdentifier(first)) {186        doPaste = true;187      } else if (IsDecimalDigit(first) &&188          (last == '.' || last == '+' || last == '-')) {189        doPaste = true; // 1. ## 0, - ## 1190      } else if (before->size() == 1 && after.size() == 1) {191        if (first == last &&192            (last == '<' || last == '>' || last == '*' || last == '/' ||193                last == '=' || last == '&' || last == '|' || last == ':')) {194          // Fortran **, //, ==, ::195          // C <<, >>, &&, || for use in #if expressions196          doPaste = true;197        } else if (first == '=' && (last == '!' || last == '/')) {198          doPaste = true; // != and /=199        }200      }201      if (doPaste) {202        result.ReopenLastToken();203      }204      result.AppendRange(text, j, 1);205      before.reset();206    }207  }208  return result;209}210 211constexpr bool IsDefinedKeyword(CharBlock token) {212  return token.size() == 7 && (token[0] == 'd' || token[0] == 'D') &&213      ToLowerCaseLetters(token.ToString()) == "defined";214}215 216TokenSequence Definition::Apply(const std::vector<TokenSequence> &args,217    Prescanner &prescanner, bool inIfExpression) {218  TokenSequence result;219  bool skipping{false};220  int parenthesesNesting{0};221  std::size_t tokens{replacement_.SizeInTokens()};222  for (std::size_t j{0}; j < tokens; ++j) {223    CharBlock token{replacement_.TokenAt(j)};224    std::size_t bytes{token.size()};225    if (skipping) {226      char ch{token.OnlyNonBlank()};227      if (ch == '(') {228        ++parenthesesNesting;229      } else if (ch == ')') {230        if (parenthesesNesting > 0) {231          --parenthesesNesting;232        }233        skipping = parenthesesNesting > 0;234      }235      continue;236    }237    if (bytes == 2 && token[0] == '~') { // argument substitution238      std::size_t index{GetArgumentIndex(token)};239      if (index >= args.size()) {240        continue;241      }242      std::size_t prev{j};243      while (prev > 0 && replacement_.TokenAt(prev - 1).IsBlank()) {244        --prev;245      }246      if (prev > 0 && replacement_.TokenAt(prev - 1).size() == 1 &&247          replacement_.TokenAt(prev - 1)[0] ==248              '#') { // stringify argument without macro replacement249        std::size_t resultSize{result.SizeInTokens()};250        while (resultSize > 0 && result.TokenAt(resultSize - 1).IsBlank()) {251          result.pop_back();252          --resultSize;253        }254        CHECK(resultSize > 0 &&255            result.TokenAt(resultSize - 1) == replacement_.TokenAt(prev - 1));256        result.pop_back();257        result.CopyAll(Stringify(args[index], prescanner.allSources()));258      } else {259        const TokenSequence *arg{&args[index]};260        std::optional<TokenSequence> replaced;261        // Don't replace macros in the actual argument if it is preceded or262        // followed by the token-pasting operator ## in the replacement text,263        // or if we have to worry about "defined(X)"/"defined X" in an264        // #if/#elif expression.265        if (!inIfExpression &&266            (prev == 0 || !IsTokenPasting(replacement_.TokenAt(prev - 1)))) {267          auto next{replacement_.SkipBlanks(j + 1)};268          if (next >= tokens || !IsTokenPasting(replacement_.TokenAt(next))) {269            // Apply macro replacement to the actual argument270            replaced = prescanner.preprocessor().MacroReplacement(271                *arg, prescanner, nullptr, inIfExpression);272            if (replaced) {273              arg = &*replaced;274            }275          }276        }277        result.CopyAll(DEREF(arg));278      }279    } else if (bytes == 11 && isVariadic_ &&280        token.ToString() == "__VA_ARGS__") {281      Provenance commaProvenance{282          prescanner.preprocessor().allSources().CompilerInsertionProvenance(283              ',')};284      for (std::size_t k{argumentCount()}; k < args.size(); ++k) {285        if (k > argumentCount()) {286          result.Put(","s, commaProvenance);287        }288        result.CopyAll(args[k]);289      }290    } else if (bytes == 10 && isVariadic_ && token.ToString() == "__VA_OPT__" &&291        j + 2 < tokens && replacement_.TokenAt(j + 1).OnlyNonBlank() == '(' &&292        parenthesesNesting == 0) {293      parenthesesNesting = 1;294      skipping = args.size() == argumentCount();295      ++j;296    } else {297      if (parenthesesNesting > 0) {298        char ch{token.OnlyNonBlank()};299        if (ch == '(') {300          ++parenthesesNesting;301        } else if (ch == ')') {302          if (--parenthesesNesting == 0) {303            skipping = false;304            continue;305          }306        }307      }308      result.AppendRange(replacement_, j);309    }310  }311  return TokenPasting(std::move(result));312}313 314static std::string FormatTime(const std::time_t &now, const char *format) {315  char buffer[16];316  return {buffer,317      std::strftime(buffer, sizeof buffer, format, std::localtime(&now))};318}319 320Preprocessor::Preprocessor(AllSources &allSources) : allSources_{allSources} {}321 322void Preprocessor::DefineStandardMacros() {323  // Capture current local date & time once now to avoid having the values324  // of __DATE__ or __TIME__ change during compilation.325  std::time_t now;326  std::time(&now);327  Define("__DATE__"s, FormatTime(now, "\"%h %e %Y\"")); // e.g., "Jun 16 1904"328  Define("__TIME__"s, FormatTime(now, "\"%T\"")); // e.g., "23:59:60"329  // The values of these predefined macros depend on their invocation sites.330  Define("__FILE__"s, "__FILE__"s);331  Define("__LINE__"s, "__LINE__"s);332  Define("__TIMESTAMP__"s, "__TIMESTAMP__"s);333  Define("__COUNTER__"s, "__COUNTER__"s);334}335 336static const std::string idChars{337    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"s};338 339static std::optional<std::vector<std::string>> TokenizeMacroNameAndArgs(340    const std::string &str) {341  // TODO: variadic macros on the command line (?)342  std::vector<std::string> names;343  for (std::string::size_type at{0};;) {344    auto nameStart{str.find_first_not_of(" "s, at)};345    if (nameStart == str.npos) {346      return std::nullopt;347    }348    auto nameEnd{str.find_first_not_of(idChars, nameStart)};349    if (nameEnd == str.npos) {350      return std::nullopt;351    }352    auto punc{str.find_first_not_of(" "s, nameEnd)};353    if (punc == str.npos) {354      return std::nullopt;355    }356    if ((at == 0 && str[punc] != '(') ||357        (at > 0 && str[punc] != ',' && str[punc] != ')')) {358      return std::nullopt;359    }360    names.push_back(str.substr(nameStart, nameEnd - nameStart));361    at = punc + 1;362    if (str[punc] == ')') {363      if (str.find_first_not_of(" "s, at) != str.npos) {364        return std::nullopt;365      } else {366        return names;367      }368    }369  }370}371 372TokenSequence Preprocessor::TokenizeMacroBody(const std::string &str) {373  TokenSequence tokens;374  Provenance provenance{allSources_.AddCompilerInsertion(str).start()};375  auto end{str.size()};376  for (std::string::size_type at{0}; at < end;) {377    char ch{str.at(at)};378    if (IsWhiteSpace(ch)) {379      ++at;380      continue;381    }382    std::string::size_type start{at};383    if (IsLegalIdentifierStart(ch)) {384      for (++at; at < end && IsLegalInIdentifier(str.at(at)); ++at) {385      }386    } else if (IsDecimalDigit(ch) || ch == '.') {387      for (++at; at < end; ++at) {388        ch = str.at(at);389        if (!IsDecimalDigit(ch) && ch != '.') {390          break;391        }392      }393      if (at < end) {394        ch = ToUpperCaseLetter(str.at(at));395        if (ch == 'E' || ch == 'D' || ch == 'Q') {396          if (++at < end) {397            ch = str.at(at);398            if (ch == '+' || ch == '-') {399              ++at;400            }401            for (; at < end && IsDecimalDigit(str.at(at)); ++at) {402            }403          }404        }405      }406    } else if (ch == '\'' || ch == '"') {407      for (++at; at < end && str.at(at) != ch; ++at) {408      }409      if (at < end) {410        ++at;411      }412    } else {413      ++at; // single-character token414    }415    if (at >= end || at == str.npos) {416      tokens.Put(str.substr(start), provenance + start);417      break;418    }419    tokens.Put(str.substr(start, at - start), provenance + start);420  }421  return tokens;422}423 424void Preprocessor::Define(const std::string &macro, const std::string &value) {425  TokenSequence rhs{TokenizeMacroBody(value)};426  if (auto lhs{TokenizeMacroNameAndArgs(macro)}) {427    // function-like macro428    CharBlock macroName{SaveTokenAsName(lhs->front())};429    auto iter{lhs->begin()};430    ++iter;431    std::vector<std::string> argNames{iter, lhs->end()};432    definitions_.emplace(std::make_pair(macroName,433        Definition{434            argNames, rhs, 0, rhs.SizeInTokens(), /*isVariadic=*/false}));435  } else { // keyword macro436    definitions_.emplace(SaveTokenAsName(macro), Definition{rhs});437  }438}439 440void Preprocessor::Undefine(std::string macro) { definitions_.erase(macro); }441 442std::optional<TokenSequence> Preprocessor::MacroReplacement(443    const TokenSequence &input, Prescanner &prescanner,444    std::optional<std::size_t> *partialFunctionLikeMacro, bool inIfExpression) {445  // Do quick scan for any use of a defined name.446  if (!inIfExpression && definitions_.empty()) {447    return std::nullopt;448  }449  std::size_t tokens{input.SizeInTokens()};450  std::size_t j{0};451  for (; j < tokens; ++j) {452    CharBlock token{input.TokenAt(j)};453    if (!token.empty() && IsLegalIdentifierStart(token[0]) &&454        (IsNameDefined(token) || (inIfExpression && IsDefinedKeyword(token)))) {455      break;456    }457  }458  if (j == tokens) {459    return std::nullopt; // input contains nothing that would be replaced460  }461  TokenSequence result{input, 0, j};462 463  // After rescanning after macro replacement has failed due to an unclosed464  // function-like macro call (no left parenthesis yet, or no closing465  // parenthesis), if tokens remain in the input, append them to the466  // replacement text and attempt to proceed.  Otherwise, return, so that467  // the caller may try again with remaining tokens in its input.468  auto CompleteFunctionLikeMacro{469      [this, &input, &prescanner, &result, &partialFunctionLikeMacro,470          inIfExpression](std::size_t after, const TokenSequence &replacement,471          std::size_t pFLMOffset) {472        if (after < input.SizeInTokens()) {473          result.AppendRange(replacement, 0, pFLMOffset);474          TokenSequence suffix;475          suffix.AppendRange(476              replacement, pFLMOffset, replacement.SizeInTokens() - pFLMOffset);477          suffix.AppendRange(input, after, input.SizeInTokens() - after);478          auto further{ReplaceMacros(479              suffix, prescanner, partialFunctionLikeMacro, inIfExpression)};480          if (partialFunctionLikeMacro && *partialFunctionLikeMacro) {481            // still not closed482            **partialFunctionLikeMacro += result.SizeInTokens();483          }484          result.CopyAll(further);485          return true;486        } else {487          if (partialFunctionLikeMacro) {488            *partialFunctionLikeMacro = pFLMOffset + result.SizeInTokens();489          }490          return false;491        }492      }};493 494  for (; j < tokens; ++j) {495    CharBlock token{input.TokenAt(j)};496    if (token.IsBlank() || !IsLegalIdentifierStart(token[0])) {497      result.AppendRange(input, j);498      continue;499    }500    // Process identifier in replacement text.501    auto it{definitions_.find(token)};502    // Is in the X in "defined(X)" or "defined X" in an #if/#elif expression?503    if (inIfExpression) {504      if (auto prev{result.SkipBlanksBackwards(result.SizeInTokens())}) {505        bool ok{true};506        std::optional<std::size_t> rightParenthesis;507        if (result.TokenAt(*prev).OnlyNonBlank() == '(') {508          prev = result.SkipBlanksBackwards(*prev);509          rightParenthesis = input.SkipBlanks(j + 1);510          ok = *rightParenthesis < tokens &&511              input.TokenAt(*rightParenthesis).OnlyNonBlank() == ')';512        }513        if (ok && prev && IsDefinedKeyword(result.TokenAt(*prev))) {514          result = TokenSequence{result, 0, *prev}; // trims off "defined ("515          char truth{it != definitions_.end() ? '1' : '0'};516          result.Put(&truth, 1, allSources_.CompilerInsertionProvenance(truth));517          j = rightParenthesis.value_or(j);518          continue;519        }520      }521    }522    if (it == definitions_.end()) {523      result.AppendRange(input, j);524      continue;525    }526    Definition *def{&it->second};527    if (def->isDisabled()) {528      result.AppendRange(input, j);529      continue;530    }531    if (!def->isFunctionLike()) {532      if (def->isPredefined() && !def->replacement().empty()) {533        std::string repl;534        std::string name{def->replacement().TokenAt(0).ToString()};535        if (name == "__FILE__") {536          repl = "\""s +537              allSources_.GetPath(prescanner.GetCurrentProvenance()) + '"';538        } else if (name == "__LINE__") {539          std::string buf;540          llvm::raw_string_ostream ss{buf};541          ss << allSources_.GetLineNumber(prescanner.GetCurrentProvenance());542          repl = ss.str();543        } else if (name == "__TIMESTAMP__") {544          auto path{allSources_.GetPath(545              prescanner.GetCurrentProvenance(), /*topLevel=*/true)};546          llvm::sys::fs::file_status status;547          repl = "??? ??? ?? ??:??:?? ????";548          if (!llvm::sys::fs::status(path, status)) {549            auto modTime{llvm::sys::toTimeT(status.getLastModificationTime())};550            if (std::string time{std::asctime(std::localtime(&modTime))};551                time.size() > 1 && time[time.size() - 1] == '\n') {552              time.erase(time.size() - 1); // clip terminal '\n'553              repl = "\""s + time + '"';554            }555          }556        } else if (name == "__COUNTER__") {557          repl = std::to_string(counterVal_++);558        }559        if (!repl.empty()) {560          ProvenanceRange insert{allSources_.AddCompilerInsertion(repl)};561          ProvenanceRange call{allSources_.AddMacroCall(562              insert, input.GetTokenProvenanceRange(j), repl)};563          result.Put(repl, call.start());564          continue;565        }566      }567      std::optional<std::size_t> partialFLM;568      def->set_isDisabled(true);569      TokenSequence replaced{TokenPasting(ReplaceMacros(570          def->replacement(), prescanner, &partialFLM, inIfExpression))};571      def->set_isDisabled(false);572      if (partialFLM &&573          CompleteFunctionLikeMacro(j + 1, replaced, *partialFLM)) {574        return result;575      }576      if (!replaced.empty()) {577        ProvenanceRange from{def->replacement().GetProvenanceRange()};578        ProvenanceRange use{input.GetTokenProvenanceRange(j)};579        ProvenanceRange newRange{580            allSources_.AddMacroCall(from, use, replaced.ToString())};581        result.CopyWithProvenance(replaced, newRange);582      }583    } else {584      // Possible function-like macro call.  Skip spaces and newlines to see585      // whether '(' is next.586      std::size_t k{j};587      bool leftParen{false};588      while (++k < tokens) {589        const CharBlock &lookAhead{input.TokenAt(k)};590        if (!lookAhead.IsBlank() && lookAhead[0] != '\n') {591          leftParen = lookAhead[0] == '(' && lookAhead.size() == 1;592          break;593        }594      }595      if (!leftParen) {596        if (partialFunctionLikeMacro) {597          *partialFunctionLikeMacro = result.SizeInTokens();598          result.AppendRange(input, j, tokens - j);599          return result;600        } else {601          result.AppendRange(input, j);602          continue;603        }604      }605      std::vector<std::size_t> argStart{++k};606      for (int nesting{0}; k < tokens; ++k) {607        CharBlock token{input.TokenAt(k)};608        char ch{token.OnlyNonBlank()};609        if (ch == '(') {610          ++nesting;611        } else if (ch == ')') {612          if (nesting == 0) {613            break;614          }615          --nesting;616        } else if (ch == ',' && nesting == 0) {617          argStart.push_back(k + 1);618        }619      }620      if (argStart.size() == 1 && k == argStart[0] &&621          def->argumentCount() == 0) {622        // Subtle: () is zero arguments, not one empty argument,623        // unless one argument was expected.624        argStart.clear();625      }626      if (k >= tokens && partialFunctionLikeMacro) {627        *partialFunctionLikeMacro = result.SizeInTokens();628        result.AppendRange(input, j, tokens - j);629        return result;630      } else if (k >= tokens || argStart.size() < def->argumentCount() ||631          (argStart.size() > def->argumentCount() && !def->isVariadic())) {632        result.AppendRange(input, j);633        continue;634      }635      std::vector<TokenSequence> args;636      for (std::size_t n{0}; n < argStart.size(); ++n) {637        std::size_t at{argStart[n]};638        std::size_t count{639            (n + 1 == argStart.size() ? k : argStart[n + 1] - 1) - at};640        args.emplace_back(TokenSequence(input, at, count));641      }642      TokenSequence applied{def->Apply(args, prescanner, inIfExpression)};643      std::optional<std::size_t> partialFLM;644      def->set_isDisabled(true);645      TokenSequence replaced{ReplaceMacros(646          std::move(applied), prescanner, &partialFLM, inIfExpression)};647      def->set_isDisabled(false);648      if (partialFLM &&649          CompleteFunctionLikeMacro(k + 1, replaced, *partialFLM)) {650        return result;651      }652      if (!replaced.empty()) {653        ProvenanceRange from{def->replacement().GetProvenanceRange()};654        ProvenanceRange use{input.GetIntervalProvenanceRange(j, k - j)};655        ProvenanceRange newRange{656            allSources_.AddMacroCall(from, use, replaced.ToString())};657        result.CopyWithProvenance(replaced, newRange);658      }659      j = k; // advance to the terminal ')'660    }661  }662  return result;663}664 665TokenSequence Preprocessor::ReplaceMacros(const TokenSequence &tokens,666    Prescanner &prescanner,667    std::optional<std::size_t> *partialFunctionLikeMacro, bool inIfExpression) {668  if (std::optional<TokenSequence> repl{MacroReplacement(669          tokens, prescanner, partialFunctionLikeMacro, inIfExpression)}) {670    return std::move(*repl);671  }672  return tokens;673}674 675void Preprocessor::Directive(const TokenSequence &dir, Prescanner &prescanner) {676  std::size_t tokens{dir.SizeInTokens()};677  std::size_t j{dir.SkipBlanks(0)};678  if (j == tokens) {679    return;680  }681  if (dir.TokenAt(j).ToString() != "#") {682    prescanner.Say(dir.GetTokenProvenanceRange(j), "missing '#'"_err_en_US);683    return;684  }685  j = dir.SkipBlanks(j + 1);686  while (tokens > 0 && dir.TokenAt(tokens - 1).IsBlank()) {687    --tokens;688  }689  if (j == tokens) {690    return;691  }692  if (IsDecimalDigit(dir.TokenAt(j)[0]) || dir.TokenAt(j)[0] == '"') {693    LineDirective(dir, j, prescanner);694    return;695  }696  std::size_t dirOffset{j};697  std::string dirName{ToLowerCaseLetters(dir.TokenAt(dirOffset).ToString())};698  j = dir.SkipBlanks(j + 1);699  CharBlock nameToken;700  if (j < tokens && IsLegalIdentifierStart(dir.TokenAt(j)[0])) {701    nameToken = dir.TokenAt(j);702  }703  if (dirName == "line") {704    LineDirective(dir, j, prescanner);705  } else if (dirName == "define") {706    if (nameToken.empty()) {707      prescanner.Say(dir.GetTokenProvenanceRange(j < tokens ? j : tokens - 1),708          "#define: missing or invalid name"_err_en_US);709      return;710    }711    nameToken = SaveTokenAsName(nameToken);712    definitions_.erase(nameToken);713    if (++j < tokens && dir.TokenAt(j).OnlyNonBlank() == '(') {714      j = dir.SkipBlanks(j + 1);715      std::vector<std::string> argName;716      bool isVariadic{false};717      if (dir.TokenAt(j).OnlyNonBlank() != ')') {718        while (true) {719          std::string an{dir.TokenAt(j).ToString()};720          if (an == "...") {721            isVariadic = true;722          } else {723            if (an.empty() || !IsLegalIdentifierStart(an[0])) {724              prescanner.Say(dir.GetTokenProvenanceRange(j),725                  "#define: missing or invalid argument name"_err_en_US);726              return;727            }728            argName.push_back(an);729          }730          j = dir.SkipBlanks(j + 1);731          if (j == tokens) {732            prescanner.Say(dir.GetTokenProvenanceRange(tokens - 1),733                "#define: malformed argument list"_err_en_US);734            return;735          }736          char punc{dir.TokenAt(j).OnlyNonBlank()};737          if (punc == ')') {738            break;739          }740          if (isVariadic || punc != ',') {741            prescanner.Say(dir.GetTokenProvenanceRange(j),742                "#define: malformed argument list"_err_en_US);743            return;744          }745          j = dir.SkipBlanks(j + 1);746          if (j == tokens) {747            prescanner.Say(dir.GetTokenProvenanceRange(tokens - 1),748                "#define: malformed argument list"_err_en_US);749            return;750          }751        }752        if (std::set<std::string>(argName.begin(), argName.end()).size() !=753            argName.size()) {754          prescanner.Say(dir.GetTokenProvenance(dirOffset),755              "#define: argument names are not distinct"_err_en_US);756          return;757        }758      }759      j = dir.SkipBlanks(j + 1);760      definitions_.emplace(std::make_pair(761          nameToken, Definition{argName, dir, j, tokens - j, isVariadic}));762    } else {763      j = dir.SkipBlanks(j + 1);764      definitions_.emplace(765          std::make_pair(nameToken, Definition{dir, j, tokens - j}));766    }767  } else if (dirName == "undef") {768    if (nameToken.empty()) {769      prescanner.Say(770          dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset),771          "# missing or invalid name"_err_en_US);772    } else {773      if (dir.IsAnythingLeft(++j)) {774        prescanner.Warn(common::UsageWarning::Portability,775            dir.GetIntervalProvenanceRange(j, tokens - j),776            "#undef: excess tokens at end of directive"_port_en_US);777      } else {778        definitions_.erase(nameToken);779      }780    }781  } else if (dirName == "ifdef" || dirName == "ifndef") {782    bool doThen{false};783    if (nameToken.empty()) {784      prescanner.Say(785          dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset),786          "#%s: missing name"_err_en_US, dirName);787    } else {788      if (dir.IsAnythingLeft(++j)) {789        prescanner.Warn(common::UsageWarning::Portability,790            dir.GetIntervalProvenanceRange(j, tokens - j),791            "#%s: excess tokens at end of directive"_port_en_US, dirName);792      }793      doThen = IsNameDefined(nameToken) == (dirName == "ifdef");794    }795    if (doThen) {796      ifStack_.push(CanDeadElseAppear::Yes);797    } else {798      SkipDisabledConditionalCode(dirName, IsElseActive::Yes, prescanner,799          dir.GetTokenProvenance(dirOffset));800    }801  } else if (dirName == "if") {802    if (IsIfPredicateTrue(dir, j, tokens - j, prescanner)) {803      ifStack_.push(CanDeadElseAppear::Yes);804    } else {805      SkipDisabledConditionalCode(dirName, IsElseActive::Yes, prescanner,806          dir.GetTokenProvenanceRange(dirOffset));807    }808  } else if (dirName == "else") {809    if (dir.IsAnythingLeft(j)) {810      prescanner.Warn(common::UsageWarning::Portability,811          dir.GetIntervalProvenanceRange(j, tokens - j),812          "#else: excess tokens at end of directive"_port_en_US);813    }814    if (ifStack_.empty()) {815      prescanner.Say(dir.GetTokenProvenanceRange(dirOffset),816          "#else: not nested within #if, #ifdef, or #ifndef"_err_en_US);817    } else if (ifStack_.top() != CanDeadElseAppear::Yes) {818      prescanner.Say(dir.GetTokenProvenanceRange(dirOffset),819          "#else: already appeared within this #if, #ifdef, or #ifndef"_err_en_US);820    } else {821      ifStack_.pop();822      SkipDisabledConditionalCode("else", IsElseActive::No, prescanner,823          dir.GetTokenProvenanceRange(dirOffset));824    }825  } else if (dirName == "elif") {826    if (ifStack_.empty()) {827      prescanner.Say(dir.GetTokenProvenanceRange(dirOffset),828          "#elif: not nested within #if, #ifdef, or #ifndef"_err_en_US);829    } else if (ifStack_.top() != CanDeadElseAppear::Yes) {830      prescanner.Say(dir.GetTokenProvenanceRange(dirOffset),831          "#elif: #else previously appeared within this #if, #ifdef, or #ifndef"_err_en_US);832    } else {833      ifStack_.pop();834      SkipDisabledConditionalCode("elif", IsElseActive::No, prescanner,835          dir.GetTokenProvenanceRange(dirOffset));836    }837  } else if (dirName == "endif") {838    if (dir.IsAnythingLeft(j)) {839      prescanner.Warn(common::UsageWarning::Portability,840          dir.GetIntervalProvenanceRange(j, tokens - j),841          "#endif: excess tokens at end of directive"_port_en_US);842    } else if (ifStack_.empty()) {843      prescanner.Say(dir.GetTokenProvenanceRange(dirOffset),844          "#endif: no #if, #ifdef, or #ifndef"_err_en_US);845    } else {846      ifStack_.pop();847    }848  } else if (dirName == "error") {849    prescanner.Say(850        dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset),851        "%s"_err_en_US, dir.ToString());852  } else if (dirName == "warning") {853    prescanner.Say(854        dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset),855        "%s"_warn_en_US, dir.ToString());856  } else if (dirName == "comment" || dirName == "note") {857    prescanner.Say(858        dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset),859        "%s"_en_US, dir.ToString());860  } else if (dirName == "include") {861    if (j == tokens) {862      prescanner.Say(863          dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset),864          "#include: missing name of file to include"_err_en_US);865      return;866    }867    std::optional<std::string> prependPath;868    TokenSequence path{dir, j, tokens - j};869    std::string include{path.TokenAt(0).ToString()};870    if (include != "<" && include.substr(0, 1) != "\"" &&871        include.substr(0, 1) != "'") {872      path = ReplaceMacros(path, prescanner);873      include = path.empty() ? ""s : path.TokenAt(0).ToString();874    }875    auto pathTokens{path.SizeInTokens()};876    std::size_t k{0};877    if (include == "<") { // #include <foo>878      k = 1;879      if (k >= pathTokens) {880        prescanner.Say(dir.GetIntervalProvenanceRange(j, pathTokens),881            "#include: file name missing"_err_en_US);882        return;883      }884      while (k < pathTokens && path.TokenAt(k) != ">") {885        ++k;886      }887      if (k >= pathTokens) {888        prescanner.Warn(common::UsageWarning::Portability,889            dir.GetIntervalProvenanceRange(j, tokens - j),890            "#include: expected '>' at end of included file"_port_en_US);891      }892      TokenSequence braced{path, 1, k - 1};893      include = braced.ToString();894    } else if ((include.substr(0, 1) == "\"" || include.substr(0, 1) == "'") &&895        include.front() == include.back()) {896      // #include "foo" and #include 'foo'897      include = include.substr(1, include.size() - 2);898      // Start search in directory of file containing the directive899      auto prov{dir.GetTokenProvenanceRange(dirOffset).start()};900      if (const auto *currentFile{allSources_.GetSourceFile(prov)}) {901        prependPath = DirectoryName(currentFile->path());902      }903    } else {904      prescanner.Say(dir.GetTokenProvenanceRange(j < tokens ? j : tokens - 1),905          "#include %s: expected name of file to include"_err_en_US,906          path.ToString());907      return;908    }909    if (include.empty()) {910      prescanner.Say(dir.GetTokenProvenanceRange(dirOffset),911          "#include %s: empty include file name"_err_en_US, path.ToString());912      return;913    }914    k = path.SkipBlanks(k + 1);915    if (k < pathTokens && path.TokenAt(k).ToString() != "!") {916      prescanner.Warn(common::UsageWarning::Portability,917          dir.GetIntervalProvenanceRange(j, tokens - j),918          "#include: extra stuff ignored after file name"_port_en_US);919    }920    std::string buf;921    llvm::raw_string_ostream error{buf};922    if (const SourceFile *923        included{allSources_.Open(include, error, std::move(prependPath))}) {924      if (included->bytes() > 0) {925        ProvenanceRange fileRange{926            allSources_.AddIncludedFile(*included, dir.GetProvenanceRange())};927        Prescanner{prescanner, *this, /*isNestedInIncludeDirective=*/true}928            .set_encoding(included->encoding())929            .Prescan(fileRange);930      }931    } else {932      prescanner.Say(dir.GetTokenProvenanceRange(j), "#include: %s"_err_en_US,933          error.str());934    }935  } else {936    prescanner.Say(dir.GetTokenProvenanceRange(dirOffset),937        "#%s: unknown or unimplemented directive"_err_en_US, dirName);938  }939}940 941void Preprocessor::PrintMacros(llvm::raw_ostream &out) const {942  // std::set is ordered. Use that to print the macros in an943  // alphabetical order.944  std::set<std::string> macroNames;945  for (const auto &[name, _] : definitions_) {946    macroNames.insert(name.ToString());947  }948 949  for (const std::string &name : macroNames) {950    out << "#define " << name;951    definitions_.at(name).Print(out, name.c_str());952    out << '\n';953  }954}955 956CharBlock Preprocessor::SaveTokenAsName(const CharBlock &t) {957  names_.push_back(t.ToString());958  return {names_.back().data(), names_.back().size()};959}960 961bool Preprocessor::IsNameDefined(const CharBlock &token) {962  return definitions_.find(token) != definitions_.end();963}964 965bool Preprocessor::IsNameDefinedEmpty(const CharBlock &token) {966  if (auto it{definitions_.find(token)}; it != definitions_.end()) {967    const Definition &def{it->second};968    return !def.isFunctionLike() && def.replacement().SizeInChars() == 0;969  } else {970    return false;971  }972}973 974bool Preprocessor::IsFunctionLikeDefinition(const CharBlock &token) {975  auto it{definitions_.find(token)};976  return it != definitions_.end() && it->second.isFunctionLike();977}978 979static std::string GetDirectiveName(980    const TokenSequence &line, std::size_t *rest) {981  std::size_t tokens{line.SizeInTokens()};982  std::size_t j{line.SkipBlanks(0)};983  if (j == tokens || line.TokenAt(j).ToString() != "#") {984    *rest = tokens;985    return "";986  }987  j = line.SkipBlanks(j + 1);988  if (j == tokens) {989    *rest = tokens;990    return "";991  }992  *rest = line.SkipBlanks(j + 1);993  return ToLowerCaseLetters(line.TokenAt(j).ToString());994}995 996void Preprocessor::SkipDisabledConditionalCode(const std::string &dirName,997    IsElseActive isElseActive, Prescanner &prescanner,998    ProvenanceRange provenanceRange) {999  int nesting{0};1000  while (!prescanner.IsAtEnd()) {1001    if (!prescanner.IsNextLinePreprocessorDirective()) {1002      prescanner.NextLine();1003      continue;1004    }1005    TokenSequence line{prescanner.TokenizePreprocessorDirective()};1006    std::size_t rest{0};1007    std::string dn{GetDirectiveName(line, &rest)};1008    if (dn == "ifdef" || dn == "ifndef" || dn == "if") {1009      ++nesting;1010    } else if (dn == "endif") {1011      if (nesting-- == 0) {1012        return;1013      }1014    } else if (isElseActive == IsElseActive::Yes && nesting == 0) {1015      if (dn == "else") {1016        ifStack_.push(CanDeadElseAppear::No);1017        return;1018      }1019      if (dn == "elif" &&1020          IsIfPredicateTrue(1021              line, rest, line.SizeInTokens() - rest, prescanner)) {1022        ifStack_.push(CanDeadElseAppear::Yes);1023        return;1024      }1025    }1026  }1027  prescanner.Say(provenanceRange, "#%s: missing #endif"_err_en_US, dirName);1028}1029 1030// Precedence level codes used here to accommodate mixed Fortran and C:1031// 15: parentheses and constants, logical !, bitwise ~1032// 14: unary + and -1033// 13: **1034// 12: *, /, % (modulus)1035// 11: + and -1036// 10: << and >>1037//  9: bitwise &1038//  8: bitwise ^1039//  7: bitwise |1040//  6: relations (.EQ., ==, &c.)1041//  5: .NOT.1042//  4: .AND., &&1043//  3: .OR., ||1044//  2: .EQV. and .NEQV. / .XOR.1045//  1: ? :1046//  0: ,1047static std::int64_t ExpressionValue(const TokenSequence &token,1048    int minimumPrecedence, std::size_t *atToken,1049    std::optional<Message> *error) {1050  enum Operator {1051    PARENS,1052    CONST,1053    NOTZERO, // !1054    COMPLEMENT, // ~1055    UPLUS,1056    UMINUS,1057    POWER,1058    TIMES,1059    DIVIDE,1060    MODULUS,1061    ADD,1062    SUBTRACT,1063    LEFTSHIFT,1064    RIGHTSHIFT,1065    BITAND,1066    BITXOR,1067    BITOR,1068    LT,1069    LE,1070    EQ,1071    NE,1072    GE,1073    GT,1074    NOT,1075    AND,1076    OR,1077    EQV,1078    NEQV,1079    SELECT,1080    COMMA1081  };1082  static const int precedence[]{1083      15, 15, 15, 15, // (), 6, !, ~1084      14, 14, // unary +, -1085      13, 12, 12, 12, 11, 11, 10, 10, // **, *, /, %, +, -, <<, >>1086      9, 8, 7, // &, ^, |1087      6, 6, 6, 6, 6, 6, // relations .LT. to .GT.1088      5, 4, 3, 2, 2, // .NOT., .AND., .OR., .EQV., .NEQV.1089      1, 0 // ?: and ,1090  };1091  static const int operandPrecedence[]{0, -1, 15, 15, 15, 15, 13, 12, 12, 12,1092      11, 11, 11, 11, 9, 8, 7, 7, 7, 7, 7, 7, 7, 6, 4, 3, 3, 3, 1, 0};1093 1094  static std::map<std::string, enum Operator> opNameMap;1095  if (opNameMap.empty()) {1096    opNameMap["("] = PARENS;1097    opNameMap["!"] = NOTZERO;1098    opNameMap["~"] = COMPLEMENT;1099    opNameMap["**"] = POWER;1100    opNameMap["*"] = TIMES;1101    opNameMap["/"] = DIVIDE;1102    opNameMap["%"] = MODULUS;1103    opNameMap["+"] = ADD;1104    opNameMap["-"] = SUBTRACT;1105    opNameMap["<<"] = LEFTSHIFT;1106    opNameMap[">>"] = RIGHTSHIFT;1107    opNameMap["&"] = BITAND;1108    opNameMap["^"] = BITXOR;1109    opNameMap["|"] = BITOR;1110    opNameMap[".lt."] = opNameMap["<"] = LT;1111    opNameMap[".le."] = opNameMap["<="] = LE;1112    opNameMap[".eq."] = opNameMap["=="] = EQ;1113    opNameMap[".ne."] = opNameMap["/="] = opNameMap["!="] = NE;1114    opNameMap[".ge."] = opNameMap[">="] = GE;1115    opNameMap[".gt."] = opNameMap[">"] = GT;1116    opNameMap[".not."] = NOT;1117    opNameMap[".and."] = opNameMap[".a."] = opNameMap["&&"] = AND;1118    opNameMap[".or."] = opNameMap[".o."] = opNameMap["||"] = OR;1119    opNameMap[".eqv."] = EQV;1120    opNameMap[".neqv."] = opNameMap[".xor."] = opNameMap[".x."] = NEQV;1121    opNameMap["?"] = SELECT;1122    opNameMap[","] = COMMA;1123  }1124 1125  std::size_t tokens{token.SizeInTokens()};1126  CHECK(tokens > 0);1127  if (*atToken >= tokens) {1128    *error =1129        Message{token.GetProvenanceRange(), "incomplete expression"_err_en_US};1130    return 0;1131  }1132 1133  // Parse and evaluate a primary or a unary operator and its operand.1134  std::size_t opAt{*atToken};1135  std::string t{token.TokenAt(opAt).ToString()};1136  enum Operator op;1137  std::int64_t left{0};1138  if (t == "(") {1139    op = PARENS;1140  } else if (IsDecimalDigit(t[0])) {1141    op = CONST;1142    std::size_t consumed{0};1143    left = std::stoll(t, &consumed, 0 /*base to be detected*/);1144    if (consumed < t.size()) {1145      *error = Message{token.GetTokenProvenanceRange(opAt),1146          "Uninterpretable numeric constant '%s'"_err_en_US, t};1147      return 0;1148    }1149  } else if (IsLegalIdentifierStart(t[0])) {1150    // undefined macro name -> zero1151    // TODO: BOZ constants?1152    op = CONST;1153  } else if (t == "+") {1154    op = UPLUS;1155  } else if (t == "-") {1156    op = UMINUS;1157  } else if (t == "." && *atToken + 2 < tokens &&1158      ToLowerCaseLetters(token.TokenAt(*atToken + 1).ToString()) == "not" &&1159      token.TokenAt(*atToken + 2).ToString() == ".") {1160    op = NOT;1161    *atToken += 2;1162  } else {1163    auto it{opNameMap.find(t)};1164    if (it != opNameMap.end()) {1165      op = it->second;1166    } else {1167      *error = Message{token.GetTokenProvenanceRange(opAt),1168          "operand expected in expression"_err_en_US};1169      return 0;1170    }1171  }1172  if (precedence[op] < minimumPrecedence) {1173    *error = Message{token.GetTokenProvenanceRange(opAt),1174        "operator precedence error"_err_en_US};1175    return 0;1176  }1177  ++*atToken;1178  if (op != CONST) {1179    left = ExpressionValue(token, operandPrecedence[op], atToken, error);1180    if (*error) {1181      return 0;1182    }1183    switch (op) {1184    case PARENS:1185      if (*atToken < tokens && token.TokenAt(*atToken).OnlyNonBlank() == ')') {1186        ++*atToken;1187        break;1188      }1189      if (*atToken >= tokens) {1190        *error = Message{token.GetProvenanceRange(),1191            "')' missing from expression"_err_en_US};1192      } else {1193        *error = Message{1194            token.GetTokenProvenanceRange(*atToken), "expected ')'"_err_en_US};1195      }1196      return 0;1197    case NOTZERO:1198      left = !left;1199      break;1200    case COMPLEMENT:1201      left = ~left;1202      break;1203    case UPLUS:1204      break;1205    case UMINUS:1206      left = -left;1207      break;1208    case NOT:1209      left = -!left;1210      break;1211    default:1212      CRASH_NO_CASE;1213    }1214  }1215 1216  // Parse and evaluate binary operators and their second operands, if present.1217  while (*atToken < tokens) {1218    int advance{1};1219    t = token.TokenAt(*atToken).ToString();1220    if (t == "." && *atToken + 2 < tokens &&1221        token.TokenAt(*atToken + 2).ToString() == ".") {1222      t += ToLowerCaseLetters(token.TokenAt(*atToken + 1).ToString()) + '.';1223      advance = 3;1224    }1225    auto it{opNameMap.find(t)};1226    if (it == opNameMap.end()) {1227      break;1228    }1229    op = it->second;1230    if (op < POWER || precedence[op] < minimumPrecedence) {1231      break;1232    }1233    opAt = *atToken;1234    *atToken += advance;1235 1236    std::int64_t right{1237        ExpressionValue(token, operandPrecedence[op], atToken, error)};1238    if (*error) {1239      return 0;1240    }1241 1242    switch (op) {1243    case POWER:1244      if (left == 0) {1245        if (right < 0) {1246          *error = Message{token.GetTokenProvenanceRange(opAt),1247              "0 ** negative power"_err_en_US};1248        }1249      } else if (left != 1 && right != 1) {1250        if (right <= 0) {1251          left = !right;1252        } else {1253          std::int64_t power{1};1254          for (; right > 0; --right) {1255            if ((power * left) / left != power) {1256              *error = Message{token.GetTokenProvenanceRange(opAt),1257                  "overflow in exponentation"_err_en_US};1258              left = 1;1259            }1260            power *= left;1261          }1262          left = power;1263        }1264      }1265      break;1266    case TIMES:1267      if (left != 0 && right != 0 && ((left * right) / left) != right) {1268        *error = Message{token.GetTokenProvenanceRange(opAt),1269            "overflow in multiplication"_err_en_US};1270      }1271      left = left * right;1272      break;1273    case DIVIDE:1274      if (right == 0) {1275        *error = Message{1276            token.GetTokenProvenanceRange(opAt), "division by zero"_err_en_US};1277        left = 0;1278      } else {1279        left = left / right;1280      }1281      break;1282    case MODULUS:1283      if (right == 0) {1284        *error = Message{1285            token.GetTokenProvenanceRange(opAt), "modulus by zero"_err_en_US};1286        left = 0;1287      } else {1288        left = left % right;1289      }1290      break;1291    case ADD:1292      if ((left < 0) == (right < 0) && (left < 0) != (left + right < 0)) {1293        *error = Message{token.GetTokenProvenanceRange(opAt),1294            "overflow in addition"_err_en_US};1295      }1296      left = left + right;1297      break;1298    case SUBTRACT:1299      if ((left < 0) != (right < 0) && (left < 0) == (left - right < 0)) {1300        *error = Message{token.GetTokenProvenanceRange(opAt),1301            "overflow in subtraction"_err_en_US};1302      }1303      left = left - right;1304      break;1305    case LEFTSHIFT:1306      if (right < 0 || right > 64) {1307        *error = Message{token.GetTokenProvenanceRange(opAt),1308            "bad left shift count"_err_en_US};1309      }1310      left = right >= 64 ? 0 : left << right;1311      break;1312    case RIGHTSHIFT:1313      if (right < 0 || right > 64) {1314        *error = Message{token.GetTokenProvenanceRange(opAt),1315            "bad right shift count"_err_en_US};1316      }1317      left = right >= 64 ? 0 : left >> right;1318      break;1319    case BITAND:1320      left = left & right;1321      break;1322    case BITXOR:1323      left = left ^ right;1324      break;1325    case BITOR:1326      left = left | right;1327      break;1328    case AND:1329      left = left && right;1330      break;1331    case OR:1332      left = left || right;1333      break;1334    case LT:1335      left = -(left < right);1336      break;1337    case LE:1338      left = -(left <= right);1339      break;1340    case EQ:1341      left = -(left == right);1342      break;1343    case NE:1344      left = -(left != right);1345      break;1346    case GE:1347      left = -(left >= right);1348      break;1349    case GT:1350      left = -(left > right);1351      break;1352    case EQV:1353      left = -(!left == !right);1354      break;1355    case NEQV:1356      left = -(!left != !right);1357      break;1358    case SELECT:1359      if (*atToken >= tokens || token.TokenAt(*atToken).ToString() != ":") {1360        *error = Message{token.GetTokenProvenanceRange(opAt),1361            "':' required in selection expression"_err_en_US};1362        return 0;1363      } else {1364        ++*atToken;1365        std::int64_t third{1366            ExpressionValue(token, operandPrecedence[op], atToken, error)};1367        left = left != 0 ? right : third;1368      }1369      break;1370    case COMMA:1371      left = right;1372      break;1373    default:1374      CRASH_NO_CASE;1375    }1376  }1377  return left;1378}1379 1380bool Preprocessor::IsIfPredicateTrue(const TokenSequence &directive,1381    std::size_t first, std::size_t exprTokens, Prescanner &prescanner) {1382  TokenSequence expr{directive, first, exprTokens};1383  TokenSequence replaced{1384      ReplaceMacros(expr, prescanner, nullptr, /*inIfExpression=*/true)};1385  if (replaced.HasBlanks()) {1386    replaced.RemoveBlanks();1387  }1388  if (replaced.empty()) {1389    prescanner.Say(expr.GetProvenanceRange(), "empty expression"_err_en_US);1390    return false;1391  }1392  std::size_t atToken{0};1393  std::optional<Message> error;1394  bool result{ExpressionValue(replaced, 0, &atToken, &error) != 0};1395  if (error) {1396    prescanner.Say(std::move(*error));1397  } else if (atToken < replaced.SizeInTokens() &&1398      replaced.TokenAt(atToken).ToString() != "!") {1399    prescanner.Say(replaced.GetIntervalProvenanceRange(1400                       atToken, replaced.SizeInTokens() - atToken),1401        atToken == 0 ? "could not parse any expression"_err_en_US1402                     : "excess characters after expression"_err_en_US);1403  }1404  return result;1405}1406 1407void Preprocessor::LineDirective(1408    const TokenSequence &dir, std::size_t j, Prescanner &prescanner) {1409  std::size_t tokens{dir.SizeInTokens()};1410  const std::string *linePath{nullptr};1411  std::optional<int> lineNumber;1412  SourceFile *sourceFile{nullptr};1413  std::optional<SourcePosition> pos;1414  for (; j < tokens; j = dir.SkipBlanks(j + 1)) {1415    std::string tstr{dir.TokenAt(j).ToString()};1416    Provenance provenance{dir.GetTokenProvenance(j)};1417    if (!pos) {1418      pos = allSources_.GetSourcePosition(provenance);1419    }1420    if (!sourceFile && pos) {1421      sourceFile = const_cast<SourceFile *>(&*pos->sourceFile);1422    }1423    if (tstr.front() == '"' && tstr.back() == '"') {1424      tstr = tstr.substr(1, tstr.size() - 2);1425      if (!tstr.empty() && sourceFile) {1426        linePath = &sourceFile->SavePath(std::move(tstr));1427      }1428    } else if (IsDecimalDigit(tstr[0])) {1429      if (!lineNumber) { // ignore later column number1430        int ln{0};1431        for (char c : tstr) {1432          if (IsDecimalDigit(c)) {1433            int nln{10 * ln + c - '0'};1434            if (nln / 10 == ln && nln % 10 == c - '0') {1435              ln = nln;1436              continue;1437            }1438          }1439          prescanner.Say(provenance,1440              "bad line number '%s' in #line directive"_err_en_US, tstr);1441          return;1442        }1443        lineNumber = ln;1444      }1445    } else {1446      prescanner.Say(1447          provenance, "bad token '%s' in #line directive"_err_en_US, tstr);1448      return;1449    }1450  }1451  if (lineNumber && sourceFile) {1452    CHECK(pos);1453    if (!linePath) {1454      linePath = &*pos->path;1455    }1456    sourceFile->LineDirective(pos->trueLineNumber + 1, *linePath, *lineNumber);1457  }1458}1459 1460} // namespace Fortran::parser1461