mirror of
https://github.com/RGBCube/serenity
synced 2025-07-23 14:07:42 +00:00
Shell: Show descriptions about syntax errors
The description contains an error message and where in the source the error happened.
This commit is contained in:
parent
034be8e74c
commit
d6de2b5828
5 changed files with 90 additions and 43 deletions
|
@ -174,8 +174,10 @@ And::And(Position position, RefPtr<Node> left, RefPtr<Node> right)
|
||||||
, m_left(move(left))
|
, m_left(move(left))
|
||||||
, m_right(move(right))
|
, m_right(move(right))
|
||||||
{
|
{
|
||||||
if (m_left->is_syntax_error() || m_right->is_syntax_error())
|
if (m_left->is_syntax_error())
|
||||||
set_is_syntax_error();
|
set_is_syntax_error(m_left->syntax_error_node());
|
||||||
|
else if (m_right->is_syntax_error())
|
||||||
|
set_is_syntax_error(m_right->syntax_error_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
And::~And()
|
And::~And()
|
||||||
|
@ -233,8 +235,10 @@ ListConcatenate::ListConcatenate(Position position, RefPtr<Node> element, RefPtr
|
||||||
, m_element(move(element))
|
, m_element(move(element))
|
||||||
, m_list(move(list))
|
, m_list(move(list))
|
||||||
{
|
{
|
||||||
if (m_element->is_syntax_error() || m_list->is_syntax_error())
|
if (m_element->is_syntax_error())
|
||||||
set_is_syntax_error();
|
set_is_syntax_error(m_element->syntax_error_node());
|
||||||
|
else if (m_list->is_syntax_error())
|
||||||
|
set_is_syntax_error(m_list->syntax_error_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
ListConcatenate::~ListConcatenate()
|
ListConcatenate::~ListConcatenate()
|
||||||
|
@ -274,7 +278,7 @@ Background::Background(Position position, RefPtr<Node> command)
|
||||||
, m_command(move(command))
|
, m_command(move(command))
|
||||||
{
|
{
|
||||||
if (m_command->is_syntax_error())
|
if (m_command->is_syntax_error())
|
||||||
set_is_syntax_error();
|
set_is_syntax_error(m_command->syntax_error_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
Background::~Background()
|
Background::~Background()
|
||||||
|
@ -386,7 +390,7 @@ CastToCommand::CastToCommand(Position position, RefPtr<Node> inner)
|
||||||
, m_inner(move(inner))
|
, m_inner(move(inner))
|
||||||
{
|
{
|
||||||
if (m_inner->is_syntax_error())
|
if (m_inner->is_syntax_error())
|
||||||
set_is_syntax_error();
|
set_is_syntax_error(m_inner->syntax_error_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
CastToCommand::~CastToCommand()
|
CastToCommand::~CastToCommand()
|
||||||
|
@ -442,7 +446,7 @@ CastToList::CastToList(Position position, RefPtr<Node> inner)
|
||||||
, m_inner(move(inner))
|
, m_inner(move(inner))
|
||||||
{
|
{
|
||||||
if (m_inner && m_inner->is_syntax_error())
|
if (m_inner && m_inner->is_syntax_error())
|
||||||
set_is_syntax_error();
|
set_is_syntax_error(m_inner->syntax_error_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
CastToList::~CastToList()
|
CastToList::~CastToList()
|
||||||
|
@ -565,7 +569,7 @@ DoubleQuotedString::DoubleQuotedString(Position position, RefPtr<Node> inner)
|
||||||
, m_inner(move(inner))
|
, m_inner(move(inner))
|
||||||
{
|
{
|
||||||
if (m_inner->is_syntax_error())
|
if (m_inner->is_syntax_error())
|
||||||
set_is_syntax_error();
|
set_is_syntax_error(m_inner->syntax_error_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
DoubleQuotedString::~DoubleQuotedString()
|
DoubleQuotedString::~DoubleQuotedString()
|
||||||
|
@ -613,7 +617,7 @@ DynamicEvaluate::DynamicEvaluate(Position position, RefPtr<Node> inner)
|
||||||
, m_inner(move(inner))
|
, m_inner(move(inner))
|
||||||
{
|
{
|
||||||
if (m_inner->is_syntax_error())
|
if (m_inner->is_syntax_error())
|
||||||
set_is_syntax_error();
|
set_is_syntax_error(m_inner->syntax_error_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
DynamicEvaluate::~DynamicEvaluate()
|
DynamicEvaluate::~DynamicEvaluate()
|
||||||
|
@ -865,7 +869,7 @@ Execute::Execute(Position position, RefPtr<Node> command, bool capture_stdout)
|
||||||
, m_capture_stdout(capture_stdout)
|
, m_capture_stdout(capture_stdout)
|
||||||
{
|
{
|
||||||
if (m_command->is_syntax_error())
|
if (m_command->is_syntax_error())
|
||||||
set_is_syntax_error();
|
set_is_syntax_error(m_command->syntax_error_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
Execute::~Execute()
|
Execute::~Execute()
|
||||||
|
@ -911,8 +915,10 @@ Join::Join(Position position, RefPtr<Node> left, RefPtr<Node> right)
|
||||||
, m_left(move(left))
|
, m_left(move(left))
|
||||||
, m_right(move(right))
|
, m_right(move(right))
|
||||||
{
|
{
|
||||||
if (m_left->is_syntax_error() || m_right->is_syntax_error())
|
if (m_left->is_syntax_error())
|
||||||
set_is_syntax_error();
|
set_is_syntax_error(m_left->syntax_error_node());
|
||||||
|
else if (m_right->is_syntax_error())
|
||||||
|
set_is_syntax_error(m_right->syntax_error_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
Join::~Join()
|
Join::~Join()
|
||||||
|
@ -969,8 +975,10 @@ Or::Or(Position position, RefPtr<Node> left, RefPtr<Node> right)
|
||||||
, m_left(move(left))
|
, m_left(move(left))
|
||||||
, m_right(move(right))
|
, m_right(move(right))
|
||||||
{
|
{
|
||||||
if (m_left->is_syntax_error() || m_right->is_syntax_error())
|
if (m_left->is_syntax_error())
|
||||||
set_is_syntax_error();
|
set_is_syntax_error(m_left->syntax_error_node());
|
||||||
|
else if (m_right->is_syntax_error())
|
||||||
|
set_is_syntax_error(m_right->syntax_error_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
Or::~Or()
|
Or::~Or()
|
||||||
|
@ -1030,8 +1038,10 @@ Pipe::Pipe(Position position, RefPtr<Node> left, RefPtr<Node> right)
|
||||||
, m_left(move(left))
|
, m_left(move(left))
|
||||||
, m_right(move(right))
|
, m_right(move(right))
|
||||||
{
|
{
|
||||||
if (m_left->is_syntax_error() || m_right->is_syntax_error())
|
if (m_left->is_syntax_error())
|
||||||
set_is_syntax_error();
|
set_is_syntax_error(m_left->syntax_error_node());
|
||||||
|
else if (m_right->is_syntax_error())
|
||||||
|
set_is_syntax_error(m_right->syntax_error_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
Pipe::~Pipe()
|
Pipe::~Pipe()
|
||||||
|
@ -1189,8 +1199,10 @@ Sequence::Sequence(Position position, RefPtr<Node> left, RefPtr<Node> right)
|
||||||
, m_left(move(left))
|
, m_left(move(left))
|
||||||
, m_right(move(right))
|
, m_right(move(right))
|
||||||
{
|
{
|
||||||
if (m_left->is_syntax_error() || m_right->is_syntax_error())
|
if (m_left->is_syntax_error())
|
||||||
set_is_syntax_error();
|
set_is_syntax_error(m_left->syntax_error_node());
|
||||||
|
else if (m_right->is_syntax_error())
|
||||||
|
set_is_syntax_error(m_right->syntax_error_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
Sequence::~Sequence()
|
Sequence::~Sequence()
|
||||||
|
@ -1408,8 +1420,10 @@ Juxtaposition::Juxtaposition(Position position, RefPtr<Node> left, RefPtr<Node>
|
||||||
, m_left(move(left))
|
, m_left(move(left))
|
||||||
, m_right(move(right))
|
, m_right(move(right))
|
||||||
{
|
{
|
||||||
if (m_left->is_syntax_error() || m_right->is_syntax_error())
|
if (m_left->is_syntax_error())
|
||||||
set_is_syntax_error();
|
set_is_syntax_error(m_left->syntax_error_node());
|
||||||
|
else if (m_right->is_syntax_error())
|
||||||
|
set_is_syntax_error(m_right->syntax_error_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
Juxtaposition::~Juxtaposition()
|
Juxtaposition::~Juxtaposition()
|
||||||
|
@ -1486,8 +1500,10 @@ StringPartCompose::StringPartCompose(Position position, RefPtr<Node> left, RefPt
|
||||||
, m_left(move(left))
|
, m_left(move(left))
|
||||||
, m_right(move(right))
|
, m_right(move(right))
|
||||||
{
|
{
|
||||||
if (m_left->is_syntax_error() || m_right->is_syntax_error())
|
if (m_left->is_syntax_error())
|
||||||
set_is_syntax_error();
|
set_is_syntax_error(m_left->syntax_error_node());
|
||||||
|
else if (m_right->is_syntax_error())
|
||||||
|
set_is_syntax_error(m_right->syntax_error_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
StringPartCompose::~StringPartCompose()
|
StringPartCompose::~StringPartCompose()
|
||||||
|
@ -1510,10 +1526,16 @@ void SyntaxError::highlight_in_editor(Line::Editor& editor, Shell&, HighlightMet
|
||||||
editor.stylize({ m_position.start_offset, m_position.end_offset }, { Line::Style::Foreground(Line::Style::XtermColor::Red), Line::Style::Bold });
|
editor.stylize({ m_position.start_offset, m_position.end_offset }, { Line::Style::Foreground(Line::Style::XtermColor::Red), Line::Style::Bold });
|
||||||
}
|
}
|
||||||
|
|
||||||
SyntaxError::SyntaxError(Position position)
|
SyntaxError::SyntaxError(Position position, String error)
|
||||||
: Node(move(position))
|
: Node(move(position))
|
||||||
|
, m_syntax_error_text(move(error))
|
||||||
{
|
{
|
||||||
set_is_syntax_error();
|
m_is_syntax_error = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SyntaxError& SyntaxError::syntax_error_node() const
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
SyntaxError::~SyntaxError()
|
SyntaxError::~SyntaxError()
|
||||||
|
@ -1692,8 +1714,12 @@ VariableDeclarations::VariableDeclarations(Position position, Vector<Variable> v
|
||||||
, m_variables(move(variables))
|
, m_variables(move(variables))
|
||||||
{
|
{
|
||||||
for (auto& decl : m_variables) {
|
for (auto& decl : m_variables) {
|
||||||
if (decl.name->is_syntax_error() || decl.value->is_syntax_error()) {
|
if (decl.name->is_syntax_error()) {
|
||||||
set_is_syntax_error();
|
set_is_syntax_error(decl.name->syntax_error_node());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (decl.value->is_syntax_error()) {
|
||||||
|
set_is_syntax_error(decl.value->syntax_error_node());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
19
Shell/AST.h
19
Shell/AST.h
|
@ -333,11 +333,21 @@ public:
|
||||||
virtual bool would_execute() const { return false; }
|
virtual bool would_execute() const { return false; }
|
||||||
|
|
||||||
const Position& position() const { return m_position; }
|
const Position& position() const { return m_position; }
|
||||||
void set_is_syntax_error() { m_is_syntax_error = true; }
|
void set_is_syntax_error(const SyntaxError& error_node)
|
||||||
|
{
|
||||||
|
m_is_syntax_error = true;
|
||||||
|
m_syntax_error_node = error_node;
|
||||||
|
}
|
||||||
|
virtual const SyntaxError& syntax_error_node() const
|
||||||
|
{
|
||||||
|
ASSERT(is_syntax_error());
|
||||||
|
return *m_syntax_error_node;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Position m_position;
|
Position m_position;
|
||||||
bool m_is_syntax_error { false };
|
bool m_is_syntax_error { false };
|
||||||
|
RefPtr<const SyntaxError> m_syntax_error_node;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PathRedirectionNode : public Node {
|
class PathRedirectionNode : public Node {
|
||||||
|
@ -766,9 +776,11 @@ private:
|
||||||
|
|
||||||
class SyntaxError final : public Node {
|
class SyntaxError final : public Node {
|
||||||
public:
|
public:
|
||||||
SyntaxError(Position);
|
SyntaxError(Position, String);
|
||||||
virtual ~SyntaxError();
|
virtual ~SyntaxError();
|
||||||
|
|
||||||
|
const String& error_text() const { return m_syntax_error_text; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void dump(int level) const override;
|
virtual void dump(int level) const override;
|
||||||
virtual RefPtr<Value> run(RefPtr<Shell>) override;
|
virtual RefPtr<Value> run(RefPtr<Shell>) override;
|
||||||
|
@ -776,6 +788,9 @@ private:
|
||||||
virtual HitTestResult hit_test_position(size_t) override { return { nullptr, nullptr }; }
|
virtual HitTestResult hit_test_position(size_t) override { return { nullptr, nullptr }; }
|
||||||
virtual String class_name() const override { return "SyntaxError"; }
|
virtual String class_name() const override { return "SyntaxError"; }
|
||||||
virtual bool is_syntax_error() const override { return true; }
|
virtual bool is_syntax_error() const override { return true; }
|
||||||
|
virtual const SyntaxError& syntax_error_node() const override;
|
||||||
|
|
||||||
|
String m_syntax_error_text;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Tilde final : public Node {
|
class Tilde final : public Node {
|
||||||
|
|
|
@ -31,5 +31,6 @@ namespace AST {
|
||||||
|
|
||||||
class Node;
|
class Node;
|
||||||
class Value;
|
class Value;
|
||||||
|
class SyntaxError;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,7 @@ RefPtr<AST::Node> Parser::parse()
|
||||||
// Parsing stopped midway, this is a syntax error.
|
// Parsing stopped midway, this is a syntax error.
|
||||||
auto error_start = push_start();
|
auto error_start = push_start();
|
||||||
m_offset = m_input.length();
|
m_offset = m_input.length();
|
||||||
return create<AST::Join>(move(toplevel), create<AST::SyntaxError>());
|
return create<AST::Join>(move(toplevel), create<AST::SyntaxError>("Unexpected tokens past the end"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return toplevel;
|
return toplevel;
|
||||||
|
@ -234,7 +234,7 @@ RefPtr<AST::Node> Parser::parse_variable_decls()
|
||||||
if (!command)
|
if (!command)
|
||||||
m_offset = start->offset;
|
m_offset = start->offset;
|
||||||
else if (!expect(')'))
|
else if (!expect(')'))
|
||||||
command->set_is_syntax_error();
|
command->set_is_syntax_error(*create<AST::SyntaxError>("Expected a terminating close paren"));
|
||||||
expression = command;
|
expression = command;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -341,7 +341,7 @@ RefPtr<AST::Node> Parser::parse_redirection()
|
||||||
// Eat a character and hope the problem goes away
|
// Eat a character and hope the problem goes away
|
||||||
consume();
|
consume();
|
||||||
}
|
}
|
||||||
return create<AST::SyntaxError>();
|
return create<AST::SyntaxError>("Expected a path");
|
||||||
}
|
}
|
||||||
return create<AST::WriteAppendRedirection>(pipe_fd, move(path)); // Redirection WriteAppend
|
return create<AST::WriteAppendRedirection>(pipe_fd, move(path)); // Redirection WriteAppend
|
||||||
}
|
}
|
||||||
|
@ -363,7 +363,10 @@ RefPtr<AST::Node> Parser::parse_redirection()
|
||||||
ASSERT(fd.has_value());
|
ASSERT(fd.has_value());
|
||||||
dest_pipe_fd = fd.value();
|
dest_pipe_fd = fd.value();
|
||||||
}
|
}
|
||||||
return create<AST::Fd2FdRedirection>(pipe_fd, dest_pipe_fd); // Redirection Fd2Fd
|
auto redir = create<AST::Fd2FdRedirection>(pipe_fd, dest_pipe_fd); // Redirection Fd2Fd
|
||||||
|
if (dest_pipe_fd == -1)
|
||||||
|
redir->set_is_syntax_error(*create<AST::SyntaxError>("Expected a file descriptor"));
|
||||||
|
return redir;
|
||||||
}
|
}
|
||||||
consume_while(is_whitespace);
|
consume_while(is_whitespace);
|
||||||
pipe_fd = pipe_fd >= 0 ? pipe_fd : STDOUT_FILENO;
|
pipe_fd = pipe_fd >= 0 ? pipe_fd : STDOUT_FILENO;
|
||||||
|
@ -373,7 +376,7 @@ RefPtr<AST::Node> Parser::parse_redirection()
|
||||||
// Eat a character and hope the problem goes away
|
// Eat a character and hope the problem goes away
|
||||||
consume();
|
consume();
|
||||||
}
|
}
|
||||||
return create<AST::SyntaxError>();
|
return create<AST::SyntaxError>("Expected a path");
|
||||||
}
|
}
|
||||||
return create<AST::WriteRedirection>(pipe_fd, move(path)); // Redirection Write
|
return create<AST::WriteRedirection>(pipe_fd, move(path)); // Redirection Write
|
||||||
}
|
}
|
||||||
|
@ -397,7 +400,7 @@ RefPtr<AST::Node> Parser::parse_redirection()
|
||||||
// Eat a character and hope the problem goes away
|
// Eat a character and hope the problem goes away
|
||||||
consume();
|
consume();
|
||||||
}
|
}
|
||||||
return create<AST::SyntaxError>();
|
return create<AST::SyntaxError>("Expected a path");
|
||||||
}
|
}
|
||||||
if (mode == Read)
|
if (mode == Read)
|
||||||
return create<AST::ReadRedirection>(pipe_fd, move(path)); // Redirection Read
|
return create<AST::ReadRedirection>(pipe_fd, move(path)); // Redirection Read
|
||||||
|
@ -530,10 +533,10 @@ RefPtr<AST::Node> Parser::parse_string()
|
||||||
consume();
|
consume();
|
||||||
auto inner = parse_doublequoted_string_inner();
|
auto inner = parse_doublequoted_string_inner();
|
||||||
if (!inner)
|
if (!inner)
|
||||||
inner = create<AST::SyntaxError>();
|
inner = create<AST::SyntaxError>("Unexpected EOF in string");
|
||||||
if (!expect('"')) {
|
if (!expect('"')) {
|
||||||
inner = create<AST::DoubleQuotedString>(move(inner));
|
inner = create<AST::DoubleQuotedString>(move(inner));
|
||||||
inner->set_is_syntax_error();
|
inner->set_is_syntax_error(*create<AST::SyntaxError>("Expected a terminating double quote"));
|
||||||
return inner;
|
return inner;
|
||||||
}
|
}
|
||||||
return create<AST::DoubleQuotedString>(move(inner)); // Double Quoted String
|
return create<AST::DoubleQuotedString>(move(inner)); // Double Quoted String
|
||||||
|
@ -547,7 +550,7 @@ RefPtr<AST::Node> Parser::parse_string()
|
||||||
is_error = true;
|
is_error = true;
|
||||||
auto result = create<AST::StringLiteral>(move(text)); // String Literal
|
auto result = create<AST::StringLiteral>(move(text)); // String Literal
|
||||||
if (is_error)
|
if (is_error)
|
||||||
result->set_is_syntax_error();
|
result->set_is_syntax_error(*create<AST::SyntaxError>("Expected a terminating single quote"));
|
||||||
return move(result);
|
return move(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -682,15 +685,15 @@ RefPtr<AST::Node> Parser::parse_evaluate()
|
||||||
consume();
|
consume();
|
||||||
auto inner = parse_pipe_sequence();
|
auto inner = parse_pipe_sequence();
|
||||||
if (!inner)
|
if (!inner)
|
||||||
inner = create<AST::SyntaxError>();
|
inner = create<AST::SyntaxError>("Unexpected EOF in list");
|
||||||
if (!expect(')'))
|
if (!expect(')'))
|
||||||
inner->set_is_syntax_error();
|
inner->set_is_syntax_error(*create<AST::SyntaxError>("Expected a terminating close paren"));
|
||||||
return create<AST::Execute>(move(inner), true);
|
return create<AST::Execute>(move(inner), true);
|
||||||
}
|
}
|
||||||
auto inner = parse_expression();
|
auto inner = parse_expression();
|
||||||
|
|
||||||
if (!inner) {
|
if (!inner) {
|
||||||
inner = create<AST::SyntaxError>();
|
inner = create<AST::SyntaxError>("Expected a command");
|
||||||
} else {
|
} else {
|
||||||
if (inner->is_list()) {
|
if (inner->is_list()) {
|
||||||
auto execute_inner = create<AST::Execute>(move(inner), true);
|
auto execute_inner = create<AST::Execute>(move(inner), true);
|
||||||
|
@ -814,7 +817,7 @@ RefPtr<AST::Node> Parser::parse_glob()
|
||||||
} else {
|
} else {
|
||||||
// FIXME: Allow composition of tilde+bareword with globs: '~/foo/bar/baz*'
|
// FIXME: Allow composition of tilde+bareword with globs: '~/foo/bar/baz*'
|
||||||
putback();
|
putback();
|
||||||
bareword_part->set_is_syntax_error();
|
bareword_part->set_is_syntax_error(*create<AST::SyntaxError>(String::format("Unexpected %s inside a glob", bareword_part->class_name().characters())));
|
||||||
return bareword_part;
|
return bareword_part;
|
||||||
}
|
}
|
||||||
textbuilder.append(text);
|
textbuilder.append(text);
|
||||||
|
|
|
@ -325,8 +325,10 @@ int Shell::run_command(const StringView& cmd)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (command->is_syntax_error()) {
|
if (command->is_syntax_error()) {
|
||||||
// FIXME: Provide descriptive messages for syntax errors.
|
auto& error_node = command->syntax_error_node();
|
||||||
fprintf(stderr, "Shell: Syntax error in command\n");
|
auto& position = error_node.position();
|
||||||
|
fprintf(stderr, "Shell: Syntax error in command: %s\n", error_node.error_text().characters());
|
||||||
|
fprintf(stderr, "Around '%.*s'\n", (int)min(position.end_offset - position.start_offset, (size_t)10), cmd.characters_without_null_termination() + position.start_offset);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue