Skip to content

Conversation

@mauke
Copy link
Contributor

@mauke mauke commented Aug 28, 2025

  • s///e is not limited to expressions. The replacement part can contain any number of statements, like do { ... }.
  • s///ee does not treat the RHS as a string. The RHS is parsed as a block (like s///e), but then the resulting string is eval()d.
  • You can have more than two es. Each additional e wraps another eval() around the result.

  • This set of changes does not require a perldelta entry.

@book
Copy link
Contributor

book commented Aug 28, 2025

If it behaves as a do {} block, does that means there is a scope limited to the replacement part? And that each extra /e creates a new enclosing scope?

@mauke
Copy link
Contributor Author

mauke commented Aug 29, 2025

If it behaves as a do {} block, does that means there is a scope limited to the replacement part?

Yes:

use v5.36;

sub Honk::DESTROY {
    say "$_[0] destroyed";
}

my $string = "hello";
$string =~ s{([eo])}{
    say "replacing '$1'";
    my $obj = bless {}, 'Honk';
    "_"
}eg;

say "done: $string";
say '$obj is ', eval('$obj') // "not in scope: $@";

Output:

replacing 'e'
Honk=HASH(0x6140b77d4418) destroyed
replacing 'o'
Honk=HASH(0x6140b77d4418) destroyed
done: h_ll_
$obj is not in scope: Global symbol "$obj" requires explicit package name (did you forget to declare "my $obj"?) at (eval 1) line 1.

And that each extra /e creates a new enclosing scope?

Only in the sense that every eval() creates a runtime scope for the code it compiles, but I'm not sure what practical difference it would make.

Copy link
Contributor

@khwilliamson khwilliamson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This p.r. is a huge improvement IMO

result.
e Evaluate the right side as a block of code.
ee Evaluate the right side as a block of code, then eval()
the resulting string.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where exactly in the source code does this evaluation of the RHS as a block of code rather than as either an expression or a string is enforced? (I'm asking this not to quibble, but just to know your reference point.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't know this beforehand. This PR was written from a Perl programmer POV; I just know how /e behaves in practice. You can put multiple statements separated by ; in the replacement part; it acts as a block scope; etc.

But I looked it up. The relevant part of the perl source is S_scan_subst in toke.c. It has special handling for EXEC_PAT_MOD flags (defined as 'e'), which it keeps a count of in es. If es is non-zero, it turns on the PMf_EVAL flag on the op and literally wraps do { ... } around the replacement part. Then, for every e beyond the first one, it prepends eval to the replacement part. In other words, it doesn't even act on op trees; it literally generates source code of the form ("eval " x ($es - 1)) . "do { $RHS }". I didn't expect that.

@book
Copy link
Contributor

book commented Sep 3, 2025

  • You can have more than two es. Each additional e wraps another eval() around the result.

And I wrote a perfect example years ago:

use strict;my$s=';pop';$_='';# 59 /e, © 2001 Philippe "BooK" Bruhat
*_=*ARGV;@_=qw(4a4a2bfe01ac410d0105f4fd0dae30150dfab448f90208fa0d98
pop print+chr(shift) print+chr(shift) unshift@_,(shift()+shift())x2
push@_,(pop,pop)x24 ; unshift@_,(map{unpack'c',$_}split//)[0..25] ;
s/../chr(hex$&)/eg $_=substr(shift,0,52) @_=map{s|$|$s|s;$_}@_;pop)
;s//$s/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee;

- s///e is not limited to expressions. The replacement part can contain
  any number of statements, like 'do { ... }'.
- s///ee does not treat the RHS as a string. The RHS is parsed as a
  block (like s///e), but then the resulting string is eval()'d.
- You can have more than two e's. Each additional 'e' wraps another
  eval() around the result.
@mauke mauke force-pushed the perlop-document-s---e branch from 417ee0c to b8322d9 Compare September 13, 2025 10:21
@mauke mauke requested a review from khwilliamson September 13, 2025 10:22
@khwilliamson khwilliamson dismissed their stale review September 13, 2025 10:54

changed as requested

@mauke mauke merged commit 040a4d7 into Perl:blead Sep 13, 2025
33 checks passed
@mauke mauke deleted the perlop-document-s---e branch September 13, 2025 22:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants