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