Annotation/correction? to a FAQ topic
[email protected] (Karl Gaissmaier) Thu, 01 Apr 2004 11:29:58 +0200
| Newsgroups | perl.recdescent |
|---|---|
| Message-ID | <[email protected]> |
Hi P::RD lovers and FAQ maintainer(s),
I stumbled over a piece of code in the FAQ
belonging to this topic:
Commit in subrule which is optional in rule
============================================
The question was, how to fail a parent rule when an optional
subrule has already commited but fails after commitment.
Damian suggests a negative look-ahead following the optional
subrule production, which works if you
have an { action } and not just the default return result.
Later on in this chapter there is an optimization example
by Marcel Grunaer which isn't working correctly IMHO:
> Marcel went on to point out an optimization:
>
> another option would be the use of a rulevar:
>
> myrule : <rulevar: local $failed>
> myrule : 'stuff' mysubrule(?) <reject:$failed>
>
> mysubrule: ID <commit> '[' ']'
> | <error?> { $failed++ }
the rule 'myrule' should fail if the subrule 'mysubrule'
has already commited. This will not happen, let me explain:
Case 1: the subrule 'mysubrule' fails before <commit>
the production '| <error?> { $failed++ }' returns
0 (not undef!) for the following reasons:
<error?> returns 0, since it wasn't commited (see below)
{ $failed++ } returns 0, since it's a postincrement
of an formerly undefined value.
The subrule matches, but the parent rule isn't successful
the <reject: $failed> matches.
This is not the intended behavior, the myrule should
match as the subrule didn't <commit>
Case 2: the subrule 'mysubrule' fails after <commit>
the production '| <error?> { $failed++ }' comes to
the directive <error?>, this directive matches and returns
undef as a <error..> directive should do.
This means, you will never come to the { $failed++ } action.
The subrule fails, the rule is successful since we have
the optional (?) and $failed is still not set.
The usual ' | <error?> <reject> ' pattern will have misled
Marcel and all other FAQ readers until now, because this
pretends that after a successful <error?> directive the
subrule is continued. But this isn't correct, the <reject>
directive is needed for uncommited errors.
Hmmm, you will ask, why that, we just hit this production
only if we are commited since the first directive is <error?>.
No, when an <error> OR <error?> is the first directive in a
production, an implicit <uncommit> is fired.
Sure, it's difficult but useful and don't forget it's from Damian ;-)
Just in case, see my attached code, this is the last source
of truth if Damian has no time to follow this mailinglist:
<<<<<<<<<<<<<<<<<<<<<<<< snip >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#!/usr/local/bin/perl
use strict;
use warnings;
use Parse::RecDescent;
$::RD_TRACE = 1;
use Data::Dumper;
my $grammar = <<'EOG';
myrule : <rulevar: local $failed>
myrule : mysubrule(?) <reject:$failed> { $return = 'success!' }
mysubrule : 'ID' <commit> '[' ']'
| <error?> { $failed++ }
EOG
my $parser = Parse::RecDescent->new($grammar)
or die "can't create parser,";
my $text = join '', <>;
print Dumper($parser->myrule($text));
<<<<<<<<<<<<<<<<<<<<<<<< snip >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Best Regards
Charly
--
Karl Gaissmaier KIZ/Infrastructure, University of Ulm, Germany
Email:[email protected] Service Group Network
Tel.: ++49 731 50-22499