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

Unified Diff: tools/gn/parser.cc

Issue 2187523003: Allow creation and modification of scopes in GN. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Review comments Created 4 years, 4 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/gn/parser.h ('k') | tools/gn/parser_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/gn/parser.cc
diff --git a/tools/gn/parser.cc b/tools/gn/parser.cc
index 7e5886f8950b3d4b4466aa34e966658ca05779ed..57a398e0187e50f03a7efa15443009fc9efb41ab 100644
--- a/tools/gn/parser.cc
+++ b/tools/gn/parser.cc
@@ -13,7 +13,7 @@
#include "tools/gn/token.h"
const char kGrammar_Help[] =
- "GN build language grammar\n"
+ "Language and grammar for GN build files\n"
"\n"
"Tokens\n"
"\n"
@@ -79,6 +79,13 @@ const char kGrammar_Help[] =
" To insert an arbitrary byte value, use $0xFF. For example, to\n"
" insert a newline character: \"Line one$0x0ALine two\".\n"
"\n"
+ " An expansion will evaluate the variable following the '$' and insert\n"
+ " a stringified version of it into the result. For example, to concat\n"
+ " two path components with a slash separating them:\n"
+ " \"$var_one/$var_two\"\n"
+ " Use the \"${var_one}\" format to be explicitly deliniate the variable\n"
+ " for otherwise-ambiguous cases.\n"
+ "\n"
"Punctuation\n"
"\n"
" The following character sequences represent punctuation:\n"
@@ -95,19 +102,20 @@ const char kGrammar_Help[] =
" File = StatementList .\n"
"\n"
" Statement = Assignment | Call | Condition .\n"
- " Assignment = identifier AssignOp Expr .\n"
+ " LValue = identifier | ArrayAccess | ScopeAccess .\n"
+ " Assignment = LValue AssignOp Expr .\n"
" Call = identifier \"(\" [ ExprList ] \")\" [ Block ] .\n"
" Condition = \"if\" \"(\" Expr \")\" Block\n"
" [ \"else\" ( Condition | Block ) ] .\n"
" Block = \"{\" StatementList \"}\" .\n"
" StatementList = { Statement } .\n"
"\n"
- " ArrayAccess = identifier \"[\" { identifier | integer } \"]\" .\n"
+ " ArrayAccess = identifier \"[\" Expr \"]\" .\n"
" ScopeAccess = identifier \".\" identifier .\n"
" Expr = UnaryExpr | Expr BinaryOp Expr .\n"
" UnaryExpr = PrimaryExpr | UnaryOp UnaryExpr .\n"
" PrimaryExpr = identifier | integer | string | Call\n"
- " | ArrayAccess | ScopeAccess\n"
+ " | ArrayAccess | ScopeAccess | Block\n"
" | \"(\" Expr \")\"\n"
" | \"[\" [ ExprList [ \",\" ] ] \"]\" .\n"
" ExprList = Expr { \",\" Expr } .\n"
@@ -120,7 +128,95 @@ const char kGrammar_Help[] =
" | \"&&\"\n"
" | \"||\" . // lowest priority\n"
"\n"
- " All binary operators are left-associative.\n";
+ " All binary operators are left-associative.\n"
+ "\n"
+ "Types\n"
+ "\n"
+ " The GN language is dynamically typed. The following types are used:\n"
+ "\n"
+ " - Boolean: Uses the keywords \"true\" and \"false\". There is no\n"
+ " implicit conversion between booleans and integers.\n"
+ "\n"
+ " - Integers: All numbers in GN are signed 64-bit integers.\n"
+ "\n"
+ " - Strings: Strings are 8-bit with no enforced encoding. When a string\n"
+ " is used to interact with other systems with particular encodings\n"
+ " (like the Windows and Mac filesystems) it is assumed to be UTF-8.\n"
+ " See \"String literals\" above for more.\n"
+ "\n"
+ " - Lists: Lists are arbitrary-length ordered lists of values. See\n"
+ " \"Lists\" below for more.\n"
+ "\n"
+ " - Scopes: Scopes are like dictionaries that use variable names for\n"
+ " keys. See \"Scopes\" below for more.\n"
+ "\n"
+ "Lists\n"
+ "\n"
+ " Lists are created with [] and using commas to separate items:\n"
+ "\n"
+ " mylist = [ 0, 1, 2, \"some string\" ]\n"
+ "\n"
+ " A comma after the last item is optional. Lists are dereferenced using\n"
+ " 0-based indexing:\n"
+ "\n"
+ " mylist[0] += 1\n"
+ " var = mylist[2]\n"
+ "\n"
+ " Lists can be concatenated using the '+' and '+=' operators. Bare\n"
+ " values can not be concatenated with lists, to add a single item,\n"
+ " it must be put into a list of length one.\n"
+ "\n"
+ " Items can be removed from lists using the '-' and '-=' operators.\n"
+ " This will remove all occurrences of every item in the right-hand list\n"
+ " from the left-hand list. It is an error to remove an item not in the\n"
+ " list. This is to prevent common typos and to detect dead code that\n"
+ " is removing things that no longer apply.\n"
+ "\n"
+ " It is an error to use '=' to replace a nonempty list with another\n"
+ " nonempty list. This is to prevent accidentally overwriting data\n"
+ " when in most cases '+=' was intended. To overwrite a list on purpose,\n"
+ " first assign it to the empty list:\n"
+ "\n"
+ " mylist = []\n"
+ " mylist = otherlist\n"
+ "\n"
+ " When assigning to a list named 'sources' using '=' or '+=', list\n"
+ " items may be automatically filtered out.\n"
+ " See \"gn help set_sources_assignment_filter\" for more.\n"
+ "\n"
+ "Scopes\n"
+ "\n"
+ " All execution happens in the context of a scope which holds the\n"
+ " current state (like variables). With the exception of loops and\n"
+ " conditions, '{' introduces a new scope that has a parent reference to\n"
+ " the old scope.\n"
+ "\n"
+ " Variable reads recursively search all nested scopes until the\n"
+ " variable is found or there are no more scopes. Variable writes always\n"
+ " go into the current scope. This means that after the closing '}'\n"
+ " (again excepting loops and conditions), all local variables will be\n"
+ " restored to the previous values. This also means that \"foo = foo\"\n"
+ " can do useful work by copying a variable into the current scope that\n"
+ " was defined in a containing scope.\n"
+ "\n"
+ " Scopes can also be assigned to variables. Such scopes can be created\n"
+ " by functions like exec_script, when invoking a template (the template\n"
+ " code refers to the variables set by the invoking code by the\n"
+ " implicitly-created \"invoker\" scope), or explicitly like:\n"
+ "\n"
+ " empty_scope = {}\n"
+ " myvalues = {\n"
+ " foo = 21\n"
+ " bar = \"something\"\n"
+ " }\n"
+ "\n"
+ " Inside such a scope definition can be any GN code including\n"
+ " conditionals and function calls. After the close of the scope, it will\n"
+ " contain all variables explicitly set by the code contained inside it.\n"
+ " After this, the values can be read, modified, or added to:\n"
+ "\n"
+ " myvalues.foo += 2\n"
+ " empty_scope.new_thing = [ 1, 2, 3 ]\n";
enum Precedence {
PRECEDENCE_ASSIGNMENT = 1, // Lowest precedence.
@@ -172,7 +268,7 @@ ParserHelper Parser::expressions_[] = {
{nullptr, nullptr, -1}, // RIGHT_PAREN
{&Parser::List, &Parser::Subscript, PRECEDENCE_CALL}, // LEFT_BRACKET
{nullptr, nullptr, -1}, // RIGHT_BRACKET
- {nullptr, nullptr, -1}, // LEFT_BRACE
+ {&Parser::Block, nullptr, -1}, // LEFT_BRACE
{nullptr, nullptr, -1}, // RIGHT_BRACE
{nullptr, nullptr, -1}, // IF
{nullptr, nullptr, -1}, // ELSE
@@ -356,6 +452,12 @@ std::unique_ptr<ParseNode> Parser::ParseExpression(int precedence) {
return left;
}
+std::unique_ptr<ParseNode> Parser::Block(Token token) {
+ // This entrypoing into ParseBlock means its part of an expression and we
+ // always want the result.
+ return ParseBlock(token, BlockNode::RETURNS_SCOPE);
+}
+
std::unique_ptr<ParseNode> Parser::Literal(Token token) {
return base::WrapUnique(new LiteralNode(token));
}
@@ -441,7 +543,7 @@ std::unique_ptr<ParseNode> Parser::IdentifierOrCall(
}
// Optionally with a scope.
if (LookAhead(Token::LEFT_BRACE)) {
- block = ParseBlock();
+ block = ParseBlock(Consume(), BlockNode::DISCARDS_RESULT);
if (has_error())
return std::unique_ptr<ParseNode>();
}
@@ -461,8 +563,10 @@ std::unique_ptr<ParseNode> Parser::IdentifierOrCall(
std::unique_ptr<ParseNode> Parser::Assignment(std::unique_ptr<ParseNode> left,
Token token) {
- if (left->AsIdentifier() == nullptr) {
- *err_ = Err(left.get(), "Left-hand side of assignment must be identifier.");
+ if (left->AsIdentifier() == nullptr && left->AsAccessor() == nullptr) {
+ *err_ = Err(left.get(),
+ "The left-hand side of an assignment must be an identifier, "
+ "scope access, or array access.");
return std::unique_ptr<ParseNode>();
}
std::unique_ptr<ParseNode> value = ParseExpression(PRECEDENCE_ASSIGNMENT);
@@ -567,7 +671,7 @@ std::unique_ptr<ListNode> Parser::ParseList(Token start_token,
}
std::unique_ptr<ParseNode> Parser::ParseFile() {
- std::unique_ptr<BlockNode> file(new BlockNode);
+ std::unique_ptr<BlockNode> file(new BlockNode(BlockNode::DISCARDS_RESULT));
for (;;) {
if (at_end())
break;
@@ -611,13 +715,13 @@ std::unique_ptr<ParseNode> Parser::ParseStatement() {
}
}
-std::unique_ptr<BlockNode> Parser::ParseBlock() {
- Token begin_token =
- Consume(Token::LEFT_BRACE, "Expected '{' to start a block.");
+std::unique_ptr<BlockNode> Parser::ParseBlock(
+ Token begin_brace,
+ BlockNode::ResultMode result_mode) {
if (has_error())
return std::unique_ptr<BlockNode>();
- std::unique_ptr<BlockNode> block(new BlockNode);
- block->set_begin_token(begin_token);
+ std::unique_ptr<BlockNode> block(new BlockNode(result_mode));
+ block->set_begin_token(begin_brace);
for (;;) {
if (LookAhead(Token::RIGHT_BRACE)) {
@@ -641,10 +745,13 @@ std::unique_ptr<ParseNode> Parser::ParseCondition() {
if (IsAssignment(condition->condition()))
*err_ = Err(condition->condition(), "Assignment not allowed in 'if'.");
Consume(Token::RIGHT_PAREN, "Expected ')' after condition of 'if'.");
- condition->set_if_true(ParseBlock());
+ condition->set_if_true(ParseBlock(
+ Consume(Token::LEFT_BRACE, "Expected '{' to start 'if' block."),
+ BlockNode::DISCARDS_RESULT));
if (Match(Token::ELSE)) {
if (LookAhead(Token::LEFT_BRACE)) {
- condition->set_if_false(ParseBlock());
+ condition->set_if_false(ParseBlock(Consume(),
+ BlockNode::DISCARDS_RESULT));
} else if (LookAhead(Token::IF)) {
condition->set_if_false(ParseStatement());
} else {
« 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