in reply to Re: How do I return a hash from Parse::RecDescent?
in thread How do I return a hash from Parse::RecDescent?

That breaks the ability to easily PreCompile (which is rather major) because the user must provide AddHash and the user must be main::. Breaking encapsulation in this fashion also prevents more than one instance of the parser from being used at a time. Fix:

use strict; use warnings; use Parse::RecDescent (); use Data::Dumper qw( Dumper ); my $grammar = <<'__END_OF_GRAMMAR__'; { use strict; use warnings; } test : <rulevar: local %data > | expr(s) /^\Z/ { \%data } expr : name '=' /\d+/ { $data{ $item{name} } = $item[-1]; 1 } name : /\w+/ __END_OF_GRAMMAR__ my $parser = new Parse::RecDescent($grammar) or die; my $data = $parser->test("x = 1 y = 2 z = 3") or die; print Dumper $data;

( Note: I prefer the solution I posted last night because it doesn't break if backtracking occurs like this one and this one's parent do. )

Replies are listed 'Best First'.
Re^3: How do I return a hash from Parse::RecDescent?
by Outaspace (Scribe) on Sep 24, 2006 at 10:46 UTC
    Ok, but actually I would use this and maybe put the MyParser package to a different file (but not everybody like OO):
    package MyParser; use constant DEBUG => 1; use strict; use base qw(Parse::RecDescent); use Data::Dumper; sub new { print "MyParser->new(@_)\n" if (DEBUG); my ($class, $szGrammar, $szCode) = @_; my $this; eval { $this = $class->SUPER::new($szGrammar); }; if ($@) { print $@; $this = {}; bless $this, $class; } else { # Start parsing code $this->test($szCode); return $this; } } sub AddToHash { print "MyParser->AddToHash(@_)\n" if (DEBUG); my ($this, $szVar, $szValue) = @_; $this->{Result}->{$szVar} = $szValue; } sub GetResult { print "MyParser->GetResult(@_)\n" if (DEBUG); my ($this) = @_; return $this->{Result}; } sub DumpResult { print "MyParser->DumpResult(@_)\n" if (DEBUG); my ($this) = @_; print Dumper($this->{Result}); } package main; my $szGrammar = q{ test: expr(s) /^\Z/ expr: name '=' /\d+/ { $thisparser->AddToHash($item{name}, $item[-1]); } name: /\w+/ }; my $szCode = "x = 1\ny = 2\nz = 3"; my $hParser = MyParser->new($szGrammar, $szCode); $hParser->DumpResult();
    Andre

      You still can't PreCompile that. You're forcing the calling script to parse the parser's grammar, generate the parser code and compile the parser code everytime it runs. That's extremely slow, so that's something you only want to do when the grammar is changed.

      To achieve that goal, start by making make_grammar.pl as follows:

      use strict; use warnings; use Parse::RecDescent (); my $szGrammar = <<'__END_OF_GRAMMAR__'; { use strict; use warnings; use constant DEBUG => 1; use Data::Dumper; sub AddToHash { print "MyParser->AddToHash(@_)\n" if (DEBUG); my ($this, $szVar, $szValue) = @_; $this->{Result}->{$szVar} = $szValue; } sub GetResult { print "MyParser->GetResult(@_)\n" if (DEBUG); my ($this) = @_; return $this->{Result}; } sub DumpResult { print "MyParser->DumpResult(@_)\n" if (DEBUG); my ($this) = @_; print Dumper($this->{Result}); } } test: expr(s) /^\Z/ expr: name '=' /\d+/ { $thisparser->AddToHash($item{name}, $item[-1]); } name: /\w+/ __END_OF_GRAMMAR__ Parse::RecDescent->PreCompile($szGrammar, 'MyParser') or die("Bad Grammar\n");

      You run the above once, then you use it as follows:

      use strict; use warnings; use MyParser (); my $hParser = MyParser->new(); my $szCode = "x = 1\ny = 2\nz = 3"; $hParser->test($szCode) or die("Bad code\n"); $hParser->DumpResult();

      I still stand by my earlier note: I prefer the solution I posted earlier because it doesn't break if backtracking occurs like this one does.

      I'd also like to add that <<'__END_OF_GRAMMAR__' ... __END_OF_GRAMMAR__ has fewer issues than q{ ... }.

        You are right, didnt used the PreCompile Option yet and in the grammars I use, the compile time is a minor issue (the time to parse is much more relevant). Also my Parser runs in a large script and I need the option to change the grammar from inside my script, without stopping it. But this maybe would be too slow if I executed my script many times in a row. So I accept that your solution is better for the given task.

        Andre