File Coverage

blib/lib/JSON/Path/Tokenizer.pm
Criterion Covered Total %
statement 69 69 100.0
branch 31 32 96.8
condition 9 12 75.0
subroutine 10 10 100.0
pod 0 1 0.0
total 119 124 95.9


line stmt bran cond sub pod time code
1             package JSON::Path::Tokenizer;
2             $JSON::Path::Tokenizer::VERSION = '0.5';
3 18     18   190187 use strict;
  18         38  
  18         551  
4 18     18   88 use warnings;
  18         31  
  18         482  
5 18     18   347 use 5.008;
  18         59  
6              
7 18     18   97 use Carp;
  18         82  
  18         1115  
8 18     18   558 use Readonly;
  18         3415  
  18         955  
9 18     18   497 use JSON::Path::Constants qw(:symbols :operators);
  18         56  
  18         5266  
10 18     18   125 use Exporter::Easy ( OK => ['tokenize'] );
  18         36  
  18         122  
11              
12             Readonly my $ESCAPE_CHAR => qq{\\};
13             Readonly my %OPERATORS => (
14             $TOKEN_ROOT => 1, # $
15             $TOKEN_RECURSIVE => 1, # ..
16             $TOKEN_CHILD => 1, # .
17             $TOKEN_FILTER_OPEN => 1, # [?(
18             $TOKEN_FILTER_SCRIPT_CLOSE => 1, # )]
19             $TOKEN_SCRIPT_OPEN => 1, # [(
20             $TOKEN_SUBSCRIPT_OPEN => 1, # [
21             $TOKEN_SUBSCRIPT_CLOSE => 1, # ]
22             );
23              
24             # ABSTRACT: Helper class for JSON::Path::Evaluator. Do not call directly.
25              
26             # Take an expression and break it up into tokens
27             sub tokenize {
28 220     220 0 46121 my $expression = shift;
29              
30 220         1787 my $chars = [ split //, $expression ];
31              
32 220         513 my @tokens;
33 220         484 while ( defined( my $token = _read_to_next_token($chars) ) ) {
34 1181         1840 push @tokens, $token;
35 1181 100 100     2084 if ( $token eq $TOKEN_SCRIPT_OPEN || $token eq $TOKEN_FILTER_OPEN ) {
36 40         367 push @tokens, _read_to_filter_script_close($chars);
37             }
38             }
39 220         1016 return @tokens;
40             }
41              
42             sub _read_to_filter_script_close {
43 40     40   70 my $chars = shift;
44              
45 40         58 my $filter;
46 40         55 while ( defined( my $char = shift @{$chars} ) ) {
  1025         4080  
47 1025         1093 $filter .= $char;
48              
49 1025 50       960 last unless @{$chars};
  1025         1462  
50 1025 100       1569 last if $chars->[0] eq $RIGHT_PARENTHESIS;
51             }
52 40         220 return $filter;
53             }
54              
55             sub _read_to_next_token {
56 1401     1401   10204 my $chars = shift;
57              
58 1401         1747 my $in_quote;
59             my $token;
60 1401         1533 while ( defined( my $char = shift @{$chars} ) ) {
  3075         11581  
61 2855 100 66     5048 if ( $char eq $APOSTROPHE || $char eq $QUOTATION_MARK ) {
62 48 100 66     262 if ( $in_quote && $in_quote eq $char ) {
63 24         34 $in_quote = '';
64 24         32 last;
65             }
66 24         31 $in_quote = $char;
67 24         43 next;
68             }
69              
70 2807 100 66     23655 if ( $char eq $ESCAPE_CHAR && !$in_quote ) {
71 2         16 $token .= shift @{$chars};
  2         5  
72 2         5 next;
73             }
74              
75 2805         11300 $token .= $char;
76              
77 2805 100       3936 next if $in_quote;
78              
79             # Break out of the loop if the current character is the last one in the stream.
80 2704 100       2824 last unless @{$chars};
  2704         4212  
81              
82 2499 100       4340 if ( $char eq $LEFT_SQUARE_BRACKET ) { # distinguish between '[', '[(', and '[?('
    100          
    100          
83 116 100       578 if ( $chars->[0] eq $LEFT_PARENTHESIS ) {
84 1         4 next;
85             }
86 115 100       601 if ( $chars->[0] eq $QUESTION_MARK ) {
87              
88             # The below appends the '?'. The '(' will be appended in the next iteration of the loop
89 39         199 $token .= shift @{$chars};
  39         71  
90 39         100 next;
91             }
92             }
93             elsif ( $char eq $RIGHT_PARENTHESIS ) {
94              
95             # A right parenthesis should be followed by a right square bracket, which itself is a token.
96             # Append the next character and proceed.
97 40         257 $token .= shift @{$chars};
  40         76  
98             }
99             elsif ( $char eq $FULL_STOP ) {
100              
101             # A full stop (i.e. a period, '.') may be the child operator '.' or the recursive operator '..'
102 330 100       7512 $token .= shift @{$chars} if $chars->[0] eq $FULL_STOP;
  17         79  
103             }
104              
105             # If we've assembled an operator, we're done.
106 2459 100       23851 last if $OPERATORS{$token};
107              
108             # Similarly, if the next character is an operator, we're done
109 1795 100       10934 last if $OPERATORS{ $chars->[0] };
110             }
111 1401         7495 return $token;
112             }
113              
114             1;
115              
116             __END__