Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / tools / gn / input_conversion.cc
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.
4
5 #include "tools/gn/input_conversion.h"
6
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"
19
20 namespace {
21
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().
25 //
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,
31                  const Err& nested) {
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"
36       "  \"my result\"\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:");
41
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(),
45                             nested.help_text()));
46   }
47   return result;
48 }
49
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,
53                   Err* err) {
54   SourceFile empty_source_for_most_vexing_parse;
55   InputFile input_file(empty_source_for_most_vexing_parse);
56   input_file.SetContents(input);
57
58   std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, err);
59   if (err->has_error()) {
60     *err = MakeParseErr(input, origin, *err);
61     return Value();
62   }
63
64   scoped_ptr<ParseNode> expression = Parser::ParseExpression(tokens, err);
65   if (err->has_error()) {
66     *err = MakeParseErr(input, origin, *err);
67     return Value();
68   }
69
70   // It's valid for the result to be a null pointer, this just means that the
71   // script returned nothing.
72   if (!expression)
73     return Value();
74
75   // The result should either be a list or a literal, anything else is
76   // invalid.
77   if (!expression->AsList() && !expression->AsLiteral()) {
78     *err = MakeParseErr(input, origin, Err());
79     return Value();
80   }
81
82   BuildSettings build_settings;
83   Settings settings(&build_settings, std::string());
84   Scope scope(&settings);
85
86   Err nested_err;
87   Value result = expression->Execute(&scope, &nested_err);
88   if (nested_err.has_error()) {
89     *err = MakeParseErr(input, origin, nested_err);
90     return Value();
91   }
92
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
96   // containing origin.
97   result.RecursivelySetOrigin(origin);
98   return result;
99 }
100
101 Value ParseList(const std::string& input,
102                 const ParseNode* origin,
103                 Err* err) {
104   Value ret(origin, Value::LIST);
105   std::vector<std::string> as_lines;
106   base::SplitString(input, '\n', &as_lines);
107
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);
113
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]));
117   return ret;
118 }
119
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
123 // errors.
124 Value DoConvertInputToValue(const std::string& input,
125                             const ParseNode* origin,
126                             const Value& original_input_conversion,
127                             const std::string& input_conversion,
128                             Err* err) {
129   if (input_conversion.empty())
130     return Value();  // Empty string means discard the result.
131
132   const char kTrimPrefix[] = "trim ";
133   if (StartsWithASCII(input_conversion, kTrimPrefix, true)) {
134     std::string trimmed;
135     TrimWhitespaceASCII(input, TRIM_ALL, &trimmed);
136
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);
141   }
142
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);
149
150   *err = Err(original_input_conversion, "Not a valid input_conversion.",
151              "Have you considered a career in retail?");
152   return Value();
153 }
154
155 }  // namespace
156
157 extern const char kInputConversion_Help[] =
158     "input_conversion: Specifies how to transform input to a variable.\n"
159     "\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"
163     "\n"
164     "  \"\" (the default)\n"
165     "      Discard the result and return None.\n"
166     "\n"
167     "  \"list lines\"\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"
171     "\n"
172     "      After splitting, each individual line will be trimmed of\n"
173     "      whitespace on both ends.\n"
174     "\n"
175     "  \"value\"\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"
179     "      or\n"
180     "        \"foo bar\"            (result will be a string)\n"
181     "      or\n"
182     "        5                    (result will be an integer)\n"
183     "\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"
186     "\n"
187     "  \"string\"\n"
188     "      Return the file contents into a single string.\n"
189     "\n"
190     "  \"trim ...\"\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"
194     "\n"
195     "      Examples: \"trim string\" or \"trim list lines\"\n"
196     "\n"
197     "      Note that \"trim value\" is useless because the value parser skips\n"
198     "      whitespace anyway.\n";
199
200 Value ConvertInputToValue(const std::string& input,
201                           const ParseNode* origin,
202                           const Value& input_conversion_value,
203                           Err* err) {
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))
207     return Value();
208   return DoConvertInputToValue(input, origin, input_conversion_value,
209                                input_conversion_value.string_value(), err);
210 }