I7 parser errors: what parts *did* the parser understand?

I’m hoping someone with more knowledge of Inform 7’s I6 underbelly can help me out.

In the event of a parser error, is there any way to grab and refer to the parts of a player’s input that the parser did manage to understand before it gave up? I would like to try writing parser errors that specifically refer to parts of the player’s input. I’m imagining something like:

The wording might need a bit of finesse, but hopefully you get the idea: instead of simply saying “You can’t see any such thing,” or “I only understood you as far as wanting to get up,” the parser error message would explicitly break down the player’s input into the parts that it did and didn’t understand, and explain why.

But first I need to know where these “parts” get stored and how to refer to them. Any suggestions?

You might be able to grab some of it with a “rule for supplying a missing noun” (or second noun).

But I’m hoping someone here will have a better answer…

I’ve been wanting this change myself, but I haven’t sat down to munge through the parser internals and make it happen. I’m sure it’s possible.

It occurs to me that Smarter Parser takes a sort of iterative approach to parsing. If nothing matches the first time around, it rearranges things and puts the command through again. I wonder if there would be any benefit in having that be built into the parser?

Some reasons:

If the parser failed, it might still have matched an Understand line (or set of lines?), but some tokens would remain as unparsed snippets. Then some Smarter Parser-like rules could have a chance at fixing them before the next iteration.

I recently implemented a verb that had optional noun and topic. If one or the other was missing, it would be supplied by the game in a context-dependent way. Because some of this work had to be done by a “rule for supplying a missing noun,” I had to implement some of the action separately for independent forms of the verb, because a Check rule redirecting one form to another wouldn’t run until parsing was finished.

It might give the game designer better control over disambiguation. I don’t really understand how disambiguation works now, though, so maybe I’m wrong.

Of course it could just end up a big hairy mess.

Maybe you’d like to take a look at an experimental extension “Explicit Error Messages” we’ve been playing around with. It seems to do something similar to what you want to achieve, I think. The responses are still limited to one word of the original input, though.

The extension requires Default Messages by Ron Newcomb. The Option “Use explicit error messages.” must be activated for the extension to show effect. Please look for max_wn and ExplicitError() in the parser substitutions. There’s no documentation, just a tiny example.

dl.dropbox.com/u/2691966/Explici … ssages.i7x

Regards,
Christian

Edit: Sorry, the changes to “Refers” in “Parser.i6t” were not included, that was why unknown verbs didn’t evoke an explicit error message. I’ve re-uploaded the file.

I’ve spent longer than is healthy rooting around in the parser while writing Disambiguation Control, and although I don’t have an answer to hand on this, it’s definitely doable.

The place where “nouns” get analysed is the NounDomain function; the inputted text itself is stored in the buffer array, but nowhere else really - if the player types in words the parser doesn’t know, they don’t get converted out of the buffer and into the word-array.

The problem I can see is for dealing with things like

PUT BLAH ON TABLE

and getting output of the form “I didn’t understand “blah” as a thing.” rather than “I didn’t understand “blah on table” as a thing.”, as the parser doesn’t “look ahead”.

(Actually, that’s not quite true – in the case of “put” the parser does look ahead and sort out the second noun first. But this is confusing, and it doesn’t do it generically for 2-noun verbs.)

With all of this - disambiguation and parser errors - we really could use a rewrite of the parser to a less opaque, more systematic framework better designed for giving errors and making choices.

I like this discussion better than other ones I’ve seen that focus on making the parser more newbie-friendly in vague sweeping ways. Seems like we have some reasonable, practical ideas that, while not earth-shaking, might really help authors and players fine tune their experience.

So to Jon, Christian, Zarf, and Michael: Thanks, and keep up the good work!

I have now munged through it.

This works as far as I can tell. Test with “jump asdf” or “pick me up asdf ghjk”.

Rule for printing a parser error when the latest parser error is the only understood as far as error:
	follow the alternate only-understood error rule.

The alternate only-understood error rule translates into I6 as "Alt_Only_Understood_Rule".

Include (-
[ Alt_Only_Understood_Rule  m ix addr len;
	print "I understood the command ~";
	
	for (ix=1: ix < wn-1; ix++) {
		if (ix ~= 1) print " ";
		addr = WordAddress(ix);
		len = WordLength(ix);
		while (len) {
			print (char) addr->0;
			addr++; len--;
		}
	}
	print "~ (that is, ";

	for (m=0 : m<32 : m++) pattern-->m = pattern2-->m;
	pcount = pcount2;
	PrintCommand(0);
	
	print "); but not the word";
	if (wn-1 ~= num_words) print "s";
	print " ~";
	
	for (ix=wn-1 : ix <= num_words; ix++) {
		if (ix ~= wn-1) print " ";
		addr = WordAddress(ix);
		len = WordLength(ix);
		while (len) {
			print (char) addr->0;
			addr++; len--;
		}
	}
	print "~ at the end.^";
];
-).

I started playing around with this way back when I read this thread, then stopped, then found the file again tonight, and wasn’t sure if I was going to have time to get back to it any time soon, so I thought I’d post my early experiment here for posterity.

[code]“showverb test”

Stage is a room.

Include Text Capture by Eric Eve.

After printing a parser error: now VerbToExplain is true.

VerbToExplain is a truth state that varies.

For reading a command when VerbToExplain is true:
now VerbToExplain is false;
replace the player’s command with “showverb [word number 1 in the player’s command]”.

VerbExplaining is an action out of world applying to nothing.

To verb-explain: (- ShowVerbSub(); -).

Include (-

[ ShowVerbSub address lines meta i x;
wn = 2; x = NextWordStopped();
if (x == 0 || ((x->#dict_par1) & 1) == 0)
“Try typing ~showverb~ and then the name of a verb.”;
meta = ((x->#dict_par1) & 2)/2;
i = DictionaryWordToVerbNum(x);
address = VM_CommandTableAddress(i);
lines = address->0;
address++;
print "Here are all the ways I know how to use the verb ";
!if (meta) print "meta ";
VM_PrintCommandWords_spec(i);
new_line;
if (lines == 0) “has no grammar lines.”;
for (: lines>0 : lines-- ) {
address = UnpackGrammarLine(address);
print " "; DebugGrammarLine(x); new_line;
}
ParaContent();
];
[ DebugGrammarLine vb pcount;
print " >", (address) vb, " ";
for (: line_token–>pcount ~= ENDIT_TOKEN : pcount++) {
if ((line_token–>pcount)->0 & $10) print “/”;
print “”, (DebugToken) line_token–>pcount, “”;
}
print " → to ", (DebugAction) action_to_be;
if (action_reversed) print “”;
];

[ DebugToken token;
AnalyseToken(token);
switch (found_ttype) {
ILLEGAL_TT:
print "<illegal token number ", token, “>”;
ELEMENTARY_TT:
switch (found_tdata) {
NOUN_TOKEN: print "{something} ";
HELD_TOKEN: print "{something held} ";
MULTI_TOKEN: print "{something} ";
MULTIHELD_TOKEN: print "{something held} ";
MULTIEXCEPT_TOKEN: print "{something} ";
MULTIINSIDE_TOKEN: print "{something inside another thing} ";
CREATURE_TOKEN: print "{someone} ";
SPECIAL_TOKEN: print "{special} ";
NUMBER_TOKEN: print "{a number} ";
TOPIC_TOKEN: print "{any text} ";
ENDIT_TOKEN: print "{END} ";
}
PREPOSITION_TT:
print “”, (address) found_tdata, “”;
ROUTINE_FILTER_TT:
print “noun=Routine(”, found_tdata, “)”;
ATTR_FILTER_TT:
print (DebugAttribute) found_tdata;
SCOPE_TT:
print “scope=Routine(”, found_tdata, “)”;
GPR_TT:
print “Routine(”, found_tdata, “)”;
}
];

[ VM_PrintCommandWords_spec i wd j dictlen entrylen ctr;
ctr = 0;
dictlen = #dictionary_table–>0;
entrylen = DICT_WORD_SIZE + 7;
for (j=0 : j<dictlen : j++) {
wd = #dictionary_table + WORDSIZE + entrylen*j;
if (DictionaryWordToVerbNum(wd) == i) {
print (address) wd;
if (ctr==0) print " (also ";
else print ", ";
ctr++;
}
}
print “)”;
];

-) instead of “Showverb Command” in “Tests.i6t”.

Test me with “I want to be happy/showverb take/showverb show”.
[/code]

Produces output like:

That looks super useful!

I like it, although I would want to apply some special-case turning. For example, the “I WANT TO…” case you mentioned in the other thread – that should give a hand-written explanation of inventory and why I isn’t a pronoun. Similarly, it’s not necessary to show every variant of “PUT X IN/INSIDE/INTO/THROUGH…” (which can be a lot of prepositions, particularly after I get through customizing it.)

But the basic principle is good.

Yeah, I’m not convinced myself this is the best solution, since has been repeatedly demonstrated to me in multiple context over the past few weeks, NOBODY EVER READS THE MANUAL. So I’m not sure man pages are the answer.

But it’s interesting to play with.