#include <algorithm>
#include <cctype>
#include <cinttypes>
+#include <ctime>
#include <map>
#include <memory>
#include <set>
+#include <sstream>
#include <utility>
namespace Fortran {
: 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;
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 == '~') {
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);
} 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()) {
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;
}
}
}
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;
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;
}
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;
}
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;
}
}
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;
}
}
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
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);
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();
}
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") {
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";
}
#include <cstring>
#include <functional>
#include <list>
+#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
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) {
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};
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_;
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)};
}
}
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() {
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]);
}
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) {
}
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();
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);
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
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_