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