Phantasmal MUD Lib for DGD

Phantasmal Site > DGD > Writing a Library > Object Binding

Binding Object Names to Objects While Parsing

This activity is also called "Object Binding", and you can find a longer explanation of what it is and what it does in Richard Bartle's article on the topic.

Date: Sun, 4 Apr 1999 03:04:53 +0200 (CEST)
From: "Felix A. Croes"
To: DGD Mailing List
Subject: Re: [DGD]parse_string

"Erlend M. Simonsen" wrote:

> I've been spending some time this easter trying to get my MudOS-like
> parser using parse_string() up and running. And I'm ALMOST happy with
> it. I've run into two problems, which I thought I'd see if any of you
> had an answer to.
>
> 1. 
> When trying to get an object from a container I have the problem that
> when my find_object() function is called, I have no idea which
> environment to look for the object in. And I can't just scan all
> containers in the environment looking for the object, since I might
> end up with the wrong object if several instances of it is present.

This same problem must have existed in the MudOS parser.  I assume
that it was solved in one of the following ways:

1) process all possible environments in a standard order.
2) if the grammar rule for an object should check an environment
   that depends on the greater context in which the rule is used,
   split it up into several rules where the grammar part is
   identical, but the LPC function called to find the object
   differs depending on the context.
3) postpone finding the object at the object rule level.  Instead,
   find it at the sentence level, where the environment is known.

The most useful and versatile solution is probably 2).  It can be
done by adding several intermediate object finding rules:

    object_in_carried_container: object ?	find_in_container
    object_in_room: object ?			find_in_room
    object_in_living: object ?			find_in_living


> I've been able to work around this due to a 'fortunate' parsing order
> in parse_string(). It seems like parse_string() goes right to left,
> which means it finds the correct container first, which I can remember 
> until I go looking for the object. It sounds a bit 'hackish' to me,
> and I would like to do this the proper way.

More precisely: parsing happens left to right, but LPC functions are
called in bottom-up right-to-left order.  It is not clear to me why
this always picks the proper container.  Are you sure that this is
actually the case?


> 2.  I want to call can_get_obj_from_obj() and the like. The problem I
> am having here is that I know nothing about the rule that is matched,
> so I build the function name by checking the type of each element in
> the result from parse_string(). Which basically breaks all STR
> (string) parts of the rules. 
>
> What I tried doing when building the function to call, is to treat all
> strings as 'str', but that gives me function names like
> can_buy_str_str_liv(), and that doesn't look too nice. I would rather
> have it look like is can_buy_str_from_liv(). Anyone have solved this
> problem and would like to share with me how they did it?

I don't fully understand the problem as you described it, but my
guess is that the solution I gave above will work here too: add
intermediate rules that differ only in the LPC function called.

Regards,
Dworkin

From: "Jason Cone"
To: DGD Mailing List
Subject: RE: [DGD]parse_string
Date: Mon, 5 Apr 1999 11:05:58 -0500

> -----Original Message-----
> From: Erlend Simonsen
> Sent: Saturday, April 03, 1999 5:36 PM
> To: DGD Mailing List
> Subject: [DGD]parse_string
>
>
> I've been spending some time this easter trying to get my MudOS-like
> parser using parse_string() up and running. And I'm ALMOST happy with
> it. I've run into two problems, which I thought I'd see if any of you
> had an answer to.
>
> 1.
> When trying to get an object from a container I have the problem that
> when my find_object() function is called, I have no idea which
> environment to look for the object in. And I can't just scan all
> containers in the environment looking for the object, since I might
> end up with the wrong object if several instances of it is present.


With MudOS, every object is required to register itself with the driver as
an object that can be targeted by the parser.  Thus, you can effectively
eliminate certain objects that shouldn't be directly interacted with in the
game.  In the event that multiple (registered) objects of the same class
exist and are being targeted via a certain parsed sentence, it's up to the
parser (not the actual grammar, but the functionality surrounding the
grammar) to determine which object is flagged as the "found" object.  DGD &
parse_string() benefit heavily in this area as you can define this behavior
based on what you want to accomplish.  For example, in a 3D space, you could
return the object that is "closest" to the person issuing the command or if
the object exists in the person's inventory, you could return the object
that was last used, etc. etc.  This isn't possible with the MudOS parser;
order of precedence is defined for you.  All that to say, your find_object()
function is going to have to take care of that for you.


> 2.  I want to call can_get_obj_from_obj() and the like. The problem I
> am having here is that I know nothing about the rule that is matched,
> so I build the function name by checking the type of each element in
> the result from parse_string(). Which basically breaks all STR
> (string) parts of the rules.
>
> What I tried doing when building the function to call, is to treat all
> strings as 'str', but that gives me function names like
> can_buy_str_str_liv(), and that doesn't look too nice. I would rather
> have it look like is can_buy_str_from_liv(). Anyone have solved this
> problem and would like to share with me how they did it?


The following description is how CornerStone's verb system works (I'm
returning to its development after a hiatus of sorts).

Each verb exists as its own file ("look" == "/bin/verbs/_look.c").
/bin/verbs/_look.c inherits /std/inherit/verb.c which contains all the
functions to set error messages, verb rules (described in a minute), and
perform the actual verb-related actions.  All verbs are handled by a verb
daemon that also keeps track of the verbs' grammar objects (described
later).  Each verb is responsible for registering the rules that will be
used for parsing out the input.  For example, the "look" verb would register
its rules via

  add_rules(({
    "", "OBJ", "DIR",
  }));

The token values will be described in a minute, but the above is called when
the object is created.

When a player issues a command and the first word of the command line is
equal to a registered verb, the daemon will do the following:

* Take each rule for the verb ("", "OBJ", "DIR") and query a rule daemon for
an object that contains the actual grammar for that rule.  This way,
multiple verbs can use the same rule objects to take advantage of the
processed parse_string() grammars.

* The rule object will parse the input string (minus the first word) and
call the necessary functions in the verb object.  Take the "OBJ" rule, for
example.  If the input matches that rule, then it will call can_look_obj in
the verb object.  If that function returns 1, it will call do_look_obj in
the verb object to perform the action.  If either the can_ or the do_
functions returns 0, then the verb rule will fail and the verb daemon will
proceed to the next verb rule for that verb.

It was a real pain to design the verb system this way, but it's really
turned out to be a blessing for (mainly) 2 reasons.  1) It's easier to
maintain the code for the overall system by breaking delegating
functionality to different subsystems.  I also use the rule daemon for a
trigger system (an add_action-like system that can optionally use verb-like
rules).  2) You can dynamically add/remove rules from particular verbs
without having to rebuild grammars.  Some may say using multiple grammars
instead of one huge grammar for every possible verb is inefficient, but I
feel the maintainability and administration value of having it broken up
outweighs that consideration.

Ok, anyway, after that long diatribe, let's see if I can actually answer
your question.  Here are the tokens that I use in my system:

OBJ
  Evaluates to:
    Object found in immediate inventory OR immediate environment

  Return value:
    Object


LIV
  Evaluates to:
    Living object (NPC or other player) in immediate environment

  Return value:
    Object


OBJI
  Evaluates to:
    Object in the caller's inventory

  Return value:
    Object


OBJE
  Evaluates to:
    Object that is contained somewhere in the caller's immediate environment

  Return value:
    Object


OBJA
  Evaluates to:
    A hypothetical object in the player's immediate inventory or
    immediate environment

  Return value:
    Ambiguous object packet
      ({ ({ Adjectives }), Noun, Object Index })


DIR
  Evaluates to:
    A legitimate direction according to the verb.

  Return value:
    String

The only token I'm compelled to really describe in detail is the OBJA token.
This token allows you to extract a noun (object) from the input
command/sentence without it actually existing as a loaded object.  Thus, if
I type "look at the blue piece of paper", the piece of paper doesn't need to
be an actual object that DGD is aware of whereas all of the OBJ* tokens
evaluate to actually loaded objects.

