1//===-- lib/Parser/parsing.cpp --------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "flang/Parser/parsing.h"
10#include "prescan.h"
11#include "type-parsers.h"
12#include "flang/Parser/message.h"
13#include "flang/Parser/preprocessor.h"
14#include "flang/Parser/provenance.h"
15#include "flang/Parser/source.h"
16#include "llvm/Support/raw_ostream.h"
17
18namespace Fortran::parser {
19
20Parsing::Parsing(AllCookedSources &allCooked) : allCooked_{allCooked} {}
21Parsing::~Parsing() {}
22
23const SourceFile *Parsing::Prescan(const std::string &path, Options options) {
24 options_ = options;
25 AllSources &allSources{allCooked_.allSources()};
26 allSources.ClearSearchPath();
27 if (options.isModuleFile) {
28 for (const auto &path : options.searchDirectories) {
29 allSources.AppendSearchPathDirectory(path);
30 }
31 }
32
33 std::string buf;
34 llvm::raw_string_ostream fileError{buf};
35 const SourceFile *sourceFile{nullptr};
36 if (path == "-") {
37 sourceFile = allSources.ReadStandardInput(fileError);
38 } else if (options.isModuleFile) {
39 // Don't mess with intrinsic module search path
40 sourceFile = allSources.Open(path, fileError);
41 } else {
42 sourceFile =
43 allSources.Open(path, fileError, "."s /*prepend to search path*/);
44 }
45 if (!buf.empty()) {
46 ProvenanceRange range{allSources.AddCompilerInsertion(path)};
47 messages_.Say(range, "%s"_err_en_US, buf);
48 return sourceFile;
49 }
50 CHECK(sourceFile);
51
52 if (!options.isModuleFile) {
53 // For .mod files we always want to look in the search directories.
54 // For normal source files we don't add them until after the primary
55 // source file has been opened. If foo.f is missing from the current
56 // working directory, we don't want to accidentally read another foo.f
57 // from another directory that's on the search path.
58 for (const auto &path : options.searchDirectories) {
59 allSources.AppendSearchPathDirectory(path);
60 }
61 }
62
63 if (!options.predefinitions.empty()) {
64 preprocessor_.DefineStandardMacros();
65 for (const auto &predef : options.predefinitions) {
66 if (predef.second) {
67 preprocessor_.Define(predef.first, *predef.second);
68 } else {
69 preprocessor_.Undefine(predef.first);
70 }
71 }
72 }
73 currentCooked_ = &allCooked_.NewCookedSource();
74 Prescanner prescanner{
75 messages_, *currentCooked_, preprocessor_, options.features};
76 prescanner.set_fixedForm(options.isFixedForm)
77 .set_fixedFormColumnLimit(options.fixedFormColumns)
78 .set_preprocessingOnly(options.prescanAndReformat)
79 .set_expandIncludeLines(!options.prescanAndReformat ||
80 options.expandIncludeLinesInPreprocessedOutput)
81 .AddCompilerDirectiveSentinel("dir$");
82 bool noneOfTheAbove{!options.features.IsEnabled(LanguageFeature::OpenACC) &&
83 !options.features.IsEnabled(LanguageFeature::OpenMP) &&
84 !options.features.IsEnabled(LanguageFeature::CUDA)};
85 if (options.features.IsEnabled(LanguageFeature::OpenACC) ||
86 (options.prescanAndReformat && noneOfTheAbove)) {
87 prescanner.AddCompilerDirectiveSentinel("$acc");
88 }
89 if (options.features.IsEnabled(LanguageFeature::OpenMP) ||
90 (options.prescanAndReformat && noneOfTheAbove)) {
91 prescanner.AddCompilerDirectiveSentinel("$omp");
92 prescanner.AddCompilerDirectiveSentinel("$"); // OMP conditional line
93 }
94 if (options.features.IsEnabled(LanguageFeature::CUDA) ||
95 (options.prescanAndReformat && noneOfTheAbove)) {
96 prescanner.AddCompilerDirectiveSentinel("$cuf");
97 prescanner.AddCompilerDirectiveSentinel("@cuf");
98 }
99 if (options.features.IsEnabled(LanguageFeature::CUDA)) {
100 preprocessor_.Define("_CUDA", "1");
101 }
102 ProvenanceRange range{allSources.AddIncludedFile(
103 *sourceFile, ProvenanceRange{}, options.isModuleFile)};
104 prescanner.Prescan(range);
105 if (currentCooked_->BufferedBytes() == 0 && !options.isModuleFile) {
106 // Input is empty. Append a newline so that any warning
107 // message about nonstandard usage will have provenance.
108 currentCooked_->Put('\n', range.start());
109 }
110 currentCooked_->Marshal(allCooked_);
111 if (options.needProvenanceRangeToCharBlockMappings) {
112 currentCooked_->CompileProvenanceRangeToOffsetMappings(allSources);
113 }
114 if (options.showColors) {
115 allSources.setShowColors(/*showColors=*/true);
116 }
117 return sourceFile;
118}
119
120void Parsing::EmitPreprocessorMacros(llvm::raw_ostream &out) const {
121 preprocessor_.PrintMacros(out);
122}
123
124void Parsing::EmitPreprocessedSource(
125 llvm::raw_ostream &out, bool lineDirectives) const {
126 const std::string *sourcePath{nullptr};
127 int sourceLine{0};
128 int column{1};
129 bool inDirective{false};
130 bool ompConditionalLine{false};
131 bool inContinuation{false};
132 bool lineWasBlankBefore{true};
133 const AllSources &allSources{allCooked().allSources()};
134 // All directives that flang supports are known to have a length of 4 chars,
135 // except for OpenMP conditional compilation lines (!$).
136 constexpr int directiveNameLength{4};
137 // We need to know the current directive in order to provide correct
138 // continuation for the directive
139 std::string directive;
140 for (const char &atChar : cooked().AsCharBlock()) {
141 char ch{atChar};
142 if (ch == '\n') {
143 out << '\n'; // TODO: DOS CR-LF line ending if necessary
144 column = 1;
145 inDirective = false;
146 ompConditionalLine = false;
147 inContinuation = false;
148 lineWasBlankBefore = true;
149 ++sourceLine;
150 directive.clear();
151 } else {
152 auto provenance{cooked().GetProvenanceRange(CharBlock{&atChar, 1})};
153
154 // Preserves original case of the character
155 const auto getOriginalChar{[&](char ch) {
156 if (IsLetter(ch) && provenance && provenance->size() == 1) {
157 if (const char *orig{allSources.GetSource(*provenance)}) {
158 char upper{ToUpperCaseLetter(ch)};
159 if (*orig == upper) {
160 return upper;
161 }
162 }
163 }
164 return ch;
165 }};
166
167 bool inDirectiveSentinel{false};
168 if (ch == '!' && lineWasBlankBefore) {
169 // Other comment markers (C, *, D) in original fixed form source
170 // input card column 1 will have been deleted or normalized to !,
171 // which signifies a comment (directive) in both source forms.
172 inDirective = true;
173 inDirectiveSentinel = true;
174 } else if (inDirective && !ompConditionalLine &&
175 directive.size() < directiveNameLength) {
176 if (IsLetter(ch) || ch == '$' || ch == '@') {
177 directive += getOriginalChar(ch);
178 inDirectiveSentinel = true;
179 } else if (directive == "$"s) {
180 ompConditionalLine = true;
181 }
182 }
183
184 std::optional<SourcePosition> position{provenance
185 ? allSources.GetSourcePosition(provenance->start())
186 : std::nullopt};
187 if (column == 1 && position) {
188 if (lineDirectives) {
189 if (&*position->path != sourcePath) {
190 out << "#line \"" << *position->path << "\" " << position->line
191 << '\n';
192 } else if (position->line != sourceLine) {
193 if (sourceLine < position->line &&
194 sourceLine + 10 >= position->line) {
195 // Emit a few newlines to catch up when they'll likely
196 // require fewer bytes than a #line directive would have
197 // occupied.
198 while (sourceLine++ < position->line) {
199 out << '\n';
200 }
201 } else {
202 out << "#line " << position->line << '\n';
203 }
204 }
205 }
206 sourcePath = &*position->path;
207 sourceLine = position->line;
208 }
209 if (column > 72) {
210 // Wrap long lines in a portable fashion that works in both
211 // of the Fortran source forms. The first free-form continuation
212 // marker ("&") lands in column 73, which begins the card commentary
213 // field of fixed form, and the second one is put in column 6,
214 // where it signifies fixed form line continuation.
215 // The standard Fortran fixed form column limit (72) is used
216 // for output, even if the input was parsed with a nonstandard
217 // column limit override option.
218 // OpenMP and OpenACC directives' continuations should have the
219 // corresponding sentinel at the next line.
220 out << "&\n";
221 if (inDirective) {
222 if (ompConditionalLine) {
223 out << "!$ &";
224 } else {
225 out << '!' << directive << '&';
226 }
227 } else {
228 out << " &";
229 }
230 column = 7; // start of fixed form source field
231 ++sourceLine;
232 inContinuation = true;
233 } else if (!inDirective && !ompConditionalLine && ch != ' ' &&
234 (ch < '0' || ch > '9')) {
235 // Put anything other than a label or directive into the
236 // Fortran fixed form source field (columns [7:72]).
237 for (int toCol{ch == '&' ? 6 : 7}; column < toCol; ++column) {
238 out << ' ';
239 }
240 }
241 if (ch != ' ') {
242 if (ompConditionalLine) {
243 // Only digits can stay in the label field
244 if (!(ch >= '0' && ch <= '9')) {
245 for (int toCol{ch == '&' ? 6 : 7}; column < toCol; ++column) {
246 out << ' ';
247 }
248 }
249 } else if (!inContinuation && !inDirectiveSentinel && position &&
250 position->line == sourceLine && position->column < 72) {
251 // Preserve original indentation
252 for (; column < position->column; ++column) {
253 out << ' ';
254 }
255 }
256 }
257 out << getOriginalChar(ch);
258 lineWasBlankBefore = ch == ' ' && lineWasBlankBefore;
259 ++column;
260 }
261 }
262}
263
264void Parsing::DumpCookedChars(llvm::raw_ostream &out) const {
265 UserState userState{allCooked_, common::LanguageFeatureControl{}};
266 ParseState parseState{cooked()};
267 parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState);
268 while (std::optional<const char *> p{parseState.GetNextChar()}) {
269 out << **p;
270 }
271}
272
273void Parsing::DumpProvenance(llvm::raw_ostream &out) const {
274 allCooked_.Dump(out);
275}
276
277void Parsing::DumpParsingLog(llvm::raw_ostream &out) const {
278 log_.Dump(out, allCooked_);
279}
280
281void Parsing::Parse(llvm::raw_ostream &out) {
282 UserState userState{allCooked_, options_.features};
283 userState.set_debugOutput(out)
284 .set_instrumentedParse(options_.instrumentedParse)
285 .set_log(&log_);
286 ParseState parseState{cooked()};
287 parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState);
288 parseTree_ = program.Parse(parseState);
289 CHECK(
290 !parseState.anyErrorRecovery() || parseState.messages().AnyFatalError());
291 consumedWholeFile_ = parseState.IsAtEnd();
292 messages_.Annex(std::move(parseState.messages()));
293 finalRestingPlace_ = parseState.GetLocation();
294}
295
296void Parsing::ClearLog() { log_.clear(); }
297
298} // namespace Fortran::parser
299

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of flang/lib/Parser/parsing.cpp