Index: tools/gn/input_conversion.cc |
diff --git a/tools/gn/input_conversion.cc b/tools/gn/input_conversion.cc |
index 246b4c9d5fb9e4f8c7deec96e31ef80fa42d4beb..e66000b5ebbedcfb4c0c0bfa94c8ce8eafb68fc8 100644 |
--- a/tools/gn/input_conversion.cc |
+++ b/tools/gn/input_conversion.cc |
@@ -12,6 +12,7 @@ |
#include "tools/gn/label.h" |
#include "tools/gn/parse_tree.h" |
#include "tools/gn/parser.h" |
+#include "tools/gn/scheduler.h" |
#include "tools/gn/scope.h" |
#include "tools/gn/settings.h" |
#include "tools/gn/tokenizer.h" |
@@ -47,36 +48,56 @@ Err MakeParseErr(const std::string& input, |
return result; |
} |
+enum ValueOrScope { |
+ PARSE_VALUE, // Treat the input as an expression. |
+ PARSE_SCOPE, // Treat the input as code and return the resulting scope. |
+}; |
+ |
// Sets the origin of the value and any nested values with the given node. |
-Value ParseString(const std::string& input, |
- const ParseNode* origin, |
- Err* err) { |
- SourceFile empty_source_for_most_vexing_parse; |
- InputFile input_file(empty_source_for_most_vexing_parse); |
- input_file.SetContents(input); |
- |
- std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, err); |
+Value ParseValueOrScope(const std::string& input, |
+ ValueOrScope what, |
+ const ParseNode* origin, |
+ Err* err) { |
+ // The memory for these will be kept around by the input file manager |
+ // so the origin parse nodes for the values will be preserved. |
+ InputFile* input_file; |
+ std::vector<Token>* tokens; |
+ scoped_ptr<ParseNode>* parse_root_ptr; |
+ g_scheduler->input_file_manager()->AddDynamicInput( |
+ &input_file, &tokens, &parse_root_ptr); |
+ |
+ input_file->SetContents(input); |
+ input_file->set_friendly_name("Script input"); // TODO(brettw) make nicer. |
+ |
+ *tokens = Tokenizer::Tokenize(input_file, err); |
if (err->has_error()) { |
*err = MakeParseErr(input, origin, *err); |
return Value(); |
} |
- scoped_ptr<ParseNode> expression = Parser::ParseExpression(tokens, err); |
+ // Parse the file according to what we're looking for. |
+ if (what == PARSE_VALUE) |
+ *parse_root_ptr = Parser::ParseExpression(*tokens, err); |
+ else |
+ *parse_root_ptr = Parser::Parse(*tokens, err); // Will return a Block. |
if (err->has_error()) { |
*err = MakeParseErr(input, origin, *err); |
return Value(); |
} |
+ ParseNode* parse_root = parse_root_ptr->get(); // For nicer syntax below. |
// It's valid for the result to be a null pointer, this just means that the |
// script returned nothing. |
- if (!expression) |
+ if (!parse_root) |
return Value(); |
- // The result should either be a list or a literal, anything else is |
- // invalid. |
- if (!expression->AsList() && !expression->AsLiteral()) { |
- *err = MakeParseErr(input, origin, Err()); |
- return Value(); |
+ // When parsing as a value, the result should either be a list or a literal, |
+ // anything else is invalid. |
+ if (what == PARSE_VALUE) { |
+ if (!parse_root->AsList() && !parse_root->AsLiteral()) { |
+ *err = MakeParseErr(input, origin, Err()); |
+ return Value(); |
+ } |
} |
BuildSettings build_settings; |
@@ -84,23 +105,31 @@ Value ParseString(const std::string& input, |
Scope scope(&settings); |
Err nested_err; |
- Value result = expression->Execute(&scope, &nested_err); |
+ Value result = parse_root->Execute(&scope, &nested_err); |
if (nested_err.has_error()) { |
*err = MakeParseErr(input, origin, nested_err); |
return Value(); |
} |
- // The returned value will have references to the temporary parse nodes we |
- // made on the stack. If the values are used in an error message in the |
- // future, this will crash. Reset the origin of all values to be our |
- // containing origin. |
- result.RecursivelySetOrigin(origin); |
+ // When we want the result as a scope, the result is actually the scope |
+ // we made, rather than the result of running the code (which will be |
+ // empty). |
+ if (what == PARSE_SCOPE) { |
+ DCHECK(result.type() == Value::NONE); |
+ // Convert the scope to a dictionary. We have to return the result, and |
+ // a scope value does not own its scope. |
+ result = Value(origin, Value::DICT); |
+ std::vector<base::StringPiece> keys; |
+ scope.GetCurrentScopeKeys(&keys); |
+ for (size_t i = 0; i < keys.size(); i++) { |
+ result.dict_value()[keys[i].as_string()] = |
+ *scope.GetValue(keys[i], false); |
+ } |
+ } |
return result; |
} |
-Value ParseList(const std::string& input, |
- const ParseNode* origin, |
- Err* err) { |
+Value ParseList(const std::string& input, const ParseNode* origin, Err* err) { |
Value ret(origin, Value::LIST); |
std::vector<std::string> as_lines; |
base::SplitString(input, '\n', &as_lines); |
@@ -141,11 +170,13 @@ Value DoConvertInputToValue(const std::string& input, |
} |
if (input_conversion == "value") |
- return ParseString(input, origin, err); |
+ return ParseValueOrScope(input, PARSE_VALUE, origin, err); |
if (input_conversion == "string") |
return Value(origin, input); |
if (input_conversion == "list lines") |
return ParseList(input, origin, err); |
+ if (input_conversion == "scope") |
+ return ParseValueOrScope(input, PARSE_SCOPE, origin, err); |
*err = Err(original_input_conversion, "Not a valid input_conversion.", |
"Have you considered a career in retail?"); |
@@ -172,6 +203,19 @@ extern const char kInputConversion_Help[] = |
" After splitting, each individual line will be trimmed of\n" |
" whitespace on both ends.\n" |
"\n" |
+ " \"scope\"\n" |
+ " Execute the block as GN code and return a scope with the\n" |
+ " resulting values in it. If the input was:\n" |
+ " a = [ \"hello.cc\", \"world.cc\" ]\n" |
+ " b = 26\n" |
+ " and you read the result into a variable named \"val\", then you\n" |
+ " could access contents the \".\" operator on \"val\":\n" |
+ " sources = val.a\n" |
+ " some_count = val.b\n" |
+ "\n" |
+ " \"string\"\n" |
+ " Return the file contents into a single string.\n" |
+ "\n" |
" \"value\"\n" |
" Parse the input as if it was a literal rvalue in a buildfile.\n" |
" Examples of typical program output using this mode:\n" |
@@ -184,9 +228,6 @@ extern const char kInputConversion_Help[] = |
" Note that if the input is empty, the result will be a null value\n" |
" which will produce an error if assigned to a variable.\n" |
"\n" |
- " \"string\"\n" |
- " Return the file contents into a single string.\n" |
- "\n" |
" \"trim ...\"\n" |
" Prefixing any of the other transformations with the word \"trim\"\n" |
" will result in whitespace being trimmed from the beginning and end\n" |