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
Parser(Delegate * delegate,const std::string & input)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
~Parser()20 Parser::~Parser() {
21 }
22
Parse()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
HasNext()35 inline bool Parser::HasNext() {
36 return pos_ < end_;
37 }
38
Start()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
ParseComment()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
ParseBugURL()71 Parser::StateFunc Parser::ParseBugURL() {
72 return SkipWhitespace(ExtractString(
73 &Parser::BeginModifiers));
74 }
75
BeginModifiers()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
InModifiers()84 Parser::StateFunc Parser::InModifiers() {
85 if (*pos_ == ']')
86 return &Parser::EndModifiers;
87
88 return ExtractString(SkipWhitespace(
89 &Parser::SaveModifier));
90 }
91
SaveModifier()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
EndModifiers()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
ParseTestName()121 Parser::StateFunc Parser::ParseTestName() {
122 return ExtractString(&Parser::SaveTestName);
123 }
124
SaveTestName()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
ParseExpectation()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
ParseExpectationType()141 Parser::StateFunc Parser::ParseExpectationType() {
142 return ExtractString(&Parser::SaveExpectationType);
143 }
144
SaveExpectationType()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
End()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
ExtractString(StateFunc success)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
SkipWhitespace(Parser::StateFunc next)174 Parser::StateFunc Parser::SkipWhitespace(Parser::StateFunc next) {
175 while ((*pos_ == ' ' || *pos_ == '\t') && HasNext()) {
176 ++pos_;
177 }
178 return next;
179 }
180
SkipWhitespaceAndNewLines(Parser::StateFunc next)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
SyntaxError(const std::string & message)191 Parser::StateFunc Parser::SyntaxError(const std::string& message) {
192 delegate_->OnSyntaxError(message);
193 return NULL;
194 }
195
DataError(const std::string & error)196 void Parser::DataError(const std::string& error) {
197 data_error_ = true;
198 delegate_->OnDataError(error);
199 }
200
201 } // namespace test_expectations
202