My concept is to wrap some configuration management command lines in a Perl framework to expose the command options along with other configuration data in a standard configuration file. For the design, I've "drawn" these boxes in my head (and now in ASCII art):
/--------------\ /------------\ /-----------\ /------------\ /
+--------\
| | | | | | | | |
+ |
| command line |-->| Parse:: |-->| Data |-->| Data:: |-->|
+ XML |
| string |<--| RecDescent |<--| structure |<--| Serializer |<--|
+ FILE |
| | | | | | | | |
+ |
/--------------\ /------------\ /-----------\ /------------\ /
+--------\
^
|
/------------\
| |
| command |
| line |
| grammar |
| |
/------------\
In the proof-of-concept stage, I've had some successes and some setbacks. PerlMonks++ to Niel Neely, the maintainer of Data::Serializer, for responding VERY quickly to a bug. Data::Serializer 0.44 includes this patch and lets me use XML::Dumper in raw mode with store and retrieve. Niel was very responsive and the patch is working great.
On the Parse::RecDescent side, I read the tutorial and wrote a simple grammar, passed alot of command line strings through it successfully to get the data structure to serialize, serialized it and deserialized it. I got stuck when I realized that Parse::RecDescent::Deparse didn't deparse my data structure into a command line string, but instead deparsed my parser into a grammar (should've rtfm before I got this far, I guess)!
Now I could write a function to take my datastructure and turn it into a command line string, but it seems a shame that I've gone to the trouble of writing a grammar to define how to go one direction, but can't use the same grammar to go the other direction. Especially if I intend to change and expand the grammar, I would like that to be the end of it and not have to also change the deparsing function.
So, now I have some questions:
Academic questions:
-
Is it necessarily possible to reverse the data through the grammar to get the command line?
-
or is this only possible for special grammars?
-
Is there something I need to in my grammar to make this possible?
Design decisions:
-
Is there a module that already reverses the parsing of Parse::RecDescent parsers?
-
Is there another module that makes this bi-directionality easier (I'd prefer not to learn another grammar specification syntax, but could)?
-
Is there a module that already does all this soup-to-nuts?
And finally, the infamous "what I've already done". This is grammarharness.pl:
#/usr/bin/perl -Wl
use strict;
use warnings;
use Parse::RecDescent;
use Data::Dumper;
use Data::Serializer;
my $grammar;
my $grammarfile = $ARGV[0];
open my $gf,'<',$grammarfile or die 'bad grammarfile';
while (<$gf>) {
$grammar.=$_;
}
close $gf;
print '$grammar is ',"\n",$grammar;
$::RD_HINT++;
#$::RD_TRACE++;
my $parser = new Parse::RecDescent ($grammar) or die 'bad grammar';
my $stringtoparsefile = $ARGV[1];
open my $sf,'<',$stringtoparsefile or die 'bad stringstoparse file';
my $result;
my $reresult;
#Data::Serializer 0.44 supports raw=>
my $serializer = Data::Serializer->new(raw=>'1',serializer => 'XML::Du
+mper');
my $outfilebasename = 'data';
my $outfileext = 'out';
my $outfileindex = 10;
while (<$sf>) {
print 'parsing $_: ',$_;
if (defined $parser->startrule($_)){
$result = $parser->startrule($_);
print Dumper($result);
$serializer->store(
$result,
$outfilebasename.$outfileindex.'.'.$outfileext,
'>>'
);
$reresult = $serializer->retrieve(
$outfilebasename.$outfileindex++.'.'.$outfileext,
);
print Dumper($reresult);
}else{ print " badstring\n"}
}
This is packagegrammar.txt:
startrule: commandline{$item{commandline}}
commandline: command options {[$item {command},$item{options}]}
command: packagecommand {$item{packagecommand}}
packagecommand: /(hap)/ | /(hccmrg)/ | /(hup)/ | /(hspp)/ | /(hpp)/ |
+/(hpg)/ | /(hdp)/ | /(hdlp)/ | /(hcp)/
options: option(s?)
option: optionflag optionvalue { [$item{optionflag}, $item{optionvalue
+}?$item{optionvalue}:1] }
optionflag: /(-\w+)/ { $1 }
optionvalue: /(\w*)/ { $1 }
This is packagecommands.txt:
hap -b
hap -b sknxharvest01
hap -b sknxharvest01 -enc testfile.dfo
hap -b sknxharvest01 -usr cgowing -pass chaspass
hap -badflag
hap -prompt
hap -b sknxharvest01 -prompt
hap -prompt -b sknxharvest01
how it works (nothing particularly wrong here; it mostly proves all the working parts without attempting the deparse):
C:\chas_sandbox\grammars>grammarharness.pl packagegrammar.txt packagec
+ommands.tx
t
$grammar is
startrule: commandline{$item{commandline}}
commandline: command options {[$item {command},$item{options}]}
command: packagecommand {$item{packagecommand}}
packagecommand: /(hap)/ | /(hccmrg)/ | /(hup)/ | /(hspp)/ | /(hpp)/ |
+/(hpg)/ |
/(hdp)/ | /(hdlp)/ | /(hcp)/
options: option(s?)
option: optionflag optionvalue { [$item{optionflag}, $item{optionvalue
+}?$item{op
tionvalue}:1] }
optionflag: /(-\w+)/ { $1 }
optionvalue: /(\w*)/ { $1 }
parsing $_: hap -b
$VAR1 = [
'hap',
[
[
'-b',
1
]
]
];
$VAR1 = [
'hap',
[
[
'-b',
'1'
]
]
];
parsing $_: hap -b sknxharvest01
$VAR1 = [
'hap',
[
[
'-b',
'sknxharvest01'
]
]
];
$VAR1 = [
'hap',
[
[
'-b',
'sknxharvest01'
]
]
];
parsing $_: hap -b sknxharvest01 -enc testfile.dfo
$VAR1 = [
'hap',
[
[
'-b',
'sknxharvest01'
],
[
'-enc',
'testfile'
]
]
];
$VAR1 = [
'hap',
[
[
'-b',
'sknxharvest01'
],
[
'-enc',
'testfile'
]
]
];
parsing $_: hap -b sknxharvest01 -usr cgowing -pass chaspass
$VAR1 = [
'hap',
[
[
'-b',
'sknxharvest01'
],
[
'-usr',
'cgowing'
],
[
'-pass',
'chaspass'
]
]
];
$VAR1 = [
'hap',
[
[
'-b',
'sknxharvest01'
],
[
'-usr',
'cgowing'
],
[
'-pass',
'chaspass'
]
]
];
parsing $_: hap -badflag
$VAR1 = [
'hap',
[
[
'-badflag',
1
]
]
];
$VAR1 = [
'hap',
[
[
'-badflag',
'1'
]
]
];
parsing $_: hap -prompt
$VAR1 = [
'hap',
[
[
'-prompt',
1
]
]
];
$VAR1 = [
'hap',
[
[
'-prompt',
'1'
]
]
];
parsing $_: hap -b sknxharvest01 -prompt
$VAR1 = [
'hap',
[
[
'-b',
'sknxharvest01'
],
[
'-prompt',
1
]
]
];
$VAR1 = [
'hap',
[
[
'-b',
'sknxharvest01'
],
[
'-prompt',
'1'
]
]
];
parsing $_: hap -prompt -b sknxharvest01
$VAR1 = [
'hap',
[
[
'-prompt',
1
],
[
'-b',
'sknxharvest01'
]
]
];
$VAR1 = [
'hap',
[
[
'-prompt',
'1'
],
[
'-b',
'sknxharvest01'
]
]
];
C:\chas_sandbox\grammars>type data10.xml
<perldata>
<arrayref memory_address="0xdee0b8">
<item key="0">hap</item>
<item key="1">
<arrayref memory_address="0xd67300">
<item key="0">
<arrayref memory_address="0xdee28c">
<item key="0">-b</item>
<item key="1">1</item>
</arrayref>
</item>
</arrayref>
</item>
</arrayref>
</perldata>
C:\chas_sandbox\grammars>
#my sig used to say 'I humbly seek wisdom. '. Now it says:
use strict;
use warnings;
I humbly seek wisdom.
-
Are you posting in the right place? Check out Where do I post X? to know for sure.
-
Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
<code> <a> <b> <big>
<blockquote> <br /> <dd>
<dl> <dt> <em> <font>
<h1> <h2> <h3> <h4>
<h5> <h6> <hr /> <i>
<li> <nbsp> <ol> <p>
<small> <strike> <strong>
<sub> <sup> <table>
<td> <th> <tr> <tt>
<u> <ul>
-
Snippets of code should be wrapped in
<code> tags not
<pre> tags. In fact, <pre>
tags should generally be avoided. If they must
be used, extreme care should be
taken to ensure that their contents do not
have long lines (<70 chars), in order to prevent
horizontal scrolling (and possible janitor
intervention).
-
Want more info? How to link
or How to display code and escape characters
are good places to start.