kdubs
kdubs

Reputation: 1722

perl parsing grammar with regex with_actions class not called for match

In the following code I'm using a class for the actions of the grammar. There's a flaw in the grammar where I don't match at the beginning of the line. I know how to fix that, but it's showing a behavior I don't understand. When it matches starting at the second character the action in the class isn't called.

here's the output, notice you see 'ANSWER called' for the first input but not for the second:

      info | Processing the main regex before any rule definitions
       |    |
       |    |...Treating <debug: run> as:
       |    |       \ Change run-time debugging mode to 'run'
       |    |
       |    |...Treating <Answer> as:
       |    |      |  match the subrule <Answer> 
       |    |       \ saving the match in $MATCH{'Answer'}
       |    |
       |     \___End of main regex
       | 
       | Defining a rule: <Answer>
       |    |...Returns: a hash
       |    |
       |    |...Treating <[Operand=Literal]> as:
       |    |      |  match the subrule <Literal> 
       |    |       \ appending the match to $MATCH{'Operand'}
       |    |
       |     \___End of rule definition
       | 
       | Defining a token: <Literal>
       |    |...Returns: a hash
       |    |
       |    |...Treating '?:' as:
       |    |       \ normal Perl regex syntax
       |    |
       |    |...Treating <MATCH=( [+-]? \d++ (?: \. \d++ )?+ )> as:
       |    |      |  match the pattern ( [+-]? \d++ (?: \. \d++ )?+ ) 
       |    |       \ saving the matched substring in $MATCH{'='}
       |    |
       |     \___End of rule definition
       | 

4 <<<==== here's my first input

=====> Trying <grammar> from position 0
4\n    |...Trying <Answer>
       |   |...Trying <[Operand=Literal]>
       |   |   |...Trying <MATCH=( [+-]? \d++ (?: \. \d++ )?+ )>
\n     |   |   |    \_____<MATCH=( [+-]? \d++ (?: \. \d++ )?+ )> matched '4'
       |   |    \_____<[Operand=Literal]> matched '4'

ANSWER called <<<=== the match and action is called

       |    \_____<Answer> matched '4'
--> 4
.4
=====> Trying <grammar> from position 0
.4\n   |...Trying <Answer>
       |   |...Trying <[Operand=Literal]>
       |   |   |...Trying <MATCH=( [+-]? \d++ (?: \. \d++ )?+ )>
       |   |   |    \FAIL <MATCH=( [+-]? \d++ (?: \. \d++ )?+ )>
       |   |    \FAIL <[Operand=Literal]>
       |    \FAIL <Answer>
        \FAIL <grammar>
=====> Trying <grammar> from position 1
4\n    |...Trying <Answer>
       |   |...Trying <[Operand=Literal]>
       |   |   |...Trying <MATCH=( [+-]? \d++ (?: \. \d++ )?+ )>
\n     |   |   |    \_____<MATCH=( [+-]? \d++ (?: \. \d++ )?+ )> matched '4'
       |   |    \_____<[Operand=Literal]> matched '4'

WHY no call to ANSWER here?

       |    \_____<Answer> matched '4'
--> HASH(0x634ee78)

and here's the code:

#!/grid/common/bin/perl -w 

use strict;

use v5.10;
use warnings;

use lib "/vobs/uth/perl";

my $calculator = do{
    use Regexp::Grammars;
    qr{
 <debug: run>
    <Answer>

        <rule: Answer>
        <[Operand=Literal]>

        <token: Literal>
        (?:
            <MATCH=( [+-]? \d++ (?: \. \d++ )?+ )>
        )
    }xms
};


while (my $input = <>) {

    if ($input =~ $calculator->with_actions('Calculator_Actions') ) {
        say '--> ', $/{Answer};
    }
    else {
    say {*STDERR} $_ for  @!;
    }
}

package Calculator_Actions;

use List::Util qw< reduce >;

sub Answer {
    my ($self, $MATCH_ref) = @_;

    say "ANSWER called";

    my $value = shift @{$MATCH_ref->{Operand}};

    return $value;
}

Upvotes: 2

Views: 63

Answers (2)

kdubs
kdubs

Reputation: 1722

I've submitted a bug for this issue.

https://rt.cpan.org/Public/Bug/Display.html?id=111051

Upvotes: 0

bolav
bolav

Reputation: 6998

I think this is a bug in the library. If I add this to clear_rule_handler:

-sub clear_rule_handler { undef $RULE_HANDLER; }
+sub clear_rule_handler {
+    warn 'Clearing $RULE_HANDLER';
+    undef $RULE_HANDLER;
+}

I get this answer:

=====> Trying <grammar> from position 0
4\n    |...Trying <Answer>
       |   |...Trying <[Operand=Literal]>
       |   |   |...Trying <MATCH=( [+-]? \d++ (?: \. \d++ )?+ )>
\n     |   |   |    \_____<MATCH=( [+-]? \d++ (?: \. \d++ )?+ )> matched '4'
       |   |    \_____<[Operand=Literal]> matched '4'
ANSWER called
       |    \_____<Answer> matched '4'
Clearing $RULE_HANDLER at /Users/bolav/perl5/perlbrew/perls/perl-5.20.1/lib/site_perl/5.20.1/Regexp/Grammars.pm line 88, <> line 1.
--> 4
.4
=====> Trying <grammar> from position 0
.4\n   |...Trying <Answer>
       |   |...Trying <[Operand=Literal]>
       |   |   |...Trying <MATCH=( [+-]? \d++ (?: \. \d++ )?+ )>
Clearing $RULE_HANDLER at /Users/bolav/perl5/perlbrew/perls/perl-5.20.1/lib/site_perl/5.20.1/Regexp/Grammars.pm line 88, <> line 2.
       |   |   |    \FAIL <MATCH=( [+-]? \d++ (?: \. \d++ )?+ )>
       |   |    \FAIL <[Operand=Literal]>
       |    \FAIL <Answer>
        \FAIL <grammar>
=====> Trying <grammar> from position 1
4\n    |...Trying <Answer>
       |   |...Trying <[Operand=Literal]>
       |   |   |...Trying <MATCH=( [+-]? \d++ (?: \. \d++ )?+ )>
\n     |   |   |    \_____<MATCH=( [+-]? \d++ (?: \. \d++ )?+ )> matched '4'
       |   |    \_____<[Operand=Literal]> matched '4'
       |    \_____<Answer> matched '4'
Clearing $RULE_HANDLER at /Users/bolav/perl5/perlbrew/perls/perl-5.20.1/lib/site_perl/5.20.1/Regexp/Grammars.pm line 88, <> line 2.
--> HASH(0x7f9212003390)

So it looks like the rule_handler is cleared in the first position, and should be reset when trying again. If i do this with a quick hack:

 my $RULE_HANDLER_OLD;
 # Prepare for debugging...
 sub _init_try_stack {
     our (@try_stack, $last_try_pos, $last_context_str);

     # Start with a representation of the entire grammar match...
     @try_stack = ({
         subrule => '<grammar>',
         height  => 0,
         errmsg  => ' \\FAIL <grammar>',
     });

     # Initialize tracking of location and context...
     $last_try_pos     = -1;
     $last_context_str = q{};

     # Report...
+    if ($RULE_HANDLER) {
+        $RULE_HANDLER_OLD = $RULE_HANDLER;
+    }
+    else {
+        $RULE_HANDLER = $RULE_HANDLER_OLD;
+    }
+
     say {*Regexp::Grammars::LOGFILE} _debug_context('=>')
                 . 'Trying <grammar> from position ' . pos();
 }

The output is as you expect it, I think:

4
=====> Trying <grammar> from position 0
4\n    |...Trying <Answer>
       |   |...Trying <[Operand=Literal]>
       |   |   |...Trying <MATCH=( [+-]? \d++ (?: \. \d++ )?+ )>
\n     |   |   |    \_____<MATCH=( [+-]? \d++ (?: \. \d++ )?+ )> matched '4'
       |   |    \_____<[Operand=Literal]> matched '4'
ANSWER called
       |    \_____<Answer> matched '4'
Clearing $RULE_HANDLER at /Users/bolav/perl5/perlbrew/perls/perl-5.20.1/lib/site_perl/5.20.1/Regexp/Grammars.pm line 88, <> line 1.
--> 4
.4
=====> Trying <grammar> from position 0
.4\n   |...Trying <Answer>
       |   |...Trying <[Operand=Literal]>
       |   |   |...Trying <MATCH=( [+-]? \d++ (?: \. \d++ )?+ )>
Clearing $RULE_HANDLER at /Users/bolav/perl5/perlbrew/perls/perl-5.20.1/lib/site_perl/5.20.1/Regexp/Grammars.pm line 88, <> line 2.
       |   |   |    \FAIL <MATCH=( [+-]? \d++ (?: \. \d++ )?+ )>
       |   |    \FAIL <[Operand=Literal]>
       |    \FAIL <Answer>
        \FAIL <grammar>
=====> Trying <grammar> from position 1
4\n    |...Trying <Answer>
       |   |...Trying <[Operand=Literal]>
       |   |   |...Trying <MATCH=( [+-]? \d++ (?: \. \d++ )?+ )>
\n     |   |   |    \_____<MATCH=( [+-]? \d++ (?: \. \d++ )?+ )> matched '4'
       |   |    \_____<[Operand=Literal]> matched '4'
ANSWER called
       |    \_____<Answer> matched '4'
Clearing $RULE_HANDLER at /Users/bolav/perl5/perlbrew/perls/perl-5.20.1/lib/site_perl/5.20.1/Regexp/Grammars.pm line 88, <> line 2.
--> 4

I don't have overview of the sideeffects of my hack.

Upvotes: 1

Related Questions