# How to Create Verbs

## Adapted for adv3Lite by Eric Eve

Note: the material in this article covers much the same ground as the section on Defining New Actions in the Adv3Lite Library Manual, but it seems worth adapting and keeping Mike Roberts's article on this topic as a complementary take on the same material, not least since it often offers more detailed explanations than the Adv3Lite Library Manual.

## Introduction

Every game based on the TADS 3 standard library automatically gets the benefit of the huge complement of verbs that the library defines - about 150 of them. Even though so many verbs are pre-defined, though, it's a good bet that many games - maybe even most - will have at least one or two situations where a custom verb is needed. Fortunately, it's relatively easy to add new, custom verbs to a TADS 3 game.

The method for adding a verb depends on what kind of verb you're adding. We'll go over the main types one at a time. Note that the techniques we describe here are exactly the same techniques that the library uses to define its own verbs - this means your new verbs are true first-class citizens, fully on par with the pre-defined library verbs.

The examples in this article are written for the English version of the library. The syntax used to define verbs varies by language, so these examples probably won't work with other language versions. To use the English library's special features in your game, you need to include the library's English language header in each of your source files, along with the main library header. Just put these lines at the start of each of your ".t" source files:

       #include "advlite.h"

## Verbs with no objects

A verb that doesn't take any objects is called an "intransitive" verb. This is a verb like LOOK AROUND or SLEEP that expresses an action without mentioning any objects.

To create an intransitive verb, you have to write two bits of code, like this:

       DefineIAction(Sleep)
         execAction(c) { DMsg(not sleepy, '{I\'m} not sleepy right now. '); }
       ;

       VerbRule(Sleep)
         'sleep' | 'doze' 'off' | 'nod' 'off'
         : VerbProduction
         action = Sleep
         verbPhrase = 'sleep/sleeping'
       ;

The first part, DefineIAction, defines the "Action" object for your new verb. This Action object represents the verb during command execution. DefineIAction (which stands for "Define Intransitive Action") is a macro defined in the adv3Lite library that takes care of some of the tedious busy-work of creating your Action object; under the covers, this macro just starts an object definition for you and fills in a few properties. The name in parentheses is the name of the object.

The execAction(c) definition is just a normal method definition that's part of your Action class. For an intransitive action, this method is what actually carries out the action, so you simply write code here for whatever you want the verb to accomplish. The c parameter is the Command object created for the current command, but we don't need to worry about that now.

The second part, VerbRule, defines the grammar for your verb. This tells the parser the various ways that the player could type this verb in command input. The name in parentheses is the "tag," which is simply a name for the rule, for reference purposes. In this case, the tag is "Sleep", which happens to be the same as the root name of the Action class - but this is completely arbitrary. We could have made it "VerbRule(gloxnfab)", and everything would have worked equally well. The only important thing about the tag is that it's unique among all of the VerbRule definitions in the entire game; by convention, the library usually uses the Action root name as the tag just because it's an easy way of ensuring uniqueness. Note, however, that you sometimes will want to define multiple VerbRule's for the same Action, and when you do that, you'll obviously have to come up with some other unique tag for each rule after the first. You could use names like "VerbRule(Sleep2)", for example.

To define the syntax of your verb, you simply list all of the different phrasings, separating each phrasing from the next with the "|" character. Each quoted string is matched exactly to a token - which means that each word has to go in a separate string, since the tokenizer breaks up a command into words before the parser starts matching syntax rules.

Note that, by default, the "|" symbol separates entire phrasings, so the definition above creates three phrasing: "sleep", "doze off", and "nod off". You can also group parts of the phrasing with parentheses, and then use the "|" symbol to separate individual sub-phrases; for example, we could also write the grammar above like this:

       'sleep' | ('doze' | 'nod') 'off'

This is the same as the original definition, because it says that one phrasing is "sleep", and another is either "doze" or "nod" followed by "off". It's sometimes more compact to write rules that group sub-phrases like this, because in English there are many verbs where there are different phrasings that involve variations in only one or two words in a longer phrase.

After the grammar rules, you have to specify that your grammar rule belongs to the VerbProduction class and then define which the Action object is associated with this VerbRule; in this case, the Action object is Sleep. Just as DefineIAction is a macro that defines an Action object, VerbRule is a macro that defines a subclass of VerbProduction.

