From 8e078cf4ab4d6834fbd9254384aae592fffc7da1 Mon Sep 17 00:00:00 2001 From: AnotherTest Date: Sat, 20 Jun 2020 18:00:45 +0430 Subject: [PATCH] Shell: Expand Juxtaposition of lists to list products This commit makes `echo x(foo bar)` create an argv of `echo xfoo xbar`, essentially modeling brace expansions in some shells. --- Shell/AST.cpp | 136 ++++++++++++++++++++++++++++++----------------- Shell/AST.h | 8 +-- Shell/Parser.cpp | 33 ++++++++---- Shell/Parser.h | 8 +-- Shell/Shell.cpp | 2 +- 5 files changed, 119 insertions(+), 68 deletions(-) diff --git a/Shell/AST.cpp b/Shell/AST.cpp index 6674dd5185..db391c26c2 100644 --- a/Shell/AST.cpp +++ b/Shell/AST.cpp @@ -35,6 +35,18 @@ namespace AST { +template +static inline RefPtr create(Args... args) +{ + return adopt(*new T(args...)); +} + +template +static inline RefPtr create(std::initializer_list> arg) +{ + return adopt(*new T(arg)); +} + static inline void print_indented(const String& str, int indent) { dbgprintf("%.*c%s\n", indent * 2, ' ', str.characters()); @@ -155,7 +167,7 @@ RefPtr ListConcatenate::run(TheExecutionInputType input_value) auto list = m_list->run(input_value); auto element = m_element->run(input_value); - return adopt(*new ListValue({ move(element), move(list) })); + return create({ move(element), move(list) }); } void ListConcatenate::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata) @@ -204,7 +216,7 @@ RefPtr Background::run(TheExecutionInputType input_value) auto& last = commands.last(); last.should_wait = false; - return adopt(*new CommandSequenceValue(move(commands))); + return create(move(commands)); } void Background::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata) @@ -238,7 +250,7 @@ void BarewordLiteral::dump(int level) const RefPtr BarewordLiteral::run(TheExecutionInputType) { - return adopt(*new StringValue(m_text)); + return create(m_text); } void BarewordLiteral::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata) @@ -294,7 +306,7 @@ RefPtr CastToCommand::run(TheExecutionInputType input_value) auto shell = input_value; auto argv = m_inner->run(input_value)->resolve_as_list(input_value); - return adopt(*new CommandValue(move(argv))); + return create(move(argv)); } void CastToCommand::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata) @@ -350,9 +362,9 @@ RefPtr CastToList::run(TheExecutionInputType input_value) auto values = m_inner->run(input_value)->resolve_as_list(input_value); Vector> cast_values; for (auto& value : values) - cast_values.append(adopt(*new StringValue(value))); + cast_values.append(create(value)); - return adopt(*new ListValue(cast_values)); + return create(cast_values); } void CastToList::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata) @@ -388,7 +400,7 @@ RefPtr CloseFdRedirection::run(TheExecutionInputType) { Command command; command.redirections.append(*new CloseRedirection(m_fd)); - return adopt(*new CommandValue(move(command))); + return create(move(command)); } void CloseFdRedirection::highlight_in_editor(Line::Editor& editor, Shell&, HighlightMetadata) @@ -415,7 +427,7 @@ void CommandLiteral::dump(int level) const RefPtr CommandLiteral::run(TheExecutionInputType) { - return adopt(*new CommandValue(m_command)); + return create(m_command); } CommandLiteral::CommandLiteral(Position position, Command command) @@ -436,7 +448,7 @@ void Comment::dump(int level) const RefPtr Comment::run(TheExecutionInputType) { - return adopt(*new StringValue("")); + return create(""); } void Comment::highlight_in_editor(Line::Editor& editor, Shell&, HighlightMetadata) @@ -468,7 +480,7 @@ RefPtr DoubleQuotedString::run(TheExecutionInputType input_value) builder.join("", values); - return adopt(*new StringValue(builder.to_string())); + return create(builder.to_string()); } void DoubleQuotedString::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata) @@ -514,12 +526,12 @@ RefPtr DynamicEvaluate::run(TheExecutionInputType input_value) if (result->is_string()) { auto name_part = result->resolve_as_list(input_value); ASSERT(name_part.size() == 1); - return adopt(*new SimpleVariableValue(name_part[0])); + return create(name_part[0]); } // If it's anything else, we're just gonna cast it to a list. auto list = result->resolve_as_list(input_value); - return adopt(*new CommandValue(move(list))); + return create(move(list)); } void DynamicEvaluate::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata) @@ -556,7 +568,7 @@ RefPtr Fd2FdRedirection::run(TheExecutionInputType) { Command command; command.redirections.append(*new FdRedirection(source_fd, dest_fd, Rewiring::Close::None)); - return adopt(*new CommandValue(move(command))); + return create(move(command)); } void Fd2FdRedirection::highlight_in_editor(Line::Editor& editor, Shell&, HighlightMetadata) @@ -583,7 +595,7 @@ void Glob::dump(int level) const RefPtr Glob::run(TheExecutionInputType) { - return adopt(*new GlobValue(m_text)); + return create(m_text); } void Glob::highlight_in_editor(Line::Editor& editor, Shell&, HighlightMetadata metadata) @@ -631,7 +643,7 @@ RefPtr Execute::run(TheExecutionInputType input_value) auto* ast = static_cast(subcommand_ast.ptr()); subcommand_ast = ast->command(); } - RefPtr substitute = adopt(*new Join(position(), move(subcommand_ast), adopt(*new CommandLiteral(position(), command)))); + RefPtr substitute = create(position(), move(subcommand_ast), create(position(), command)); commands.append(substitute->run(input_value)->resolve_as_commands(input_value)); } else { commands.append(command); @@ -685,7 +697,7 @@ RefPtr Execute::run(TheExecutionInputType input_value) int rc = pipe(pipefd); if (rc < 0) { dbg() << "Error: cannot pipe(): " << strerror(errno); - return adopt(*new StringValue("")); + return create(""); } auto last_in_commands = commands.take_last(); @@ -731,7 +743,7 @@ RefPtr Execute::run(TheExecutionInputType input_value) dbg() << "close() failed: " << strerror(errno); } - return adopt(*new StringValue(builder.build(), shell->local_variable_or("IFS", "\n"))); + return create(builder.build(), shell->local_variable_or("IFS", "\n")); } run_commands(commands); @@ -739,7 +751,7 @@ RefPtr Execute::run(TheExecutionInputType input_value) shell->block_on_job(job); } - return adopt(*new JobValue(move(job))); + return create(move(job)); } void Execute::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata) @@ -817,7 +829,7 @@ RefPtr Join::run(TheExecutionInputType input_value) commands.append(command); commands.append(right); - return adopt(*new CommandSequenceValue(move(commands))); + return create(move(commands)); } void Join::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata) @@ -920,7 +932,7 @@ RefPtr Pipe::run(TheExecutionInputType input_value) int rc = pipe(pipefd); if (rc < 0) { dbg() << "Error: cannot pipe(): " << strerror(errno); - return adopt(*new StringValue("")); + return create(""); } auto left = m_left->run(input_value)->resolve_as_commands(input_value); auto right = m_right->run(input_value)->resolve_as_commands(input_value); @@ -939,7 +951,7 @@ RefPtr Pipe::run(TheExecutionInputType input_value) commands.append(first_in_right); commands.append(right); - return adopt(*new CommandSequenceValue(move(commands))); + return create(move(commands)); } void Pipe::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata) @@ -1041,7 +1053,7 @@ RefPtr ReadRedirection::run(TheExecutionInputType input_value) builder.join(" ", path_segments); command.redirections.append(*new PathRedirection(builder.to_string(), m_fd, PathRedirection::Read)); - return adopt(*new CommandValue(move(command))); + return create(move(command)); } ReadRedirection::ReadRedirection(Position position, int fd, RefPtr path) @@ -1068,7 +1080,7 @@ RefPtr ReadWriteRedirection::run(TheExecutionInputType input_value) builder.join(" ", path_segments); command.redirections.append(*new PathRedirection(builder.to_string(), m_fd, PathRedirection::ReadWrite)); - return adopt(*new CommandValue(move(command))); + return create(move(command)); } ReadWriteRedirection::ReadWriteRedirection(Position position, int fd, RefPtr path) @@ -1096,7 +1108,7 @@ RefPtr Sequence::run(TheExecutionInputType input_value) commands.append(left); commands.append(right); - return adopt(*new CommandSequenceValue(move(commands))); + return create(move(commands)); } void Sequence::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata) @@ -1135,7 +1147,7 @@ void SimpleVariable::dump(int level) const RefPtr SimpleVariable::run(TheExecutionInputType) { - return adopt(*new SimpleVariableValue(m_name)); + return create(m_name); } void SimpleVariable::highlight_in_editor(Line::Editor& editor, Shell&, HighlightMetadata metadata) @@ -1188,7 +1200,7 @@ void SpecialVariable::dump(int level) const RefPtr SpecialVariable::run(TheExecutionInputType) { - return adopt(*new SpecialVariableValue(m_name)); + return create(m_name); } void SpecialVariable::highlight_in_editor(Line::Editor& editor, Shell&, HighlightMetadata) @@ -1219,26 +1231,54 @@ SpecialVariable::~SpecialVariable() { } -void StringConcatenate::dump(int level) const +void Juxtaposition::dump(int level) const { Node::dump(level); m_left->dump(level + 1); m_right->dump(level + 1); } -RefPtr StringConcatenate::run(TheExecutionInputType input_value) +RefPtr Juxtaposition::run(TheExecutionInputType input_value) { - auto left = m_left->run(input_value)->resolve_as_list(input_value); - auto right = m_right->run(input_value)->resolve_as_list(input_value); + auto left_value = m_left->run(input_value); + auto right_value = m_right->run(input_value); + + auto left = left_value->resolve_as_list(input_value); + auto right = right_value->resolve_as_list(input_value); + + if (left_value->is_string() && right_value->is_string()) { + + ASSERT(left.size() == 1); + ASSERT(right.size() == 1); + + StringBuilder builder; + builder.append(left[0]); + builder.append(right[0]); + + return create(builder.to_string()); + } + + // Otherwise, treat them as lists and create a list product. + if (left.is_empty() || right.is_empty()) + return create({}); + + Vector result; + result.ensure_capacity(left.size() * right.size()); StringBuilder builder; - builder.join(" ", left); - builder.join(" ", right); + for (auto& left_element : left) { + for (auto& right_element : right) { + builder.append(left_element); + builder.append(right_element); + result.append(builder.to_string()); + builder.clear(); + } + } - return adopt(*new StringValue(builder.to_string())); + return create(move(result)); } -void StringConcatenate::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata) +void Juxtaposition::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata) { m_left->highlight_in_editor(editor, shell, metadata); @@ -1247,7 +1287,7 @@ void StringConcatenate::highlight_in_editor(Line::Editor& editor, Shell& shell, m_right->highlight_in_editor(editor, shell, metadata); } -HitTestResult StringConcatenate::hit_test_position(size_t offset) +HitTestResult Juxtaposition::hit_test_position(size_t offset) { if (!position().contains(offset)) return {}; @@ -1258,14 +1298,14 @@ HitTestResult StringConcatenate::hit_test_position(size_t offset) return m_right->hit_test_position(offset); } -StringConcatenate::StringConcatenate(Position position, RefPtr left, RefPtr right) +Juxtaposition::Juxtaposition(Position position, RefPtr left, RefPtr right) : Node(move(position)) , m_left(move(left)) , m_right(move(right)) { } -StringConcatenate::~StringConcatenate() +Juxtaposition::~Juxtaposition() { } @@ -1277,7 +1317,7 @@ void StringLiteral::dump(int level) const RefPtr StringLiteral::run(TheExecutionInputType) { - return adopt(*new StringValue(m_text)); + return create(m_text); } void StringLiteral::highlight_in_editor(Line::Editor& editor, Shell&, HighlightMetadata metadata) @@ -1314,7 +1354,7 @@ RefPtr StringPartCompose::run(TheExecutionInputType input_value) builder.join(" ", left); builder.join(" ", right); - return adopt(*new StringValue(builder.to_string())); + return create(builder.to_string()); } void StringPartCompose::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata) @@ -1353,7 +1393,7 @@ void SyntaxError::dump(int level) const RefPtr SyntaxError::run(TheExecutionInputType) { dbg() << "SYNTAX ERROR AAAA"; - return adopt(*new StringValue("")); + return create(""); } void SyntaxError::highlight_in_editor(Line::Editor& editor, Shell&, HighlightMetadata) @@ -1378,7 +1418,7 @@ void Tilde::dump(int level) const RefPtr Tilde::run(TheExecutionInputType) { - return adopt(*new TildeValue(m_username)); + return create(m_username); } void Tilde::highlight_in_editor(Line::Editor& editor, Shell&, HighlightMetadata) @@ -1432,7 +1472,7 @@ RefPtr WriteAppendRedirection::run(TheExecutionInputType input_value) builder.join(" ", path_segments); command.redirections.append(*new PathRedirection(builder.to_string(), m_fd, PathRedirection::WriteAppend)); - return adopt(*new CommandValue(move(command))); + return create(move(command)); } WriteAppendRedirection::WriteAppendRedirection(Position position, int fd, RefPtr path) @@ -1459,7 +1499,7 @@ RefPtr WriteRedirection::run(TheExecutionInputType input_value) builder.join(" ", path_segments); command.redirections.append(*new PathRedirection(builder.to_string(), m_fd, PathRedirection::Write)); - return adopt(*new CommandValue(move(command))); + return create(move(command)); } WriteRedirection::WriteRedirection(Position position, int fd, RefPtr path) @@ -1498,7 +1538,7 @@ RefPtr VariableDeclarations::run(TheExecutionInputType input_value) } } - return adopt(*new ListValue(Vector {})); + return create({}); } void VariableDeclarations::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata) @@ -1546,10 +1586,6 @@ Vector Value::resolve_as_commands(TheExecutionInputType input_valu return { command }; } -ListValue::~ListValue() -{ -} - ListValue::ListValue(Vector values) { m_contained_values.ensure_capacity(values.size()); @@ -1557,6 +1593,10 @@ ListValue::ListValue(Vector values) m_contained_values.append(adopt(*new StringValue(move(str)))); } +ListValue::~ListValue() +{ +} + Vector ListValue::resolve_as_list(TheExecutionInputType input_value) { Vector values; diff --git a/Shell/AST.h b/Shell/AST.h index 1cd7f8e4fa..bfa31dfdbd 100644 --- a/Shell/AST.h +++ b/Shell/AST.h @@ -690,17 +690,17 @@ private: char m_name { -1 }; }; -class StringConcatenate final : public Node { +class Juxtaposition final : public Node { public: - StringConcatenate(Position, RefPtr, RefPtr); - virtual ~StringConcatenate(); + Juxtaposition(Position, RefPtr, RefPtr); + virtual ~Juxtaposition(); private: virtual void dump(int level) const override; virtual RefPtr run(TheExecutionInputType) 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 "StringConcatenate"; } + virtual String class_name() const override { return "Juxtaposition"; } RefPtr m_left; RefPtr m_right; diff --git a/Shell/Parser.cpp b/Shell/Parser.cpp index 2f8cd59afb..caed8a1d61 100644 --- a/Shell/Parser.cpp +++ b/Shell/Parser.cpp @@ -262,8 +262,9 @@ RefPtr Parser::parse_command() if (!list_expr) return nullptr; - auto next_command = parse_command(); auto cast = create(move(list_expr)); // Cast List Command + + auto next_command = parse_command(); if (!next_command) return cast; @@ -396,6 +397,16 @@ RefPtr Parser::parse_expression() auto rule_start = push_start(); auto starting_char = peek(); + auto read_concat = [&](auto expr) -> RefPtr { + if (is_whitespace(peek())) + return expr; + + if (auto next_expr = parse_expression()) + return create(move(expr), move(next_expr)); + + return expr; + }; + if (strchr("&|[]){} ;<>", starting_char) != nullptr) return nullptr; @@ -409,10 +420,10 @@ RefPtr Parser::parse_expression() if (starting_char == '$') { if (auto variable = parse_variable()) - return variable; + return read_concat(variable); if (auto inline_exec = parse_evaluate()) - return inline_exec; + return read_concat(inline_exec); } if (starting_char == '#') @@ -424,11 +435,11 @@ RefPtr Parser::parse_expression() if (!list) list = create(); if (!expect(')')) - return create(); - return create(move(list)); // Cast To List + return read_concat(create()); + return read_concat(create(move(list))); // Cast To List } - return parse_string_composite(); + return read_concat(parse_string_composite()); } RefPtr Parser::parse_string_composite() @@ -436,35 +447,35 @@ RefPtr Parser::parse_string_composite() auto rule_start = push_start(); if (auto string = parse_string()) { if (auto next_part = parse_string_composite()) - return create(move(string), move(next_part)); // Concatenate String StringComposite + return create(move(string), move(next_part)); // Concatenate String StringComposite return string; } if (auto variable = parse_variable()) { if (auto next_part = parse_string_composite()) - return create(move(variable), move(next_part)); // Concatenate Variable StringComposite + return create(move(variable), move(next_part)); // Concatenate Variable StringComposite return variable; } if (auto glob = parse_glob()) { if (auto next_part = parse_string_composite()) - return create(move(glob), move(next_part)); // Concatenate Glob StringComposite + return create(move(glob), move(next_part)); // Concatenate Glob StringComposite return glob; } if (auto bareword = parse_bareword()) { if (auto next_part = parse_string_composite()) - return create(move(bareword), move(next_part)); // Concatenate Bareword StringComposite + return create(move(bareword), move(next_part)); // Concatenate Bareword StringComposite return bareword; } if (auto inline_command = parse_evaluate()) { if (auto next_part = parse_string_composite()) - return create(move(inline_command), move(next_part)); // Concatenate Execute StringComposite + return create(move(inline_command), move(next_part)); // Concatenate Execute StringComposite return inline_command; } diff --git a/Shell/Parser.h b/Shell/Parser.h index 3956f518fc..f27364f9e1 100644 --- a/Shell/Parser.h +++ b/Shell/Parser.h @@ -120,10 +120,10 @@ redirection :: number? '>'{1,2} ' '* string_composite list_expression :: ' '* expression (' '+ list_expression)? -expression :: evaluate - | string_composite - | comment - | '(' list_expression ')' +expression :: evaluate expression? + | string_composite expression? + | comment expession? + | '(' list_expression ')' expression? evaluate :: '$' expression {eval / dynamic resolve} diff --git a/Shell/Shell.cpp b/Shell/Shell.cpp index ac945dd893..5a0d413353 100644 --- a/Shell/Shell.cpp +++ b/Shell/Shell.cpp @@ -324,7 +324,7 @@ int Shell::run_command(const StringView& cmd) if (!command) return 0; -#ifdef SH_DEBUG +#ifndef SH_DEBUG dbg() << "Command follows"; command->dump(0); #endif