forked from OSchip/llvm-project
[flang] f18-parse-demo
Original-commit: flang-compiler/f18@96d9aefc4f Reviewed-on: https://github.com/flang-compiler/f18/pull/330 Tree-same-pre-rewrite: false
This commit is contained in:
parent
4b6ccbdd2c
commit
8492b7f2b9
|
@ -29,3 +29,12 @@ target_link_libraries(f18
|
|||
${FORTRAN_FIR_LIB}
|
||||
${LLVM_COMMON_LIBS}
|
||||
)
|
||||
|
||||
add_executable(f18-parse-demo
|
||||
f18-parse-demo.cc
|
||||
)
|
||||
|
||||
target_link_libraries(f18-parse-demo
|
||||
FortranParser
|
||||
FortranSemantics
|
||||
)
|
||||
|
|
|
@ -0,0 +1,476 @@
|
|||
// Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// F18 parsing demonstration.
|
||||
// f18-parse-demo [ -E | -fdump-parse-tree | -funparse-only ]
|
||||
// foo.{f,F,f77,F77,f90,F90,&c.}
|
||||
//
|
||||
// By default, runs the supplied source files through the F18 preprocessing and
|
||||
// parsing phases, reconstitutes a Fortran program from the parse tree, and
|
||||
// passes that Fortran program to a Fortran compiler identified by the $F18_FC
|
||||
// environment variable (defaulting to gfortran). The Fortran preprocessor is
|
||||
// always run, whatever the case of the source file extension. Unrecognized
|
||||
// options are passed through to the underlying Fortran compiler.
|
||||
//
|
||||
// This program is actually a stripped-down variant of f18.cc, a temporary
|
||||
// scaffolding compiler driver that can test some semantic passes of the
|
||||
// F18 compiler under development.
|
||||
|
||||
#include "../../lib/common/default-kinds.h"
|
||||
#include "../../lib/parser/characters.h"
|
||||
#include "../../lib/parser/features.h"
|
||||
#include "../../lib/parser/message.h"
|
||||
#include "../../lib/parser/parse-tree-visitor.h"
|
||||
#include "../../lib/parser/parse-tree.h"
|
||||
#include "../../lib/parser/parsing.h"
|
||||
#include "../../lib/parser/provenance.h"
|
||||
#include "../../lib/parser/unparse.h"
|
||||
#include "../../lib/semantics/dump-parse-tree.h"
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
static std::list<std::string> argList(int argc, char *const argv[]) {
|
||||
std::list<std::string> result;
|
||||
for (int j = 0; j < argc; ++j) {
|
||||
result.emplace_back(argv[j]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> filesToDelete;
|
||||
|
||||
void CleanUpAtExit() {
|
||||
for (const auto &path : filesToDelete) {
|
||||
if (!path.empty()) {
|
||||
unlink(path.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double CPUseconds() {
|
||||
struct timespec tspec;
|
||||
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tspec);
|
||||
return tspec.tv_nsec * 1.0e-9 + tspec.tv_sec;
|
||||
}
|
||||
|
||||
struct DriverOptions {
|
||||
DriverOptions() {}
|
||||
bool verbose{false}; // -v
|
||||
bool compileOnly{false}; // -c
|
||||
std::string outputPath; // -o path
|
||||
std::vector<std::string> searchDirectories{"."s}; // -I dir
|
||||
std::string moduleDirectory{"."s}; // -module dir
|
||||
bool forcedForm{false}; // -Mfixed or -Mfree appeared
|
||||
bool warnOnNonstandardUsage{false}; // -Mstandard
|
||||
bool warningsAreErrors{false}; // -Werror
|
||||
Fortran::parser::Encoding encoding{Fortran::parser::Encoding::UTF8};
|
||||
bool parseOnly{false};
|
||||
bool dumpProvenance{false};
|
||||
bool dumpCookedChars{false};
|
||||
bool dumpUnparse{false};
|
||||
bool dumpUnparseWithSymbols{false};
|
||||
bool dumpParseTree{false};
|
||||
bool timeParse{false};
|
||||
std::vector<std::string> fcArgs;
|
||||
const char *prefix{nullptr};
|
||||
};
|
||||
|
||||
bool ParentProcess() {
|
||||
if (fork() == 0) {
|
||||
return false; // in child process
|
||||
}
|
||||
int childStat{0};
|
||||
wait(&childStat);
|
||||
if (!WIFEXITED(childStat) || WEXITSTATUS(childStat) != 0) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Exec(std::vector<char *> &argv, bool verbose = false) {
|
||||
if (verbose) {
|
||||
for (size_t j{0}; j < argv.size(); ++j) {
|
||||
std::cerr << (j > 0 ? " " : "") << argv[j];
|
||||
}
|
||||
std::cerr << '\n';
|
||||
}
|
||||
argv.push_back(nullptr);
|
||||
execvp(argv[0], &argv[0]);
|
||||
std::cerr << "execvp(" << argv[0] << ") failed: " << std::strerror(errno)
|
||||
<< '\n';
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void RunOtherCompiler(DriverOptions &driver, char *source, char *relo) {
|
||||
std::vector<char *> argv;
|
||||
for (size_t j{0}; j < driver.fcArgs.size(); ++j) {
|
||||
argv.push_back(driver.fcArgs[j].data());
|
||||
}
|
||||
char dashC[3] = "-c", dashO[3] = "-o";
|
||||
argv.push_back(dashC);
|
||||
argv.push_back(dashO);
|
||||
argv.push_back(relo);
|
||||
argv.push_back(source);
|
||||
Exec(argv, driver.verbose);
|
||||
}
|
||||
|
||||
std::string RelocatableName(const DriverOptions &driver, std::string path) {
|
||||
if (driver.compileOnly && !driver.outputPath.empty()) {
|
||||
return driver.outputPath;
|
||||
}
|
||||
std::string base{path};
|
||||
auto slash{base.rfind("/")};
|
||||
if (slash != std::string::npos) {
|
||||
base = base.substr(slash + 1);
|
||||
}
|
||||
std::string relo{base};
|
||||
auto dot{base.rfind(".")};
|
||||
if (dot != std::string::npos) {
|
||||
relo = base.substr(0, dot);
|
||||
}
|
||||
relo += ".o";
|
||||
return relo;
|
||||
}
|
||||
|
||||
int exitStatus{EXIT_SUCCESS};
|
||||
|
||||
std::string CompileFortran(
|
||||
std::string path, Fortran::parser::Options options, DriverOptions &driver) {
|
||||
if (!driver.forcedForm) {
|
||||
auto dot{path.rfind(".")};
|
||||
if (dot != std::string::npos) {
|
||||
std::string suffix{path.substr(dot + 1)};
|
||||
options.isFixedForm = suffix == "f" || suffix == "F" || suffix == "ff";
|
||||
}
|
||||
}
|
||||
options.searchDirectories = driver.searchDirectories;
|
||||
Fortran::parser::Parsing parsing;
|
||||
|
||||
auto start{CPUseconds()};
|
||||
parsing.Prescan(path, options);
|
||||
if (!parsing.messages().empty() &&
|
||||
(driver.warningsAreErrors || parsing.messages().AnyFatalError())) {
|
||||
std::cerr << driver.prefix << "could not scan " << path << '\n';
|
||||
parsing.messages().Emit(std::cerr, parsing.cooked());
|
||||
exitStatus = EXIT_FAILURE;
|
||||
return {};
|
||||
}
|
||||
if (driver.dumpProvenance) {
|
||||
parsing.DumpProvenance(std::cout);
|
||||
return {};
|
||||
}
|
||||
if (driver.dumpCookedChars) {
|
||||
parsing.DumpCookedChars(std::cout);
|
||||
return {};
|
||||
}
|
||||
parsing.Parse(&std::cout);
|
||||
auto stop{CPUseconds()};
|
||||
if (driver.timeParse) {
|
||||
std::cout << "parse time for " << path << ": " << (stop - start)
|
||||
<< " CPU seconds\n";
|
||||
}
|
||||
|
||||
parsing.ClearLog();
|
||||
parsing.messages().Emit(std::cerr, parsing.cooked());
|
||||
if (!parsing.consumedWholeFile()) {
|
||||
parsing.EmitMessage(
|
||||
std::cerr, parsing.finalRestingPlace(), "parser FAIL (final position)");
|
||||
exitStatus = EXIT_FAILURE;
|
||||
return {};
|
||||
}
|
||||
if ((!parsing.messages().empty() &&
|
||||
(driver.warningsAreErrors || parsing.messages().AnyFatalError())) ||
|
||||
!parsing.parseTree().has_value()) {
|
||||
std::cerr << driver.prefix << "could not parse " << path << '\n';
|
||||
exitStatus = EXIT_FAILURE;
|
||||
return {};
|
||||
}
|
||||
auto &parseTree{*parsing.parseTree()};
|
||||
if (driver.dumpParseTree) {
|
||||
Fortran::semantics::DumpTree(std::cout, parseTree);
|
||||
return {};
|
||||
}
|
||||
if (driver.dumpUnparse) {
|
||||
Unparse(std::cout, parseTree, driver.encoding, true /*capitalize*/,
|
||||
options.features.IsEnabled(
|
||||
Fortran::parser::LanguageFeature::BackslashEscapes));
|
||||
return {};
|
||||
}
|
||||
if (driver.parseOnly) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string relo{RelocatableName(driver, path)};
|
||||
|
||||
char tmpSourcePath[32];
|
||||
std::snprintf(tmpSourcePath, sizeof tmpSourcePath, "/tmp/f18-%lx.f90",
|
||||
static_cast<unsigned long>(getpid()));
|
||||
{
|
||||
std::ofstream tmpSource;
|
||||
tmpSource.open(tmpSourcePath);
|
||||
Unparse(tmpSource, parseTree, driver.encoding, true /*capitalize*/,
|
||||
options.features.IsEnabled(
|
||||
Fortran::parser::LanguageFeature::BackslashEscapes));
|
||||
}
|
||||
|
||||
if (ParentProcess()) {
|
||||
filesToDelete.push_back(tmpSourcePath);
|
||||
if (!driver.compileOnly && driver.outputPath.empty()) {
|
||||
filesToDelete.push_back(relo);
|
||||
}
|
||||
return relo;
|
||||
}
|
||||
RunOtherCompiler(driver, tmpSourcePath, relo.data());
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string CompileOtherLanguage(std::string path, DriverOptions &driver) {
|
||||
std::string relo{RelocatableName(driver, path)};
|
||||
if (ParentProcess()) {
|
||||
if (!driver.compileOnly && driver.outputPath.empty()) {
|
||||
filesToDelete.push_back(relo);
|
||||
}
|
||||
return relo;
|
||||
}
|
||||
RunOtherCompiler(driver, path.data(), relo.data());
|
||||
return {};
|
||||
}
|
||||
|
||||
void Link(std::vector<std::string> &relocatables, DriverOptions &driver) {
|
||||
if (!ParentProcess()) {
|
||||
std::vector<char *> argv;
|
||||
for (size_t j{0}; j < driver.fcArgs.size(); ++j) {
|
||||
argv.push_back(driver.fcArgs[j].data());
|
||||
}
|
||||
for (auto &relo : relocatables) {
|
||||
argv.push_back(relo.data());
|
||||
}
|
||||
if (!driver.outputPath.empty()) {
|
||||
char dashO[3] = "-o";
|
||||
argv.push_back(dashO);
|
||||
argv.push_back(driver.outputPath.data());
|
||||
}
|
||||
Exec(argv, driver.verbose);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *const argv[]) {
|
||||
|
||||
atexit(CleanUpAtExit);
|
||||
|
||||
DriverOptions driver;
|
||||
const char *fc{getenv("F18_FC")};
|
||||
driver.fcArgs.push_back(fc ? fc : "gfortran");
|
||||
|
||||
std::list<std::string> args{argList(argc, argv)};
|
||||
std::string prefix{args.front()};
|
||||
args.pop_front();
|
||||
prefix += ": ";
|
||||
driver.prefix = prefix.data();
|
||||
|
||||
Fortran::parser::Options options;
|
||||
options.predefinitions.emplace_back("__F18", "1");
|
||||
options.predefinitions.emplace_back("__F18_MAJOR__", "1");
|
||||
options.predefinitions.emplace_back("__F18_MINOR__", "1");
|
||||
options.predefinitions.emplace_back("__F18_PATCHLEVEL__", "1");
|
||||
|
||||
options.features.Enable(
|
||||
Fortran::parser::LanguageFeature::BackslashEscapes, true);
|
||||
|
||||
Fortran::common::IntrinsicTypeDefaultKinds defaultKinds;
|
||||
|
||||
std::vector<std::string> fortranSources, otherSources, relocatables;
|
||||
bool anyFiles{false};
|
||||
|
||||
while (!args.empty()) {
|
||||
std::string arg{std::move(args.front())};
|
||||
args.pop_front();
|
||||
if (arg.empty()) {
|
||||
} else if (arg.at(0) != '-') {
|
||||
anyFiles = true;
|
||||
auto dot{arg.rfind(".")};
|
||||
if (dot == std::string::npos) {
|
||||
driver.fcArgs.push_back(arg);
|
||||
} else {
|
||||
std::string suffix{arg.substr(dot + 1)};
|
||||
if (suffix == "f" || suffix == "F" || suffix == "ff" ||
|
||||
suffix == "f90" || suffix == "F90" || suffix == "ff90" ||
|
||||
suffix == "f95" || suffix == "F95" || suffix == "ff95" ||
|
||||
suffix == "cuf" || suffix == "CUF" || suffix == "f18" ||
|
||||
suffix == "F18" || suffix == "ff18") {
|
||||
fortranSources.push_back(arg);
|
||||
} else if (suffix == "o" || suffix == "a") {
|
||||
relocatables.push_back(arg);
|
||||
} else {
|
||||
otherSources.push_back(arg);
|
||||
}
|
||||
}
|
||||
} else if (arg == "-") {
|
||||
fortranSources.push_back("-");
|
||||
} else if (arg == "--") {
|
||||
while (!args.empty()) {
|
||||
fortranSources.emplace_back(std::move(args.front()));
|
||||
args.pop_front();
|
||||
}
|
||||
break;
|
||||
} else if (arg == "-Mfixed") {
|
||||
driver.forcedForm = true;
|
||||
options.isFixedForm = true;
|
||||
} else if (arg == "-Mfree") {
|
||||
driver.forcedForm = true;
|
||||
options.isFixedForm = false;
|
||||
} else if (arg == "-Mextend") {
|
||||
options.fixedFormColumns = 132;
|
||||
} else if (arg == "-Mbackslash") {
|
||||
options.features.Enable(
|
||||
Fortran::parser::LanguageFeature::BackslashEscapes, false);
|
||||
} else if (arg == "-Mnobackslash") {
|
||||
options.features.Enable(
|
||||
Fortran::parser::LanguageFeature::BackslashEscapes);
|
||||
} else if (arg == "-Mstandard") {
|
||||
driver.warnOnNonstandardUsage = true;
|
||||
} else if (arg == "-fopenmp") {
|
||||
options.features.Enable(Fortran::parser::LanguageFeature::OpenMP);
|
||||
options.predefinitions.emplace_back("_OPENMP", "201511");
|
||||
} else if (arg == "-Werror") {
|
||||
driver.warningsAreErrors = true;
|
||||
} else if (arg == "-ed") {
|
||||
options.features.Enable(Fortran::parser::LanguageFeature::OldDebugLines);
|
||||
} else if (arg == "-E" || arg == "-fpreprocess-only") {
|
||||
driver.dumpCookedChars = true;
|
||||
} else if (arg == "-fbackslash") {
|
||||
options.features.Enable(
|
||||
Fortran::parser::LanguageFeature::BackslashEscapes);
|
||||
} else if (arg == "-fno-backslash") {
|
||||
options.features.Enable(
|
||||
Fortran::parser::LanguageFeature::BackslashEscapes, false);
|
||||
} else if (arg == "-fdump-provenance") {
|
||||
driver.dumpProvenance = true;
|
||||
} else if (arg == "-fdump-parse-tree") {
|
||||
driver.dumpParseTree = true;
|
||||
} else if (arg == "-funparse") {
|
||||
driver.dumpUnparse = true;
|
||||
} else if (arg == "-ftime-parse") {
|
||||
driver.timeParse = true;
|
||||
} else if (arg == "-fparse-only") {
|
||||
driver.parseOnly = true;
|
||||
} else if (arg == "-c") {
|
||||
driver.compileOnly = true;
|
||||
} else if (arg == "-o") {
|
||||
driver.outputPath = args.front();
|
||||
args.pop_front();
|
||||
} else if (arg.substr(0, 2) == "-D") {
|
||||
auto eq{arg.find('=')};
|
||||
if (eq == std::string::npos) {
|
||||
options.predefinitions.emplace_back(arg.substr(2), "1");
|
||||
} else {
|
||||
options.predefinitions.emplace_back(
|
||||
arg.substr(2, eq - 2), arg.substr(eq + 1));
|
||||
}
|
||||
} else if (arg.substr(0, 2) == "-U") {
|
||||
options.predefinitions.emplace_back(
|
||||
arg.substr(2), std::optional<std::string>{});
|
||||
} else if (arg == "-r8" || arg == "-fdefault-real-8") {
|
||||
defaultKinds.set_defaultRealKind(8);
|
||||
} else if (arg == "-i8" || arg == "-fdefault-integer-8") {
|
||||
defaultKinds.set_defaultIntegerKind(8);
|
||||
} else if (arg == "-fno-large-arrays") {
|
||||
defaultKinds.set_subscriptIntegerKind(4);
|
||||
} else if (arg == "-help" || arg == "--help" || arg == "-?") {
|
||||
std::cerr
|
||||
<< "f18-parse-demo options:\n"
|
||||
<< " -Mfixed | -Mfree force the source form\n"
|
||||
<< " -Mextend 132-column fixed form\n"
|
||||
<< " -f[no-]backslash enable[disable] \\escapes in literals\n"
|
||||
<< " -M[no]backslash disable[enable] \\escapes in literals\n"
|
||||
<< " -Mstandard enable conformance warnings\n"
|
||||
<< " -r8 | -fdefault-real-8 | -i8 | -fdefault-integer-8 "
|
||||
"change default kinds of intrinsic types\n"
|
||||
<< " -Werror treat warnings as errors\n"
|
||||
<< " -ed enable fixed form D lines\n"
|
||||
<< " -E prescan & preprocess only\n"
|
||||
<< " -ftime-parse measure parsing time\n"
|
||||
<< " -fparse-only parse only, no output except messages\n"
|
||||
<< " -funparse parse & reformat only, no code "
|
||||
"generation\n"
|
||||
<< " -fdump-provenance dump the provenance table (no code)\n"
|
||||
<< " -fdump-parse-tree dump the parse tree (no code)\n"
|
||||
<< " -v -c -o -I -D -U have their usual meanings\n"
|
||||
<< " -help print this again\n"
|
||||
<< "Other options are passed through to the $F18_FC compiler.\n";
|
||||
return exitStatus;
|
||||
} else if (arg == "-V") {
|
||||
std::cerr << "\nf18-parse-demo\n";
|
||||
return exitStatus;
|
||||
} else {
|
||||
driver.fcArgs.push_back(arg);
|
||||
if (arg == "-v") {
|
||||
driver.verbose = true;
|
||||
} else if (arg == "-I") {
|
||||
driver.fcArgs.push_back(args.front());
|
||||
driver.searchDirectories.push_back(args.front());
|
||||
args.pop_front();
|
||||
} else if (arg.substr(0, 2) == "-I") {
|
||||
driver.searchDirectories.push_back(arg.substr(2));
|
||||
} else if (arg == "-module") {
|
||||
driver.moduleDirectory = args.front();
|
||||
driver.fcArgs.push_back(driver.moduleDirectory);
|
||||
args.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
driver.encoding = options.encoding;
|
||||
|
||||
if (driver.warnOnNonstandardUsage) {
|
||||
options.features.WarnOnAllNonstandard();
|
||||
}
|
||||
if (!options.features.IsEnabled(
|
||||
Fortran::parser::LanguageFeature::BackslashEscapes)) {
|
||||
driver.fcArgs.push_back("-fno-backslash"); // PGI "-Mbackslash"
|
||||
}
|
||||
|
||||
if (!anyFiles) {
|
||||
driver.dumpUnparse = true;
|
||||
CompileFortran("-", options, driver);
|
||||
return exitStatus;
|
||||
}
|
||||
for (const auto &path : fortranSources) {
|
||||
std::string relo{CompileFortran(path, options, driver)};
|
||||
if (!driver.compileOnly && !relo.empty()) {
|
||||
relocatables.push_back(relo);
|
||||
}
|
||||
}
|
||||
for (const auto &path : otherSources) {
|
||||
std::string relo{CompileOtherLanguage(path, driver)};
|
||||
if (!driver.compileOnly && !relo.empty()) {
|
||||
relocatables.push_back(relo);
|
||||
}
|
||||
}
|
||||
if (!relocatables.empty()) {
|
||||
Link(relocatables, driver);
|
||||
}
|
||||
return exitStatus;
|
||||
}
|
Loading…
Reference in New Issue