164ab3302SCarolineConcatto //===-- lib/Parser/parsing.cpp --------------------------------------------===// 264ab3302SCarolineConcatto // 364ab3302SCarolineConcatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 464ab3302SCarolineConcatto // See https://llvm.org/LICENSE.txt for license information. 564ab3302SCarolineConcatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 664ab3302SCarolineConcatto // 764ab3302SCarolineConcatto //===----------------------------------------------------------------------===// 864ab3302SCarolineConcatto 964ab3302SCarolineConcatto #include "flang/Parser/parsing.h" 1064ab3302SCarolineConcatto #include "prescan.h" 1164ab3302SCarolineConcatto #include "type-parsers.h" 1264ab3302SCarolineConcatto #include "flang/Parser/message.h" 137d60232bSKrzysztof Parzyszek #include "flang/Parser/preprocessor.h" 1464ab3302SCarolineConcatto #include "flang/Parser/provenance.h" 1564ab3302SCarolineConcatto #include "flang/Parser/source.h" 168670e499SCaroline Concatto #include "llvm/Support/raw_ostream.h" 1764ab3302SCarolineConcatto 1864ab3302SCarolineConcatto namespace Fortran::parser { 1964ab3302SCarolineConcatto 2092a54197Speter klausler Parsing::Parsing(AllCookedSources &allCooked) : allCooked_{allCooked} {} 2164ab3302SCarolineConcatto Parsing::~Parsing() {} 2264ab3302SCarolineConcatto 2364ab3302SCarolineConcatto const SourceFile *Parsing::Prescan(const std::string &path, Options options) { 2464ab3302SCarolineConcatto options_ = options; 2592a54197Speter klausler AllSources &allSources{allCooked_.allSources()}; 2652a1346bSPeter Klausler allSources.ClearSearchPath(); 2764ab3302SCarolineConcatto if (options.isModuleFile) { 2864ab3302SCarolineConcatto for (const auto &path : options.searchDirectories) { 296110e771Speter klausler allSources.AppendSearchPathDirectory(path); 3064ab3302SCarolineConcatto } 3164ab3302SCarolineConcatto } 3264ab3302SCarolineConcatto 338670e499SCaroline Concatto std::string buf; 348670e499SCaroline Concatto llvm::raw_string_ostream fileError{buf}; 35e12ffe6aSPeter Klausler const SourceFile *sourceFile{nullptr}; 3664ab3302SCarolineConcatto if (path == "-") { 378670e499SCaroline Concatto sourceFile = allSources.ReadStandardInput(fileError); 3815faac90SPeter Klausler } else if (options.isModuleFile) { 3915faac90SPeter Klausler // Don't mess with intrinsic module search path 4015faac90SPeter Klausler sourceFile = allSources.Open(path, fileError); 4164ab3302SCarolineConcatto } else { 4215faac90SPeter Klausler sourceFile = 4315faac90SPeter Klausler allSources.Open(path, fileError, "."s /*prepend to search path*/); 4464ab3302SCarolineConcatto } 4584d7f294SYoungsuk Kim if (!buf.empty()) { 4664ab3302SCarolineConcatto ProvenanceRange range{allSources.AddCompilerInsertion(path)}; 4784d7f294SYoungsuk Kim messages_.Say(range, "%s"_err_en_US, buf); 4864ab3302SCarolineConcatto return sourceFile; 4964ab3302SCarolineConcatto } 5064ab3302SCarolineConcatto CHECK(sourceFile); 5164ab3302SCarolineConcatto 5264ab3302SCarolineConcatto if (!options.isModuleFile) { 5364ab3302SCarolineConcatto // For .mod files we always want to look in the search directories. 546110e771Speter klausler // For normal source files we don't add them until after the primary 5564ab3302SCarolineConcatto // source file has been opened. If foo.f is missing from the current 5664ab3302SCarolineConcatto // working directory, we don't want to accidentally read another foo.f 5764ab3302SCarolineConcatto // from another directory that's on the search path. 5864ab3302SCarolineConcatto for (const auto &path : options.searchDirectories) { 596110e771Speter klausler allSources.AppendSearchPathDirectory(path); 6064ab3302SCarolineConcatto } 6164ab3302SCarolineConcatto } 6264ab3302SCarolineConcatto 638880a63aSpeter klausler if (!options.predefinitions.empty()) { 647d60232bSKrzysztof Parzyszek preprocessor_.DefineStandardMacros(); 6564ab3302SCarolineConcatto for (const auto &predef : options.predefinitions) { 6664ab3302SCarolineConcatto if (predef.second) { 677d60232bSKrzysztof Parzyszek preprocessor_.Define(predef.first, *predef.second); 6864ab3302SCarolineConcatto } else { 697d60232bSKrzysztof Parzyszek preprocessor_.Undefine(predef.first); 7064ab3302SCarolineConcatto } 7164ab3302SCarolineConcatto } 728880a63aSpeter klausler } 7392a54197Speter klausler currentCooked_ = &allCooked_.NewCookedSource(); 7492a54197Speter klausler Prescanner prescanner{ 757d60232bSKrzysztof Parzyszek messages_, *currentCooked_, preprocessor_, options.features}; 7664ab3302SCarolineConcatto prescanner.set_fixedForm(options.isFixedForm) 7764ab3302SCarolineConcatto .set_fixedFormColumnLimit(options.fixedFormColumns) 789fb2db1eSPeter Klausler .set_preprocessingOnly(options.prescanAndReformat) 794dfed691SPeter Klausler .set_expandIncludeLines(!options.prescanAndReformat || 804dfed691SPeter Klausler options.expandIncludeLinesInPreprocessedOutput) 8164ab3302SCarolineConcatto .AddCompilerDirectiveSentinel("dir$"); 820a90ffa7SValentin Clement if (options.features.IsEnabled(LanguageFeature::OpenACC)) { 830a90ffa7SValentin Clement prescanner.AddCompilerDirectiveSentinel("$acc"); 840a90ffa7SValentin Clement } 8564ab3302SCarolineConcatto if (options.features.IsEnabled(LanguageFeature::OpenMP)) { 8664ab3302SCarolineConcatto prescanner.AddCompilerDirectiveSentinel("$omp"); 8764ab3302SCarolineConcatto prescanner.AddCompilerDirectiveSentinel("$"); // OMP conditional line 8864ab3302SCarolineConcatto } 894ad72793SPeter Klausler if (options.features.IsEnabled(LanguageFeature::CUDA)) { 904ad72793SPeter Klausler prescanner.AddCompilerDirectiveSentinel("$cuf"); 914ad72793SPeter Klausler prescanner.AddCompilerDirectiveSentinel("@cuf"); 927d60232bSKrzysztof Parzyszek preprocessor_.Define("_CUDA", "1"); 934ad72793SPeter Klausler } 9464ab3302SCarolineConcatto ProvenanceRange range{allSources.AddIncludedFile( 9564ab3302SCarolineConcatto *sourceFile, ProvenanceRange{}, options.isModuleFile)}; 9664ab3302SCarolineConcatto prescanner.Prescan(range); 9792a54197Speter klausler if (currentCooked_->BufferedBytes() == 0 && !options.isModuleFile) { 9864ab3302SCarolineConcatto // Input is empty. Append a newline so that any warning 9964ab3302SCarolineConcatto // message about nonstandard usage will have provenance. 10092a54197Speter klausler currentCooked_->Put('\n', range.start()); 10164ab3302SCarolineConcatto } 10246ade6d0Speter klausler currentCooked_->Marshal(allCooked_); 10364ab3302SCarolineConcatto if (options.needProvenanceRangeToCharBlockMappings) { 10492a54197Speter klausler currentCooked_->CompileProvenanceRangeToOffsetMappings(allSources); 10564ab3302SCarolineConcatto } 1068df63a23SPeixin Qiao if (options.showColors) { 1078df63a23SPeixin Qiao allSources.setShowColors(/*showColors=*/true); 1088df63a23SPeixin Qiao } 10964ab3302SCarolineConcatto return sourceFile; 11064ab3302SCarolineConcatto } 11164ab3302SCarolineConcatto 1127d60232bSKrzysztof Parzyszek void Parsing::EmitPreprocessorMacros(llvm::raw_ostream &out) const { 1137d60232bSKrzysztof Parzyszek preprocessor_.PrintMacros(out); 1147d60232bSKrzysztof Parzyszek } 1157d60232bSKrzysztof Parzyszek 1163338ef93Speter klausler void Parsing::EmitPreprocessedSource( 1173338ef93Speter klausler llvm::raw_ostream &out, bool lineDirectives) const { 118e12ffe6aSPeter Klausler const std::string *sourcePath{nullptr}; 1193338ef93Speter klausler int sourceLine{0}; 1203338ef93Speter klausler int column{1}; 1213338ef93Speter klausler bool inDirective{false}; 1223338ef93Speter klausler bool inContinuation{false}; 123889c7c8eSDaniil Dudkin bool lineWasBlankBefore{true}; 1243338ef93Speter klausler const AllSources &allSources{allCooked().allSources()}; 125889c7c8eSDaniil Dudkin // All directives that flang support are known to have a length of 3 chars 126889c7c8eSDaniil Dudkin constexpr int directiveNameLength{3}; 127889c7c8eSDaniil Dudkin // We need to know the current directive in order to provide correct 128889c7c8eSDaniil Dudkin // continuation for the directive 129889c7c8eSDaniil Dudkin std::string directive; 1303338ef93Speter klausler for (const char &atChar : cooked().AsCharBlock()) { 1313338ef93Speter klausler char ch{atChar}; 1323338ef93Speter klausler if (ch == '\n') { 1333338ef93Speter klausler out << '\n'; // TODO: DOS CR-LF line ending if necessary 1343338ef93Speter klausler column = 1; 1353338ef93Speter klausler inDirective = false; 1363338ef93Speter klausler inContinuation = false; 137889c7c8eSDaniil Dudkin lineWasBlankBefore = true; 1383338ef93Speter klausler ++sourceLine; 139889c7c8eSDaniil Dudkin directive.clear(); 1403338ef93Speter klausler } else { 141889c7c8eSDaniil Dudkin auto provenance{cooked().GetProvenanceRange(CharBlock{&atChar, 1})}; 142889c7c8eSDaniil Dudkin 143889c7c8eSDaniil Dudkin // Preserves original case of the character 144889c7c8eSDaniil Dudkin const auto getOriginalChar{[&](char ch) { 145889c7c8eSDaniil Dudkin if (IsLetter(ch) && provenance && provenance->size() == 1) { 146889c7c8eSDaniil Dudkin if (const char *orig{allSources.GetSource(*provenance)}) { 147889c7c8eSDaniil Dudkin const char upper{ToUpperCaseLetter(ch)}; 148889c7c8eSDaniil Dudkin if (*orig == upper) { 149889c7c8eSDaniil Dudkin return upper; 150889c7c8eSDaniil Dudkin } 151889c7c8eSDaniil Dudkin } 152889c7c8eSDaniil Dudkin } 153889c7c8eSDaniil Dudkin return ch; 154889c7c8eSDaniil Dudkin }}; 155889c7c8eSDaniil Dudkin 156889c7c8eSDaniil Dudkin if (ch == '!' && lineWasBlankBefore) { 1573338ef93Speter klausler // Other comment markers (C, *, D) in original fixed form source 1583338ef93Speter klausler // input card column 1 will have been deleted or normalized to !, 1593338ef93Speter klausler // which signifies a comment (directive) in both source forms. 1603338ef93Speter klausler inDirective = true; 1613338ef93Speter klausler } 162*3c3094b6Smacurtis-amd bool inDirectiveSentinel{ 163*3c3094b6Smacurtis-amd inDirective && directive.size() < directiveNameLength}; 164*3c3094b6Smacurtis-amd if (inDirectiveSentinel && IsLetter(ch)) { 165889c7c8eSDaniil Dudkin directive += getOriginalChar(ch); 166889c7c8eSDaniil Dudkin } 167889c7c8eSDaniil Dudkin 1683338ef93Speter klausler std::optional<SourcePosition> position{provenance 1693338ef93Speter klausler ? allSources.GetSourcePosition(provenance->start()) 1703338ef93Speter klausler : std::nullopt}; 1713338ef93Speter klausler if (lineDirectives && column == 1 && position) { 172e12ffe6aSPeter Klausler if (&*position->path != sourcePath) { 173e12ffe6aSPeter Klausler out << "#line \"" << *position->path << "\" " << position->line 1743338ef93Speter klausler << '\n'; 1753338ef93Speter klausler } else if (position->line != sourceLine) { 1763338ef93Speter klausler if (sourceLine < position->line && 1773338ef93Speter klausler sourceLine + 10 >= position->line) { 1783338ef93Speter klausler // Emit a few newlines to catch up when they'll likely 1793338ef93Speter klausler // require fewer bytes than a #line directive would have 1803338ef93Speter klausler // occupied. 1813338ef93Speter klausler while (sourceLine++ < position->line) { 1823338ef93Speter klausler out << '\n'; 1833338ef93Speter klausler } 1843338ef93Speter klausler } else { 1853338ef93Speter klausler out << "#line " << position->line << '\n'; 1863338ef93Speter klausler } 1873338ef93Speter klausler } 188e12ffe6aSPeter Klausler sourcePath = &*position->path; 1893338ef93Speter klausler sourceLine = position->line; 1903338ef93Speter klausler } 1913338ef93Speter klausler if (column > 72) { 1923338ef93Speter klausler // Wrap long lines in a portable fashion that works in both 1933338ef93Speter klausler // of the Fortran source forms. The first free-form continuation 1943338ef93Speter klausler // marker ("&") lands in column 73, which begins the card commentary 1953338ef93Speter klausler // field of fixed form, and the second one is put in column 6, 1963338ef93Speter klausler // where it signifies fixed form line continuation. 1973338ef93Speter klausler // The standard Fortran fixed form column limit (72) is used 1983338ef93Speter klausler // for output, even if the input was parsed with a nonstandard 1993338ef93Speter klausler // column limit override option. 200889c7c8eSDaniil Dudkin // OpenMP and OpenACC directives' continuations should have the 201889c7c8eSDaniil Dudkin // corresponding sentinel at the next line. 202889c7c8eSDaniil Dudkin const auto continuation{ 203889c7c8eSDaniil Dudkin inDirective ? "&\n!$" + directive + "&" : "&\n &"s}; 204889c7c8eSDaniil Dudkin out << continuation; 2053338ef93Speter klausler column = 7; // start of fixed form source field 2063338ef93Speter klausler ++sourceLine; 2073338ef93Speter klausler inContinuation = true; 2083338ef93Speter klausler } else if (!inDirective && ch != ' ' && (ch < '0' || ch > '9')) { 2093338ef93Speter klausler // Put anything other than a label or directive into the 2103338ef93Speter klausler // Fortran fixed form source field (columns [7:72]). 2113338ef93Speter klausler for (; column < 7; ++column) { 2123338ef93Speter klausler out << ' '; 2133338ef93Speter klausler } 2143338ef93Speter klausler } 215*3c3094b6Smacurtis-amd if (!inContinuation && !inDirectiveSentinel && position && 216*3c3094b6Smacurtis-amd position->column <= 72 && ch != ' ') { 2173338ef93Speter klausler // Preserve original indentation 2183338ef93Speter klausler for (; column < position->column; ++column) { 2193338ef93Speter klausler out << ' '; 2203338ef93Speter klausler } 2213338ef93Speter klausler } 222889c7c8eSDaniil Dudkin out << getOriginalChar(ch); 223889c7c8eSDaniil Dudkin lineWasBlankBefore = ch == ' ' && lineWasBlankBefore; 2243338ef93Speter klausler ++column; 2253338ef93Speter klausler } 2263338ef93Speter klausler } 2273338ef93Speter klausler } 2283338ef93Speter klausler 2298670e499SCaroline Concatto void Parsing::DumpCookedChars(llvm::raw_ostream &out) const { 23092a54197Speter klausler UserState userState{allCooked_, common::LanguageFeatureControl{}}; 23192a54197Speter klausler ParseState parseState{cooked()}; 23264ab3302SCarolineConcatto parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState); 23364ab3302SCarolineConcatto while (std::optional<const char *> p{parseState.GetNextChar()}) { 23464ab3302SCarolineConcatto out << **p; 23564ab3302SCarolineConcatto } 23664ab3302SCarolineConcatto } 23764ab3302SCarolineConcatto 2388670e499SCaroline Concatto void Parsing::DumpProvenance(llvm::raw_ostream &out) const { 23992a54197Speter klausler allCooked_.Dump(out); 2408670e499SCaroline Concatto } 24164ab3302SCarolineConcatto 2428670e499SCaroline Concatto void Parsing::DumpParsingLog(llvm::raw_ostream &out) const { 24392a54197Speter klausler log_.Dump(out, allCooked_); 24464ab3302SCarolineConcatto } 24564ab3302SCarolineConcatto 2468670e499SCaroline Concatto void Parsing::Parse(llvm::raw_ostream &out) { 24792a54197Speter klausler UserState userState{allCooked_, options_.features}; 24864ab3302SCarolineConcatto userState.set_debugOutput(out) 24964ab3302SCarolineConcatto .set_instrumentedParse(options_.instrumentedParse) 25064ab3302SCarolineConcatto .set_log(&log_); 25192a54197Speter klausler ParseState parseState{cooked()}; 25264ab3302SCarolineConcatto parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState); 25364ab3302SCarolineConcatto parseTree_ = program.Parse(parseState); 25464ab3302SCarolineConcatto CHECK( 25564ab3302SCarolineConcatto !parseState.anyErrorRecovery() || parseState.messages().AnyFatalError()); 25664ab3302SCarolineConcatto consumedWholeFile_ = parseState.IsAtEnd(); 25764ab3302SCarolineConcatto messages_.Annex(std::move(parseState.messages())); 25864ab3302SCarolineConcatto finalRestingPlace_ = parseState.GetLocation(); 25964ab3302SCarolineConcatto } 26064ab3302SCarolineConcatto 26164ab3302SCarolineConcatto void Parsing::ClearLog() { log_.clear(); } 26264ab3302SCarolineConcatto 2631f879005STim Keith } // namespace Fortran::parser 264