Serenity Operating System
1/*
2 * Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#pragma once
8
9#include <Shell/AST.h>
10#include <Shell/PosixLexer.h>
11
12namespace Shell::Posix {
13
14class Parser {
15public:
16 Parser(StringView input, bool interactive = false, Optional<Reduction> starting_reduction = {})
17 : m_lexer(input)
18 , m_in_interactive_mode(interactive)
19 , m_eof_token(Token::eof())
20 {
21 (void)fill_token_buffer(starting_reduction);
22 }
23
24 RefPtr<AST::Node> parse();
25 RefPtr<AST::Node> parse_word_list();
26
27 struct Error {
28 DeprecatedString message;
29 Optional<AST::Position> position;
30 };
31 auto& errors() const { return m_errors; }
32
33private:
34 ErrorOr<Optional<Token>> next_expanded_token(Optional<Reduction> starting_reduction = {});
35 Vector<Token> perform_expansions(Vector<Token> tokens);
36 ErrorOr<void> fill_token_buffer(Optional<Reduction> starting_reduction = {});
37 void handle_heredoc_contents();
38
39 Token const& peek()
40 {
41 if (eof())
42 return m_eof_token;
43 handle_heredoc_contents();
44 return m_token_buffer[m_token_index];
45 }
46 Token const& consume()
47 {
48 if (eof())
49 return m_eof_token;
50 handle_heredoc_contents();
51 return m_token_buffer[m_token_index++];
52 }
53 void skip()
54 {
55 if (eof())
56 return;
57 m_token_index++;
58 }
59 bool eof() const
60 {
61 return m_token_index == m_token_buffer.size() || m_token_buffer[m_token_index].type == Token::Type::Eof;
62 }
63
64 struct CaseItemsResult {
65 Vector<AST::Position> pipe_positions;
66 Vector<NonnullRefPtr<AST::Node>> nodes;
67 };
68
69 ErrorOr<RefPtr<AST::Node>> parse_complete_command();
70 ErrorOr<RefPtr<AST::Node>> parse_list();
71 ErrorOr<RefPtr<AST::Node>> parse_and_or();
72 ErrorOr<RefPtr<AST::Node>> parse_pipeline();
73 ErrorOr<RefPtr<AST::Node>> parse_pipe_sequence();
74 ErrorOr<RefPtr<AST::Node>> parse_command();
75 ErrorOr<RefPtr<AST::Node>> parse_compound_command();
76 ErrorOr<RefPtr<AST::Node>> parse_subshell();
77 ErrorOr<RefPtr<AST::Node>> parse_compound_list();
78 ErrorOr<RefPtr<AST::Node>> parse_term();
79 ErrorOr<RefPtr<AST::Node>> parse_for_clause();
80 ErrorOr<RefPtr<AST::Node>> parse_case_clause();
81 ErrorOr<RefPtr<AST::Node>> parse_if_clause();
82 ErrorOr<RefPtr<AST::Node>> parse_while_clause();
83 ErrorOr<RefPtr<AST::Node>> parse_until_clause();
84 ErrorOr<RefPtr<AST::Node>> parse_function_definition();
85 ErrorOr<RefPtr<AST::Node>> parse_function_body();
86 ErrorOr<RefPtr<AST::Node>> parse_brace_group();
87 ErrorOr<RefPtr<AST::Node>> parse_do_group();
88 ErrorOr<RefPtr<AST::Node>> parse_simple_command();
89 ErrorOr<RefPtr<AST::Node>> parse_prefix();
90 ErrorOr<RefPtr<AST::Node>> parse_suffix();
91 ErrorOr<RefPtr<AST::Node>> parse_io_redirect();
92 ErrorOr<RefPtr<AST::Node>> parse_redirect_list();
93 ErrorOr<RefPtr<AST::Node>> parse_io_file(AST::Position, Optional<int> fd);
94 ErrorOr<RefPtr<AST::Node>> parse_io_here(AST::Position, Optional<int> fd);
95 ErrorOr<RefPtr<AST::Node>> parse_word();
96 ErrorOr<CaseItemsResult> parse_case_list();
97
98 template<typename... Ts>
99 void error(Token const& token, CheckedFormatString<Ts...> fmt, Ts&&... args)
100 {
101 m_errors.append(Error {
102 DeprecatedString::formatted(fmt.view(), forward<Ts>(args)...),
103 token.position,
104 });
105 }
106
107 Lexer m_lexer;
108 bool m_in_interactive_mode { false };
109 Vector<Token, 2> m_token_buffer;
110 size_t m_token_index { 0 };
111 Vector<Token> m_previous_token_buffer;
112
113 Vector<Error> m_errors;
114 HashMap<String, NonnullRefPtr<AST::Heredoc>> m_unprocessed_heredoc_entries;
115
116 Token m_eof_token;
117
118 bool m_disallow_command_prefix { true };
119};
120
121}