#include "flang/Parser/parsing.h"
#include "prescan.h"
#include "type-parsers.h"
#include "flang/Parser/message.h"
#include "flang/Parser/preprocessor.h"
#include "flang/Parser/provenance.h"
#include "flang/Parser/source.h"
#include "llvm/Support/raw_ostream.h"
namespace Fortran::parser {
Parsing::Parsing(AllCookedSources &allCooked) : allCooked_{allCooked} {}
Parsing::~Parsing() {}
const SourceFile *Parsing::Prescan(const std::string &path, Options options) {
options_ = options;
AllSources &allSources{allCooked_.allSources()};
allSources.ClearSearchPath();
if (options.isModuleFile) {
for (const auto &path : options.searchDirectories) {
allSources.AppendSearchPathDirectory(path);
}
}
std::string buf;
llvm::raw_string_ostream fileError{buf};
const SourceFile *sourceFile{nullptr};
if (path == "-") {
sourceFile = allSources.ReadStandardInput(fileError);
} else if (options.isModuleFile) {
sourceFile = allSources.Open(path, fileError);
} else {
sourceFile =
allSources.Open(path, fileError, "."s );
}
if (!fileError.str().empty()) {
ProvenanceRange range{allSources.AddCompilerInsertion(path)};
messages_.Say(range, "%s"_err_en_US, fileError.str());
return sourceFile;
}
CHECK(sourceFile);
if (!options.isModuleFile) {
for (const auto &path : options.searchDirectories) {
allSources.AppendSearchPathDirectory(path);
}
}
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);
}
}
}
currentCooked_ = &allCooked_.NewCookedSource();
Prescanner prescanner{
messages_, *currentCooked_, preprocessor_, options.features};
prescanner.set_fixedForm(options.isFixedForm)
.set_fixedFormColumnLimit(options.fixedFormColumns)
.AddCompilerDirectiveSentinel("dir$");
if (options.features.IsEnabled(LanguageFeature::OpenACC)) {
prescanner.AddCompilerDirectiveSentinel("$acc");
}
if (options.features.IsEnabled(LanguageFeature::OpenMP)) {
prescanner.AddCompilerDirectiveSentinel("$omp");
prescanner.AddCompilerDirectiveSentinel("$");
}
if (options.features.IsEnabled(LanguageFeature::CUDA)) {
prescanner.AddCompilerDirectiveSentinel("$cuf");
prescanner.AddCompilerDirectiveSentinel("@cuf");
preprocessor_.Define("_CUDA", "1");
}
ProvenanceRange range{allSources.AddIncludedFile(
*sourceFile, ProvenanceRange{}, options.isModuleFile)};
prescanner.Prescan(range);
if (currentCooked_->BufferedBytes() == 0 && !options.isModuleFile) {
currentCooked_->Put('\n', range.start());
}
currentCooked_->Marshal(allCooked_);
if (options.needProvenanceRangeToCharBlockMappings) {
currentCooked_->CompileProvenanceRangeToOffsetMappings(allSources);
}
if (options.showColors) {
allSources.setShowColors(true);
}
return sourceFile;
}
void Parsing::EmitPreprocessorMacros(llvm::raw_ostream &out) const {
preprocessor_.PrintMacros(out);
}
void Parsing::EmitPreprocessedSource(
llvm::raw_ostream &out, bool lineDirectives) const {
const std::string *sourcePath{nullptr};
int sourceLine{0};
int column{1};
bool inDirective{false};
bool inContinuation{false};
bool lineWasBlankBefore{true};
const AllSources &allSources{allCooked().allSources()};
constexpr int directiveNameLength{3};
std::string directive;
for (const char &atChar : cooked().AsCharBlock()) {
char ch{atChar};
if (ch == '\n') {
out << '\n';
column = 1;
inDirective = false;
inContinuation = false;
lineWasBlankBefore = true;
++sourceLine;
directive.clear();
} else {
auto provenance{cooked().GetProvenanceRange(CharBlock{&atChar, 1})};
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) {
inDirective = true;
}
if (inDirective && directive.size() < directiveNameLength &&
IsLetter(ch)) {
directive += getOriginalChar(ch);
}
std::optional<SourcePosition> position{provenance
? allSources.GetSourcePosition(provenance->start())
: std::nullopt};
if (lineDirectives && column == 1 && position) {
if (&*position->path != sourcePath) {
out << "#line \"" << *position->path << "\" " << position->line
<< '\n';
} else if (position->line != sourceLine) {
if (sourceLine < position->line &&
sourceLine + 10 >= position->line) {
while (sourceLine++ < position->line) {
out << '\n';
}
} else {
out << "#line " << position->line << '\n';
}
}
sourcePath = &*position->path;
sourceLine = position->line;
}
if (column > 72) {
const auto continuation{
inDirective ? "&\n!$" + directive + "&" : "&\n &"s};
out << continuation;
column = 7;
++sourceLine;
inContinuation = true;
} else if (!inDirective && ch != ' ' && (ch < '0' || ch > '9')) {
for (; column < 7; ++column) {
out << ' ';
}
}
if (!inContinuation && position && position->column <= 72 && ch != ' ') {
for (; column < position->column; ++column) {
out << ' ';
}
}
out << getOriginalChar(ch);
lineWasBlankBefore = ch == ' ' && lineWasBlankBefore;
++column;
}
}
}
void Parsing::DumpCookedChars(llvm::raw_ostream &out) const {
UserState userState{allCooked_, common::LanguageFeatureControl{}};
ParseState parseState{cooked()};
parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState);
while (std::optional<const char *> p{parseState.GetNextChar()}) {
out << **p;
}
}
void Parsing::DumpProvenance(llvm::raw_ostream &out) const {
allCooked_.Dump(out);
}
void Parsing::DumpParsingLog(llvm::raw_ostream &out) const {
log_.Dump(out, allCooked_);
}
void Parsing::Parse(llvm::raw_ostream &out) {
UserState userState{allCooked_, options_.features};
userState.set_debugOutput(out)
.set_instrumentedParse(options_.instrumentedParse)
.set_log(&log_);
ParseState parseState{cooked()};
parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState);
parseTree_ = program.Parse(parseState);
CHECK(
!parseState.anyErrorRecovery() || parseState.messages().AnyFatalError());
consumedWholeFile_ = parseState.IsAtEnd();
messages_.Annex(std::move(parseState.messages()));
finalRestingPlace_ = parseState.GetLocation();
}
void Parsing::ClearLog() { log_.clear(); }
}