[flang] Predefined macros (__FILE__, __LINE__, __DATE__, __TIME__).
authorpeter klausler <pklausler@nvidia.com>
Thu, 1 Feb 2018 23:01:23 +0000 (15:01 -0800)
committerpeter klausler <pklausler@nvidia.com>
Thu, 1 Feb 2018 23:01:23 +0000 (15:01 -0800)
Original-commit: flang-compiler/f18@8c7f51aa87d711ab63f343cf4ba0131cf3728264

flang/preprocessor.cc
flang/preprocessor.h
flang/prescan.cc
flang/prescan.h
flang/source.h

index 514cb03..abfdc74 100644 (file)
@@ -5,9 +5,11 @@
 #include <algorithm>
 #include <cctype>
 #include <cinttypes>
+#include <ctime>
 #include <map>
 #include <memory>
 #include <set>
+#include <sstream>
 #include <utility>
 
 namespace Fortran {
@@ -51,6 +53,9 @@ Definition::Definition(const std::vector<std::string> &argNames,
   : isFunctionLike_{true}, argumentCount_(argNames.size()),
     replacement_{Tokenize(argNames, repl, firstToken, tokens)} {}
 
+Definition::Definition(const std::string &predefined)
+  : isPredefined_{true}, replacement_{predefined} {}
+
 bool Definition::set_isDisabled(bool disable) {
   bool was{isDisabled_};
   isDisabled_ = disable;
@@ -109,7 +114,7 @@ TokenSequence Definition::Apply(const std::vector<TokenSequence> &args) {
   bool stringify{false}, pasting{false};
   size_t tokens{replacement_.size()};
   for (size_t j{0}; j < tokens; ++j) {
-    const CharPointerWithLength &token{replacement_.GetToken(j)};
+    const CharPointerWithLength &token{replacement_[j]};
     size_t bytes{token.size()};
     const char *text{token.data()};
     if (bytes == 2 && *text == '~') {
@@ -136,7 +141,7 @@ TokenSequence Definition::Apply(const std::vector<TokenSequence> &args) {
         result.push_back(strung);
       } else {
         for (size_t k{0}; k < argTokens; ++k) {
-          const CharPointerWithLength &argToken{args[index].GetToken(k)};
+          const CharPointerWithLength &argToken{args[index][k]};
           if (pasting && IsBlank(argToken)) {
           } else {
             result.push_back(argToken);
@@ -147,8 +152,7 @@ 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.GetToken(result.size() - 1))) {
+      while (!result.empty() && IsBlank(result[result.size() - 1])) {
         result.pop_back();
       }
       if (!result.empty()) {
@@ -166,19 +170,36 @@ TokenSequence Definition::Apply(const std::vector<TokenSequence> &args) {
   return result;
 }
 
+static std::string FormatTime(const std::time_t &now, const char *format) {
+  char buffer[16];
+  return {buffer,
+          std::strftime(buffer, sizeof buffer, format, std::localtime(&now))};
+}
+
+Preprocessor::Preprocessor(Prescanner &ps) : prescanner_{ps} {
+  // 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(SaveToken("__DATE__"s),  // e.g., "Jun 16 1904"
+                       Definition{FormatTime(now, "\"%h %e %Y\""), 0, 1});
+  definitions_.emplace(SaveToken("__TIME__"s),  // e.g., "23:59:60"
+                       Definition{FormatTime(now, "\"%T\""), 0, 1});
+  // The values of these predefined macros depend on their invocation sites.
+  definitions_.emplace(SaveToken("__FILE__"s), Definition{"__FILE__"s});
+  definitions_.emplace(SaveToken("__LINE__"s), Definition{"__LINE__"s});
+}
+
 bool Preprocessor::MacroReplacement(const TokenSequence &input,
                                     TokenSequence *result) {
   // Do quick scan for any use of a defined name.
-  if (definitions_.empty()) {
-    return false;
-  }
   size_t tokens{input.size()};
   size_t j;
   for (j = 0; j < tokens; ++j) {
     size_t bytes{input.GetBytes(j)};
     if (bytes > 0 &&
         IsIdentifierFirstCharacter(*input.GetText(j)) &&
-        IsNameDefined(input.GetToken(j))) {
+        IsNameDefined(input[j])) {
       break;
     }
   }
@@ -187,10 +208,10 @@ bool Preprocessor::MacroReplacement(const TokenSequence &input,
   }
 
   for (size_t k{0}; k < j; ++k) {
-    result->push_back(input.GetToken(k));
+    result->push_back(input[k]);
   }
   for (; j < tokens; ++j) {
-    const CharPointerWithLength &token{input.GetToken(j)};
+    const CharPointerWithLength &token{input[j]};
     if (IsBlank(token) || !IsIdentifierFirstCharacter(token[0])) {
       result->push_back(token);
       continue;
@@ -206,10 +227,21 @@ bool Preprocessor::MacroReplacement(const TokenSequence &input,
       continue;
     }
     if (!def.isFunctionLike()) {
+      if (def.isPredefined()) {
+        std::string name{def.replacement()[0].ToString()};
+        if (name == "__FILE__") {
+          result->Append("\""s + prescanner_.sourceFile().path() + '"');
+          continue;
+        }
+        if (name == "__LINE__") {
+          std::stringstream ss;
+          ss << prescanner_.position().lineNumber();
+          result->Append(ss.str());
+          continue;
+        }
+      }
       def.set_isDisabled(true);
-      TokenSequence repl;
-      result->Append(MacroReplacement(def.replacement(), &repl) ? repl
-                       : def.replacement());
+      result->Append(ReplaceMacros(def.replacement()));
       def.set_isDisabled(false);
       continue;
     }
@@ -218,7 +250,7 @@ bool Preprocessor::MacroReplacement(const TokenSequence &input,
     size_t k{j};
     bool leftParen{false};
     while (++k < tokens) {
-      const CharPointerWithLength &lookAhead{input.GetToken(k)};
+      const CharPointerWithLength &lookAhead{input[k]};
       if (!IsBlank(lookAhead) && lookAhead[0] != '\n') {
         leftParen = lookAhead[0] == '(' && lookAhead.size() == 1;
         break;
@@ -260,19 +292,22 @@ bool Preprocessor::MacroReplacement(const TokenSequence &input,
       }
       args.emplace_back(std::move(actual));
     }
-    TokenSequence repl{def.Apply(args)};
     def.set_isDisabled(true);
-    TokenSequence rescanned;
-    result->Append(MacroReplacement(repl, &rescanned) ? rescanned : repl);
+    result->Append(ReplaceMacros(def.Apply(args)));
     def.set_isDisabled(false);
   }
   return true;
 }
 
+TokenSequence Preprocessor::ReplaceMacros(const TokenSequence &tokens) {
+  TokenSequence repl;
+  return MacroReplacement(tokens, &repl) ? repl : tokens;
+}
+
 static size_t SkipBlanks(const TokenSequence &tokens, size_t at,
                          size_t lastToken) {
   for (; at < lastToken; ++at) {
-    if (!IsBlank(tokens.GetToken(at))) {
+    if (!IsBlank(tokens[at])) {
       break;
     }
   }
@@ -284,7 +319,7 @@ static TokenSequence StripBlanks(const TokenSequence &token, size_t first,
   TokenSequence noBlanks;
   for (size_t j{SkipBlanks(token, first, tokens)}; j < tokens;
        j = SkipBlanks(token, j + 1, tokens)) {
-    noBlanks.push_back(token.GetToken(j));
+    noBlanks.push_back(token[j]);
   }
   return noBlanks;
 }
@@ -331,11 +366,9 @@ std::string Preprocessor::Directive(const TokenSequence &dir) {
   }
   std::string dirName{ConvertToLowerCase(dir.GetString(j))};
   j = SkipBlanks(dir, j + 1, tokens);
-  std::string nameString;
   CharPointerWithLength nameToken;
   if (j < tokens && IsIdentifierFirstCharacter(*dir.GetText(j))) {
-    nameString = dir.GetString(j);
-    nameToken = dir.GetToken(j);
+    nameToken = dir[j];
   }
   if (dirName == "line") {
     // TODO
@@ -345,11 +378,7 @@ std::string Preprocessor::Directive(const TokenSequence &dir) {
     if (nameToken.empty()) {
       return "#define: missing or invalid name";
     }
-    // Get a pointer to a "permanent" copy of the name for use as the
-    // key in the definitions_ map.
-    names_.push_back(nameString);
-    nameToken = CharPointerWithLength{names_.back().data(),
-                                      names_.back().size()};
+    nameToken = SaveToken(nameToken);
     definitions_.erase(nameToken);
     if (++j < tokens && dir.GetBytes(j) == 1 && *dir.GetText(j) == '(') {
       j = SkipBlanks(dir, j + 1, tokens);
@@ -465,6 +494,11 @@ std::string Preprocessor::Directive(const TokenSequence &dir) {
   return "#"s + dirName + ": unknown or unimplemented directive";
 }
 
+CharPointerWithLength Preprocessor::SaveToken(const CharPointerWithLength &t) {
+  names_.push_back(t.ToString());
+  return {names_.back().data(), names_.back().size()};
+}
+
 bool Preprocessor::IsNameDefined(const CharPointerWithLength &token) {
   return definitions_.find(token) != definitions_.end();
 }
@@ -473,7 +507,7 @@ std::string
 Preprocessor::SkipDisabledConditionalCode(const std::string &dirName,
                                           IsElseActive isElseActive) {
   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") {
@@ -762,36 +796,32 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
 bool
 Preprocessor::IsIfPredicateTrue(const TokenSequence &expr, size_t first,
                                 size_t exprTokens, std::string *errors) {
-  TokenSequence noBlanks{StripBlanks(expr, first, first + exprTokens)};
+  TokenSequence expr1{StripBlanks(expr, first, first + exprTokens)};
   TokenSequence expr2;
-  for (size_t j{0}; j < noBlanks.size(); ++j) {
-    if (ConvertToLowerCase(noBlanks.GetString(j)) == "defined") {
+  for (size_t j{0}; j < expr1.size(); ++j) {
+    if (ConvertToLowerCase(expr1.GetString(j)) == "defined") {
       CharPointerWithLength name;
-      if (j + 3 < noBlanks.size() &&
-          noBlanks.GetString(j + 1) == "(" &&
-          noBlanks.GetString(j + 3) == ")") {
-        name = noBlanks.GetToken(j + 2);
+      if (j + 3 < expr1.size() &&
+          expr1.GetString(j + 1) == "(" &&
+          expr1.GetString(j + 3) == ")") {
+        name = expr1[j + 2];
         j += 3;
-      } else if (j + 1 < noBlanks.size() &&
-                 IsIdentifierFirstCharacter(noBlanks.GetToken(j + 1))) {
-        name = noBlanks.GetToken(j++);
+      } else if (j + 1 < expr1.size() &&
+                 IsIdentifierFirstCharacter(expr1[j + 1])) {
+        name = expr1[j++];
       }
       if (!name.empty()) {
         expr2.push_back(IsNameDefined(name) ? "1" : "0", 1);
         continue;
       }
     }
-    expr2.push_back(noBlanks.GetToken(j));
-  }
-  TokenSequence repl;
-  if (MacroReplacement(expr2, &repl)) {
-    repl = StripBlanks(repl, 0, repl.size());
-  } else {
-    repl = std::move(expr2);
+    expr2.push_back(expr1[j]);
   }
+  TokenSequence expr3{ReplaceMacros(expr2)};
+  TokenSequence expr4{StripBlanks(expr3, 0, expr3.size())};
   size_t atToken{0};
-  bool result{ExpressionValue(repl, 0, &atToken, errors) != 0};
-  if (atToken < repl.size() && errors->empty()) {
+  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";
   }
index eb9de47..ad9fee1 100644 (file)
@@ -12,6 +12,7 @@
 #include <cstring>
 #include <functional>
 #include <list>
+#include <sstream>
 #include <stack>
 #include <string>
 #include <unordered_map>
@@ -28,6 +29,8 @@ class CharPointerWithLength {
  public:
   CharPointerWithLength() {}
   CharPointerWithLength(const char *x, size_t n) : data_{x}, bytes_{n} {}
+  CharPointerWithLength(const std::string &s)
+    : data_{s.data()}, bytes_{s.size()} {}
   CharPointerWithLength(const CharPointerWithLength &that)
     : data_{that.data_}, bytes_{that.bytes_} {}
   CharPointerWithLength &operator=(const CharPointerWithLength &that) {
@@ -41,6 +44,8 @@ class CharPointerWithLength {
   const char *data() const { return data_; }
   const char &operator[](size_t j) const { return data_[j]; }
 
+  std::string ToString() const { return std::string{data_, bytes_}; }
+
  private:
   const char *data_{nullptr};
   size_t bytes_{0};
@@ -76,9 +81,17 @@ namespace Fortran {
 class TokenSequence {
  public:
   TokenSequence() {}
+  TokenSequence(const TokenSequence &that) { Append(that); }
   TokenSequence(TokenSequence &&that)
     : start_{std::move(that.start_)}, nextStart_{that.nextStart_},
       char_{std::move(that.char_)} {}
+  TokenSequence(const std::string &s) { push_back(s); }
+
+  TokenSequence &operator=(const TokenSequence &that) {
+    clear();
+    Append(that);
+    return *this;
+  }
   TokenSequence &operator=(TokenSequence &&that) {
     start_ = std::move(that.start_);
     nextStart_ = that.nextStart_;
@@ -96,7 +109,7 @@ class TokenSequence {
   std::string GetString(size_t token) const {
     return std::string(GetText(token), GetBytes(token));
   }
-  CharPointerWithLength GetToken(size_t token) const {
+  CharPointerWithLength operator[](size_t token) const {
     return {GetText(token), GetBytes(token)};
   }
 
@@ -116,13 +129,10 @@ class TokenSequence {
   }
 
   void Append(const TokenSequence &);
-
   void EmitWithCaseConversion(CharBuffer *);
 
   bool empty() const { return start_.empty(); }
-
   size_t size() const { return start_.size(); }
-
   const char *data() const { return &char_[0]; }
 
   void clear() {
@@ -131,12 +141,6 @@ class TokenSequence {
     char_.clear();
   }
 
-  void pop_back() {
-    nextStart_ = start_.back();
-    start_.pop_back();
-    char_.resize(nextStart_);
-  }
-
   void push_back(const char *s, size_t bytes) {
     for (size_t j{0}; j < bytes; ++j) {
       AddChar(s[j]);
@@ -145,13 +149,10 @@ class TokenSequence {
   }
 
   void push_back(const CharPointerWithLength &t) {
-    size_t bytes{t.size()};
-    for (size_t j{0}; j < bytes; ++j) {
-      AddChar(t[j]);
-    }
-    EndToken();
+    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) {
@@ -159,6 +160,15 @@ class TokenSequence {
     }
     EndToken();
   }
+#endif
+
+  void push_back(const std::stringstream &ss) { push_back(ss.str()); }
+
+  void pop_back() {
+    nextStart_ = start_.back();
+    start_.pop_back();
+    char_.resize(nextStart_);
+  }
 
   void shrink_to_fit() {
     start_.shrink_to_fit();
@@ -177,11 +187,13 @@ class Definition {
   Definition(const TokenSequence &, size_t firstToken, size_t tokens);
   Definition(const std::vector<std::string> &argNames, const TokenSequence &,
              size_t firstToken, size_t tokens);
+  explicit Definition(const std::string &predefined);
 
   bool isFunctionLike() const { return isFunctionLike_; }
   size_t argumentCount() const { return argumentCount_; }
   bool isVariadic() const { return isVariadic_; }
   bool isDisabled() const { return isDisabled_; }
+  bool isPredefined() const { return isPredefined_; }
   const TokenSequence &replacement() const { return replacement_; }
 
   bool set_isDisabled(bool disable);
@@ -197,13 +209,14 @@ class Definition {
   size_t argumentCount_{0};
   bool isVariadic_{false};
   bool isDisabled_{false};
+  bool isPredefined_{false};
   TokenSequence replacement_;
 };
 
 // Preprocessing state
 class Preprocessor {
  public:
-  Preprocessor(Prescanner *ps) : prescanner_{ps} {}
+  explicit Preprocessor(Prescanner &);
 
   // When the input contains macros to be replaced, the new token sequence
   // is appended to the output and the returned value is true.  When
@@ -218,16 +231,19 @@ class Preprocessor {
  private:
   enum class IsElseActive { No, Yes };
   enum class CanDeadElseAppear { No, Yes };
+
+  CharPointerWithLength SaveToken(const CharPointerWithLength &);
   bool IsNameDefined(const CharPointerWithLength &);
+  TokenSequence ReplaceMacros(const TokenSequence &);
   std::string SkipDisabledConditionalCode(const std::string &dirName,
                                           IsElseActive);
   bool IsIfPredicateTrue(const TokenSequence &expr, size_t first,
                          size_t exprTokens, std::string *errors);
 
+  Prescanner &prescanner_;
   std::list<std::string> names_;
   std::unordered_map<CharPointerWithLength, Definition> definitions_;
   std::stack<CanDeadElseAppear> ifStack_;
-  Prescanner *prescanner_;
 };
 }  // namespace Fortran
 #endif  // FORTRAN_PREPROCESSOR_H_
index 3763f53..2c5a27b 100644 (file)
@@ -10,6 +10,7 @@
 namespace Fortran {
 
 CharBuffer Prescanner::Prescan(const SourceFile &source) {
+  sourceFile_ = &source;
   lineStart_ = source.content();
   limit_ = lineStart_ + source.bytes();
   CharBuffer out;
@@ -46,6 +47,7 @@ CharBuffer Prescanner::Prescan(const SourceFile &source) {
     PayNewlineDebt(&out);
   }
   PayNewlineDebt(&out);
+  sourceFile_ = nullptr;
   return std::move(out);
 }
 
index 1586833..4e09ed5 100644 (file)
@@ -24,7 +24,10 @@ namespace Fortran {
 class Prescanner {
  public:
   explicit Prescanner(std::stringstream *err)
-    : error_{err}, preprocessor_{this} {}
+    : error_{err}, preprocessor_{*this} {}
+
+  const SourceFile &sourceFile() const { return *sourceFile_; }
+  Position position() const { return atPosition_; }
 
   Prescanner &set_fixedForm(bool yes) {
     inFixedForm_ = yes;
@@ -90,6 +93,7 @@ class Prescanner {
   int column_{1};  // card image column position of next character
   const char *limit_{nullptr};  // first address after end of source
   int newlineDebt_{0};  // newline characters consumed but not yet emitted
+  const SourceFile *sourceFile_{nullptr};
   Position atPosition_, lineStartPosition_;
   bool inCharLiteral_{false};
   bool inPreprocessorDirective_{false};
index bfdc613..5088086 100644 (file)
@@ -16,6 +16,7 @@ class SourceFile {
   ~SourceFile();
   bool Open(std::string path, std::stringstream *error);
   void Close();
+  std::string path() const { return path_; }
   const char *content() const { return content_; }
   size_t bytes() const { return bytes_; }
  private: