summaryrefslogtreecommitdiff
path: root/Userland/Shell/PosixParser.h
blob: a45a0cbc4a624b64a9886a6e80448484762e6f0e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/*
 * Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <Shell/AST.h>
#include <Shell/PosixLexer.h>

namespace Shell::Posix {

class Parser {
public:
    Parser(StringView input, bool interactive = false, Optional<Reduction> starting_reduction = {})
        : m_lexer(input)
        , m_in_interactive_mode(interactive)
        , m_eof_token(Token::eof())
    {
        (void)fill_token_buffer(starting_reduction);
    }

    RefPtr<AST::Node> parse();
    RefPtr<AST::Node> parse_word_list();

    struct Error {
        DeprecatedString message;
        Optional<AST::Position> position;
    };
    auto& errors() const { return m_errors; }

private:
    ErrorOr<Optional<Token>> next_expanded_token(Optional<Reduction> starting_reduction = {});
    Vector<Token> perform_expansions(Vector<Token> tokens);
    ErrorOr<void> fill_token_buffer(Optional<Reduction> starting_reduction = {});
    void handle_heredoc_contents();

    Token const& peek()
    {
        if (eof())
            return m_eof_token;
        handle_heredoc_contents();
        return m_token_buffer[m_token_index];
    }
    Token const& consume()
    {
        if (eof())
            return m_eof_token;
        handle_heredoc_contents();
        return m_token_buffer[m_token_index++];
    }
    void skip()
    {
        if (eof())
            return;
        m_token_index++;
    }
    bool eof() const
    {
        return m_token_index == m_token_buffer.size() || m_token_buffer[m_token_index].type == Token::Type::Eof;
    }

    struct CaseItemsResult {
        Vector<AST::Position> pipe_positions;
        Vector<NonnullRefPtr<AST::Node>> nodes;
    };

    ErrorOr<RefPtr<AST::Node>> parse_complete_command();
    ErrorOr<RefPtr<AST::Node>> parse_list();
    ErrorOr<RefPtr<AST::Node>> parse_and_or();
    ErrorOr<RefPtr<AST::Node>> parse_pipeline();
    ErrorOr<RefPtr<AST::Node>> parse_pipe_sequence();
    ErrorOr<RefPtr<AST::Node>> parse_command();
    ErrorOr<RefPtr<AST::Node>> parse_compound_command();
    ErrorOr<RefPtr<AST::Node>> parse_subshell();
    ErrorOr<RefPtr<AST::Node>> parse_compound_list();
    ErrorOr<RefPtr<AST::Node>> parse_term();
    ErrorOr<RefPtr<AST::Node>> parse_for_clause();
    ErrorOr<RefPtr<AST::Node>> parse_case_clause();
    ErrorOr<RefPtr<AST::Node>> parse_if_clause();
    ErrorOr<RefPtr<AST::Node>> parse_while_clause();
    ErrorOr<RefPtr<AST::Node>> parse_until_clause();
    ErrorOr<RefPtr<AST::Node>> parse_function_definition();
    ErrorOr<RefPtr<AST::Node>> parse_function_body();
    ErrorOr<RefPtr<AST::Node>> parse_brace_group();
    ErrorOr<RefPtr<AST::Node>> parse_do_group();
    ErrorOr<RefPtr<AST::Node>> parse_simple_command();
    ErrorOr<RefPtr<AST::Node>> parse_prefix();
    ErrorOr<RefPtr<AST::Node>> parse_suffix();
    ErrorOr<RefPtr<AST::Node>> parse_io_redirect();
    ErrorOr<RefPtr<AST::Node>> parse_redirect_list();
    ErrorOr<RefPtr<AST::Node>> parse_io_file(AST::Position, Optional<int> fd);
    ErrorOr<RefPtr<AST::Node>> parse_io_here(AST::Position, Optional<int> fd);
    ErrorOr<RefPtr<AST::Node>> parse_word();
    ErrorOr<CaseItemsResult> parse_case_list();

    template<typename... Ts>
    void error(Token const& token, CheckedFormatString<Ts...> fmt, Ts&&... args)
    {
        m_errors.append(Error {
            DeprecatedString::formatted(fmt.view(), forward<Ts>(args)...),
            token.position,
        });
    }

    Lexer m_lexer;
    bool m_in_interactive_mode { false };
    Vector<Token, 2> m_token_buffer;
    size_t m_token_index { 0 };
    Vector<Token> m_previous_token_buffer;

    Vector<Error> m_errors;
    HashMap<String, NonnullRefPtr<AST::Heredoc>> m_unprocessed_heredoc_entries;

    Token m_eof_token;

    bool m_disallow_command_prefix { true };
};

}