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