diff --git a/Userland/Libraries/LibGUI/ShellSyntaxHighlighter.cpp b/Userland/Libraries/LibGUI/ShellSyntaxHighlighter.cpp index 8f3c27fa8ec..a552764c092 100644 --- a/Userland/Libraries/LibGUI/ShellSyntaxHighlighter.cpp +++ b/Userland/Libraries/LibGUI/ShellSyntaxHighlighter.cpp @@ -380,21 +380,19 @@ private: } virtual void visit(const AST::Sequence* node) override { - { + for (auto& entry : node->entries()) { ScopedValueRollback first_in_command { m_is_first_in_command }; - node->left()->visit(*this); - } - { - ScopedValueRollback first_in_command { m_is_first_in_command }; - node->right()->visit(*this); + entry.visit(*this); } - auto& span = span_for_node(node); - span.range.set_start({ node->separator_position().start_line.line_number, node->separator_position().start_line.line_column }); - set_offset_range_end(span.range, node->separator_position().end_line); - span.attributes.color = m_palette.syntax_punctuation(); - span.attributes.bold = true; - span.is_skippable = true; + for (auto& position : node->separator_positions()) { + auto& span = span_for_node(node); + span.range.set_start({ position.start_line.line_number, position.start_line.line_column }); + set_offset_range_end(span.range, position.end_line); + span.attributes.color = m_palette.syntax_punctuation(); + span.attributes.bold = true; + span.is_skippable = true; + } } virtual void visit(const AST::Subshell* node) override { diff --git a/Userland/Shell/AST.cpp b/Userland/Shell/AST.cpp index 93dc4fee8ed..3bb9acf09a6 100644 --- a/Userland/Shell/AST.cpp +++ b/Userland/Shell/AST.cpp @@ -2302,32 +2302,37 @@ ReadWriteRedirection::~ReadWriteRedirection() void Sequence::dump(int level) const { Node::dump(level); - m_left->dump(level + 1); - m_right->dump(level + 1); + for (auto& entry : m_entries) + entry.dump(level + 1); } RefPtr Sequence::run(RefPtr shell) { - auto left = m_left->to_lazy_evaluated_commands(shell); - // This could happen if a comment is next to a command. - if (left.size() == 1) { - auto& command = left.first(); - if (command.argv.is_empty() && command.redirections.is_empty() && command.next_chain.is_empty()) - return m_right->run(shell); + Vector all_commands; + Command* last_command_in_sequence = nullptr; + for (auto& entry : m_entries) { + if (!last_command_in_sequence) { + auto commands = entry.to_lazy_evaluated_commands(shell); + all_commands.append(move(commands)); + last_command_in_sequence = &all_commands.last(); + continue; + } + + if (last_command_in_sequence->should_wait) { + last_command_in_sequence->next_chain.append(NodeWithAction { entry, NodeWithAction::Sequence }); + } else { + all_commands.append(entry.to_lazy_evaluated_commands(shell)); + last_command_in_sequence = &all_commands.last(); + } } - if (left.last().should_wait) - left.last().next_chain.append(NodeWithAction { *m_right, NodeWithAction::Sequence }); - else - left.append(m_right->to_lazy_evaluated_commands(shell)); - - return create(move(left)); + return create(move(all_commands)); } void Sequence::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata) { - m_left->highlight_in_editor(editor, shell, metadata); - m_right->highlight_in_editor(editor, shell, metadata); + for (auto& entry : m_entries) + entry.highlight_in_editor(editor, shell, metadata); } HitTestResult Sequence::hit_test_position(size_t offset) @@ -2335,29 +2340,29 @@ HitTestResult Sequence::hit_test_position(size_t offset) if (!position().contains(offset)) return {}; - auto result = m_left->hit_test_position(offset); - if (result.matching_node) { - if (!result.closest_command_node) - result.closest_command_node = m_right; - return result; + for (auto& entry : m_entries) { + auto result = entry.hit_test_position(offset); + if (result.matching_node) { + if (!result.closest_command_node) + result.closest_command_node = entry; + return result; + } } - result = m_right->hit_test_position(offset); - if (!result.closest_command_node) - result.closest_command_node = m_right; - return result; + return {}; } -Sequence::Sequence(Position position, NonnullRefPtr left, NonnullRefPtr right, Position separator_position) +Sequence::Sequence(Position position, NonnullRefPtrVector entries, Vector separator_positions) : Node(move(position)) - , m_left(move(left)) - , m_right(move(right)) - , m_separator_position(separator_position) + , m_entries(move(entries)) + , m_separator_positions(separator_positions) { - if (m_left->is_syntax_error()) - set_is_syntax_error(m_left->syntax_error_node()); - else if (m_right->is_syntax_error()) - set_is_syntax_error(m_right->syntax_error_node()); + for (auto& entry : m_entries) { + if (entry.is_syntax_error()) { + set_is_syntax_error(entry.syntax_error_node()); + break; + } + } } Sequence::~Sequence() diff --git a/Userland/Shell/AST.h b/Userland/Shell/AST.h index 6d74f0f5126..5d34c49173b 100644 --- a/Userland/Shell/AST.h +++ b/Userland/Shell/AST.h @@ -1139,14 +1139,13 @@ private: class Sequence final : public Node { public: - Sequence(Position, NonnullRefPtr, NonnullRefPtr, Position separator_position); + Sequence(Position, NonnullRefPtrVector, Vector separator_positions); virtual ~Sequence(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } - const NonnullRefPtr& left() const { return m_left; } - const NonnullRefPtr& right() const { return m_right; } + const NonnullRefPtrVector& entries() const { return m_entries; } - const Position& separator_position() const { return m_separator_position; } + const Vector& separator_positions() const { return m_separator_positions; } private: NODE(Sequence); @@ -1157,9 +1156,8 @@ private: virtual bool is_list() const override { return true; } virtual bool should_override_execution_in_current_process() const override { return true; } - NonnullRefPtr m_left; - NonnullRefPtr m_right; - Position m_separator_position; + NonnullRefPtrVector m_entries; + Vector m_separator_positions; }; class Subshell final : public Node { diff --git a/Userland/Shell/Formatter.cpp b/Userland/Shell/Formatter.cpp index ee5a3ad28e5..27a0dc08b1e 100644 --- a/Userland/Shell/Formatter.cpp +++ b/Userland/Shell/Formatter.cpp @@ -631,10 +631,17 @@ void Formatter::visit(const AST::Sequence* node) test_and_update_output_cursor(node); TemporaryChange parent { m_parent_node, node }; - node->left()->visit(*this); - insert_separator(); - node->right()->visit(*this); + bool first = true; + for (auto& entry : node->entries()) { + if (first) + first = false; + else + insert_separator(); + + entry.visit(*this); + } + visited(node); } diff --git a/Userland/Shell/NodeVisitor.cpp b/Userland/Shell/NodeVisitor.cpp index 44ce39a4c47..b4edd8426ba 100644 --- a/Userland/Shell/NodeVisitor.cpp +++ b/Userland/Shell/NodeVisitor.cpp @@ -186,8 +186,8 @@ void NodeVisitor::visit(const AST::ReadWriteRedirection* node) void NodeVisitor::visit(const AST::Sequence* node) { - node->left()->visit(*this); - node->right()->visit(*this); + for (auto& entry : node->entries()) + entry.visit(*this); } void NodeVisitor::visit(const AST::Subshell* node) diff --git a/Userland/Shell/Parser.cpp b/Userland/Shell/Parser.cpp index f5b6f7a5e79..d705a9a27a6 100644 --- a/Userland/Shell/Parser.cpp +++ b/Userland/Shell/Parser.cpp @@ -111,6 +111,11 @@ NonnullRefPtr Parser::create(Args... args) return make(m_rule_start_offsets, m_rule_start_lines, m_offset, m_line.line_number, m_line.line_column); } +Parser::Offset Parser::current_position() +{ + return Offset { m_offset, { m_line.line_number, m_line.line_column } }; +} + static constexpr bool is_whitespace(char c) { return c == ' ' || c == '\t'; @@ -180,58 +185,70 @@ RefPtr Parser::parse_toplevel() { auto rule_start = push_start(); - if (auto sequence = parse_sequence()) - return create(sequence.release_nonnull()); + SequenceParseResult result; + NonnullRefPtrVector sequence; + Vector positions; + do { + result = parse_sequence(); + if (result.entries.is_empty()) + break; - return nullptr; + sequence.append(move(result.entries)); + positions.append(move(result.separator_positions)); + } while (result.decision == ShouldReadMoreSequences::Yes); + + if (sequence.is_empty()) + return nullptr; + + return create( + create(move(sequence), move(positions))); } -RefPtr Parser::parse_sequence() +Parser::SequenceParseResult Parser::parse_sequence() { consume_while(is_any_of(" \t\n;")); // ignore whitespaces or terminators without effect. + NonnullRefPtrVector left; + auto rule_start = push_start(); - auto var_decls = parse_variable_decls(); + { + auto var_decls = parse_variable_decls(); + if (var_decls) + left.append(var_decls.release_nonnull()); + } auto pos_before_seps = save_offset(); switch (peek()) { case '}': - return var_decls; + return { move(left), {}, ShouldReadMoreSequences::No }; case ';': case '\n': { - if (!var_decls) + if (left.is_empty()) break; consume_while(is_any_of("\n;")); - auto pos_after_seps = save_offset(); + AST::Position separator_position { pos_before_seps.offset, pos_after_seps.offset, pos_before_seps.line, pos_after_seps.line }; - auto rest = parse_sequence(); - if (rest) - return create( - var_decls.release_nonnull(), - rest.release_nonnull(), - AST::Position { pos_before_seps.offset, pos_after_seps.offset, pos_before_seps.line, pos_after_seps.line }); - return var_decls; + return { move(left), { move(separator_position) }, ShouldReadMoreSequences::Yes }; } default: break; } - auto first = parse_function_decl(); + auto first_entry = parse_function_decl(); - if (!first) - first = parse_or_logical_sequence(); + Vector separator_positions; - if (!first) - return var_decls; + if (!first_entry) + first_entry = parse_or_logical_sequence(); - if (var_decls) - first = create( - var_decls.release_nonnull(), - first.release_nonnull(), - AST::Position { pos_before_seps.offset, pos_before_seps.offset, pos_before_seps.line, pos_before_seps.line }); + if (!first_entry) + return { move(left), {}, ShouldReadMoreSequences::No }; + + left.append(first_entry.release_nonnull()); + separator_positions.empend(pos_before_seps.offset, pos_before_seps.offset, pos_before_seps.line, pos_before_seps.line); consume_while(is_whitespace); @@ -241,29 +258,19 @@ RefPtr Parser::parse_sequence() case '\n': { consume_while(is_any_of("\n;")); auto pos_after_seps = save_offset(); - - if (auto expr = parse_sequence()) { - return create( - first.release_nonnull(), - expr.release_nonnull(), - AST::Position { pos_before_seps.offset, pos_after_seps.offset, pos_before_seps.line, pos_after_seps.line }); // Sequence - } - return first; + separator_positions.empend(pos_before_seps.offset, pos_after_seps.offset, pos_before_seps.line, pos_after_seps.line); + return { move(left), move(separator_positions), ShouldReadMoreSequences::Yes }; } case '&': { consume(); auto pos_after_seps = save_offset(); - auto bg = create(first.release_nonnull()); // Execute Background - if (auto rest = parse_sequence()) - return create( - move(bg), - rest.release_nonnull(), - AST::Position { pos_before_seps.offset, pos_after_seps.offset, pos_before_seps.line, pos_before_seps.line }); // Sequence Background Sequence - - return bg; + auto bg = create(left.take_last()); // Execute Background + left.append(move(bg)); + separator_positions.empend(pos_before_seps.offset, pos_after_seps.offset, pos_before_seps.line, pos_after_seps.line); + return { move(left), move(separator_positions), ShouldReadMoreSequences::Yes }; } default: - return first; + return { move(left), move(separator_positions), ShouldReadMoreSequences::No }; } } diff --git a/Userland/Shell/Parser.h b/Userland/Shell/Parser.h index aa2a3b9e531..a3f09b944e3 100644 --- a/Userland/Shell/Parser.h +++ b/Userland/Shell/Parser.h @@ -55,9 +55,19 @@ public: SavedOffset save_offset() const; private: + enum class ShouldReadMoreSequences { + Yes, + No, + }; + struct SequenceParseResult { + NonnullRefPtrVector entries; + Vector separator_positions; + ShouldReadMoreSequences decision; + }; + constexpr static size_t max_allowed_nested_rule_depth = 2048; RefPtr parse_toplevel(); - RefPtr parse_sequence(); + SequenceParseResult parse_sequence(); RefPtr parse_function_decl(); RefPtr parse_and_logical_sequence(); RefPtr parse_or_logical_sequence(); @@ -108,6 +118,10 @@ private: StringView consume_while(Function); + struct Offset { + size_t offset; + AST::Position::Line line; + }; struct ScopedOffset { ScopedOffset(Vector& offsets, Vector& lines, size_t offset, size_t lineno, size_t linecol) : offsets(offsets) @@ -136,6 +150,7 @@ private: void restore_to(const ScopedOffset& offset) { restore_to(offset.offset, offset.line); } OwnPtr push_start(); + Offset current_position(); StringView m_input; size_t m_offset { 0 };