2020-02-25 23:11:52 +08:00
|
|
|
//===-- lib/Parser/parsing.cpp --------------------------------------------===//
|
2018-05-02 03:50:34 +08:00
|
|
|
//
|
2019-12-21 04:52:07 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2018-05-02 03:50:34 +08:00
|
|
|
//
|
2020-01-11 04:12:03 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2018-05-02 03:50:34 +08:00
|
|
|
|
2020-02-25 23:11:52 +08:00
|
|
|
#include "flang/Parser/parsing.h"
|
2018-03-01 08:56:10 +08:00
|
|
|
#include "preprocessor.h"
|
|
|
|
#include "prescan.h"
|
2019-12-10 03:09:44 +08:00
|
|
|
#include "type-parsers.h"
|
2020-02-25 23:11:52 +08:00
|
|
|
#include "flang/Parser/message.h"
|
|
|
|
#include "flang/Parser/provenance.h"
|
|
|
|
#include "flang/Parser/source.h"
|
2020-02-28 23:11:03 +08:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2018-03-01 08:56:10 +08:00
|
|
|
|
2018-05-03 04:48:12 +08:00
|
|
|
namespace Fortran::parser {
|
2018-03-01 08:56:10 +08:00
|
|
|
|
2020-09-01 03:22:24 +08:00
|
|
|
Parsing::Parsing(AllCookedSources &allCooked) : allCooked_{allCooked} {}
|
2018-07-28 05:58:14 +08:00
|
|
|
Parsing::~Parsing() {}
|
|
|
|
|
2019-09-24 08:10:58 +08:00
|
|
|
const SourceFile *Parsing::Prescan(const std::string &path, Options options) {
|
2018-03-01 08:56:10 +08:00
|
|
|
options_ = options;
|
2020-09-01 03:22:24 +08:00
|
|
|
AllSources &allSources{allCooked_.allSources()};
|
2022-01-27 01:54:58 +08:00
|
|
|
allSources.ClearSearchPath();
|
2019-09-24 08:10:58 +08:00
|
|
|
if (options.isModuleFile) {
|
|
|
|
for (const auto &path : options.searchDirectories) {
|
2021-01-27 05:57:44 +08:00
|
|
|
allSources.AppendSearchPathDirectory(path);
|
2019-09-24 08:10:58 +08:00
|
|
|
}
|
|
|
|
}
|
2018-03-01 08:56:10 +08:00
|
|
|
|
2020-02-28 23:11:03 +08:00
|
|
|
std::string buf;
|
|
|
|
llvm::raw_string_ostream fileError{buf};
|
2018-04-07 01:34:59 +08:00
|
|
|
const SourceFile *sourceFile;
|
|
|
|
if (path == "-") {
|
2020-02-28 23:11:03 +08:00
|
|
|
sourceFile = allSources.ReadStandardInput(fileError);
|
2022-05-31 03:47:32 +08:00
|
|
|
} else if (options.isModuleFile) {
|
|
|
|
// Don't mess with intrinsic module search path
|
|
|
|
sourceFile = allSources.Open(path, fileError);
|
2018-04-07 01:34:59 +08:00
|
|
|
} else {
|
2022-05-31 03:47:32 +08:00
|
|
|
sourceFile =
|
|
|
|
allSources.Open(path, fileError, "."s /*prepend to search path*/);
|
2018-04-07 01:34:59 +08:00
|
|
|
}
|
2019-09-24 08:10:58 +08:00
|
|
|
if (!fileError.str().empty()) {
|
2018-07-28 02:44:31 +08:00
|
|
|
ProvenanceRange range{allSources.AddCompilerInsertion(path)};
|
2019-05-07 00:33:45 +08:00
|
|
|
messages_.Say(range, "%s"_err_en_US, fileError.str());
|
2019-09-24 08:10:58 +08:00
|
|
|
return sourceFile;
|
2018-03-01 08:56:10 +08:00
|
|
|
}
|
2019-12-04 01:49:44 +08:00
|
|
|
CHECK(sourceFile);
|
2018-03-01 08:56:10 +08:00
|
|
|
|
2019-09-24 08:10:58 +08:00
|
|
|
if (!options.isModuleFile) {
|
|
|
|
// For .mod files we always want to look in the search directories.
|
2021-01-27 05:57:44 +08:00
|
|
|
// For normal source files we don't add them until after the primary
|
2019-09-24 08:10:58 +08:00
|
|
|
// source file has been opened. If foo.f is missing from the current
|
|
|
|
// working directory, we don't want to accidentally read another foo.f
|
|
|
|
// from another directory that's on the search path.
|
|
|
|
for (const auto &path : options.searchDirectories) {
|
2021-01-27 05:57:44 +08:00
|
|
|
allSources.AppendSearchPathDirectory(path);
|
2019-09-24 08:10:58 +08:00
|
|
|
}
|
2018-03-17 07:13:49 +08:00
|
|
|
}
|
|
|
|
|
2018-07-28 02:44:31 +08:00
|
|
|
Preprocessor preprocessor{allSources};
|
2021-02-11 08:15:20 +08:00
|
|
|
if (!options.predefinitions.empty()) {
|
|
|
|
preprocessor.DefineStandardMacros();
|
|
|
|
for (const auto &predef : options.predefinitions) {
|
|
|
|
if (predef.second) {
|
|
|
|
preprocessor.Define(predef.first, *predef.second);
|
|
|
|
} else {
|
|
|
|
preprocessor.Undefine(predef.first);
|
|
|
|
}
|
2018-03-17 07:13:49 +08:00
|
|
|
}
|
|
|
|
}
|
2020-09-01 03:22:24 +08:00
|
|
|
currentCooked_ = &allCooked_.NewCookedSource();
|
|
|
|
Prescanner prescanner{
|
2020-09-18 03:19:42 +08:00
|
|
|
messages_, *currentCooked_, preprocessor, options.features};
|
2018-03-01 08:56:10 +08:00
|
|
|
prescanner.set_fixedForm(options.isFixedForm)
|
|
|
|
.set_fixedFormColumnLimit(options.fixedFormColumns)
|
2018-03-24 06:14:52 +08:00
|
|
|
.AddCompilerDirectiveSentinel("dir$");
|
[flang][openacc] OpenACC 3.0 parser
Summary:
This patch introduce the parser for OpenACC 3.0 in Flang. It uses the same TableGen mechanism
than OpenMP.
Reviewers: nvdatian, sscalpone, tskeith, klausler, ichoyjx, jdoerfert, DavidTruby
Reviewed By: klausler
Subscribers: MaskRay, SouraVX, mgorny, hiraditya, jfb, sstefan1, llvm-commits
Tags: #llvm, #flang
Differential Revision: https://reviews.llvm.org/D83649
2020-07-15 02:28:34 +08:00
|
|
|
if (options.features.IsEnabled(LanguageFeature::OpenACC)) {
|
|
|
|
prescanner.AddCompilerDirectiveSentinel("$acc");
|
|
|
|
}
|
2018-07-19 02:19:21 +08:00
|
|
|
if (options.features.IsEnabled(LanguageFeature::OpenMP)) {
|
2018-05-16 22:28:54 +08:00
|
|
|
prescanner.AddCompilerDirectiveSentinel("$omp");
|
2020-03-29 12:00:16 +08:00
|
|
|
prescanner.AddCompilerDirectiveSentinel("$"); // OMP conditional line
|
2018-05-16 22:28:54 +08:00
|
|
|
}
|
2018-07-28 06:18:36 +08:00
|
|
|
ProvenanceRange range{allSources.AddIncludedFile(
|
|
|
|
*sourceFile, ProvenanceRange{}, options.isModuleFile)};
|
2018-04-03 06:51:04 +08:00
|
|
|
prescanner.Prescan(range);
|
2020-09-01 03:22:24 +08:00
|
|
|
if (currentCooked_->BufferedBytes() == 0 && !options.isModuleFile) {
|
2019-12-07 01:37:07 +08:00
|
|
|
// Input is empty. Append a newline so that any warning
|
|
|
|
// message about nonstandard usage will have provenance.
|
2020-09-01 03:22:24 +08:00
|
|
|
currentCooked_->Put('\n', range.start());
|
2019-12-07 01:37:07 +08:00
|
|
|
}
|
2021-03-17 05:32:45 +08:00
|
|
|
currentCooked_->Marshal(allCooked_);
|
2019-09-07 04:00:47 +08:00
|
|
|
if (options.needProvenanceRangeToCharBlockMappings) {
|
2020-09-01 03:22:24 +08:00
|
|
|
currentCooked_->CompileProvenanceRangeToOffsetMappings(allSources);
|
2019-09-07 04:00:47 +08:00
|
|
|
}
|
2019-09-24 08:10:58 +08:00
|
|
|
return sourceFile;
|
2018-03-01 08:56:10 +08:00
|
|
|
}
|
|
|
|
|
2021-07-24 07:41:04 +08:00
|
|
|
void Parsing::EmitPreprocessedSource(
|
|
|
|
llvm::raw_ostream &out, bool lineDirectives) const {
|
|
|
|
const SourceFile *sourceFile{nullptr};
|
|
|
|
int sourceLine{0};
|
|
|
|
int column{1};
|
|
|
|
bool inDirective{false};
|
|
|
|
bool inContinuation{false};
|
2022-05-27 18:45:15 +08:00
|
|
|
bool lineWasBlankBefore{true};
|
2021-07-24 07:41:04 +08:00
|
|
|
const AllSources &allSources{allCooked().allSources()};
|
2022-05-27 18:45:15 +08:00
|
|
|
// All directives that flang support are known to have a length of 3 chars
|
|
|
|
constexpr int directiveNameLength{3};
|
|
|
|
// We need to know the current directive in order to provide correct
|
|
|
|
// continuation for the directive
|
|
|
|
std::string directive;
|
2021-07-24 07:41:04 +08:00
|
|
|
for (const char &atChar : cooked().AsCharBlock()) {
|
|
|
|
char ch{atChar};
|
|
|
|
if (ch == '\n') {
|
|
|
|
out << '\n'; // TODO: DOS CR-LF line ending if necessary
|
|
|
|
column = 1;
|
|
|
|
inDirective = false;
|
|
|
|
inContinuation = false;
|
2022-05-27 18:45:15 +08:00
|
|
|
lineWasBlankBefore = true;
|
2021-07-24 07:41:04 +08:00
|
|
|
++sourceLine;
|
2022-05-27 18:45:15 +08:00
|
|
|
directive.clear();
|
2021-07-24 07:41:04 +08:00
|
|
|
} else {
|
2022-05-27 18:45:15 +08:00
|
|
|
auto provenance{cooked().GetProvenanceRange(CharBlock{&atChar, 1})};
|
|
|
|
|
|
|
|
// Preserves original case of the character
|
|
|
|
const auto getOriginalChar{[&](char ch) {
|
|
|
|
if (IsLetter(ch) && provenance && provenance->size() == 1) {
|
|
|
|
if (const char *orig{allSources.GetSource(*provenance)}) {
|
|
|
|
const char upper{ToUpperCaseLetter(ch)};
|
|
|
|
if (*orig == upper) {
|
|
|
|
return upper;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ch;
|
|
|
|
}};
|
|
|
|
|
|
|
|
if (ch == '!' && lineWasBlankBefore) {
|
2021-07-24 07:41:04 +08:00
|
|
|
// Other comment markers (C, *, D) in original fixed form source
|
|
|
|
// input card column 1 will have been deleted or normalized to !,
|
|
|
|
// which signifies a comment (directive) in both source forms.
|
|
|
|
inDirective = true;
|
|
|
|
}
|
2022-05-27 18:45:15 +08:00
|
|
|
if (inDirective && directive.size() < directiveNameLength &&
|
|
|
|
IsLetter(ch)) {
|
|
|
|
directive += getOriginalChar(ch);
|
|
|
|
}
|
|
|
|
|
2021-07-24 07:41:04 +08:00
|
|
|
std::optional<SourcePosition> position{provenance
|
|
|
|
? allSources.GetSourcePosition(provenance->start())
|
|
|
|
: std::nullopt};
|
|
|
|
if (lineDirectives && column == 1 && position) {
|
|
|
|
if (&position->file != sourceFile) {
|
|
|
|
out << "#line \"" << position->file.path() << "\" " << position->line
|
|
|
|
<< '\n';
|
|
|
|
} else if (position->line != sourceLine) {
|
|
|
|
if (sourceLine < position->line &&
|
|
|
|
sourceLine + 10 >= position->line) {
|
|
|
|
// Emit a few newlines to catch up when they'll likely
|
|
|
|
// require fewer bytes than a #line directive would have
|
|
|
|
// occupied.
|
|
|
|
while (sourceLine++ < position->line) {
|
|
|
|
out << '\n';
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
out << "#line " << position->line << '\n';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sourceFile = &position->file;
|
|
|
|
sourceLine = position->line;
|
|
|
|
}
|
|
|
|
if (column > 72) {
|
|
|
|
// Wrap long lines in a portable fashion that works in both
|
2022-05-27 18:45:15 +08:00
|
|
|
// of the Fortran source forms. The first free-form continuation
|
2021-07-24 07:41:04 +08:00
|
|
|
// marker ("&") lands in column 73, which begins the card commentary
|
|
|
|
// field of fixed form, and the second one is put in column 6,
|
|
|
|
// where it signifies fixed form line continuation.
|
|
|
|
// The standard Fortran fixed form column limit (72) is used
|
|
|
|
// for output, even if the input was parsed with a nonstandard
|
|
|
|
// column limit override option.
|
2022-05-27 18:45:15 +08:00
|
|
|
// OpenMP and OpenACC directives' continuations should have the
|
|
|
|
// corresponding sentinel at the next line.
|
|
|
|
const auto continuation{
|
|
|
|
inDirective ? "&\n!$" + directive + "&" : "&\n &"s};
|
|
|
|
out << continuation;
|
2021-07-24 07:41:04 +08:00
|
|
|
column = 7; // start of fixed form source field
|
|
|
|
++sourceLine;
|
|
|
|
inContinuation = true;
|
|
|
|
} else if (!inDirective && ch != ' ' && (ch < '0' || ch > '9')) {
|
|
|
|
// Put anything other than a label or directive into the
|
|
|
|
// Fortran fixed form source field (columns [7:72]).
|
|
|
|
for (; column < 7; ++column) {
|
|
|
|
out << ' ';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!inContinuation && position && position->column <= 72 && ch != ' ') {
|
|
|
|
// Preserve original indentation
|
|
|
|
for (; column < position->column; ++column) {
|
|
|
|
out << ' ';
|
|
|
|
}
|
|
|
|
}
|
2022-05-27 18:45:15 +08:00
|
|
|
out << getOriginalChar(ch);
|
|
|
|
lineWasBlankBefore = ch == ' ' && lineWasBlankBefore;
|
2021-07-24 07:41:04 +08:00
|
|
|
++column;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-28 23:11:03 +08:00
|
|
|
void Parsing::DumpCookedChars(llvm::raw_ostream &out) const {
|
2020-09-01 03:22:24 +08:00
|
|
|
UserState userState{allCooked_, common::LanguageFeatureControl{}};
|
|
|
|
ParseState parseState{cooked()};
|
2018-03-01 08:56:10 +08:00
|
|
|
parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState);
|
2018-04-03 07:33:10 +08:00
|
|
|
while (std::optional<const char *> p{parseState.GetNextChar()}) {
|
|
|
|
out << **p;
|
2018-03-01 08:56:10 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-28 23:11:03 +08:00
|
|
|
void Parsing::DumpProvenance(llvm::raw_ostream &out) const {
|
2020-09-01 03:22:24 +08:00
|
|
|
allCooked_.Dump(out);
|
2020-02-28 23:11:03 +08:00
|
|
|
}
|
2018-03-01 08:56:10 +08:00
|
|
|
|
2020-02-28 23:11:03 +08:00
|
|
|
void Parsing::DumpParsingLog(llvm::raw_ostream &out) const {
|
2020-09-01 03:22:24 +08:00
|
|
|
log_.Dump(out, allCooked_);
|
2018-04-20 06:46:02 +08:00
|
|
|
}
|
2018-04-20 04:03:23 +08:00
|
|
|
|
2020-02-28 23:11:03 +08:00
|
|
|
void Parsing::Parse(llvm::raw_ostream &out) {
|
2020-09-01 03:22:24 +08:00
|
|
|
UserState userState{allCooked_, options_.features};
|
2018-04-21 07:14:57 +08:00
|
|
|
userState.set_debugOutput(out)
|
|
|
|
.set_instrumentedParse(options_.instrumentedParse)
|
|
|
|
.set_log(&log_);
|
2020-09-01 03:22:24 +08:00
|
|
|
ParseState parseState{cooked()};
|
2019-06-13 06:26:37 +08:00
|
|
|
parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState);
|
2018-04-21 02:23:51 +08:00
|
|
|
parseTree_ = program.Parse(parseState);
|
2018-04-03 06:51:04 +08:00
|
|
|
CHECK(
|
2018-04-14 07:00:03 +08:00
|
|
|
!parseState.anyErrorRecovery() || parseState.messages().AnyFatalError());
|
2018-03-01 08:56:10 +08:00
|
|
|
consumedWholeFile_ = parseState.IsAtEnd();
|
2018-08-09 02:29:05 +08:00
|
|
|
messages_.Annex(std::move(parseState.messages()));
|
2018-04-18 06:47:51 +08:00
|
|
|
finalRestingPlace_ = parseState.GetLocation();
|
2018-04-03 06:51:04 +08:00
|
|
|
}
|
|
|
|
|
2018-04-20 08:02:12 +08:00
|
|
|
void Parsing::ClearLog() { log_.clear(); }
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
} // namespace Fortran::parser
|