Everything else in the VerbRule is an ordinary property or method definition. Every VerbRule should define a verbPhrase property, giving a string that's used as a message template for the verb. Don't confuse this with the grammar rule - the verbPhrase isn't used to parse anything, but is simply used to generate messages. You should always use the format shown above for an intransitive verb. Note that you only specify one phrasing in the verbPhrase, no matter how many phrasings you define in the grammar.

## Verbs with one object

Many verbs require an object to operate upon: TAKE THE KEY, READ THE BOOK. These are called "transitive" verbs, and the object that a transitive verb operates upon is called the verb's "direct object."

Here's an example of defining a transitive verb:

       DefineTAction(Repair);

       VerbRule(Repair)
         ('repair' | 'fix') singleDobj
         : VerbProduction
         action = Repair
         verbPhrase = 'repair/repairing (what)'
         missingQ = 'what do you want to repair'
         dobjReply = singleNoun
       ;

       modify Thing
         dobjFor(Repair)
         {
           preCond = [touchObj]

           verify()
           {
             if(!isRepairable)
                illogical(cannotRepairMsg);
           }
         }

         isRepairable = nil
         cannotRepairMsg = '{I} {do}n\'t know how to repair {that dobj}. '
       ;

The first part defines the Action class for the new verb. DefineTAction stands for "Define Transitive Action"; it's a macro provided by the library to handle the tedious parts of defining the new verb. Note that unlike our intransitive verb example earlier, we do not define an execAction(c) for a transitive verb; this is because a transitive verb is executed by the direct object, not by the action class itself.