Dworkin answered your question pretty well with the "buy STR from LIV".
Though, I would highly recommend separating the verb from the rules; "buy
STR from LIV" should be "STR from LIV" and be associated with the "buy verb.
This would allow rule reuse (I'm an OO freak, I guess).

The one challenge I'm facing is how to handle chained tokens.  For example,
what would a rule look like that could evaluate something like "look at the
first note on the board"?  The obvious solution would be to have a rule that
looked like "OBJ on OBJ".  But what happens when the depth is really
unknown?  Could it be possible to have an OBJS token that could handle any
number of objects that appear in relation to each other?  Food for
thought... I would love to hear ideas if any exist.

Anyway, I think I've rambled entirely too long and probably haven't answered
anything directly.

JC

From: Par Winzell
Subject: Re: [DGD] Algorithm for Parsing Commands
Date: Sat, 04 Sep 2004 07:49:11 -0500

> First, a question; for a long time, the paradigm has been to have a 
> basic command parser determine which verb to use, and then call that 
> verb function, which would parse the command further.  What is the 
> reasoning behind this?

Well, no. As I recall, by 1994, it was well established among those who 
spent their time thinking about such things that a global, static list 
of verbs is much more intuitive than e.g. LPMud's notion that rooms can 
add a verb that doesn't work elsewhere, or works differently elsewhere.

The Skotos mudlib, my only hands-on experience with a modern mudlib, 
makes a very clear distinction between grammatical constructs, which are 
part of the 'text-based player interface' module, and 'actions' which 
belong in the 'virtual world' module.

The code that deals with grammatical constructs (i.e. parses verb 
phrases) is preoccupied with the nearly-infinitely complicated task of 
unravelling the vagueries of the English language and produce useful 
error output when something's off. The code that deals with actions 
needs know nothing about language.

As a simple example, 'light light'. Chances are the TextInterface module 
would turn this into either ACT_FLIP(light_switch) or ACT_IGNITE(torch). 
  The actions are never ambiguous, and they could be generated by NPC 
scripting code just as easily as they are generated by the parser.

Anyway, that wasn't your main question. :)

> Assumption:  Articles are treated as whitespace

This assumption isn't necessary; adding article support is one of the 
simpler extensions. If you get 'the', you require that the noun resolves 
to a single object. If you get 'one' or 'a'/'an', you pick a random one. 
If you get 'six' you return six random ones. If you get 'sixth' you pick 
the sixth... etc.

> 2)  Determine which words are verbs, by comparing them to a list of 
> suitable verbs (this takes into account synonyms for verbs)
>      a) If there is no suitable verb, produce error output

Yes, verbs are terminals in our grammar as well.

> I know that this, so far, is still relatively simple; it can handle 
> "verb adjective noun preposition adjective noun" sentences all day 
> long.  I would like to make it more robust, to handle sentences such as;

I think you've pretty much got the essentials down. Parsers do double in 
complexity every time you try to make them smarter, but the examples 
below seem like fairly minor additions:

> "cast gnusto at spellbook" - gnusto is a noun, but would forever be 
> missed because it doesn't exist in the player's environment

SkotOS splits a sentence down by verbs, prepositions and articles. Those 
are grammatical constructs, as opposed to nouns and adjectives which are 
more or less free form. Since we allow any number of prepositions in our 
sentences, you can no longer usefully talk of 'the noun' or even 'the 
direct vs the indirect noun'. We had to come up with a new term, 'role', 
a single word label to describe the purpose of a noun in a sentence.

Thus:

   'wave my sword at the sky'
and
   'wave at the sword with my sky'

will resolve to the same thing; when parsing is done, the sword will be 
stored under one label, the sky under another. This remapping of grammar 
construct to logical construct makes it a lot easier to write scripts -- 
the sword should obviously be able to respond identically to be shaken 
at the sky regardless of which method players use to shake it.

To handle the 'gnusto' case, SkotOS introduced 'raw roles', where the 
parser would return the raw words rather than attempt noun resolution on 
a subset of the sentence, once it had finished chopping it up into 
roles. Thus the 'cast' verb could have two roles defined, 'spell' and 
'target' perhaps. The 'to' preposition would be configured to map to the 
'target' role for that verb, and the 'spell' role would be configured as 
a direct noun. The 'spell' role is then marked 'raw', to stop the parser 
from trying to interpret it, and "gnusto" will be sent on uninterpreted 
for some other code to deal with.


> "wear red cloak from burlap sack" - Would only work if there was 
> preposition/adjective/noun checking in "wear".

