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

I've lately begun to explore Parse::RecDescent and have a couple of questions. Given:

#!/usr/bin/perl # parse.pl -- learning Parse::RecDescent ex. use strict; use warnings; use diagnostics; use Parse::RecDescent; my $parser = new Parse::RecDescent( q{ startrule: calculation eofile {$return = $item[1];} statement: grouped_op | number grouped_op: '(' calculation ')' {$return = $item[2];} calculation: add_calculation | sub_calculation | mul_calculation | + div_calculation add_calculation: statement '+' statement {$return = $item[1] + $it +em[3];} sub_calculation: statement '-' statement {$return = $item[1] - $it +em[3];} mul_calculation: statement '*' statement {$return = $item[1] * $it +em[3];} div_calculation: statement '/' statement {$return = $item[1] / $it +em[3];} number: /\d+/ eofile: /^\Z/ }); my @tests = ( '4 + 8', '6 + (3 - 2)', '(6 + (3 - 2)) + 1', '4 + 8 + 5', '5 * 5', '5 / 5', ); my $result; foreach my $test (@tests) { if ( $result = $parser->startrule($test) ) { print "'$test' = $result\n"; } else { print "'$test' failed parser\n"; } }
It's clear that the parentheses work, my first question is why does the lack fail? '4 + 8 + 5' working from left to right should collapse into '12 + 5' and then to '17'. Obviously using $::RD_TRACE = 1; shows that it gets as far as '12', but then fails after that. I'm fairly sure this is a beginner's error but it doesn't leap out at me.

Looking ahead, I'd want to implement some sort of precedence scheme. Pointers, brickbats and suggestions gratefully accepted!

–hsm

"Never try to teach a pig to sing…it wastes your time and it annoys the pig."

Replies are listed 'Best First'.
Re: Parse::RecDescent without parentheses
by maverick (Curate) on Feb 03, 2002 at 19:58 UTC
    Your grammer doesn't allow a statement to contain a calculation, where a calculation is 4+8 let's say. So, after it consumes the 4+8 it has no way to know what to do with the +5 part. Also as an asside, you have all the math operators at the same precedence...so you'll end up wandering later why 2+4*5 = 30 and not 22 :)

    The grammer for parsing standard algebra is a pretty common example for teaching parsing. (Give me a sec to dig up a link for an example) :)

    <update style="big">
    Complete example (almost) taken straight from the "Dragon" compiler book. Page 259

    expression: expression '+' term | expression '-' term | term term: term '*' factor | term '/' factor | factor factor: '(' expression ')' | /\d+/
    I'm sorry I don't have time to explain exactly how it works, but I can tell you how to figure that out. Turn on tracing as you have, and watch as many examples as you can and pay close attention to HOW Parse::RecDescent walks this grammer and your input.

    /\/\averick
    perl -l -e "eval pack('h*','072796e6470272f2c5f2c5166756279636b672');"

      That's a "left recursive" grammar, which gives Recursive Descent compilers (of which P::RD is one) a hissy fit.

      I believe that the P::RD examples include a simple parser that actually works. {grin}

      -- Randal L. Schwartz, Perl hacker

        DOH! I was in such a rush that I didn't check that the grammer wasn't left recursive. Hmm... wouldn't swapping the order of the nonterminals in expression and term make it 'right recursive' and solve the problem?

        /\/\averick
        perl -l -e "eval pack('h*','072796e6470272f2c5f2c5166756279636b672');"

Re: Parse::RecDescent without parentheses
by jmcnamara (Monsignor) on Feb 03, 2002 at 22:24 UTC

    One of the main things that you are missing is the leftop directive. However, the grammar also need to be restructured.

    To see how it could/should be done have a look at the demo_calc.pl example that comes the Parse::RecDescent distro. It demonstrates almost exactly what you are trying to do here.

    --
    John.

Re: Parse::RecDescent without parentheses
by dreadpiratepeter (Priest) on Feb 03, 2002 at 20:20 UTC

    WARNING: I might be talking out of my butt here.

    I havn't used Parse::RecDescent yet, but I was weened on yacc.
    The first one (4+8) works because you have and add_calculation which is a calculation that is allowed in your start rule.
    The second one (4+8+5) doesn't because it can't match the add calculation. I think it's because it matches 4+8 as the statement leaving +5 left over, causing your startrule to fail.

    The heart of the problem is that a num op num expression isn't a calculation unless it is surrounded by ().

    I'm, however not exactly sure how to fix it, and I'm under the gun on my own project, so hopefully, an expert can reply here and fix the grammar. UPDATE: Maverick beat me to the punch with a better answer. See his response

    -pete
    Entropy is not what is used to be.