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 <utility> | 7 #include <utility> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/memory/ptr_util.h" | 10 #include "base/memory/ptr_util.h" |
11 #include "tools/gn/functions.h" | 11 #include "tools/gn/functions.h" |
12 #include "tools/gn/operators.h" | 12 #include "tools/gn/operators.h" |
13 #include "tools/gn/token.h" | 13 #include "tools/gn/token.h" |
14 | 14 |
15 const char kGrammar_Help[] = | 15 const char kGrammar_Help[] = |
16 "GN build language grammar\n" | 16 "Language and grammar for GN build files\n" |
17 "\n" | 17 "\n" |
18 "Tokens\n" | 18 "Tokens\n" |
19 "\n" | 19 "\n" |
20 " GN build files are read as sequences of tokens. While splitting the\n" | 20 " GN build files are read as sequences of tokens. While splitting the\n" |
21 " file into tokens, the next token is the longest sequence of characters\n" | 21 " file into tokens, the next token is the longest sequence of characters\n" |
22 " that form a valid token.\n" | 22 " that form a valid token.\n" |
23 "\n" | 23 "\n" |
24 "White space and comments\n" | 24 "White space and comments\n" |
25 "\n" | 25 "\n" |
26 " White space is comprised of spaces (U+0020), horizontal tabs (U+0009),\n" | 26 " White space is comprised of spaces (U+0020), horizontal tabs (U+0009),\n" |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
72 "\n" | 72 "\n" |
73 " \\\" U+0022 quotation mark\n" | 73 " \\\" U+0022 quotation mark\n" |
74 " \\$ U+0024 dollar sign\n" | 74 " \\$ U+0024 dollar sign\n" |
75 " \\\\ U+005C backslash\n" | 75 " \\\\ U+005C backslash\n" |
76 "\n" | 76 "\n" |
77 " All other backslashes represent themselves.\n" | 77 " All other backslashes represent themselves.\n" |
78 "\n" | 78 "\n" |
79 " To insert an arbitrary byte value, use $0xFF. For example, to\n" | 79 " To insert an arbitrary byte value, use $0xFF. For example, to\n" |
80 " insert a newline character: \"Line one$0x0ALine two\".\n" | 80 " insert a newline character: \"Line one$0x0ALine two\".\n" |
81 "\n" | 81 "\n" |
| 82 " An expansion will evaluate the variable following the '$' and insert\n" |
| 83 " a stringified version of it into the result. For example, to concat\n" |
| 84 " two path components with a slash separating them:\n" |
| 85 " \"$var_one/$var_two\"\n" |
| 86 " Use the \"${var_one}\" format to be explicitly deliniate the variable\n" |
| 87 " for otherwise-ambiguous cases.\n" |
| 88 "\n" |
82 "Punctuation\n" | 89 "Punctuation\n" |
83 "\n" | 90 "\n" |
84 " The following character sequences represent punctuation:\n" | 91 " The following character sequences represent punctuation:\n" |
85 "\n" | 92 "\n" |
86 " + += == != ( )\n" | 93 " + += == != ( )\n" |
87 " - -= < <= [ ]\n" | 94 " - -= < <= [ ]\n" |
88 " ! = > >= { }\n" | 95 " ! = > >= { }\n" |
89 " && || . ,\n" | 96 " && || . ,\n" |
90 "\n" | 97 "\n" |
91 "Grammar\n" | 98 "Grammar\n" |
92 "\n" | 99 "\n" |
93 " The input tokens form a syntax tree following a context-free grammar:\n" | 100 " The input tokens form a syntax tree following a context-free grammar:\n" |
94 "\n" | 101 "\n" |
95 " File = StatementList .\n" | 102 " File = StatementList .\n" |
96 "\n" | 103 "\n" |
97 " Statement = Assignment | Call | Condition .\n" | 104 " Statement = Assignment | Call | Condition .\n" |
98 " Assignment = identifier AssignOp Expr .\n" | 105 " LValue = identifier | ArrayAccess | ScopeAccess .\n" |
| 106 " Assignment = LValue AssignOp Expr .\n" |
99 " Call = identifier \"(\" [ ExprList ] \")\" [ Block ] .\n" | 107 " Call = identifier \"(\" [ ExprList ] \")\" [ Block ] .\n" |
100 " Condition = \"if\" \"(\" Expr \")\" Block\n" | 108 " Condition = \"if\" \"(\" Expr \")\" Block\n" |
101 " [ \"else\" ( Condition | Block ) ] .\n" | 109 " [ \"else\" ( Condition | Block ) ] .\n" |
102 " Block = \"{\" StatementList \"}\" .\n" | 110 " Block = \"{\" StatementList \"}\" .\n" |
103 " StatementList = { Statement } .\n" | 111 " StatementList = { Statement } .\n" |
104 "\n" | 112 "\n" |
105 " ArrayAccess = identifier \"[\" { identifier | integer } \"]\" .\n" | 113 " ArrayAccess = identifier \"[\" Expr \"]\" .\n" |
106 " ScopeAccess = identifier \".\" identifier .\n" | 114 " ScopeAccess = identifier \".\" identifier .\n" |
107 " Expr = UnaryExpr | Expr BinaryOp Expr .\n" | 115 " Expr = UnaryExpr | Expr BinaryOp Expr .\n" |
108 " UnaryExpr = PrimaryExpr | UnaryOp UnaryExpr .\n" | 116 " UnaryExpr = PrimaryExpr | UnaryOp UnaryExpr .\n" |
109 " PrimaryExpr = identifier | integer | string | Call\n" | 117 " PrimaryExpr = identifier | integer | string | Call\n" |
110 " | ArrayAccess | ScopeAccess\n" | 118 " | ArrayAccess | ScopeAccess | Block\n" |
111 " | \"(\" Expr \")\"\n" | 119 " | \"(\" Expr \")\"\n" |
112 " | \"[\" [ ExprList [ \",\" ] ] \"]\" .\n" | 120 " | \"[\" [ ExprList [ \",\" ] ] \"]\" .\n" |
113 " ExprList = Expr { \",\" Expr } .\n" | 121 " ExprList = Expr { \",\" Expr } .\n" |
114 "\n" | 122 "\n" |
115 " AssignOp = \"=\" | \"+=\" | \"-=\" .\n" | 123 " AssignOp = \"=\" | \"+=\" | \"-=\" .\n" |
116 " UnaryOp = \"!\" .\n" | 124 " UnaryOp = \"!\" .\n" |
117 " BinaryOp = \"+\" | \"-\" // highest priority\n" | 125 " BinaryOp = \"+\" | \"-\" // highest priority\n" |
118 " | \"<\" | \"<=\" | \">\" | \">=\"\n" | 126 " | \"<\" | \"<=\" | \">\" | \">=\"\n" |
119 " | \"==\" | \"!=\"\n" | 127 " | \"==\" | \"!=\"\n" |
120 " | \"&&\"\n" | 128 " | \"&&\"\n" |
121 " | \"||\" . // lowest priority\n" | 129 " | \"||\" . // lowest priority\n" |
122 "\n" | 130 "\n" |
123 " All binary operators are left-associative.\n"; | 131 " All binary operators are left-associative.\n" |
| 132 "\n" |
| 133 "Types\n" |
| 134 "\n" |
| 135 " The GN language is dynamically typed. The following types are used:\n" |
| 136 "\n" |
| 137 " - Boolean: Uses the keywords \"true\" and \"false\". There is no\n" |
| 138 " implicit conversion between booleans and integers.\n" |
| 139 "\n" |
| 140 " - Integers: All numbers in GN are signed 64-bit integers.\n" |
| 141 "\n" |
| 142 " - Strings: Strings are 8-bit with no enforced encoding. When a string\n" |
| 143 " is used to interact with other systems with particular encodings\n" |
| 144 " (like the Windows and Mac filesystems) it is assumed to be UTF-8.\n" |
| 145 " See \"String literals\" above for more.\n" |
| 146 "\n" |
| 147 " - Lists: Lists are arbitrary-length ordered lists of values. See\n" |
| 148 " \"Lists\" below for more.\n" |
| 149 "\n" |
| 150 " - Scopes: Scopes are like dictionaries that use variable names for\n" |
| 151 " keys. See \"Scopes\" below for more.\n" |
| 152 "\n" |
| 153 "Lists\n" |
| 154 "\n" |
| 155 " Lists are created with [] and using commas to separate items:\n" |
| 156 "\n" |
| 157 " mylist = [ 0, 1, 2, \"some string\" ]\n" |
| 158 "\n" |
| 159 " A comma after the last item is optional. Lists are dereferenced using\n" |
| 160 " 0-based indexing:\n" |
| 161 "\n" |
| 162 " mylist[0] += 1\n" |
| 163 " var = mylist[2]\n" |
| 164 "\n" |
| 165 " Lists can be concatenated using the '+' and '+=' operators. Bare\n" |
| 166 " values can not be concatenated with lists, to add a single item,\n" |
| 167 " it must be put into a list of length one.\n" |
| 168 "\n" |
| 169 " Items can be removed from lists using the '-' and '-=' operators.\n" |
| 170 " This will remove all occurrences of every item in the right-hand list\n" |
| 171 " from the left-hand list. It is an error to remove an item not in the\n" |
| 172 " list. This is to prevent common typos and to detect dead code that\n" |
| 173 " is removing things that no longer apply.\n" |
| 174 "\n" |
| 175 " It is an error to use '=' to replace a nonempty list with another\n" |
| 176 " nonempty list. This is to prevent accidentally overwriting data\n" |
| 177 " when in most cases '+=' was intended. To overwrite a list on purpose,\n" |
| 178 " first assign it to the empty list:\n" |
| 179 "\n" |
| 180 " mylist = []\n" |
| 181 " mylist = otherlist\n" |
| 182 "\n" |
| 183 " When assigning to a list named 'sources' using '=' or '+=', list\n" |
| 184 " items may be automatically filtered out.\n" |
| 185 " See \"gn help set_sources_assignment_filter\" for more.\n" |
| 186 "\n" |
| 187 "Scopes\n" |
| 188 "\n" |
| 189 " All execution happens in the context of a scope which holds the\n" |
| 190 " current state (like variables). With the exception of loops and\n" |
| 191 " conditions, '{' introduces a new scope that has a parent reference to\n" |
| 192 " the old scope.\n" |
| 193 "\n" |
| 194 " Variable reads recursively search all nested scopes until the\n" |
| 195 " variable is found or there are no more scopes. Variable writes always\n" |
| 196 " go into the current scope. This means that after the closing '}'\n" |
| 197 " (again excepting loops and conditions), all local variables will be\n" |
| 198 " restored to the previous values. This also means that \"foo = foo\"\n" |
| 199 " can do useful work by copying a variable into the current scope that\n" |
| 200 " was defined in a containing scope.\n" |
| 201 "\n" |
| 202 " Scopes can also be assigned to variables. Such scopes can be created\n" |
| 203 " by functions like exec_script, when invoking a template (the template\n" |
| 204 " code refers to the variables set by the invoking code by the\n" |
| 205 " implicitly-created \"invoker\" scope), or explicitly like:\n" |
| 206 "\n" |
| 207 " empty_scope = {}\n" |
| 208 " myvalues = {\n" |
| 209 " foo = 21\n" |
| 210 " bar = \"something\"\n" |
| 211 " }\n" |
| 212 "\n" |
| 213 " Inside such a scope definition can be any GN code including\n" |
| 214 " conditionals and function calls. After the close of the scope, it will\n" |
| 215 " contain all variables explicitly set by the code contained inside it.\n" |
| 216 " After this, the values can be read, modified, or added to:\n" |
| 217 "\n" |
| 218 " myvalues.foo += 2\n" |
| 219 " empty_scope.new_thing = [ 1, 2, 3 ]\n"; |
124 | 220 |
125 enum Precedence { | 221 enum Precedence { |
126 PRECEDENCE_ASSIGNMENT = 1, // Lowest precedence. | 222 PRECEDENCE_ASSIGNMENT = 1, // Lowest precedence. |
127 PRECEDENCE_OR = 2, | 223 PRECEDENCE_OR = 2, |
128 PRECEDENCE_AND = 3, | 224 PRECEDENCE_AND = 3, |
129 PRECEDENCE_EQUALITY = 4, | 225 PRECEDENCE_EQUALITY = 4, |
130 PRECEDENCE_RELATION = 5, | 226 PRECEDENCE_RELATION = 5, |
131 PRECEDENCE_SUM = 6, | 227 PRECEDENCE_SUM = 6, |
132 PRECEDENCE_PREFIX = 7, | 228 PRECEDENCE_PREFIX = 7, |
133 PRECEDENCE_CALL = 8, | 229 PRECEDENCE_CALL = 8, |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
165 {nullptr, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // LESS_THAN | 261 {nullptr, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // LESS_THAN |
166 {nullptr, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // GREATER_THAN | 262 {nullptr, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // GREATER_THAN |
167 {nullptr, &Parser::BinaryOperator, PRECEDENCE_AND}, // BOOLEAN_AND | 263 {nullptr, &Parser::BinaryOperator, PRECEDENCE_AND}, // BOOLEAN_AND |
168 {nullptr, &Parser::BinaryOperator, PRECEDENCE_OR}, // BOOLEAN_OR | 264 {nullptr, &Parser::BinaryOperator, PRECEDENCE_OR}, // BOOLEAN_OR |
169 {&Parser::Not, nullptr, -1}, // BANG | 265 {&Parser::Not, nullptr, -1}, // BANG |
170 {nullptr, &Parser::DotOperator, PRECEDENCE_DOT}, // DOT | 266 {nullptr, &Parser::DotOperator, PRECEDENCE_DOT}, // DOT |
171 {&Parser::Group, nullptr, -1}, // LEFT_PAREN | 267 {&Parser::Group, nullptr, -1}, // LEFT_PAREN |
172 {nullptr, nullptr, -1}, // RIGHT_PAREN | 268 {nullptr, nullptr, -1}, // RIGHT_PAREN |
173 {&Parser::List, &Parser::Subscript, PRECEDENCE_CALL}, // LEFT_BRACKET | 269 {&Parser::List, &Parser::Subscript, PRECEDENCE_CALL}, // LEFT_BRACKET |
174 {nullptr, nullptr, -1}, // RIGHT_BRACKET | 270 {nullptr, nullptr, -1}, // RIGHT_BRACKET |
175 {nullptr, nullptr, -1}, // LEFT_BRACE | 271 {&Parser::Block, nullptr, -1}, // LEFT_BRACE |
176 {nullptr, nullptr, -1}, // RIGHT_BRACE | 272 {nullptr, nullptr, -1}, // RIGHT_BRACE |
177 {nullptr, nullptr, -1}, // IF | 273 {nullptr, nullptr, -1}, // IF |
178 {nullptr, nullptr, -1}, // ELSE | 274 {nullptr, nullptr, -1}, // ELSE |
179 {&Parser::Name, &Parser::IdentifierOrCall, PRECEDENCE_CALL}, // IDENTIFIER | 275 {&Parser::Name, &Parser::IdentifierOrCall, PRECEDENCE_CALL}, // IDENTIFIER |
180 {nullptr, nullptr, -1}, // COMMA | 276 {nullptr, nullptr, -1}, // COMMA |
181 {nullptr, nullptr, -1}, // UNCLASSIFIED_COMMENT | 277 {nullptr, nullptr, -1}, // UNCLASSIFIED_COMMENT |
182 {nullptr, nullptr, -1}, // LINE_COMMENT | 278 {nullptr, nullptr, -1}, // LINE_COMMENT |
183 {nullptr, nullptr, -1}, // SUFFIX_COMMENT | 279 {nullptr, nullptr, -1}, // SUFFIX_COMMENT |
184 {&Parser::BlockComment, nullptr, -1}, // BLOCK_COMMENT | 280 {&Parser::BlockComment, nullptr, -1}, // BLOCK_COMMENT |
185 }; | 281 }; |
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
349 return std::unique_ptr<ParseNode>(); | 445 return std::unique_ptr<ParseNode>(); |
350 } | 446 } |
351 left = (this->*infix)(std::move(left), token); | 447 left = (this->*infix)(std::move(left), token); |
352 if (has_error()) | 448 if (has_error()) |
353 return std::unique_ptr<ParseNode>(); | 449 return std::unique_ptr<ParseNode>(); |
354 } | 450 } |
355 | 451 |
356 return left; | 452 return left; |
357 } | 453 } |
358 | 454 |
| 455 std::unique_ptr<ParseNode> Parser::Block(Token token) { |
| 456 // This entrypoing into ParseBlock means its part of an expression and we |
| 457 // always want the result. |
| 458 return ParseBlock(token, BlockNode::RETURNS_SCOPE); |
| 459 } |
| 460 |
359 std::unique_ptr<ParseNode> Parser::Literal(Token token) { | 461 std::unique_ptr<ParseNode> Parser::Literal(Token token) { |
360 return base::WrapUnique(new LiteralNode(token)); | 462 return base::WrapUnique(new LiteralNode(token)); |
361 } | 463 } |
362 | 464 |
363 std::unique_ptr<ParseNode> Parser::Name(Token token) { | 465 std::unique_ptr<ParseNode> Parser::Name(Token token) { |
364 return IdentifierOrCall(std::unique_ptr<ParseNode>(), token); | 466 return IdentifierOrCall(std::unique_ptr<ParseNode>(), token); |
365 } | 467 } |
366 | 468 |
367 std::unique_ptr<ParseNode> Parser::BlockComment(Token token) { | 469 std::unique_ptr<ParseNode> Parser::BlockComment(Token token) { |
368 std::unique_ptr<BlockCommentNode> comment(new BlockCommentNode()); | 470 std::unique_ptr<BlockCommentNode> comment(new BlockCommentNode()); |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
434 if (Match(Token::RIGHT_PAREN)) { | 536 if (Match(Token::RIGHT_PAREN)) { |
435 // Nothing, just an empty call. | 537 // Nothing, just an empty call. |
436 } else { | 538 } else { |
437 list = ParseList(start_token, Token::RIGHT_PAREN, false); | 539 list = ParseList(start_token, Token::RIGHT_PAREN, false); |
438 if (has_error()) | 540 if (has_error()) |
439 return std::unique_ptr<ParseNode>(); | 541 return std::unique_ptr<ParseNode>(); |
440 Consume(Token::RIGHT_PAREN, "Expected ')' after call"); | 542 Consume(Token::RIGHT_PAREN, "Expected ')' after call"); |
441 } | 543 } |
442 // Optionally with a scope. | 544 // Optionally with a scope. |
443 if (LookAhead(Token::LEFT_BRACE)) { | 545 if (LookAhead(Token::LEFT_BRACE)) { |
444 block = ParseBlock(); | 546 block = ParseBlock(Consume(), BlockNode::DISCARDS_RESULT); |
445 if (has_error()) | 547 if (has_error()) |
446 return std::unique_ptr<ParseNode>(); | 548 return std::unique_ptr<ParseNode>(); |
447 } | 549 } |
448 } | 550 } |
449 | 551 |
450 if (!left && !has_arg) { | 552 if (!left && !has_arg) { |
451 // Not a function call, just a standalone identifier. | 553 // Not a function call, just a standalone identifier. |
452 return std::unique_ptr<ParseNode>(new IdentifierNode(token)); | 554 return std::unique_ptr<ParseNode>(new IdentifierNode(token)); |
453 } | 555 } |
454 std::unique_ptr<FunctionCallNode> func_call(new FunctionCallNode); | 556 std::unique_ptr<FunctionCallNode> func_call(new FunctionCallNode); |
455 func_call->set_function(token); | 557 func_call->set_function(token); |
456 func_call->set_args(std::move(list)); | 558 func_call->set_args(std::move(list)); |
457 if (block) | 559 if (block) |
458 func_call->set_block(std::move(block)); | 560 func_call->set_block(std::move(block)); |
459 return std::move(func_call); | 561 return std::move(func_call); |
460 } | 562 } |
461 | 563 |
462 std::unique_ptr<ParseNode> Parser::Assignment(std::unique_ptr<ParseNode> left, | 564 std::unique_ptr<ParseNode> Parser::Assignment(std::unique_ptr<ParseNode> left, |
463 Token token) { | 565 Token token) { |
464 if (left->AsIdentifier() == nullptr) { | 566 if (left->AsIdentifier() == nullptr && left->AsAccessor() == nullptr) { |
465 *err_ = Err(left.get(), "Left-hand side of assignment must be identifier."); | 567 *err_ = Err(left.get(), |
| 568 "The left-hand side of an assignment must be an identifier, " |
| 569 "scope access, or array access."); |
466 return std::unique_ptr<ParseNode>(); | 570 return std::unique_ptr<ParseNode>(); |
467 } | 571 } |
468 std::unique_ptr<ParseNode> value = ParseExpression(PRECEDENCE_ASSIGNMENT); | 572 std::unique_ptr<ParseNode> value = ParseExpression(PRECEDENCE_ASSIGNMENT); |
469 if (!value) { | 573 if (!value) { |
470 if (!has_error()) | 574 if (!has_error()) |
471 *err_ = Err(token, "Expected right-hand side for assignment."); | 575 *err_ = Err(token, "Expected right-hand side for assignment."); |
472 return std::unique_ptr<ParseNode>(); | 576 return std::unique_ptr<ParseNode>(); |
473 } | 577 } |
474 std::unique_ptr<BinaryOpNode> assign(new BinaryOpNode); | 578 std::unique_ptr<BinaryOpNode> assign(new BinaryOpNode); |
475 assign->set_op(token); | 579 assign->set_op(token); |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
560 } | 664 } |
561 if (just_got_comma && !allow_trailing_comma) { | 665 if (just_got_comma && !allow_trailing_comma) { |
562 *err_ = Err(cur_token(), "Trailing comma"); | 666 *err_ = Err(cur_token(), "Trailing comma"); |
563 return std::unique_ptr<ListNode>(); | 667 return std::unique_ptr<ListNode>(); |
564 } | 668 } |
565 list->set_end(base::WrapUnique(new EndNode(cur_token()))); | 669 list->set_end(base::WrapUnique(new EndNode(cur_token()))); |
566 return list; | 670 return list; |
567 } | 671 } |
568 | 672 |
569 std::unique_ptr<ParseNode> Parser::ParseFile() { | 673 std::unique_ptr<ParseNode> Parser::ParseFile() { |
570 std::unique_ptr<BlockNode> file(new BlockNode); | 674 std::unique_ptr<BlockNode> file(new BlockNode(BlockNode::DISCARDS_RESULT)); |
571 for (;;) { | 675 for (;;) { |
572 if (at_end()) | 676 if (at_end()) |
573 break; | 677 break; |
574 std::unique_ptr<ParseNode> statement = ParseStatement(); | 678 std::unique_ptr<ParseNode> statement = ParseStatement(); |
575 if (!statement) | 679 if (!statement) |
576 break; | 680 break; |
577 file->append_statement(std::move(statement)); | 681 file->append_statement(std::move(statement)); |
578 } | 682 } |
579 if (!at_end() && !has_error()) | 683 if (!at_end() && !has_error()) |
580 *err_ = Err(cur_token(), "Unexpected here, should be newline."); | 684 *err_ = Err(cur_token(), "Unexpected here, should be newline."); |
(...skipping 23 matching lines...) Expand all Loading... |
604 return stmt; | 708 return stmt; |
605 } | 709 } |
606 if (!has_error()) { | 710 if (!has_error()) { |
607 Token token = at_end() ? tokens_[tokens_.size() - 1] : cur_token(); | 711 Token token = at_end() ? tokens_[tokens_.size() - 1] : cur_token(); |
608 *err_ = Err(token, "Expecting assignment or function call."); | 712 *err_ = Err(token, "Expecting assignment or function call."); |
609 } | 713 } |
610 return std::unique_ptr<ParseNode>(); | 714 return std::unique_ptr<ParseNode>(); |
611 } | 715 } |
612 } | 716 } |
613 | 717 |
614 std::unique_ptr<BlockNode> Parser::ParseBlock() { | 718 std::unique_ptr<BlockNode> Parser::ParseBlock( |
615 Token begin_token = | 719 Token begin_brace, |
616 Consume(Token::LEFT_BRACE, "Expected '{' to start a block."); | 720 BlockNode::ResultMode result_mode) { |
617 if (has_error()) | 721 if (has_error()) |
618 return std::unique_ptr<BlockNode>(); | 722 return std::unique_ptr<BlockNode>(); |
619 std::unique_ptr<BlockNode> block(new BlockNode); | 723 std::unique_ptr<BlockNode> block(new BlockNode(result_mode)); |
620 block->set_begin_token(begin_token); | 724 block->set_begin_token(begin_brace); |
621 | 725 |
622 for (;;) { | 726 for (;;) { |
623 if (LookAhead(Token::RIGHT_BRACE)) { | 727 if (LookAhead(Token::RIGHT_BRACE)) { |
624 block->set_end(base::WrapUnique(new EndNode(Consume()))); | 728 block->set_end(base::WrapUnique(new EndNode(Consume()))); |
625 break; | 729 break; |
626 } | 730 } |
627 | 731 |
628 std::unique_ptr<ParseNode> statement = ParseStatement(); | 732 std::unique_ptr<ParseNode> statement = ParseStatement(); |
629 if (!statement) | 733 if (!statement) |
630 return std::unique_ptr<BlockNode>(); | 734 return std::unique_ptr<BlockNode>(); |
631 block->append_statement(std::move(statement)); | 735 block->append_statement(std::move(statement)); |
632 } | 736 } |
633 return block; | 737 return block; |
634 } | 738 } |
635 | 739 |
636 std::unique_ptr<ParseNode> Parser::ParseCondition() { | 740 std::unique_ptr<ParseNode> Parser::ParseCondition() { |
637 std::unique_ptr<ConditionNode> condition(new ConditionNode); | 741 std::unique_ptr<ConditionNode> condition(new ConditionNode); |
638 condition->set_if_token(Consume(Token::IF, "Expected 'if'")); | 742 condition->set_if_token(Consume(Token::IF, "Expected 'if'")); |
639 Consume(Token::LEFT_PAREN, "Expected '(' after 'if'."); | 743 Consume(Token::LEFT_PAREN, "Expected '(' after 'if'."); |
640 condition->set_condition(ParseExpression()); | 744 condition->set_condition(ParseExpression()); |
641 if (IsAssignment(condition->condition())) | 745 if (IsAssignment(condition->condition())) |
642 *err_ = Err(condition->condition(), "Assignment not allowed in 'if'."); | 746 *err_ = Err(condition->condition(), "Assignment not allowed in 'if'."); |
643 Consume(Token::RIGHT_PAREN, "Expected ')' after condition of 'if'."); | 747 Consume(Token::RIGHT_PAREN, "Expected ')' after condition of 'if'."); |
644 condition->set_if_true(ParseBlock()); | 748 condition->set_if_true(ParseBlock( |
| 749 Consume(Token::LEFT_BRACE, "Expected '{' to start 'if' block."), |
| 750 BlockNode::DISCARDS_RESULT)); |
645 if (Match(Token::ELSE)) { | 751 if (Match(Token::ELSE)) { |
646 if (LookAhead(Token::LEFT_BRACE)) { | 752 if (LookAhead(Token::LEFT_BRACE)) { |
647 condition->set_if_false(ParseBlock()); | 753 condition->set_if_false(ParseBlock(Consume(), |
| 754 BlockNode::DISCARDS_RESULT)); |
648 } else if (LookAhead(Token::IF)) { | 755 } else if (LookAhead(Token::IF)) { |
649 condition->set_if_false(ParseStatement()); | 756 condition->set_if_false(ParseStatement()); |
650 } else { | 757 } else { |
651 *err_ = Err(cur_token(), "Expected '{' or 'if' after 'else'."); | 758 *err_ = Err(cur_token(), "Expected '{' or 'if' after 'else'."); |
652 return std::unique_ptr<ParseNode>(); | 759 return std::unique_ptr<ParseNode>(); |
653 } | 760 } |
654 } | 761 } |
655 if (has_error()) | 762 if (has_error()) |
656 return std::unique_ptr<ParseNode>(); | 763 return std::unique_ptr<ParseNode>(); |
657 return std::move(condition); | 764 return std::move(condition); |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
761 break; | 868 break; |
762 } | 869 } |
763 } | 870 } |
764 | 871 |
765 // Suffix comments were assigned in reverse, so if there were multiple on | 872 // Suffix comments were assigned in reverse, so if there were multiple on |
766 // the same node, they need to be reversed. | 873 // the same node, they need to be reversed. |
767 if ((*i)->comments() && !(*i)->comments()->suffix().empty()) | 874 if ((*i)->comments() && !(*i)->comments()->suffix().empty()) |
768 const_cast<ParseNode*>(*i)->comments_mutable()->ReverseSuffix(); | 875 const_cast<ParseNode*>(*i)->comments_mutable()->ReverseSuffix(); |
769 } | 876 } |
770 } | 877 } |
OLD | NEW |