Subcommands

A new extension is available at Github. This adds a “subcommand” property to objects, exposing the snippet which matched it. So if I >TAKE WAND, then the subcommand of the rusty iron rod is “wand”; if I >TAKE RUSTY IRON ROD, then it’s “rusty iron rod”.

This has been tested with 6L38, and should be portable to other ones by re-copy-pasting the I6 sections. The parts I modified are marked “===NEW===” to be easily moved around.

woot

Very cool.

Ooooh, this could be very useful!

The attached file seems to work fine on 6M62.
subcommands.i7x (15.7 KB)

I’ll just post a link here, in case anyone is subscribing to this topic.

Six years later, a new version is out! I realized it would be useful to have a snippet that referred to the verb itself, and then I realized it should go in this extension, and then I realized I should really incorporate vlaviano’s fixes, and…

Anyway! Here are new versions for 6L38, 6M62, and 10.1. Version 2 provides a new phrase (“the subcommand of the verb”) and also incorporates the bugfixes that add invalid snippets when disambiguating. The extensions also have different names so they can coexist in your Extensions directory.

Notably the last one crashes the IDE for me, which seems like a definite bug, but probably on my end; can anyone test out the 10.1 version? I’ll put them on Github once I know they work.

Subcommands 6L38.i7x (20.7 KB)
Subcommands 6M62.i7x (20.6 KB)
Subcommands 10-v2.i7x (20.5 KB)

4 Likes

wow. It has so vexed me that I couldn’t get the preposition used with a verb. This is going to be one of my obligatory extensions now. (The v10 version looks fine to me.)

2 Likes

That preposition is actually the one part that isn’t easily accessible within I6! The trick is to look at the parser’s results, then count how many tokens at the start are literals in the grammar line rather than tokens like [something], and return a snippet with that many words in it.

3 Likes

Now I’m getting greedy. There’s the words before the noun, and the words after. For a command with one noun, we might be interested in “before noun”, “noun”, “after noun”, For a command with two nouns, that balloons to “before noun”, “noun”, “between nouns”, “second noun”, “after noun”. Imagine this ugly Understand line:

Understand "teeter on/in [thing] as/to/upon/without [thing] flourish/--" as teetering.

For two imaginable invocations, gould we get at all of “teeter on”, “upon”, “flourish” vs. “teeter in”, “without”, “” ?

I have a vague recollection that I once read an extension or other code of yours, @Draconis, that modified storied actions to preserve the text of topics. That ring a bell? I may just be massively misremembering Ron Newcomb’s Editable Stored Actions, which stores the whole text of the player’s command.

It’s easy enough to step through the matched grammar pattern to extract words/objects- this is how the PrintCommand() function works:

[ PrintCommand from i k spacing_flag;
    if (from == 0) {
        i = verb_word;
        if (LanguageVerb(i) == 0)
            if (PrintVerb(i) == 0) print (address) i;
        from++; spacing_flag = true;
    }
    for (k=from : k<pcount : k++) {
        i = pattern-->k;
        if (i == PATTERN_NULL) continue;
        if (spacing_flag) print (char) ' ';
        if (i == 0) { PARSER_CLARIF_INTERNAL_RM('F'); jump TokenPrinted; }
        if (i == 1) { PARSER_CLARIF_INTERNAL_RM('G'); jump TokenPrinted; }
        if (i >= REPARSE_CODE)
            print (address) VM_NumberToDictionaryAddress(i-REPARSE_CODE);
        else
            if (i ofclass K3_direction)
                print (LanguageDirection) i; the direction name as adverb
            else
                print (the) i;
      .TokenPrinted;
        spacing_flag = true;
    }
];

the gist is that each entry in the pattern array has the special meanings PATTERN_NULL (meaning null, skip it), 0 (meaning ‘that’- a topic) or 1 (meaning ‘those things’- a multi-token) else if < the constant REPARSE_CODE it’s an object or if >= REPARSE_CODE it’s a preposition.

This enables you to easily reconstruct the matched command, and its parts, but a little bit more work would be required to match this back to the exact wording of the player’s original command…

