xref: /llvm-project/flang/lib/Parser/parsing.cpp (revision 3c3094b60d3587b1db8ef35b3bf54e73ac5894d9)
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