[flang] Predefined macros (__FILE__, __LINE__, __DATE__, __TIME__).

Original-commit: flang-compiler/f18@8c7f51aa87
This commit is contained in:
peter klausler 2018-02-01 15:01:23 -08:00
parent 809235196a
commit ad7125ffb0
5 changed files with 118 additions and 65 deletions

View 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";
}

View 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_

View 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);
}

View 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};

View 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: