/src/serenity/Userland/Libraries/LibShell/ImmediateFunctions.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2021, the SerenityOS developers. |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include "Formatter.h" |
8 | | #include "Shell.h" |
9 | | #include <LibRegex/Regex.h> |
10 | | #include <math.h> |
11 | | |
12 | | namespace Shell { |
13 | | |
14 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_length_impl(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments, bool across) |
15 | 0 | { |
16 | 0 | auto name = across ? "length_across" : "length"; |
17 | 0 | if (arguments.size() < 1 || arguments.size() > 2) { |
18 | 0 | raise_error(ShellError::EvaluatedSyntaxError, ByteString::formatted("Expected one or two arguments to `{}'", name), invoking_node.position()); |
19 | 0 | return nullptr; |
20 | 0 | } |
21 | | |
22 | 0 | enum { |
23 | 0 | Infer, |
24 | 0 | String, |
25 | 0 | List, |
26 | 0 | } mode { Infer }; |
27 | |
|
28 | 0 | bool is_inferred = false; |
29 | |
|
30 | 0 | const AST::Node* expr_node; |
31 | 0 | if (arguments.size() == 2) { |
32 | | // length string <expr> |
33 | | // length list <expr> |
34 | |
|
35 | 0 | auto& mode_arg = arguments.first(); |
36 | 0 | if (!mode_arg->is_bareword()) { |
37 | 0 | raise_error(ShellError::EvaluatedSyntaxError, ByteString::formatted("Expected a bareword (either 'string' or 'list') in the two-argument form of the `{}' immediate", name), mode_arg->position()); |
38 | 0 | return nullptr; |
39 | 0 | } |
40 | | |
41 | 0 | auto const& mode_name = static_cast<const AST::BarewordLiteral&>(*mode_arg).text(); |
42 | 0 | if (mode_name == "list") { |
43 | 0 | mode = List; |
44 | 0 | } else if (mode_name == "string") { |
45 | 0 | mode = String; |
46 | 0 | } else if (mode_name == "infer") { |
47 | 0 | mode = Infer; |
48 | 0 | } else { |
49 | 0 | raise_error(ShellError::EvaluatedSyntaxError, ByteString::formatted("Expected either 'string' or 'list' (and not {}) in the two-argument form of the `{}' immediate", mode_name, name), mode_arg->position()); |
50 | 0 | return nullptr; |
51 | 0 | } |
52 | | |
53 | 0 | expr_node = arguments[1]; |
54 | 0 | } else { |
55 | 0 | expr_node = arguments[0]; |
56 | 0 | } |
57 | | |
58 | 0 | if (mode == Infer) { |
59 | 0 | is_inferred = true; |
60 | 0 | if (expr_node->is_list()) |
61 | 0 | mode = List; |
62 | 0 | else if (expr_node->is_simple_variable()) // "Look inside" variables |
63 | 0 | mode = TRY(TRY(const_cast<AST::Node*>(expr_node)->run(this))->resolve_without_cast(this))->is_list_without_resolution() ? List : String; |
64 | 0 | else if (is<AST::ImmediateExpression>(expr_node)) |
65 | 0 | mode = List; |
66 | 0 | else |
67 | 0 | mode = String; |
68 | 0 | } |
69 | |
|
70 | 0 | auto value_with_number = [&](auto number) -> ErrorOr<NonnullRefPtr<AST::Node>> { |
71 | 0 | return AST::make_ref_counted<AST::BarewordLiteral>(invoking_node.position(), String::number(number)); |
72 | 0 | }; Unexecuted instantiation: ImmediateFunctions.cpp:AK::ErrorOr<AK::NonnullRefPtr<Shell::AST::Node>, AK::Error> Shell::Shell::immediate_length_impl(Shell::AST::ImmediateExpression&, AK::Vector<AK::NonnullRefPtr<Shell::AST::Node>, 0ul> const&, bool)::$_0::operator()<int>(int) const Unexecuted instantiation: ImmediateFunctions.cpp:AK::ErrorOr<AK::NonnullRefPtr<Shell::AST::Node>, AK::Error> Shell::Shell::immediate_length_impl(Shell::AST::ImmediateExpression&, AK::Vector<AK::NonnullRefPtr<Shell::AST::Node>, 0ul> const&, bool)::$_0::operator()<unsigned long>(unsigned long) const |
73 | |
|
74 | 0 | auto do_across = [&](StringView mode_name, auto& values) -> ErrorOr<RefPtr<AST::Node>> { |
75 | 0 | if (is_inferred) |
76 | 0 | mode_name = "infer"sv; |
77 | | // Translate to a list of applications of `length <mode_name>` |
78 | 0 | Vector<NonnullRefPtr<AST::Node>> resulting_nodes; |
79 | 0 | resulting_nodes.ensure_capacity(values.size()); |
80 | 0 | for (auto& entry : values) { |
81 | | // ImmediateExpression(length <mode_name> <entry>) |
82 | 0 | resulting_nodes.unchecked_append(AST::make_ref_counted<AST::ImmediateExpression>( |
83 | 0 | expr_node->position(), |
84 | 0 | AST::NameWithPosition { "length"_string, invoking_node.function_position() }, |
85 | 0 | Vector<NonnullRefPtr<AST::Node>> { Vector<NonnullRefPtr<AST::Node>> { |
86 | 0 | static_cast<NonnullRefPtr<AST::Node>>(AST::make_ref_counted<AST::BarewordLiteral>(expr_node->position(), TRY(String::from_utf8(mode_name)))), |
87 | 0 | AST::make_ref_counted<AST::SyntheticNode>(expr_node->position(), NonnullRefPtr<AST::Value>(entry)), |
88 | 0 | } }, |
89 | 0 | expr_node->position())); |
90 | 0 | } |
91 | |
|
92 | 0 | return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(resulting_nodes)); |
93 | 0 | }; |
94 | |
|
95 | 0 | switch (mode) { |
96 | 0 | default: |
97 | 0 | case Infer: |
98 | 0 | VERIFY_NOT_REACHED(); |
99 | 0 | case List: { |
100 | 0 | auto value = TRY(const_cast<AST::Node*>(expr_node)->run(this)); |
101 | 0 | if (!value) |
102 | 0 | return value_with_number(0); |
103 | | |
104 | 0 | value = TRY(value->resolve_without_cast(this)); |
105 | |
|
106 | 0 | if (auto list = dynamic_cast<AST::ListValue*>(value.ptr())) { |
107 | 0 | if (across) |
108 | 0 | return do_across("list"sv, list->values()); |
109 | | |
110 | 0 | return value_with_number(list->values().size()); |
111 | 0 | } |
112 | | |
113 | 0 | auto list = TRY(value->resolve_as_list(this)); |
114 | 0 | if (!across) |
115 | 0 | return value_with_number(list.size()); |
116 | | |
117 | 0 | dbgln("List has {} entries", list.size()); |
118 | 0 | auto values = AST::make_ref_counted<AST::ListValue>(move(list)); |
119 | 0 | return do_across("list"sv, values->values()); |
120 | 0 | } |
121 | 0 | case String: { |
122 | | // 'across' will only accept lists, and '!across' will only accept non-lists here. |
123 | 0 | if (expr_node->is_list()) { |
124 | 0 | if (!across) { |
125 | 0 | raise_no_list_allowed:; |
126 | 0 | Formatter formatter { *expr_node }; |
127 | |
|
128 | 0 | if (is_inferred) { |
129 | 0 | raise_error(ShellError::EvaluatedSyntaxError, |
130 | 0 | ByteString::formatted("Could not infer expression type, please explicitly use `{0} string' or `{0} list'", name), |
131 | 0 | invoking_node.position()); |
132 | 0 | return nullptr; |
133 | 0 | } |
134 | | |
135 | 0 | auto source = formatter.format(); |
136 | 0 | raise_error(ShellError::EvaluatedSyntaxError, |
137 | 0 | source.is_empty() |
138 | 0 | ? "Invalid application of `length' to a list" |
139 | 0 | : ByteString::formatted("Invalid application of `length' to a list\nperhaps you meant `{1}length \"{0}\"{2}' or `{1}length_across {0}{2}'?", source, "\x1b[32m", "\x1b[0m"), |
140 | 0 | expr_node->position()); |
141 | 0 | return nullptr; |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | 0 | auto value = TRY(const_cast<AST::Node*>(expr_node)->run(this)); |
146 | 0 | if (!value) |
147 | 0 | return value_with_number(0); |
148 | | |
149 | 0 | value = TRY(value->resolve_without_cast(*this)); |
150 | |
|
151 | 0 | if (auto list = dynamic_cast<AST::ListValue*>(value.ptr())) { |
152 | 0 | if (!across) |
153 | 0 | goto raise_no_list_allowed; |
154 | | |
155 | 0 | return do_across("string"sv, list->values()); |
156 | 0 | } |
157 | | |
158 | 0 | if (across && !value->is_list()) { |
159 | 0 | Formatter formatter { *expr_node }; |
160 | |
|
161 | 0 | auto source = formatter.format(); |
162 | 0 | raise_error(ShellError::EvaluatedSyntaxError, |
163 | 0 | ByteString::formatted("Invalid application of `length_across' to a non-list\nperhaps you meant `{1}length {0}{2}'?", source, "\x1b[32m", "\x1b[0m"), |
164 | 0 | expr_node->position()); |
165 | 0 | return nullptr; |
166 | 0 | } |
167 | | |
168 | | // Evaluate the nodes and substitute with the lengths. |
169 | 0 | auto list = TRY(value->resolve_as_list(this)); |
170 | |
|
171 | 0 | if (!expr_node->is_list()) { |
172 | 0 | if (list.size() == 1) { |
173 | 0 | if (across) |
174 | 0 | goto raise_no_list_allowed; |
175 | | |
176 | | // This is the normal case, the expression is a normal non-list expression. |
177 | 0 | return value_with_number(list.first().bytes_as_string_view().length()); |
178 | 0 | } |
179 | | |
180 | | // This can be hit by asking for the length of a command list (e.g. `(>/dev/null)`) |
181 | | // raise an error about misuse of command lists for now. |
182 | | // FIXME: What's the length of `(>/dev/null)` supposed to be? |
183 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Length of meta value (or command list) requested, this is currently not supported.", expr_node->position()); |
184 | 0 | return nullptr; |
185 | 0 | } |
186 | | |
187 | 0 | auto values = AST::make_ref_counted<AST::ListValue>(move(list)); |
188 | 0 | return do_across("string"sv, values->values()); |
189 | 0 | } |
190 | 0 | } |
191 | 0 | } |
192 | | |
193 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_length(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
194 | 0 | { |
195 | 0 | return immediate_length_impl(invoking_node, arguments, false); |
196 | 0 | } |
197 | | |
198 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_length_across(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
199 | 0 | { |
200 | 0 | return immediate_length_impl(invoking_node, arguments, true); |
201 | 0 | } |
202 | | |
203 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_regex_replace(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
204 | 0 | { |
205 | 0 | if (arguments.size() != 3) { |
206 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 3 arguments to regex_replace", invoking_node.position()); |
207 | 0 | return nullptr; |
208 | 0 | } |
209 | | |
210 | 0 | auto pattern = TRY(const_cast<AST::Node&>(*arguments[0]).run(this)); |
211 | 0 | auto replacement = TRY(const_cast<AST::Node&>(*arguments[1]).run(this)); |
212 | 0 | auto value = TRY(TRY(const_cast<AST::Node&>(*arguments[2]).run(this))->resolve_without_cast(this)); |
213 | |
|
214 | 0 | if (!pattern->is_string()) { |
215 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected the regex_replace pattern to be a string", arguments[0]->position()); |
216 | 0 | return nullptr; |
217 | 0 | } |
218 | | |
219 | 0 | if (!replacement->is_string()) { |
220 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected the regex_replace replacement string to be a string", arguments[1]->position()); |
221 | 0 | return nullptr; |
222 | 0 | } |
223 | | |
224 | 0 | if (!value->is_string()) { |
225 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected the regex_replace target value to be a string", arguments[2]->position()); |
226 | 0 | return nullptr; |
227 | 0 | } |
228 | | |
229 | 0 | Regex<PosixExtendedParser> re { TRY(pattern->resolve_as_list(this)).first().to_byte_string() }; |
230 | 0 | auto result = re.replace( |
231 | 0 | TRY(value->resolve_as_list(this))[0], |
232 | 0 | TRY(replacement->resolve_as_list(this))[0], |
233 | 0 | PosixFlags::Global | PosixFlags::Multiline | PosixFlags::Unicode); |
234 | |
|
235 | 0 | return AST::make_ref_counted<AST::StringLiteral>(invoking_node.position(), TRY(String::from_byte_string(result)), AST::StringLiteral::EnclosureType::None); |
236 | 0 | } |
237 | | |
238 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_remove_suffix(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
239 | 0 | { |
240 | 0 | if (arguments.size() != 2) { |
241 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to remove_suffix", invoking_node.position()); |
242 | 0 | return nullptr; |
243 | 0 | } |
244 | | |
245 | 0 | auto suffix = TRY(const_cast<AST::Node&>(*arguments[0]).run(this)); |
246 | 0 | auto value = TRY(TRY(const_cast<AST::Node&>(*arguments[1]).run(this))->resolve_without_cast(this)); |
247 | |
|
248 | 0 | if (!suffix->is_string()) { |
249 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected the remove_suffix suffix string to be a string", arguments[0]->position()); |
250 | 0 | return nullptr; |
251 | 0 | } |
252 | | |
253 | 0 | auto suffix_str = TRY(suffix->resolve_as_list(this))[0]; |
254 | 0 | auto values = TRY(value->resolve_as_list(this)); |
255 | |
|
256 | 0 | Vector<NonnullRefPtr<AST::Node>> nodes; |
257 | |
|
258 | 0 | for (auto& value_str : values) { |
259 | 0 | String removed = value_str; |
260 | |
|
261 | 0 | if (value_str.bytes_as_string_view().ends_with(suffix_str)) |
262 | 0 | removed = TRY(removed.substring_from_byte_offset(0, value_str.bytes_as_string_view().length() - suffix_str.bytes_as_string_view().length())); |
263 | |
|
264 | 0 | nodes.append(AST::make_ref_counted<AST::StringLiteral>(invoking_node.position(), move(removed), AST::StringLiteral::EnclosureType::None)); |
265 | 0 | } |
266 | |
|
267 | 0 | return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(nodes)); |
268 | 0 | } |
269 | | |
270 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_remove_prefix(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
271 | 0 | { |
272 | 0 | if (arguments.size() != 2) { |
273 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to remove_prefix", invoking_node.position()); |
274 | 0 | return nullptr; |
275 | 0 | } |
276 | | |
277 | 0 | auto prefix = TRY(const_cast<AST::Node&>(*arguments[0]).run(this)); |
278 | 0 | auto value = TRY(TRY(const_cast<AST::Node&>(*arguments[1]).run(this))->resolve_without_cast(this)); |
279 | |
|
280 | 0 | if (!prefix->is_string()) { |
281 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected the remove_prefix prefix string to be a string", arguments[0]->position()); |
282 | 0 | return nullptr; |
283 | 0 | } |
284 | | |
285 | 0 | auto prefix_str = TRY(prefix->resolve_as_list(this))[0]; |
286 | 0 | auto values = TRY(value->resolve_as_list(this)); |
287 | |
|
288 | 0 | Vector<NonnullRefPtr<AST::Node>> nodes; |
289 | |
|
290 | 0 | for (auto& value_str : values) { |
291 | 0 | String removed = value_str; |
292 | |
|
293 | 0 | if (value_str.bytes_as_string_view().starts_with(prefix_str)) |
294 | 0 | removed = TRY(removed.substring_from_byte_offset(prefix_str.bytes_as_string_view().length())); |
295 | 0 | nodes.append(AST::make_ref_counted<AST::StringLiteral>(invoking_node.position(), move(removed), AST::StringLiteral::EnclosureType::None)); |
296 | 0 | } |
297 | |
|
298 | 0 | return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(nodes)); |
299 | 0 | } |
300 | | |
301 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_split(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
302 | 0 | { |
303 | 0 | if (arguments.size() != 2) { |
304 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to split", invoking_node.position()); |
305 | 0 | return nullptr; |
306 | 0 | } |
307 | | |
308 | 0 | auto delimiter = TRY(const_cast<AST::Node&>(*arguments[0]).run(this)); |
309 | 0 | auto value = TRY(TRY(const_cast<AST::Node&>(*arguments[1]).run(this))->resolve_without_cast(this)); |
310 | |
|
311 | 0 | if (!delimiter->is_string()) { |
312 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected the split delimiter string to be a string", arguments[0]->position()); |
313 | 0 | return nullptr; |
314 | 0 | } |
315 | | |
316 | 0 | auto delimiter_str = TRY(delimiter->resolve_as_list(this))[0]; |
317 | |
|
318 | 0 | auto transform = [&](auto const& values) { |
319 | | // Translate to a list of applications of `split <delimiter>` |
320 | 0 | Vector<NonnullRefPtr<AST::Node>> resulting_nodes; |
321 | 0 | resulting_nodes.ensure_capacity(values.size()); |
322 | 0 | for (auto& entry : values) { |
323 | | // ImmediateExpression(split <delimiter> <entry>) |
324 | 0 | resulting_nodes.unchecked_append(AST::make_ref_counted<AST::ImmediateExpression>( |
325 | 0 | arguments[1]->position(), |
326 | 0 | invoking_node.function(), |
327 | 0 | Vector<NonnullRefPtr<AST::Node>> { Vector<NonnullRefPtr<AST::Node>> { |
328 | 0 | arguments[0], |
329 | 0 | AST::make_ref_counted<AST::SyntheticNode>(arguments[1]->position(), NonnullRefPtr<AST::Value>(entry)), |
330 | 0 | } }, |
331 | 0 | arguments[1]->position())); |
332 | 0 | } |
333 | |
|
334 | 0 | return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(resulting_nodes)); |
335 | 0 | }; |
336 | |
|
337 | 0 | if (auto list = dynamic_cast<AST::ListValue*>(value.ptr())) { |
338 | 0 | return transform(list->values()); |
339 | 0 | } |
340 | | |
341 | | // Otherwise, just resolve to a list and transform that. |
342 | 0 | auto list = TRY(value->resolve_as_list(this)); |
343 | 0 | if (!value->is_list()) { |
344 | 0 | if (list.is_empty()) |
345 | 0 | return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), Vector<NonnullRefPtr<AST::Node>> {}); |
346 | | |
347 | 0 | auto& value = list.first(); |
348 | 0 | Vector<String> split_strings; |
349 | 0 | if (delimiter_str.is_empty()) { |
350 | 0 | StringBuilder builder; |
351 | 0 | for (auto code_point : Utf8View { value }) { |
352 | 0 | builder.append_code_point(code_point); |
353 | 0 | split_strings.append(TRY(builder.to_string())); |
354 | 0 | builder.clear(); |
355 | 0 | } |
356 | 0 | } else { |
357 | 0 | auto split = StringView { value }.split_view(delimiter_str, options.inline_exec_keep_empty_segments ? SplitBehavior::KeepEmpty : SplitBehavior::Nothing); |
358 | 0 | split_strings.ensure_capacity(split.size()); |
359 | 0 | for (auto& entry : split) |
360 | 0 | split_strings.append(TRY(String::from_utf8(entry))); |
361 | 0 | } |
362 | 0 | return AST::make_ref_counted<AST::SyntheticNode>(invoking_node.position(), AST::make_ref_counted<AST::ListValue>(move(split_strings))); |
363 | 0 | } |
364 | | |
365 | 0 | return transform(AST::make_ref_counted<AST::ListValue>(list)->values()); |
366 | 0 | } |
367 | | |
368 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_concat_lists(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
369 | 0 | { |
370 | 0 | Vector<NonnullRefPtr<AST::Node>> result; |
371 | |
|
372 | 0 | for (auto& argument : arguments) { |
373 | 0 | if (auto* list = dynamic_cast<AST::ListConcatenate const*>(argument.ptr())) { |
374 | 0 | result.extend(list->list()); |
375 | 0 | } else { |
376 | 0 | auto list_of_values = TRY(TRY(const_cast<AST::Node&>(*argument).run(this))->resolve_without_cast(this)); |
377 | 0 | if (auto* list = dynamic_cast<AST::ListValue*>(list_of_values.ptr())) { |
378 | 0 | for (auto& entry : static_cast<Vector<NonnullRefPtr<AST::Value>>&>(list->values())) |
379 | 0 | result.append(AST::make_ref_counted<AST::SyntheticNode>(argument->position(), entry)); |
380 | 0 | } else { |
381 | 0 | auto values = TRY(list_of_values->resolve_as_list(this)); |
382 | 0 | for (auto& entry : values) |
383 | 0 | result.append(AST::make_ref_counted<AST::StringLiteral>(argument->position(), entry, AST::StringLiteral::EnclosureType::None)); |
384 | 0 | } |
385 | 0 | } |
386 | 0 | } |
387 | |
|
388 | 0 | return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(result)); |
389 | 0 | } |
390 | | |
391 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_filter_glob(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
392 | 0 | { |
393 | | // filter_glob string list |
394 | 0 | if (arguments.size() != 2) { |
395 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly two arguments to filter_glob (<glob> <list>)", invoking_node.position()); |
396 | 0 | return nullptr; |
397 | 0 | } |
398 | | |
399 | 0 | auto glob_list = TRY(TRY(const_cast<AST::Node&>(*arguments[0]).run(*this))->resolve_as_list(*this)); |
400 | 0 | if (glob_list.size() != 1) { |
401 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected the <glob> argument to filter_glob to be a single string", arguments[0]->position()); |
402 | 0 | return nullptr; |
403 | 0 | } |
404 | 0 | auto& glob = glob_list.first(); |
405 | 0 | auto& list_node = arguments[1]; |
406 | |
|
407 | 0 | Vector<NonnullRefPtr<AST::Node>> result; |
408 | |
|
409 | 0 | TRY(const_cast<AST::Node&>(*list_node).for_each_entry(*this, [&](NonnullRefPtr<AST::Value> entry) -> ErrorOr<IterationDecision> { |
410 | 0 | auto value = TRY(entry->resolve_as_list(*this)); |
411 | 0 | if (value.size() == 0) |
412 | 0 | return IterationDecision::Continue; |
413 | 0 | if (value.size() == 1) { |
414 | 0 | if (!value.first().bytes_as_string_view().matches(glob)) |
415 | 0 | return IterationDecision::Continue; |
416 | 0 | result.append(AST::make_ref_counted<AST::StringLiteral>(arguments[1]->position(), value.first(), AST::StringLiteral::EnclosureType::None)); |
417 | 0 | return IterationDecision::Continue; |
418 | 0 | } |
419 | |
|
420 | 0 | for (auto& entry : value) { |
421 | 0 | if (entry.bytes_as_string_view().matches(glob)) { |
422 | 0 | Vector<NonnullRefPtr<AST::Node>> nodes; |
423 | 0 | for (auto& string : value) |
424 | 0 | nodes.append(AST::make_ref_counted<AST::StringLiteral>(arguments[1]->position(), string, AST::StringLiteral::EnclosureType::None)); |
425 | 0 | result.append(AST::make_ref_counted<AST::ListConcatenate>(arguments[1]->position(), move(nodes))); |
426 | 0 | return IterationDecision::Continue; |
427 | 0 | } |
428 | 0 | } |
429 | 0 | return IterationDecision::Continue; |
430 | 0 | })); |
431 | |
|
432 | 0 | return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(result)); |
433 | 0 | } |
434 | | |
435 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_join(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
436 | 0 | { |
437 | 0 | if (arguments.size() != 2) { |
438 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to join", invoking_node.position()); |
439 | 0 | return nullptr; |
440 | 0 | } |
441 | | |
442 | 0 | auto delimiter = TRY(const_cast<AST::Node&>(*arguments[0]).run(this)); |
443 | 0 | if (!delimiter->is_string()) { |
444 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected the join delimiter string to be a string", arguments[0]->position()); |
445 | 0 | return nullptr; |
446 | 0 | } |
447 | | |
448 | 0 | auto value = TRY(TRY(const_cast<AST::Node&>(*arguments[1]).run(this))->resolve_without_cast(this)); |
449 | 0 | if (!value->is_list()) { |
450 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected the joined list to be a list", arguments[1]->position()); |
451 | 0 | return nullptr; |
452 | 0 | } |
453 | | |
454 | 0 | auto delimiter_str = TRY(delimiter->resolve_as_list(this))[0]; |
455 | 0 | StringBuilder builder; |
456 | 0 | builder.join(delimiter_str, TRY(value->resolve_as_list(*this))); |
457 | |
|
458 | 0 | return AST::make_ref_counted<AST::StringLiteral>(invoking_node.position(), TRY(builder.to_string()), AST::StringLiteral::EnclosureType::None); |
459 | 0 | } |
460 | | |
461 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_value_or_default(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
462 | 0 | { |
463 | 0 | if (arguments.size() != 2) { |
464 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to value_or_default", invoking_node.position()); |
465 | 0 | return nullptr; |
466 | 0 | } |
467 | | |
468 | 0 | auto name = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_string(*this)); |
469 | 0 | if (!TRY(local_variable_or(name, ""sv)).is_empty()) |
470 | 0 | return make_ref_counted<AST::SimpleVariable>(invoking_node.position(), name); |
471 | | |
472 | 0 | return arguments.last(); |
473 | 0 | } |
474 | | |
475 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_assign_default(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
476 | 0 | { |
477 | 0 | if (arguments.size() != 2) { |
478 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to assign_default", invoking_node.position()); |
479 | 0 | return nullptr; |
480 | 0 | } |
481 | | |
482 | 0 | auto name = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_string(*this)); |
483 | 0 | if (!TRY(local_variable_or(name, ""sv)).is_empty()) |
484 | 0 | return make_ref_counted<AST::SimpleVariable>(invoking_node.position(), name); |
485 | | |
486 | 0 | auto value = TRY(TRY(const_cast<AST::Node&>(*arguments.last()).run(*this))->resolve_without_cast(*this)); |
487 | 0 | set_local_variable(name.to_byte_string(), value); |
488 | |
|
489 | 0 | return make_ref_counted<AST::SyntheticNode>(invoking_node.position(), value); |
490 | 0 | } |
491 | | |
492 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_error_if_empty(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
493 | 0 | { |
494 | 0 | if (arguments.size() != 2) { |
495 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to error_if_empty", invoking_node.position()); |
496 | 0 | return nullptr; |
497 | 0 | } |
498 | | |
499 | 0 | auto name = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_string(*this)); |
500 | 0 | if (!TRY(local_variable_or(name, ""sv)).is_empty()) |
501 | 0 | return make_ref_counted<AST::SimpleVariable>(invoking_node.position(), name); |
502 | | |
503 | 0 | auto error_value = TRY(TRY(const_cast<AST::Node&>(*arguments.last()).run(*this))->resolve_as_string(*this)); |
504 | 0 | if (error_value.is_empty()) |
505 | 0 | error_value = TRY(String::formatted("Expected {} to be non-empty", name)); |
506 | |
|
507 | 0 | raise_error(ShellError::EvaluatedSyntaxError, error_value.bytes_as_string_view(), invoking_node.position()); |
508 | 0 | return nullptr; |
509 | 0 | } |
510 | | |
511 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_null_or_alternative(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
512 | 0 | { |
513 | 0 | if (arguments.size() != 2) { |
514 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to null_or_alternative", invoking_node.position()); |
515 | 0 | return nullptr; |
516 | 0 | } |
517 | | |
518 | 0 | auto name = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_string(*this)); |
519 | 0 | auto frame = find_frame_containing_local_variable(name); |
520 | 0 | if (!frame) |
521 | 0 | return make_ref_counted<AST::StringLiteral>(invoking_node.position(), ""_string, AST::StringLiteral::EnclosureType::None); |
522 | | |
523 | 0 | auto value = frame->local_variables.get(name.bytes_as_string_view()).value(); |
524 | 0 | if ((value->is_string() && TRY(value->resolve_as_string(*this)).is_empty()) || (value->is_list() && TRY(value->resolve_as_list(*this)).is_empty())) |
525 | 0 | return make_ref_counted<AST::SyntheticNode>(invoking_node.position(), *value); |
526 | | |
527 | 0 | return arguments.last(); |
528 | 0 | } |
529 | | |
530 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_defined_value_or_default(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
531 | 0 | { |
532 | 0 | if (arguments.size() != 2) { |
533 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to defined_value_or_default", invoking_node.position()); |
534 | 0 | return nullptr; |
535 | 0 | } |
536 | | |
537 | 0 | auto name = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_string(*this)); |
538 | 0 | if (!find_frame_containing_local_variable(name)) |
539 | 0 | return arguments.last(); |
540 | | |
541 | 0 | return make_ref_counted<AST::SimpleVariable>(invoking_node.position(), name); |
542 | 0 | } |
543 | | |
544 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_assign_defined_default(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
545 | 0 | { |
546 | 0 | if (arguments.size() != 2) { |
547 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to assign_defined_default", invoking_node.position()); |
548 | 0 | return nullptr; |
549 | 0 | } |
550 | | |
551 | 0 | auto name = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_string(*this)); |
552 | 0 | if (find_frame_containing_local_variable(name)) |
553 | 0 | return make_ref_counted<AST::SimpleVariable>(invoking_node.position(), name); |
554 | | |
555 | 0 | auto value = TRY(TRY(const_cast<AST::Node&>(*arguments.last()).run(*this))->resolve_without_cast(*this)); |
556 | 0 | set_local_variable(name.to_byte_string(), value); |
557 | |
|
558 | 0 | return make_ref_counted<AST::SyntheticNode>(invoking_node.position(), value); |
559 | 0 | } |
560 | | |
561 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_error_if_unset(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
562 | 0 | { |
563 | 0 | if (arguments.size() != 2) { |
564 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to error_if_unset", invoking_node.position()); |
565 | 0 | return nullptr; |
566 | 0 | } |
567 | | |
568 | 0 | auto name = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_string(*this)); |
569 | 0 | if (find_frame_containing_local_variable(name)) |
570 | 0 | return make_ref_counted<AST::SimpleVariable>(invoking_node.position(), name); |
571 | | |
572 | 0 | auto error_value = TRY(TRY(const_cast<AST::Node&>(*arguments.last()).run(*this))->resolve_as_string(*this)); |
573 | 0 | if (error_value.is_empty()) |
574 | 0 | error_value = TRY(String::formatted("Expected {} to be set", name)); |
575 | |
|
576 | 0 | raise_error(ShellError::EvaluatedSyntaxError, error_value.bytes_as_string_view(), invoking_node.position()); |
577 | 0 | return nullptr; |
578 | 0 | } |
579 | | |
580 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_null_if_unset_or_alternative(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
581 | 0 | { |
582 | 0 | if (arguments.size() != 2) { |
583 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 2 arguments to null_if_unset_or_alternative", invoking_node.position()); |
584 | 0 | return nullptr; |
585 | 0 | } |
586 | | |
587 | 0 | auto name = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_string(*this)); |
588 | 0 | if (find_frame_containing_local_variable(name)) |
589 | 0 | return arguments.last(); |
590 | | |
591 | 0 | return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), Vector<NonnullRefPtr<AST::Node>>()); |
592 | 0 | } |
593 | | |
594 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_reexpand(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
595 | 0 | { |
596 | 0 | if (arguments.size() != 1) { |
597 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 1 argument to reexpand", invoking_node.position()); |
598 | 0 | return nullptr; |
599 | 0 | } |
600 | | |
601 | 0 | auto values = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_list(*this)); |
602 | 0 | auto result = Vector<NonnullRefPtr<AST::Node>> {}; |
603 | 0 | for (auto& value : values) { |
604 | 0 | if (auto node = parse(value, m_is_interactive, false)) |
605 | 0 | result.append(node.release_nonnull()); |
606 | 0 | } |
607 | |
|
608 | 0 | if (values.size() == 1) |
609 | 0 | return result.take_first(); |
610 | | |
611 | 0 | return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(result)); |
612 | 0 | } |
613 | | |
614 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_length_of_variable(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
615 | 0 | { |
616 | 0 | if (arguments.size() != 1) { |
617 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 1 argument to length_of_variable", invoking_node.position()); |
618 | 0 | return nullptr; |
619 | 0 | } |
620 | | |
621 | 0 | auto name = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_string(*this)); |
622 | 0 | auto variable = make_ref_counted<AST::SimpleVariable>(invoking_node.position(), name); |
623 | |
|
624 | 0 | return immediate_length_impl( |
625 | 0 | invoking_node, |
626 | 0 | { move(variable) }, |
627 | 0 | false); |
628 | 0 | } |
629 | | |
630 | | namespace Arithmetic { |
631 | | struct BinaryOperationNode; |
632 | | struct UnaryOperationNode; |
633 | | struct TernaryOperationNode; |
634 | | struct ErrorNode; |
635 | | |
636 | | struct Node { |
637 | | Variant<String, i64, NonnullOwnPtr<BinaryOperationNode>, NonnullOwnPtr<UnaryOperationNode>, NonnullOwnPtr<TernaryOperationNode>, NonnullOwnPtr<ErrorNode>> value; |
638 | | }; |
639 | | |
640 | | struct ErrorNode { |
641 | | String error; |
642 | | }; |
643 | | |
644 | | enum class Operator { |
645 | | Add, // + |
646 | | Subtract, // - |
647 | | Multiply, // * |
648 | | Quotient, // / |
649 | | Remainder, // % |
650 | | Power, // ** |
651 | | Equal, // == |
652 | | GreaterThan, // > |
653 | | LessThan, // < |
654 | | NotEqual, // != |
655 | | GreaterThanOrEqual, // >= |
656 | | LessThanOrEqual, // <= |
657 | | BitwiseAnd, // & |
658 | | BitwiseOr, // | |
659 | | BitwiseXor, // ^ |
660 | | ShiftLeft, // << |
661 | | ShiftRight, // >> |
662 | | ArithmeticAnd, // && |
663 | | ArithmeticOr, // || |
664 | | Comma, // , |
665 | | Negate, // ! |
666 | | BitwiseNegate, // ~ |
667 | | TernaryQuestion, // ? |
668 | | TernaryColon, // : |
669 | | Assignment, // = |
670 | | PlusAssignment, // += |
671 | | MinusAssignment, // -= |
672 | | MultiplyAssignment, // *= |
673 | | DivideAssignment, // /= |
674 | | ModuloAssignment, // %= |
675 | | AndAssignment, // &= |
676 | | OrAssignment, // |= |
677 | | XorAssignment, // ^= |
678 | | LeftShiftAssignment, // <<= |
679 | | RightShiftAssignment, // >>= |
680 | | |
681 | | OpenParen, // ( |
682 | | CloseParen, // ) |
683 | | }; |
684 | | |
685 | | static Operator assignment_operation_of(Operator op) |
686 | 0 | { |
687 | 0 | switch (op) { |
688 | 0 | case Operator::PlusAssignment: |
689 | 0 | return Operator::Add; |
690 | 0 | case Operator::MinusAssignment: |
691 | 0 | return Operator::Subtract; |
692 | 0 | case Operator::MultiplyAssignment: |
693 | 0 | return Operator::Multiply; |
694 | 0 | case Operator::DivideAssignment: |
695 | 0 | return Operator::Quotient; |
696 | 0 | case Operator::ModuloAssignment: |
697 | 0 | return Operator::Remainder; |
698 | 0 | case Operator::AndAssignment: |
699 | 0 | return Operator::BitwiseAnd; |
700 | 0 | case Operator::OrAssignment: |
701 | 0 | return Operator::BitwiseOr; |
702 | 0 | case Operator::XorAssignment: |
703 | 0 | return Operator::BitwiseXor; |
704 | 0 | case Operator::LeftShiftAssignment: |
705 | 0 | return Operator::ShiftLeft; |
706 | 0 | case Operator::RightShiftAssignment: |
707 | 0 | return Operator::ShiftRight; |
708 | 0 | default: |
709 | 0 | VERIFY_NOT_REACHED(); |
710 | 0 | } |
711 | 0 | } |
712 | | |
713 | | static bool is_assignment_operator(Operator op) |
714 | 0 | { |
715 | 0 | switch (op) { |
716 | 0 | case Operator::Assignment: |
717 | 0 | case Operator::PlusAssignment: |
718 | 0 | case Operator::MinusAssignment: |
719 | 0 | case Operator::MultiplyAssignment: |
720 | 0 | case Operator::DivideAssignment: |
721 | 0 | case Operator::ModuloAssignment: |
722 | 0 | case Operator::AndAssignment: |
723 | 0 | case Operator::OrAssignment: |
724 | 0 | case Operator::XorAssignment: |
725 | 0 | case Operator::LeftShiftAssignment: |
726 | 0 | case Operator::RightShiftAssignment: |
727 | 0 | return true; |
728 | 0 | default: |
729 | 0 | return false; |
730 | 0 | } |
731 | 0 | } |
732 | | |
733 | | using Token = Variant<String, i64, Operator>; |
734 | | |
735 | | struct BinaryOperationNode { |
736 | | BinaryOperationNode(Operator op, Node lhs, Node rhs) |
737 | 0 | : op(op) |
738 | 0 | , lhs(move(lhs)) |
739 | 0 | , rhs(move(rhs)) |
740 | 0 | { |
741 | 0 | } |
742 | | |
743 | | Operator op; |
744 | | Node lhs; |
745 | | Node rhs; |
746 | | }; |
747 | | |
748 | | struct UnaryOperationNode { |
749 | | UnaryOperationNode(Operator op, Node rhs) |
750 | 0 | : op(op) |
751 | 0 | , rhs(move(rhs)) |
752 | 0 | { |
753 | 0 | } |
754 | | |
755 | | Operator op; |
756 | | Node rhs; |
757 | | }; |
758 | | |
759 | | struct TernaryOperationNode { |
760 | | TernaryOperationNode(Node condition, Node true_value, Node false_value) |
761 | 0 | : condition(move(condition)) |
762 | 0 | , true_value(move(true_value)) |
763 | 0 | , false_value(move(false_value)) |
764 | 0 | { |
765 | 0 | } |
766 | | |
767 | | Node condition; |
768 | | Node true_value; |
769 | | Node false_value; |
770 | | }; |
771 | | |
772 | | static ErrorOr<Node> parse_expression(Span<Token>); |
773 | | static ErrorOr<Node> parse_assignment_expression(Span<Token>&); |
774 | | static ErrorOr<Node> parse_comma_expression(Span<Token>&); |
775 | | static ErrorOr<Node> parse_ternary_expression(Span<Token>&); |
776 | | static ErrorOr<Node> parse_logical_or_expression(Span<Token>&); |
777 | | static ErrorOr<Node> parse_logical_and_expression(Span<Token>&); |
778 | | static ErrorOr<Node> parse_bitwise_or_expression(Span<Token>&); |
779 | | static ErrorOr<Node> parse_bitwise_xor_expression(Span<Token>&); |
780 | | static ErrorOr<Node> parse_bitwise_and_expression(Span<Token>&); |
781 | | static ErrorOr<Node> parse_equality_expression(Span<Token>&); |
782 | | static ErrorOr<Node> parse_comparison_expression(Span<Token>&); |
783 | | static ErrorOr<Node> parse_shift_expression(Span<Token>&); |
784 | | static ErrorOr<Node> parse_additive_expression(Span<Token>&); |
785 | | static ErrorOr<Node> parse_multiplicative_expression(Span<Token>&); |
786 | | static ErrorOr<Node> parse_exponential_expression(Span<Token>&); |
787 | | static ErrorOr<Node> parse_unary_expression(Span<Token>&); |
788 | | static ErrorOr<Node> parse_primary_expression(Span<Token>&); |
789 | | template<size_t N> |
790 | | static ErrorOr<Node> parse_binary_expression_using_operators(Span<Token>&, Array<Operator, N>, Function<ErrorOr<Node>(Span<Token>&)> const& parse_rhs); |
791 | | static ErrorOr<Node> parse_binary_expression_using_operator(Span<Token>& tokens, Operator op, Function<ErrorOr<Node>(Span<Token>&)> const& parse_rhs) |
792 | 0 | { |
793 | 0 | return parse_binary_expression_using_operators(tokens, Array { op }, parse_rhs); |
794 | 0 | } |
795 | | |
796 | | static bool next_token_is_operator(Span<Token>& tokens, Operator op) |
797 | 0 | { |
798 | 0 | if (tokens.is_empty()) |
799 | 0 | return false; |
800 | 0 | return tokens.first().has<Operator>() && tokens.first().get<Operator>() == op; |
801 | 0 | } |
802 | | |
803 | | ErrorOr<Node> parse_expression(Span<Token> tokens) |
804 | 0 | { |
805 | 0 | return parse_comma_expression(tokens); |
806 | 0 | } |
807 | | |
808 | | ErrorOr<Node> parse_comma_expression(Span<Token>& tokens) |
809 | 0 | { |
810 | 0 | return parse_binary_expression_using_operator(tokens, Operator::Comma, &parse_assignment_expression); |
811 | 0 | } |
812 | | |
813 | | ErrorOr<Node> parse_assignment_expression(Span<Token>& tokens) |
814 | 0 | { |
815 | 0 | auto lhs = TRY(parse_ternary_expression(tokens)); |
816 | 0 | if (tokens.is_empty()) |
817 | 0 | return lhs; |
818 | | |
819 | 0 | auto is_assignment_operator = [](Operator op) { |
820 | 0 | return op == Operator::Assignment |
821 | 0 | || op == Operator::PlusAssignment |
822 | 0 | || op == Operator::MinusAssignment |
823 | 0 | || op == Operator::MultiplyAssignment |
824 | 0 | || op == Operator::DivideAssignment |
825 | 0 | || op == Operator::ModuloAssignment |
826 | 0 | || op == Operator::AndAssignment |
827 | 0 | || op == Operator::OrAssignment |
828 | 0 | || op == Operator::XorAssignment |
829 | 0 | || op == Operator::LeftShiftAssignment |
830 | 0 | || op == Operator::RightShiftAssignment; |
831 | 0 | }; |
832 | |
|
833 | 0 | auto& token = tokens.first(); |
834 | 0 | if (auto op = token.get_pointer<Operator>(); op && is_assignment_operator(*op)) { |
835 | 0 | if (!lhs.value.has<String>()) { |
836 | 0 | return Node { |
837 | 0 | make<ErrorNode>("Left-hand side of assignment must be a variable"_string) |
838 | 0 | }; |
839 | 0 | } |
840 | | |
841 | 0 | tokens = tokens.slice(1); |
842 | 0 | auto rhs = TRY(parse_assignment_expression(tokens)); |
843 | 0 | return Node { |
844 | 0 | make<BinaryOperationNode>(*op, move(lhs), move(rhs)) |
845 | 0 | }; |
846 | 0 | } |
847 | | |
848 | 0 | return lhs; |
849 | 0 | } |
850 | | |
851 | | ErrorOr<Node> parse_ternary_expression(Span<Token>& tokens) |
852 | 0 | { |
853 | 0 | auto condition = TRY(parse_logical_or_expression(tokens)); |
854 | 0 | if (!next_token_is_operator(tokens, Operator::TernaryQuestion)) |
855 | 0 | return condition; |
856 | | |
857 | 0 | tokens = tokens.slice(1); |
858 | |
|
859 | 0 | auto true_value = TRY(parse_comma_expression(tokens)); |
860 | |
|
861 | 0 | if (!next_token_is_operator(tokens, Operator::TernaryColon)) { |
862 | 0 | return Node { |
863 | 0 | make<ErrorNode>("Expected ':' after true value in ternary expression"_string) |
864 | 0 | }; |
865 | 0 | } |
866 | | |
867 | 0 | tokens = tokens.slice(1); |
868 | |
|
869 | 0 | auto false_value = TRY(parse_ternary_expression(tokens)); |
870 | |
|
871 | 0 | return Node { |
872 | 0 | make<TernaryOperationNode>(move(condition), move(true_value), move(false_value)) |
873 | 0 | }; |
874 | 0 | } |
875 | | |
876 | | ErrorOr<Node> parse_logical_or_expression(Span<Token>& tokens) |
877 | 0 | { |
878 | 0 | return parse_binary_expression_using_operator(tokens, Operator::ArithmeticOr, &parse_logical_and_expression); |
879 | 0 | } |
880 | | |
881 | | ErrorOr<Node> parse_logical_and_expression(Span<Token>& tokens) |
882 | 0 | { |
883 | 0 | return parse_binary_expression_using_operator(tokens, Operator::ArithmeticAnd, &parse_bitwise_or_expression); |
884 | 0 | } |
885 | | |
886 | | ErrorOr<Node> parse_bitwise_or_expression(Span<Token>& tokens) |
887 | 0 | { |
888 | 0 | return parse_binary_expression_using_operator(tokens, Operator::BitwiseOr, &parse_bitwise_xor_expression); |
889 | 0 | } |
890 | | |
891 | | ErrorOr<Node> parse_bitwise_xor_expression(Span<Token>& tokens) |
892 | 0 | { |
893 | 0 | return parse_binary_expression_using_operator(tokens, Operator::BitwiseXor, &parse_bitwise_and_expression); |
894 | 0 | } |
895 | | |
896 | | ErrorOr<Node> parse_bitwise_and_expression(Span<Token>& tokens) |
897 | 0 | { |
898 | 0 | return parse_binary_expression_using_operator(tokens, Operator::BitwiseAnd, &parse_equality_expression); |
899 | 0 | } |
900 | | |
901 | | ErrorOr<Node> parse_equality_expression(Span<Token>& tokens) |
902 | 0 | { |
903 | 0 | return parse_binary_expression_using_operators(tokens, Array { Operator::Equal, Operator::NotEqual }, &parse_comparison_expression); |
904 | 0 | } |
905 | | |
906 | | ErrorOr<Node> parse_comparison_expression(Span<Token>& tokens) |
907 | 0 | { |
908 | 0 | return parse_binary_expression_using_operators(tokens, Array { Operator::LessThan, Operator::GreaterThan, Operator::LessThanOrEqual, Operator::GreaterThanOrEqual }, &parse_shift_expression); |
909 | 0 | } |
910 | | |
911 | | ErrorOr<Node> parse_shift_expression(Span<Token>& tokens) |
912 | 0 | { |
913 | 0 | return parse_binary_expression_using_operators(tokens, Array { Operator::ShiftLeft, Operator::ShiftRight }, &parse_additive_expression); |
914 | 0 | } |
915 | | |
916 | | ErrorOr<Node> parse_additive_expression(Span<Token>& tokens) |
917 | 0 | { |
918 | 0 | return parse_binary_expression_using_operators(tokens, Array { Operator::Add, Operator::Subtract }, &parse_multiplicative_expression); |
919 | 0 | } |
920 | | |
921 | | ErrorOr<Node> parse_multiplicative_expression(Span<Token>& tokens) |
922 | 0 | { |
923 | 0 | return parse_binary_expression_using_operators(tokens, Array { Operator::Multiply, Operator::Quotient, Operator::Remainder }, &parse_exponential_expression); |
924 | 0 | } |
925 | | |
926 | | ErrorOr<Node> parse_exponential_expression(Span<Token>& tokens) |
927 | 0 | { |
928 | 0 | auto lhs = TRY(parse_unary_expression(tokens)); |
929 | 0 | if (!next_token_is_operator(tokens, Operator::Power)) |
930 | 0 | return lhs; |
931 | | |
932 | 0 | tokens = tokens.slice(1); |
933 | 0 | auto rhs = TRY(parse_exponential_expression(tokens)); |
934 | |
|
935 | 0 | return Node { |
936 | 0 | make<BinaryOperationNode>(Operator::Power, move(lhs), move(rhs)) |
937 | 0 | }; |
938 | 0 | } |
939 | | |
940 | | ErrorOr<Node> parse_unary_expression(Span<Token>& tokens) |
941 | 0 | { |
942 | 0 | if (tokens.is_empty()) { |
943 | 0 | return Node { |
944 | 0 | make<ErrorNode>("Expected expression, got end of input"_string) |
945 | 0 | }; |
946 | 0 | } |
947 | | |
948 | 0 | auto& token = tokens.first(); |
949 | 0 | if (auto op = token.get_pointer<Operator>()) { |
950 | 0 | if (*op == Operator::Add || *op == Operator::Subtract || *op == Operator::Negate || *op == Operator::BitwiseNegate) { |
951 | 0 | tokens = tokens.slice(1); |
952 | 0 | auto rhs = TRY(parse_unary_expression(tokens)); |
953 | 0 | return Node { |
954 | 0 | make<UnaryOperationNode>(*op, move(rhs)) |
955 | 0 | }; |
956 | 0 | } |
957 | 0 | } |
958 | | |
959 | 0 | return parse_primary_expression(tokens); |
960 | 0 | } |
961 | | |
962 | | ErrorOr<Node> parse_primary_expression(Span<Token>& tokens) |
963 | 0 | { |
964 | 0 | if (tokens.is_empty()) |
965 | 0 | return Node { make<ErrorNode>("Expected expression, got end of input"_string) }; |
966 | | |
967 | 0 | auto& token = tokens.first(); |
968 | 0 | return token.visit( |
969 | 0 | [&](String const& var) -> ErrorOr<Node> { |
970 | 0 | tokens = tokens.slice(1); |
971 | 0 | return Node { var }; |
972 | 0 | }, |
973 | 0 | [&](i64 value) -> ErrorOr<Node> { |
974 | 0 | tokens = tokens.slice(1); |
975 | 0 | return Node { value }; |
976 | 0 | }, |
977 | 0 | [&](Operator op) -> ErrorOr<Node> { |
978 | 0 | switch (op) { |
979 | 0 | case Operator::OpenParen: { |
980 | 0 | tokens = tokens.slice(1); |
981 | 0 | auto value = TRY(parse_expression(tokens)); |
982 | 0 | if (!next_token_is_operator(tokens, Operator::CloseParen)) { |
983 | 0 | return Node { |
984 | 0 | make<ErrorNode>("Expected ')' after expression in parentheses"_string) |
985 | 0 | }; |
986 | 0 | } |
987 | 0 | tokens = tokens.slice(1); |
988 | 0 | return value; |
989 | 0 | } |
990 | 0 | default: |
991 | 0 | return Node { |
992 | 0 | make<ErrorNode>("Expected expression, got operator"_string) |
993 | 0 | }; |
994 | 0 | } |
995 | 0 | }); |
996 | 0 | } |
997 | | |
998 | | template<size_t N> |
999 | | ErrorOr<Node> parse_binary_expression_using_operators(Span<Token>& tokens, Array<Operator, N> operators, Function<ErrorOr<Node>(Span<Token>&)> const& parse_rhs) |
1000 | 0 | { |
1001 | 0 | auto lhs = TRY(parse_rhs(tokens)); |
1002 | 0 | for (;;) { |
1003 | 0 | Optional<Operator> op; |
1004 | 0 | for (auto candidate : operators) { |
1005 | 0 | if (next_token_is_operator(tokens, candidate)) { |
1006 | 0 | op = candidate; |
1007 | 0 | break; |
1008 | 0 | } |
1009 | 0 | } |
1010 | |
|
1011 | 0 | if (!op.has_value()) |
1012 | 0 | return lhs; |
1013 | | |
1014 | 0 | tokens = tokens.slice(1); |
1015 | 0 | auto rhs = TRY(parse_rhs(tokens)); |
1016 | 0 | lhs = Node { |
1017 | 0 | make<BinaryOperationNode>(*op, move(lhs), move(rhs)) |
1018 | 0 | }; |
1019 | 0 | } |
1020 | 0 | } Unexecuted instantiation: ImmediateFunctions.cpp:AK::ErrorOr<Shell::Arithmetic::Node, AK::Error> Shell::Arithmetic::parse_binary_expression_using_operators<1ul>(AK::Span<AK::Variant<AK::String, long, Shell::Arithmetic::Operator> >&, AK::Array<Shell::Arithmetic::Operator, 1ul>, AK::Function<AK::ErrorOr<Shell::Arithmetic::Node, AK::Error> (AK::Span<AK::Variant<AK::String, long, Shell::Arithmetic::Operator> >&)> const&) Unexecuted instantiation: ImmediateFunctions.cpp:AK::ErrorOr<Shell::Arithmetic::Node, AK::Error> Shell::Arithmetic::parse_binary_expression_using_operators<2ul>(AK::Span<AK::Variant<AK::String, long, Shell::Arithmetic::Operator> >&, AK::Array<Shell::Arithmetic::Operator, 2ul>, AK::Function<AK::ErrorOr<Shell::Arithmetic::Node, AK::Error> (AK::Span<AK::Variant<AK::String, long, Shell::Arithmetic::Operator> >&)> const&) Unexecuted instantiation: ImmediateFunctions.cpp:AK::ErrorOr<Shell::Arithmetic::Node, AK::Error> Shell::Arithmetic::parse_binary_expression_using_operators<4ul>(AK::Span<AK::Variant<AK::String, long, Shell::Arithmetic::Operator> >&, AK::Array<Shell::Arithmetic::Operator, 4ul>, AK::Function<AK::ErrorOr<Shell::Arithmetic::Node, AK::Error> (AK::Span<AK::Variant<AK::String, long, Shell::Arithmetic::Operator> >&)> const&) Unexecuted instantiation: ImmediateFunctions.cpp:AK::ErrorOr<Shell::Arithmetic::Node, AK::Error> Shell::Arithmetic::parse_binary_expression_using_operators<3ul>(AK::Span<AK::Variant<AK::String, long, Shell::Arithmetic::Operator> >&, AK::Array<Shell::Arithmetic::Operator, 3ul>, AK::Function<AK::ErrorOr<Shell::Arithmetic::Node, AK::Error> (AK::Span<AK::Variant<AK::String, long, Shell::Arithmetic::Operator> >&)> const&) |
1021 | | |
1022 | | } |
1023 | | |
1024 | | ErrorOr<RefPtr<AST::Node>> Shell::immediate_math(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
1025 | 0 | { |
1026 | 0 | if (arguments.size() != 1) { |
1027 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Expected exactly 1 argument to math", invoking_node.position()); |
1028 | 0 | return nullptr; |
1029 | 0 | } |
1030 | | |
1031 | 0 | auto expression_parts = TRY(TRY(const_cast<AST::Node&>(*arguments.first()).run(*this))->resolve_as_list(*this)); |
1032 | 0 | auto expression = TRY(String::join(' ', expression_parts)); |
1033 | |
|
1034 | 0 | using Arithmetic::Operator; |
1035 | 0 | using Arithmetic::Token; |
1036 | |
|
1037 | 0 | Vector<Token> tokens; |
1038 | |
|
1039 | 0 | auto view = expression.code_points(); |
1040 | 0 | Optional<size_t> integer_or_word_start_offset; |
1041 | 0 | for (auto it = view.begin(); it != view.end(); ++it) { |
1042 | 0 | auto code_point = *it; |
1043 | 0 | if (is_ascii_alphanumeric(code_point) || code_point == U'_') { |
1044 | 0 | if (!integer_or_word_start_offset.has_value()) |
1045 | 0 | integer_or_word_start_offset = view.byte_offset_of(it); |
1046 | 0 | continue; |
1047 | 0 | } |
1048 | | |
1049 | 0 | if (integer_or_word_start_offset.has_value()) { |
1050 | 0 | auto integer_or_word = view.substring_view( |
1051 | 0 | *integer_or_word_start_offset, |
1052 | 0 | view.byte_offset_of(it) - *integer_or_word_start_offset); |
1053 | |
|
1054 | 0 | if (all_of(integer_or_word, is_ascii_digit)) |
1055 | 0 | tokens.append(*integer_or_word.as_string().to_number<int>()); |
1056 | 0 | else |
1057 | 0 | tokens.append(TRY(expression.substring_from_byte_offset_with_shared_superstring(*integer_or_word_start_offset, integer_or_word.length()))); |
1058 | |
|
1059 | 0 | integer_or_word_start_offset.clear(); |
1060 | 0 | } |
1061 | |
|
1062 | 0 | switch (code_point) { |
1063 | 0 | case U'!': |
1064 | 0 | if (it.peek(1) == U'=') { |
1065 | 0 | ++it; |
1066 | 0 | tokens.append(Operator::NotEqual); |
1067 | 0 | } else { |
1068 | 0 | tokens.append(Operator::Negate); |
1069 | 0 | } |
1070 | 0 | break; |
1071 | 0 | case U'=': |
1072 | 0 | if (it.peek(1) == U'=') { |
1073 | 0 | ++it; |
1074 | 0 | tokens.append(Operator::Equal); |
1075 | 0 | } else { |
1076 | 0 | tokens.append(Operator::Assignment); |
1077 | 0 | } |
1078 | 0 | break; |
1079 | 0 | case U'~': |
1080 | 0 | tokens.append(Operator::BitwiseNegate); |
1081 | 0 | break; |
1082 | 0 | case U'(': |
1083 | 0 | tokens.append(Operator::OpenParen); |
1084 | 0 | break; |
1085 | 0 | case U')': |
1086 | 0 | tokens.append(Operator::CloseParen); |
1087 | 0 | break; |
1088 | 0 | case U'&': |
1089 | 0 | switch (it.peek(1).value_or(0)) { |
1090 | 0 | case U'&': |
1091 | 0 | ++it; |
1092 | 0 | tokens.append(Operator::ArithmeticAnd); |
1093 | 0 | break; |
1094 | 0 | case U'=': |
1095 | 0 | ++it; |
1096 | 0 | tokens.append(Operator::AndAssignment); |
1097 | 0 | break; |
1098 | 0 | default: |
1099 | 0 | tokens.append(Operator::BitwiseAnd); |
1100 | 0 | break; |
1101 | 0 | } |
1102 | 0 | break; |
1103 | 0 | case U'|': |
1104 | 0 | switch (it.peek(1).value_or(0)) { |
1105 | 0 | case U'|': |
1106 | 0 | ++it; |
1107 | 0 | tokens.append(Operator::ArithmeticOr); |
1108 | 0 | break; |
1109 | 0 | case U'=': |
1110 | 0 | ++it; |
1111 | 0 | tokens.append(Operator::OrAssignment); |
1112 | 0 | break; |
1113 | 0 | default: |
1114 | 0 | tokens.append(Operator::BitwiseOr); |
1115 | 0 | break; |
1116 | 0 | } |
1117 | 0 | break; |
1118 | 0 | case U'^': |
1119 | 0 | if (it.peek(1) == U'=') { |
1120 | 0 | ++it; |
1121 | 0 | tokens.append(Operator::XorAssignment); |
1122 | 0 | } else { |
1123 | 0 | tokens.append(Operator::BitwiseXor); |
1124 | 0 | } |
1125 | 0 | break; |
1126 | 0 | case U',': |
1127 | 0 | tokens.append(Operator::Comma); |
1128 | 0 | break; |
1129 | 0 | case U'?': |
1130 | 0 | tokens.append(Operator::TernaryQuestion); |
1131 | 0 | break; |
1132 | 0 | case U':': |
1133 | 0 | tokens.append(Operator::TernaryColon); |
1134 | 0 | break; |
1135 | 0 | case U'+': |
1136 | 0 | switch (it.peek(1).value_or(0)) { |
1137 | 0 | case U'=': |
1138 | 0 | ++it; |
1139 | 0 | tokens.append(Operator::PlusAssignment); |
1140 | 0 | break; |
1141 | 0 | default: |
1142 | 0 | tokens.append(Operator::Add); |
1143 | 0 | break; |
1144 | 0 | } |
1145 | 0 | break; |
1146 | 0 | case U'-': |
1147 | 0 | switch (it.peek(1).value_or(0)) { |
1148 | 0 | case U'=': |
1149 | 0 | ++it; |
1150 | 0 | tokens.append(Operator::MinusAssignment); |
1151 | 0 | break; |
1152 | 0 | default: |
1153 | 0 | tokens.append(Operator::Subtract); |
1154 | 0 | break; |
1155 | 0 | } |
1156 | 0 | break; |
1157 | 0 | case U'*': |
1158 | 0 | switch (it.peek(1).value_or(0)) { |
1159 | 0 | case U'=': |
1160 | 0 | ++it; |
1161 | 0 | tokens.append(Operator::MultiplyAssignment); |
1162 | 0 | break; |
1163 | 0 | case U'*': |
1164 | 0 | ++it; |
1165 | 0 | tokens.append(Operator::Power); |
1166 | 0 | break; |
1167 | 0 | default: |
1168 | 0 | tokens.append(Operator::Multiply); |
1169 | 0 | break; |
1170 | 0 | } |
1171 | 0 | break; |
1172 | 0 | case U'/': |
1173 | 0 | if (it.peek(1) == U'=') { |
1174 | 0 | ++it; |
1175 | 0 | tokens.append(Operator::DivideAssignment); |
1176 | 0 | } else { |
1177 | 0 | tokens.append(Operator::Quotient); |
1178 | 0 | } |
1179 | 0 | break; |
1180 | 0 | case U'%': |
1181 | 0 | if (it.peek(1) == U'=') { |
1182 | 0 | ++it; |
1183 | 0 | tokens.append(Operator::ModuloAssignment); |
1184 | 0 | } else { |
1185 | 0 | tokens.append(Operator::Remainder); |
1186 | 0 | } |
1187 | 0 | break; |
1188 | 0 | case U'<': |
1189 | 0 | switch (it.peek(1).value_or(0)) { |
1190 | 0 | case U'<': |
1191 | 0 | ++it; |
1192 | 0 | if (it.peek(1) == U'=') { |
1193 | 0 | ++it; |
1194 | 0 | tokens.append(Operator::LeftShiftAssignment); |
1195 | 0 | } else { |
1196 | 0 | tokens.append(Operator::ShiftLeft); |
1197 | 0 | } |
1198 | 0 | break; |
1199 | 0 | case U'=': |
1200 | 0 | ++it; |
1201 | 0 | tokens.append(Operator::LessThanOrEqual); |
1202 | 0 | break; |
1203 | 0 | default: |
1204 | 0 | tokens.append(Operator::LessThan); |
1205 | 0 | break; |
1206 | 0 | } |
1207 | 0 | break; |
1208 | 0 | case U'>': |
1209 | 0 | switch (it.peek(1).value_or(0)) { |
1210 | 0 | case U'>': |
1211 | 0 | ++it; |
1212 | 0 | if (it.peek(1) == U'=') { |
1213 | 0 | ++it; |
1214 | 0 | tokens.append(Operator::RightShiftAssignment); |
1215 | 0 | } else { |
1216 | 0 | tokens.append(Operator::ShiftRight); |
1217 | 0 | } |
1218 | 0 | break; |
1219 | 0 | case U'=': |
1220 | 0 | ++it; |
1221 | 0 | tokens.append(Operator::GreaterThanOrEqual); |
1222 | 0 | break; |
1223 | 0 | default: |
1224 | 0 | tokens.append(Operator::GreaterThan); |
1225 | 0 | break; |
1226 | 0 | } |
1227 | 0 | break; |
1228 | 0 | case U' ': |
1229 | 0 | case U'\t': |
1230 | 0 | case U'\n': |
1231 | 0 | case U'\r': |
1232 | 0 | break; |
1233 | 0 | default: |
1234 | 0 | raise_error(ShellError::EvaluatedSyntaxError, ByteString::formatted("Unexpected character '{:c}' in math expression", code_point), arguments.first()->position()); |
1235 | 0 | return nullptr; |
1236 | 0 | } |
1237 | 0 | } |
1238 | 0 | if (integer_or_word_start_offset.has_value()) { |
1239 | 0 | auto integer_or_word = view.substring_view(*integer_or_word_start_offset); |
1240 | |
|
1241 | 0 | if (all_of(integer_or_word, is_ascii_digit)) |
1242 | 0 | tokens.append(*integer_or_word.as_string().to_number<int>()); |
1243 | 0 | else |
1244 | 0 | tokens.append(TRY(expression.substring_from_byte_offset_with_shared_superstring(*integer_or_word_start_offset, integer_or_word.length()))); |
1245 | |
|
1246 | 0 | integer_or_word_start_offset.clear(); |
1247 | 0 | } |
1248 | |
|
1249 | 0 | auto ast = TRY(Arithmetic::parse_expression(tokens)); |
1250 | | |
1251 | | // Now interpret that. |
1252 | 0 | Function<ErrorOr<i64>(Arithmetic::Node const&)> interpret = [&](Arithmetic::Node const& node) -> ErrorOr<i64> { |
1253 | 0 | return node.value.visit( |
1254 | 0 | [&](String const& name) -> ErrorOr<i64> { |
1255 | 0 | size_t resolution_attempts_remaining = 100; |
1256 | 0 | for (auto resolved_name = name; resolution_attempts_remaining > 0; --resolution_attempts_remaining) { |
1257 | 0 | auto value = TRY(look_up_local_variable(resolved_name.bytes_as_string_view())); |
1258 | 0 | if (!value) |
1259 | 0 | break; |
1260 | | |
1261 | 0 | StringBuilder builder; |
1262 | 0 | builder.join(' ', TRY(const_cast<AST::Value&>(*value).resolve_as_list(const_cast<Shell&>(*this)))); |
1263 | 0 | resolved_name = TRY(builder.to_string()); |
1264 | |
|
1265 | 0 | auto integer = resolved_name.to_number<i64>(); |
1266 | 0 | if (integer.has_value()) |
1267 | 0 | return *integer; |
1268 | 0 | } |
1269 | | |
1270 | 0 | if (resolution_attempts_remaining == 0) |
1271 | 0 | raise_error(ShellError::EvaluatedSyntaxError, ByteString::formatted("Too many indirections when resolving variable '{}'", name), arguments.first()->position()); |
1272 | |
|
1273 | 0 | return 0; |
1274 | 0 | }, |
1275 | 0 | [&](i64 value) -> ErrorOr<i64> { |
1276 | 0 | return value; |
1277 | 0 | }, |
1278 | 0 | [&](NonnullOwnPtr<Arithmetic::BinaryOperationNode> const& node) -> ErrorOr<i64> { |
1279 | 0 | if (Arithmetic::is_assignment_operator(node->op)) { |
1280 | | // lhs must be a variable name. |
1281 | 0 | auto name = node->lhs.value.get_pointer<String>(); |
1282 | 0 | if (!name) { |
1283 | 0 | raise_error(ShellError::EvaluatedSyntaxError, "Invalid left-hand side of assignment", arguments.first()->position()); |
1284 | 0 | return 0; |
1285 | 0 | } |
1286 | | |
1287 | 0 | auto rhs = TRY(interpret(node->rhs)); |
1288 | |
|
1289 | 0 | if (node->op != Arithmetic::Operator::Assignment) { |
1290 | | // Evaluate the new value |
1291 | 0 | rhs = TRY(interpret(Arithmetic::Node { |
1292 | 0 | .value = make<Arithmetic::BinaryOperationNode>( |
1293 | 0 | Arithmetic::assignment_operation_of(node->op), |
1294 | 0 | Arithmetic::Node { *name }, |
1295 | 0 | Arithmetic::Node { rhs }), |
1296 | 0 | })); |
1297 | 0 | } |
1298 | |
|
1299 | 0 | set_local_variable(name->to_byte_string(), make_ref_counted<AST::StringValue>(String::number(rhs))); |
1300 | 0 | return rhs; |
1301 | 0 | } |
1302 | | |
1303 | 0 | auto lhs = TRY(interpret(node->lhs)); |
1304 | 0 | auto rhs = TRY(interpret(node->rhs)); |
1305 | |
|
1306 | 0 | using Arithmetic::Operator; |
1307 | 0 | switch (node->op) { |
1308 | 0 | case Operator::Add: |
1309 | 0 | return lhs + rhs; |
1310 | 0 | case Operator::Subtract: |
1311 | 0 | return lhs - rhs; |
1312 | 0 | case Operator::Multiply: |
1313 | 0 | return lhs * rhs; |
1314 | 0 | case Operator::Quotient: |
1315 | 0 | return lhs / rhs; |
1316 | 0 | case Operator::Remainder: |
1317 | 0 | return lhs % rhs; |
1318 | 0 | case Operator::ShiftLeft: |
1319 | 0 | return lhs << rhs; |
1320 | 0 | case Operator::ShiftRight: |
1321 | 0 | return lhs >> rhs; |
1322 | 0 | case Operator::BitwiseAnd: |
1323 | 0 | return lhs & rhs; |
1324 | 0 | case Operator::BitwiseOr: |
1325 | 0 | return lhs | rhs; |
1326 | 0 | case Operator::BitwiseXor: |
1327 | 0 | return lhs ^ rhs; |
1328 | 0 | case Operator::ArithmeticAnd: |
1329 | 0 | return lhs != 0 && rhs != 0; |
1330 | 0 | case Operator::ArithmeticOr: |
1331 | 0 | return lhs != 0 || rhs != 0; |
1332 | 0 | case Operator::LessThan: |
1333 | 0 | return lhs < rhs; |
1334 | 0 | case Operator::LessThanOrEqual: |
1335 | 0 | return lhs <= rhs; |
1336 | 0 | case Operator::GreaterThan: |
1337 | 0 | return lhs > rhs; |
1338 | 0 | case Operator::GreaterThanOrEqual: |
1339 | 0 | return lhs >= rhs; |
1340 | 0 | case Operator::Equal: |
1341 | 0 | return lhs == rhs; |
1342 | 0 | case Operator::NotEqual: |
1343 | 0 | return lhs != rhs; |
1344 | 0 | case Operator::Power: |
1345 | 0 | return trunc(pow(static_cast<double>(lhs), static_cast<double>(rhs))); |
1346 | 0 | case Operator::Comma: |
1347 | 0 | return rhs; |
1348 | 0 | default: |
1349 | 0 | VERIFY_NOT_REACHED(); |
1350 | 0 | } |
1351 | 0 | }, |
1352 | 0 | [&](NonnullOwnPtr<Arithmetic::UnaryOperationNode> const& node) -> ErrorOr<i64> { |
1353 | 0 | auto value = TRY(interpret(node->rhs)); |
1354 | |
|
1355 | 0 | switch (node->op) { |
1356 | 0 | case Arithmetic::Operator::Negate: |
1357 | 0 | return value == 0; |
1358 | 0 | case Arithmetic::Operator::BitwiseNegate: |
1359 | 0 | return ~value; |
1360 | 0 | case Arithmetic::Operator::Add: |
1361 | 0 | return value; |
1362 | 0 | case Arithmetic::Operator::Subtract: |
1363 | 0 | return -value; |
1364 | 0 | default: |
1365 | 0 | VERIFY_NOT_REACHED(); |
1366 | 0 | } |
1367 | 0 | }, |
1368 | 0 | [&](NonnullOwnPtr<Arithmetic::TernaryOperationNode> const& node) -> ErrorOr<i64> { |
1369 | 0 | auto condition = TRY(interpret(node->condition)); |
1370 | 0 | if (condition != 0) |
1371 | 0 | return TRY(interpret(node->true_value)); |
1372 | 0 | return TRY(interpret(node->false_value)); |
1373 | 0 | }, |
1374 | 0 | [&](NonnullOwnPtr<Arithmetic::ErrorNode> const& node) -> ErrorOr<i64> { |
1375 | 0 | raise_error(ShellError::EvaluatedSyntaxError, node->error.to_byte_string(), arguments.first()->position()); |
1376 | 0 | return 0; |
1377 | 0 | }); |
1378 | 0 | }; |
1379 | |
|
1380 | 0 | auto result = TRY(interpret(ast)); |
1381 | |
|
1382 | 0 | return make_ref_counted<AST::StringLiteral>(arguments.first()->position(), String::number(result), AST::StringLiteral::EnclosureType::None); |
1383 | 0 | } |
1384 | | |
1385 | | ErrorOr<RefPtr<AST::Node>> Shell::run_immediate_function(StringView str, AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const& arguments) |
1386 | 0 | { |
1387 | 0 | #define __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(name) \ |
1388 | 0 | if (str == #name) \ |
1389 | 0 | return immediate_##name(invoking_node, arguments); |
1390 | |
|
1391 | 0 | ENUMERATE_SHELL_IMMEDIATE_FUNCTIONS() |
1392 | | |
1393 | 0 | #undef __ENUMERATE_SHELL_IMMEDIATE_FUNCTION |
1394 | 0 | raise_error(ShellError::EvaluatedSyntaxError, ByteString::formatted("Unknown immediate function {}", str), invoking_node.position()); |
1395 | 0 | return nullptr; |
1396 | 0 | } |
1397 | | |
1398 | | bool Shell::has_immediate_function(StringView str) |
1399 | 0 | { |
1400 | 0 | #define __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(name) \ |
1401 | 0 | if (str == #name) \ |
1402 | 0 | return true; |
1403 | |
|
1404 | 0 | ENUMERATE_SHELL_IMMEDIATE_FUNCTIONS() |
1405 | | |
1406 | 0 | #undef __ENUMERATE_SHELL_IMMEDIATE_FUNCTION |
1407 | | |
1408 | 0 | return false; |
1409 | 0 | } |
1410 | | } |