1// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "base/test/expectations/parser.h" 6 7#include "base/strings/string_util.h" 8 9namespace test_expectations { 10 11Parser::Parser(Delegate* delegate, const std::string& input) 12 : delegate_(delegate), 13 input_(input), 14 pos_(NULL), 15 end_(NULL), 16 line_number_(0), 17 data_error_(false) { 18} 19 20Parser::~Parser() { 21} 22 23void Parser::Parse() { 24 pos_ = &input_[0]; 25 end_ = pos_ + input_.length(); 26 27 line_number_ = 1; 28 29 StateFuncPtr state = &Parser::Start; 30 while (state) { 31 state = (this->*state)(); 32 } 33} 34 35inline bool Parser::HasNext() { 36 return pos_ < end_; 37} 38 39Parser::StateFunc Parser::Start() { 40 // If at the start of a line is whitespace, skip it and arrange to come back 41 // here. 42 if (IsAsciiWhitespace(*pos_)) 43 return SkipWhitespaceAndNewLines(&Parser::Start); 44 45 // Handle comments at the start of lines. 46 if (*pos_ == '#') 47 return &Parser::ParseComment; 48 49 // After arranging to come back here from skipping whitespace and comments, 50 // the parser may be at the end of the input. 51 if (pos_ >= end_) 52 return NULL; 53 54 current_ = Expectation(); 55 data_error_ = false; 56 57 return &Parser::ParseBugURL; 58} 59 60Parser::StateFunc Parser::ParseComment() { 61 if (*pos_ != '#') 62 return SyntaxError("Invalid start of comment"); 63 64 do { 65 ++pos_; 66 } while (HasNext() && *pos_ != '\n'); 67 68 return &Parser::Start; 69} 70 71Parser::StateFunc Parser::ParseBugURL() { 72 return SkipWhitespace(ExtractString( 73 &Parser::BeginModifiers)); 74} 75 76Parser::StateFunc Parser::BeginModifiers() { 77 if (*pos_ != '[' || !HasNext()) 78 return SyntaxError("Expected '[' for start of modifiers"); 79 80 ++pos_; 81 return SkipWhitespace(&Parser::InModifiers); 82} 83 84Parser::StateFunc Parser::InModifiers() { 85 if (*pos_ == ']') 86 return &Parser::EndModifiers; 87 88 return ExtractString(SkipWhitespace( 89 &Parser::SaveModifier)); 90} 91 92Parser::StateFunc Parser::SaveModifier() { 93 if (extracted_string_.empty()) 94 return SyntaxError("Invalid modifier list"); 95 96 Configuration config; 97 if (ConfigurationFromString(extracted_string_, &config)) { 98 if (current_.configuration != CONFIGURATION_UNSPECIFIED) 99 DataError("Cannot use more than one configuration modifier"); 100 else 101 current_.configuration = config; 102 } else { 103 Platform platform; 104 if (PlatformFromString(extracted_string_, &platform)) 105 current_.platforms.push_back(platform); 106 else 107 DataError("Invalid modifier string"); 108 } 109 110 return SkipWhitespace(&Parser::InModifiers); 111} 112 113Parser::StateFunc Parser::EndModifiers() { 114 if (*pos_ != ']' || !HasNext()) 115 return SyntaxError("Expected ']' for end of modifiers list"); 116 117 ++pos_; 118 return SkipWhitespace(&Parser::ParseTestName); 119} 120 121Parser::StateFunc Parser::ParseTestName() { 122 return ExtractString(&Parser::SaveTestName); 123} 124 125Parser::StateFunc Parser::SaveTestName() { 126 if (extracted_string_.empty()) 127 return SyntaxError("Invalid test name"); 128 129 current_.test_name = extracted_string_.as_string(); 130 return SkipWhitespace(&Parser::ParseExpectation); 131} 132 133Parser::StateFunc Parser::ParseExpectation() { 134 if (*pos_ != '=' || !HasNext()) 135 return SyntaxError("Expected '=' for expectation result"); 136 137 ++pos_; 138 return SkipWhitespace(&Parser::ParseExpectationType); 139} 140 141Parser::StateFunc Parser::ParseExpectationType() { 142 return ExtractString(&Parser::SaveExpectationType); 143} 144 145Parser::StateFunc Parser::SaveExpectationType() { 146 if (!ResultFromString(extracted_string_, ¤t_.result)) 147 DataError("Unknown expectation type"); 148 149 return SkipWhitespace(&Parser::End); 150} 151 152Parser::StateFunc Parser::End() { 153 if (!data_error_) 154 delegate_->EmitExpectation(current_); 155 156 if (HasNext()) 157 return SkipWhitespaceAndNewLines(&Parser::Start); 158 159 return NULL; 160} 161 162Parser::StateFunc Parser::ExtractString(StateFunc success) { 163 const char* start = pos_; 164 while (!IsAsciiWhitespace(*pos_) && *pos_ != ']' && HasNext()) { 165 ++pos_; 166 if (*pos_ == '#') { 167 return SyntaxError("Unexpected start of comment"); 168 } 169 } 170 extracted_string_ = base::StringPiece(start, pos_ - start); 171 return success; 172} 173 174Parser::StateFunc Parser::SkipWhitespace(Parser::StateFunc next) { 175 while ((*pos_ == ' ' || *pos_ == '\t') && HasNext()) { 176 ++pos_; 177 } 178 return next; 179} 180 181Parser::StateFunc Parser::SkipWhitespaceAndNewLines(Parser::StateFunc next) { 182 while (IsAsciiWhitespace(*pos_) && HasNext()) { 183 if (*pos_ == '\n') { 184 ++line_number_; 185 } 186 ++pos_; 187 } 188 return next; 189} 190 191Parser::StateFunc Parser::SyntaxError(const std::string& message) { 192 delegate_->OnSyntaxError(message); 193 return NULL; 194} 195 196void Parser::DataError(const std::string& error) { 197 data_error_ = true; 198 delegate_->OnDataError(error); 199} 200 201} // namespace test_expectations 202