FLANG
prescan.h
1//===-- lib/Parser/prescan.h ------------------------------------*- C++ -*-===//
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#ifndef FORTRAN_PARSER_PRESCAN_H_
10#define FORTRAN_PARSER_PRESCAN_H_
11
12// Defines a fast Fortran source prescanning phase that implements some
13// character-level features of the language that can be inefficient to
14// support directly in a backtracking parser. This phase handles Fortran
15// line continuation, comment removal, card image margins, padding out
16// fixed form character literals on truncated card images, file
17// inclusion, and driving the Fortran source preprocessor.
18
19#include "flang/Parser/characters.h"
20#include "flang/Parser/message.h"
21#include "flang/Parser/provenance.h"
22#include "flang/Parser/token-sequence.h"
23#include "flang/Support/Fortran-features.h"
24#include <bitset>
25#include <optional>
26#include <string>
27#include <unordered_set>
28
29namespace Fortran::parser {
30
31class Messages;
32class Preprocessor;
33
34class Prescanner {
35public:
36 Prescanner(Messages &, CookedSource &, Preprocessor &,
38 Prescanner(
39 const Prescanner &, Preprocessor &, bool isNestedInIncludeDirective);
40 Prescanner(const Prescanner &) = delete;
41 Prescanner(Prescanner &&) = delete;
42
43 const AllSources &allSources() const { return allSources_; }
44 AllSources &allSources() { return allSources_; }
45 const Messages &messages() const { return messages_; }
46 Messages &messages() { return messages_; }
47 const Preprocessor &preprocessor() const { return preprocessor_; }
48 Preprocessor &preprocessor() { return preprocessor_; }
49 common::LanguageFeatureControl &features() { return features_; }
50
51 Prescanner &set_preprocessingEnabled(bool yes) {
52 preprocessingEnabled_ = yes;
53 return *this;
54 }
55 Prescanner &set_preprocessingOnly(bool yes) {
56 preprocessingOnly_ = yes;
57 return *this;
58 }
59 Prescanner &set_expandIncludeLines(bool yes) {
60 expandIncludeLines_ = yes;
61 return *this;
62 }
63 Prescanner &set_fixedForm(bool yes) {
64 inFixedForm_ = yes;
65 return *this;
66 }
67 Prescanner &set_encoding(Encoding code) {
68 encoding_ = code;
69 return *this;
70 }
71 Prescanner &set_fixedFormColumnLimit(int limit) {
72 fixedFormColumnLimit_ = limit;
73 return *this;
74 }
75
76 Prescanner &AddCompilerDirectiveSentinel(const std::string &);
77
78 void Prescan(ProvenanceRange);
79 void Statement();
80 void NextLine();
81
82 // Callbacks for use by Preprocessor.
83 bool IsAtEnd() const { return nextLine_ >= limit_; }
84 bool IsNextLinePreprocessorDirective() const;
85 TokenSequence TokenizePreprocessorDirective();
86 Provenance GetCurrentProvenance() const { return GetProvenance(at_); }
87
88 std::optional<CharBlock> GetKeywordMacroName(const char *) const;
89 TokenSequence ExpandKeywordMacro(CharBlock, Provenance) const;
90
91 const char *IsCompilerDirectiveSentinel(const char *, std::size_t) const;
92 const char *IsCompilerDirectiveSentinel(CharBlock) const;
93 // 'first' is the sentinel, 'second' is beginning of payload
94 std::optional<std::pair<const char *, const char *>>
95 IsCompilerDirectiveSentinel(const char *p) const;
96
97 template <typename... A> Message &Say(A &&...a) {
98 return messages_.Say(std::forward<A>(a)...);
99 }
100 template <typename... A>
101 Message *Warn(common::UsageWarning warning, A &&...a) {
102 return messages_.Warn(false, features_, warning, std::forward<A>(a)...);
103 }
104 template <typename... A>
105 Message *Warn(common::LanguageFeature feature, A &&...a) {
106 return messages_.Warn(false, features_, feature, std::forward<A>(a)...);
107 }
108
109private:
110 struct LineClassification {
111 enum class Kind {
112 Comment,
113 ConditionalCompilationDirective,
114 IncludeDirective, // #include
115 DefinitionDirective, // #define & #undef
116 PreprocessorDirective,
117 IncludeLine, // Fortran INCLUDE
119 CompilerDirectiveAfterMacroExpansion, // !MACRO -> !$OMP ...
120 Source
121 };
122 LineClassification(Kind k, std::size_t po = 0, const char *s = nullptr)
123 : kind{k}, payloadOffset{po}, sentinel{s} {}
124 LineClassification(LineClassification &&) = default;
125 LineClassification &operator=(LineClassification &&) = default;
126 Kind kind;
127 std::size_t payloadOffset; // byte offset of content
128 const char *sentinel; // if it's a compiler directive
129 };
130
131 void BeginSourceLine(const char *at) {
132 at_ = at;
133 column_ = 1;
134 tabInCurrentLine_ = false;
135 }
136
137 void BeginSourceLineAndAdvance() {
138 BeginSourceLine(nextLine_);
139 NextLine();
140 }
141
142 void BeginStatementAndAdvance() {
143 BeginSourceLineAndAdvance();
144 slashInCurrentStatement_ = false;
145 preventHollerith_ = false;
146 parenthesisNesting_ = 0;
147 continuationLines_ = 0;
148 isPossibleMacroCall_ = false;
149 disableSourceContinuation_ = false;
150 }
151
152 Provenance GetProvenance(const char *sourceChar) const {
153 return startProvenance_ + (sourceChar - start_);
154 }
155
156 ProvenanceRange GetProvenanceRange(
157 const char *first, const char *afterLast) const {
158 std::size_t bytes = afterLast - first;
159 return {startProvenance_ + (first - start_), bytes};
160 }
161
162 void EmitChar(TokenSequence &tokens, char ch) {
163 tokens.PutNextTokenChar(ch, GetCurrentProvenance());
164 }
165
166 void EmitInsertedChar(TokenSequence &tokens, char ch) {
167 Provenance provenance{allSources().CompilerInsertionProvenance(ch)};
168 tokens.PutNextTokenChar(ch, provenance);
169 }
170
171 char EmitCharAndAdvance(TokenSequence &tokens, char ch) {
172 EmitChar(tokens, ch);
173 NextChar();
174 return *at_;
175 }
176
177 bool IsOpenMPConditionalLine(const char *sentinel) const {
178 return sentinel && sentinel[0] == '$' && !sentinel[1];
179 }
180 bool IsOpenACCConditionalLine(const char *sentinel) const {
181 return sentinel && sentinel[0] == '@' && sentinel[1] == 'a' &&
182 sentinel[2] == 'c' && sentinel[3] == 'c' && sentinel[4] == '\0';
183 }
184 bool IsCUDAConditionalLine(const char *sentinel) const {
185 return sentinel && sentinel[0] == '@' && sentinel[1] == 'c' &&
186 sentinel[2] == 'u' && sentinel[3] == 'f' && sentinel[4] == '\0';
187 }
188 bool InCompilerDirective() const { return directiveSentinel_ != nullptr; }
189 bool InOpenMPConditionalLine() const {
190 return IsOpenMPConditionalLine(directiveSentinel_);
191 }
192 bool InOpenACCConditionalLine() const {
193 return IsOpenACCConditionalLine(directiveSentinel_);
194 }
195 bool InCUDAConditionalLine() const {
196 return IsCUDAConditionalLine(directiveSentinel_);
197 }
198 bool InOpenACCOrCUDAConditionalLine() const {
199 return InOpenACCConditionalLine() || InCUDAConditionalLine();
200 }
201 bool InConditionalLine() const {
202 return InOpenMPConditionalLine() || InOpenACCOrCUDAConditionalLine();
203 }
204 bool IsOpenMPDirective() const {
205 return directiveSentinel_ &&
206 (std::strcmp(directiveSentinel_, "$omp") == 0 ||
207 // Implementation-defined extension sentinels (OpenMP 5.2, 3.1):
208 // "$omx" (fixed form) and "$ompx" (free form). The form is
209 // enforced during recognition (IsCompilerDirectiveSentinel), so a
210 // wrong-form spelling is treated as a comment and never reaches
211 // here.
212 std::strcmp(directiveSentinel_, "$omx") == 0 ||
213 std::strcmp(directiveSentinel_, "$ompx") == 0);
214 }
215 bool InFixedFormSource() const {
216 return inFixedForm_ && !inPreprocessorDirective_ && !InCompilerDirective();
217 }
218
219 bool IsCComment(const char *p) const {
220 return p[0] == '/' && p[1] == '*' &&
221 (inPreprocessorDirective_ ||
222 (!inCharLiteral_ &&
223 features_.IsEnabled(
224 common::LanguageFeature::ClassicCComments)));
225 }
226
227 void CheckAndEmitLine(TokenSequence &, Provenance newlineProvenance);
228 void LabelField(TokenSequence &);
229 void EnforceStupidEndStatementRules(const TokenSequence &);
230 void SkipToEndOfLine();
231 bool MustSkipToEndOfLine() const;
232 void NextChar();
233 // True when input flowed to a continuation line
234 bool SkipToNextSignificantCharacter();
235 void SkipCComments(bool reportUnterminated);
236 void WarnCComment(const char *at);
237 void SkipSpaces();
238 static const char *SkipWhiteSpace(const char *);
239 const char *SkipWhiteSpaceIncludingEmptyMacros(
240 const char *, const char **) const;
241 const char *SkipWhiteSpaceAndCComments(const char *) const;
242 const char *SkipCComment(const char *) const;
243 void UpdateSourcePositionAfterSkip(const char *);
244 bool NextToken(TokenSequence &);
245 bool HandleExponent(TokenSequence &);
246 bool HandleKindSuffix(TokenSequence &);
247 bool HandleExponentAndOrKindSuffix(TokenSequence &);
248 void QuotedCharacterLiteral(TokenSequence &, const char *start);
249 void Hollerith(TokenSequence &, int count, const char *start);
250 bool PadOutCharacterLiteral(TokenSequence &);
251 bool SkipCommentLine(bool afterAmpersand);
252 bool IsFixedFormCommentLine(const char *) const;
253 const char *IsFreeFormComment(const char *) const;
254 std::optional<std::size_t> IsIncludeLine(const char *) const;
255 void FortranInclude(const char *quote);
256 const char *IsPreprocessorDirectiveLine(const char *) const;
257 const char *FixedFormContinuationLine(
258 bool atNewline, const char *&cComment, const char *&unterminatedCComment);
259 const char *GetFreeFormContinuationLine(bool ampersand, const char *p);
260 const char *FreeFormContinuationLine(bool ampersand);
261 bool IsImplicitContinuation() const;
262 bool FixedFormContinuation(bool atNewline);
263 bool FreeFormContinuation();
264 bool Continuation(bool mightNeedFixedFormSpace);
265 std::optional<LineClassification> IsFixedFormCompilerDirectiveLine(
266 const char *) const;
267 std::optional<LineClassification> IsFreeFormCompilerDirectiveLine(
268 const char *) const;
269 LineClassification ClassifyLine(const char *) const;
270 LineClassification ClassifyLine(
271 TokenSequence &, Provenance newlineProvenance) const;
272 bool SourceFormChange(std::string &&);
273 bool CompilerDirectiveContinuation(TokenSequence &, const char *sentinel);
274 bool SourceLineContinuation(TokenSequence &);
275 std::optional<LineClassification>
276 IsCompilerDirectiveSentinelAfterKeywordMacro(const char *p) const;
277
278 Messages &messages_;
279 CookedSource &cooked_;
280 Preprocessor &preprocessor_;
281 AllSources &allSources_;
283 bool preprocessingEnabled_{false};
284 bool preprocessingOnly_{false};
285 bool expandIncludeLines_{true};
286 bool isNestedInIncludeDirective_{false};
287 bool backslashFreeFormContinuation_{false};
288 bool inFixedForm_{false};
289 int fixedFormColumnLimit_{72};
290 Encoding encoding_{Encoding::UTF_8};
291 int parenthesisNesting_{0};
292 int prescannerNesting_{0};
293 int continuationLines_{0};
294 bool isPossibleMacroCall_{false};
295 bool afterPreprocessingDirective_{false};
296 bool disableSourceContinuation_{false};
297
298 Provenance startProvenance_;
299 const char *start_{nullptr}; // beginning of current source file content
300 const char *limit_{nullptr}; // first address after end of current source
301 const char *nextLine_{nullptr}; // next line to process; <= limit_
302 const char *directiveSentinel_{nullptr}; // current compiler directive
303
304 // These data members are state for processing the source line containing
305 // "at_", which goes to up to the newline character before "nextLine_".
306 const char *at_{nullptr}; // next character to process; < nextLine_
307 int column_{1}; // card image column position of next character
308 bool tabInCurrentLine_{false};
309 bool slashInCurrentStatement_{false};
310 bool preventHollerith_{false}; // CHARACTER*4HIMOM not Hollerith
311 bool inCharLiteral_{false};
312 bool continuationInCharLiteral_{false};
313 bool inPreprocessorDirective_{false};
314
315 // True after processing a continuation that can't be allowed
316 // to appear in the middle of an identifier token, but is fixed form,
317 // or is free form and doesn't have a space character handy to use as
318 // a separator when:
319 // a) (standard) doesn't begin with a leading '&' on the continuation
320 // line, but has a non-blank in column 1, or
321 // b) (extension) does have a leading '&', but didn't have one
322 // on the continued line.
323 bool brokenToken_{false};
324
325 // When a free form continuation marker (&) appears at the end of a line
326 // before a INCLUDE or #include, we delete it and omit the newline, so
327 // that the first line of the included file is truly a continuation of
328 // the line before. Also used when the & appears at the end of the last
329 // line in an include file.
330 bool omitNewline_{false};
331 bool skipLeadingAmpersand_{false};
332
333 const std::size_t firstCookedCharacterOffset_{cooked_.BufferedBytes()};
334
335 const Provenance spaceProvenance_{
336 allSources().CompilerInsertionProvenance(' ')};
337 const Provenance backslashProvenance_{
338 allSources().CompilerInsertionProvenance('\\')};
339
340 // To avoid probing the set of active compiler directive sentinel strings
341 // on every comment line, they're checked first with a cheap Bloom filter.
342 static const int prime1{1019}, prime2{1021};
343 std::bitset<prime2> compilerDirectiveBloomFilter_; // 128 bytes
344 std::unordered_set<std::string> compilerDirectiveSentinels_;
345};
346} // namespace Fortran::parser
347#endif // FORTRAN_PARSER_PRESCAN_H_
Definition Fortran-features.h:101
Definition provenance.h:139
Definition char-block.h:26
Definition provenance.h:238
Definition message.h:200
Definition message.h:332
Definition preprocessor.h:74
Definition provenance.h:52
Definition token-sequence.h:34
Definition check-expression.h:19
Definition parse-tree.h:3444