pervasive type constraints
[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 DX::Utils qw(expand_deps);
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   expand_deps($self->current_resolution->veracity_depends_on);
32 });
33
34 has alternative_step => (is => 'lazy', init_arg => undef, builder => sub {
35   my ($self) = @_;
36   my $rspace = $self->resolution_space->remaining_resolution_space;
37   return undef unless @{$rspace->members};
38   return $rspace->next_step;
39 });
40
41 sub but_first {
42   my ($self, @actions) = @_;
43   $self->but(actions => [ @actions, @{$self->actions} ]);
44 }
45
46 sub but_with_dependencies_on {
47   my ($self, @deps) = @_;
48   $self->but(depends_on => [ @{$self->depends_on}, @deps ]);
49 }
50
51 sub apply_to {
52   my ($self, $old_ss) = @_;
53   trace resolve => [ statement => [
54     [ symbol => 'resolve' ],
55     [ block => [
56       [ statement => [
57         [ symbol => 'proposition' ],
58         @{$self->resolves->for_deparse->[1]},
59       ] ],
60       (@{$self->actions}
61         ? [ statement => [
62             [ symbol => 'actions' ],
63             [ block => $self->actions ],
64           ] ]
65         : ()),
66       [ statement => [
67         [ symbol => 'depends_on' ],
68         [ block => [
69           map [ statement => [
70             [ symbol => (split '::', ${$_->[0]})[-1] ],
71             [ value_path => [ @{$_}[1..$#$_] ] ]
72           ] ], @{$self->depends_on}
73         ] ],
74       ] ],
75     ] ]
76   ] ];
77   my $ns = do {
78     if (my $prop = $old_ss->next_proposition) {
79       DX::Step::ConsiderProposition->new(
80         proposition => $prop
81       )
82     } else {
83       $old_ss->on_solution_step
84     }
85   };
86   my $ss = $old_ss->but(
87     next_step => $ns,
88     decisions_taken => [
89       [ $self->resolution_space, $old_ss ],
90       @{$old_ss->decisions_taken}
91     ],
92   );
93   my $new_ss = $self->_apply_to_ss($ss);
94   return $ss->but(next_step => DX::Step::Backtrack->new) unless $new_ss;
95   return $new_ss;
96 }
97
98 sub _apply_to_ss {
99   my ($self, $old_ss) = @_;
100   my $old_hyp = $old_ss->current_hypothesis;
101   (my $hyp, my @recheck) = $old_hyp->with_resolution(
102     $self->resolves, $self->depends_on, $self->actions
103   );
104   return undef unless $hyp;
105   return $self->_recheck_for(
106     $old_ss->but(current_hypothesis => $hyp),
107     @recheck
108   );
109 }
110
111 sub _recheck_for {
112   my ($self, $old_ss, @recheck) = @_;
113
114   return $old_ss unless @recheck;
115
116   my $ss = $old_ss->but(
117     next_step => DX::Step::EnterRecheck->new(
118       proposition_list => \@recheck,
119       on_completion_step => $old_ss->next_step,
120       on_failure_step => DX::Step::Backtrack->new,
121     ),
122   );
123
124   return $ss;
125 }
126
127 1;