| // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/test/expectations/parser.h" |
| |
| #include "base/strings/string_util.h" |
| |
| namespace test_expectations { |
| |
| Parser::Parser(Delegate* delegate, const std::string& input) |
| : delegate_(delegate), |
| input_(input), |
| pos_(NULL), |
| end_(NULL), |
| line_number_(0), |
| data_error_(false) { |
| } |
| |
| Parser::~Parser() { |
| } |
| |
| void Parser::Parse() { |
| pos_ = &input_[0]; |
| end_ = pos_ + input_.length(); |
| |
| line_number_ = 1; |
| |
| StateFuncPtr state = &Parser::Start; |
| while (state) { |
| state = (this->*state)(); |
| } |
| } |
| |
| inline bool Parser::HasNext() { |
| return pos_ < end_; |
| } |
| |
| Parser::StateFunc Parser::Start() { |
| // If at the start of a line is whitespace, skip it and arrange to come back |
| // here. |
| if (base::IsAsciiWhitespace(*pos_)) |
| return SkipWhitespaceAndNewLines(&Parser::Start); |
| |
| // Handle comments at the start of lines. |
| if (*pos_ == '#') |
| return &Parser::ParseComment; |
| |
| // After arranging to come back here from skipping whitespace and comments, |
| // the parser may be at the end of the input. |
| if (pos_ >= end_) |
| return NULL; |
| |
| current_ = Expectation(); |
| data_error_ = false; |
| |
| return &Parser::ParseBugURL; |
| } |
| |
| Parser::StateFunc Parser::ParseComment() { |
| if (*pos_ != '#') |
| return SyntaxError("Invalid start of comment"); |
| |
| do { |
| ++pos_; |
| } while (HasNext() && *pos_ != '\n'); |
| |
| return &Parser::Start; |
| } |
| |
| Parser::StateFunc Parser::ParseBugURL() { |
| return SkipWhitespace(ExtractString( |
| &Parser::BeginModifiers)); |
| } |
| |
| Parser::StateFunc Parser::BeginModifiers() { |
| if (*pos_ != '[' || !HasNext()) |
| return SyntaxError("Expected '[' for start of modifiers"); |
| |
| ++pos_; |
| return SkipWhitespace(&Parser::InModifiers); |
| } |
| |
| Parser::StateFunc Parser::InModifiers() { |
| if (*pos_ == ']') |
| return &Parser::EndModifiers; |
| |
| return ExtractString(SkipWhitespace( |
| &Parser::SaveModifier)); |
| } |
| |
| Parser::StateFunc Parser::SaveModifier() { |
| if (extracted_string_.empty()) |
| return SyntaxError("Invalid modifier list"); |
| |
| Configuration config; |
| if (ConfigurationFromString(extracted_string_, &config)) { |
| if (current_.configuration != CONFIGURATION_UNSPECIFIED) |
| DataError("Cannot use more than one configuration modifier"); |
| else |
| current_.configuration = config; |
| } else { |
| Platform platform; |
| if (PlatformFromString(extracted_string_, &platform)) |
| current_.platforms.push_back(platform); |
| else |
| DataError("Invalid modifier string"); |
| } |
| |
| return SkipWhitespace(&Parser::InModifiers); |
| } |
| |
| Parser::StateFunc Parser::EndModifiers() { |
| if (*pos_ != ']' || !HasNext()) |
| return SyntaxError("Expected ']' for end of modifiers list"); |
| |
| ++pos_; |
| return SkipWhitespace(&Parser::ParseTestName); |
| } |
| |
| Parser::StateFunc Parser::ParseTestName() { |
| return ExtractString(&Parser::SaveTestName); |
| } |
| |
| Parser::StateFunc Parser::SaveTestName() { |
| if (extracted_string_.empty()) |
| return SyntaxError("Invalid test name"); |
| |
| current_.test_name = extracted_string_.as_string(); |
| return SkipWhitespace(&Parser::ParseExpectation); |
| } |
| |
| Parser::StateFunc Parser::ParseExpectation() { |
| if (*pos_ != '=' || !HasNext()) |
| return SyntaxError("Expected '=' for expectation result"); |
| |
| ++pos_; |
| return SkipWhitespace(&Parser::ParseExpectationType); |
| } |
| |
| Parser::StateFunc Parser::ParseExpectationType() { |
| return ExtractString(&Parser::SaveExpectationType); |
| } |
| |
| Parser::StateFunc Parser::SaveExpectationType() { |
| if (!ResultFromString(extracted_string_, ¤t_.result)) |
| DataError("Unknown expectation type"); |
| |
| return SkipWhitespace(&Parser::End); |
| } |
| |
| Parser::StateFunc Parser::End() { |
| if (!data_error_) |
| delegate_->EmitExpectation(current_); |
| |
| if (HasNext()) |
| return SkipWhitespaceAndNewLines(&Parser::Start); |
| |
| return NULL; |
| } |
| |
| Parser::StateFunc Parser::ExtractString(StateFunc success) { |
| const char* start = pos_; |
| while (!base::IsAsciiWhitespace(*pos_) && *pos_ != ']' && HasNext()) { |
| ++pos_; |
| if (*pos_ == '#') { |
| return SyntaxError("Unexpected start of comment"); |
| } |
| } |
| extracted_string_ = base::StringPiece(start, pos_ - start); |
| return success; |
| } |
| |
| Parser::StateFunc Parser::SkipWhitespace(Parser::StateFunc next) { |
| while ((*pos_ == ' ' || *pos_ == '\t') && HasNext()) { |
| ++pos_; |
| } |
| return next; |
| } |
| |
| Parser::StateFunc Parser::SkipWhitespaceAndNewLines(Parser::StateFunc next) { |
| while (base::IsAsciiWhitespace(*pos_) && HasNext()) { |
| if (*pos_ == '\n') { |
| ++line_number_; |
| } |
| ++pos_; |
| } |
| return next; |
| } |
| |
| Parser::StateFunc Parser::SyntaxError(const std::string& message) { |
| delegate_->OnSyntaxError(message); |
| return NULL; |
| } |
| |
| void Parser::DataError(const std::string& error) { |
| data_error_ = true; |
| delegate_->OnDataError(error); |
| } |
| |
| } // namespace test_expectations |