DM4 §30: How verbs are parsed

## §30 How verbs are parsed

> “…I can see that the grammar gets tucked into the tales and poetry as one gives pills in jelly.”
> — Louisa May Alcott (1832–1888), Little Women

Here is how the parser reads in a whole command. Given a stream of text like

`saint` / `peter` / `,` / `take` / `the` / `keys` / `from` / `paul`

it first breaks it into words, as shown, and then calls the entry point routine `BeforeParsing` (which you can provide, if you want to, in order to meddle with the text stream before parsing gets underway). The parser then works out who is being addressed, if anyone, by looking for a comma, and trying out the text up to there as a noun matching an `animate` or `talkable` object: in this case St Peter. This person is called the “actor”, since he or she is going to perform the action, and is most often the player (thus, typing “myself, go north” is equivalent to typing “go north”). The next word, in this case `'take'`, is the “verb word”. An Inform verb usually has several English verb words attached, which are called synonyms of each other: for instance, the library is set up with

“take” = “carry” = “hold”

all referring to the same Inform verb.

▲ The parser sets up variables `actor` and `verb_word` while working. (In the example above, their values would be the St Peter object and `'take'`, respectively.)

▲ This brief discussion is simplified in two ways. Firstly, it leaves out directions, because Inform considers that the name of a direction-object implies “go”: thus “north” means “go north”. Secondly, it misses out the grammar property described in §18, which can cause different actors to recognise different grammars.

•▲ EXERCISE 81
Use `BeforeParsing` to implement a lamp which, when rubbed, produces a genie who casts a spell to make the player confuse the words “white” and “black”.

· · · · ·

This section is about verbs, which are defined with “grammar”, meaning usages of the directives `Verb` and `Extend`. The library contains a substantial amount of grammar as it is, and this forms (most of) the library file `"Grammar.h"`. Grammar defined in your own code can either build on this or selectively knock it down, but either way it should be made after the inclusion of `"Grammar.h"`.

For instance, making a new synonym for an existing verb is easy:

    Verb 'steal' 'acquire' 'grab' = 'take';

Now “steal”, “acquire” and “grab” are synonyms for “take”.

▲ One can also prise synonyms apart, as will appear later.

· · · · ·

To return to the text above, the parser has now recognised the English word “take” as one of those which can refer to a particular Inform verb. It has reached word 5 and still has “the keys from paul” left to understand.

Every Inform verb has a “grammar” which consists of a list of one or more “grammar lines”, each of them a pattern which the rest of the text might match. The parser tries the first, then the second and so on, and accepts the earliest one that matches, without ever considering later ones.

A line is a row of “tokens”. Typical tokens might mean ‘the name of a nearby object’, ‘the word `'from'`’ or ‘somebody's name’. To match a line, the parser must match against each token in sequence. Continuing the example, the parser accepts the line of three tokens

‹one or more nouns› ‹the word `from`› ‹a noun›

as matching “the keys from paul”.

Every grammar line has the name of an action attached, and in this case it is `Remove`: so the parser has ground up the original text into just four quantities, ending up with

`actor = StPeter` `action = Remove` `noun = gold_keys` `second = StPaul`

The parser's job is now complete, and the rest of the Inform library can get on with processing the action or, as in this case, an order being addressed to somebody other than the player.

▲ The action for the line currently being worked through is stored in the variable `action_to_be`; or, at earlier stages when the verb hasn't been deciphered yet, it holds the value `NULL`.

· · · · ·

The `Verb` directive creates Inform verbs, giving them some English verb words and a grammar. The library's `"Grammar.h"` file consists almost exclusively of `Verb` directives: here is an example simplified from one of them.

    Verb 'take' 'get' 'carry' 'hold'
        * 'out'                   -> Exit
        * multi                   -> Take
        * multiinside 'from' noun -> Remove
        * 'in' noun               -> Enter
        * multiinside 'off' noun  -> Remove
        * 'off' held              -> Disrobe
        * 'inventory'             -> Inv;

