Index: tools/gn/input_conversion.cc |
diff --git a/tools/gn/input_conversion.cc b/tools/gn/input_conversion.cc |
index 246b4c9d5fb9e4f8c7deec96e31ef80fa42d4beb..d25ceff9414a0277c42946f068d71b109bac0355 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" |
@@ -19,88 +20,80 @@ |
namespace { |
-// When parsing the result as a value, we may get various types of errors. |
-// This creates an error message for this case with an optional nested error |
-// message to reference. If there is no nested err, pass Err(). |
-// |
-// This code also takes care to rewrite the original error which will reference |
-// the temporary InputFile which won't exist when the error is propogated |
-// out to a higher level. |
-Err MakeParseErr(const std::string& input, |
- const ParseNode* origin, |
- const Err& nested) { |
- std::string help_text = |
- "When parsing a result as a \"value\" it should look like a list:\n" |
- " [ \"a\", \"b\", 5 ]\n" |
- "or a single literal:\n" |
- " \"my result\"\n" |
- "but instead I got this, which I find very confusing:\n"; |
- help_text.append(input); |
- if (nested.has_error()) |
- help_text.append("\nThe exact error was:"); |
- |
- Err result(origin, "Script result wasn't a valid value.", help_text); |
- if (nested.has_error()) { |
- result.AppendSubErr(Err(LocationRange(), nested.message(), |
- nested.help_text())); |
- } |
- 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); |
- if (err->has_error()) { |
- *err = MakeParseErr(input, origin, *err); |
- return Value(); |
+Value ParseValueOrScope(const Settings* settings, |
+ 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); |
+ if (origin) { |
+ // This description will be the blame for any error messages caused by |
+ // script parsing or if a value is blamed. It will say |
+ // "Error at <...>:line:char" so here we try to make a string for <...> |
+ // that reads well in this context. |
+ input_file->set_friendly_name( |
+ "dynamically parsed input that " + |
+ origin->GetRange().begin().Describe(true) + |
+ " loaded "); |
+ } else { |
+ input_file->set_friendly_name("dynamic input"); |
} |
- scoped_ptr<ParseNode> expression = Parser::ParseExpression(tokens, err); |
- if (err->has_error()) { |
- *err = MakeParseErr(input, origin, *err); |
+ *tokens = Tokenizer::Tokenize(input_file, err); |
+ if (err->has_error()) |
return Value(); |
- } |
+ |
+ // 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()) |
+ 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()) |
+ return Value(); |
} |
- BuildSettings build_settings; |
- Settings settings(&build_settings, std::string()); |
- Scope scope(&settings); |
+ scoped_ptr<Scope> scope(new Scope(settings)); |
- Err nested_err; |
- Value result = expression->Execute(&scope, &nested_err); |
- if (nested_err.has_error()) { |
- *err = MakeParseErr(input, origin, nested_err); |
+ Value result = parse_root->Execute(scope.get(), err); |
+ if (err->has_error()) |
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 block (which will be empty). |
+ if (what == PARSE_SCOPE) { |
+ DCHECK(result.type() == Value::NONE); |
+ result = Value(origin, scope.Pass()); |
+ } |
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); |
@@ -121,7 +114,8 @@ Value ParseList(const std::string& input, |
// input conversion so we can recursively call ourselves to handle the optional |
// "trim" prefix. This original value is also kept for the purposes of throwing |
// errors. |
-Value DoConvertInputToValue(const std::string& input, |
+Value DoConvertInputToValue(const Settings* settings, |
+ const std::string& input, |
const ParseNode* origin, |
const Value& original_input_conversion, |
const std::string& input_conversion, |
@@ -136,16 +130,18 @@ Value DoConvertInputToValue(const std::string& input, |
// Remove "trim" prefix from the input conversion and re-run. |
return DoConvertInputToValue( |
- trimmed, origin, original_input_conversion, |
+ settings, trimmed, origin, original_input_conversion, |
input_conversion.substr(arraysize(kTrimPrefix) - 1), err); |
} |
if (input_conversion == "value") |
- return ParseString(input, origin, err); |
+ return ParseValueOrScope(settings, 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(settings, input, PARSE_SCOPE, origin, err); |
*err = Err(original_input_conversion, "Not a valid input_conversion.", |
"Have you considered a career in retail?"); |
@@ -172,6 +168,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 +193,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" |
@@ -197,7 +203,8 @@ extern const char kInputConversion_Help[] = |
" Note that \"trim value\" is useless because the value parser skips\n" |
" whitespace anyway.\n"; |
-Value ConvertInputToValue(const std::string& input, |
+Value ConvertInputToValue(const Settings* settings, |
+ const std::string& input, |
const ParseNode* origin, |
const Value& input_conversion_value, |
Err* err) { |
@@ -205,6 +212,6 @@ Value ConvertInputToValue(const std::string& input, |
return Value(); // Allow null inputs to mean discard the result. |
if (!input_conversion_value.VerifyTypeIs(Value::STRING, err)) |
return Value(); |
- return DoConvertInputToValue(input, origin, input_conversion_value, |
+ return DoConvertInputToValue(settings, input, origin, input_conversion_value, |
input_conversion_value.string_value(), err); |
} |