I’m not familiar with Parse::RecDescent,
Then thanks for being brave enough to take a look!
by reference to the docs plus a bit of trial-and-error I got this to work by adjusting the regex and assigning $1 to a local variable.
Nice going!
After reading the "Start up Action" section in the docs, I reread the "Action" section, and I noticed this statement:
The results of named subrules are stored in the hash under each subrule's name (including the repetition specifier, if any)
So the key I was using in the %item hash, 'dir', was wrong. The key should be 'dir(s)'. So now I can get this output:
use strict;
use warnings;
use 5.012;
use Parse::RecDescent;
$::RD_ERRORS = 1; #Parser dies when it encounters an error
$::RD_WARN = 1; #Enable warnings - warn on unused rules &c.
$::RD_HINT = 1; # Give out hints to help fix problems.
our %HASH;
my $grammar = <<'END_OF_GRAMMAR';
startrule : from_clause
from_clause : 'from' dir(s)
{
print "-->@{$item{'dir(s)'}}<-- \n";
$main::HASH{dirs} = @{$item{'dir(s)'}};
}
dir : 'hello'
END_OF_GRAMMAR
my $parser = Parse::RecDescent->new($grammar);
$parser->startrule("from hello world");
use Data::Dumper;
say Dumper(\%HASH);
--output:--
-->hello<--
$VAR1 = {
'dirs' => 1
};
Partial success! Note the dereference of $item{'dir(s)'}. Now, what is that '1'? The return value from print()? But print() isn't the last statement of the action. If I change the dir rule to:
dir : 'hello'
| 'world'
I get this output:
-->hello world<--
$VAR1 = {
'dirs' => 2
};
Is 2 the count of the words matched? What is going on? I am using the exact same array in each of these lines:
print "-->@{$item{'dir(s)'}}<-- \n";
$main::HASH{dirs} = @{$item{'dir(s)'}};
...yet I am getting different results 'hello world' v. 2! How is that possible? Argghh, of course! Perl doesn't care about giving you the exact same results for any expression you use--because perl determines the result by the context in which the expression appears. In my case, the print() statement supplies list context for the array, and "$main::... =" provides scalar context for the array--and an array provides its length in scalar context.
So now I can get the expected output:
use strict;
use warnings;
use 5.012;
use Parse::RecDescent;
$::RD_ERRORS = 1; #Parser dies when it encounters an error
$::RD_WARN = 1; #Enable warnings - warn on unused rules &c.
$::RD_HINT = 1; # Give out hints to help fix problems.
our %HASH;
my $grammar = <<'END_OF_GRAMMAR';
startrule : from_clause
from_clause : 'from' dir(s)
{
print "-->@{$item{'dir(s)'}}<-- \n";
$main::HASH{dirs} = $item{'dir(s)'};
}
dir : 'hello'
| 'world'
END_OF_GRAMMAR
my $parser = Parse::RecDescent->new($grammar);
$parser->startrule("from hello world hello");
use Data::Dumper;
say Dumper(\%HASH);
--output:--
-->hello world<--
$VAR1 = {
'dirs' => [
'hello',
'world'
]
};
Next up, the regex problem. This doesn't work:
my $grammar = <<'END_OF_GRAMMAR';
#Start up action(executed in parser namespace):
{
use 5.012; #So I can use say()
}
startrule : from_clause
from_clause : 'from' dir(s)
{
say "-->$item[0]<--";
say "-->@{$item[-1]}<--";
}
dir : m{/}
END_OF_GRAMMAR
my $parser = Parse::RecDescent->new($grammar);
$parser->startrule("from ./hello");
--output:--
(blank)
Note that I tried using the @item array this time. The first item in @item is the rule name, "from_clause", and the next items should be the matches for the subrules, so $item[2], or equivalently $item[-1], should be the matches for dir(s). But because I am not even seeing the arrows in my print statement, that means the parser isn't finding a match for my rule.
I also notice there are weird rules the parser follows for comments. This does not cause an error:
my $grammar = <<'END_OF_GRAMMAR';
#Start up action(executed in parser namespace):
{
use 5.012; #So I can use say()
}
...
...but this does cause an error: my $grammar = <<'END_OF_GRAMMAR';
#Start up action(executed in parser namespace):
{ use 5.012; #So I can use say() }
...
--output:--
Unknown starting rule (Parse::RecDescent::namespace000001::startrule)
+called
at 3.pl line 76.
Back to the regex problem. It seems that Parse::RecDescent takes the regex pattern and adds a ^ to the beginning of the pattern and adds $ to the end of the pattern. In other words, the regex you specify has to match all of the text you are interested in examining.
my $grammar = <<'END_OF_GRAMMAR';
#Start up action(executed in parser namespace):
{
use 5.012; #So I can use say()
}
startrule : from_clause
from_clause : 'from' dir(s)
{ say "-->$_<--" for @{ $item{'dir(s)'} }; }
dir : m{\S* / \S*}xms
END_OF_GRAMMAR
my $parser = Parse::RecDescent->new($grammar);
$parser->startrule("from ./hello hello/world");
--output:--
-->./hello<--
-->hello/world<--
And reworking my original example:
use strict;
use warnings;
use 5.012;
use Parse::RecDescent;
$::RD_ERRORS = 1; #Parser dies when it encounters an error
$::RD_WARN = 1; #Enable warnings - warn on unused rules &c.
$::RD_HINT = 1; # Give out hints to help fix problems.
our %HASH;
my $grammar = <<'END_OF_GRAMMAR';
#Start up action(executed in parser namespace):
{
use 5.012; #So I can use say()
}
startrule : from_clause
from_clause : 'from' dir(s)
{
say "-->$_<--" for @{ $item{'dir(s)'} };
$main::HASH{target_dirs} = $item{'dir(s)'};
}
dir : m{\S* / \S*}xms
END_OF_GRAMMAR
my $parser = Parse::RecDescent->new($grammar);
$parser->startrule("from ./hello hello/world");
use Data::Dumper;
say Dumper(\%HASH);
--output:--
-->./hello<--
-->hello/world<--
$VAR1 = {
'target_dirs' => [
'./hello',
'hello/world'
]
};
Success! Thanks.
|