|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
  
 
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 NAME  | 
| 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Parse::BooleanLogic - parser of boolean expressions  | 
| 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 SYNOPSIS  | 
| 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     use Parse::BooleanLogic;  | 
| 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     use Data::Dumper;  | 
| 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $parser = Parse::BooleanLogic->new( operators => ['', 'OR'] );  | 
| 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $tree = $parser->as_array( 'label:parser subject:"boolean logic"' );  | 
| 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     print Dumper($tree);  | 
| 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $parser = new Parse::BooleanLogic;  | 
| 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $tree = $parser->as_array( 'x = 10' );  | 
| 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     print Dumper($tree);  | 
| 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
18
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $tree = $parser->as_array( 'x = 10 OR (x > 20 AND x < 30)' );  | 
| 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     print Dumper($tree);  | 
| 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # custom parsing using callbacks  | 
| 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $parser->parse(  | 
| 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         string   => 'x = 10 OR (x > 20 AND x < 30)',  | 
| 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         callback => {  | 
| 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             open_paren   => sub { ... },  | 
| 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             operator     => sub { ... },  | 
| 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             operand      => sub { ... },  | 
| 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             close_paren  => sub { ... },  | 
| 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             error        => sub { ... },  | 
| 
30
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         },  | 
| 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     );  | 
| 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 DESCRIPTION  | 
| 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This module is quite fast parser for boolean expressions. Originally it's been writen for  | 
| 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Request Tracker to parse SQL like expressions and it's still capable, but  | 
| 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 it can be used to parse other boolean logic sentences with OPERANDs joined using  | 
| 
38
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 binary OPERATORs and grouped and nested using parentheses (OPEN_PAREN and CLOSE_PAREN).  | 
| 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Operand is not qualified strictly what makes parser flexible enough to parse different  | 
| 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 things, for example:  | 
| 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
43
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # SQL like expressions  | 
| 
44
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     (task.status = "new" OR task.status = "open") AND task.owner_id = 123  | 
| 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
46
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Google like search syntax used in Gmail and other service  | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     subject:"some text" (from:me OR to:me) label:todo !label:done  | 
| 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
49
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Binary boolean logic expressions  | 
| 
50
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     (a | b) & (c | d)  | 
| 
51
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 You can change literals used for boolean operators and parens. Read more  | 
| 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 about this in description of constructor's arguments.  | 
| 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
55
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 As you can see quoted strings are supported. Read about that below in  | 
| 
56
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 L.  | 
| 
57
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
58
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
59
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
60
 | 
10
 | 
 
 | 
 
 | 
  
10
  
 | 
 
 | 
8220
 | 
 use 5.008;  | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
37
 | 
    | 
| 
61
 | 
10
 | 
 
 | 
 
 | 
  
10
  
 | 
 
 | 
53
 | 
 use strict;  | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19
 | 
    | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
201
 | 
    | 
| 
62
 | 
10
 | 
 
 | 
 
 | 
  
10
  
 | 
 
 | 
46
 | 
 use warnings;  | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
    | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
560
 | 
    | 
| 
63
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
64
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 package Parse::BooleanLogic;  | 
| 
65
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
66
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our $VERSION = '0.10';  | 
| 
67
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
68
 | 
10
 | 
 
 | 
 
 | 
  
10
  
 | 
 
 | 
63
 | 
 use constant OPERAND     => 1;  | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
27
 | 
    | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
891
 | 
    | 
| 
69
 | 
10
 | 
 
 | 
 
 | 
  
10
  
 | 
 
 | 
67
 | 
 use constant OPERATOR    => 2;  | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19
 | 
    | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
548
 | 
    | 
| 
70
 | 
10
 | 
 
 | 
 
 | 
  
10
  
 | 
 
 | 
58
 | 
 use constant OPEN_PAREN  => 4;  | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
    | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
532
 | 
    | 
| 
71
 | 
10
 | 
 
 | 
 
 | 
  
10
  
 | 
 
 | 
66
 | 
 use constant CLOSE_PAREN => 8;  | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
    | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
532
 | 
    | 
| 
72
 | 
10
 | 
 
 | 
 
 | 
  
10
  
 | 
 
 | 
61
 | 
 use constant STOP        => 16;  | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19
 | 
    | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
752
 | 
    | 
| 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my @tokens = qw[OPERAND OPERATOR OPEN_PAREN CLOSE_PAREN STOP];  | 
| 
74
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
75
 | 
10
 | 
 
 | 
 
 | 
  
10
  
 | 
 
 | 
5588
 | 
 use Regexp::Common qw(delimited);  | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
27415
 | 
    | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
57
 | 
    | 
