The Naming of Cats is a difficult matter,
It isn't just one of your holiday games;
You may think at first I'm as mad as a hatter
When I tell you, a cat must have THREE DIFFERENT NAMES.
— T. S. Eliot (1888–1965), The Naming of Cats
Suppose we have a tomato defined with
name 'fried' 'green' 'tomato',
but which is going to redden later and need to be
referred to as “red tomato”. The
holds an array of dictionary words, so that
(tomato.#name)/2 == 3 tomato.&name-->0 == 'fried' tomato.&name-->1 == 'green' tomato.&name-->2 == 'tomato'
X.#Y tells you the number of
-> entries in such a property array, in this case six,
X.#Y/2 tells you the number of
entries, in this case three.) You are quite free to alter this array
tomato.&name-->1 = 'red';
The down side of this technique is that it's clumsy,
when all's said and done, and not so very flexible, because you can't
change the length of the
tomato.&name array during
play. Of course you could define the tomato
with name 'fried' 'green' 'tomato' 'blank.' 'blank.' 'blank.' 'blank.' 'blank.' 'blank.' 'blank.' 'blank.' 'blank.' 'blank.' 'blank.' 'blank.' 'blank.' 'blank.' 'blank.',
or something similar, giving yourself another (say)
fifteen “slots” to put new names into, but this is
inelegant even by Inform standards. Instead, an object like the tomato
can be given a
parse_name routine, allowing complete flexibility
for the designer to specify just what names it does and doesn't match.
It is time to begin looking into the parser and how it works.
· · · · ·
The Inform parser has two cardinal principles: firstly, it is designed to be as “open-access” as possible, because a parser cannot ever be general enough for every game without being highly modifiable. This means that there are many levels on which you can augment or override what it does. Secondly, it tries to be generous in what it accepts from the player, understanding the broadest possible range of commands and making no effort to be strict in rejecting ungrammatical requests. For instance, given a shallow pool nearby, “examine shallow” has an adjective without a noun: but it's clear what the player means. In general, all sensible commands should be accepted but it is not important whether or not nonsensical ones are rejected.
The first thing the parser does is to read in text from the keyboard and break it up into a stream of words: so the text “wizened man, eat the grey bread” becomes
wizened / man / , / eat / the / grey / bread
and these words are numbered from 1. At all times
the parser keeps a “word number” marker to keep its place
along this line, and this is held in the variable
NextWord() returns the word at the current
position of the marker, and moves it forward, i.e., adds 1 to
For instance, the parser may find itself at word 6 and trying to
match “grey bread” as the name of an object. Calling
NextWord() returns the value
calling it again gives
Note that if the player had mistyped “grye bread”,
“grye” being a word which isn't mentioned anywhere in the
program or created by the library, then
0 for ‘not in the dictionary’. Inform creates the dictionary
of a story file by taking all the
name words of objects, all
the verbs and prepositions from grammar lines, and all the words used
in constants like
'frog' written in the source code,
and then sorting these into alphabetical order.
However, the story file's dictionary only has 9-character resolution.
(And only 6 if Inform has been told to compile an early-model story
file: see §45.) Thus the values of
are equal. Also, upper case and lower case letters are considered the
same. Although dictionary words are permitted to contain numerals
or typewriter symbols like
/, these cost as much as two ordinary letters, so
'catch-22' looks the same as
A dictionary word can even contain spaces, full stops or commas, but
if so it is ‘untypeable’. For instance,
is an untypeable word because if the player were to type something
like “go in,out”, the text would be broken up into four
'in,out' may be in the story file's dictionary but
it will never match against any word of what the player typed. Surprisingly,
this can be useful, as it was at the end of §18.
· · · · ·
Since the story file's dictionary isn't always perfect, there is sometimes no alternative but to actually look at the player's text one character at a time: for instance, to check that a 12-digit phone number has been typed correctly and in full.
a byte array of the characters in the word, and
tells you how many characters there are in it. Given the above example
text of “wizened man, eat the grey bread”:
WordLength(4) == 3 WordAddress(4)->0 == 'e' WordAddress(4)->1 == 'a' WordAddress(4)->2 == 't'
because word number 4 is “eat”. (Recall that the comma is considered as a word in its own right.)
The parser provides a basic routine for comparing a word against the
'10000' or, in other words, against
small numbers. This is the library routine
which tries to parse the word at
wordnum as a number and
returns that number, if it finds a match. Besides numbers written
out in digits, it also recognises the texts
If it fails to recognise the text as a number, it returns −1,000;
if it finds a number greater than 10,000, it rounds down and returns
· · · · ·
To return to the naming of objects, the parser normally
recognises any arrangement of some or all of the
name words of an
object as a noun which refers to it: and the more words, the better the
match is considered to be. Thus “fried green tomato” is
a better match than “fried tomato” or “green tomato”
but all three are considered to match. On the other hand, so is
“fried green”, and “green green tomato green fried
green” is considered a very good match indeed. The method is quick
and good at understanding a wide variety of sensible texts, though
poor at throwing out foolish ones. (An example of the parser's strategy
of being generous rather than strict.) To be more precise, here is what
happens when the parser wants to match some text against an object:
parse_nameroutine, ask this routine to determine how good a match there is.
parse_nameroutine, or if there was but it returned −1, ask the entry point routine
ParseNoun, if the game has one, to make the decision.
ParseNounentry point, or if there was but it returned −1, look at the
nameof the object and match the longest possible sequence of words given in the
parse_name routine, if provided,
is expected to try to match as many words as possible starting from
the current position of
wn and reading them in one at
a time using the
NextWord() routine. Thus it must not stop
just because the first word makes sense, but must keep reading and find
out how many words in a row make sense. It should return:
|0||if the text didn't make any sense at all,|
|k||if k words in a row of the text seem to refer to the object, or|
|−1||to tell the parser it doesn't want to decide after all.|
The word marker
wn can be left anywhere
afterwards. For example, here is the fried tomato with which this section
parse_name [ n colour; if (self.ripe) colour = 'red'; else colour = 'green'; while (NextWord() == 'tomato' or 'fried' or colour) n++; return n; ],
The effect of this is that if
true then the tomato responds to the names “tomato”, “fried”
and “red”, and otherwise to “tomato”, “fried”
As a second example of how
be useful, suppose you define:
Object -> "fly in amber" with name 'fly' 'in' 'amber';
If the player then types “put fly in amber in hole”, the parser will be thrown, because it will think “fly in amber in” is all just naming the object and then it won't know what the word “hole” is doing at the end. However:
Object -> "fly in amber" with parse_name [; if (NextWord() ~= 'fly' or 'amber') return 0; if (NextWord() == 'in' && NextWord() == 'amber') return 3; return 1; ];
Now the word “in” is only recognised as part of the fly's name if it is followed by the word “amber”, and the ambiguity goes away. (“amber in amber” is also recognised, but then it's not worth the bother of excluding.)
parse_name is also used to spot plurals: see
Rewrite the tomato's
parse_name to insist that the adjectives
must come before the noun, which must be present.
Create a musician called Princess who, when kissed, is transformed into “/?%?/ (the artiste formerly known as Princess)”.
Construct a drinks machine capable of serving cola, coffee or tea, using only one object for the buttons and one for the possible drinks.
parse_name routine which looks through
in just the way that the parser would have done anyway if there hadn't
parse_name in the first place.
Some adventure game parsers split object names into ‘adjectives’ and ‘nouns’, so that only the pattern ‹0 or more adjectives› ‹1 or more nouns› is recognised. Implement this.
During debugging it sometimes helps to be able to refer to objects by their internal numbers, so that “put object 31 on object 5” would work. Implement this.
How could the word “#” be made a wild-card, meaning “match any single object”?
And how could “*” be a wild-card for “match any collection of objects”? (Note: you need to have read §29 to answer this.)
parse_name examples are the chess pieces
object and the kittens class of ‘Alice Through the Looking-Glass’.
Lengthier ones are found in ‘Balances’, especially
in the white cubes class.
•Miron Schmidt's library
extension "calyx_adjectives.h", based on earlier
work by Andrew Clover, provides for objects to have “adnames”
as well as “names”: “adnames” are usually
adjectives, and are regarded as being less good
matches for an object
than “names”. In this system “get string”
would take either a string bag or a ball of string, but if both were
present would take the ball of string, because “string”
is in that case a noun rather than an adjective.