Author: | Paul McGuire <ptmcg@austin.rr.com> |
---|---|
Version: | 1.1 |
Design-driven:
language -> BNF -> parser impl --+-> concept ^ | | refine/extend | +--- language --+
Data-driven:
gather determine sample -> text -> BNF -> parser ---+-> inputs patterns | ^ gather new | +----- (non-conforming) ---+ inputs
At startup, user program defines the pyparsing grammar, by building up expressions that comprise the supported application grammar
Basic building blocks are Literals and Words
Basics are combined using And, Or, MatchFirst operations
oneOf("red green blue") is a short-cut for:
Literal("red") | Literal("green") | Literal("blue")
Compose grammar:
greeting = oneOf("Hello Ahoy Yo Hi") + Literal(",") + Word( string.uppercase, string.lowercase ) + Literal("!")
Call parseString():
print greeting.parseString("Hello, World!") print greeting.parseString("Ahoy, Matey !") print greeting.parseString("Yo,Adrian!") ['Hello', ',', 'World', '!'] ['Ahoy', ',', 'Matey', '!'] ['Yo', ',', 'Adrian', '!']
Pyparsing grammar elements can be given results names
At runtime, the matched tokens are assigned these results names
The individual tokens can be retrieved by name from the parsed results:
greeting = oneOf("Hello Ahoy Yo Hi").setResultsName("greeting") + Literal(",") + Word( string.uppercase, string.lowercase ).setResultsName("subject") + Literal("!") results = greeting.parseString("Yo,Adrian!") print results.greeting # prints 'Yo' print results.subject # prints 'Adrian'
We'll use this feature to access command qualifiers
Pyparsing grammar elements can keep references to callbacks or "parse actions", executed when a particular grammar element is parsed from the input string
Parse actions are called with 3 arguments:
A parse action can modify the matched tokens by returning a value:
Define grammar expression for each command
Create command processor class, inherit from base Command class
Attach command processor to grammar expression, using parse action
Compose command parser from Or of all grammar expressions
At runtime:
while not game over: prompt for command string parse command string, Command subclass is returned perform Command
Commands:
- INVENTORY or INV or I - lists what items you have - DROP or LEAVE <objectname> - drop aan object - TAKE or PICKUP or PICK UP <objectname&> - pick up an object - USE or U <objectname> [ IN or ON &llt;objectname> ] - use an object, optionally IN or ON another object - OPEN or O <objectname> - open an obbject - MOVE or GO - go NORTH, SOUTH, EAST, or WEEST (can abbreviate as 'GO N' and 'GO W', or even just 'E' and 'S') - LOOK or L - describes the current room annd any objects in it - DOORS - display what doors are visible frrom this room - QUIT or Q - ends the game - HELP or H or ? - displays this help messaage
Define verbs for each command:
invVerb = oneOf("INV INVENTORY I", caseless=True) dropVerb = oneOf("DROP LEAVE", caseless=True) takeVerb = oneOf("TAKE PICKUP", caseless=True) | \ (CaselessLiteral("PICK") + CaselessLiteral("UP") ) moveVerb = oneOf("MOVE GO", caseless=True) | empty useVerb = oneOf("USE U", caseless=True) openVerb = oneOf("OPEN O", caseless=True) quitVerb = oneOf("QUIT Q", caseless=True) lookVerb = oneOf("LOOK L", caseless=True) doorsVerb = CaselessLiteral("DOORS") helpVerb = oneOf("H HELP ?",caseless=True)
itemRef = OneOrMore(Word(alphas)).setParseAction( self.validateItemName ) nDir = oneOf("N NORTH",caseless=True).setParseAction(replaceWith("N")) sDir = oneOf("S SOUTH",caseless=True).setParseAction(replaceWith("S")) eDir = oneOf("E EAST",caseless=True).setParseAction(replaceWith("E")) wDir = oneOf("W WEST",caseless=True).setParseAction(replaceWith("W")) moveDirection = nDir | sDir | eDir | wDir invCommand = invVerb dropCommand = dropVerb + itemRef.setResultsName("item") takeCommand = takeVerb + itemRef.setResultsName("item") useCommand = useVerb + itemRef.setResultsName("usedObj") + \ Optional(oneOf("IN ON",caseless=True)) + \ Optional(itemRef,default=None).setResultsName("targetObj") openCommand = openVerb + itemRef.setResultsName("item") moveCommand = moveVerb + moveDirection.setResultsName("direction") quitCommand = quitVerb lookCommand = lookVerb doorsCommand = doorsVerb helpCommand = helpVerb
Base Command class:
class Command(object): "Base class for commands" def __init__(self, verb, verbProg): self.verb = verb self.verbProg = verbProg @staticmethod def helpDescription(): return "" def _doCommand(self, player): pass def __call__(self, player ): print self.verbProg.capitalize()+"..." self._doCommand(player)
class InventoryCommand(Command): def __init__(self, quals): super(InventoryCommand,self).__init__("INV", "taking inventory") @staticmethod def helpDescription(): return "INVENTORY or INV or I - lists what items you have" def _doCommand(self, player): print "You have %s." % enumerateItems( player.inv ) def makeCommandParseAction( self, cls ): def cmdParseAction(s,l,t): return cls(t) return cmdParseAction invCommand.setParseAction( self.makeCommandParseAction( InventoryCommand ) ) dropCommand.setParseAction( self.makeCommandParseAction( DropCommand ) ) takeCommand.setParseAction( self.makeCommandParseAction( TakeCommand ) ) ... etc. ...
class TakeCommand(Command): def __init__(self, quals): super(TakeCommand,self).__init__("TAKE", "taking") self.subject = quals["item"] @staticmethod def helpDescription(): return "TAKE or PICKUP or PICK UP - pick up an object (but some are deadly)" def _doCommand(self, player): rm = player.room subj = Item.items[self.subject] if subj in rm.inv and subj.isVisible: if subj.isTakeable: rm.removeItem(subj) player.take(subj) else: print "You can't take that!" else: print "There is no %s here." % subj
Complete game grammar - Or of all defined commands:
return ( invCommand | useCommand | openCommand | dropCommand | takeCommand | moveCommand | lookCommand | doorsCommand | helpCommand | quitCommand ).setResultsName("command") + LineEnd()
roomMap = """ # define global variables for referencing rooms d-Z frontPorch = rooms["A"] | garden = rooms["b"] f-c-e kitchen = rooms["c"] . | backPorch = rooms["d"] q<b library = rooms["e"] | patio = rooms["f"] A """ rooms = createRooms( roomMap ) rooms["A"].desc = "You are standing at the front door." rooms["b"].desc = "You are in a garden." rooms["c"].desc = "You are in a kitchen." rooms["d"].desc = "You are on the back porch." rooms["e"].desc = "You are in a library." rooms["f"].desc = "You are on the patio." rooms["q"].desc = "You are sinking in quicksand. You're dead..." rooms["q"].gameOver = True
# create items itemNames = """sword.diamond.apple.flower.coin.n shovel.book.mirror.telescope.gold bar""".split(".") for itemName in itemNames: # Item ctor also updates class dict of all items by name Item( itemName ) Item.items["apple"].isDeadly = True Item.items["mirror"].isFragile = True Item.items["coin"].isVisible = False Item.items["shovel"].usableConditionTest = ( lambda p,t: p.room is garden ) def useShovel(p,subj,target): coin = Item.items["coin"] if not coin.isVisible and coin in p.room.inv: coin.isVisible = True Item.items["shovel"].useAction = useShovel OpenableItem("treasure chest", Item.items["gold bar"])
putItemInRoom("shovel", frontPorch) putItemInRoom("coin", garden) putItemInRoom("flower", garden) putItemInRoom("apple", library) putItemInRoom("mirror", library) putItemInRoom("telescope", library) putItemInRoom("book", kitchen) putItemInRoom("diamond", backPorch) putItemInRoom("treasure chest", patio)
def playGame(p,startRoom): # create parser parser = Parser() p.moveTo( startRoom ) while not p.gameOver: cmdstr = raw_input(">> ") cmd = parser.parseCmd(cmdstr) if cmd is not None: cmd.command( p ) print print "You ended the game with:" for i in p.inv: print " -", aOrAn(i), i # create player plyr = Player("Bob") plyr.take( Item.items["sword"] ) # start game playGame( plyr, frontPorch )
You are standing at the front door. There is a shovel here. >> take shovel Taking... >> n Moving... You are in a garden. There is a flower here. >> use fhovel No such item 'fhovel'. >> use shovel Using... >> l Looking... You are in a garden. There are a coin and a flower here. >> take flower Taking... >> take coin Taking...
>> i Taking inventory... You have a sword, a shovel, a flower, and a coin. >> doors Looking for doors... There are doors to the north, south, and west. >> w Moving... You are sinking in quicksand. You're dead... There is nothing here. Game over! You ended the game with: - a sword - a shovel - a flower - a coin
Save game / load game
Fold globals into Game class instance
Mapping
Documentation