Coverage Report

Created: 2025-09-05 06:52

/src/serenity/Userland/Libraries/LibShell/Parser.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2020-2021, the SerenityOS developers.
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include "Parser.h"
8
#include "Shell.h"
9
#include <AK/AllOf.h>
10
#include <AK/GenericLexer.h>
11
#include <AK/ScopeGuard.h>
12
#include <AK/ScopedValueRollback.h>
13
#include <AK/TemporaryChange.h>
14
#include <ctype.h>
15
#include <stdio.h>
16
#include <unistd.h>
17
18
2.54M
#define TRY_OR_THROW_PARSE_ERROR(expr) ({                                              \
19
2.54M
    /* Ignore -Wshadow to allow nesting the macro. */                                  \
20
2.54M
    AK_IGNORE_DIAGNOSTIC("-Wshadow",                                                   \
21
2.54M
                         auto&& _value_or_error = expr;)                               \
22
2.54M
    if (_value_or_error.is_error()) {                                                  \
23
120k
        AK_IGNORE_DIAGNOSTIC("-Wshadow",                                               \
24
120k
                             auto _error = _value_or_error.release_error();)           \
25
120k
        if (_error.is_errno() && _error.code() == ENOMEM)                              \
26
120k
            return create<AST::SyntaxError>("OOM"_string);                             \
27
120k
        return create<AST::SyntaxError>(MUST(String::formatted("Error: {}", _error))); \
28
120k
    }                                                                                  \
29
2.54M
    _value_or_error.release_value();                                                   \
30
2.42M
})
31
32
69.1k
#define TRY_OR_RESOLVE_TO_ERROR_STRING(expr) ({                                   \
33
69.1k
    /* Ignore -Wshadow to allow nesting the macro. */                             \
34
69.1k
    AK_IGNORE_DIAGNOSTIC("-Wshadow",                                              \
35
69.1k
                         auto&& _value_or_error = expr;                           \
36
69.1k
                         String _string_value;)                                   \
37
69.1k
    if (_value_or_error.is_error()) {                                             \
38
0
        AK_IGNORE_DIAGNOSTIC("-Wshadow",                                          \
39
0
                             auto _error = _value_or_error.release_error();)      \
40
0
        if (_error.is_errno() && _error.code() == ENOMEM)                         \
41
0
            _string_value = "OOM"_string;                                         \
42
0
        else                                                                      \
43
0
            _string_value = MUST(String::formatted("Error: {}", _error));         \
44
0
    }                                                                             \
45
69.1k
    _value_or_error.is_error() ? _string_value : _value_or_error.release_value(); \
46
69.1k
})
47
48
159
#define TRY_OR(expr, catch_expr) ({                                          \
49
159
    /* Ignore -Wshadow to allow nesting the macro. */                        \
50
159
    AK_IGNORE_DIAGNOSTIC("-Wshadow",                                         \
51
159
                         auto&& _value_or_error = expr;)                     \
52
159
    if (_value_or_error.is_error()) {                                        \
53
0
        AK_IGNORE_DIAGNOSTIC("-Wshadow",                                     \
54
0
                             auto _error = _value_or_error.release_error();) \
55
0
        catch_expr;                                                          \
56
0
    }                                                                        \
57
159
    _value_or_error.release_value();                                         \
