|  | // 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 (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 (!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 (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 |