1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-28 16:15:10 +00:00

Shell: Start implementing a POSIX-compliant parser

The parser is still very much a work-in-progress, but it can currently
parse most of the basic bits, the only *completely* unimplemented things
in the parser are:
- heredocs (io_here)
- alias expansion
- arithmetic expansion

There are a whole suite of bugs, and syntax highlighting is unreliable
at best.
For now, this is not attached anywhere, a future commit will enable it
for /bin/sh or a `Shell --posix` invocation.
This commit is contained in:
Ali Mohammad Pur 2023-02-11 17:59:15 +03:30 committed by Ali Mohammad Pur
parent 2dc1682274
commit 2a276c86d4
14 changed files with 3444 additions and 28 deletions

View file

@ -26,6 +26,7 @@
#include <LibCore/System.h>
#include <LibCore/Timer.h>
#include <LibLine/Editor.h>
#include <Shell/PosixParser.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@ -297,7 +298,7 @@ Vector<AST::Command> Shell::expand_aliases(Vector<AST::Command> initial_commands
auto alias = resolve_alias(command.argv[0]);
if (!alias.is_null()) {
auto argv0 = command.argv.take_first();
auto subcommand_ast = Parser { alias }.parse();
auto subcommand_ast = parse(alias, false);
if (subcommand_ast) {
while (subcommand_ast->is_execute()) {
auto* ast = static_cast<AST::Execute*>(subcommand_ast.ptr());
@ -477,7 +478,7 @@ bool Shell::invoke_function(const AST::Command& command, int& retval)
DeprecatedString Shell::format(StringView source, ssize_t& cursor) const
{
Formatter formatter(source, cursor);
Formatter formatter(source, cursor, m_in_posix_mode);
auto result = formatter.format();
cursor = formatter.cursor();
@ -580,7 +581,7 @@ int Shell::run_command(StringView cmd, Optional<SourcePosition> source_position_
if (cmd.is_empty())
return 0;
auto command = Parser(cmd, m_is_interactive).parse();
auto command = parse(cmd, m_is_interactive);
if (!command)
return 0;
@ -1410,8 +1411,7 @@ void Shell::remove_entry_from_cache(StringView entry)
void Shell::highlight(Line::Editor& editor) const
{
auto line = editor.line();
Parser parser(line, m_is_interactive);
auto ast = parser.parse();
auto ast = parse(line, m_is_interactive);
if (!ast)
return;
ast->highlight_in_editor(editor, const_cast<Shell&>(*this));
@ -1425,9 +1425,7 @@ Vector<Line::CompletionSuggestion> Shell::complete()
Vector<Line::CompletionSuggestion> Shell::complete(StringView line)
{
Parser parser(line, m_is_interactive);
auto ast = parser.parse();
auto ast = parse(line, m_is_interactive);
if (!ast)
return {};
@ -2177,8 +2175,9 @@ Shell::Shell()
cache_path();
}
Shell::Shell(Line::Editor& editor, bool attempt_interactive)
: m_editor(editor)
Shell::Shell(Line::Editor& editor, bool attempt_interactive, bool posix_mode)
: m_in_posix_mode(posix_mode)
, m_editor(editor)
{
uid = getuid();
tcsetpgrp(0, getpgrp());
@ -2224,8 +2223,8 @@ Shell::Shell(Line::Editor& editor, bool attempt_interactive)
cache_path();
}
m_editor->register_key_input_callback('\n', [](Line::Editor& editor) {
auto ast = Parser(editor.line()).parse();
m_editor->register_key_input_callback('\n', [this](Line::Editor& editor) {
auto ast = parse(editor.line(), false);
if (ast && ast->is_syntax_error() && ast->syntax_error_node().is_continuable())
return true;
@ -2484,6 +2483,32 @@ void Shell::timer_event(Core::TimerEvent& event)
m_editor->save_history(get_history_path());
}
RefPtr<AST::Node> Shell::parse(StringView input, bool interactive, bool as_command) const
{
if (m_in_posix_mode) {
Posix::Parser parser(input);
if (as_command) {
auto node = parser.parse();
if constexpr (SHELL_POSIX_PARSER_DEBUG) {
dbgln("Parsed with the POSIX Parser:");
node->dump(0);
}
return node;
}
return parser.parse_word_list();
}
Parser parser { input, interactive };
if (as_command)
return parser.parse();
auto nodes = parser.parse_as_multiple_expressions();
return make_ref_counted<AST::ListConcatenate>(
nodes.is_empty() ? AST::Position { 0, 0, { 0, 0 }, { 0, 0 } } : nodes.first().position(),
move(nodes));
}
void FileDescriptionCollector::collect()
{
for (auto fd : m_fds)