[flang] Implement Fortran INCLUDE lines.
authorpeter klausler <pklausler@nvidia.com>
Tue, 13 Feb 2018 22:22:08 +0000 (14:22 -0800)
committerGitHub <noreply@github.com>
Thu, 15 Feb 2018 23:58:44 +0000 (15:58 -0800)
Original-commit: flang-compiler/f18@d01af8950628b51baee28b5c9b8052b09f2b0dbf
Reviewed-on: https://github.com/flang-compiler/f18/pull/9
Tree-same-pre-rewrite: false

flang/lib/parser/prescan.cc
flang/lib/parser/prescan.h
flang/lib/parser/provenance.cc
flang/lib/parser/provenance.h
flang/tools/f18/f18.cc

index e015c71..5aaf39f 100644 (file)
@@ -6,6 +6,7 @@
 #include "token-sequence.h"
 #include <cctype>
 #include <cstring>
+#include <sstream>
 #include <utility>
 #include <vector>
 
@@ -16,6 +17,14 @@ Prescanner::Prescanner(
     Messages *messages, CookedSource *cooked, Preprocessor *preprocessor)
   : messages_{messages}, cooked_{cooked}, preprocessor_{preprocessor} {}
 
+Prescanner::Prescanner(const Prescanner &that)
+  : messages_{that.messages_}, cooked_{that.cooked_},
+    preprocessor_{that.preprocessor_}, inFixedForm_{that.inFixedForm_},
+    fixedFormColumnLimit_{that.fixedFormColumnLimit_},
+    enableOldDebugLines_{that.enableOldDebugLines_},
+    enableBackslashEscapesInCharLiterals_{
+        that.enableBackslashEscapesInCharLiterals_} {}
+
 bool Prescanner::Prescan(ProvenanceRange range) {
   startProvenance_ = range.start;
   ProvenanceRange around{
@@ -30,6 +39,7 @@ bool Prescanner::Prescan(ProvenanceRange range) {
   TokenSequence tokens, preprocessed;
   while (lineStart_ < limit_) {
     if (CommentLinesAndPreprocessorDirectives() && lineStart_ >= limit_) {
+      PayNewlineDebt();
       break;
     }
     BeginSourceLineAndAdvance();
@@ -56,9 +66,9 @@ bool Prescanner::Prescan(ProvenanceRange range) {
     }
     tokens.clear();
     cooked_->Put('\n', newlineProvenance_);
-    PayNewlineDebt(cooked_);
+    PayNewlineDebt();
   }
-  PayNewlineDebt(cooked_);
+  PayNewlineDebt();
   return !anyFatalErrors_;
 }
 
@@ -373,6 +383,62 @@ bool Prescanner::IsFreeFormComment(const char *p) {
   return *p == '!' || *p == '\n';
 }
 
+bool Prescanner::IncludeLine(const char *p) {
+  if (p >= limit_) {
+    return false;
+  }
+  const char *start{p};
+  while (*p == ' ' || *p == '\t') {
+    ++p;
+  }
+  for (char ch : "include"s) {
+    if (tolower(*p++) != ch) {
+      return false;
+    }
+  }
+  while (*p == ' ' || *p == '\t') {
+    ++p;
+  }
+  if (*p != '"' && *p != '\'') {
+    return false;
+  }
+  char quote{*p};
+  std::string path;
+  for (++p; *p != '\n'; ++p) {
+    if (*p == quote) {
+      if (p[1] != quote) {
+        break;
+      }
+      ++p;
+    }
+    path += *p;
+  }
+  if (*p != quote) {
+    messages_->Put({GetProvenance(p), "malformed path name string"});
+    anyFatalErrors_ = true;
+    return true;
+  }
+  for (++p; *p == ' ' || *p == '\t'; ++p) {
+  }
+  if (*p != '\n' && *p != '!') {
+    messages_->Put({GetProvenance(p), "excess characters after path name"});
+  }
+  std::stringstream error;
+  Provenance provenance{GetProvenance(start)};
+  AllSources *allSources{cooked_->allSources()};
+  const SourceFile *included{allSources->Open(path, &error)};
+  if (included == nullptr) {
+    messages_->Put({provenance, error.str()});
+    anyFatalErrors_ = true;
+    return true;
+  }
+  ProvenanceRange includeLineRange{provenance, static_cast<size_t>(p - start)};
+  ProvenanceRange fileRange{
+      allSources->AddIncludedFile(*included, includeLineRange)};
+  anyFatalErrors_ |= !Prescanner{*this}.Prescan(fileRange);
+  return true;
+}
+
 bool Prescanner::IsPreprocessorDirectiveLine(const char *start) {
   const char *p{start};
   if (p >= limit_ || inPreprocessorDirective_) {
@@ -405,7 +471,8 @@ bool Prescanner::CommentLines() {
 bool Prescanner::CommentLinesAndPreprocessorDirectives() {
   bool any{false};
   while (lineStart_ < limit_) {
-    if (IsFixedFormCommentLine(lineStart_) || IsFreeFormComment(lineStart_)) {
+    if (IsFixedFormCommentLine(lineStart_) || IsFreeFormComment(lineStart_) ||
+        IncludeLine(lineStart_)) {
       NextLine();
     } else if (IsPreprocessorDirectiveLine(lineStart_)) {
       if (std::optional<TokenSequence> tokens{NextTokenizedLine()}) {
@@ -499,9 +566,9 @@ bool Prescanner::FreeFormContinuation() {
   return true;
 }
 
-void Prescanner::PayNewlineDebt(CookedSource *cooked) {
+void Prescanner::PayNewlineDebt() {
   for (; newlineDebt_ > 0; --newlineDebt_) {
-    cooked->Put('\n', newlineProvenance_);
+    cooked_->Put('\n', newlineProvenance_);
   }
 }
 }  // namespace parser
index 98a9b6c..76f0850 100644 (file)
@@ -22,6 +22,7 @@ class Preprocessor;
 class Prescanner {
 public:
   Prescanner(Messages *, CookedSource *, Preprocessor *);
+  Prescanner(const Prescanner &);
 
   Messages *messages() const { return messages_; }
 
@@ -42,9 +43,9 @@ public:
     return *this;
   }
 
-  CookedSource *cooked() const { return cooked_; }
-
   bool Prescan(ProvenanceRange);
+
+  // Callbacks for use by Preprocessor.
   std::optional<TokenSequence> NextTokenizedLine();
   Provenance GetCurrentProvenance() const { return GetProvenance(at_); }
   void Complain(const std::string &message);
@@ -89,11 +90,12 @@ private:
   bool CommentLinesAndPreprocessorDirectives();
   bool IsFixedFormCommentLine(const char *);
   bool IsFreeFormComment(const char *);
+  bool IncludeLine(const char *);
   bool IsPreprocessorDirectiveLine(const char *);
   const char *FixedFormContinuationLine();
   bool FixedFormContinuation();
   bool FreeFormContinuation();
-  void PayNewlineDebt(CookedSource *);
+  void PayNewlineDebt();
 
   Messages *messages_;
   CookedSource *cooked_;
index c93ca6f..2417766 100644 (file)
@@ -68,11 +68,21 @@ AllSources::AllSources() {
   }
 }
 
+AllSources::~AllSources() {}
+
 const char &AllSources::operator[](Provenance at) const {
   const Origin &origin{MapToOrigin(at)};
   return origin[at - origin.start];
 }
 
+const SourceFile *AllSources::Open(std::string path, std::stringstream *error) {
+  std::unique_ptr<SourceFile> source{std::make_unique<SourceFile>()};
+  if (source->Open(path, error)) {
+    return ownedSourceFiles_.emplace_back(std::move(source)).get();
+  }
+  return nullptr;
+}
+
 ProvenanceRange AllSources::AddIncludedFile(
     const SourceFile &source, ProvenanceRange from) {
   size_t start{bytes_}, bytes{source.bytes()};
index 6f621a2..1817f86 100644 (file)
@@ -1,13 +1,17 @@
 #ifndef FORTRAN_PROVENANCE_H_
 #define FORTRAN_PROVENANCE_H_
+
 #include "char-buffer.h"
 #include "source.h"
 #include <map>
+#include <memory>
 #include <ostream>
+#include <sstream>
 #include <string>
 #include <utility>
 #include <variant>
 #include <vector>
+
 namespace Fortran {
 namespace parser {
 
@@ -66,10 +70,13 @@ private:
 class AllSources {
 public:
   AllSources();
+  ~AllSources();
 
   size_t size() const { return bytes_; }
   const char &operator[](Provenance) const;
 
+  const SourceFile *Open(std::string path, std::stringstream *error);
+
   ProvenanceRange AddIncludedFile(const SourceFile &, ProvenanceRange);
   ProvenanceRange AddMacroCall(
       ProvenanceRange def, ProvenanceRange use, const std::string &expansion);
@@ -114,6 +121,7 @@ private:
   std::vector<Origin> origin_;
   size_t bytes_{0};
   std::map<char, Provenance> compilerInsertionProvenance_;
+  std::vector<std::unique_ptr<SourceFile>> ownedSourceFiles_;
 };
 
 class CookedSource {
index f1b78f5..ec23c33 100644 (file)
@@ -10,7 +10,6 @@
 #include "../../lib/parser/preprocessor.h"
 #include "../../lib/parser/prescan.h"
 #include "../../lib/parser/provenance.h"
-#include "../../lib/parser/source.h"
 #include "../../lib/parser/user-state.h"
 #include <cerrno>
 #include <cstdio>
@@ -98,16 +97,16 @@ int main(int argc, char *const argv[]) {
     }
   }
 
-  Fortran::parser::SourceFile sourceFile;
+  Fortran::parser::AllSources allSources;
   std::stringstream error;
-  if (!sourceFile.Open(path, &error)) {
+  const auto *sourceFile = allSources.Open(path, &error);
+  if (!sourceFile) {
     std::cerr << error.str() << '\n';
     return 1;
   }
 
-  Fortran::parser::AllSources allSources;
   Fortran::parser::ProvenanceRange range{allSources.AddIncludedFile(
-      sourceFile, Fortran::parser::ProvenanceRange{})};
+      *sourceFile, Fortran::parser::ProvenanceRange{})};
   Fortran::parser::Messages messages{allSources};
   Fortran::parser::CookedSource cooked{&allSources};
   Fortran::parser::Preprocessor preprocessor{&allSources};