RE: Parsing "start ... end" - with symbolic refs?
[email protected] Mon, 17 May 2004 13:52:12 +0200
| Newsgroups | perl.recdescent |
|---|---|
| Message-ID | <[email protected]> |
Oops, I needed to move the "no strict 'refs'"
from the top of my script into the action.
But actually I've found a nicer solution, without symbolic refs:
use strict;
use vars qw($parser $text %top %wins %marm %bitmap %resource);
use Data::Dumper;
use Parse::RecDescent;
$RD_WARN = 1;
$RD_HINT = 1;
$RD_TRACE = 120;
$parser = Parse::RecDescent->new(q(
mmpfile: chunk(s) /^\Z/
chunk: assignment[$arg[0]] | local | <error>
assignment: keyword <skip: '[ \t]+'> value(s) {
# a reference to a hash of lists
my $href = $arg[0] || \%::top;
push @{$href->{uc $item{keyword}}}, @{$item[3]};
}
local: /START\b/i <skip: '[ \t]+'> ( platform | resource | bitmap )
<skip: $item[2]> chunk[$item[3]](s?)
/END\b/i
value: /VAL\d+\b/i
keyword: /KEY\d+\b/i
platform: /WINS\b/i { \%::wins } | /MARM\b/i { \%::marm }
bitmap: /BITMAP\b/i value {
# this action would fail if you return undef,
# so create an empty hash if it doesn't exist yet
$::bitmap{uc $item[-1]} ||= {};
$::bitmap{uc $item[-1]};
}
resource: /RESOURCE\b/i value {
# this action would fail if you return undef,
# so create an empty hash if it doesn't exist yet
$::resource{uc $item[-1]} ||= {};
$::resource{uc $item[-1]};
}
)) or die 'Bad grammar';
$text .= $_ while (<DATA>);
defined $parser->mmpfile($text) or die 'Bad text';
print Data::Dumper->Dump([\%top,
\%wins,
\%marm,
\%bitmap,
\%resource],
[qw(top wins marm bitmap resource)]);
__DATA__
key1 val1
START BITMAP val2
key2 val3 val4
END
START WINS
key2 val5 val6
END
START RESOURCE val7
key3 val8
key4 val9
END
> -----Original Message-----
> From: ext Alexander Farber [mailto:[email protected]]
>
> I have a text consisting of keyword/value assignments,
> placed on 1 line (please see the __DATA__ on the bottom).
> Some of the assignments are surrounded by START/END:
>
> START WINS
> key1 val1 val2
> END
>
> START MARM
> key1 val1
> END
>
> I would like to capture all of the assignments into hashes
> of lists. The top-level assignments should go into %hol,
> and the START/END-surrounded assignments should go into
> %wins and %marm. I'm trying to achieve this by passing the
> platform name (the word following START) as an argument to
> the assignment rule and by using that name (lowercased) as
> a symbolic reference.
>
> Unfortunately my script listed below dies with the error:
>
> Can't use string ("::hol") as a HASH ref while "strict refs"
> in use at (eval 18) line 303, <DATA> line 6.
>
> Why does it mention "strict refs"? I'm explicitly setting
> "no strict 'refs'" on the top of my script.
>
> Also, I wonder if there is a nicer way to solve my problem.
>
> The START/END areas do not nest, they are all 1-level deep.
>
> Thank you
> Alex
>
> use strict;
> no strict 'refs';
> use vars qw($parser $text %hol %wins %marm);
> use Data::Dumper;
> use Parse::RecDescent;
> $RD_WARN = 1;
> $RD_HINT = 1;
> $RD_TRACE = 120;
> $parser = Parse::RecDescent->new(q(
>
> mmpfile: chunk(s) /^\Z/
> chunk: assignment[$arg[0]] | local | <error>
> assignment: keyword <skip: '[ \t]+'> value(s) {
> my $hname = lc $arg[0] || 'hol';
> push @{${"::$hname"}{uc $item{keyword}}}, @{$item[3]};
> }
>
> local: /start/i <skip: '[ \t]+'> platform
> <skip: $item[2]> chunk[$item[3]](s?)
> /end/i { print "ITEM3: $item[3]\n" }
>
> value: /val\d+/i
> keyword: /key\d+/i
> platform: /wins/i || /marm/i
>
> )) or die 'Bad grammar';
> $text .= $_ while (<DATA>);
> defined $parser->mmpfile($text) or die 'bad text';
> print Data::Dumper->Dump([\%hol,
> \%wins,
> \%marm],
> [qw(hol wins marm)]);
> __DATA__
>
> key1 val1
> START WINS
> key2 val2 val3
> END
>
>