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

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

Issue 1015063003: tools/gn: add "gn help grammar" with formal grammar (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 8 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
« no previous file with comments | « tools/gn/parser.h ('k') | tools/gn/parser_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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"
11 11
12 // grammar: 12 const char kSyntax_Help[] =
13 // 13 "GN build language syntax\n"
14 // file := (statement)* 14 "\n"
15 // statement := block | if | assignment 15 " This help topic defines the syntax of the GN build language.\n"
16 // block := '{' statement* '}' 16 "\n"
17 // if := 'if' '(' expr ')' statement [ else ] 17 "Tokens\n"
18 // else := 'else' (if | statement)* 18 "\n"
19 // assignment := ident {'=' | '+=' | '-='} expr 19 " GN build files are read as sequences of tokens. While splitting the\n"
20 " file into tokens, the next token is the longest sequence of characters\n"
21 " that form a valid token.\n"
22 "\n"
23 "White space and comments\n"
24 "\n"
25 " White space is comprised of spaces (U+0020), horizontal tabs (U+0009),\n"
26 " carriage returns (U+000D), and newlines (U+000A).\n"
27 "\n"
28 " Comments start at the character \"#\" and stop at the end of the line.\n"
Dirk Pranke 2015/03/26 01:29:47 If you wanted to be picky, how is the "end of the
29 "\n"
30 " White space and comments are ignored except that they may separate\n"
31 " tokens that would otherwise combine into a single token.\n"
32 "\n"
33 "Identifiers\n"
34 "\n"
35 " Identifiers name variables and functions.\n"
36 "\n"
37 " identifier = letter { letter | digit } .\n"
38 " letter = \"A\" ... \"Z\" | \"a\" ... \"z\" | \"_\" .\n"
39 " digit = \"0\" ... \"9\" .\n"
40 "\n"
41 "Keywords\n"
42 "\n"
43 " The following keywords are reserved and may not be used as\n"
44 " identifiers:\n"
45 "\n"
46 " else false if true\n"
47 "\n"
48 "Integer literals\n"
49 "\n"
50 " An integer literal represents a decimal integer value.\n"
51 "\n"
52 " integer = [ \"-\" ] digit { digit } .\n"
53 "\n"
54 "String literals\n"
55 "\n"
56 " A string literal represents a string value consisting of the quoted\n"
57 " characters with possible escape sequences and variable expansions.\n"
58 "\n"
59 " string = `\"` { char | escape | expansion } `\"` .\n"
60 " escape = `\\` ( \"$\" | `\"` | char ) .\n"
61 " expansion = \"$\" ( identifier | \"{\" identifier \"}\" ) .\n"
62 " char = /* any character except \"$\", `\"`, or newline */ .\n"
63 "\n"
64 " After a backslash, certain sequences represent special characters:\n"
65 "\n"
66 " \\\" U+0022 quotation mark\n"
67 " \\$ U+0024 dollar sign\n"
68 " \\\\ U+005c backslash\n"
69 "\n"
70 " All other backslashes represent themselves.\n"
71 "\n"
72 "Punctuation\n"
73 "\n"
74 " The following character sequences represent punctuation:\n"
75 "\n"
76 " + += == != ( )\n"
77 " - -= < <= [ ]\n"
78 " ! = > >= { }\n"
79 " && || . ,\n"
80 "\n"
81 "Grammar\n"
82 "\n"
83 " The input tokens form a syntax tree following a context-free grammar:\n"
84 "\n"
85 " File = StatementList .\n"
86 "\n"
87 " Statement = Assignment | Call | Condition .\n"
88 " Assignment = identifier AssignOp Expr .\n"
89 " Call = identifier \"(\" ExprList \")\" [ Block ] .\n"
90 " Condition = \"if\" \"(\" Expr \")\" Block\n"
91 " [ \"else\" ( Condition | Block ) ] .\n"
92 " Block = \"{\" StatementList \"}\" .\n"
93 " StatementList = { Statement } .\n"
94 "\n"
95 " Expr = UnaryExpr | Expr BinaryOp Expr .\n"
96 " UnaryExpr = PrimaryExpr | UnaryOp UnaryExpr .\n"
97 " PrimaryExpr = identifier | integer | string | Call\n"
98 " | identifier \"[\" Expr \"]\"\n"
99 " | identifier \".\" identifier\n"
100 " | \"(\" Expr \")\" | \"[\" ExprList \"] .\n"
101 " ExprList = [ Expr { \",\" Expr } [ \",\" ] ] .\n"
102 "\n"
103 " AssignOp = \"=\" | \"+=\" | \"-=\" .\n"
104 " UnaryOp = \"!\" .\n"
105 " BinaryOp = \"+\" | \"-\" // highest priority\n"
106 " | \"<\" | \"<=\" | \">\" | \">=\"\n"
107 " | \"==\" | \"!=\"\n"
108 " | \"&&\"\n"
109 " | \"||\" . // lowest priority\n"
110 "\n"
111 " All binary operators are left-associative.\n";
20 112
21 enum Precedence { 113 enum Precedence {
22 PRECEDENCE_ASSIGNMENT = 1, // Lowest precedence. 114 PRECEDENCE_ASSIGNMENT = 1, // Lowest precedence.
23 PRECEDENCE_OR = 2, 115 PRECEDENCE_OR = 2,
24 PRECEDENCE_AND = 3, 116 PRECEDENCE_AND = 3,
25 PRECEDENCE_EQUALITY = 4, 117 PRECEDENCE_EQUALITY = 4,
26 PRECEDENCE_RELATION = 5, 118 PRECEDENCE_RELATION = 5,
27 PRECEDENCE_SUM = 6, 119 PRECEDENCE_SUM = 6,
28 PRECEDENCE_PREFIX = 7, 120 PRECEDENCE_PREFIX = 7,
29 PRECEDENCE_CALL = 8, 121 PRECEDENCE_CALL = 8,
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after
250 scoped_ptr<ParseNode> expr = ParseExpression(PRECEDENCE_PREFIX + 1); 342 scoped_ptr<ParseNode> expr = ParseExpression(PRECEDENCE_PREFIX + 1);
251 if (has_error()) 343 if (has_error())
252 return scoped_ptr<ParseNode>(); 344 return scoped_ptr<ParseNode>();
253 scoped_ptr<UnaryOpNode> unary_op(new UnaryOpNode); 345 scoped_ptr<UnaryOpNode> unary_op(new UnaryOpNode);
254 unary_op->set_op(token); 346 unary_op->set_op(token);
255 unary_op->set_operand(expr.Pass()); 347 unary_op->set_operand(expr.Pass());
256 return unary_op.Pass(); 348 return unary_op.Pass();
257 } 349 }
258 350
259 scoped_ptr<ParseNode> Parser::List(Token node) { 351 scoped_ptr<ParseNode> Parser::List(Token node) {
260 scoped_ptr<ParseNode> list(ParseList(node, Token::RIGHT_BRACKET, true)); 352 scoped_ptr<ParseNode> list(ParseList(node, Token::RIGHT_BRACKET));
261 if (!has_error() && !at_end()) 353 if (!has_error() && !at_end())
262 Consume(Token::RIGHT_BRACKET, "Expected ']'"); 354 Consume(Token::RIGHT_BRACKET, "Expected ']'");
263 return list.Pass(); 355 return list.Pass();
264 } 356 }
265 357
266 scoped_ptr<ParseNode> Parser::BinaryOperator(scoped_ptr<ParseNode> left, 358 scoped_ptr<ParseNode> Parser::BinaryOperator(scoped_ptr<ParseNode> left,
267 Token token) { 359 Token token) {
268 scoped_ptr<ParseNode> right = 360 scoped_ptr<ParseNode> right =
269 ParseExpression(expressions_[token.type()].precedence + 1); 361 ParseExpression(expressions_[token.type()].precedence + 1);
270 if (!right) { 362 if (!right) {
(...skipping 16 matching lines...) Expand all
287 list->set_end(make_scoped_ptr(new EndNode(token))); 379 list->set_end(make_scoped_ptr(new EndNode(token)));
288 scoped_ptr<BlockNode> block; 380 scoped_ptr<BlockNode> block;
289 bool has_arg = false; 381 bool has_arg = false;
290 if (LookAhead(Token::LEFT_PAREN)) { 382 if (LookAhead(Token::LEFT_PAREN)) {
291 Token start_token = Consume(); 383 Token start_token = Consume();
292 // Parsing a function call. 384 // Parsing a function call.
293 has_arg = true; 385 has_arg = true;
294 if (Match(Token::RIGHT_PAREN)) { 386 if (Match(Token::RIGHT_PAREN)) {
295 // Nothing, just an empty call. 387 // Nothing, just an empty call.
296 } else { 388 } else {
297 list = ParseList(start_token, Token::RIGHT_PAREN, false); 389 list = ParseList(start_token, Token::RIGHT_PAREN);
298 if (has_error()) 390 if (has_error())
299 return scoped_ptr<ParseNode>(); 391 return scoped_ptr<ParseNode>();
300 Consume(Token::RIGHT_PAREN, "Expected ')' after call"); 392 Consume(Token::RIGHT_PAREN, "Expected ')' after call");
301 } 393 }
302 // Optionally with a scope. 394 // Optionally with a scope.
303 if (LookAhead(Token::LEFT_BRACE)) { 395 if (LookAhead(Token::LEFT_BRACE)) {
304 block = ParseBlock(); 396 block = ParseBlock();
305 if (has_error()) 397 if (has_error())
306 return scoped_ptr<ParseNode>(); 398 return scoped_ptr<ParseNode>();
307 } 399 }
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
371 463
372 scoped_ptr<AccessorNode> accessor(new AccessorNode); 464 scoped_ptr<AccessorNode> accessor(new AccessorNode);
373 accessor->set_base(left->AsIdentifier()->value()); 465 accessor->set_base(left->AsIdentifier()->value());
374 accessor->set_member(scoped_ptr<IdentifierNode>( 466 accessor->set_member(scoped_ptr<IdentifierNode>(
375 static_cast<IdentifierNode*>(right.release()))); 467 static_cast<IdentifierNode*>(right.release())));
376 return accessor.Pass(); 468 return accessor.Pass();
377 } 469 }
378 470
379 // Does not Consume the start or end token. 471 // Does not Consume the start or end token.
380 scoped_ptr<ListNode> Parser::ParseList(Token start_token, 472 scoped_ptr<ListNode> Parser::ParseList(Token start_token,
381 Token::Type stop_before, 473 Token::Type stop_before) {
382 bool allow_trailing_comma) {
383 scoped_ptr<ListNode> list(new ListNode); 474 scoped_ptr<ListNode> list(new ListNode);
384 list->set_begin_token(start_token); 475 list->set_begin_token(start_token);
385 bool just_got_comma = false; 476 bool just_got_comma = false;
386 bool first_time = true; 477 bool first_time = true;
387 while (!LookAhead(stop_before)) { 478 while (!LookAhead(stop_before)) {
388 if (!first_time) { 479 if (!first_time) {
389 if (!just_got_comma) { 480 if (!just_got_comma) {
390 // Require commas separate things in lists. 481 // Require commas separate things in lists.
391 *err_ = Err(cur_token(), "Expected comma between items."); 482 *err_ = Err(cur_token(), "Expected comma between items.");
392 return scoped_ptr<ListNode>(); 483 return scoped_ptr<ListNode>();
393 } 484 }
394 } 485 }
395 first_time = false; 486 first_time = false;
396 487
397 // Why _OR? We're parsing things that are higher precedence than the , 488 // Why _OR? We're parsing things that are higher precedence than the ,
398 // that separates the items of the list. , should appear lower than 489 // that separates the items of the list. , should appear lower than
399 // boolean expressions (the lowest of which is OR), but above assignments. 490 // boolean expressions (the lowest of which is OR), but above assignments.
400 list->append_item(ParseExpression(PRECEDENCE_OR)); 491 list->append_item(ParseExpression(PRECEDENCE_OR));
401 if (has_error()) 492 if (has_error())
402 return scoped_ptr<ListNode>(); 493 return scoped_ptr<ListNode>();
403 if (at_end()) { 494 if (at_end()) {
404 *err_ = 495 *err_ =
405 Err(tokens_[tokens_.size() - 1], "Unexpected end of file in list."); 496 Err(tokens_[tokens_.size() - 1], "Unexpected end of file in list.");
406 return scoped_ptr<ListNode>(); 497 return scoped_ptr<ListNode>();
407 } 498 }
408 if (list->contents().back()->AsBlockComment()) { 499 if (list->contents().back()->AsBlockComment()) {
409 // If there was a comment inside the list, we don't need a comma to the 500 // If there was a comment inside the list, we don't need a comma to the
410 // next item, so pretend we got one, if we're expecting one. 501 // next item, so pretend we got one, if we're expecting one.
411 just_got_comma = allow_trailing_comma; 502 just_got_comma = true;
412 } else { 503 } else {
413 just_got_comma = Match(Token::COMMA); 504 just_got_comma = Match(Token::COMMA);
414 } 505 }
415 } 506 }
416 if (just_got_comma && !allow_trailing_comma) {
417 *err_ = Err(cur_token(), "Trailing comma");
418 return scoped_ptr<ListNode>();
419 }
420 list->set_end(make_scoped_ptr(new EndNode(cur_token()))); 507 list->set_end(make_scoped_ptr(new EndNode(cur_token())));
421 return list.Pass(); 508 return list.Pass();
422 } 509 }
423 510
424 scoped_ptr<ParseNode> Parser::ParseFile() { 511 scoped_ptr<ParseNode> Parser::ParseFile() {
425 scoped_ptr<BlockNode> file(new BlockNode); 512 scoped_ptr<BlockNode> file(new BlockNode);
426 for (;;) { 513 for (;;) {
427 if (at_end()) 514 if (at_end())
428 break; 515 break;
429 scoped_ptr<ParseNode> statement = ParseStatement(); 516 scoped_ptr<ParseNode> statement = ParseStatement();
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after
616 break; 703 break;
617 } 704 }
618 } 705 }
619 706
620 // Suffix comments were assigned in reverse, so if there were multiple on 707 // Suffix comments were assigned in reverse, so if there were multiple on
621 // the same node, they need to be reversed. 708 // the same node, they need to be reversed.
622 if ((*i)->comments() && !(*i)->comments()->suffix().empty()) 709 if ((*i)->comments() && !(*i)->comments()->suffix().empty())
623 const_cast<ParseNode*>(*i)->comments_mutable()->ReverseSuffix(); 710 const_cast<ParseNode*>(*i)->comments_mutable()->ReverseSuffix();
624 } 711 }
625 } 712 }
OLDNEW
« no previous file with comments | « tools/gn/parser.h ('k') | tools/gn/parser_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698