The second part defines the grammar rule for the new verb. This is very similar to defining a VerbRule for an intransitive action, but note some important differences. First, the grammar rule has to include a "slot" for the direct object. In this case, we did that using the special keyword singleDobj: this indicates that the slot can only take a single object, not a list of objects (so we won't be allowed to type REPAIR ALL or REPAIR TOY, SLIDE PROJECTOR, AND TOASTER; if we do, the parser will politely let us know that we have to repair one thing at a time). We could instead have used multiDobj, which indicates that the slot can take multiple objects at once. Second, the verbPhrase also has to include a placeholder for the direct object, which we do by writing "(what)". We could instead write "(whom)", if the direct object for the verb is typically a person rather than an inanimate object; for example, a "talk to" action would probably write its verbPhrase as 'talk/talking (to whom)'. Third, the missingQ provides the text of the question the parser will ask if the player simply types REPAIR, without specifying what to repair (the parser will capitalize the start of the sentence and finish it with a question mark, so we don't need to so on the missingQ property. Finally dobjReply = singleNoun tells the parser to expect the player to respond with the name of a single noun if it asks the player what they want to repair. If we'd used multiDobj instead of singleDobj in our grammar definition we could specify dobjReply = nounList, but we don't need to since the library will assume this if we don't specify otherwise.

> verbPhrase and prepositions: Verb phrasings that involve prepositions, such as "talk/talking (to whom)", can be written two ways: with the preposition inside or outside the parentheses. And the placement does matter. Write the preposition inside the parentheses if it belongs with the noun phrase of the direct or indirect object. Write the preposition outside the parentheses if it's not a true preposition a "particle" - that is, a word that happens to be a preposition but when used with the given verb actually changes the meaning of the verb. For example, the "up" in "pick up" is a particle, because it changes the whole meaning of the verb from "choose" to "take."

> Fortunately, there's an easy rule of thumb for deciding which is which. Try sounding out the sentence using a pronoun for the direct object, and see if it sounds more natural for the preposition to go before or after the pronoun. If the preposition goes before, then it's really a preposition, so it goes with the direct object phrase, hence it goes inside the parentheses: "talk to him" sounds right, and "talk him to" obviously sounds wrong. Thus, the verbPhrase for this command would be 'talk (to whom)'. If, however, the preposition sounds better at the end of the sentence, it's a particle: "pick it up" sounds much better than "pick up it." If it's a particle, it goes outside the parentheses, so the verbPhrase becomes 'pick up (what)'.

> To summarize: if the preposition goes after the pronoun, it's a particle, so put the preposition with the verb: 'pick up (what)'. If the preposition goes before the pronoun, it goes with the direct object: 'talk (to whom)'.

The third part is where we carry out the verb's action, or at least carry out its default action. A transitive verb is executed by calling methods on the verb's direct object. Now, in many cases, a new verb will only apply to a few objects, so it might be tempting to define handlers only for those objects. However, a player might not know the limits you had in mind, or might just want to be difficult, so the player could conceivably try applying your verb to any object in the game. The best way to cope with this is to write a default handler in the Thing class, since virtually every game object is typically based on Thing, either directly or indirectly. Because a new verb usually won't have any effect on just any random object, the default Thing handler can be a simple "illogical" call in the verify() method, as shown above, or it may be more elaborate, (e.g., to rule out an actor trying to repair themselves). If you want the default handling to actually do something, just write the appropriate dobjFor() code as you would for any pre-defined library verb.

It's a good idea to define custom properties such as `isRepairable` and `cannotRepairMsg` as above, since this allows easier customization than having to override verify() methods just to vary the refusal response or allowed repairing (which may be particularly pertinent if we have a more complex verify() method containing further global sanity checks which we want to keep on all objects). Then, for example, if you want the response to REPAIR AUNT MABEL to be 'If you only you could!' you only need to override the `cannotRepairMsg` on your mabel object without having to rewrite the entire verify() method.

## Verbs with two objects

Some verbs take not one but two objects: PUT BOOK ON SHELF, UNLOCK DOOR WITH KEY.

Note that we're not talking about commands that list multiple direct objects, such as TAKE BOOK AND KEY: those are still one-object verbs in the sense we're talking about here, because there's only one role that an object can play with the verb. If a list of objects is specified for the single role of a one-object verb, it's just saying that the verb should be applied in turn to each object in the list, with each object serving the same role. Instead, what we're talking about here is verbs that have two objects in different roles.

In UNLOCK DOOR WITH KEY, the key is the "instrument" or "agent" of the action, but the door is what's being unlocked; we're not unlocking the key, obviously. The door is called the "direct object," because it's the object that's being directly affected by the action. The key is called the "indirect object," because it's being used in the course of performing the action but isn't the main focus of the action.

Creating a two-object action is a lot like creating a one-object verb:

       DefineTIAction(ScrapeWith);

       VerbRule(ScrapeWith)
         'scrape' multiDobj 'with' singleIobj
         : VerbProduction
         action = ScrapeWith
         verbPhrase = 'scrape/scraping (what) (with what)'
         missingQ = 'what do you want to scrape; what do you want to scrape it with'
         iobjReply = withSingleNoun
       ;

       modify Thing
         dobjFor(ScrapeWith)
         {
           preCond = [objHeld]

           verify()
           {
              if(!isScrapeable)
                 illogical(cannotScrapeMsg);

              if(gIobj == self)
                 illogicalSelf(cannotScrapeWithSelfMsg);
           }
         }
         iobjFor(ScrapeWith)
         {
           preCond = [objHeld]

           verify()
           {
             if(!canScrapeWithMe)
                 illogical(cannotScrapeWithMsg);
           }
         }

         cannotScrapeMsg = '{That subj dobj} {isn\'t} something {i} {can} scrape}. '
         cannotScrapeWithSelfMsg = '{I} {can} hardly scrape {the dobj} with {itself dobj}. '
         cannotScrapeWithMsg = '{That subj iobj} {doesn\'t look} very useful as a scraping tool. '
         isScrapeable = nil
         canScrapeWithMe = nil
     ;

As with a regular transitive action, we first define the Action object, this time using DefineTIAction. DefineTIAction stands for "Define Transitive with Indirect object Action"; it defines an action with both a direct and an indirect object.

The VerbRule grammar for a two-object action requires two object slots, one for the direct object and one for the indirect object. The direct object slot is indicated using singleDobj or multiDobj, just as it is for a regular transitive action. The indirect object slot is indicated with the similar singleIobj or multiIobj keywords.

There are a couple of restrictions on these object slots keyword to be aware of. First, exactly one direct object and one indirect object slot must be included in each grammar rule, for fairly obvious reasons. Second, less obviously, at most one of the slots can be a "multi" keyword. So, you can't use multiDobj and multiIobj in the same rule. You can use singleDobj and singleIobj together, and you can mix and match the "single" and "multi" keywords, but you can't use "multi" for both. The reason for this makes sense if you think about it: if the player could specify multiple objects for both slots, the meaning would be terribly unclear, because it would be difficult or impossible to know which direct object was meant to be used with which indirect object. We avoid this confusion by allowing only one or the other slot to accept multiple objects.

Note that the verbPhrase needs two "(what)" (or "(whom)") markers, since the verb has two object roles.

> In the verbPhrase string, the indirect object preposition will always go inside the parentheses of the placeholder, but the direct object preposition can sometimes go inside and sometimes go outside. Use the same rule of thumb as for single-object verbs to figure out which format to use. We'd say "look at it through telescope," not "look it at through telescope," so we'd write our verbPhrase as 'look (at what) (through what)'. However, we'd say "look it up in book" rather than "look up it in book," so this verbPhrase would be 'look up (what) (in what)'. You can repeat the test for the indirect object, but as far as I can tell, English doesn't have any verb phrases where the indirect object part of the phrase involves a particle.

In writing our default action handler in Thing, we must write not only a direct object handler (using dobjFor), but an indirect object handler (using iobjFor) as well. As with plain transitive actions, these default handlers will usually just disallow the action in one way or another, and, in this case, rule out an object being used on itself.

## Verbs with a "literal" object

Sometimes, a verb superficially looks like a transitive verb, but rather than taking an ordinary direct object, the verb takes a literal phrase as its object. For example, in SAY HELLO, the object of the verb is literally the word "hello." (Note that the adv3Lite libary already has its own SAY verb, though).

You can define a verb that takes a literal object like this:

       DefineLiteralAction(Say)
         execAction(c)
         {
           "Okay,  <<gLiteral>>. ";
         }
       ;

       VerbRule(Say)
         'say' literalDobj
         : VerbProduction
         action = Say
         verbPhrase = 'say/saying (what)'
         missingQ = 'what do you want to say'
       ;

Even though a "literal action" has the superficial grammatical form of a transitive action, we must define an execAction(c) method to execute the verb, just as we would for an intransitive action. This is because a literal action doesn't have a direct object in the usual sense; there's no game object where we can write a dobjFor() definition, since the "object" of the verb is just some literal text. Note also that we can obtain the literal text that the user typed in by using the gLiteral macro. gLiteral returns a string giving the actual text that the user typed in (the string returned even preserves the original upper/lower case of the text the user entered).

In the VerbRule, the "slot" for the literal text must be indicated with the literalDobj keyword. In command input, the player can enter literal text with or without quotes: SAY HELLO and SAY "HELLO" will both match this grammar. If the user does use quotes, note that getLiteral will not include the quotes in its returned value; this means that you don't have to worry about whether or not the player used quotes, since getLiteral() will just return the meaningful part of the text in either case.

## Verbs with one object plus a literal

Some verbs take both an ordinary object and a literal: TYPE XYZ ON KEYBOARD or WRITE "SEND HELP" ON NAPKIN. We can define this kind of verb like so:

       DefineLiteralTAction(WriteOn);

       VerbRule(WriteOn)
        'write' literalDobj ('on' | 'in') singleIobj
        : VerbProduction
        action = WriteOn
        verbPhrase = 'write/writing (what) (on what)'
        missingQ = 'what do you want to write;what do you want to write that on'
        dobjReply = onSingleNoun
    ;

       modify Thing
        canWriteOnMe = nil

        dobjFor(WriteOn)
        {
            preCond = [touchObj]
            verify()
            {
                if(!canWriteOnMe)
                   illogical(cannotWriteOnMsg);
            }

        }
       ;

       // later, as part of some room definition...
       + paper: Thing 'piece of paper'
         "It's a piece of paper, with some hand-written notes:
          \b<< noteText >> "

         noteText = 'Get eggs!'
         canWriteOnMe = true

         dobjFor(WriteOn)
         {
           action()
           {
             "You add your notes to the paper. ";
             noteText += '\n' + gLiteral;
           }
         }
       ;

As you might expect, this type of verb combines some of the features of the regular transitive action with the literal action. There's one special thing to note, though: the ordinary object (the object that's not the literal) is always considered the direct object, for the purposes of the dobjFor() handler. In this example, you can see that the ordinary object fills what would be the indirect object in a TIAction: it's in the second object slot in the grammar, and it follows a preposition, which is usually a dead give-away that it's an indirect object. Regardless of this, we must treat the ordinary object as a direct object in all of our object definitions.

