Orabîg Orabîg - 24 days ago 7
Perl Question

perl operator s///e not working as expected

I'm trying to figure out why this is working :

$_='12+34';$x=1;
s/(\d+)(.)(\d+)/"\$x$2$2"/ee; # This is working, does $x++

print "x=$x \n"; # x=2


while this is not :

$_='12+34';$x=1;
s/(\d+)(.)(\d+)/\$x$2$2/e; # This is NOT working

# Error message is :
# Scalar found where operator expected at ./test.pl line 2, near "$x$2"
# (Missing operator before $2?)


I have the guts that
s/xxx/yyy/e
and
s/xxx/"yyy"/ee
should behave the same, but obviously I'm wrong.

What am I missing ?

Answer

You're misunderstanding the expression modifier -- a single /e

It is essentially an alternative to the standard mode, which is to process the replacement string as if it were in double-quotes

Normally

my $x = 1;
my $y = '12+34';

$y =~ s/(\d+)(.)(\d+)/\$x$2$2/

produces a replacement equivalent to the string qq{\$x$2$2}, which is $x++

If you add an /e then the replacement is treated as a Perl expression, and you are getting errors because \$x$2$2 isn't valid Perl. You could get the same result as before by using

s/(\d+)(.)(\d+)/'$x' . $2 . $2/e

or, as you have seen, the string expression

s/(\d+)(.)(\d+)/"\$x$2$2"/e

But all these do is evaluate the Perl expression. There is no way to execute arbitrary Perl code that is constructed from parts of the target string without adding a second /e modifier which stands for eval

The resulting /ee causes Perl to treat the replacement as an expression (instead of doing double-quote interpolation on it) and then eval the result of that expression

For instance

s/(\d+)(.)(\d+)/'$x' . $2 . $2/ee

first evaluates the expression '$x' . $2 . $2 giving $x++ and then does eval on that string, returning 1 (so the original 12+34 is replaced with 1) and incrementing $x

You can use expression mode with a single /e if you can write a Perl expression that does what you need. Otherwise you need to use /ee to get an eval stage as well

I think it's clearer to use braces if you involve /e at all. That way it looks like Perl code and not a string replacement

s{(\d+)(.)(\d+)}{
    '$x' . $2 . $2
}ee