Subject: | RecDescent incorrectly parses code in actions |
Hi,
The following problem has been reproduced under the following conditions:
RecDescent 1.80
SunOS steamtown 5.8 Generic_108528-10 sun4u sparc SUNW,Sun-Blade-100
perl, version 5.005_03 built for sun4-solaris
perl, v5.6.1 built for sun4-solaris-thread-multi (Binary build 626 provided by ActiveState)
I found what seems to be a bug in the way RecDescent 1.80 extracts code fragments from actions, in very specific circumstances.
I'm using a grammar within an object-oriented module I wrote. In order for the grammar to interact with an instance of my object, I pass the object instance as an argument to the start rule and store that as a local rulevar. I then use that object within the grammar. That seems to work fine in general. However, I found a very weird instance where this does not work: I wanted to capture all errors generated by RecDescent into my object instance, so I used code similar to what is described in the FAQ.
Included below is a complete script that shows what I mean. If you run that script with RecDescent 1.80, you should get the following errors:
Warning: Undefined (sub)rule "word" used in a production.
Warning: Undefined (sub)rule "EOFILE" used in a production.
Run it with $::RD_TRACE enabled, and look for the error-catching action code in the RD_TRACE file: it's not there (do a 'grep _add_errors RD_TRACE' for example). This seems to be caused by the following line:
$obj->_add_errors("Error at line $line: $msg\n");
Either comment out the above line from the grammar, or just try adding the following line of code right after that line:
print("");
If you do that, the grammar errors will magically go away. Looking at RD_TRACE again, you will find that the error-catching code has been correctly included (for example 'grep _add_errors RD_TRACE' will find one matching line).
In the real, more complex grammar I wrote, RD_TRACE shows that under the above conditions, RecDescent mistakenly goes past the action block that contains the $obj->_add_errors() call, and identifies a big chunk of the grammar as being part of that action, thus resulting in an invalid grammar (or so it thinks!).
-- Cut here --
use strict;
use Parse::RecDescent;
my $grammar = <<'EOF';
grammar : <rulevar: local $obj = $arg[0]>
grammar : word(s?) EOFILE { return $item[1]; }
|
# The following action will generate grammar errors
# unless you comment out the $obj line, or
# add any valid perl code after it.
{
foreach my $err (@{$thisparser->{errors}}) {
my $line = $err->[1];
my $msg = $err->[0];
$obj->_add_errors("Error at line $line: $msg\n");
}
$thisparser->{errors} = undef;
}
word : m/\w+/
EOFILE : /^\Z/
EOF
my $obj = Object->new();
my $grammar = Parse::RecDescent->new($grammar);
$grammar->grammar("This is a test", 1, $obj);
$obj->print_errors();
package Object;
sub new {
my($class) = @_;
return bless ( { _error => [ ] }, ref($class) || $class);
}
sub _add_errors {
my($self, @err) = @_;
push(@{$self->{_error}}, @err);
}
sub print_errors {
my($self) = @_;
print(join("", @{$self->{_error}}), "\n");
}
Show quoted text
--- Cut here ---