File Coverage

blib/lib/PPI/Token/_QuoteEngine.pm
Criterion Covered Total %
statement 65 71 91.5
branch 24 32 75.0
condition n/a
subroutine 6 6 100.0
pod n/a
total 95 109 87.1


line stmt bran cond sub pod time code
1             package PPI::Token::_QuoteEngine;
2              
3             =pod
4              
5             =head1 NAME
6              
7             PPI::Token::_QuoteEngine - The PPI Quote Engine
8              
9             =head1 DESCRIPTION
10              
11             The C<PPI::Token::_QuoteEngine> package is designed hold functionality
12             for processing quotes and quote like operators, including regexes.
13             These have special requirements in parsing.
14              
15             The C<PPI::Token::_QuoteEngine> package itself provides various parsing
16             methods, which the L<PPI::Token::Quote>, L<PPI::Token::QuoteLike> and
17             L<PPI::Token::Regexp> can inherit from. In this sense, it serves
18             as a base class.
19              
20             =head2 Using this class
21              
22             I<(Refers only to internal uses. This class does not provide a
23             public interface)>
24              
25             To use these, you should initialize them as normal C<'$Class-E<gt>new'>,
26             and then call the 'fill' method, which will cause the specialised
27             parser to scan forwards and parse the quote to its end point.
28              
29             If -E<gt>fill returns true, finalise the token.
30              
31             =cut
32              
33 67     67   328 use strict;
  67         109  
  67         1743  
34 67     67   256 use Carp ();
  67         87  
  67         50155  
