1 //===-- lib/Parser/prescan.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 "prescan.h"
10 #include "preprocessor.h"
11 #include "token-sequence.h"
12 #include "flang/Common/idioms.h"
13 #include "flang/Parser/characters.h"
14 #include "flang/Parser/message.h"
15 #include "flang/Parser/source.h"
16 #include "llvm/Support/raw_ostream.h"
17 #include <cstddef>
18 #include <cstring>
19 #include <utility>
20 #include <vector>
21 
22 namespace Fortran::parser {
23 
24 using common::LanguageFeature;
25 
26 static constexpr int maxPrescannerNesting{100};
27 
28 Prescanner::Prescanner(Messages &messages, CookedSource &cooked,
29     Preprocessor &preprocessor, common::LanguageFeatureControl lfc)
30     : messages_{messages}, cooked_{cooked}, preprocessor_{preprocessor},
31       allSources_{preprocessor_.allSources()}, features_{lfc},
32       encoding_{allSources_.encoding()} {}
33 
34 Prescanner::Prescanner(const Prescanner &that)
35     : messages_{that.messages_}, cooked_{that.cooked_},
36       preprocessor_{that.preprocessor_}, allSources_{that.allSources_},
37       features_{that.features_}, inFixedForm_{that.inFixedForm_},
38       fixedFormColumnLimit_{that.fixedFormColumnLimit_},
39       encoding_{that.encoding_}, prescannerNesting_{that.prescannerNesting_ +
40                                      1},
41       skipLeadingAmpersand_{that.skipLeadingAmpersand_},
42       compilerDirectiveBloomFilter_{that.compilerDirectiveBloomFilter_},
43       compilerDirectiveSentinels_{that.compilerDirectiveSentinels_} {}
44 
45 static inline constexpr bool IsFixedFormCommentChar(char ch) {
46   return ch == '!' || ch == '*' || ch == 'C' || ch == 'c';
47 }
48 
49 static void NormalizeCompilerDirectiveCommentMarker(TokenSequence &dir) {
50   char *p{dir.GetMutableCharData()};
51   char *limit{p + dir.SizeInChars()};
52   for (; p < limit; ++p) {
53     if (*p != ' ') {
54       CHECK(IsFixedFormCommentChar(*p));
55       *p = '!';
56       return;
57     }
58   }
59   DIE("compiler directive all blank");
60 }
61 
62 void Prescanner::Prescan(ProvenanceRange range) {
63   startProvenance_ = range.start();
64   start_ = allSources_.GetSource(range);
65   CHECK(start_);
66   limit_ = start_ + range.size();
67   nextLine_ = start_;
68   const bool beganInFixedForm{inFixedForm_};
69   if (prescannerNesting_ > maxPrescannerNesting) {
70     Say(GetProvenance(start_),
71         "too many nested INCLUDE/#include files, possibly circular"_err_en_US);
72     return;
73   }
74   while (!IsAtEnd()) {
75     Statement();
76   }
77   if (inFixedForm_ != beganInFixedForm) {
78     std::string dir{"!dir$ "};
79     if (beganInFixedForm) {
80       dir += "fixed";
81     } else {
82       dir += "free";
83     }
84     dir += '\n';
85     TokenSequence tokens{dir, allSources_.AddCompilerInsertion(dir).start()};
86     tokens.Emit(cooked_);
87   }
88 }
89 
90 void Prescanner::Statement() {
91   TokenSequence tokens;
92   LineClassification line{ClassifyLine(nextLine_)};
93   switch (line.kind) {
94   case LineClassification::Kind::Comment:
95     nextLine_ += line.payloadOffset; // advance to '!' or newline
96     NextLine();
97     return;
98   case LineClassification::Kind::IncludeLine:
99     FortranInclude(nextLine_ + line.payloadOffset);
100     NextLine();
101     return;
102   case LineClassification::Kind::ConditionalCompilationDirective:
103   case LineClassification::Kind::IncludeDirective:
104   case LineClassification::Kind::DefinitionDirective:
105   case LineClassification::Kind::PreprocessorDirective:
106     preprocessor_.Directive(TokenizePreprocessorDirective(), *this);
107     return;
108   case LineClassification::Kind::CompilerDirective:
109     directiveSentinel_ = line.sentinel;
110     CHECK(InCompilerDirective());
111     BeginStatementAndAdvance();
112     if (inFixedForm_) {
113       CHECK(IsFixedFormCommentChar(*at_));
114     } else {
115       while (*at_ == ' ' || *at_ == '\t') {
116         ++at_, ++column_;
117       }
118       CHECK(*at_ == '!');
119     }
120     if (directiveSentinel_[0] == '$' && directiveSentinel_[1] == '\0') {
121       // OpenMP conditional compilation line.  Remove the sentinel and then
122       // treat the line as if it were normal source.
123       at_ += 2, column_ += 2;
124       if (inFixedForm_) {
125         LabelField(tokens);
126       } else {
127         SkipSpaces();
128       }
129     } else {
130       // Compiler directive.  Emit normalized sentinel.
131       EmitChar(tokens, '!');
132       ++at_, ++column_;
133       for (const char *sp{directiveSentinel_}; *sp != '\0';
134            ++sp, ++at_, ++column_) {
135         EmitChar(tokens, *sp);
136       }
137       if (*at_ == ' ') {
138         EmitChar(tokens, ' ');
139         ++at_, ++column_;
140       }
141       tokens.CloseToken();
142     }
143     break;
144   case LineClassification::Kind::Source:
145     BeginStatementAndAdvance();
146     if (inFixedForm_) {
147       LabelField(tokens);
148     } else if (skipLeadingAmpersand_) {
149       skipLeadingAmpersand_ = false;
150       const char *p{SkipWhiteSpace(at_)};
151       if (p < limit_ && *p == '&') {
152         column_ += ++p - at_;
153         at_ = p;
154       }
155     } else {
156       SkipSpaces();
157     }
158     break;
159   }
160 
161   while (NextToken(tokens)) {
162   }
163 
164   Provenance newlineProvenance{GetCurrentProvenance()};
165   if (std::optional<TokenSequence> preprocessed{
166           preprocessor_.MacroReplacement(tokens, *this)}) {
167     // Reprocess the preprocessed line.  Append a newline temporarily.
168     preprocessed->PutNextTokenChar('\n', newlineProvenance);
169     preprocessed->CloseToken();
170     const char *ppd{preprocessed->ToCharBlock().begin()};
171     LineClassification ppl{ClassifyLine(ppd)};
172     preprocessed->RemoveLastToken(); // remove the newline
173     switch (ppl.kind) {
174     case LineClassification::Kind::Comment:
175       break;
176     case LineClassification::Kind::IncludeLine:
177       FortranInclude(ppd + ppl.payloadOffset);
178       break;
179     case LineClassification::Kind::ConditionalCompilationDirective:
180     case LineClassification::Kind::IncludeDirective:
181     case LineClassification::Kind::DefinitionDirective:
182     case LineClassification::Kind::PreprocessorDirective:
183       Say(preprocessed->GetProvenanceRange(),
184           "Preprocessed line resembles a preprocessor directive"_en_US);
185       preprocessed->ToLowerCase().CheckBadFortranCharacters(messages_).Emit(
186           cooked_);
187       break;
188     case LineClassification::Kind::CompilerDirective:
189       if (preprocessed->HasRedundantBlanks()) {
190         preprocessed->RemoveRedundantBlanks();
191       }
192       NormalizeCompilerDirectiveCommentMarker(*preprocessed);
193       preprocessed->ToLowerCase();
194       SourceFormChange(preprocessed->ToString());
195       preprocessed->ClipComment(true /* skip first ! */)
196           .CheckBadFortranCharacters(messages_)
197           .Emit(cooked_);
198       break;
199     case LineClassification::Kind::Source:
200       if (inFixedForm_) {
201         if (preprocessed->HasBlanks(/*after column*/ 6)) {
202           preprocessed->RemoveBlanks(/*after column*/ 6);
203         }
204       } else {
205         if (preprocessed->HasRedundantBlanks()) {
206           preprocessed->RemoveRedundantBlanks();
207         }
208       }
209       preprocessed->ToLowerCase()
210           .ClipComment()
211           .CheckBadFortranCharacters(messages_)
212           .Emit(cooked_);
213       break;
214     }
215   } else {
216     tokens.ToLowerCase();
217     if (line.kind == LineClassification::Kind::CompilerDirective) {
218       SourceFormChange(tokens.ToString());
219     }
220     if (inFixedForm_ && line.kind == LineClassification::Kind::Source) {
221       EnforceStupidEndStatementRules(tokens);
222     }
223     tokens.CheckBadFortranCharacters(messages_).Emit(cooked_);
224   }
225   if (omitNewline_) {
226     omitNewline_ = false;
227   } else {
228     cooked_.Put('\n', newlineProvenance);
229   }
230   directiveSentinel_ = nullptr;
231 }
232 
233 TokenSequence Prescanner::TokenizePreprocessorDirective() {
234   CHECK(!IsAtEnd() && !inPreprocessorDirective_);
235   inPreprocessorDirective_ = true;
236   BeginStatementAndAdvance();
237   TokenSequence tokens;
238   while (NextToken(tokens)) {
239   }
240   inPreprocessorDirective_ = false;
241   return tokens;
242 }
243 
244 void Prescanner::NextLine() {
245   void *vstart{static_cast<void *>(const_cast<char *>(nextLine_))};
246   void *v{std::memchr(vstart, '\n', limit_ - nextLine_)};
247   if (!v) {
248     nextLine_ = limit_;
249   } else {
250     const char *nl{const_cast<const char *>(static_cast<char *>(v))};
251     nextLine_ = nl + 1;
252   }
253 }
254 
255 void Prescanner::LabelField(TokenSequence &token) {
256   const char *bad{nullptr};
257   int outCol{1};
258   for (; *at_ != '\n' && column_ <= 6; ++at_) {
259     if (*at_ == '\t') {
260       ++at_;
261       column_ = 7;
262       break;
263     }
264     if (*at_ != ' ' &&
265         !(*at_ == '0' && column_ == 6)) { // '0' in column 6 becomes space
266       EmitChar(token, *at_);
267       ++outCol;
268       if (!bad && !IsDecimalDigit(*at_)) {
269         bad = at_;
270       }
271     }
272     ++column_;
273   }
274   if (outCol == 1) { // empty label field
275     // Emit a space so that, if the line is rescanned after preprocessing,
276     // a leading 'C' or 'D' won't be left-justified and then accidentally
277     // misinterpreted as a comment card.
278     EmitChar(token, ' ');
279     ++outCol;
280   } else {
281     if (bad && !preprocessor_.IsNameDefined(token.CurrentOpenToken())) {
282       Say(GetProvenance(bad),
283           "Character in fixed-form label field must be a digit"_en_US);
284     }
285   }
286   token.CloseToken();
287   SkipToNextSignificantCharacter();
288   if (IsDecimalDigit(*at_)) {
289     Say(GetProvenance(at_),
290         "Label digit is not in fixed-form label field"_en_US);
291   }
292 }
293 
294 // 6.3.3.5: A program unit END statement, or any other statement whose
295 // initial line resembles an END statement, shall not be continued in
296 // fixed form source.
297 void Prescanner::EnforceStupidEndStatementRules(const TokenSequence &tokens) {
298   CharBlock cBlock{tokens.ToCharBlock()};
299   const char *str{cBlock.begin()};
300   std::size_t n{cBlock.size()};
301   if (n < 3) {
302     return;
303   }
304   std::size_t j{0};
305   for (; j < n && (str[j] == ' ' || (str[j] >= '0' && str[j] <= '9')); ++j) {
306   }
307   if (j + 3 > n || std::memcmp(str + j, "end", 3) != 0) {
308     return;
309   }
310   // It starts with END, possibly after a label.
311   auto start{allSources_.GetSourcePosition(tokens.GetCharProvenance(j))};
312   auto end{allSources_.GetSourcePosition(tokens.GetCharProvenance(n - 1))};
313   if (!start || !end) {
314     return;
315   }
316   if (&start->file == &end->file && start->line == end->line) {
317     return; // no continuation
318   }
319   j += 3;
320   static const char *const prefixes[]{"program", "subroutine", "function",
321       "blockdata", "module", "submodule", nullptr};
322   bool isPrefix{j == n || !IsLegalInIdentifier(str[j])}; // prefix is END
323   std::size_t endOfPrefix{j - 1};
324   for (const char *const *p{prefixes}; *p; ++p) {
325     std::size_t pLen{std::strlen(*p)};
326     if (j + pLen <= n && std::memcmp(str + j, *p, pLen) == 0) {
327       isPrefix = true; // END thing as prefix
328       j += pLen;
329       endOfPrefix = j - 1;
330       for (; j < n && IsLegalInIdentifier(str[j]); ++j) {
331       }
332       break;
333     }
334   }
335   if (isPrefix) {
336     auto range{tokens.GetTokenProvenanceRange(1)};
337     if (j == n) { // END or END thing [name]
338       Say(range,
339           "Program unit END statement may not be continued in fixed form source"_err_en_US);
340     } else {
341       auto endOfPrefixPos{
342           allSources_.GetSourcePosition(tokens.GetCharProvenance(endOfPrefix))};
343       auto next{allSources_.GetSourcePosition(tokens.GetCharProvenance(j))};
344       if (endOfPrefixPos && next && &endOfPrefixPos->file == &start->file &&
345           endOfPrefixPos->line == start->line &&
346           (&next->file != &start->file || next->line != start->line)) {
347         Say(range,
348             "Initial line of continued statement must not appear to be a program unit END in fixed form source"_err_en_US);
349       }
350     }
351   }
352 }
353 
354 void Prescanner::SkipToEndOfLine() {
355   while (*at_ != '\n') {
356     ++at_, ++column_;
357   }
358 }
359 
360 bool Prescanner::MustSkipToEndOfLine() const {
361   if (inFixedForm_ && column_ > fixedFormColumnLimit_ && !tabInCurrentLine_) {
362     return true; // skip over ignored columns in right margin (73:80)
363   } else if (*at_ == '!' && !inCharLiteral_) {
364     return true; // inline comment goes to end of source line
365   } else {
366     return false;
367   }
368 }
369 
370 void Prescanner::NextChar() {
371   CHECK(*at_ != '\n');
372   ++at_, ++column_;
373   while (at_[0] == '\xef' && at_[1] == '\xbb' && at_[2] == '\xbf') {
374     // UTF-8 byte order mark - treat this file as UTF-8
375     at_ += 3;
376     encoding_ = Encoding::UTF_8;
377   }
378   SkipToNextSignificantCharacter();
379 }
380 
381 // Skip everything that should be ignored until the next significant
382 // character is reached; handles C-style comments in preprocessing
383 // directives, Fortran ! comments, stuff after the right margin in
384 // fixed form, and all forms of line continuation.
385 void Prescanner::SkipToNextSignificantCharacter() {
386   if (inPreprocessorDirective_) {
387     SkipCComments();
388   } else {
389     bool mightNeedSpace{false};
390     if (MustSkipToEndOfLine()) {
391       SkipToEndOfLine();
392     } else {
393       mightNeedSpace = *at_ == '\n';
394     }
395     for (; Continuation(mightNeedSpace); mightNeedSpace = false) {
396       if (MustSkipToEndOfLine()) {
397         SkipToEndOfLine();
398       }
399     }
400     if (*at_ == '\t') {
401       tabInCurrentLine_ = true;
402     }
403   }
404 }
405 
406 void Prescanner::SkipCComments() {
407   while (true) {
408     if (IsCComment(at_)) {
409       if (const char *after{SkipCComment(at_)}) {
410         column_ += after - at_;
411         // May have skipped over one or more newlines; relocate the start of
412         // the next line.
413         nextLine_ = at_ = after;
414         NextLine();
415       } else {
416         // Don't emit any messages about unclosed C-style comments, because
417         // the sequence /* can appear legally in a FORMAT statement.  There's
418         // no ambiguity, since the sequence */ cannot appear legally.
419         break;
420       }
421     } else if (inPreprocessorDirective_ && at_[0] == '\\' && at_ + 2 < limit_ &&
422         at_[1] == '\n' && !IsAtEnd()) {
423       BeginSourceLineAndAdvance();
424     } else {
425       break;
426     }
427   }
428 }
429 
430 void Prescanner::SkipSpaces() {
431   while (*at_ == ' ' || *at_ == '\t') {
432     NextChar();
433   }
434   insertASpace_ = false;
435 }
436 
437 const char *Prescanner::SkipWhiteSpace(const char *p) {
438   while (*p == ' ' || *p == '\t') {
439     ++p;
440   }
441   return p;
442 }
443 
444 const char *Prescanner::SkipWhiteSpaceAndCComments(const char *p) const {
445   while (true) {
446     if (*p == ' ' || *p == '\t') {
447       ++p;
448     } else if (IsCComment(p)) {
449       if (const char *after{SkipCComment(p)}) {
450         p = after;
451       } else {
452         break;
453       }
454     } else {
455       break;
456     }
457   }
458   return p;
459 }
460 
461 const char *Prescanner::SkipCComment(const char *p) const {
462   char star{' '}, slash{' '};
463   p += 2;
464   while (star != '*' || slash != '/') {
465     if (p >= limit_) {
466       return nullptr; // signifies an unterminated comment
467     }
468     star = slash;
469     slash = *p++;
470   }
471   return p;
472 }
473 
474 bool Prescanner::NextToken(TokenSequence &tokens) {
475   CHECK(at_ >= start_ && at_ < limit_);
476   if (InFixedFormSource()) {
477     SkipSpaces();
478   } else {
479     if (*at_ == '/' && IsCComment(at_)) {
480       // Recognize and skip over classic C style /*comments*/ when
481       // outside a character literal.
482       if (features_.ShouldWarn(LanguageFeature::ClassicCComments)) {
483         Say(GetProvenance(at_), "nonstandard usage: C-style comment"_en_US);
484       }
485       SkipCComments();
486     }
487     if (*at_ == ' ' || *at_ == '\t') {
488       // Compress free-form white space into a single space character.
489       const auto theSpace{at_};
490       char previous{at_ <= start_ ? ' ' : at_[-1]};
491       NextChar();
492       SkipSpaces();
493       if (*at_ == '\n') {
494         // Discard white space at the end of a line.
495       } else if (!inPreprocessorDirective_ &&
496           (previous == '(' || *at_ == '(' || *at_ == ')')) {
497         // Discard white space before/after '(' and before ')', unless in a
498         // preprocessor directive.  This helps yield space-free contiguous
499         // names for generic interfaces like OPERATOR( + ) and
500         // READ ( UNFORMATTED ), without misinterpreting #define f (notAnArg).
501         // This has the effect of silently ignoring the illegal spaces in
502         // the array constructor ( /1,2/ ) but that seems benign; it's
503         // hard to avoid that while still removing spaces from OPERATOR( / )
504         // and OPERATOR( // ).
505       } else {
506         // Preserve the squashed white space as a single space character.
507         tokens.PutNextTokenChar(' ', GetProvenance(theSpace));
508         tokens.CloseToken();
509         return true;
510       }
511     }
512   }
513   if (insertASpace_) {
514     tokens.PutNextTokenChar(' ', spaceProvenance_);
515     insertASpace_ = false;
516   }
517   if (*at_ == '\n') {
518     return false;
519   }
520   const char *start{at_};
521   if (*at_ == '\'' || *at_ == '"') {
522     QuotedCharacterLiteral(tokens, start);
523     preventHollerith_ = false;
524   } else if (IsDecimalDigit(*at_)) {
525     int n{0}, digits{0};
526     static constexpr int maxHollerith{256 /*lines*/ * (132 - 6 /*columns*/)};
527     do {
528       if (n < maxHollerith) {
529         n = 10 * n + DecimalDigitValue(*at_);
530       }
531       EmitCharAndAdvance(tokens, *at_);
532       ++digits;
533       if (InFixedFormSource()) {
534         SkipSpaces();
535       }
536     } while (IsDecimalDigit(*at_));
537     if ((*at_ == 'h' || *at_ == 'H') && n > 0 && n < maxHollerith &&
538         !preventHollerith_) {
539       Hollerith(tokens, n, start);
540     } else if (*at_ == '.') {
541       while (IsDecimalDigit(EmitCharAndAdvance(tokens, *at_))) {
542       }
543       ExponentAndKind(tokens);
544     } else if (ExponentAndKind(tokens)) {
545     } else if (digits == 1 && n == 0 && (*at_ == 'x' || *at_ == 'X') &&
546         inPreprocessorDirective_) {
547       do {
548         EmitCharAndAdvance(tokens, *at_);
549       } while (IsHexadecimalDigit(*at_));
550     } else if (IsLetter(*at_)) {
551       // Handles FORMAT(3I9HHOLLERITH) by skipping over the first I so that
552       // we don't misrecognize I9HOLLERITH as an identifier in the next case.
553       EmitCharAndAdvance(tokens, *at_);
554     } else if (at_[0] == '_' && (at_[1] == '\'' || at_[1] == '"')) { // 4_"..."
555       EmitCharAndAdvance(tokens, *at_);
556       QuotedCharacterLiteral(tokens, start);
557     }
558     preventHollerith_ = false;
559   } else if (*at_ == '.') {
560     char nch{EmitCharAndAdvance(tokens, '.')};
561     if (!inPreprocessorDirective_ && IsDecimalDigit(nch)) {
562       while (IsDecimalDigit(EmitCharAndAdvance(tokens, *at_))) {
563       }
564       ExponentAndKind(tokens);
565     } else if (nch == '.' && EmitCharAndAdvance(tokens, '.') == '.') {
566       EmitCharAndAdvance(tokens, '.'); // variadic macro definition ellipsis
567     }
568     preventHollerith_ = false;
569   } else if (IsLegalInIdentifier(*at_)) {
570     do {
571     } while (IsLegalInIdentifier(EmitCharAndAdvance(tokens, *at_)));
572     if ((*at_ == '\'' || *at_ == '"') &&
573         tokens.CharAt(tokens.SizeInChars() - 1) == '_') { // kind_"..."
574       QuotedCharacterLiteral(tokens, start);
575     }
576     preventHollerith_ = false;
577   } else if (*at_ == '*') {
578     if (EmitCharAndAdvance(tokens, '*') == '*') {
579       EmitCharAndAdvance(tokens, '*');
580     } else {
581       // Subtle ambiguity:
582       //  CHARACTER*2H     declares H because *2 is a kind specifier
583       //  DATAC/N*2H  /    is repeated Hollerith
584       preventHollerith_ = !slashInCurrentStatement_;
585     }
586   } else {
587     char ch{*at_};
588     if (ch == '(' || ch == '[') {
589       ++delimiterNesting_;
590     } else if ((ch == ')' || ch == ']') && delimiterNesting_ > 0) {
591       --delimiterNesting_;
592     }
593     char nch{EmitCharAndAdvance(tokens, ch)};
594     preventHollerith_ = false;
595     if ((nch == '=' &&
596             (ch == '<' || ch == '>' || ch == '/' || ch == '=' || ch == '!')) ||
597         (ch == nch &&
598             (ch == '/' || ch == ':' || ch == '*' || ch == '#' || ch == '&' ||
599                 ch == '|' || ch == '<' || ch == '>')) ||
600         (ch == '=' && nch == '>')) {
601       // token comprises two characters
602       EmitCharAndAdvance(tokens, nch);
603     } else if (ch == '/') {
604       slashInCurrentStatement_ = true;
605     }
606   }
607   tokens.CloseToken();
608   return true;
609 }
610 
611 bool Prescanner::ExponentAndKind(TokenSequence &tokens) {
612   char ed{ToLowerCaseLetter(*at_)};
613   if (ed != 'e' && ed != 'd') {
614     return false;
615   }
616   EmitCharAndAdvance(tokens, ed);
617   if (*at_ == '+' || *at_ == '-') {
618     EmitCharAndAdvance(tokens, *at_);
619   }
620   while (IsDecimalDigit(*at_)) {
621     EmitCharAndAdvance(tokens, *at_);
622   }
623   if (*at_ == '_') {
624     while (IsLegalInIdentifier(EmitCharAndAdvance(tokens, *at_))) {
625     }
626   }
627   return true;
628 }
629 
630 void Prescanner::QuotedCharacterLiteral(
631     TokenSequence &tokens, const char *start) {
632   char quote{*at_};
633   const char *end{at_ + 1};
634   inCharLiteral_ = true;
635   const auto emit{[&](char ch) { EmitChar(tokens, ch); }};
636   const auto insert{[&](char ch) { EmitInsertedChar(tokens, ch); }};
637   bool isEscaped{false};
638   bool escapesEnabled{features_.IsEnabled(LanguageFeature::BackslashEscapes)};
639   while (true) {
640     if (*at_ == '\\') {
641       if (escapesEnabled) {
642         isEscaped = !isEscaped;
643       } else {
644         // The parser always processes escape sequences, so don't confuse it
645         // when escapes are disabled.
646         insert('\\');
647       }
648     } else {
649       isEscaped = false;
650     }
651     EmitQuotedChar(static_cast<unsigned char>(*at_), emit, insert, false,
652         Encoding::LATIN_1);
653     while (PadOutCharacterLiteral(tokens)) {
654     }
655     if (*at_ == '\n') {
656       if (!inPreprocessorDirective_) {
657         Say(GetProvenanceRange(start, end),
658             "Incomplete character literal"_err_en_US);
659       }
660       break;
661     }
662     end = at_ + 1;
663     NextChar();
664     if (*at_ == quote && !isEscaped) {
665       // A doubled unescaped quote mark becomes a single instance of that
666       // quote character in the literal (later).  There can be spaces between
667       // the quotes in fixed form source.
668       EmitChar(tokens, quote);
669       inCharLiteral_ = false; // for cases like print *, '...'!comment
670       NextChar();
671       if (InFixedFormSource()) {
672         SkipSpaces();
673       }
674       if (*at_ != quote) {
675         break;
676       }
677       inCharLiteral_ = true;
678     }
679   }
680   inCharLiteral_ = false;
681 }
682 
683 void Prescanner::Hollerith(
684     TokenSequence &tokens, int count, const char *start) {
685   inCharLiteral_ = true;
686   CHECK(*at_ == 'h' || *at_ == 'H');
687   EmitChar(tokens, 'H');
688   while (count-- > 0) {
689     if (PadOutCharacterLiteral(tokens)) {
690     } else if (*at_ == '\n') {
691       Say(GetProvenanceRange(start, at_),
692           "Possible truncated Hollerith literal"_en_US);
693       break;
694     } else {
695       NextChar();
696       // Each multi-byte character encoding counts as a single character.
697       // No escape sequences are recognized.
698       // Hollerith is always emitted to the cooked character
699       // stream in UTF-8.
700       DecodedCharacter decoded{DecodeCharacter(
701           encoding_, at_, static_cast<std::size_t>(limit_ - at_), false)};
702       if (decoded.bytes > 0) {
703         EncodedCharacter utf8{
704             EncodeCharacter<Encoding::UTF_8>(decoded.codepoint)};
705         for (int j{0}; j < utf8.bytes; ++j) {
706           EmitChar(tokens, utf8.buffer[j]);
707         }
708         at_ += decoded.bytes - 1;
709       } else {
710         Say(GetProvenanceRange(start, at_),
711             "Bad character in Hollerith literal"_err_en_US);
712         break;
713       }
714     }
715   }
716   if (*at_ != '\n') {
717     NextChar();
718   }
719   inCharLiteral_ = false;
720 }
721 
722 // In fixed form, source card images must be processed as if they were at
723 // least 72 columns wide, at least in character literal contexts.
724 bool Prescanner::PadOutCharacterLiteral(TokenSequence &tokens) {
725   while (inFixedForm_ && !tabInCurrentLine_ && at_[1] == '\n') {
726     if (column_ < fixedFormColumnLimit_) {
727       tokens.PutNextTokenChar(' ', spaceProvenance_);
728       ++column_;
729       return true;
730     }
731     if (!FixedFormContinuation(false /*no need to insert space*/) ||
732         tabInCurrentLine_) {
733       return false;
734     }
735     CHECK(column_ == 7);
736     --at_; // point to column 6 of continuation line
737     column_ = 6;
738   }
739   return false;
740 }
741 
742 bool Prescanner::IsFixedFormCommentLine(const char *start) const {
743   const char *p{start};
744   if (IsFixedFormCommentChar(*p) || *p == '%' || // VAX %list, %eject, &c.
745       ((*p == 'D' || *p == 'd') &&
746           !features_.IsEnabled(LanguageFeature::OldDebugLines))) {
747     return true;
748   }
749   bool anyTabs{false};
750   while (true) {
751     if (*p == ' ') {
752       ++p;
753     } else if (*p == '\t') {
754       anyTabs = true;
755       ++p;
756     } else if (*p == '0' && !anyTabs && p == start + 5) {
757       ++p; // 0 in column 6 must treated as a space
758     } else {
759       break;
760     }
761   }
762   if (!anyTabs && p >= start + fixedFormColumnLimit_) {
763     return true;
764   }
765   if (*p == '!' && !inCharLiteral_ && (anyTabs || p != start + 5)) {
766     return true;
767   }
768   return *p == '\n';
769 }
770 
771 const char *Prescanner::IsFreeFormComment(const char *p) const {
772   p = SkipWhiteSpaceAndCComments(p);
773   if (*p == '!' || *p == '\n') {
774     return p;
775   } else {
776     return nullptr;
777   }
778 }
779 
780 std::optional<std::size_t> Prescanner::IsIncludeLine(const char *start) const {
781   const char *p{SkipWhiteSpace(start)};
782   for (char ch : "include"s) {
783     if (ToLowerCaseLetter(*p++) != ch) {
784       return std::nullopt;
785     }
786   }
787   p = SkipWhiteSpace(p);
788   if (*p == '"' || *p == '\'') {
789     return {p - start};
790   }
791   return std::nullopt;
792 }
793 
794 void Prescanner::FortranInclude(const char *firstQuote) {
795   const char *p{firstQuote};
796   while (*p != '"' && *p != '\'') {
797     ++p;
798   }
799   char quote{*p};
800   std::string path;
801   for (++p; *p != '\n'; ++p) {
802     if (*p == quote) {
803       if (p[1] != quote) {
804         break;
805       }
806       ++p;
807     }
808     path += *p;
809   }
810   if (*p != quote) {
811     Say(GetProvenanceRange(firstQuote, p),
812         "malformed path name string"_err_en_US);
813     return;
814   }
815   p = SkipWhiteSpace(p + 1);
816   if (*p != '\n' && *p != '!') {
817     const char *garbage{p};
818     for (; *p != '\n' && *p != '!'; ++p) {
819     }
820     Say(GetProvenanceRange(garbage, p),
821         "excess characters after path name"_en_US);
822   }
823   std::string buf;
824   llvm::raw_string_ostream error{buf};
825   Provenance provenance{GetProvenance(nextLine_)};
826   std::optional<std::string> prependPath;
827   if (const SourceFile * currentFile{allSources_.GetSourceFile(provenance)}) {
828     prependPath = DirectoryName(currentFile->path());
829   }
830   const SourceFile *included{
831       allSources_.Open(path, error, std::move(prependPath))};
832   if (!included) {
833     Say(provenance, "INCLUDE: %s"_err_en_US, error.str());
834   } else if (included->bytes() > 0) {
835     ProvenanceRange includeLineRange{
836         provenance, static_cast<std::size_t>(p - nextLine_)};
837     ProvenanceRange fileRange{
838         allSources_.AddIncludedFile(*included, includeLineRange)};
839     Prescanner{*this}.set_encoding(included->encoding()).Prescan(fileRange);
840   }
841 }
842 
843 const char *Prescanner::IsPreprocessorDirectiveLine(const char *start) const {
844   const char *p{start};
845   for (; *p == ' '; ++p) {
846   }
847   if (*p == '#') {
848     if (inFixedForm_ && p == start + 5) {
849       return nullptr;
850     }
851   } else {
852     p = SkipWhiteSpace(p);
853     if (*p != '#') {
854       return nullptr;
855     }
856   }
857   return SkipWhiteSpace(p + 1);
858 }
859 
860 bool Prescanner::IsNextLinePreprocessorDirective() const {
861   return IsPreprocessorDirectiveLine(nextLine_) != nullptr;
862 }
863 
864 bool Prescanner::SkipCommentLine(bool afterAmpersand) {
865   if (IsAtEnd()) {
866     if (afterAmpersand && prescannerNesting_ > 0) {
867       // A continuation marker at the end of the last line in an
868       // include file inhibits the newline for that line.
869       SkipToEndOfLine();
870       omitNewline_ = true;
871     }
872     return false;
873   }
874   auto lineClass{ClassifyLine(nextLine_)};
875   if (lineClass.kind == LineClassification::Kind::Comment) {
876     NextLine();
877     return true;
878   } else if (inPreprocessorDirective_) {
879     return false;
880   } else if (lineClass.kind ==
881           LineClassification::Kind::ConditionalCompilationDirective ||
882       lineClass.kind == LineClassification::Kind::PreprocessorDirective) {
883     // Allow conditional compilation directives (e.g., #ifdef) to affect
884     // continuation lines.
885     // Allow other preprocessor directives, too, except #include
886     // (when it does not follow '&'), #define, and #undef (because
887     // they cannot be allowed to affect preceding text on a
888     // continued line).
889     preprocessor_.Directive(TokenizePreprocessorDirective(), *this);
890     return true;
891   } else if (afterAmpersand &&
892       (lineClass.kind == LineClassification::Kind::IncludeDirective ||
893           lineClass.kind == LineClassification::Kind::IncludeLine)) {
894     SkipToEndOfLine();
895     omitNewline_ = true;
896     skipLeadingAmpersand_ = true;
897     return false;
898   } else {
899     return false;
900   }
901 }
902 
903 const char *Prescanner::FixedFormContinuationLine(bool mightNeedSpace) {
904   if (IsAtEnd()) {
905     return nullptr;
906   }
907   tabInCurrentLine_ = false;
908   char col1{*nextLine_};
909   if (InCompilerDirective()) {
910     // Must be a continued compiler directive.
911     if (!IsFixedFormCommentChar(col1)) {
912       return nullptr;
913     }
914     int j{1};
915     for (; j < 5; ++j) {
916       char ch{directiveSentinel_[j - 1]};
917       if (ch == '\0') {
918         break;
919       }
920       if (ch != ToLowerCaseLetter(nextLine_[j])) {
921         return nullptr;
922       }
923     }
924     for (; j < 5; ++j) {
925       if (nextLine_[j] != ' ') {
926         return nullptr;
927       }
928     }
929     char col6{nextLine_[5]};
930     if (col6 != '\n' && col6 != '\t' && col6 != ' ' && col6 != '0') {
931       if (nextLine_[6] != ' ' && mightNeedSpace) {
932         insertASpace_ = true;
933       }
934       return nextLine_ + 6;
935     }
936     return nullptr;
937   } else {
938     // Normal case: not in a compiler directive.
939     if (col1 == '&' &&
940         features_.IsEnabled(
941             LanguageFeature::FixedFormContinuationWithColumn1Ampersand)) {
942       // Extension: '&' as continuation marker
943       if (features_.ShouldWarn(
944               LanguageFeature::FixedFormContinuationWithColumn1Ampersand)) {
945         Say(GetProvenance(nextLine_), "nonstandard usage"_en_US);
946       }
947       return nextLine_ + 1;
948     }
949     if (col1 == '\t' && nextLine_[1] >= '1' && nextLine_[1] <= '9') {
950       tabInCurrentLine_ = true;
951       return nextLine_ + 2; // VAX extension
952     }
953     if (col1 == ' ' && nextLine_[1] == ' ' && nextLine_[2] == ' ' &&
954         nextLine_[3] == ' ' && nextLine_[4] == ' ') {
955       char col6{nextLine_[5]};
956       if (col6 != '\n' && col6 != '\t' && col6 != ' ' && col6 != '0') {
957         return nextLine_ + 6;
958       }
959     }
960     if (IsImplicitContinuation()) {
961       return nextLine_;
962     }
963   }
964   return nullptr; // not a continuation line
965 }
966 
967 const char *Prescanner::FreeFormContinuationLine(bool ampersand) {
968   const char *p{nextLine_};
969   if (p >= limit_) {
970     return nullptr;
971   }
972   p = SkipWhiteSpace(p);
973   if (InCompilerDirective()) {
974     if (*p++ != '!') {
975       return nullptr;
976     }
977     for (const char *s{directiveSentinel_}; *s != '\0'; ++p, ++s) {
978       if (*s != ToLowerCaseLetter(*p)) {
979         return nullptr;
980       }
981     }
982     p = SkipWhiteSpace(p);
983     if (*p == '&') {
984       if (!ampersand) {
985         insertASpace_ = true;
986       }
987       return p + 1;
988     } else if (ampersand) {
989       return p;
990     } else {
991       return nullptr;
992     }
993   } else {
994     if (*p == '&') {
995       return p + 1;
996     } else if (*p == '!' || *p == '\n' || *p == '#') {
997       return nullptr;
998     } else if (ampersand || IsImplicitContinuation()) {
999       if (p > nextLine_) {
1000         --p;
1001       } else {
1002         insertASpace_ = true;
1003       }
1004       return p;
1005     } else {
1006       return nullptr;
1007     }
1008   }
1009 }
1010 
1011 bool Prescanner::FixedFormContinuation(bool mightNeedSpace) {
1012   // N.B. We accept '&' as a continuation indicator in fixed form, too,
1013   // but not in a character literal.
1014   if (*at_ == '&' && inCharLiteral_) {
1015     return false;
1016   }
1017   do {
1018     if (const char *cont{FixedFormContinuationLine(mightNeedSpace)}) {
1019       BeginSourceLine(cont);
1020       column_ = 7;
1021       NextLine();
1022       return true;
1023     }
1024   } while (SkipCommentLine(false /* not after ampersand */));
1025   return false;
1026 }
1027 
1028 bool Prescanner::FreeFormContinuation() {
1029   const char *p{at_};
1030   bool ampersand{*p == '&'};
1031   if (ampersand) {
1032     p = SkipWhiteSpace(p + 1);
1033   }
1034   if (*p != '\n') {
1035     if (inCharLiteral_) {
1036       return false;
1037     } else if (*p != '!' &&
1038         features_.ShouldWarn(LanguageFeature::CruftAfterAmpersand)) {
1039       Say(GetProvenance(p), "missing ! before comment after &"_en_US);
1040     }
1041   }
1042   do {
1043     if (const char *cont{FreeFormContinuationLine(ampersand)}) {
1044       BeginSourceLine(cont);
1045       NextLine();
1046       return true;
1047     }
1048   } while (SkipCommentLine(ampersand));
1049   return false;
1050 }
1051 
1052 // Implicit line continuation allows a preprocessor macro call with
1053 // arguments to span multiple lines.
1054 bool Prescanner::IsImplicitContinuation() const {
1055   return !inPreprocessorDirective_ && !inCharLiteral_ &&
1056       delimiterNesting_ > 0 && !IsAtEnd() &&
1057       ClassifyLine(nextLine_).kind == LineClassification::Kind::Source;
1058 }
1059 
1060 bool Prescanner::Continuation(bool mightNeedFixedFormSpace) {
1061   if (*at_ == '\n' || *at_ == '&') {
1062     if (inFixedForm_) {
1063       return FixedFormContinuation(mightNeedFixedFormSpace);
1064     } else {
1065       return FreeFormContinuation();
1066     }
1067   } else {
1068     return false;
1069   }
1070 }
1071 
1072 std::optional<Prescanner::LineClassification>
1073 Prescanner::IsFixedFormCompilerDirectiveLine(const char *start) const {
1074   const char *p{start};
1075   char col1{*p++};
1076   if (!IsFixedFormCommentChar(col1)) {
1077     return std::nullopt;
1078   }
1079   char sentinel[5], *sp{sentinel};
1080   int column{2};
1081   for (; column < 6; ++column, ++p) {
1082     if (*p != ' ') {
1083       if (*p == '\n' || *p == '\t') {
1084         break;
1085       }
1086       if (sp == sentinel + 1 && sentinel[0] == '$' && IsDecimalDigit(*p)) {
1087         // OpenMP conditional compilation line: leave the label alone
1088         break;
1089       }
1090       *sp++ = ToLowerCaseLetter(*p);
1091     }
1092   }
1093   if (column == 6) {
1094     if (*p == ' ' || *p == '\t' || *p == '0') {
1095       ++p;
1096     } else {
1097       // This is a Continuation line, not an initial directive line.
1098       return std::nullopt;
1099     }
1100   }
1101   if (sp == sentinel) {
1102     return std::nullopt;
1103   }
1104   *sp = '\0';
1105   if (const char *ss{IsCompilerDirectiveSentinel(sentinel)}) {
1106     std::size_t payloadOffset = p - start;
1107     return {LineClassification{
1108         LineClassification::Kind::CompilerDirective, payloadOffset, ss}};
1109   }
1110   return std::nullopt;
1111 }
1112 
1113 std::optional<Prescanner::LineClassification>
1114 Prescanner::IsFreeFormCompilerDirectiveLine(const char *start) const {
1115   char sentinel[8];
1116   const char *p{SkipWhiteSpace(start)};
1117   if (*p++ != '!') {
1118     return std::nullopt;
1119   }
1120   for (std::size_t j{0}; j + 1 < sizeof sentinel; ++p, ++j) {
1121     if (*p == '\n') {
1122       break;
1123     }
1124     if (*p == ' ' || *p == '\t' || *p == '&') {
1125       if (j == 0) {
1126         break;
1127       }
1128       sentinel[j] = '\0';
1129       p = SkipWhiteSpace(p + 1);
1130       if (*p == '!') {
1131         break;
1132       }
1133       if (const char *sp{IsCompilerDirectiveSentinel(sentinel)}) {
1134         std::size_t offset = p - start;
1135         return {LineClassification{
1136             LineClassification::Kind::CompilerDirective, offset, sp}};
1137       }
1138       break;
1139     }
1140     sentinel[j] = ToLowerCaseLetter(*p);
1141   }
1142   return std::nullopt;
1143 }
1144 
1145 Prescanner &Prescanner::AddCompilerDirectiveSentinel(const std::string &dir) {
1146   std::uint64_t packed{0};
1147   for (char ch : dir) {
1148     packed = (packed << 8) | (ToLowerCaseLetter(ch) & 0xff);
1149   }
1150   compilerDirectiveBloomFilter_.set(packed % prime1);
1151   compilerDirectiveBloomFilter_.set(packed % prime2);
1152   compilerDirectiveSentinels_.insert(dir);
1153   return *this;
1154 }
1155 
1156 const char *Prescanner::IsCompilerDirectiveSentinel(
1157     const char *sentinel) const {
1158   std::uint64_t packed{0};
1159   std::size_t n{0};
1160   for (; sentinel[n] != '\0'; ++n) {
1161     packed = (packed << 8) | (sentinel[n] & 0xff);
1162   }
1163   if (n == 0 || !compilerDirectiveBloomFilter_.test(packed % prime1) ||
1164       !compilerDirectiveBloomFilter_.test(packed % prime2)) {
1165     return nullptr;
1166   }
1167   const auto iter{compilerDirectiveSentinels_.find(std::string(sentinel, n))};
1168   return iter == compilerDirectiveSentinels_.end() ? nullptr : iter->c_str();
1169 }
1170 
1171 constexpr bool IsDirective(const char *match, const char *dir) {
1172   for (; *match; ++match) {
1173     if (*match != ToLowerCaseLetter(*dir++)) {
1174       return false;
1175     }
1176   }
1177   return true;
1178 }
1179 
1180 Prescanner::LineClassification Prescanner::ClassifyLine(
1181     const char *start) const {
1182   if (inFixedForm_) {
1183     if (std::optional<LineClassification> lc{
1184             IsFixedFormCompilerDirectiveLine(start)}) {
1185       return std::move(*lc);
1186     }
1187     if (IsFixedFormCommentLine(start)) {
1188       return {LineClassification::Kind::Comment};
1189     }
1190   } else {
1191     if (std::optional<LineClassification> lc{
1192             IsFreeFormCompilerDirectiveLine(start)}) {
1193       return std::move(*lc);
1194     }
1195     if (const char *bang{IsFreeFormComment(start)}) {
1196       return {LineClassification::Kind::Comment,
1197           static_cast<std::size_t>(bang - start)};
1198     }
1199   }
1200   if (std::optional<std::size_t> quoteOffset{IsIncludeLine(start)}) {
1201     return {LineClassification::Kind::IncludeLine, *quoteOffset};
1202   }
1203   if (const char *dir{IsPreprocessorDirectiveLine(start)}) {
1204     if (IsDirective("if", dir) || IsDirective("elif", dir) ||
1205         IsDirective("else", dir) || IsDirective("endif", dir)) {
1206       return {LineClassification::Kind::ConditionalCompilationDirective};
1207     } else if (IsDirective("include", dir)) {
1208       return {LineClassification::Kind::IncludeDirective};
1209     } else if (IsDirective("define", dir) || IsDirective("undef", dir)) {
1210       return {LineClassification::Kind::DefinitionDirective};
1211     } else {
1212       return {LineClassification::Kind::PreprocessorDirective};
1213     }
1214   }
1215   return {LineClassification::Kind::Source};
1216 }
1217 
1218 void Prescanner::SourceFormChange(std::string &&dir) {
1219   if (dir == "!dir$ free") {
1220     inFixedForm_ = false;
1221   } else if (dir == "!dir$ fixed") {
1222     inFixedForm_ = true;
1223   }
1224 }
1225 } // namespace Fortran::parser
1226