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