| Index: tools/gn/parser.cc
|
| diff --git a/tools/gn/parser.cc b/tools/gn/parser.cc
|
| index 2d5e8fe5595146573b91f6890b60ef09b8902bfd..972f01ca6c26f88117e8fe852a9558977a4b3dac 100644
|
| --- a/tools/gn/parser.cc
|
| +++ b/tools/gn/parser.cc
|
| @@ -12,7 +12,7 @@
|
| // grammar:
|
| //
|
| // file := (statement)*
|
| -// statement := block | if | assignment
|
| +// statement := block | if | assignment | top-level-comment
|
| // block := '{' statement* '}'
|
| // if := 'if' '(' expr ')' statement [ else ]
|
| // else := 'else' (if | statement)*
|
| @@ -74,11 +74,27 @@ ParserHelper Parser::expressions_[] = {
|
| {NULL, NULL, -1}, // ELSE
|
| {&Parser::Name, &Parser::IdentifierOrCall, PRECEDENCE_CALL}, // IDENTIFIER
|
| {NULL, NULL, -1}, // COMMA
|
| - {NULL, NULL, -1}, // COMMENT
|
| + {NULL, NULL, -1}, // UNCLASSIFIED_COMMENT
|
| + {NULL, NULL, -1}, // LINE_COMMENT
|
| + {NULL, NULL, -1}, // SUFFIX_COMMENT
|
| };
|
|
|
| Parser::Parser(const std::vector<Token>& tokens, Err* err)
|
| - : tokens_(tokens), err_(err), cur_(0) {
|
| + : err_(err), cur_(0) {
|
| + for (std::vector<Token>::const_iterator i(tokens.begin()); i != tokens.end();
|
| + ++i) {
|
| + switch(i->type()) {
|
| + case Token::LINE_COMMENT:
|
| + line_comment_tokens_.push_back(*i);
|
| + break;
|
| + case Token::SUFFIX_COMMENT:
|
| + suffix_comment_tokens_.push_back(*i);
|
| + break;
|
| + default:
|
| + tokens_.push_back(*i);
|
| + break;
|
| + }
|
| + }
|
| }
|
|
|
| Parser::~Parser() {
|
| @@ -403,6 +419,13 @@ scoped_ptr<ParseNode> Parser::ParseFile() {
|
| *err_ = Err(cur_token(), "Unexpected here, should be newline.");
|
| if (has_error())
|
| return scoped_ptr<ParseNode>();
|
| +
|
| + // TODO(scottmg): If this is measurably expensive, it could be done only
|
| + // when necessary (when reformatting, or during tests). Comments are
|
| + // separate from the parse tree at this point, so downstream code can remain
|
| + // ignorant of them.
|
| + AssignComments(file.get());
|
| +
|
| return file.PassAs<ParseNode>();
|
| }
|
|
|
| @@ -451,7 +474,7 @@ scoped_ptr<BlockNode> Parser::ParseBlock() {
|
|
|
| scoped_ptr<ParseNode> Parser::ParseCondition() {
|
| scoped_ptr<ConditionNode> condition(new ConditionNode);
|
| - Consume(Token::IF, "Expected 'if'");
|
| + condition->set_if_token(Consume(Token::IF, "Expected 'if'"));
|
| Consume(Token::LEFT_PAREN, "Expected '(' after 'if'.");
|
| condition->set_condition(ParseExpression());
|
| if (IsAssignment(condition->condition()))
|
| @@ -464,3 +487,96 @@ scoped_ptr<ParseNode> Parser::ParseCondition() {
|
| return scoped_ptr<ParseNode>();
|
| return condition.PassAs<ParseNode>();
|
| }
|
| +
|
| +void Parser::TraverseOrder(const ParseNode* root,
|
| + std::vector<const ParseNode*>* pre,
|
| + std::vector<const ParseNode*>* post) {
|
| + if (root) {
|
| + pre->push_back(root);
|
| +
|
| + if (const AccessorNode* accessor = root->AsAccessor()) {
|
| + TraverseOrder(accessor->index(), pre, post);
|
| + TraverseOrder(accessor->member(), pre, post);
|
| + } else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
|
| + TraverseOrder(binop->left(), pre, post);
|
| + TraverseOrder(binop->right(), pre, post);
|
| + } else if (const BlockNode* block = root->AsBlock()) {
|
| + const std::vector<ParseNode*>& statements = block->statements();
|
| + for (std::vector<ParseNode*>::const_iterator i(statements.begin());
|
| + i != statements.end();
|
| + ++i) {
|
| + TraverseOrder(*i, pre, post);
|
| + }
|
| + } else if (const ConditionNode* condition = root->AsConditionNode()) {
|
| + TraverseOrder(condition->condition(), pre, post);
|
| + TraverseOrder(condition->if_true(), pre, post);
|
| + TraverseOrder(condition->if_false(), pre, post);
|
| + } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) {
|
| + TraverseOrder(func_call->args(), pre, post);
|
| + TraverseOrder(func_call->block(), pre, post);
|
| + } else if (root->AsIdentifier()) {
|
| + // Nothing.
|
| + } else if (const ListNode* list = root->AsList()) {
|
| + const std::vector<const ParseNode*>& contents = list->contents();
|
| + for (std::vector<const ParseNode*>::const_iterator i(contents.begin());
|
| + i != contents.end();
|
| + ++i) {
|
| + TraverseOrder(*i, pre, post);
|
| + }
|
| + } else if (root->AsLiteral()) {
|
| + // Nothing.
|
| + } else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) {
|
| + TraverseOrder(unaryop->operand(), pre, post);
|
| + } else {
|
| + CHECK(false) << "Unhandled case in TraverseOrder.";
|
| + }
|
| +
|
| + post->push_back(root);
|
| + }
|
| +}
|
| +
|
| +void Parser::AssignComments(ParseNode* file) {
|
| + // Start by generating a pre- and post- order traversal of the tree so we
|
| + // can determine what's before and after comments.
|
| + std::vector<const ParseNode*> pre;
|
| + std::vector<const ParseNode*> post;
|
| + TraverseOrder(file, &pre, &post);
|
| +
|
| + size_t j = 0;
|
| + // Assign line comments to syntax immediately following.
|
| + for (std::vector<const ParseNode*>::const_iterator i = pre.begin();
|
| + i != pre.end();
|
| + ++i) {
|
| + /*
|
| + std::ostringstream os;
|
| + (*i)->Print(os, 0);
|
| + size_t end_of_first_line = os.str().find_first_of('\n');
|
| + std::string name;
|
| + if (end_of_first_line != std::string::npos)
|
| + name = os.str().substr(0, end_of_first_line);
|
| + else
|
| + name = os.str();
|
| + printf("'%s': (%d, %d)\n",
|
| + name.c_str(),
|
| + (*i)->GetRange().begin().byte(),
|
| + (*i)->GetRange().end().byte());
|
| + */
|
| + const Location& start = (*i)->GetRange().begin();
|
| + while (j < line_comment_tokens_.size()) {
|
| + if (start.byte() >= line_comment_tokens_[j].location().byte()) {
|
| + const_cast<ParseNode*>((*i))->comments_mutable()->append_before(
|
| + line_comment_tokens_[j]);
|
| + ++j;
|
| + } else {
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Remaining line comments go at end of file.
|
| + for (; j < line_comment_tokens_.size(); ++j)
|
| + file->comments_mutable()->append_after(line_comment_tokens_[j]);
|
| +
|
| + // Assign suffix to syntax immediately before.
|
| + // TODO(scottmg).
|
| +}
|
|
|