File Coverage

blib/lib/MarpaX/xPathLike.pm
Criterion Covered Total %
statement 51 402 12.6
branch 0 122 0.0
condition 0 83 0.0
subroutine 17 64 26.5
pod 3 3 100.0
total 71 674 10.5


line stmt bran cond sub pod time code
1             package MarpaX::xPathLike;
2 1     1   40371 use utf8;
  1         14  
  1         5  
3 1     1   49 use warnings qw(FATAL utf8); # Fatalize encoding glitches.
  1         3  
  1         50  
4 1     1   3036 use open qw(:std :utf8); # Undeclared streams in UTF-8.
  1         2152  
  1         7  
5 1     1   2293 use charnames qw(:full :short); # Unneeded in v5.16.
  1         52068  
  1         8  
6 1     1   260 use 5.006;
  1         4  
  1         42  
7 1     1   8 use strict;
  1         2  
  1         39  
8 1     1   7 use Carp;
  1         2  
  1         87  
9             #use warnings FATAL => 'all';
10 1     1   8 use warnings;
  1         2  
  1         35  
11 1     1   1083 use Marpa::R2;
  1         214794  
  1         48  
12 1     1   12 use Data::Dumper;
  1         2  
  1         76  
13 1     1   7 use Scalar::Util qw{looks_like_number weaken};
  1         2  
  1         110  
14              
15             require Exporter;
16             our @ISA = qw(Exporter);
17             our @EXPORT = qw();
18              
19             our $VERSION = '0.203';
20 1     1   805 use MarpaX::xPathLike::DSL;
  1         3  
  1         28  
21 1     1   657 use MarpaX::xPathLike::Actions;
  1         4  
  1         69  
22 1     1   6050 use Test::Deep qw {cmp_details deep_diag};
  1         16705  
  1         568  
23              
24              
25              
26             my $grammar = Marpa::R2::Scanless::G->new({
27             #default_action => '::first',
28             action_object => 'MarpaX::xPathLike::Actions',
29             source => \($MarpaX::xPathLike::DSL::xpath),
30              
31             });
32              
33             #############################end of rules################################
34              
35             my @context = ();
36             sub _names{
37 0     0     return map {$_->{name}} _getSubObjectsOrCurrent(@_);
  0            
38             }
39             sub _values{
40             #print 'Values arg = ', Dumper \@_;
41 0     0     return map {${$_->{data}}} _getSubObjectsOrCurrent(@_);
  0            
  0            
42             }
43             sub _positions{
44 0     0     my @r = _getSubObjectsOrCurrent(@_);
45 0           return map {$_->{pos}} @r;
  0            
46             }
47             sub _lasts{
48 0     0     my @r = _getSubObjectsOrCurrent(@_);
49 0           return map {$_->{size}} @r;
  0            
50             }
51              
52 1     1   11 no warnings qw{uninitialized numeric};
  1         3  
  1         6726  
