diff options
author | Evan Martin <martine@danga.com> | 2011-07-26 00:51:47 (GMT) |
---|---|---|
committer | Evan Martin <martine@danga.com> | 2011-07-26 00:51:47 (GMT) |
commit | b86a9ebb949767ac6cfc73aacfd9428bfd9dc9a7 (patch) | |
tree | 5379b23d73c501a65c214230148faa31b4c8b329 | |
parent | f506c49387407789aabb9ee19e47eb49422ac79b (diff) | |
download | Ninja-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.cc | 38 | ||||
-rw-r--r-- | src/parsers.h | 33 |
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_; }; |