forked from OSchip/llvm-project
[flang] Predefined macros (__FILE__, __LINE__, __DATE__, __TIME__).
Original-commit: flang-compiler/f18@8c7f51aa87
This commit is contained in:
parent
809235196a
commit
ad7125ffb0
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue