Multiple versions of a verb, with and without counts

Is there a standard syntax for declaring a verb with a count, or is it a roll-your-own sort of thing, and if so what is the correct preposition format for the verbPhrase in the verb declaration?

As always, an example:

#charset "us-ascii"
#include <adv3.h>
#include <en_us.h>

DefineTAction(Stack);
VerbRule(Stack) 'stack' dobjList : StackAction
        verbPhrase = 'stack/stacking (what)'
;
modify Thing
        dobjFor(Stack) {
                verify() {
                        illogical('{You/he} can\'t stack {that dobj/him}. ');
                }
        }
;
DefineLiteralTAction(StackNum, DirectObject);
VerbRule(StackNum) 'stack' singleLiteral dobjList : StackNumAction
        verbPhrase = 'stack/stacking (count) (what)'
;
modify Thing
        stackCount = nil
        dobjFor(StackNum) {
                verify() {
                        illogical('{You/he} can\'t stack {that dobj/him}. ');
                }
                action() {
                        if(!rexMatch('(<digit>+)$', gLiteral)) {
                                "<q><<gLiteral>></q> isn't a valid number. ";
                                exit;
                        }
                        self.stackCount = toInteger(gLiteral);
                }
        }
;

startRoom:      Room 'Featureless Void'
        "This is a room, more or less. "
;
+ pebble: Thing 'round pebble*pebbles' 'pebble'
        "It's a round pebble. "
        isEquivalent = true
        dobjFor(Stack) {
                verify() { illogical('How many do you want to stack?'); }
        }
        dobjFor(StackNum) {
                verify() {}
                action() {
                        inherited();
                        "We should really check to see if you have
                        <<spellInt(self.stackCount)>> handy, but we don't. ";
                }
        }
;
+ brick: Thing 'red brick*bricks' 'brick'
        "It's a standard red brick. "
        isEquivalent = true
        dobjFor(Stack) {
                verify() {}
                action() {
                        "This isn't actually an action although it looks
                        very much like one. ";
                }
        }
        dobjFor(StackNum) {
                verify() {
                        illogical('You can\'t stack more than one at a time. ');
                }
        }
;
+ blob: Thing 'unstackable blob*blobs' 'blob'
        "It's an unstackable blob. "
        isEquivalent = true
;

me:     Actor
        location = startRoom
;

versionInfo:    GameID
        name = 'sample'
        byline = 'nobody'
        authorEmail = 'nobody <foo@bar.com>'
        desc = '[This space intentionally left blank]'
        version = '1.0'
        IFID = '12345'
;
gameMain:       GameMainDef
        initialPlayerChar = me
;

This creates two new “stack” verbs, Stack which just takes a dobj and StackNum that takes a literal and a dobj. There are also two kinds of Thing: pebbles, which must be stacked with a count, and bricks, which must be stacked individually.

The code works, mostly:

>stack brick
This isn't actually an action although it looks very much like one.

>stack 2 bricks
You can't stack more than one at a time.

>stack pebble
How many do you want to stack?

>stack 2 pebbles
We should really check to see if you have two handy, but we don't.

>stack x bricks
"x" isn't a valid number.

…covers all of the basic behaviours I care about.

My first problem/question is when the parser gives a disambiguation prompt:

>stack
Count do you want to stack?

This is clearly because of the format of verbPhrase in the StackNum VerbRule:

        verbPhrase = 'stack/stacking (count) (what)'

But if there’s a guide to prepositions (beyond what’s mentioned in passing in the Creating Verbs in TADS 3 documentation, I haven’t been able to locate it.

So that’s one question: what’s the correct preposition/format for declaring the VerbRule here.

Another question is: is this approach fundamentally off-base? That is, is there some more straightforward method of doing this (that is, specifying a verb that can or cannot take a count in different circumstances)?

I’d earlier posted a question about using CollectiveGroup to manage actions with counts, but now I’m looking at an alternate approach: instead of modelling the situation like a bag of equivalent objects (where the basic behaviour is controlled by the individual objects and a CollectiveGroup tries to figure out when to step in to treat them as a group), I’m now considering treating it more like a debit card with a balance (one object that holds a count, so it’s always just a single object and the verb handlers figure out if an action involving a count makes sense or not), if that makes sense.

Edit: Slightly edited sample code to include a blob object that takes the default behaviours for both verbs.

For sanity reasons, I would reconsider having two STACK verbs with different semantics until you have more of the basics in place.

The problem is that >STACK is being treated as a StackNum verb when, in this context, it should be treated as Stack verb (“What do you want to stack?”) I would expect if the user typed >STACK 3 for the parser to disambiguate the verb as StackNum (and respond with something like “What do you want stack three of?”)

This is why I’m suggesting forgoing STACK for now, or using separate verbs (STACK and PILE?) At least while you’re debugging you’ll have a better idea what path the parser is taking.

Looking at other verbs that deal with singleLiteral, I notice most (all?) have one or both of these properties set:

    askDobjResponseProd = singleNoun
    omitIobjInDobjQuery = true

I would suggest looking at other verbs that use singleLiteral and see what they’re up to. I also see that there’s a singleNumber available for verb production, but it’s only used for >FOOTNOTE, so it may or may not be usable for you.

Finally, there’s a pragmatic approach: Use the simple command STACK PEBBLES that prompts the user to enter the number of X to stack (“How many pebbles? _”) It’s unsatisfying, I know, but it might be easier than mucking around in the depths of the parser.