[flang] f18-parse-demo
authorpeter klausler <pklausler@nvidia.com>
Wed, 13 Mar 2019 23:05:05 +0000 (16:05 -0700)
committerpeter klausler <pklausler@nvidia.com>
Thu, 14 Mar 2019 23:18:20 +0000 (16:18 -0700)
Original-commit: flang-compiler/f18@96d9aefc4f31898e104409d06b0d5d4d97007857
Reviewed-on: https://github.com/flang-compiler/f18/pull/330
Tree-same-pre-rewrite: false

flang/tools/f18/CMakeLists.txt
flang/tools/f18/f18-parse-demo.cc [new file with mode: 0644]

index e9aad66..c04cb47 100644 (file)
@@ -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
+)
diff --git a/flang/tools/f18/f18-parse-demo.cc b/flang/tools/f18/f18-parse-demo.cc
new file mode 100644 (file)
index 0000000..b39e316
--- /dev/null
@@ -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;
+}