1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 07:48:11 +00:00

Shell: Parse lists serially, and flatten them only when needed

This allows `((1 2 3) (4 5 6))` to remain nested until we explicitly
flatten it out.
This commit is contained in:
AnotherTest 2020-07-12 01:41:24 +04:30 committed by Andreas Kling
parent 9c1da8fca1
commit 95fc7dd03a
4 changed files with 96 additions and 49 deletions

View file

@ -216,33 +216,59 @@ And::~And()
void ListConcatenate::dump(int level) const void ListConcatenate::dump(int level) const
{ {
Node::dump(level); Node::dump(level);
m_element->dump(level + 1); for (auto& element : m_list)
m_list->dump(level + 1); element->dump(level + 1);
} }
RefPtr<Value> ListConcatenate::run(RefPtr<Shell> shell) RefPtr<Value> ListConcatenate::run(RefPtr<Shell> shell)
{ {
auto list = m_list->run(shell)->resolve_without_cast(shell); RefPtr<Value> result = nullptr;
auto element = m_element->run(shell)->resolve_without_cast(shell);
if (list->is_command() || element->is_command()) { for (auto& element : m_list) {
auto joined_commands = join_commands(element->resolve_as_commands(shell), list->resolve_as_commands(shell)); if (!result) {
result = create<ListValue>({ element->run(shell)->resolve_without_cast(shell) });
continue;
}
auto element_value = element->run(shell)->resolve_without_cast(shell);
if (joined_commands.size() == 1) if (result->is_command() || element_value->is_command()) {
return create<CommandValue>(joined_commands[0]); auto joined_commands = join_commands(result->resolve_as_commands(shell), element_value->resolve_as_commands(shell));
return create<CommandSequenceValue>(move(joined_commands));
if (joined_commands.size() == 1)
result = create<CommandValue>(joined_commands[0]);
else
result = create<CommandSequenceValue>(move(joined_commands));
} else {
Vector<RefPtr<Value>> values;
if (result->is_list_without_resolution()) {
values.append(static_cast<ListValue*>(result.ptr())->values());
} else {
for (auto& result : result->resolve_as_list(shell))
values.append(create<StringValue>(result));
}
values.append(move(element_value));
result = create<ListValue>(move(values));
}
} }
if (!result)
return create<ListValue>({});
return create<ListValue>({ move(element), move(list) }); return result;
} }
void ListConcatenate::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata) void ListConcatenate::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata)
{ {
auto first = metadata.is_first_in_list; auto first = metadata.is_first_in_list;
metadata.is_first_in_list = false; metadata.is_first_in_list = false;
m_list->highlight_in_editor(editor, shell, metadata);
metadata.is_first_in_list = first; metadata.is_first_in_list = first;
m_element->highlight_in_editor(editor, shell, metadata); for (auto& element : m_list) {
element->highlight_in_editor(editor, shell, metadata);
metadata.is_first_in_list = false;
}
} }
HitTestResult ListConcatenate::hit_test_position(size_t offset) HitTestResult ListConcatenate::hit_test_position(size_t offset)
@ -250,29 +276,37 @@ HitTestResult ListConcatenate::hit_test_position(size_t offset)
if (!position().contains(offset)) if (!position().contains(offset))
return {}; return {};
auto result = m_element->hit_test_position(offset); bool first = true;
if (result.matching_node) for (auto& element : m_list) {
return result; auto result = element->hit_test_position(offset);
result = m_list->hit_test_position(offset); if (!result.closest_node_with_semantic_meaning && !first)
if (!result.closest_node_with_semantic_meaning) result.closest_node_with_semantic_meaning = this;
result.closest_node_with_semantic_meaning = this; if (result.matching_node)
return result; return result;
first = false;
}
return {};
} }
RefPtr<Node> ListConcatenate::leftmost_trivial_literal() const RefPtr<Node> ListConcatenate::leftmost_trivial_literal() const
{ {
return m_element->leftmost_trivial_literal(); if (m_list.is_empty())
return nullptr;
return m_list.first()->leftmost_trivial_literal();
} }
ListConcatenate::ListConcatenate(Position position, RefPtr<Node> element, RefPtr<Node> list) ListConcatenate::ListConcatenate(Position position, Vector<RefPtr<Node>> list)
: Node(move(position)) : Node(move(position))
, m_element(move(element))
, m_list(move(list)) , m_list(move(list))
{ {
if (m_element->is_syntax_error()) for (auto& element : m_list) {
set_is_syntax_error(m_element->syntax_error_node()); if (element->is_syntax_error()) {
else if (m_list->is_syntax_error()) set_is_syntax_error(element->syntax_error_node());
set_is_syntax_error(m_list->syntax_error_node()); break;
}
}
} }
ListConcatenate::~ListConcatenate() ListConcatenate::~ListConcatenate()
@ -451,9 +485,9 @@ RefPtr<Value> CastToList::run(RefPtr<Shell> shell)
if (!m_inner) if (!m_inner)
return create<ListValue>({}); return create<ListValue>({});
auto inner_value = m_inner->run(shell); auto inner_value = m_inner->run(shell)->resolve_without_cast(shell);
if (inner_value->is_command()) if (inner_value->is_command() || inner_value->is_list())
return inner_value; return inner_value;
auto values = inner_value->resolve_as_list(shell); auto values = inner_value->resolve_as_list(shell);
@ -1783,6 +1817,15 @@ Vector<String> ListValue::resolve_as_list(RefPtr<Shell> shell)
return values; return values;
} }
RefPtr<Value> ListValue::resolve_without_cast(RefPtr<Shell> shell)
{
Vector<RefPtr<Value>> values;
for (auto& value : m_contained_values)
values.append(value->resolve_without_cast(shell));
return create<ListValue>(move(values));
}
CommandValue::~CommandValue() CommandValue::~CommandValue()
{ {
} }

