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.430';
3 17     17   184575 use strict;
  17         38  
  17         478  
4 17     17   83 use warnings;
  17         37  
  17         367  
5 17     17   266 use 5.008;
  17         65  
6              
7 17     17   81 use Carp;
  17         26  
  17         913  
8 17     17   530 use Readonly;
  17         3185  
  17         906  
9 17     17   464 use JSON::Path::Constants qw(:symbols :operators);
  17         39  
  17         4867  
10 17     17   116 use Exporter::Easy ( OK => ['tokenize'] );
  17         28  
  17         98  
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 160     160 0 46188 my $expression = shift;
29              
30 160         1003 my $chars = [ split //, $expression ];
31              
32 160         355 my @tokens;
33 160         319 while ( defined( my $token = _read_to_next_token($chars) ) ) {
34 933         1434 push @tokens, $token;
35 933 100 100     1674 if ( $token eq $TOKEN_SCRIPT_OPEN || $token eq $TOKEN_FILTER_OPEN ) {
36 23         213 push @tokens, _read_to_filter_script_close($chars);
37             }
38             }
39 160         689 return @tokens;
40             }
41              
42             sub _read_to_filter_script_close {
43 23     23   34 my $chars = shift;
44              
45 23         34 my $filter;
46 23         32 while ( defined( my $char = shift @{$chars} ) ) {
  383         1572  
47 383         417 $filter .= $char;
48              
49 383 50       401 last unless @{$chars};
  383         526  
50 383 100       647 last if $chars->[0] eq $RIGHT_PARENTHESIS;
51             }
52 23         120 return $filter;
53             }
54              
55             sub _read_to_next_token {
56 1093     1093   7954 my $chars = shift;
57              
58 1093         1363 my $in_quote;
59             my $token;
60 1093         1199 while ( defined( my $char = shift @{$chars} ) ) {
  2382         8854  
61 2222 100 66     3983 if ( $char eq $APOSTROPHE || $char eq $QUOTATION_MARK ) {
62 48 100 66     263 if ( $in_quote && $in_quote eq $char ) {
63 24         39 $in_quote = '';
64 24         30 last;
65             }
66 24         34 $in_quote = $char;
67 24         72 next;
68             }
69              
70 2174 100 66     17667 if ( $char eq $ESCAPE_CHAR && !$in_quote ) {
71 2         13 $token .= shift @{$chars};
  2         4  
72 2         4 next;
73             }
74              
75 2172         8749 $token .= $char;
76              
77 2172 100       3129 next if $in_quote;
78              
79             # Break out of the loop if the current character is the last one in the stream.
80 2071 100       2160 last unless @{$chars};
  2071         3239  
81              
82 1926 100       3125 if ( $char eq $LEFT_SQUARE_BRACKET ) { # distinguish between '[', '[(', and '[?('
    100          
    100          
83 99 100       476 if ( $chars->[0] eq $LEFT_PARENTHESIS ) {
84 1         5 next;
85             }
86 98 100       520 if ( $chars->[0] eq $QUESTION_MARK ) {
87              
88             # The below appends the '?'. The '(' will be appended in the next iteration of the loop
89 22         95 $token .= shift @{$chars};
  22         38  
90 22         43 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 23         149 $token .= shift @{$chars};
  23         34  
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 253 100       2534 $token .= shift @{$chars} if $chars->[0] eq $FULL_STOP;
  17         85  
103             }
104              
105             # If we've assembled an operator, we're done.
106 1903 100       18259 last if $OPERATORS{$token};
107              
108             # Similarly, if the next character is an operator, we're done
109 1367 100       8368 last if $OPERATORS{ $chars->[0] };
110             }
111 1093         5783 return $token;
112             }
113              
114             1;
115              
116             __END__