diff options
author | AnotherTest <ali.mpfard@gmail.com> | 2020-09-28 14:27:20 +0330 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-09-30 20:05:24 +0200 |
commit | a10cfee0d4132977490ba5efbd880a20a9ad32bd (patch) | |
tree | 50b5c0c80c711fb45fdfd25e8969c4ec5a907934 | |
parent | b91be8b9fd06b9253dc5931318cae308f87a8375 (diff) | |
download | serenity-a10cfee0d4132977490ba5efbd880a20a9ad32bd.zip |
Shell: Track line numbers and the positions of some keywords
-rw-r--r-- | Shell/AST.cpp | 23 | ||||
-rw-r--r-- | Shell/AST.h | 144 | ||||
-rw-r--r-- | Shell/Parser.cpp | 160 | ||||
-rw-r--r-- | Shell/Parser.h | 31 |
4 files changed, 251 insertions, 107 deletions
diff --git a/Shell/AST.cpp b/Shell/AST.cpp index 862b575ef6..c66328e971 100644 --- a/Shell/AST.cpp +++ b/Shell/AST.cpp @@ -121,7 +121,15 @@ Vector<Command> Node::to_lazy_evaluated_commands(RefPtr<Shell> shell) void Node::dump(int level) const { - print_indented(String::format("%s at %d:%d", class_name().characters(), m_position.start_offset, m_position.end_offset), level); + print_indented(String::format("%s at %d:%d (from %d.%d to %d.%d)", + class_name().characters(), + m_position.start_offset, + m_position.end_offset, + m_position.start_line.line_number, + m_position.start_line.line_column, + m_position.end_line.line_number, + m_position.end_line.line_column), + level); } Node::Node(Position position) @@ -226,10 +234,11 @@ HitTestResult And::hit_test_position(size_t offset) return result; } -And::And(Position position, NonnullRefPtr<Node> left, NonnullRefPtr<Node> right) +And::And(Position position, NonnullRefPtr<Node> left, NonnullRefPtr<Node> right, Position and_position) : Node(move(position)) , m_left(move(left)) , m_right(move(right)) + , m_and_position(and_position) { if (m_left->is_syntax_error()) set_is_syntax_error(m_left->syntax_error_node()); @@ -913,7 +922,7 @@ void ForLoop::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightM { editor.stylize({ m_position.start_offset, m_position.start_offset + 3 }, { Line::Style::Foreground(Line::Style::XtermColor::Yellow) }); if (m_in_kw_position.has_value()) - editor.stylize({ m_in_kw_position.value(), m_in_kw_position.value() + 2 }, { Line::Style::Foreground(Line::Style::XtermColor::Yellow) }); + editor.stylize({ m_in_kw_position.value().start_offset, m_in_kw_position.value().end_offset }, { Line::Style::Foreground(Line::Style::XtermColor::Yellow) }); metadata.is_first_in_list = false; m_iterated_expression->highlight_in_editor(editor, shell, metadata); @@ -937,7 +946,7 @@ HitTestResult ForLoop::hit_test_position(size_t offset) return m_block->hit_test_position(offset); } -ForLoop::ForLoop(Position position, String variable_name, NonnullRefPtr<AST::Node> iterated_expr, RefPtr<AST::Node> block, Optional<size_t> in_kw_position) +ForLoop::ForLoop(Position position, String variable_name, NonnullRefPtr<AST::Node> iterated_expr, RefPtr<AST::Node> block, Optional<Position> in_kw_position) : Node(move(position)) , m_variable_name(move(variable_name)) , m_iterated_expression(move(iterated_expr)) @@ -1541,10 +1550,11 @@ HitTestResult Or::hit_test_position(size_t offset) return result; } -Or::Or(Position position, NonnullRefPtr<Node> left, NonnullRefPtr<Node> right) +Or::Or(Position position, NonnullRefPtr<Node> left, NonnullRefPtr<Node> right, Position or_position) : Node(move(position)) , m_left(move(left)) , m_right(move(right)) + , m_or_position(or_position) { if (m_left->is_syntax_error()) set_is_syntax_error(m_left->syntax_error_node()); @@ -1796,10 +1806,11 @@ HitTestResult Sequence::hit_test_position(size_t offset) return m_right->hit_test_position(offset); } -Sequence::Sequence(Position position, NonnullRefPtr<Node> left, NonnullRefPtr<Node> right) +Sequence::Sequence(Position position, NonnullRefPtr<Node> left, NonnullRefPtr<Node> right, Position separator_position) : Node(move(position)) , m_left(move(left)) , m_right(move(right)) + , m_separator_position(separator_position) { if (m_left->is_syntax_error()) set_is_syntax_error(m_left->syntax_error_node()); diff --git a/Shell/AST.h b/Shell/AST.h index 15fe59fa67..112eb39041 100644 --- a/Shell/AST.h +++ b/Shell/AST.h @@ -47,6 +47,16 @@ struct HighlightMetadata { 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==(const Line& 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; } }; @@ -418,12 +428,57 @@ public: virtual void visit(NodeVisitor&) { ASSERT_NOT_REACHED(); } virtual void visit(NodeVisitor& visitor) const { const_cast<Node*>(this)->visit(visitor); } + enum class Kind : u32 { + And, + ListConcatenate, + Background, + BarewordLiteral, + CastToCommand, + CastToList, + CloseFdRedirection, + CommandLiteral, + Comment, + DynamicEvaluate, + DoubleQuotedString, + Fd2FdRedirection, + FunctionDeclaration, + ForLoop, + Glob, + Execute, + IfCond, + Join, + MatchExpr, + Or, + Pipe, + ReadRedirection, + ReadWriteRedirection, + Sequence, + Subshell, + SimpleVariable, + SpecialVariable, + Juxtaposition, + StringLiteral, + StringPartCompose, + SyntaxError, + Tilde, + VariableDeclarations, + WriteAppendRedirection, + WriteRedirection, + __Count, + }; + + virtual Kind kind() const = 0; + protected: Position m_position; bool m_is_syntax_error { false }; RefPtr<const SyntaxError> 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<Node>); @@ -445,22 +500,24 @@ protected: class And final : public Node { public: - And(Position, NonnullRefPtr<Node>, NonnullRefPtr<Node>); + And(Position, NonnullRefPtr<Node>, NonnullRefPtr<Node>, Position and_position); virtual ~And(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } const NonnullRefPtr<Node>& left() const { return m_left; } const NonnullRefPtr<Node>& right() const { return m_right; } + const Position& and_position() const { return m_and_position; } private: + NODE(And); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "And"; } NonnullRefPtr<Node> m_left; NonnullRefPtr<Node> m_right; + Position m_and_position; }; class ListConcatenate final : public Node { @@ -471,11 +528,11 @@ public: const Vector<NonnullRefPtr<Node>> list() const { return m_list; } private: + NODE(ListConcatenate); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "ListConcatenate"; } virtual bool is_list() const override { return true; } virtual RefPtr<Node> leftmost_trivial_literal() const override; @@ -491,11 +548,11 @@ public: const NonnullRefPtr<Node>& command() const { return m_command; } private: + NODE(Background); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "Background"; } NonnullRefPtr<Node> m_command; }; @@ -509,10 +566,10 @@ public: const String& text() const { return m_text; } private: + NODE(BarewordLiteral); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; - virtual String class_name() const override { return "BarewordLiteral"; } virtual bool is_bareword() const override { return true; } virtual RefPtr<Node> leftmost_trivial_literal() const override { return this; } @@ -528,12 +585,12 @@ public: const NonnullRefPtr<Node>& inner() const { return m_inner; } private: + NODE(CastToCommand); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; virtual Vector<Line::CompletionSuggestion> complete_for_editor(Shell&, size_t, const HitTestResult&) override; - virtual String class_name() const override { return "CastToCommand"; } virtual bool is_command() const override { return true; } virtual bool is_list() const override { return true; } virtual RefPtr<Node> leftmost_trivial_literal() const override; @@ -550,11 +607,11 @@ public: const RefPtr<Node>& inner() const { return m_inner; } private: + NODE(CastToList); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "CastToList"; } virtual bool is_list() const override { return true; } virtual RefPtr<Node> leftmost_trivial_literal() const override; @@ -570,10 +627,10 @@ public: int fd() const { return m_fd; } private: + NODE(CloseFdRedirection); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; - virtual String class_name() const override { return "CloseFdRedirection"; } virtual bool is_command() const override { return true; } int m_fd { -1 }; @@ -588,10 +645,10 @@ public: const Command& command() const { return m_command; } private: + NODE(CommandLiteral); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override { ASSERT_NOT_REACHED(); } - virtual String class_name() const override { return "CommandLiteral"; } virtual bool is_command() const override { return true; } virtual bool is_list() const override { return true; } @@ -607,10 +664,10 @@ public: const String& text() const { return m_text; } private: + NODE(Comment); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; - virtual String class_name() const override { return "Comment"; } String m_text; }; @@ -624,11 +681,11 @@ public: const NonnullRefPtr<Node>& inner() const { return m_inner; } private: + NODE(DynamicEvaluate); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "DynamicEvaluate"; } virtual bool is_bareword() const override { return m_inner->is_bareword(); } virtual bool is_command() const override { return is_list(); } @@ -651,11 +708,11 @@ public: const RefPtr<Node>& inner() const { return m_inner; } private: + NODE(DoubleQuotedString); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "DoubleQuotedString"; } RefPtr<Node> m_inner; }; @@ -670,10 +727,10 @@ public: int dest_fd() const { return m_dest_fd; } private: + NODE(Fd2FdRedirection); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; - virtual String class_name() const override { return "Fd2FdRedirection"; } virtual bool is_command() const override { return true; } int m_source_fd { -1 }; @@ -695,12 +752,12 @@ public: const RefPtr<Node>& block() const { return m_block; } private: + NODE(FunctionDeclaration); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; virtual Vector<Line::CompletionSuggestion> complete_for_editor(Shell&, size_t, const HitTestResult&) override; - virtual String class_name() const override { return "FunctionDeclaration"; } virtual bool would_execute() const override { return true; } NameWithPosition m_name; @@ -710,26 +767,27 @@ private: class ForLoop final : public Node { public: - ForLoop(Position, String variable_name, NonnullRefPtr<AST::Node> iterated_expr, RefPtr<AST::Node> block, Optional<size_t> in_kw_position = {}); + ForLoop(Position, String variable_name, NonnullRefPtr<AST::Node> iterated_expr, RefPtr<AST::Node> block, Optional<Position> in_kw_position = {}); virtual ~ForLoop(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } const String& variable_name() const { return m_variable_name; } const NonnullRefPtr<Node>& iterated_expression() const { return m_iterated_expression; } const RefPtr<Node>& block() const { return m_block; } + const Optional<Position> in_keyword_position() const { return m_in_kw_position; } private: + NODE(ForLoop); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "ForLoop"; } virtual bool would_execute() const override { return true; } String m_variable_name; NonnullRefPtr<AST::Node> m_iterated_expression; RefPtr<AST::Node> m_block; - Optional<size_t> m_in_kw_position; + Optional<Position> m_in_kw_position; }; class Glob final : public Node { @@ -741,10 +799,10 @@ public: const String& text() const { return m_text; } private: + NODE(Glob); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; - virtual String class_name() const override { return "Glob"; } virtual bool is_glob() const override { return true; } virtual bool is_list() const override { return true; } @@ -764,12 +822,12 @@ public: bool does_capture_stdout() const { return m_capture_stdout; } private: + NODE(Execute); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; virtual Vector<Line::CompletionSuggestion> complete_for_editor(Shell&, size_t, const HitTestResult&) override; - virtual String class_name() const override { return "Execute"; } virtual bool is_execute() const override { return true; } virtual bool would_execute() const override { return true; } @@ -789,11 +847,11 @@ public: const Optional<Position> else_position() const { return m_else_position; } private: + NODE(IfCond); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "IfCond"; } virtual bool would_execute() const override { return true; } NonnullRefPtr<AST::Node> m_condition; @@ -813,11 +871,11 @@ public: const NonnullRefPtr<Node>& right() const { return m_right; } private: + NODE(Join); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "Join"; } virtual bool is_command() const override { return true; } virtual bool is_list() const override { return true; } virtual RefPtr<Node> leftmost_trivial_literal() const override; @@ -841,13 +899,14 @@ public: const NonnullRefPtr<Node>& matched_expr() const { return m_matched_expr; } const String& expr_name() const { return m_expr_name; } const Vector<MatchEntry>& entries() const { return m_entries; } + const Optional<Position>& as_position() const { return m_as_position; } private: + NODE(MatchExpr); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "MatchExpr"; } virtual bool would_execute() const override { return true; } NonnullRefPtr<Node> m_matched_expr; @@ -858,22 +917,24 @@ private: class Or final : public Node { public: - Or(Position, NonnullRefPtr<Node>, NonnullRefPtr<Node>); + Or(Position, NonnullRefPtr<Node>, NonnullRefPtr<Node>, Position or_position); virtual ~Or(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } const NonnullRefPtr<Node>& left() const { return m_left; } const NonnullRefPtr<Node>& right() const { return m_right; } + const Position& or_position() const { return m_or_position; } private: + NODE(Or); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "Or"; } NonnullRefPtr<Node> m_left; NonnullRefPtr<Node> m_right; + Position m_or_position; }; class Pipe final : public Node { @@ -886,11 +947,11 @@ public: const NonnullRefPtr<Node>& right() const { return m_right; } private: + NODE(Pipe); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "Pipe"; } virtual bool is_command() const override { return true; } NonnullRefPtr<Node> m_left; @@ -904,9 +965,9 @@ public: virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } private: + NODE(ReadRedirection); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; - virtual String class_name() const override { return "ReadRedirection"; } }; class ReadWriteRedirection final : public PathRedirectionNode { @@ -916,31 +977,34 @@ public: virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } private: + NODE(ReadWriteRedirection); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; - virtual String class_name() const override { return "ReadWriteRedirection"; } }; class Sequence final : public Node { public: - Sequence(Position, NonnullRefPtr<Node>, NonnullRefPtr<Node>); + Sequence(Position, NonnullRefPtr<Node>, NonnullRefPtr<Node>, Position separator_position); virtual ~Sequence(); virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } const NonnullRefPtr<Node>& left() const { return m_left; } const NonnullRefPtr<Node>& right() const { return m_right; } + const Position& separator_position() const { return m_separator_position; } + private: + NODE(Sequence); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "Sequence"; } virtual bool is_list() const override { return true; } virtual bool would_execute() const override { return m_left->would_execute() || m_right->would_execute(); } NonnullRefPtr<Node> m_left; NonnullRefPtr<Node> m_right; + Position m_separator_position; }; class Subshell final : public Node { @@ -952,11 +1016,11 @@ public: const RefPtr<Node>& block() const { return m_block; } private: + NODE(Subshell); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "Subshell"; } virtual bool would_execute() const override { return true; } RefPtr<AST::Node> m_block; @@ -971,12 +1035,12 @@ public: const String& name() const { return m_name; } private: + NODE(SimpleVariable); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual Vector<Line::CompletionSuggestion> complete_for_editor(Shell&, size_t, const HitTestResult&) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "SimpleVariable"; } virtual bool is_simple_variable() const override { return true; } String m_name; @@ -991,12 +1055,12 @@ public: char name() const { return m_name; } private: + NODE(SpecialVariable); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual Vector<Line::CompletionSuggestion> complete_for_editor(Shell&, size_t, const HitTestResult&) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "SpecialVariable"; } char m_name { -1 }; }; @@ -1011,12 +1075,12 @@ public: const NonnullRefPtr<Node>& right() const { return m_right; } private: + NODE(Juxtaposition); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; virtual Vector<Line::CompletionSuggestion> complete_for_editor(Shell&, size_t, const HitTestResult&) override; - virtual String class_name() const override { return "Juxtaposition"; } NonnullRefPtr<Node> m_left; NonnullRefPtr<Node> m_right; @@ -1031,10 +1095,10 @@ public: const String& text() const { return m_text; } private: + NODE(StringLiteral); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; - virtual String class_name() const override { return "StringLiteral"; } virtual RefPtr<Node> leftmost_trivial_literal() const override { return this; }; String m_text; @@ -1050,11 +1114,11 @@ public: const NonnullRefPtr<Node>& right() const { return m_right; } private: + NODE(StringPartCompose); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "StringPartCompose"; } NonnullRefPtr<Node> m_left; NonnullRefPtr<Node> m_right; @@ -1069,11 +1133,11 @@ public: const String& error_text() const { return m_syntax_error_text; } private: + NODE(SyntaxError); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override { return { nullptr, nullptr, nullptr }; } - virtual String class_name() const override { return "SyntaxError"; } virtual bool is_syntax_error() const override { return true; } virtual const SyntaxError& syntax_error_node() const override; @@ -1089,12 +1153,12 @@ public: String text() const; private: + NODE(Tilde); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual Vector<Line::CompletionSuggestion> complete_for_editor(Shell&, size_t, const HitTestResult&) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "Tilde"; } virtual bool is_tilde() const override { return true; } String m_username; @@ -1113,11 +1177,11 @@ public: const Vector<Variable>& variables() const { return m_variables; } private: + NODE(VariableDeclarations); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override; virtual HitTestResult hit_test_position(size_t) override; - virtual String class_name() const override { return "VariableDeclarations"; } virtual bool is_variable_decls() const override { return true; } Vector<Variable> m_variables; @@ -1130,9 +1194,9 @@ public: virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } private: + NODE(WriteAppendRedirection); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; - virtual String class_name() const override { return "WriteAppendRedirection"; } }; class WriteRedirection final : public PathRedirectionNode { @@ -1142,9 +1206,9 @@ public: virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); } private: + NODE(WriteRedirection); virtual void dump(int level) const override; virtual RefPtr<Value> run(RefPtr<Shell>) override; - virtual String class_name() const override { return "WriteRedirection"; } }; } diff --git a/Shell/Parser.cpp b/Shell/Parser.cpp index a38d4a67d7..1c4a05e9f2 100644 --- a/Shell/Parser.cpp +++ b/Shell/Parser.cpp @@ -29,6 +29,11 @@ #include <stdio.h> #include <unistd.h> +Parser::SavedOffset Parser::save_offset() const +{ + return { m_offset, m_line }; +} + char Parser::peek() { if (m_offset == m_input.length()) @@ -39,6 +44,8 @@ char Parser::peek() auto ch = m_input[m_offset]; if (ch == '\\' && m_input.length() > m_offset + 1 && m_input[m_offset + 1] == '\n') { m_offset += 2; + ++m_line.line_number; + m_line.line_column = 0; return peek(); } @@ -50,13 +57,14 @@ char Parser::consume() auto ch = peek(); ++m_offset; - return ch; -} + if (ch == '\n') { + ++m_line.line_number; + m_line.line_column = 0; + } else { + ++m_line.line_column; + } -void Parser::putback() -{ - ASSERT(m_offset > 0); - --m_offset; + return ch; } bool Parser::expect(char ch) @@ -67,13 +75,14 @@ bool Parser::expect(char ch) bool Parser::expect(const StringView& expected) { auto offset_at_start = m_offset; + auto line_at_start = line(); if (expected.length() + m_offset > m_input.length()) return false; for (size_t i = 0; i < expected.length(); ++i) { if (peek() != expected[i]) { - m_offset = offset_at_start; + restore_to(offset_at_start, line_at_start); return false; } @@ -86,12 +95,12 @@ bool Parser::expect(const StringView& expected) template<typename A, typename... Args> NonnullRefPtr<A> Parser::create(Args... args) { - return adopt(*new A(AST::Position { m_rule_start_offsets.last(), m_offset }, args...)); + return adopt(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, args...)); } [[nodiscard]] OwnPtr<Parser::ScopedOffset> Parser::push_start() { - return make<ScopedOffset>(m_rule_start_offsets, m_offset); + return make<ScopedOffset>(m_rule_start_offsets, m_rule_start_lines, m_offset, m_line.line_number, m_line.line_column); } static constexpr bool is_whitespace(char c) @@ -128,13 +137,14 @@ static inline char to_byte(char a, char b) RefPtr<AST::Node> Parser::parse() { m_offset = 0; + m_line = { 0, 0 }; auto toplevel = parse_toplevel(); if (m_offset < m_input.length()) { // Parsing stopped midway, this is a syntax error. auto error_start = push_start(); - m_offset = m_input.length(); + consume_while([](auto) { return true; }); auto syntax_error_node = create<AST::SyntaxError>("Unexpected tokens past the end"); if (!toplevel) toplevel = move(syntax_error_node); @@ -162,6 +172,8 @@ RefPtr<AST::Node> Parser::parse_sequence() auto rule_start = push_start(); auto var_decls = parse_variable_decls(); + auto pos_before_seps = save_offset(); + switch (peek()) { case '}': return var_decls; @@ -171,9 +183,15 @@ RefPtr<AST::Node> Parser::parse_sequence() break; consume_while(is_any_of("\n;")); + + auto pos_after_seps = save_offset(); + auto rest = parse_sequence(); if (rest) - return create<AST::Sequence>(var_decls.release_nonnull(), rest.release_nonnull()); + return create<AST::Sequence>( + 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; } default: @@ -189,24 +207,38 @@ RefPtr<AST::Node> Parser::parse_sequence() return var_decls; if (var_decls) - first = create<AST::Sequence>(var_decls.release_nonnull(), first.release_nonnull()); + first = create<AST::Sequence>( + 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 }); consume_while(is_whitespace); + pos_before_seps = save_offset(); switch (peek()) { case ';': - case '\n': + case '\n': { consume_while(is_any_of("\n;")); + auto pos_after_seps = save_offset(); + if (auto expr = parse_sequence()) { - return create<AST::Sequence>(first.release_nonnull(), expr.release_nonnull()); // Sequence + return create<AST::Sequence>( + 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; + } case '&': { auto execute_pipe_seq = first->would_execute() ? first.release_nonnull() : static_cast<NonnullRefPtr<AST::Node>>(create<AST::Execute>(first.release_nonnull())); consume(); + auto pos_after_seps = save_offset(); auto bg = create<AST::Background>(execute_pipe_seq); // Execute Background if (auto rest = parse_sequence()) - return create<AST::Sequence>(move(bg), rest.release_nonnull()); // Sequence Background Sequence + return create<AST::Sequence>( + 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; } @@ -221,13 +253,13 @@ RefPtr<AST::Node> Parser::parse_variable_decls() consume_while(is_whitespace); - auto offset_before_name = m_offset; + auto pos_before_name = save_offset(); auto var_name = consume_while(is_word_character); if (var_name.is_empty()) return nullptr; if (!expect('=')) { - m_offset = offset_before_name; + restore_to(pos_before_name.offset, pos_before_name.line); return nullptr; } @@ -236,12 +268,12 @@ RefPtr<AST::Node> Parser::parse_variable_decls() auto start = push_start(); auto expression = parse_expression(); if (!expression || expression->is_syntax_error()) { - m_offset = start->offset; + restore_to(*start); if (peek() == '(') { consume(); auto command = parse_pipe_sequence(); if (!command) - m_offset = start->offset; + restore_to(*start); else if (!expect(')')) command->set_is_syntax_error(*create<AST::SyntaxError>("Expected a terminating close paren")); expression = command; @@ -252,7 +284,7 @@ RefPtr<AST::Node> Parser::parse_variable_decls() auto string_start = push_start(); expression = create<AST::StringLiteral>(""); } else { - m_offset = offset_before_name; + restore_to(pos_before_name.offset, pos_before_name.line); return nullptr; } } @@ -280,14 +312,14 @@ RefPtr<AST::Node> Parser::parse_function_decl() auto rule_start = push_start(); auto restore = [&] { - m_offset = rule_start->offset; + restore_to(*rule_start); return nullptr; }; consume_while(is_whitespace); - auto offset_before_name = m_offset; + auto pos_before_name = save_offset(); auto function_name = consume_while(is_word_character); - auto offset_after_name = m_offset; + auto pos_after_name = save_offset(); if (function_name.is_empty()) return restore(); @@ -302,12 +334,13 @@ RefPtr<AST::Node> Parser::parse_function_decl() break; auto name_offset = m_offset; + auto start_line = line(); auto arg_name = consume_while(is_word_character); if (arg_name.is_empty()) { // FIXME: Should this be a syntax error, or just return? return restore(); } - arguments.append({ arg_name, { name_offset, m_offset } }); + arguments.append({ arg_name, { name_offset, m_offset, start_line, line() } }); } consume_while(is_whitespace); @@ -322,7 +355,7 @@ RefPtr<AST::Node> Parser::parse_function_decl() return create<AST::FunctionDeclaration>( AST::FunctionDeclaration::NameWithPosition { move(function_name), - { offset_before_name, offset_after_name } }, + { pos_before_name.offset, pos_after_name.offset, pos_before_name.line, pos_after_name.line } }, move(arguments), move(syntax_error)); } @@ -345,7 +378,7 @@ RefPtr<AST::Node> Parser::parse_function_decl() return create<AST::FunctionDeclaration>( AST::FunctionDeclaration::NameWithPosition { move(function_name), - { offset_before_name, offset_after_name } }, + { pos_before_name.offset, pos_after_name.offset, pos_before_name.line, pos_after_name.line } }, move(arguments), move(body)); } @@ -354,7 +387,7 @@ RefPtr<AST::Node> Parser::parse_function_decl() return create<AST::FunctionDeclaration>( AST::FunctionDeclaration::NameWithPosition { move(function_name), - { offset_before_name, offset_after_name } }, + { pos_before_name.offset, pos_after_name.offset, pos_before_name.line, pos_after_name.line } }, move(arguments), move(body)); } @@ -368,17 +401,19 @@ RefPtr<AST::Node> Parser::parse_or_logical_sequence() return nullptr; consume_while(is_whitespace); - auto saved_offset = m_offset; - if (!expect("||")) { - m_offset = saved_offset; + auto pos_before_or = save_offset(); + if (!expect("||")) return and_sequence; - } + auto pos_after_or = save_offset(); auto right_and_sequence = parse_and_logical_sequence(); if (!right_and_sequence) right_and_sequence = create<AST::SyntaxError>("Expected an expression after '||'"); - return create<AST::Or>(and_sequence.release_nonnull(), right_and_sequence.release_nonnull()); + return create<AST::Or>( + and_sequence.release_nonnull(), + right_and_sequence.release_nonnull(), + AST::Position { pos_before_or.offset, pos_after_or.offset, pos_before_or.line, pos_after_or.line }); } RefPtr<AST::Node> Parser::parse_and_logical_sequence() @@ -390,17 +425,19 @@ RefPtr<AST::Node> Parser::parse_and_logical_sequence() return nullptr; consume_while(is_whitespace); - auto saved_offset = m_offset; - if (!expect("&&")) { - m_offset = saved_offset; + auto pos_before_and = save_offset(); + if (!expect("&&")) return pipe_sequence; - } + auto pos_after_end = save_offset(); auto right_and_sequence = parse_and_logical_sequence(); if (!right_and_sequence) right_and_sequence = create<AST::SyntaxError>("Expected an expression after '&&'"); - return create<AST::And>(pipe_sequence.release_nonnull(), right_and_sequence.release_nonnull()); + return create<AST::And>( + pipe_sequence.release_nonnull(), + right_and_sequence.release_nonnull(), + AST::Position { pos_before_and.offset, pos_after_end.offset, pos_before_and.line, pos_after_end.line }); } RefPtr<AST::Node> Parser::parse_pipe_sequence() @@ -419,13 +456,14 @@ RefPtr<AST::Node> Parser::parse_pipe_sequence() if (peek() != '|') return left; + auto before_pipe = save_offset(); consume(); if (auto pipe_seq = parse_pipe_sequence()) { return create<AST::Pipe>(left.release_nonnull(), pipe_seq.release_nonnull()); // Pipe } - putback(); + restore_to(before_pipe.offset, before_pipe.line); return left; } @@ -478,28 +516,26 @@ RefPtr<AST::Node> Parser::parse_control_structure() RefPtr<AST::Node> Parser::parse_for_loop() { auto rule_start = push_start(); - if (!expect("for")) { - m_offset = rule_start->offset; + if (!expect("for")) return nullptr; - } if (consume_while(is_any_of(" \t\n")).is_empty()) { - m_offset = rule_start->offset; + restore_to(*rule_start); return nullptr; } auto variable_name = consume_while(is_word_character); - Optional<size_t> in_start_position; + Optional<AST::Position> in_start_position; if (variable_name.is_empty()) { variable_name = "it"; } else { consume_while(is_whitespace); auto in_error_start = push_start(); - in_start_position = in_error_start->offset; if (!expect("in")) { auto syntax_error = create<AST::SyntaxError>("Expected 'in' after a variable name in a 'for' loop"); return create<AST::ForLoop>(move(variable_name), move(syntax_error), nullptr); // ForLoop Var Iterated Block } + in_start_position = AST::Position { in_error_start->offset, m_offset, in_error_start->line, line() }; } consume_while(is_whitespace); @@ -542,13 +578,11 @@ RefPtr<AST::Node> Parser::parse_for_loop() RefPtr<AST::Node> Parser::parse_if_expr() { auto rule_start = push_start(); - if (!expect("if")) { - m_offset = rule_start->offset; + if (!expect("if")) return nullptr; - } if (consume_while(is_any_of(" \t\n")).is_empty()) { - m_offset = rule_start->offset; + restore_to(*rule_start); return nullptr; } @@ -595,7 +629,7 @@ RefPtr<AST::Node> Parser::parse_if_expr() { auto else_start = push_start(); if (expect("else")) - else_position = AST::Position { else_start->offset, m_offset }; + else_position = AST::Position { else_start->offset, m_offset, else_start->line, line() }; } if (else_position.has_value()) { @@ -642,7 +676,7 @@ RefPtr<AST::Node> Parser::parse_match_expr() return nullptr; if (consume_while(is_whitespace).is_empty()) { - m_offset = rule_start->offset; + restore_to(*rule_start); return nullptr; } @@ -658,8 +692,9 @@ RefPtr<AST::Node> Parser::parse_match_expr() String match_name; Optional<AST::Position> as_position; auto as_start = m_offset; + auto as_line = line(); if (expect("as")) { - as_position = AST::Position { as_start, m_offset }; + as_position = AST::Position { as_start, m_offset, as_line, line() }; if (consume_while(is_any_of(" \t\n")).is_empty()) { auto node = create<AST::MatchExpr>( @@ -730,9 +765,10 @@ AST::MatchEntry Parser::parse_match_entry() consume_while(is_any_of(" \t\n")); auto previous_pipe_start_position = m_offset; + auto previous_pipe_start_line = line(); RefPtr<AST::SyntaxError> error; while (expect('|')) { - pipe_positions.append({ previous_pipe_start_position, m_offset }); + pipe_positions.append({ previous_pipe_start_position, m_offset, previous_pipe_start_line, line() }); consume_while(is_any_of(" \t\n")); auto pattern = parse_match_pattern(); if (!pattern) { @@ -742,6 +778,9 @@ AST::MatchEntry Parser::parse_match_entry() consume_while(is_any_of(" \t\n")); patterns.append(pattern.release_nonnull()); + + previous_pipe_start_line = line(); + previous_pipe_start_position = m_offset; } consume_while(is_any_of(" \t\n")); @@ -864,7 +903,7 @@ RefPtr<AST::Node> Parser::parse_redirection() return create<AST::ReadWriteRedirection>(pipe_fd, path.release_nonnull()); // Redirection ReadWrite } default: - m_offset = rule_start->offset; + restore_to(*rule_start); return nullptr; } } @@ -930,7 +969,7 @@ RefPtr<AST::Node> Parser::parse_expression() consume(); auto list = parse_list_expression(); if (!expect(')')) { - m_offset = rule_start->offset; + restore_to(*rule_start); return nullptr; } return read_concat(create<AST::CastToList>(move(list))); // Cast To List @@ -1126,7 +1165,7 @@ RefPtr<AST::Node> Parser::parse_variable() auto name = consume_while(is_word_character); if (name.length() == 0) { - putback(); + restore_to(rule_start->offset, rule_start->line); return nullptr; } @@ -1215,6 +1254,7 @@ RefPtr<AST::Node> Parser::parse_bareword() return nullptr; auto current_end = m_offset; + auto current_line = line(); auto string = builder.to_string(); if (string.starts_with('~')) { String username; @@ -1231,7 +1271,9 @@ RefPtr<AST::Node> Parser::parse_bareword() // Synthesize a Tilde Node with the correct positioning information. { - m_offset -= string.length(); + restore_to(rule_start->offset, rule_start->line); + auto ch = consume(); + ASSERT(ch == '~'); tilde = create<AST::Tilde>(move(username)); } @@ -1240,9 +1282,8 @@ RefPtr<AST::Node> Parser::parse_bareword() // Synthesize a BarewordLiteral Node with the correct positioning information. { - m_offset = tilde->position().end_offset; auto text_start = push_start(); - m_offset = current_end; + restore_to(current_end, current_line); text = create<AST::BarewordLiteral>(move(string)); } @@ -1267,6 +1308,7 @@ RefPtr<AST::Node> Parser::parse_glob() char ch = peek(); if (ch == '*' || ch == '?') { + auto saved_offset = save_offset(); consume(); StringBuilder textbuilder; if (bareword_part) { @@ -1276,7 +1318,7 @@ RefPtr<AST::Node> Parser::parse_glob() text = bareword->text(); } else { // FIXME: Allow composition of tilde+bareword with globs: '~/foo/bar/baz*' - putback(); + restore_to(saved_offset.offset, saved_offset.line); bareword_part->set_is_syntax_error(*create<AST::SyntaxError>(String::format("Unexpected %s inside a glob", bareword_part->class_name().characters()))); return bareword_part; } diff --git a/Shell/Parser.h b/Shell/Parser.h index 36cce62991..4425f5bf1a 100644 --- a/Shell/Parser.h +++ b/Shell/Parser.h @@ -42,6 +42,12 @@ public: RefPtr<AST::Node> parse(); + struct SavedOffset { + size_t offset; + AST::Position::Line line; + }; + SavedOffset save_offset() const; + private: RefPtr<AST::Node> parse_toplevel(); RefPtr<AST::Node> parse_sequence(); @@ -76,34 +82,55 @@ private: bool at_end() const { return m_input.length() <= m_offset; } char peek(); char consume(); - void putback(); bool expect(char); bool expect(const StringView&); + void restore_to(size_t offset, AST::Position::Line line) + { + m_offset = offset; + m_line = move(line); + } + + AST::Position::Line line() const { return m_line; } + StringView consume_while(Function<bool(char)>); struct ScopedOffset { - ScopedOffset(Vector<size_t>& offsets, size_t offset) + ScopedOffset(Vector<size_t>& offsets, Vector<AST::Position::Line>& lines, size_t offset, size_t lineno, size_t linecol) : offsets(offsets) + , lines(lines) , offset(offset) + , line({ lineno, linecol }) { offsets.append(offset); + lines.append(line); } ~ScopedOffset() { auto last = offsets.take_last(); ASSERT(last == offset); + + auto last_line = lines.take_last(); + ASSERT(last_line == line); } Vector<size_t>& offsets; + Vector<AST::Position::Line>& lines; size_t offset; + AST::Position::Line line; }; + void restore_to(const ScopedOffset& offset) { restore_to(offset.offset, offset.line); } + OwnPtr<ScopedOffset> push_start(); StringView m_input; size_t m_offset { 0 }; + + AST::Position::Line m_line { 0, 0 }; + Vector<size_t> m_rule_start_offsets; + Vector<AST::Position::Line> m_rule_start_lines; }; #if 0 |