Jelajahi Sumber

Shell: Implement `for_each_entry()` for syntactic list nodes

This allows correct iteration over nested lists.
Also store values to variables without resolving them, to delay the
resolution step as much as possible (this helps with storing nested
lists in variables).
AnotherTest 4 tahun lalu
induk
melakukan
1c78d12f1c
2 mengubah file dengan 24 tambahan dan 10 penghapusan
  1. 22 10
      Userland/Shell/AST.cpp
  2. 2 0
      Userland/Shell/AST.h

+ 22 - 10
Userland/Shell/AST.cpp

@@ -390,6 +390,17 @@ RefPtr<Value> ListConcatenate::run(RefPtr<Shell> shell)
     return result;
 }
 
+void ListConcatenate::for_each_entry(RefPtr<Shell> shell, Function<IterationDecision(NonnullRefPtr<Value>)> callback)
+{
+    for (auto& entry : m_list) {
+        auto value = entry->run(shell);
+        if (!value)
+            continue;
+        if (callback(value.release_nonnull()) == IterationDecision::Break)
+            break;
+    }
+}
+
 void ListConcatenate::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata)
 {
     auto first = metadata.is_first_in_list;
@@ -694,6 +705,12 @@ RefPtr<Value> CastToList::run(RefPtr<Shell> shell)
     return create<ListValue>(cast_values);
 }
 
+void CastToList::for_each_entry(RefPtr<Shell> shell, Function<IterationDecision(NonnullRefPtr<Value>)> callback)
+{
+    if (m_inner)
+        m_inner->for_each_entry(shell, move(callback));
+}
+
 void CastToList::highlight_in_editor(Line::Editor& editor, Shell& shell, HighlightMetadata metadata)
 {
     if (m_inner)
@@ -1046,7 +1063,7 @@ FunctionDeclaration::~FunctionDeclaration()
 void ForLoop::dump(int level) const
 {
     Node::dump(level);
-    print_indented(String::format("%s in\n", m_variable_name.characters()), level + 1);
+    print_indented(String::format("%s in", m_variable_name.characters()), level + 1);
     if (m_iterated_expression)
         m_iterated_expression->dump(level + 2);
     else
@@ -1075,6 +1092,9 @@ RefPtr<Value> ForLoop::run(RefPtr<Shell> shell)
             return IterationDecision::Continue;
         }
 
+        if (!shell->has_error(Shell::ShellError::None))
+            return IterationDecision::Break;
+
         if (block_value->is_job()) {
             auto job = static_cast<JobValue*>(block_value.ptr())->job();
             if (!job || job->is_running_in_background())
@@ -2855,15 +2875,7 @@ RefPtr<Value> VariableDeclarations::run(RefPtr<Shell> shell)
         ASSERT(name_value.size() == 1);
         auto name = name_value[0];
         auto value = var.value->run(shell);
-        if (value->is_list()) {
-            auto parts = value->resolve_as_list(shell);
-            shell->set_local_variable(name, adopt(*new ListValue(move(parts))));
-        } else if (value->is_command()) {
-            shell->set_local_variable(name, value);
-        } else {
-            auto part = value->resolve_as_list(shell);
-            shell->set_local_variable(name, adopt(*new StringValue(part[0])));
-        }
+        shell->set_local_variable(name, value.release_nonnull());
     }
 
     return create<ListValue>({});

+ 2 - 0
Userland/Shell/AST.h

@@ -558,6 +558,7 @@ public:
 private:
     NODE(ListConcatenate);
     virtual void dump(int level) const override;
+    virtual void for_each_entry(RefPtr<Shell> shell, Function<IterationDecision(NonnullRefPtr<Value>)> callback) override;
     virtual RefPtr<Value> run(RefPtr<Shell>) override;
     virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override;
     virtual HitTestResult hit_test_position(size_t) override;
@@ -656,6 +657,7 @@ private:
     NODE(CastToList);
     virtual void dump(int level) const override;
     virtual RefPtr<Value> run(RefPtr<Shell>) override;
+    virtual void for_each_entry(RefPtr<Shell> shell, Function<IterationDecision(NonnullRefPtr<Value>)> callback) override;
     virtual void highlight_in_editor(Line::Editor&, Shell&, HighlightMetadata = {}) override;
     virtual HitTestResult hit_test_position(size_t) override;
     virtual bool is_list() const override { return true; }