Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(201)

Side by Side Diff: tools/gn/parser.cc

Issue 207233003: Add "." accessors to GN. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: review comments Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tools/gn/parser.h ('k') | tools/gn/parser_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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 }
OLDNEW
« no previous file with comments | « tools/gn/parser.h ('k') | tools/gn/parser_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698