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