35              
36             our $VERSION = '1.284';
37              
38              
39              
40              
41              
42             # Hook for the __TOKENIZER__on_char token call
43             sub __TOKENIZER__on_char {
44 16691     16691   24736 my $class = shift;
45 16691 50       41376 my $t = $_[0]->{token} ? shift : return undef;
46              
47             # Call the fill method to process the quote
48 16691         39972 my $rv = $t->{token}->_fill( $t );
49 16691 50       29946 return undef unless defined $rv;
50              
51             ## Doesn't support "end of file" indicator
52              
53             # Finalize the token and return 0 to tell the tokenizer
54             # to go to the next character.
55 16691         37016 $t->_finalize_token;
56              
57 16691         40113 0;
58             }
59              
60              
61              
62              
63              
64             #####################################################################
65             # Optimised character processors, used for quotes
66             # and quote like stuff, and accessible to the child classes
67              
68             # An outright scan, raw and fast.
69             # Searches for a particular character, not escaped, loading in new
70             # lines as needed.
71             # When called, we start at the current position.
72             # When leaving, the position should be set to the position
73             # of the character, NOT the one after it.
74             sub _scan_for_unescaped_character {
75 15294     15294   18528 my $class = shift;
76 15294         16890 my $t = shift;
77 15294 50       32838 my $char = (length $_[0] == 1) ? quotemeta shift : return undef;
78              
79             # Create the search regex.
80             # Same as above but with a negative look-behind assertion.
81 15294         196041 my $search = qr/(.*?(?<!\\)(?:\\\\)*$char)/;
82              
83 15294         27697 my $string = '';
84 15294         30578 while ( exists $t->{line} ) {
85             # Get the search area for the current line
86 18027         39555 pos $t->{line} = $t->{line_cursor};
87              
88             # Can we find a match on this line
89 18027 100       222558 if ( $t->{line} =~ m/\G$search/gc ) {
90             # Found the character on this line
91 13603         36220 $t->{line_cursor} += length($1) - 1;
92 13603         55008 return $string . $1;
93             }
94              
95             # Load in the next line
96 4424         9336 $string .= substr $t->{line}, $t->{line_cursor};
97 4424         7694 my $rv = $t->_fill_line('inscan');
98 4424 100       7806 if ( $rv ) {
    50          
99             # Push to first character
100 2733         5437 $t->{line_cursor} = 0;
101             } elsif ( defined $rv ) {
102             # We hit the End of File
103 1691         5173 return \$string;
104             } else {
105             # Unexpected error
106 0         0 return undef;
107             }
108             }
109              
110             # We shouldn't be able to get here
111 0         0 return undef;
112             }
113              
114             # Scan for a close braced, and take into account both escaping,
115             # and open close bracket pairs in the string. When complete, the
116             # method leaves the line cursor on the LAST character found.
117             sub _scan_for_brace_character {
118 1689     1689   2393 my $class = shift;
119 1689         2211 my $t = shift;
120 1689 50       6677 my $close_brace = $_[0] =~ /^(?:\>|\)|\}|\])$/ ? shift : Carp::confess(''); # return undef;
121 1689         2320 my $open_brace = $close_brace;
122 1689         3331 $open_brace =~ tr/\>\)\}\]/\<\(\{\[/;
123              
124             # Create the search string
125 1689         2821 $close_brace = quotemeta $close_brace;
126 1689         2217 $open_brace = quotemeta $open_brace;
127 1689         24445 my $search = qr/\G(.*?(?<!\\)(?:\\\\)*(?:$open_brace|$close_brace))/;
128              
129             # Loop as long as we can get new lines
130 1689         3119 my $string = '';
131 1689         2383 my $depth = 1;
132 1689         4139 while ( exists $t->{line} ) {
133             # Get the search area
134 2982         5995 pos $t->{line} = $t->{line_cursor};
135              
136             # Look for a match
137 2982 100       19977 unless ( $t->{line} =~ /$search/gc ) {
138             # Load in the next line
139 803         1754 $string .= substr( $t->{line}, $t->{line_cursor} );
140 803         1566 my $rv = $t->_fill_line('inscan');
141 803 100       1441 if ( $rv ) {
142             # Push to first character
143 636         829 $t->{line_cursor} = 0;
144 636         1091 next;
145             }
146 167 50       452 if ( defined $rv ) {
147             # We hit the End of File
148 167         523 return \$string;
149             }
150              
151             # Unexpected error
152 0         0 return undef;
153             }
154              
155             # Add to the string
156 2179         5085 $string .= $1;
157 2179         3703 $t->{line_cursor} += length $1;
158              
159             # Alter the depth and continue if we aren't at the end
160 2179 100       13382 $depth += ($1 =~ /$open_brace$/) ? 1 : -1 and next;
    100          
161              
162             # Rewind the cursor by one character ( cludgy hack )
163 1522         2708 $t->{line_cursor} -= 1;
164 1522         5690 return $string;
165             }
166              
167             # Returning the string as a reference indicates EOF
168 0         0 \$string;
169             }
170              
171             # Find all spaces and comments, up to, but not including
172             # the first non-whitespace character.
173             #
174             # Although it doesn't return it, it leaves the cursor
175             # on the character following the gap
176             sub _scan_quote_like_operator_gap {
177 653     653   823 my $t = $_[1];
178              
179 653         1021 my $string = '';
180 653         1394 while ( exists $t->{line} ) {
181             # Get the search area for the current line
182 720         1396 pos $t->{line} = $t->{line_cursor};
183              
184             # Since this regex can match zero characters, it should always match
185 720 50       2877 $t->{line} =~ /\G(\s*(?:\#.*)?)/gc or return undef;
186              
187             # Add the chars found to the string
188 720         1732 $string .= $1;
189              
190             # Did we match the entire line?
191 720 100       1977 unless ( $t->{line_cursor} + length $1 == length $t->{line} ) {
192             # Partial line match, which means we are at
193             # the end of the gap. Fix the cursor and return
194             # the string.
195 466         836 $t->{line_cursor} += length $1;
196 466         1061 return $string;
197             }
198              
199             # Load in the next line.
200             # If we reach the EOF, $t->{line} gets deleted,
201             # which is caught by the while.
202 254         552 my $rv = $t->_fill_line('inscan');
203 254 100       767 if ( $rv ) {
    50          
204             # Set the cursor to the first character
205 67         145 $t->{line_cursor} = 0;
206             } elsif ( defined $rv ) {
207             # Returning the string as a reference indicates EOF
208 187         533 return \$string;
209             } else {
210 0           return undef;
211             }
212             }
213              
214             # Shouldn't be able to get here
215 0           return undef;
216             }
217              
218             1;
219              
220             =pod
221              
222             =head1 SUPPORT
223              
224             See the L<support section|PPI/SUPPORT> in the main module.
225              
226             =head1 AUTHOR
227              
228             Adam Kennedy E<lt>adamk@cpan.orgE<gt>
229              
230             =head1 COPYRIGHT
231              
232             Copyright 2001 - 2011 Adam Kennedy.
233              
234             This program is free software; you can redistribute
235             it and/or modify it under the same terms as Perl itself.
236              
237             The full text of the license can be found in the
238             LICENSE file included with this module.
239              
240             =cut