SkotOS by default will search the player's environment and inventory for 
  noun resolution, but before it does the search it looks to see if 
there is a Merry script defined on the verb object (each verb's 
configuration is stored in a separate object) that wishes to replace the 
normal search operation for the current role. If there is, the script 
may return an array of objects whose inventories to seek. This is needed 
for something as trivial as 'take cup from bag'.

> "read newspaper by candlelight" - another sentence that would just fail.
> "check settings on dial" - and another sentence that would just fail.

Both these would be handled by 'raw roles'. The only problem is that it 
is sometimes unpleasant to have to give up all noun parsing for a role 
in a verb. For the 'check' example, you'd sort of like 'check my sword' 
to still perform basic noun lookup, but if you mark the direct object 
role of check as 'raw', it's always going to be raw. That remains an 
unsolved irritation. Many other designs are no doubt possible.

Zell

From: dgd@dworkin.nl (Par Winzell)
Date: Tue Apr 19 17:04:01 2005
Subject: [DGD] Inheriting the same program twice

Petter Nyström wrote:

> But your mail have had intended effect, and I find myself indeed
> reconsidering. I will for sure give it some more thought. Maybe I will
> come up with something that I feel sure enough about to replace my
> current plans.
> 
> I know that you at Skotos have done things like this, and if you have
> any online material covering this, please point me there. (I vaguely
> recall reading some article over at your website, but can't find it in a
> brief search.)

I suspect the DGD mailing list is probably as likely a place to find
mails describing this as any internal Skotos documentation. Anyway, the
idea is very simple, and I don't mind re-summarizing it.

The Skotos approach -- and so far it looks like this will remain the
architecture in SkotOS 2.0 -- begins by cleanly separating the virtual
world from any interface considerations. The simplest way to keep this
abstraction clean is to always imagine maintaining a Quake interface to
the virtual world as well as the text interface, and ideally also make
sure a NPC can react to anything a player can react to without having to
parse text.

A simple example:

 - I type 'gently place my sword on the ground' onto my command line.

 - My user object, which is a clone of an object in the TextIF module,
sends my command line to the central text parser. The parser knows there
is a place verb, it knows the place verb can take a direct object as
well as an object prepositioned by 'on', and it finds that I am carrying
precisely one sword. It finishes the interface input handling by
ordering my body to execute the general action ACT_PLACE, which takes an
object in my possession and a destination as arguments. Note:
      * Everything is an object pointer, at this point; the text input
is long gone.
      * The ACT_PLACE action could equally well have been caused by the
QuakeIF module as a response to e.g. right-clicking on an object in your
inventory in the GUI, or it could have been triggered by a NPC as part
of a script. In neither of these cases is text involved.
      * The injection of ACT_PLACE into the virtual world could be done
in a zero-second callout without problem, drastically reducing the
danger of thread contention in a DGD/MP world.

 - In the virtual world, ACT_PLACE performs a minor state change -- it
atomically changes the environment (and perhaps proximity) of the object
in question. But -- vitally -- it finishes by firing off a state change
notification event to any object that cares.

 - Almost certainly any player within view does care, and so in practice
control passes back to the interface modules. The TextIF module, for
example, is now asked to describe an event that occured in the virtual
world. It doesn't care that in this case it ordered the action a few
milliseconds ago; the action might just as well have been ordered by a
Cursed Ring of Sword Dropping.

 - Various hideously complicated text generation algorithms are now run
which end up telling the player that he gently placed a sword on the
ground; the sword that the player gently placed it on the ground; the
ground that the player gently placed a sword on it, and any witness that
the player gently placed a sword on the ground.
      * Alternately, any observer that happens to be running the Quake
interface, receives a stream of UDP packets ordering the client to show
me placing my sword on the ground. Or an NPC can directly respond to the
event by subscribing to it.
      * Every event description can occur in a separate zero-second
callout... as long as there is some sanity to the order in which the
callouts are processed. I have yet to really think this through for
DGD/MP, and I'm a little nervous about it.



OK, so that all sounds very complicated compared to the 2.4.5 approach
now that I type it out. There are probably other modern approaches that
do not rely on a central parser... but I'm not sure those would be any
simpler, and I really like the clean design of this one.

Zell