Since the issue is the interpolation of the replacement string, can you not stamp on '$' and '@', leaving only '$1' et al ? That is:
use strict ;
use warnings ;
while (<DATA>) {
my ($pat, $repl) = (split '/') [1, 2];
# Escape $ and @ preceded by an even number of '\', except for $1
+etc.
$repl =~ s/(?<!\\)((?:\\\\)*(?:\$(?!\d)|\@))/\\$1/g ;
my $e = "\$s =~ s/$pat/$repl/" ;
my $s = "foo";
print "$e '$s' -> " ;
eval $e ; die $@ if $@ ;
print "'$s'\n" ;
}
__DATA__
s/foo/bar/
s/(fo)o/bar$1/
s/oo/\${system "echo hello sailor !"}/
s/oo/\\${system "echo hello sailor !"}/
s/oo/\\\${system "echo hello sailor !"}/
s/oo/\\\\${system "echo hello sailor !"}/
s/oo/\\\\\${system "echo hello sailor !"}/
s/oo/@{system "echo hello sailor !"}/
s/oo/\@{system "echo hello sailor !"}/
s/oo/\\@{system "echo hello sailor !"}/
Noting that you don't want to "activate" '\$' or '\@' by inserting '\'.
Hope I didn't miss anything... | [reply] [d/l] |
Hope I didn't miss anything...
That's the crucial point: You never know. Perl is a language with a very complex syntax (compared to all other programming languages that I know of) that you can basically never be sure if you approach things in this way.
I thought I was quite confident with Perl's syntax, until I found out that you can have whitespaces between the sigil and the variable, and that even works in string interpolation:
$ perl -wle 'my $x = 3; print "x: $ x"'
x: 3
I certainly didn't expect that.
What I learned from this is to never assume that you know every detail of Perl syntax, so I won't be sure that I've covered all corner cases. Can you be? | [reply] [d/l] |
What I learned from this is to never assume that you know every detail of Perl syntax, so I won't be sure that I've covered all corner cases. Can you be?
I would not presume.
AFAIK the only characters that trigger interpolation are '$' and '@'. But, as you say, with Perl it's unsafe to assume that anything can be depended on.
However, if we can trust that much, then escaping '$' unless it's followed by a digit only lets past stuff which is supposed to be safe. And escaping '@' unconditionally seems to cover that. Unless there's some other magic that throws the escaping mechansm.
<sigh>
I suppose one could fall back on a hand affair:
use strict ;
use warnings ;
while (<DATA>) {
my ($pat, $repl) = (split '/') [1, 2];
my $s = "foo" ;
printf "%40s '%s' -> ", "/$pat/$repl/", $s ;
my @trap = $s =~ m/$pat/ ;
$repl =~ s/(?<!\\)((?:\\\\)*)\$([1-9])/(defined($1) ? $1 : '')
.(defined($trap[$2-1]) ? $tra
+p[$2-1] : '')/eg ;
$s =~ s/$pat/$repl/ ;
print "'$s'\n" ;
}
__DATA__
s/foo/bar/
s/(fo)o/bar$1/
s/(fo)o/bar\$1/
s/(fo)o/bar\\$1/
s/oo/${system "echo hello sailor !"}/
s/oo/@{system "echo hello sailor !"}/
which only attempts to simulate '$1'..'$9' -- but that may be sufficient. Of course, String::Interpolate may well do a more complete job.
Unless, of course, there's some other gotcha...
| [reply] [d/l] |