[flang] Preparatory work for file inclusion.
authorpeter klausler <pklausler@nvidia.com>
Tue, 13 Feb 2018 20:24:54 +0000 (12:24 -0800)
committerGitHub <noreply@github.com>
Thu, 15 Feb 2018 23:58:44 +0000 (15:58 -0800)
Original-commit: flang-compiler/f18@1cbbd4a3574c97de0157007234108012c87fbd3f
Reviewed-on: https://github.com/flang-compiler/f18/pull/9
Tree-same-pre-rewrite: false

flang/lib/parser/idioms.h
flang/lib/parser/preprocessor.cc
flang/lib/parser/preprocessor.h
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 e988688..b9587b9 100644 (file)
@@ -66,11 +66,14 @@ template<typename A> bool operator!(const std::optional<A> &x) {
   Fortran::parser::die("no case at " __FILE__ "(%d)", __LINE__)
 
 // For cheap assertions that should be applied in production.
+// To disable all CHECK tests, compile with -DCHECK.
+#ifndef CHECK
 #define CHECK(x) \
   ((x) || \
       (Fortran::parser::die( \
            "CHECK(" #x ") failed at " __FILE__ "(%d)", __LINE__), \
           false))
+#endif
 
 // To make error messages more informative, wrap some type information
 // around a false compile-time value, e.g.
index 5b21501..9f08593 100644 (file)
@@ -174,7 +174,7 @@ TokenSequence Definition::Tokenize(const std::vector<std::string> &argNames,
 }
 
 TokenSequence Definition::Apply(
-    const std::vector<TokenSequence> &args, const Prescanner &prescanner) {
+    const std::vector<TokenSequence> &args, const AllSources &allSources) {
   TokenSequence result;
   bool pasting{false};
   bool skipping{false};
@@ -210,8 +210,8 @@ TokenSequence Definition::Apply(
         while (result.size() >= afterLastNonBlank) {
           result.pop_back();
         }
-        result.PutNextTokenChar(
-            '"', prescanner.CompilerInsertionProvenance('"'));
+        Provenance quoteProvenance{allSources.CompilerInsertionProvenance('"')};
+        result.PutNextTokenChar('"', quoteProvenance);
         for (size_t k{0}; k < argTokens; ++k) {
           const CharPointerWithLength &arg{args[index][k]};
           size_t argBytes{args[index][k].size()};
@@ -224,8 +224,7 @@ TokenSequence Definition::Apply(
             result.PutNextTokenChar(ch, from);
           }
         }
-        result.PutNextTokenChar(
-            '"', prescanner.CompilerInsertionProvenance('"'));
+        result.PutNextTokenChar('"', quoteProvenance);
         result.CloseToken();
       } else {
         for (size_t k{0}; k < argTokens; ++k) {
@@ -251,7 +250,7 @@ TokenSequence Definition::Apply(
         token.ToString() == "__VA_ARGS__") {
       for (size_t k{argumentCount_}; k < args.size(); ++k) {
         if (k > argumentCount_) {
-          result.Put(","s, prescanner.CompilerInsertionProvenance(','));
+          result.Put(","s, allSources.CompilerInsertionProvenance(','));
         }
         result.Put(args[k]);
       }
@@ -282,24 +281,24 @@ static std::string FormatTime(const std::time_t &now, const char *format) {
       std::strftime(buffer, sizeof buffer, format, std::localtime(&now))};
 }
 
-Preprocessor::Preprocessor(Prescanner &ps) : prescanner_{ps} {
+Preprocessor::Preprocessor(AllSources *allSources) : allSources_{allSources} {
   // Capture current local date & time once now to avoid having the values
   // of __DATE__ or __TIME__ change during compilation.
   std::time_t now;
   std::time(&now);
   definitions_.emplace(SaveTokenAsName("__DATE__"s),  // e.g., "Jun 16 1904"
-      Definition{FormatTime(now, "\"%h %e %Y\""), ps.allSources()});
+      Definition{FormatTime(now, "\"%h %e %Y\""), allSources});
   definitions_.emplace(SaveTokenAsName("__TIME__"s),  // e.g., "23:59:60"
-      Definition{FormatTime(now, "\"%T\""), ps.allSources()});
+      Definition{FormatTime(now, "\"%T\""), allSources});
   // The values of these predefined macros depend on their invocation sites.
   definitions_.emplace(
-      SaveTokenAsName("__FILE__"s), Definition{"__FILE__"s, ps.allSources()});
+      SaveTokenAsName("__FILE__"s), Definition{"__FILE__"s, allSources});
   definitions_.emplace(
-      SaveTokenAsName("__LINE__"s), Definition{"__LINE__"s, ps.allSources()});
+      SaveTokenAsName("__LINE__"s), Definition{"__LINE__"s, allSources});
 }
 
-bool Preprocessor::MacroReplacement(
-    const TokenSequence &input, TokenSequence *result) {
+bool Preprocessor::MacroReplacement(const TokenSequence &input,
+    const Prescanner &prescanner, TokenSequence *result) {
   // Do quick scan for any use of a defined name.
   size_t tokens{input.size()};
   size_t j;
@@ -335,25 +334,20 @@ bool Preprocessor::MacroReplacement(
         std::string name{def.replacement()[0].ToString()};
         if (name == "__FILE__") {
           std::string f{"\""s +
-              prescanner_.allSources()->GetPath(
-                  prescanner_.GetCurrentProvenance()) +
-              '"'};
-          result->Put(
-              f, prescanner_.allSources()->AddCompilerInsertion(f).start);
+              allSources_->GetPath(prescanner.GetCurrentProvenance()) + '"'};
+          result->Put(f, allSources_->AddCompilerInsertion(f).start);
           continue;
         }
         if (name == "__LINE__") {
           std::stringstream ss;
-          ss << prescanner_.allSources()->GetLineNumber(
-              prescanner_.GetCurrentProvenance());
+          ss << allSources_->GetLineNumber(prescanner.GetCurrentProvenance());
           std::string s{ss.str()};
-          result->Put(
-              s, prescanner_.allSources()->AddCompilerInsertion(s).start);
+          result->Put(s, allSources_->AddCompilerInsertion(s).start);
           continue;
         }
       }
       def.set_isDisabled(true);
-      result->Put(ReplaceMacros(def.replacement()));
+      result->Put(ReplaceMacros(def.replacement(), prescanner));
       def.set_isDisabled(false);
       continue;
     }
@@ -401,15 +395,16 @@ bool Preprocessor::MacroReplacement(
       args.emplace_back(TokenSequence(input, at, count));
     }
     def.set_isDisabled(true);
-    result->Put(ReplaceMacros(def.Apply(args, prescanner_)));
+    result->Put(ReplaceMacros(def.Apply(args, *allSources_), prescanner));
     def.set_isDisabled(false);
   }
   return true;
 }
 