The verb_word (if there is one) is held as a separate global variable, which is printed up first

EDIT there’s a bug in the case of implied go, in that typing ‘n’ doesn’t set up a new pattern or pcount correctly (pcount should be set to 0).

EDIT2 this patch to PrintCommand() fixes it by checking whether the verb_word was a direction and halting printing if so:

[ PrintCommand from i k spacing_flag;
	if (from == 0) {
		i = verb_word;
		if (LanguageVerb(i) == 0)
			if (PrintVerb(i) == 0) print (address) i;
		from++; spacing_flag = true;
		! #################### Insertion Begins ###############
		@push wn; wn=verb_wordnum;
		k = NounDomain(Compass, 0, 0);
		@pull wn;
		if ((k~=0) && (k ofclass K3_direction)) rtrue;
		! #################### Insertion Ends ################
	}
	for (k=from : k<pcount : k++) {
		i = pattern-->k;
		if (i == PATTERN_NULL) continue;
		if (spacing_flag) print (char) ' ';
		if (i == 0) { PARSER_CLARIF_INTERNAL_RM('F'); jump TokenPrinted; }
		if (i == 1) { PARSER_CLARIF_INTERNAL_RM('G'); jump TokenPrinted; }
		if (i >= REPARSE_CODE)
			print (address) VM_NumberToDictionaryAddress(i-REPARSE_CODE);
		else
			if (i ofclass K3_direction)
				print (LanguageDirection) i;
			else
				print (the) i;
	  .TokenPrinted;
		spacing_flag = true;
	}
];
2 Likes

That should work straight out of the box—there’s code in the stored actions kit to handle this specific case—but it doesn’t quite due to a bug in Inform’s compiler. Stored actions will preserve the text of the topic if they’re generated in play, but not if they’re written out by the author in the source text (stored action literals).

Unfortunately, since that’s a compiler bug, there’s no easy way around it.

1 Like

This should be doable, as Dr Bates pointed out; stepping through the pattern is how “the subcommand of the verb” is calculated, and referencing “the subcommand of the noun” would let us step past the appropriate number of words when we run into an object token. This would glitch out for a command that references the same object multiple times, or uses a multiple object token, but there’s probably a way around that too—and since we’re already hacking NounDomain to set the subcommands, in the worst case we could hack the parser to store a couple more word numbers in global variables for us.

What would these subcommands be called? The subcommand after the noun and the subcommand after the second noun?

In pursuing a new Remembering extension I’ve wanted to be able to sensibly rephrase a player’s command with the actual words they used including words the grammar made optional but were present in the command. So I’d like it to canonicalize the noun names like PrintCommand does today and to omit ‘both’, ‘all’, etc. (This is a flourish; we could live without it, but I’d like it.)

Suppose we add Understand "stand atop/-- [thing]" as entering.

If we enter stand atop desk, “atop” doesn’t end up in the pattern array: I suppose because it wasn’t relevant to disambiguate from “stand [thing]” and, so PrintCommand doesn’t show it and Subcommands doesn’t include it in the subcommand of the verb.

I don’t know about names, but what I’d like is something to the effect of:

subcommand of the pre-noun prepositions
subcommand of the post-noun prepositions
subcommand of the post-second-noun prepositions

1 Like

Yes, these (potentially) null-matched prepositions are (perhaps unhelpfully) always inserted into the pattern array as PATTERN_NULL place-holders rather than any actually matched preposition, so just skipped over by PrintCommand()

EDIT these place-holders are always inserted into the pattern array, even if no preposition is supplied-
‘stand desk’ => PATTERN_NULL desk
‘stand atop desk’ => PATTERN_NULL desk

EDIT2 in fact, I notice that when there is no null alternative a PATTERN_NULL is inserted to the pattern array for every unmatched potential preposition when one from alternates is matched. So after Understand “stand atop/astride/over/upon [something]” as entering:

‘stand over desk’ => over PATTERN_NULL PATTERN_NULL PATTERN_NULL desk

Whereas when there is a null alternative a single PATTERN_NULL is inserted, regardless of how many alternate prepositions there are and whether there was a preposition match or a null-match.