I want to parse input but when I'm in a particular top-level rule, save the text that was parsed instead of executing the actions, to do so instead later. I've got a solution but I'd like a sanity check.
I can explain it in terms of some adventure game commands. (That's not my application, but it's exactly equivalent and more entertaining. I cut my gaming teeth on VAX/VMS DUNGEON and it's colored my thinking ever since.) So I want to be able to "Kill troll with axe", and "Open window", but also, "Tell robot open door" where there is a subset of commands I can execute that the robot can also execute. So for a "tell <actor> <command>" statement I want to save <command> and pass that text to the action for 'tell' where it decides what to do with it (maybe setting the global actor and then calling the parser again). I found Parse::RecDecent::Consumer and came up with the below, which does what I want, but I thought I'd ask here for comments on improving it. I want to minimize the impact upon the grammar for executing regular commands caused by having to be able to handle them as delegated commands also. #!/usr/local/bin/perl use strict; use warnings; use Parse::RecDescent; use Parse::RecDescent::Consumer 'Consumer'; # $::RD_TRACE = 1; my $parser = Parse::RecDescent->new(join '', <DATA>); for ( 'open window', 'kill troll with sword', 'tell robot kill thief with knife', 'tell troll drop sack', 'tell thief open door' ) { print "$_ : "; defined $parser->start($_) or print "Error\n"; } sub Parse::RecDescent::do_open { print "OPEN(@_)\n"; } sub Parse::RecDescent::do_drop { print "DROP(@_)\n"; } sub Parse::RecDescent::do_attack { my @args = map { ref $_ ? @$_ : $_ } @_; print "KILL(", join (', ', @args), ")\n"; } sub Parse::RecDescent::do_tell { print "TELL($_[0] => '$_[1]')\n" } __END__ { $::SAVING = 0 } start : tell | immediate tell : 'tell' actor slave_command { do_tell($item{actor}, $item{slave_command}); $::SAVING = 1 } immediate: ( slave_command | master_command ) { $::SAVING = 0 } slave_command : <rulevar: $C> slave_command : { $C = Consumer($text) ; 1} (drop | attack) { $C->($text); } master_command : open # Only I can open stuff actor : 'robot' | 'thief' | 'troll' drop : 'drop' object <defer: do_drop($item[2]) unless $::SAVING > object : 'sack' | 'bottle' | weapon attack : 'kill' actor ('with' weapon)(?) <defer: do_attack(@item[2,3]) unless $::SAVING > weapon : 'knife' | 'sword' open : 'open' orifice <defer: do_open($item[2]) unless $::SAVING > orifice : 'door' | 'window' $ ./telltest open window : OPEN(window) kill troll with sword : KILL(troll, sword) tell robot kill thief with knife : TELL(robot => ' kill thief with knife') tell troll drop sack : TELL(troll => ' drop sack') tell thief open door : Error -- Peter Scott http://www.perlmedic.com/