View file

@ -164,6 +164,7 @@ public:
virtual bool is_job() const { return false; } virtual bool is_job() const { return false; }
virtual bool is_list() const { return false; } virtual bool is_list() const { return false; }
virtual bool is_string() const { return false; } virtual bool is_string() const { return false; }
virtual bool is_list_without_resolution() const { return false; }
}; };
class CommandValue final : public Value { class CommandValue final : public Value {
@ -221,14 +222,18 @@ private:
class ListValue final : public Value { class ListValue final : public Value {
public: public:
virtual Vector<String> resolve_as_list(RefPtr<Shell>) override; virtual Vector<String> resolve_as_list(RefPtr<Shell>) override;
virtual RefPtr<Value> resolve_without_cast(RefPtr<Shell>) override;
virtual ~ListValue(); virtual ~ListValue();
virtual bool is_list() const override { return true; } virtual bool is_list() const override { return true; }
virtual bool is_list_without_resolution() const override { return true; }
ListValue(Vector<String> values); ListValue(Vector<String> values);
ListValue(Vector<RefPtr<Value>> values) ListValue(Vector<RefPtr<Value>> values)
: m_contained_values(move(values)) : m_contained_values(move(values))
{ {
} }
const Vector<RefPtr<Value>>& values() const { return m_contained_values; }
private: private:
Vector<RefPtr<Value>> m_contained_values; Vector<RefPtr<Value>> m_contained_values;
}; };
@ -389,7 +394,7 @@ private:
class ListConcatenate final : public Node { class ListConcatenate final : public Node {
public: public:
ListConcatenate(Position, RefPtr<Node>, RefPtr<Node>); ListConcatenate(Position, Vector<RefPtr<Node>>);
virtual ~ListConcatenate(); virtual ~ListConcatenate();
private: private:
@ -401,8 +406,7 @@ private:
virtual bool is_list() const override { return true; } virtual bool is_list() const override { return true; }
virtual RefPtr<Node> leftmost_trivial_literal() const override; virtual RefPtr<Node> leftmost_trivial_literal() const override;
RefPtr<Node> m_element; Vector<RefPtr<Node>> m_list;
RefPtr<Node> m_list;
}; };
class Background final : public Node { class Background final : public Node {

View file

@ -420,19 +420,19 @@ RefPtr<AST::Node> Parser::parse_list_expression()
consume_while(is_whitespace); consume_while(is_whitespace);
auto rule_start = push_start(); auto rule_start = push_start();
Vector<RefPtr<AST::Node>> nodes;
auto expr = parse_expression(); do {
if (!expr) auto expr = parse_expression();
if (!expr)
break;
nodes.append(move(expr));
} while (!consume_while(is_whitespace).is_empty());
if (nodes.is_empty())
return nullptr; return nullptr;
if (consume_while(is_whitespace).is_empty()) return create<AST::ListConcatenate>(move(nodes)); // Concatenate List
return expr;
auto list = parse_list_expression();
if (!list)
return create<AST::CastToList>(move(expr));
return create<AST::ListConcatenate>(move(expr), move(list)); // Join Element List
} }
RefPtr<AST::Node> Parser::parse_expression() RefPtr<AST::Node> Parser::parse_expression()

View file

@ -361,19 +361,19 @@ int Shell::run_command(const StringView& cmd)
if (!command) if (!command)
return 0; return 0;
if (command->is_syntax_error()) {
auto& error_node = command->syntax_error_node();
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;
}
#ifdef SH_DEBUG #ifdef SH_DEBUG
dbg() << "Command follows"; dbg() << "Command follows";
command->dump(0); command->dump(0);
#endif #endif
if (command->is_syntax_error()) {
auto& error_node = command->syntax_error_node();
auto& position = error_node.position();
fprintf(stderr, "Shell: Syntax error in command: %s\n", error_node.error_text().characters());
fprintf(stderr, "Around '%.*s' at %zu:%zu\n", (int)min(position.end_offset - position.start_offset, (size_t)10), cmd.characters_without_null_termination() + position.start_offset, position.start_offset, position.end_offset);
return 1;
}
tcgetattr(0, &termios); tcgetattr(0, &termios);
auto result = command->run(*this); auto result = command->run(*this);