| 
76
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $re_delim = qr{$RE{delimited}{-delim=>qq{\'\"}}{-esc=>'\\'}};  | 
| 
77
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 METHODS  | 
| 
79
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
80
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 Building parser  | 
| 
81
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
82
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head3 new  | 
| 
83
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
84
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 A constuctor, takes the following named arguments:  | 
| 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
86
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =over 4  | 
| 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
88
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item operators, default is ['AND' 'OR']  | 
| 
89
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
90
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Pair of literal strings representing boolean operators AND and OR,  | 
| 
91
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 pass it as array reference. For example:  | 
| 
92
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
93
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # from t/custom_ops.t  | 
| 
94
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $parser = Parse::BooleanLogic->new( operators => [qw(& |)] );  | 
| 
95
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
96
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # from t/custom_googlish.t  | 
| 
97
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $parser = Parse::BooleanLogic->new( operators => ['', 'OR'] );  | 
| 
98
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
99
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 It's ok to have any operators and even empty.  | 
| 
100
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
101
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item parens, default is ['(', ')']  | 
| 
102
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
103
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Pair of literal strings representing parentheses, for example it's  | 
| 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 possible to use curly braces:  | 
| 
105
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
106
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # from t/custom_parens.t  | 
| 
107
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $parser = Parse::BooleanLogic->new( parens => [qw({ })] );  | 
| 
108
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
109
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 No matter which pair is used parens must be balanced in expression.  | 
| 
110
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
111
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =back  | 
| 
112
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
113
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This constructor compiles several heavy weight regular expressions  | 
| 
114
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 so it's better avoid building object each time right before parsing,  | 
| 
115
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 but instead use global or cached one.  | 
| 
116
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
117
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
118
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
119
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub new {  | 
| 
120
 | 
9
 | 
 
 | 
 
 | 
  
9
  
 | 
  
1
  
 | 
5745
 | 
     my $proto = shift;  | 
| 
121
 | 
9
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
79
 | 
     my $self = bless {}, ref($proto) || $proto;  | 
| 
122
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
40
 | 
     return $self->init( @_ );  | 
| 
123
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
124
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
125
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head3 init  | 
| 
126
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
127
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 An initializer, called from the constructor. Compiles regular expressions  | 
| 
128
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 and do other things with constructor's arguments. Returns this object back.  | 
| 
129
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
130
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
131
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
132
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub init {  | 
| 
133
 | 
9
 | 
 
 | 
 
 | 
  
9
  
 | 
  
1
  
 | 
22
 | 
     my $self = shift;  | 
| 
134
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
26
 | 
     my %args = @_;  | 
| 
135
 | 
9
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
46
 | 
     if ( $args{'operators'} ) {  | 
| 
136
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
         my @ops = map lc $_, @{ $args{'operators'} };  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13
 | 
    | 
| 
137
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
         $self->{'operators'} = [ @ops ];  | 
| 
138
 | 
2
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
9
 | 
         @ops = reverse @ops if length $ops[1] > length $ops[0];  | 
| 
139
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
         foreach ( @ops ) {  | 
| 
140
 | 
4
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
15
 | 
             unless ( length ) {  | 
| 
141
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
                 $_ = "(?<=\\s)";  | 
| 
142
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
143
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             else {  | 
| 
144
 | 
3
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
14
 | 
                 if ( /^\w/ ) {  | 
| 
145
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
                     $_ = '\b'. "\Q$_\E";  | 
| 
146
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 }  | 
| 
147
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 else {  | 
| 
148
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
                     $_ = "\Q$_\E";  | 
| 
149
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 }  | 
| 
150
 | 
3
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
12
 | 
                 if ( /\w$/ ) {  | 
| 
151
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
                     $_ .= '\b';  | 
| 
152
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 }  | 
| 
153
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
154
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
126
 | 
             $self->{'re_operator'} = qr{(?:$ops[0]|$ops[1])}i;  | 
| 
155
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
156
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
157
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34
 | 
         $self->{'operators'} = [qw(and or)];  | 
| 
158
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
33
 | 
         $self->{'re_operator'} = qr{\b(?:AND|OR)\b}i;  | 
| 
159
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
160
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
161
 | 
9
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
34
 | 
     if ( $args{'parens'} ) {  | 
| 
162
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
         $self->{'parens'} = $args{'parens'};  | 
| 
163
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
33
 | 
         $self->{'re_open_paren'} = qr{\Q$args{'parens'}[0]\E};  | 
| 
164
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
24
 | 
         $self->{'re_close_paren'} = qr{\Q$args{'parens'}[1]\E};  | 
| 
165
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
166
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34
 | 
         $self->{'re_open_paren'} = qr{\(};  | 
| 
167
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
25
 | 
         $self->{'re_close_paren'} = qr{\)};  | 
| 
168
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
169
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
488
 | 
     $self->{'re_tokens'}  = qr{(?:$self->{'re_operator'}|$self->{'re_open_paren'}|$self->{'re_close_paren'})};  | 
| 
170
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # the following need some explanation  | 
| 
171
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # operand is something consisting of delimited strings and other strings that are not our major tokens  | 
| 
172
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # so it's a (delim string or anything until a token, ['"](start of a delim) or \Z) - this is required part  | 
| 
173
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # then you can have zero or more ocurences of above group, but with one exception - "anything" can not start with a token or ["']  | 
| 
174
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1126
 | 
     $self->{'re_operand'} = qr{(?:$re_delim|.+?(?=$self->{re_tokens}|["']|\Z))(?:$re_delim|(?!$self->{re_tokens}|["']).+?(?=$self->{re_tokens}|["']|\Z))*};  | 
| 
175
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
176
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
41
 | 
     foreach my $re (qw(re_operator re_operand re_open_paren re_close_paren)) {  | 
| 
177
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1448
 | 
         $self->{"m$re"} = qr{\G($self->{$re})};  | 
| 
178
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
179
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
180
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
49
 | 
     return $self;  | 
| 
181
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
182
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
183
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
184
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 Parsing expressions  | 
| 
185
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
186
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head3 as_array $string [ %options ]  | 
| 
187
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
188
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Takes a string and parses it into perl structure, where parentheses represented using  | 
| 
189
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 array references, operands are hash references with one key/value pair: operand,  | 
| 
190
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 when binary operators are simple scalars. So string C 20 AND x < 30)>  | 
| 
191
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 is parsed into the following structure:  | 
| 
192
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     [  | 
| 
194
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         { operand => 'x = 10' },  | 
| 
195
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'OR',  | 
| 
196
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         [  | 
| 
197
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             { operand => 'x > 20' },  | 
| 
198
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             'AND',  | 
| 
199
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             { operand => 'x < 30' },  | 
| 
200
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         ]  | 
| 
201
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ]  | 
| 
202
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
203
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Aditional options:  | 
| 
204
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
205
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =over 4  | 
| 
206
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
207
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item operand_cb - custom operands handler, for example:  | 
| 
208
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
209
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $tree = $parser->as_array(  | 
| 
210
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         "some string",  | 
| 
211
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         operand_cb => sub {  | 
| 
212
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $op = shift;  | 
| 
213
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             if ( $op =~ m/^(!?)(label|subject|from|to):(.*)/ ) {  | 
| 
214
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 ...  | 
| 
215
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             } else {  | 
| 
216
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 die "You have an error in your query, in '$op'";  | 
| 
217
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
218
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         },  | 
| 
219
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     );  | 
| 
220
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
221
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
222
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item error_cb - custom errors handler  | 
| 
223
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
224
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $tree = $parser->as_array(  | 
| 
225
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         "some string",  | 
| 
226
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         error_cb => sub {  | 
| 
227
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $msg = shift;  | 
| 
228
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             MyParseException->throw($msg);  | 
| 
229
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         },  | 
| 
230
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     );  | 
| 
231
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
232
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =back  | 
| 
233
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
234
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
235
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
236
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 { # static variables  | 
| 
237
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
238
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my ($tree, $node, @pnodes);  | 
| 
239
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my %callback;  | 
| 
240
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 $callback{'open_paren'} = sub {  | 
| 
241
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     push @pnodes, $node;  | 
| 
242
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     push @{ $pnodes[-1] }, $node = []  | 
| 
243
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 };  | 
| 
244
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 $callback{'close_paren'}     = sub { $node = pop @pnodes };  | 
| 
245
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 $callback{'operator'} = sub { push @$node, $_[0] };  | 
| 
246
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 $callback{'operand'} = sub { push @$node, { operand => $_[0] } };  | 
| 
247
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
248
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub as_array {  | 
| 
249
 | 
  
0
  
 | 
 
 | 
 
 | 
  
0
  
 | 
  
1
  
 | 
0
 | 
     my $self = shift;  | 
| 
250
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $string = shift;  | 
| 
251
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my %arg = (@_);  | 
| 
252
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
253
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     $node = $tree = [];  | 
| 
254
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     @pnodes = ();  | 
| 
255
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
256
 | 
  
0
  
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
     unless ( $arg{'operand_cb'} || $arg{'error_cb'} ) {  | 
| 
257
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         $self->parse(string => $string, callback => \%callback);  | 
| 
258
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         return $tree;  | 
| 
259
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
260
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
261
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my %cb = %callback;  | 
| 
262
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     if ( $arg{'operand_cb'} ) {  | 
| 
263
 | 
  
0
  
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
         $cb{'operand'} = sub { push @$node, $arg{'operand_cb'}->( $_[0] ) };  | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
264
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
265
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     $cb{'error'} = $arg{'error_cb'} if $arg{'error_cb'};  | 
| 
266
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     $self->parse(string => $string, callback => \%cb);  | 
| 
267
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     return $tree;  | 
| 
268
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 } }  | 
| 
269
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
270
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head3 parse  | 
| 
271
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
272
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Takes named arguments: string and callback. Where the first one is scalar with  | 
| 
273
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 expression, the latter is a reference to hash with callbacks: open_paren, operator  | 
| 
274
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 operand, close_paren and error. Callback for errors is optional and parser dies if  | 
| 
275
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 it's omitted. Each callback is called when parser finds corresponding element in the  | 
| 
276
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 string. In all cases the current match is passed as argument into the callback.  | 
| 
277
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
278
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Here is simple example based on L method:  | 
| 
279
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
280
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # result tree and the current group  | 
| 
281
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my ($tree, $node);  | 
| 
282
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $tree = $node = [];  | 
| 
283
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
284
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # stack with nested groups, outer most in the bottom, inner on the top  | 
| 
285
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my @pnodes = ();  | 
| 
286
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
287
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my %callback;  | 
| 
288
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # on open_paren put the current group on top of the stack,  | 
| 
289
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # create new empty group and at the same time put it into  | 
| 
290
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # the end of previous one  | 
| 
291
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $callback{'open_paren'} = sub {  | 
| 
292
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         push @pnodes, $node;  | 
| 
293
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         push @{ $pnodes[-1] }, $node = []  | 
| 
294
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     };  | 
| 
295
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
296
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # on close_paren just switch to previous group by taking it  | 
| 
297
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # from the top of the stack  | 
| 
298
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $callback{'close_paren'} = sub { $node = pop @pnodes };  | 
| 
299
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
300
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # push binary operators as is and operands as hash references  | 
| 
301
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $callback{'operator'} = sub { push @$node, $_[0] };  | 
| 
302
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $callback{'operand'}  = sub { push @$node, { operand => $_[0] } };  | 
| 
303
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
304
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # run parser  | 
| 
305
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $parser->parse( string => $string, callback => \%callback );  | 
| 
306
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
307
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return $tree;  | 
| 
308
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
309
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Using this method you can build other representations of an expression.  | 
| 
310
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
311
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
312
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
313
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub parse {  | 
| 
314
 | 
33
 | 
 
 | 
 
 | 
  
33
  
 | 
  
1
  
 | 
139658
 | 
     my $self = shift;  | 
| 
315
 | 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
146
 | 
     my %args = (  | 
| 
316
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         string => '',  | 
| 
317
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         callback => {},  | 
| 
318
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         @_  | 
| 
319
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     );  | 
| 
320
 | 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
95
 | 
     my ($string, $cb) = @args{qw(string callback)};  | 
| 
321
 | 
33
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
96
 | 
     $string = '' unless defined $string;  | 
| 
322
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
323
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # States  | 
| 
324
 | 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
55
 | 
     my $want = OPERAND | OPEN_PAREN | STOP;  | 
| 
325
 | 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
45
 | 
     my $last = 0;  | 
| 
326
 | 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
55
 | 
     my $depth = 0;  | 
| 
327
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
328
 | 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
43
 | 
     while (1) {  | 
| 
329
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # State Machine  | 
| 
330
 | 
143
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
2238
 | 
         if ( $string =~ /\G\s+/gc ) {  | 
| 
 
 | 
 
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
331
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
332
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         elsif ( ($want & OPERATOR   ) && $string =~ /$self->{'mre_operator'}/gc ) {  | 
| 
333
 | 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
67
 | 
             $cb->{'operator'}->( $1 );  | 
| 
334
 | 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
105
 | 
             $last = OPERATOR;  | 
| 
335
 | 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
27
 | 
             $want = OPERAND | OPEN_PAREN;  | 
| 
336
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
337
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         elsif ( ($want & OPEN_PAREN ) && $string =~ /$self->{'mre_open_paren'}/gc ) {  | 
| 
338
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
61
 | 
             $cb->{'open_paren'}->( $1 );  | 
| 
339
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
110
 | 
             $depth++;  | 
| 
340
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
22
 | 
             $last = OPEN_PAREN;  | 
| 
341
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
25
 | 
             $want = OPERAND | OPEN_PAREN;  | 
| 
342
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         elsif ( ($want & CLOSE_PAREN) && $string =~ /$self->{'mre_close_paren'}/gc ) {  | 
| 
344
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
53
 | 
             $cb->{'close_paren'}->( $1 );  | 
| 
345
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
63
 | 
             $depth--;  | 
| 
346
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
23
 | 
             $last = CLOSE_PAREN;  | 
| 
347
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
23
 | 
             $want = OPERATOR;  | 
| 
348
 | 
14
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
30
 | 
             $want |= $depth? CLOSE_PAREN : STOP;  | 
| 
349
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
350
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         elsif ( ($want & OPERAND    ) && $string =~ /$self->{'mre_operand'}/gc ) {  | 
| 
351
 | 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
147
 | 
             my $m = $1;  | 
| 
352
 | 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
199
 | 
             $m=~ s/\s+$//;  | 
| 
353
 | 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
157
 | 
             $cb->{'operand'}->( $m );  | 
| 
354
 | 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
320
 | 
             $last = OPERAND;  | 
| 
355
 | 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
70
 | 
             $want = OPERATOR;  | 
| 
356
 | 
48
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
108
 | 
             $want |= $depth? CLOSE_PAREN : STOP;  | 
| 
357
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
358
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         elsif ( ($want & STOP) && $string =~ /\G\s*$/igc ) {  | 
| 
359
 | 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
64
 | 
             $last = STOP;  | 
| 
360
 | 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
60
 | 
             last;  | 
| 
361
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
362
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         else {  | 
| 
363
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             last;  | 
| 
364
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
365
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
366
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
367
 | 
33
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
131
 | 
     if (!$last || !($want & $last)) {  | 
| 
368
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         my $tmp = substr( $string, 0, pos($string) );  | 
| 
369
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         $tmp .= '>>>here<<<'. substr($string, pos($string));  | 
| 
370
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         my $msg = "Incomplete or incorrect expression, expecting a ". $self->bitmask_to_string($want) ." in '$tmp'";  | 
| 
371
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         $cb->{'error'}? $cb->{'error'}->($msg): die $msg;  | 
| 
372
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         return;  | 
| 
373
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
374
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
375
 | 
33
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
135
 | 
     if ( $depth ) {  | 
| 
376
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         my $msg = "Incomplete query, $depth paren(s) isn't closed in '$string'";  | 
| 
377
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         $cb->{'error'}? $cb->{'error'}->($msg): die $msg;  | 
| 
378
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         return;  | 
| 
379
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
380
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
381
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
382
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub bitmask_to_string {  | 
| 
383
 | 
  
0
  
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
     my $self = shift;  | 
| 
384
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $mask = shift;  | 
| 
385
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
386
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my @res;  | 
| 
387
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     for( my $i = 0; $i < @tokens; $i++ ) {  | 
| 
388
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         next unless $mask & (1<<$i);  | 
| 
389
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         push @res, $tokens[$i];  | 
| 
390
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
391
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
392
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $tmp = join ', ', splice @res, 0, -1;  | 
| 
393
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     unshift @res, $tmp if $tmp;  | 
| 
394
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     return join ' or ', @res;  | 
| 
395
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
396
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
397
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 Quoting and dequoting  | 
| 
398
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
399
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This module supports quoting with single quote ' and double ",  | 
| 
400
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 literal quotes escaped with \.  | 
| 
401
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
402
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 from L with ' and " as delimiters.  | 
| 
403
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
404
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head3 q, qq, fq and dq  | 
| 
405
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
406
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Four methods to work with quotes:  | 
| 
407
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
408
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =over 4  | 
| 
409
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
410
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item q - quote a string with single quote character.  | 
| 
411
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
412
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item qq - quote a string with double quote character.  | 
| 
413
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
414
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item fq - quote with single if string has no single quote character, otherwisee use double quotes.  | 
| 
415
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
416
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item dq - delete either single or double quotes from a string if it's quoted.  | 
| 
417
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
418
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =back  | 
| 
419
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
420
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 All four works either in place or return result, for example:  | 
| 
421
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
422
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $parser->q($str); # inplace  | 
| 
423
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
424
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $q = $parser->q($s); # $s is untouched  | 
| 
425
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
426
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
427
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
428
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub q {  | 
| 
429
 | 
8
 | 
  
100
  
 | 
 
 | 
  
8
  
 | 
  
1
  
 | 
4632
 | 
     if ( defined wantarray ) {  | 
| 
430
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
         my $s = $_[1];  | 
| 
431
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
         $s =~ s/(?=['\\])/\\/g;  | 
| 
432
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
         return "'$s'";  | 
| 
433
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
434
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
21
 | 
         $_[1] =~ s/(?=['\\])/\\/g;  | 
| 
435
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
         substr($_[1], 0, 0) = "'";  | 
| 
436
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
         $_[1] .= "'";  | 
| 
437
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
         return;  | 
| 
438
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
439
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
440
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
441
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub qq {  | 
| 
442
 | 
8
 | 
  
100
  
 | 
 
 | 
  
8
  
 | 
  
1
  
 | 
5118
 | 
     if ( defined wantarray ) {  | 
| 
443
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
         my $s = $_[1];  | 
| 
444
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
         $s =~ s/(?=["\\])/\\/g;  | 
| 
445
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
         return "\"$s\"";  | 
| 
446
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
447
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
         $_[1] =~ s/(?=["\\])/\\/g;  | 
| 
448
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
         substr($_[1], 0, 0) = '"';  | 
| 
449
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
         $_[1] .= '"';  | 
| 
450
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
         return;  | 
| 
451
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
452
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
453
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
454
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub fq {  | 
| 
455
 | 
6
 | 
  
100
  
 | 
 
 | 
  
6
  
 | 
  
1
  
 | 
3770
 | 
     if ( index( $_[1], "'" ) >= 0 ) {  | 
| 
456
 | 
2
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
8
 | 
         if ( defined wantarray ) {  | 
| 
457
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
             my $s = $_[1];  | 
| 
458
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
             $s =~ s/(?=["\\])/\\/g;  | 
| 
459
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
             return "\"$s\"";  | 
| 
460
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
461
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
             $_[1] =~ s/(?=["\\])/\\/g;  | 
| 
462
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
             substr($_[1], 0, 0) = '"';  | 
| 
463
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
             $_[1] .= '"';  | 
| 
464
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
             return;  | 
| 
465
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
466
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
467
 | 
4
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
14
 | 
         if ( defined wantarray ) {  | 
| 
468
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
             my $s = $_[1];  | 
| 
469
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
             $s =~ s/(?=\\)/\\/g;  | 
| 
470
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
             return "'$s'";  | 
| 
471
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
472
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
             $_[1] =~ s/(?=\\)/\\/g;  | 
| 
473
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
             substr($_[1], 0, 0) = "'";  | 
| 
474
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
             $_[1] .= "'";  | 
| 
475
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
             return;  | 
| 
476
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
477
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
478
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
479
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
480
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub dq {  | 
| 
481
 | 
22
 | 
  
  0
  
 | 
 
 | 
  
22
  
 | 
  
1
  
 | 
14203
 | 
     return defined wantarray? $_[1] : ()  | 
| 
 
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
482
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         unless $_[1] =~ /^$re_delim$/o;  | 
| 
483
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
484
 | 
22
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
66
 | 
     if ( defined wantarray ) {  | 
| 
485
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
         my $s = $_[1];  | 
| 
486
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
32
 | 
         my $q = substr( $s, 0, 1, '' );  | 
| 
487
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
         substr( $s, -1   ) = '';  | 
| 
488
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
150
 | 
         $s =~ s/\\([$q\\])/$1/g;  | 
| 
489
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
44
 | 
         return $s;  | 
| 
490
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
491
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
33
 | 
         my $q = substr( $_[1], 0, 1, '' );  | 
| 
492
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
         substr( $_[1], -1 ) = '';  | 
| 
493
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
153
 | 
         $_[1] =~ s/\\([$q\\])/$1/g;  | 
| 
494
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
37
 | 
         return;  | 
| 
495
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
496
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
497
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
498
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 Tree evaluation and modification  | 
| 
499
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
500
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Several functions taking a tree of boolean expressions as returned by  | 
| 
501
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 L method and evaluating or changing it using a callback.  | 
| 
502
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
503
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head3 walk $tree $callbacks @rest  | 
| 
504
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
505
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 A simple method for walking a $tree using four callbacks: open_paren,  | 
| 
506
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 close_paren, operand and operator. All callbacks are optional.  | 
| 
507
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
508
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Example:  | 
| 
509
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
510
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $parser->walk(  | 
| 
511
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $tree,  | 
| 
512
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         {  | 
| 
513
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             open_paren => sub { ... },  | 
| 
514
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             close_paren => sub { ... },  | 
| 
515
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             ...  | 
| 
516
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         },  | 
| 
517
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $some_context_argument, $another, ...  | 
| 
518
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     );  | 
| 
519
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
520
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Any additional arguments (@rest) are passed all the time into callbacks.  | 
| 
521
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
522
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
523
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
524
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub walk {  | 
| 
525
 | 
  
0
  
 | 
 
 | 
 
 | 
  
0
  
 | 
  
1
  
 | 
0
 | 
     my ($self, $tree, $cb, @rest) = @_;  | 
| 
526
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
527
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     foreach my $entry ( @$tree ) {  | 
| 
528
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         if ( ref $entry eq 'ARRAY' ) {  | 
| 
 
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
529
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $cb->{'open_paren'}->( @rest ) if $cb->{'open_paren'};  | 
| 
530
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $self->walk( $entry, $cb, @rest );  | 
| 
531
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $cb->{'close_paren'}->( @rest ) if $cb->{'close_paren'};  | 
| 
532
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } elsif ( ref $entry ) {  | 
| 
533
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $cb->{'operand'}->( $entry, @rest ) if $cb->{'operand'};  | 
| 
534
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
535
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $cb->{'operator'}->( $entry, @rest ) if $cb->{'operator'};  | 
| 
536
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
537
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
538
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
539
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
540
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head3 filter $tree $callback @rest  | 
| 
541
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
542
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Filters a $tree using provided $callback. The callback is called for each operand  | 
| 
543
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 in the tree and operand is left when it returns true value.  | 
| 
544
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
545
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Any additional arguments (@rest) are passed all the time into the callback.  | 
| 
546
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 See example below.  | 
| 
547
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
548
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Boolean operators (AND/OR) are skipped according to parens and left first rule,  | 
| 
549
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 for example:  | 
| 
550
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
551
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     X OR Y AND Z -> X AND Z  | 
| 
552
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     X OR (Y AND Z) -> X OR Z  | 
| 
553
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     X OR Y AND Z -> Y AND Z  | 
| 
554
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     X OR (Y AND Z) -> Y AND Z  | 
| 
555
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     X OR Y AND Z -> X OR Y  | 
| 
556
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     X OR (Y AND Z) -> X OR Y  | 
| 
557
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
558
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Returns new sub-tree. Original tree is not changed, but operands in new tree  | 
| 
559
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 still refer to the same hashes in the original.  | 
| 
560
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
561
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Example:  | 
| 
562
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
563
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $filter = sub {  | 
| 
564
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my ($condition, $some) = @_;  | 
| 
565
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         return 1 if $condition->{'operand'} eq $some;  | 
| 
566
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         return 0;  | 
| 
567
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     };  | 
| 
568
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $new_tree = $parser->filter( $tree, $filter, $some );  | 
| 
569
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
570
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 See also L  | 
| 
571
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
572
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
573
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
574
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub filter {  | 
| 
575
 | 
34
 | 
 
 | 
 
 | 
  
34
  
 | 
  
1
  
 | 
97
 | 
     my ($self, $tree, $cb, @rest) = @_;  | 
| 
576
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
577
 | 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
48
 | 
     my $skip_next = 0;  | 
| 
578
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
579
 | 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
48
 | 
     my @res;  | 
| 
580
 | 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
68
 | 
     foreach my $entry ( @$tree ) {  | 
| 
581
 | 
114
 | 
  
100
  
 | 
  
 50
  
 | 
 
 | 
 
 | 
224
 | 
         $skip_next-- and next if $skip_next > 0;  | 
| 
582
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
583
 | 
96
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
218
 | 
         if ( ref $entry eq 'ARRAY' ) {  | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
584
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
21
 | 
             my $tmp = $self->filter( $entry, $cb, @rest );  | 
| 
585
 | 
8
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
24
 | 
             $tmp = $tmp->[0] if @$tmp == 1;  | 
| 
586
 | 
8
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
43
 | 
             if ( !$tmp || (ref $tmp eq 'ARRAY' && !@$tmp) ) {  | 
| 
 
 | 
 
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
587
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
                 pop @res;  | 
| 
588
 | 
2
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
7
 | 
                 $skip_next++ unless @res;  | 
| 
589
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             } else {  | 
| 
590
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13
 | 
                 push @res, $tmp;  | 
| 
591
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
592
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } elsif ( ref $entry ) {  | 
| 
593
 | 
66
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
131
 | 
             if ( $cb->( $entry, @rest ) ) {  | 
| 
594
 | 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
130
 | 
                 push @res, $entry;  | 
| 
595
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             } else {  | 
| 
596
 | 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
101
 | 
                 pop @res;  | 
| 
597
 | 
33
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
84
 | 
                 $skip_next++ unless @res;  | 
| 
598
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
599
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
600
 | 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
43
 | 
             push @res, $entry;  | 
| 
601
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
602
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
603
 | 
34
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
116
 | 
     return $res[0] if @res == 1 && ref $res[0] eq 'ARRAY';  | 
| 
604
 | 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
170
 | 
     return \@res;  | 
| 
605
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
606
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
607
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head3 solve $tree $callback @rest  | 
| 
608
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
609
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Solves a boolean expression represented by a $tree using provided $callback.  | 
| 
610
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 The callback is called for operands and should return a boolean value  | 
| 
611
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 (0 or 1 will work).  | 
| 
612
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
613
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Any additional arguments (@rest) are passed all the time into the callback.  | 
| 
614
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 See example below.  | 
| 
615
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
616
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Functions matrixes:  | 
| 
617
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
618
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     A B AND OR  | 
| 
619
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     0 0 0   0  | 
| 
620
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     0 1 0   1  | 
| 
621
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     1 0 0   1  | 
| 
622
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     1 1 1   1  | 
| 
623
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
624
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Whole branches of the tree can be skipped when result is obvious, for example:  | 
| 
625
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
626
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     1 OR  (...)  | 
| 
627
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     0 AND (...)  | 
| 
628
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
629
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Returns result of the expression.  | 
| 
630
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
631
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Example:  | 
| 
632
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
633
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $solver = sub {  | 
| 
634
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my ($condition, $some) = @_;  | 
| 
635
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         return 1 if $condition->{'operand'} eq $some;  | 
| 
636
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         return 0;  | 
| 
637
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     };  | 
| 
638
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $result = $parser->solve( $tree, $filter, $some );  | 
| 
639
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
640
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 See also L.  | 
| 
641
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
642
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
643
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
644
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub solve {  | 
| 
645
 | 
50
 | 
 
 | 
 
 | 
  
50
  
 | 
  
1
  
 | 
116
 | 
     my ($self, $tree, $cb, @rest) = @_;  | 
| 
646
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
647
 | 
50
 | 
 
 | 
 
 | 
 
 | 
 
 | 
112
 | 
     my ($res, $ea, $skip_next) = (0, $self->{'operators'}[1], 0);  | 
| 
648
 | 
50
 | 
 
 | 
 
 | 
 
 | 
 
 | 
90
 | 
     foreach my $entry ( @$tree ) {  | 
| 
649
 | 
178
 | 
  
100
  
 | 
  
 50
  
 | 
 
 | 
 
 | 
334
 | 
         $skip_next-- and next if $skip_next > 0;  | 
| 
650
 | 
150
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
262
 | 
         unless ( ref $entry ) {  | 
| 
651
 | 
64
 | 
 
 | 
 
 | 
 
 | 
 
 | 
99
 | 
             $ea = lc $entry;  | 
| 
652
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $skip_next++ if  | 
| 
653
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                    ( $res && $ea eq $self->{'operators'}[1])  | 
| 
654
 | 
64
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
286
 | 
                 || (!$res && $ea eq $self->{'operators'}[0]);  | 
| 
 
 | 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
655
 | 
64
 | 
 
 | 
 
 | 
 
 | 
 
 | 
112
 | 
             next;  | 
| 
656
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
657
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
658
 | 
86
 | 
 
 | 
 
 | 
 
 | 
 
 | 
112
 | 
         my $cur;  | 
| 
659
 | 
86
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
146
 | 
         if ( ref $entry eq 'ARRAY' ) {  | 
| 
660
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
28
 | 
             $cur = $self->solve( $entry, $cb, @rest );  | 
| 
661
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
662
 | 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
151
 | 
             $cur = $cb->( $entry, @rest );  | 
| 
663
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
664
 | 
86
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
272
 | 
         if ( $ea eq $self->{'operators'}[1] ) {  | 
| 
665
 | 
68
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
207
 | 
             $res ||= $cur;  | 
| 
666
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
667
 | 
18
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
55
 | 
             $res &&= $cur;  | 
| 
668
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
669
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
670
 | 
50
 | 
 
 | 
 
 | 
 
 | 
 
 | 
224
 | 
     return $res;  | 
| 
671
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
672
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
673
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head3 fsolve $tree $callback @rest  | 
| 
674
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
675
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Does in filter+solve in one go. Callback can return undef to filter out an operand,  | 
| 
676
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 and a defined boolean value to be used in solve.  | 
| 
677
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
678
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Any additional arguments (@rest) are passed all the time into the callback.  | 
| 
679
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
680
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Returns boolean result of the equation or undef if all operands have been filtered.  | 
| 
681
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
682
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 See also L and L.  | 
| 
683
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
684
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
685
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
686
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub fsolve {  | 
| 
687
 | 
40
 | 
 
 | 
 
 | 
  
40
  
 | 
  
1
  
 | 
122
 | 
     my ($self, $tree, $cb, @rest) = @_;  | 
| 
688
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
689
 | 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
125
 | 
     my ($res, $ea, $skip_next) = (undef, $self->{'operators'}[1], 0);  | 
| 
690
 | 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
81
 | 
     foreach my $entry ( @$tree ) {  | 
| 
691
 | 
154
 | 
  
100
  
 | 
  
 50
  
 | 
 
 | 
 
 | 
317
 | 
         $skip_next-- and next if $skip_next > 0;  | 
| 
692
 | 
124
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
233
 | 
         unless ( ref $entry ) {  | 
| 
693
 | 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
79
 | 
             $ea = lc $entry;  | 
| 
694
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $skip_next++ if  | 
| 
695
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                    ( $res && $ea eq $self->{'operators'}[1])  | 
| 
696
 | 
48
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
217
 | 
                 || (!$res && $ea eq $self->{'operators'}[0]);  | 
| 
 
 | 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
697
 | 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
88
 | 
             next;  | 
| 
698
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
699
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
700
 | 
76
 | 
 
 | 
 
 | 
 
 | 
 
 | 
123
 | 
         my $cur;  | 
| 
701
 | 
76
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
138
 | 
         if ( ref $entry eq 'ARRAY' ) {  | 
| 
702
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $cur = $self->fsolve( $entry, $cb, @rest );  | 
| 
703
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
704
 | 
76
 | 
 
 | 
 
 | 
 
 | 
 
 | 
159
 | 
             $cur = $cb->( $entry, @rest );  | 
| 
705
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
706
 | 
76
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
242
 | 
         if ( defined $cur ) {  | 
| 
707
 | 
58
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
208
 | 
             $res ||= 0;  | 
| 
708
 | 
58
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
104
 | 
             if ( $ea eq $self->{'operators'}[1] ) {  | 
| 
709
 | 
50
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
166
 | 
                 $res ||= $cur;  | 
| 
710
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             } else {  | 
| 
711
 | 
8
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
27
 | 
                 $res &&= $cur;  | 
| 
712
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
713
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
714
 | 
18
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
42
 | 
             $skip_next++ unless defined $res;  | 
| 
715
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
716
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
717
 | 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
178
 | 
     return $res;  | 
| 
718
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
719
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
720
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head3 partial_solve $tree $callback @rest  | 
| 
721
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
722
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Partially solve a $tree. Callback can return undef or a new expression  | 
| 
723
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 and a defined boolean value to be used in solve.  | 
| 
724
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
725
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Returns either result or array reference with expression.  | 
| 
726
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
727
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Any additional arguments (@rest) are passed all the time into the callback.  | 
| 
728
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
729
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
730
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
731
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub partial_solve {  | 
| 
732
 | 
19
 | 
 
 | 
 
 | 
  
19
  
 | 
  
1
  
 | 
60
 | 
     my ($self, $tree, $cb, @rest) = @_;  | 
| 
733
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
734
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
32
 | 
     my @res;  | 
| 
735
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
736
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
47
 | 
     my ($last, $ea, $skip_next) = (0, $self->{'operators'}[1], 0);  | 
| 
737
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34
 | 
     foreach my $entry ( @$tree ) {  | 
| 
738
 | 
51
 | 
  
100
  
 | 
  
 50
  
 | 
 
 | 
 
 | 
98
 | 
         $skip_next-- and next if $skip_next > 0;  | 
| 
739
 | 
47
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
93
 | 
         unless ( ref $entry ) {  | 
| 
740
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
31
 | 
             $ea = lc $entry;  | 
| 
741
 | 
16
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
27
 | 
             unless ( ref $last ) {  | 
| 
742
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $skip_next++ if  | 
| 
743
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                        ( $last && $ea eq $self->{'operators'}[1])  | 
| 
744
 | 
8
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
52
 | 
                     || (!$last && $ea eq $self->{'operators'}[0]);  | 
| 
 
 | 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
745
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             } else {  | 
| 
746
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
                 push @res, $entry;  | 
| 
747
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
748
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
33
 | 
             next;  | 
| 
749
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
750
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
751
 | 
31
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
62
 | 
         if ( ref $entry eq 'ARRAY' ) {  | 
| 
752
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $last = $self->solve( $entry, $cb, @rest );  | 
| 
753
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # drop parens with one condition inside  | 
| 
754
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
             $last = $last->[0] if ref $last && @$last == 1;  | 
| 
755
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
756
 | 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
64
 | 
             $last = $cb->( $entry, @rest );  | 
| 
757
 | 
31
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
129
 | 
             $last = $entry unless defined $last;  | 
| 
758
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
759
 | 
31
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
52
 | 
         unless ( ref $last ) {  | 
| 
760
 | 
18
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
49
 | 
             if ( $ea eq $self->{'operators'}[0] ) {  | 
| 
 
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
761
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 # (...) AND 0  | 
| 
762
 | 
4
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
10
 | 
                 unless ( $last ) { @res = () } else { pop @res };  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    | 
| 
763
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
764
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             elsif ( $ea eq $self->{'operators'}[1] ) {  | 
| 
765
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 # (...) OR 1  | 
| 
766
 | 
14
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
26
 | 
                 if ( $last ) { @res = () } else { pop @res };  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
    | 
| 
767
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
768
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
769
 | 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
30
 | 
             push @res, $last;  | 
| 
770
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
771
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
772
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
773
 | 
19
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
78
 | 
     return $last unless @res; # solution  | 
| 
774
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
46
 | 
     return \@res; # more than one condition  | 
| 
775
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
776
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
777
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 1;  | 
| 
778
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
779
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 ALTERNATIVES  | 
| 
780
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
781
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 There are some alternative implementations available on the CPAN.  | 
| 
782
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
783
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =over 4  | 
| 
784
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
785
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item L - similar purpose with several differences.  | 
| 
786
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
787
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item Another?  | 
| 
788
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
789
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =back  | 
| 
790
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
791
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 AUTHORS  | 
| 
792
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
793
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Ruslan Zakirov Eruz@cpan.orgE, Robert Spier Erspier@pobox.comE  | 
| 
794
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
795
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 COPYRIGHT  | 
| 
796
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
797
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This program is free software; you can redistribute it and/or modify it under  | 
| 
798
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 the same terms as Perl itself.  | 
| 
799
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
800
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  |