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. 4 5 #include "tools/gn/parser.h" 6 7 #include "base/logging.h" 8 #include "tools/gn/functions.h" 9 #include "tools/gn/operators.h" 10 #include "tools/gn/token.h" 11 12 // grammar: 13 // 14 // file := (statement)* 15 // statement := block | if | assignment 16 // block := '{' statement* '}' 17 // if := 'if' '(' expr ')' statement [ else ] 18 // else := 'else' (if | statement)* 19 // assignment := ident {'=' | '+=' | '-='} expr 20 21 enum Precedence { 22 PRECEDENCE_ASSIGNMENT = 1, // Lowest precedence. 23 PRECEDENCE_OR = 2, 24 PRECEDENCE_AND = 3, 25 PRECEDENCE_EQUALITY = 4, 26 PRECEDENCE_RELATION = 5, 27 PRECEDENCE_SUM = 6, 28 PRECEDENCE_PREFIX = 7, 29 PRECEDENCE_CALL = 8, 30 PRECEDENCE_DOT = 9, // Highest precedence. 31 }; 32 33 // The top-level for blocks/ifs is recursive descent, the expression parser is 34 // a Pratt parser. The basic idea there is to have the precedences (and 35 // associativities) encoded relative to each other and only parse up until you 36 // hit something of that precedence. There's a dispatch table in expressions_ 37 // at the top of parser.cc that describes how each token dispatches if it's 38 // seen as either a prefix or infix operator, and if it's infix, what its 39 // precedence is. 40 // 41 // Refs: 42 // - http://javascript.crockford.com/tdop/tdop.html 43 // - http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/ 44 45 // Indexed by Token::Type. 46 ParserHelper Parser::expressions_[] = { 47 {NULL, NULL, -1}, // INVALID 48 {&Parser::Literal, NULL, -1}, // INTEGER 49 {&Parser::Literal, NULL, -1}, // STRING 50 {&Parser::Literal, NULL, -1}, // TRUE_TOKEN 51 {&Parser::Literal, NULL, -1}, // FALSE_TOKEN 52 {NULL, &Parser::Assignment, PRECEDENCE_ASSIGNMENT}, // EQUAL 53 {NULL, &Parser::BinaryOperator, PRECEDENCE_SUM}, // PLUS 54 {NULL, &Parser::BinaryOperator, PRECEDENCE_SUM}, // MINUS 55 {NULL, &Parser::Assignment, PRECEDENCE_ASSIGNMENT}, // PLUS_EQUALS 56 {NULL, &Parser::Assignment, PRECEDENCE_ASSIGNMENT}, // MINUS_EQUALS 57 {NULL, &Parser::BinaryOperator, PRECEDENCE_EQUALITY}, // EQUAL_EQUAL 58 {NULL, &Parser::BinaryOperator, PRECEDENCE_EQUALITY}, // NOT_EQUAL 59 {NULL, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // LESS_EQUAL 60 {NULL, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // GREATER_EQUAL 61 {NULL, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // LESS_THAN 62 {NULL, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // GREATER_THAN 63 {NULL, &Parser::BinaryOperator, PRECEDENCE_AND}, // BOOLEAN_AND 64 {NULL, &Parser::BinaryOperator, PRECEDENCE_OR}, // BOOLEAN_OR 65 {&Parser::Not, NULL, -1}, // BANG 66 {NULL, &Parser::DotOperator, PRECEDENCE_DOT}, // DOT 67 {&Parser::Group, NULL, -1}, // LEFT_PAREN 68 {NULL, NULL, -1}, // RIGHT_PAREN 69 {&Parser::List, &Parser::Subscript, PRECEDENCE_CALL}, // LEFT_BRACKET 70 {NULL, NULL, -1}, // RIGHT_BRACKET 71 {NULL, NULL, -1}, // LEFT_BRACE 72 {NULL, NULL, -1}, // RIGHT_BRACE 73 {NULL, NULL, -1}, // IF 74 {NULL, NULL, -1}, // ELSE 75 {&Parser::Name, &Parser::IdentifierOrCall, PRECEDENCE_CALL}, // IDENTIFIER 76 {NULL, NULL, -1}, // COMMA 77 {NULL, NULL, -1}, // UNCLASSIFIED_COMMENT 78 {NULL, NULL, -1}, // LINE_COMMENT 79 {NULL, NULL, -1}, // SUFFIX_COMMENT 80 {&Parser::BlockComment, NULL, -1}, // BLOCK_COMMENT 81 }; 82 83 Parser::Parser(const std::vector<Token>& tokens, Err* err) 84 : err_(err), cur_(0) { 85 for (std::vector<Token>::const_iterator i(tokens.begin()); i != tokens.end(); 86 ++i) { 87 switch(i->type()) { 88 case Token::LINE_COMMENT: 89 line_comment_tokens_.push_back(*i); 90 break; 91 case Token::SUFFIX_COMMENT: 92 suffix_comment_tokens_.push_back(*i); 93 break; 94 default: 95 // Note that BLOCK_COMMENTs (top-level standalone comments) are passed 96 // through the real parser. 97 tokens_.push_back(*i); 98 break; 99 } 100 } 101 } 102 103 Parser::~Parser() { 104 } 105 106 // static 107 scoped_ptr<ParseNode> Parser::Parse(const std::vector<Token>& tokens, 108 Err* err) { 109 Parser p(tokens, err); 110 return p.ParseFile().PassAs<ParseNode>(); 111 } 112 113 // static 114 scoped_ptr<ParseNode> Parser::ParseExpression(const std::vector<Token>& tokens, 115 Err* err) { 116 Parser p(tokens, err); 117 return p.ParseExpression().Pass(); 118 } 119 120 bool Parser::IsAssignment(const ParseNode* node) const { 121 return node && node->AsBinaryOp() && 122 (node->AsBinaryOp()->op().type() == Token::EQUAL || 123 node->AsBinaryOp()->op().type() == Token::PLUS_EQUALS || 124 node->AsBinaryOp()->op().type() == Token::MINUS_EQUALS); 125 } 126 127 bool Parser::IsStatementBreak(Token::Type token_type) const { 128 switch (token_type) { 129 case Token::IDENTIFIER: 130 case Token::LEFT_BRACE: 131 case Token::RIGHT_BRACE: 132 case Token::IF: 133 case Token::ELSE: 134 return true; 135 default: 136 return false; 137 } 138 } 139 140 bool Parser::LookAhead(Token::Type type) { 141 if (at_end()) 142 return false; 143 return cur_token().type() == type; 144 } 145 146 bool Parser::Match(Token::Type type) { 147 if (!LookAhead(type)) 148 return false; 149 Consume(); 150 return true; 151 } 152 153 Token Parser::Consume(Token::Type type, const char* error_message) { 154 Token::Type types[1] = { type }; 155 return Consume(types, 1, error_message); 156 } 157 158 Token Parser::Consume(Token::Type* types, 159 size_t num_types, 160 const char* error_message) { 161 if (has_error()) { 162 // Don't overwrite current error, but make progress through tokens so that 163 // a loop that's expecting a particular token will still terminate. 164 cur_++; 165 return Token(Location(), Token::INVALID, base::StringPiece()); 166 } 167 if (at_end()) { 168 const char kEOFMsg[] = "I hit EOF instead."; 169 if (tokens_.empty()) 170 *err_ = Err(Location(), error_message, kEOFMsg); 171 else 172 *err_ = Err(tokens_[tokens_.size() - 1], error_message, kEOFMsg); 173 return Token(Location(), Token::INVALID, base::StringPiece()); 174 } 175 176 for (size_t i = 0; i < num_types; ++i) { 177 if (cur_token().type() == types[i]) 178 return tokens_[cur_++]; 179 } 180 *err_ = Err(cur_token(), error_message); 181 return Token(Location(), Token::INVALID, base::StringPiece()); 182 } 183 184 Token Parser::Consume() { 185 return tokens_[cur_++]; 186 } 187 188 scoped_ptr<ParseNode> Parser::ParseExpression() { 189 return ParseExpression(0); 190 } 191 192 scoped_ptr<ParseNode> Parser::ParseExpression(int precedence) { 193 if (at_end()) 194 return scoped_ptr<ParseNode>(); 195 196 Token token = Consume(); 197 PrefixFunc prefix = expressions_[token.type()].prefix; 198 199 if (prefix == NULL) { 200 *err_ = Err(token, 201 std::string("Unexpected token '") + token.value().as_string() + 202 std::string("'")); 203 return scoped_ptr<ParseNode>(); 204 } 205 206 scoped_ptr<ParseNode> left = (this->*prefix)(token); 207 if (has_error()) 208 return left.Pass(); 209 210 while (!at_end() && !IsStatementBreak(cur_token().type()) && 211 precedence <= expressions_[cur_token().type()].precedence) { 212 token = Consume(); 213 InfixFunc infix = expressions_[token.type()].infix; 214 if (infix == NULL) { 215 *err_ = Err(token, 216 std::string("Unexpected token '") + 217 token.value().as_string() + std::string("'")); 218 return scoped_ptr<ParseNode>(); 219 } 220 left = (this->*infix)(left.Pass(), token); 221 if (has_error()) 222 return scoped_ptr<ParseNode>(); 223 } 224 225 return left.Pass(); 226 } 227 228 scoped_ptr<ParseNode> Parser::Literal(Token token) { 229 return scoped_ptr<ParseNode>(new LiteralNode(token)).Pass(); 230 } 231 232 scoped_ptr<ParseNode> Parser::Name(Token token) { 233 return IdentifierOrCall(scoped_ptr<ParseNode>(), token).Pass(); 234 } 235 236 scoped_ptr<ParseNode> Parser::BlockComment(Token token) { 237 scoped_ptr<BlockCommentNode> comment(new BlockCommentNode()); 238 comment->set_comment(token); 239 return comment.PassAs<ParseNode>(); 240 } 241 242 scoped_ptr<ParseNode> Parser::Group(Token token) { 243 scoped_ptr<ParseNode> expr = ParseExpression(); 244 if (has_error()) 245 return scoped_ptr<ParseNode>(); 246 Consume(Token::RIGHT_PAREN, "Expected ')'"); 247 return expr.Pass(); 248 } 249 250 scoped_ptr<ParseNode> Parser::Not(Token token) { 251 scoped_ptr<ParseNode> expr = ParseExpression(PRECEDENCE_PREFIX + 1); 252 if (has_error()) 253 return scoped_ptr<ParseNode>(); 254 scoped_ptr<UnaryOpNode> unary_op(new UnaryOpNode); 255 unary_op->set_op(token); 256 unary_op->set_operand(expr.Pass()); 257 return unary_op.PassAs<ParseNode>(); 258 } 259 260 scoped_ptr<ParseNode> Parser::List(Token node) { 261 scoped_ptr<ParseNode> list(ParseList(node, Token::RIGHT_BRACKET, true)); 262 if (!has_error() && !at_end()) 263 Consume(Token::RIGHT_BRACKET, "Expected ']'"); 264 return list.Pass(); 265 } 266 267 scoped_ptr<ParseNode> Parser::BinaryOperator(scoped_ptr<ParseNode> left, 268 Token token) { 269 scoped_ptr<ParseNode> right = 270 ParseExpression(expressions_[token.type()].precedence + 1); 271 if (!right) { 272 *err_ = 273 Err(token, 274 "Expected right hand side for '" + token.value().as_string() + "'"); 275 return scoped_ptr<ParseNode>(); 276 } 277 scoped_ptr<BinaryOpNode> binary_op(new BinaryOpNode); 278 binary_op->set_op(token); 279 binary_op->set_left(left.Pass()); 280 binary_op->set_right(right.Pass()); 281 return binary_op.PassAs<ParseNode>(); 282 } 283 284 scoped_ptr<ParseNode> Parser::IdentifierOrCall(scoped_ptr<ParseNode> left, 285 Token token) { 286 scoped_ptr<ListNode> list(new ListNode); 287 list->set_begin_token(token); 288 list->set_end_token(token); 289 scoped_ptr<BlockNode> block; 290 bool has_arg = false; 291 if (LookAhead(Token::LEFT_PAREN)) { 292 Token start_token = Consume(); 293 // Parsing a function call. 294 has_arg = true; 295 if (Match(Token::RIGHT_PAREN)) { 296 // Nothing, just an empty call. 297 } else { 298 list = ParseList(start_token, Token::RIGHT_PAREN, false); 299 if (has_error()) 300 return scoped_ptr<ParseNode>(); 301 Consume(Token::RIGHT_PAREN, "Expected ')' after call"); 302 } 303 // Optionally with a scope. 304 if (LookAhead(Token::LEFT_BRACE)) { 305 block = ParseBlock(); 306 if (has_error()) 307 return scoped_ptr<ParseNode>(); 308 } 309 } 310 311 if (!left && !has_arg) { 312 // Not a function call, just a standalone identifier. 313 return scoped_ptr<ParseNode>(new IdentifierNode(token)).Pass(); 314 } 315 scoped_ptr<FunctionCallNode> func_call(new FunctionCallNode); 316 func_call->set_function(token); 317 func_call->set_args(list.Pass()); 318 if (block) 319 func_call->set_block(block.Pass()); 320 return func_call.PassAs<ParseNode>(); 321 } 322 323 scoped_ptr<ParseNode> Parser::Assignment(scoped_ptr<ParseNode> left, 324 Token token) { 325 if (left->AsIdentifier() == NULL) { 326 *err_ = Err(left.get(), "Left-hand side of assignment must be identifier."); 327 return scoped_ptr<ParseNode>(); 328 } 329 scoped_ptr<ParseNode> value = ParseExpression(PRECEDENCE_ASSIGNMENT); 330 scoped_ptr<BinaryOpNode> assign(new BinaryOpNode); 331 assign->set_op(token); 332 assign->set_left(left.Pass()); 333 assign->set_right(value.Pass()); 334 return assign.PassAs<ParseNode>(); 335 } 336 337 scoped_ptr<ParseNode> Parser::Subscript(scoped_ptr<ParseNode> left, 338 Token token) { 339 // TODO: Maybe support more complex expressions like a[0][0]. This would 340 // require work on the evaluator too. 341 if (left->AsIdentifier() == NULL) { 342 *err_ = Err(left.get(), "May only subscript identifiers.", 343 "The thing on the left hand side of the [] must be an identifier\n" 344 "and not an expression. If you need this, you'll have to assign the\n" 345 "value to a temporary before subscripting. Sorry."); 346 return scoped_ptr<ParseNode>(); 347 } 348 scoped_ptr<ParseNode> value = ParseExpression(); 349 Consume(Token::RIGHT_BRACKET, "Expecting ']' after subscript."); 350 scoped_ptr<AccessorNode> accessor(new AccessorNode); 351 accessor->set_base(left->AsIdentifier()->value()); 352 accessor->set_index(value.Pass()); 353 return accessor.PassAs<ParseNode>(); 354 } 355 356 scoped_ptr<ParseNode> Parser::DotOperator(scoped_ptr<ParseNode> left, 357 Token token) { 358 if (left->AsIdentifier() == NULL) { 359 *err_ = Err(left.get(), "May only use \".\" for identifiers.", 360 "The thing on the left hand side of the dot must be an identifier\n" 361 "and not an expression. If you need this, you'll have to assign the\n" 362 "value to a temporary first. Sorry."); 363 return scoped_ptr<ParseNode>(); 364 } 365 366 scoped_ptr<ParseNode> right = ParseExpression(PRECEDENCE_DOT); 367 if (!right || !right->AsIdentifier()) { 368 *err_ = Err(token, "Expected identifier for right-hand-side of \".\"", 369 "Good: a.cookies\nBad: a.42\nLooks good but still bad: a.cookies()"); 370 return scoped_ptr<ParseNode>(); 371 } 372 373 scoped_ptr<AccessorNode> accessor(new AccessorNode); 374 accessor->set_base(left->AsIdentifier()->value()); 375 accessor->set_member(scoped_ptr<IdentifierNode>( 376 static_cast<IdentifierNode*>(right.release()))); 377 return accessor.PassAs<ParseNode>(); 378 } 379 380 // Does not Consume the start or end token. 381 scoped_ptr<ListNode> Parser::ParseList(Token start_token, 382 Token::Type stop_before, 383 bool allow_trailing_comma) { 384 scoped_ptr<ListNode> list(new ListNode); 385 list->set_begin_token(start_token); 386 bool just_got_comma = false; 387 bool first_time = true; 388 while (!LookAhead(stop_before)) { 389 if (!first_time) { 390 if (!just_got_comma) { 391 // Require commas separate things in lists. 392 *err_ = Err(cur_token(), "Expected comma between items."); 393 return scoped_ptr<ListNode>(); 394 } 395 } 396 first_time = false; 397 398 // Why _OR? We're parsing things that are higher precedence than the , 399 // that separates the items of the list. , should appear lower than 400 // boolean expressions (the lowest of which is OR), but above assignments. 401 list->append_item(ParseExpression(PRECEDENCE_OR)); 402 if (has_error()) 403 return scoped_ptr<ListNode>(); 404 if (at_end()) { 405 *err_ = 406 Err(tokens_[tokens_.size() - 1], "Unexpected end of file in list."); 407 return scoped_ptr<ListNode>(); 408 } 409 if (list->contents().back()->AsBlockComment()) { 410 // If there was a comment inside the list, we don't need a comma to the 411 // next item, so pretend we got one, if we're expecting one. 412 just_got_comma = allow_trailing_comma; 413 } else { 414 just_got_comma = Match(Token::COMMA); 415 } 416 } 417 if (just_got_comma && !allow_trailing_comma) { 418 *err_ = Err(cur_token(), "Trailing comma"); 419 return scoped_ptr<ListNode>(); 420 } 421 list->set_end_token(cur_token()); 422 return list.Pass(); 423 } 424 425 scoped_ptr<ParseNode> Parser::ParseFile() { 426 scoped_ptr<BlockNode> file(new BlockNode(false)); 427 for (;;) { 428 if (at_end()) 429 break; 430 scoped_ptr<ParseNode> statement = ParseStatement(); 431 if (!statement) 432 break; 433 file->append_statement(statement.Pass()); 434 } 435 if (!at_end() && !has_error()) 436 *err_ = Err(cur_token(), "Unexpected here, should be newline."); 437 if (has_error()) 438 return scoped_ptr<ParseNode>(); 439 440 // TODO(scottmg): If this is measurably expensive, it could be done only 441 // when necessary (when reformatting, or during tests). Comments are 442 // separate from the parse tree at this point, so downstream code can remain 443 // ignorant of them. 444 AssignComments(file.get()); 445 446 return file.PassAs<ParseNode>(); 447 } 448 449 scoped_ptr<ParseNode> Parser::ParseStatement() { 450 if (LookAhead(Token::LEFT_BRACE)) { 451 return ParseBlock().PassAs<ParseNode>(); 452 } else if (LookAhead(Token::IF)) { 453 return ParseCondition(); 454 } else if (LookAhead(Token::BLOCK_COMMENT)) { 455 return BlockComment(Consume()); 456 } else { 457 // TODO(scottmg): Is this too strict? Just drop all the testing if we want 458 // to allow "pointless" expressions and return ParseExpression() directly. 459 scoped_ptr<ParseNode> stmt = ParseExpression(); 460 if (stmt) { 461 if (stmt->AsFunctionCall() || IsAssignment(stmt.get())) 462 return stmt.Pass(); 463 } 464 if (!has_error()) { 465 Token token = at_end() ? tokens_[tokens_.size() - 1] : cur_token(); 466 *err_ = Err(token, "Expecting assignment or function call."); 467 } 468 return scoped_ptr<ParseNode>(); 469 } 470 } 471 472 scoped_ptr<BlockNode> Parser::ParseBlock() { 473 Token begin_token = 474 Consume(Token::LEFT_BRACE, "Expected '{' to start a block."); 475 if (has_error()) 476 return scoped_ptr<BlockNode>(); 477 scoped_ptr<BlockNode> block(new BlockNode(true)); 478 block->set_begin_token(begin_token); 479 480 for (;;) { 481 if (LookAhead(Token::RIGHT_BRACE)) { 482 block->set_end_token(Consume()); 483 break; 484 } 485 486 scoped_ptr<ParseNode> statement = ParseStatement(); 487 if (!statement) 488 return scoped_ptr<BlockNode>(); 489 block->append_statement(statement.Pass()); 490 } 491 return block.Pass(); 492 } 493 494 scoped_ptr<ParseNode> Parser::ParseCondition() { 495 scoped_ptr<ConditionNode> condition(new ConditionNode); 496 condition->set_if_token(Consume(Token::IF, "Expected 'if'")); 497 Consume(Token::LEFT_PAREN, "Expected '(' after 'if'."); 498 condition->set_condition(ParseExpression()); 499 if (IsAssignment(condition->condition())) 500 *err_ = Err(condition->condition(), "Assignment not allowed in 'if'."); 501 Consume(Token::RIGHT_PAREN, "Expected ')' after condition of 'if'."); 502 condition->set_if_true(ParseBlock().Pass()); 503 if (Match(Token::ELSE)) 504 condition->set_if_false(ParseStatement().Pass()); 505 if (has_error()) 506 return scoped_ptr<ParseNode>(); 507 return condition.PassAs<ParseNode>(); 508 } 509 510 void Parser::TraverseOrder(const ParseNode* root, 511 std::vector<const ParseNode*>* pre, 512 std::vector<const ParseNode*>* post) { 513 if (root) { 514 pre->push_back(root); 515 516 if (const AccessorNode* accessor = root->AsAccessor()) { 517 TraverseOrder(accessor->index(), pre, post); 518 TraverseOrder(accessor->member(), pre, post); 519 } else if (const BinaryOpNode* binop = root->AsBinaryOp()) { 520 TraverseOrder(binop->left(), pre, post); 521 TraverseOrder(binop->right(), pre, post); 522 } else if (const BlockNode* block = root->AsBlock()) { 523 const std::vector<ParseNode*>& statements = block->statements(); 524 for (std::vector<ParseNode*>::const_iterator i(statements.begin()); 525 i != statements.end(); 526 ++i) { 527 TraverseOrder(*i, pre, post); 528 } 529 } else if (const ConditionNode* condition = root->AsConditionNode()) { 530 TraverseOrder(condition->condition(), pre, post); 531 TraverseOrder(condition->if_true(), pre, post); 532 TraverseOrder(condition->if_false(), pre, post); 533 } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) { 534 TraverseOrder(func_call->args(), pre, post); 535 TraverseOrder(func_call->block(), pre, post); 536 } else if (root->AsIdentifier()) { 537 // Nothing. 538 } else if (const ListNode* list = root->AsList()) { 539 const std::vector<const ParseNode*>& contents = list->contents(); 540 for (std::vector<const ParseNode*>::const_iterator i(contents.begin()); 541 i != contents.end(); 542 ++i) { 543 TraverseOrder(*i, pre, post); 544 } 545 } else if (root->AsLiteral()) { 546 // Nothing. 547 } else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) { 548 TraverseOrder(unaryop->operand(), pre, post); 549 } else if (root->AsBlockComment()) { 550 // Nothing. 551 } else { 552 CHECK(false) << "Unhandled case in TraverseOrder."; 553 } 554 555 post->push_back(root); 556 } 557 } 558 559 void Parser::AssignComments(ParseNode* file) { 560 // Start by generating a pre- and post- order traversal of the tree so we 561 // can determine what's before and after comments. 562 std::vector<const ParseNode*> pre; 563 std::vector<const ParseNode*> post; 564 TraverseOrder(file, &pre, &post); 565 566 // Assign line comments to syntax immediately following. 567 int cur_comment = 0; 568 for (std::vector<const ParseNode*>::const_iterator i = pre.begin(); 569 i != pre.end(); 570 ++i) { 571 const Location& start = (*i)->GetRange().begin(); 572 while (cur_comment < static_cast<int>(line_comment_tokens_.size())) { 573 if (start.byte() >= line_comment_tokens_[cur_comment].location().byte()) { 574 const_cast<ParseNode*>(*i)->comments_mutable()->append_before( 575 line_comment_tokens_[cur_comment]); 576 ++cur_comment; 577 } else { 578 break; 579 } 580 } 581 } 582 583 // Remaining line comments go at end of file. 584 for (; cur_comment < static_cast<int>(line_comment_tokens_.size()); 585 ++cur_comment) 586 file->comments_mutable()->append_after(line_comment_tokens_[cur_comment]); 587 588 // Assign suffix to syntax immediately before. 589 cur_comment = static_cast<int>(suffix_comment_tokens_.size() - 1); 590 for (std::vector<const ParseNode*>::const_reverse_iterator i = post.rbegin(); 591 i != post.rend(); 592 ++i) { 593 // Don't assign suffix comments to the function call or list, but instead 594 // to the last thing inside. 595 if ((*i)->AsFunctionCall() || (*i)->AsList()) 596 continue; 597 598 const Location& start = (*i)->GetRange().begin(); 599 const Location& end = (*i)->GetRange().end(); 600 601 // Don't assign suffix comments to something that starts on an earlier 602 // line, so that in: 603 // 604 // sources = [ "a", 605 // "b" ] # comment 606 // 607 // it's attached to "b", not sources = [ ... ]. 608 if (start.line_number() != end.line_number()) 609 continue; 610 611 while (cur_comment >= 0) { 612 if (end.byte() <= suffix_comment_tokens_[cur_comment].location().byte()) { 613 const_cast<ParseNode*>(*i)->comments_mutable()->append_suffix( 614 suffix_comment_tokens_[cur_comment]); 615 --cur_comment; 616 } else { 617 break; 618 } 619 } 620 621 // Suffix comments were assigned in reverse, so if there were multiple on 622 // the same node, they need to be reversed. 623 const_cast<ParseNode*>(*i)->comments_mutable()->ReverseSuffix(); 624 } 625 } 626