Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(44)

Side by Side Diff: tools/gn/parser.cc

Issue 588893006: gn: attach comments to parse tree (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: suffix comments too Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 "base/logging.h" 7 #include "base/logging.h"
8 #include "tools/gn/functions.h" 8 #include "tools/gn/functions.h"
9 #include "tools/gn/operators.h" 9 #include "tools/gn/operators.h"
10 #include "tools/gn/token.h" 10 #include "tools/gn/token.h"
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
67 {&Parser::Group, NULL, -1}, // LEFT_PAREN 67 {&Parser::Group, NULL, -1}, // LEFT_PAREN
68 {NULL, NULL, -1}, // RIGHT_PAREN 68 {NULL, NULL, -1}, // RIGHT_PAREN
69 {&Parser::List, &Parser::Subscript, PRECEDENCE_CALL}, // LEFT_BRACKET 69 {&Parser::List, &Parser::Subscript, PRECEDENCE_CALL}, // LEFT_BRACKET
70 {NULL, NULL, -1}, // RIGHT_BRACKET 70 {NULL, NULL, -1}, // RIGHT_BRACKET
71 {NULL, NULL, -1}, // LEFT_BRACE 71 {NULL, NULL, -1}, // LEFT_BRACE
72 {NULL, NULL, -1}, // RIGHT_BRACE 72 {NULL, NULL, -1}, // RIGHT_BRACE
73 {NULL, NULL, -1}, // IF 73 {NULL, NULL, -1}, // IF
74 {NULL, NULL, -1}, // ELSE 74 {NULL, NULL, -1}, // ELSE
75 {&Parser::Name, &Parser::IdentifierOrCall, PRECEDENCE_CALL}, // IDENTIFIER 75 {&Parser::Name, &Parser::IdentifierOrCall, PRECEDENCE_CALL}, // IDENTIFIER
76 {NULL, NULL, -1}, // COMMA 76 {NULL, NULL, -1}, // COMMA
77 {NULL, NULL, -1}, // COMMENT 77 {NULL, NULL, -1}, // UNCLASSIFIED_COMMENT
78 {NULL, NULL, -1}, // LINE_COMMENT
79 {NULL, NULL, -1}, // SUFFIX_COMMENT
78 }; 80 };
79 81
80 Parser::Parser(const std::vector<Token>& tokens, Err* err) 82 Parser::Parser(const std::vector<Token>& tokens, Err* err)
81 : tokens_(tokens), err_(err), cur_(0) { 83 : err_(err), cur_(0) {
84 for (std::vector<Token>::const_iterator i(tokens.begin()); i != tokens.end();
85 ++i) {
86 switch(i->type()) {
87 case Token::LINE_COMMENT:
88 line_comment_tokens_.push_back(*i);
89 break;
90 case Token::SUFFIX_COMMENT:
91 suffix_comment_tokens_.push_back(*i);
92 break;
93 default:
94 tokens_.push_back(*i);
95 break;
96 }
97 }
82 } 98 }
83 99
84 Parser::~Parser() { 100 Parser::~Parser() {
85 } 101 }
86 102
87 // static 103 // static
88 scoped_ptr<ParseNode> Parser::Parse(const std::vector<Token>& tokens, 104 scoped_ptr<ParseNode> Parser::Parse(const std::vector<Token>& tokens,
89 Err* err) { 105 Err* err) {
90 Parser p(tokens, err); 106 Parser p(tokens, err);
91 return p.ParseFile().PassAs<ParseNode>(); 107 return p.ParseFile().PassAs<ParseNode>();
(...skipping 304 matching lines...) Expand 10 before | Expand all | Expand 10 after
396 break; 412 break;
397 scoped_ptr<ParseNode> statement = ParseStatement(); 413 scoped_ptr<ParseNode> statement = ParseStatement();
398 if (!statement) 414 if (!statement)
399 break; 415 break;
400 file->append_statement(statement.Pass()); 416 file->append_statement(statement.Pass());
401 } 417 }
402 if (!at_end() && !has_error()) 418 if (!at_end() && !has_error())
403 *err_ = Err(cur_token(), "Unexpected here, should be newline."); 419 *err_ = Err(cur_token(), "Unexpected here, should be newline.");
404 if (has_error()) 420 if (has_error())
405 return scoped_ptr<ParseNode>(); 421 return scoped_ptr<ParseNode>();
422
423 // TODO(scottmg): If this is measurably expensive, it could be done only
424 // when necessary (when reformatting, or during tests). Comments are
425 // separate from the parse tree at this point, so downstream code can remain
426 // ignorant of them.
427 AssignComments(file.get());
428
406 return file.PassAs<ParseNode>(); 429 return file.PassAs<ParseNode>();
407 } 430 }
408 431
409 scoped_ptr<ParseNode> Parser::ParseStatement() { 432 scoped_ptr<ParseNode> Parser::ParseStatement() {
410 if (LookAhead(Token::LEFT_BRACE)) { 433 if (LookAhead(Token::LEFT_BRACE)) {
411 return ParseBlock().PassAs<ParseNode>(); 434 return ParseBlock().PassAs<ParseNode>();
412 } else if (LookAhead(Token::IF)) { 435 } else if (LookAhead(Token::IF)) {
413 return ParseCondition(); 436 return ParseCondition();
414 } else { 437 } else {
415 // TODO(scottmg): Is this too strict? Just drop all the testing if we want 438 // TODO(scottmg): Is this too strict? Just drop all the testing if we want
(...skipping 28 matching lines...) Expand all
444 scoped_ptr<ParseNode> statement = ParseStatement(); 467 scoped_ptr<ParseNode> statement = ParseStatement();
445 if (!statement) 468 if (!statement)
446 return scoped_ptr<BlockNode>(); 469 return scoped_ptr<BlockNode>();
447 block->append_statement(statement.Pass()); 470 block->append_statement(statement.Pass());
448 } 471 }
449 return block.Pass(); 472 return block.Pass();
450 } 473 }
451 474
452 scoped_ptr<ParseNode> Parser::ParseCondition() { 475 scoped_ptr<ParseNode> Parser::ParseCondition() {
453 scoped_ptr<ConditionNode> condition(new ConditionNode); 476 scoped_ptr<ConditionNode> condition(new ConditionNode);
454 Consume(Token::IF, "Expected 'if'"); 477 condition->set_if_token(Consume(Token::IF, "Expected 'if'"));
455 Consume(Token::LEFT_PAREN, "Expected '(' after 'if'."); 478 Consume(Token::LEFT_PAREN, "Expected '(' after 'if'.");
456 condition->set_condition(ParseExpression()); 479 condition->set_condition(ParseExpression());
457 if (IsAssignment(condition->condition())) 480 if (IsAssignment(condition->condition()))
458 *err_ = Err(condition->condition(), "Assignment not allowed in 'if'."); 481 *err_ = Err(condition->condition(), "Assignment not allowed in 'if'.");
459 Consume(Token::RIGHT_PAREN, "Expected ')' after condition of 'if'."); 482 Consume(Token::RIGHT_PAREN, "Expected ')' after condition of 'if'.");
460 condition->set_if_true(ParseBlock().Pass()); 483 condition->set_if_true(ParseBlock().Pass());
461 if (Match(Token::ELSE)) 484 if (Match(Token::ELSE))
462 condition->set_if_false(ParseStatement().Pass()); 485 condition->set_if_false(ParseStatement().Pass());
463 if (has_error()) 486 if (has_error())
464 return scoped_ptr<ParseNode>(); 487 return scoped_ptr<ParseNode>();
465 return condition.PassAs<ParseNode>(); 488 return condition.PassAs<ParseNode>();
466 } 489 }
490
491 void Parser::TraverseOrder(const ParseNode* root,
492 std::vector<const ParseNode*>* pre,
493 std::vector<const ParseNode*>* post) {
494 if (root) {
495 pre->push_back(root);
496
497 if (const AccessorNode* accessor = root->AsAccessor()) {
498 TraverseOrder(accessor->index(), pre, post);
499 TraverseOrder(accessor->member(), pre, post);
500 } else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
501 TraverseOrder(binop->left(), pre, post);
502 TraverseOrder(binop->right(), pre, post);
503 } else if (const BlockNode* block = root->AsBlock()) {
504 const std::vector<ParseNode*>& statements = block->statements();
505 for (std::vector<ParseNode*>::const_iterator i(statements.begin());
506 i != statements.end();
507 ++i) {
508 TraverseOrder(*i, pre, post);
509 }
510 } else if (const ConditionNode* condition = root->AsConditionNode()) {
511 TraverseOrder(condition->condition(), pre, post);
512 TraverseOrder(condition->if_true(), pre, post);
513 TraverseOrder(condition->if_false(), pre, post);
514 } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) {
515 TraverseOrder(func_call->args(), pre, post);
516 TraverseOrder(func_call->block(), pre, post);
517 } else if (root->AsIdentifier()) {
518 // Nothing.
519 } else if (const ListNode* list = root->AsList()) {
520 const std::vector<const ParseNode*>& contents = list->contents();
521 for (std::vector<const ParseNode*>::const_iterator i(contents.begin());
522 i != contents.end();
523 ++i) {
524 TraverseOrder(*i, pre, post);
525 }
526 } else if (root->AsLiteral()) {
527 // Nothing.
528 } else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) {
529 TraverseOrder(unaryop->operand(), pre, post);
530 } else {
531 CHECK(false) << "Unhandled case in TraverseOrder.";
532 }
533
534 post->push_back(root);
535 }
536 }
537
538 void Parser::AssignComments(ParseNode* file) {
539 // Start by generating a pre- and post- order traversal of the tree so we
540 // can determine what's before and after comments.
541 std::vector<const ParseNode*> pre;
542 std::vector<const ParseNode*> post;
543 TraverseOrder(file, &pre, &post);
544
545 // Assign line comments to syntax immediately following.
546 int j = 0;
brettw 2014/09/23 21:33:15 Can you give this a better name?
scottmg 2014/09/23 22:15:37 Done.
547 for (std::vector<const ParseNode*>::const_iterator i = pre.begin();
548 i != pre.end();
549 ++i) {
550 const Location& start = (*i)->GetRange().begin();
551 while (j < static_cast<int>(line_comment_tokens_.size())) {
552 if (start.byte() >= line_comment_tokens_[j].location().byte()) {
553 const_cast<ParseNode*>((*i))->comments_mutable()->append_before(
554 line_comment_tokens_[j]);
555 ++j;
556 } else {
557 break;
558 }
559 }
560 }
561
562 // Remaining line comments go at end of file.
563 for (; j < static_cast<int>(line_comment_tokens_.size()); ++j)
564 file->comments_mutable()->append_after(line_comment_tokens_[j]);
565
566 // Assign suffix to syntax immediately before.
567 j = suffix_comment_tokens_.size() - 1;
568 for (std::vector<const ParseNode*>::const_reverse_iterator i = post.rbegin();
569 i != post.rend();
570 ++i) {
571 // Don't assign suffix comments to the function call or list, but instead
572 // to the last thing inside.
573 if ((*i)->AsFunctionCall() || (*i)->AsList())
574 continue;
575
576 const Location& start = (*i)->GetRange().begin();
577 const Location& end = (*i)->GetRange().end();
578
579 // Don't assign suffic comments to something that starts on an earlier
580 // line, so that in:
581 //
582 // sources = [ "a",
583 // "b" ] # comment
584 //
585 // it's attached to "b", not sources = [ ... ].
586 if (start.line_number() != end.line_number())
587 continue;
588
589 while (j >= 0) {
590 if (end.byte() <= suffix_comment_tokens_[j].location().byte()) {
591 const_cast<ParseNode*>((*i))->comments_mutable()->append_suffix(
592 suffix_comment_tokens_[j]);
593 --j;
594 } else {
595 break;
596 }
597 }
598 }
599 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698