1 // Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "parsing.h"
16 #include "grammar.h"
17 #include "instrumented-parser.h"
18 #include "message.h"
19 #include "openmp-grammar.h"
20 #include "preprocessor.h"
21 #include "prescan.h"
22 #include "provenance.h"
23 #include "source.h"
24 #include <sstream>
25
26 namespace Fortran::parser {
27
Parsing(AllSources & s)28 Parsing::Parsing(AllSources &s) : cooked_{s} {}
~Parsing()29 Parsing::~Parsing() {}
30
Prescan(const std::string & path,Options options)31 const SourceFile *Parsing::Prescan(const std::string &path, Options options) {
32 options_ = options;
33 AllSources &allSources{cooked_.allSources()};
34 if (options.isModuleFile) {
35 for (const auto &path : options.searchDirectories) {
36 allSources.PushSearchPathDirectory(path);
37 }
38 }
39
40 std::stringstream fileError;
41 const SourceFile *sourceFile;
42 if (path == "-") {
43 sourceFile = allSources.ReadStandardInput(&fileError);
44 } else {
45 sourceFile = allSources.Open(path, &fileError);
46 }
47 if (!fileError.str().empty()) {
48 ProvenanceRange range{allSources.AddCompilerInsertion(path)};
49 messages_.Say(range, "%s"_err_en_US, fileError.str());
50 return sourceFile;
51 }
52 if (sourceFile->bytes() == 0) {
53 ProvenanceRange range{allSources.AddCompilerInsertion(path)};
54 messages_.Say(range, "file is empty"_err_en_US);
55 return sourceFile;
56 }
57
58 if (!options.isModuleFile) {
59 // For .mod files we always want to look in the search directories.
60 // For normal source files we don't push them until after the primary
61 // source file has been opened. If foo.f is missing from the current
62 // working directory, we don't want to accidentally read another foo.f
63 // from another directory that's on the search path.
64 for (const auto &path : options.searchDirectories) {
65 allSources.PushSearchPathDirectory(path);
66 }
67 }
68
69 Preprocessor preprocessor{allSources};
70 for (const auto &predef : options.predefinitions) {
71 if (predef.second.has_value()) {
72 preprocessor.Define(predef.first, *predef.second);
73 } else {
74 preprocessor.Undefine(predef.first);
75 }
76 }
77 Prescanner prescanner{messages_, cooked_, preprocessor, options.features};
78 prescanner.set_fixedForm(options.isFixedForm)
79 .set_fixedFormColumnLimit(options.fixedFormColumns)
80 .AddCompilerDirectiveSentinel("dir$");
81 if (options.features.IsEnabled(LanguageFeature::OpenMP)) {
82 prescanner.AddCompilerDirectiveSentinel("$omp");
83 prescanner.AddCompilerDirectiveSentinel("$"); // OMP conditional line
84 }
85 ProvenanceRange range{allSources.AddIncludedFile(
86 *sourceFile, ProvenanceRange{}, options.isModuleFile)};
87 prescanner.Prescan(range);
88 cooked_.Marshal();
89 if (options.needProvenanceRangeToCharBlockMappings) {
90 cooked_.CompileProvenanceRangeToOffsetMappings();
91 }
92 return sourceFile;
93 }
94
DumpCookedChars(std::ostream & out) const95 void Parsing::DumpCookedChars(std::ostream &out) const {
96 UserState userState{cooked_, LanguageFeatureControl{}};
97 ParseState parseState{cooked_};
98 parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState);
99 while (std::optional<const char *> p{parseState.GetNextChar()}) {
100 out << **p;
101 }
102 }
103
DumpProvenance(std::ostream & out) const104 void Parsing::DumpProvenance(std::ostream &out) const { cooked_.Dump(out); }
105
DumpParsingLog(std::ostream & out) const106 void Parsing::DumpParsingLog(std::ostream &out) const {
107 log_.Dump(out, cooked_);
108 }
109
Parse(std::ostream * out)110 void Parsing::Parse(std::ostream *out) {
111 UserState userState{cooked_, options_.features};
112 userState.set_debugOutput(out)
113 .set_instrumentedParse(options_.instrumentedParse)
114 .set_log(&log_);
115 ParseState parseState{cooked_};
116 parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState);
117 parseTree_ = program.Parse(parseState);
118 CHECK(
119 !parseState.anyErrorRecovery() || parseState.messages().AnyFatalError());
120 consumedWholeFile_ = parseState.IsAtEnd();
121 messages_.Annex(std::move(parseState.messages()));
122 finalRestingPlace_ = parseState.GetLocation();
123 }
124
ClearLog()125 void Parsing::ClearLog() { log_.clear(); }
126
ForTesting(std::string path,std::ostream & err)127 bool Parsing::ForTesting(std::string path, std::ostream &err) {
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();
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 }
147