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 9 namespace test_expectations { 10 11 Parser::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 20 Parser::~Parser() { 21 } 22 23 void 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 35 inline bool Parser::HasNext() { 36 return pos_ < end_; 37 } 38 39 Parser::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 60 Parser::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 71 Parser::StateFunc Parser::ParseBugURL() { 72 return SkipWhitespace(ExtractString( 73 &Parser::BeginModifiers)); 74 } 75 76 Parser::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 84 Parser::StateFunc Parser::InModifiers() { 85 if (*pos_ == ']') 86 return &Parser::EndModifiers; 87 88 return ExtractString(SkipWhitespace( 89 &Parser::SaveModifier)); 90 } 91 92 Parser::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 113 Parser::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 121 Parser::StateFunc Parser::ParseTestName() { 122 return ExtractString(&Parser::SaveTestName); 123 } 124 125 Parser::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 133 Parser::StateFunc Parser::ParseExpectation() { 134 if (*pos_ != '=' || !HasNext()) 135 return SyntaxError("Expected '=' for expectation result"); 136 137 ++pos_; 138 return SkipWhitespace(&Parser::ParseExpectationType); 139 } 140 141 Parser::StateFunc Parser::ParseExpectationType() { 142 return ExtractString(&Parser::SaveExpectationType); 143 } 144 145 Parser::StateFunc Parser::SaveExpectationType() { 146 if (!ResultFromString(extracted_string_, ¤t_.result)) 147 DataError("Unknown expectation type"); 148 149 return SkipWhitespace(&Parser::End); 150 } 151 152 Parser::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 162 Parser::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 174 Parser::StateFunc Parser::SkipWhitespace(Parser::StateFunc next) { 175 while ((*pos_ == ' ' || *pos_ == '\t') && HasNext()) { 176 ++pos_; 177 } 178 return next; 179 } 180 181 Parser::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 191 Parser::StateFunc Parser::SyntaxError(const std::string& message) { 192 delegate_->OnSyntaxError(message); 193 return NULL; 194 } 195 196 void Parser::DataError(const std::string& error) { 197 data_error_ = true; 198 delegate_->OnDataError(error); 199 } 200 201 } // namespace test_expectations 202