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
3 // found in the LICENSE file.
5 #include "tools/gn/input_conversion.h"
7 #include "base/strings/string_split.h"
8 #include "base/strings/string_util.h"
9 #include "tools/gn/build_settings.h"
10 #include "tools/gn/err.h"
11 #include "tools/gn/input_file.h"
12 #include "tools/gn/label.h"
13 #include "tools/gn/parse_tree.h"
14 #include "tools/gn/parser.h"
15 #include "tools/gn/scope.h"
16 #include "tools/gn/settings.h"
17 #include "tools/gn/tokenizer.h"
18 #include "tools/gn/value.h"
22 // When parsing the result as a value, we may get various types of errors.
23 // This creates an error message for this case with an optional nested error
24 // message to reference. If there is no nested err, pass Err().
26 // This code also takes care to rewrite the original error which will reference
27 // the temporary InputFile which won't exist when the error is propogated
28 // out to a higher level.
29 Err
MakeParseErr(const std::string
& input
,
30 const ParseNode
* origin
,
32 std::string help_text
=
33 "When parsing a result as a \"value\" it should look like a list:\n"
34 " [ \"a\", \"b\", 5 ]\n"
35 "or a single literal:\n"
37 "but instead I got this, which I find very confusing:\n";
38 help_text
.append(input
);
39 if (nested
.has_error())
40 help_text
.append("\nThe exact error was:");
42 Err
result(origin
, "Script result wasn't a valid value.", help_text
);
43 if (nested
.has_error()) {
44 result
.AppendSubErr(Err(LocationRange(), nested
.message(),
50 // Sets the origin of the value and any nested values with the given node.
51 Value
ParseString(const std::string
& input
,
52 const ParseNode
* origin
,
54 SourceFile empty_source_for_most_vexing_parse
;
55 InputFile
input_file(empty_source_for_most_vexing_parse
);
56 input_file
.SetContents(input
);
58 std::vector
<Token
> tokens
= Tokenizer::Tokenize(&input_file
, err
);
59 if (err
->has_error()) {
60 *err
= MakeParseErr(input
, origin
, *err
);
64 scoped_ptr
<ParseNode
> expression
= Parser::ParseExpression(tokens
, err
);
65 if (err
->has_error()) {
66 *err
= MakeParseErr(input
, origin
, *err
);
70 // It's valid for the result to be a null pointer, this just means that the
71 // script returned nothing.
75 // The result should either be a list or a literal, anything else is
77 if (!expression
->AsList() && !expression
->AsLiteral()) {
78 *err
= MakeParseErr(input
, origin
, Err());
82 BuildSettings build_settings
;
83 Settings
settings(&build_settings
, std::string());
84 Scope
scope(&settings
);
87 Value result
= expression
->Execute(&scope
, &nested_err
);
88 if (nested_err
.has_error()) {
89 *err
= MakeParseErr(input
, origin
, nested_err
);
93 // The returned value will have references to the temporary parse nodes we
94 // made on the stack. If the values are used in an error message in the
95 // future, this will crash. Reset the origin of all values to be our
97 result
.RecursivelySetOrigin(origin
);
101 Value
ParseList(const std::string
& input
,
102 const ParseNode
* origin
,
104 Value
ret(origin
, Value::LIST
);
105 std::vector
<std::string
> as_lines
;
106 base::SplitString(input
, '\n', &as_lines
);
108 // Trim one empty line from the end since the last line might end in a
109 // newline. If the user wants more trimming, they'll specify "trim" in the
110 // input conversion options.
111 if (!as_lines
.empty() && as_lines
[as_lines
.size() - 1].empty())
112 as_lines
.resize(as_lines
.size() - 1);
114 ret
.list_value().reserve(as_lines
.size());
115 for (size_t i
= 0; i
< as_lines
.size(); i
++)
116 ret
.list_value().push_back(Value(origin
, as_lines
[i
]));
120 // Backend for ConvertInputToValue, this takes the extracted string for the
121 // input conversion so we can recursively call ourselves to handle the optional
122 // "trim" prefix. This original value is also kept for the purposes of throwing
124 Value
DoConvertInputToValue(const std::string
& input
,
125 const ParseNode
* origin
,
126 const Value
& original_input_conversion
,
127 const std::string
& input_conversion
,
129 if (input_conversion
.empty())
130 return Value(); // Empty string means discard the result.
132 const char kTrimPrefix
[] = "trim ";
133 if (StartsWithASCII(input_conversion
, kTrimPrefix
, true)) {
135 TrimWhitespaceASCII(input
, TRIM_ALL
, &trimmed
);
137 // Remove "trim" prefix from the input conversion and re-run.
138 return DoConvertInputToValue(
139 trimmed
, origin
, original_input_conversion
,
140 input_conversion
.substr(arraysize(kTrimPrefix
) - 1), err
);
143 if (input_conversion
== "value")
144 return ParseString(input
, origin
, err
);
145 if (input_conversion
== "string")
146 return Value(origin
, input
);
147 if (input_conversion
== "list lines")
148 return ParseList(input
, origin
, err
);
150 *err
= Err(original_input_conversion
, "Not a valid input_conversion.",
151 "Have you considered a career in retail?");
157 extern const char kInputConversion_Help
[] =
158 "input_conversion: Specifies how to transform input to a variable.\n"
160 " input_conversion is an argument to read_file and exec_script that\n"
161 " specifies how the result of the read operation should be converted\n"
162 " into a variable.\n"
164 " \"\" (the default)\n"
165 " Discard the result and return None.\n"
168 " Return the file contents as a list, with a string for each line.\n"
169 " The newlines will not be present in the result. The last line may\n"
170 " or may not end in a newline.\n"
172 " After splitting, each individual line will be trimmed of\n"
173 " whitespace on both ends.\n"
176 " Parse the input as if it was a literal rvalue in a buildfile.\n"
177 " Examples of typical program output using this mode:\n"
178 " [ \"foo\", \"bar\" ] (result will be a list)\n"
180 " \"foo bar\" (result will be a string)\n"
182 " 5 (result will be an integer)\n"
184 " Note that if the input is empty, the result will be a null value\n"
185 " which will produce an error if assigned to a variable.\n"
188 " Return the file contents into a single string.\n"
191 " Prefixing any of the other transformations with the word \"trim\"\n"
192 " will result in whitespace being trimmed from the beginning and end\n"
193 " of the result before processing.\n"
195 " Examples: \"trim string\" or \"trim list lines\"\n"
197 " Note that \"trim value\" is useless because the value parser skips\n"
198 " whitespace anyway.\n";
200 Value
ConvertInputToValue(const std::string
& input
,
201 const ParseNode
* origin
,
202 const Value
& input_conversion_value
,
204 if (input_conversion_value
.type() == Value::NONE
)
205 return Value(); // Allow null inputs to mean discard the result.
206 if (!input_conversion_value
.VerifyTypeIs(Value::STRING
, err
))
208 return DoConvertInputToValue(input
, origin
, input_conversion_value
,
209 input_conversion_value
.string_value(), err
);