• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_, &current_.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