line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
=head1 NAME |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
PPIx::Regexp::Token::Code - Represent a chunk of Perl embedded in a regular expression. |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 SYNOPSIS |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
use PPIx::Regexp::Dumper; |
8
|
|
|
|
|
|
|
PPIx::Regexp::Dumper->new( |
9
|
|
|
|
|
|
|
'qr{(?{print "hello sailor\n"})}smx')->print; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 INHERITANCE |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
C is a |
14
|
|
|
|
|
|
|
L. |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
C is the parent of |
17
|
|
|
|
|
|
|
L. |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=head1 DESCRIPTION |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
This class represents a chunk of Perl code embedded in a regular |
22
|
|
|
|
|
|
|
expression. Specifically, it results from parsing things like |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
(?{ code }) |
25
|
|
|
|
|
|
|
(??{ code }) |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
or from the replacement side of an s///e. Technically, interpolations |
28
|
|
|
|
|
|
|
are also code, but they parse differently and therefore end up in a |
29
|
|
|
|
|
|
|
different token. |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
This token may not appear inside a regex set (i.e. C<(?[ ... ])>. If |
32
|
|
|
|
|
|
|
found, it will become a C. |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=head1 METHODS |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
This class provides the following public methods. Methods not documented |
37
|
|
|
|
|
|
|
here are private, and unsupported in the sense that the author reserves |
38
|
|
|
|
|
|
|
the right to change or remove them without notice. |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=cut |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
package PPIx::Regexp::Token::Code; |
43
|
|
|
|
|
|
|
|
44
|
9
|
|
|
9
|
|
62
|
use strict; |
|
9
|
|
|
|
|
28
|
|
|
9
|
|
|
|
|
264
|
|
45
|
9
|
|
|
9
|
|
42
|
use warnings; |
|
9
|
|
|
|
|
18
|
|
|
9
|
|
|
|
|
235
|
|
46
|
|
|
|
|
|
|
|
47
|
9
|
|
|
9
|
|
57
|
use base qw{ PPIx::Regexp::Token }; |
|
9
|
|
|
|
|
18
|
|
|
9
|
|
|
|
|
725
|
|
48
|
|
|
|
|
|
|
|
49
|
9
|
|
|
9
|
|
2837
|
use PPI::Document; |
|
9
|
|
|
|
|
591933
|
|
|
9
|
|
|
|
|
417
|
|
50
|
9
|
|
|
|
|
1286
|
use PPIx::Regexp::Constant qw{ |
51
|
|
|
|
|
|
|
COOKIE_REGEX_SET |
52
|
|
|
|
|
|
|
LOCATION_COLUMN |
53
|
|
|
|
|
|
|
LOCATION_LOGICAL_LINE |
54
|
|
|
|
|
|
|
LOCATION_LOGICAL_FILE |
55
|
|
|
|
|
|
|
@CARP_NOT |
56
|
9
|
|
|
9
|
|
77
|
}; |
|
9
|
|
|
|
|
28
|
|
57
|
9
|
|
|
9
|
|
83
|
use PPIx::Regexp::Util qw{ __instance }; |
|
9
|
|
|
|
|
28
|
|
|
9
|
|
|
|
|
599
|
|
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
our $VERSION = '0.087_01'; |
60
|
|
|
|
|
|
|
|
61
|
9
|
|
|
9
|
|
63
|
use constant TOKENIZER_ARGUMENT_REQUIRED => 1; |
|
9
|
|
|
|
|
607
|
|
|
9
|
|
|
|
|
547
|
|
62
|
9
|
|
|
9
|
|
470
|
use constant VERSION_WHEN_IN_REGEX_SET => undef; |
|
9
|
|
|
|
|
49
|
|
|
9
|
|
|
|
|
6373
|
|
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
sub __new { |
65
|
146
|
|
|
146
|
|
6894
|
my ( $class, $content, %arg ) = @_; |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
defined $arg{perl_version_introduced} |
68
|
146
|
100
|
|
|
|
524
|
or $arg{perl_version_introduced} = '5.005'; |
69
|
|
|
|
|
|
|
|
70
|
146
|
|
|
|
|
702
|
my $self = $class->SUPER::__new( $content, %arg ); |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# TODO sort this out, since Token::Interpolation is a subclass, and |
73
|
|
|
|
|
|
|
# those are legal in regex sets |
74
|
146
|
100
|
|
|
|
663
|
if ( $arg{tokenizer}->cookie( COOKIE_REGEX_SET ) ) { |
75
|
1
|
50
|
|
|
|
10
|
my $ver = $self->VERSION_WHEN_IN_REGEX_SET() |
76
|
|
|
|
|
|
|
or return $self->__error( 'Code token not valid in Regex set' ); |
77
|
|
|
|
|
|
|
$self->{perl_version_introduced} < $ver |
78
|
1
|
50
|
|
|
|
10
|
and $self->{perl_version_introduced} = $ver; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
$arg{tokenizer}->__recognize_postderef( $self ) |
82
|
|
|
|
|
|
|
and $self->{perl_version_introduced} < 5.019005 |
83
|
146
|
100
|
66
|
|
|
742
|
and $self->{perl_version_introduced} = '5.019005'; |
84
|
|
|
|
|
|
|
|
85
|
146
|
|
|
|
|
752
|
return $self; |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub content { |
89
|
271
|
|
|
271
|
1
|
630
|
my ( $self ) = @_; |
90
|
271
|
50
|
|
|
|
697
|
if ( exists $self->{content} ) { |
|
|
0
|
|
|
|
|
|
91
|
271
|
|
|
|
|
922
|
return $self->{content}; |
92
|
|
|
|
|
|
|
} elsif ( exists $self->{ppi} ) { |
93
|
0
|
|
|
|
|
0
|
return ( $self->{content} = $self->{ppi}->content() ); |
94
|
|
|
|
|
|
|
} else { |
95
|
0
|
|
|
|
|
0
|
return; |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
sub explain { |
100
|
1
|
|
|
1
|
1
|
6
|
return 'Perl expression'; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=head2 is_matcher |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
This method returns C because a static analysis can not in |
106
|
|
|
|
|
|
|
general tell whether an interpolated value matches anything. |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=cut |
109
|
|
|
|
|
|
|
|
110
|
0
|
|
|
0
|
1
|
0
|
sub is_matcher { return undef; } ## no critic (ProhibitExplicitReturnUndef) |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=head2 ppi |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
This convenience method returns the L |
115
|
|
|
|
|
|
|
representing the content. This document should be considered read only. |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
B that if the location of the invocant is available the PPI |
118
|
|
|
|
|
|
|
document will have stuff prefixed to it to make the location of the |
119
|
|
|
|
|
|
|
tokens in the new document consistent with the location. This "stuff" |
120
|
|
|
|
|
|
|
will include at least a C<#line> directive, and maybe leading white |
121
|
|
|
|
|
|
|
space. |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
=cut |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub ppi { |
126
|
149
|
|
|
149
|
1
|
354
|
my ( $self ) = @_; |
127
|
149
|
100
|
|
|
|
611
|
if ( exists $self->{ppi} ) { |
|
|
50
|
|
|
|
|
|
128
|
5
|
|
|
|
|
24
|
return $self->{ppi}; |
129
|
|
|
|
|
|
|
} elsif ( exists $self->{content} ) { |
130
|
144
|
|
|
|
|
205
|
my $content; |
131
|
144
|
|
|
|
|
290
|
my $location = $self->{location}; |
132
|
144
|
100
|
|
|
|
369
|
if ( $location ) { |
133
|
2
|
|
|
|
|
4
|
my $fn; |
134
|
2
|
50
|
|
|
|
9
|
if( defined( $fn = $location->[LOCATION_LOGICAL_FILE] ) ) { |
135
|
2
|
|
|
|
|
5
|
$fn =~ s/ (?= [\\"] ) /\\/smxg; |
136
|
2
|
|
|
|
|
20
|
$content = qq{#line $location->[LOCATION_LOGICAL_LINE] "$fn"\n}; |
137
|
|
|
|
|
|
|
} else { |
138
|
0
|
|
|
|
|
0
|
$content = qq{#line $location->[LOCATION_LOGICAL_LINE]\n}; |
139
|
|
|
|
|
|
|
} |
140
|
2
|
|
|
|
|
30
|
$content .= ' ' x ( $location->[LOCATION_COLUMN] - 1 ); |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
144
|
|
|
|
|
542
|
$content .= $self->__ppi_normalize_content(); |
144
|
|
|
|
|
|
|
|
145
|
144
|
|
|
|
|
698
|
$self->{ppi} = PPI::Document->new( \$content ); |
146
|
|
|
|
|
|
|
|
147
|
144
|
100
|
|
|
|
150022
|
if ( $location ) { |
148
|
|
|
|
|
|
|
# Generate locations now. |
149
|
2
|
|
|
|
|
13
|
$self->{ppi}->location(); |
150
|
|
|
|
|
|
|
# Remove the stuff we originally injected. NOTE that we can |
151
|
|
|
|
|
|
|
# only get away with doing this if the removal does not |
152
|
|
|
|
|
|
|
# invalidate the locations of the other tokens that we just |
153
|
|
|
|
|
|
|
# generated. |
154
|
2
|
|
|
|
|
862
|
my $elem; |
155
|
|
|
|
|
|
|
# Remove the '#line' directive if we find it |
156
|
2
|
50
|
33
|
|
|
12
|
$elem = $self->{ppi}->child( 0 ) |
|
|
|
33
|
|
|
|
|
157
|
|
|
|
|
|
|
and $elem->isa( 'PPI::Token::Comment' ) |
158
|
|
|
|
|
|
|
and $elem->content() =~ m/ \A \#line\b /smx |
159
|
|
|
|
|
|
|
and $elem->remove(); |
160
|
|
|
|
|
|
|
# Remove the white space if we find it, and if it in fact |
161
|
|
|
|
|
|
|
# represents only the white space we injected to get the |
162
|
|
|
|
|
|
|
# column numbers right. |
163
|
2
|
|
|
|
|
159
|
my $wid = $location->[LOCATION_COLUMN] - 1; |
164
|
|
|
|
|
|
|
$wid |
165
|
2
|
100
|
33
|
|
|
16
|
and $elem = $self->{ppi}->child( 0 ) |
|
|
|
33
|
|
|
|
|
|
|
|
66
|
|
|
|
|
166
|
|
|
|
|
|
|
and $elem->isa( 'PPI::Token::Whitespace' ) |
167
|
|
|
|
|
|
|
and $wid == length $elem->content() |
168
|
|
|
|
|
|
|
and $elem->remove(); |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
|
171
|
144
|
|
|
|
|
629
|
return $self->{ppi}; |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
} else { |
174
|
0
|
|
|
|
|
0
|
return; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
sub width { |
179
|
21
|
|
|
21
|
1
|
88
|
return ( undef, undef ); |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub __ppi_normalize_content { |
183
|
51
|
|
|
51
|
|
120
|
my ( $self ) = @_; |
184
|
51
|
|
|
|
|
150
|
return $self->{content}; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
# Return true if the token can be quantified, and false otherwise |
188
|
|
|
|
|
|
|
# sub can_be_quantified { return }; |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
{ |
191
|
9
|
|
|
9
|
|
73
|
no warnings qw{ qw }; ## no critic (ProhibitNoWarnings) |
|
9
|
|
|
|
|
30
|
|
|
9
|
|
|
|
|
1780
|
|
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
my %accept = map { $_ => 1 } qw{ $ $# @ % & * }; |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# Say what casts are accepted, since not all are in an |
196
|
|
|
|
|
|
|
# interpolation. |
197
|
|
|
|
|
|
|
sub __postderef_accept_cast { |
198
|
40
|
|
|
40
|
|
127
|
return \%accept; |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub __PPIX_TOKENIZER__regexp { |
203
|
14
|
|
|
14
|
|
53
|
my ( undef, $tokenizer, $character ) = @_; |
204
|
|
|
|
|
|
|
|
205
|
14
|
50
|
|
|
|
55
|
$character eq '{' or return; |
206
|
|
|
|
|
|
|
|
207
|
14
|
50
|
|
|
|
49
|
my $offset = $tokenizer->find_matching_delimiter() |
208
|
|
|
|
|
|
|
or return; |
209
|
|
|
|
|
|
|
|
210
|
14
|
|
|
|
|
68
|
return $offset + 1; # to include the closing delimiter. |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
1; |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
__END__ |