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