Please note that this first section is largely not something you need
to use: the addVerb
method allows you to avoid most of the
technical details. The section on Parse Records, on the other hand,
is quite important.
Verb records are used to associate particular types of user input with methods on objects. This means that when you type "list languages", a verb record somewhere (on your Player object, in fact) is used to compare against that to find out what method to call (in this case, the 'languagesVerb' method).
Verb records are stored in the verbs
attribute. The verbs
record has features for each language that the object has verbs on, like
so:
verb: allVerbs( en: <verb records> lb: <verb records> )
The label of the record, in this case allVerbs, is irrelevant, as are the labels of all records in this section, unless specificially mentioned otherwise.
The verb records themselves contain one feature for each verb word (that is, the first word of input) that the object wants to accept, like so:
en: verbs( help: <verb record> languages: <verb record> )
Each actual verb record contains the language the verb was called in1, and the parsing structure:
help: help( language: en parses: [ helpVerbParseName( method: helpVerb endOfInput: nil ) ] )
The parses feature and its list are both complicated and unusual, and are discussed in the next section.
The parses list is something that is very unusual for a MUD: it allows each verb to define how its arguments are parsed, and in fact requires that each verb do so.
Normally, a MUD understand some basic linguistic structures of one language, and attempts to shoe-horn whatever the player says into what it understands. For example, it might understand the English concepts of subject, preposition, and object, and will attempt to understand all input in those terms.
MOZ, on the other hand, allows each verb in each language to define how it wishes its input to be broken up. It attempts to do this in a way that requires as little programming knowledge as possible, but it's still not exactly simple.
The parses
feature is, in fact, a list of records. This
is used so that one verb word can access different methods,
depending on how the rest of the line is parsed.
Important: the label of the individual parse records, such as helpVerbParseName above, must be unique within the verb in question on whatever object the parse record is being added, as addVerb uses that label to decide what to override when you update the verb.
The record structure for the records inside the parses
list
is as follows: the method
feature contains the name of the
method, whatever feature is left after that feature is removed
(there should be only one) is used as the first parsing directive.
In this example:
help: verb( language: en parses: [ playerHelpVerb( method: helpVerb endOfInput: nil ) ] )
the first, and only, parsing directive is endOfInput, which sees if the end of the user input has been reached2. This means that nothing, other than whitespace, can follow the verb word "help" for this parsing structure to match.
On the other hand, we have:
list: verb( language: en parses: [ playerListLanguagesVerb( method: languagesVerb matchWord: matchWord( word: "languages" rest: rest( endOfInput: nil ) ) ) playerListHelpVerb( method: helpVerb matchWord: matchWord( word: "help" rest: rest( endOfInput: nil ) ) ) ] )
which matches either the word languages, then end of input, or the word help followed by end of input. In the two cases, different verbs are called.
In some cases, there will be parse records inside a parse directive; in
this case, matchWord
has a feature, rest
, which is used to
match everything after whatever word matchWord is being used to match.
These parse sub-records work just like the general parse records
described here, except they cannot be lists, the must be single
records, and they should not include a method
feature.
Directive Name | Arguments | Effect
|
endOfInput | nil | Matches end of character input.
|
string | any 1 atom | Bind the longest string (i.e. series of space-seperated words) it
can find to the atom passed to it, which is passed to the verb's method.
A word is a list of anything that Char.isGraph returns true for.
Because of this, string pretty much always matches the entire
rest of the line. So, for example, "string: inputString" will pass the
argument inputString to the verb's method containing the rest of
the line.
|
stringUntil | until string rest
| Fills string with words until it reaches a word that
matches the word (or list of words) stored in until .
|
matchWord | word rest
| The word argument should contain a string with the word that
needs to be matched in the input. rest contains a full parse
tree.
|
getWord | word rest
| The word is filled with the next word in the input.
rest contains a full parse tree.
|
plus | first second
| This is the choice operator. Evaluates first as a full
parse tree. If the parsing of first succeeds, returns that.
Otherwise, tries to parse with second , returning that if
succesful. Otherwise fails.
|
multiMatchWord | words wordFound rest
| Attempts to match anything from the list of strings in
words . Whichever word is actually matched is given to the method
in the atom named by wordFound . rest is the parse tree
for everything after that word.
|
article | wordFound rest
| Same as multiMatchWord with words set to the contents of the
articles attribute on the Parser object.
|
mayHaveArticle | wordFound rest
| Same as article, but accepts strings that don't start
with an article as well.
|
bracket | left right rest
| Matches anything entirely inside the brackets defined by
left and right . Works if both left and right are words or if both are
single characters, but not for a mix of the two. Note that it does not deal with nesting
in any real way, and will only succeed if the first word or character in
that part of the parse matches left and the last matches
right , regardless of what's in the middle.
|