File Coverage

blib/lib/Neo4j/Cypher/Abstract/Peeler.pm
Criterion Covered Total %
statement 214 264 81.0
branch 125 178 70.2
condition 67 154 43.5
subroutine 29 33 87.8
pod 6 21 28.5
total 441 650 67.8


line stmt bran cond sub pod time code
1             package Neo4j::Cypher::Abstract::Peeler;
2 6     6   90492 use Carp;
  6         16  
  6         550  
3 6     6   47 use List::Util 1.33 qw(any);
  6         154  
  6         588  
4 6     6   49 use Scalar::Util qw(looks_like_number blessed);
  6         15  
  6         539  
5 6     6   41 use strict;
  6         17  
  6         143  
6 6     6   34 use warnings;
  6         16  
  6         1856  
7              
8             # issues to solve:
9             # quoting
10             # parens
11             # param binding
12              
13             # quoting logic:
14             # if config:bind = true
15             # if anon_placeholder (like ?) is in config, then return literals without
16             # quoting in array $obj->bind_values, and the placeholder in the statement
17             # if anon_placeholder is undef, then return literals quoted directly in the
18             # statement; return named parameters in $obj->parameters
19             #
20             # if config:bind false
21             # leave tokens and identifiers as-is, no bind_values or parameters
22              
23             our $VERSION = '0.1001';
24             our $VERSION = '0.1001';
25             my $SQL_ABSTRACT = 1;
26              
27             sub puke(@);
28             sub belch(@);
29              
30             my %config = (
31             bind => 1,
32             anon_placeholder => undef, # '?',
33             hash_op => '-and',
34             array_op => '-or',
35             list_braces => '[]',
36             ineq_op => '<>',
37             implicit_eq_op => '=',
38             quote_lit => "'",
39             esc_quote_lit => "\\",
40             parameter_sigil => qw/^([:$?])|({[^}]+}$)/,
41             quote_fld => undef,
42             safe_identifier => qw/[a-zA-Z_.]+/
43             );
44              
45             # for each operator type (key in %type_table), there
46             # should be a handler with the same name
47              
48             my %type_table = (
49             infix_binary => [qw{ - / % =~ = <> < > <= >=
50             -contains -starts_with -ends_with}],
51             infix_listarg => [qw{ -in } ],
52             infix_distributable => [qw{ + * -and -or }],
53             prefix => [qw{ -not }],
54             postfix => [qw{ -is_null -is_not_null }],
55             function => [qw{
56             ()
57             -abs -ceil -floor -rand -round -sign -degrees
58             -e -exp -log -log10 -sqrt -acos -asin -atan -atan2
59             -cos -cot -haversin -pi -radians -sin -tan
60             -exists -toInt
61             -left -lower -ltrim -replace -reverse -right
62             -rtrim -split -substring -toString -trim -upper
63             -length -size -type -id -coalesce -head -last
64             -labels -nodes -relationships -keys -tail -range
65             -collect -count -max -min -percentileCont
66             -percentileDisc -stDev -stDevP -sum
67             -shortestPath -allShortestPaths
68             }],
69             predicate => [qw{ -all -any -none -single -filter}],
70             extract => [qw{ -extract }],
71             reduce => [qw{ -reduce }],
72             list => [qw( -list )], # returns args in list format
73             bind => [qw( -bind )], # handles parameters and literal quoting
74             thing => [qw( -thing )], # the literal thing itself
75             );
76              
77             my %dispatch;
78             foreach my $type (keys %type_table) {
79 6     6   47 no strict 'refs';
  6         16  
  6         22235  
80             my @ops = @{$type_table{$type}};
81             @dispatch{@ops} = ( *${type}{CODE} ) x @ops;
82             }
83              
84             sub new {
85 34     34 0 2901 my $class = shift;
86 34         105 my %args = @_;
87 34 50 33     125 if ($args{dispatch} and !(ref $args{dispatch} eq 'HASH')) {
88 0         0 puke "dispatch must be hashref mapping operators to coderefs (or absent)"
89             }
90 34 50 33     115 if ($args{config} and !(ref $args{config} eq 'HASH')) {
91 0         0 puke "config must be hashref defining peeler options"
92             }
93             my $self = {
94             dispatch => $args{dispatch} || \%dispatch,
95 34   50     292 config => $args{config} || \%config
      50        
96             };
97             # update config elts according to constructor args
98 34 50       144 if (length scalar keys %args) {
99 34         166 for (keys %config) {
100 408 50       932 defined $args{$_} and $self->{config}{$_} = $args{$_};
101             }
102             }
103 34         161 bless $self, $class;
104             }
105              
106             sub belch (@) {
107 0     0 0 0 my($func) = (caller(1))[3];
108 0         0 Carp::carp "[$func] Warning: ", @_;
109             }
110              
111             sub puke (@) {
112 0     0 0 0 my($func) = (caller(1))[3];
113 0         0 Carp::croak "[$func] Fatal: ", @_;
114             }
115              
116 30 100   30 1 95 sub bind_values { $_[0]->{bind_values} ? @{$_[0]->{bind_values}} : return ; }
  22         67  
