Coverage Report

Created: 2025-11-16 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}