[flang] More preprocessing.
authorpeter klausler <pklausler@nvidia.com>
Fri, 2 Feb 2018 23:52:43 +0000 (15:52 -0800)
committerpeter klausler <pklausler@nvidia.com>
Fri, 2 Feb 2018 23:52:43 +0000 (15:52 -0800)
Original-commit: flang-compiler/f18@a553505c3c99ec8744d5e3435bb14f4feb2f19c9

flang/f2018-demo.cc
flang/message.h
flang/preprocessor.cc
flang/preprocessor.h
flang/prescan.cc
flang/prescan.h

index de5a8b2..23c3794 100644 (file)
@@ -5,6 +5,7 @@
 #include "cooked-chars.h"
 #include "grammar.h"
 #include "idioms.h"
+#include "message.h"
 #include "parse-tree.h"
 #include "prescan.h"
 #include "source.h"
@@ -95,8 +96,8 @@ int main(int argc, char *const argv[]) {
     }
   }
 
-  std::stringstream error;
   Fortran::SourceFile source;
+  std::stringstream error;
   if (!source.Open(path, &error)) {
     std::cerr << error.str() << '\n';
     return 1;
@@ -106,16 +107,17 @@ int main(int argc, char *const argv[]) {
   size_t sourceBytes{source.bytes()};
   std::unique_ptr<char[]> prescanned;
   if (prescan) {
+    Fortran::Messages messages;
+    Fortran::Prescanner prescanner{messages};
     Fortran::CharBuffer
-      buffer{Fortran::Prescanner{&error}.
+      buffer{prescanner.
                set_fixedForm(fixedForm).
                set_enableBackslashEscapesInCharLiterals(backslashEscapes).
                set_fixedFormColumnLimit(columns).
                set_enableOldDebugLines(enableOldDebugLines).
                Prescan(source)};
-    std::string err{error.str()};
-    if (!err.empty()) {
-      std::cerr << err;
+    std::cerr << messages;
+    if (prescanner.anyFatalErrors()) {
       return 1;
     }
     sourceBytes = buffer.bytes();
index c98ea9b..cdf6b41 100644 (file)
@@ -19,6 +19,10 @@ using MessageContext = std::shared_ptr<Message>;
 
 class Message {
  public:
+  Message() {}
+  Message(const Message &) = default;
+  Message(Message &&) = default;
+
   Message(Position pos, const std::string &msg, MessageContext ctx = nullptr)
     : position_{pos}, message_{msg}, context_{ctx} {}
   Message(Position pos, std::string &&msg, MessageContext ctx = nullptr)
@@ -29,9 +33,6 @@ class Message {
     : position_{pos}, message_{"expected '"s + ch + '\''},
       context_{ctx} {}
 
-  Message() {}
-  Message(const Message &) = default;
-  Message(Message &&) = default;
   Message &operator=(const Message &that) = default;
   Message &operator=(Message &&that) = default;
 
index abfdc74..9fe3fff 100644 (file)
 
 namespace Fortran {
 
+bool CharPointerWithLength::IsBlank() const {
+  for (size_t j{0}; j < bytes_; ++j) {
+    char ch{data_[j]};
+    if (ch != ' ' && ch != '\t') {
+      return false;
+    }
+  }
+  return true;
+}
+
 void TokenSequence::Append(const TokenSequence &that) {
   if (nextStart_ < char_.size()) {
     start_.push_back(nextStart_);
@@ -26,7 +36,7 @@ void TokenSequence::Append(const TokenSequence &that) {
   nextStart_ = char_.size();
 }
 
-void TokenSequence::EmitWithCaseConversion(CharBuffer *out) {
+void TokenSequence::EmitWithCaseConversion(CharBuffer *out) const {
   size_t tokens{start_.size()};
   size_t chars{char_.size()};
   size_t atToken{0};
@@ -43,6 +53,10 @@ void TokenSequence::EmitWithCaseConversion(CharBuffer *out) {
   }
 }
 
+std::string TokenSequence::ToString() const {
+  return {&char_[0], char_.size()};
+}
+
 Definition::Definition(const TokenSequence &repl, size_t firstToken,
                        size_t tokens)
   : replacement_{Tokenize({}, repl, firstToken, tokens)} {}
@@ -81,13 +95,13 @@ TokenSequence Definition::Tokenize(const std::vector<std::string> &argNames,
   }
   TokenSequence result;
   for (size_t j{0}; j < tokens; ++j) {
-    size_t bytes{token.GetBytes(firstToken + j)};
+    size_t bytes{token[firstToken + j].size()};
     if (bytes == 0) {
       continue;
     }
-    const char *text{token.GetText(firstToken + j)};
+    const char *text{token[firstToken + j].data()};
     if (bytes > 0 && IsIdentifierFirstCharacter(*text)) {
-      auto it = args.find(token.GetString(firstToken + j));
+      auto it = args.find(token[firstToken + j].ToString());
       if (it != args.end()) {
         result.push_back(it->second);
         continue;
@@ -98,20 +112,9 @@ TokenSequence Definition::Tokenize(const std::vector<std::string> &argNames,
   return result;
 }
 
-static bool IsBlank(const CharPointerWithLength &cpl) {
-  size_t bytes{cpl.size()};
-  for (size_t j{0}; j < bytes; ++j) {
-    char ch{cpl[j]};
-    if (ch != ' ' && ch != '\t') {
-      return false;
-    }
-  }
-  return true;
-}
-
 TokenSequence Definition::Apply(const std::vector<TokenSequence> &args) {
   TokenSequence result;
-  bool stringify{false}, pasting{false};
+  bool pasting{false};
   size_t tokens{replacement_.size()};
   for (size_t j{0}; j < tokens; ++j) {
     const CharPointerWithLength &token{replacement_[j]};
@@ -122,27 +125,34 @@ TokenSequence Definition::Apply(const std::vector<TokenSequence> &args) {
       if (index >= args.size()) {
         continue;
       }
+      int lastNonBlank{static_cast<int>(result.size()) - 1};
+      for (; lastNonBlank >= 0; --lastNonBlank) {
+        if (!result[lastNonBlank].IsBlank()) {
+          break;
+        }
+      }
       size_t argTokens{args[index].size()};
-      if (stringify) {
+      if (lastNonBlank >= 0 && result[lastNonBlank].ToString() == "#") {
+        while (result.size() > static_cast<size_t>(lastNonBlank)) {
+          result.pop_back();
+        }
         std::string strung{'"'};
         for (size_t k{0}; k < argTokens; ++k) {
-          size_t argBytes{args[index].GetBytes(k)};
-          const char *arg{args[index].GetText(k)};
+          size_t argBytes{args[index][k].size()};
+          const char *arg{args[index][k].data()};
           for (size_t n{0}; n < argBytes; ++n) {
             char ch{arg[n]};
             if (ch == '"' || ch == '\\') {
-              strung += '\\';
+              strung += ch;
             }
             strung += ch;
           }
         }
-        strung += '"';
-        result.pop_back();  // remove the '#'
-        result.push_back(strung);
+        result.push_back(strung + '"');
       } else {
         for (size_t k{0}; k < argTokens; ++k) {
           const CharPointerWithLength &argToken{args[index][k]};
-          if (pasting && IsBlank(argToken)) {
+          if (pasting && argToken.IsBlank()) {
           } else {
             result.push_back(argToken);
             pasting = false;
@@ -152,19 +162,17 @@ TokenSequence Definition::Apply(const std::vector<TokenSequence> &args) {
     } else if (bytes == 2 && text[0] == '#' && text[1] == '#') {
       // Token pasting operator in body (not expanded argument); discard any
       // immediately preceding white space, then reopen the last token.
-      while (!result.empty() && IsBlank(result[result.size() - 1])) {
+      while (!result.empty() && result[result.size() - 1].IsBlank()) {
         result.pop_back();
       }
       if (!result.empty()) {
         result.ReopenLastToken();
         pasting = true;
       }
-    } else if (pasting && IsBlank(token)) {
+    } else if (pasting && token.IsBlank()) {
       // Delete whitespace immediately following ## in the body.
     } else {
-      stringify = bytes == 1 && *text == '#';
-      result.push_back(text, bytes);
-      pasting = false;
+      result.push_back(token);
     }
   }
   return result;
@@ -196,9 +204,9 @@ bool Preprocessor::MacroReplacement(const TokenSequence &input,
   size_t tokens{input.size()};
   size_t j;
   for (j = 0; j < tokens; ++j) {
-    size_t bytes{input.GetBytes(j)};
+    size_t bytes{input[j].size()};
     if (bytes > 0 &&
-        IsIdentifierFirstCharacter(*input.GetText(j)) &&
+        IsIdentifierFirstCharacter(input[j][0]) &&
         IsNameDefined(input[j])) {
       break;
     }
@@ -212,7 +220,7 @@ bool Preprocessor::MacroReplacement(const TokenSequence &input,
   }
   for (; j < tokens; ++j) {
     const CharPointerWithLength &token{input[j]};
-    if (IsBlank(token) || !IsIdentifierFirstCharacter(token[0])) {
+    if (token.IsBlank() || !IsIdentifierFirstCharacter(token[0])) {
       result->push_back(token);
       continue;
     }
@@ -251,7 +259,7 @@ bool Preprocessor::MacroReplacement(const TokenSequence &input,
     bool leftParen{false};
     while (++k < tokens) {
       const CharPointerWithLength &lookAhead{input[k]};
-      if (!IsBlank(lookAhead) && lookAhead[0] != '\n') {
+      if (!lookAhead.IsBlank() && lookAhead[0] != '\n') {
         leftParen = lookAhead[0] == '(' && lookAhead.size() == 1;
         break;
       }
@@ -262,8 +270,8 @@ bool Preprocessor::MacroReplacement(const TokenSequence &input,
     }
     std::vector<size_t> argStart{++k};
     for (int nesting{0}; k < tokens; ++k) {
-      if (input.GetBytes(k) == 1) {
-        char ch{*input.GetText(k)};
+      if (input[k].size() == 1) {
+        char ch{input[k][0]};
         if (ch == '(') {
           ++nesting;
         } else if (ch == ')') {
@@ -288,7 +296,7 @@ bool Preprocessor::MacroReplacement(const TokenSequence &input,
       size_t count{(k + 1 == argStart.size() ? j : argStart[k+1] - 1) - at};
       TokenSequence actual;
       for (; count-- > 0; ++at) {
-        actual.push_back(input.GetText(at), input.GetBytes(at));
+        actual.push_back(input[at].data(), input[at].size());
       }
       args.emplace_back(std::move(actual));
     }
@@ -307,7 +315,7 @@ TokenSequence Preprocessor::ReplaceMacros(const TokenSequence &tokens) {
 static size_t SkipBlanks(const TokenSequence &tokens, size_t at,
                          size_t lastToken) {
   for (; at < lastToken; ++at) {
-    if (!IsBlank(tokens[at])) {
+    if (!tokens[at].IsBlank()) {
       break;
     }
   }
@@ -335,7 +343,7 @@ static std::string ConvertToLowerCase(const std::string &str) {
 static std::string GetDirectiveName(const TokenSequence &line, size_t *rest) {
   size_t tokens{line.size()};
   size_t j{SkipBlanks(line, 0, tokens)};
-  if (j == tokens || line.GetString(j) != "#") {
+  if (j == tokens || line[j].ToString() != "#") {
     *rest = tokens;
     return {};
   }
@@ -345,70 +353,77 @@ static std::string GetDirectiveName(const TokenSequence &line, size_t *rest) {
     return {};
   }
   *rest = SkipBlanks(line, j + 1, tokens);
-  return ConvertToLowerCase(line.GetString(j));
+  return ConvertToLowerCase(line[j].ToString());
 }
 
-std::string Preprocessor::Directive(const TokenSequence &dir) {
+bool Preprocessor::Directive(const TokenSequence &dir) {
   size_t tokens{dir.size()};
   size_t j{SkipBlanks(dir, 0, tokens)};
   if (j == tokens) {
-    return {};
+    return true;
   }
-  if (dir.GetString(j) != "#") {
-    return "missing '#'";
+  if (dir[j].ToString() != "#") {
+    Complain("missing '#'");
+    return false;
   }
   j = SkipBlanks(dir, j + 1, tokens);
   if (j == tokens) {
-    return {};
+    return true;
   }
-  if (isdigit(*dir.GetText(j)) || *dir.GetText(j) == '"') {
-    return {};  // TODO: treat as #line
+  if (isdigit(dir[j][0]) || dir[j][0] == '"') {
+    return true;  // TODO: treat as #line
   }
-  std::string dirName{ConvertToLowerCase(dir.GetString(j))};
+  std::string dirName{ConvertToLowerCase(dir[j].ToString())};
   j = SkipBlanks(dir, j + 1, tokens);
   CharPointerWithLength nameToken;
-  if (j < tokens && IsIdentifierFirstCharacter(*dir.GetText(j))) {
+  if (j < tokens && IsIdentifierFirstCharacter(dir[j][0])) {
     nameToken = dir[j];
   }
   if (dirName == "line") {
     // TODO
-    return {};
+    return true;
   }
   if (dirName == "define") {
     if (nameToken.empty()) {
-      return "#define: missing or invalid name";
+      Complain("#define: missing or invalid name");
+      return false;
     }
     nameToken = SaveToken(nameToken);
     definitions_.erase(nameToken);
-    if (++j < tokens && dir.GetBytes(j) == 1 && *dir.GetText(j) == '(') {
+    if (++j < tokens && dir[j].size() == 1 && dir[j][0] == '(') {
       j = SkipBlanks(dir, j + 1, tokens);
       std::vector<std::string> argName;
-      if (dir.GetString(j) != ")") {
+      if (dir[j].ToString() != ")") {
         while (true) {
-          std::string an{dir.GetString(j)};
+          std::string an{dir[j].ToString()};
           if (an.empty() || !IsIdentifierFirstCharacter(an[0])) {
-            return "#define: missing or invalid argument name";
+            Complain("#define: missing or invalid argument name");
+            return false;
           }
           argName.push_back(an);
           j = SkipBlanks(dir, j + 1, tokens);
           if (j == tokens) {
-            return "#define: malformed argument list";
+            Complain("#define: malformed argument list");
+            return false;
           }
-          std::string punc{dir.GetString(j)};
+          std::string punc{dir[j].ToString()};
           if (punc == ")") {
             break;
           }
           if (punc != ",") {
-            return "#define: malformed argument list";
+            Complain("#define: malformed argument list");
+            return false;
           }
           j = SkipBlanks(dir, j + 1, tokens);
           if (j == tokens) {
-            return "#define: malformed argument list";
+            Complain("#define: malformed argument list");
+            return false;
           }
         }
         if (std::set<std::string>(argName.begin(), argName.end()).size() !=
             argName.size()) {
-          return "#define: argument names are not distinct";
+          Complain("#define: argument names are not distinct");
+          return false;
         }
       }
       j = SkipBlanks(dir, j + 1, tokens);
@@ -418,80 +433,91 @@ std::string Preprocessor::Directive(const TokenSequence &dir) {
       definitions_.emplace(
         std::make_pair(nameToken, Definition{dir, j, tokens - j}));
     }
-    return {};
+    return true;
   }
   if (dirName == "undef") {
     if (nameToken.empty()) {
-      return "#undef: missing or invalid name";
+      Complain("# missing or invalid name");
+      return false;
     }
     j = SkipBlanks(dir, j + 1, tokens);
     if (j != tokens) {
-      return "#undef: excess tokens at end of directive";
+      Complain("#undef: excess tokens at end of directive");
+      return false;
     }
     definitions_.erase(nameToken);
-    return {};
+    return true;
   }
   if (dirName == "ifdef" || dirName == "ifndef") {
     if (nameToken.empty()) {
-      return "#"s + dirName + ": missing name";
+      Complain("#"s + dirName + ": missing name");
+      return false;
     }
     j = SkipBlanks(dir, j + 1, tokens);
     if (j != tokens) {
-      return "#"s + dirName + ": excess tokens at end of directive";
+      Complain("#"s + dirName + ": excess tokens at end of directive");
+      return false;
     }
     if (IsNameDefined(nameToken) == (dirName == "ifdef")) {
       ifStack_.push(CanDeadElseAppear::Yes);
-      return {};
+      return true;
     }
     return SkipDisabledConditionalCode(dirName, IsElseActive::Yes);
   }
   if (dirName == "if") {
-    std::string errors;
-    if (IsIfPredicateTrue(dir, j, tokens - j, &errors) || !errors.empty()) {
+    if (IsIfPredicateTrue(dir, j, tokens - j)) {
       ifStack_.push(CanDeadElseAppear::Yes);
-    } else {
-      errors = SkipDisabledConditionalCode(dirName, IsElseActive::Yes);
+      return true;
     }
-    return errors.empty() ? ""s : "#if: "s + errors;
+    return SkipDisabledConditionalCode(dirName, IsElseActive::Yes);
   }
   if (dirName == "else") {
     if (j != tokens) {
-      return "#else: excess tokens at end of directive";
+      Complain("#else: excess tokens at end of directive");
+      return false;
     }
     if (ifStack_.empty()) {
-      return "#else: not nested within #if, #ifdef, or #ifndef";
+      Complain("#else: not nested within #if, #ifdef, or #ifndef");
+      return false;
     }
     if (ifStack_.top() != CanDeadElseAppear::Yes) {
-      return "#else: already appeared within this #if, #ifdef, or #ifndef";
+      Complain("#else: already appeared within this #if, #ifdef, or #ifndef");
+      return false;
     }
     ifStack_.pop();
     return SkipDisabledConditionalCode("else", IsElseActive::No);
   }
   if (dirName == "elif") {
     if (ifStack_.empty()) {
-      return "#elif: not nested within #if, #ifdef, or #ifndef";
+      Complain("#elif: not nested within #if, #ifdef, or #ifndef");
+      return false;
     }
     if (ifStack_.top() != CanDeadElseAppear::Yes) {
-      return "#elif: #else previously appeared within this "
-             "#if, #ifdef, or #ifndef";
+      Complain("#elif: #else previously appeared within this "
+               "#if, #ifdef, or #ifndef");
+      return false;
     }
     ifStack_.pop();
     return SkipDisabledConditionalCode("elif", IsElseActive::No);
   }
   if (dirName == "endif") {
     if (j != tokens) {
-      return "#endif: excess tokens at end of directive";
+      Complain("#endif: excess tokens at end of directive");
+      return false;
     }
     if (ifStack_.empty()) {
-      return "#endif: no #if, #ifdef, or #ifndef";
+      Complain("#endif: no #if, #ifdef, or #ifndef");
+      return false;
     }
     ifStack_.pop();
-    return {};
+    return true;
   }
   if (dirName == "error" || dirName == "warning") {
-    return {dir.data(), dir.size()};
+    Complain(dir.ToString());
+    return dirName != "error";
   }
-  return "#"s + dirName + ": unknown or unimplemented directive";
+  Complain("#"s + dirName + ": unknown or unimplemented directive");
+  return false;
 }
 
 CharPointerWithLength Preprocessor::SaveToken(const CharPointerWithLength &t) {
@@ -503,7 +529,7 @@ bool Preprocessor::IsNameDefined(const CharPointerWithLength &token) {
   return definitions_.find(token) != definitions_.end();
 }
 
-std::string
+bool
 Preprocessor::SkipDisabledConditionalCode(const std::string &dirName,
                                           IsElseActive isElseActive) {
   int nesting{0};
@@ -514,24 +540,23 @@ Preprocessor::SkipDisabledConditionalCode(const std::string &dirName,
       ++nesting;
     } else if (dn == "endif") {
       if (nesting-- == 0) {
-        return {};
-      }
-    } else if (isElseActive == IsElseActive::Yes && nesting == 0) {
-      if (dn == "else") {
-        ifStack_.push(CanDeadElseAppear::No);
-        return {};
-      }
-      if (dn == "elif") {
-        std::string errors;
-        if (IsIfPredicateTrue(*line, rest, line->size() - rest, &errors) ||
-            !errors.empty()) {
-          ifStack_.push(CanDeadElseAppear::No);
-          return errors.empty() ? ""s : "#elif: "s + errors;
-        }
+        return true;
       }
+    } else if (isElseActive == IsElseActive::Yes &&
+               nesting == 0 &&
+               (dn == "else" ||
+                (dn == "elif" &&
+                 IsIfPredicateTrue(*line, rest, line->size() - rest)))) {
+      ifStack_.push(CanDeadElseAppear::No);
+      return true;
     }
   }
-  return "#"s + dirName + ": missing #endif";
+  Complain("#"s + dirName + ": missing #endif");
+  return false;
+}
+
+void Preprocessor::Complain(const std::string &message) {
+  prescanner_.messages().Add({prescanner_.position(), message});
 }
 
 // Precedence level codes used here to accommodate mixed Fortran and C:
@@ -611,7 +636,7 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
     *errors = "incomplete expression";
     return 0;
   }
-  std::string t{token.GetString(*atToken)};
+  std::string t{token[*atToken].ToString()};
   enum Operator op;
 
   // Parse and evaluate a primary or a unary operator and its operand.
@@ -634,8 +659,8 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
   } else if (t == "-") {
     op = UMINUS;
   } else if (t == "." && *atToken + 2 < tokens &&
-             ConvertToLowerCase(token.GetString(*atToken + 1)) == "not" &&
-             token.GetString(*atToken + 2) == ".") {
+             ConvertToLowerCase(token[*atToken + 1].ToString()) == "not" &&
+             token[*atToken + 2].ToString() == ".") {
     op = NOT;
     *atToken += 2;
   } else {
@@ -655,7 +680,7 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
     left = ExpressionValue(token, operandPrecedence[op], atToken, errors);
     switch (op) {
     case PARENS:
-      if (*atToken < tokens && token.GetString(*atToken) == ")") {
+      if (*atToken < tokens && token[*atToken].ToString() == ")") {
         ++*atToken;
       } else if (errors->empty()) {
         *errors = "')' missing from expression";
@@ -684,10 +709,10 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
 
   // Parse and evaluate a binary operator and its second operand, if present.
   int advance{1};
-  t = token.GetString(*atToken);
+  t = token[*atToken].ToString();
   if (t == "." && *atToken + 2 < tokens &&
-      token.GetString(*atToken + 2) == ".") {
-    t += ConvertToLowerCase(token.GetString(*atToken + 1)) + '.';
+      token[*atToken + 2].ToString() == ".") {
+    t += ConvertToLowerCase(token[*atToken + 1].ToString()) + '.';
     advance = 3;
   }
   auto it = opNameMap.find(t);
@@ -795,15 +820,15 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
 
 bool
 Preprocessor::IsIfPredicateTrue(const TokenSequence &expr, size_t first,
-                                size_t exprTokens, std::string *errors) {
+                                size_t exprTokens) {
   TokenSequence expr1{StripBlanks(expr, first, first + exprTokens)};
   TokenSequence expr2;
   for (size_t j{0}; j < expr1.size(); ++j) {
-    if (ConvertToLowerCase(expr1.GetString(j)) == "defined") {
+    if (ConvertToLowerCase(expr1[j].ToString()) == "defined") {
       CharPointerWithLength name;
       if (j + 3 < expr1.size() &&
-          expr1.GetString(j + 1) == "(" &&
-          expr1.GetString(j + 3) == ")") {
+          expr1[j + 1].ToString() == "(" &&
+          expr1[j + 3].ToString() == ")") {
         name = expr1[j + 2];
         j += 3;
       } else if (j + 1 < expr1.size() &&
@@ -820,10 +845,13 @@ Preprocessor::IsIfPredicateTrue(const TokenSequence &expr, size_t first,
   TokenSequence expr3{ReplaceMacros(expr2)};
   TokenSequence expr4{StripBlanks(expr3, 0, expr3.size())};
   size_t atToken{0};
-  bool result{ExpressionValue(expr4, 0, &atToken, errors) != 0};
-  if (atToken < expr4.size() && errors->empty()) {
-    *errors = atToken == 0 ? "could not parse any expression"
-                           : "excess characters after expression";
+  std::string error;
+  bool result{ExpressionValue(expr4, 0, &atToken, &error) != 0};
+  if (!error.empty()) {
+    Complain(error);
+  } else if (atToken < expr4.size()) {
+    Complain(atToken == 0 ? "could not parse any expression"
+                          : "excess characters after expression");
   }
   return result;
 }
index ad9fee1..29f8d1b 100644 (file)
@@ -12,8 +12,8 @@
 #include <cstring>
 #include <functional>
 #include <list>
-#include <sstream>
 #include <stack>
+#include <sstream>
 #include <string>
 #include <unordered_map>
 #include <vector>
@@ -44,6 +44,7 @@ class CharPointerWithLength {
   const char *data() const { return data_; }
   const char &operator[](size_t j) const { return data_[j]; }
 
+  bool IsBlank() const;
   std::string ToString() const { return std::string{data_, bytes_}; }
 
  private:
@@ -99,18 +100,10 @@ class TokenSequence {
     return *this;
   }
 
-  size_t GetBytes(size_t token) const {
-    return (token + 1 >= start_.size() ? char_.size() : start_[token + 1]) -
-           start_[token];
-  }
-  const char *GetText(size_t token) const {
-    return &char_[start_[token]];
-  }
-  std::string GetString(size_t token) const {
-    return std::string(GetText(token), GetBytes(token));
-  }
   CharPointerWithLength operator[](size_t token) const {
-    return {GetText(token), GetBytes(token)};
+    return {&char_[start_[token]],
+            (token + 1 >= start_.size() ? char_.size() : start_[token + 1]) -
+              start_[token]};
   }
 
   void AddChar(char ch) {
@@ -129,7 +122,8 @@ class TokenSequence {
   }
 
   void Append(const TokenSequence &);
-  void EmitWithCaseConversion(CharBuffer *);
+  void EmitWithCaseConversion(CharBuffer *) const;
+  std::string ToString() const;
 
   bool empty() const { return start_.empty(); }
   size_t size() const { return start_.size(); }
@@ -152,16 +146,6 @@ class TokenSequence {
     push_back(t.data(), t.size());
   }
 
-#if 0
-  void push_back(const std::string &s) {
-    size_t bytes{s.size()};
-    for (size_t j{0}; j < bytes; ++j) {
-      AddChar(s[j]);
-    }
-    EndToken();
-  }
-#endif
-
   void push_back(const std::stringstream &ss) { push_back(ss.str()); }
 
   void pop_back() {
@@ -224,21 +208,20 @@ class Preprocessor {
   // return value is false.
   bool MacroReplacement(const TokenSequence &, TokenSequence *);
 
-  // Implements a preprocessor directive; returns an error message, or an
-  // empty string when successful.
-  std::string Directive(const TokenSequence &);
+  // Implements a preprocessor directive; returns true when no fatal error.
+  bool Directive(const TokenSequence &);
 
  private:
   enum class IsElseActive { No, Yes };
   enum class CanDeadElseAppear { No, Yes };
 
+  void Complain(const std::string &);
   CharPointerWithLength SaveToken(const CharPointerWithLength &);
   bool IsNameDefined(const CharPointerWithLength &);
   TokenSequence ReplaceMacros(const TokenSequence &);
-  std::string SkipDisabledConditionalCode(const std::string &dirName,
-                                          IsElseActive);
+  bool SkipDisabledConditionalCode(const std::string &dirName, IsElseActive);
   bool IsIfPredicateTrue(const TokenSequence &expr, size_t first,
-                         size_t exprTokens, std::string *errors);
+                         size_t exprTokens);
 
   Prescanner &prescanner_;
   std::list<std::string> names_;
index 2c5a27b..9d1a0d9 100644 (file)
@@ -398,12 +398,8 @@ bool Prescanner::CommentLinesAndPreprocessorDirectives() {
         IsFreeFormComment(lineStart_)) {
       NextLine();
     } else if (IsPreprocessorDirectiveLine(lineStart_)) {
-      auto here = lineStartPosition_;
       if (std::optional<TokenSequence> tokens{NextTokenizedLine()}) {
-        std::string err{preprocessor_.Directive(*tokens)};
-        if (!err.empty()) {
-          *error_ << here << ' ' << err << '\n';
-        }
+        anyFatalErrors_ |= !preprocessor_.Directive(*tokens);
       }
     } else {
       break;
index 4e09ed5..22d12f8 100644 (file)
 // preprocessing and INCLUDE lines need not be handled.
 
 #include "char-buffer.h"
+#include "message.h"
 #include "position.h"
 #include "preprocessor.h"
 #include "source.h"
 #include <optional>
-#include <sstream>
 
 namespace Fortran {
 
 class Prescanner {
  public:
-  explicit Prescanner(std::stringstream *err)
-    : error_{err}, preprocessor_{*this} {}
+  explicit Prescanner(Messages &messages)
+    : messages_{messages}, preprocessor_{*this} {}
 
+  Messages &messages() const { return messages_; }
   const SourceFile &sourceFile() const { return *sourceFile_; }
   Position position() const { return atPosition_; }
+  bool anyFatalErrors() const { return anyFatalErrors_; }
 
   Prescanner &set_fixedForm(bool yes) {
     inFixedForm_ = yes;
@@ -87,7 +89,8 @@ class Prescanner {
   bool FreeFormContinuation();
   void PayNewlineDebt(CharBuffer *);
 
-  std::stringstream *error_;
+  Messages &messages_;
+  bool anyFatalErrors_{false};
   const char *lineStart_{nullptr};  // next line to process; <= limit_
   const char *at_{nullptr};  // next character to process; < lineStart_
   int column_{1};  // card image column position of next character