From: peter klausler Date: Thu, 1 Feb 2018 23:01:23 +0000 (-0800) Subject: [flang] Predefined macros (__FILE__, __LINE__, __DATE__, __TIME__). X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ad7125ffb0aa13262a44699ac16e0614d00e1046;p=platform%2Fupstream%2Fllvm.git [flang] Predefined macros (__FILE__, __LINE__, __DATE__, __TIME__). Original-commit: flang-compiler/f18@8c7f51aa87d711ab63f343cf4ba0131cf3728264 --- diff --git a/flang/preprocessor.cc b/flang/preprocessor.cc index 514cb03..abfdc74 100644 --- a/flang/preprocessor.cc +++ b/flang/preprocessor.cc @@ -5,9 +5,11 @@ #include #include #include +#include #include #include #include +#include #include namespace Fortran { @@ -51,6 +53,9 @@ Definition::Definition(const std::vector &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 &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 &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 &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 &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 line{prescanner_->NextTokenizedLine()}) { + while (std::optional 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"; } diff --git a/flang/preprocessor.h b/flang/preprocessor.h index eb9de47..ad9fee1 100644 --- a/flang/preprocessor.h +++ b/flang/preprocessor.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -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 &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 names_; std::unordered_map definitions_; std::stack ifStack_; - Prescanner *prescanner_; }; } // namespace Fortran #endif // FORTRAN_PREPROCESSOR_H_ diff --git a/flang/prescan.cc b/flang/prescan.cc index 3763f53..2c5a27b 100644 --- a/flang/prescan.cc +++ b/flang/prescan.cc @@ -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); } diff --git a/flang/prescan.h b/flang/prescan.h index 1586833..4e09ed5 100644 --- a/flang/prescan.h +++ b/flang/prescan.h @@ -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}; diff --git a/flang/source.h b/flang/source.h index bfdc613..5088086 100644 --- a/flang/source.h +++ b/flang/source.h @@ -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: