richard5mith has asked for the wisdom of the Perl Monks concerning the following question:

I've got a problem with Parse::RecDescent where it doesn't appear to be passing on subrule arguments. The grammar works fine otherwise, but the "parameter" rule does not seem to get any value for $arg[0], which should contain the name of the function matched in the "functiondefine" rule.

This is simplified example, but it is the basics of the problem...

use Parse::RecDescent; $grammar = << 'STOP'; functiondefine: /function/i name "(" parameter[ $item[2] ](s? /,/) +")" "{" name: /[A-Za-z][a-zA-Z0-9_]*/ parameter: name /as/i variabletype { warn $arg[0] } variabletype: /variant/i | /array/i | /keyarray/i STOP $parser = new Parse::RecDescent($grammar); $text = << 'STOP'; function myfunction(something as variant, somethingelse as array, foo +as keyarray) { } STOP $parser->functiondefine($text);

The result of which is...

Warning: something's wrong at (eval 15) line 168.
Warning: something's wrong at (eval 15) line 168.
Warning: something's wrong at (eval 15) line 168.

Meaning that $arg[0] isn't getting set.

Replies are listed 'Best First'.
Re: Problem with Parse::RecDescent and subrule arguments
by ikegami (Patriarch) on Aug 26, 2004 at 00:36 UTC

    I added the line
    Parse::RecDescent->Precompile($grammar, "Grammar");
    and looked at the source of the parser that was created. @args is simply never set! It's simply inherited down from the parent production as if [ $item[2] ] wasn't there. So I tried to see if it worked with simpler expressions:

    # NO WORKS: funcdefine: /function/i name "(" parameter[ $item[2] ](s? /,/) ")" "{" # WORKS: funcdefine: /function/i name "(" parameter[ $item[2] ](s?) ")" "{" # WORKS: funcdefine: /function/i name "(" parameter[ $item[2] ] ")" "{"

    So it's a bug in Parse::RecDescent in the (... /,/) code. btw, (... /,/) is implemented as a <leftop>, so you could try using <leftop> directly (might not work), or try using the code said to be equivalent to <leftop> in the docmentaton (will definitely work), or use my little hack below (will definitely work).

    # HACKED TO WORK: functiondefine: /function/i name "(" {@arg=($item[2]);1} parameter(s? /,/) ")" "{"

    I only renamed functiondefine above so that the lines wouldn't wrap.

      Yup, that little hacked version at the end works a treat. Thanks!

Re: Problem with Parse::RecDescent and subrule arguments
by jryan (Vicar) on Aug 26, 2004 at 00:30 UTC

    This one had me stumpted. subrule(s /,/) is just a macro for <leftop: subrule /,/ subrule>, and so subrule(s? /,/) is just a macro for <leftop: subrule /,/ subrule>(?). However, internally, I think that leftop goes through another level, and so @arg must get overwritten. (that's just a guess as to what is happening, so don't take it as fact.) Anyways, whatever P::RD is doing internally, I would classify it as a bug.

    However, just telling you that its a bug isn't really helping you, so here is a workaround:

    my $parser = new Parse::RecDescent (q[ functiondefine: /function/i name "(" parameters[$item[1]](?) ")" " +{" name: /[A-Za-z][a-zA-Z0-9_]*/ parameters: parameter[$arg[0]] ',' parameters[$arg[0]] { [@ite +m[1,3]] } | parameter[$arg[0]] { [$item[1]] } parameter: name /as/i variabletype { [@item[1..$#item]] } variabletype: /variant|array|keyarray/i ]);

    This is much more inefficent then using <leftop:> would be, but at least it works (at least as far as I've tested it.) I also collapsed your variabletype rule into a single regex, which will be much more efficient.