Original Parser

version 1 by Ron Newcomb

  • Home page
  • Beginning
  • Previous
  • Next



  • Chapter - Reading the Command

    [The |Keyboard| routine actually receives the player's words, putting the
    words in |a_buffer| and their dictionary addresses in |a_table|. It is
    assumed that the table is the same one on each (standard) call. Much
    of the code handles the OOPS and UNDO commands, which are not actions and
    do not pass through the rest of the parser. The undo state is saved --
    it is essentially an internal saved game, in the VM interpreter's memory
    rather than in an external file -- and note that this is therefore also
    where execution picks up if an UNDO has been typed. Since UNDO recreates
    the former machine state perfectly, it might seem impossible to tell that
    an UNDO had occurred, but in fact the VM passes information back in the
    form of a return code from the relevant instruction, and this allows us
    to detect an undo. (We deal with it by printing the current location and
    asking another command.)

    |Keyboard| can also be used by miscellaneous routines in the game to ask
    yes/no questions and the like, without invoking the rest of the parser.

    The return value is the number of words typed.]


    [ This is usually called from the default For Reading A Command rule, but can also be called in response to Which Did You Mean and What/Whom Did You Want To Take. ]
    To decide what number is the number of words TYPED IN AT THE KEYBOARD into (input array - a 1-based index based rulebook producing ZSCII letters) and (parsed array - a 1-based index based rulebook producing structs) (this is Keyboard):
        now the saved score is the score;
        now the saved turn count is the turn count;
        repeat forever:
            copy 64 letters from the input array to the previous turn's input;
            draw the command prompt;
            draw the status line;
            READ A COMMAND into the input array and PARSE it into the parsed array;
            if the word count is zero:
                save the latest parser error;
                now the latest parser error is the I beg your pardon error;
                carry out THE PRINTING A PARSER ERROR ACTIVITY; [ This activity is called again below. ]
    [ begin THE PRINTING A PARSER ERROR ACTIVITY; [ This activity is called again below. ]
                if handling THE PRINTING A PARSER ERROR ACTIVITY:
                    issue the 10th response "I beg your pardon?";
                end THE PRINTING A PARSER ERROR ACTIVITY;]
                restore the latest parser error;
            otherwise if the 1st word of the parsed array is 'OOPS':
                [ CHECK correcting a typo rulebook: ]
                if where the previous typo's at is 0:
                    issue the 14th response "Sorry, that can't be corrected.";
                otherwise if the word count is less than 2:
                    issue the 15th response "Think nothing of it.";
                otherwise if the word count is greater than 2:
                    issue the 16th response "'Oops' can only correct a single word.";
                otherwise:
                    [ CARRY OUT correcting a typo: ]
                    let the new word's position be the 2nd word's position of the parsed array;
                    let the new word's length be the 2nd word's length of the parsed array;
                    copy the input array into the secondary input buffer; [ saves away OOPS SWORD ]
                    copy the previous turn's input into the input array; [ retrieves TAKE WSORD ]
                    PARSE the input array into the parsed array; [ updates position & length of the parsed array ]
                    replace the single word where the previous typo's at with the new word's length in letters from the new word's position; [ WSORD becomes SWORD ]
                    PARSE the input array into the parsed array; [ re-parse TAKE SWORD and we're done. ]
                    decide on the word count;
            otherwise if the 1st word of the parsed array is 'UNDO' and the word count is one:
                [[ CHECK undoing a game turn rules: ]
                if the prevent undo option is active:
                    issue the 70th response "The use of 'undo' is forbidden in this game.";
                otherwise if the turn count is one:
                    issue the 11th response "You can't 'undo' what hasn't been done!";
                otherwise if the UNDO status is UNDO's unsupported:
                    issue the 6th response "Your interpreter does not provide 'undo'. Sorry!";
                otherwise if the UNDO status is no further saved state exists to return to:
                    issue the 7th response "You cannot 'undo' any further.";
                [ CARRY OUT undoing a game turn: ]
                otherwise if the result of carrying out undoing a game turn is no further saved state exists to return to:
                    issue the 7th response "You cannot 'undo' any further.";]
                consider the check-'n-carry out undoing a game turn rule directly;
            otherwise:
                now the UNDO status is the result of saving the game state;
                if the undo prevention option is active, now the UNDO status is UNDO's unsupported;
                [ Usually the UNDO status is 'ready to UNDO' here, unless the saving tells us we just UNDid & need to Report. ]
                if the UNDO status is UNDO succeeded:
                    now the UNDO status is ready to UNDO;
                    [ REPORT undoing a game turn: ]
                    consider the restore window colors rule directly;
                    say "[bold type][the player's surroundings][line break][roman type]";
                    issue the 13th response "(Previous turn undone.)";
                otherwise:
                    decide on the word count.

    [ Keyboard a_buffer a_table nw i w w2 x1 x2;
        sline1 = score; sline2 = turns;

        while (true) { ! this while(true) only exists so we can use next/continue instead of goto
            ! 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; ! Position of word following "oops"
                x2 = a_table->8; ! Length of word following "oops"
                #Ifnot; ! TARGET_GLULX
                x1 = a_table-->6; ! Position 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); ! Position of word to go
                w2 = a_table->(4*oops_from); ! Length of word to go
                #Ifnot; ! TARGET_GLULX
                w = a_table-->(3*oops_from); ! Position 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(); ! which asks undo_flag if it's ok to VM_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 "^"; ! say "[the player’s surroundings][line break]"
                ! print (name) location, "^";
                VM_Style(NORMAL_VMSTY);
                L__M(##Miscellany, 13);
                continue;
            }
            return nw;
        }
    ]