## Verbs with a "topic" object

Some verbs involve a mere reference to an object, rather than an action applied to the object. For example, THINK ABOUT AIRLOCK doesn't actually do anything to the "airlock" object, and it doesn't require that the object be physically present; the action simply makes mention of the airlock. In a sense, the direct object is not really the object itself but instead some kind of abstract idea of the object. We call these abstract ideas "topics."

We can define a verb that operates on a topic like this:

       DefineTopicAction(ThinkAbout)
         execAction(c)
         {
             "Hmm <<gTopicText>>, very interesting";
         }
       ;

       VerbRule(ThinkAbout)
        ('think' | 'ponder' | 'cogitate') 'about' topicDobj
        : VerbProduction
        action = ThinkAbout
        verbPhrase = 'think/thinking (about what)'
        missingQ = 'what do you want to think about'
        dobjReply = aboutTopicPhrase
    ;

As with literal actions, we must define the handler for a topic action with an execAction(c) method on the topic action itself. Note that we can use the gTopic macro to obtain the topic. gTopic returns an object of class ResolvedTopic that describes the topic that the player typed. We won't go into detail on ResolvedTopic here, but we will point out that you can get the original text the player typed by calling the getTopicText() method on the ResolvedTopic object or by just using the gTopicText macro.

In defining the VerbRule for a topic action, you must use the topicDobj keyword to indicate the slot for the topic in the grammar.

