use DX::Op::Return;
use Moo;
-with 'DX::Role::Op';
+with 'DX::Role::Op::FindIsh';
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(
+sub make_result_handler {
+ my ($self, $coll) = @_;
+ my $coll_name = $self->coll_name;
+ DX::Op::FromCode->new(
code => sub {
my ($self, $state) = @_;
- $state->push_return_then($collect, $self->next);
+ $state->bind_value($state->scope->{$coll_name} => $coll)
+ ->then($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
- )
+ next => $self->next
);
- $state->assign_vars($self->var_name => {})
- ->push_return_then($ret, $invoke)->mark_choice($var);
}
1;
--- /dev/null
+package DX::Op::ForEach;
+
+use DX::Op::OneEach;
+use Moo;
+
+with 'DX::Role::Op::FindIsh';
+
+has each_body => (is => 'ro', required => 1);
+
+sub make_result_handler {
+ my ($self, $coll) = @_;
+ DX::Op::OneEach->new(
+ var_name => $self->var_name,
+ each_of => $coll->values,
+ each_body => $self->each_body,
+ next => $self->next
+ );
+}
+
+1;
--- /dev/null
+package DX::Op::OneEach;
+
+use DX::Op::FromCode;
+use Moo;
+
+with 'DX::Role::Op';
+
+has var_name => (is => 'ro', required => 1);
+
+has each_of => (is => 'ro', required => 1);
+
+has each_body => (is => 'ro', required => 1);
+
+sub run {
+ my ($self, $state) = @_;
+ my ($this, @rest) = @{$self->each_of};
+ my $next_op = (@rest ? $self->but(each_of => \@rest) : $self->next);
+ my $ret_op = DX::Op::SetScope->new(
+ scope => $state->scope, next => $next_op
+ );
+ $state->assign_vars($self->var_name => { bound_value => $this })
+ ->push_return_then($ret_op, $self->each_body);
+}
+
+1;
--- /dev/null
+package DX::Role::Op::FindIsh;
+
+use DX::Op::FromCode;
+use DX::Op::FindAllCollect;
+use DX::Var;
+use DX::OrderedSet;
+use DX::Op::Return;
+use Moo::Role;
+
+with 'DX::Role::Op';
+
+has var_name => (is => 'ro', required => 1);
+
+has body => (is => 'ro', required => 1);
+
+requires 'make_result_handler';
+
+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 $ret = DX::Op::SetScope->new(
+ scope => $state->scope,
+ next => $self->make_result_handler($coll),
+ );
+ $state->assign_vars($self->var_name => {})
+ ->push_return_then($ret, $invoke)->mark_choice($var);
+}
+
+1;
use DX::Op::Predicate;
use DX::Op::HasAction;
use DX::Op::FindAll;
+use DX::Op::ForEach;
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(
+ 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_foreach {
+ my ($self, $var_name, $body, $each_body) = @_;
+ DX::Op::ForEach->new(
+ var_name => $var_name,
+ body => $self->expand_and_link(DX::Op::Return->new, @$body),
+ each_body => $self->expand_and_link(DX::Op::Return->new, @$each_body),
+ );
+}
+
sub _expand_op_member_of {
my ($self, $member_var, $coll_var) = @_;
DX::Op::MemberOf->new(
],
);
+$solver->add_rule(has_shell => [ 'Srv', 'Shell' ],
+ [ exists => [ qw(Name SI) ] =>
+ [ member_of => 'SI', \'shell_installed' ],
+ [ prop => 'SI' => \'server' => 'Name' ],
+ [ prop => 'Srv' => \'name' => 'Name' ],
+ [ prop => 'SI' => \'shell' => 'Shell' ]
+ ],
+);
+
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' ]
- ],
+ [ has_shell => 'S', \'bash' ],
]
)->results;
[ qw(one three four) ]
);
+@r = $solver->query([],
+ [ foreach => S => [ [ member_of => 'S', \'server' ] ],
+ [ [ has_shell => 'S' => \'bash' ] ] ]
+)->results;
+
+ok(!@r, 'No results for only bash');
+
+$solver->add_rule(has_any_shell => [ 'S' ] => [ has_shell => 'S' => \'bash' ]);
+$solver->add_rule(has_any_shell => [ 'S' ] => [ has_shell => 'S' => \'csh' ]);
+
+@r = $solver->query([],
+ [ foreach => S => [ [ member_of => 'S', \'server' ] ],
+ [ [ has_any_shell => 'S' ] ] ]
+)->results;
+
+# only three matches both legs of has_any_shell
+
+is(scalar(@r), 2, 'Two solutions for any shell');
+
done_testing;