117 30 50   30 1 109 sub parameters { $_[0]->{parameters} ? @{$_[0]->{parameters}} : return ; }
  0         0  
118              
119             sub _dispatch {
120 248     248   755 $_[0]->{dispatch}{$_[1]}->(@_);
121             }
122              
123             sub _quote_lit {
124 167     167   334 my $arg = "$_[1]";
125 167         298 my $q = $_[0]->{config}{quote_lit};
126 167 100 66     1685 if (looks_like_number $arg or
      66        
      66        
127             $arg =~ /^\s*$q(.*)$q\s*$/ or
128             $arg =~ $_[0]->{config}{parameter_sigil} or
129             blessed($_[1])
130             ) {
131             # numeric, already quoted, a parameter, or an object
132 70         205 return "$arg";
133             }
134             else {
135 97         226 my $e = $_[0]->{config}{esc_quote_lit};
136 97         243 $arg =~ s/$q/$e$q/g;
137 97         354 return "$q$arg$q";
138             }
139             }
140              
141             sub _quote_fld { # noop
142 0     0   0 return $_[1];
143             }
144              
145             sub express {
146 30     30 1 56 my $self = shift;
147 30         58 my $x = $_[0];
148 30 50       75 if ($SQL_ABSTRACT) {
149 30         83 $x = $self->canonize($x);
150             }
151 30         94 return $self->peel($x);
152             }
153              
154             sub config {
155 1     1 1 3 my $self = shift;
156 1         4 my ($key, $val) = @_;
157 1 50       5 if (!defined $key) {
    50          
158 0         0 return %{$self->{config}};
  0         0  
159             }
160             elsif (!defined $val) {
161 0         0 return $self->{config}{$key};
162             }
163             else {
164 1         7 return $self->{config}{$key} = $val;
165             }
166             }
167              
168              
169             # canonize - rewrite mixed hash/array expressions in canonical lispy
170             # array format - interpret like SQL::A
171             sub canonize {
172 69     69 1 47290 my $self = shift;
173 69         156 my ($expr) = @_;
174 69         140 my $ret = [];
175 69         137 my ($do,$is_op);
176             $is_op = sub {
177 544 100 66 544   2162 if (!defined $_[0] || ref $_[0]) {
178 8         31 return 0;
179             }
180 536 100       1092 if (!$_[1]) {
181 213 100       586 if (defined $self->{dispatch}{$_[0]}) {
182 63         160 1;
183             }
184             else {
185 150 50 66     980 puke "Unknown operator '$_[0]'" if (
      33        
186             $_[0] !~ /$$self{config}{safe_identifier}/ and
187             $_[0]=~/^-|[[:punct:]]/ and !looks_like_number($_[0])
188             );
189 150         670 0;
190             }
191             }
192             else {
193 323         478 grep /^\Q$_[0]\E$/, @{$type_table{$_[1]}};
  323         5840  
194             }
195 69         327 };
196 69         146 my $level=0;
197             $do = sub {
198 342     342   1031 my ($expr, $lhs, $arg_of) = @_;
199 342         950 for (ref $expr) {
200 342 100 100     1506 ($_ eq '' or blessed($expr)) && do {
201 111 50       265 if (defined $expr) {
202             # literal (value?)
203 111 100       807 return $self->{config}{bind} ? [ -bind => $expr ] : $expr;
204             }
205             else {
206 0         0 puke "undef not interpretable";
207             }
208             };
209 231 100       961 /REF|SCALAR/ && do { # literals
210 22 100       73 if ($_ eq 'SCALAR') {
    50          
211 17         91 return [-thing => $$expr] ; # never bind
212             }
213             elsif (ref $$expr eq 'ARRAY') {
214             # very SQL:A thing, but we'll do it
215 5         18 my @a = @$$expr;
216 5         11 my $thing = shift @a;
217 5         14 @a = map { $self->_quote_lit($_) } @a;
  5         16  
218 5 50       17 if ($self->{config}{bind}) {
219 0         0 push @{$self->{bind_values}}, @a;
  0         0  
220             }
221             else {
222 5 100       16 if ($self->{config}{anon_placeholder}) {
223 4         33 ($thing =~ s/\Q$$self{config}{anon_placeholder}\E/$_/) for @a;
224             }
225             }
226 5 100       41 return $lhs ? [-thing => $lhs, [-thing => $thing]] : [-thing => $thing];
227             }
228             };
229 209 100       597 /ARRAY/ && do {
230 32 100       97 if ($is_op->($$expr[0],'infix_distributable')) {
231             # handle implicit equality pairs in an array
232 12         36 my $op = shift @$expr;
233 12         29 my (@args,@flat);
234             # flatten
235 12 100       39 if (@$expr == 1) {
236 3         10 for (ref($$expr[0])) {
237 3 50       12 /ARRAY/ && do {
238 3         7 @flat = @{$$expr[0]};
  3         10  
239 3         8 last;
240             };
241 0 0       0 /HASH/ && do {
242 0         0 @flat = %{$$expr[0]};
  0         0  
243 0         0 last;
244             };
245 0         0 puke 'Huh?';
246             };
247             }
248             else {
249 9         29 @flat = @$expr; # already flat
250             }
251 12         45 while (@flat) {
252 25         55 my $elt = shift @flat;
253             # $DB::single=1 if !defined($elt);
254 25 100       69 if (!ref $elt) { # scalar means lhs of a pair or another op
255 14         104 push @args, $do->({$elt => shift @flat},$lhs,$op);
256             }
257             else {
258 11 100 66     102 next if (ref $elt eq 'ARRAY') && ! scalar @$elt or
      100        
      66        
259             (ref $elt eq 'HASH') && ! scalar %$elt;
260 10         89 push @args, $do->($elt,$lhs,$op);
261             }
262             }
263 12         61 return [$op => @args];
264             }
265 20 100 66     63 if ($is_op->($$expr[0]) and !$is_op->($$expr[0],'infix_distributable')) {
    100          
266             # some other op
267             return [ $$expr[0] => map {
268 4         20 $do->($_,undef,$$expr[0])
  7         39  
269             } @$expr[1..$#$expr] ];
270             }
271             elsif (ref $$expr[0] eq 'HASH') { #?
272             return [ $self->{config}{array_op} =>
273 3         10 map { $do->($_,$lhs,$self->{config}{array_op}) } @$expr ];
  6         36  
274             }
275             else { # is a plain list
276 13 100       40 if ($lhs) {
277             # implicit equality over array default op
278             return [ $self->{config}{array_op} => map {
279 4         11 defined() ?
280             [ $self->{config}{implicit_eq_op} => $lhs,
281 13 100       72 $do->($_,undef,$self->{config}{implicit_eq_op}) ] :
282             [ -is_null => $lhs ]
283             } @$expr ];
284             }
285             else {
286 9 100 66     100 if ($arg_of and any { $is_op->($arg_of, $_) }
  15         44  
287             qw/function infix_listarg predicate reduce/
288             ) {
289             # function argument - return list itself
290 6         25 return [ -list => map { $do->($_) } @$expr ];
  16         109  
291             }
292             else {
293             # distribute $array_op over implicit equality
294 3         46 return $do->([ $self->{config}{array_op} => @$expr ]);
295             }
296             }
297             }
298             };
299 177 50       550 /HASH/ && do {
300 177         623 my @k = keys %$expr;
301             #######
302 177 100       881 if (@k == 1) {
303 154         279 my $k = $k[0];
304             # single hashpair
305 154 100 100     362 if ($is_op->($k)) {
    100          
306 59 100       130 $is_op->($k,'infix_binary') && do {
307 33 50       101 puke "Expected LHS for $k" unless $lhs;
308 33 100       120 if (ref $$expr{$k} eq 'ARRAY') {
    100          
309             # apply binary operator and join with array default op
310             return [ $self->{config}{array_op} => map {
311             defined() ?
312             [ $k => $lhs, $do->($_)] :
313             { $self->{config}{ineq_op} => [-is_not_null => $lhs],
314             $self->{config}{implicit_eq_op} => [-is_null => $lhs]
315 2 100       28 }->{$k}
316 1         4 } @{$$expr{$k}} ]
  1         3  
317             }
318             elsif (defined $$expr{$k}) {
319 29         348 return [ $k => $lhs, $do->($$expr{$k},undef,$k) ]; #?
320             }
321             else { # IS (NOT) NULL
322 3 50       25 $k eq $self->{config}{ineq_op} && do {
323 3         25 return [ -is_not_null => $lhs ];
324             };
325 0 0       0 $k eq $self->{config}{implicit_eq_op} && do {
326 0         0 return [ -is_null => $lhs ];
327             };
328 0         0 puke "Can't handle undef as argument to $k";
329             }
330             };
331 26 100       85 $is_op->($k,'function') && do {
332 6         85 return [ $k => $do->($$expr{$k},undef,$k) ];
333             };
334 20 100       64 $is_op->($k,'infix_listarg') && do {
335 4         66 return [ $k => $lhs, $do->($$expr{$k},undef,$k) ];
336             };
337 16 100       38 $is_op->($k,'prefix') && do {
338 8         122 return [ $k => $do->($$expr{$k}) ];
339             };
340 8 100       21 $is_op->($k,'infix_distributable') && do {
341 4 50 33     32 if (!ref $$expr{$k} && $lhs) {
    100          
    50          
342 0         0 return [ $k => $lhs, $do->($$expr{$k}) ];
343             }
344             elsif ( ref $$expr{$k} eq 'HASH' ) {
345 1         3 my @ar = %{$$expr{$k}};
  1         6  
346 1         9 return $do->([$k=>@ar]); #?
347             }
348             elsif ( ref $$expr{$k} eq 'ARRAY') {
349 3         47 return $do->([$k => $$expr{$k}]);
350             }
351             else {
352 0         0 puke "arg type '".ref($$expr{$k})."' not expected for op '$k'";
353             }
354             };
355 4 100       9 $is_op->($k,'predicate') && do {
356             puke "predicate function '$k' requires an length 3 arrayref argument"
357 2 50 33     12 unless ref $$expr{$k} eq 'ARRAY' and @{$$expr{$k}} == 3;
  2         9  
358             return [ $k => [-thing => $$expr{$k}->[0]],
359             $do->($$expr{$k}->[1], undef, $k),
360 2         26 $do->($$expr{$k}->[2], undef, $k) ];
361             };
362 2 50       6 $is_op->($k,'reduce') && do {
363             puke "reduce function '$k' requires an length 5 arrayref argument"
364 2 50 33     9 unless ref $$expr{$k} eq 'ARRAY' and @{$$expr{$k}} == 5;
  2         8  
365             return [ $k => [-thing => $$expr{$k}->[0]],
366             $do->($$expr{$k}->[1], undef, $k),
367             [-thing => $$expr{$k}->[2]],
368             $do->($$expr{$k}->[3], undef, $k),
369 2         27 $do->($$expr{$k}->[4], undef, $k)];
370             };
371 0         0 puke "Operator $k not expected";
372             }
373             elsif (ref($$expr{$k}) && ref($$expr{$k}) ne 'SCALAR') {
374             # $k is an LHS
375 38         519 return $do->($$expr{$k}, $k, undef);
376             }
377             else {
378             # implicit equality
379             return defined $$expr{$k} ?
380             [ $self->{config}{implicit_eq_op} => $k,
381 57 100       544 $do->($$expr{$k},undef,$self->{config}{implicit_eq_op}) ] :
382             [ -is_null => $k ];
383             }
384             }
385             #######
386             else {
387             # >1 hashpair
388 23         43 my @args;
389 23         53 for my $k (@k) {
390             # all keys are ops, or none is - otherwise barf
391 52 100 66     126 if ( $is_op->($k, 'infix_binary') ) {
    100          
    50          
392 6 50       19 puke "No LHS provided for implicit $$self{config}{hash_op}" unless defined $lhs;
393 6         34 push @args, $do->({$k => $$expr{$k}},$lhs);
394             }
395             elsif ( $is_op->($k, 'prefix') || $is_op->($k,'function') ) {
396 3         60 push @args, [ $k => $do->($$expr{$k},undef, $k) ];
397             }
398             elsif (!$is_op->($k)) {
399 43         268 push @args, $do->({$k => $$expr{$k}});
400             }
401             else {
402 0         0 puke "Problem handling operator '$k'";
403             }
404             }
405 23         108 return [ $self->{config}{hash_op} => @args ];
406             }
407             };
408             }
409 69         740 };
410 69         201 $ret = $do->($expr);
411 69         315 return $ret;
412             }
413              
414             # peel - recurse $args = [ -op, @args ] to create complete production
415             sub peel {
416 437     437 1 852 my ($self, $args) = @_;
417              
418 437 50 100     2046 if (!defined $args) {
    100          
    50          
419 0         0 return '';
420             }
421             elsif (!ref $args or blessed($args)) { # single literal argument
422 189         639 return $args;
423             }
424             elsif (ref $args eq 'ARRAY') {
425 248 50       598 return '' unless (@$args);
426 248         468 my $op = shift @$args;
427 248 50       678 puke "'$op' : unknown operator" unless $self->{dispatch}{$op};
428 248         475 my $expr = $self->_dispatch( $op, [map { $self->peel($_) } @$args] );
  383         872  
429 248 100       554 if (grep /\Q$op\E/, @{$type_table{infix_distributable}}) {
  248         2952  
430             # group
431 34         193 return "($expr)"
432             }
433             else {
434 214         889 return $expr;
435             }
436             }
437             else {
438 0         0 puke "Can only peel() arrayrefs, scalars or literals";
439             }
440             }
441              
442             ### writers
443              
444             sub infix_binary {
445 76     76 0 201 my ($self, $op, $args) = @_;
446 76 50 33     657 unless ($op and $args and !ref($op)
      33        
      33        
447             and ref($args) eq 'ARRAY'){
448 0         0 puke "arg1 must be scalar, arg2 must be arrayref";
449             }
450 76 50       214 unless ( @$args == 2 ) {
451 0         0 puke "For $op, arg2 must have length 2";
452             }
453 76         213 return '('.join(" ", $$args[0], _write_op($op), $$args[1]).')';
454             }
455              
456 3     3 0 13 sub infix_listarg { infix_binary(@_) }
457              
458             sub infix_distributable {
459 35     35 0 88 my ($self, $op, $args) = @_;
460 35 50 33     313 unless ($op and $args and !ref($op)
      33        
      33        
461             and ref($args) eq 'ARRAY'){
462 0         0 puke "arg1 must be scalar, arg2 must be arrayref";
463             }
464 35         124 $op = _write_op($op);
465 35         140 return join(" $op ", @$args);
466             }
467              
468             sub prefix {
469 12     12 0 32 my ($self, $op, $args) = @_;
470 12 50 33     133 unless ($op and $args and !ref($op)
      33        
      33        
471             and ref($args) eq 'ARRAY'){
472 0         0 puke "arg1 must be scalar, arg2 must be arrayref";
473             }
474 12 50       41 unless (@$args == 1) {
475 0         0 puke "For $op, arg2 must have length 1"
476             }
477 12         33 return _write_op($op)." ".$$args[0];
478             }
479              
480             sub postfix {
481 6     6 0 18 my ($self, $op, $args) = @_;
482 6 50 33     72 unless ($op and $args and !ref($op)
      33        
      33        
483             and ref($args) eq 'ARRAY'){
484 0         0 puke "arg1 must be scalar, arg2 must be arrayref";
485             }
486 6 50       26 unless (@$args == 1) {
487 0         0 puke "For $op, arg2 must have length 1"
488             }
489 6         27 return $$args[0]." "._write_op($op);
490             }
491              
492             sub function {
493 6     6 0 17 my ($self, $op, $args) = @_;
494 6 50 33     626 unless ($op and $args and !ref($op)
      33        
      33        
495             and ref($args) eq 'ARRAY'){
496 0         0 puke "arg1 must be scalar, arg2 must be arrayref";
497             }
498 6         21 return _write_op($op).'('.join(',',@$args).')';
499             }
500              
501             sub predicate {
502 2     2 0 5 my ($self, $op, $args) = @_;
503 2 50 33     29 unless ($op and $args and !ref($op)
      33        
      33        
504             and ref($args) eq 'ARRAY'){
505 0         0 puke "arg1 must be scalar, arg2 must be arrayref";
506             }
507 2 50       6 unless ( @$args == 3 ) {
508 0         0 puke "For $op, arg2 must have length 3";
509             }
510 2         5 return _write_op($op)."("."$$args[0] IN $$args[1] WHERE $$args[2]".")";
511             }
512              
513             sub extract {
514 0     0 0 0 my ($self, $op, $args) = @_;
515 0 0 0     0 unless ($op and $args and !ref($op)
      0        
      0        
516             and ref($args) eq 'ARRAY'){
517 0         0 puke "arg1 must be scalar, arg2 must be arrayref";
518             }
519 0 0       0 unless ( @$args == 3 ) {
520 0         0 puke "For $op, arg2 must have length 3";
521             }
522 0         0 return _write_op($op)."("."$$args[0] IN $$args[1] | $$args[2]".")";
523             }
524              
525             sub reduce {
526 2     2 0 6 my ($self, $op, $args) = @_;
527 2 50 33     25 unless ($op and $args and !ref($op)
      33        
      33        
528             and ref($args) eq 'ARRAY'){
529 0         0 puke "arg1 must be scalar, arg2 must be arrayref";
530             }
531 2 50       7 unless ( @$args == 5 ) {
532 0         0 puke "For $op, arg2 must have length 5";
533             }
534 2         5 return _write_op($op)."("."$$args[0] = $$args[1], $$args[2] IN $$args[3] | $$args[4]".")";
535             }
536              
537             # bind either:
538             # - pulls out literals, pushes them into {bind_values}, and replaces them
539             # with '?' in the produced statement (a la SQL:A), -or-
540             # - identifies named parameters in the expression and pushes these into
541             # {parameters}, leaving them in the produced statement
542             sub bind { # special
543 81     81 0 180 my ($self, $op, $args) = @_;
544 81 50 33     719 unless ($op and $args and !ref($op)
      33        
      33        
545             and ref($args) eq 'ARRAY'){
546 0         0 puke "arg1 must be scalar, arg2 must be arrayref";
547             }
548 81 50       523 if ($$args[0] =~ $self->{config}{parameter_sigil}) {
549 0         0 push @{$self->{parameters}}, $$args[0];
  0         0  
550             }
551             else {
552 81         338 push @{$self->{bind_values}},
553 81 50       148 $self->{config}{anon_placeholder} ? $$args[0] :
554             $self->_quote_lit($$args[0]);
555             }
556             return $self->{config}{anon_placeholder} ?
557             $self->{config}{anon_placeholder} :
558 81 50       260 $self->_quote_lit($$args[0]);
559             }
560              
561             sub list { # special
562 5     5 0 17 my ($self, $op, $args) = @_;
563 5 50 33     83 unless ($op and $args and !ref($op)
      33        
      33        
564             and ref($args) eq 'ARRAY'){
565 0         0 puke "arg1 must be scalar, arg2 must be arrayref";
566             }
567 5         35 my ($l,$r) = split '',$self->{config}{list_braces};
568 5         28 return $l.join(',',@$args).$r;
569             }
570              
571             sub thing { # special
572 29     29 0 67 my ($self, $op, $args) = @_;
573 29         85 return join(' ',@$args);
574             }
575              
576             sub _write_op {
577 139     139   281 my ($op) = @_;
578 139         421 $op =~ s/^-//;
579 139         849 my $c = (caller(1))[3];
580 139 50       442 return '' if ($op eq '()');
581 139 100       389 return join(' ', map { ($c=~/infix|prefix|postfix/) ? uc $_ : $_ } split /_/,$op);
  153         1336  
582             }
583              
584             =head1 NAME
585              
586             Neo4j::Cypher::Abstract::Peeler - Parse Perl structures as expressions
587              
588             =head1 SYNOPSIS
589              
590             =head1 DESCRIPTION
591              
592             C allows the user to write L
593             Cypher|https://neo4j.com/docs/developer-manual/current/cypher/> query
594             language expressions as Perl data structures. The interpretation of
595             data structures follows L very closely, but attempts to
596             be more systematic and general.
597              
598             C only produces expressions, typically used as arguments to
599             C clauses. It is integrated into L,
600             which produces full Cypher statements.
601              
602             Like L, C translates scalars, scalar refs,
603             array refs, and hash refs syntactically to create expression
604             components such as functions and operators. The contents of the
605             scalars or references are generally operators or the arguments to
606             operators or functions.
607              
608             Contents of scalar references are always treated as literals and
609             inserted into the expression verbatim.
610              
611             =over
612              
613             =item * Functions
614              
615             Ordinary functions in Cypher are written as the name of the function
616             preceded by a dash. They can be expressed as follows:
617              
618             { -func => $arg }
619             [ -func => $arg ]
620             \"func($arg)"
621              
622             { -sin => $pi/2 }
623             # returns sin(/2)
624              
625             =item * Infix Operators
626              
627             Infix operators, like equality (C<=>), inequality (CE>),
628             binary operations (C<+,-,*,/>), and certain string operators
629             (C<-contains>, C<-starts_with>, C) are expressed as follows:
630              
631             { $expr1 => { $infix_op => $expr2 } }
632              
633             { 'n.name' => { '<>' => 'Fred' } }
634             # returns n.name <> "Fred"
635              
636             This may seem like overkill, but comes in handy for...
637              
638             =item * AND and OR
639              
640             C implements the L convention that hash refs
641             represent conditions joined by C and array refs represent
642             conditions joined by C. Key-value pairs and array value pairs are
643             interpreted as an implicit equalities to be ANDed or ORed.
644              
645             { $lhs1 => $rhs1, $lhs2 => $rhs2 }
646             { al => 'king', 'eddie' => 'prince', vicky => 'queen' }
647             # returns al = "king" AND eddie = "prince" AND vicky = "queen"
648              
649             [ $lhs1 => $rhs1, $lhs2 => $rhs2 ]
650             [ 'a.name' => 'Fred', 'a.name' => 'Barney']
651             # returns a.name = "Fred" OR a.name = "Barney"
652              
653             A single left-hand side can be "distributed" over a set of conditions,
654             with corresponding conjunction:
655              
656             { zzyxx => [ 'narf', 'boog', 'frelb' ] } # implicit equality, OR
657             # returns zzyxx = "narf" OR zzyxx = "boog" OR zzyxx = "frelb"
658             { zzyxx => { '<>' => 'narf', '<>' => 'boog' } } # explicit infix, AND
659             # returns zzyxx <> "narf" AND zzyxx <> "boog"
660             { zzyxx => [ '<>' => 'narf', -contains => 'boog' ] } # explicit infix, OR
661             # returns zzyxx <> "narf" OR zzyxx CONTAINS "boog"
662              
663             =item * Expressing null
664              
665             C can be used to express NULL mostly as in L so
666             that the following are equivalent
667              
668             { a.name => { '<>' => undef}, b.name => undef}
669             { -is_not_null => 'a.name', -is_null => 'b.name' }
670             # returns a.name IS NOT NULL AND b.name IS NULL
671              
672             =item * Predicates: -all, -any, -none, -single, -filter
673              
674             These Cypher functions have the form
675              
676             func(variable IN list WHERE predicate)
677              
678             To render these, provide an array ref of the three arguments in order:
679              
680             { -all => ['x', [1,2,3], {'x' => 3}] }
681             # returns all(x IN [1,2,3] WHERE x = 3)
682              
683             =item * List arguments
684              
685             For cypher expressions that accept lists (arrays in square brackets), use arrayrefs:
686              
687             { 'n.name' => { -in => ['fred','wilma','pebbles'] }}
688             # returns n.name IN ['fred','wilma','pebbles']
689              
690             =back
691              
692             =head2 Parameters and Bind Values
693              
694             Cypher parameters (which use the '$' sigil) may be included in
695             expressions (with the dollar sign appropriately escaped). These are
696             collected during parsing and can be reported in order with the the
697             C method.
698              
699             L automatically collects literal values and replaces
700             them with anonymous placeholders (C), returning an array of values
701             for binding in L. C will collect values and report them
702             with the C method. If the config key
703             C is set:
704              
705             $peeler->{config}{anon_placeholder} = '?'
706              
707             then C will also do the replacement in the final expression
708             production like L.
709              
710             The real reason to pay attention to literal values is to be able to
711             appropriately quote them in the final production. When
712             C is not set (default), then an attempt is made to
713             correctly quote string values and such.
714              
715             =head1 GUTS
716              
717             TBD
718              
719             =head2 The config hash
720              
721             TBD
722              
723             =head1 METHODS
724              
725             =over
726              
727             =item express()
728              
729             Canonize, then peel.
730              
731             =item canonize()
732              
733             Render SQL:A-like expression into a canonical lisp-like array tree.
734              
735             =item peel()
736              
737             Render a canonical tree as a Cypher string expression
738              
739             =item parameters()
740              
741             Get a list in order of all named parameters.
742              
743             =item bind_values()
744              
745             Get a list of bind values in order that were replaced by the anonymous
746             placeholder.
747              
748             =back
749              
750             =head1 SEE ALSO
751              
752             L, L.
753              
754             =head1 AUTHOR
755              
756             Mark A. Jensen
757             CPAN: MAJENSEN
758             majensen -at- cpan -dot- org
759              
760             =head1 COPYRIGHT
761              
762             (c) 2017 Mark A. Jensen
763              
764             =cut
765              
766             1;