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

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"


{ $::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

Search Discussions

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
grouprecdescent @
postedFeb 6, '08 at 4:41p
activeFeb 6, '08 at 4:41p

1 user in discussion

Peter Scott: 1 post



site design / logo © 2019 Grokbase