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