53              
54             my $operatorBy = {
55             '=' => sub($$){
56             return _logicalOper(sub {$_[0] == $_[1]}, $_[0], $_[1]);
57             },
58             '==' => sub($$){
59             return _logicalOper(sub {$_[0] == $_[1]}, $_[0], $_[1]);
60             },
61             '!=' => sub($$){
62             return _logicalOper(sub {$_[0] != $_[1]}, $_[0], $_[1]);
63             },
64             'eq' => sub($$){
65             return _logicalOper(sub {$_[0] eq $_[1]}, $_[0], $_[1]);
66             },
67             'ne' => sub($$){
68             return _logicalOper(sub {$_[0] ne $_[1]}, $_[0], $_[1]);
69             },
70             '===' => sub($$){
71             return _logicalOper(sub {
72             looks_like_number($_[0])
73             and looks_like_number($_[1])
74             and $_[0] == $_[1]
75             }, $_[0], $_[1]);
76             },
77             '!==' => sub($$){
78             return _logicalOper(sub {
79             $_[0] != $_[1]
80             }, $_[0], $_[1]);
81             },
82             '>' => sub($$){
83             return _logicalOper(sub {$_[0] > $_[1]}, $_[0], $_[1]);
84             },
85             '>=' => sub($$){
86             return _logicalOper(sub {$_[0] >= $_[1]}, $_[0], $_[1]);
87             },
88             '<' => sub($$){
89             return _logicalOper(sub {$_[0] < $_[1]}, $_[0], $_[1]);
90             },
91             '<=' => sub($$){
92             return _logicalOper(sub {$_[0] <= $_[1]}, $_[0], $_[1]);
93             },
94             '>=' => sub($$){
95             return _logicalOper(sub {$_[0] >= $_[1]}, $_[0], $_[1]);
96             },
97             'lt' => sub($$){
98             return _logicalOper(sub {$_[0] lt $_[1]}, $_[0], $_[1]);
99             },
100             'le' => sub($$){
101             return _logicalOper(sub {$_[0] le $_[1]}, $_[0], $_[1]);
102             },
103             'gt' => sub($$){
104             return _logicalOper(sub {$_[0] gt $_[1]}, $_[0], $_[1]);
105             },
106             'ge' => sub($$){
107             return _logicalOper(sub {$_[0] ge $_[1]}, $_[0], $_[1]);
108             },
109             'and' => sub($$){
110             return _logicalOper(sub {$_[0] and $_[1]}, $_[0], $_[1]);
111             },
112             'or' => sub($$){
113             return _logicalOper(sub {$_[0] or $_[1]}, $_[0], $_[1]);
114             },
115             '~' => sub($$){
116             return _logicalOper(sub {$_[0] =~ $_[1]}, $_[0], $_[1]);
117             },
118             '!~' => sub($$){
119             return _logicalOper(sub {$_[0] !~ $_[1]}, $_[0], $_[1]);
120             },
121             '*' => sub($$;@){
122             return _naryOper(sub {$_[0] * $_[1]}, $_[0], $_[1], @_[2..$#_]);
123             },
124             'div' => sub($$;@){
125             return _naryOper(sub {
126             my $r = eval {$_[0] / $_[1]};
127             carp qq|Division problems\n$@| if $@;
128             return $r;
129             }, $_[0], $_[1], @_[2..$#_]);
130             },
131             '/' => sub($$;@){
132             return _naryOper(sub {
133             my $r = eval {$_[0] / $_[1]};
134             carp qq|Division problems\n$@| if $@;
135             return $r;
136             }, $_[1], @_[2..$#_]);
137             },
138             '+' => sub($$;@){
139             return _naryOper(sub {$_[0] + $_[1]}, $_[0], $_[1], @_[2..$#_]);
140             },
141             '-' => sub($$;@){
142             return _naryOper(sub {$_[0] - $_[1]}, $_[0], $_[1], @_[2..$#_]);
143             },
144             'mod' => sub($$;@){
145             return _naryOper(sub {$_[0] % $_[1]}, $_[0], $_[1], @_[2..$#_]);
146             },
147             '%' => sub($$;@){
148             return _naryOper(sub {$_[0] % $_[1]}, $_[0], $_[1], @_[2..$#_]);
149             },
150             '||' => sub{
151             return _naryOper(sub {$_[0] . $_[1]}, $_[0], $_[1], @_[2..$#_])
152             },
153             names => \&_names,
154             values => \&_values,
155             positions => \&_positions,
156             lasts => \&_lasts,
157             name => sub {
158             return (_names(@_))[0] // q||;
159             },
160             value => sub(){
161             return (_values(@_))[0] // q||;
162             },
163             position => sub{
164             my @r = _positions(@_);
165             return $r[$#r] // 0;
166             },
167             last => sub{
168             my @r = _lasts(@_);
169             return $r[$#r] // 0;
170             },
171             isHash => sub{
172             my @r = grep {ref ${$_->{data}} eq q|HASH|} _getSubObjectsOrCurrent(@_);
173             return @r > 0;
174             },
175             isArray => sub{
176             my @r = grep {ref ${$_->{data}} eq q|ARRAY|} _getSubObjectsOrCurrent(@_);
177             return @r > 0;
178             },
179             isCode => sub{
180             my @r = grep {ref ${$_->{data}} eq q|CODE|} _getSubObjectsOrCurrent(@_);
181             return @r > 0;
182             },
183             isRef => sub{
184             my @r = grep {ref ${$_->{data}}} _getSubObjectsOrCurrent(@_);
185             return @r > 0;
186             },
187             isScalar => sub{
188             my @r = grep {!ref ${$_->{data}}} _getSubObjectsOrCurrent(@_);
189             return @r > 0;
190             },
191             count =>sub{
192             my @r = _getSubObjectsOrCurrent(@_);
193             return scalar @r;
194             },
195             exists => sub{
196             my @r = _getSubObjectsOrCurrent(@_);
197             return scalar @r > 0;
198             },
199             not => sub{
200             return !_operation($_[0]);
201             },
202             sum => sub{
203             my @r = _getSubObjectsOrCurrent($_[0]);
204             my @s = grep{ref $_->{data} eq q|SCALAR| and looks_like_number(${$_->{data}})} @r; #ignore entry if it is not a scalar
205             my $s = 0;
206             $s += ${$_->{data}} foreach (@s);
207             return $s;
208             },
209             sumproduct => sub{
210             my @r = _getSubObjectsOrCurrent($_[0]);
211             my @s = _getSubObjectsOrCurrent($_[1]);
212             my $size = $#r < $#s ? $#r: $#s;
213             my $s = 0;
214             foreach (0..$size){
215             $s += ${$r[$_]->{data}} * ${$s[$_]->{data}}
216             if ref $r[$_]->{data} eq q|SCALAR|
217             and ref $s[$_]->{data} eq q|SCALAR|
218             and looks_like_number(${$r[$_]->{data}})
219             and looks_like_number(${$s[$_]->{data}})
220             }
221             return $s;
222             },
223             };
224             sub _operation($){
225 0     0     my $operData = $_[0];
226 0 0 0       return undef unless defined $operData and ref $operData eq q|HASH|;
227             my %types = (
228             oper => sub{
229 0     0     my ($oper, @args) = @{$operData->{oper}};
  0            
230             #print "oper=$oper";
231             #my $oper = $params[0];
232 0 0 0       return undef unless defined $oper and exists $operatorBy->{$oper};
233             #my @args = @params[1..$#params];
234 0           return $operatorBy->{$oper}->(@args);
235             },
236             values =>sub{
237 0     0     my @r = $operatorBy->{values}->($operData->{values});
238 0           return @r;
239             }
240 0           );
241             #print 'operdata = ', Dumper $operData;
242 0           my @r = map {$types{$_}->()} grep {exists $types{$_}} keys %$operData;
  0            
  0            
243 0 0         return @r if wantarray();
244 0           return $r[0];
245             }
246             sub _naryOper(&$$;@){
247 0     0     my ($oper,$x,$y,@e) = @_;
248 0 0         $x = _operation($x) if ref $x;
249 0 0         $y = _operation($y) if ref $y;
250 0           my $res = $oper->($x,$y);
251 0           foreach my $e (@e){
252 0 0         $e = _operation($e) if ref $e;
253 0           $res = $oper->($res,$e);
254             }
255 0           return $res
256             }
257             sub _logicalOper(&$$){
258 0     0     my ($oper,$x,$y) = @_;
259             #print "x=", Dumper $x;
260             #print "y=", Dumper $y;
261 0           my @x = ($x);
262 0           my @y = ($y);
263 0 0 0       @x = _operation($x) if ref $x and ref $x ne q|Regexp|;
264 0 0 0       @y = _operation($y) if ref $y and ref $y ne q|Regexp|;
265             #my @r = eval {};
266             #warn qq|Warning: $@| if $@;
267 0           foreach my $x (@x){
268 0           foreach my $y (@y){
269 0 0         return 1 if $oper->($x,$y)
270             }
271             }
272 0           return 0;
273             #return $oper->($x,$y);
274             }
275              
276              
277             sub _evaluate{
278 0     0     my $x = $_[0];
279 0 0 0       return $x unless ref $x eq q|HASH| and exists $x->{oper};
280 0           return _operation($x);
281             }
282             sub _getStruct{
283 0     0     my ($context, $subpath) = @_;
284 0 0         return ($context) unless defined $subpath;
285 0           push @context, $context;
286 0           my @r = _getObjectSubset(${$context->{data}}, $subpath);
  0            
287 0           pop @context;
288 0           return @r;
289             }
290             my %filterType = (
291             boolean => sub {
292             return _operation($_[0]);
293             }
294             , indexes => sub{
295             sub __computeIndex{
296 0     0     my $index = 0 + _evaluate($_[0]);
297 0 0         $index += 1 + $context[$#context]->{size} if $index < 0;
298 0           return $index;
299             };
300             my %indexType = (
301             index => sub{
302             return $context[$#context]->{pos} == __computeIndex($_[0]);
303             }
304             , range => sub{
305             #print 'range', Dumper $_[0];
306             my $pos = $context[$#context]->{pos};
307             my ($start, $end) = map {__computeIndex($_)} @{$_[0]};
308             return $pos >= $start && $pos <= $end;
309             }
310             , from => sub{
311             #print 'from', Dumper $_[0];
312             return $context[$#context]->{pos} >= __computeIndex($_[0]);
313             }
314             , to => sub{
315             #print 'to', Dumper $_[0];
316             return $context[$#context]->{pos} <= __computeIndex($_[0]);
317             }
318             );
319             #print 'indexes filter ',Dumper @_;
320             my $indexes = $_[0];
321             foreach my $index (@$indexes){
322             #print 'evaluate', Dumper $index;
323             return 1 if (map {$indexType{$_}->($index->{$_})} grep {exists $indexType{$_}} keys %$index)[0];
324             }
325             return 0;
326             }
327             );
328             sub _filter{
329 0     0     my ($context,$filter) = @_;
330             #print 'validate -> ', Dumper \@_;
331 0 0 0       return 1 unless defined $filter and ref $filter eq q|HASH|; #just in case
332 0           push @context, $context;
333 0           my ($r) = map {$filterType{$_}->($filter->{$_})} grep {exists $filterType{$_}} keys %$filter;
  0            
  0            
334 0           pop @context;
335 0           return $r;
336             }
337             sub _getFilteredKeys{
338 0     0     my ($data,$filter,@keys) = @_;
339 0   0       $filter //= [];
340 0   0       my $order = $context[$#context]->{order} // q||;
341 0           my $size = scalar @keys;
342              
343 0           my @keyIndex = map{{
  0            
344             name => $keys[$_],
345             type => q|HASH|,
346             data => \$data->{$keys[$_]},
347             order => qq|$order/$keys[$_]|,
348             size => scalar @keys
349             }} 0..$#keys;
350 0           foreach my $filter (@$filter){
351 0           my $pos = 1;
352 0           $size = scalar @keyIndex;
353 0           @keyIndex = grep {_filter(
  0            
354             $_
355             ,$filter
356 0           )} map {@{$_}{qw|pos size|} = ($pos++, $size); $_} @keyIndex ;
  0            
  0            
357             }
358              
359 0           my $pos = 1;
360 0           $size = scalar @keyIndex;
361 0           return map {@{$_}{qw|pos size|} = ($pos++, $size); $_} @keyIndex
  0            
  0            
  0            
362             }
363             sub _getFilteredIndexes{
364 0     0     my ($data,$filter,@indexes) = @_;
365 0   0       $filter //= [];
366 0   0       my $order = $context[$#context]->{order} // q||;
367 0           my $size = scalar @indexes;
368 0           my $large = 1;
369 1     1   15 { use integer; my $n = $size; $large++ while($n /= 10); } #a scope to do integer operations;
  1         3  
  1         11  
  0            
  0            
  0            
370              
371 0           my @r = map {{ #init result array
  0            
372             name => $_,
373             type => q|ARRAY|,
374             data => \$data->[$_],
375             order => qq|$order/|.sprintf("%0*u",$large,$_),
376             size => $size
377             }} @indexes;
378            
379 0           foreach my $filter (@$filter){
380 0           my $pos = 1;
381 0           $size = scalar @r;
382 0           @r = grep {_filter( #filter out from result
  0            
383             $_
384             ,$filter
385 0           )} map {@{$_}{qw|pos size|} = ($pos++, $size); $_} @r ;
  0            
  0            
386             }
387              
388 0           my $pos = 1;
389 0           $size = scalar @r;
390 0           return map{ @{$_}{qw|pos size|} = ($pos++, $size); $_} @r; #compute final positions in context
  0            
  0            
  0            
391             }
392             sub _anyChildType{
393 0     0     my ($type,$name,$data,$subpath,$filter) = @_;
394             my %filterByDataType = (
395             HASH => sub{
396 0 0 0 0     return () if defined $type and $type ne q|HASH|;
397 0           my @keys = keys %$data;
398 0 0         @keys = grep {$_ eq $name} @keys if defined $name;
  0            
399 0           return _getFilteredKeys($data,$filter, sort @keys);
400             }
401             , ARRAY => sub{
402 0 0 0 0     return () if defined $type and $type ne q|ARRAY|;
403 0           my @indexes = 0..$#$data;
404 0 0         @indexes = grep {$_ == $name} @indexes if defined $name;
  0            
405 0           return _getFilteredIndexes($data,$filter, @indexes);
406             }
407 0           );
408             return
409 0           map {_getStruct($_, $subpath)}
  0            
410 0           map { $filterByDataType{$_}->()}
411 0           grep {exists $filterByDataType{$_}}
412             (ref $data);
413             }
414             sub _descendant{
415 0     0     my ($data,$path) = @_;
416             #print 'context', Dumper \@context;
417 0           my @r = _getObjectSubset($data,$path);
418 0   0       my $order = $context[$#context]->{order} // q||;
419             #print "order = $order";
420 0 0         if (ref $data eq q|HASH|){
421 0           my @keys = sort keys %$data;
422 0           foreach (@keys){
423 0           push @context, {name => $_, type => q|HASH|, data => \$data->{$_}, order => qq|$order/$_|, pos =>1, size => scalar @keys };
424 0           push @r, _descendant($data->{$_}, $path);
425 0           pop @context;
426             }
427             }
428 0 0         if (ref $data eq q|ARRAY|){
429 0           foreach (0 .. $#$data){
430 0           push @context, {name => $_, type => q|ARRAY|, data => \$data->[$_], order => qq|$order/$_|, pos=> 1, size => scalar @$data};
431 0           push @r, _descendant($data->[$_], $path);
432 0           pop @context;
433             }
434             }
435 0           return @r;
436             }
437             sub _getDescendants{
438 0     0     my($descendants,$subpath) = @_;
439 0           my @r=();
440 0           foreach (0..$#$descendants){
441 0 0         if (defined $descendants->[$_]){ #only if descendants was selected
442 0           my $last = $#context;
443             #print "descendant of $_", Dumper $descendants->[$_];
444             #print "subpath", Dumper $subpath;
445 0           push @context, @{$descendants->[$_]};
  0            
446 0           push @r, defined $subpath ?
447 0 0         _getObjectSubset(${$context[$#context]->{data}}, $subpath)
448             : ($context[$#context]);
449 0           $#context = $last;
450             }
451             }
452 0           return @r;
453             }
454              
455             sub _getDescContexts{
456 0     0     my (@context) = @_;
457 0           my @r = ();
458 0   0       my $order = $context[$#context]->{order} // q||;
459 0           my $data = ${$context[$#context]->{data}};
  0            
460 0           my $pos = 1;
461 0 0         if (ref $data eq q|HASH|){
462 0           my @keys = sort keys %$data;
463 0           foreach (@keys){
464 0           push @r, _getDescContexts(@context, {name => $_, type => q|HASH|, data => \$data->{$_}, order => qq|$order/$_|, pos =>$pos++, size => scalar @keys });
465             }
466             }
467 0 0         if (ref $data eq q|ARRAY|){
468 0           foreach (0 .. $#$data){
469 0           push @r, _getDescContexts(@context, {name => $_, type => q|ARRAY|, data => \$data->[$_], order => qq|$order/$_|, pos => $pos++, size => scalar @$data});
470             }
471             }
472 0           return (\@context, @r);
473             }
474              
475             sub _filterOutDescendants{
476 0     0     my ($filters,$size,$descendants) = @_;
477 0   0       $filters //= [];
478              
479            
480             #print 'descendants', scalar @$descendants, Dumper \@$descendants;
481 0           foreach my $filter (@$filters){
482 0           my $pos = 1;
483 0           my $cnt = 0;
484 0           foreach my $k (0..$#$descendants){
485 0 0         if (defined $descendants->[$k]){
486 0           my $last = $#context;
487 0           push @context, @{$descendants->[$k]};
  0            
488 0           my ($s,$p) = @{$context[$#context]}{qw|size pos|};
  0            
489 0           @{$context[$#context]}{qw|size pos|} = ($size,$pos++);
  0            
490 0 0         $cnt++, undef $descendants->[$k] if !_filter($context[$#context],$filter);
491 0           @{$context[$#context]}{qw|size pos|} = ($s,$p);
  0            
492 0           $#context = $last;
493             }
494             }
495 0           $size -= $cnt;
496             }
497             #print 'Selected descendants', scalar @$descendants, Dumper \@$descendants;
498 0           return $descendants;
499             }
500             sub _getDescendantsByTypeAndName{
501 0     0     my ($type, $name, $subpath,$filter,$self) = @_;
502 0           my $descendants = [_getDescContexts($context[$#context])];
503 0 0         shift @$descendants unless $self;
504 0 0         $descendants = [grep {$_->[$#$_]->{name} eq $name} @$descendants] if defined $name;
  0            
505 0 0         $descendants = [grep {$_->[$#$_]->{type} eq $type} @$descendants] if defined $type;
  0            
506 0           shift @{$descendants->[$_]} foreach (0..$#$descendants); #remove the current context from context list.
  0            
507 0           my $size = scalar @$descendants;
508 0           return _getDescendants(_filterOutDescendants($filter,$size,$descendants), $subpath);
509             }
510              
511             sub _getAncestorsOrSelf{
512 0     0     my ($ancestors,$subpath) = @_;
513 0           my @tmp = ();
514 0           my @r;
515 0           foreach (0..$#$ancestors){
516 0 0         if (defined $ancestors->[$_]){ #only if ancestor was selected
517 0           push @r, defined $subpath ?
518 0 0         _getObjectSubset(${$context[$#context]->{data}}, $subpath)
519             : ($context[$#context])
520             }
521 0           push @tmp, pop @context;
522             }
523 0           push @context, pop @tmp while(scalar @tmp > 0); #repo @context
524 0           return @r;
525             }
526             # foreach (0..$#$ancestors){ #pre filter ancestors with named ones, only!
527             # $size--, undef $ancestors->[$_] if $context[$_]->{name} ne $name;
528             # }
529             sub _filterOutAncestorsOrSelf{
530 0     0     my($type,$name,$filter,$ancestorsIndex) = @_;
531 0   0       $filter //= [];
532              
533             #as array of flags. Each position flags a correpondent ancestor
534             #my @ancestorsIndex = map {1} (0..$#context);
535            
536              
537             #filter out ancestors with a different name!
538 0 0         map {
539 0 0         undef $ancestorsIndex->[$_] if $context[$#context - $_]->{name} ne $name;
540             } 0..$#$ancestorsIndex if defined $name;
541              
542             #filter out ancestors of a different type!
543 0 0         map {
544 0 0         undef $ancestorsIndex->[$_] if $context[$#context - $_]->{type} ne $type;#Não se devia decrementar duplamente
545             } 0..$#$ancestorsIndex if defined $type;
546            
547 0           my $size = 0;
548 0 0         map {$size++ if defined $_} @$ancestorsIndex;
  0            
549              
550 0           foreach my $filter (@$filter){
551 0           my $pos = 1;
552 0           my @tmp = ();
553 0           my $cnt = 0;
554 0           foreach my $k (0..$#$ancestorsIndex){
555 0 0         if (defined $ancestorsIndex->[$k]){
556 0           my ($s,$p) = @{$context[$#context]}{qw|size pos|};
  0            
557 0           @{$context[$#context]}{qw|size pos|} = ($size,$pos++);
  0            
558 0 0         $cnt++, undef $ancestorsIndex->[$k] if !_filter($context[$#context],$filter);
559 0           @{$context[$#context]}{qw|size pos|} = ($s,$p);
  0            
560             }
561 0           push @tmp, pop @context;
562             }
563 0           push @context, pop @tmp while(scalar @tmp > 0); #repo @context
564 0           $size -= $cnt; #adjust the group's size;
565             }
566 0           return $ancestorsIndex;
567             }
568             sub _filterOutSiblings{
569 0     0     my ($type, $name, $subpath,$filter,$direction) = @_;
570 0           my $mySelf = $context[$#context]->{data};
571 0           my $context = pop @context;
572 0           my $data = ${$context[$#context]->{data}};
  0            
573              
574             my %filterByDataType = (
575             HASH => sub{
576 0     0     my @keys = sort keys %$data;
577 0           my $cnt = $#keys;
578 0   0       $cnt-- while($cnt >= 0 and \$data->{$keys[$cnt]} != $mySelf);
579 0           my @siblings = do {
580 0 0         if ($direction eq q|preceding|){
    0          
581 0           $#keys = $cnt-1;
582 0           reverse @keys[0 .. $cnt-1];
583             }elsif($direction eq q|following|){
584 0           @keys[$cnt+1 .. $#keys]
585             }
586             };
587 0 0         @siblings = grep {$_ eq $name} @siblings if defined $name;
  0            
588 0 0         @siblings = grep {q|HASH| eq $type} @siblings if defined $type;
  0            
589 0           return _getFilteredKeys($data,$filter, @siblings);
590             }
591             , ARRAY => sub{
592 0     0     my $cnt = $#$data;
593 0   0       $cnt-- while($cnt >= 0 and \$data->[$cnt] != $mySelf);
594 0           my @siblings = do {
595 0 0         if ($direction eq q|preceding|){
    0          
596 0           reverse 0..$cnt-1
597             }elsif($direction eq q|following|){
598 0           $cnt+1 .. $#$data
599             }
600             };
601 0 0         @siblings = grep {$_ eq $name} @siblings if defined $name;
  0            
602 0 0         @siblings = grep {q|ARRAY| eq $type} @siblings if defined $type;
  0            
603 0           return _getFilteredIndexes($data,$filter, @siblings);
604             }
605 0           );
606 0           my @r =
607 0           map {_getStruct($_, $subpath)}
608 0           map { $filterByDataType{$_}->()}
609 0           grep {exists $filterByDataType{$_}}
610             (ref $data);
611 0           push @context, $context;
612 0           return @r;
613             }
614              
615             my $dispatcher = {
616             self => sub{
617             my (undef, undef, $subpath,$filter) = @_;
618             return _getAncestorsOrSelf(_filterOutAncestorsOrSelf(undef, undef, $filter, [0]), $subpath);
619             },
620             selfArray => sub{
621             my (undef, undef, $subpath,$filter) = @_;
622             return _getAncestorsOrSelf(_filterOutAncestorsOrSelf(q|ARRAY|, undef, $filter, [0]), $subpath);
623             },
624             selfHash => sub {
625             my (undef, undef, $subpath,$filter) = @_;
626             return _getAncestorsOrSelf(_filterOutAncestorsOrSelf(q|HASH|, undef, $filter, [0]), $subpath);
627             },
628             selfNamed => sub{
629             my (undef, $name, $subpath,$filter) = @_;
630             return _getAncestorsOrSelf(_filterOutAncestorsOrSelf(q|HASH|, $name, $filter, [0]), $subpath);
631             },
632             selfIndexed => sub{
633             my (undef, $index, $subpath,$filter) = @_;
634             return _getAncestorsOrSelf(_filterOutAncestorsOrSelf(q|ARRAY|, $index, $filter, [0]), $subpath);
635             },
636             selfIndexedOrNamed => sub{
637             my (undef, $index, $subpath,$filter) = @_;
638             return _getAncestorsOrSelf(_filterOutAncestorsOrSelf(undef, $index, $filter, [0]), $subpath);
639             },
640             parent => sub{
641             my (undef, undef, $subpath,$filter) = @_;
642              
643             my $current = pop @context;
644             my @r = _getAncestorsOrSelf(_filterOutAncestorsOrSelf(undef, undef, $filter, [0]), $subpath);
645             push @context, $current;
646             return @r;
647             },
648             parentArray => sub{
649             my (undef, undef, $subpath,$filter) = @_;
650              
651             my $current = pop @context;
652             my @r = _getAncestorsOrSelf(_filterOutAncestorsOrSelf(q|ARRAY|, undef, $filter, [0]), $subpath);
653             push @context, $current;
654             return @r;
655             },
656             parentHash => sub{
657             my (undef, undef, $subpath,$filter) = @_;
658              
659             my $current = pop @context;
660             my @r = _getAncestorsOrSelf(_filterOutAncestorsOrSelf(q|HASH|, undef, $filter, [0]), $subpath);
661             push @context, $current;
662             return @r;
663             },
664             parentNamed => sub{
665             my (undef, $name, $subpath,$filter) = @_;
666              
667             my $current = pop @context;
668             my @r = _getAncestorsOrSelf(_filterOutAncestorsOrSelf(q|HASH|, $name, $filter, [0]), $subpath);
669             push @context, $current;
670             return @r;
671             },
672             parentIndexed => sub{
673             my (undef, $index, $subpath,$filter) = @_;
674              
675             my $current = pop @context;
676             my @r = _getAncestorsOrSelf(_filterOutAncestorsOrSelf(q|ARRAY|, $index, $filter, [0]), $subpath);
677             push @context, $current;
678             return @r;
679             },
680             parentIndexedOrNamed => sub{
681             my (undef, $index, $subpath,$filter) = @_;
682              
683             my $current = pop @context;
684             my @r = _getAncestorsOrSelf(_filterOutAncestorsOrSelf(undef, $index, $filter, [0]), $subpath);
685             push @context, $current;
686             return @r;
687             },
688             ancestor => sub{
689             my (undef, undef, $subpath,$filter) = @_;
690              
691             my $current = pop @context;
692             my @r = _getAncestorsOrSelf(_filterOutAncestorsOrSelf(undef, undef, $filter, [0..$#context]), $subpath);
693             push @context, $current;
694             return @r;
695             },
696             ancestorArray => sub{
697             my (undef, undef, $subpath,$filter) = @_;
698              
699             my $current = pop @context;
700             my @r = _getAncestorsOrSelf(_filterOutAncestorsOrSelf(q|ARRAY|, undef, $filter, [0..$#context]), $subpath);
701             push @context, $current;
702             return @r;
703             },
704             ancestorHash => sub{
705             my (undef, undef, $subpath,$filter) = @_;
706              
707             my $current = pop @context;
708             my @r = _getAncestorsOrSelf(_filterOutAncestorsOrSelf(q|HASH|, undef, $filter, [0..$#context]), $subpath);
709             push @context, $current;
710             return @r;
711             },
712             ancestorNamed => sub{
713             my (undef, $name, $subpath,$filter) = @_;
714            
715             my $current = pop @context;
716             my @r = _getAncestorsOrSelf(_filterOutAncestorsOrSelf(q|HASH|, $name, $filter, [0..$#context]), $subpath);
717             push @context, $current;
718             return @r;
719             },
720             ancestorIndexed => sub{
721             my (undef, $index, $subpath,$filter) = @_;
722            
723             my $current = pop @context;
724             my @r = _getAncestorsOrSelf(_filterOutAncestorsOrSelf(q|ARRAY|, $index, $filter, [0..$#context]), $subpath);
725             push @context, $current;
726             return @r;
727             },
728             ancestorIndexedOrNamed => sub{
729             my (undef, $index, $subpath,$filter) = @_;
730            
731             my $current = pop @context;
732             my @r = _getAncestorsOrSelf(_filterOutAncestorsOrSelf(undef, $index, $filter, [0..$#context]), $subpath);
733             push @context, $current;
734             return @r;
735             },
736             ancestorOrSelf => sub{
737             my (undef, undef, $subpath,$filter) = @_;
738            
739             return _getAncestorsOrSelf(_filterOutAncestorsOrSelf(undef, undef, $filter, [0..$#context]), $subpath);
740             },
741             ancestorOrSelfArray => sub{
742             my (undef, undef, $subpath,$filter) = @_;
743            
744             return _getAncestorsOrSelf(_filterOutAncestorsOrSelf(q|ARRAY|, undef, $filter, [0..$#context]), $subpath);
745             },
746             ancestorOrSelfHash => sub{
747             my (undef, undef, $subpath,$filter) = @_;
748            
749             return _getAncestorsOrSelf(_filterOutAncestorsOrSelf(q|HASH|, undef, $filter, [0..$#context]), $subpath);
750             },
751             ancestorOrSelfNamed => sub{
752             my (undef, $name, $subpath,$filter) = @_;
753            
754             return _getAncestorsOrSelf(_filterOutAncestorsOrSelf(q|HASH|,$name,$filter, [0..$#context]), $subpath);
755             },
756             ancestorOrSelfIndexed => sub{
757             my (undef, $index, $subpath,$filter) = @_;
758            
759             return _getAncestorsOrSelf(_filterOutAncestorsOrSelf(q|ARRAY|,$index,$filter, [0..$#context]), $subpath);
760             },
761             ancestorOrSelfIndexedOrNamed => sub{
762             my (undef, $index, $subpath,$filter) = @_;
763            
764             return _getAncestorsOrSelf(_filterOutAncestorsOrSelf(undef,$index,$filter,[0..$#context]), $subpath);
765             },
766             child => sub{
767             my ($data, undef, $subpath,$filter) = @_;
768             return _anyChildType(undef,undef,$data,$subpath,$filter);
769             },
770             childArray => sub{
771             my ($data, undef, $subpath,$filter) = @_;
772             return _anyChildType(q|ARRAY|,undef,$data,$subpath,$filter);
773             },
774             childHash => sub{
775             my ($data, undef, $subpath,$filter) = @_;
776             return _anyChildType(q|HASH|,undef,$data,$subpath,$filter);
777             },
778             childNamed => sub{
779             my ($data, $name, $subpath,$filter) = @_;
780             return _anyChildType(q|HASH|,$name,$data,$subpath,$filter);
781             },
782             childIndexed => sub{
783             my ($data, $index, $subpath,$filter) = @_;
784             return _anyChildType(q|ARRAY|,$index,$data,$subpath,$filter);
785             },
786             childIndesxedOrNamed => sub{
787             my ($data, $index, $subpath,$filter) = @_;
788             return _anyChildType(undef,$index,$data,$subpath,$filter);
789             },
790             descendant => sub{
791             my ($data, undef, $subpath,$filter) = @_;
792             return _getDescendantsByTypeAndName(undef,undef,$subpath,$filter)
793             },
794             descendantArray => sub{
795             my ($data, undef, $subpath,$filter) = @_;
796             return _getDescendantsByTypeAndName(q|ARRAY|,undef,$subpath,$filter)
797             },
798             descendantHash => sub{
799             my ($data, undef, $subpath,$filter) = @_;
800             print "AQUI";
801             return _getDescendantsByTypeAndName(q|HASH|,undef,$subpath,$filter)
802             },
803             descendantNamed => sub{
804             my ($data, $name, $subpath,$filter) = @_;
805             return _getDescendantsByTypeAndName(q|HASH|,$name,$subpath,$filter)
806             },
807             descendantIndexed => sub{
808             my ($data, $index, $subpath,$filter) = @_;
809             return _getDescendantsByTypeAndName(q|ARRAY|,$index,$subpath,$filter)
810             },
811             descendantIndexedOrNamed => sub{
812             my ($data, $index, $subpath,$filter) = @_;
813             return _getDescendantsByTypeAndName(undef,$index,$subpath,$filter)
814             },
815             descendantOrSelf => sub{
816             my ($data, undef, $subpath,$filter) = @_;
817             return _getDescendantsByTypeAndName(undef,undef,$subpath,$filter,1)
818             },
819             descendantOrSelfArray => sub{
820             my ($data, undef, $subpath,$filter) = @_;
821             return _getDescendantsByTypeAndName(q|ARRAY|,undef,$subpath,$filter,1)
822             },
823             descendantOrSelfHash => sub{
824             my ($data, undef, $subpath,$filter) = @_;
825             return _getDescendantsByTypeAndName(q|HASH|,undef,$subpath,$filter,1)
826             },
827             descendantOrSelfNamed => sub{
828             my ($data, $name, $subpath,$filter) = @_;
829             return _getDescendantsByTypeAndName(q|HASH|,$name,$subpath,$filter,1)
830             },
831             descendantOrSelfIndexed => sub{
832             my ($data, $index, $subpath,$filter) = @_;
833             return _getDescendantsByTypeAndName(q|ARRAY|,$index,$subpath,$filter,1)
834             },
835             descendantOrSelfIndexedOrNamed => sub{
836             my ($data, $index, $subpath,$filter) = @_;
837             return _getDescendantsByTypeAndName(undef,$index,$subpath,$filter,1)
838             },
839             precedingSibling => sub{
840             my ($data, undef, $subpath,$filter) = @_;
841             return _filterOutSiblings(undef,undef,$subpath, $filter,q|preceding|)
842             },
843             precedingSiblingArray => sub{
844             my ($data, undef, $subpath,$filter) = @_;
845             return _filterOutSiblings(q|ARRAY|,undef,$subpath, $filter,q|preceding|)
846             },
847             precedingSiblingHash => sub{
848             my ($data, undef, $subpath,$filter) = @_;
849             _filterOutSiblings(q|HASH|,undef,$subpath, $filter,q|preceding|)
850             },
851             precedingSiblingNamed => sub{
852             my ($data, $name, $subpath,$filter) = @_;
853             return _filterOutSiblings(q|HASH|,$name,$subpath, $filter,q|preceding|)
854             },
855             precedingSiblingIndexed => sub{
856             my ($data, $index, $subpath,$filter) = @_;
857             return _filterOutSiblings(q|ARRAY|,$index,$subpath, $filter,q|preceding|)
858             },
859             precedingSiblingIndexedOrNamed => sub{
860             my ($data, $index, $subpath,$filter) = @_;
861             return _filterOutSiblings(undef,$index,$subpath, $filter,q|preceding|)
862             },
863             followingSibling => sub{
864             my ($data, undef, $subpath,$filter) = @_;
865             return _filterOutSiblings(undef,undef,$subpath, $filter,q|following|)
866             },
867             followingSiblingArray => sub{
868             my ($data, undef, $subpath,$filter) = @_;
869             return _filterOutSiblings(q|ARRAY|,undef,$subpath, $filter,q|following|)
870             },
871             followingSiblingHash => sub{
872             my ($data, undef, $subpath,$filter) = @_;
873             return _filterOutSiblings(q|HASH|,undef,$subpath, $filter,q|following|)
874             },
875             followingSiblingNamed => sub{
876             my ($data, $name, $subpath,$filter) = @_;
877             return _filterOutSiblings(q|HASH|,$name,$subpath, $filter,q|following|)
878             },
879             followingSiblingIndexed => sub{
880             my ($data, $index, $subpath,$filter) = @_;
881             return _filterOutSiblings(q|ARRAY|,$index,$subpath, $filter,q|following|)
882             },
883             followingSiblingIndexedOrNamed => sub{
884             my ($data, $index, $subpath,$filter) = @_;
885             return _filterOutSiblings(undef,$index,$subpath, $filter,q|following|)
886             },
887             slashslash => sub{
888             my ($data, undef, $subpath,undef) = @_;
889             return _descendant($data,$subpath);
890             }
891             };
892              
893              
894             sub _getObjectSubset{
895 0     0     my ($data,$path) = @_;
896 0   0       $path //= {}; #if not defined $path
897              
898 0           my %seen;
899             return
900 0           sort {
901 0 0 0       $a->{order} cmp $b->{order}
      0        
902             }grep {
903 0           defined $_
904             and defined $_->{data}
905             and defined $_->{order}
906             and !$seen{$_->{data}}++
907             } map {
908 0           $dispatcher->{$_}->($data, $path->{$_}, $path->{subpath}, $path->{filter})
909             } grep{
910 0           exists $path->{$_}
911             } keys %$dispatcher;
912             }
913             sub _getSubObjectsOrCurrent{
914 0     0     my $paths = $_[0];
915 0 0 0       return _getObjects(@$paths) if defined $paths and ref $paths eq q|ARRAY| and scalar @$paths > 0;
      0        
916 0           return ($context[$#context]);
917             }
918             sub _getObjects{
919 0     0     my @paths = @_;
920 0           my @r = ();
921 0           foreach my $entry (@paths){
922 0 0         my $data = ${$context[defined $entry->{absolute} ? 0 : $#context]->{data}};
  0            
923 0           push @r, _getObjectSubset($data,$entry->{path});
924             }
925 0           return @r;
926             }
927              
928             ###########object based invocation methods ########################
929             sub _execute{
930 0     0     my ($self,$data,$query) = @_;
931 0 0 0       return undef unless ref $data eq q|HASH| or ref $data eq q|ARRAY|;
932 0 0 0       return undef unless defined $query and (defined $query->{oper} or defined $query->{paths});
      0        
933 0           push @context, {data => \$data, type => ref $data, order => '', name => '/', size => 1, pos => 1};
934 0           my @r = defined $query->{oper} ?
935 0           map {\$_} (_operation($query)) #if an operation
936 0 0         : map {$_->{data}} sort {$a->{order} cmp $b->{order}} _getObjects(@{$query->{paths}}); #else is a path
  0            
  0            
937 0           pop @context;
938 0           return MarpaX::xPathLike::Results->new(@r);
939             }
940              
941             #########################################public methods ###################################################################
942             $Data::Dumper::Deepcopy = 1;
943 0     0 1   sub new {} #The Marpa::R2 needs it
944             sub compile{
945 0     0 1   my ($self,$q) = @_;
946 0 0         return undef unless $q;
947              
948 0 0         my $reader = Marpa::R2::Scanless::R->new({
949             grammar => $grammar,
950             trace_terminals => 0,
951             }) or return undef;
952             #code utf8 characters with sequece #utfcode#. Marpa problem?
953 0           $q =~ s/[#\N{U+A0}-\N{U+10FFFF}]/sprintf "#%d#", ord $&/ge;
  0            
954             #and, if we replace, we need to delimite the key if not already delimited
955             #and $q =~ s/\/{(?!["'])(.*?#\d+#.*?)(?!["'])}/\/{"$1"}/g; # and print "new q = $q";
956 0           eval {$reader->read(\$q)};
  0            
957 0 0 0       carp qq|Wrong xPathLike Expression $q\n$@| and return undef if $@;
958             #my $qp = $reader->value or return undef;
959 0           my @ptree = ();
960 0           while(my $pt = $reader->value){
961 0           push @ptree, $pt;
962             }
963 0           my $nt = scalar @ptree;
964 0 0         return undef unless $nt;
965 0 0         if ($nt > 1){
966 0           foreach my $got (@ptree[1..$#ptree]){
967 0           my ($ok, $stack) = cmp_details($got, $ptree[0]);
968 0 0         unless ($ok){
969 0           my $fh = *STDOUT;
970 0           *STDOUT = *STDERR;
971 0           carp qq|Found $nt trees for query $q, I will use the first\n|;
972 0           deep_diag($stack);
973 0           *STDOUT = $fh;
974             }
975             }
976             }
977             #print "compile", Dumper [@ptree];
978 0           return MarpaX::xPathLike::Data->new(${$ptree[0]})
  0            
979             }
980              
981             sub data{
982 0     0 1   my ($self,$data) = @_;
983 0           return MarpaX::xPathLike::Compiler->new($data)
984             }
985              
986 0     0     sub DESTROY{
987             }
988              
989             package MarpaX::xPathLike::Compiler;
990 1     1   12031 use Data::Dumper;
  1         3  
  1         922  
991             sub new{
992 0     0     my ($self,$data) = @_;
993 0 0 0       return undef unless defined $data and (ref $data eq q|HASH| or ref $data eq q|ARRAY|);
      0        
994 0           return bless {data=>$data}, $self;
995             }
996              
997             sub query{
998 0     0     my ($self,$xPathLikeString) = @_;
999 0 0         my $c = MarpaX::xPathLike->compile($xPathLikeString) or return undef;
1000 0           return $c->data($self->{data});
1001             }
1002 0     0     sub DESTROY{
1003             }
1004              
1005              
1006             package MarpaX::xPathLike::Data;
1007             #use Data::Dumper;
1008              
1009             sub new{
1010 0     0     my ($self,$xPathLike) = @_;
1011 0 0 0       return undef unless defined $xPathLike and (defined $xPathLike->{oper} or defined $xPathLike->{paths});
      0        
1012 0           return bless {xPathLike=>$xPathLike}, $self;
1013             }
1014              
1015             sub data{
1016 0     0     my ($self,$data) = @_;
1017 0           return MarpaX::xPathLike->_execute($data,$self->{xPathLike});
1018             }
1019              
1020 0     0     sub DESTROY{
1021             }
1022              
1023             package MarpaX::xPathLike::Results;
1024             #use Data::Dumper;
1025              
1026             sub new {
1027 0     0     my ($self,@results) = @_;
1028 0           return bless {results=>[@results]}, $self;
1029             }
1030              
1031             sub getrefs{
1032 0     0     my $self = shift;
1033 0           return @{$self->{results}};
  0            
1034             }
1035             sub getref{
1036 0     0     my $self = shift;
1037 0           return $self->{results}->[0];
1038             }
1039             sub getvalues{
1040 0     0     my $self = shift;
1041 0           return map {$$_} @{$self->{results}};
  0            
  0            
1042             }
1043             sub getvalue{
1044 0     0     my $self = shift;
1045 0 0         return undef unless ref $self->{results}->[0];
1046 0           return ${$self->{results}->[0]};
  0            
1047             }
1048              
1049             1;