mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 23:50:19 +00:00
Shell: Add a (very basic) formatter
This commit is contained in:
parent
6e6be8e56e
commit
b3dd97a694
Notes:
sideshowbarker
2024-07-19 02:12:06 +09:00
Author: https://github.com/alimpfard Commit: https://github.com/SerenityOS/serenity/commit/b3dd97a6944 Pull-request: https://github.com/SerenityOS/serenity/pull/3505 Reviewed-by: https://github.com/awesomekling Reviewed-by: https://github.com/devsh0
10 changed files with 1198 additions and 3 deletions
115
Shell/AST.h
115
Shell/AST.h
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "Forward.h"
|
||||
#include "Job.h"
|
||||
#include "NodeVisitor.h"
|
||||
#include <AK/InlineLinkedList.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <AK/RefCounted.h>
|
||||
|
@ -414,6 +415,9 @@ public:
|
|||
|
||||
Vector<Command> to_lazy_evaluated_commands(RefPtr<Shell> shell);
|
||||
|
||||
virtual void visit(NodeVisitor&) { ASSERT_NOT_REACHED(); }
|
||||
virtual void visit(NodeVisitor& visitor) const { const_cast<Node*>(this)->visit(visitor); }
|
||||
|
||||
protected:
|
||||
Position m_position;
|
||||
bool m_is_syntax_error { false };
|
||||
|
@ -429,6 +433,10 @@ public:
|
|||
virtual HitTestResult hit_test_position(size_t offset) 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); }
|
||||
|
||||
const NonnullRefPtr<Node>& path() const { return m_path; }
|
||||
int fd() const { return m_fd; }
|
||||
|
||||
protected:
|
||||
int m_fd { -1 };
|
||||
|
@ -439,6 +447,10 @@ class And final : public Node {
|
|||
public:
|
||||
And(Position, NonnullRefPtr<Node>, NonnullRefPtr<Node>);
|
||||
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; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -455,6 +467,8 @@ class ListConcatenate final : public Node {
|
|||
public:
|
||||
ListConcatenate(Position, Vector<NonnullRefPtr<Node>>);
|
||||
virtual ~ListConcatenate();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
const Vector<NonnullRefPtr<Node>> list() const { return m_list; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -472,6 +486,9 @@ class Background final : public Node {
|
|||
public:
|
||||
Background(Position, NonnullRefPtr<Node>);
|
||||
virtual ~Background();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
const NonnullRefPtr<Node>& command() const { return m_command; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -487,6 +504,8 @@ class BarewordLiteral final : public Node {
|
|||
public:
|
||||
BarewordLiteral(Position, String);
|
||||
virtual ~BarewordLiteral();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
const String& text() const { return m_text; }
|
||||
|
||||
private:
|
||||
|
@ -504,6 +523,9 @@ class CastToCommand final : public Node {
|
|||
public:
|
||||
CastToCommand(Position, NonnullRefPtr<Node>);
|
||||
virtual ~CastToCommand();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
const NonnullRefPtr<Node>& inner() const { return m_inner; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -523,6 +545,9 @@ class CastToList final : public Node {
|
|||
public:
|
||||
CastToList(Position, RefPtr<Node>);
|
||||
virtual ~CastToList();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
const RefPtr<Node>& inner() const { return m_inner; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -540,6 +565,9 @@ 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:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -555,6 +583,9 @@ class CommandLiteral final : public Node {
|
|||
public:
|
||||
CommandLiteral(Position, Command);
|
||||
virtual ~CommandLiteral();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
const Command& command() const { return m_command; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -571,6 +602,8 @@ class Comment : public Node {
|
|||
public:
|
||||
Comment(Position, String);
|
||||
virtual ~Comment();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
const String& text() const { return m_text; }
|
||||
|
||||
private:
|
||||
|
@ -586,6 +619,9 @@ class DynamicEvaluate final : public Node {
|
|||
public:
|
||||
DynamicEvaluate(Position, NonnullRefPtr<Node>);
|
||||
virtual ~DynamicEvaluate();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
const NonnullRefPtr<Node>& inner() const { return m_inner; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -610,6 +646,9 @@ class DoubleQuotedString final : public Node {
|
|||
public:
|
||||
DoubleQuotedString(Position, RefPtr<Node>);
|
||||
virtual ~DoubleQuotedString();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
const RefPtr<Node>& inner() const { return m_inner; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -625,6 +664,10 @@ 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_source_fd; }
|
||||
int dest_fd() const { return m_dest_fd; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -645,6 +688,11 @@ public:
|
|||
};
|
||||
FunctionDeclaration(Position, NameWithPosition name, Vector<NameWithPosition> argument_names, RefPtr<AST::Node> body);
|
||||
virtual ~FunctionDeclaration();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
const NameWithPosition& name() const { return m_name; }
|
||||
const Vector<NameWithPosition> arguments() const { return m_arguments; }
|
||||
const RefPtr<Node>& block() const { return m_block; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -664,6 +712,11 @@ 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 = {});
|
||||
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; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -683,6 +736,8 @@ class Glob final : public Node {
|
|||
public:
|
||||
Glob(Position, String);
|
||||
virtual ~Glob();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
const String& text() const { return m_text; }
|
||||
|
||||
private:
|
||||
|
@ -701,8 +756,12 @@ public:
|
|||
Execute(Position, NonnullRefPtr<Node>, bool capture_stdout = false);
|
||||
virtual ~Execute();
|
||||
void capture_stdout() { m_capture_stdout = true; }
|
||||
NonnullRefPtr<Node> command() { return m_command; }
|
||||
NonnullRefPtr<Node>& command() { return m_command; }
|
||||
virtual void for_each_entry(RefPtr<Shell> shell, Function<IterationDecision(NonnullRefPtr<Value>)> callback) override;
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
const NonnullRefPtr<Node>& command() const { return m_command; }
|
||||
bool does_capture_stdout() const { return m_capture_stdout; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -722,6 +781,12 @@ class IfCond final : public Node {
|
|||
public:
|
||||
IfCond(Position, Optional<Position> else_position, NonnullRefPtr<AST::Node> cond_expr, RefPtr<AST::Node> true_branch, RefPtr<AST::Node> false_branch);
|
||||
virtual ~IfCond();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
const NonnullRefPtr<Node>& condition() const { return m_condition; }
|
||||
const RefPtr<Node>& true_branch() const { return m_true_branch; }
|
||||
const RefPtr<Node>& false_branch() const { return m_false_branch; }
|
||||
const Optional<Position> else_position() const { return m_else_position; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -742,6 +807,10 @@ class Join final : public Node {
|
|||
public:
|
||||
Join(Position, NonnullRefPtr<Node>, NonnullRefPtr<Node>);
|
||||
virtual ~Join();
|
||||
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; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -767,6 +836,11 @@ class MatchExpr final : public Node {
|
|||
public:
|
||||
MatchExpr(Position, NonnullRefPtr<Node> expr, String name, Optional<Position> as_position, Vector<MatchEntry> entries);
|
||||
virtual ~MatchExpr();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
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; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -786,6 +860,10 @@ class Or final : public Node {
|
|||
public:
|
||||
Or(Position, NonnullRefPtr<Node>, NonnullRefPtr<Node>);
|
||||
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; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -802,6 +880,10 @@ class Pipe final : public Node {
|
|||
public:
|
||||
Pipe(Position, NonnullRefPtr<Node>, NonnullRefPtr<Node>);
|
||||
virtual ~Pipe();
|
||||
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; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -809,7 +891,7 @@ private:
|
|||
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_list() const override { return true; }
|
||||
virtual bool is_command() const override { return true; }
|
||||
|
||||
NonnullRefPtr<Node> m_left;
|
||||
NonnullRefPtr<Node> m_right;
|
||||
|
@ -819,6 +901,7 @@ class ReadRedirection final : public PathRedirectionNode {
|
|||
public:
|
||||
ReadRedirection(Position, int, NonnullRefPtr<Node>);
|
||||
virtual ~ReadRedirection();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -830,6 +913,7 @@ class ReadWriteRedirection final : public PathRedirectionNode {
|
|||
public:
|
||||
ReadWriteRedirection(Position, int, NonnullRefPtr<Node>);
|
||||
virtual ~ReadWriteRedirection();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -841,6 +925,10 @@ class Sequence final : public Node {
|
|||
public:
|
||||
Sequence(Position, NonnullRefPtr<Node>, NonnullRefPtr<Node>);
|
||||
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; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -859,6 +947,9 @@ class Subshell final : public Node {
|
|||
public:
|
||||
Subshell(Position, RefPtr<Node> block);
|
||||
virtual ~Subshell();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
const RefPtr<Node>& block() const { return m_block; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -876,6 +967,7 @@ public:
|
|||
SimpleVariable(Position, String);
|
||||
virtual ~SimpleVariable();
|
||||
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
const String& name() const { return m_name; }
|
||||
|
||||
private:
|
||||
|
@ -894,6 +986,9 @@ class SpecialVariable final : public Node {
|
|||
public:
|
||||
SpecialVariable(Position, char);
|
||||
virtual ~SpecialVariable();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
char name() const { return m_name; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -910,6 +1005,10 @@ class Juxtaposition final : public Node {
|
|||
public:
|
||||
Juxtaposition(Position, NonnullRefPtr<Node>, NonnullRefPtr<Node>);
|
||||
virtual ~Juxtaposition();
|
||||
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; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -927,6 +1026,8 @@ class StringLiteral final : public Node {
|
|||
public:
|
||||
StringLiteral(Position, String);
|
||||
virtual ~StringLiteral();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
const String& text() const { return m_text; }
|
||||
|
||||
private:
|
||||
|
@ -943,6 +1044,10 @@ class StringPartCompose final : public Node {
|
|||
public:
|
||||
StringPartCompose(Position, NonnullRefPtr<Node>, NonnullRefPtr<Node>);
|
||||
virtual ~StringPartCompose();
|
||||
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; }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -959,6 +1064,7 @@ class SyntaxError final : public Node {
|
|||
public:
|
||||
SyntaxError(Position, String);
|
||||
virtual ~SyntaxError();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
const String& error_text() const { return m_syntax_error_text; }
|
||||
|
||||
|
@ -978,6 +1084,8 @@ class Tilde final : public Node {
|
|||
public:
|
||||
Tilde(Position, String);
|
||||
virtual ~Tilde();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
String text() const;
|
||||
|
||||
private:
|
||||
|
@ -1000,6 +1108,7 @@ public:
|
|||
};
|
||||
VariableDeclarations(Position, Vector<Variable> variables);
|
||||
virtual ~VariableDeclarations();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
const Vector<Variable>& variables() const { return m_variables; }
|
||||
|
||||
|
@ -1018,6 +1127,7 @@ class WriteAppendRedirection final : public PathRedirectionNode {
|
|||
public:
|
||||
WriteAppendRedirection(Position, int, NonnullRefPtr<Node>);
|
||||
virtual ~WriteAppendRedirection();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
@ -1029,6 +1139,7 @@ class WriteRedirection final : public PathRedirectionNode {
|
|||
public:
|
||||
WriteRedirection(Position, int, NonnullRefPtr<Node>);
|
||||
virtual ~WriteRedirection();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
private:
|
||||
virtual void dump(int level) const override;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
set(SOURCES
|
||||
AST.cpp
|
||||
Builtin.cpp
|
||||
Formatter.cpp
|
||||
Job.cpp
|
||||
NodeVisitor.cpp
|
||||
Parser.cpp
|
||||
Shell.cpp
|
||||
main.cpp
|
||||
|
|
597
Shell/Formatter.cpp
Normal file
597
Shell/Formatter.cpp
Normal file
|
@ -0,0 +1,597 @@
|
|||
/*
|
||||
* Copyright (c) 2020, the SerenityOS developers.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "Formatter.h"
|
||||
#include "AST.h"
|
||||
#include "Parser.h"
|
||||
#include <AK/TemporaryChange.h>
|
||||
|
||||
String Formatter::format()
|
||||
{
|
||||
auto node = Parser(m_source).parse();
|
||||
if (m_cursor >= 0)
|
||||
m_output_cursor = m_cursor;
|
||||
|
||||
if (!node)
|
||||
return String();
|
||||
|
||||
if (node->is_syntax_error())
|
||||
return m_source;
|
||||
|
||||
if (m_cursor >= 0) {
|
||||
auto hit_test = node->hit_test_position(m_cursor);
|
||||
if (hit_test.matching_node)
|
||||
m_hit_node = hit_test.matching_node.ptr();
|
||||
else
|
||||
m_hit_node = nullptr;
|
||||
}
|
||||
|
||||
m_parent_node = nullptr;
|
||||
|
||||
node->visit(*this);
|
||||
|
||||
auto string = m_builder.string_view();
|
||||
|
||||
if (!string.ends_with(" "))
|
||||
m_builder.append(m_trivia);
|
||||
|
||||
return m_builder.to_string();
|
||||
}
|
||||
|
||||
void Formatter::with_added_indent(int indent, Function<void()> callback)
|
||||
{
|
||||
TemporaryChange indent_change { m_current_indent, m_current_indent + indent };
|
||||
callback();
|
||||
}
|
||||
|
||||
void Formatter::in_new_block(Function<void()> callback)
|
||||
{
|
||||
current_builder().append('{');
|
||||
|
||||
with_added_indent(1, [&] {
|
||||
insert_separator();
|
||||
callback();
|
||||
});
|
||||
|
||||
insert_separator();
|
||||
current_builder().append('}');
|
||||
}
|
||||
|
||||
void Formatter::test_and_update_output_cursor(const AST::Node* node)
|
||||
{
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
if (node != m_hit_node)
|
||||
return;
|
||||
|
||||
m_output_cursor = current_builder().length() + m_cursor - node->position().start_offset;
|
||||
}
|
||||
|
||||
void Formatter::insert_separator()
|
||||
{
|
||||
current_builder().append('\n');
|
||||
insert_indent();
|
||||
}
|
||||
|
||||
void Formatter::insert_indent()
|
||||
{
|
||||
for (size_t i = 0; i < m_current_indent; ++i)
|
||||
current_builder().append(" ");
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::PathRedirectionNode* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
NodeVisitor::visit(node);
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::And* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
auto should_indent = m_parent_node && m_parent_node->class_name() != "And";
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
|
||||
with_added_indent(should_indent ? 1 : 0, [&] {
|
||||
node->left()->visit(*this);
|
||||
|
||||
current_builder().append(" \\");
|
||||
insert_separator();
|
||||
current_builder().append("&& ");
|
||||
|
||||
node->right()->visit(*this);
|
||||
});
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::ListConcatenate* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
|
||||
auto first = true;
|
||||
for (auto& subnode : node->list()) {
|
||||
if (!first)
|
||||
current_builder().append(' ');
|
||||
first = false;
|
||||
subnode->visit(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::Background* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
NodeVisitor::visit(node);
|
||||
current_builder().append(" &");
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::BarewordLiteral* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
current_builder().append(node->text());
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::CastToCommand* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
if (m_options.explicit_parentheses)
|
||||
current_builder().append('(');
|
||||
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
NodeVisitor::visit(node);
|
||||
|
||||
if (m_options.explicit_parentheses)
|
||||
current_builder().append(')');
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::CastToList* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
current_builder().append('(');
|
||||
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
NodeVisitor::visit(node);
|
||||
|
||||
current_builder().append(')');
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::CloseFdRedirection* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
|
||||
current_builder().appendf(" %d>&-", node->fd());
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::CommandLiteral*)
|
||||
{
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::Comment* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
current_builder().append("#");
|
||||
current_builder().append(node->text());
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::DynamicEvaluate* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
current_builder().append('$');
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
NodeVisitor::visit(node);
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::DoubleQuotedString* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
current_builder().append("\"");
|
||||
|
||||
TemporaryChange quotes { m_options.in_double_quotes, true };
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
|
||||
NodeVisitor::visit(node);
|
||||
|
||||
current_builder().append("\"");
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::Fd2FdRedirection* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
|
||||
current_builder().appendf(" %d>&%d", node->source_fd(), node->dest_fd());
|
||||
if (m_hit_node == node)
|
||||
++m_output_cursor;
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::FunctionDeclaration* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
current_builder().append(node->name().name);
|
||||
current_builder().append('(');
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
|
||||
auto first = true;
|
||||
for (auto& arg : node->arguments()) {
|
||||
if (!first)
|
||||
current_builder().append(' ');
|
||||
first = false;
|
||||
current_builder().append(arg.name);
|
||||
}
|
||||
|
||||
current_builder().append(") ");
|
||||
|
||||
in_new_block([&] {
|
||||
if (node->block())
|
||||
node->block()->visit(*this);
|
||||
});
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::ForLoop* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
current_builder().append("for ");
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
|
||||
if (node->variable_name() != "it") {
|
||||
current_builder().append(node->variable_name());
|
||||
current_builder().append(" in ");
|
||||
}
|
||||
|
||||
node->iterated_expression()->visit(*this);
|
||||
|
||||
current_builder().append(' ');
|
||||
in_new_block([&] {
|
||||
if (node->block())
|
||||
node->block()->visit(*this);
|
||||
});
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::Glob* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
current_builder().append(node->text());
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::Execute* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
auto& builder = current_builder();
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
ScopedValueRollback options_rollback { m_options };
|
||||
|
||||
if (node->does_capture_stdout()) {
|
||||
builder.append("$");
|
||||
m_options.explicit_parentheses = true;
|
||||
}
|
||||
|
||||
NodeVisitor::visit(node);
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::IfCond* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
|
||||
current_builder().append("if ");
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
|
||||
node->condition()->visit(*this);
|
||||
|
||||
current_builder().append(' ');
|
||||
|
||||
in_new_block([&] {
|
||||
if (node->true_branch())
|
||||
node->true_branch()->visit(*this);
|
||||
});
|
||||
|
||||
if (node->false_branch()) {
|
||||
current_builder().append(" else ");
|
||||
if (node->false_branch()->class_name() != "IfCond") {
|
||||
in_new_block([&] {
|
||||
node->false_branch()->visit(*this);
|
||||
});
|
||||
} else {
|
||||
node->false_branch()->visit(*this);
|
||||
}
|
||||
} else if (node->else_position().has_value()) {
|
||||
current_builder().append(" else ");
|
||||
}
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::Join* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
auto should_parenthesise = m_options.explicit_parentheses;
|
||||
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
TemporaryChange parens { m_options.explicit_parentheses, false };
|
||||
|
||||
if (should_parenthesise)
|
||||
current_builder().append('(');
|
||||
|
||||
NodeVisitor::visit(node);
|
||||
|
||||
if (should_parenthesise)
|
||||
current_builder().append(')');
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::MatchExpr* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
current_builder().append("match ");
|
||||
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
|
||||
node->matched_expr()->visit(*this);
|
||||
|
||||
if (!node->expr_name().is_empty()) {
|
||||
current_builder().append(" as ");
|
||||
current_builder().append(node->expr_name());
|
||||
}
|
||||
|
||||
current_builder().append(' ');
|
||||
in_new_block([&] {
|
||||
auto first_entry = true;
|
||||
for (auto& entry : node->entries()) {
|
||||
if (!first_entry)
|
||||
insert_separator();
|
||||
first_entry = false;
|
||||
auto first = true;
|
||||
for (auto& option : entry.options) {
|
||||
if (!first)
|
||||
current_builder().append(" | ");
|
||||
first = false;
|
||||
option.visit(*this);
|
||||
}
|
||||
|
||||
in_new_block([&] {
|
||||
if (entry.body)
|
||||
entry.body->visit(*this);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::Or* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
auto should_indent = m_parent_node && m_parent_node->class_name() != "Or";
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
|
||||
with_added_indent(should_indent ? 1 : 0, [&] {
|
||||
node->left()->visit(*this);
|
||||
|
||||
current_builder().append(" \\");
|
||||
insert_separator();
|
||||
current_builder().append("|| ");
|
||||
|
||||
node->right()->visit(*this);
|
||||
});
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::Pipe* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
auto should_indent = m_parent_node && m_parent_node->class_name() != "Pipe";
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
|
||||
node->left()->visit(*this);
|
||||
current_builder().append(" \\");
|
||||
|
||||
with_added_indent(should_indent ? 1 : 0, [&] {
|
||||
insert_separator();
|
||||
current_builder().append("| ");
|
||||
|
||||
node->right()->visit(*this);
|
||||
});
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::ReadRedirection* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
|
||||
if (node->fd() != 0)
|
||||
current_builder().appendf(" %d<", node->fd());
|
||||
else
|
||||
current_builder().append(" <");
|
||||
NodeVisitor::visit(node);
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::ReadWriteRedirection* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
|
||||
if (node->fd() != 0)
|
||||
current_builder().appendf(" %d<>", node->fd());
|
||||
else
|
||||
current_builder().append(" <>");
|
||||
NodeVisitor::visit(node);
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::Sequence* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
node->left()->visit(*this);
|
||||
insert_separator();
|
||||
|
||||
node->right()->visit(*this);
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::Subshell* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
|
||||
in_new_block([&] {
|
||||
insert_separator();
|
||||
NodeVisitor::visit(node);
|
||||
insert_separator();
|
||||
});
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::SimpleVariable* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
current_builder().append('$');
|
||||
current_builder().append(node->name());
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::SpecialVariable* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
current_builder().append('$');
|
||||
current_builder().append(node->name());
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::Juxtaposition* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
NodeVisitor::visit(node);
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::StringLiteral* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
if (!m_options.in_double_quotes)
|
||||
current_builder().append("'");
|
||||
|
||||
if (m_options.in_double_quotes) {
|
||||
for (auto ch : node->text()) {
|
||||
switch (ch) {
|
||||
case '"':
|
||||
case '\\':
|
||||
case '$':
|
||||
current_builder().append('\\');
|
||||
break;
|
||||
case '\n':
|
||||
current_builder().append("\\n");
|
||||
continue;
|
||||
case '\r':
|
||||
current_builder().append("\\r");
|
||||
continue;
|
||||
case '\t':
|
||||
current_builder().append("\\t");
|
||||
continue;
|
||||
case '\v':
|
||||
current_builder().append("\\v");
|
||||
continue;
|
||||
case '\f':
|
||||
current_builder().append("\\f");
|
||||
continue;
|
||||
case '\a':
|
||||
current_builder().append("\\a");
|
||||
continue;
|
||||
case '\e':
|
||||
current_builder().append("\\e");
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
current_builder().append(ch);
|
||||
}
|
||||
} else {
|
||||
current_builder().append(node->text());
|
||||
}
|
||||
|
||||
if (!m_options.in_double_quotes)
|
||||
current_builder().append("'");
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::StringPartCompose* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
NodeVisitor::visit(node);
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::SyntaxError* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
NodeVisitor::visit(node);
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::Tilde* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
current_builder().append(node->text());
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::VariableDeclarations* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
|
||||
auto first = true;
|
||||
for (auto& entry : node->variables()) {
|
||||
if (!first)
|
||||
current_builder().append(' ');
|
||||
first = false;
|
||||
entry.name->visit(*this);
|
||||
current_builder().append('=');
|
||||
|
||||
if (entry.value->is_command())
|
||||
current_builder().append('(');
|
||||
|
||||
entry.value->visit(*this);
|
||||
|
||||
if (entry.value->is_command())
|
||||
current_builder().append(')');
|
||||
}
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::WriteAppendRedirection* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
|
||||
if (node->fd() != 1)
|
||||
current_builder().appendf(" %d>>", node->fd());
|
||||
else
|
||||
current_builder().append(" >>");
|
||||
NodeVisitor::visit(node);
|
||||
}
|
||||
|
||||
void Formatter::visit(const AST::WriteRedirection* node)
|
||||
{
|
||||
test_and_update_output_cursor(node);
|
||||
TemporaryChange<const AST::Node*> parent { m_parent_node, node };
|
||||
|
||||
if (node->fd() != 1)
|
||||
current_builder().appendf(" %d>", node->fd());
|
||||
else
|
||||
current_builder().append(" >");
|
||||
NodeVisitor::visit(node);
|
||||
}
|
121
Shell/Formatter.h
Normal file
121
Shell/Formatter.h
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) 2020, the SerenityOS developers.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "NodeVisitor.h"
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <ctype.h>
|
||||
|
||||
class Formatter final : public AST::NodeVisitor {
|
||||
public:
|
||||
Formatter(const StringView& source, ssize_t cursor = -1)
|
||||
: m_builder(round_up_to_power_of_two(source.length(), 16))
|
||||
, m_source(source)
|
||||
, m_cursor(cursor)
|
||||
{
|
||||
size_t offset = 0;
|
||||
for (auto ptr = m_source.end() - 1; ptr >= m_source.begin() && isspace(*ptr); --ptr)
|
||||
++offset;
|
||||
|
||||
m_trivia = m_source.substring_view(m_source.length() - offset, offset);
|
||||
}
|
||||
|
||||
String format();
|
||||
size_t cursor() const { return m_output_cursor; }
|
||||
|
||||
private:
|
||||
virtual void visit(const AST::PathRedirectionNode*) override;
|
||||
virtual void visit(const AST::And*) override;
|
||||
virtual void visit(const AST::ListConcatenate*) override;
|
||||
virtual void visit(const AST::Background*) override;
|
||||
virtual void visit(const AST::BarewordLiteral*) override;
|
||||
virtual void visit(const AST::CastToCommand*) override;
|
||||
virtual void visit(const AST::CastToList*) override;
|
||||
virtual void visit(const AST::CloseFdRedirection*) override;
|
||||
virtual void visit(const AST::CommandLiteral*) override;
|
||||
virtual void visit(const AST::Comment*) override;
|
||||
virtual void visit(const AST::DynamicEvaluate*) override;
|
||||
virtual void visit(const AST::DoubleQuotedString*) override;
|
||||
virtual void visit(const AST::Fd2FdRedirection*) override;
|
||||
virtual void visit(const AST::FunctionDeclaration*) override;
|
||||
virtual void visit(const AST::ForLoop*) override;
|
||||
virtual void visit(const AST::Glob*) override;
|
||||
virtual void visit(const AST::Execute*) override;
|
||||
virtual void visit(const AST::IfCond*) override;
|
||||
virtual void visit(const AST::Join*) override;
|
||||
virtual void visit(const AST::MatchExpr*) override;
|
||||
virtual void visit(const AST::Or*) override;
|
||||
virtual void visit(const AST::Pipe*) override;
|
||||
virtual void visit(const AST::ReadRedirection*) override;
|
||||
virtual void visit(const AST::ReadWriteRedirection*) override;
|
||||
virtual void visit(const AST::Sequence*) override;
|
||||
virtual void visit(const AST::Subshell*) override;
|
||||
virtual void visit(const AST::SimpleVariable*) override;
|
||||
virtual void visit(const AST::SpecialVariable*) override;
|
||||
virtual void visit(const AST::Juxtaposition*) override;
|
||||
virtual void visit(const AST::StringLiteral*) override;
|
||||
virtual void visit(const AST::StringPartCompose*) override;
|
||||
virtual void visit(const AST::SyntaxError*) override;
|
||||
virtual void visit(const AST::Tilde*) override;
|
||||
virtual void visit(const AST::VariableDeclarations*) override;
|
||||
virtual void visit(const AST::WriteAppendRedirection*) override;
|
||||
virtual void visit(const AST::WriteRedirection*) override;
|
||||
|
||||
void test_and_update_output_cursor(const AST::Node*);
|
||||
void insert_separator();
|
||||
void insert_indent();
|
||||
|
||||
ALWAYS_INLINE void with_added_indent(int indent, Function<void()>);
|
||||
ALWAYS_INLINE void in_new_block(Function<void()>);
|
||||
|
||||
StringBuilder& current_builder() { return m_builder; }
|
||||
|
||||
struct Options {
|
||||
size_t max_line_length_hint { 80 };
|
||||
bool explicit_parentheses { false };
|
||||
bool explicit_braces { false };
|
||||
bool in_double_quotes { false };
|
||||
} m_options;
|
||||
|
||||
size_t m_current_line_length { 0 };
|
||||
size_t m_current_indent { 0 };
|
||||
|
||||
StringBuilder m_builder;
|
||||
|
||||
StringView m_source;
|
||||
size_t m_output_cursor { 0 };
|
||||
ssize_t m_cursor { -1 };
|
||||
AST::Node* m_hit_node { nullptr };
|
||||
|
||||
const AST::Node* m_parent_node { nullptr };
|
||||
|
||||
StringView m_trivia;
|
||||
};
|
|
@ -35,5 +35,43 @@ class Value;
|
|||
class SyntaxError;
|
||||
class Pipeline;
|
||||
struct Rewiring;
|
||||
class NodeVisitor;
|
||||
|
||||
class PathRedirectionNode;
|
||||
class And;
|
||||
class ListConcatenate;
|
||||
class Background;
|
||||
class BarewordLiteral;
|
||||
class CastToCommand;
|
||||
class CastToList;
|
||||
class CloseFdRedirection;
|
||||
class CommandLiteral;
|
||||
class Comment;
|
||||
class DynamicEvaluate;
|
||||
class DoubleQuotedString;
|
||||
class Fd2FdRedirection;
|
||||
class FunctionDeclaration;
|
||||
class ForLoop;
|
||||
class Glob;
|
||||
class Execute;
|
||||
class IfCond;
|
||||
class Join;
|
||||
class MatchExpr;
|
||||
class Or;
|
||||
class Pipe;
|
||||
class ReadRedirection;
|
||||
class ReadWriteRedirection;
|
||||
class Sequence;
|
||||
class Subshell;
|
||||
class SimpleVariable;
|
||||
class SpecialVariable;
|
||||
class Juxtaposition;
|
||||
class StringLiteral;
|
||||
class StringPartCompose;
|
||||
class SyntaxError;
|
||||
class Tilde;
|
||||
class VariableDeclarations;
|
||||
class WriteAppendRedirection;
|
||||
class WriteRedirection;
|
||||
|
||||
}
|
||||
|
|
228
Shell/NodeVisitor.cpp
Normal file
228
Shell/NodeVisitor.cpp
Normal file
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* Copyright (c) 2020, the SerenityOS developers.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "NodeVisitor.h"
|
||||
#include "AST.h"
|
||||
|
||||
namespace AST {
|
||||
|
||||
void NodeVisitor::visit(const AST::PathRedirectionNode* node)
|
||||
{
|
||||
node->path()->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::And* node)
|
||||
{
|
||||
node->left()->visit(*this);
|
||||
node->right()->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::ListConcatenate* node)
|
||||
{
|
||||
for (auto& subnode : node->list())
|
||||
subnode->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::Background* node)
|
||||
{
|
||||
node->command()->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::BarewordLiteral*)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::CastToCommand* node)
|
||||
{
|
||||
node->inner()->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::CastToList* node)
|
||||
{
|
||||
if (node->inner())
|
||||
node->inner()->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::CloseFdRedirection*)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::CommandLiteral*)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::Comment*)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::DynamicEvaluate* node)
|
||||
{
|
||||
node->inner()->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::DoubleQuotedString* node)
|
||||
{
|
||||
if (node->inner())
|
||||
node->inner()->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::Fd2FdRedirection*)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::FunctionDeclaration* node)
|
||||
{
|
||||
if (node->block())
|
||||
node->block()->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::ForLoop* node)
|
||||
{
|
||||
node->iterated_expression()->visit(*this);
|
||||
if (node->block())
|
||||
node->block()->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::Glob*)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::Execute* node)
|
||||
{
|
||||
node->command()->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::IfCond* node)
|
||||
{
|
||||
node->condition()->visit(*this);
|
||||
if (node->true_branch())
|
||||
node->true_branch()->visit(*this);
|
||||
if (node->false_branch())
|
||||
node->false_branch()->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::Join* node)
|
||||
{
|
||||
node->left()->visit(*this);
|
||||
node->right()->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::MatchExpr* node)
|
||||
{
|
||||
node->matched_expr()->visit(*this);
|
||||
for (auto& entry : node->entries()) {
|
||||
for (auto& option : entry.options)
|
||||
option.visit(*this);
|
||||
if (entry.body)
|
||||
entry.body->visit(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::Or* node)
|
||||
{
|
||||
node->left()->visit(*this);
|
||||
node->right()->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::Pipe* node)
|
||||
{
|
||||
node->left()->visit(*this);
|
||||
node->right()->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::ReadRedirection* node)
|
||||
{
|
||||
visit(static_cast<const AST::PathRedirectionNode*>(node));
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::ReadWriteRedirection* node)
|
||||
{
|
||||
visit(static_cast<const AST::PathRedirectionNode*>(node));
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::Sequence* node)
|
||||
{
|
||||
node->left()->visit(*this);
|
||||
node->right()->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::Subshell* node)
|
||||
{
|
||||
if (node->block())
|
||||
node->block()->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::SimpleVariable*)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::SpecialVariable*)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::Juxtaposition* node)
|
||||
{
|
||||
node->left()->visit(*this);
|
||||
node->right()->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::StringLiteral*)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::StringPartCompose* node)
|
||||
{
|
||||
node->left()->visit(*this);
|
||||
node->right()->visit(*this);
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::SyntaxError*)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::Tilde*)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::VariableDeclarations* node)
|
||||
{
|
||||
for (auto& entry : node->variables()) {
|
||||
entry.name->visit(*this);
|
||||
entry.value->visit(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::WriteAppendRedirection* node)
|
||||
{
|
||||
visit(static_cast<const AST::PathRedirectionNode*>(node));
|
||||
}
|
||||
|
||||
void NodeVisitor::visit(const AST::WriteRedirection* node)
|
||||
{
|
||||
visit(static_cast<const AST::PathRedirectionNode*>(node));
|
||||
}
|
||||
|
||||
}
|
73
Shell/NodeVisitor.h
Normal file
73
Shell/NodeVisitor.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2020, the SerenityOS developers.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Forward.h"
|
||||
|
||||
namespace AST {
|
||||
|
||||
class NodeVisitor {
|
||||
public:
|
||||
virtual void visit(const AST::PathRedirectionNode*);
|
||||
virtual void visit(const AST::And*);
|
||||
virtual void visit(const AST::ListConcatenate*);
|
||||
virtual void visit(const AST::Background*);
|
||||
virtual void visit(const AST::BarewordLiteral*);
|
||||
virtual void visit(const AST::CastToCommand*);
|
||||
virtual void visit(const AST::CastToList*);
|
||||
virtual void visit(const AST::CloseFdRedirection*);
|
||||
virtual void visit(const AST::CommandLiteral*);
|
||||
virtual void visit(const AST::Comment*);
|
||||
virtual void visit(const AST::DynamicEvaluate*);
|
||||
virtual void visit(const AST::DoubleQuotedString*);
|
||||
virtual void visit(const AST::Fd2FdRedirection*);
|
||||
virtual void visit(const AST::FunctionDeclaration*);
|
||||
virtual void visit(const AST::ForLoop*);
|
||||
virtual void visit(const AST::Glob*);
|
||||
virtual void visit(const AST::Execute*);
|
||||
virtual void visit(const AST::IfCond*);
|
||||
virtual void visit(const AST::Join*);
|
||||
virtual void visit(const AST::MatchExpr*);
|
||||
virtual void visit(const AST::Or*);
|
||||
virtual void visit(const AST::Pipe*);
|
||||
virtual void visit(const AST::ReadRedirection*);
|
||||
virtual void visit(const AST::ReadWriteRedirection*);
|
||||
virtual void visit(const AST::Sequence*);
|
||||
virtual void visit(const AST::Subshell*);
|
||||
virtual void visit(const AST::SimpleVariable*);
|
||||
virtual void visit(const AST::SpecialVariable*);
|
||||
virtual void visit(const AST::Juxtaposition*);
|
||||
virtual void visit(const AST::StringLiteral*);
|
||||
virtual void visit(const AST::StringPartCompose*);
|
||||
virtual void visit(const AST::SyntaxError*);
|
||||
virtual void visit(const AST::Tilde*);
|
||||
virtual void visit(const AST::VariableDeclarations*);
|
||||
virtual void visit(const AST::WriteAppendRedirection*);
|
||||
virtual void visit(const AST::WriteRedirection*);
|
||||
};
|
||||
|
||||
}
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include "Shell.h"
|
||||
#include "Execution.h"
|
||||
#include "Formatter.h"
|
||||
#include <AK/Function.h>
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <AK/ScopeGuard.h>
|
||||
|
@ -449,6 +450,15 @@ bool Shell::invoke_function(const AST::Command& command, int& retval)
|
|||
return true;
|
||||
}
|
||||
|
||||
String Shell::format(const StringView& source, ssize_t& cursor) const
|
||||
{
|
||||
Formatter formatter(source, cursor);
|
||||
auto result = formatter.format();
|
||||
cursor = formatter.cursor();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Shell::Frame Shell::push_frame()
|
||||
{
|
||||
m_local_frames.empend();
|
||||
|
|
|
@ -102,6 +102,8 @@ public:
|
|||
bool has_function(const String&);
|
||||
bool invoke_function(const AST::Command&, int& retval);
|
||||
|
||||
String format(const StringView&, ssize_t& cursor) const;
|
||||
|
||||
struct LocalFrame {
|
||||
HashMap<String, RefPtr<AST::Value>> local_variables;
|
||||
};
|
||||
|
|
|
@ -200,21 +200,34 @@ int main(int argc, char** argv)
|
|||
const char* file_to_read_from = nullptr;
|
||||
Vector<const char*> script_args;
|
||||
bool skip_rc_files = false;
|
||||
const char* format = nullptr;
|
||||
|
||||
Core::ArgsParser parser;
|
||||
parser.add_option(command_to_run, "String to read commands from", "command-string", 'c', "command-string");
|
||||
parser.add_option(skip_rc_files, "Skip running shellrc files", "skip-shellrc", 0);
|
||||
parser.add_option(format, "File to format", "format", 0, "file");
|
||||
parser.add_positional_argument(file_to_read_from, "File to read commands from", "file", Core::ArgsParser::Required::No);
|
||||
parser.add_positional_argument(script_args, "Extra argumets to pass to the script (via $* and co)", "argument", Core::ArgsParser::Required::No);
|
||||
|
||||
parser.parse(argc, argv);
|
||||
|
||||
if (format) {
|
||||
auto file = Core::File::open(format, Core::IODevice::ReadOnly);
|
||||
if (file.is_error()) {
|
||||
fprintf(stderr, "Error: %s", file.error().characters());
|
||||
return 1;
|
||||
}
|
||||
|
||||
ssize_t cursor = -1;
|
||||
puts(shell->format(file.value()->read_all(), cursor).characters());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (getsid(getpid()) == 0) {
|
||||
if (setsid() < 0) {
|
||||
perror("setsid");
|
||||
// Let's just hope that it's ok.
|
||||
}
|
||||
}
|
||||
|
||||
shell->current_script = argv[0];
|
||||
|
||||
|
|
Loading…
Reference in a new issue