## Verbs with one object plus a topic

As with literals, we sometimes want to combine a topic and an ordinary object: CONSULT TEXTBOOK ABOUT NUMERICAL INTEGRATION. We can define these verbs in much the same way that we define literal actions:

       DefineTopicTAction(LookUp);

       VerbRule(LookUp)
        (('look' | 'l') ('up' | 'for') | 'find'  | 'search' 'for' | 'read' 'about')
        topicIobj ('in' | 'on') singleDobj
        | ('look' | 'l') topicIobj 'up' ('in' | 'on') singleDobj
        : VerbProduction
        action = LookUo
        verbPhrase = 'look/looking up (what) (in what)'
        missingQ = 'what do you want to look that up in;what do you want to look up'
        dobjReply = singleNoun
        iobjReply = topicPhrase
    ;

       modify Thing
         dobjFor(LookUp)
         {
           preCond = [objVisible]

           verify()
           {
             illogical('{The subj dobj} {doesn\'t seem} to work that way. ');
           }
         }
       ;

       // later, in some room definition
       + dictionary: Thing'dictionary'
         "Frobster's Third Collegiate Unabridged. "

         dobjFor(LookUp)
         {
           verify() { }
           action()
           {
             local wrd = gTopicText()
             local def = dictionaryTable[wrd];
             if (entry != nil)
               "<b><<wrd>></b>: <<def>> ";
             else
               "You can't seem to find an entry for <q><<wrd>></q>
               in the dictionary. ";
           }
         }

         // filling up the dictionary is left as an exercise to the reader
         dictionaryTable = static new LookupTable(32, 64)
       ;

Note that we use topicIobj to indicate the topic slot in the grammar. As with the literal transitive action, the ordinary object always takes the functional role of the direct object, and we must include the grammatical role of the literal phrase, for message-generation purposes, as the second argument to the DefineTopicTAction macro.

## Verbs with three or more objects

The adv3Lite library doesn't provide any verb with more than two object roles. This doesn't mean that you can't define such a verb; and adv3Lite does provide a TIA Action extension to allow you to create your own verbs with three objects.

The basic principles of the library's parsing and command execution mechanisms can extend to arbitrarily many object roles in a single action, so creating a verb with four or more objects would be a matter of extending the extensions three-object action class (TIAAction) to resolve and coordinate execution with additional object roles. But this would require writing significant amounts of new code; such a new action class would be at least as complicated as the TIAAction class, which is not at all trivial. The details are beyond the scope of this discussion.

If you're seriously thinking about creating a three-object action, we'd like to offer this bit of non-technical advice: please reconsider. While you might have devised an elegant puzzle that requires manipulating three things at once in a single command, keep in mind that your most of your players will be never think of such a command. Players don't like having to guess at verbs; guessing at syntax is even worse. Players have come to expect a certain range of syntax in IF, and this works to everyone's advantage: it makes it easy for players to learn and remember how to write commands, and it limits the number of variations authors have to program. In practice, the two-object verb has proven to be a very comfortable limit that provides plenty of expressive power but keeps things simple.

One more suggestion: in most cases that people have found where three-object verbs seem to be needed, there's almost always a way to decompose the action into two actions of two objects apiece. For example, PUT COIN IN SLOT WITH TWEEZERS could be decomposed into GET COIN WITH TWEEZERS; PUT COIN IN SLOT. Many, if not most, three-object actions involve this pattern of moving one object in relation to another using a third object, and this pattern frequently lends itself to decomposition along these same lines.

TADS 3 Technical Manual
Table of Contents | TADS 3 In Depth > How to Create Verbs
Prev: Verify, Check, and When to Use Which Next: Custom Preconditions