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