Unfortunately, most of the behavior for empty commands is coded into the parser in a way that can’t easily be exposed to I7. Looking at Ron’s Original Parser extension, it looks as if the code that checks for an empty command, and gives the I beg your pardon parser error if the command is empty, is part of a “For reading the command” rule, meaning that things cut off before you get to an “After reading the command” rule.
One thing you could probably do is have your behavior run from a “Rule for printing a parser error when the latest parser error is the I beg your pardon error” (haven’t double-checked the syntax there). There can be weird effects from running code that changes the game state from parser errors – I think they don’t run the advance time rules and it’s hard to end the game in the middle of one – but for the behavior you describe it shouldn’t be so bad (since you’re rejecting the command anyway). There’s also some I6 code that Victor Gijsbers (I think; might have been Juhana Leinonen) wrote for me to redirect the empty command behavior:
[spoiler][code]Section Intercepting Blank Commands
Include (- [ Keyboard a_buffer a_table nw i w w2 x1 x2;
sline1 = score; sline2 = turns;
while (true) {
! Save the start of the buffer, in case "oops" needs to restore it
for (i=0 : i<64 : i++) oops_workspace->i = a_buffer->i;
! In case of an array entry corruption that shouldn't happen, but would be
! disastrous if it did:
#Ifdef TARGET_ZCODE;
a_buffer->0 = INPUT_BUFFER_LEN;
a_table->0 = 15; ! Allow to split input into this many words
#Endif; ! TARGET_
! Print the prompt, and read in the words and dictionary addresses
PrintPrompt();
DrawStatusLine();
KeyboardPrimitive(a_buffer, a_table);
! Set nw to the number of words
#Ifdef TARGET_ZCODE; nw = a_table->1; #Ifnot; nw = a_table-->0; #Endif;
! If the line was blank, get a fresh line
!if (nw == 0) {
! @push etype; etype = BLANKLINE_PE;
! players_command = 100;
! BeginActivity(PRINTING_A_PARSER_ERROR_ACT);
! if (ForActivity(PRINTING_A_PARSER_ERROR_ACT) == false) L__M(##Miscellany,10);
! EndActivity(PRINTING_A_PARSER_ERROR_ACT);
! @pull etype;
! continue;
!}
! Unless the opening word was OOPS, return
! Conveniently, a_table-->1 is the first word on both the Z-machine and Glulx
w = a_table-->1;
if (w == OOPS1__WD or OOPS2__WD or OOPS3__WD) {
if (oops_from == 0) { L__M(##Miscellany, 14); continue; }
if (nw == 1) { L__M(##Miscellany, 15); continue; }
if (nw > 2) { L__M(##Miscellany, 16); continue; }
! So now we know: there was a previous mistake, and the player has
! attempted to correct a single word of it.
for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer2->i = a_buffer->i;
#Ifdef TARGET_ZCODE;
x1 = a_table->9; ! Start of word following "oops"
x2 = a_table->8; ! Length of word following "oops"
#Ifnot; ! TARGET_GLULX
x1 = a_table-->6; ! Start of word following "oops"
x2 = a_table-->5; ! Length of word following "oops"
#Endif; ! TARGET_
! Repair the buffer to the text that was in it before the "oops"
! was typed:
for (i=0 : i<64 : i++) a_buffer->i = oops_workspace->i;
VM_Tokenise(a_buffer,a_table);
! Work out the position in the buffer of the word to be corrected:
#Ifdef TARGET_ZCODE;
w = a_table->(4*oops_from + 1); ! Start of word to go
w2 = a_table->(4*oops_from); ! Length of word to go
#Ifnot; ! TARGET_GLULX
w = a_table-->(3*oops_from); ! Start of word to go
w2 = a_table-->(3*oops_from - 1); ! Length of word to go
#Endif; ! TARGET_
! Write spaces over the word to be corrected:
for (i=0 : i<w2 : i++) a_buffer->(i+w) = ' ';
if (w2 < x2) {
! If the replacement is longer than the original, move up...
for (i=INPUT_BUFFER_LEN-1 : i>=w+x2 : i-- )
a_buffer->i = a_buffer->(i-x2+w2);
! ...increasing buffer size accordingly.
#Ifdef TARGET_ZCODE;
a_buffer->1 = (a_buffer->1) + (x2-w2);
#Ifnot; ! TARGET_GLULX
a_buffer-->0 = (a_buffer-->0) + (x2-w2);
#Endif; ! TARGET_
}
! Write the correction in:
for (i=0 : i<x2 : i++) a_buffer->(i+w) = buffer2->(i+x1);
VM_Tokenise(a_buffer, a_table);
#Ifdef TARGET_ZCODE; nw = a_table->1; #Ifnot; nw = a_table-->0; #Endif;
return nw;
}
! Undo handling
if ((w == UNDO1__WD or UNDO2__WD or UNDO3__WD) && (nw==1)) {
Perform_Undo();
continue;
}
i = VM_Save_Undo();
#ifdef PREVENT_UNDO; undo_flag = 0; #endif;
#ifndef PREVENT_UNDO; undo_flag = 2; #endif;
if (i == -1) undo_flag = 0;
if (i == 0) undo_flag = 1;
if (i == 2) {
VM_RestoreWindowColours();
VM_Style(SUBHEADER_VMSTY);
SL_Location(); print "^";
! print (name) location, "^";
VM_Style(NORMAL_VMSTY);
L__M(##Miscellany, 13);
continue;
}
return nw;
}
]; -) instead of “Reading the Command” in “Parser.i6t”.
After reading a command (this is the blank line = xyzzy rule):
let T be indexed text;
now T is the player’s command;
if T is “”, change the text of the player’s command to “xyzzy”.[/code][/spoiler]
which will let you write an “after reading the command” rule that works when the player’s command is “”. (But when the new I7 comes out, you will probably need a new parser hack.) I think this allows all blank commands through; if you do this and you only want to allow blank commands some of the time, then you would have to find a way to have this rule invoke the I beg your pardon error when naming mode wasn’t true.
As for your other question, I fear there is also no easy way to do this. The only thing I can think of to do is repeat through the table, checking whether each row fits your specification, and when you find that row choose it and break. (But someone else might have a better idea.)