2023-02-11 14:29:15 +00:00
|
|
|
/*
|
|
|
|
* 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:
|
2023-02-16 06:22:13 +00:00
|
|
|
Parser(StringView input, bool interactive = false, Optional<Reduction> starting_reduction = {})
|
2023-02-11 14:29:15 +00:00
|
|
|
: m_lexer(input)
|
|
|
|
, m_in_interactive_mode(interactive)
|
|
|
|
, m_eof_token(Token::eof())
|
|
|
|
{
|
2023-02-19 16:01:17 +00:00
|
|
|
(void)fill_token_buffer(starting_reduction);
|
2023-02-11 14:29:15 +00:00
|
|
|
}
|
|
|
|
|
2023-10-05 09:39:43 +00:00
|
|
|
enum class AllowNewlines {
|
|
|
|
No,
|
|
|
|
Yes,
|
|
|
|
};
|
|
|
|
|
2023-02-11 14:29:15 +00:00
|
|
|
RefPtr<AST::Node> parse();
|
2023-10-05 09:39:43 +00:00
|
|
|
RefPtr<AST::Node> parse_word_list(AllowNewlines = AllowNewlines::No);
|
2023-02-11 14:29:15 +00:00
|
|
|
|
|
|
|
struct Error {
|
2023-12-16 14:19:34 +00:00
|
|
|
ByteString message;
|
2023-02-11 14:29:15 +00:00
|
|
|
Optional<AST::Position> position;
|
|
|
|
};
|
|
|
|
auto& errors() const { return m_errors; }
|
|
|
|
|
|
|
|
private:
|
2023-02-19 16:01:17 +00:00
|
|
|
ErrorOr<Optional<Token>> next_expanded_token(Optional<Reduction> starting_reduction = {});
|
2023-02-11 14:29:15 +00:00
|
|
|
Vector<Token> perform_expansions(Vector<Token> tokens);
|
2023-02-19 16:01:17 +00:00
|
|
|
ErrorOr<void> fill_token_buffer(Optional<Reduction> starting_reduction = {});
|
2023-02-16 06:22:13 +00:00
|
|
|
void handle_heredoc_contents();
|
2023-02-11 14:29:15 +00:00
|
|
|
|
2023-02-16 06:22:13 +00:00
|
|
|
Token const& peek()
|
2023-02-11 14:29:15 +00:00
|
|
|
{
|
|
|
|
if (eof())
|
|
|
|
return m_eof_token;
|
2023-02-16 06:22:13 +00:00
|
|
|
handle_heredoc_contents();
|
2023-02-11 14:29:15 +00:00
|
|
|
return m_token_buffer[m_token_index];
|
|
|
|
}
|
|
|
|
Token const& consume()
|
|
|
|
{
|
|
|
|
if (eof())
|
|
|
|
return m_eof_token;
|
2023-02-16 06:22:13 +00:00
|
|
|
handle_heredoc_contents();
|
2023-02-11 14:29:15 +00:00
|
|
|
return m_token_buffer[m_token_index++];
|
|
|
|
}
|
|
|
|
void skip()
|
|
|
|
{
|
|
|
|
if (eof())
|
|
|
|
return;
|
2023-04-18 13:18:01 +00:00
|
|
|
handle_heredoc_contents();
|
2023-02-11 14:29:15 +00:00
|
|
|
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;
|
2023-03-06 13:17:01 +00:00
|
|
|
Vector<NonnullRefPtr<AST::Node>> nodes;
|
2023-02-11 14:29:15 +00:00
|
|
|
};
|
|
|
|
|
2023-02-19 16:01:17 +00:00
|
|
|
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();
|
2023-07-31 12:49:39 +00:00
|
|
|
ErrorOr<RefPtr<AST::Node>> parse_pipe_sequence(bool is_negated);
|
2023-02-19 16:01:17 +00:00
|
|
|
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();
|
2023-10-05 09:39:43 +00:00
|
|
|
ErrorOr<RefPtr<AST::Node>> parse_bash_like_list();
|
2023-02-19 16:01:17 +00:00
|
|
|
ErrorOr<CaseItemsResult> parse_case_list();
|
2023-02-11 14:29:15 +00:00
|
|
|
|
|
|
|
template<typename... Ts>
|
|
|
|
void error(Token const& token, CheckedFormatString<Ts...> fmt, Ts&&... args)
|
|
|
|
{
|
|
|
|
m_errors.append(Error {
|
2023-12-16 14:19:34 +00:00
|
|
|
ByteString::formatted(fmt.view(), forward<Ts>(args)...),
|
2023-02-11 14:29:15 +00:00
|
|
|
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;
|
2023-02-19 16:01:17 +00:00
|
|
|
HashMap<String, NonnullRefPtr<AST::Heredoc>> m_unprocessed_heredoc_entries;
|
2023-02-11 14:29:15 +00:00
|
|
|
|
|
|
|
Token m_eof_token;
|
|
|
|
|
|
|
|
bool m_disallow_command_prefix { true };
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|