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 ¯o, 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