http://qs321.pair.com?node_id=894775

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

Hi all

Another one with Parse::RecDescent:

If you want to get back a list of tokens from a grammar you have to do something like this:

use strict; use warnings; use Data::Dumper use Parse::RecDescent; use vars qw/@tokens/; my $p6 = Parse::RecDescent->new(q( letter : /\w/ character : letter { push @main::tokens, $item{letter}; } Format : character Format )); $p6->Format('abc'); die "Invalid pattern" unless (defined $p6); print Dumper \@tokens;

Is there any way to avoid the use of global variables for achieving this? I would like to feed the grammar with different input strings

Thanks in advance!

citromatik

Replies are listed 'Best First'.
Re: scope issues in Parse::RecDescent
by Corion (Patriarch) on Mar 22, 2011 at 12:21 UTC

    As Parse::RecDescent gets its grammar as a string, I see no way to create clever closures or anything that will allow you to use lexical variables. One approach I can think of would be judicious use of local to isolate the effects, but that implies that your parsers won't call each other recursively:

    ... sub do_parse { local $parser_info = { tokens => [], }; my $p6 = Parse::RecDescent->new(q{ { push @{ $parser_info->{tokens} }, $item{letter} } }); }; ...

    That way, you reduce your need for a global variable to one entry point, $parser_info. I think you can also instruct Parse::RecDescent to return you the whole parse tree as an AST instead of having it execute code immediately, by using the <autotree> directive. That wouldn't require any code within your parser, but you have to walk the tree yourself after it has been constructed.

      It seems that you can define "top level" actions and define variables in them. So this would do the trick:

      use strict; use warnings; use Data::Dumper use Parse::RecDescent; ### use vars qw/@tokens/; Avoided using global variables my $p6 = Parse::RecDescent->new(q( {my @tokens} letter : /\w/ character : letter { push @tokens, $item{letter}; } Format : character(s) { \@tokens } )); my $tokens_ref = $p6->Format('abc'); die "Invalid pattern" unless (defined $tokens_ref); print Dumper $tokens_ref;

      Outputs:

      $VAR1 = [ 'a', 'b', 'c' ];

      citromatik

Re: scope issues in Parse::RecDescent
by jethro (Monsignor) on Mar 22, 2011 at 13:36 UTC
    my $p6 = Parse::RecDescent->new(q( letter : /\w/ character : letter Format : character(s) )); my $x= $p6->Format('abc'); die "Invalid pattern" unless (defined $p6); print Dumper $x; #prints $VAR1 = [ 'a', 'b', 'c' ];

    Be careful to specify what a rule returns with $return if more than one production is in a rule (i.e. with a rule like "strangeword: character number character(s)", since only the last production is returned as a default

    With $return you can also return anything you want, i.e. parsed and interpreted commands, hashes, objects ...