It also won't handle
$string = '\\$var'; # escaped '$'
and
$string = '${foo}bar'; # $foo, not $foobar
which serve necessary functions (unlike
$string = '$_->[4]';
which is just a feature).
As a bonus, I've added support for \n and \t. It's easy to add the more escapes.
The following code does, and like yours, limits the strings being eval'ed to a minumum.
You can even get rid of eval completely if you don't interpolate from lexicals:
All told, though, it's safer and cleaner to only interpolate from variables in hash:
our %ESCAPES = (
n => "\n",
t => "\t",
);
sub interpolate {
local *_ = \$_[0]; # Alias $_ to $_[0].
my $symtab = $_[1];
my $interpolated = '';
for (;;) {
if (/\G \$(\w+) /gcsx || /\G \${(\w+)} /gcsx) {
if (!exists($symtab->{$1})) {
$interpolated .= "[unknown symbol \$$1]";
} elsif (!defined($symtab->{$1})) {
$interpolated .= "[undefined symbol \$$1]";
} else {
$interpolated .= $symtab->{$1};
}
next;
}
if (/\G \\(.) /gcsx) {
$interpolated .= exists($ESCAPES{$1}) ? $ESCAPES{$1} : $1;
next;
}
/\G
(
. # Catchall.
(?: # These four lines are optional.
(?!\\) # They are here to speed things up
(?!\$) # by avoiding adding individual
.)* # characters to the $interpolated.
)
/gcsx && do { $interpolated .= $1; next; };
last;
}
return $interpolated;
}
my %symtab = (
string => "cheese",
#user => $user,
#...
);
my $need_to_interpolate = 'smell my $string\n';
print interpolate($need_to_interpolate, \%symtab);
|