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

source code of flang/lib/Parser/preprocessor.cpp