line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package String::Interpolate::RE; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# ABSTRACT: interpolate variables into strings using regular expressions |
4
|
|
|
|
|
|
|
|
5
|
5
|
|
|
5
|
|
1122920
|
use strict; |
|
5
|
|
|
|
|
36
|
|
|
5
|
|
|
|
|
147
|
|
6
|
5
|
|
|
5
|
|
26
|
use warnings; |
|
5
|
|
|
|
|
11
|
|
|
5
|
|
|
|
|
113
|
|
7
|
5
|
|
|
5
|
|
23
|
use Carp; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
292
|
|
8
|
|
|
|
|
|
|
|
9
|
5
|
|
|
5
|
|
2174
|
use Exporter::Shiny qw[ strinterp ]; |
|
5
|
|
|
|
|
20962
|
|
|
5
|
|
|
|
|
32
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
our $VERSION = '0.09'; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
## no critic (ProhibitAccessOfPrivateData) |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
my %Opt = ( |
16
|
|
|
|
|
|
|
variable_re => qr/\w+/, |
17
|
|
|
|
|
|
|
raiseundef => 0, |
18
|
|
|
|
|
|
|
emptyundef => 0, |
19
|
|
|
|
|
|
|
useenv => 1, |
20
|
|
|
|
|
|
|
format => 0, |
21
|
|
|
|
|
|
|
recurse => 0, |
22
|
|
|
|
|
|
|
recurse_limit => 0, |
23
|
|
|
|
|
|
|
recurse_fail_limit => 100, |
24
|
|
|
|
|
|
|
); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
my $default_strinterp; |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
sub _generate_strinterp { |
29
|
|
|
|
|
|
|
|
30
|
6
|
|
|
6
|
|
739
|
my ( $me, $name, $args ) = @_; |
31
|
|
|
|
|
|
|
|
32
|
6
|
100
|
100
|
|
|
44
|
if ( ! defined $args || ! defined $args->{opts}) { |
33
|
5
|
|
33
|
|
|
24
|
return $default_strinterp || _mk_strinterp( \%Opt ); |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
|
36
|
1
|
|
|
|
|
7
|
my %opt = %Opt; |
37
|
1
|
|
|
|
|
2
|
$opt{lc $_} = $args->{opts}{$_} foreach keys %{$args->{opts}}; |
|
1
|
|
|
|
|
7
|
|
38
|
1
|
|
|
|
|
3
|
return _mk_strinterp( \%opt ); |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
sub _mk_strinterp { |
42
|
|
|
|
|
|
|
|
43
|
6
|
|
|
6
|
|
13
|
my $default_opt = shift; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
return sub { |
46
|
|
|
|
|
|
|
|
47
|
48
|
|
|
48
|
|
28052
|
my ( $text, $var, $opts ) = @_; |
48
|
|
|
|
|
|
|
|
49
|
48
|
100
|
|
|
|
140
|
$var = {} unless defined $var; |
50
|
|
|
|
|
|
|
|
51
|
48
|
|
|
|
|
260
|
my %opt = %$default_opt; |
52
|
|
|
|
|
|
|
|
53
|
48
|
100
|
|
|
|
140
|
if ( defined $opts ) { |
54
|
26
|
|
|
|
|
136
|
$opt{ lc $_ } = $opts->{$_} foreach keys %$opts; |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
## use critic |
57
|
|
|
|
|
|
|
|
58
|
48
|
100
|
|
|
|
136
|
my $fmt = $opt{format} ? ':([^}]+)' : '()'; |
59
|
|
|
|
|
|
|
|
60
|
48
|
|
|
|
|
98
|
$opt{track} = {}; |
61
|
48
|
|
|
|
|
82
|
$opt{loop} = 0; |
62
|
48
|
|
|
|
|
83
|
$opt{fmt} = $fmt; |
63
|
|
|
|
|
|
|
|
64
|
48
|
|
|
|
|
153
|
_strinterp( $text, $var, \%opt ); |
65
|
|
|
|
|
|
|
|
66
|
38
|
|
|
|
|
264
|
return $text; |
67
|
|
|
|
|
|
|
} |
68
|
6
|
|
|
|
|
61
|
} |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub _strinterp { |
71
|
|
|
|
|
|
|
|
72
|
93
|
|
|
93
|
|
150
|
my $var = $_[1]; |
73
|
93
|
|
|
|
|
136
|
my $opt = $_[2]; |
74
|
93
|
|
|
|
|
148
|
my $fmt = $opt->{fmt}; |
75
|
93
|
|
|
|
|
159
|
my $re = $opt->{variable_re}; |
76
|
|
|
|
|
|
|
|
77
|
93
|
|
|
|
|
1045
|
$_[0] =~ s{ |
78
|
|
|
|
|
|
|
\$ # find a literal dollar sign |
79
|
|
|
|
|
|
|
( # followed by either |
80
|
|
|
|
|
|
|
\{ ($re)(?:$fmt)? \} # a variable name in curly brackets ($2) |
81
|
|
|
|
|
|
|
# and an optional sprintf format |
82
|
|
|
|
|
|
|
| # or |
83
|
|
|
|
|
|
|
(\w+) # a bareword ($3) |
84
|
|
|
|
|
|
|
) |
85
|
|
|
|
|
|
|
}{ |
86
|
99
|
100
|
|
|
|
325
|
my $t = defined $4 ? $4 : $2; |
87
|
|
|
|
|
|
|
|
88
|
99
|
100
|
|
|
|
269
|
my $user_value = 'CODE' eq ref $var ? $var->($t) : $var->{$t}; |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
my $v = |
91
|
|
|
|
|
|
|
# user provided? |
92
|
|
|
|
|
|
|
defined $user_value ? $user_value |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
# maybe in the environment |
95
|
|
|
|
|
|
|
: $opt->{useenv} && exists $ENV{$t} ? $ENV{$t} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# undefined: throw an error? |
98
|
|
|
|
|
|
|
: $opt->{raiseundef} ? croak( "undefined variable: $t\n" ) |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
# undefined: replace with ''? |
101
|
99
|
100
|
100
|
|
|
905
|
: $opt->{emptyundef} ? '' |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
# undefined |
104
|
|
|
|
|
|
|
: undef |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
; |
107
|
|
|
|
|
|
|
|
108
|
96
|
100
|
66
|
|
|
296
|
if ( $opt->{recurse} && defined $v ) { |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
RECURSE: |
112
|
|
|
|
|
|
|
{ |
113
|
|
|
|
|
|
|
|
114
|
55
|
|
|
|
|
80
|
croak( |
115
|
|
|
|
|
|
|
"circular interpolation loop detected with repeated interpolation of <\$$t>\n" |
116
|
55
|
100
|
|
|
|
640
|
) if $opt->{track}{$t}++; |
117
|
|
|
|
|
|
|
|
118
|
51
|
|
|
|
|
68
|
++$opt->{loop}; |
119
|
|
|
|
|
|
|
|
120
|
51
|
100
|
100
|
|
|
140
|
last RECURSE if $opt->{recurse_limit} && $opt->{loop} > $opt->{recurse_limit}; |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
croak( |
123
|
|
|
|
|
|
|
"recursion fail-safe limit ($opt->{recurse_fail_limit}) reached at interpolation of <\$$t>\n" |
124
|
48
|
100
|
100
|
|
|
723
|
) if $opt->{recurse_fail_limit} && $opt->{loop} > $opt->{recurse_fail_limit}; |
125
|
|
|
|
|
|
|
|
126
|
45
|
|
|
|
|
106
|
_strinterp( $v, $_[1], $_[2] ); |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
32
|
|
|
|
|
68
|
delete $opt->{track}{$t}; |
131
|
32
|
|
|
|
|
41
|
--$opt->{loop}; |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
# if not defined, just put it back into the string |
135
|
73
|
100
|
100
|
|
|
412
|
! defined $v ? '$' . $1 |
|
|
100
|
|
|
|
|
|
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
# no format? return as is |
138
|
|
|
|
|
|
|
: ! defined $3 || $3 eq '' ? $v |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
# format it |
141
|
|
|
|
|
|
|
: sprintf( $3, $v) |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
; |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
}egx; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
1; |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
# |
151
|
|
|
|
|
|
|
# This file is part of String-Interpolate-RE |
152
|
|
|
|
|
|
|
# |
153
|
|
|
|
|
|
|
# This software is Copyright (c) 2017 by Smithsonian Astrophysical Observatory. |
154
|
|
|
|
|
|
|
# |
155
|
|
|
|
|
|
|
# This is free software, licensed under: |
156
|
|
|
|
|
|
|
# |
157
|
|
|
|
|
|
|
# The GNU General Public License, Version 3, June 2007 |
158
|
|
|
|
|
|
|
# |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=pod |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=head1 NAME |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
String::Interpolate::RE - interpolate variables into strings using regular expressions |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=head1 VERSION |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
version 0.09 |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=head1 SYNOPSIS |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
# default formulation |
173
|
|
|
|
|
|
|
use String::Interpolate::RE qw( strinterp ); |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
$str = strinterp( "${Var1} $Var2", $vars, \%opts ); |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
# import with different default options. |
178
|
|
|
|
|
|
|
use String::Interpolate::RE strinterp => { opts => { useENV => 0 } }; |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=head1 DESCRIPTION |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
This module interpolates variables into strings using regular |
183
|
|
|
|
|
|
|
expression matching rather than Perl's built-in interpolation |
184
|
|
|
|
|
|
|
mechanism and thus hopefully does not suffer from the security |
185
|
|
|
|
|
|
|
problems inherent in using B to interpolate into strings of |
186
|
|
|
|
|
|
|
suspect ancestry. |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
=head2 Changing the default option values |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
The default values for L's options were not all well |
191
|
|
|
|
|
|
|
thought out. B uses L, |
192
|
|
|
|
|
|
|
allowing a version of L with saner defaults to be |
193
|
|
|
|
|
|
|
exported. Simply specify them when importing: |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
use String::Interpolate::RE strinterp => { opts => { useENV => 0 } }; |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
The subroutine may be renamed using the C<-as> option: |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
use String::Interpolate::RE strinterp => { -as => strinterp_noenv, |
200
|
|
|
|
|
|
|
opts => { useENV => 0 } }; |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
strinterp_noenv( ... ); |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=head1 INTERFACE |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=over |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
=item strinterp |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
$str = strinterp( $template ); |
211
|
|
|
|
|
|
|
$str = strinterp( $template, $vars ); |
212
|
|
|
|
|
|
|
$str = strinterp( $template, $vars, \%opts ); |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
Interpolate variables into a template string, returning the |
215
|
|
|
|
|
|
|
resultant string. The template string is scanned for tokens of the |
216
|
|
|
|
|
|
|
form |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
$VAR |
219
|
|
|
|
|
|
|
${VAR} |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
where C is composed of one or more word characters (as defined by |
222
|
|
|
|
|
|
|
the C<\w> Perl regular expression pattern). C is resolved using |
223
|
|
|
|
|
|
|
the optional C<$vars> argument, which may either by a hashref (in |
224
|
|
|
|
|
|
|
which case C must be a key), or a function reference (which is |
225
|
|
|
|
|
|
|
passed C as its only argument and must return the value). |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
If the value returned for C is defined, it will be interpolated |
228
|
|
|
|
|
|
|
into the string at that point. By default, variables which are not |
229
|
|
|
|
|
|
|
defined are by default left as is in the string. |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
The C<%opts> parameter may be used to modify the behavior of this |
232
|
|
|
|
|
|
|
function. The following (case insensitive) keys are recognized: |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=over |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=item format I |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
If this flag is true, the template string may provide a C |
239
|
|
|
|
|
|
|
compatible format which will be used to generate the interpolated |
240
|
|
|
|
|
|
|
value. The format should be appended to the variable name with |
241
|
|
|
|
|
|
|
an intervening C<:> character, e.g. |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
${VAR:fmt} |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
For example, |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
%var = ( foo => 3 ); |
248
|
|
|
|
|
|
|
print strinterp( '${foo:%03d}', \%var, { format => 1 } ); |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
would result in |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
003 |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=item raiseundef I |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
If true, a variable which has not been defined will result in an |
257
|
|
|
|
|
|
|
exception being raised. This defaults to false. |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=item emptyundef I |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
If true, a variable which has not been defined will be replaced with |
262
|
|
|
|
|
|
|
the empty string. This defaults to false. |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=item useENV I |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
If true, the C<%ENV> hash will be searched for variables which are not |
267
|
|
|
|
|
|
|
defined in the passed C<%var> hash. This defaults to true. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=item recurse I |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
If true, derived values are themselves scanned for variables to |
272
|
|
|
|
|
|
|
interpolate. To specify a limit to the number of levels of recursions |
273
|
|
|
|
|
|
|
to attempt, set the C option. Circular dependencies |
274
|
|
|
|
|
|
|
are caught, but just to be safe there's a limit of recursion levels |
275
|
|
|
|
|
|
|
specified by C, beyond which an exception is |
276
|
|
|
|
|
|
|
thrown. |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
For example, |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
my %var = ( a => '$b', b => '$c', c => 'd' ); |
281
|
|
|
|
|
|
|
strinterp( '$a', \%var ) => '$b' |
282
|
|
|
|
|
|
|
strinterp( '$a', \%var, { recurse => 1 } ) => 'd' |
283
|
|
|
|
|
|
|
strinterp( '$a', \%var, { recurse => 1, recurse_limit => 1 } ) => '$c' |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
strinterp( '$a', { a => '$b', b => '$a' } , { recurse => 1 } |
286
|
|
|
|
|
|
|
recursive interpolation loop detected with repeated |
287
|
|
|
|
|
|
|
interpolation of $a |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
=item recurse_limit I |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
The number of recursion levels to descend when recursing into a |
292
|
|
|
|
|
|
|
variable's value before stopping. The default is C<0>, which means no |
293
|
|
|
|
|
|
|
limit. |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
=item recurse_fail_limit I |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
The number of recursion levels to descend when recursing into a |
298
|
|
|
|
|
|
|
variable's value before giving up and croaking. The default is C<100>. |
299
|
|
|
|
|
|
|
Setting this to C<0> means no limit. |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
=item variable_re I |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
This specifies the regular expression (created with the C |
304
|
|
|
|
|
|
|
operator) which will match a variable name. It defaults to |
305
|
|
|
|
|
|
|
C. Don't use C<:>, C<{>, or C<}> in the regex, or things may |
306
|
|
|
|
|
|
|
break. |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
=back |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=back |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
=head1 DIAGNOSTICS |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=over |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
=item C<< undefined variable: %s >> |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
This string is thrown if the C option is set and the |
319
|
|
|
|
|
|
|
variable C<%s> is not defined. |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
=item C<< recursive interpolation loop detected with repeated interpolation of <%s> >> |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
When resolving nested interpolated values (with the C option |
324
|
|
|
|
|
|
|
true ) a circular loop was found. |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
=item C<< recursion fail-safe limit (%d) reached at interpolation of <%s> >> |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
The recursion fail safe limit (C) was reached while |
329
|
|
|
|
|
|
|
interpolating nested variable values (with the C option true ). |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=back |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=head1 BUGS AND LIMITATIONS |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
You can make new bug reports, and view existing ones, through the |
336
|
|
|
|
|
|
|
web interface at L. |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
=head1 AUTHOR |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
Diab Jerius |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
This software is Copyright (c) 2017 by Smithsonian Astrophysical Observatory. |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
This is free software, licensed under: |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
The GNU General Public License, Version 3, June 2007 |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=cut |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
__END__ |