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
3 // found in the LICENSE file.
5 #include "tools/gn/parser.h"
7 #include "base/logging.h"
8 #include "tools/gn/functions.h"
9 #include "tools/gn/operators.h"
10 #include "tools/gn/token.h"
12 const char kGrammar_Help
[] =
13 "GN build language grammar\n"
17 " GN build files are read as sequences of tokens. While splitting the\n"
18 " file into tokens, the next token is the longest sequence of characters\n"
19 " that form a valid token.\n"
21 "White space and comments\n"
23 " White space is comprised of spaces (U+0020), horizontal tabs (U+0009),\n"
24 " carriage returns (U+000D), and newlines (U+000A).\n"
26 " Comments start at the character \"#\" and stop at the next newline.\n"
28 " White space and comments are ignored except that they may separate\n"
29 " tokens that would otherwise combine into a single token.\n"
33 " Identifiers name variables and functions.\n"
35 " identifier = letter { letter | digit } .\n"
36 " letter = \"A\" ... \"Z\" | \"a\" ... \"z\" | \"_\" .\n"
37 " digit = \"0\" ... \"9\" .\n"
41 " The following keywords are reserved and may not be used as\n"
44 " else false if true\n"
48 " An integer literal represents a decimal integer value.\n"
50 " integer = [ \"-\" ] digit { digit } .\n"
52 " Leading zeros and negative zero are disallowed.\n"
56 " A string literal represents a string value consisting of the quoted\n"
57 " characters with possible escape sequences and variable expansions.\n"
59 " string = `\"` { char | escape | expansion } `\"` .\n"
60 " escape = `\\` ( \"$\" | `\"` | char ) .\n"
61 " BracketExpansion = \"{\" ( identifier | ArrayAccess | ScopeAccess "
63 " expansion = \"$\" ( identifier | BracketExpansion ) .\n"
64 " char = /* any character except \"$\", `\"`, or newline "
67 " After a backslash, certain sequences represent special characters:\n"
69 " \\\" U+0022 quotation mark\n"
70 " \\$ U+0024 dollar sign\n"
71 " \\\\ U+005C backslash\n"
73 " All other backslashes represent themselves.\n"
77 " The following character sequences represent punctuation:\n"
86 " The input tokens form a syntax tree following a context-free grammar:\n"
88 " File = StatementList .\n"
90 " Statement = Assignment | Call | Condition .\n"
91 " Assignment = identifier AssignOp Expr .\n"
92 " Call = identifier \"(\" [ ExprList ] \")\" [ Block ] .\n"
93 " Condition = \"if\" \"(\" Expr \")\" Block\n"
94 " [ \"else\" ( Condition | Block ) ] .\n"
95 " Block = \"{\" StatementList \"}\" .\n"
96 " StatementList = { Statement } .\n"
98 " ArrayAccess = identifier \"[\" { identifier | integer } \"]\" .\n"
99 " ScopeAccess = identifier \".\" identifier .\n"
100 " Expr = UnaryExpr | Expr BinaryOp Expr .\n"
101 " UnaryExpr = PrimaryExpr | UnaryOp UnaryExpr .\n"
102 " PrimaryExpr = identifier | integer | string | Call\n"
103 " | ArrayAccess | ScopeAccess\n"
104 " | \"(\" Expr \")\"\n"
105 " | \"[\" [ ExprList [ \",\" ] ] \"]\" .\n"
106 " ExprList = Expr { \",\" Expr } .\n"
108 " AssignOp = \"=\" | \"+=\" | \"-=\" .\n"
109 " UnaryOp = \"!\" .\n"
110 " BinaryOp = \"+\" | \"-\" // highest priority\n"
111 " | \"<\" | \"<=\" | \">\" | \">=\"\n"
112 " | \"==\" | \"!=\"\n"
114 " | \"||\" . // lowest priority\n"
116 " All binary operators are left-associative.\n";
119 PRECEDENCE_ASSIGNMENT
= 1, // Lowest precedence.
122 PRECEDENCE_EQUALITY
= 4,
123 PRECEDENCE_RELATION
= 5,
125 PRECEDENCE_PREFIX
= 7,
127 PRECEDENCE_DOT
= 9, // Highest precedence.
130 // The top-level for blocks/ifs is recursive descent, the expression parser is
131 // a Pratt parser. The basic idea there is to have the precedences (and
132 // associativities) encoded relative to each other and only parse up until you
133 // hit something of that precedence. There's a dispatch table in expressions_
134 // at the top of parser.cc that describes how each token dispatches if it's
135 // seen as either a prefix or infix operator, and if it's infix, what its
139 // - http://javascript.crockford.com/tdop/tdop.html
140 // - http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
142 // Indexed by Token::Type.
143 ParserHelper
Parser::expressions_
[] = {
144 {nullptr, nullptr, -1}, // INVALID
145 {&Parser::Literal
, nullptr, -1}, // INTEGER
146 {&Parser::Literal
, nullptr, -1}, // STRING
147 {&Parser::Literal
, nullptr, -1}, // TRUE_TOKEN
148 {&Parser::Literal
, nullptr, -1}, // FALSE_TOKEN
149 {nullptr, &Parser::Assignment
, PRECEDENCE_ASSIGNMENT
}, // EQUAL
150 {nullptr, &Parser::BinaryOperator
, PRECEDENCE_SUM
}, // PLUS
151 {nullptr, &Parser::BinaryOperator
, PRECEDENCE_SUM
}, // MINUS
152 {nullptr, &Parser::Assignment
, PRECEDENCE_ASSIGNMENT
}, // PLUS_EQUALS
153 {nullptr, &Parser::Assignment
, PRECEDENCE_ASSIGNMENT
}, // MINUS_EQUALS
154 {nullptr, &Parser::BinaryOperator
, PRECEDENCE_EQUALITY
}, // EQUAL_EQUAL
155 {nullptr, &Parser::BinaryOperator
, PRECEDENCE_EQUALITY
}, // NOT_EQUAL
156 {nullptr, &Parser::BinaryOperator
, PRECEDENCE_RELATION
}, // LESS_EQUAL
157 {nullptr, &Parser::BinaryOperator
, PRECEDENCE_RELATION
}, // GREATER_EQUAL
158 {nullptr, &Parser::BinaryOperator
, PRECEDENCE_RELATION
}, // LESS_THAN
159 {nullptr, &Parser::BinaryOperator
, PRECEDENCE_RELATION
}, // GREATER_THAN
160 {nullptr, &Parser::BinaryOperator
, PRECEDENCE_AND
}, // BOOLEAN_AND
161 {nullptr, &Parser::BinaryOperator
, PRECEDENCE_OR
}, // BOOLEAN_OR
162 {&Parser::Not
, nullptr, -1}, // BANG
163 {nullptr, &Parser::DotOperator
, PRECEDENCE_DOT
}, // DOT
164 {&Parser::Group
, nullptr, -1}, // LEFT_PAREN
165 {nullptr, nullptr, -1}, // RIGHT_PAREN
166 {&Parser::List
, &Parser::Subscript
, PRECEDENCE_CALL
}, // LEFT_BRACKET
167 {nullptr, nullptr, -1}, // RIGHT_BRACKET
168 {nullptr, nullptr, -1}, // LEFT_BRACE
169 {nullptr, nullptr, -1}, // RIGHT_BRACE
170 {nullptr, nullptr, -1}, // IF
171 {nullptr, nullptr, -1}, // ELSE
172 {&Parser::Name
, &Parser::IdentifierOrCall
, PRECEDENCE_CALL
}, // IDENTIFIER
173 {nullptr, nullptr, -1}, // COMMA
174 {nullptr, nullptr, -1}, // UNCLASSIFIED_COMMENT
175 {nullptr, nullptr, -1}, // LINE_COMMENT
176 {nullptr, nullptr, -1}, // SUFFIX_COMMENT
177 {&Parser::BlockComment
, nullptr, -1}, // BLOCK_COMMENT
180 Parser::Parser(const std::vector
<Token
>& tokens
, Err
* err
)
181 : err_(err
), cur_(0) {
182 for (const auto& token
: tokens
) {
183 switch (token
.type()) {
184 case Token::LINE_COMMENT
:
185 line_comment_tokens_
.push_back(token
);
187 case Token::SUFFIX_COMMENT
:
188 suffix_comment_tokens_
.push_back(token
);
191 // Note that BLOCK_COMMENTs (top-level standalone comments) are passed
192 // through the real parser.
193 tokens_
.push_back(token
);
203 scoped_ptr
<ParseNode
> Parser::Parse(const std::vector
<Token
>& tokens
,
205 Parser
p(tokens
, err
);
206 return p
.ParseFile();
210 scoped_ptr
<ParseNode
> Parser::ParseExpression(const std::vector
<Token
>& tokens
,
212 Parser
p(tokens
, err
);
213 scoped_ptr
<ParseNode
> expr
= p
.ParseExpression();
214 if (!p
.at_end() && !err
->has_error()) {
215 *err
= Err(p
.cur_token(), "Trailing garbage");
222 scoped_ptr
<ParseNode
> Parser::ParseValue(const std::vector
<Token
>& tokens
,
224 for (const Token
& token
: tokens
) {
225 switch (token
.type()) {
228 case Token::TRUE_TOKEN
:
229 case Token::FALSE_TOKEN
:
230 case Token::LEFT_BRACKET
:
231 case Token::RIGHT_BRACKET
:
235 *err
= Err(token
, "Invalid token in literal value");
240 return ParseExpression(tokens
, err
);
243 bool Parser::IsAssignment(const ParseNode
* node
) const {
244 return node
&& node
->AsBinaryOp() &&
245 (node
->AsBinaryOp()->op().type() == Token::EQUAL
||
246 node
->AsBinaryOp()->op().type() == Token::PLUS_EQUALS
||
247 node
->AsBinaryOp()->op().type() == Token::MINUS_EQUALS
);
250 bool Parser::IsStatementBreak(Token::Type token_type
) const {
251 switch (token_type
) {
252 case Token::IDENTIFIER
:
253 case Token::LEFT_BRACE
:
254 case Token::RIGHT_BRACE
:
263 bool Parser::LookAhead(Token::Type type
) {
266 return cur_token().type() == type
;
269 bool Parser::Match(Token::Type type
) {
270 if (!LookAhead(type
))
276 Token
Parser::Consume(Token::Type type
, const char* error_message
) {
277 Token::Type types
[1] = { type
};
278 return Consume(types
, 1, error_message
);
281 Token
Parser::Consume(Token::Type
* types
,
283 const char* error_message
) {
285 // Don't overwrite current error, but make progress through tokens so that
286 // a loop that's expecting a particular token will still terminate.
288 return Token(Location(), Token::INVALID
, base::StringPiece());
291 const char kEOFMsg
[] = "I hit EOF instead.";
293 *err_
= Err(Location(), error_message
, kEOFMsg
);
295 *err_
= Err(tokens_
[tokens_
.size() - 1], error_message
, kEOFMsg
);
296 return Token(Location(), Token::INVALID
, base::StringPiece());
299 for (size_t i
= 0; i
< num_types
; ++i
) {
300 if (cur_token().type() == types
[i
])
303 *err_
= Err(cur_token(), error_message
);
304 return Token(Location(), Token::INVALID
, base::StringPiece());
307 Token
Parser::Consume() {
308 return tokens_
[cur_
++];
311 scoped_ptr
<ParseNode
> Parser::ParseExpression() {
312 return ParseExpression(0);
315 scoped_ptr
<ParseNode
> Parser::ParseExpression(int precedence
) {
317 return scoped_ptr
<ParseNode
>();
319 Token token
= Consume();
320 PrefixFunc prefix
= expressions_
[token
.type()].prefix
;
322 if (prefix
== nullptr) {
324 std::string("Unexpected token '") + token
.value().as_string() +
326 return scoped_ptr
<ParseNode
>();
329 scoped_ptr
<ParseNode
> left
= (this->*prefix
)(token
);
333 while (!at_end() && !IsStatementBreak(cur_token().type()) &&
334 precedence
<= expressions_
[cur_token().type()].precedence
) {
336 InfixFunc infix
= expressions_
[token
.type()].infix
;
337 if (infix
== nullptr) {
339 std::string("Unexpected token '") +
340 token
.value().as_string() + std::string("'"));
341 return scoped_ptr
<ParseNode
>();
343 left
= (this->*infix
)(left
.Pass(), token
);
345 return scoped_ptr
<ParseNode
>();
351 scoped_ptr
<ParseNode
> Parser::Literal(Token token
) {
352 return make_scoped_ptr(new LiteralNode(token
));
355 scoped_ptr
<ParseNode
> Parser::Name(Token token
) {
356 return IdentifierOrCall(scoped_ptr
<ParseNode
>(), token
).Pass();
359 scoped_ptr
<ParseNode
> Parser::BlockComment(Token token
) {
360 scoped_ptr
<BlockCommentNode
> comment(new BlockCommentNode());
361 comment
->set_comment(token
);
362 return comment
.Pass();
365 scoped_ptr
<ParseNode
> Parser::Group(Token token
) {
366 scoped_ptr
<ParseNode
> expr
= ParseExpression();
368 return scoped_ptr
<ParseNode
>();
369 Consume(Token::RIGHT_PAREN
, "Expected ')'");
373 scoped_ptr
<ParseNode
> Parser::Not(Token token
) {
374 scoped_ptr
<ParseNode
> expr
= ParseExpression(PRECEDENCE_PREFIX
+ 1);
376 return scoped_ptr
<ParseNode
>();
379 *err_
= Err(token
, "Expected right-hand side for '!'.");
380 return scoped_ptr
<ParseNode
>();
382 scoped_ptr
<UnaryOpNode
> unary_op(new UnaryOpNode
);
383 unary_op
->set_op(token
);
384 unary_op
->set_operand(expr
.Pass());
385 return unary_op
.Pass();
388 scoped_ptr
<ParseNode
> Parser::List(Token node
) {
389 scoped_ptr
<ParseNode
> list(ParseList(node
, Token::RIGHT_BRACKET
, true));
390 if (!has_error() && !at_end())
391 Consume(Token::RIGHT_BRACKET
, "Expected ']'");
395 scoped_ptr
<ParseNode
> Parser::BinaryOperator(scoped_ptr
<ParseNode
> left
,
397 scoped_ptr
<ParseNode
> right
=
398 ParseExpression(expressions_
[token
.type()].precedence
+ 1);
401 *err_
= Err(token
, "Expected right-hand side for '" +
402 token
.value().as_string() + "'");
404 return scoped_ptr
<ParseNode
>();
406 scoped_ptr
<BinaryOpNode
> binary_op(new BinaryOpNode
);
407 binary_op
->set_op(token
);
408 binary_op
->set_left(left
.Pass());
409 binary_op
->set_right(right
.Pass());
410 return binary_op
.Pass();
413 scoped_ptr
<ParseNode
> Parser::IdentifierOrCall(scoped_ptr
<ParseNode
> left
,
415 scoped_ptr
<ListNode
> list(new ListNode
);
416 list
->set_begin_token(token
);
417 list
->set_end(make_scoped_ptr(new EndNode(token
)));
418 scoped_ptr
<BlockNode
> block
;
419 bool has_arg
= false;
420 if (LookAhead(Token::LEFT_PAREN
)) {
421 Token start_token
= Consume();
422 // Parsing a function call.
424 if (Match(Token::RIGHT_PAREN
)) {
425 // Nothing, just an empty call.
427 list
= ParseList(start_token
, Token::RIGHT_PAREN
, false);
429 return scoped_ptr
<ParseNode
>();
430 Consume(Token::RIGHT_PAREN
, "Expected ')' after call");
432 // Optionally with a scope.
433 if (LookAhead(Token::LEFT_BRACE
)) {
434 block
= ParseBlock();
436 return scoped_ptr
<ParseNode
>();
440 if (!left
&& !has_arg
) {
441 // Not a function call, just a standalone identifier.
442 return scoped_ptr
<ParseNode
>(new IdentifierNode(token
)).Pass();
444 scoped_ptr
<FunctionCallNode
> func_call(new FunctionCallNode
);
445 func_call
->set_function(token
);
446 func_call
->set_args(list
.Pass());
448 func_call
->set_block(block
.Pass());
449 return func_call
.Pass();
452 scoped_ptr
<ParseNode
> Parser::Assignment(scoped_ptr
<ParseNode
> left
,
454 if (left
->AsIdentifier() == nullptr) {
455 *err_
= Err(left
.get(), "Left-hand side of assignment must be identifier.");
456 return scoped_ptr
<ParseNode
>();
458 scoped_ptr
<ParseNode
> value
= ParseExpression(PRECEDENCE_ASSIGNMENT
);
461 *err_
= Err(token
, "Expected right-hand side for assignment.");
462 return scoped_ptr
<ParseNode
>();
464 scoped_ptr
<BinaryOpNode
> assign(new BinaryOpNode
);
465 assign
->set_op(token
);
466 assign
->set_left(left
.Pass());
467 assign
->set_right(value
.Pass());
468 return assign
.Pass();
471 scoped_ptr
<ParseNode
> Parser::Subscript(scoped_ptr
<ParseNode
> left
,
473 // TODO: Maybe support more complex expressions like a[0][0]. This would
474 // require work on the evaluator too.
475 if (left
->AsIdentifier() == nullptr) {
476 *err_
= Err(left
.get(), "May only subscript identifiers.",
477 "The thing on the left hand side of the [] must be an identifier\n"
478 "and not an expression. If you need this, you'll have to assign the\n"
479 "value to a temporary before subscripting. Sorry.");
480 return scoped_ptr
<ParseNode
>();
482 scoped_ptr
<ParseNode
> value
= ParseExpression();
483 Consume(Token::RIGHT_BRACKET
, "Expecting ']' after subscript.");
484 scoped_ptr
<AccessorNode
> accessor(new AccessorNode
);
485 accessor
->set_base(left
->AsIdentifier()->value());
486 accessor
->set_index(value
.Pass());
487 return accessor
.Pass();
490 scoped_ptr
<ParseNode
> Parser::DotOperator(scoped_ptr
<ParseNode
> left
,
492 if (left
->AsIdentifier() == nullptr) {
493 *err_
= Err(left
.get(), "May only use \".\" for identifiers.",
494 "The thing on the left hand side of the dot must be an identifier\n"
495 "and not an expression. If you need this, you'll have to assign the\n"
496 "value to a temporary first. Sorry.");
497 return scoped_ptr
<ParseNode
>();
500 scoped_ptr
<ParseNode
> right
= ParseExpression(PRECEDENCE_DOT
);
501 if (!right
|| !right
->AsIdentifier()) {
502 *err_
= Err(token
, "Expected identifier for right-hand-side of \".\"",
503 "Good: a.cookies\nBad: a.42\nLooks good but still bad: a.cookies()");
504 return scoped_ptr
<ParseNode
>();
507 scoped_ptr
<AccessorNode
> accessor(new AccessorNode
);
508 accessor
->set_base(left
->AsIdentifier()->value());
509 accessor
->set_member(scoped_ptr
<IdentifierNode
>(
510 static_cast<IdentifierNode
*>(right
.release())));
511 return accessor
.Pass();
514 // Does not Consume the start or end token.
515 scoped_ptr
<ListNode
> Parser::ParseList(Token start_token
,
516 Token::Type stop_before
,
517 bool allow_trailing_comma
) {
518 scoped_ptr
<ListNode
> list(new ListNode
);
519 list
->set_begin_token(start_token
);
520 bool just_got_comma
= false;
521 bool first_time
= true;
522 while (!LookAhead(stop_before
)) {
524 if (!just_got_comma
) {
525 // Require commas separate things in lists.
526 *err_
= Err(cur_token(), "Expected comma between items.");
527 return scoped_ptr
<ListNode
>();
532 // Why _OR? We're parsing things that are higher precedence than the ,
533 // that separates the items of the list. , should appear lower than
534 // boolean expressions (the lowest of which is OR), but above assignments.
535 list
->append_item(ParseExpression(PRECEDENCE_OR
));
537 return scoped_ptr
<ListNode
>();
540 Err(tokens_
[tokens_
.size() - 1], "Unexpected end of file in list.");
541 return scoped_ptr
<ListNode
>();
543 if (list
->contents().back()->AsBlockComment()) {
544 // If there was a comment inside the list, we don't need a comma to the
545 // next item, so pretend we got one, if we're expecting one.
546 just_got_comma
= allow_trailing_comma
;
548 just_got_comma
= Match(Token::COMMA
);
551 if (just_got_comma
&& !allow_trailing_comma
) {
552 *err_
= Err(cur_token(), "Trailing comma");
553 return scoped_ptr
<ListNode
>();
555 list
->set_end(make_scoped_ptr(new EndNode(cur_token())));
559 scoped_ptr
<ParseNode
> Parser::ParseFile() {
560 scoped_ptr
<BlockNode
> file(new BlockNode
);
564 scoped_ptr
<ParseNode
> statement
= ParseStatement();
567 file
->append_statement(statement
.Pass());
569 if (!at_end() && !has_error())
570 *err_
= Err(cur_token(), "Unexpected here, should be newline.");
572 return scoped_ptr
<ParseNode
>();
574 // TODO(scottmg): If this is measurably expensive, it could be done only
575 // when necessary (when reformatting, or during tests). Comments are
576 // separate from the parse tree at this point, so downstream code can remain
578 AssignComments(file
.get());
583 scoped_ptr
<ParseNode
> Parser::ParseStatement() {
584 if (LookAhead(Token::IF
)) {
585 return ParseCondition();
586 } else if (LookAhead(Token::BLOCK_COMMENT
)) {
587 return BlockComment(Consume());
589 // TODO(scottmg): Is this too strict? Just drop all the testing if we want
590 // to allow "pointless" expressions and return ParseExpression() directly.
591 scoped_ptr
<ParseNode
> stmt
= ParseExpression();
593 if (stmt
->AsFunctionCall() || IsAssignment(stmt
.get()))
597 Token token
= at_end() ? tokens_
[tokens_
.size() - 1] : cur_token();
598 *err_
= Err(token
, "Expecting assignment or function call.");
600 return scoped_ptr
<ParseNode
>();
604 scoped_ptr
<BlockNode
> Parser::ParseBlock() {
606 Consume(Token::LEFT_BRACE
, "Expected '{' to start a block.");
608 return scoped_ptr
<BlockNode
>();
609 scoped_ptr
<BlockNode
> block(new BlockNode
);
610 block
->set_begin_token(begin_token
);
613 if (LookAhead(Token::RIGHT_BRACE
)) {
614 block
->set_end(make_scoped_ptr(new EndNode(Consume())));
618 scoped_ptr
<ParseNode
> statement
= ParseStatement();
620 return scoped_ptr
<BlockNode
>();
621 block
->append_statement(statement
.Pass());
626 scoped_ptr
<ParseNode
> Parser::ParseCondition() {
627 scoped_ptr
<ConditionNode
> condition(new ConditionNode
);
628 condition
->set_if_token(Consume(Token::IF
, "Expected 'if'"));
629 Consume(Token::LEFT_PAREN
, "Expected '(' after 'if'.");
630 condition
->set_condition(ParseExpression());
631 if (IsAssignment(condition
->condition()))
632 *err_
= Err(condition
->condition(), "Assignment not allowed in 'if'.");
633 Consume(Token::RIGHT_PAREN
, "Expected ')' after condition of 'if'.");
634 condition
->set_if_true(ParseBlock().Pass());
635 if (Match(Token::ELSE
)) {
636 if (LookAhead(Token::LEFT_BRACE
)) {
637 condition
->set_if_false(ParseBlock().Pass());
638 } else if (LookAhead(Token::IF
)) {
639 condition
->set_if_false(ParseStatement().Pass());
641 *err_
= Err(cur_token(), "Expected '{' or 'if' after 'else'.");
642 return scoped_ptr
<ParseNode
>();
646 return scoped_ptr
<ParseNode
>();
647 return condition
.Pass();
650 void Parser::TraverseOrder(const ParseNode
* root
,
651 std::vector
<const ParseNode
*>* pre
,
652 std::vector
<const ParseNode
*>* post
) {
654 pre
->push_back(root
);
656 if (const AccessorNode
* accessor
= root
->AsAccessor()) {
657 TraverseOrder(accessor
->index(), pre
, post
);
658 TraverseOrder(accessor
->member(), pre
, post
);
659 } else if (const BinaryOpNode
* binop
= root
->AsBinaryOp()) {
660 TraverseOrder(binop
->left(), pre
, post
);
661 TraverseOrder(binop
->right(), pre
, post
);
662 } else if (const BlockNode
* block
= root
->AsBlock()) {
663 for (const auto& statement
: block
->statements())
664 TraverseOrder(statement
, pre
, post
);
665 TraverseOrder(block
->End(), pre
, post
);
666 } else if (const ConditionNode
* condition
= root
->AsConditionNode()) {
667 TraverseOrder(condition
->condition(), pre
, post
);
668 TraverseOrder(condition
->if_true(), pre
, post
);
669 TraverseOrder(condition
->if_false(), pre
, post
);
670 } else if (const FunctionCallNode
* func_call
= root
->AsFunctionCall()) {
671 TraverseOrder(func_call
->args(), pre
, post
);
672 TraverseOrder(func_call
->block(), pre
, post
);
673 } else if (root
->AsIdentifier()) {
675 } else if (const ListNode
* list
= root
->AsList()) {
676 for (const auto& node
: list
->contents())
677 TraverseOrder(node
, pre
, post
);
678 TraverseOrder(list
->End(), pre
, post
);
679 } else if (root
->AsLiteral()) {
681 } else if (const UnaryOpNode
* unaryop
= root
->AsUnaryOp()) {
682 TraverseOrder(unaryop
->operand(), pre
, post
);
683 } else if (root
->AsBlockComment()) {
685 } else if (root
->AsEnd()) {
688 CHECK(false) << "Unhandled case in TraverseOrder.";
691 post
->push_back(root
);
695 void Parser::AssignComments(ParseNode
* file
) {
696 // Start by generating a pre- and post- order traversal of the tree so we
697 // can determine what's before and after comments.
698 std::vector
<const ParseNode
*> pre
;
699 std::vector
<const ParseNode
*> post
;
700 TraverseOrder(file
, &pre
, &post
);
702 // Assign line comments to syntax immediately following.
704 for (const auto& node
: pre
) {
705 const Location
& start
= node
->GetRange().begin();
706 while (cur_comment
< static_cast<int>(line_comment_tokens_
.size())) {
707 if (start
.byte() >= line_comment_tokens_
[cur_comment
].location().byte()) {
708 const_cast<ParseNode
*>(node
)->comments_mutable()->append_before(
709 line_comment_tokens_
[cur_comment
]);
717 // Remaining line comments go at end of file.
718 for (; cur_comment
< static_cast<int>(line_comment_tokens_
.size());
720 file
->comments_mutable()->append_after(line_comment_tokens_
[cur_comment
]);
722 // Assign suffix to syntax immediately before.
723 cur_comment
= static_cast<int>(suffix_comment_tokens_
.size() - 1);
724 for (std::vector
<const ParseNode
*>::const_reverse_iterator i
= post
.rbegin();
727 // Don't assign suffix comments to the function, list, or block, but instead
728 // to the last thing inside.
729 if ((*i
)->AsFunctionCall() || (*i
)->AsList() || (*i
)->AsBlock())
732 const Location
& start
= (*i
)->GetRange().begin();
733 const Location
& end
= (*i
)->GetRange().end();
735 // Don't assign suffix comments to something that starts on an earlier
741 // it's attached to "b", not sources = [ ... ].
742 if (start
.line_number() != end
.line_number())
745 while (cur_comment
>= 0) {
746 if (end
.byte() <= suffix_comment_tokens_
[cur_comment
].location().byte()) {
747 const_cast<ParseNode
*>(*i
)->comments_mutable()->append_suffix(
748 suffix_comment_tokens_
[cur_comment
]);
755 // Suffix comments were assigned in reverse, so if there were multiple on
756 // the same node, they need to be reversed.
757 if ((*i
)->comments() && !(*i
)->comments()->suffix().empty())
758 const_cast<ParseNode
*>(*i
)->comments_mutable()->ReverseSuffix();