Original Parser

version 1 by Ron Newcomb

  • Home page
  • Beginning
  • Previous
  • Next



  • Chapter - Words

    [The player's command is broken down into a numbered sequence of words, which
    break at spaces or certain punctuation (see the DM4). The numbering runs
    upwards from 1 to |WordCount()|. The following utility routines provide
    access to words in the current command; because buffers have different
    definitions in Z and Glulx, so these routines must vary also.

    The actual text of each word is stored as a sequence of ZSCII values in
    a |->| (byte) array, with address |WordAddress(x)| and length |WordLength(x)|.

    We picture the command as a stream of words to be read one at a time, with
    the global variable |wn| being the ``current word'' marker. |NextWord|, which
    takes no arguments, returns:
    (a) 0 if the word at |wn| is unrecognised by the dictionary or |wn| is out
    of range,
    (b) |comma_word| if the word was a comma,
    (c) |THEN1__WD| if it was a full stop (because of the Infocom tradition that
    a full stop abbreviates for the word ``then'': e.g., TAKE BOX. EAST was read
    as two commands in succession),
    (d) or the dictionary address if the word was recognised.

    The current word marker |wn| is always advanced.

    |NextWordStopped| does the same, but returns $-1$ when |wn| is out of range
    (e.g., by having advanced past the last word in the command).

    |MoveWord(at1, b2, at2)| copies word |at2| from parse buffer |b2| -- which
    doesn't need to be |buffer| -- to word |at1| in |parse|.]

    [Section 1 - (for Z-machine only)

    Include (-
    !#Ifdef TARGET_ZCODE;
    ![ WordCount; return parse->1; ];
    ![ WordAddress wordnum; return buffer + parse->(wordnum*4+1); ];
    ![ WordLength wordnum; return parse->(wordnum*4); ];
    ![ MoveWord at1 b2 at2 x y;
    ! x = at1*2-1; y = at2*2-1;
    ! parse-->x++ = b2-->y++;
    ! parse-->x = b2-->y;
    !];
    -).

    Section 2 - (for Glulx only)

    Include (-
    !#Ifnot;
    ![ WordCount; return parse-->0; ];
    ![ WordAddress wordnum; return buffer + parse-->(wordnum*3); ];
    ![ WordLength wordnum; return parse-->(wordnum*3-1); ];
    ![ MoveWord at1 b2 at2 x y;
    ! x = at1*3-2; y = at2*3-2;
    ! parse-->x++ = b2-->y++;
    ! parse-->x++ = b2-->y++;
    ! parse-->x = b2-->y;
    !];
    !#Endif;
    -).]

    [Section 3 - both VMs]

    [ MoveWord() removed from the parser per bug #755 ]
    [To move (Nth - a number) word of (source - a 1-based index based rulebook producing structs) to (Pth - a number) position (this is MoveWord):
        change the Pth word of the player's parsed command to the Nth word of the source;
        change the Pth word's length of the player's parsed command to the Nth word's length of the source;
        change the Pth word's position of the player's parsed command to the Nth word's position of the source.]

    [To choose (Nth - a number) element from (arr - a 1-based index based rulebook producing structs):
        (- {-require-ctvs} ct_0 = {arr}; ct_1 = {Nth}; -).

    Every turn:
        choose the 2nd element from the player's parsed command;
        say "The 2nd word used was [the chosen word of the player's parsed command].";
        say "The position is at [the chosen word's position of the player's parsed command].";
        say "The length is [the chosen word's length of the player's parsed command].";
        say "The 1st word used was [the 1st word of the player's parsed command].";
        say "The position is at [the 1st word's position of the player's parsed command].";
        say "The length is [the 1st word's length of the player's parsed command].";
        repeat with x running from 1 to 4:
            say the x word of the player's parsed command;
        move the 3rd word of the player's parsed command to the 2nd position;
        repeat with x running from 1 to 4:
            say the x word of the player's parsed command;]

    To decide what number is the word count (this is WordCount):
        decide on the number of words in the player's parsed command.

    To decide which 1-based index based rulebook producing ZSCII letters is the (nth - a number) word's address (this is WordAddress):
        decide on the player's input buffer advanced to the Nth word's real position of the player's parsed command.
        
    To decide what number is the length of (Nth - a number) word (this is WordLength):
        decide on the Nth word's length of the player's parsed command.

    [ Remember: 1) check bounds, 2) always increase wn, even on error, 3) don't deref the array until after bounds are checked ]
    To decide what understood word is the next word (this is NextWord):
        if the parser's current word position is less than 1 or the parser's current word position is greater than the word count:
            increment the parser's current word position;
            decide on a word unknown by the game;
        let the current word be the (parser's current word position) -th word of the player's parsed command;
        increment the parser's current word position;
        if the current word is the 'comma', now the current word is the comma;
        if the current word is the 'period', now the current word is 'THEN';
        decide on the current word.

    [ NextWord i j wc;
        #Ifdef TARGET_ZCODE; wc = parse->1; i = wn*2-1;
        #Ifnot; wc = parse-->0; i = wn*3-2; #Endif;
    wn++;
    if ((wn < 2) || (wn > wc+1)) return 0;
    j = parse-->i;
    if (j == ',//') j = comma_word;
    if (j == './/') j = THEN1__WD;
    return j;
    ]

    To decide what understood word is the next word if any (this is NextWordStopped):
        if the parser's current word position is less than 1 or the parser's current word position is greater than the word count:
            increment the parser's current word position;
            decide on no more words left to parse;
        decide on the next word;

    [ NextWordStopped wc;
        #Ifdef TARGET_ZCODE; wc = parse->1; #Ifnot; wc = parse-->0; #Endif;
        if ((wn < 1) || (wn > wc)) { wn++; return -1; }
    return NextWord();
    ]

    [ This is used only once, to loop through the words in the 2nd parsed command, trying to find a comma. ]
    [ So it may be refactored as an "is listed in" command. ]
    To decide what understood word is the next word from (Nth - a number) position of (array - a 1-based index based rulebook producing structs) (this is WordFrom):
        if the Nth is less than 1 or Nth is greater than the word count:
            decide on a word unknown by the game;
        let the current word be the Nth word of the array;
        if the current word is the 'comma', now the current word is the comma;
        if the current word is the 'period', now the current word is 'THEN';
        decide on the current word.

    To decide if (wanted word - an understood word) is listed in (wordlist - a 1-based index based rulebook producing structs):
        repeat with this running from 1 to the number of words in the wordlist:
            if this word of the wordlist is the wanted word:
                now where that word was found is this;
                yes.

    [ WordFrom w p i j wc;
        #Ifdef TARGET_ZCODE; wc = p->1; i = w*2-1;
        #Ifnot; wc = p-->0; i = w*3-2; #Endif;
    if ((w < 1) || (w > wc)) return 0;
    j = p-->i;
    if (j == ',//') j = comma_word;
    if (j == './/') j = THEN1__WD;
    return j;
    ]