File Coverage

blib/lib/Logic/Data.pm
Criterion Covered Total %
statement 137 155 88.3
branch 43 52 82.6
condition 36 57 63.1
subroutine 32 37 86.4
pod 0 1 0.0
total 248 302 82.1


line stmt bran cond sub pod time code
1             package Logic::Data;
2              
3 4     4   2619 use 5.006001;
  4         14  
  4         154  
4              
5 4     4   19 use strict;
  4         6  
  4         109  
6 4     4   21 no warnings;
  4         14  
  4         155  
7              
8 4     4   19 use Exporter;
  4         52  
  4         165  
9 4     4   23 use base 'Exporter';
  4         6  
  4         439  
10              
11 4     4   37 use Perl6::Attributes;
  4         8  
  4         30  
12 4     4   7651 use Scalar::Util qw;
  4         8  
  4         442  
13 4     4   20 use Carp;
  4         7  
  4         269  
14              
15 4     4   20 use Logic::Basic;
  4         6  
  4         93  
16 4     4   19 use Logic::Variable;
  4         7  
  4         1144  
17              
18             our @EXPORT = qw<>;
19             our @EXPORT_OK = qw;
20              
21             sub resolve {
22 150     150 0 278 my ($data, $state, %options) = @_;
23 150 100 66     926 if (blessed($data) && $data->isa('Logic::Variable')) {
    100 33        
    50          
24 68 100       164 if ($data->bound($state)) {
25 67         188 @_ = ($data->binding($state), $state, %options); goto &resolve;
  67         303  
26             }
27             else {
28 1 50       9 if ($options{vars} eq 'string') {
    50          
29 0         0 $data->id;
30             }
31             elsif ($options{vars}) {
32 0         0 $data;
33             }
34             else {
35 1         175 croak "Variadic state, unable to resolve";
36             }
37             }
38             }
39             elsif (ref $data eq 'ARRAY') {
40 9         24 [ map { resolve($_, $state, %options) } @$data ];
  19         43  
41             }
42             elsif (blessed($data) && $data->can('resolve')) {
43 0         0 @_ = ($data, $state, %options); goto &{$data->can('resolve')};
  0         0  
  0         0  
44             }
45             else {
46 73         375 $data;
47             }
48             }
49              
50              
51             package Logic::Data::Unify;
52              
53 4     4   20 use Scalar::Util qw;
  4         10  
  4         187  
54 4     4   18 use Carp;
  4         16  
  4         3500  
