Home | History | Annotate | Download | only in expectations
      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_, &current_.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