In this series of articles about parsing, were now at the point where the input has been broken down into a nested list of atoms which is passed to the final phase of the parsing process, the binder. What is it, and why is it called the binder?
Well what it is is a collection of routines that make sense of commands in the context of the game world. As for why its called the binder, well, OK, a confession: thats just what I call it. Ive no idea what other people call things that do similar tasks for non-MUD applications, but I call it the binder as it binds atoms that represent classes to atoms that represent instances. Hey, if I can get the word mobiles into common currency among MUD players, binder is worth a shot!
Verbs
Ill start with the easy stuff: verbs.
Consider the sentences GET THE SAND WITH THE BUCKET and GET THE SAND FROM THE BUCKET. In the first, theres some sand lying around on the ground and you want to move it to the bucket; in the second, theres some sand in the bucket and you want to move it to your hands. If youre standing in a room where theres sand on the floor and youre holding a bucket half-full of sand, the parser had better be able to figure out which one you mean. Both commands use the same verb, though: GET.
You could avoid the problem by forcing players to use different words. SCOOP THE SAND WITH THE BUCKET and REMOVE THE SAND FROM THE BUCKET would be fine. Your players wouldnt necessarily think so (especially newbies), but your parser would be happy.
Alternatively, you could use the fact that the pronouns are different to generate the appropriate verbs yourself. This is where the binder comes in. The binder takes verb modifiers and applies each one in turn to the verb to get a new verb.
To demonstrate, first, Ill define the atoms:
adverb: |
*+ [ ?? ] +* |
//ADVERB is a subclass of root. |
with: |
*+ [ adverb ] +* |
//WITH is a subclass of ADVERB. |
from: |
*+ [ adverb ] +* |
//FROM is a subclass of ADVERB. |
Now Ill define the functions:
{ adverb ?? }: |
first |
//ADVERB applied to anything returns it |
|
|
//unchanged |
{ with get }: |
scoop |
//GET x WITH y => SCOOP x WITH y |
{ from get }: |
remove |
//GET x FROM y => REMOVE x FROM y |
The binder will take the verb and the list of modifiers and apply each in turn. For [get, [with]] it would call with(get) and find scoop. For [get, [from]] it would call from(get) and find remove. For other verbs and prepositions it would return the verb unaltered, as eg. both with(tickle) and to(get) would match only the pattern for { adverb ?? }.
A more complicated verb modification list such as [inside, very, quickly, secretively] would be handled the same way. Applying it to DROP, this particular list might first call inside(drop) to generate INSERT, then very(insert) would do nothing, then quickly(insert) which might convert it to INSERT_FAST. If you really wanted to be flash you could note that VERY is an adverb that modifies adverbs, and use it to change QUICKLY into VERY_FAST before applying very_fast(insert), but I wouldnt bother; although the players may occasionally want to make this kind of fine distinction, the game itself is unlikely to have the necessary physics in place to handle it any differently.
Note that although Ive said here that handling verbs is easy, its impossible if you cant quantify over them (ie. cant treat them as objects in their own right). This is precisely the case with most of the object-oriented MUDs out there. Object over-oriented might be a better way of describing them...
Nouns
Nouns are harder to bind than verbs, but not that much harder. Ill start by explaining the theory.
If you were to issue the command PICK UP THE INGOTS in a room containing 3 ingots, how should the game handle it? First it would convert up(pick) into GET, which Ive just described how to do. Then it would have to apply GET to the ingots. The naïve way to approach this is to pass the noun phrase as a parameter to GET, but whereas this might work if the most complicated thing a noun phrase can be is a single noun, its not going to work here. You might have said PICK UP 2 INGOTS, for example.
No, the way to do it is to find all possible referents for the noun phrase in the context of the verb, and apply the verb to each one in turn. In this manner, PICK UP THE INGOTS will convert into three separate function calls one for each ingot. These would be, say: get(ingot1), get(ingot4), get(ingot7). Thus, by the time the code for GET is given control, theres only ever one object to be got.
I said in the context of the verb there because different verbs will want to refer to different objects. If you were carrying an axe in a room containing an axe, GET THE AXE would pick up the axe from the floor whereas DROP THE AXE would drop the axe you were holding. When binding AXE to an object for GET, you look in the room before you look on the character; for DROP, you look on the character before you look in the room. Fortunately, most verbs follow similar search patterns when binding objects, so you can group them together as a class and write the necessary routines only once. GET and DROP are likely to be among the most complicated, and will probably need their own individual binding strategies, but the rest will tend to be one of the following:
- Local command bind noun to objects held, then objects in room. Most commands are of this kind, eg. OPEN, KISS, KILL, SMELL.
- Global command bind noun to objects wherever they are. Spells often tend to use these, eg. SUMMON, DEAFEN, GLOW.
- No bind the concept the noun describes is wanted, not the objects to which it refers. Useful for admin commands such as ones to print out parent/child relationships from the atom hierarchy, but also a good idea to bypass binding for commands you know dont need it (eg. ones that take strings as parameters).
Note that commands can bind differently for each parameter. THROW DART AT PHOTO would want to bind DART to some dart you were holding and PHOTO to some photograph you werent!
Rebinding
The beauty of having the binder written in your game definition language is that the commands of the game can call the binder directly. If youre really keen, you can implement an operator to call the parser on an arbitrary string, too. Thus, if you have some truly horrible command that you dont know right up until execution time how to parse it, your game will still be OK.
The archetypal example of this is HELP, which must perforce be an enquoting string because people will type things like HELP ME, I DONT KNOW WHAT TO DO that wouldnt parse otherwise. However, people will also type things like HELP JACK a command to assist someone else (it might take 2 people to open a door, for example). In binding terms, HELP would be a no bind command that always took a string as a parameter. When help(<string>) was called, it would have to check to see if <string> were parsable first; if so, it would do the necessary help-a-person stuff, and if not then it would invoke the general context-sensitive help system.
If binding individual verbs and nouns were all there was to it, I wouldnt have to explain the binder further. Youd use adverbs and prepositions to modify the verb, then use the context of the verb to determines a search strategy for the noun. Then youd go through all objects in the search space, recording those that were instances of the class that the noun represents (or, strictly speaking, that the atom representing the noun represents). This would give you a set of bindings for the noun, and youd call the verb on each one in turn as a separate command invocation.
This isnt all there is to it, though. The binder has to bind noun phrases, not just nouns. Next time, Ill describe the principles involved in this.