-TokenSequence Preprocessor::ReplaceMacros(const TokenSequence &tokens) {
+TokenSequence Preprocessor::ReplaceMacros(
+    const TokenSequence &tokens, const Prescanner &prescanner) {
   TokenSequence repl;
-  return MacroReplacement(tokens, &repl) ? repl : tokens;
+  return MacroReplacement(tokens, prescanner, &repl) ? repl : tokens;
 }
 
 static size_t SkipBlanks(
@@ -456,14 +451,14 @@ static std::string GetDirectiveName(const TokenSequence &line, size_t *rest) {
   return ConvertToLowerCase(line[j].ToString());
 }
 
-bool Preprocessor::Directive(const TokenSequence &dir) {
+bool Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
   size_t tokens{dir.size()};
   size_t j{SkipBlanks(dir, 0, tokens)};
   if (j == tokens) {
     return true;
   }
   if (dir[j].ToString() != "#") {
-    Complain("missing '#'");
+    prescanner->Complain("missing '#'");
     return false;
   }
   j = SkipBlanks(dir, j + 1, tokens);
@@ -485,7 +480,7 @@ bool Preprocessor::Directive(const TokenSequence &dir) {
   }
   if (dirName == "define") {
     if (nameToken.empty()) {
-      Complain("#define: missing or invalid name");
+      prescanner->Complain("#define: missing or invalid name");
       return false;
     }
     nameToken = SaveTokenAsName(nameToken);
@@ -501,14 +496,14 @@ bool Preprocessor::Directive(const TokenSequence &dir) {
             isVariadic = true;
           } else {
             if (an.empty() || !IsIdentifierFirstCharacter(an[0])) {
-              Complain("#define: missing or invalid argument name");
+              prescanner->Complain("#define: missing or invalid argument name");
               return false;
             }
             argName.push_back(an);
           }
           j = SkipBlanks(dir, j + 1, tokens);
           if (j == tokens) {
-            Complain("#define: malformed argument list");
+            prescanner->Complain("#define: malformed argument list");
             return false;
           }
           std::string punc{dir[j].ToString()};
@@ -516,18 +511,18 @@ bool Preprocessor::Directive(const TokenSequence &dir) {
             break;
           }
           if (punc != ",") {
-            Complain("#define: malformed argument list");
+            prescanner->Complain("#define: malformed argument list");
             return false;
           }
           j = SkipBlanks(dir, j + 1, tokens);
           if (j == tokens || isVariadic) {
-            Complain("#define: malformed argument list");
+            prescanner->Complain("#define: malformed argument list");
             return false;
           }
         }
         if (std::set<std::string>(argName.begin(), argName.end()).size() !=
             argName.size()) {
-          Complain("#define: argument names are not distinct");
+          prescanner->Complain("#define: argument names are not distinct");
           return false;
         }
       }
@@ -542,12 +537,12 @@ bool Preprocessor::Directive(const TokenSequence &dir) {
   }
   if (dirName == "undef") {
     if (nameToken.empty()) {
-      Complain("# missing or invalid name");
+      prescanner->Complain("# missing or invalid name");
       return false;
     }
     j = SkipBlanks(dir, j + 1, tokens);
     if (j != tokens) {
-      Complain("#undef: excess tokens at end of directive");
+      prescanner->Complain("#undef: excess tokens at end of directive");
       return false;
     }
     definitions_.erase(nameToken);
@@ -555,73 +550,75 @@ bool Preprocessor::Directive(const TokenSequence &dir) {
   }
   if (dirName == "ifdef" || dirName == "ifndef") {
     if (nameToken.empty()) {
-      Complain("#"s + dirName + ": missing name");
+      prescanner->Complain("#"s + dirName + ": missing name");
       return false;
     }
     j = SkipBlanks(dir, j + 1, tokens);
     if (j != tokens) {
-      Complain("#"s + dirName + ": excess tokens at end of directive");
+      prescanner->Complain(
+          "#"s + dirName + ": excess tokens at end of directive");
       return false;
     }
     if (IsNameDefined(nameToken) == (dirName == "ifdef")) {
       ifStack_.push(CanDeadElseAppear::Yes);
       return true;
     }
-    return SkipDisabledConditionalCode(dirName, IsElseActive::Yes);
+    return SkipDisabledConditionalCode(dirName, IsElseActive::Yes, prescanner);
   }
   if (dirName == "if") {
-    if (IsIfPredicateTrue(dir, j, tokens - j)) {
+    if (IsIfPredicateTrue(dir, j, tokens - j, prescanner)) {
       ifStack_.push(CanDeadElseAppear::Yes);
       return true;
     }
-    return SkipDisabledConditionalCode(dirName, IsElseActive::Yes);
+    return SkipDisabledConditionalCode(dirName, IsElseActive::Yes, prescanner);
   }
   if (dirName == "else") {
     if (j != tokens) {
-      Complain("#else: excess tokens at end of directive");
+      prescanner->Complain("#else: excess tokens at end of directive");
       return false;
     }
     if (ifStack_.empty()) {
-      Complain("#else: not nested within #if, #ifdef, or #ifndef");
+      prescanner->Complain("#else: not nested within #if, #ifdef, or #ifndef");
       return false;
     }
     if (ifStack_.top() != CanDeadElseAppear::Yes) {
-      Complain("#else: already appeared within this #if, #ifdef, or #ifndef");
+      prescanner->Complain(
+          "#else: already appeared within this #if, #ifdef, or #ifndef");
       return false;
     }
     ifStack_.pop();
-    return SkipDisabledConditionalCode("else", IsElseActive::No);
+    return SkipDisabledConditionalCode("else", IsElseActive::No, prescanner);
   }
   if (dirName == "elif") {
     if (ifStack_.empty()) {
-      Complain("#elif: not nested within #if, #ifdef, or #ifndef");
+      prescanner->Complain("#elif: not nested within #if, #ifdef, or #ifndef");
       return false;
     }
     if (ifStack_.top() != CanDeadElseAppear::Yes) {
-      Complain("#elif: #else previously appeared within this "
-               "#if, #ifdef, or #ifndef");
+      prescanner->Complain("#elif: #else previously appeared within this "
+                           "#if, #ifdef, or #ifndef");
       return false;
     }
     ifStack_.pop();
-    return SkipDisabledConditionalCode("elif", IsElseActive::No);
+    return SkipDisabledConditionalCode("elif", IsElseActive::No, prescanner);
   }
   if (dirName == "endif") {
     if (j != tokens) {
-      Complain("#endif: excess tokens at end of directive");
+      prescanner->Complain("#endif: excess tokens at end of directive");
       return false;
     }
     if (ifStack_.empty()) {
-      Complain("#endif: no #if, #ifdef, or #ifndef");
+      prescanner->Complain("#endif: no #if, #ifdef, or #ifndef");
       return false;
     }
     ifStack_.pop();
     return true;
   }
   if (dirName == "error" || dirName == "warning") {
-    Complain(dir.ToString());
+    prescanner->Complain(dir.ToString());
     return dirName != "error";
   }
-  Complain("#"s + dirName + ": unknown or unimplemented directive");
+  prescanner->Complain("#"s + dirName + ": unknown or unimplemented directive");
   return false;
 }
 
@@ -635,10 +632,10 @@ bool Preprocessor::IsNameDefined(const CharPointerWithLength &token) {
   return definitions_.find(token) != definitions_.end();
 }
 
-bool Preprocessor::SkipDisabledConditionalCode(
-    const std::string &dirName, IsElseActive isElseActive) {
+bool Preprocessor::SkipDisabledConditionalCode(const std::string &dirName,
+    IsElseActive isElseActive, Prescanner *prescanner) {
   int nesting{0};
-  while (std::optional<TokenSequence> line{prescanner_.NextTokenizedLine()}) {
+  while (std::optional<TokenSequence> line{prescanner->NextTokenizedLine()}) {
     size_t rest{0};
     std::string dn{GetDirectiveName(*line, &rest)};
     if (dn == "ifdef" || dn == "ifndef" || dn == "if") {
@@ -652,20 +649,17 @@ bool Preprocessor::SkipDisabledConditionalCode(
         ifStack_.push(CanDeadElseAppear::No);
         return true;
       }
-      if (dn == "elif" && IsIfPredicateTrue(*line, rest, line->size() - rest)) {
+      if (dn == "elif" &&
+          IsIfPredicateTrue(*line, rest, line->size() - rest, prescanner)) {
         ifStack_.push(CanDeadElseAppear::Yes);
         return true;
       }
     }
   }
-  Complain("#"s + dirName + ": missing #endif");
+  prescanner->Complain("#"s + dirName + ": missing #endif");
   return false;
 }
 
-void Preprocessor::Complain(const std::string &message) {
-  prescanner_.messages()->Put({prescanner_.GetCurrentProvenance(), message});
-}
-
 // Precedence level codes used here to accommodate mixed Fortran and C:
 // 15: parentheses and constants, logical !, bitwise ~
 // 14: unary + and -
@@ -937,8 +931,8 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
   return 0;  // silence compiler warning
 }
 
-bool Preprocessor::IsIfPredicateTrue(
-    const TokenSequence &expr, size_t first, size_t exprTokens) {
+bool Preprocessor::IsIfPredicateTrue(const TokenSequence &expr, size_t first,
+    size_t exprTokens, Prescanner *prescanner) {
   TokenSequence expr1{StripBlanks(expr, first, first + exprTokens)};
   TokenSequence expr2;
   for (size_t j{0}; j < expr1.size(); ++j) {
@@ -954,22 +948,22 @@ bool Preprocessor::IsIfPredicateTrue(
       }
       if (!name.empty()) {
         char truth{IsNameDefined(name) ? '1' : '0'};
-        expr2.Put(&truth, 1, prescanner_.CompilerInsertionProvenance(truth));
+        expr2.Put(&truth, 1, allSources_->CompilerInsertionProvenance(truth));
         continue;
       }
     }
     expr2.Put(expr1, j);
   }
-  TokenSequence expr3{ReplaceMacros(expr2)};
+  TokenSequence expr3{ReplaceMacros(expr2, *prescanner)};
   TokenSequence expr4{StripBlanks(expr3, 0, expr3.size())};
   size_t atToken{0};
   std::string error;
   bool result{ExpressionValue(expr4, 0, &atToken, &error) != 0};
   if (!error.empty()) {
-    Complain(error);
+    prescanner->Complain(error);
   } else if (atToken < expr4.size()) {
-    Complain(atToken == 0 ? "could not parse any expression"
-                          : "excess characters after expression");
+    prescanner->Complain(atToken == 0 ? "could not parse any expression"
+                                      : "excess characters after expression");
   }
   return result;
 }
index b30602c..e33766b 100644 (file)
@@ -22,7 +22,6 @@
 namespace Fortran {
 namespace parser {
 
-class CookedSource;
 class Prescanner;
 
 // Just a const char pointer with an associated length; does not own the
@@ -171,7 +170,7 @@ public:
   bool set_isDisabled(bool disable);
 
   TokenSequence Apply(
-      const std::vector<TokenSequence> &args, const Prescanner &);
+      const std::vector<TokenSequence> &args, const AllSources &);
 
 private:
   static TokenSequence Tokenize(const std::vector<std::string> &argNames,
@@ -188,30 +187,31 @@ private:
 // Preprocessing state
 class Preprocessor {
 public:
-  explicit Preprocessor(Prescanner &);
+  explicit Preprocessor(AllSources *);
 
   // When the input contains macros to be replaced, the new token sequence
   // is appended to the output and the returned value is true.  When
   // no macro replacement is necessary, the output is unmodified and the
   // return value is false.
-  bool MacroReplacement(const TokenSequence &, TokenSequence *);
+  bool MacroReplacement(
+      const TokenSequence &, const Prescanner &, TokenSequence *);
 
   // Implements a preprocessor directive; returns true when no fatal error.
-  bool Directive(const TokenSequence &);
+  bool Directive(const TokenSequence &, Prescanner *);
 
 private:
   enum class IsElseActive { No, Yes };
   enum class CanDeadElseAppear { No, Yes };
 
-  void Complain(const std::string &);
   CharPointerWithLength SaveTokenAsName(const CharPointerWithLength &);
   bool IsNameDefined(const CharPointerWithLength &);
-  TokenSequence ReplaceMacros(const TokenSequence &);
-  bool SkipDisabledConditionalCode(const std::string &dirName, IsElseActive);
+  TokenSequence ReplaceMacros(const TokenSequence &, const Prescanner &);
+  bool SkipDisabledConditionalCode(
+      const std::string &dirName, IsElseActive, Prescanner *);
   bool IsIfPredicateTrue(
-      const TokenSequence &expr, size_t first, size_t exprTokens);
+      const TokenSequence &expr, size_t first, size_t exprTokens, Prescanner *);
 
-  Prescanner &prescanner_;
+  AllSources *allSources_;
   std::list<std::string> names_;
   std::unordered_map<CharPointerWithLength, Definition> definitions_;
   std::stack<CanDeadElseAppear> ifStack_;
index 14c15ed..5a1cc86 100644 (file)
@@ -9,22 +9,22 @@
 namespace Fortran {
 namespace parser {
 
-Prescanner::Prescanner(Messages *messages, AllSources *allSources)
-  : messages_{messages}, allSources_{allSources}, start_{&(*allSources)[0]},
-    limit_{start_ + allSources->size()}, preprocessor_{*this} {
-  std::string compilerInserts{" ,\"01\n"};
-  ProvenanceRange range{allSources->AddCompilerInsertion(compilerInserts)};
-  for (size_t j{0}; j < compilerInserts.size(); ++j) {
-    compilerInsertionProvenance_[compilerInserts[j]] = range.start + j;
-  }
-  newlineProvenance_ = CompilerInsertionProvenance('\n');
-}
+Prescanner::Prescanner(
+    Messages *messages, CookedSource *cooked, Preprocessor *preprocessor)
+  : messages_{messages}, cooked_{cooked}, preprocessor_{preprocessor} {}
 
-CookedSource Prescanner::Prescan() {
-  lineStart_ = start_;
+bool Prescanner::Prescan(ProvenanceRange range) {
+  startProvenance_ = range.start;
+  ProvenanceRange around{
+      cooked_->allSources()->GetContiguousRangeAround(startProvenance_)};
+  CHECK(startProvenance_ + range.bytes <= around.start + around.bytes);
+  const SourceFile *source{
+      cooked_->allSources()->GetSourceFile(startProvenance_)};
+  size_t offset{startProvenance_ - around.start};
+  lineStart_ = start_ = source->content() + offset;
+  limit_ = start_ + range.bytes;
   BeginSourceLine(start_);
   TokenSequence tokens, preprocessed;
-  CookedSource cooked{allSources_};
   while (lineStart_ < limit_) {
     if (CommentLinesAndPreprocessorDirectives() && lineStart_ >= limit_) {
       break;
@@ -37,7 +37,7 @@ CookedSource Prescanner::Prescan() {
     }
     while (NextToken(&tokens)) {
     }
-    if (preprocessor_.MacroReplacement(tokens, &preprocessed)) {
+    if (preprocessor_->MacroReplacement(tokens, *this, &preprocessed)) {
       preprocessed.PutNextTokenChar('\n', newlineProvenance_);
       preprocessed.CloseToken();
       if (IsFixedFormCommentLine(preprocessed.data()) ||
@@ -45,19 +45,18 @@ CookedSource Prescanner::Prescan() {
         ++newlineDebt_;
       } else {
         preprocessed.pop_back();  // clip the newline added above
-        preprocessed.EmitWithCaseConversion(&cooked);
+        preprocessed.EmitWithCaseConversion(cooked_);
       }
       preprocessed.clear();
     } else {
-      tokens.EmitWithCaseConversion(&cooked);
+      tokens.EmitWithCaseConversion(cooked_);
     }
     tokens.clear();
-    cooked.Put('\n', newlineProvenance_);
-    PayNewlineDebt(&cooked);
+    cooked_->Put('\n', newlineProvenance_);
+    PayNewlineDebt(cooked_);
   }
-  PayNewlineDebt(&cooked);
-  cooked.Marshal();
-  return cooked;
+  PayNewlineDebt(cooked_);
+  return !anyFatalErrors_;
 }
 
 std::optional<TokenSequence> Prescanner::NextTokenizedLine() {
@@ -76,8 +75,8 @@ std::optional<TokenSequence> Prescanner::NextTokenizedLine() {
   return {std::move(tokens)};
 }
 
-Provenance Prescanner::CompilerInsertionProvenance(char ch) const {
-  return compilerInsertionProvenance_.find(ch)->second;
+void Prescanner::Complain(const std::string &message) {
+  messages_->Put({GetCurrentProvenance(), message});
 }
 
 void Prescanner::NextLine() {
@@ -110,16 +109,15 @@ void Prescanner::LabelField(TokenSequence *token) {
     token->CloseToken();
   }
   if (outCol < 7) {
-    Provenance provenance{CompilerInsertionProvenance(' ')};
     for (; outCol < 7; ++outCol) {
-      token->PutNextTokenChar(' ', provenance);
+      token->PutNextTokenChar(' ', spaceProvenance_);
     }
     token->CloseToken();
   }
 }
 
 void Prescanner::NextChar() {
-  CHECK(*at_ != '\n');  // TODO pmk
+  CHECK(*at_ != '\n');
   ++at_;
   ++column_;
   if (inPreprocessorDirective_) {
@@ -168,7 +166,7 @@ static inline bool IsNameChar(char ch) {
 }
 
 bool Prescanner::NextToken(TokenSequence *tokens) {
-  CHECK(at_ >= start_ && at_ < limit_);  // TODO pmk rm?
+  CHECK(at_ >= start_ && at_ < limit_);
   if (inFixedForm_) {
     SkipSpaces();
   } else if (*at_ == ' ' || *at_ == '\t') {
@@ -206,7 +204,7 @@ bool Prescanner::NextToken(TokenSequence *tokens) {
       inCharLiteral_ = true;
       while (n-- > 0) {
         if (PadOutCharacterLiteral()) {
-          tokens->PutNextTokenChar(' ', compilerInsertionProvenance_[' ']);
+          tokens->PutNextTokenChar(' ', spaceProvenance_);
         } else {
           if (*at_ == '\n') {
             break;  // TODO error
@@ -297,12 +295,12 @@ void Prescanner::QuotedCharacterLiteral(TokenSequence *tokens) {
   do {
     EmitCharAndAdvance(tokens, *at_);
     while (PadOutCharacterLiteral()) {
-      tokens->PutNextTokenChar(' ', compilerInsertionProvenance_[' ']);
+      tokens->PutNextTokenChar(' ', spaceProvenance_);
     }
     if (*at_ == '\\' && enableBackslashEscapesInCharLiterals_) {
       EmitCharAndAdvance(tokens, '\\');
       while (PadOutCharacterLiteral()) {
-        tokens->PutNextTokenChar(' ', compilerInsertionProvenance_[' ']);
+        tokens->PutNextTokenChar(' ', spaceProvenance_);
       }
     } else if (*at_ == quote) {
       // A doubled quote mark becomes a single instance of the quote character
@@ -408,7 +406,7 @@ bool Prescanner::CommentLinesAndPreprocessorDirectives() {
       NextLine();
     } else if (IsPreprocessorDirectiveLine(lineStart_)) {
       if (std::optional<TokenSequence> tokens{NextTokenizedLine()}) {
-        anyFatalErrors_ |= !preprocessor_.Directive(*tokens);
+        anyFatalErrors_ |= !preprocessor_->Directive(*tokens, this);
       }
     } else {
       break;
index b5a28c3..ae8c996 100644 (file)
@@ -5,14 +5,13 @@
 // character-level features of the language that can be inefficient to
 // support directly in a backtracking parser.  This phase handles Fortran
 // line continuation, comment removal, card image margins, padding out
-// fixed form character literals on truncated card images, and drives the
-// Fortran source preprocessor.
+// fixed form character literals on truncated card images, file
+// inclusion, and driving the Fortran source preprocessor.
 
 #include "message.h"
 #include "preprocessor.h"
 #include "provenance.h"
 #include "source.h"
-#include <map>
 #include <optional>
 #include <string>
 
@@ -21,10 +20,9 @@ namespace parser {
 
 class Prescanner {
 public:
-  Prescanner(Messages *, AllSources *);
+  Prescanner(Messages *, CookedSource *, Preprocessor *);
 
   Messages *messages() const { return messages_; }
-  bool anyFatalErrors() const { return anyFatalErrors_; }
 
   Prescanner &set_fixedForm(bool yes) {
     inFixedForm_ = yes;
@@ -43,12 +41,12 @@ public:
     return *this;
   }
 
-  AllSources *allSources() const { return allSources_; }
+  CookedSource *cooked() const { return cooked_; }
 
-  CookedSource Prescan();
+  bool Prescan(ProvenanceRange);
   std::optional<TokenSequence> NextTokenizedLine();
   Provenance GetCurrentProvenance() const { return GetProvenance(at_); }
-  Provenance CompilerInsertionProvenance(char ch) const;
+  void Complain(const std::string &message);
 
 private:
   void BeginSourceLine(const char *at) {
@@ -65,7 +63,7 @@ private:
   }
 
   Provenance GetProvenance(const char *sourceChar) const {
-    return startProvenance_ /*TODO pmk rm?*/ + sourceChar - start_;
+    return startProvenance_ + sourceChar - start_;
   }
 
   void EmitChar(TokenSequence *tokens, char ch) {
@@ -97,11 +95,12 @@ private:
   void PayNewlineDebt(CookedSource *);
 
   Messages *messages_;
-  AllSources *allSources_;
+  CookedSource *cooked_;
+  Preprocessor *preprocessor_;
 
   Provenance startProvenance_{0};
-  const char *start_{nullptr};  // beginning of sourceFile_ content
-  const char *limit_{nullptr};  // first address after end of source
+  const char *start_{nullptr};  // beginning of current source file content
+  const char *limit_{nullptr};  // first address after end of current source
   const char *at_{nullptr};  // next character to process; < lineStart_
   int column_{1};  // card image column position of next character
   const char *lineStart_{nullptr};  // next line to process; <= limit_
@@ -117,9 +116,10 @@ private:
   bool enableOldDebugLines_{false};
   bool enableBackslashEscapesInCharLiterals_{true};
   int delimiterNesting_{0};
-  Preprocessor preprocessor_;
-  std::map<char, Provenance> compilerInsertionProvenance_;
-  Provenance newlineProvenance_{0};
+  Provenance newlineProvenance_{
+      cooked_->allSources()->CompilerInsertionProvenance('\n')};
+  Provenance spaceProvenance_{
+      cooked_->allSources()->CompilerInsertionProvenance(' ')};
 };
 }  // namespace parser
 }  // namespace Fortran
index 26af663..c93ca6f 100644 (file)
@@ -60,8 +60,12 @@ void OffsetToProvenanceMappings::RemoveLastBytes(size_t bytes) {
   }
 }
 
-AllSources::AllSources(const SourceFile &initialSourceFile) {
-  AddIncludedFile(initialSourceFile, ProvenanceRange{});
+AllSources::AllSources() {
+  std::string compilerInserts{" ,\"01\n"};
+  ProvenanceRange range{AddCompilerInsertion(compilerInserts)};
+  for (size_t j{0}; j < range.bytes; ++j) {
+    compilerInsertionProvenance_[compilerInserts[j]] = range.start + j;
+  }
 }
 
 const char &AllSources::operator[](Provenance at) const {
@@ -135,6 +139,11 @@ const SourceFile *AllSources::GetSourceFile(Provenance at) const {
       origin.u);
 }
 
+ProvenanceRange AllSources::GetContiguousRangeAround(Provenance at) const {
+  const Origin &origin{MapToOrigin(at)};
+  return {origin.start, origin.size()};
+}
+
 std::string AllSources::GetPath(Provenance at) const {
   const SourceFile *source{GetSourceFile(at)};
   return source ? source->path() : ""s;
@@ -145,6 +154,10 @@ int AllSources::GetLineNumber(Provenance at) const {
   return source ? source->FindOffsetLineAndColumn(at).first : 0;
 }
 
+Provenance AllSources::CompilerInsertionProvenance(char ch) const {
+  return compilerInsertionProvenance_.find(ch)->second;
+}
+
 AllSources::Origin::Origin(size_t s, const SourceFile &source)
   : start{s}, u{Inclusion{source}} {}
 AllSources::Origin::Origin(
index d172396..6f621a2 100644 (file)
@@ -2,6 +2,7 @@
 #define FORTRAN_PROVENANCE_H_
 #include "char-buffer.h"
 #include "source.h"
+#include <map>
 #include <ostream>
 #include <string>
 #include <utility>
@@ -32,6 +33,11 @@ struct ProvenanceRange {
   ProvenanceRange(ProvenanceRange &&) = default;
   ProvenanceRange &operator=(const ProvenanceRange &) = default;
   ProvenanceRange &operator=(ProvenanceRange &&) = default;
+
+  bool operator==(const ProvenanceRange &that) const {
+    return start == that.start && bytes == that.bytes;
+  }
+
   Provenance start{0};
   size_t bytes{0};
 };
@@ -59,7 +65,7 @@ private:
 
 class AllSources {
 public:
-  explicit AllSources(const SourceFile &initialSourceFile);
+  AllSources();
 
   size_t size() const { return bytes_; }
   const char &operator[](Provenance) const;
@@ -71,8 +77,10 @@ public:
 
   void Identify(std::ostream &, Provenance, const std::string &prefix) const;
   const SourceFile *GetSourceFile(Provenance) const;
+  ProvenanceRange GetContiguousRangeAround(Provenance) const;
   std::string GetPath(Provenance) const;  // __FILE__
   int GetLineNumber(Provenance) const;  // __LINE__
+  Provenance CompilerInsertionProvenance(char ch) const;
 
 private:
   struct Inclusion {
@@ -105,6 +113,7 @@ private:
 
   std::vector<Origin> origin_;
   size_t bytes_{0};
+  std::map<char, Provenance> compilerInsertionProvenance_;
 };
 
 class CookedSource {
index 89bbc5e..f1b78f5 100644 (file)
@@ -7,6 +7,7 @@
 #include "../../lib/parser/idioms.h"
 #include "../../lib/parser/message.h"
 #include "../../lib/parser/parse-tree.h"
+#include "../../lib/parser/preprocessor.h"
 #include "../../lib/parser/prescan.h"
 #include "../../lib/parser/provenance.h"
 #include "../../lib/parser/source.h"
@@ -104,21 +105,25 @@ int main(int argc, char *const argv[]) {
     return 1;
   }
 
-  Fortran::parser::AllSources allSources{sourceFile};
+  Fortran::parser::AllSources allSources;
+  Fortran::parser::ProvenanceRange range{allSources.AddIncludedFile(
+      sourceFile, Fortran::parser::ProvenanceRange{})};
   Fortran::parser::Messages messages{allSources};
-  Fortran::parser::Prescanner prescanner{&messages, &allSources};
-  Fortran::parser::CookedSource cooked{
-      prescanner.set_fixedForm(fixedForm)
-          .set_enableBackslashEscapesInCharLiterals(backslashEscapes)
-          .set_fixedFormColumnLimit(columns)
-          .set_enableOldDebugLines(enableOldDebugLines)
-          .Prescan()};
+  Fortran::parser::CookedSource cooked{&allSources};
+  Fortran::parser::Preprocessor preprocessor{&allSources};
+  Fortran::parser::Prescanner prescanner{&messages, &cooked, &preprocessor};
+  bool prescanOk{prescanner.set_fixedForm(fixedForm)
+                     .set_enableBackslashEscapesInCharLiterals(backslashEscapes)
+                     .set_fixedFormColumnLimit(columns)
+                     .set_enableOldDebugLines(enableOldDebugLines)
+                     .Prescan(range)};
   messages.Emit(std::cerr);
-  if (prescanner.anyFatalErrors()) {
+  if (!prescanOk) {
     return 1;
   }
   columns = std::numeric_limits<int>::max();
 
+  cooked.Marshal();
   Fortran::parser::ParseState state{cooked};
   state.set_inFixedForm(fixedForm);
   state.set_enableBackslashEscapesInCharLiterals(backslashEscapes);