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
21enum 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.
46ParserHelper 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
83Parser::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
103Parser::~Parser() {
104}
105
106// static
107scoped_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
114scoped_ptr<ParseNode> Parser::ParseExpression(const std::vector<Token>& tokens,
115                                              Err* err) {
116  Parser p(tokens, err);
117  return p.ParseExpression().Pass();
118}
119
120bool 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
127bool 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
140bool Parser::LookAhead(Token::Type type) {
141  if (at_end())
142    return false;
143  return cur_token().type() == type;
144}
145
146bool Parser::Match(Token::Type type) {
147  if (!LookAhead(type))
148    return false;
149  Consume();
150  return true;
151}
152
153Token Parser::Consume(Token::Type type, const char* error_message) {
154  Token::Type types[1] = { type };
155  return Consume(types, 1, error_message);
156}
157
158Token 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
184Token Parser::Consume() {
185  return tokens_[cur_++];
186}
187
188scoped_ptr<ParseNode> Parser::ParseExpression() {
189  return ParseExpression(0);
190}
191
192scoped_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
228scoped_ptr<ParseNode> Parser::Literal(Token token) {
229  return scoped_ptr<ParseNode>(new LiteralNode(token)).Pass();
230}
231
232scoped_ptr<ParseNode> Parser::Name(Token token) {
233  return IdentifierOrCall(scoped_ptr<ParseNode>(), token).Pass();
234}
235
236scoped_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
242scoped_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
250scoped_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
260scoped_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
267scoped_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
284scoped_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
323scoped_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
337scoped_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
356scoped_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.
381scoped_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
425scoped_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
449scoped_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
472scoped_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
494scoped_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
510void 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
559void 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