The Verb

Note: This page is for advanced coders who need to
create new verbs, or modify existing ones. Newbie
coders should not assume they need to know anything
here.

The verb is like anything else. Exactly as
good as you make it, no more and no less. Deconstructing
a new verb for the first time can be confusing, but
in doing so you will gain a better understanding of
the parsing system Dead Souls uses.

Please note, if there's anything here hard to
follow or understand, you need to review the Creator's
Manual for key LPC concepts.


The verb itself

Rules and tokens

can's and do's

The object: direct_ and indirect_

References


The verb itself

Let's take a look at the code for a verb, and dissect
it for meaning.
#include <lib.h>

inherit LIB_VERB;

static void create() {
verb::create();
SetVerb("buy");
SetSynonyms("purchase");
SetRules("STR from LIV");
SetErrorMessage("Buy what from whom?");
SetHelp("Syntax: <buy ITEM from VENDOR>\n\n"
"Blah blah this is the help message.");
}

mixed can_buy_str_from_liv(string str) {
if( this_player()->GetParalyzed() ) {
return "You cannot do anything.";
}
return this_player()->CanManipulate();
}

mixed do_buy_str_from_liv(string str, object vendor) {
return vendor->eventSell(this_player(), remove_article(lower_case(str)));
}


If you never looked at a verb before, it can
look rather alien and disgusting, like a dead facehugger.
But like the facehugger, it's actually quite beautiful
in its own way.
Let's dissect and comment it now:


#include <lib.h>
// This include statement allows us to use macros, or nicknames,
// for certain things. Specifically, the lib include gives us
// knowledge of what we mean by LIB_VERB.

inherit LIB_VERB;
// This line tells us that we will be inheriting all the
// functions and variables defined in /lib/verb.c .

static void create() {
// The create function is like main() in C/C++. A function that
// is *always* called when an object is loaded. Therefore,
// anything important goes inside here.

verb::create();
// The :: operator is called the "scope resolution operator".
// That's a fancy way of saying that we are trying to use
// a function that exists somewhere in the inheritance tree, and
// not in this individual file. The verb::create() line above
// is saying "At this point, execute any directives in the
// create() function of LIB_VERB". This is necessary because
// the create() function we define here will override the create()
// function we inherit, and that overridden function might have
// had important stuff in it we need to have happen.
// If you didn't understand any of that, you should probably
// stop reading this tutorial and get started on reading
// the Creator's manual.

SetVerb("buy");
// This identifies what word the parser should associate
// with this verb.

SetSynonyms("purchase");
// Self explanatory. This is where you specify words with a
// similar meaning that should also be associated with this verb.

SetRules("STR from LIV");
// Here's one of the new and exotic features of verbs. Rules and tokens.
// When the parser catches the "buy" word from the user's input,
// it will check to see if the rest of the words in the
// command line conform the the known set of rules for the verb.
// if they don't, the parser will error. We'll go over the rules
// a bit further on.

SetErrorMessage("Buy what from whom?");
// By default, the parser's error messages aren't terribly
// descriptive or helpful...usually it's along the lines
// of "You can't X the Y". The line above lets you provide the
// user a clue as to how to better avail themselves of the
// verb's functionality.

SetHelp("Blah blah this is the help message.");
// When the user types "help buy", this is what they get.

// The create function ends here, with the close brace
// below this line.
}