(You can look at the grammar being used in a game with the debugging verb “showverb”: see §7.) Each line of grammar begins with a `*`, gives a list of tokens as far as `->` and then the action which the line produces. The first line can only be matched by something like “get out”, the second might be matched by

“take the banana”
“get all the fruit except the apple”

and so on. A full list of tokens will be given later: briefly, `'out'` means the literal word “out”, `multi` means one or more objects nearby, `noun` means just one and `multiinside` means one or more objects inside the second noun. In this book, grammar tokens are written in the style `noun` to prevent confusion (as there is also a variable called `noun`).

▲ Some verbs are marked as `meta` – these are the verbs leading to Group 1 actions, those which are not really part of the game's world: for example, “save”, “score” and “quit”. For example:

    Verb meta 'score' * -> Score;

and any debugging verbs you create would probably work better this way, since meta-verbs are protected from interference by the game and take up no game time.

· · · · ·

After the `->` in each line is the name of an action. Giving a name in this way is what creates an action, and if you give the name of one which doesn't already exist then you must also write a routine to execute the action, even if it's one which doesn't do very much. The name of the routine is always the name of the action with `Sub` appended. For instance:

    [ XyzzySub; "Nothing happens."; ];
    Verb 'xyzzy' * -> Xyzzy;

will make a new magic-word verb “xyzzy”, which always says “Nothing happens” – always, that is, unless some before rule gets there first, as it might do in certain magic places. `Xyzzy` is now an action just as good as all the standard ones: `##Xyzzy` gives its action number, and you can write `before` rules for it in `Xyzzy:` fields just as you would for, say, `Take`.

▲ Finally, the line can end with the word `reverse`. This is only useful if there are objects or numbers in the line which occur in the wrong order. An example from the library's grammar:

    Verb 'show' 'present' 'display'
        * creature held      -> Show reverse
        * held 'to' creature -> Show;

The point is that the `Show` action expects the first parameter to be an item, and the second to be a person. When the text “show him the shield” is typed in, the parser must reverse the two parameters “him” and “the shield” before causing a `Show` action. On the other hand, in “show the shield to him” the parameters are in the right order already.

· · · · ·

The library defines grammars for the 100 or so English verbs most often used by adventure games. However, in practice you quite often need to alter these, usually to add extra lines of grammar but sometimes to remove existing ones. For instance, suppose you would like “drop charges” to be a command in a detection game (or a naval warfare game). This means adding a new grammar line to the “drop” verb. The `Extend` directive is provided for exactly this purpose:

    Extend 'drop' * 'charges' -> DropCharges;

Normally, extra lines of grammar are added at the bottom of those already there, so that this will be the very last grammar line tested by the parser. This may not be what you want. For instance, “take” has a grammar line reading

    * multi -> Take

quite early on. So if you want to add a grammar line diverting “take ‹food›” to a different action, like so:

    * edible -> Eat

(`edible` being a token matching anything which has the attribute `edible`) then it's no good adding this at the bottom of the `Take` grammar, because the earlier line will always be matched first. What you need is for the new line to go in at the top, not the bottom:

    Extend 'take' first
        * edible -> Eat;

You might even want to throw away the old grammar completely, not just add a line or two. For this, use

    Extend 'press' replace
        * 'charges' -> PressCharges;

and now the verb “press” has no other sense but this, and can't be used in the sense of pressing down on objects any more, because those grammar lines are gone. To sum up, `Extend` can optionally take one of three keywords:

`replace` | replace the old grammar with this one;
`first` | insert the new grammar at the top;
`last` | insert the new grammar at the bottom;

with `last` being the default.

▲ In library grammar, some verbs have many synonyms: for instance,

    'attack' 'break' 'smash' 'hit' 'fight' 'wreck' 'crack'
    'destroy' 'murder' 'kill' 'torture' 'punch' 'thump'

are all treated as identical. But you might want to distinguish between murder and lesser crimes. For this, try

    Extend only 'murder' 'kill' replace
        * animate -> Murder;

