Somewhere between "use re 'eval'" and "no re 'eval'"

Say, you have a function, which takes a string, a pattern, and then matches the pattern to the string, but with a twist; a (fixed) code fragment is inserted:

sub example {
    my ($str, $pattern) = @_;
    $str =~ /$pattern(?{ 1; })/;
}

The code block here is trivial, because it's not about the content of the block, just about its existence.

The above doesn't work, because by default you cannot interpolate a string in a pattern containing code blocks. So, you have to use re 'eval';:

sub example {
    my ($str, $pattern) = @_;
    use re 'eval';
    $str =~ /$pattern(?{ 1; })/;
}

Now, that works, but that works a bit too well for my liking - it would now run code blocks if they are passed in using $pattern. This is not what I want. The following seems to fix that:

sub example {
    my ($str, $pattern) = @_;
    no re 'eval';
    $pattern = qr /$pattern/;
    use re 'eval';
    $str =~ /$pattern(?{ 1; })/;
}

This dies if $pattern contains a (?{ code }) block. Except that it doesn't die when $pattern is already a compiled pattern - that's deemed safe. So, we need an additional stringification:

sub example {
    my ($str, $pattern) = @_;   
    no re 'eval';
    $pattern = "$pattern";
    $pattern = qr /$pattern/;
    use re 'eval';
    $str =~ /$pattern(?{ 1; })/;
}

This does what I want it to do: run the code block, but die if $pattern contains a code block, regardless whether it's passed as a string or a compiled regexp.

But I wonder, is there a less cluncky way?

1 Comment

Not without going the Perl 6 way, i.e. letting the regexp engine see $pattern as a token (and leaving to fetch its value) rather than just the interpolated contents of $pattern – so that the regexp compiler would know which part of the pattern is literal (and code blocks therefore allowed) and which is interpolated (and code blocks therefore forbidden).

With the Perl 5 approach it could at least be simplified if instead of a re pragma this was controlled by regexp flags. Then again we have too many of them anyway.

But as it is I see no better way.

You can move things around a bit in your example: since compiled patterns as deemed safe, you could compile your code block and then later just interpolate it, obviating the need for an explicit “use re 'eval'”. But you don’t gain a thing.

Leave a comment

About Abigail

user-pic I am Abigail.