Parse

Parse is the routine that interprets the input line. For the most part, authors won’t need to touch it at all (unless they are writing their own parser, at which point this whole site would probably be useless). Still, you will probably want to take a look at it, especially if you are working with PreParse, AssignPronoun, SetObjWord, or ParseError.

The code

Here is the Parse routine itself. For a better understanding of every routine that Parse calls, take a look at hugolib.h.

!----------------------------------------------------------------------------
! Parse is called by the engine without parameters after input but before
! any grammar matching; returning true signals the engine to reparse the
! existing input (in case it has been changed)
!
! The engine parser is responsible for splitting the input line into the
! word[] array, removing removals, replacing synonyms, and combining
! compounds.
!
! This Parse routine is then called to take care of:
!       - picking out extra_scenery words
!       - checking for an "again" command
!       - rewording imperatives to "<Name>, <command>"
!       - renaming pronouns to objects
!       - changing "then <do something else>" into a new command
!
! Control then returns to the engine for grammar matching and, if that is
! successful, calling of the matched verb routine.

! NOTE:  There are some snags with the object-based pronoun replacement
! system (such as, for example, requiring a specific object in grammar)
! so OLD_STYLE_PRONOUNS are still being used.

#set OLD_STYLE_PRONOUNS

! skipping some #ifclear OLD_STYLE_PRONOUNS code

routine Parse
{
    local a
    local reparse
    local temp_verb, temp_obj, temp_xobj

    ResetParse

    list_nest = 0
    event_flag = 0

    if nothing ~= 0                   ! best place to check this
        print "[WARNING:  Objects/classes defined before library]"

#ifset DEBUG
    if debug_flags & D_PARSE_RANK
    {
        print "[parser_data[BEST_PARSE_RANK] = 0]"
    }
#endif

! skipping some #ifclear OLD_STYLE_PRONOUNS code

    if word[1] = "~oops"
        return

    ! Allow the player object to override the parsing cycle completely
    temp_verb = verbroutine
    temp_obj = object
    temp_xobj = xobject
    verbroutine = &PreParse
    object = nothing
    xobject = nothing
    if player.before
    {
        verbroutine = temp_verb
        object = temp_obj
        xobject = temp_xobj
        parser_data[PARSER_STATUS] = 0
        parser_data[LAST_PARSER_STATUS] = 0
        return false
    }
    verbroutine = temp_verb
    object = temp_obj
    xobject = temp_xobj

! The following, devised by Jim Newland, checks to see if the player
! input refers to an unnecessary item of scenery (for example) which is
! nonetheless mentioned in the location description.

    for (a=2; a<=words and word[a]~="" and word[a]~="then"; a++)
    {
        if Inlist(location, extra_scenery, word[a])
        {
            Message(&Parse, 1)
            word[1] = ""            ! force ParseError(0)
            words = 0
            customerror_flag = true
            return true
        }
    }

    if PreParse:  reparse = true            ! easily replaceable routine

    ! Last player-specified object
    if object > 0 and object < objects and last_object ~= -1
        AssignPronoun(object)
    parser_data[LAST_PARSER_STATUS] = 0

    ! Must do this after AssignPronoun, and reset it to 0
    ! after both of Perform and ParseError:
    parser_data[PARSER_STATUS] = PARSER_ACTIVE

! skipping some #ifclear OLD_STYLE_PRONOUNS code

    ! To repeat last command
    if (word[1] = "again" or word[1] = "g") and word[2] = ""
    {
        for (a=1; a<=oldword[0]; a++)
            word[a] = oldword[a]
        words = oldword[0]
        reparse = true
        jump LeaveParse
    }

    local count
#ifset OLD_STYLE_PRONOUNS
    local n, number_pronouns
#endif

    for (count=2; count<=words and word[count]~=""; count++)
    {
        select word[count]

#ifset OLD_STYLE_PRONOUNS
            ! Rename pronouns to appropriate objects
            case "it", "them", "him", "her"
            {
                select word[count]
                    case "it":      a = it_obj
                    case "them":    a = them_obj
                    case "him":     a = him_obj
                    case "her":     a = her_obj

                if a = nothing
                {
                    ! "...be a little more specific"
                    ParseError(13)
                    words = 0
                    customerror_flag = true
                    return
                }
                ! Avoid, e.g., "put it it something"
                elseif word[count] ~= word[count+1]
                {
                    ! For her_obj, we can run into trouble, since
                    ! it's both possessive and a pronoun, so only
                    ! replace "her" where it's the last word:
                    if a ~= her_obj or
                        (a = her_obj and (count = 2 or count = words))
                    {
                        n = SetObjWord(count, a)
                        replace_pronoun[number_pronouns] = a
                        number_pronouns++
                        if n > 1
                            count = count + (n - 1)
                        parser_data[PARSER_STATUS] |= PRONOUNS_SET
                        reparse = true
                    }
                }
            }
#endif

            ! Allow for "then..." and "and then..."
            case "then"
            {
                ! end of this command
                word[count] = "."
                if word[count-1] = "~and"
                    DeleteWord(count-1)
                reparse = true
                break
            }
    }

#ifset OLD_STYLE_PRONOUNS
    if number_pronouns = 2 and replace_pronoun[0] = replace_pronoun[1]
        number_pronouns--

    if number_pronouns > 0
    {
        PrintReplacedPronouns(number_pronouns)
    }
#endif

! Reword imperatives given as "Tell Bob to do something" as "Bob, do
! something" so that the engine automatically reroutes them to SpeakTo

    if word[1] = "tell", "order", "instruct", "ask", "command"
    {
        for (a=1; a<=words and word[a]~=""; a++)
        {
            if word[a] = "to"
            {
                !DeleteWord(a)   ! "to"
                word[a] = "~and"
                DeleteWord(1)   ! "tell", "order", etc.
                reparse = true
                break
            }
        }
    }

#ifset USE_PLURAL_OBJECTS
    if ParsePluralObjects:  reparse = true
#endif

#ifset USE_CHECKHELD
    ParseCheckHeld
#endif

    ! Store current command for future reference
    local ow
    for (a=1; a<=words and a<MAX_WORDS; a++)
    {
        if word[a] = "g", "again"
        {
            if word[a+1] = ""
                a++
        }
        else
            oldword[++ow] = word[a]
    }
    oldword[a] = ""
    oldword[0] = ow

:LeaveParse

#ifset DEBUG
    if debug_flags & D_PARSE
    {
        Font(BOLD_ON)
        for (a=1; a<=words and word[a]~=""; a++)
            print "["; word[a]; "] ";
        print newline
        if parse$ ~= ""
            print "[parse$ = \""; parse$; "\"]"
        Font(BOLD_OFF)
    }
#endif

    return reparse
}