The keyword only tells Inform to extract the two verbs “murder" and “kill". These then become a new verb which is initially an identical copy of the old one, but then `replace` tells Inform to throw that away in favour of an entirely new grammar. Similarly,

    Extend only 'run' * 'program' -> Compute;

makes “run" behave exactly like “go" and “walk”, as these three words are ordinarily synonymous to the library, except that it also recognises “program", so that “run program" activates a computer but “walk program" doesn't. Other good pairs to separate might be “cross” and “enter”, “drop” and “throw”, “give” and “feed”, “swim” and “dive”, “kiss” and “hug”, “cut” and “prune”. Bear in mind that once a pair has been split apart like this, any subsequent change to one will not also change the other.

▲▲ Occasionally verb definition commands are not enough. For example, in the original ‘Advent’, the player could type the name of an adjacent place which had previously been visited, and be taken there. (This feature isn't included in the Inform example version of the game in order to keep the source code as simple as possible.) There are several laborious ways to code this, but here's a concise way. The library calls the `UnknownVerb` entry point routine (if you provide one) when the parser can't even get past the first word. This has two options: it can return `false`, in which case the parser just goes on to complain as it would have done anyway. Otherwise, it can return a verb word which is substituted for what the player actually typed. Here is one way the ‘Advent’ room-naming might work. Suppose that every room has been given a property called `go_verb` listing the words which refer to it, so for instance the well house might be defined along these lines:

    AboveGround Inside_Building "Inside Building"
      with description
               "You are inside a building, a well house for a
               large spring.",
           go_verb 'well' 'house' 'inside' 'building',
    ...

The `UnknownVerb` routine then looks through the possible compass directions for already-visited rooms, checking against words stored in this new property:

    Global go_verb_direction;
    [ UnknownVerb word room direction adjacent;
      room = real_location;
      objectloop (direction in compass) {
          adjacent = room.(direction.door_dir);
          if (adjacent ofclass Object && adjacent has visited
              && adjacent provides go_verb
              && WordInProperty(word, adjacent, go_verb)) {
                  go_verb_direction = direction;
                  return 'go.verb';
          }
      }
      if (room provides go_verb
          && WordInProperty(word, room, go_verb)) {
          go_verb_direction = "You're already there!";
          return 'go.verb';
      }
      objectloop (room provides go_verb && room has visited
                  && WordInProperty(word, room, go_verb)) {
          go_verb_direction = "You can't get there from here!";
          return 'go.verb';
      }
      objectloop (room provides go_verb && room hasnt visited
                  && WordInProperty(word, room, go_verb)) {
          go_verb_direction = "But you don't know the way there!";
          return 'go.verb';
      }
      rfalse;
    ];

When successful, this routine stores either a compass direction (an object belonging to the compass) in the variable `go_verb_direction`, or else a string to print. (Note that an `UnknownVerb` routine shouldn't print anything itself, as this might be inappropriate in view of subsequent parsing, or if the actor isn't the player.) The routine then tells the parser to treat the verb as if it were `'go.verb'`, and as this doesn't exist yet, we must define it:

    [ Go_VerbSub;
      if (go_verb_direction ofclass String)
          print_ret (string) go_verb_direction;
      <<Go go_verb_direction>>;
    ];
    Verb 'go.verb' * -> Go_Verb;

•▲▲ EXERCISE 82
A minor deficiency with the above system is that the parser may print out strange responses like “I only understood you as far as wanting to go.verb.” if the player types something odd like “bedquilt the nugget”. How can we ensure that the parser will always say something like “I only understood you as far as wanting to go to Bedquilt.”?

• REFERENCES
‘Advent’ makes a string of simple `Verb` definitions; ‘Alice Through the Looking-Glass’ uses `Extend` a little. •‘Balances’ has a large extra grammar and also uses the `UnknownVerb` and `PrintVerb` entry points. •Irene Callaci's `"AskTellOrder.h"` library extension file makes an elegant use of `BeforeParsing` to convert commands in the form “ask mr darcy to dance” or “tell jack to go north” to Inform's preferred form “mr darcy, dance” and “jack, go north”.