summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvan Martin <martine@danga.com>2011-07-26 00:51:47 (GMT)
committerEvan Martin <martine@danga.com>2011-07-26 00:51:47 (GMT)
commitb86a9ebb949767ac6cfc73aacfd9428bfd9dc9a7 (patch)
tree5379b23d73c501a65c214230148faa31b4c8b329
parentf506c49387407789aabb9ee19e47eb49422ac79b (diff)
downloadNinja-b86a9ebb949767ac6cfc73aacfd9428bfd9dc9a7.zip
Ninja-b86a9ebb949767ac6cfc73aacfd9428bfd9dc9a7.tar.gz
Ninja-b86a9ebb949767ac6cfc73aacfd9428bfd9dc9a7.tar.bz2
don't track line/column until you encounter an error, then re-parse
This speeds up the common case (where you don't need a line number) at the small expense of the uncommon case (for error messages, you do need a line number). And it's less code.
-rw-r--r--src/parsers.cc38
-rw-r--r--src/parsers.h33
2 files changed, 31 insertions, 40 deletions
diff --git a/src/parsers.cc b/src/parsers.cc
index 2599b07..7c08179 100644
--- a/src/parsers.cc
+++ b/src/parsers.cc
@@ -41,23 +41,31 @@ string Token::AsString() const {
return "";
}
-bool SourceLocation::Error(const string& message, string* err) {
+bool Tokenizer::ErrorAt(const char* pos, const string& message, string* err) {
+ // Re-scan the input, counting newlines so that we can compute the
+ // correct position.
+ int line = 1;
+ const char* line_start = start_;
+ for (const char* p = start_; p < pos; ++p) {
+ if (*p == '\n') {
+ ++line;
+ line_start = p + 1;
+ }
+ }
+ int col = pos - line_start + 1;
+
char buf[1024];
- snprintf(buf, sizeof(buf), "line %d, col %d: %s", line_, column_,
- message.c_str());
+ snprintf(buf, sizeof(buf),
+ "line %d, col %d: %s", line, col, message.c_str());
err->assign(buf);
return false;
}
void Tokenizer::Start(const char* start, const char* end) {
- cur_line_ = cur_ = start;
+ cur_line_ = cur_ = start_ = start;
end_ = end;
}
-bool Tokenizer::Error(const string& message, string* err) {
- return Location().Error(message, err);
-}
-
bool Tokenizer::ErrorExpected(const string& expected, string* err) {
return Error("expected " + expected + ", got " + token_.AsString(), err);
}
@@ -75,16 +83,12 @@ void Tokenizer::SkipWhitespace(bool newline) {
Newline(NULL);
} else if (*cur_ == kContinuation && cur_ + 1 < end_ && cur_[1] == '\n') {
++cur_; ++cur_;
- cur_line_ = cur_;
- ++line_number_;
} else if (*cur_ == '#' && cur_ == cur_line_) {
while (cur_ < end_ && *cur_ != '\n')
++cur_;
- if (cur_ < end_ && *cur_ == '\n') {
+ if (cur_ < end_ && *cur_ == '\n')
++cur_;
- cur_line_ = cur_;
- ++line_number_;
- }
+ cur_line_ = cur_;
} else {
break;
}
@@ -222,7 +226,6 @@ Token::Type Tokenizer::PeekToken() {
++cur_;
cur_line_ = cur_;
cur_indent_ = -1;
- ++line_number_;
}
SkipWhitespace();
@@ -335,7 +338,7 @@ bool ManifestParser::ParseRule(string* err) {
tokenizer_.ConsumeToken();
while (tokenizer_.PeekToken() != Token::OUTDENT) {
- SourceLocation let_loc = tokenizer_.Location();
+ const char* let_loc = tokenizer_.token_.pos_;
string key;
if (!ParseLetKey(&key, err))
@@ -351,7 +354,8 @@ bool ManifestParser::ParseRule(string* err) {
} else {
// Die on other keyvals for now; revisit if we want to add a
// scope here.
- return let_loc.Error("unexpected variable '" + key + "'", err);
+ return tokenizer_.ErrorAt(let_loc, "unexpected variable '" + key + "'",
+ err);
}
if (!ParseLetValue(eval_target, err))
diff --git a/src/parsers.h b/src/parsers.h
index eb50523..6bee0ae 100644
--- a/src/parsers.h
+++ b/src/parsers.h
@@ -48,24 +48,11 @@ struct Token {
const char* end_;
};
-/// Represents a user-understandable position within a source file.
-struct SourceLocation {
- SourceLocation(int line, int col) : line_(line), column_(col) {}
-
- /// Construct an error message based on the position and message,
- /// write it into \a err, then return false.
- bool Error(const string& message, string* err);
-
- /// 1-based line and column numbers.
- int line_;
- int column_;
-};
-
/// Processes an input stream into Tokens.
struct Tokenizer {
Tokenizer()
: makefile_flavor_(false),
- token_(Token::NONE), line_number_(0),
+ token_(Token::NONE),
last_indent_(0), cur_indent_(-1) {}
/// Tokenization differs slightly between ninja files and Makefiles.
@@ -76,8 +63,12 @@ struct Tokenizer {
}
void Start(const char* start, const char* end);
+ /// Report an error at a particular location.
+ bool ErrorAt(const char* pos, const string& message, string* err);
/// Report an error with a location pointing at the current token.
- bool Error(const string& message, string* err);
+ bool Error(const string& message, string* err) {
+ return ErrorAt(token_.pos_, message, err);
+ }
/// Call Error() with "expected foo, got bar".
bool ErrorExpected(const string& expected, string* err);
@@ -94,18 +85,14 @@ struct Tokenizer {
Token::Type PeekToken();
void ConsumeToken();
- SourceLocation Location() {
- return SourceLocation(line_number_ + 1, token_.pos_ - cur_line_ + 1);
- }
-
bool makefile_flavor_;
- const char* cur_;
- const char* end_;
+ const char* start_; /// Start of the input.
+ const char* cur_; /// Current position within the input.
+ const char* end_; /// End of the input.
- const char* cur_line_;
+ const char* cur_line_; /// Start of current line.
Token token_;
- int line_number_;
int last_indent_, cur_indent_;
};