58
159
})
59
60
namespace Shell {
61
62
Parser::SavedOffset Parser::save_offset() const
63
7.18M
{
64
7.18M
    return { m_offset, m_line };
65
7.18M
}
66
67
char Parser::peek()
68
1.14G
{
69
1.14G
    if (at_end())
70
74.3k
        return 0;
71
72
1.14G
    VERIFY(m_offset < m_input.length());
73
74
1.14G
    auto ch = m_input[m_offset];
75
1.14G
    if (ch == '\\' && m_input.length() > m_offset + 1 && m_input[m_offset + 1] == '\n') {
76
147k
        m_offset += 2;
77
147k
        ++m_line.line_number;
78
147k
        m_line.line_column = 0;
79
147k
        return peek();
80
147k
    }
81
82
1.14G
    return ch;
83
1.14G
}
84
85
char Parser::consume()
86
447M
{
87
447M
    if (at_end())
88
0
        return 0;
89
90
447M
    auto ch = peek();
91
447M
    ++m_offset;
92
93
447M
    if (ch == '\n') {
94
16.8M
        ++m_line.line_number;
95
16.8M
        m_line.line_column = 0;
96
431M
    } else {
97
431M
        ++m_line.line_column;
98
431M
    }
99
100
447M
    return ch;
101
447M
}
102
103
bool Parser::expect(char ch)
104
8.23M
{
105
8.23M
    return expect(StringView { &ch, 1 });
106
8.23M
}
107
108
bool Parser::expect(StringView expected)
109
143M
{
110
143M
    auto offset_at_start = m_offset;
111
143M
    auto line_at_start = line();
112
113
143M
    if (expected.length() + m_offset > m_input.length())
114
134k
        return false;
115
116
143M
    for (auto& c : expected) {
117
143M
        if (peek() != c) {
118
138M
            restore_to(offset_at_start, line_at_start);
119
138M
            return false;
120
138M
        }
121
122
5.48M
        consume();
123
5.48M
    }
124
125
5.08M
    return true;
126
143M
}
127
128
template<typename A, typename... Args>
129
NonnullRefPtr<A> Parser::create(Args&&... args)
130
8.63M
{
131
8.63M
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
8.63M
}
AK::NonnullRefPtr<Shell::AST::StringPartCompose> Shell::Parser::create<Shell::AST::StringPartCompose, AK::NonnullRefPtr<Shell::AST::StringLiteral>, AK::NonnullRefPtr<Shell::AST::Node> >(AK::NonnullRefPtr<Shell::AST::StringLiteral>&&, AK::NonnullRefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
14.6k
{
131
14.6k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
14.6k
}
AK::NonnullRefPtr<Shell::AST::StringPartCompose> Shell::Parser::create<Shell::AST::StringPartCompose, AK::NonnullRefPtr<Shell::AST::StringPartCompose>, AK::NonnullRefPtr<Shell::AST::Node> >(AK::NonnullRefPtr<Shell::AST::StringPartCompose>&&, AK::NonnullRefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
14.1k
{
131
14.1k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
14.1k
}
AK::NonnullRefPtr<Shell::AST::SyntaxError> Shell::Parser::create<Shell::AST::SyntaxError, AK::String>(AK::String&&)
Line
Count
Source
130
233k
{
131
233k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
233k
}
AK::NonnullRefPtr<Shell::AST::Sequence> Shell::Parser::create<Shell::AST::Sequence, AK::Vector<AK::NonnullRefPtr<Shell::AST::Node>, 0ul>, AK::Vector<Shell::AST::Position, 0ul> >(AK::Vector<AK::NonnullRefPtr<Shell::AST::Node>, 0ul>&&, AK::Vector<Shell::AST::Position, 0ul>&&)
Line
Count
Source
130
22.0k
{
131
22.0k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
22.0k
}
AK::NonnullRefPtr<Shell::AST::Execute> Shell::Parser::create<Shell::AST::Execute, AK::NonnullRefPtr<Shell::AST::Sequence> >(AK::NonnullRefPtr<Shell::AST::Sequence>&&)
Line
Count
Source
130
22.0k
{
131
22.0k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
22.0k
}
AK::NonnullRefPtr<Shell::AST::SyntaxError> Shell::Parser::create<Shell::AST::SyntaxError, AK::String, bool>(AK::String&&, bool&&)
Line
Count
Source
130
154k
{
131
154k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
154k
}
AK::NonnullRefPtr<Shell::AST::Background> Shell::Parser::create<Shell::AST::Background, AK::NonnullRefPtr<Shell::AST::Node> >(AK::NonnullRefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
550
{
131
550
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
550
}
AK::NonnullRefPtr<Shell::AST::BarewordLiteral> Shell::Parser::create<Shell::AST::BarewordLiteral, AK::String>(AK::String&&)
Line
Count
Source
130
2.19M
{
131
2.19M
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
2.19M
}
AK::NonnullRefPtr<Shell::AST::StringLiteral> Shell::Parser::create<Shell::AST::StringLiteral, AK::String, Shell::AST::StringLiteral::EnclosureType>(AK::String&&, Shell::AST::StringLiteral::EnclosureType&&)
Line
Count
Source
130
3.26M
{
131
3.26M
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
3.26M
}
AK::NonnullRefPtr<Shell::AST::VariableDeclarations> Shell::Parser::create<Shell::AST::VariableDeclarations, AK::Vector<Shell::AST::VariableDeclarations::Variable, 0ul> >(AK::Vector<Shell::AST::VariableDeclarations::Variable, 0ul>&&)
Line
Count
Source
130
984
{
131
984
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
984
}
Unexecuted instantiation: AK::NonnullRefPtr<Shell::AST::FunctionDeclaration> Shell::Parser::create<Shell::AST::FunctionDeclaration, Shell::AST::NameWithPosition, AK::Vector<Shell::AST::NameWithPosition, 0ul>, AK::RefPtr<Shell::AST::Node> >(Shell::AST::NameWithPosition&&, AK::Vector<Shell::AST::NameWithPosition, 0ul>&&, AK::RefPtr<Shell::AST::Node>&&)
AK::NonnullRefPtr<Shell::AST::Or> Shell::Parser::create<Shell::AST::Or, AK::NonnullRefPtr<Shell::AST::Node>, AK::NonnullRefPtr<Shell::AST::Node>, Shell::AST::Position>(AK::NonnullRefPtr<Shell::AST::Node>&&, AK::NonnullRefPtr<Shell::AST::Node>&&, Shell::AST::Position&&)
Line
Count
Source
130
226
{
131
226
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
226
}
AK::NonnullRefPtr<Shell::AST::And> Shell::Parser::create<Shell::AST::And, AK::NonnullRefPtr<Shell::AST::Node>, AK::NonnullRefPtr<Shell::AST::Node>, Shell::AST::Position>(AK::NonnullRefPtr<Shell::AST::Node>&&, AK::NonnullRefPtr<Shell::AST::Node>&&, Shell::AST::Position&&)
Line
Count
Source
130
2.15k
{
131
2.15k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
2.15k
}
Unexecuted instantiation: AK::NonnullRefPtr<Shell::AST::Fd2FdRedirection> Shell::Parser::create<Shell::AST::Fd2FdRedirection, int, int>(int&&, int&&)
AK::NonnullRefPtr<Shell::AST::Join> Shell::Parser::create<Shell::AST::Join, AK::NonnullRefPtr<Shell::AST::Node>, AK::NonnullRefPtr<Shell::AST::Node> >(AK::NonnullRefPtr<Shell::AST::Node>&&, AK::NonnullRefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
81.0k
{
131
81.0k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
81.0k
}
AK::NonnullRefPtr<Shell::AST::Pipe> Shell::Parser::create<Shell::AST::Pipe, AK::NonnullRefPtr<Shell::AST::Node>, AK::NonnullRefPtr<Shell::AST::Node> >(AK::NonnullRefPtr<Shell::AST::Node>&&, AK::NonnullRefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
18.2k
{
131
18.2k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
18.2k
}
AK::NonnullRefPtr<Shell::AST::CastToCommand> Shell::Parser::create<Shell::AST::CastToCommand, AK::NonnullRefPtr<Shell::AST::Node> >(AK::NonnullRefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
940k
{
131
940k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
940k
}
AK::NonnullRefPtr<Shell::AST::Join> Shell::Parser::create<Shell::AST::Join, AK::NonnullRefPtr<Shell::AST::CastToCommand>, AK::NonnullRefPtr<Shell::AST::Node> >(AK::NonnullRefPtr<Shell::AST::CastToCommand>&&, AK::NonnullRefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
31.7k
{
131
31.7k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
31.7k
}
Unexecuted instantiation: AK::NonnullRefPtr<Shell::AST::ContinuationControl> Shell::Parser::create<Shell::AST::ContinuationControl, Shell::AST::ContinuationControl::ContinuationKind>(Shell::AST::ContinuationControl::ContinuationKind&&)
Unexecuted instantiation: AK::NonnullRefPtr<Shell::AST::ForLoop> Shell::Parser::create<Shell::AST::ForLoop, AK::Optional<Shell::AST::NameWithPosition>, AK::Optional<Shell::AST::NameWithPosition>, AK::NonnullRefPtr<Shell::AST::SyntaxError>, decltype(nullptr)>(AK::Optional<Shell::AST::NameWithPosition>&&, AK::Optional<Shell::AST::NameWithPosition>&&, AK::NonnullRefPtr<Shell::AST::SyntaxError>&&, decltype(nullptr)&&)
Unexecuted instantiation: AK::NonnullRefPtr<Shell::AST::ForLoop> Shell::Parser::create<Shell::AST::ForLoop, AK::Optional<Shell::AST::NameWithPosition>, AK::Optional<Shell::AST::NameWithPosition>, AK::RefPtr<Shell::AST::Node>, AK::NonnullRefPtr<Shell::AST::SyntaxError>, AK::Optional<Shell::AST::Position>, AK::Optional<Shell::AST::Position> >(AK::Optional<Shell::AST::NameWithPosition>&&, AK::Optional<Shell::AST::NameWithPosition>&&, AK::RefPtr<Shell::AST::Node>&&, AK::NonnullRefPtr<Shell::AST::SyntaxError>&&, AK::Optional<Shell::AST::Position>&&, AK::Optional<Shell::AST::Position>&&)
AK::NonnullRefPtr<Shell::AST::ForLoop> Shell::Parser::create<Shell::AST::ForLoop, AK::Optional<Shell::AST::NameWithPosition>, AK::Optional<Shell::AST::NameWithPosition>, AK::RefPtr<Shell::AST::Node>, AK::RefPtr<Shell::AST::Node>, AK::Optional<Shell::AST::Position>, AK::Optional<Shell::AST::Position> >(AK::Optional<Shell::AST::NameWithPosition>&&, AK::Optional<Shell::AST::NameWithPosition>&&, AK::RefPtr<Shell::AST::Node>&&, AK::RefPtr<Shell::AST::Node>&&, AK::Optional<Shell::AST::Position>&&, AK::Optional<Shell::AST::Position>&&)
Line
Count
Source
130
668
{
131
668
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
668
}
Unexecuted instantiation: AK::NonnullRefPtr<Shell::AST::ForLoop> Shell::Parser::create<Shell::AST::ForLoop, Shell::AST::NameWithPosition, Shell::AST::NameWithPosition, decltype(nullptr), AK::NonnullRefPtr<Shell::AST::SyntaxError> >(Shell::AST::NameWithPosition&&, Shell::AST::NameWithPosition&&, decltype(nullptr)&&, AK::NonnullRefPtr<Shell::AST::SyntaxError>&&)
Unexecuted instantiation: AK::NonnullRefPtr<Shell::AST::ForLoop> Shell::Parser::create<Shell::AST::ForLoop, Shell::AST::NameWithPosition, Shell::AST::NameWithPosition, decltype(nullptr), AK::RefPtr<Shell::AST::Node> >(Shell::AST::NameWithPosition&&, Shell::AST::NameWithPosition&&, decltype(nullptr)&&, AK::RefPtr<Shell::AST::Node>&&)
AK::NonnullRefPtr<Shell::AST::IfCond> Shell::Parser::create<Shell::AST::IfCond, AK::Optional<Shell::AST::Position>&, AK::NonnullRefPtr<Shell::AST::Node>, AK::RefPtr<Shell::AST::Node>, AK::RefPtr<Shell::AST::Node> >(AK::Optional<Shell::AST::Position>&, AK::NonnullRefPtr<Shell::AST::Node>&&, AK::RefPtr<Shell::AST::Node>&&, AK::RefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
2.13k
{
131
2.13k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
2.13k
}
AK::NonnullRefPtr<Shell::AST::IfCond> Shell::Parser::create<Shell::AST::IfCond, AK::Optional<Shell::AST::Position>&, AK::NonnullRefPtr<Shell::AST::Node>, AK::RefPtr<Shell::AST::Node>, decltype(nullptr)>(AK::Optional<Shell::AST::Position>&, AK::NonnullRefPtr<Shell::AST::Node>&&, AK::RefPtr<Shell::AST::Node>&&, decltype(nullptr)&&)
Line
Count
Source
130
1.27k
{
131
1.27k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
1.27k
}
AK::NonnullRefPtr<Shell::AST::Subshell> Shell::Parser::create<Shell::AST::Subshell, AK::RefPtr<Shell::AST::Node> >(AK::RefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
6.42k
{
131
6.42k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
6.42k
}
AK::NonnullRefPtr<Shell::AST::MatchExpr> Shell::Parser::create<Shell::AST::MatchExpr, AK::NonnullRefPtr<Shell::AST::SyntaxError>, AK::String, AK::Optional<Shell::AST::Position>, AK::Vector<Shell::AST::MatchEntry, 0ul> >(AK::NonnullRefPtr<Shell::AST::SyntaxError>&&, AK::String&&, AK::Optional<Shell::AST::Position>&&, AK::Vector<Shell::AST::MatchEntry, 0ul>&&)
Line
Count
Source
130
64
{
131
64
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
64
}
AK::NonnullRefPtr<Shell::AST::MatchExpr> Shell::Parser::create<Shell::AST::MatchExpr, AK::NonnullRefPtr<Shell::AST::Node>, AK::String, AK::Optional<Shell::AST::Position>, AK::Vector<Shell::AST::MatchEntry, 0ul> >(AK::NonnullRefPtr<Shell::AST::Node>&&, AK::String&&, AK::Optional<Shell::AST::Position>&&, AK::Vector<Shell::AST::MatchEntry, 0ul>&&)
Line
Count
Source
130
3.44k
{
131
3.44k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
3.44k
}
AK::NonnullRefPtr<Shell::AST::WriteAppendRedirection> Shell::Parser::create<Shell::AST::WriteAppendRedirection, int&, AK::NonnullRefPtr<Shell::AST::Node> >(int&, AK::NonnullRefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
9.92k
{
131
9.92k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
9.92k
}
AK::NonnullRefPtr<Shell::AST::CloseFdRedirection> Shell::Parser::create<Shell::AST::CloseFdRedirection, int&>(int&)
Line
Count
Source
130
13.6k
{
131
13.6k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
13.6k
}
AK::NonnullRefPtr<Shell::AST::Fd2FdRedirection> Shell::Parser::create<Shell::AST::Fd2FdRedirection, int&, int&>(int&, int&)
Line
Count
Source
130
274
{
131
274
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
274
}
AK::NonnullRefPtr<Shell::AST::WriteRedirection> Shell::Parser::create<Shell::AST::WriteRedirection, int&, AK::NonnullRefPtr<Shell::AST::Node> >(int&, AK::NonnullRefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
49.0k
{
131
49.0k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
49.0k
}
AK::NonnullRefPtr<Shell::AST::ReadRedirection> Shell::Parser::create<Shell::AST::ReadRedirection, int&, AK::NonnullRefPtr<Shell::AST::Node> >(int&, AK::NonnullRefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
18.9k
{
131
18.9k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
18.9k
}
AK::NonnullRefPtr<Shell::AST::ReadWriteRedirection> Shell::Parser::create<Shell::AST::ReadWriteRedirection, int&, AK::NonnullRefPtr<Shell::AST::Node> >(int&, AK::NonnullRefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
23
{
131
23
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
23
}
AK::NonnullRefPtr<Shell::AST::ListConcatenate> Shell::Parser::create<Shell::AST::ListConcatenate, AK::Vector<AK::NonnullRefPtr<Shell::AST::Node>, 0ul> >(AK::Vector<AK::NonnullRefPtr<Shell::AST::Node>, 0ul>&&)
Line
Count
Source
130
980k
{
131
980k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
980k
}
AK::NonnullRefPtr<Shell::AST::CastToList> Shell::Parser::create<Shell::AST::CastToList, AK::RefPtr<Shell::AST::Node> >(AK::RefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
24.5k
{
131
24.5k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
24.5k
}
AK::NonnullRefPtr<Shell::AST::Juxtaposition> Shell::Parser::create<Shell::AST::Juxtaposition, AK::NonnullRefPtr<Shell::AST::CastToList>, AK::NonnullRefPtr<Shell::AST::Node> >(AK::NonnullRefPtr<Shell::AST::CastToList>&&, AK::NonnullRefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
19.1k
{
131
19.1k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
19.1k
}
AK::NonnullRefPtr<Shell::AST::Juxtaposition> Shell::Parser::create<Shell::AST::Juxtaposition, AK::NonnullRefPtr<Shell::AST::Node>, AK::NonnullRefPtr<Shell::AST::Node> >(AK::NonnullRefPtr<Shell::AST::Node>&&, AK::NonnullRefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
152k
{
131
152k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
152k
}
AK::NonnullRefPtr<Shell::AST::DoubleQuotedString> Shell::Parser::create<Shell::AST::DoubleQuotedString, AK::RefPtr<Shell::AST::Node> >(AK::RefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
27.4k
{
131
27.4k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
27.4k
}
AK::NonnullRefPtr<Shell::AST::SpecialVariable> Shell::Parser::create<Shell::AST::SpecialVariable, char>(char&&)
Line
Count
Source
130
13.2k
{
131
13.2k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
13.2k
}
AK::NonnullRefPtr<Shell::AST::SimpleVariable> Shell::Parser::create<Shell::AST::SimpleVariable, AK::String>(AK::String&&)
Line
Count
Source
130
39.5k
{
131
39.5k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
39.5k
}
AK::NonnullRefPtr<Shell::AST::Slice> Shell::Parser::create<Shell::AST::Slice, AK::NonnullRefPtr<Shell::AST::Node> >(AK::NonnullRefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
36.7k
{
131
36.7k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
36.7k
}
AK::NonnullRefPtr<Shell::AST::Execute> Shell::Parser::create<Shell::AST::Execute, AK::NonnullRefPtr<Shell::AST::Node>, bool>(AK::NonnullRefPtr<Shell::AST::Node>&&, bool&&)
Line
Count
Source
130
11.5k
{
131
11.5k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
11.5k
}
AK::NonnullRefPtr<Shell::AST::DynamicEvaluate> Shell::Parser::create<Shell::AST::DynamicEvaluate, AK::NonnullRefPtr<Shell::AST::Node> >(AK::NonnullRefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
28.6k
{
131
28.6k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
28.6k
}
AK::NonnullRefPtr<Shell::AST::ImmediateExpression> Shell::Parser::create<Shell::AST::ImmediateExpression, Shell::AST::NameWithPosition, AK::Vector<AK::NonnullRefPtr<Shell::AST::Node>, 0ul>, AK::Optional<Shell::AST::Position>&>(Shell::AST::NameWithPosition&&, AK::Vector<AK::NonnullRefPtr<Shell::AST::Node>, 0ul>&&, AK::Optional<Shell::AST::Position>&)
Line
Count
Source
130
19.3k
{
131
19.3k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
19.3k
}
Unexecuted instantiation: AK::NonnullRefPtr<Shell::AST::HistoryEvent> Shell::Parser::create<Shell::AST::HistoryEvent, Shell::AST::HistorySelector>(Shell::AST::HistorySelector&&)
AK::NonnullRefPtr<Shell::AST::Comment> Shell::Parser::create<Shell::AST::Comment, AK::String>(AK::String&&)
Line
Count
Source
130
73.3k
{
131
73.3k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
73.3k
}
AK::NonnullRefPtr<Shell::AST::Tilde> Shell::Parser::create<Shell::AST::Tilde, AK::String>(AK::String&&)
Line
Count
Source
130
8.15k
{
131
8.15k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
8.15k
}
AK::NonnullRefPtr<Shell::AST::Glob> Shell::Parser::create<Shell::AST::Glob, AK::String>(AK::String&&)
Line
Count
Source
130
11.5k
{
131
11.5k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
11.5k
}
AK::NonnullRefPtr<Shell::AST::Range> Shell::Parser::create<Shell::AST::Range, AK::NonnullRefPtr<Shell::AST::Node>, AK::NonnullRefPtr<Shell::AST::Node> >(AK::NonnullRefPtr<Shell::AST::Node>&&, AK::NonnullRefPtr<Shell::AST::Node>&&)
Line
Count
Source
130
7.50k
{
131
7.50k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
7.50k
}
AK::NonnullRefPtr<Shell::AST::Range> Shell::Parser::create<Shell::AST::Range, AK::NonnullRefPtr<Shell::AST::Node>, AK::NonnullRefPtr<Shell::AST::SyntaxError> >(AK::NonnullRefPtr<Shell::AST::Node>&&, AK::NonnullRefPtr<Shell::AST::SyntaxError>&&)
Line
Count
Source
130
16
{
131
16
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
16
}
AK::NonnullRefPtr<Shell::AST::BraceExpansion> Shell::Parser::create<Shell::AST::BraceExpansion, AK::Vector<AK::NonnullRefPtr<Shell::AST::Node>, 0ul> >(AK::Vector<AK::NonnullRefPtr<Shell::AST::Node>, 0ul>&&)
Line
Count
Source
130
74.2k
{
131
74.2k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
74.2k
}
AK::NonnullRefPtr<Shell::AST::Heredoc> Shell::Parser::create<Shell::AST::Heredoc, AK::String&, bool&, bool&>(AK::String&, bool&, bool&)
Line
Count
Source
130
7.33k
{
131
7.33k
    return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...));
132
7.33k
}
133
134
[[nodiscard]] OwnPtr<Parser::ScopedOffset> Parser::push_start()
135
62.4M
{
136
62.4M
    return make<ScopedOffset>(m_rule_start_offsets, m_rule_start_lines, m_offset, m_line.line_number, m_line.line_column);
137
62.4M
}
138
139
Parser::Offset Parser::current_position()
140
227M
{
141
227M
    return Offset { m_offset, { m_line.line_number, m_line.line_column } };
142
227M
}
143
144
static constexpr bool is_whitespace(char c)
145
16.8M
{
146
16.8M
    return c == ' ' || c == '\t';
147
16.8M
}
148
149
static constexpr bool is_digit(char c)
150
11.9M
{
151
11.9M
    return c <= '9' && c >= '0';
152
11.9M
}
153
154
static constexpr auto is_not(char c)
155
7.23M
{
156
210M
    return [c](char ch) { return ch != c; };
157
7.23M
}
158
159
static inline char to_byte(char a, char b)
160
194k
{
161
194k
    char buf[3] { a, b, 0 };
162
194k
    return strtol(buf, nullptr, 16);
163
194k
}
164
165
RefPtr<AST::Node> Parser::parse()
166
5.45k
{
167
5.45k
    m_offset = 0;
168
5.45k
    m_line = { 0, 0 };
169
170
5.45k
    auto toplevel = parse_toplevel();
171
172
5.45k
    if (m_offset < m_input.length()) {
173
        // Parsing stopped midway, this is a syntax error.
174
396
        auto error_start = push_start();
175
55.8M
        while (!at_end())
176
55.8M
            consume();
177
396
        auto syntax_error_node = create<AST::SyntaxError>("Unexpected tokens past the end"_string);
178
396
        if (!toplevel)
179
6
            toplevel = move(syntax_error_node);
180
390
        else if (!toplevel->is_syntax_error())
181
90
            toplevel->set_is_syntax_error(*syntax_error_node);
182
396
    }
183
184
5.45k
    return toplevel;
185
5.45k
}
186
187
RefPtr<AST::Node> Parser::parse_as_single_expression()
188
0
{
189
0
    auto input = Shell::escape_token_for_double_quotes(m_input);
190
0
    Parser parser { input };
191
0
    return parser.parse_expression();
192
0
}
193
194
Vector<NonnullRefPtr<AST::Node>> Parser::parse_as_multiple_expressions()
195
0
{
196
0
    Vector<NonnullRefPtr<AST::Node>> nodes;
197
0
    for (;;) {
198
0
        consume_while(is_whitespace);
199
0
        auto node = parse_expression();
200
0
        if (!node)
201
0
            node = parse_redirection();
202
0
        if (!node)
203
0
            return nodes;
204
0
        nodes.append(node.release_nonnull());
205
0
    }
206
0
}
207
208
RefPtr<AST::Node> Parser::parse_toplevel()
209
24.3k
{
210
24.3k
    auto rule_start = push_start();
211
212
24.3k
    SequenceParseResult result;
213
24.3k
    Vector<NonnullRefPtr<AST::Node>> sequence;
214
24.3k
    Vector<AST::Position> positions;
215
896k
    do {
216
896k
        result = parse_sequence();
217
896k
        if (result.entries.is_empty())
218
4.38k
            break;
219
220
891k
        sequence.extend(move(result.entries));
221
891k
        positions.extend(result.separator_positions.span());
222
891k
    } while (result.decision == ShouldReadMoreSequences::Yes);
223
224
24.3k
    if (sequence.is_empty())
225
2.30k
        return nullptr;
226
227
22.0k
    return create<AST::Execute>(
228
22.0k
        create<AST::Sequence>(move(sequence), move(positions)));
229
24.3k
}
230
231
Parser::SequenceParseResult Parser::parse_sequence()
232
896k
{
233
896k
    Vector<NonnullRefPtr<AST::Node>> left;
234
1.53M
    auto read_terminators = [&](bool consider_tabs_and_spaces) {
235
1.53M
        if (m_heredoc_initiations.is_empty()) {
236
1.53M
        discard_terminators:;
237
1.53M
            consume_while(is_any_of(consider_tabs_and_spaces ? " \t\n;"sv : "\n;"sv));
238
1.53M
        } else {
239
7.82k
            for (;;) {
240
7.82k
                if (consider_tabs_and_spaces && (peek() == '\t' || peek() == ' ')) {
241
4
                    consume();
242
4
                    continue;
243
4
                }
244
7.81k
                if (peek() == ';') {
245
0
                    consume();
246
0
                    continue;
247
0
                }
248
7.81k
                if (peek() == '\n') {
249
1.05k
                    auto rule_start = push_start();
250
1.05k
                    consume();
251
1.05k
                    if (!parse_heredoc_entries()) {
252
0
                        StringBuilder error_builder;
253
0
                        error_builder.append("Expected to find heredoc entries for "sv);
254
0
                        bool first = true;
255
0
                        for (auto& entry : m_heredoc_initiations) {
256
0
                            if (first)
257
0
                                error_builder.appendff("{} (at {}:{})", entry.end, entry.node->position().start_line.line_column, entry.node->position().start_line.line_number);
258
0
                            else
259
0
                                error_builder.appendff(", {} (at {}:{})", entry.end, entry.node->position().start_line.line_column, entry.node->position().start_line.line_number);
260
0
                            first = false;
261
0
                        }
262
0
                        left.append(create<AST::SyntaxError>(TRY_OR_RESOLVE_TO_ERROR_STRING(error_builder.to_string()), true));
263
                        // Just read the rest of the newlines
264
0
                        goto discard_terminators;
265
0
                    }
266
1.05k
                    continue;
267
1.05k
                }
268
6.76k
                break;
269
7.81k
            }
270
6.76k
        }
271
1.53M
    };
272
273
896k
    read_terminators(true);
274
275
896k
    auto rule_start = push_start();
276
896k
    {
277
896k
        auto var_decls = parse_variable_decls();
278
896k
        if (var_decls)
279
984
            left.append(var_decls.release_nonnull());
280
896k
    }
281
282
896k
    auto pos_before_seps = save_offset();
283
284
896k
    switch (peek()) {
285
2.13k
    case '}':
286
2.13k
        return { move(left), {}, ShouldReadMoreSequences::No };
287
333
    case '\n':
288
333
        read_terminators(false);
289
333
        [[fallthrough]];
290
455
    case ';': {
291
455
        if (left.is_empty())
292
0
            break;
293
294
455
        consume_while(is_any_of("\n;"sv));
295
455
        auto pos_after_seps = save_offset();
296
455
        AST::Position separator_position { pos_before_seps.offset, pos_after_seps.offset, pos_before_seps.line, pos_after_seps.line };
297
298
455
        return { move(left), { move(separator_position) }, ShouldReadMoreSequences::Yes };
299
455
    }
300
893k
    default:
301
893k
        break;
302
896k
    }
303
304
893k
    auto first_entry = parse_function_decl();
305
306
893k
    Vector<AST::Position> separator_positions;
307
308
893k
    if (!first_entry)
309
893k
        first_entry = parse_or_logical_sequence();
310
311
893k
    if (!first_entry)
312
2.26k
        return { move(left), {}, ShouldReadMoreSequences::No };
313
314
891k
    left.append(first_entry.release_nonnull());
315
891k
    separator_positions.empend(pos_before_seps.offset, pos_before_seps.offset, pos_before_seps.line, pos_before_seps.line);
316
317
891k
    consume_while(is_whitespace);
318
319
891k
    pos_before_seps = save_offset();
320
891k
    switch (peek()) {
321
641k
    case '\n':
322
641k
        read_terminators(false);
323
641k
        [[fallthrough]];
324
870k
    case ';': {
325
870k
        consume_while(is_any_of("\n;"sv));
326
870k
        auto pos_after_seps = save_offset();
327
870k
        separator_positions.empend(pos_before_seps.offset, pos_after_seps.offset, pos_before_seps.line, pos_after_seps.line);
328
870k
        return { move(left), move(separator_positions), ShouldReadMoreSequences::Yes };
329
641k
    }
330
550
    case '&': {
331
550
        consume();
332
550
        auto pos_after_seps = save_offset();
333
550
        auto bg = create<AST::Background>(left.take_last()); // Execute Background
334
550
        left.append(move(bg));
335
550
        separator_positions.empend(pos_before_seps.offset, pos_after_seps.offset, pos_before_seps.line, pos_after_seps.line);
336
550
        return { move(left), move(separator_positions), ShouldReadMoreSequences::Yes };
337
641k
    }
338
19.9k
    default:
339
19.9k
        return { move(left), move(separator_positions), ShouldReadMoreSequences::No };
340
891k
    }
341
891k
}
342
343
RefPtr<AST::Node> Parser::parse_variable_decls()
344
896k
{
345
896k
    auto rule_start = push_start();
346
347
896k
    consume_while(is_whitespace);
348
349
896k
    auto pos_before_name = save_offset();
350
896k
    auto var_name = consume_while(is_word_character);
351
896k
    if (var_name.is_empty())
352
639k
        return nullptr;
353
354
256k
    if (!expect('=')) {
355
220k
        restore_to(pos_before_name.offset, pos_before_name.line);
356
220k
        return nullptr;
357
220k
    }
358
359
36.2k
    auto name_expr = create<AST::BarewordLiteral>(TRY_OR_THROW_PARSE_ERROR(String::from_utf8(var_name)));
360
361
0
    auto start = push_start();
362
36.2k
    auto expression = parse_expression();
363
36.2k
    if (!expression || expression->is_syntax_error()) {
364
35.3k
        restore_to(*start);
365
35.3k
        if (peek() == '(') {
366
2
            consume();
367
2
            auto command = parse_pipe_sequence();
368
2
            if (!command)
369
0
                restore_to(*start);
370
2
            else if (!expect(')'))
371
2
                command->set_is_syntax_error(*create<AST::SyntaxError>("Expected a terminating close paren"_string, true));
372
2
            expression = command;
373
2
        }
374
35.3k
    }
375
36.2k
    if (!expression) {
376
35.2k
        if (is_whitespace(peek())) {
377
1
            auto string_start = push_start();
378
1
            expression = create<AST::StringLiteral>(String {}, AST::StringLiteral::EnclosureType::None);
379
35.2k
        } else {
380
35.2k
            restore_to(pos_before_name.offset, pos_before_name.line);
381
35.2k
            return nullptr;
382
35.2k
        }
383
35.2k
    }
384
385
984
    Vector<AST::VariableDeclarations::Variable> variables;
386
984
    variables.append({ move(name_expr), expression.release_nonnull() });
387
388
984
    if (consume_while(is_whitespace).is_empty())
389
983
        return create<AST::VariableDeclarations>(move(variables));
390
391
1
    auto rest = parse_variable_decls();
392
1
    if (!rest)
393
1
        return create<AST::VariableDeclarations>(move(variables));
394
395
0
    VERIFY(rest->is_variable_decls());
396
0
    auto* rest_decl = static_cast<AST::VariableDeclarations*>(rest.ptr());
397
398
0
    variables.extend(rest_decl->variables());
399
400
0
    return create<AST::VariableDeclarations>(move(variables));
401
0
}
402
403
RefPtr<AST::Node> Parser::parse_function_decl()
404
893k
{
405
893k
    auto rule_start = push_start();
406
407
893k
    auto restore = [&] {
408
893k
        restore_to(*rule_start);
409
893k
        return nullptr;
410
893k
    };
411
412
893k
    consume_while(is_whitespace);
413
893k
    auto pos_before_name = save_offset();
414
893k
    auto function_name = consume_while(is_word_character);
415
893k
    auto pos_after_name = save_offset();
416
893k
    if (function_name.is_empty())
417
638k
        return restore();
418
419
255k
    if (!expect('('))
420
255k
        return restore();
421
422
28
    Vector<AST::NameWithPosition> arguments;
423
30
    for (;;) {
424
30
        consume_while(is_whitespace);
425
426
30
        if (expect(')'))
427
0
            break;
428
429
30
        auto name_offset = m_offset;
430
30
        auto start_line = line();
431
30
        auto arg_name = consume_while(is_word_character);
432
30
        if (arg_name.is_empty()) {
433
            // FIXME: Should this be a syntax error, or just return?
434
28
            return restore();
435
28
        }
436
2
        arguments.append({ TRY_OR_THROW_PARSE_ERROR(String::from_utf8(arg_name)), { name_offset, m_offset, start_line, line() } });
437
2
    }
438
439
0
    consume_while(is_any_of("\n\t "sv));
440
441
0
    {
442
0
        RefPtr<AST::Node> syntax_error;
443
0
        {
444
0
            auto obrace_error_start = push_start();
445
0
            syntax_error = create<AST::SyntaxError>("Expected an open brace '{' to start a function body"_string, true);
446
0
        }
447
0
        if (!expect('{')) {
448
0
            return create<AST::FunctionDeclaration>(
449
0
                AST::NameWithPosition {
450
0
                    TRY_OR_THROW_PARSE_ERROR(String::from_utf8(function_name)),
451
0
                    { pos_before_name.offset, pos_after_name.offset, pos_before_name.line, pos_after_name.line } },
452
0
                move(arguments),
453
0
                move(syntax_error));
454
0
        }
455
0
    }
456
457
0
    TemporaryChange controls { m_continuation_controls_allowed, false };
458
0
    auto body = parse_toplevel();
459
460
0
    {
461
0
        RefPtr<AST::SyntaxError> syntax_error;
462
0
        {
463
0
            auto cbrace_error_start = push_start();
464
0
            syntax_error = create<AST::SyntaxError>("Expected a close brace '}' to end a function body"_string, true);
465
0
        }
466
0
        if (!expect('}')) {
467
0
            if (body)
468
0
                body->set_is_syntax_error(*syntax_error);
469
0
            else
470
0
                body = move(syntax_error);
471
472
0
            return create<AST::FunctionDeclaration>(
473
0
                AST::NameWithPosition {
474
0
                    TRY_OR_THROW_PARSE_ERROR(String::from_utf8(function_name)),
475
0
                    { pos_before_name.offset, pos_after_name.offset, pos_before_name.line, pos_after_name.line } },
476
0
                move(arguments),
477
0
                move(body));
478
0
        }
479
0
    }
480
481
0
    return create<AST::FunctionDeclaration>(
482
0
        AST::NameWithPosition {
483
0
            TRY_OR_THROW_PARSE_ERROR(String::from_utf8(function_name)),
484
0
            { pos_before_name.offset, pos_after_name.offset, pos_before_name.line, pos_after_name.line } },
485
0
        move(arguments),
486
0
        move(body));
487
0
}
488
489
RefPtr<AST::Node> Parser::parse_or_logical_sequence()
490
896k
{
491
896k
    consume_while(is_whitespace);
492
896k
    auto rule_start = push_start();
493
896k
    auto and_sequence = parse_and_logical_sequence();
494
896k
    if (!and_sequence)
495
2.26k
        return nullptr;
496
497
894k
    consume_while(is_whitespace);
498
894k
    auto pos_before_or = save_offset();
499
894k
    if (!expect("||"sv))
500
894k
        return and_sequence;
501
226
    auto pos_after_or = save_offset();
502
503
226
    auto right_and_sequence = parse_and_logical_sequence();
504
226
    if (!right_and_sequence)
505
7
        right_and_sequence = create<AST::SyntaxError>("Expected an expression after '||'"_string, true);
506
507
226
    return create<AST::Or>(
508
226
        and_sequence.release_nonnull(),
509
226
        right_and_sequence.release_nonnull(),
510
226
        AST::Position { pos_before_or.offset, pos_after_or.offset, pos_before_or.line, pos_after_or.line });
511
894k
}
512
513
RefPtr<AST::Node> Parser::parse_and_logical_sequence()
514
899k
{
515
899k
    consume_while(is_whitespace);
516
899k
    auto rule_start = push_start();
517
899k
    auto pipe_sequence = parse_pipe_sequence();
518
899k
    if (!pipe_sequence)
519
2.29k
        return nullptr;
520
521
896k
    consume_while(is_whitespace);
522
896k
    auto pos_before_and = save_offset();
523
896k
    if (!expect("&&"sv))
524
894k
        return pipe_sequence;
525
2.15k
    auto pos_after_end = save_offset();
526
527
2.15k
    auto right_and_sequence = parse_and_logical_sequence();
528
2.15k
    if (!right_and_sequence)
529
28
        right_and_sequence = create<AST::SyntaxError>("Expected an expression after '&&'"_string, true);
530
531
2.15k
    return create<AST::And>(
532
2.15k
        pipe_sequence.release_nonnull(),
533
2.15k
        right_and_sequence.release_nonnull(),
534
2.15k
        AST::Position { pos_before_and.offset, pos_after_end.offset, pos_before_and.line, pos_after_end.line });
535
896k
}
536
537
RefPtr<AST::Node> Parser::parse_pipe_sequence()
538
929k
{
539
929k
    auto rule_start = push_start();
540
929k
    auto left = parse_control_structure();
541
929k
    if (!left) {
542
917k
        if (auto cmd = parse_command())
543
914k
            left = cmd;
544
2.89k
        else
545
2.89k
            return nullptr;
546
917k
    }
547
548
926k
    consume_while(is_whitespace);
549
550
926k
    if (peek() != '|')
551
907k
        return left;
552
553
18.5k
    auto before_pipe = save_offset();
554
18.5k
    consume();
555
18.5k
    auto also_pipe_stderr = peek() == '&';
556
18.5k
    if (also_pipe_stderr) {
557
0
        consume();
558
559
0
        RefPtr<AST::Node> redirection;
560
0
        {
561
0
            auto redirection_start = push_start();
562
0
            redirection = create<AST::Fd2FdRedirection>(STDERR_FILENO, STDOUT_FILENO);
563
0
        }
564
565
0
        left = create<AST::Join>(left.release_nonnull(), redirection.release_nonnull());
566
0
    }
567
568
18.5k
    if (auto pipe_seq = parse_pipe_sequence()) {
569
18.2k
        return create<AST::Pipe>(left.release_nonnull(), pipe_seq.release_nonnull()); // Pipe
570
18.2k
    }
571
572
281
    restore_to(before_pipe.offset, before_pipe.line);
573
281
    return left;
574
18.5k
}
575
576
RefPtr<AST::Node> Parser::parse_command()
577
1.94M
{
578
1.94M
    auto rule_start = push_start();
579
1.94M
    consume_while(is_whitespace);
580
581
1.94M
    auto redir = parse_redirection();
582
1.94M
    if (!redir) {
583
1.85M
        auto list_expr = parse_list_expression();
584
1.85M
        if (!list_expr)
585
917k
            return nullptr;
586
587
940k
        auto cast = create<AST::CastToCommand>(list_expr.release_nonnull()); // Cast List Command
588
589
940k
        auto next_command = parse_command();
590
940k
        if (!next_command)
591
908k
            return cast;
592
593
31.7k
        return create<AST::Join>(move(cast), next_command.release_nonnull()); // Join List Command
594
940k
    }
595
596
86.9k
    auto command = parse_command();
597
86.9k
    if (!command)
598
5.90k
        return redir;
599
600
81.0k
    return create<AST::Join>(redir.release_nonnull(), command.release_nonnull()); // Join Command Command
601
86.9k
}
602
603
RefPtr<AST::Node> Parser::parse_control_structure()
604
929k
{
605
929k
    auto rule_start = push_start();
606
929k
    consume_while(is_whitespace);
607
929k
    if (auto control = parse_continuation_control())
608
0
        return control;
609
610
929k
    if (auto for_loop = parse_for_loop())
611
668
        return for_loop;
612
613
928k
    if (auto loop = parse_loop_loop())
614
0
        return loop;
615
616
928k
    if (auto if_expr = parse_if_expr())
617
1.59k
        return if_expr;
618
619
927k
    if (auto subshell = parse_subshell())
620
6.42k
        return subshell;
621
622
920k
    if (auto match = parse_match_expr())
623
3.51k
        return match;
624
625
917k
    return nullptr;
626
920k
}
627
628
RefPtr<AST::Node> Parser::parse_continuation_control()
629
929k
{
630
929k
    if (!m_continuation_controls_allowed)
631
922k
        return nullptr;
632
633
6.76k
    auto rule_start = push_start();
634
635
6.76k
    if (expect("break"sv)) {
636
0
        {
637
0
            auto break_end = push_start();
638
0
            if (consume_while(is_any_of(" \t\n;"sv)).is_empty()) {
639
0
                restore_to(*rule_start);
640
0
                return nullptr;
641
0
            }
642
0
            restore_to(*break_end);
643
0
        }
644
0
        return create<AST::ContinuationControl>(AST::ContinuationControl::Break);
645
0
    }
646
647
6.76k
    if (expect("continue"sv)) {
648
0
        {
649
0
            auto continue_end = push_start();
650
0
            if (consume_while(is_any_of(" \t\n;"sv)).is_empty()) {
651
0
                restore_to(*rule_start);
652
0
                return nullptr;
653
0
            }
654
0
            restore_to(*continue_end);
655
0
        }
656
0
        return create<AST::ContinuationControl>(AST::ContinuationControl::Continue);
657
0
    }
658
659
6.76k
    return nullptr;
660
6.76k
}
661
662
RefPtr<AST::Node> Parser::parse_for_loop()
663
929k
{
664
929k
    auto rule_start = push_start();
665
929k
    if (!expect("for"sv))
666
928k
        return nullptr;
667
668
668
    if (consume_while(is_any_of(" \t\n"sv)).is_empty()) {
669
0
        restore_to(*rule_start);
670
0
        return nullptr;
671
0
    }
672
673
668
    Optional<AST::NameWithPosition> index_variable_name, variable_name;
674
668
    Optional<AST::Position> in_start_position, index_start_position;
675
676
668
    auto offset_before_index = current_position();
677
668
    if (expect("index"sv)) {
678
0
        auto offset = current_position();
679
0
        if (!consume_while(is_whitespace).is_empty()) {
680
0
            auto offset_before_variable = current_position();
681
0
            auto variable = consume_while(is_word_character);
682
0
            if (!variable.is_empty()) {
683
0
                index_start_position = AST::Position { offset_before_index.offset, offset.offset, offset_before_index.line, offset.line };
684
685
0
                auto offset_after_variable = current_position();
686
0
                index_variable_name = AST::NameWithPosition {
687
0
                    TRY_OR_THROW_PARSE_ERROR(String::from_utf8(variable)),
688
0
                    { offset_before_variable.offset, offset_after_variable.offset, offset_before_variable.line, offset_after_variable.line },
689
0
                };
690
691
0
                consume_while(is_whitespace);
692
0
            } else {
693
0
                restore_to(offset_before_index.offset, offset_before_index.line);
694
0
            }
695
0
        } else {
696
0
            restore_to(offset_before_index.offset, offset_before_index.line);
697
0
        }
698
0
    }
699
700
668
    auto variable_name_start_offset = current_position();
701
668
    auto name = consume_while(is_word_character);
702
668
    auto variable_name_end_offset = current_position();
703
668
    if (!name.is_empty()) {
704
0
        variable_name = AST::NameWithPosition {
705
0
            TRY_OR_THROW_PARSE_ERROR(String::from_utf8(name)),
706
0
            { variable_name_start_offset.offset, variable_name_end_offset.offset, variable_name_start_offset.line, variable_name_end_offset.line }
707
0
        };
708
0
        consume_while(is_whitespace);
709
0
        auto in_error_start = push_start();
710
0
        if (!expect("in"sv)) {
711
0
            auto syntax_error = create<AST::SyntaxError>("Expected 'in' after a variable name in a 'for' loop"_string, true);
712
0
            return create<AST::ForLoop>(move(variable_name), move(index_variable_name), move(syntax_error), nullptr); // ForLoop Var Iterated Block
713
0
        }
714
0
        in_start_position = AST::Position { in_error_start->offset, m_offset, in_error_start->line, line() };
715
0
    }
716
717
668
    consume_while(is_whitespace);
718
668
    RefPtr<AST::Node> iterated_expression;
719
668
    {
720
668
        auto iter_error_start = push_start();
721
668
        iterated_expression = parse_expression();
722
668
        if (!iterated_expression)
723
64
            iterated_expression = create<AST::SyntaxError>("Expected an expression in 'for' loop"_string, true);
724
668
    }
725
726
668
    consume_while(is_any_of(" \t\n"sv));
727
668
    {
728
668
        auto obrace_error_start = push_start();
729
668
        if (!expect('{')) {
730
0
            auto syntax_error = create<AST::SyntaxError>("Expected an open brace '{' to start a 'for' loop body"_string, true);
731
0
            return create<AST::ForLoop>(move(variable_name), move(index_variable_name), move(iterated_expression), move(syntax_error), move(in_start_position), move(index_start_position)); // ForLoop Var Iterated Block
732
0
        }
733
668
    }
734
735
668
    TemporaryChange controls { m_continuation_controls_allowed, true };
736
668
    auto body = parse_toplevel();
737
738
668
    {
739
668
        auto cbrace_error_start = push_start();
740
668
        if (!expect('}')) {
741
604
            auto error_start = push_start();
742
604
            auto syntax_error = create<AST::SyntaxError>("Expected a close brace '}' to end a 'for' loop body"_string, true);
743
604
            if (body)
744
604
                body->set_is_syntax_error(*syntax_error);
745
0
            else
746
0
                body = syntax_error;
747
604
        }
748
668
    }
749
750
668
    return create<AST::ForLoop>(move(variable_name), move(index_variable_name), move(iterated_expression), move(body), move(in_start_position), move(index_start_position)); // ForLoop Var Iterated Block
751
668
}
752
753
RefPtr<AST::Node> Parser::parse_loop_loop()
754
928k
{
755
928k
    auto rule_start = push_start();
756
928k
    if (!expect("loop"sv))
757
928k
        return nullptr;
758
759
0
    if (consume_while(is_any_of(" \t\n"sv)).is_empty()) {
760
0
        restore_to(*rule_start);
761
0
        return nullptr;
762
0
    }
763
764
0
    {
765
0
        auto obrace_error_start = push_start();
766
0
        if (!expect('{')) {
767
0
            auto syntax_error = create<AST::SyntaxError>("Expected an open brace '{' to start a 'loop' loop body"_string, true);
768
0
            return create<AST::ForLoop>(AST::NameWithPosition {}, AST::NameWithPosition {}, nullptr, move(syntax_error)); // ForLoop null null Block
769
0
        }
770
0
    }
771
772
0
    TemporaryChange controls { m_continuation_controls_allowed, true };
773
0
    auto body = parse_toplevel();
774
775
0
    {
776
0
        auto cbrace_error_start = push_start();
777
0
        if (!expect('}')) {
778
0
            auto error_start = push_start();
779
0
            auto syntax_error = create<AST::SyntaxError>("Expected a close brace '}' to end a 'loop' loop body"_string, true);
780
0
            if (body)
781
0
                body->set_is_syntax_error(*syntax_error);
782
0
            else
783
0
                body = syntax_error;
784
0
        }
785
0
    }
786
787
0
    return create<AST::ForLoop>(AST::NameWithPosition {}, AST::NameWithPosition {}, nullptr, move(body)); // ForLoop null null Block
788
0
}
789
790
RefPtr<AST::Node> Parser::parse_if_expr()
791
930k
{
792
930k
    auto rule_start = push_start();
793
930k
    if (!expect("if"sv))
794
927k
        return nullptr;
795
796
3.40k
    if (consume_while(is_any_of(" \t\n"sv)).is_empty()) {
797
5
        restore_to(*rule_start);
798
5
        return nullptr;
799
5
    }
800
801
3.40k
    RefPtr<AST::Node> condition;
802
3.40k
    {
803
3.40k
        auto cond_error_start = push_start();
804
3.40k
        condition = parse_or_logical_sequence();
805
3.40k
        if (!condition)
806
0
            condition = create<AST::SyntaxError>("Expected a logical sequence after 'if'"_string, true);
807
3.40k
    }
808
809
3.40k
    auto parse_braced_toplevel = [&]() -> RefPtr<AST::Node> {
810
3.40k
        RefPtr<AST::Node> body;
811
3.40k
        {
812
3.40k
            auto obrace_error_start = push_start();
813
3.40k
            if (!expect('{')) {
814
1.59k
                body = create<AST::SyntaxError>("Expected an open brace '{' to start an 'if' true branch"_string, true);
815
1.59k
            }
816
3.40k
        }
817
818
3.40k
        if (!body)
819
1.81k
            body = parse_toplevel();
820
821
3.40k
        {
822
3.40k
            auto cbrace_error_start = push_start();
823
3.40k
            if (!expect('}')) {
824
1.27k
                auto error_start = push_start();
825
1.27k
                RefPtr<AST::SyntaxError> syntax_error = create<AST::SyntaxError>("Expected a close brace '}' to end an 'if' true branch"_string, true);
826
1.27k
                if (body)
827
1.27k
                    body->set_is_syntax_error(*syntax_error);
828
0
                else
829
0
                    body = syntax_error;
830
1.27k
            }
831
3.40k
        }
832
833
3.40k
        return body;
834
3.40k
    };
835
836
3.40k
    consume_while(is_any_of(" \t\n"sv));
837
3.40k
    auto true_branch = parse_braced_toplevel();
838
839
3.40k
    auto end_before_else = m_offset;
840
3.40k
    auto line_before_else = line();
841
3.40k
    consume_while(is_any_of(" \t\n"sv));
842
3.40k
    Optional<AST::Position> else_position;
843
3.40k
    {
844
3.40k
        auto else_start = push_start();
845
3.40k
        if (expect("else"sv))
846
2.13k
            else_position = AST::Position { else_start->offset, m_offset, else_start->line, line() };
847
1.27k
        else
848
1.27k
            restore_to(end_before_else, line_before_else);
849
3.40k
    }
850
851
3.40k
    if (else_position.has_value()) {
852
2.13k
        consume_while(is_any_of(" \t\n"sv));
853
2.13k
        if (peek() == '{') {
854
0
            auto false_branch = parse_braced_toplevel();
855
0
            return create<AST::IfCond>(else_position, condition.release_nonnull(), move(true_branch), move(false_branch)); // If expr true_branch Else false_branch
856
0
        }
857
858
2.13k
        auto else_if_branch = parse_if_expr();
859
2.13k
        return create<AST::IfCond>(else_position, condition.release_nonnull(), move(true_branch), move(else_if_branch)); // If expr true_branch Else If ...
860
2.13k
    }
861
862
1.27k
    return create<AST::IfCond>(else_position, condition.release_nonnull(), move(true_branch), nullptr); // If expr true_branch
863
3.40k
}
864
865
RefPtr<AST::Node> Parser::parse_subshell()
866
927k
{
867
927k
    auto rule_start = push_start();
868
927k
    if (!expect('{'))
869
920k
        return nullptr;
870
871
6.42k
    auto body = parse_toplevel();
872
873
6.42k
    {
874
6.42k
        auto cbrace_error_start = push_start();
875
6.42k
        if (!expect('}')) {
876
5.38k
            auto error_start = push_start();
877
5.38k
            RefPtr<AST::SyntaxError> syntax_error = create<AST::SyntaxError>("Expected a close brace '}' to end a subshell"_string, true);
878
5.38k
            if (body)
879
5.34k
                body->set_is_syntax_error(*syntax_error);
880
39
            else
881
39
                body = syntax_error;
882
5.38k
        }
883
6.42k
    }
884
885
6.42k
    return create<AST::Subshell>(move(body));
886
927k
}
887
888
RefPtr<AST::Node> Parser::parse_match_expr()
889
920k
{
890
920k
    auto rule_start = push_start();
891
920k
    if (!expect("match"sv))
892
917k
        return nullptr;
893
894
3.51k
    if (consume_while(is_whitespace).is_empty()) {
895
6
        restore_to(*rule_start);
896
6
        return nullptr;
897
6
    }
898
899
3.51k
    auto match_expression = parse_expression();
900
3.51k
    if (!match_expression) {
901
64
        return create<AST::MatchExpr>(
902
64
            create<AST::SyntaxError>("Expected an expression after 'match'"_string, true),
903
64
            String {}, Optional<AST::Position> {}, Vector<AST::MatchEntry> {});
904
64
    }
905
906
3.44k
    consume_while(is_any_of(" \t\n"sv));
907
908
3.44k
    String match_name;
909
3.44k
    Optional<AST::Position> as_position;
910
3.44k
    auto as_start = m_offset;
911
3.44k
    auto as_line = line();
912
3.44k
    if (expect("as"sv)) {
913
0
        as_position = AST::Position { as_start, m_offset, as_line, line() };
914
915
0
        if (consume_while(is_any_of(" \t\n"sv)).is_empty()) {
916
0
            auto node = create<AST::MatchExpr>(
917
0
                match_expression.release_nonnull(),
918
0
                String {}, move(as_position), Vector<AST::MatchEntry> {});
919
0
            node->set_is_syntax_error(create<AST::SyntaxError>("Expected whitespace after 'as' in 'match'"_string, true));
920
0
            return node;
921
0
        }
922
923
0
        match_name = TRY_OR_THROW_PARSE_ERROR(String::from_utf8(consume_while(is_word_character)));
924
0
        if (match_name.is_empty()) {
925
0
            auto node = create<AST::MatchExpr>(
926
0
                match_expression.release_nonnull(),
927
0
                String {}, move(as_position), Vector<AST::MatchEntry> {});
928
0
            node->set_is_syntax_error(create<AST::SyntaxError>("Expected an identifier after 'as' in 'match'"_string, true));
929
0
            return node;
930
0
        }
931
0
    }
932
933
3.44k
    consume_while(is_any_of(" \t\n"sv));
934
935
3.44k
    if (!expect('{')) {
936
662
        auto node = create<AST::MatchExpr>(
937
662
            match_expression.release_nonnull(),
938
662
            move(match_name), move(as_position), Vector<AST::MatchEntry> {});
939
662
        node->set_is_syntax_error(create<AST::SyntaxError>("Expected an open brace '{' to start a 'match' entry list"_string, true));
940
662
        return node;
941
662
    }
942
943
2.78k
    consume_while(is_any_of(" \t\n"sv));
944
945
2.78k
    Vector<AST::MatchEntry> entries;
946
12.7k
    for (;;) {
947
12.7k
        auto entry = parse_match_entry();
948
12.7k
        consume_while(is_any_of(" \t\n"sv));
949
12.7k
        if (entry.options.visit([](auto& x) { return x.is_empty(); }))
Parser.cpp:auto Shell::Parser::parse_match_expr()::$_0::operator()<AK::Vector<AK::NonnullRefPtr<Shell::AST::Node>, 0ul> >(AK::Vector<AK::NonnullRefPtr<Shell::AST::Node>, 0ul>&) const
Line
Count
Source
949
5.89k
        if (entry.options.visit([](auto& x) { return x.is_empty(); }))
Parser.cpp:auto Shell::Parser::parse_match_expr()::$_0::operator()<AK::Vector<regex::Regex<regex::ECMA262Parser>, 0ul> >(AK::Vector<regex::Regex<regex::ECMA262Parser>, 0ul>&) const
Line
Count
Source
949
6.85k
        if (entry.options.visit([](auto& x) { return x.is_empty(); }))
950
2.78k
            break;
951
952
9.96k
        entries.append(move(entry));
953
9.96k
    }
954
955
2.78k
    consume_while(is_any_of(" \t\n"sv));
956
957
2.78k
    if (!expect('}')) {
958
2.78k
        auto node = create<AST::MatchExpr>(
959
2.78k
            match_expression.release_nonnull(),
960
2.78k
            move(match_name), move(as_position), move(entries));
961
2.78k
        node->set_is_syntax_error(create<AST::SyntaxError>("Expected a close brace '}' to end a 'match' entry list"_string, true));
962
2.78k
        return node;
963
2.78k
    }
964
965
3
    return create<AST::MatchExpr>(match_expression.release_nonnull(), move(match_name), move(as_position), move(entries));
966
2.78k
}
967
968
AST::MatchEntry Parser::parse_match_entry()
969
12.7k
{
970
12.7k
    auto rule_start = push_start();
971
972
12.7k
    Vector<NonnullRefPtr<AST::Node>> patterns;
973
12.7k
    Vector<Regex<ECMA262>> regexps;
974
12.7k
    Vector<AST::Position> pipe_positions;
975
12.7k
    Optional<Vector<String>> match_names;
976
12.7k
    Optional<AST::Position> match_as_position;
977
12.7k
    enum {
978
12.7k
        Regex,
979
12.7k
        Glob,
980
12.7k
    } pattern_kind;
981
982
12.7k
    consume_while(is_any_of(" \t\n"sv));
983
984
12.7k
    auto regex_pattern = parse_regex_pattern();
985
12.7k
    if (regex_pattern.has_value()) {
986
7.80k
        if (auto error = regex_pattern.value().parser_result.error; error != regex::Error::NoError)
987
951
            return { Vector<NonnullRefPtr<AST::Node>> {}, {}, {}, {}, create<AST::SyntaxError>(TRY_OR_RESOLVE_TO_ERROR_STRING(String::from_utf8(regex::get_error_string(error))), false) };
988
989
6.85k
        pattern_kind = Regex;
990
6.85k
        regexps.append(regex_pattern.release_value());
991
6.85k
    } else {
992
4.94k
        auto glob_pattern = parse_match_pattern();
993
4.94k
        if (!glob_pattern)
994
1.83k
            return { Vector<NonnullRefPtr<AST::Node>> {}, {}, {}, {}, create<AST::SyntaxError>("Expected a pattern in 'match' body"_string, true) };
995
996
3.11k
        pattern_kind = Glob;
997
3.11k
        patterns.append(glob_pattern.release_nonnull());
998
3.11k
    }
999
1000
9.96k
    consume_while(is_any_of(" \t\n"sv));
1001
1002
9.96k
    auto previous_pipe_start_position = m_offset;
1003
9.96k
    auto previous_pipe_start_line = line();
1004
9.96k
    RefPtr<AST::SyntaxError> error;
1005
10.3k
    while (expect('|')) {
1006
434
        pipe_positions.append({ previous_pipe_start_position, m_offset, previous_pipe_start_line, line() });
1007
434
        consume_while(is_any_of(" \t\n"sv));
1008
434
        switch (pattern_kind) {
1009
333
        case Regex: {
1010
333
            auto pattern = parse_regex_pattern();
1011
333
            if (!pattern.has_value()) {
1012
325
                error = create<AST::SyntaxError>("Expected a regex pattern to follow '|' in 'match' body"_string, true);
1013
325
                break;
1014
325
            }
1015
8
            regexps.append(pattern.release_value());
1016
8
            break;
1017
333
        }
1018
101
        case Glob: {
1019
101
            auto pattern = parse_match_pattern();
1020
101
            if (!pattern) {
1021
0
                error = create<AST::SyntaxError>("Expected a pattern to follow '|' in 'match' body"_string, true);
1022
0
                break;
1023
0
            }
1024
101
            patterns.append(pattern.release_nonnull());
1025
101
            break;
1026
101
        }
1027
434
        }
1028
1029
434
        consume_while(is_any_of(" \t\n"sv));
1030
1031
434
        previous_pipe_start_line = line();
1032
434
        previous_pipe_start_position = m_offset;
1033
434
    }
1034
1035
9.96k
    consume_while(is_any_of(" \t\n"sv));
1036
1037
9.96k
    auto as_start_position = m_offset;
1038
9.96k
    auto as_start_line = line();
1039
9.96k
    if (pattern_kind == Glob && expect("as"sv)) {
1040
189
        match_as_position = AST::Position { as_start_position, m_offset, as_start_line, line() };
1041
189
        consume_while(is_any_of(" \t\n"sv));
1042
189
        if (!expect('(')) {
1043
53
            if (!error)
1044
53
                error = create<AST::SyntaxError>("Expected an explicit list of identifiers after a pattern 'as'"_string);
1045
136
        } else {
1046
136
            match_names = Vector<String>();
1047
272
            for (;;) {
1048
272
                consume_while(is_whitespace);
1049
272
                auto name = consume_while(is_word_character);
1050
272
                if (name.is_empty())
1051
136
                    break;
1052
136
                match_names->append(TRY_OR(
1053
136
                    String::from_utf8(name),
1054
136
                    error = create<AST::SyntaxError>(MUST(String::from_utf8(_error.string_literal())));
1055
136
                    break;));
1056
136
            }
1057
1058
136
            if (!expect(')')) {
1059
136
                if (!error)
1060
136
                    error = create<AST::SyntaxError>("Expected a close paren ')' to end the identifier list of pattern 'as'"_string, true);
1061
136
            }
1062
136
        }
1063
189
        consume_while(is_any_of(" \t\n"sv));
1064
189
    }
1065
1066
9.96k
    if (pattern_kind == Regex) {
1067
6.85k
        Vector<String> names;
1068
6.86k
        for (auto& regex : regexps) {
1069
6.86k
            if (names.is_empty()) {
1070
6.85k
                for (auto& name : regex.parser_result.capture_groups)
1071
23
                    names.append(TRY_OR(
1072
23
                        String::from_byte_string(name),
1073
23
                        error = create<AST::SyntaxError>(MUST(String::from_utf8(_error.string_literal())));
1074
23
                        break;));
1075
6.85k
            } else {
1076
8
                size_t index = 0;
1077
8
                for (auto& name : regex.parser_result.capture_groups) {
1078
0
                    if (names.size() <= index) {
1079
0
                        names.append(TRY_OR(
1080
0
                            String::from_byte_string(name),
1081
0
                            error = create<AST::SyntaxError>(MUST(String::from_utf8(_error.string_literal())));
1082
0
                            break;));
1083
0
                        continue;
1084
0
                    }
1085
1086
0
                    if (names[index] != name.view()) {
1087
0
                        if (!error)
1088
0
                            error = create<AST::SyntaxError>("Alternative regex patterns must have the same capture groups"_string, false);
1089
0
                        break;
1090
0
                    }
1091
0
                }
1092
8
            }
1093
6.86k
        }
1094
6.85k
        match_names = move(names);
1095
6.85k
    }
1096
1097
9.96k
    if (!expect('{')) {
1098
9.88k
        if (!error)
1099
9.64k
            error = create<AST::SyntaxError>("Expected an open brace '{' to start a match entry body"_string, true);
1100
9.88k
    }
1101
1102
9.96k
    auto body = parse_toplevel();
1103
1104
9.96k
    if (!expect('}')) {
1105
9.61k
        if (!error)
1106
65
            error = create<AST::SyntaxError>("Expected a close brace '}' to end a match entry body"_string, true);
1107
9.61k
    }
1108
1109
9.96k
    if (body && error)
1110
7.75k
        body->set_is_syntax_error(*error);
1111
2.21k
    else if (error)
1112
2.19k
        body = error;
1113
1114
9.96k
    if (pattern_kind == Glob)
1115
3.11k
        return { move(patterns), move(match_names), move(match_as_position), move(pipe_positions), move(body) };
1116
1117
6.85k
    return { move(regexps), move(match_names), move(match_as_position), move(pipe_positions), move(body) };
1118
9.96k
}
1119
1120
RefPtr<AST::Node> Parser::parse_match_pattern()
1121
5.04k
{
1122
5.04k
    return parse_expression();
1123
5.04k
}
1124
1125
Optional<Regex<ECMA262>> Parser::parse_regex_pattern()
1126
13.0k
{
1127
13.0k
    auto rule_start = push_start();
1128
1129
13.0k
    auto start = m_offset;
1130
13.0k
    if (!expect("(?:"sv) && !expect("(?<"sv))
1131
5.14k
        return {};
1132
1133
7.93k
    size_t open_parens = 1;
1134
45.7M
    while (open_parens > 0) {
1135
45.6M
        if (at_end())
1136
127
            break;
1137
1138
45.6M
        if (next_is("("sv))
1139
337k
            ++open_parens;
1140
45.3M
        else if (next_is(")"sv))
1141
342k
            --open_parens;
1142
45.6M
        consume();
1143
45.6M
    }
1144
1145
7.93k
    if (open_parens != 0) {
1146
127
        restore_to(*rule_start);
1147
127
        return {};
1148
127
    }
1149
1150
7.81k
    auto end = m_offset;
1151
7.81k
    auto pattern = m_input.substring_view(start, end - start);
1152
7.81k
    return Regex<ECMA262>(pattern);
1153
7.93k
}
1154
1155
RefPtr<AST::Node> Parser::parse_redirection()
1156
3.18M
{
1157
3.18M
    auto rule_start = push_start();
1158
1159
    // heredoc entry
1160
3.18M
    if (next_is("<<-"sv) || next_is("<<~"sv))
1161
215
        return nullptr;
1162
1163
3.18M
    auto pipe_fd = 0;
1164
3.18M
    auto number = consume_while(is_digit);
1165
3.18M
    if (number.is_empty()) {
1166
1.93M
        pipe_fd = -1;
1167
1.93M
    } else {
1168
1.25M
        auto fd = number.to_number<int>();
1169
1.25M
        pipe_fd = fd.value_or(-1);
1170
1.25M
    }
1171
1172
3.18M
    switch (peek()) {
1173
72.9k
    case '>': {
1174
72.9k
        consume();
1175
72.9k
        if (peek() == '>') {
1176
9.92k
            consume();
1177
9.92k
            consume_while(is_whitespace);
1178
9.92k
            pipe_fd = pipe_fd >= 0 ? pipe_fd : STDOUT_FILENO;
1179
9.92k
            auto path = parse_expression();
1180
9.92k
            if (!path) {
1181
9.71k
                if (!at_end()) {
1182
                    // Eat a character and hope the problem goes away
1183
9.71k
                    consume();
1184
9.71k
                }
1185
9.71k
                path = create<AST::SyntaxError>("Expected a path after redirection"_string, true);
1186
9.71k
            }
1187
9.92k
            return create<AST::WriteAppendRedirection>(pipe_fd, path.release_nonnull()); // Redirection WriteAppend
1188
9.92k
        }
1189
62.9k
        if (peek() == '&') {
1190
13.9k
            consume();
1191
            // FIXME: 'fd>&-' Syntax not the best. needs discussion.
1192
13.9k
            if (peek() == '-') {
1193
13.6k
                consume();
1194
13.6k
                pipe_fd = pipe_fd >= 0 ? pipe_fd : STDOUT_FILENO;
1195
13.6k
                return create<AST::CloseFdRedirection>(pipe_fd); // Redirection CloseFd
1196
13.6k
            }
1197
274
            int dest_pipe_fd = 0;
1198
274
            auto number = consume_while(is_digit);
1199
274
            pipe_fd = pipe_fd >= 0 ? pipe_fd : STDOUT_FILENO;
1200
274
            if (number.is_empty()) {
1201
225
                dest_pipe_fd = -1;
1202
225
            } else {
1203
49
                auto fd = number.to_number<int>();
1204
49
                dest_pipe_fd = fd.value_or(-1);
1205
49
            }
1206
274
            auto redir = create<AST::Fd2FdRedirection>(pipe_fd, dest_pipe_fd); // Redirection Fd2Fd
1207
274
            if (dest_pipe_fd == -1)
1208
246
                redir->set_is_syntax_error(*create<AST::SyntaxError>("Expected a file descriptor"_string));
1209
274
            return redir;
1210
13.9k
        }
1211
49.0k
        consume_while(is_whitespace);
1212
49.0k
        pipe_fd = pipe_fd >= 0 ? pipe_fd : STDOUT_FILENO;
1213
49.0k
        auto path = parse_expression();
1214
49.0k
        if (!path) {
1215
35.7k
            if (!at_end()) {
1216
                // Eat a character and hope the problem goes away
1217
35.7k
                consume();
1218
35.7k
            }
1219
35.7k
            path = create<AST::SyntaxError>("Expected a path after redirection"_string, true);
1220
35.7k
        }
1221
49.0k
        return create<AST::WriteRedirection>(pipe_fd, path.release_nonnull()); // Redirection Write
1222
62.9k
    }
1223
18.9k
    case '<': {
1224
18.9k
        consume();
1225
18.9k
        enum {
1226
18.9k
            Read,
1227
18.9k
            ReadWrite,
1228
18.9k
        } mode { Read };
1229
1230
18.9k
        if (peek() == '>') {
1231
23
            mode = ReadWrite;
1232
23
            consume();
1233
23
        }
1234
1235
18.9k
        consume_while(is_whitespace);
1236
18.9k
        pipe_fd = pipe_fd >= 0 ? pipe_fd : STDIN_FILENO;
1237
18.9k
        auto path = parse_expression();
1238
18.9k
        if (!path) {
1239
11.0k
            if (!at_end()) {
1240
                // Eat a character and hope the problem goes away
1241
11.0k
                consume();
1242
11.0k
            }
1243
11.0k
            path = create<AST::SyntaxError>("Expected a path after redirection"_string, true);
1244
11.0k
        }
1245
18.9k
        if (mode == Read)
1246
18.9k
            return create<AST::ReadRedirection>(pipe_fd, path.release_nonnull()); // Redirection Read
1247
1248
23
        return create<AST::ReadWriteRedirection>(pipe_fd, path.release_nonnull()); // Redirection ReadWrite
1249
18.9k
    }
1250
3.08M
    default:
1251
3.08M
        restore_to(*rule_start);
1252
3.08M
        return nullptr;
1253
3.18M
    }
1254
3.18M
}
1255
1256
RefPtr<AST::Node> Parser::parse_list_expression()
1257
1.92M
{
1258
1.92M
    consume_while(is_whitespace);
1259
1260
1.92M
    auto rule_start = push_start();
1261
1.92M
    Vector<NonnullRefPtr<AST::Node>> nodes;
1262
1263
2.31M
    do {
1264
2.31M
        auto expr = parse_expression();
1265
2.31M
        if (!expr)
1266
945k
            break;
1267
1.36M
        nodes.append(expr.release_nonnull());
1268
1.36M
    } while (!consume_while(is_whitespace).is_empty());
1269
1270
1.92M
    if (nodes.is_empty())
1271
942k
        return nullptr;
1272
1273
980k
    return create<AST::ListConcatenate>(move(nodes)); // Concatenate List
1274
1.92M
}
1275
1276
RefPtr<AST::Node> Parser::parse_expression()
1277
8.62M
{
1278
8.62M
    auto rule_start = push_start();
1279
8.62M
    if (m_rule_start_offsets.size() > max_allowed_nested_rule_depth)
1280
42.2k
        return create<AST::SyntaxError>(TRY_OR_RESOLVE_TO_ERROR_STRING(String::formatted("Expression nested too deep (max allowed is {})", max_allowed_nested_rule_depth)));
1281
1282
8.58M
    auto starting_char = peek();
1283
1284
8.58M
    auto read_concat = [&](auto&& expr) -> NonnullRefPtr<AST::Node> {
1285
2.36M
        if (is_whitespace(peek()))
1286
447k
            return move(expr);
1287
1288
1.92M
        if (auto next_expr = parse_expression())
1289
35.0k
            return create<AST::Juxtaposition>(move(expr), next_expr.release_nonnull());
1290
1291
1.88M
        return move(expr);
1292
1.92M
    };
Parser.cpp:AK::NonnullRefPtr<Shell::AST::Node> Shell::Parser::parse_expression()::$_0::operator()<AK::NonnullRefPtr<Shell::AST::Node> >(AK::NonnullRefPtr<Shell::AST::Node>&&) const
Line
Count
Source
1284
2.34M
    auto read_concat = [&](auto&& expr) -> NonnullRefPtr<AST::Node> {
1285
2.34M
        if (is_whitespace(peek()))
1286
444k
            return move(expr);
1287
1288
1.90M
        if (auto next_expr = parse_expression())
1289
15.8k
            return create<AST::Juxtaposition>(move(expr), next_expr.release_nonnull());
1290
1291
1.88M
        return move(expr);
1292
1.90M
    };
Parser.cpp:AK::NonnullRefPtr<Shell::AST::Node> Shell::Parser::parse_expression()::$_0::operator()<AK::NonnullRefPtr<Shell::AST::CastToList> >(AK::NonnullRefPtr<Shell::AST::CastToList>&&) const
Line
Count
Source
1284
24.5k
    auto read_concat = [&](auto&& expr) -> NonnullRefPtr<AST::Node> {
1285
24.5k
        if (is_whitespace(peek()))
1286
3.13k
            return move(expr);
1287
1288
21.4k
        if (auto next_expr = parse_expression())
1289
19.1k
            return create<AST::Juxtaposition>(move(expr), next_expr.release_nonnull());
1290
1291
2.23k
        return move(expr);
1292
21.4k
    };
1293
1294
    // Heredocs are expressions, so allow them
1295
8.58M
    if (!(next_is("<<-"sv) || next_is("<<~"sv))) {
1296
8.58M
        if (strchr("&|)} ;<>\n", starting_char) != nullptr)
1297
1.96M
            return nullptr;
1298
8.58M
    }
1299
1300
6.61M
    if (m_extra_chars_not_allowed_in_barewords.contains_slow(starting_char))
1301
4.10M
        return nullptr;
1302
1303
2.51M
    if (m_is_in_brace_expansion_spec && next_is(".."sv))
1304
14.7k
        return nullptr;
1305
1306
2.50M
    if (isdigit(starting_char)) {
1307
1.23M
        ScopedValueRollback offset_rollback { m_offset };
1308
1309
1.23M
        auto redir = parse_redirection();
1310
1.23M
        if (redir)
1311
4.98k
            return nullptr;
1312
1.23M
    }
1313
1314
2.49M
    if (starting_char == '$') {
1315
59.6k
        if (auto variable = parse_variable())
1316
35.8k
            return read_concat(variable.release_nonnull());
1317
1318
23.7k
        if (auto immediate = parse_immediate_expression())
1319
13.8k
            return read_concat(immediate.release_nonnull());
1320
1321
9.92k
        auto inline_exec = parse_evaluate();
1322
9.92k
        if (inline_exec && !inline_exec->is_syntax_error())
1323
881
            return read_concat(inline_exec.release_nonnull());
1324
9.04k
        return inline_exec;
1325
9.92k
    }
1326
1327
2.43M
    if (starting_char == '#')
1328
74.0k
        return parse_comment();
1329
1330
2.36M
    if (starting_char == '(') {
1331
65.5k
        consume();
1332
65.5k
        auto list = parse_list_expression();
1333
65.5k
        if (!expect(')')) {
1334
41.0k
            restore_to(*rule_start);
1335
41.0k
            return nullptr;
1336
41.0k
        }
1337
24.5k
        return read_concat(create<AST::CastToList>(move(list))); // Cast To List
1338
65.5k
    }
1339
1340
2.29M
    if (starting_char == '!' && m_in_interactive_mode) {
1341
0
        if (auto designator = parse_history_designator())
1342
0
            return designator;
1343
0
    }
1344
1345
2.29M
    if (auto composite = parse_string_composite())
1346
2.29M
        return read_concat(composite.release_nonnull());
1347
1348
5.41k
    return nullptr;
1349
2.29M
}
1350
1351
RefPtr<AST::Node> Parser::parse_string_composite()
1352
4.73M
{
1353
4.73M
    auto rule_start = push_start();
1354
4.73M
    if (auto string = parse_string()) {
1355
57.4k
        if (auto next_part = parse_string_composite())
1356
19.8k
            return create<AST::Juxtaposition>(string.release_nonnull(), next_part.release_nonnull()); // Concatenate String StringComposite
1357
1358
37.5k
        return string;
1359
57.4k
    }
1360
1361
4.67M
    if (auto variable = parse_variable()) {
1362
10.1k
        if (auto next_part = parse_string_composite())
1363
7.85k
            return create<AST::Juxtaposition>(variable.release_nonnull(), next_part.release_nonnull()); // Concatenate Variable StringComposite
1364
1365
2.34k
        return variable;
1366
10.1k
    }
1367
1368
4.66M
    if (auto glob = parse_glob()) {
1369
2.27M
        if (auto next_part = parse_string_composite())
1370
73.9k
            return create<AST::Juxtaposition>(glob.release_nonnull(), next_part.release_nonnull()); // Concatenate Glob StringComposite
1371
1372
2.20M
        return glob;
1373
2.27M
    }
1374
1375
2.38M
    if (auto expansion = parse_brace_expansion()) {
1376
47.1k
        if (auto next_part = parse_string_composite())
1377
17.5k
            return create<AST::Juxtaposition>(expansion.release_nonnull(), next_part.release_nonnull()); // Concatenate BraceExpansion StringComposite
1378
1379
29.5k
        return expansion;
1380
47.1k
    }
1381
1382
2.33M
    if (auto bareword = parse_bareword()) {
1383
0
        if (auto next_part = parse_string_composite())
1384
0
            return create<AST::Juxtaposition>(bareword.release_nonnull(), next_part.release_nonnull()); // Concatenate Bareword StringComposite
1385
1386
0
        return bareword;
1387
0
    }
1388
1389
2.33M
    if (auto inline_command = parse_evaluate()) {
1390
30.4k
        if (auto next_part = parse_string_composite())
1391
15.5k
            return create<AST::Juxtaposition>(inline_command.release_nonnull(), next_part.release_nonnull()); // Concatenate Execute StringComposite
1392
1393
14.8k
        return inline_command;
1394
30.4k
    }
1395
1396
2.30M
    if (auto heredoc = parse_heredoc_initiation_record()) {
1397
7.33k
        if (auto next_part = parse_string_composite())
1398
1.53k
            return create<AST::Juxtaposition>(heredoc.release_nonnull(), next_part.release_nonnull()); // Concatenate Heredoc StringComposite
1399
1400
5.79k
        return heredoc;
1401
7.33k
    }
1402
1403
2.29M
    return nullptr;
1404
2.30M
}
1405
1406
RefPtr<AST::Node> Parser::parse_string()
1407
4.73M
{
1408
4.73M
    auto rule_start = push_start();
1409
4.73M
    if (at_end())
1410
7.60k
        return nullptr;
1411
1412
4.72M
    if (peek() == '"') {
1413
26.1k
        consume();
1414
26.1k
        auto inner = parse_string_inner(StringEndCondition::DoubleQuote);
1415
26.1k
        if (!inner)
1416
2
            inner = create<AST::SyntaxError>("Unexpected EOF in string"_string, true);
1417
26.1k
        if (!expect('"')) {
1418
2.26k
            inner = create<AST::DoubleQuotedString>(move(inner));
1419
2.26k
            inner->set_is_syntax_error(*create<AST::SyntaxError>("Expected a terminating double quote"_string, true));
1420
2.26k
            return inner;
1421
2.26k
        }
1422
23.8k
        return create<AST::DoubleQuotedString>(move(inner)); // Double Quoted String
1423
26.1k
    }
1424
1425
4.69M
    if (peek() == '\'') {
1426
31.2k
        consume();
1427
31.2k
        auto text = consume_while(is_not('\''));
1428
31.2k
        bool is_error = false;
1429
31.2k
        if (!expect('\''))
1430
100
            is_error = true;
1431
31.2k
        auto result = create<AST::StringLiteral>(TRY_OR_THROW_PARSE_ERROR(String::from_utf8(text)), AST::StringLiteral::EnclosureType::SingleQuotes); // String Literal
1432
31.1k
        if (is_error)
1433
29
            result->set_is_syntax_error(*create<AST::SyntaxError>("Expected a terminating single quote"_string, true));
1434
31.1k
        return result;
1435
31.2k
    }
1436
1437
4.66M
    return nullptr;
1438
4.69M
}
1439
1440
RefPtr<AST::Node> Parser::parse_string_inner(StringEndCondition condition)
1441
42.0k
{
1442
42.0k
    auto rule_start = push_start();
1443
42.0k
    if (at_end())
1444
1.06k
        return nullptr;
1445
1446
40.9k
    StringBuilder builder;
1447
34.9M
    while (!at_end()) {
1448
34.9M
        if (condition == StringEndCondition::DoubleQuote && peek() == '"') {
1449
23.8k
            break;
1450
23.8k
        }
1451
1452
34.9M
        if (peek() == '\\') {
1453
4.36M
            consume();
1454
4.36M
            if (at_end()) {
1455
38
                break;
1456
38
            }
1457
4.36M
            auto ch = consume();
1458
4.36M
            switch (ch) {
1459
1.57M
            case '\\':
1460
3.69M
            default:
1461
3.69M
                builder.append(ch);
1462
3.69M
                break;
1463
196k
            case 'x': {
1464
196k
                if (m_input.length() <= m_offset + 2)
1465
8
                    break;
1466
196k
                auto first_nibble = tolower(consume());
1467
196k
                auto second_nibble = tolower(consume());
1468
196k
                if (!isxdigit(first_nibble) || !isxdigit(second_nibble)) {
1469
2.38k
                    builder.append(first_nibble);
1470
2.38k
                    builder.append(second_nibble);
1471
2.38k
                    break;
1472
2.38k
                }
1473
194k
                builder.append(to_byte(first_nibble, second_nibble));
1474
194k
                break;
1475
196k
            }
1476
407k
            case 'u': {
1477
407k
                if (m_input.length() <= m_offset + 8)
1478
24
                    break;
1479
407k
                size_t counter = 8;
1480
3.66M
                auto chars = consume_while([&](auto) { return counter-- > 0; });
1481
407k
                if (auto number = AK::StringUtils::convert_to_uint_from_hex(chars); number.has_value())
1482
10.4k
                    builder.append(Utf32View { &number.value(), 1 });
1483
397k
                else
1484
397k
                    builder.append(chars);
1485
1486
407k
                break;
1487
407k
            }
1488
27.3k
            case '0':
1489
28.9k
            case 'o':
1490
29.0k
            case 'c': {
1491
29.0k
                auto read_anything = false;
1492
29.0k
                u8 byte = 0;
1493
29.0k
                auto start = m_offset;
1494
118k
                while (!at_end() && is_ascii_octal_digit(peek())) {
1495
89.0k
                    if (byte > 32)
1496
55
                        break;
1497
88.9k
                    read_anything = true;
1498
88.9k
                    byte *= 8;
1499
88.9k
                    byte += consume() - '0';
1500
88.9k
                }
1501
29.0k
                if (read_anything)
1502
25.2k
                    builder.append(byte);
1503
3.84k
                else
1504
3.84k
                    builder.append(m_input.substring_view(start, m_offset - start));
1505
29.0k
                break;
1506
28.9k
            }
1507
308
            case 'a':
1508
308
                builder.append('\a');
1509
308
                break;
1510
19.6k
            case 'b':
1511
19.6k
                builder.append('\b');
1512
19.6k
                break;
1513
5.32k
            case 'e':
1514
5.32k
                builder.append('\x1b');
1515
5.32k
                break;
1516
1.90k
            case 'f':
1517
1.90k
                builder.append('\f');
1518
1.90k
                break;
1519
876
            case 'r':
1520
876
                builder.append('\r');
1521
876
                break;
1522
882
            case 'n':
1523
882
                builder.append('\n');
1524
882
                break;
1525
11.9k
            case 't':
1526
11.9k
                builder.append('\t');
1527
11.9k
                break;
1528
4.36M
            }
1529
4.36M
            continue;
1530
4.36M
        }
1531
30.5M
        if (peek() == '$') {
1532
16.7k
            auto string_literal = create<AST::StringLiteral>(TRY_OR_THROW_PARSE_ERROR(builder.to_string()), AST::StringLiteral::EnclosureType::DoubleQuotes); // String Literal
1533
14.6k
            auto read_concat = [&](auto&& node) {
1534
14.6k
                auto inner = create<AST::StringPartCompose>(
1535
14.6k
                    move(string_literal),
1536
14.6k
                    move(node)); // Compose String Node
1537
1538
14.6k
                if (auto string = parse_string_inner(condition)) {
1539
14.1k
                    return create<AST::StringPartCompose>(move(inner), string.release_nonnull()); // Compose Composition Composition
1540
14.1k
                }
1541
1542
509
                return inner;
1543
14.6k
            };
1544
1545
14.6k
            if (auto variable = parse_variable())
1546
6.69k
                return read_concat(variable.release_nonnull());
1547
1548
7.92k
            if (auto immediate = parse_immediate_expression())
1549
5.47k
                return read_concat(immediate.release_nonnull());
1550
1551
2.44k
            if (auto evaluate = parse_evaluate())
1552
2.44k
                return read_concat(evaluate.release_nonnull());
1553
2.44k
        }
1554
1555
30.5M
        builder.append(consume());
1556
30.5M
    }
1557
1558
24.2k
    return create<AST::StringLiteral>(TRY_OR_THROW_PARSE_ERROR(builder.to_string()), AST::StringLiteral::EnclosureType::DoubleQuotes); // String Literal
1559
24.2k
}
1560
1561
RefPtr<AST::Node> Parser::parse_variable()
1562
4.74M
{
1563
4.74M
    auto rule_start = push_start();
1564
4.74M
    auto ref = parse_variable_ref();
1565
1566
4.74M
    if (!ref)
1567
4.69M
        return nullptr;
1568
1569
52.7k
    auto variable = static_ptr_cast<AST::VariableNode>(ref);
1570
52.7k
    if (auto slice = parse_slice())
1571
36.7k
        variable->set_slice(slice.release_nonnull());
1572
1573
52.7k
    return variable;
1574
4.74M
}
1575
1576
RefPtr<AST::Node> Parser::parse_variable_ref()
1577
4.74M
{
1578
4.74M
    auto rule_start = push_start();
1579
4.74M
    if (at_end())
1580
7.60k
        return nullptr;
1581
1582
4.73M
    if (peek() != '$')
1583
4.62M
        return nullptr;
1584
1585
114k
    consume();
1586
114k
    switch (peek()) {
1587
8.40k
    case '$':
1588
8.49k
    case '?':
1589
13.2k
    case '*':
1590
13.2k
    case '#':
1591
13.2k
        return create<AST::SpecialVariable>(consume()); // Variable Special
1592
101k
    default:
1593
101k
        break;
1594
114k
    }
1595
1596
101k
    auto name = consume_while(is_word_character);
1597
1598
101k
    if (name.length() == 0) {
1599
62.1k
        restore_to(rule_start->offset, rule_start->line);
1600
62.1k
        return nullptr;
1601
62.1k
    }
1602
1603
39.5k
    return create<AST::SimpleVariable>(TRY_OR_THROW_PARSE_ERROR(String::from_utf8(name))); // Variable Simple
1604
39.5k
}
1605
1606
RefPtr<AST::Slice> Parser::parse_slice()
1607
52.7k
{
1608
52.7k
    auto rule_start = push_start();
1609
52.7k
    if (!next_is("["sv))
1610
16.0k
        return nullptr;
1611
1612
36.7k
    consume(); // [
1613
1614
36.7k
    ScopedValueRollback chars_change { m_extra_chars_not_allowed_in_barewords };
1615
36.7k
    m_extra_chars_not_allowed_in_barewords.append(']');
1616
36.7k
    auto spec = parse_brace_expansion_spec();
1617
1618
36.7k
    RefPtr<AST::SyntaxError> error;
1619
1620
36.7k
    if (peek() != ']')
1621
36.6k
        error = create<AST::SyntaxError>("Expected a close bracket ']' to end a variable slice"_string);
1622
31
    else
1623
31
        consume();
1624
1625
36.7k
    if (!spec) {
1626
2.01k
        if (error)
1627
2.01k
            spec = move(error);
1628
2
        else
1629
2
            spec = create<AST::SyntaxError>("Expected either a range, or a comma-seprated list of selectors"_string);
1630
2.01k
    }
1631
1632
36.7k
    auto node = create<AST::Slice>(spec.release_nonnull());
1633
36.7k
    if (error)
1634
34.6k
        node->set_is_syntax_error(*error);
1635
36.7k
    return node;
1636
52.7k
}
1637
1638
RefPtr<AST::Node> Parser::parse_evaluate()
1639
2.34M
{
1640
2.34M
    auto rule_start = push_start();
1641
2.34M
    if (at_end())
1642
7.60k
        return nullptr;
1643
1644
2.34M
    if (peek() != '$')
1645
2.29M
        return nullptr;
1646
1647
42.8k
    consume();
1648
42.8k
    if (peek() == '(') {
1649
11.5k
        consume();
1650
11.5k
        auto inner = parse_pipe_sequence();
1651
11.5k
        if (!inner)
1652
317
            inner = create<AST::SyntaxError>("Unexpected EOF in list"_string, true);
1653
11.5k
        if (!expect(')'))
1654
10.7k
            inner->set_is_syntax_error(*create<AST::SyntaxError>("Expected a terminating close paren"_string, true));
1655
1656
11.5k
        return create<AST::Execute>(inner.release_nonnull(), true);
1657
11.5k
    }
1658
31.2k
    auto inner = parse_expression();
1659
1660
31.2k
    if (!inner) {
1661
2.54k
        inner = create<AST::SyntaxError>("Expected a command"_string, true);
1662
28.6k
    } else {
1663
28.6k
        if (inner->is_list()) {
1664
0
            auto execute_inner = create<AST::Execute>(inner.release_nonnull(), true);
1665
0
            inner = move(execute_inner);
1666
28.6k
        } else {
1667
28.6k
            auto dyn_inner = create<AST::DynamicEvaluate>(inner.release_nonnull());
1668
28.6k
            inner = move(dyn_inner);
1669
28.6k
        }
1670
28.6k
    }
1671
1672
31.2k
    return inner;
1673
42.8k
}
1674
1675
RefPtr<AST::Node> Parser::parse_immediate_expression()
1676
31.7k
{
1677
31.7k
    auto rule_start = push_start();
1678
31.7k
    if (at_end())
1679
0
        return nullptr;
1680
1681
31.7k
    if (peek() != '$')
1682
0
        return nullptr;
1683
1684
31.7k
    consume();
1685
1686
31.7k
    if (peek() != '{') {
1687
12.3k
        restore_to(*rule_start);
1688
12.3k
        return nullptr;
1689
12.3k
    }
1690
1691
19.3k
    consume();
1692
19.3k
    consume_while(is_whitespace);
1693
1694
19.3k
    auto function_name_start_offset = current_position();
1695
19.3k
    auto function_name = consume_while(is_word_character);
1696
19.3k
    auto function_name_end_offset = current_position();
1697
19.3k
    AST::Position function_position {
1698
19.3k
        function_name_start_offset.offset,
1699
19.3k
        function_name_end_offset.offset,
1700
19.3k
        function_name_start_offset.line,
1701
19.3k
        function_name_end_offset.line,
1702
19.3k
    };
1703
1704
19.3k
    consume_while(is_whitespace);
1705
1706
19.3k
    Vector<NonnullRefPtr<AST::Node>> arguments;
1707
40.7k
    do {
1708
40.7k
        auto expr = parse_expression();
1709
40.7k
        if (!expr)
1710
664
            break;
1711
40.0k
        arguments.append(expr.release_nonnull());
1712
40.0k
    } while (!consume_while(is_whitespace).is_empty());
1713
1714
0
    auto ending_brace_start_offset = current_position();
1715
19.3k
    if (peek() == '}')
1716
12.0k
        consume();
1717
1718
19.3k
    auto ending_brace_end_offset = current_position();
1719
1720
19.3k
    auto ending_brace_position = ending_brace_start_offset.offset == ending_brace_end_offset.offset
1721
19.3k
        ? Optional<AST::Position> {}
1722
19.3k
        : Optional<AST::Position> {
1723
12.0k
              AST::Position {
1724
12.0k
                  ending_brace_start_offset.offset,
1725
12.0k
                  ending_brace_end_offset.offset,
1726
12.0k
                  ending_brace_start_offset.line,
1727
12.0k
                  ending_brace_end_offset.line,
1728
12.0k
              }
1729
12.0k
          };
1730
1731
19.3k
    auto node = create<AST::ImmediateExpression>(
1732
19.3k
        AST::NameWithPosition { TRY_OR_THROW_PARSE_ERROR(String::from_utf8(function_name)), move(function_position) },
1733
0
        move(arguments),
1734
19.3k
        ending_brace_position);
1735
1736
19.3k
    if (!ending_brace_position.has_value())
1737
7.29k
        node->set_is_syntax_error(create<AST::SyntaxError>("Expected a closing brace '}' to end an immediate expression"_string, true));
1738
12.0k
    else if (node->function_name().is_empty())
1739
2.57k
        node->set_is_syntax_error(create<AST::SyntaxError>("Expected an immediate function name"_string));
1740
1741
19.3k
    return node;
1742
19.3k
}
1743
1744
RefPtr<AST::Node> Parser::parse_history_designator()
1745
0
{
1746
0
    auto rule_start = push_start();
1747
1748
0
    VERIFY(peek() == '!');
1749
0
    consume();
1750
1751
    // Event selector
1752
0
    AST::HistorySelector selector;
1753
0
    RefPtr<AST::SyntaxError> syntax_error;
1754
0
    selector.event.kind = AST::HistorySelector::EventKind::StartingStringLookup;
1755
0
    selector.event.text_position = { m_offset, m_offset, m_line, m_line };
1756
0
    selector.word_selector_range = {
1757
0
        AST::HistorySelector::WordSelector {
1758
0
            AST::HistorySelector::WordSelectorKind::Index,
1759
0
            0,
1760
0
            { m_offset, m_offset, m_line, m_line },
1761
0
            nullptr },
1762
0
        AST::HistorySelector::WordSelector {
1763
0
            AST::HistorySelector::WordSelectorKind::Last,
1764
0
            0,
1765
0
            { m_offset, m_offset, m_line, m_line },
1766
0
            nullptr }
1767
0
    };
1768
1769
0
    bool is_word_selector = false;
1770
1771
0
    switch (peek()) {
1772
0
    case ':':
1773
0
        consume();
1774
0
        [[fallthrough]];
1775
0
    case '^':
1776
0
    case '$':
1777
0
    case '*':
1778
0
        is_word_selector = true;
1779
0
        break;
1780
0
    case '!':
1781
0
        consume();
1782
0
        selector.event.kind = AST::HistorySelector::EventKind::IndexFromEnd;
1783
0
        selector.event.index = 0;
1784
0
        selector.event.text = "!"_string;
1785
0
        break;
1786
0
    case '?':
1787
0
        consume();
1788
0
        selector.event.kind = AST::HistorySelector::EventKind::ContainingStringLookup;
1789
0
        [[fallthrough]];
1790
0
    default: {
1791
0
        TemporaryChange chars_change { m_extra_chars_not_allowed_in_barewords, { ':', '^', '$', '*' } };
1792
1793
0
        auto bareword = parse_bareword();
1794
0
        if (!bareword || !bareword->is_bareword()) {
1795
0
            restore_to(*rule_start);
1796
0
            return nullptr;
1797
0
        }
1798
1799
0
        selector.event.text = static_ptr_cast<AST::BarewordLiteral>(bareword)->text();
1800
0
        selector.event.text_position = bareword->position();
1801
0
        auto selector_bytes = selector.event.text.bytes();
1802
0
        auto it = selector_bytes.begin();
1803
0
        bool is_negative = false;
1804
0
        if (*it == '-') {
1805
0
            ++it;
1806
0
            is_negative = true;
1807
0
        }
1808
0
        if (it != selector_bytes.end() && all_of(it, selector_bytes.end(), is_digit)) {
1809
0
            if (is_negative)
1810
0
                selector.event.kind = AST::HistorySelector::EventKind::IndexFromEnd;
1811
0
            else
1812
0
                selector.event.kind = AST::HistorySelector::EventKind::IndexFromStart;
1813
0
            auto number = abs(selector.event.text.to_number<int>().value_or(0));
1814
0
            if (number != 0)
1815
0
                selector.event.index = number - 1;
1816
0
            else
1817
0
                syntax_error = create<AST::SyntaxError>("History entry index value invalid or out of range"_string);
1818
0
        }
1819
0
        if (":^$*"sv.contains(peek())) {
1820
0
            is_word_selector = true;
1821
0
            if (peek() == ':')
1822
0
                consume();
1823
0
        }
1824
0
    }
1825
0
    }
1826
1827
0
    if (!is_word_selector) {
1828
0
        auto node = create<AST::HistoryEvent>(move(selector));
1829
0
        if (syntax_error)
1830
0
            node->set_is_syntax_error(*syntax_error);
1831
0
        return node;
1832
0
    }
1833
1834
    // Word selectors
1835
0
    auto parse_word_selector = [&]() -> Optional<AST::HistorySelector::WordSelector> {
1836
0
        auto c = peek();
1837
0
        AST::HistorySelector::WordSelectorKind word_selector_kind;
1838
0
        ssize_t offset = -1;
1839
0
        if (isdigit(c)) {
1840
0
            auto num = consume_while(is_digit);
1841
0
            auto value = num.to_number<unsigned>();
1842
0
            if (!value.has_value())
1843
0
                return {};
1844
0
            word_selector_kind = AST::HistorySelector::WordSelectorKind::Index;
1845
0
            offset = value.value();
1846
0
        } else if (c == '^') {
1847
0
            consume();
1848
0
            word_selector_kind = AST::HistorySelector::WordSelectorKind::Index;
1849
0
            offset = 1;
1850
0
        } else if (c == '$') {
1851
0
            consume();
1852
0
            word_selector_kind = AST::HistorySelector::WordSelectorKind::Last;
1853
0
            offset = 0;
1854
0
        }
1855
0
        if (offset == -1)
1856
0
            return {};
1857
0
        return AST::HistorySelector::WordSelector {
1858
0
            word_selector_kind,
1859
0
            static_cast<size_t>(offset),
1860
0
            { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() },
1861
0
            syntax_error
1862
0
        };
1863
0
    };
1864
1865
0
    auto make_word_selector = [&](AST::HistorySelector::WordSelectorKind word_selector_kind, size_t offset) {
1866
0
        return AST::HistorySelector::WordSelector {
1867
0
            word_selector_kind,
1868
0
            offset,
1869
0
            { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() },
1870
0
            syntax_error
1871
0
        };
1872
0
    };
1873
1874
0
    auto first_char = peek();
1875
0
    if (!(is_digit(first_char) || "^$-*"sv.contains(first_char))) {
1876
0
        if (!syntax_error)
1877
0
            syntax_error = create<AST::SyntaxError>("Expected a word selector after ':' in a history event designator"_string, true);
1878
0
    } else if (first_char == '*') {
1879
0
        consume();
1880
0
        selector.word_selector_range.start = make_word_selector(AST::HistorySelector::WordSelectorKind::Index, 1);
1881
0
        selector.word_selector_range.end = make_word_selector(AST::HistorySelector::WordSelectorKind::Last, 0);
1882
0
    } else if (first_char == '-') {
1883
0
        consume();
1884
0
        selector.word_selector_range.start = make_word_selector(AST::HistorySelector::WordSelectorKind::Index, 0);
1885
0
        auto last_selector = parse_word_selector();
1886
0
        if (!last_selector.has_value())
1887
0
            selector.word_selector_range.end = make_word_selector(AST::HistorySelector::WordSelectorKind::Last, 1);
1888
0
        else
1889
0
            selector.word_selector_range.end = last_selector.release_value();
1890
0
    } else {
1891
0
        auto first_selector = parse_word_selector();
1892
        // peek() should be a digit, ^, or $ here, so this should always have value.
1893
0
        VERIFY(first_selector.has_value());
1894
0
        selector.word_selector_range.start = first_selector.release_value();
1895
0
        if (peek() == '-') {
1896
0
            consume();
1897
0
            auto last_selector = parse_word_selector();
1898
0
            if (last_selector.has_value()) {
1899
0
                selector.word_selector_range.end = last_selector.release_value();
1900
0
            } else {
1901
0
                selector.word_selector_range.end = make_word_selector(AST::HistorySelector::WordSelectorKind::Last, 1);
1902
0
            }
1903
0
        } else if (peek() == '*') {
1904
0
            consume();
1905
0
            selector.word_selector_range.end = make_word_selector(AST::HistorySelector::WordSelectorKind::Last, 0);
1906
0
        } else {
1907
0
            selector.word_selector_range.end.clear();
1908
0
        }
1909
0
    }
1910
1911
0
    auto node = create<AST::HistoryEvent>(move(selector));
1912
0
    if (syntax_error)
1913
0
        node->set_is_syntax_error(*syntax_error);
1914
0
    return node;
1915
0
}
1916
1917
RefPtr<AST::Node> Parser::parse_comment()
1918
74.0k
{
1919
74.0k
    if (at_end())
1920
0
        return nullptr;
1921
1922
74.0k
    if (peek() != '#')
1923
0
        return nullptr;
1924
1925
74.0k
    consume();
1926
74.0k
    auto text = consume_while(is_not('\n'));
1927
74.0k
    return create<AST::Comment>(TRY_OR_THROW_PARSE_ERROR(String::from_utf8(text))); // Comment
1928
74.0k
}
1929
1930
RefPtr<AST::Node> Parser::parse_bareword()
1931
7.03M
{
1932
7.03M
    auto rule_start = push_start();
1933
7.03M
    StringBuilder builder;
1934
53.5M
    auto is_acceptable_bareword_character = [&](char c) {
1935
53.5M
        return strchr("\\\"'*$&|(){} ?;<>\n", c) == nullptr
1936
53.5M
            && !m_extra_chars_not_allowed_in_barewords.contains_slow(c);
1937
53.5M
    };
1938
53.5M
    while (!at_end()) {
1939
53.5M
        char ch = peek();
1940
53.5M
        if (ch == '\\') {
1941
478k
            consume();
1942
478k
            if (!at_end()) {
1943
478k
                ch = consume();
1944
478k
                if (is_acceptable_bareword_character(ch))
1945
419k
                    builder.append('\\');
1946
478k
            }
1947
478k
            builder.append(ch);
1948
478k
            continue;
1949
478k
        }
1950
1951
53.0M
        if (m_is_in_brace_expansion_spec && next_is(".."sv)) {
1952
            // Don't eat '..' in a brace expansion spec.
1953
27.0k
            break;
1954
27.0k
        }
1955
1956
53.0M
        if (is_acceptable_bareword_character(ch)) {
1957
46.0M
            builder.append(consume());
1958
46.0M
            continue;
1959
46.0M
        }
1960
1961
6.99M
        break;
1962
53.0M
    }
1963
1964
7.03M
    if (builder.is_empty())
1965
4.75M
        return nullptr;
1966
1967
2.27M
    auto current_end = m_offset;
1968
2.27M
    auto current_line = line();
1969
2.27M
    auto string = TRY_OR_THROW_PARSE_ERROR(builder.to_string());
1970
2.16M
    if (string.starts_with('~')) {
1971
8.15k
        String username;
1972
8.15k
        RefPtr<AST::Node> tilde, text;
1973
1974
8.15k
        auto first_slash_index = string.find_byte_offset('/');
1975
8.15k
        if (first_slash_index.has_value()) {
1976
4
            username = TRY_OR_THROW_PARSE_ERROR(string.substring_from_byte_offset(1, *first_slash_index - 1));
1977
4
            string = TRY_OR_THROW_PARSE_ERROR(string.substring_from_byte_offset(*first_slash_index));
1978
8.15k
        } else {
1979
8.15k
            username = TRY_OR_THROW_PARSE_ERROR(string.substring_from_byte_offset(1));
1980
0
            string = {};
1981
8.15k
        }
1982
1983
        // Synthesize a Tilde Node with the correct positioning information.
1984
8.15k
        {
1985
8.15k
            restore_to(rule_start->offset, rule_start->line);
1986
8.15k
            auto ch = consume();
1987
8.15k
            VERIFY(ch == '~');
1988
8.15k
            auto username_length = username.bytes_as_string_view().length();
1989
8.15k
            tilde = create<AST::Tilde>(move(username));
1990
            // Consume the username (if any)
1991
2.25M
            for (size_t i = 0; i < username_length; ++i)
1992
2.24M
                consume();
1993
8.15k
        }
1994
1995
8.15k
        if (string.is_empty())
1996
8.15k
            return tilde;
1997
1998
        // Synthesize a BarewordLiteral Node with the correct positioning information.
1999
4
        {
2000
4
            auto text_start = push_start();
2001
4
            restore_to(current_end, current_line);
2002
4
            text = create<AST::BarewordLiteral>(move(string));
2003
4
        }
2004
2005
4
        return create<AST::Juxtaposition>(tilde.release_nonnull(), text.release_nonnull()); // Juxtaposition Variable Bareword
2006
8.15k
    }
2007
2008
2.15M
    if (string.starts_with_bytes("\\~"sv)) {
2009
        // Un-escape the tilde, but only at the start (where it would be an expansion)
2010
386
        string = TRY_OR_THROW_PARSE_ERROR(string.substring_from_byte_offset(1));
2011
386
    }
2012
2013
2.15M
    return create<AST::BarewordLiteral>(move(string)); // Bareword Literal
2014
2.15M
}
2015
2016
RefPtr<AST::Node> Parser::parse_glob()
2017
4.69M
{
2018
4.69M
    auto rule_start = push_start();
2019
4.69M
    auto bareword_part = parse_bareword();
2020
2021
4.69M
    if (at_end())
2022
8.11k
        return bareword_part;
2023
2024
4.68M
    char ch = peek();
2025
4.68M
    if (ch == '*' || ch == '?') {
2026
33.1k
        auto saved_offset = save_offset();
2027
33.1k
        consume();
2028
33.1k
        StringBuilder textbuilder;
2029
33.1k
        if (bareword_part) {
2030
6.90k
            StringView text;
2031
6.90k
            if (bareword_part->is_bareword()) {
2032
4.84k
                auto bareword = static_cast<AST::BarewordLiteral*>(bareword_part.ptr());
2033
4.84k
                text = bareword->text();
2034
4.84k
            } else {
2035
                // FIXME: Allow composition of tilde+bareword with globs: '~/foo/bar/baz*'
2036
2.05k
                restore_to(saved_offset.offset, saved_offset.line);
2037
2.05k
                bareword_part->set_is_syntax_error(*create<AST::SyntaxError>(TRY_OR_RESOLVE_TO_ERROR_STRING(String::formatted("Unexpected {} inside a glob", bareword_part->class_name()))));
2038
0
                return bareword_part;
2039
2.05k
            }
2040
4.84k
            textbuilder.append(text);
2041
4.84k
        }
2042
2043
31.0k
        textbuilder.append(ch);
2044
2045
31.0k
        auto glob_after = parse_glob();
2046
31.0k
        if (glob_after) {
2047
24.3k
            if (glob_after->is_glob()) {
2048
776
                auto glob = static_cast<AST::Glob*>(glob_after.ptr());
2049
776
                textbuilder.append(glob->text());
2050
23.5k
            } else if (glob_after->is_bareword()) {
2051
4.07k
                auto bareword = static_cast<AST::BarewordLiteral*>(glob_after.ptr());
2052
4.07k
                textbuilder.append(bareword->text());
2053
19.4k
            } else if (glob_after->is_tilde()) {
2054
1
                auto bareword = static_cast<AST::Tilde*>(glob_after.ptr());
2055
1
                textbuilder.append('~');
2056
1
                textbuilder.append(bareword->text());
2057
19.4k
            } else {
2058
19.4k
                return create<AST::SyntaxError>(TRY_OR_RESOLVE_TO_ERROR_STRING(String::formatted("Invalid node '{}' in glob position, escape shell special characters", glob_after->class_name())));
2059
19.4k
            }
2060
24.3k
        }
2061
2062
11.5k
        return create<AST::Glob>(TRY_OR_THROW_PARSE_ERROR(textbuilder.to_string())); // Glob
2063
11.5k
    }
2064
2065
4.65M
    return bareword_part;
2066
4.68M
}
2067
2068
RefPtr<AST::Node> Parser::parse_brace_expansion()
2069
2.38M
{
2070
2.38M
    auto rule_start = push_start();
2071
2072
2.38M
    if (!expect('{'))
2073
2.33M
        return nullptr;
2074
2075
53.8k
    if (auto spec = parse_brace_expansion_spec()) {
2076
47.1k
        if (!expect('}'))
2077
43.9k
            spec->set_is_syntax_error(create<AST::SyntaxError>("Expected a close brace '}' to end a brace expansion"_string, true));
2078
2079
47.1k
        return spec;
2080
47.1k
    }
2081
2082
6.76k
    restore_to(*rule_start);
2083
6.76k
    return nullptr;
2084
53.8k
}
2085
2086
RefPtr<AST::Node> Parser::parse_brace_expansion_spec()
2087
90.5k
{
2088
90.5k
    TemporaryChange is_in_brace_expansion { m_is_in_brace_expansion_spec, true };
2089
90.5k
    ScopedValueRollback chars_change { m_extra_chars_not_allowed_in_barewords };
2090
2091
90.5k
    m_extra_chars_not_allowed_in_barewords.append(',');
2092
2093
90.5k
    auto rule_start = push_start();
2094
90.5k
    Vector<NonnullRefPtr<AST::Node>> subexpressions;
2095
2096
90.5k
    if (next_is(","sv)) {
2097
        // Note that we don't consume the ',' here.
2098
12
        subexpressions.append(create<AST::StringLiteral>(String {}, AST::StringLiteral::EnclosureType::None));
2099
90.5k
    } else {
2100
90.5k
        auto start_expr = parse_expression();
2101
90.5k
        if (start_expr) {
2102
81.8k
            if (expect(".."sv)) {
2103
7.52k
                if (auto end_expr = parse_expression()) {
2104
7.50k
                    if (end_expr->position().start_offset != start_expr->position().end_offset + 2)
2105
7.11k
                        end_expr->set_is_syntax_error(create<AST::SyntaxError>("Expected no whitespace between '..' and the following expression in brace expansion"_string));
2106
2107
7.50k
                    return create<AST::Range>(start_expr.release_nonnull(), end_expr.release_nonnull());
2108
7.50k
                }
2109
2110
16
                return create<AST::Range>(start_expr.release_nonnull(), create<AST::SyntaxError>("Expected an expression to end range brace expansion with"_string, true));
2111
7.52k
            }
2112
81.8k
        }
2113
2114
83.0k
        if (start_expr)
2115
74.2k
            subexpressions.append(start_expr.release_nonnull());
2116
83.0k
    }
2117
2118
4.17M
    while (expect(',')) {
2119
4.09M
        auto expr = parse_expression();
2120
4.09M
        if (expr) {
2121
902k
            subexpressions.append(expr.release_nonnull());
2122
3.19M
        } else {
2123
3.19M
            subexpressions.append(create<AST::StringLiteral>(String {}, AST::StringLiteral::EnclosureType::None));
2124
3.19M
        }
2125
4.09M
    }
2126
2127
83.0k
    if (subexpressions.is_empty())
2128
8.77k
        return nullptr;
2129
2130
74.2k
    return create<AST::BraceExpansion>(move(subexpressions));
2131
83.0k
}
2132
2133
RefPtr<AST::Node> Parser::parse_heredoc_initiation_record()
2134
2.30M
{
2135
2.30M
    if (!next_is("<<"sv))
2136
2.29M
        return nullptr;
2137
2138
11.3k
    auto rule_start = push_start();
2139
2140
    // '<' '<'
2141
11.3k
    consume();
2142
11.3k
    consume();
2143
2144
11.3k
    HeredocInitiationRecord record;
2145
11.3k
    record.end = "<error>"_string;
2146
2147
11.3k
    RefPtr<AST::SyntaxError> syntax_error_node;
2148
2149
    // '-' | '~'
2150
11.3k
    switch (peek()) {
2151
4.47k
    case '-':
2152
4.47k
        record.deindent = false;
2153
4.47k
        consume();
2154
4.47k
        break;
2155
2.85k
    case '~':
2156
2.85k
        record.deindent = true;
2157
2.85k
        consume();
2158
2.85k
        break;
2159
3.97k
    default:
2160
3.97k
        restore_to(*rule_start);
2161
3.97k
        return nullptr;
2162
11.3k
    }
2163
2164
    // StringLiteral | bareword
2165
7.33k
    if (auto bareword = parse_bareword()) {
2166
3.42k
        if (!bareword->is_bareword()) {
2167
2.52k
            syntax_error_node = create<AST::SyntaxError>(TRY_OR_RESOLVE_TO_ERROR_STRING(String::formatted("Expected a bareword or a quoted string, not {}", bareword->class_name())));
2168
2.52k
        } else {
2169
899
            if (bareword->is_syntax_error())
2170
0
                syntax_error_node = bareword->syntax_error_node();
2171
899
            else
2172
899
                record.end = static_cast<AST::BarewordLiteral*>(bareword.ptr())->text();
2173
899
        }
2174
2175
3.42k
        record.interpolate = true;
2176
3.90k
    } else if (peek() == '\'') {
2177
183
        consume();
2178
183
        auto text = consume_while(is_not('\''));
2179
183
        bool is_error = false;
2180
183
        if (!expect('\''))
2181
0
            is_error = true;
2182
183
        if (is_error)
2183
0
            syntax_error_node = create<AST::SyntaxError>("Expected a terminating single quote"_string, true);
2184
2185
183
        record.end = TRY_OR_THROW_PARSE_ERROR(String::from_utf8(text));
2186
0
        record.interpolate = false;
2187
3.72k
    } else {
2188
3.72k
        syntax_error_node = create<AST::SyntaxError>("Expected a bareword or a single-quoted string literal for heredoc end key"_string, true);
2189
3.72k
    }
2190
2191
7.33k
    auto node = create<AST::Heredoc>(record.end, record.interpolate, record.deindent);
2192
7.33k
    if (syntax_error_node)
2193
6.25k
        node->set_is_syntax_error(*syntax_error_node);
2194
1.08k
    else
2195
1.08k
        node->set_is_syntax_error(*create<AST::SyntaxError>(TRY_OR_RESOLVE_TO_ERROR_STRING(String::formatted("Expected heredoc contents for heredoc with end key '{}'", node->end())), true));
2196
2197
7.33k
    record.node = node;
2198
7.33k
    m_heredoc_initiations.append(move(record));
2199
2200
7.33k
    return node;
2201
7.33k
}
2202
2203
bool Parser::parse_heredoc_entries()
2204
1.05k
{
2205
1.05k
    auto heredocs = move(m_heredoc_initiations);
2206
1.05k
    m_heredoc_initiations.clear();
2207
    // Try to parse heredoc entries, as reverse recorded in the initiation records
2208
1.30k
    for (auto& record : heredocs) {
2209
1.30k
        auto rule_start = push_start();
2210
1.30k
        if (m_rule_start_offsets.size() > max_allowed_nested_rule_depth) {
2211
0
            record.node->set_is_syntax_error(*create<AST::SyntaxError>(TRY_OR_RESOLVE_TO_ERROR_STRING(String::formatted("Expression nested too deep (max allowed is {})", max_allowed_nested_rule_depth))));
2212
0
            continue;
2213
0
        }
2214
1.30k
        bool found_key = false;
2215
1.30k
        if (!record.interpolate) {
2216
            // Since no interpolation is allowed, just read lines until we hit the key
2217
9
            Optional<Offset> last_line_offset;
2218
104
            for (;;) {
2219
104
                if (at_end())
2220
6
                    break;
2221
98
                if (peek() == '\n')
2222
87
                    consume();
2223
98
                last_line_offset = current_position();
2224
98
                auto line = consume_while(is_not('\n'));
2225
98
                if (peek() == '\n')
2226
95
                    consume();
2227
98
                if (line.trim_whitespace() == record.end) {
2228
3
                    found_key = true;
2229
3
                    break;
2230
3
                }
2231
98
            }
2232
2233
9
            if (!last_line_offset.has_value())
2234
0
                last_line_offset = current_position();
2235
            // Now just wrap it in a StringLiteral and set it as the node's contents
2236
9
            auto node = create<AST::StringLiteral>(
2237
9
                MUST(String::from_utf8(m_input.substring_view(rule_start->offset, last_line_offset->offset - rule_start->offset))),
2238
0
                AST::StringLiteral::EnclosureType::None);
2239
9
            if (!found_key)
2240
6
                node->set_is_syntax_error(*create<AST::SyntaxError>(TRY_OR_RESOLVE_TO_ERROR_STRING(String::formatted("Expected to find the heredoc key '{}', but found Eof", record.end)), true));
2241
9
            record.node->set_contents(move(node));
2242
1.29k
        } else {
2243
            // Interpolation is allowed, so we're going to read doublequoted string innards
2244
            // until we find a line that contains the key
2245
1.29k
            auto end_condition = move(m_end_condition);
2246
1.29k
            found_key = false;
2247
97.4M
            set_end_condition(make<Function<bool()>>([this, end = record.end, &found_key] {
2248
97.4M
                if (found_key)
2249
0
                    return true;
2250
97.4M
                auto offset = current_position();
2251
97.4M
                auto cond = move(m_end_condition);
2252
97.4M
                ScopeGuard guard {
2253
97.4M
                    [&] {
2254
97.4M
                        m_end_condition = move(cond);
2255
97.4M
                    }
2256
97.4M
                };
2257
97.4M
                if (peek() == '\n') {
2258
7.12M
                    consume();
2259
7.12M
                    auto line = consume_while(is_not('\n'));
2260
7.12M
                    if (peek() == '\n')
2261
7.12M
                        consume();
2262
7.12M
                    if (line.trim_whitespace() == end) {
2263
545
                        restore_to(offset.offset, offset.line);
2264
545
                        found_key = true;
2265
545
                        return true;
2266
545
                    }
2267
7.12M
                }
2268
97.4M
                restore_to(offset.offset, offset.line);
2269
97.4M
                return false;
2270
97.4M
            }));
2271
2272
1.29k
            auto expr = parse_string_inner(StringEndCondition::Heredoc);
2273
1.29k
            set_end_condition(move(end_condition));
2274
2275
1.29k
            if (found_key) {
2276
545
                auto offset = current_position();
2277
545
                if (peek() == '\n')
2278
545
                    consume();
2279
545
                auto line = consume_while(is_not('\n'));
2280
545
                if (peek() == '\n')
2281
545
                    consume();
2282
545
                if (line.trim_whitespace() != record.end)
2283
0
                    restore_to(offset.offset, offset.line);
2284
545
            }
2285
2286
1.29k
            if (!expr && found_key) {
2287
544
                expr = create<AST::StringLiteral>(String {}, AST::StringLiteral::EnclosureType::None);
2288
748
            } else if (!expr) {
2289
10
                expr = create<AST::SyntaxError>(TRY_OR_RESOLVE_TO_ERROR_STRING(String::formatted("Expected to find a valid string inside a heredoc (with end key '{}')", record.end)), true);
2290
738
            } else if (!found_key) {
2291
737
                expr->set_is_syntax_error(*create<AST::SyntaxError>(TRY_OR_RESOLVE_TO_ERROR_STRING(String::formatted("Expected to find the heredoc key '{}'", record.end)), true));
2292
737
            }
2293
2294
1.29k
            record.node->set_contents(create<AST::DoubleQuotedString>(move(expr)));
2295
1.29k
        }
2296
1.30k
    }
2297
1.05k
    return true;
2298
1.05k
}
2299
2300
StringView Parser::consume_while(Function<bool(char)> condition)
2301
28.7M
{
2302
28.7M
    if (at_end())
2303
67.8k
        return {};
2304
2305
28.6M
    auto start_offset = m_offset;
2306
2307
265M
    while (!at_end() && condition(peek()))
2308
236M
        consume();
2309
2310
28.6M
    return m_input.substring_view(start_offset, m_offset - start_offset);
2311
28.7M
}
2312
2313
bool Parser::next_is(StringView next)
2314
129M
{
2315
129M
    auto start = current_position();
2316
129M
    auto res = expect(next);
2317
129M
    restore_to(start.offset, start.line);
2318
129M
    return res;
2319
129M
}
2320
2321
}