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.431';
3 17     17   251021 use strict;
  17         42  
  17         505  
4 17     17   88 use warnings;
  17         29  
  17         386  
5 17     17   331 use 5.008;
  17         59  
6              
7 17     17   91 use Carp;
  17         29  
  17         1039  
8 17     17   673 use Readonly;
  17         4205  
  17         932  
9 17     17   609 use JSON::Path::Constants qw(:symbols :operators);
  17         36  
  17         4789  
10 17     17   125 use Exporter::Easy ( OK => ['tokenize'] );
  17         34  
  17         115  
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 56983 my $expression = shift;
29              
30 160         1118 my $chars = [ split //, $expression ];
31              
32 160         393 my @tokens;
33 160         363 while ( defined( my $token = _read_to_next_token($chars) ) ) {
34 933         1579 push @tokens, $token;
35 933 100 100     1782 if ( $token eq $TOKEN_SCRIPT_OPEN || $token eq $TOKEN_FILTER_OPEN ) {
36 23         248 push @tokens, _read_to_filter_script_close($chars);
37             }
38             }
39 160         846 return @tokens;
40             }
41              
42             sub _read_to_filter_script_close {
43 23     23   41 my $chars = shift;
44              
45 23         36 my $filter;
46 23         35 while ( defined( my $char = shift @{$chars} ) ) {
  383         1880  
47 383         501 $filter .= $char;
48              
49 383 50       448 last unless @{$chars};
  383         619  
50 383 100       720 last if $chars->[0] eq $RIGHT_PARENTHESIS;
51             }
52 23         160 return $filter;
53             }
54              
55             sub _read_to_next_token {
56 1093     1093   8720 my $chars = shift;
57              
58 1093         1534 my $in_quote;
59             my $token;
60 1093         1324 while ( defined( my $char = shift @{$chars} ) ) {
  2382         9884  
61 2222 100 66     4397 if ( $char eq $APOSTROPHE || $char eq $QUOTATION_MARK ) {
62 48 100 66     256 if ( $in_quote && $in_quote eq $char ) {
63 24         33 $in_quote = '';
64 24         31 last;
65             }
66 24         32 $in_quote = $char;
67 24         32 next;
68             }
69              
70 2174 100 66     19902 if ( $char eq $ESCAPE_CHAR && !$in_quote ) {
71 2         16 $token .= shift @{$chars};
  2         6  
72 2         6 next;
73             }
74              
75 2172         9654 $token .= $char;
76              
77 2172 100       3471 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       2444 last unless @{$chars};
  2071         3605  
81              
82 1926 100       3641 if ( $char eq $LEFT_SQUARE_BRACKET ) { # distinguish between '[', '[(', and '[?('
    100          
    100          
83 99 100       515 if ( $chars->[0] eq $LEFT_PARENTHESIS ) {
84 1         6 next;
85             }
86 98 100       539 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         146 $token .= shift @{$chars};
  22         47  
90 22         51 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         176 $token .= shift @{$chars};
  23         39  
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       2785 $token .= shift @{$chars} if $chars->[0] eq $FULL_STOP;
  17         82  
103             }
104              
105             # If we've assembled an operator, we're done.
106 1903 100       20279 last if $OPERATORS{$token};
107              
108             # Similarly, if the next character is an operator, we're done
109 1367 100       9474 last if $OPERATORS{ $chars->[0] };
110             }
111 1093         6355 return $token;
112             }
113              
114             1;
115              
116             __END__