Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "tools/gn/parser.h" | 5 #include "tools/gn/parser.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "tools/gn/functions.h" | 8 #include "tools/gn/functions.h" |
| 9 #include "tools/gn/operators.h" | 9 #include "tools/gn/operators.h" |
| 10 #include "tools/gn/token.h" | 10 #include "tools/gn/token.h" |
| 11 | 11 |
| 12 // grammar: | 12 // grammar: |
| 13 // | 13 // |
| 14 // file := (statement)* | 14 // file := (statement)* |
| 15 // statement := block | if | assignment | 15 // statement := block | if | assignment |
| 16 // block := '{' statement* '}' | 16 // block := '{' statement* '}' |
| 17 // if := 'if' '(' expr ')' statement [ else ] | 17 // if := 'if' '(' expr ')' statement [ else ] |
| 18 // else := 'else' (if | statement)* | 18 // else := 'else' (if | statement)* |
| 19 // assignment := ident {'=' | '+=' | '-='} expr | 19 // assignment := ident {'=' | '+=' | '-='} expr |
| 20 | 20 |
| 21 enum Precedence { | 21 enum Precedence { |
| 22 PRECEDENCE_ASSIGNMENT = 1, | 22 PRECEDENCE_ASSIGNMENT = 1, // Lowest prescedence. |
|
scottmg
2014/03/25 05:04:23
no 's' in precedence
| |
| 23 PRECEDENCE_OR = 2, | 23 PRECEDENCE_OR = 2, |
| 24 PRECEDENCE_AND = 3, | 24 PRECEDENCE_AND = 3, |
| 25 PRECEDENCE_EQUALITY = 4, | 25 PRECEDENCE_EQUALITY = 4, |
| 26 PRECEDENCE_RELATION = 5, | 26 PRECEDENCE_RELATION = 5, |
| 27 PRECEDENCE_SUM = 6, | 27 PRECEDENCE_SUM = 6, |
| 28 PRECEDENCE_PREFIX = 7, | 28 PRECEDENCE_PREFIX = 7, |
| 29 PRECEDENCE_CALL = 8, | 29 PRECEDENCE_CALL = 8, |
| 30 PRECEDENCE_DOT = 9, // Highest perscedence. | |
| 30 }; | 31 }; |
| 31 | 32 |
| 32 // The top-level for blocks/ifs is still recursive descent, the expression | 33 // The top-level for blocks/ifs is recursive descent, the expression parser is |
| 33 // parser is a Pratt parser. The basic idea there is to have the precedences | 34 // a Pratt parser. The basic idea there is to have the precedences (and |
| 34 // (and associativities) encoded relative to each other and only parse up | 35 // associativities) encoded relative to each other and only parse up until you |
| 35 // until you hit something of that precedence. There's a dispatch table in | 36 // hit something of that precedence. There's a dispatch table in expressions_ |
| 36 // expressions_ at the top of parser.cc that describes how each token | 37 // at the top of parser.cc that describes how each token dispatches if it's |
| 37 // dispatches if it's seen as either a prefix or infix operator, and if it's | 38 // seen as either a prefix or infix operator, and if it's infix, what its |
| 38 // infix, what its precedence is. | 39 // precedence is. |
| 39 // | 40 // |
| 40 // Refs: | 41 // Refs: |
| 41 // - http://javascript.crockford.com/tdop/tdop.html | 42 // - http://javascript.crockford.com/tdop/tdop.html |
| 42 // - http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsi ng-made-easy/ | 43 // - http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsi ng-made-easy/ |
| 43 | 44 |
| 44 // Indexed by Token::Type. | 45 // Indexed by Token::Type. |
| 45 ParserHelper Parser::expressions_[] = { | 46 ParserHelper Parser::expressions_[] = { |
| 46 {NULL, NULL, -1}, // INVALID | 47 {NULL, NULL, -1}, // INVALID |
| 47 {&Parser::Literal, NULL, -1}, // INTEGER | 48 {&Parser::Literal, NULL, -1}, // INTEGER |
| 48 {&Parser::Literal, NULL, -1}, // STRING | 49 {&Parser::Literal, NULL, -1}, // STRING |
| 49 {&Parser::Literal, NULL, -1}, // TRUE_TOKEN | 50 {&Parser::Literal, NULL, -1}, // TRUE_TOKEN |
| 50 {&Parser::Literal, NULL, -1}, // FALSE_TOKEN | 51 {&Parser::Literal, NULL, -1}, // FALSE_TOKEN |
| 51 {NULL, &Parser::Assignment, PRECEDENCE_ASSIGNMENT}, // EQUAL | 52 {NULL, &Parser::Assignment, PRECEDENCE_ASSIGNMENT}, // EQUAL |
| 52 {NULL, &Parser::BinaryOperator, PRECEDENCE_SUM}, // PLUS | 53 {NULL, &Parser::BinaryOperator, PRECEDENCE_SUM}, // PLUS |
| 53 {NULL, &Parser::BinaryOperator, PRECEDENCE_SUM}, // MINUS | 54 {NULL, &Parser::BinaryOperator, PRECEDENCE_SUM}, // MINUS |
| 54 {NULL, &Parser::Assignment, PRECEDENCE_ASSIGNMENT}, // PLUS_EQUALS | 55 {NULL, &Parser::Assignment, PRECEDENCE_ASSIGNMENT}, // PLUS_EQUALS |
| 55 {NULL, &Parser::Assignment, PRECEDENCE_ASSIGNMENT}, // MINUS_EQUALS | 56 {NULL, &Parser::Assignment, PRECEDENCE_ASSIGNMENT}, // MINUS_EQUALS |
| 56 {NULL, &Parser::BinaryOperator, PRECEDENCE_EQUALITY}, // EQUAL_EQUAL | 57 {NULL, &Parser::BinaryOperator, PRECEDENCE_EQUALITY}, // EQUAL_EQUAL |
| 57 {NULL, &Parser::BinaryOperator, PRECEDENCE_EQUALITY}, // NOT_EQUAL | 58 {NULL, &Parser::BinaryOperator, PRECEDENCE_EQUALITY}, // NOT_EQUAL |
| 58 {NULL, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // LESS_EQUAL | 59 {NULL, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // LESS_EQUAL |
| 59 {NULL, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // GREATER_EQUAL | 60 {NULL, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // GREATER_EQUAL |
| 60 {NULL, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // LESS_THAN | 61 {NULL, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // LESS_THAN |
| 61 {NULL, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // GREATER_THAN | 62 {NULL, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // GREATER_THAN |
| 62 {NULL, &Parser::BinaryOperator, PRECEDENCE_AND}, // BOOLEAN_AND | 63 {NULL, &Parser::BinaryOperator, PRECEDENCE_AND}, // BOOLEAN_AND |
| 63 {NULL, &Parser::BinaryOperator, PRECEDENCE_OR}, // BOOLEAN_OR | 64 {NULL, &Parser::BinaryOperator, PRECEDENCE_OR}, // BOOLEAN_OR |
| 64 {&Parser::Not, NULL, -1}, // BANG | 65 {&Parser::Not, NULL, -1}, // BANG |
| 66 {NULL, &Parser::DotOperator, PRECEDENCE_DOT}, // DOT | |
| 65 {&Parser::Group, NULL, -1}, // LEFT_PAREN | 67 {&Parser::Group, NULL, -1}, // LEFT_PAREN |
| 66 {NULL, NULL, -1}, // RIGHT_PAREN | 68 {NULL, NULL, -1}, // RIGHT_PAREN |
| 67 {&Parser::List, &Parser::Subscript, PRECEDENCE_CALL}, // LEFT_BRACKET | 69 {&Parser::List, &Parser::Subscript, PRECEDENCE_CALL}, // LEFT_BRACKET |
| 68 {NULL, NULL, -1}, // RIGHT_BRACKET | 70 {NULL, NULL, -1}, // RIGHT_BRACKET |
| 69 {NULL, NULL, -1}, // LEFT_BRACE | 71 {NULL, NULL, -1}, // LEFT_BRACE |
| 70 {NULL, NULL, -1}, // RIGHT_BRACE | 72 {NULL, NULL, -1}, // RIGHT_BRACE |
| 71 {NULL, NULL, -1}, // IF | 73 {NULL, NULL, -1}, // IF |
| 72 {NULL, NULL, -1}, // ELSE | 74 {NULL, NULL, -1}, // ELSE |
| 73 {&Parser::Name, &Parser::IdentifierOrCall, PRECEDENCE_CALL}, // IDENTIFIER | 75 {&Parser::Name, &Parser::IdentifierOrCall, PRECEDENCE_CALL}, // IDENTIFIER |
| 74 {NULL, NULL, -1}, // COMMA | 76 {NULL, NULL, -1}, // COMMA |
| (...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 304 assign->set_left(left.Pass()); | 306 assign->set_left(left.Pass()); |
| 305 assign->set_right(value.Pass()); | 307 assign->set_right(value.Pass()); |
| 306 return assign.PassAs<ParseNode>(); | 308 return assign.PassAs<ParseNode>(); |
| 307 } | 309 } |
| 308 | 310 |
| 309 scoped_ptr<ParseNode> Parser::Subscript(scoped_ptr<ParseNode> left, | 311 scoped_ptr<ParseNode> Parser::Subscript(scoped_ptr<ParseNode> left, |
| 310 Token token) { | 312 Token token) { |
| 311 // TODO: Maybe support more complex expressions like a[0][0]. This would | 313 // TODO: Maybe support more complex expressions like a[0][0]. This would |
| 312 // require work on the evaluator too. | 314 // require work on the evaluator too. |
| 313 if (left->AsIdentifier() == NULL) { | 315 if (left->AsIdentifier() == NULL) { |
| 314 *err_ = Err(left.get(), "May only subscript simple identifiers"); | 316 *err_ = Err(left.get(), "May only subscript identifiers.", |
| 317 "The thing on the left hand side of the [] must be an identifier\n" | |
| 318 "and not an expression. If you need this, you'll have to assign the\n" | |
| 319 "value to a temporary before subscripting. Sorry."); | |
| 315 return scoped_ptr<ParseNode>(); | 320 return scoped_ptr<ParseNode>(); |
| 316 } | 321 } |
| 317 scoped_ptr<ParseNode> value = ParseExpression(); | 322 scoped_ptr<ParseNode> value = ParseExpression(); |
| 318 Consume(Token::RIGHT_BRACKET, "Expecting ']' after subscript."); | 323 Consume(Token::RIGHT_BRACKET, "Expecting ']' after subscript."); |
| 319 scoped_ptr<AccessorNode> accessor(new AccessorNode); | 324 scoped_ptr<AccessorNode> accessor(new AccessorNode); |
| 320 accessor->set_base(left->AsIdentifier()->value()); | 325 accessor->set_base(left->AsIdentifier()->value()); |
| 321 accessor->set_index(value.Pass()); | 326 accessor->set_index(value.Pass()); |
| 322 return accessor.PassAs<ParseNode>(); | 327 return accessor.PassAs<ParseNode>(); |
| 323 } | 328 } |
| 324 | 329 |
| 330 scoped_ptr<ParseNode> Parser::DotOperator(scoped_ptr<ParseNode> left, | |
| 331 Token token) { | |
| 332 if (left->AsIdentifier() == NULL) { | |
| 333 *err_ = Err(left.get(), "May only use \".\" for identifiers.", | |
| 334 "The thing on the left hand side of the dot must be an identifier\n" | |
| 335 "and not an expression. If you need this, you'll have to assign the\n" | |
| 336 "value to a temporary first. Sorry."); | |
| 337 return scoped_ptr<ParseNode>(); | |
| 338 } | |
| 339 | |
| 340 scoped_ptr<ParseNode> right = ParseExpression(PRECEDENCE_DOT); | |
| 341 if (!right || !right->AsIdentifier()) { | |
| 342 *err_ = Err(token, "Expected identifier for right-hand-side of \".\"", | |
| 343 "Good: a.cookies\nBad: a.42\nLooks good but still bad: a.cookies()"); | |
| 344 return scoped_ptr<ParseNode>(); | |
| 345 } | |
| 346 | |
| 347 scoped_ptr<AccessorNode> accessor(new AccessorNode); | |
| 348 accessor->set_base(left->AsIdentifier()->value()); | |
| 349 accessor->set_member(scoped_ptr<IdentifierNode>( | |
| 350 static_cast<IdentifierNode*>(right.release()))); | |
| 351 return accessor.PassAs<ParseNode>(); | |
| 352 } | |
| 353 | |
| 325 // Does not Consume the start or end token. | 354 // Does not Consume the start or end token. |
| 326 scoped_ptr<ListNode> Parser::ParseList(Token::Type stop_before, | 355 scoped_ptr<ListNode> Parser::ParseList(Token::Type stop_before, |
| 327 bool allow_trailing_comma) { | 356 bool allow_trailing_comma) { |
| 328 scoped_ptr<ListNode> list(new ListNode); | 357 scoped_ptr<ListNode> list(new ListNode); |
| 329 list->set_begin_token(cur_token()); | 358 list->set_begin_token(cur_token()); |
| 330 bool just_got_comma = false; | 359 bool just_got_comma = false; |
| 331 while (!LookAhead(stop_before)) { | 360 while (!LookAhead(stop_before)) { |
| 332 just_got_comma = false; | 361 just_got_comma = false; |
| 333 // Why _OR? We're parsing things that are higher precedence than the , | 362 // Why _OR? We're parsing things that are higher precedence than the , |
| 334 // that separates the items of the list. , should appear lower than | 363 // that separates the items of the list. , should appear lower than |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 419 if (IsAssignment(condition->condition())) | 448 if (IsAssignment(condition->condition())) |
| 420 *err_ = Err(condition->condition(), "Assignment not allowed in 'if'."); | 449 *err_ = Err(condition->condition(), "Assignment not allowed in 'if'."); |
| 421 Consume(Token::RIGHT_PAREN, "Expected ')' after condition of 'if'."); | 450 Consume(Token::RIGHT_PAREN, "Expected ')' after condition of 'if'."); |
| 422 condition->set_if_true(ParseBlock().Pass()); | 451 condition->set_if_true(ParseBlock().Pass()); |
| 423 if (Match(Token::ELSE)) | 452 if (Match(Token::ELSE)) |
| 424 condition->set_if_false(ParseStatement().Pass()); | 453 condition->set_if_false(ParseStatement().Pass()); |
| 425 if (has_error()) | 454 if (has_error()) |
| 426 return scoped_ptr<ParseNode>(); | 455 return scoped_ptr<ParseNode>(); |
| 427 return condition.PassAs<ParseNode>(); | 456 return condition.PassAs<ParseNode>(); |
| 428 } | 457 } |
| OLD | NEW |