<?xml version="1.0" encoding="windows-1252"?>
<node id="674283" title="Reversible parsing (with Parse::RecDescent?)" created="2008-03-14 15:55:44" updated="2008-03-14 11:55:44">
<type id="115">
perlquestion</type>
<author id="632330">
goibhniu</author>
<data>
<field name="doctext">
&lt;p&gt;
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):
&lt;/p&gt;
&lt;p&gt;
&lt;c&gt;


/--------------\   /------------\   /-----------\   /------------\   /--------\
|              |   |            |   |           |   |            |   |        |
| command line |--&gt;|  Parse::   |--&gt;|   Data    |--&gt;|   Data::   |--&gt;|  XML   |
|    string    |&lt;--| RecDescent |&lt;--| structure |&lt;--| Serializer |&lt;--|  FILE  | 
|              |   |            |   |           |   |            |   |        |
/--------------\   /------------\   /-----------\   /------------\   /--------\
                         ^
                         |
                   /------------\
                   |            |
                   |  command   |
                   |  line      |
                   |  grammar   |
                   |            |
                   /------------\

&lt;/c&gt;
&lt;/p&gt;
&lt;p&gt;
In the proof-of-concept stage, I've had some successes and some setbacks. PerlMonks++ to Niel Neely, the maintainer of [cpan://Data::Serializer], for responding VERY quickly to a bug.  [cpan://Data::Serializer] 0.44 includes this patch and lets me use [cpan://XML::Dumper] in raw mode with store and retrieve. Niel was very responsive and the patch is working great.
&lt;/p&gt;
&lt;p&gt;
On the [cpan://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 [cpan://Parse::RecDescent::Deparse] didn't deparse my data structure into a command line string, but instead deparsed my &lt;em&gt;parser&lt;/em&gt; into a &lt;em&gt;grammar&lt;/em&gt; (should've rtfm before I got this far, I guess)!
&lt;/p&gt;
&lt;p&gt;
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. 
&lt;/p&gt;
&lt;p&gt;
So, now I have some questions:&lt;br /&gt;
Academic questions: &lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;
Is it necessarily possible to reverse the data through the grammar to get the command line? 
&lt;/li&gt;
&lt;li&gt;
or is this only possible for special grammars?  
&lt;/li&gt;
&lt;li&gt;
Is there something I need to in my grammar to make this possible?
&lt;/li&gt;
&lt;/ul&gt;
Design decisions: 
&lt;ul&gt;
&lt;li&gt;
Is there a module that already reverses the parsing of [cpan://Parse::RecDescent] parsers?  
&lt;/li&gt;
&lt;li&gt;
Is there another module that makes this bi-directionality easier (I'd prefer not to learn &lt;em&gt;another&lt;/em&gt; grammar specification syntax, but could)?  
&lt;/li&gt;
&lt;li&gt;
Is there a module that already does all this soup-to-nuts?
&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;p&gt;
And finally, the infamous "what I've already done". This is grammarharness.pl:&lt;br /&gt;
&lt;readmore&gt;
&lt;c&gt;
#/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,'&lt;',$grammarfile or die 'bad grammarfile';

while (&lt;$gf&gt;) {
    $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,'&lt;',$stringtoparsefile or die 'bad stringstoparse file';

my $result;
my $reresult;
#Data::Serializer 0.44 supports raw=&gt;
my $serializer = Data::Serializer-&gt;new(raw=&gt;'1',serializer =&gt; 'XML::Dumper');
my $outfilebasename = 'data';
my $outfileext = 'out';
my $outfileindex = 10;
while (&lt;$sf&gt;) {
    print 'parsing $_: ',$_;
    if (defined $parser-&gt;startrule($_)){
        $result = $parser-&gt;startrule($_);
        print Dumper($result);
        $serializer-&gt;store(
            $result,
            $outfilebasename.$outfileindex.'.'.$outfileext,
            '&gt;&gt;'
        );
        $reresult = $serializer-&gt;retrieve(
            $outfilebasename.$outfileindex++.'.'.$outfileext,
        );
        print Dumper($reresult);
    }else{ print "  badstring\n"}
}
&lt;/c&gt;
&lt;/readmore&gt;
&lt;br /&gt;
This is packagegrammar.txt:&lt;br /&gt;
&lt;readmore&gt;
&lt;c&gt;
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 }
&lt;/c&gt;
&lt;/readmore&gt;
&lt;br /&gt;
This is packagecommands.txt:&lt;br /&gt;
&lt;readmore&gt;
&lt;c&gt;
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
&lt;/c&gt;
&lt;/readmore&gt;
&lt;br /&gt;
how it works (nothing particularly wrong here; it mostly proves all the working parts without attempting the deparse):&lt;br /&gt;
&lt;readmore&gt;
&lt;c&gt;
C:\chas_sandbox\grammars&gt;grammarharness.pl packagegrammar.txt packagecommands.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&gt;type data10.xml
&lt;perldata&gt;
 &lt;arrayref memory_address="0xdee0b8"&gt;
  &lt;item key="0"&gt;hap&lt;/item&gt;
  &lt;item key="1"&gt;
   &lt;arrayref memory_address="0xd67300"&gt;
    &lt;item key="0"&gt;
     &lt;arrayref memory_address="0xdee28c"&gt;
      &lt;item key="0"&gt;-b&lt;/item&gt;
      &lt;item key="1"&gt;1&lt;/item&gt;
     &lt;/arrayref&gt;
    &lt;/item&gt;
   &lt;/arrayref&gt;
  &lt;/item&gt;
 &lt;/arrayref&gt;
&lt;/perldata&gt;


C:\chas_sandbox\grammars&gt;
&lt;/c&gt;
&lt;/readmore&gt;
&lt;/p&gt;&lt;div class="pmsig"&gt;&lt;div class="pmsig-632330"&gt;
&lt;hr /&gt;
&lt;font size='1'&gt;
#my sig used to say 'I humbly seek wisdom. '.  Now it says:&lt;br /&gt;
use strict;&lt;br /&gt;
use warnings;&lt;br /&gt;
I humbly seek wisdom. 
&lt;/font&gt;
&lt;/div&gt;&lt;/div&gt;</field>
</data>
</node>