mixed can_buy_str_from_liv(string str) {
// This function does some very rudimentary checks of
// whether the user can use the verb. The can_* type
// functions will be discussed in more detail a bit
// further on.

if( this_player()->GetParalyzed() ) {
return "You cannot do anything.";
}
// Pretty self-explanatory. An obvious check.

return this_player()->CanManipulate();
// The player object has a CanManipulate() function. If
// the player has no prehensile limbs (i.e. "wielding limbs",
// like hands) then the CanManipulate() function in their
// object returns 0. Since the line above returns whatever
// CanManipulate() returns, that means that if the player
// is missing both hands, he can't sell anything. The verb
// will fail.

mixed do_buy_str_from_liv(string str, object vendor) {
// Like the can_* functions, do_ functions will get more
// treatment further on. This is generally where the actual
// "doing" of the verb gets fired off, if appropriate.

return vendor->eventSell(this_player(), remove_article(lower_case(str)));
// Did you notice that this function is declared as type "mixed"?
// This allows us to return weird stuff, like in this case where
// we're returning the result of a function call. We're not
// 100% sure what data type eventSell() in the vendor returns,
// so mixed covers all the bases. Whatever eventSell() returns,
// that's what gets returned here. If the parser got this far,
// then the verb's execution was successful.
// That doesn't necessarily mean that the player successfully
// sold anything. It may be he lacks enough quatloos for the
// shizbat. But that is no concern of the verb. That is the
// business of the eventSell() function it called on the vendor.
}


Rules and tokens

By a magic I do not fully understand myself, the
parser (which lives in the compiled driver binary, meaning
it was written in C and therefore inscrutable to me) can
actually work out from your command line what object you meant
to do what to with what.

For example, if you are in a room full of orcs,
and you type:

kill orcs

The parser actually susses out that orcs are objects
in your environment, and that you intend to act on more than
one of them. The parser will go on to check the attack verb
for its known rules. The rules for "attack" look like this:

SetRules("LVS", "only LVS","LVS only")

The parser sees that you meant two objects that
are living, and sure enough, the attack verb will accept
"multiple living things" as a rule. In this case, the
matched rule is the first one, with the token LVS.

Hold on there, you say. What if I typed kill orc ,
in the singular? This would still match the first rule,
because LVS doesn't just stand for "multiple living things".
It's rather more like "one or more living things".

If the first attack token were LIV instead, then
killing a singular orc would succeed in the parser, but
killing plural orcs would probably fail.

The second rule has two tokens. One is an object
token, LVS, where object is meant in a semi-grammatical sense.
The other is a prepositional token, which is also meant in
a semi-grammatical sense.

The way "object" and "preposition" are handled in
the parser's grammar is not the way they are handled in
English grammar. For example, you may be feeling indignant
at being told that "only" is a preposition. Obviously, in
English, it isn't. However, the parser isn't as linguistically
sophisticated as you. For the parser, anything used
periverbally that isn't an object is a preposition. Please don't
email me telling me I don't know my English grammar. When I
talk subject, object, and preposition here, I am using
the parser's simplified grammar.

As you might have guessed, an "object token" in this case
is the noun that the verb will be working with. Note that the
subject is assumed to be the caller, or player, that invoked the
verb's execution, and does not have an explicit token identified.

So, back to our second rule. "only LVS" means that
I can issue a command like this:

kill only the second orc

And the parser will not reject it. However, failing
to use the preposition token explicitly allowed in a rule
will cause the parser to error. Trying to:

kill merely the second orc

will not bring joy.

The known object tokens are:

LIV - one living thing
LVS - one or more living things
OBJ - one object
OBS - one or more objects
STR - a string of characters
WRD - a generic thing that may be a string, object, or living thing

To get a list of known preposition tokens, type:

eval return MASTER_D->parse_command_prepos_list()


can's and do's

Following the create() fun you saw two types of functions:
can_* and do_*. These are sometimes called "applies". When you see
someone referring to an apply, they mean a function called by the driver.

Notionally, the can_ applies are meant to test the
validity of the verb's execution, and the do_ applies are
meant to perform the actions required.

In practice, this is only somewhat the case. Whether
it's a bug or feature, the can_ applies tend to mangle the
arguments they receive. Pretty much no matter what the
argument is, it gets turned into a string. It's a peculiar
"gotcha" that will have you spinning your wheels for a long time
if you don't happen to know it.

That doesn't mean the can_ applies are useless. They are
in fact necessary. If the can_ apply doesn't exist, or doesn't
return a positive integer, the do_ apply won't happen, meaning your
verb won't execute. Since the can_ applies are somewhat hobbled,
use them for quick, obvious sanity checks, like "is this
player dead" or "does she have hands to manipulate anything with".

If you need to do a check, for example, to ensure that
the vendor is an elf, that means that you need to make a
call to that vendor object. Since the can_ applies won't
handle object arguments, this check will need to happen in the
do_ function.

Some examples of can_ and do_ verb applies:

can_throw_obj()

Pretty obvious. The command in question would be something
like "throw switch" perhaps.

can_throw_obj_word_obj()

A little trickier. Now we're allowing for a wildcard prepositional token. Here
the command might be "throw ball at benny", but because the token
is not explicitly listed (that would be
something like can_throw_obj_at_obj())
and the target is not explicitly a living thing, this apply
also matches "throw the sword in the lake". Note it won't match
"throw the jacks on the floor".


do_read_str_on_obj(string str, object ob)

If your verb has a can
_read_str_word_obj(string str, object ob) apply that
returned 1, then this sweet baby gets called. Presumably here you'd
have the verb call the object and tell it to do something with the
arguments provided. Here is where the actual reading action begins.


Alas, the work of parser applies is not done. The object in
question must have matching direct_ and indirect_ applies, otherwise,
error occurs. These applies are discussed in the next section.



The object: direct_ and indirect_

As you can see, the thing that gives a do_ apply
its whammy is a call to an object. That's the point of a verb,
is doing something, and in the case above, its:

return vendor->eventSell(this_player(), remove_article(lower_case(str)));

However, if you make a verb like this, and then
just try to use it, it'll fail. This is because the driver
needs the objects it acts on to have applies that
correspond to the do_ applies.

The corresponding applies are direct_ and indirect_ ,
and the appropriate one must exist in the object.

Verb applies are usually added to the lib
objects that are appropriate. For example, let's take
the "press" verb. When you "push the button", the parser
checks with the verb daemon, VERBS_D, to find out if
"push" means anything. Sure enough, it's a synonym for
"press", so the parser checks out the rules for press.

Eventually the parser decides all the t's are
crossed and i's are dotted, and it's time to actually
see whether the "button" you're talking about is
pressable. It does this by calling the following apply
in the button object:

direct_press_obj(<the button object is the argument here>)

If the button does not have this function,
or if the function returns 0, the parser errors, assuming
that you're trying to press something that isn't
supposed to be pressable. Therefore, if you're going to
make a button somewhere, or a wall or anything that
should do something when pushed, it will need to
have that function in it. The way it looks is something
like this:


mixed direct_press_obj(object target) {
return CanPress(this_player());
}

Now, this gets complicated, because the CanPress()
fun is a function that checks to see whether the
relationship between the button, the presser, and the lib
permits the pressing.

If you're going to have an object that is
pressed, it's going to be a right pain in the butt to
code all the necessary checks into that object. Fortunately,
you don't need to. If you make a pressable thing, you don't
need to code all that stuff yourself. Just have it
inherit LIB_PRESS, which is a file that already
contains the applies you need.

But, we're not done. You'll need to understand
a couple more things to be able to construct your new verb.

direct and indirect refer to the relationship
of two objects on a command line. If all you have is
"throw ball", there's no need to worry about object relationship.
But what if the command is "shoot nice guy eddy with pistol"?
Or, suppose the player types "shoot pistol at eddy"?

The parser needs to differentiate between the two,
but it isn't nearly sophisticated enough to understand the
concept of who is acting on whom. The parser just needs to
know which is first and which is second. therefore, in
LIB_SELL you'll see:


mixed direct_sell_obj_to_liv()

where the direct object is obj and the indirect object
is liv, and:

mixed indirect_sell_liv_obj()

where the direct object is liv and the indirect object is obj.
Again, this is not English grammar, it's parser grammar.



References

Though sparse, there are other sources of information
on verbs and the parser. Some of it is quite thorough but
incomprehensible. Some of it is crystal clear but not applicable
to the current version of MudOS and/or Dead Souls.

You're encouraged to visit the following sites
to gain more insight into the parser, but be warned that I
cannot vouch for the intelligibility or applicability of
their information.

Please let me know if you find others.


http://www.dnd.utwente.nl/~krimud/Docs/NMAdmin/Parser/


http://www.islandsofmyth.org/wiz/parser_guide.html



Within the mud, you should read and understand the
parser related man page, specifically:

man parse_command



Dead Souls Homepage