xref: /llvm-project/flang/lib/Parser/parsing.cpp (revision 6110e7716cd0000fdeb2a7edfbec7c9991f1a08a)
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(AllCookedSources &allCooked) : allCooked_{allCooked} {}
21 Parsing::~Parsing() {}
22 
23 const SourceFile *Parsing::Prescan(const std::string &path, Options options) {
24   options_ = options;
25   AllSources &allSources{allCooked_.allSources()};
26   if (options.isModuleFile) {
27     for (const auto &path : options.searchDirectories) {
28       allSources.AppendSearchPathDirectory(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     std::optional<std::string> currentDirectory{"."};
39     sourceFile = allSources.Open(path, fileError, std::move(currentDirectory));
40   }
41   if (!fileError.str().empty()) {
42     ProvenanceRange range{allSources.AddCompilerInsertion(path)};
43     messages_.Say(range, "%s"_err_en_US, fileError.str());
44     return sourceFile;
45   }
46   CHECK(sourceFile);
47 
48   if (!options.isModuleFile) {
49     // For .mod files we always want to look in the search directories.
50     // For normal source files we don't add them until after the primary
51     // source file has been opened.  If foo.f is missing from the current
52     // working directory, we don't want to accidentally read another foo.f
53     // from another directory that's on the search path.
54     for (const auto &path : options.searchDirectories) {
55       allSources.AppendSearchPathDirectory(path);
56     }
57   }
58 
59   Preprocessor preprocessor{allSources};
60   for (const auto &predef : options.predefinitions) {
61     if (predef.second) {
62       preprocessor.Define(predef.first, *predef.second);
63     } else {
64       preprocessor.Undefine(predef.first);
65     }
66   }
67   currentCooked_ = &allCooked_.NewCookedSource();
68   Prescanner prescanner{
69       messages_, *currentCooked_, preprocessor, options.features};
70   prescanner.set_fixedForm(options.isFixedForm)
71       .set_fixedFormColumnLimit(options.fixedFormColumns)
72       .AddCompilerDirectiveSentinel("dir$");
73   if (options.features.IsEnabled(LanguageFeature::OpenACC)) {
74     prescanner.AddCompilerDirectiveSentinel("$acc");
75   }
76   if (options.features.IsEnabled(LanguageFeature::OpenMP)) {
77     prescanner.AddCompilerDirectiveSentinel("$omp");
78     prescanner.AddCompilerDirectiveSentinel("$"); // OMP conditional line
79   }
80   ProvenanceRange range{allSources.AddIncludedFile(
81       *sourceFile, ProvenanceRange{}, options.isModuleFile)};
82   prescanner.Prescan(range);
83   if (currentCooked_->BufferedBytes() == 0 && !options.isModuleFile) {
84     // Input is empty.  Append a newline so that any warning
85     // message about nonstandard usage will have provenance.
86     currentCooked_->Put('\n', range.start());
87   }
88   currentCooked_->Marshal(allSources);
89   if (options.needProvenanceRangeToCharBlockMappings) {
90     currentCooked_->CompileProvenanceRangeToOffsetMappings(allSources);
91   }
92   return sourceFile;
93 }
94 
95 void Parsing::DumpCookedChars(llvm::raw_ostream &out) const {
96   UserState userState{allCooked_, common::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 
104 void Parsing::DumpProvenance(llvm::raw_ostream &out) const {
105   allCooked_.Dump(out);
106 }
107 
108 void Parsing::DumpParsingLog(llvm::raw_ostream &out) const {
109   log_.Dump(out, allCooked_);
110 }
111 
112 void Parsing::Parse(llvm::raw_ostream &out) {
113   UserState userState{allCooked_, options_.features};
114   userState.set_debugOutput(out)
115       .set_instrumentedParse(options_.instrumentedParse)
116       .set_log(&log_);
117   ParseState parseState{cooked()};
118   parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState);
119   parseTree_ = program.Parse(parseState);
120   CHECK(
121       !parseState.anyErrorRecovery() || parseState.messages().AnyFatalError());
122   consumedWholeFile_ = parseState.IsAtEnd();
123   messages_.Annex(std::move(parseState.messages()));
124   finalRestingPlace_ = parseState.GetLocation();
125 }
126 
127 void Parsing::ClearLog() { log_.clear(); }
128 
129 } // namespace Fortran::parser
130