How do I force the parser to only accept explicit commands?

I move a jewel into the current room.

The player types x alone and the parser infers the jewel:

> x
(the jewel)
...

The player shouldn’t know about the jewel but the parser has provided a major spoiler.

I assume the best way to prevent this would be to insist the parser on takes explicit command. x jewel is fine. How do I ensure that happens?

Is there a better way of preventing the inference of the jewel before the player knows about it?

I assume the jewel is meant to be hidden from the player in some way? How? (It’s in a box, it’s invisible, etc…)

It’s in the river which is a transparent container. When the scene begins, the player is only aware of something catching the sunlight. The player should only become aware of the jewel when re-examining the river.

There is no perfectly reliable way to do this.

It is better to set up your game so that the object is off-stage until the player is allowed to interact with it.

1 Like

This is, unfortunately, one of the limitations of Inform 7 that’s very hard to get around. The code responsible for this is approaching 30 years old now and has accumulated a lot of complications and edge cases in that time, and then those complications and edge cases became load-bearing, and now there’s no easy way to disable this feature without a lot of hacking in the parser.

Here’s an example of bringing something on-stage at the time you want the player to be able to interact with it.

A jewel is a thing. The jewel is nowhere. The jewel can be glimpsed.

Field is a room. The description of Field is "[A river] runs through the field[If the jewel is not glimpsed]. Something in the water catches the sunlight[otherwise if something is in the river]. In the river you can see [a list of things in the river][end if]."

The river is a transparent container in Field.

Instead of examining the river for the first time:
	say "You think you see a jewel in the river.";
	now the jewel is glimpsed;
	now the jewel is in the river.
1 Like

What about concealed possessions:

lab is a room.
the river is in lab.
the river is scenery.
the river is a transparent container.

the jewel is in river.

rule for deciding the concealed possessions of the river:
	if we have examined the river, decide no;
	otherwise yes;
2 Likes

There sure is!

Like this!

"Test" by Climbingstars

Understand "Examine" as examining.

Rule for supplying a missing noun while examining (this is the custom message rule): say "You must supply a noun.".

The Darkness is A Room. The description of the darkness is "[text of the basic visibility rule response (A)]". The jewel is in the darkness.

Test me with "x".

The custom message rule is technically optional and you would get a “You must supply a noun“ message without it, but if you wanted to customise that message, you could do it like this.

Hope this helps!

I would not recommend that solution. If the player types LOOK AT, this solution won’t catch it. Sure, you could add another rule to understand “look at” as examining, but there’s one more synonym I haven’t mentioned—do you know off the top of your head what it is?

And that’s just for one action. There’s also TAKE, and WEAR, and…

That would be understand “read [something]” as examining, although I didn’t remember it off the top of my head and did look it up, even though I’ve dealt with this one before. You are right that with the specificity of the example code, there would many cases that still give an implicit response. However, this is more of a proof of concept more than anything. You can brute force the understand grammar lines so that it covers all the built in grammar lines, like this.

"Test" by Climbingstars

Understand "get" as taking.
Understand "eat" as eating.
Understand "cut" as cutting.
Understand "pull" as pulling.
Understand "buy" as buying.
Understand "take" as taking.
Understand "kiss" as kissing.
Understand "rub" as rubbing.
Understand "look" as looking.
Understand "turn" as turning.
Understand "burn" as burning.
Understand "close" as closing.
Understand "taste" as tasting.
Understand "push" as pushing.
Understand "open" as opening.
Understand "wear" as wearing.
Understand "drink" as drinking.
Understand "drop" as dropping.
Understand "pick up" as taking.
Understand "touch" as touching.
Understand "shed" as taking off.
Understand "read" as examining.
Understand "close up" as closing.
Understand "attack" as attacking.
Understand "hear" as listening to.
Understand "search" as searching.
Understand "remove" as taking off.
Understand "take off" as taking off.
Understand "stand on" as entering.
Understand "look at" as examining.
Understand "put down" as dropping.
Understand "squeeze" as squeezing.
Understand "examine" as examining.
Understand "listen to" as listening to.
Understand "turn on" as switching on.
Understand "turn off" as switching off.
Understand "sit on top of" as entering.
Understand "close off" as switching off.
Understand "switch off" as switching off.
Understand "get off/down" as getting off.
Understand "sit on/in/inside" as entering.
Understand "put" or "put on" as wearing.
Understand "look under" as looking under.
Understand "put [things]" as putting it on.
Understand "get in/into/on/onto" as entering.
Understand "swing" or "swing on" as swinging.
Understand "climb" or "climb up/over" as climbing.
Understand "go into/in/inside/through" as entering.
Understand "tie" or "tie [something]" as tying it to.
Understand "switch" or "switch on" as switching on.
Understand "look inside/in/into/through" as searching.
Understand "set" or "set [something]" as setting it to.
Understand "tell" or "tell [someone]" as telling it about.
Understand "ask" or "ask [someone]" as asking it about.
Understand "lock" or "lock [something]" as locking it with.
Understand "insert" or "insert [things]" as inserting it into.
Understand "unlock" or "unlock [something]" as unlocking it with.
Understand "consult" or "consult [something]" as consulting it about.
Understand "give" or "give [something preferably held]" as giving it to.
Understand "show" or "show [something preferably held]" as showing it to.

