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