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