better action debug info
[scpubgit/DKit.git] / lib / DX / Solver.pm
1 package DX::Solver;
2
3 use DX::RuleSet;
4 use DX::State;
5 use DX::ResultStream;
6 use List::Util qw(reduce);
7 use Moo;
8
9 has rule_set => (
10   is => 'lazy',
11   handles => [ qw(add_predicate add_rule) ],
12   builder => sub {
13     DX::RuleSet->new
14   },
15 );
16
17 has facts => (is => 'ro', default => sub { {} });
18
19 has observation_policy => (is => 'rw');
20
21 has action_callback => (is => 'rw');
22
23 sub query {
24   my ($self, @terms) = @_;
25   $self->_solve({ allow_actions => 0 }, @terms);
26 }
27
28 sub solve {
29   my ($self, @terms) = @_;
30   $self->_solve({ allow_actions => 1 }, @terms);
31 }
32
33 sub ensure {
34   my ($self, @terms) = @_;
35   my $rs = $self->_solve({ allow_actions => 1 }, @terms);
36   my $r = $rs->next;
37   while ($r and $r->actions and my @ind = $r->independent_actions) {
38     $self->run_action($_) for @ind;
39     $rs = $self->_solve({ allow_actions => 1 }, @terms);
40     $r = $rs->next;
41   }
42   return $r;
43 }
44
45 sub _solve {
46   my ($self, $attrs, @terms) = @_;
47   my $rule_set = $self->rule_set;
48   my $head = $rule_set->expand_and_link(undef, @terms, [ 'materialize' ]);
49   my $state = DX::State->new(
50     next_op => $head,
51     return_stack => [],
52     by_id => {},
53     scope => {},
54     last_choice => [],
55     facts => $self->facts,
56     rule_set => $rule_set,
57     %$attrs
58   );
59   return DX::ResultStream->new(
60     for_state => $state,
61     ($self->observation_policy
62       ? (observation_policy => $self->observation_policy)
63       : ()),
64   );
65 }
66
67 sub run_action {
68   my ($self, $action) = @_;
69   if (my $cb = $self->action_callback) {
70     $cb->($action);
71   }
72   my @invalidate = $action->run;
73   while (my ($type, $value) = splice @invalidate, 0, 2) {
74     $self->facts->{$type}->remove_value($value);
75   }
76 }
77
78 1;