--- /dev/null
+package DX::Op::FindAll;
+
+use DX::Op::FromCode;
+use DX::Op::FindAllCollect;
+use DX::Var;
+use DX::OrderedSet;
+use DX::Op::Return;
+use Moo;
+
+with 'DX::Role::Op';
+
+has coll_name => (is => 'ro', required => 1);
+
+has var_name => (is => 'ro', required => 1);
+
+has body => (is => 'ro', required => 1);
+
+sub run {
+ my ($self, $state) = @_;
+ my $values = [];
+ my $coll = DX::OrderedSet->new(values => $values);
+ my $collect = DX::Op::FindAllCollect->new(
+ var_name => $self->var_name,
+ into => $values
+ );
+ my $do_body = DX::Op::FromCode->new(
+ code => sub {
+ my ($self, $state) = @_;
+ $state->push_return_then($collect, $self->next);
+ },
+ next => $self->body
+ );
+ my $var = DX::Var->new(id => "rule:findall")
+ ->with_stream(DX::ArrayStream->from_array(
+ $do_body, DX::Op::Return->new
+ ));
+ my $invoke = DX::Op::FromCode->new(
+ code => sub {
+ my ($self, $state) = @_;
+ my $op = $state->resolve_value($var);
+ $state->then($op);
+ }
+ );
+ my $coll_name = $self->coll_name;
+ my $ret = DX::Op::SetScope->new(
+ scope => $state->scope,
+ next => DX::Op::FromCode->new(
+ code => sub {
+ my ($self, $state) = @_;
+ $state->bind_value($state->scope->{$coll_name} => $coll)
+ ->then($self->next);
+ },
+ next => $self->next
+ )
+ );
+ $state->assign_vars($self->var_name => {})
+ ->push_return_then($ret, $invoke)->mark_choice($var);
+}
+
+1;
--- /dev/null
+package DX::Op::FindAllCollect;
+
+use Moo;
+
+with 'DX::Role::Op';
+
+has var_name => (is => 'ro', required => 1);
+
+has into => (is => 'ro', required => 1);
+
+sub run {
+ my ($self, $state) = @_;
+ my $current = $state->resolve_value($state->scope_var($self->var_name));
+ push @{$self->into}, $current;
+ return $state->backtrack;
+}
+
+1;
sub to_stream { DX::ArrayStream->from_array($_[0]->all) }
+sub key_list { 0..$#{$_[0]->values} }
+
+sub get { $_[0]->values->[$_[1]] }
+
1;
use DX::Op::Exists;
use DX::Op::Predicate;
use DX::Op::HasAction;
+use DX::Op::FindAll;
use List::Util qw(reduce);
has rules => (is => 'ro', default => sub { {} });
);
}
+sub _expand_op_findall {
+ my ($self, $coll_name, $var_name, @contents) = @_;
+ my $findall = DX::Op::FindAll->new(
+ coll_name => $coll_name,
+ var_name => $var_name,
+ body => $self->expand_and_link(DX::Op::Return->new, @contents),
+ );
+}
+
sub _expand_op_member_of {
my ($self, $member_var, $coll_var) = @_;
DX::Op::MemberOf->new(
--- /dev/null
+use strictures 1;
+use Test::More;
+use DX::Solver;
+use DX::OrderedSet;
+
+my $solver = DX::Solver->new;
+
+{ package My::Server;
+ use Moo;
+ has name => (is => 'ro', required => 1);
+
+ package My::ShellInstalled;
+ use Moo;
+ has server => (is => 'ro', required => 1);
+ has shell => (is => 'ro', required => 1);
+}
+
+$solver->facts->{server} = DX::OrderedSet->new(
+ values => [ map My::Server->new(name => $_), qw(one two three four five) ]
+);
+$solver->facts->{shell_installed} = DX::OrderedSet->new(
+ values => [
+ (map My::ShellInstalled->new(server => $_, shell => 'bash'),
+ qw(one three four)),
+ (map My::ShellInstalled->new(server => $_, shell => 'csh'),
+ qw(two three five)),
+ ],
+);
+
+my @r = $solver->query([ 'X' ],
+ [ findall => X => S =>
+ [ member_of => 'S', \'server' ],
+ [ exists => [ qw(Name SI) ] =>
+ [ member_of => 'SI', \'shell_installed' ],
+ [ prop => 'SI' => \'server' => 'Name' ],
+ [ prop => 'S' => \'name' => 'Name' ],
+ [ prop => 'SI' => \'shell' => \'bash' ]
+ ],
+ ]
+)->results;
+
+is_deeply(
+ [ map $_->name, $r[0]->value_for('X')->all ],
+ [ qw(one three four) ]
+);
+
+done_testing;