add depends_on info to resolve trace
[scpubgit/DX.git] / lib / DX / Step / ResolveProposition.pm
1 package DX::Step::ResolveProposition;
2
3 use DX::Step::EnterRecheck;
4 use DX::Step::Backtrack;
5
6 use Types::Standard qw(ArrayRef);
7 use DX::Utils qw(deparse step);
8
9 use DX::Class;
10
11 with 'DX::Role::Step';
12
13 has resolves => (is => 'ro', isa => Proposition);
14
15 has resolution_space => (is => 'ro', isa => ResolutionSpace);
16
17 has current_resolution => (is => 'lazy', init_arg => undef, builder => sub {
18   my ($self) = @_;
19   $self->resolution_space->next_resolution;
20 });
21
22 has actions => (is => 'lazy', init_arg => undef, builder => sub {
23   my ($self) = @_;
24   $self->current_resolution->actions;
25 });
26
27 has depends_on => (is => 'lazy', init_arg => undef, builder => sub {
28   my ($self) = @_;
29   my $_expand_dep = sub {
30     my ($type, @path) = @{$_[0]};
31     my @expanded = map {
32       ref() ? @{$_->value_path or return ()} : $_
33     } @path;
34     return [ $type, @expanded ];
35   };
36   [ map $_expand_dep->($_),
37       @{$self->current_resolution->veracity_depends_on} ];
38 });
39
40 has alternative_step => (is => 'lazy', init_arg => undef, builder => sub {
41   my ($self) = @_;
42   my $rspace = $self->resolution_space->remaining_resolution_space;
43   return undef unless @{$rspace->members};
44   return step(
45     resolves => $self->resolves,
46     resolution_space => $rspace
47   );
48 });
49
50 sub but_first {
51   my ($self, @actions) = @_;
52   $self->but(actions => [ @actions, @{$self->actions} ]);
53 }
54
55 sub but_with_dependencies_on {
56   my ($self, @deps) = @_;
57   $self->but(depends_on => [ @{$self->depends_on}, @deps ]);
58 }
59
60 sub apply_to {
61   my ($self, $old_ss) = @_;
62   trace resolve => [ statement => [
63     [ symbol => 'resolve' ],
64     [ block => [
65       [ statement => [
66         [ symbol => 'proposition' ],
67         @{$self->resolves->for_deparse->[1]},
68       ] ],
69       (@{$self->actions}
70         ? [ statement => [
71             [ symbol => 'actions' ],
72             [ block => [ @{$self->actions} ] ],
73           ] ]
74         : ()),
75       [ statement => [
76         [ symbol => 'depends_on' ],
77         [ pairs => [
78           map [
79             (split '::', ${$_->[0]})[-1],
80             [ value_path => [ @{$_}[1..$#$_] ] ]
81           ], @{$self->depends_on}
82         ] ],
83       ] ],
84     ] ]
85   ] ];
86   my $ns = do {
87     if (my $prop = $old_ss->next_proposition) {
88       DX::Step::ConsiderProposition->new(
89         proposition => $prop
90       )
91     } else {
92       $old_ss->on_solution_step
93     }
94   };
95   my $ss = $old_ss->but(
96     next_step => $ns,
97     (@{$self->actions}
98       ? (adjustments_made => [
99           [ $self, $old_ss ],
100           @{$old_ss->adjustments_made}
101         ])
102       : ()
103     ),
104   );
105   my $new_ss = $self->_apply_to_ss($ss);
106   return $ss->but(next_step => DX::Step::Backtrack->new) unless $new_ss;
107   return $new_ss;
108 }
109
110 sub _apply_to_ss {
111   my ($self, $old_ss) = @_;
112   my $old_hyp = $old_ss->current_hypothesis;
113   (my $hyp, my @recheck) = $old_hyp->with_resolution(
114     $self->resolves, $self->depends_on, $self->actions
115   );
116   return undef unless $hyp;
117   return $self->_recheck_for(
118     $old_ss->but(current_hypothesis => $hyp),
119     @recheck
120   );
121 }
122
123 sub _recheck_for {
124   my ($self, $old_ss, @recheck) = @_;
125
126   return $old_ss unless @recheck;
127
128   my $ss = $old_ss->but(
129     next_step => DX::Step::EnterRecheck->new(
130       proposition_list => \@recheck,
131       on_completion_step => $old_ss->next_step,
132       on_failure_step => DX::Step::Backtrack->new,
133     ),
134   );
135
136   return $ss;
137 }
138
139 1;