xref: /llvm-project/flang/lib/Parser/parsing.cpp (revision 0a90ffa77293e8e2c99843f770cc0f2cd1d8947c)
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 "preprocessor.h"
11 #include "prescan.h"
12 #include "type-parsers.h"
13 #include "flang/Parser/message.h"
14 #include "flang/Parser/provenance.h"
15 #include "flang/Parser/source.h"
16 #include "llvm/Support/raw_ostream.h"
17 
18 namespace Fortran::parser {
19 
20 Parsing::Parsing(AllSources &s) : cooked_{s} {}
21 Parsing::~Parsing() {}
22 
23 const SourceFile *Parsing::Prescan(const std::string &path, Options options) {
24   options_ = options;
25   AllSources &allSources{cooked_.allSources()};
26   if (options.isModuleFile) {
27     for (const auto &path : options.searchDirectories) {
28       allSources.PushSearchPathDirectory(path);
29     }
30   }
31 
32   std::string buf;
33   llvm::raw_string_ostream fileError{buf};
34   const SourceFile *sourceFile;
35   if (path == "-") {
36     sourceFile = allSources.ReadStandardInput(fileError);
37   } else {
38     sourceFile = allSources.Open(path, fileError);
39   }
40   if (!fileError.str().empty()) {
41     ProvenanceRange range{allSources.AddCompilerInsertion(path)};
42     messages_.Say(range, "%s"_err_en_US, fileError.str());
43     return sourceFile;
44   }
45   CHECK(sourceFile);
46 
47   if (!options.isModuleFile) {
48     // For .mod files we always want to look in the search directories.
49     // For normal source files we don't push them until after the primary
50     // source file has been opened.  If foo.f is missing from the current
51     // working directory, we don't want to accidentally read another foo.f
52     // from another directory that's on the search path.
53     for (const auto &path : options.searchDirectories) {
54       allSources.PushSearchPathDirectory(path);
55     }
56   }
57 
58   Preprocessor preprocessor{allSources};
59   for (const auto &predef : options.predefinitions) {
60     if (predef.second) {
61       preprocessor.Define(predef.first, *predef.second);
62     } else {
63       preprocessor.Undefine(predef.first);
64     }
65   }
66   Prescanner prescanner{messages_, cooked_, preprocessor, options.features};
67   prescanner.set_fixedForm(options.isFixedForm)
68       .set_fixedFormColumnLimit(options.fixedFormColumns)
69       .AddCompilerDirectiveSentinel("dir$");
70   if (options.features.IsEnabled(LanguageFeature::OpenACC)) {
71     prescanner.AddCompilerDirectiveSentinel("$acc");
72   }
73   if (options.features.IsEnabled(LanguageFeature::OpenMP)) {
74     prescanner.AddCompilerDirectiveSentinel("$omp");
75     prescanner.AddCompilerDirectiveSentinel("$"); // OMP conditional line
76   }
77   ProvenanceRange range{allSources.AddIncludedFile(
78       *sourceFile, ProvenanceRange{}, options.isModuleFile)};
79   prescanner.Prescan(range);
80   if (cooked_.BufferedBytes() == 0 && !options.isModuleFile) {
81     // Input is empty.  Append a newline so that any warning
82     // message about nonstandard usage will have provenance.
83     cooked_.Put('\n', range.start());
84   }
85   cooked_.Marshal();
86   if (options.needProvenanceRangeToCharBlockMappings) {
87     cooked_.CompileProvenanceRangeToOffsetMappings();
88   }
89   return sourceFile;
90 }
91 
92 void Parsing::DumpCookedChars(llvm::raw_ostream &out) const {
93   UserState userState{cooked_, common::LanguageFeatureControl{}};
94   ParseState parseState{cooked_};
95   parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState);
96   while (std::optional<const char *> p{parseState.GetNextChar()}) {
97     out << **p;
98   }
99 }
100 
101 void Parsing::DumpProvenance(llvm::raw_ostream &out) const {
102   cooked_.Dump(out);
103 }
104 
105 void Parsing::DumpParsingLog(llvm::raw_ostream &out) const {
106   log_.Dump(out, cooked_);
107 }
108 
109 void Parsing::Parse(llvm::raw_ostream &out) {
110   UserState userState{cooked_, options_.features};
111   userState.set_debugOutput(out)
112       .set_instrumentedParse(options_.instrumentedParse)
113       .set_log(&log_);
114   ParseState parseState{cooked_};
115   parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState);
116   parseTree_ = program.Parse(parseState);
117   CHECK(
118       !parseState.anyErrorRecovery() || parseState.messages().AnyFatalError());
119   consumedWholeFile_ = parseState.IsAtEnd();
120   messages_.Annex(std::move(parseState.messages()));
121   finalRestingPlace_ = parseState.GetLocation();
122 }
123 
124 void Parsing::ClearLog() { log_.clear(); }
125 
126 bool Parsing::ForTesting(std::string path, llvm::raw_ostream &err) {
127   llvm::raw_null_ostream NullStream;
128   Prescan(path, Options{});
129   if (messages_.AnyFatalError()) {
130     messages_.Emit(err, cooked_);
131     err << "could not scan " << path << '\n';
132     return false;
133   }
134   Parse(NullStream);
135   messages_.Emit(err, cooked_);
136   if (!consumedWholeFile_) {
137     EmitMessage(err, finalRestingPlace_, "parser FAIL; final position");
138     return false;
139   }
140   if (messages_.AnyFatalError() || !parseTree_.has_value()) {
141     err << "could not parse " << path << '\n';
142     return false;
143   }
144   return true;
145 }
146 } // namespace Fortran::parser
147