The Darkness is A Room. The description of the darkness is "[text of the basic visibility rule response (A)]". The jewel is in the darkness.

However, anyone creating a new action would need to remember to add the extra grammar lines for that action. You can also stop this behaviour entirely at the parser level, like this.

"Test" by Climbingstars

Include (-

[ Parser__parse
	syntax line num_lines line_address i j k token l m inferred_go;
	cobj_flag = 0;
	parser_results-->ACTION_PRES = 0;
	parser_results-->NO_INPS_PRES = 0;
	parser_results-->INP1_PRES = 0;
	parser_results-->INP2_PRES = 0;
	meta = false;

	if (held_back_mode) {
		held_back_mode = false; wn = hb_wn;
		if (verb_wordnum > 0) i = WordAddress(verb_wordnum); else i = WordAddress(1);
		j = WordAddress(wn);
		if (i<=j) for (: i<j : i++) i->0 = ' ';
		i = NextWord();
		if (i == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) {
			! Delete the words "then again" from the again buffer,
			! in which we have just realised that it must occur:
			! prevents an infinite loop on "i. again"

			i = WordAddress(wn-2)-buffer;
			if (wn > num_words) j = INPUT_BUFFER_LEN-1;
			else j = WordAddress(wn)-buffer;
			for (: i<j : i++) buffer3->i = ' ';
		}

		VM_Tokenise(buffer, parse);
		jump ReParse;
	}

	.ReType;

	cobj_flag = 0;
	actors_location = ScopeCeiling(player);
	BeginActivity(READING_A_COMMAND_ACT); if (ForActivity(READING_A_COMMAND_ACT)==false) {
		Keyboard(buffer,parse);
		num_words = WordCount(); players_command = 100 + num_words;
	} if (EndActivity(READING_A_COMMAND_ACT)) jump ReType;

	.ReParse;

	parser_inflection = name;

	! Initially assume the command is aimed at the player, and the verb
	! is the first word

	num_words = WordCount(); players_command = 100 + num_words;
	wn = 1; inferred_go = false;

	LanguageToInformese();
	! Re-tokenise:
	VM_Tokenise(buffer,parse);

	num_words = WordCount(); players_command = 100 + num_words; token_filter = 0;
	allow_plurals = true; ResetDescriptors();

	k=0;
	#Ifdef DEBUG;
	if (parser_trace >= 2) {
		print "[ ";
		for (i=0 : i<num_words : i++) {

			#Ifdef TARGET_ZCODE;
			j = parse-->(i*2 + 1);
			#Ifnot; ! TARGET_GLULX
			j = parse-->(i*3 + 1);
			#Endif; ! TARGET_
			k = WordAddress(i+1);
			l = WordLength(i+1);
			print "~"; for (m=0 : m<l : m++) print (char) k->m; print "~ ";

			if (j == 0) print "?";
			else {
				#Ifdef TARGET_ZCODE;
				if (UnsignedCompare(j, HDR_DICTIONARY-->0) >= 0 &&
					UnsignedCompare(j, HDR_HIGHMEMORY-->0) < 0)
					print (address) j;
				else print j;
				#Ifnot; ! TARGET_GLULX
				if (j->0 == $60) print (address) j;
				else print j;
				#Endif; ! TARGET_
			}
			if (i ~= num_words-1) print " / ";
		}
		print " ]^";
	}
	#Endif; ! DEBUG
	verb_wordnum = 1;
	actor = player;
	actors_location = ScopeCeiling(player);
	usual_grammar_after = 0;

	.AlmostReParse;

	scope_token = 0;
	action_to_be = NULL;

	! Begin from what we currently think is the verb word

	.BeginCommand;
	wn = verb_wordnum;
	verb_word = NextWordStopped();
	! If there's no input here, we must have something like "person,".

	if (verb_word == -1) {
		best_etype = STUCK_PE; jump GiveError;
	}
	if (verb_word == comma_word) {
		best_etype = COMMABEGIN_PE; jump GiveError;
	}

	! Now try for "again" or "g", which are special cases: don't allow "again" if nothing
	! has previously been typed; simply copy the previous text across

	if (verb_word == AGAIN2__WD or AGAIN3__WD) verb_word = AGAIN1__WD;
	if (verb_word == AGAIN1__WD) {
		if (actor ~= player) {
			best_etype = ANIMAAGAIN_PE;
			jump GiveError;
		}
		#Ifdef TARGET_ZCODE;
		if (buffer3->1 == 0) {
			PARSER_COMMAND_INTERNAL_RM('D'); new_line;
			jump ReType;
		}
		#Ifnot; ! TARGET_GLULX
		if (buffer3-->0 == 0) {
			PARSER_COMMAND_INTERNAL_RM('D'); new_line;
			jump ReType;
		}
		#Endif; ! TARGET_
		for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer->i = buffer3->i;
		VM_Tokenise(buffer,parse);
		num_words = WordCount(); players_command = 100 + num_words;
		jump ReParse;
	}

	! Save the present input in case of an "again" next time

	if (verb_word ~= AGAIN1__WD)
		for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer3->i = buffer->i;

	if (usual_grammar_after == 0) {
		j = verb_wordnum;
		i = 0; ! force parsing without grammar property; see DM4 p. 140
		#Ifdef DEBUG;
		if (parser_trace >= 2 && i ~= 0 or NULL)
			print " [Grammar property returned ", i, "]^";
		#Endif; ! DEBUG

		if ((i ~= 0 or 1) && (VM_InvalidDictionaryAddress(i))) {
			usual_grammar_after = verb_wordnum; i=-i;
		}

		if (i == 1) {
			parser_results-->ACTION_PRES = action;
			parser_results-->NO_INPS_PRES = 0;
			parser_results-->INP1_PRES = noun;
			parser_results-->INP2_PRES = second;
			if (noun) parser_results-->NO_INPS_PRES = 1;
			if (second) parser_results-->NO_INPS_PRES = 2;
			rtrue;
		}
		if (i ~= 0) { verb_word = i; wn--; verb_wordnum--; }
		else { wn = verb_wordnum; verb_word = NextWord(); }
	}
	else usual_grammar_after = 0;

	if (verb_word == 0) {
		i = wn; verb_word = LanguageIsVerb(buffer, parse, verb_wordnum);
		wn = i;
	}

	! If the first word is not listed as a verb, it must be a direction
	! or the name of someone to talk to

	if (verb_word == 0 || ((verb_word->#dict_par1) & 1) == 0) {
		! So is the first word an object contained in the special object "compass"
		! (i.e., a direction)?  This needs use of NounDomain, a routine which
		! does the object matching, returning the object number, or 0 if none found,
		! or REPARSE_CODE if it has restructured the parse table so the whole parse
		! must be begun again...

		wn = verb_wordnum; indef_mode = false; token_filter = 0; parameters = 0;
		@push actor; @push action; @push action_to_be;
		actor = player; meta = false; action = ##Go; action_to_be = ##Go;
		l = NounDomain(Compass, 0, 0);
		@pull action_to_be; @pull action; @pull actor;
		if (l == REPARSE_CODE) jump ReParse;

		! If it is a direction, send back the results:
		! action=GoSub, no of arguments=1, argument 1=the direction.

		if ((l~=0) && (l ofclass K3_direction)) {
			parser_results-->ACTION_PRES = ##Go;
			parser_results-->NO_INPS_PRES = 1;
			parser_results-->INP1_PRES = l;
			inferred_go = true;
			jump LookForMore;
		}

	} ! end of first-word-not-a-verb

	! Only check for a comma (a "someone, do something" command) if we are
	! not already in the middle of one.  (This simplification stops us from
	! worrying about "robot, wizard, you are an idiot", telling the robot to
	! tell the wizard that she is an idiot.)

	if (actor == player) {
		for (j=2 : j<=num_words : j++) {
			i=NextWord();
			if (i == comma_word) jump Conversation;
		}
	}
	jump NotConversation;

	! NextWord nudges the word number wn on by one each time, so we've now
	! advanced past a comma.  (A comma is a word all on its own in the table.)

	.Conversation;
	j = wn - 1;

	! Use NounDomain (in the context of "animate creature") to see if the
	! words make sense as the name of someone held or nearby

	wn = 1; lookahead = HELD_TOKEN;
	scope_reason = TALKING_REASON;
	l = NounDomain(player, actors_location, CREATURE_TOKEN);
	scope_reason = PARSING_REASON;
	if (l == REPARSE_CODE) jump ReParse;
	if (l == 0) {
		if (verb_word && ((verb_word->#dict_par1) & 1)) jump NotConversation;
		best_etype = MISSINGPERSON_PE; jump GiveError;
	}

	.Conversation2;

	! The object addressed must at least be "talkable" if not actually "animate"
	! (the distinction allows, for instance, a microphone to be spoken to,
	! without the parser thinking that the microphone is human).

	if (l hasnt animate && l hasnt talkable) {
		best_etype = ANIMALISTEN_PE; noun = l; jump GiveError;
	}

	! Check that there aren't any mystery words between the end of the person's
	! name and the comma (eg, throw out "dwarf sdfgsdgs, go north").

	if (wn ~= j) {
		if (verb_word && ((verb_word->#dict_par1) & 1)) jump NotConversation;
		best_etype = TOTALK_PE; jump GiveError;
	}

	! The player has now successfully named someone.  Adjust "him", "her", "it":

	PronounNotice(l);

	! Set the global variable "actor", adjust the number of the first word,
	! and begin parsing again from there.

	verb_wordnum = j + 1;

	! Stop things like "me, again":

	if (l == player) {
		wn = verb_wordnum;
		if (NextWordStopped() == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) {
			best_etype = ANIMAAGAIN_PE;
			jump GiveError;
		}
	}

	actor = l;
	actors_location = ScopeCeiling(l);
	#Ifdef DEBUG;
	if (parser_trace >= 1)
		print "[Actor is ", (the) actor, " in ", (name) actors_location, "]^";
	#Endif; ! DEBUG
	jump BeginCommand;

	.NotConversation;
	if (verb_word == 0 || ((verb_word->#dict_par1) & 1) == 0) {
		verb_word = UnknownVerb(verb_word);
		if (verb_word ~= 0) jump VerbAccepted;
		best_etype = VERB_PE;
		jump GiveError;
	}
	.VerbAccepted;

	! We now definitely have a verb, not a direction, whether we got here by the
	! "take ..." or "person, take ..." method.  Get the meta flag for this verb:

	meta = ((verb_word->#dict_par1) & 2)/2;

	! You can't order other people to "full score" for you, and so on...

	if (meta == 1 && actor ~= player) {
		best_etype = VERB_PE;
		meta = 0;
		jump GiveError;
	}

	! Now let i be the corresponding verb number...

	i = DictionaryWordToVerbNum(verb_word);

	! ...then look up the i-th entry in the verb table, whose address is at word
	! 7 in the Z-machine (in the header), so as to get the address of the syntax
	! table for the given verb...

	syntax = VM_CommandTableAddress(i);

	! ...and then see how many lines (ie, different patterns corresponding to the
	! same verb) are stored in the parse table...

	num_lines = (syntax->0);

	! ...and now go through them all, one by one.
	! To prevent pronoun_word 0 being misunderstood,

	pronoun_word = NULL; pronoun_obj = NULL;

	#Ifdef DEBUG;
	if (parser_trace >= 1)
		print "[Parsing for the verb '", (address) verb_word, "' (", num_lines, " lines)]^";
	#Endif; ! DEBUG

	best_etype = STUCK_PE; nextbest_etype = STUCK_PE;
	multiflag = false;

	! "best_etype" is the current failure-to-match error - it is by default
	! the least informative one, "don't understand that sentence".
	! "nextbest_etype" remembers the best alternative to having to ask a
	! scope token for an error message (i.e., the best not counting ASKSCOPE_PE).
	! multiflag is used here to prevent inappropriate MULTI_PE errors
	! in addition to its unrelated duties passing information to action routines

	line_address = syntax + 1;

	for (line=0 : line<num_lines : line++) {

		! Unpack the syntax line from Inform format into three arrays; ensure that
		! the sequence of tokens ends in an ENDIT_TOKEN.

		line_address = UnpackGrammarLine(line_address);

		#Ifdef DEBUG;
		if (parser_trace >= 1) {
			if (parser_trace >= 2) new_line;
			print "[line ", line; DebugGrammarLine();
			print "]^";
		}
		#Endif; ! DEBUG

		! We aren't in "not holding" or inferring modes, and haven't entered
		! any parameters on the line yet, or any special numbers; the multiple
		! object is still empty.

		inferfrom = 0;
		parameters = 0;
		nsns = 0; special_word = 0;
		multiple_object-->0 = 0;
		multi_context = 0;
		etype = STUCK_PE; multi_had = 0;

		! Put the word marker back to just after the verb

		wn = verb_wordnum+1;
		advance_warning = -1; indef_mode = false;
		for (i=0,m=false,pcount=0 : line_token-->pcount ~= ENDIT_TOKEN : pcount++) {
			scope_token = 0;

			if (line_ttype-->pcount ~= PREPOSITION_TT) i++;

			if (line_ttype-->pcount == ELEMENTARY_TT) {
				if (line_tdata-->pcount == MULTI_TOKEN) m = true;
				if (line_tdata-->pcount == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN  && i == 1) {
					! First non-preposition is "multiexcept" or
					! "multiinside", so look ahead.

					#Ifdef DEBUG;
					if (parser_trace >= 2) print " [Trying look-ahead]^";
					#Endif; ! DEBUG

					! We need this to be followed by 1 or more prepositions.

					pcount++;
					if (line_ttype-->pcount == PREPOSITION_TT) {
						! skip ahead to a preposition word in the input
						do {
							l = NextWord();
						} until ((wn > num_words) ||
								(l && (l->#dict_par1) & 8 ~= 0));

						if (wn > num_words) {
							#Ifdef DEBUG;
							if (parser_trace >= 2)
								print " [Look-ahead aborted: prepositions missing]^";
							#Endif;
							jump EmptyLine;
						}

						do {
							if (PrepositionChain(l, pcount) ~= -1) {
								! advance past the chain
								if ((line_token-->pcount)->0 & $20 ~= 0) {
									pcount++;
									while ((line_token-->pcount ~= ENDIT_TOKEN) &&
										((line_token-->pcount)->0 & $10 ~= 0))
										pcount++;
								}
								else {
									pcount++;
								}
							}
							else {
								! try to find another preposition word
								do {
									l = NextWord();
								} until ((wn >= num_words) ||
										(l && (l->#dict_par1) & 8 ~= 0));

								if (l && (l->#dict_par1) & 8) continue;

								! lookahead failed
								#Ifdef DEBUG;
								if (parser_trace >= 2)
									print " [Look-ahead aborted: prepositions don't match]^";
								#endif;
								jump LineFailed;
							}
							if (wn <= num_words) l = NextWord();
						} until (line_ttype-->pcount ~= PREPOSITION_TT);

						.EmptyLine;
						! put back the non-preposition we just read
						wn--;

						if ((line_ttype-->pcount == ELEMENTARY_TT) &&
							(line_tdata-->pcount == NOUN_TOKEN)) {
							l = Descriptors();  ! skip past THE etc
							if (l~=0) etype=l;  ! don't allow multiple objects
							k = parser_results-->INP1_PRES; @push k; @push parameters;
							parameters = 1; parser_results-->INP1_PRES = 0;
							l = NounDomain(actors_location, actor, NOUN_TOKEN, true);
							@pull parameters; @pull k; parser_results-->INP1_PRES = k;
							#Ifdef DEBUG;
							if (parser_trace >= 2) {
								print " [Advanced to ~noun~ token: ";
								if (l == REPARSE_CODE) print "re-parse request]^";
								else {
									if (l == 1) print "but multiple found]^";
									if (l == 0) print "error ", etype, "]^";
									if (l >= 2) print (the) l, "]^";
								}
							}
							#Endif; ! DEBUG
							if (l == REPARSE_CODE) jump ReParse;
							if (l >= 2) advance_warning = l;
						}
					}
					break;
				}
			}
		}

		! Slightly different line-parsing rules will apply to "take multi", to
		! prevent "take all" behaving correctly but misleadingly when there's
		! nothing to take.

		take_all_rule = 0;
		if (m && params_wanted == 1 && action_to_be == ##Take)
			take_all_rule = 1;

		! And now start again, properly, forearmed or not as the case may be.
		! As a precaution, we clear all the variables again (they may have been
		! disturbed by the call to NounDomain, which may have called outside
		! code, which may have done anything!).

		inferfrom = 0;
		parameters = 0;
		nsns = 0; special_word = 0;
		multiple_object-->0 = 0;
		etype = STUCK_PE; multi_had = 1;
		wn = verb_wordnum+1;
		m = true;
		for (pcount=1 : : pcount++)
			if (line_token-->(pcount-1) == ENDIT_TOKEN) {
				if (pcount >= 2) {
					while ((((line_token-->(pcount-2))->0) & $10) ~= 0) pcount--;
					if (line_ttype-->(pcount-2) == PREPOSITION_TT) {
						l = -1;
						while (true) {
							m = NextWordStopped();
							if (m == -1) break;
							l = m;
						}
						if (PrepositionChain(l, pcount-2) == -1) {
							m = false;
							#Ifdef DEBUG;
							if (parser_trace >= 2)
								print "[line rejected for not ending with correct preposition]^";
							#Endif; ! DEBUG
						} else m = true;
					}
				}
				break;
			}
		wn = verb_wordnum+1;

		if (m) for (pcount=1 : : pcount++) {
			pattern-->pcount = PATTERN_NULL; scope_token = 0;

			token = line_token-->(pcount-1);
			lookahead = line_token-->pcount;

			#Ifdef DEBUG;
			if (parser_trace >= 2)
				print " [line ", line, " token ", pcount, " word ", wn, " : ", (DebugToken) token,
				"]^";
			#Endif; ! DEBUG

			if (token ~= ENDIT_TOKEN && wn <= num_words) {
				scope_reason = PARSING_REASON;

				l = ParseToken(line_ttype-->(pcount-1), line_tdata-->(pcount-1), pcount-1, token);
				while ((l >= GPR_NOUN) && (l < -1)) l = ParseToken(ELEMENTARY_TT, l + 256);
				scope_reason = PARSING_REASON;

				if (l == GPR_PREPOSITION) {
					if (line_ttype-->(pcount-1)~=PREPOSITION_TT && (line_ttype-->(pcount-1)~=ELEMENTARY_TT ||
						line_tdata-->(pcount-1)~=TOPIC_TOKEN)) params_wanted--;
					l = true;
				}
				else
					if (l < 0) l = false;
					else
						if (l ~= GPR_REPARSE) {
							if (l == GPR_NUMBER) {
								if (nsns == 0) special_number1 = parsed_number;
								else special_number2 = parsed_number;
								nsns++; l = 1;
							}
							if (l == GPR_MULTIPLE) l = 0;
							parser_results-->(parameters+INP1_PRES) = l;
							parameters++;
							pattern-->pcount = l;
							l = true;
						}

				#Ifdef DEBUG;
				if (parser_trace >= 3) {
					print "  [token resulted in ";
					if (l == REPARSE_CODE) print "re-parse request]^";
					if (l == 0) print "failure with error type ", etype, "]^";
					if (l == 1) print "success]^";
				}
				#Endif; ! DEBUG

				if (l == REPARSE_CODE) jump ReParse;
				if (l == false) break;
			}
			else {

				! If the player has entered enough already but there's still
				! text to wade through: store the pattern away so as to be able to produce
				! a decent error message if this turns out to be the best we ever manage,
				! and in the mean time give up on this line

				! However, if the superfluous text begins with a comma or "then" then
				! take that to be the start of another instruction

				if (wn <= num_words) {
					l = NextWord();
					if (l == THEN1__WD or THEN2__WD or THEN3__WD or comma_word) {
						held_back_mode = true; hb_wn = wn-1;
					}
					else {
						for (m=0 : m<32 : m++) pattern2-->m = pattern-->m;
						pcount2 = pcount;
						etype = UPTO_PE;
						break;
					}
				}

				! Now, we may need to revise the multiple object because of the single one
				! we now know (but didn't when the list was drawn up).

				if (parameters >= 1) {
					if (parser_results-->INP1_PRES == 0) {
						l = ReviseMulti(parser_results-->INP2_PRES);
						if (l ~= 0) { etype = l; parser_results-->ACTION_PRES = action_to_be; break; }
					}
				}
				if (parameters >= 2) {
					if (parser_results-->INP2_PRES == 0) {
						l = ReviseMulti(parser_results-->INP1_PRES);
						if (l ~= 0) { etype = l; break; }
					}
					else {
						k = parser_results-->INP1_PRES; l = parser_results-->INP2_PRES;
						if (k && l) {
							if ((multi_context==MULTIEXCEPT_TOKEN && k == l) ||
								((multi_context==MULTIINSIDE_TOKEN && k notin l && l notin k))) {
								best_etype = NOTHING_PE;
								parser_results-->ACTION_PRES = action_to_be; jump GiveError;
							}
						}
					}
				}

				! To trap the case of "take all" inferring only "yourself" when absolutely
				! nothing else is in the vicinity...

				if (take_all_rule == 2 && parser_results-->INP1_PRES == actor) {
					best_etype = NOTHING_PE;
					jump GiveError;
				}

				if (multi_had > 1) {
					best_etype = TOOFEW_PE;
					jump GiveError;
				}

				#Ifdef DEBUG;
				if (parser_trace >= 1) print "[Line successfully parsed]^";
				#Endif; ! DEBUG

				! The line has successfully matched the text.  Declare the input error-free...

				oops_from = 0;

				! ...explain any inferences made (using the pattern)...

				if (inferfrom ~= 0) {
					PrintInferredCommand(inferfrom);
					ClearParagraphing(20);
				}

				! ...copy the action number, and the number of parameters...

				parser_results-->ACTION_PRES = action_to_be;
				parser_results-->NO_INPS_PRES = parameters;

				! ...reverse first and second parameters if need be...

				if (action_reversed && parameters == 2) {
					i = parser_results-->INP1_PRES;
					parser_results-->INP1_PRES = parser_results-->INP2_PRES;
					parser_results-->INP2_PRES = i;
					if (nsns == 2) {
						i = special_number1; special_number1 = special_number2;
						special_number2 = i;
					}
				}

				! ...and to reset "it"-style objects to the first of these parameters, if
				! there is one (and it really is an object)...

				if (parameters > 0 && parser_results-->INP1_PRES >= 2)
					PronounNotice(parser_results-->INP1_PRES);

				! ...and return from the parser altogether, having successfully matched
				! a line.

				if (held_back_mode) {
					wn=hb_wn;
					jump LookForMore;
				}
				rtrue;

			} ! end of if(token ~= ENDIT_TOKEN) else
		} ! end of for(pcount++)

		.LineFailed;
		! The line has failed to match.
		! We continue the outer "for" loop, trying the next line in the grammar.

		if (etype > best_etype) best_etype = etype;
		if (etype ~= ASKSCOPE_PE && etype > nextbest_etype) nextbest_etype = etype;

		! ...unless the line was something like "take all" which failed because
		! nothing matched the "all", in which case we stop and give an error now.

		if (take_all_rule == 2 && etype==NOTHING_PE) break;

	} ! end of for(line++)

	! The grammar is exhausted: every line has failed to match.
	.GiveError;

	etype = best_etype;
	if (actor ~= player) {
		if (usual_grammar_after ~= 0) {
			verb_wordnum = usual_grammar_after;
			jump AlmostReParse;
		}
		m = wn; ! Save wn so extension authors can parse command errors if they want to
		wn = 1;
		while ((wn <= num_words) && (NextWord() ~= comma_word)) ;
		parser_results-->ACTION_PRES = ##Answer;
		parser_results-->NO_INPS_PRES = 2;
		parser_results-->INP1_PRES = actor;
		parser_results-->INP2_PRES = 1; special_number1 = special_word;
		actor = player;
		consult_from = wn; consult_words = num_words-consult_from+1;
		wn = m; ! Restore wn so extension authors can parse command errors if they want to
		rtrue;
	}
	! If the player was the actor (eg, in "take dfghh") the error must be printed,
	! and fresh input called for.  In three cases the oops word must be jiggled.

	if ((etype ofclass Routine) || (etype ofclass String)) {
		if (ParserError(etype) ~= 0) jump ReType;
	}
	else {
		if (verb_wordnum == 0 && etype == CANTSEE_PE) etype = VERB_PE;
		players_command = 100 + WordCount(); ! The snippet variable "player's command"
		BeginActivity(PRINTING_A_PARSER_ERROR_ACT);
		if (ForActivity(PRINTING_A_PARSER_ERROR_ACT)) jump SkipParserError;
	}
	pronoun_word = pronoun__word; pronoun_obj = pronoun__obj;

	if (etype == STUCK_PE) { PARSER_ERROR_INTERNAL_RM('A'); new_line; oops_from = 1; }
	if (etype == UPTO_PE) {
		for (m=0 : m<32 : m++) pattern-->m = pattern2-->m;
		pcount = pcount2;
		if (inferred_go) {
			PARSER_ERROR_INTERNAL_RM('C');
			print (name) parser_results-->INP1_PRES;
		}
		else {
			PARSER_ERROR_INTERNAL_RM('B');
			PrintCommand(0);
		}
		print ".^";
	}
	if (etype == NUMBER_PE) { PARSER_ERROR_INTERNAL_RM('D'); new_line; }
	if (etype == CANTSEE_PE) { PARSER_ERROR_INTERNAL_RM('E'); new_line; oops_from=saved_oops; }
	if (etype == TOOLIT_PE) { PARSER_ERROR_INTERNAL_RM('F'); new_line; }
	if (etype == NOTHELD_PE) { PARSER_ERROR_INTERNAL_RM('G'); new_line; oops_from=saved_oops; }
	if (etype == MULTI_PE) { PARSER_ERROR_INTERNAL_RM('H'); new_line; }
	if (etype == MMULTI_PE) { PARSER_ERROR_INTERNAL_RM('I'); new_line; }
	if (etype == VAGUE_PE) { PARSER_ERROR_INTERNAL_RM('J'); new_line; }
	if (etype == ITGONE_PE) {
		if (pronoun_obj == NULL) { PARSER_ERROR_INTERNAL_RM('J'); new_line; }
		else { PARSER_ERROR_INTERNAL_RM('K', noun); new_line; }
	}
	if (etype == EXCEPT_PE) { PARSER_ERROR_INTERNAL_RM('L'); new_line; }
	if (etype == ANIMA_PE) { PARSER_ERROR_INTERNAL_RM('M'); new_line; }
	if (etype == VERB_PE) { PARSER_ERROR_INTERNAL_RM('N'); new_line; }
	if (etype == SCENERY_PE) { PARSER_ERROR_INTERNAL_RM('O'); new_line; }
	if (etype == JUNKAFTER_PE) { PARSER_ERROR_INTERNAL_RM('P'); new_line; }
	if (etype == TOOFEW_PE) { PARSER_ERROR_INTERNAL_RM('Q', multi_had); new_line; }
	if (etype == NOTHING_PE) {
		if (parser_results-->ACTION_PRES == ##Remove &&
			parser_results-->INP2_PRES ofclass Object) {
			noun = parser_results-->INP2_PRES; ! ensure valid for messages
			if (noun has animate) { PARSER_N_ERROR_INTERNAL_RM('C', noun); new_line; }
			else if (noun hasnt container or supporter) { PARSER_N_ERROR_INTERNAL_RM('D', noun); new_line; }
			else if (noun has container && noun hasnt open)  { PARSER_N_ERROR_INTERNAL_RM('E', noun); new_line; }
			else if (children(noun)==0) { PARSER_N_ERROR_INTERNAL_RM('F', noun); new_line; }
			else parser_results-->ACTION_PRES = 0;
		}
		if (parser_results-->ACTION_PRES ~= ##Remove) {
			if (multi_wanted==100) { PARSER_N_ERROR_INTERNAL_RM('A'); new_line; }
			else { PARSER_N_ERROR_INTERNAL_RM('B'); new_line; }
		}
	}
	if (etype == NOTINCONTEXT_PE) { PARSER_ERROR_INTERNAL_RM('R'); new_line; }
	if (etype == ANIMAAGAIN_PE) { PARSER_ERROR_INTERNAL_RM('S'); new_line; }
	if (etype == COMMABEGIN_PE) { PARSER_ERROR_INTERNAL_RM('T'); new_line; }
	if (etype == MISSINGPERSON_PE) { PARSER_ERROR_INTERNAL_RM('U'); new_line; }
	if (etype == ANIMALISTEN_PE) { PARSER_ERROR_INTERNAL_RM('V', noun); new_line; }
	if (etype == TOTALK_PE) { PARSER_ERROR_INTERNAL_RM('W'); new_line; }
	if (etype == ASKSCOPE_PE) {
		scope_stage = 3;
		if (scope_error() == -1) {
			best_etype = nextbest_etype;
			if (~~((etype ofclass Routine) || (etype ofclass String)))
				EndActivity(PRINTING_A_PARSER_ERROR_ACT);
			jump GiveError;
		}
	}

	.SkipParserError;
	if ((etype ofclass Routine) || (etype ofclass String)) jump ReType;
	say__p = 1;
	EndActivity(PRINTING_A_PARSER_ERROR_ACT);
	! And go (almost) right back to square one...

	jump ReType;

	! ...being careful not to go all the way back, to avoid infinite repetition
	! of a deferred command causing an error.
	! At this point, the return value is all prepared, and we are only looking
	! to see if there is a "then" followed by subsequent instruction(s).

	.LookForMore;

	if (wn > num_words) rtrue;

	i = NextWord();
	if (i == THEN1__WD or THEN2__WD or THEN3__WD or comma_word) {
		if (wn > num_words) {
			held_back_mode = false;
			return;
		}
		hb_wn = wn;
		held_back_mode = true;
		return;
	}
	best_etype = UPTO_PE;
	jump GiveError;
]; ! end of Parser__parse

-) replacing "Parser__parse".

The Darkness is A Room. The description of the darkness is "[text of the basic visibility rule response (A)]". The jewel is in the darkness.

Although, most users will most likely go with the former rather than the latter due to it being I7 rather than I6.