File Coverage

blib/lib/Logic/Stack.pm
Criterion Covered Total %
statement 119 140 85.0
branch 17 22 77.2
condition 8 18 44.4
subroutine 29 31 93.5
pod 0 14 0.0
total 173 225 76.8


line stmt bran cond sub pod time code
1             package Logic::Stack;
2              
3 7     7   114737 use 5.006001;
  7         37  
  7         339  
4              
5 7     7   36 use strict;
  7         15  
  7         268  
6 7     7   34 no warnings;
  7         16  
  7         231  
7              
8 7     7   41 use Carp;
  7         12  
  7         727  
9 7     7   5923 use Perl6::Attributes;
  7         238163  
  7         50  
10 7     7   16479 use Logic::Variable;
  7         20  
  7         13969  
11              
12             sub new {
13 107     107 0 236 my ($class, @init) = @_;
14 107   33     362 bless {
15             state => Logic::Variable::Pad->new,
16             run => [ ],
17             cur => {
18             up => undef,
19             back => undef,
20             ptr => 0,
21             gen => \@init,
22             },
23             } => ref $class || $class;
24             }
25              
26             sub state {
27 64     64 0 84 my ($self) = @_;
28 64         211 $.state;
29             }
30              
31             sub descend {
32 89     89 0 150 my ($self, @gen) = @_;
33 89         380 $.cur = {
34             up => $.cur,
35             back => $.cur,
36             ptr => 0,
37             gen => \@gen
38             };
39 89         330 1;
40             }
41              
42             # criteria for when you can replace a descend with a tail_descend:
43             # you're only descending into one thing
44             # your backtrack does nothing (or will do nothing after this time)
45             # your cleanup does nothing
46             sub tail_descend {
47 15     15 0 29 my ($self, @gen) = @_; #only single gen allowed
48 15 50       35 croak "Only one gen allowed on tail_descend" unless @gen == 1;
49 15         71 my $new = $gen[0]->create($self, $.state);
50 15 50       58 if ($new) {
51 15         16 pop @.run;
  15         27  
52 15         22 push @.run, $new;
  15         26  
53 15         67 $.run[-1]->enter($self, $.state);
54             }
55             else {
56 0         0 undef;
57             }
58             }
59              
60             sub print_stack {
61 0     0 0 0 my ($self) = @_;
62 0         0 print STDERR "-----\nSTACK:\n";
63 0         0 my $cptr = $.cur;
64 0         0 while ($cptr) {
65 0         0 print STDERR " PTR: $cptr->{ptr}; FRAME: (@{$cptr->{gen}})\n";
  0         0  
66 0         0 $cptr = $cptr->{up};
67             }
68 0         0 print STDERR "RUN:\n";
69 0         0 for (reverse @.run) {
  0         0  
70 0         0 print " $_\n";
71             }
72 0         0 print STDERR "-----\n";
73             }
74              
75             sub advance {
76 627     627 0 690 my ($self) = @_;
77 627 100       1360 return unless $.cur;
78 622 100       822 if ($.cur{ptr} < @{$.cur{gen}}) {
  622         1379  
79 449         1522 my $next = $.cur{gen}[$.cur{ptr}++]->create($self, $.state);
80 449 50       953 if ($next) {
81 449         422 push @.run, $next;
  449         699  
82 449         484 goto &{$self->can('enter')};
  449         1562  
83             }
84             else {
85 0         0 goto &{$self->can('backup')};
  0         0  
86             }
87             }
88             else {
89 173 100       311 if ($.cur{up}) {
90 86         378 $.cur = {
91             up => $.cur{up}{up},
92             back => $.cur,
93             ptr => $.cur{up}{ptr},
94             gen => $.cur{up}{gen},
95             };
96 86         102 goto &{$self->can('advance')};
  86         278  
97             }
98             else {
99 87         229 return;
100             }
101             }
102             }
103              
104             sub enter {
105 449     449 0 542 my ($self) = @_;
106 449 100       1331 if ($.run[-1]->enter($self, $.state)) {
107 264         1061 return 1;
108             }
109             else {
110 185         509 goto &{$self->can('backup')};
  185         558  
111             }
112             }
113              
114             sub backup {
115 308     308 0 344 my ($self) = @_;
116            
117 308         499 $self->backup_gen;
118 308         305 (pop @.run)->cleanup($self, $.state);
  308         946  
119 308 100       394 return unless @.run;
  308         709  
120            
121 254 100       702 if ($.run[-1]->backtrack($self, $.state)) {
122 154         629 return 1;
123             }
124             else {
125 100         123 goto &{$self->can('backup')};
  100         294  
126             }
127             }
128              
129             sub backup_gen {
130 319     319 0 299 my ($self) = @_;
131 319 50       628 return unless $.cur;
132 319 50       562 if ($.cur{ptr}) {
133 319         366 $.cur{ptr}--;
134 319         356 my $ret = $.cur;
135 319   100     1335 until (!$.cur || $.cur{ptr}) {
136 98         405 $.cur = $.cur{back};
137             }
138 319         521 return $ret;
139             }
140             else {
141 0         0 $.cur = $.cur{back};
142 0         0 goto &{$self->can('backup_gen')};
  0         0  
143             }
144             }
145              
146             sub failto {
147 0     0 0 0 my ($self, $mark) = @_;
148 0   0     0 $self->backtrack until !@.run || $.run[-1] == $mark;
  0         0  
149             }
150              
151             sub run {
152 130     130 0 1534 my ($self) = @_;
153 130         271 while ($self->advance) { }
154 130         207 scalar @.run; # if there's nothing on the stack, we fail
  130         550  
155             }
156              
157             sub backtrack {
158 23     23 0 35 my ($self) = @_;
159 23         52 $self->backup;
160 23         29 goto &{$self->can('run')};
  23         87  
161             }
162              
163             sub snip {
164 11     11 0 11 my ($self) = @_;
165 11         11 my $run = pop @.run;
  11         23  
166 11         37 my $top = $self->backup_gen;
167 11         13 my ($gen) = splice @{$top->{gen}}, $top->{ptr}, 1;
  11         24  
168 11         32 ($run, $gen);
169             }
170              
171             sub cut {
172 10     10 0 12 my ($self, $mark) = @_;
173 10         17 my ($cutter_run, $cutter_gen) = $self->snip;
174 10   66     12 until (!@.run || $.run[-1] == $mark) {
  11         55  
175 1         3 $self->snip;
176             }
177 10         11 splice @{$.cur{gen}}, $.cur{ptr}, 0, $cutter_gen;
  10         23  
178 10         10 push @.run, $cutter_run;
  10         15  
179 10         12 $.cur{ptr}++;
180 10         19 $.state->commit($mark->revision);
181 10         37 1;
182             }
183              
184             package Logic::Stack::Mark;
185              
186             sub new {
187 1     1   13 my ($class) = @_;
188 1   33     11 bless {
189             rev => undef,
190             } => ref $class || $class;
191             }
192              
193             sub revision {
194 10     10   10 my ($self) = @_;
195 10         40 $.rev;
196             }
197              
198             sub create {
199 10     10   11 my ($self) = @_;
200 10         21 $self;
201             }
202              
203             sub enter {
204 10     10   11 my ($self, $stack, $state) = @_;
205 10         21 $.rev = $state->save;
206 10         22 1;
207             }
208              
209 10     10   41 sub backtrack { }
210             sub cleanup {
211 10     10   11 my ($self, $stack, $state) = @_;
212 10         22 $state->restore;
213             }
214              
215             package Logic::Stack::Cut;
216              
217             sub new {
218 1     1   40 my ($class, $mark) = @_;
219 1   33     18 bless {
220             mark => $mark,
221             } => ref $class || $class;
222             }
223              
224             sub create {
225 10     10   11 my ($self) = @_;
226 10         13 $self;
227             }
228              
229             sub enter {
230 10     10   12 my ($self, $stack, $state) = @_;
231 10         20 $stack->cut($.mark);
232             }
233              
234 10     10   16 sub backtrack { } # the cut already did it for us
235 10     10   10 sub cleanup { }
236              
237             1;