variables crash inside (?{})

J

Jerome Abela

Hi there,

The following code comes directly from perlre(1), except it's inside a
sub:

animals();
animals();
sub animals {
my ($color, $animal);
$_ = "The brown fox jumps over the lazy dog";
/the (\S+)(?{ $color = $^N }) (\S+)(?{ $animal = $^N })/i;
print "color = $color, animal = $animal\n";
}

But its output is surprizing (at least with v5.8.5 and v5.8.7):

color = brown, animal = fox
color = , animal =

Everything works if I don't declare lexical variables, so it may be
related to some kind of scoping from inside the (?{}). But I can't find
any reasonable explanation for this behavior.

I need help from more knowledgeable people: how can I write more robust
code to be used from (?{}) expressions ?

Or, as an alternative, how can I store data matched inside nested
groups ? My real original code is more like:
m/special\((?:before(group)after(?{save it, using $^N}))*\)/
where the {save using $^N} part only works once because of the bug
above.


Jerome.
 
X

xhoster

Jerome Abela said:
Hi there,

The following code comes directly from perlre(1), except it's inside a
sub:

animals();
animals();
sub animals {
my ($color, $animal);
$_ = "The brown fox jumps over the lazy dog";
/the (\S+)(?{ $color = $^N }) (\S+)(?{ $animal = $^N })/i;
print "color = $color, animal = $animal\n";
}

But its output is surprizing (at least with v5.8.5 and v5.8.7):

color = brown, animal = fox
color = , animal =

Everything works if I don't declare lexical variables, so it may be
related to some kind of scoping from inside the (?{}). But I can't find
any reasonable explanation for this behavior.


The regex gloms onto the $color and $animal which are in scope the first
time the regex is encountered, and doesn't update them. The second time
animals is called, a different $color and $animal are created, but the
regex is still using the old ones.

It appears to be just like as if you did:

animals();
animals();
sub animals {
my ($color, $animal);
sub foo {
$color="brown";
$animal="fox";
};
foo();
print "color = $color, animal = $animal\n";
}

Only this generates a 'Variable "$color" will not stay shared' warning
while the regex does not generate warnings.

I need help from more knowledgeable people: how can I write more robust
code to be used from (?{}) expressions ?

Considering the "highly experimental" disclaimer, I would suggest you can't
write robust code to be used thus. Ignoring that, use a package variable.
Or, as an alternative, how can I store data matched inside nested
groups ?

I've never had problems doing that with plain old regular expressions.
My real original code is more like:
m/special\((?:before(group)after(?{save it, using $^N}))*\)/
where the {save using $^N} part only works once because of the bug
above.

How about something like:

my ($thing) = m/special\(((?:beforegroupafter)*)\)/ or die;
my (@data) = $thing=~/before(group)after/g;

Xho
 
B

Brian McCauley

Jerome said:
Hi there,

The following code comes directly from perlre(1), except it's inside a
sub:

animals();
animals();
sub animals {
my ($color, $animal);
$_ = "The brown fox jumps over the lazy dog";
/the (\S+)(?{ $color = $^N }) (\S+)(?{ $animal = $^N })/i;
print "color = $color, animal = $animal\n";
}

But its output is surprizing (at least with v5.8.5 and v5.8.7):

color = brown, animal = fox
color = , animal =

Everything works if I don't declare lexical variables, so it may be
related to some kind of scoping from inside the (?{}). But I can't find
any reasonable explanation for this behavior.

There is no reasonable explaination for why (?{}) doesn't play nicely
with lexical variables. There's an unreasonable one analagous to the
mechanism that results in the the "will not remain shared" warning.

This is a know problem but (?{}) is still considered experimental isn't
it?
I need help from more knowledgeable people: how can I write more robust
code to be used from (?{}) expressions ?

Until further notice use only package variables inside (?{}).

Simply change

my ($color, $animal);

to

local our ($color, $animal);
 
J

Jerome Abela

Thanks a lot, Xho and Brian !

Now, I have a nice explanation of what happens (the regex creates a
closure on the lexical variables when executed for the first time), and
I have the solution, which consist in using "our", so that both the
closure and my local variables refer to the same thing.

Thank you to both of you !
How about something like:
my ($thing) = m/special\(((?:beforegroupafter)*)\)/ or die;
my (@data) = $thing=~/before(group)after/g;

"before", "group", and "after" are complex regexps, so it hurts to
execute them twice.


Jerome.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top