55              
56             sub new {
57 218     218   329 my ($class, $a, $b) = @_;
58 218   33     1459 bless {
59             a => $a,
60             b => $b,
61             } => ref $class || $class;
62             }
63              
64             sub create {
65 216     216   235 my ($self) = @_;
66 216         491 $self;
67             }
68              
69             sub enter {
70 216     216   268 my ($self, $stack, $state) = @_;
71              
72             # get information into nice testable forms
73             # lvar: $.a is a variable
74             # left: otherwise, what data is $.a
75            
76             # XXX: refactor this double programming!
77 216         219 my ($lvar, $rvar, $left, $right);
78 216 100 100     927 if (blessed($.a) && $.a->isa('Logic::Variable')) {
79 56 100       167 if ($.a->bound($state)) {
80 13         36 $left = $.a->binding($state);
81 13 100 66     63 if (blessed($left) && $left->isa('Logic::Variable')) {
82 1         4 return $stack->tail_descend(Logic::Data::Unify->new($left, $.b));
83             }
84             }
85             else {
86 43         87 $lvar = 1; $left = $.a;
  43         66  
87             }
88             }
89             else {
90 160         312 $left = $.a;
91             }
92              
93 215 100 100     933 if (blessed($.b) && $.b->isa('Logic::Variable')) {
94 35 100       97 if ($.b->bound($state)) {
95 1         4 $right = $.b->binding($state);
96 1 50 33     6 if (blessed($right) && $right->isa('Logic::Variable')) {
97 0         0 return $stack->tail_descend(Logic::Data::Unify->new($.a, $right));
98             }
99             }
100             else {
101 34         37 $rvar = 1; $right = $.b;
  34         49  
102             }
103             }
104             else {
105 180         222 $right = $.b;
106             }
107            
108 215         549 $state->save;
109            
110 215 100 100     761 if ($lvar && $rvar) { # variable-variable
    100          
    100          
111 2         9 my $intermediate = Logic::Variable->new;
112 2         9 $left->bind($state, $intermediate);
113 2         8 $right->bind($state, $intermediate);
114 2         7 1;
115             }
116             elsif ($lvar) { # variable-data
117 41         117 $left->bind($state, $right);
118 41         169 1;
119             }
120             elsif ($rvar) { # data-variable
121 32         77 $right->bind($state, $left);
122 32         125 1;
123             }
124             else { # data-data
125 140         425 @_ = ($self, $stack, $state, $left, $right);
126 140         168 goto &{$self->can('unify_data_data')};
  140         520  
127             }
128             }
129              
130             sub unify_data_data {
131 140     140   224 my ($self, $stack, $state, $left, $right) = @_;
132            
133 140 100 100     740 unless (ref $left || ref $right) {
    100 100        
134 31 100       154 $left eq $right ? 1 : undef;
135             }
136             elsif (ref $left eq 'ARRAY' && ref $right eq 'ARRAY') {
137 97 100 100     440 if (!@$left && !@$right) {
    100 100        
138 41         157 1;
139             }
140             elsif (@$left && @$right) {
141 42         104 my $head = Logic::Data::Unify->new($left->[0], $right->[0]);
142 42         187 my $tail = Logic::Data::Unify->new([ @$left[1..$#$left] ], [ @$right[1..$#$right] ]);
143 42         138 $stack->descend($head, $tail);
144             }
145             else {
146 14         59 undef; # a null list is not equal to a non-null list
147             }
148             }
149             else {
150 12 100 66     121 if (blessed($left) && $left->can('unify')) {
    50 33        
151 1         2 @_ = ($left, $right, $stack, $state); goto &{$left->can('unify')};
  1         2  
  1         5  
152             }
153             elsif (blessed($right) && $right->can('unify')) {
154 11         31 @_ = ($right, $left, $stack, $state); goto &{$right->can('unify')};
  11         17  
  11         45  
155             }
156             else {
157 0         0 $left == $right; # referentially equal (or overloadedly equal)
158             }
159             }
160             }
161              
162 31     31   71 sub backtrack { }
163              
164             sub cleanup {
165 82     82   143 my ($self, $stack, $state) = @_;
166 82         194 $state->restore;
167             }
168              
169              
170             package Logic::Data::Cons;
171              
172 4     4   23 use Scalar::Util qw;
  4         5  
  4         3896  
173              
174             sub new {
175 10     10   17 my ($class, $head, $tail) = @_;
176 10   33     94 bless {
177             head => $head,
178             tail => $tail,
179             } => ref $class || $class;
180             }
181              
182             sub head {
183 8     8   11 my ($self) = @_;
184 8         41 $.head;
185             }
186              
187             sub tail {
188 8     8   11 my ($self) = @_;
189 8         40 $.tail;
190             }
191              
192             sub resolve {
193 0     0   0 my ($self, $state, %options) = @_;
194 0         0 my $head = Logic::Data::resolve($.head, $state, %options);
195 0         0 my $tail = Logic::Data::resolve($.tail, $state, %options);
196 0 0       0 if (ref $tail eq 'ARRAY') {
197 0         0 [ $head, @$tail ];
198             }
199             else {
200 0         0 $self->new($head, $tail);
201             }
202             }
203              
204             sub unify {
205 10     10   14 my ($self, $other, $stack, $state) = @_;
206 10 50 33     70 if (blessed($other) && $other->isa('Logic::Data::Cons')) {
    50          
207 0         0 $stack->descend(
208             Logic::Data::Unify->new($self->head, $other->head),
209             Logic::Data::Unify->new($self->tail, $other->tail),
210             );
211             }
212             elsif (ref $other eq 'ARRAY') {
213 10 100       31 if (@$other) {
214 8         23 $stack->descend(
215             Logic::Data::Unify->new($self->head, $other->[0]),
216             Logic::Data::Unify->new($self->tail, [ @$other[1..$#$other] ]),
217             );
218             }
219             }
220             }
221              
222              
223             package Logic::Data::Assign;
224              
225             sub new {
226 2     2   16 my ($class, $code, @vars) = @_;
227 2   33     33 bless {
228             vars => \@vars,
229             code => $code,
230             } => ref $class || $class;
231             }
232              
233             sub create {
234 2     2   4 my ($self) = @_;
235 2         4 $self;
236             }
237              
238             sub enter {
239 2     2   3 my ($self, $stack, $state) = @_;
240 2         4 my (@vals) = &.code();
  2         6  
241 2         12 splice @vals, scalar @.vars;
  2         5  
242 3         10 $stack->descend(
243 2         5 map { Logic::Data::Unify->new($.vars[$_], $vals[$_]) } 0..@.vars-1
  2         6  
244             )
245             }
246              
247 0     0   0 sub backtrack { }
248 0     0   0 sub cleanup { }
249              
250              
251             package Logic::Data::For;
252              
253             sub new {
254 4     4   13 my ($class, $var, @values) = @_;
255 4   33     52 bless {
256             var => $var,
257             values => \@values,
258             } => ref $class || $class;
259             }
260              
261             sub create {
262 4     4   8 my ($self) = @_;
263 4         10 $self;
264             }
265              
266             sub enter {
267 4     4   18 my ($self, $stack, $state) = @_;
268 26         63 $stack->tail_descend(
269             Logic::Basic::Alternation->new(
270 4         8 map { Logic::Data::Unify->new($.var, $_) } @.values
  4         10  
271             ),
272             );
273             }
274              
275 0     0   0 sub backtrack { }
276 0     0   0 sub cleanup { }
277              
278              
279             package Logic::Data::Disjunction;
280              
281             sub new {
282 2     2   364 my ($class, @values) = @_;
283 2   33     15 bless {
284             values => \@values,
285             } => ref $class || $class;
286             }
287              
288             sub unify {
289 2     2   4 my ($self, $other, $stack, $state) = @_;
290 2         11 $stack->tail_descend(
291 2         3 Logic::Data::For->new($other, @.values),
292             );
293             }
294              
295             1;