Orabîg
Orabîg

Reputation: 12012

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 ?

Upvotes: 0

Views: 220

Answers (1)

Borodin
Borodin

Reputation: 126742

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

It causes the replacement string to be treated as a Perl expression, and is essentially an alternative to the standard mode, which is to process the 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

Upvotes: 7

Related Questions