| Index: tools/gn/parser.cc
|
| diff --git a/tools/gn/parser.cc b/tools/gn/parser.cc
|
| index 6392eedbd3cb8def63402c3058435c1bf28a5725..f533542e7197b16f3c998f8066155152738798f6 100644
|
| --- a/tools/gn/parser.cc
|
| +++ b/tools/gn/parser.cc
|
| @@ -19,7 +19,7 @@
|
| // assignment := ident {'=' | '+=' | '-='} expr
|
|
|
| enum Precedence {
|
| - PRECEDENCE_ASSIGNMENT = 1,
|
| + PRECEDENCE_ASSIGNMENT = 1, // Lowest precedence.
|
| PRECEDENCE_OR = 2,
|
| PRECEDENCE_AND = 3,
|
| PRECEDENCE_EQUALITY = 4,
|
| @@ -27,15 +27,16 @@ enum Precedence {
|
| PRECEDENCE_SUM = 6,
|
| PRECEDENCE_PREFIX = 7,
|
| PRECEDENCE_CALL = 8,
|
| + PRECEDENCE_DOT = 9, // Highest precedence.
|
| };
|
|
|
| -// The top-level for blocks/ifs is still recursive descent, the expression
|
| -// parser is a Pratt parser. The basic idea there is to have the precedences
|
| -// (and associativities) encoded relative to each other and only parse up
|
| -// until you hit something of that precedence. There's a dispatch table in
|
| -// expressions_ at the top of parser.cc that describes how each token
|
| -// dispatches if it's seen as either a prefix or infix operator, and if it's
|
| -// infix, what its precedence is.
|
| +// The top-level for blocks/ifs is recursive descent, the expression parser is
|
| +// a Pratt parser. The basic idea there is to have the precedences (and
|
| +// associativities) encoded relative to each other and only parse up until you
|
| +// hit something of that precedence. There's a dispatch table in expressions_
|
| +// at the top of parser.cc that describes how each token dispatches if it's
|
| +// seen as either a prefix or infix operator, and if it's infix, what its
|
| +// precedence is.
|
| //
|
| // Refs:
|
| // - http://javascript.crockford.com/tdop/tdop.html
|
| @@ -62,6 +63,7 @@ ParserHelper Parser::expressions_[] = {
|
| {NULL, &Parser::BinaryOperator, PRECEDENCE_AND}, // BOOLEAN_AND
|
| {NULL, &Parser::BinaryOperator, PRECEDENCE_OR}, // BOOLEAN_OR
|
| {&Parser::Not, NULL, -1}, // BANG
|
| + {NULL, &Parser::DotOperator, PRECEDENCE_DOT}, // DOT
|
| {&Parser::Group, NULL, -1}, // LEFT_PAREN
|
| {NULL, NULL, -1}, // RIGHT_PAREN
|
| {&Parser::List, &Parser::Subscript, PRECEDENCE_CALL}, // LEFT_BRACKET
|
| @@ -311,7 +313,10 @@ scoped_ptr<ParseNode> Parser::Subscript(scoped_ptr<ParseNode> left,
|
| // TODO: Maybe support more complex expressions like a[0][0]. This would
|
| // require work on the evaluator too.
|
| if (left->AsIdentifier() == NULL) {
|
| - *err_ = Err(left.get(), "May only subscript simple identifiers");
|
| + *err_ = Err(left.get(), "May only subscript identifiers.",
|
| + "The thing on the left hand side of the [] must be an identifier\n"
|
| + "and not an expression. If you need this, you'll have to assign the\n"
|
| + "value to a temporary before subscripting. Sorry.");
|
| return scoped_ptr<ParseNode>();
|
| }
|
| scoped_ptr<ParseNode> value = ParseExpression();
|
| @@ -322,6 +327,30 @@ scoped_ptr<ParseNode> Parser::Subscript(scoped_ptr<ParseNode> left,
|
| return accessor.PassAs<ParseNode>();
|
| }
|
|
|
| +scoped_ptr<ParseNode> Parser::DotOperator(scoped_ptr<ParseNode> left,
|
| + Token token) {
|
| + if (left->AsIdentifier() == NULL) {
|
| + *err_ = Err(left.get(), "May only use \".\" for identifiers.",
|
| + "The thing on the left hand side of the dot must be an identifier\n"
|
| + "and not an expression. If you need this, you'll have to assign the\n"
|
| + "value to a temporary first. Sorry.");
|
| + return scoped_ptr<ParseNode>();
|
| + }
|
| +
|
| + scoped_ptr<ParseNode> right = ParseExpression(PRECEDENCE_DOT);
|
| + if (!right || !right->AsIdentifier()) {
|
| + *err_ = Err(token, "Expected identifier for right-hand-side of \".\"",
|
| + "Good: a.cookies\nBad: a.42\nLooks good but still bad: a.cookies()");
|
| + return scoped_ptr<ParseNode>();
|
| + }
|
| +
|
| + scoped_ptr<AccessorNode> accessor(new AccessorNode);
|
| + accessor->set_base(left->AsIdentifier()->value());
|
| + accessor->set_member(scoped_ptr<IdentifierNode>(
|
| + static_cast<IdentifierNode*>(right.release())));
|
| + return accessor.PassAs<ParseNode>();
|
| +}
|
| +
|
| // Does not Consume the start or end token.
|
| scoped_ptr<ListNode> Parser::ParseList(Token::Type stop_before,
|
| bool allow_trailing_comma) {
|
|
|