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 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 precedence. |
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 |