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 "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 Loading... |
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 Loading... |
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 Loading... |
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 cur_comment = 0; |
| 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 (cur_comment < static_cast<int>(line_comment_tokens_.size())) { |
| 552 if (start.byte() >= line_comment_tokens_[cur_comment].location().byte()) { |
| 553 const_cast<ParseNode*>((*i))->comments_mutable()->append_before( |
| 554 line_comment_tokens_[cur_comment]); |
| 555 ++cur_comment; |
| 556 } else { |
| 557 break; |
| 558 } |
| 559 } |
| 560 } |
| 561 |
| 562 // Remaining line comments go at end of file. |
| 563 for (; cur_comment < static_cast<int>(line_comment_tokens_.size()); |
| 564 ++cur_comment) |
| 565 file->comments_mutable()->append_after(line_comment_tokens_[cur_comment]); |
| 566 |
| 567 // Assign suffix to syntax immediately before. |
| 568 cur_comment = static_cast<int>(suffix_comment_tokens_.size() - 1); |
| 569 for (std::vector<const ParseNode*>::const_reverse_iterator i = post.rbegin(); |
| 570 i != post.rend(); |
| 571 ++i) { |
| 572 // Don't assign suffix comments to the function call or list, but instead |
| 573 // to the last thing inside. |
| 574 if ((*i)->AsFunctionCall() || (*i)->AsList()) |
| 575 continue; |
| 576 |
| 577 const Location& start = (*i)->GetRange().begin(); |
| 578 const Location& end = (*i)->GetRange().end(); |
| 579 |
| 580 // Don't assign suffix comments to something that starts on an earlier |
| 581 // line, so that in: |
| 582 // |
| 583 // sources = [ "a", |
| 584 // "b" ] # comment |
| 585 // |
| 586 // it's attached to "b", not sources = [ ... ]. |
| 587 if (start.line_number() != end.line_number()) |
| 588 continue; |
| 589 |
| 590 while (cur_comment >= 0) { |
| 591 if (end.byte() <= suffix_comment_tokens_[cur_comment].location().byte()) { |
| 592 const_cast<ParseNode*>((*i))->comments_mutable()->append_suffix( |
| 593 suffix_comment_tokens_[cur_comment]); |
| 594 --cur_comment; |
| 595 } else { |
| 596 break; |
| 597 } |
| 598 } |
| 599 } |
| 600 } |
OLD | NEW |