/* * Copyright (c) 2020, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include "Forward.h" #include "Job.h" #include "NodeVisitor.h" #include #include #include #include #include #include #include #include #include namespace Shell::AST { using AK::make_ref_counted; template static inline NonnullRefPtr make_ref_counted(std::initializer_list> arg) { return adopt_ref(*new T(arg)); } struct HighlightMetadata { bool is_first_in_list { true }; }; struct Position { size_t start_offset { 0 }; size_t end_offset { 0 }; struct Line { size_t line_number { 0 }; size_t line_column { 0 }; bool operator==(Line const& other) const { return line_number == other.line_number && line_column == other.line_column; } } start_line, end_line; bool contains(size_t offset) const { return start_offset <= offset && offset <= end_offset; } }; struct NameWithPosition { String name; Position position; }; struct FdRedirection; struct Rewiring : public RefCounted { int old_fd { -1 }; int new_fd { -1 }; FdRedirection* other_pipe_end { nullptr }; enum class Close { None, Old, New, RefreshNew, RefreshOld, ImmediatelyCloseNew, } fd_action { Close::None }; Rewiring(int source, int dest, Close close = Close::None) : old_fd(source) , new_fd(dest) , fd_action(close) { } Rewiring(int source, int dest, FdRedirection* other_end, Close close) : old_fd(source) , new_fd(dest) , other_pipe_end(other_end) , fd_action(close) { } }; struct Redirection : public RefCounted { virtual ErrorOr> apply() const = 0; virtual ~Redirection(); virtual bool is_path_redirection() const { return false; } virtual bool is_fd_redirection() const { return false; } virtual bool is_close_redirection() const { return false; } }; struct CloseRedirection : public Redirection { int fd { -1 }; virtual ErrorOr> apply() const override; virtual ~CloseRedirection(); CloseRedirection(int fd) : fd(fd) { } private: virtual bool is_close_redirection() const override { return true; } }; struct PathRedirection : public Redirection { String path; int fd { -1 }; enum { Read, Write, WriteAppend, ReadWrite, } direction { Read }; static NonnullRefPtr create(String path, int fd, decltype(direction) direction) { return adopt_ref(*new PathRedirection(move(path), fd, direction)); } virtual ErrorOr> apply() const override; virtual ~PathRedirection(); private: PathRedirection(String path, int fd, decltype(direction) direction) : path(move(path)) , fd(fd) , direction(direction) { } virtual bool is_path_redirection() const override { return true; } }; struct FdRedirection : public Redirection { public: static NonnullRefPtr create(int old_fd, int new_fd, Rewiring::Close close) { return adopt_ref(*new FdRedirection(old_fd, new_fd, close)); } static NonnullRefPtr create(int old_fd, int new_fd, FdRedirection* pipe_end, Rewiring::Close close) { return adopt_ref(*new FdRedirection(old_fd, new_fd, pipe_end, close)); } virtual ~FdRedirection(); virtual ErrorOr> apply() const override { return adopt_ref(*new Rewiring(old_fd, new_fd, other_pipe_end, action)); } int old_fd { -1 }; int new_fd { -1 }; FdRedirection* other_pipe_end { nullptr }; Rewiring::Close action { Rewiring::Close::None }; private: FdRedirection(int source, int dest, Rewiring::Close close) : FdRedirection(source, dest, nullptr, close) { } FdRedirection(int old_fd, int new_fd, FdRedirection* pipe_end, Rewiring::Close close) : old_fd(old_fd) , new_fd(new_fd) , other_pipe_end(pipe_end) , action(close) { } virtual bool is_fd_redirection() const override { return true; } }; class Pipeline : public RefCounted { public: pid_t pgid { -1 }; }; struct NodeWithAction { mutable NonnullRefPtr node; enum Action { And, Or, Sequence, } action; NodeWithAction(Node& node, Action action) : node(node) , action(action) { } }; struct Command { Vector argv; NonnullRefPtrVector redirections; bool should_wait { true }; bool is_pipe_source { false }; bool should_notify_if_in_background { true }; bool should_immediately_execute_next { false }; mutable RefPtr pipeline; Vector next_chain; Optional position; }; struct HitTestResult { RefPtr matching_node; RefPtr closest_node_with_semantic_meaning; // This is used if matching_node is a bareword RefPtr closest_command_node; // This is used if matching_node is a bareword, and it is not the first in a list }; class Value : public RefCounted { public: virtual Vector resolve_as_list(RefPtr) = 0; virtual String resolve_as_string(RefPtr shell); virtual Vector resolve_as_commands(RefPtr); virtual NonnullRefPtr resolve_without_cast(RefPtr) { return *this; } virtual NonnullRefPtr clone() const = 0; virtual NonnullRefPtr with_slices(NonnullRefPtr slice) const&; virtual NonnullRefPtr with_slices(NonnullRefPtrVector slices) const&; virtual ~Value(); virtual bool is_command() const { return false; } virtual bool is_glob() const { return false; } virtual bool is_job() const { return false; } virtual bool is_list() const { return false; } virtual bool is_string() const { return false; } virtual bool is_list_without_resolution() const { return false; } protected: Value& set_slices(NonnullRefPtrVector slices) { m_slices = move(slices); return *this; } NonnullRefPtrVector m_slices; }; class CommandValue final : public Value { public: virtual Vector resolve_as_list(RefPtr) override; virtual Vector resolve_as_commands(RefPtr) override; virtual NonnullRefPtr clone() const override { return make_ref_counted(m_command)->set_slices(m_slices); } virtual ~CommandValue(); virtual bool is_command() const override { return true; } CommandValue(Command command) : m_command(move(command)) { } CommandValue(Vector argv, Position position) : m_command({ move(argv), {}, true, false, true, false, nullptr, {}, move(position) }) { } private: Command m_command; }; class CommandSequenceValue final : public Value { public: virtual Vector resolve_as_list(RefPtr) override; virtual Vector resolve_as_commands(RefPtr) override; virtual NonnullRefPtr clone() const override { return make_ref_counted(m_contained_values)->set_slices(m_slices); } virtual ~CommandSequenceValue(); virtual bool is_command() const override { return true; } CommandSequenceValue(Vector commands) : m_contained_values(move(commands)) { } private: Vector m_contained_values; }; class JobValue final : public Value { public: virtual Vector resolve_as_list(RefPtr) override { VERIFY_NOT_REACHED(); } virtual String resolve_as_string(RefPtr) override { return String::formatted("%{}", m_job->job_id()); } virtual Vector resolve_as_commands(RefPtr) override { VERIFY_NOT_REACHED(); } virtual NonnullRefPtr clone() const override { return make_ref_counted(m_job)->set_slices(m_slices); } virtual ~JobValue(); virtual bool is_job() const override { return true; } JobValue(RefPtr job) : m_job(move(job)) { } RefPtr const job() const { return m_job; } private: RefPtr m_job; }; class ListValue final : public Value { public: virtual Vector resolve_as_list(RefPtr) override; virtual NonnullRefPtr resolve_without_cast(RefPtr) override; virtual NonnullRefPtr clone() const override { return make_ref_counted(m_contained_values)->set_slices(m_slices); } virtual ~ListValue(); virtual bool is_list() const override { return true; } virtual bool is_list_without_resolution() const override { return true; } ListValue(Vector values); ListValue(Vector> values) : m_contained_values(move(static_cast&>(values))) { } ListValue(NonnullRefPtrVector values) : m_contained_values(move(values)) { } NonnullRefPtrVector const& values() const { return m_contained_values; } NonnullRefPtrVector& values() { return m_contained_values; } private: NonnullRefPtrVector m_contained_values; }; class StringValue final : public Value { public: virtual Vector resolve_as_list(RefPtr) override; virtual String resolve_as_string(RefPtr shell) override; virtual NonnullRefPtr clone() const override { return make_ref_counted(m_string, m_split, m_keep_empty)->set_slices(m_slices); } virtual ~StringValue(); virtual bool is_string() const override { return m_split.is_null(); } virtual bool is_list() const override { return !m_split.is_null(); } NonnullRefPtr resolve_without_cast(RefPtr) override; StringValue(String string, String split_by = {}, bool keep_empty = false) : m_string(move(string)) , m_split(move(split_by)) , m_keep_empty(keep_empty) { } private: String m_string; String m_split; bool m_keep_empty { false }; }; class GlobValue final : public Value { public: virtual Vector resolve_as_list(RefPtr) override; virtual NonnullRefPtr clone() const override { return make_ref_counted(m_glob, m_generation_position)->set_slices(m_slices); } virtual ~GlobValue(); virtual bool is_glob() const override { return true; } GlobValue(String glob, Position position) : m_glob(move(glob)) , m_generation_position(move(position)) { } private: String m_glob; Position m_generation_position; }; class SimpleVariableValue final : public Value { public: virtual Vector resolve_as_list(RefPtr) override; virtual String resolve_as_string(RefPtr) override; virtual NonnullRefPtr resolve_without_cast(RefPtr) override; virtual NonnullRefPtr clone() const override { return make_ref_counted(m_name)->set_slices(m_slices); } virtual ~SimpleVariableValue(); SimpleVariableValue(String name) : m_name(move(name)) { } private: String m_name; }; class SpecialVariableValue final : public Value { public: virtual Vector resolve_as_list(RefPtr) override; virtual String resolve_as_string(RefPtr) override; virtual NonnullRefPtr resolve_without_cast(RefPtr) override; virtual NonnullRefPtr clone() const override { return make_ref_counted(m_name)->set_slices(m_slices); } virtual ~SpecialVariableValue(); SpecialVariableValue(char name) : m_name(name) { } private: char m_name { 0 }; }; class TildeValue final : public Value { public: virtual Vector resolve_as_list(RefPtr) override; virtual String resolve_as_string(RefPtr) override; virtual NonnullRefPtr clone() const override { return make_ref_counted(m_username)->set_slices(m_slices); } virtual ~TildeValue(); virtual bool is_string() const override { return true; } TildeValue(String name) : m_username(move(name)) { } private: String m_username; }; class Node : public RefCounted { AK_MAKE_NONCOPYABLE(Node); AK_MAKE_NONMOVABLE(Node); public: virtual void dump(int level) const = 0; virtual void for_each_entry(RefPtr shell, Function)> callback); virtual RefPtr run(RefPtr) = 0; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) = 0; virtual Vector complete_for_editor(Shell&, size_t, HitTestResult const&); Vector complete_for_editor(Shell& shell, size_t offset); virtual HitTestResult hit_test_position(size_t offset) const { if (m_position.contains(offset)) return { this, nullptr, nullptr }; return { nullptr, nullptr, nullptr }; } virtual String class_name() const { return "Node"; } Node(Position); virtual ~Node() = default; virtual bool is_bareword() const { return false; } virtual bool is_command() const { return false; } virtual bool is_execute() const { return false; } virtual bool is_glob() const { return false; } virtual bool is_tilde() const { return false; } virtual bool is_variable_decls() const { return false; } virtual bool is_simple_variable() const { return false; } virtual bool is_syntax_error() const; virtual bool is_list() const { return false; } virtual bool would_execute() const { return false; } virtual bool should_override_execution_in_current_process() const { return false; } Position const& position() const { return m_position; } virtual void clear_syntax_error(); virtual void set_is_syntax_error(SyntaxError const& error_node); virtual SyntaxError const& syntax_error_node() const { VERIFY(is_syntax_error()); return *m_syntax_error_node; } virtual RefPtr leftmost_trivial_literal() const { return nullptr; } Vector to_lazy_evaluated_commands(RefPtr shell); virtual void visit(NodeVisitor&) { VERIFY_NOT_REACHED(); } virtual void visit(NodeVisitor& visitor) const { const_cast(this)->visit(visitor); } enum class Kind : u32 { And, Background, BarewordLiteral, BraceExpansion, CastToCommand, CastToList, CloseFdRedirection, CommandLiteral, Comment, ContinuationControl, DoubleQuotedString, DynamicEvaluate, Execute, Fd2FdRedirection, ForLoop, FunctionDeclaration, Glob, Heredoc, HistoryEvent, IfCond, ImmediateExpression, Join, Juxtaposition, ListConcatenate, MatchExpr, Or, Pipe, Range, ReadRedirection, ReadWriteRedirection, Sequence, Slice, SimpleVariable, SpecialVariable, StringLiteral, StringPartCompose, Subshell, SyntaxError, SyntheticValue, Tilde, VariableDeclarations, WriteAppendRedirection, WriteRedirection, __Count, }; virtual Kind kind() const = 0; protected: Position m_position; RefPtr m_syntax_error_node; }; #define NODE(name) \ virtual String class_name() const override \ { \ return #name; \ } \ virtual Kind kind() const override \ { \ return Kind::name; \ } class PathRedirectionNode : public Node { public: PathRedirectionNode(Position, int, NonnullRefPtr); virtual ~PathRedirectionNode(); virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual Vector complete_for_editor(Shell&, size_t, HitTestResult const&) override; virtual HitTestResult hit_test_position(size_t offset) const override; virtual bool is_command() const override { return true; } virtual bool is_list() const override { return true; } virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NonnullRefPtr const& path() const { return m_path; } int fd() const { return m_fd; } protected: int m_fd { -1 }; NonnullRefPtr m_path; }; class And final : public Node { public: And(Position, NonnullRefPtr, NonnullRefPtr, Position and_position); virtual ~And() = default; virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NonnullRefPtr const& left() const { return m_left; } NonnullRefPtr const& right() const { return m_right; } Position const& and_position() const { return m_and_position; } private: NODE(And); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; NonnullRefPtr m_left; NonnullRefPtr m_right; Position m_and_position; }; class ListConcatenate final : public Node { public: ListConcatenate(Position, Vector>); virtual ~ListConcatenate() = default; virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } Vector> const list() const { return m_list; } private: NODE(ListConcatenate); virtual void dump(int level) const override; virtual void for_each_entry(RefPtr shell, Function)> callback) override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; virtual bool is_list() const override { return true; } virtual RefPtr leftmost_trivial_literal() const override; Vector> m_list; }; class Background final : public Node { public: Background(Position, NonnullRefPtr); virtual ~Background() = default; virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NonnullRefPtr const& command() const { return m_command; } private: NODE(Background); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; NonnullRefPtr m_command; }; class BarewordLiteral final : public Node { public: BarewordLiteral(Position, String); virtual ~BarewordLiteral() = default; virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } String const& text() const { return m_text; } private: NODE(BarewordLiteral); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual bool is_bareword() const override { return true; } virtual RefPtr leftmost_trivial_literal() const override { return this; } String m_text; }; class BraceExpansion final : public Node { public: BraceExpansion(Position, NonnullRefPtrVector); virtual ~BraceExpansion() = default; virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NonnullRefPtrVector const& entries() const { return m_entries; } private: NODE(BraceExpansion); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; NonnullRefPtrVector m_entries; }; class CastToCommand final : public Node { public: CastToCommand(Position, NonnullRefPtr); virtual ~CastToCommand() = default; virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NonnullRefPtr const& inner() const { return m_inner; } private: NODE(CastToCommand); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; virtual Vector complete_for_editor(Shell&, size_t, HitTestResult const&) override; virtual bool is_command() const override { return true; } virtual bool is_list() const override { return true; } virtual RefPtr leftmost_trivial_literal() const override; NonnullRefPtr m_inner; }; class CastToList final : public Node { public: CastToList(Position, RefPtr); virtual ~CastToList() = default; virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } RefPtr const& inner() const { return m_inner; } private: NODE(CastToList); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void for_each_entry(RefPtr shell, Function)> callback) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; virtual bool is_list() const override { return true; } virtual RefPtr leftmost_trivial_literal() const override; RefPtr m_inner; }; class CloseFdRedirection final : public Node { public: CloseFdRedirection(Position, int); virtual ~CloseFdRedirection(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } int fd() const { return m_fd; } private: NODE(CloseFdRedirection); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual bool is_command() const override { return true; } int m_fd { -1 }; }; class CommandLiteral final : public Node { public: CommandLiteral(Position, Command); virtual ~CommandLiteral(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } Command const& command() const { return m_command; } private: NODE(CommandLiteral); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override { VERIFY_NOT_REACHED(); } virtual bool is_command() const override { return true; } virtual bool is_list() const override { return true; } Command m_command; }; class Comment : public Node { public: Comment(Position, String); virtual ~Comment(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } String const& text() const { return m_text; } private: NODE(Comment); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; String m_text; }; class ContinuationControl final : public Node { public: enum ContinuationKind { Break, Continue, }; ContinuationControl(Position position, ContinuationKind kind) : Node(move(position)) , m_kind(kind) { } virtual ~ContinuationControl() = default; virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } ContinuationKind continuation_kind() const { return m_kind; } private: NODE(ContinuationControl); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; ContinuationKind m_kind { ContinuationKind::Break }; }; class DynamicEvaluate final : public Node { public: DynamicEvaluate(Position, NonnullRefPtr); virtual ~DynamicEvaluate(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NonnullRefPtr const& inner() const { return m_inner; } private: NODE(DynamicEvaluate); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; virtual bool is_bareword() const override { return m_inner->is_bareword(); } virtual bool is_command() const override { return is_list(); } virtual bool is_execute() const override { return true; } virtual bool is_glob() const override { return m_inner->is_glob(); } virtual bool is_list() const override { return m_inner->is_list() || m_inner->is_command() || m_inner->is_glob(); // Anything that generates a list. } NonnullRefPtr m_inner; }; class DoubleQuotedString final : public Node { public: DoubleQuotedString(Position, RefPtr); virtual ~DoubleQuotedString(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } RefPtr const& inner() const { return m_inner; } private: NODE(DoubleQuotedString); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; RefPtr m_inner; }; class Fd2FdRedirection final : public Node { public: Fd2FdRedirection(Position, int, int); virtual ~Fd2FdRedirection(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } int source_fd() const { return m_old_fd; } int dest_fd() const { return m_new_fd; } private: NODE(Fd2FdRedirection); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual bool is_command() const override { return true; } int m_old_fd { -1 }; int m_new_fd { -1 }; }; class FunctionDeclaration final : public Node { public: FunctionDeclaration(Position, NameWithPosition name, Vector argument_names, RefPtr body); virtual ~FunctionDeclaration(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NameWithPosition const& name() const { return m_name; } Vector const arguments() const { return m_arguments; } RefPtr const& block() const { return m_block; } private: NODE(FunctionDeclaration); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; virtual Vector complete_for_editor(Shell&, size_t, HitTestResult const&) override; virtual bool would_execute() const override { return true; } virtual bool should_override_execution_in_current_process() const override { return true; } NameWithPosition m_name; Vector m_arguments; RefPtr m_block; }; class ForLoop final : public Node { public: ForLoop(Position, Optional variable, Optional index_variable, RefPtr iterated_expr, RefPtr block, Optional in_kw_position = {}, Optional index_kw_position = {}); virtual ~ForLoop(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } Optional const& variable() const { return m_variable; } Optional const& index_variable() const { return m_index_variable; } RefPtr const& iterated_expression() const { return m_iterated_expression; } RefPtr const& block() const { return m_block; } Optional const index_keyword_position() const { return m_index_kw_position; } Optional const in_keyword_position() const { return m_in_kw_position; } private: NODE(ForLoop); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; virtual bool would_execute() const override { return true; } virtual bool should_override_execution_in_current_process() const override { return true; } Optional m_variable; Optional m_index_variable; RefPtr m_iterated_expression; RefPtr m_block; Optional m_in_kw_position; Optional m_index_kw_position; }; class Glob final : public Node { public: Glob(Position, String); virtual ~Glob(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } String const& text() const { return m_text; } private: NODE(Glob); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual bool is_glob() const override { return true; } virtual bool is_list() const override { return true; } String m_text; }; struct HistorySelector { enum EventKind { IndexFromStart, IndexFromEnd, StartingStringLookup, ContainingStringLookup, }; enum WordSelectorKind { Index, Last, }; struct { EventKind kind { IndexFromStart }; size_t index { 0 }; Position text_position; String text; } event; struct WordSelector { WordSelectorKind kind { Index }; size_t selector { 0 }; Position position; RefPtr syntax_error_node; size_t resolve(size_t size) const { if (kind == Index) return selector; if (kind == Last) return size - selector - 1; VERIFY_NOT_REACHED(); } }; struct { WordSelector start; Optional end; } word_selector_range; }; class HistoryEvent final : public Node { public: HistoryEvent(Position, HistorySelector); virtual ~HistoryEvent(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } HistorySelector const& selector() const { return m_selector; } private: NODE(HistoryEvent); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; HistorySelector m_selector; }; class Execute final : public Node { public: Execute(Position, NonnullRefPtr, bool capture_stdout = false); virtual ~Execute(); void capture_stdout() { m_capture_stdout = true; } NonnullRefPtr& command() { return m_command; } virtual void for_each_entry(RefPtr shell, Function)> callback) override; virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NonnullRefPtr const& command() const { return m_command; } bool does_capture_stdout() const { return m_capture_stdout; } private: NODE(Execute); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; virtual Vector complete_for_editor(Shell&, size_t, HitTestResult const&) override; virtual bool is_execute() const override { return true; } virtual bool would_execute() const override { return true; } NonnullRefPtr m_command; bool m_capture_stdout { false }; }; class IfCond final : public Node { public: IfCond(Position, Optional else_position, NonnullRefPtr cond_expr, RefPtr true_branch, RefPtr false_branch); virtual ~IfCond(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NonnullRefPtr const& condition() const { return m_condition; } RefPtr const& true_branch() const { return m_true_branch; } RefPtr const& false_branch() const { return m_false_branch; } Optional const else_position() const { return m_else_position; } private: NODE(IfCond); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; virtual bool should_override_execution_in_current_process() const override { return true; } NonnullRefPtr m_condition; RefPtr m_true_branch; RefPtr m_false_branch; Optional m_else_position; }; class ImmediateExpression final : public Node { public: ImmediateExpression(Position, NameWithPosition function, NonnullRefPtrVector arguments, Optional closing_brace_position); virtual ~ImmediateExpression(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NonnullRefPtrVector const& arguments() const { return m_arguments; } auto const& function() const { return m_function; } String const& function_name() const { return m_function.name; } Position const& function_position() const { return m_function.position; } bool has_closing_brace() const { return m_closing_brace_position.has_value(); } private: NODE(ImmediateExpression); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; Vector complete_for_editor(Shell&, size_t, HitTestResult const&) override; virtual HitTestResult hit_test_position(size_t) const override; NonnullRefPtrVector m_arguments; NameWithPosition m_function; Optional m_closing_brace_position; }; class Join final : public Node { public: Join(Position, NonnullRefPtr, NonnullRefPtr); virtual ~Join(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NonnullRefPtr const& left() const { return m_left; } NonnullRefPtr const& right() const { return m_right; } private: NODE(Join); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; virtual bool is_command() const override { return true; } virtual bool is_list() const override { return true; } virtual RefPtr leftmost_trivial_literal() const override; NonnullRefPtr m_left; NonnullRefPtr m_right; }; struct MatchEntry { Variant, Vector>> options; Optional> match_names; Optional match_as_position; Vector pipe_positions; RefPtr body; }; class MatchExpr final : public Node { public: MatchExpr(Position, NonnullRefPtr expr, String name, Optional as_position, Vector entries); virtual ~MatchExpr(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NonnullRefPtr const& matched_expr() const { return m_matched_expr; } String const& expr_name() const { return m_expr_name; } Vector const& entries() const { return m_entries; } Optional const& as_position() const { return m_as_position; } private: NODE(MatchExpr); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; virtual bool would_execute() const override { return true; } virtual bool should_override_execution_in_current_process() const override { return true; } NonnullRefPtr m_matched_expr; String m_expr_name; Optional m_as_position; Vector m_entries; }; class Or final : public Node { public: Or(Position, NonnullRefPtr, NonnullRefPtr, Position or_position); virtual ~Or(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NonnullRefPtr const& left() const { return m_left; } NonnullRefPtr const& right() const { return m_right; } Position const& or_position() const { return m_or_position; } private: NODE(Or); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; NonnullRefPtr m_left; NonnullRefPtr m_right; Position m_or_position; }; class Pipe final : public Node { public: Pipe(Position, NonnullRefPtr, NonnullRefPtr); virtual ~Pipe(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NonnullRefPtr const& left() const { return m_left; } NonnullRefPtr const& right() const { return m_right; } private: NODE(Pipe); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; virtual bool is_command() const override { return true; } NonnullRefPtr m_left; NonnullRefPtr m_right; }; class Range final : public Node { public: Range(Position, NonnullRefPtr, NonnullRefPtr); virtual ~Range(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NonnullRefPtr const& start() const { return m_start; } NonnullRefPtr const& end() const { return m_end; } private: NODE(Range); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; NonnullRefPtr m_start; NonnullRefPtr m_end; }; class ReadRedirection final : public PathRedirectionNode { public: ReadRedirection(Position, int, NonnullRefPtr); virtual ~ReadRedirection(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } private: NODE(ReadRedirection); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; }; class ReadWriteRedirection final : public PathRedirectionNode { public: ReadWriteRedirection(Position, int, NonnullRefPtr); virtual ~ReadWriteRedirection(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } private: NODE(ReadWriteRedirection); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; }; class Sequence final : public Node { public: Sequence(Position, NonnullRefPtrVector, Vector separator_positions); virtual ~Sequence(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NonnullRefPtrVector const& entries() const { return m_entries; } Vector const& separator_positions() const { return m_separator_positions; } private: NODE(Sequence); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; virtual bool is_list() const override { return true; } virtual bool should_override_execution_in_current_process() const override { return true; } virtual RefPtr leftmost_trivial_literal() const override; NonnullRefPtrVector m_entries; Vector m_separator_positions; }; class Subshell final : public Node { public: Subshell(Position, RefPtr block); virtual ~Subshell(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } RefPtr const& block() const { return m_block; } private: NODE(Subshell); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; virtual bool would_execute() const override { return false; } virtual bool should_override_execution_in_current_process() const override { return true; } RefPtr m_block; }; class Slice final : public Node { public: Slice(Position, NonnullRefPtr); virtual ~Slice() override; virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NonnullRefPtr selector() const { return m_selector; } virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual Vector complete_for_editor(Shell&, size_t, HitTestResult const&) override; virtual HitTestResult hit_test_position(size_t) const override; protected: NODE(Slice); NonnullRefPtr m_selector; }; class VariableNode : public Node { public: VariableNode(Position position) : Node(move(position)) { } void set_slice(NonnullRefPtr&& slice) { VERIFY(!m_slice); m_slice = move(slice); if (m_slice->is_syntax_error()) set_is_syntax_error(m_slice->syntax_error_node()); } Slice const* slice() const { return m_slice.ptr(); } protected: RefPtr m_slice; }; class SimpleVariable final : public VariableNode { public: SimpleVariable(Position, String); virtual ~SimpleVariable(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } String const& name() const { return m_name; } private: NODE(SimpleVariable); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual Vector complete_for_editor(Shell&, size_t, HitTestResult const&) override; virtual HitTestResult hit_test_position(size_t) const override; virtual bool is_simple_variable() const override { return true; } String m_name; }; class SpecialVariable final : public VariableNode { public: SpecialVariable(Position, char); virtual ~SpecialVariable(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } char name() const { return m_name; } private: NODE(SpecialVariable); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual Vector complete_for_editor(Shell&, size_t, HitTestResult const&) override; virtual HitTestResult hit_test_position(size_t) const override; char m_name { 0 }; }; class Juxtaposition final : public Node { public: Juxtaposition(Position, NonnullRefPtr, NonnullRefPtr); virtual ~Juxtaposition(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NonnullRefPtr const& left() const { return m_left; } NonnullRefPtr const& right() const { return m_right; } private: NODE(Juxtaposition); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; virtual Vector complete_for_editor(Shell&, size_t, HitTestResult const&) override; NonnullRefPtr m_left; NonnullRefPtr m_right; }; class Heredoc final : public Node { public: Heredoc(Position, String end, bool allow_interpolation, bool deindent); virtual ~Heredoc(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } String const& end() const { return m_end; } bool allow_interpolation() const { return m_allows_interpolation; } bool deindent() const { return m_deindent; } RefPtr const& contents() const { return m_contents; } void set_contents(RefPtr contents) { m_contents = move(contents); if (m_contents->is_syntax_error()) set_is_syntax_error(m_contents->syntax_error_node()); else clear_syntax_error(); } private: NODE(Heredoc); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; virtual RefPtr leftmost_trivial_literal() const override { return this; }; String m_end; bool m_allows_interpolation { false }; bool m_deindent { false }; RefPtr m_contents; }; class StringLiteral final : public Node { public: enum class EnclosureType { None, SingleQuotes, DoubleQuotes, }; StringLiteral(Position, String, EnclosureType); virtual ~StringLiteral(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } String const& text() const { return m_text; } EnclosureType enclosure_type() const { return m_enclosure_type; } private: NODE(StringLiteral); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual RefPtr leftmost_trivial_literal() const override { return this; }; String m_text; EnclosureType m_enclosure_type; }; class StringPartCompose final : public Node { public: StringPartCompose(Position, NonnullRefPtr, NonnullRefPtr); virtual ~StringPartCompose(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } NonnullRefPtr const& left() const { return m_left; } NonnullRefPtr const& right() const { return m_right; } private: NODE(StringPartCompose); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; NonnullRefPtr m_left; NonnullRefPtr m_right; }; class SyntaxError final : public Node { public: SyntaxError(Position, String, bool is_continuable = false); virtual ~SyntaxError(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } String const& error_text() const { return m_syntax_error_text; } bool is_continuable() const { return m_is_continuable; } virtual void clear_syntax_error() override { m_is_cleared = true; } virtual void set_is_syntax_error(SyntaxError const& error) override { m_position = error.position(); m_is_cleared = error.m_is_cleared; m_is_continuable = error.m_is_continuable; m_syntax_error_text = error.error_text(); } virtual bool is_syntax_error() const override { return !m_is_cleared; } private: NODE(SyntaxError); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override { return { nullptr, nullptr, nullptr }; } virtual SyntaxError const& syntax_error_node() const override; String m_syntax_error_text; bool m_is_continuable { false }; bool m_is_cleared { false }; }; class SyntheticNode final : public Node { public: SyntheticNode(Position, NonnullRefPtr); virtual ~SyntheticNode() = default; virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } Value const& value() const { return m_value; } private: NODE(SyntheticValue); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; NonnullRefPtr m_value; }; class Tilde final : public Node { public: Tilde(Position, String); virtual ~Tilde(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } String text() const; private: NODE(Tilde); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual Vector complete_for_editor(Shell&, size_t, HitTestResult const&) override; virtual HitTestResult hit_test_position(size_t) const override; virtual bool is_tilde() const override { return true; } String m_username; }; class VariableDeclarations final : public Node { public: struct Variable { NonnullRefPtr name; NonnullRefPtr value; }; VariableDeclarations(Position, Vector variables); virtual ~VariableDeclarations(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } Vector const& variables() const { return m_variables; } private: NODE(VariableDeclarations); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) const override; virtual bool is_variable_decls() const override { return true; } Vector m_variables; }; class WriteAppendRedirection final : public PathRedirectionNode { public: WriteAppendRedirection(Position, int, NonnullRefPtr); virtual ~WriteAppendRedirection(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } private: NODE(WriteAppendRedirection); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; }; class WriteRedirection final : public PathRedirectionNode { public: WriteRedirection(Position, int, NonnullRefPtr); virtual ~WriteRedirection(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } private: NODE(WriteRedirection); virtual void dump(int level) const override; virtual RefPtr run(RefPtr) override; }; } namespace AK { template<> struct Formatter : StandardFormatter { Formatter() = default; explicit Formatter(StandardFormatter formatter) : StandardFormatter(formatter) { } ErrorOr format(FormatBuilder&, Shell::AST::Command const& value); }; }