! ----------------------------------------------------------------------------
!  PARSERM:  Core of parser.
!
!  Supplied for use with Inform 6                         Serial number 991113
!                                                                 Release 6/10
!  (c) Graham Nelson 1993, 1994, 1995, 1996, 1997, 1998, 1999
!      but freely usable (see manuals)
! ----------------------------------------------------------------------------
!  Inclusion of "linklpa"
!                   (which defines properties and attributes)
!  Global variables, constants and arrays
!                1: outside of the parser
!                2: used within the parser
!  Inclusion of natural language definition file
!                   (which creates a compass and direction-objects)
!  Darkness and player objects
!  Definition of grammar token numbering system used by Inform
!
!  The InformParser object
!          keyboard reading
!          level 0: outer shell, conversation, errors
!                1: grammar lines
!                2: tokens
!                3: object lists
!                4: scope and ambiguity resolving
!                5: object comparisons
!                6: word comparisons
!                7: reading words and moving tables about
!          pronoun management
!
!  The InformLibrary object
!          main game loop
!          action processing
!          end of turn sequence
!          scope looping, before/after sequence, sending messages out
!          timers, daemons, time of day, score notification
!          light and darkness
!          changing player personality
!          tracing code (only present if DEBUG is set)
!
!  Status line printing, menu display
!  Printing object names with articles
!  Miscellaneous utility routines
!  Game banner, "version" verb, run-time errors
! ----------------------------------------------------------------------------

System_file;

IFDEF MODULE_MODE;
Constant DEBUG;
Constant Grammar__Version 2;
Include "linklpa";
ENDIF;

! ============================================================================
!   Global variables and their associated Constant and Array declarations
! ----------------------------------------------------------------------------
Global location = InformLibrary;     ! Must be first global defined
Global sline1;                       ! Must be second
Global sline2;                       ! Must be third
                                     ! (for status line display)
! ------------------------------------------------------------------------------
!   Z-Machine and interpreter issues
! ------------------------------------------------------------------------------
#ifdef TARGET_ZCODE;
Global top_object;                   ! Largest valid number of any tree object
Global standard_interpreter;         ! The version number of the Z-Machine
                                     ! Standard which the interpreter claims
                                     ! to support, in form (upper byte).(lower)
! ### these globals are not meaningful... well, maybe standard_interpreter,
! but I'll decide that later.
#endif; ! TARGET_ZCODE
Global undo_flag;                    ! Can the interpreter provide "undo"?
Global just_undone;                  ! Can't have two successive UNDOs
#ifdef TARGET_ZCODE;
Global transcript_mode;              ! true when game scripting is on
IFDEF DEBUG;
Global xcommsdir;                    ! true if command recording is on
ENDIF;
#endif; ! TARGET_ZCODE
#ifdef TARGET_GLULX;
Constant GG_MAINWIN_ROCK  201;
Constant GG_STATUSWIN_ROCK  202;
Constant GG_QUOTEWIN_ROCK  203;
Constant GG_SAVESTR_ROCK 301;
Constant GG_SCRIPTSTR_ROCK 302;
Constant GG_COMMANDWSTR_ROCK 303;
Constant GG_COMMANDRSTR_ROCK 304;
Constant GG_SCRIPTFREF_ROCK 401;
Array gg_event --> 4;
Array gg_arguments --> 8;
Global gg_mainwin = 0;
Global gg_statuswin = 0;
Global gg_quotewin = 0;
Global gg_scriptfref = 0;
Global gg_scriptstr = 0;
Global gg_savestr = 0;
Global gg_statuswin_cursize = 0;
Global gg_statuswin_size = 1;
IFDEF DEBUG;
Global gg_commandstr = 0;
Global gg_command_reading = 0; ! true if gg_commandstr is being replayed
ENDIF;
#endif; ! TARGET_GLULX
! ------------------------------------------------------------------------------
!   Time and score
! (for linkage reasons, the task_* arrays are created not here but in verblib.h)
! ------------------------------------------------------------------------------
Global turns = 0;                    ! Number of turns of play so far
Global the_time = NULL;              ! Current time (in minutes since midnight)
Global time_rate = 1;                ! How often time is updated
Global time_step;                    ! By how much

#ifndef MAX_TIMERS;
Constant MAX_TIMERS  32;             ! Max number timers/daemons active at once
#endif;
Array  the_timers  --> MAX_TIMERS;
Global active_timers;                ! Number of timers/daemons actives

Global score;                        ! The current score
Global last_score;                   ! Score last turn (for testing for changes)
Global notify_mode = true;           ! Score notification
Global places_score;                 ! Contribution to score made by visiting
Global things_score;                 ! Contribution made by acquisition
! ------------------------------------------------------------------------------
!   The player
! ------------------------------------------------------------------------------
Global player;                       ! Which object the human is playing through
Global deadflag;                     ! Normally 0, or false; 1 for dead;
                                     ! 2 for victorious, and higher numbers
                                     ! represent exotic forms of death
! ------------------------------------------------------------------------------
!   Light and room descriptions
! ------------------------------------------------------------------------------
Global lightflag = true;             ! Is there currently light to see by?
Global real_location;                ! When in darkness, location = thedark
                                     ! and this holds the real location
Global visibility_ceiling;           ! Highest object in tree visible from
                                     ! the player's point of view (usually
                                     ! the room, sometimes darkness, sometimes
                                     ! a closed non-transparent container).

Global lookmode = 1;                 ! 1=standard, 2=verbose, 3=brief room descs
Global print_player_flag;            ! If set, print something like "(as Fred)"
                                     ! in room descriptions, to reveal whom
                                     ! the human is playing through
Global lastdesc;                     ! Value of location at time of most recent
                                     ! room description printed out
! ------------------------------------------------------------------------------
!   List writing  (style bits are defined as Constants in "verblibm.h")
! ------------------------------------------------------------------------------
Global c_style;                      ! Current list-writer style
Global lt_value;                     ! Common value of list_together
Global listing_together;             ! Object number of one member of a group
                                     ! being listed together
Global listing_size;                 ! Size of such a group
Global wlf_indent;                   ! Current level of indentation printed by
                                     ! WriteListFrom routine

Global inventory_stage = 1;          ! 1 or 2 according to the context in which
                                     ! "invent" routines of objects are called
Global inventory_style;              ! List-writer style currently used while
                                     ! printing inventories
! ------------------------------------------------------------------------------
!   Menus and printing
! ------------------------------------------------------------------------------
Global pretty_flag = true;           ! Use character graphics, or plain text?
Global menu_nesting;                 ! Level of nesting (0 = root menu)
Global menu_item;                    ! These are used in communicating
Global item_width = 8;               ! with the menu-creating routines
Global item_name = "---";

Global lm_n;                         ! Parameters used by LibraryMessages
Global lm_o;                         ! mechanism

IFDEF DEBUG;
Global debug_flag;                   ! Bitmap of flags for tracing actions,
                                     ! calls to object routines, etc.
Global x_scope_count;                ! Used in printing a list of everything
                                     ! in scope
ENDIF;
! ------------------------------------------------------------------------------
!   Action processing
! ------------------------------------------------------------------------------
Global action;                       ! Action currently being asked to perform
Global inp1;                         ! 0 (nothing), 1 (number) or first noun
Global inp2;                         ! 0 (nothing), 1 (number) or second noun
Global noun;                         ! First noun or numerical value
Global second;                       ! Second noun or numerical value

Global keep_silent;                  ! If true, attempt to perform the action
                                     ! silently (e.g. for implicit takes,
                                     ! implicit opening of unlocked doors)

Global reason_code;                  ! Reason for calling a "life" rule
                                     ! (an action or fake such as ##Kiss)

Global receive_action;               ! Either ##PutOn or ##Insert, whichever
                                     ! is action being tried when an object's
                                     ! "before" rule is checking "Receive"
! ==============================================================================
!   Parser variables: first, for communication to the parser
! ------------------------------------------------------------------------------
Global parser_trace = 0;             ! Set this to 1 to make the parser trace
                                     ! tokens and lines
Global parser_action;                ! For the use of the parser when calling
Global parser_one;                   ! user-supplied routines
Global parser_two;                   !
Array  inputobjs       --> 16;       ! For parser to write its results in
Global parser_inflection;            ! A property (usually "name") to find
                                     ! object names in
! ------------------------------------------------------------------------------
!   Parser output
! ------------------------------------------------------------------------------
Global actor;                        ! Person asked to do something
Global actors_location;              ! Like location, but for the actor
Global meta;                         ! Verb is a meta-command (such as "save")

Array  multiple_object --> 64;       ! List of multiple parameters
Global multiflag;                    ! Multiple-object flag
Global toomany_flag;                 ! Flag for "multiple match too large"
                                     ! (e.g. if "take all" took over 100 things)

Global special_word;                 ! Dictionary address for "special" token
Global special_number;               ! Number typed for "special" token
Global parsed_number;                ! For user-supplied parsing routines
Global consult_from;                 ! Word that a "consult" topic starts on
Global consult_words;                ! ...and number of words in topic
! ------------------------------------------------------------------------------
!   Implicit taking
! ------------------------------------------------------------------------------
Global notheld_mode;                 ! To do with implicit taking
Global onotheld_mode;                !     "old copy of notheld_mode", ditto
Global not_holding;                  ! Object to be automatically taken as an
                                     ! implicit command
Array  kept_results --> 16;          ! Delayed command (while the take happens)
! ------------------------------------------------------------------------------
!   Error numbers when parsing a grammar line
! ------------------------------------------------------------------------------
Global etype;                        ! Error number on current line
Global best_etype;                   ! Preferred error number so far
Global nextbest_etype;               ! Preferred one, if ASKSCOPE_PE disallowed

Constant STUCK_PE     = 1;
Constant UPTO_PE      = 2;
Constant NUMBER_PE    = 3;
Constant CANTSEE_PE   = 4;
Constant TOOLIT_PE    = 5;
Constant NOTHELD_PE   = 6;
Constant MULTI_PE     = 7;
Constant MMULTI_PE    = 8;
Constant VAGUE_PE     = 9;
Constant EXCEPT_PE    = 10;
Constant ANIMA_PE     = 11;
Constant VERB_PE      = 12;
Constant SCENERY_PE   = 13;
Constant ITGONE_PE    = 14;
Constant JUNKAFTER_PE = 15;
Constant TOOFEW_PE    = 16;
Constant NOTHING_PE   = 17;
Constant ASKSCOPE_PE  = 18;
! ------------------------------------------------------------------------------
!   Pattern-matching against a single grammar line
! ------------------------------------------------------------------------------
Array pattern --> 32;                ! For the current pattern match
Global pcount;                       ! and a marker within it
Array pattern2 --> 32;               ! And another, which stores the best match
Global pcount2;                      ! so far
Constant PATTERN_NULL = $ffff;       ! Entry for a token producing no text

Array  line_ttype-->32;              ! For storing an analysed grammar line
Array  line_tdata-->32;
Array  line_token-->32;

Global parameters;                   ! Parameters (objects) entered so far
Global nsns;                         ! Number of special_numbers entered so far
Global special_number1;              ! First number, if one was typed
Global special_number2;              ! Second number, if two were typed
! ------------------------------------------------------------------------------
!   Inferences and looking ahead
! ------------------------------------------------------------------------------
Global params_wanted;                ! Number of parameters needed
                                     ! (which may change in parsing)

Global inferfrom;                    ! The point from which the rest of the
                                     ! command must be inferred
Global inferword;                    ! And the preposition inferred
Global dont_infer;                   ! Another dull flag

Global action_to_be;                 ! (If the current line were accepted.)
Global action_reversed;              ! (Parameters would be reversed in order.)
Global advance_warning;              ! What a later-named thing will be
! ------------------------------------------------------------------------------
!   At the level of individual tokens now
! ------------------------------------------------------------------------------
Global found_ttype;                  ! Used to break up tokens into type
Global found_tdata;                  ! and data (by AnalyseToken)
Global token_filter;                 ! For noun filtering by user routines

Global length_of_noun;               ! Set by NounDomain to no of words in noun
#ifdef TARGET_ZCODE;
Constant REPARSE_CODE = 10000;       ! Signals "reparse the text" as a reply
                                     ! from NounDomain
#ifnot; ! TARGET_GLULX
Constant REPARSE_CODE = $40000000;   ! The parser rather gunkily adds addresses
                                     ! to REPARSE_CODE for some purposes.
                                     ! And expects the result to be greater
                                     ! than REPARSE_CODE (signed comparison).
                                     ! So Glulx Inform is limited to a single
                                     ! gigabyte of storage, for the moment.
#endif; ! TARGET_
Global lookahead;                    ! The token after the one now being matched

Global multi_mode;                   ! Multiple mode
Global multi_wanted;                 ! Number of things needed in multitude
Global multi_had;                    ! Number of things actually found
Global multi_context;                ! What token the multi-obj was accepted for

Global indef_mode;                   ! "Indefinite" mode - ie, "take a brick"
                                     ! is in this mode
Global indef_type;                   ! Bit-map holding types of specification
Global indef_wanted;                 ! Number of items wanted (100 for all)
Global indef_guess_p;                ! Plural-guessing flag
Global indef_owner;                  ! Object which must hold these items
Global indef_cases;                  ! Possible gender and numbers of them
Global indef_possambig;              ! Has a possibly dangerous assumption
                                     ! been made about meaning of a descriptor?
Global indef_nspec_at;               ! Word at which a number like "two" was
                                     ! parsed (for backtracking)
Global allow_plurals;                ! Whether plurals presently allowed or not

Global take_all_rule;                ! Slightly different rules apply to
                                     ! "take all" than other uses of multiple
                                     ! objects, to make adjudication produce
                                     ! more pragmatically useful results
                                     ! (Not a flag: possible values 0, 1, 2)

Global dict_flags_of_noun;           ! Of the noun currently being parsed
                                     ! (a bitmap in #dict_par1 format)
Global pronoun_word;                 ! Records which pronoun ("it", "them", ...)
                                     ! caused an error
Global pronoun_obj;                  ! And what obj it was thought to refer to
Global pronoun__word;                ! Saved value
Global pronoun__obj;                 ! Saved value
! ------------------------------------------------------------------------------
!   Searching through scope and parsing "scope=Routine" grammar tokens
! ------------------------------------------------------------------------------
Constant PARSING_REASON       = 0;   ! Possible reasons for searching scope
Constant TALKING_REASON       = 1;
Constant EACH_TURN_REASON     = 2;
Constant REACT_BEFORE_REASON  = 3;
Constant REACT_AFTER_REASON   = 4;
Constant LOOPOVERSCOPE_REASON = 5;
Constant TESTSCOPE_REASON     = 6;

Global scope_reason = PARSING_REASON; ! Current reason for searching scope

Global scope_token;                  ! For "scope=Routine" grammar tokens
Global scope_error;
Global scope_stage;                  ! 1, 2 then 3

Global ats_flag = 0;                 ! For AddToScope routines
Global ats_hls;                      !

Global placed_in_flag;               ! To do with PlaceInScope

! ------------------------------------------------------------------------------
!   The match list of candidate objects for a given token
! ------------------------------------------------------------------------------
Constant MATCH_LIST_SIZE = 128;
Array  match_list    --> 64;         ! An array of matched objects so far
Array  match_classes --> 64;         ! An array of equivalence classes for them
Array  match_scores --> 64;          ! An array of match scores for them
Global number_matched;               ! How many items in it?  (0 means none)
Global number_of_classes;            ! How many equivalence classes?
Global match_length;                 ! How many words long are these matches?
Global match_from;                   ! At what word of the input do they begin?
Global bestguess_score;              ! What did the best-guess object score?
! ------------------------------------------------------------------------------
!   Low level textual manipulation
! ------------------------------------------------------------------------------
#ifdef TARGET_ZCODE;

Constant INPUT_BUFFER_LEN = 120;     ! Length of buffer array (although we
                                     ! leave an extra byte to allow for
                                     ! interpreter bugs)

!Bugfix, Inform Patches [Buffers declared incorrectly]
Array  buffer    -> 123;             ! Buffer for parsing main line of input
Array  parse     -> 65;              ! Parse table mirroring it
Array  buffer2   -> 123;             ! Buffers for supplementary questions
Array  parse2    -> 65;              !
Array  buffer3   -> 123;             ! Buffer retaining input for "again"

#ifnot; ! TARGET_GLULX

Constant INPUT_BUFFER_LEN = 260;     ! No extra byte necessary
Constant MAX_BUFFER_WORDS = 20;
Constant PARSE_BUFFER_LEN = 244;     ! 4 + MAX_BUFFER_WORDS*4;

Array  buffer    -> INPUT_BUFFER_LEN;
Array  parse     -> PARSE_BUFFER_LEN;
Array  buffer2   -> INPUT_BUFFER_LEN;
Array  parse2    -> PARSE_BUFFER_LEN;
Array  buffer3   -> INPUT_BUFFER_LEN;

#endif; ! TARGET_

Constant comma_word = 'comma,';      ! An "untypeable word" used to substitute
                                     ! for commas in parse buffers

Global wn;                           ! Word number within "parse" (from 1)
Global num_words;                    ! Number of words typed
Global verb_word;                    ! Verb word (eg, take in "take all" or
                                     ! "dwarf, take all") - address in dict
Global verb_wordnum;                 ! its number in typing order (eg, 1 or 3)
Global usual_grammar_after;          ! Point from which usual grammar is parsed
                                     ! (it may vary from the above if user's
                                     ! routines match multi-word verbs)

Global oops_from;                    ! The "first mistake" word number
Global saved_oops;                   ! Used in working this out
Array  oops_workspace -> 64;         ! Used temporarily by "oops" routine

Global held_back_mode;               ! Flag: is there some input from last time
Global hb_wn;                        ! left over?  (And a save value for wn.)
                                     ! (Used for full stops and "then".)
! ----------------------------------------------------------------------------
Array PowersOfTwo_TB                 ! Used in converting case numbers to
  --> $$100000000000                 ! case bitmaps
      $$010000000000
      $$001000000000
      $$000100000000
      $$000010000000
      $$000001000000
      $$000000100000
      $$000000010000
      $$000000001000
      $$000000000100
      $$000000000010
      $$000000000001;
! ============================================================================


! ============================================================================
!  Constants, and one variable, needed for the language definition file
! ----------------------------------------------------------------------------
Constant POSSESS_PK  = $100;
Constant DEFART_PK   = $101;
Constant INDEFART_PK = $102;
Global short_name_case;
! ----------------------------------------------------------------------------
Include "language__";                !  The natural language definition,
                                     !  whose filename is taken from the ICL
                                     !  language_name variable
! ----------------------------------------------------------------------------
#ifndef LanguageCases;
Constant LanguageCases = 1;
#endif;
! ------------------------------------------------------------------------------
!   Pronouns support for the cruder (library 6/2 and earlier) version:
!   only needed in English
! ------------------------------------------------------------------------------
#ifdef EnglishNaturalLanguage;
Global itobj = NULL;                 ! The object which is currently "it"
Global himobj = NULL;                ! The object which is currently "him"
Global herobj = NULL;                ! The object which is currently "her"

Global old_itobj = NULL;             ! The object which is currently "it"
Global old_himobj = NULL;            ! The object which is currently "him"
Global old_herobj = NULL;            ! The object which is currently "her"
#endif;
 
! ----------------------------------------------------------------------------
! Dies ist die deutsche Version, also definieren wir hier noch ein
! paar Globale Variablen, die spezifisch für diese sind.
! ----------------------------------------------------------------------------

Global changed_gender;        !Wird von NounDomain für Synonyme
                              !vers. Geschlechts gebraucht, muß von
                              !parse_name gesetzt werden, bei 'name'
                              !geht's automatisch.
Array  ND_answer_words --> 5; !dito, wird gebraucht, um bei Nachfragen
                              !des Parsers (an verschiedenen Stellen)
                              !das Geschlecht korrekt bestimmen zu
                              !können. An mehr als 5 Stellen wird er
                              !wohl nicht nachfragen.

! ============================================================================

! ============================================================================
! "Darkness" is not really a place: but it has to be an object so that the
!  location-name on the status line can be "Darkness".
! ----------------------------------------------------------------------------
Object thedark "(darkness object)"
  with initial 0,
       short_name DARKNESS__TX,
       description
       [;  return L__M(##Miscellany, 17);
       ];
Object selfobj "(self object)"
  with dekl 0, !geändert von Max Kalus
       short_name
       [;  return L__M(##Miscellany, 18);
       ],
       description
       [;  return L__M(##Miscellany, 19);
       ],
       before NULL,   after NULL,    life NULL,    each_turn NULL,
       time_out NULL, describe NULL,
       capacity 100, parse_name 0,
       orders 0, number 0,
  has  concealed animate proper transparent male;

! ============================================================================
!  The definition of the token-numbering system used by Inform.
! ----------------------------------------------------------------------------

Constant ILLEGAL_TT        = 0;      ! Types of grammar token: illegal
Constant ELEMENTARY_TT     = 1;      !     (one of those below)
Constant PREPOSITION_TT    = 2;      !     e.g. 'into'
Constant ROUTINE_FILTER_TT = 3;      !     e.g. noun=CagedCreature
Constant ATTR_FILTER_TT    = 4;      !     e.g. edible
Constant SCOPE_TT          = 5;      !     e.g. scope=Spells
Constant GPR_TT            = 6;      !     a general parsing routine

Constant NOUN_TOKEN        = 0;      ! The elementary grammar tokens, and
Constant HELD_TOKEN        = 1;      ! the numbers compiled by Inform to
Constant MULTI_TOKEN       = 2;      ! encode them
Constant MULTIHELD_TOKEN   = 3;
Constant MULTIEXCEPT_TOKEN = 4;
Constant MULTIINSIDE_TOKEN = 5;
Constant CREATURE_TOKEN    = 6;
Constant SPECIAL_TOKEN     = 7;
Constant NUMBER_TOKEN      = 8;
Constant TOPIC_TOKEN       = 9;


Constant GPR_FAIL          = -1;     ! Return values from General Parsing
Constant GPR_PREPOSITION   = 0;      ! Routines
Constant GPR_NUMBER        = 1;
Constant GPR_MULTIPLE      = 2;
Constant GPR_REPARSE       = REPARSE_CODE;
Constant GPR_NOUN          = $ff00;
Constant GPR_HELD          = $ff01;
Constant GPR_MULTI         = $ff02;
Constant GPR_MULTIHELD     = $ff03;
Constant GPR_MULTIEXCEPT   = $ff04;
Constant GPR_MULTIINSIDE   = $ff05;
Constant GPR_CREATURE      = $ff06;

Constant ENDIT_TOKEN       = 15;     ! Value used to mean "end of grammar line"

#Iftrue Grammar__Version == 1;
[ AnalyseToken token m;

    found_tdata = token;

    if (token < 0)   { found_ttype = ILLEGAL_TT; return; }
    if (token <= 8)  { found_ttype = ELEMENTARY_TT; return; }
    if (token < 15)  { found_ttype = ILLEGAL_TT; return; }
    if (token == 15) { found_ttype = ELEMENTARY_TT; return; }
    if (token < 48)  { found_ttype = ROUTINE_FILTER_TT;
                       found_tdata = token - 16;
                       return; }
    if (token < 80)  { found_ttype = GPR_TT;
                       found_tdata = #preactions_table-->(token-48);
                       return; }
    if (token < 128) { found_ttype = SCOPE_TT;
                       found_tdata = #preactions_table-->(token-80);
                       return; }
    if (token < 180) { found_ttype = ATTR_FILTER_TT;
                       found_tdata = token - 128;
                       return; }

    found_ttype = PREPOSITION_TT;
    m=#adjectives_table;
    for (::)
    {   if (token==m-->1) { found_tdata = m-->0; return; }
        m=m+4;
    }
    m=#adjectives_table; RunTimeError(1);
    found_tdata = m;
];
[ UnpackGrammarLine line_address i m;
  for (i = 0 : i < 32 : i++)
  {   line_token-->i = ENDIT_TOKEN;
      line_ttype-->i = ELEMENTARY_TT;
      line_tdata-->i = ENDIT_TOKEN;
  }
  for (i = 0: i <= 5 :i++)
  {   line_token-->i = line_address->(i+1);
      AnalyseToken(line_token-->i);
      if ((found_ttype == ELEMENTARY_TT) && (found_tdata == NOUN_TOKEN)
          && (m == line_address->0))
      {   line_token-->i = ENDIT_TOKEN;
          break;
      }
      line_ttype-->i = found_ttype;
      line_tdata-->i = found_tdata;
      if (found_ttype ~= PREPOSITION_TT) m++;
  }
  action_to_be = line_address->7;
  action_reversed = false;
  params_wanted = line_address->0;
  return line_address + 8;
];
#Ifnot;
[ AnalyseToken token;

    if (token == ENDIT_TOKEN)
    {   found_ttype = ELEMENTARY_TT;
        found_tdata = ENDIT_TOKEN;
        return;
    }

    found_ttype = (token->0) & $$1111;
    found_tdata = (token+1)-->0;
];
#ifdef TARGET_ZCODE;
[ UnpackGrammarLine line_address i;
  for (i = 0 : i < 32 : i++)
  {   line_token-->i = ENDIT_TOKEN;
      line_ttype-->i = ELEMENTARY_TT;
      line_tdata-->i = ENDIT_TOKEN;
  }
  action_to_be = 256*(line_address->0) + line_address->1;
  action_reversed = ((action_to_be & $400) ~= 0);
  action_to_be = action_to_be & $3ff;
  line_address--;
  params_wanted = 0;
  for (i=0::i++)
  {   line_address = line_address + 3;
      if (line_address->0 == ENDIT_TOKEN) break;
      line_token-->i = line_address;
      AnalyseToken(line_address);
      if (found_ttype ~= PREPOSITION_TT) params_wanted++;
      line_ttype-->i = found_ttype;
      line_tdata-->i = found_tdata;
  }
  return line_address + 1;
];
#ifnot; ! TARGET_GLULX
[ UnpackGrammarLine line_address i;
  for (i = 0 : i < 32 : i++)
  {   line_token-->i = ENDIT_TOKEN;
      line_ttype-->i = ELEMENTARY_TT;
      line_tdata-->i = ENDIT_TOKEN;
  }
  @aloads line_address 0 action_to_be;
  action_reversed = (((line_address->2) & 1) ~= 0);
  line_address = line_address - 2;
  params_wanted = 0;
  for (i=0::i++)
  {   line_address = line_address + 5;
      if (line_address->0 == ENDIT_TOKEN) break;
      line_token-->i = line_address;
      AnalyseToken(line_address);
      if (found_ttype ~= PREPOSITION_TT) params_wanted++;
      line_ttype-->i = found_ttype;
      line_tdata-->i = found_tdata;
  }
  return line_address + 1;
];
#endif; ! TARGET_
#Endif;

!  To protect against a bug in early versions of the "Zip" interpreter:
!  Of course, in Glulx, this routine actually performs work.

#ifdef TARGET_ZCODE;

[ Tokenise__ b p; b->(2 + b->1) = 0; @tokenise b p; ];

#endif; ! TARGET_ZCODE

#ifdef TARGET_GLULX;

Array gg_tokenbuf -> DICT_WORD_SIZE;

[ GGWordCompare str1 str2 ix jx;
  for (ix=0 : ix < DICT_WORD_SIZE : ix++) {
    jx = (str1->ix) - (str2->ix);
    if (jx ~= 0)
      return jx;
  }
  return 0;
];

[ Tokenise__ buf tab 
    cx numwords len bx ix wx wpos wlen val res dictlen entrylen;
  len = buf-->0;
  buf = buf+WORDSIZE;

  ! First, split the buffer up into words. We use the standard Infocom
  ! list of word separators (comma, period, double-quote).

  cx = 0;
  numwords = 0;
  while (cx < len) {
    while (cx < len && buf->cx == ' ')
      cx++;
    if (cx >= len)
      break;
    bx = cx;
    if (buf->cx == '.' or ',' or '"') {
      cx++;
    }
    else {
      while (cx < len && buf->cx ~= ' ' or '.' or ',' or '"')
        cx++;
    }
    tab-->(numwords*3+2) = (cx-bx);
    tab-->(numwords*3+3) = WORDSIZE+bx;
    numwords++;
    if (numwords >= MAX_BUFFER_WORDS)
      break;
  }
  tab-->0 = numwords;  

  ! Now we look each word up in the dictionary.

  dictlen = #dictionary_table-->0;
  entrylen = DICT_WORD_SIZE + 7;

  for (wx=0 : wx < numwords : wx++) {
    wlen = tab-->(wx*3+2);
    wpos = tab-->(wx*3+3);

    ! Copy the word into the gg_tokenbuf array, clipping to DICT_WORD_SIZE
    ! characters and lower case.
    if (wlen > DICT_WORD_SIZE)
      wlen = DICT_WORD_SIZE;
    cx = wpos-WORDSIZE;
    for (ix=0 : ix < wlen : ix++) {
      gg_tokenbuf->ix = glk($00A0, buf->(cx+ix));
    }
    for ( : ix < DICT_WORD_SIZE : ix++) {
      gg_tokenbuf->ix = 0;
    }

    val = #dictionary_table + WORDSIZE;
    @binarysearch gg_tokenbuf DICT_WORD_SIZE val entrylen dictlen
      1 1 res;
    tab-->(wx*3+1) = res;
  }
];

#endif; ! TARGET_GLULX

! ============================================================================
!  The InformParser object abstracts the front end of the parser.
!
!  InformParser.parse_input(results)
!  returns only when a sensible request has been made, and puts into the
!  "results" buffer:
!
!  --> 0 = The action number
!  --> 1 = Number of parameters
!  --> 2, 3, ... = The parameters (object numbers), but
!                  0 means "put the multiple object list here"
!                  1 means "put one of the special numbers here"
!
! ----------------------------------------------------------------------------

Object InformParser "(Inform Parser)"
  with parse_input
       [ results; Parser__parse(results);
       ], has proper;

! ----------------------------------------------------------------------------
!  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.
!
!  It 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.
!
!  Return the number of words typed
! ----------------------------------------------------------------------------


#ifdef TARGET_ZCODE;
[ KeyboardPrimitive  a_buffer a_table;
  read a_buffer a_table;
];
#ifnot; ! TARGET_GLULX
[ KeyCharPrimitive win nostat done res ix jx ch;
  jx = ch; ! squash compiler warnings
  if (win == 0)
    win = gg_mainwin;
#IFDEF DEBUG;
  if (gg_commandstr ~= 0 && gg_command_reading ~= false) {
    ! get_line_stream
    done = glk($0091, gg_commandstr, gg_arguments, 31);
    if (done == 0) {
      glk($0044, gg_commandstr, 0); ! stream_close
      gg_commandstr = 0;
      gg_command_reading = false;
      ! fall through to normal user input.
    }
    else {
      ! Trim the trailing newline
      if (gg_arguments->(done-1) == 10)
        done = done-1;
      res = gg_arguments->0;
      if (res == '\') {
        res = 0;
        for (ix=1 : ix<done : ix++) {
          ch = gg_arguments->ix;
          if (ch >= '0' && ch <= '9') {
            @shiftl res 4 res;
            res = res + (ch-'0');
          }
          else if (ch >= 'a' && ch <= 'f') {
            @shiftl res 4 res;
            res = res + (ch+10-'a');
          }
          else if (ch >= 'A' && ch <= 'F') {
            @shiftl res 4 res;
            res = res + (ch+10-'A');
          }
        }
      }
      jump KCPContinue;
    }
  }
#ENDIF;
  done = false;
  glk($00D2, win); ! request_char_event
  while (~~done) {
    glk($00C0, gg_event); ! select
    switch (gg_event-->0) {
      5: ! evtype_Arrange
        if (nostat) {
          glk($00D3, win); ! cancel_char_event
          res = $80000000;
          done = true;
          break;
        }
        DrawStatusLine();
      2: ! evtype_CharInput
        if (gg_event-->1 == win) {
          res = gg_event-->2;
          done = true;
        }
    }
    ix = HandleGlkEvent(gg_event, 1, gg_arguments);
    if (ix == 2) {
      res = gg_arguments-->0;
      done = true;
    }
    else if (ix == -1) {
      done = false;
    }
  }
#IFDEF DEBUG;
  if (gg_commandstr ~= 0 && gg_command_reading == false) {
    if (res < 32 || res >= 256 || (res == '\' or ' ')) {
      glk($0081, gg_commandstr, '\'); ! put_buffer_char
      done = 0;
      jx = res;
      for (ix=0 : ix<8 : ix++) {
        @ushiftr jx 28 ch;
        @shiftl jx 4 jx;
        ch = ch & $0F;
        if (ch ~= 0 || ix == 7) done = 1;
        if (done) {
          if (ch >= 0 && ch <= 9)
            ch = ch + '0';
          else
            ch = (ch - 10) + 'A';
          glk($0081, gg_commandstr, ch); ! put_buffer_char
        }
      }
    }
    else {
      glk($0081, gg_commandstr, res); ! put_buffer_char
    }
    glk($0081, gg_commandstr, 10); ! put_char_stream (newline)
  }
#ENDIF;
.KCPContinue;
  return res;
];

[ KeyboardPrimitive  a_buffer a_table done ix;
#IFDEF DEBUG;
  if (gg_commandstr ~= 0 && gg_command_reading ~= false) {
    ! get_line_stream
    done = glk($0091, gg_commandstr, a_buffer+WORDSIZE,
      (INPUT_BUFFER_LEN-WORDSIZE)-1);
    if (done == 0) {
      glk($0044, gg_commandstr, 0); ! stream_close
      gg_commandstr = 0;
      gg_command_reading = false;
      print "[Command replay complete.]^";
      ! fall through to normal user input.
    }
    else {
      ! Trim the trailing newline
      if ((a_buffer+WORDSIZE)->(done-1) == 10)
        done = done-1;
      a_buffer-->0 = done;
      glk($0086, 8); ! set input style
      glk($0084, a_buffer+WORDSIZE, done); ! put_buffer
      glk($0086, 0); ! set normal style
      print "^";
      jump KPContinue;
    }
  }
#ENDIF;
  done = false;
  glk($00D0, gg_mainwin, a_buffer+WORDSIZE, INPUT_BUFFER_LEN-WORDSIZE, 
    0); ! request_line_event
  while (~~done) {
    glk($00C0, gg_event); ! select
    switch (gg_event-->0) {
      5: ! evtype_Arrange
        DrawStatusLine();
      3: ! evtype_LineInput
        if (gg_event-->1 == gg_mainwin) {
          a_buffer-->0 = gg_event-->2;
          done = true;
        }
    }
    ix = HandleGlkEvent(gg_event, 0, a_buffer);
    if (ix == 2) {
      done = true;
    }
    else if (ix == -1) {
      done = false;
    }
  }
#IFDEF DEBUG;
  if (gg_commandstr ~= 0 && gg_command_reading == false) {
    ! put_buffer_stream
    glk($0085, gg_commandstr, a_buffer+WORDSIZE, a_buffer-->0); 
    glk($0081, gg_commandstr, 10); ! put_char_stream (newline)
  }
#ENDIF;
.KPContinue;
  Tokenise__(a_buffer,a_table);
  ! It's time to close any quote window we've got going.
  if (gg_quotewin) {
    glk($0024, gg_quotewin, 0); ! close_window
    gg_quotewin = 0;
  }
];
#endif; ! TARGET_

[ Keyboard  a_buffer a_table flag  nw i w w2 x1 x2;

    DisplayStatus();
    .FreshInput;

!  Save the start of the buffer, in case "oops" needs to restore it
!  to the previous time's buffer

    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

    if (flag)				!Eingefügt durch Max Kalus
    	  L__M(##Miscellany, 54);
    else
    	L__M(##Prompt);
    AfterPrompt();
    #IFV5; DrawStatusLine(); #ENDIF;
    KeyboardPrimitive(a_buffer, a_table);
#ifdef TARGET_ZCODE;
    nw=a_table->1;
#ifnot; ! TARGET_GLULX
    nw=a_table-->0;
#endif; ! TARGET_

!  If the line was blank, get a fresh line
    if (nw == 0)
    { L__M(##Miscellany,10); jump FreshInput; }

!  Unless the opening word was "oops", return
!  Conveniently, a_table-->1 is the first word in both ZCODE and GLULX.

    w=a_table-->1;
    if (w == OOPS1__WD or OOPS2__WD or OOPS3__WD) jump DoOops;

#IFV5;
!  Undo handling

!  In Graham's 6/9 code, the following line tests (parse->1==1) instead
!  of (nw==1). I believe that's wrong. In particular, it prevents "undo"
!  from working during "Which do you mean...?" disambiguation, because
!  input at that prompt goes into parse2 instead of parse.

    !Bugfix, Inform Patches [Plural containers listed wrong, also turns == 0 instead of 1]
    if ((w == UNDO1__WD or UNDO2__WD or UNDO3__WD or UNDO4__WD) && (nw==1)) !Patch OMU
    {   if (turns==0)
        {   L__M(##Miscellany,11); jump FreshInput;
        }
        if (undo_flag==0)
        {   L__M(##Miscellany,6); jump FreshInput;
        }
        if (undo_flag==1) jump UndoFailed;
#ifdef TARGET_ZCODE;
        ! The just_undone check shouldn't be done in Glulx, as multiple
        ! undo is possible.
        if (just_undone==1)
        {   L__M(##Miscellany,12); jump FreshInput;
        }
        @restore_undo i;
#ifnot; ! TARGET_GLULX
        @restoreundo i;
        i = (~~i);
#endif; ! TARGET_
        if (i==0)
        {   .UndoFailed;
            L__M(##Miscellany,7);
        }
        jump FreshInput;
    }
#ifdef TARGET_ZCODE;
    @save_undo i;
#ifnot; ! TARGET_GLULX
    @saveundo i;
    if (i == -1) {
        GGRecoverObjects();
        i = 2;
    }
    else {
        i = (~~i);
    }
#endif; ! TARGET_
    just_undone=0;
    undo_flag=2;
    if (i==-1) undo_flag=0;
    if (i==0) undo_flag=1;
    if (i==2)
    {
#ifdef TARGET_ZCODE;
        style bold;
#ifnot; ! TARGET_GLULX
        glk($0086, 4); ! set subheader style
#endif; ! TARGET_
        print (name) location, "^";
#ifdef TARGET_ZCODE;
        style roman;
#ifnot; ! TARGET_GLULX
        glk($0086, 0); ! set normal style
#endif; ! TARGET_
        L__M(##Miscellany,13);
        just_undone=1;
        jump FreshInput;
    }
#ENDIF;

    return nw;

    .DoOops;
    if (oops_from == 0)
    {   L__M(##Miscellany,14); jump FreshInput; }
    if (nw == 1)
    {   L__M(##Miscellany,15); jump FreshInput; }
    if (nw > 2)
    {   L__M(##Miscellany,16); jump FreshInput; }

!  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;
    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);

    Tokenise__(a_buffer,a_table);
#ifdef TARGET_ZCODE;
    nw=a_table->1;
#ifnot; ! TARGET_GLULX
    nw=a_table-->0;
#endif; ! TARGET_

    return nw;
];

! ----------------------------------------------------------------------------
!  To simplify the picture a little, a rough map of the main routine:
!
!  (A)    Get the input, do "oops" and "again"
!  (B)    Is it a direction, and so an implicit "go"?  If so go to (K)
!  (C)    Is anyone being addressed?
!  (D)    Get the verb: try all the syntax lines for that verb
!  (E)    Break down a syntax line into analysed tokens
!  (F)    Look ahead for advance warning for multiexcept/multiinside
!  (G)    Parse each token in turn (calling ParseToken to do most of the work)
!  (H)    Cheaply parse otherwise unrecognised conversation and return
!  (I)    Print best possible error message
!  (J)    Retry the whole lot
!  (K)    Last thing: check for "then" and further instructions(s), return.
!
!  The strategic points (A) to (K) are marked in the commentary.
!
!  Note that there are three different places where a return can happen.
! ----------------------------------------------------------------------------

[ Parser__parse  results   syntax line num_lines line_address i j k
                           token l m;

!  **** (A) ****

!  Firstly, in "not held" mode, we still have a command left over from last
!  time (eg, the user typed "eat biscuit", which was parsed as "take biscuit"
!  last time, with "eat biscuit" tucked away until now).  So we return that.

    if (notheld_mode==1)
    {   for (i=0:i<8:i++) results-->i=kept_results-->i;
        notheld_mode=0; rtrue;
    }

    if (held_back_mode==1)
    {   held_back_mode=0;
        Tokenise__(buffer,parse);
        jump ReParse;
    }

  .ReType;

    Keyboard(buffer,parse);
    for(i=0:i<5:i++)
	ND_answer_words --> i = 0;

  .ReParse;

    parser_inflection = name;

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

#ifdef TARGET_ZCODE;
    num_words=parse->1;
#ifnot; ! TARGET_GLULX
    num_words=parse-->0;
#endif; ! TARGET_
    wn=1;
#ifdef LanguageToInformese;
    LanguageToInformese();
#ifv5;
!   Re-tokenise:
    Tokenise__(buffer,parse);
#endif;
#endif;

    BeforeParsing();
#ifdef TARGET_ZCODE;
    num_words=parse->1;
#ifnot; ! TARGET_GLULX
    num_words=parse-->0;
#endif; ! TARGET_

    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, 0-->4)>=0
                    && UnsignedCompare(j, 0-->2)<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;
    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; }

!  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)
        {   L__M(##Miscellany,20); jump ReType; }
#ifdef TARGET_ZCODE;
        if (buffer3->1==0)
        {   L__M(##Miscellany,21); jump ReType; }
#ifnot; ! TARGET_GLULX
        if (buffer3-->0==0)
        {   L__M(##Miscellany,21); jump ReType; }
#endif; ! TARGET_
        for (i=0:i<INPUT_BUFFER_LEN:i++) buffer->i=buffer3->i;
        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)
    {   i = RunRoutines(actor, grammar);
        #ifdef DEBUG;
        if (parser_trace>=2 && actor.grammar~=0 or NULL)
            print " [Grammar property returned ", i, "]^";
        #endif;
        if (i<0) { usual_grammar_after = verb_wordnum; i=-i; }
        if (i==1)
        {   results-->0 = action;
            results-->1 = noun;
            results-->2 = second;
            rtrue;
        }
        if (i~=0) { verb_word = i; wn--; verb_wordnum--; }
        else
        {   wn = verb_wordnum; verb_word=NextWord();
        }
    }
    else usual_grammar_after=0;

!  **** (B) ****

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

!  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;
        l=NounDomain(compass,0,0); 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)
        {   results-->0 = ##Go;
            action_to_be = ##Go;
            results-->1 = 1;
            results-->2 = l;
            jump LookForMore;
        }

!  **** (C) ****

!  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;
            }

            verb_word=UnknownVerb(verb_word);
            if (verb_word~=0) jump VerbAccepted;
        }

        best_etype=VERB_PE; jump GiveError;

!  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;
        if (j==1) { L__M(##Miscellany,22); jump ReType; }

!  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,6);
        scope_reason = PARSING_REASON;
        if (l==REPARSE_CODE) jump ReParse;

        if (l==0) { L__M(##Miscellany,23); jump ReType; }

!  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)
        {   L__M(##Miscellany, 24, l); jump ReType; }

!  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)
        {   L__M(##Miscellany, 25); jump ReType; }

!  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)
            {   L__M(##Miscellany,20); jump ReType;
            }
        }

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


!  **** (D) ****

   .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, stored in the dictionary entry
!  (in a peculiar 255-n fashion for traditional Infocom reasons)...

    i=$ff-(verb_word->#dict_par2);

!  ...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...

#ifdef TARGET_ZCODE;
    syntax=(0-->7)-->i;
#ifnot; ! TARGET_GLULX
    syntax=(#grammar_table)-->(i+1);
#endif; ! TARGET_

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

    num_lines=(syntax->0)-1;

!  ...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+1, " lines)]^";
   }
   #endif;

   best_etype=STUCK_PE; nextbest_etype=STUCK_PE;

!  "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).


!  **** (E) ****

    line_address = syntax + 1;

    for (line=0:line<=num_lines:line++)
    {   
        for (i = 0 : i < 32 : i++)
        {   line_token-->i = ENDIT_TOKEN;
            line_ttype-->i = ELEMENTARY_TT;
            line_tdata-->i = ENDIT_TOKEN;
        }

!  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;

!  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.

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

!  Put the word marker back to just after the verb

        wn=verb_wordnum+1;

!  **** (F) ****
!  There are two special cases where parsing a token now has to be
!  affected by the result of parsing another token later, and these
!  two cases (multiexcept and multiinside tokens) are helped by a quick
!  look ahead, to work out the future token now.  We can only carry this
!  out in the simple (but by far the most common) case:
!
!      multiexcept <one or more prepositions> noun
!
!  and similarly for multiinside.

        advance_warning = NULL; 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;

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

                    pcount++;
                    if (line_ttype-->pcount == PREPOSITION_TT)
                    {   while (line_ttype-->pcount == PREPOSITION_TT)
                            pcount++;

                        if ((line_ttype-->pcount == ELEMENTARY_TT)
                            && (line_tdata-->pcount == NOUN_TOKEN))
                        {
                            !  Advance past the last preposition

                            while (wn <= num_words)
                            {   if (NextWord() == line_tdata-->(pcount-1))
                                {   l = NounDomain(actors_location, actor,
                                            NOUN_TOKEN);
                                    #ifdef DEBUG;
                                    if (parser_trace>=2)
                                    {   print " [Advanced to ~noun~ token: ";
                                        if (l==REPARSE_CODE)
                                            print "re-parse request]^";
                                        if (l==1) print "but multiple found]^";
                                        if (l==0) print "error ", etype, "]^";
                                        if (l>=2) print (the) l, "]^";
                                    }
                                    #endif;
                                    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!).

        not_holding=0;
        inferfrom=0;
        parameters=0;
        nsns=0; special_word=0; special_number=0;
        multiple_object-->0 = 0;
        etype=STUCK_PE;
        wn=verb_wordnum+1;

!  **** (G) ****
!  "Pattern" gradually accumulates what has been recognised so far,
!  so that it may be reprinted by the parser later on

        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;

            if (token ~= ENDIT_TOKEN)
            {   scope_reason = PARSING_REASON;
                parser_inflection = name;
                AnalyseToken(token);
                l = ParseToken__(found_ttype, found_tdata, pcount-1, token);
                while (l<-200) l = ParseToken__(ELEMENTARY_TT, l + 256);
                scope_reason = PARSING_REASON;

                if (l==GPR_PREPOSITION)
                {   if (found_ttype~=PREPOSITION_TT
                        && (found_ttype~=ELEMENTARY_TT
                            || found_tdata~=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;
                    results-->(parameters+2) = 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;

                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=1; 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 && results-->2 == 0)
                {   l=ReviseMulti(results-->3);
                    if (l~=0) { etype=l; break; }
                }
                if (parameters>=2 && results-->3 == 0)
                {   l=ReviseMulti(results-->2);
                    if (l~=0) { etype=l; break; }
                }

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

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

                #ifdef DEBUG;
                if (parser_trace>=1)
                    print "[Line successfully parsed]^";
                #endif;

!  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)
                {   print "("; PrintCommand(inferfrom); print ")^";
                }

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

                results-->0 = action_to_be;
                results-->1 = parameters;

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

                if (action_reversed && parameters==2)
                {   i = results-->2; results-->2 = results-->3;
                    results-->3 = 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 && results-->2 >= 2)
                  !Bugfix, Inform Patches ["IT" handled suboptimally]
                  if (pronoun_obj == NULL)
                    PronounNotice(results-->2);

!  ...and worry about the case where an object was allowed as a parameter
!  even though the player wasn't holding it and should have been: in this
!  event, keep the results for next time round, go into "not holding" mode,
!  and for now tell the player what's happening and return a "take" request
!  instead...

                if (not_holding~=0 && actor==player)
                {   notheld_mode=1;
                    for (i=0:i<8:i++) kept_results-->i = results-->i;
                    results-->0 = ##Take;
                    results-->1 = 1;
                    results-->2 = not_holding;
                    L__M(##Miscellany, 26, not_holding);
                }

!  (Notice that implicit takes are only generated for the player, and not
!  for other actors.  This avoids entirely logical, but misleading, text
!  being printed.)

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

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

!  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;
   }

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

!  **** (H) ****

  .GiveError;
        etype=best_etype;

!  Errors are handled differently depending on who was talking.

!  If the command was addressed to somebody else (eg, "dwarf, sfgh") then
!  it is taken as conversation which the parser has no business in disallowing.

    if (actor~=player)
    {   if (usual_grammar_after>0)
        {   verb_wordnum = usual_grammar_after;
            jump AlmostReParse;
        }
        wn=verb_wordnum;
        special_word=NextWord();
        if (special_word==comma_word)
        {   special_word=NextWord();
            verb_wordnum++;
        }
        special_number=TryNumber(verb_wordnum);
        results-->0=##NotUnderstood;
        results-->1=2;
        results-->2=1; special_number1=special_word;
        results-->3=actor;
        consult_from = verb_wordnum; consult_words = num_words-consult_from+1;
        rtrue;
    }

!  **** (I) ****

!  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 (ParserError(etype)~=0) jump ReType;
    pronoun_word = pronoun__word; pronoun_obj = pronoun__obj;

    if (etype==STUCK_PE)   { L__M(##Miscellany, 27); oops_from=1; }
    if (etype==UPTO_PE)    { L__M(##Miscellany, 28);
                             for (m=0:m<32:m++) pattern-->m = pattern2-->m;
                             pcount=pcount2; PrintCommand(0); print ".^";
                           }
    if (etype==NUMBER_PE)  L__M(##Miscellany, 29);
    if (etype==CANTSEE_PE) { L__M(##Miscellany, 30); oops_from=saved_oops; }
    if (etype==TOOLIT_PE)  L__M(##Miscellany, 31);
    if (etype==NOTHELD_PE) { L__M(##Miscellany, 32); oops_from=saved_oops; }
    if (etype==MULTI_PE)   L__M(##Miscellany, 33);
    if (etype==MMULTI_PE)  L__M(##Miscellany, 34);
    if (etype==VAGUE_PE)   L__M(##Miscellany, 35);
    if (etype==EXCEPT_PE)  L__M(##Miscellany, 36);
    if (etype==ANIMA_PE)   L__M(##Miscellany, 37);
    if (etype==VERB_PE)    L__M(##Miscellany, 38);
    if (etype==SCENERY_PE) L__M(##Miscellany, 39);
    if (etype==ITGONE_PE)
    {   if (pronoun_obj == NULL) L__M(##Miscellany, 35);
                            else L__M(##Miscellany, 40);
    }
    if (etype==JUNKAFTER_PE) L__M(##Miscellany, 41);
    if (etype==TOOFEW_PE)  L__M(##Miscellany, 42, multi_had);
    if (etype==NOTHING_PE) { if (multi_wanted==100) L__M(##Miscellany, 43);
                             else L__M(##Miscellany, 44);  }

    if (etype==ASKSCOPE_PE)
    {   scope_stage=3;
        if (indirect(scope_error)==-1)
        {   best_etype=nextbest_etype; jump GiveError;  }
    }

!  **** (J) ****

!  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.


!  **** (K) ****

!  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; }
       i = WordAddress(verb_wordnum);
       j = WordAddress(wn);
       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 = ' ';
       }
       Tokenise__(buffer,parse); held_back_mode = true; return;
   }
   best_etype=UPTO_PE; jump GiveError;
];

[ ScopeCeiling person act;
  act = parent(person); if (act == 0) return person;
  if (person == player && location == thedark) return thedark;
  while (parent(act)~=0
         && (act has transparent || act has supporter
             || (act has container && act has open)))
      act = parent(act);
  return act;
];

! ----------------------------------------------------------------------------
!  Descriptors()
!
!  Handles descriptive words like "my", "his", "another" and so on.
!  Skips "the", and leaves wn pointing to the first misunderstood word.
!
!  Allowed to set up for a plural only if allow_p is set
!
!  Returns error number, or 0 if no error occurred
! ----------------------------------------------------------------------------

Constant OTHER_BIT  =   1;     !  These will be used in Adjudicate()
Constant MY_BIT     =   2;     !  to disambiguate choices
Constant THAT_BIT   =   4;
Constant PLURAL_BIT =   8;
Constant LIT_BIT    =  16;
Constant UNLIT_BIT  =  32;

[ ResetDescriptors;
   indef_mode=0; indef_type=0; indef_wanted=0; indef_guess_p=0;
   indef_possambig = false;
   indef_owner = nothing;
   indef_cases = $$111111111111;
   indef_nspec_at = 0;
];

[ Descriptors allow_multiple  o x flag cto type n;

   ResetDescriptors();
   if (wn > num_words) return 0;

   for (flag=true:flag:)
   {   o=NextWordStopped(); flag=false;

       for (x=1:x<=LanguageDescriptors-->0:x=x+4)
           if (o == LanguageDescriptors-->x)
           {   flag = true;
               type = LanguageDescriptors-->(x+2);
               if (type ~= DEFART_PK) indef_mode = true;
               indef_possambig = true;
               indef_cases = indef_cases & (LanguageDescriptors-->(x+1));

               if (type == POSSESS_PK)
               {   cto = LanguageDescriptors-->(x+3);
                   switch(cto)
                   {  0: indef_type = indef_type | MY_BIT;
                      1: indef_type = indef_type | THAT_BIT;
                      default: indef_owner = PronounValue(cto);
                        if (indef_owner == NULL) indef_owner = InformParser;
                   }
               }

               if (type == light)
                   indef_type = indef_type | LIT_BIT;
               if (type == -light)
                   indef_type = indef_type | UNLIT_BIT;
           }

       if (o==OTHER1__WD or OTHER2__WD or OTHER3__WD)
                            { indef_mode=1; flag=1;
                              indef_type = indef_type | OTHER_BIT; }
       if (o==ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD)
                            { indef_mode=1; flag=1; indef_wanted=100;
                              if (take_all_rule == 1)
                                  take_all_rule = 2;
                              indef_type = indef_type | PLURAL_BIT; }
       if (allow_plurals && allow_multiple)
       {   n=TryNumber(wn-1);
           if (n==1)        { indef_mode=1; flag=1; }
           if (n>1)         { indef_guess_p=1;
                              indef_mode=1; flag=1; indef_wanted=n;
                              indef_nspec_at=wn-1;
                              indef_type = indef_type | PLURAL_BIT; }
       }
       if (flag==1
           && NextWordStopped() ~= OF1__WD or OF2__WD or OF3__WD or OF4__WD)
           wn--;  ! Skip 'of' after these
   }
   wn--;
   if ((indef_wanted > 0) && (~~allow_multiple)) return MULTI_PE;
   return 0;
];

! ----------------------------------------------------------------------------
!  CreatureTest: Will this person do for a "creature" token?
! ----------------------------------------------------------------------------

[ CreatureTest obj;
  if (obj has animate) rtrue;
  if (obj hasnt talkable) rfalse;
  if (action_to_be == ##Ask or ##Answer or ##Tell or ##AskFor) rtrue;
  rfalse;
];

[ PrepositionChain wd index;
  if (line_tdata-->index == wd) return wd;
  if ((line_token-->index)->0 & $20 == 0) return -1;
  do
  {   if (line_tdata-->index == wd) return wd;
      index++;
  }
  until ((line_token-->index == ENDIT_TOKEN)
         || (((line_token-->index)->0 & $10) == 0));
  return -1;
];

! ----------------------------------------------------------------------------
!  ParseToken(type, data):
!      Parses the given token, from the current word number wn, with exactly
!      the specification of a general parsing routine.
!      (Except that for "topic" tokens and prepositions, you need to supply
!      a position in a valid grammar line as third argument.)
!
!  Returns:
!    GPR_REPARSE  for "reconstructed input, please re-parse from scratch"
!    GPR_PREPOSITION  for "token accepted with no result"
!    $ff00 + x    for "please parse ParseToken(ELEMENTARY_TT, x) instead"
!    0            for "token accepted, result is the multiple object list"
!    1            for "token accepted, result is the number in parsed_number"
!    object num   for "token accepted with this object as result"
!    -1           for "token rejected"
!
!  (A)            Analyse the token; handle all tokens not involving
!                 object lists and break down others into elementary tokens
!  (B)            Begin parsing an object list
!  (C)            Parse descriptors (articles, pronouns, etc.) in the list
!  (D)            Parse an object name
!  (E)            Parse connectives ("and", "but", etc.) and go back to (C)
!  (F)            Return the conclusion of parsing an object list
! ----------------------------------------------------------------------------

[ ParseToken given_ttype given_tdata token_n x y;
  x = lookahead; lookahead = NOUN_TOKEN;
  y = ParseToken__(given_ttype,given_tdata,token_n);
  if (y == GPR_REPARSE) Tokenise__(buffer,parse);
  lookahead = x; return y;
];

[ ParseToken__ given_ttype given_tdata token_n
             token l o i j k and_parity single_object desc_wn many_flag
             token_allows_multiple;

   !Bugfix, Inform Patches ["Read" verb on out-of-scope objects]
   if (o==-1 && (line_ttype-->(token_n+1) == PREPOSITION_TT))
       return GPR_FAIL;    ! don't infer if required preposition is absent
   
   !  **** (A) ****

   token_filter = 0;

   switch(given_ttype)
   {   ELEMENTARY_TT:
           switch(given_tdata)
           {   SPECIAL_TOKEN:
                   l=TryNumber(wn);
                   special_word=NextWord();
                   #ifdef DEBUG;
                   if (l~=-1000)
                       if (parser_trace>=3)
                           print "  [Read special as the number ", l, "]^";
                   #endif;
                   if (l==-1000)
                   {   #ifdef DEBUG;
                       if (parser_trace>=3)
                         print "  [Read special word at word number ", wn, "]^";
                       #endif;
                       l = special_word;
                   }
                   parsed_number = l; return GPR_NUMBER;

               NUMBER_TOKEN:
                   l=TryNumber(wn++);
                   if (l==-1000) { etype=NUMBER_PE; return GPR_FAIL; } 
                   #ifdef DEBUG;
                   if (parser_trace>=3) print "  [Read number as ", l, "]^";
                   #endif;
                   parsed_number = l; return GPR_NUMBER;

               CREATURE_TOKEN:
                   if (action_to_be==##Answer or ##Ask or ##AskFor or ##Tell)
                       scope_reason = TALKING_REASON;

               TOPIC_TOKEN:
                   consult_from = wn;
                   if ((line_ttype-->(token_n+1) ~= PREPOSITION_TT)
                       && (line_token-->(token_n+1) ~= ENDIT_TOKEN))
                       RunTimeError(13);
                   do o=NextWordStopped();
                   until (o==-1 || PrepositionChain(o, token_n+1) ~= -1);
                   wn--;
                   consult_words = wn-consult_from;
                   if (consult_words==0) return GPR_FAIL;
                   if (action_to_be==##Ask or ##Answer or ##Tell)
                   {   o=wn; wn=consult_from; parsed_number=NextWord();
                       #IFDEF EnglishNaturalLanguage;
                       if (parsed_number=='the' && consult_words>1)
                           parsed_number=NextWord();
                       #ENDIF;
                       wn=o; return 1;
                   }
                   return GPR_PREPOSITION;
           }

       PREPOSITION_TT:
           #Iffalse Grammar__Version==1;
!  Is it an unnecessary alternative preposition, when a previous choice
!  has already been matched?
           if ((token->0) & $10) return GPR_PREPOSITION;
           #Endif;

!  If we've run out of the player's input, but still have parameters to
!  specify, we go into "infer" mode, remembering where we are and the
!  preposition we are inferring...

           if (wn > num_words)
           {   if (inferfrom==0 && parameters<params_wanted)
               {   inferfrom = pcount; inferword = token;
                   pattern-->pcount = REPARSE_CODE + Dword__No(given_tdata);
               }

!  If we are not inferring, then the line is wrong...

               if (inferfrom==0) return -1;

!  If not, then the line is right but we mark in the preposition...

               pattern-->pcount = REPARSE_CODE + Dword__No(given_tdata);
               return GPR_PREPOSITION;
           }

           o = NextWord();

           pattern-->pcount = REPARSE_CODE + Dword__No(o);

!  Whereas, if the player has typed something here, see if it is the
!  required preposition... if it's wrong, the line must be wrong,
!  but if it's right, the token is passed (jump to finish this token).

           if (o == given_tdata) return GPR_PREPOSITION;
           #Iffalse Grammar__Version==1;
           if (PrepositionChain(o, token_n) ~= -1)
               return GPR_PREPOSITION;
           #Endif;
           return -1;

       GPR_TT:
           l=indirect(given_tdata);
           #ifdef DEBUG;
           if (parser_trace>=3)
               print "  [Outside parsing routine returned ", l, "]^";
           #endif;
           return l;

       SCOPE_TT:
           scope_token = given_tdata;
           scope_stage = 1;
           l = indirect(scope_token);
           #ifdef DEBUG;
           if (parser_trace>=3)
               print "  [Scope routine returned multiple-flag of ", l, "]^";
           #endif;
           if (l==1) given_tdata = MULTI_TOKEN; else given_tdata = NOUN_TOKEN;

       ATTR_FILTER_TT:
           token_filter = 1 + given_tdata;
           given_tdata = NOUN_TOKEN;

       ROUTINE_FILTER_TT:
           token_filter = given_tdata;
           given_tdata = NOUN_TOKEN;
   }

   token = given_tdata;

!  **** (B) ****

!  There are now three possible ways we can be here:
!      parsing an elementary token other than "special" or "number";
!      parsing a scope token;
!      parsing a noun-filter token (either by routine or attribute).
!
!  In each case, token holds the type of elementary parse to
!  perform in matching one or more objects, and
!  token_filter is 0 (default), an attribute + 1 for an attribute filter
!  or a routine address for a routine filter.

   token_allows_multiple = false;
   if (token == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN
                or MULTIINSIDE_TOKEN) token_allows_multiple = true;

   many_flag = false; and_parity = true; dont_infer = false;

!  **** (C) ****
!  We expect to find a list of objects next in what the player's typed.

  .ObjectList;

   #ifdef DEBUG;
   if (parser_trace>=3) print "  [Object list from word ", wn, "]^";
   #endif;

!  Take an advance look at the next word: if it's "it" or "them", and these
!  are unset, set the appropriate error number and give up on the line
!  (if not, these are still parsed in the usual way - it is not assumed
!  that they still refer to something in scope)

    o=NextWord(); wn--;

    pronoun_word = NULL; pronoun_obj = NULL;
    l = PronounValue(o);
    if (l ~= 0)
    {   pronoun_word = o; pronoun_obj = l;
        if (l == NULL)
        {   !   Don't assume this is a use of an unset pronoun until the
            !   descriptors have been checked, because it might be an
            !   article (or some such) instead

            for (l=1:l<=LanguageDescriptors-->0:l=l+4)
                if (o == LanguageDescriptors-->l) jump AssumeDescriptor;
            pronoun__word=pronoun_word; pronoun__obj=pronoun_obj;
            etype=VAGUE_PE; return GPR_FAIL; 
        }
    }

    .AssumeDescriptor;

    if (o==ME1__WD or ME2__WD or ME3__WD)
    {   pronoun_word = o; pronoun_obj = player;
    }

    allow_plurals = true; desc_wn = wn;

    .TryAgain;
!   First, we parse any descriptive words (like "the", "five" or "every"):
    l = Descriptors(token_allows_multiple); 
    if (l~=0) { etype=l; return GPR_FAIL; }

    .TryAgain2;

!  **** (D) ****

!  This is an actual specified object, and is therefore where a typing error
!  is most likely to occur, so we set:

    oops_from = wn;

!  So, two cases.  Case 1: token not equal to "held" (so, no implicit takes)
!  but we may well be dealing with multiple objects

!  In either case below we use NounDomain, giving it the token number as
!  context, and two places to look: among the actor's possessions, and in the
!  present location.  (Note that the order depends on which is likeliest.)

    if (token ~= HELD_TOKEN)
    {   i=multiple_object-->0;
        #ifdef DEBUG;
        if (parser_trace>=3)
            print "  [Calling NounDomain on location and actor]^";
        #endif;
        l=NounDomain(actors_location, actor, token);
        if (l==REPARSE_CODE) return l;                  ! Reparse after Q&A
        if (l==0) {   if (indef_possambig)
                      {   ResetDescriptors(); wn = desc_wn; jump TryAgain2; }
                      etype=CantSee(); jump FailToken; } ! Choose best error

        #ifdef DEBUG;
        if (parser_trace>=3)
        {   if (l>1)
                print "  [ND returned ", (the) l, "]^";
            else
            {   print "  [ND appended to the multiple object list:^";
                k=multiple_object-->0;
                for (j=i+1:j<=k:j++)
                    print "  Entry ", j, ": ", (The) multiple_object-->j,
                          " (", multiple_object-->j, ")^";
                print "  List now has size ", k, "]^";
            }
        }
        #endif;

        if (l==1)
        {   if (~~many_flag)
            {   many_flag = true;
            }
            else                                  ! Merge with earlier ones
            {   k=multiple_object-->0;            ! (with either parity)
                multiple_object-->0 = i;
                for (j=i+1:j<=k:j++)
                {   if (and_parity) MultiAdd(multiple_object-->j);
                    else MultiSub(multiple_object-->j);
                }
                #ifdef DEBUG;
                if (parser_trace>=3)
                    print "  [Merging ", k-i, " new objects to the ",
                        i, " old ones]^";
                #endif;
            }
        }
        else
        {   ! A single object was indeed found

            if (match_length == 0 && indef_possambig)
            {   !   So the answer had to be inferred from no textual data,
                !   and we know that there was an ambiguity in the descriptor
                !   stage (such as a word which could be a pronoun being
                !   parsed as an article or possessive).  It's worth having
                !   another go.

                ResetDescriptors(); wn = desc_wn; jump TryAgain2;
            }
        
            if (token==CREATURE_TOKEN && CreatureTest(l)==0)
            {   etype=ANIMA_PE; jump FailToken; } !  Animation is required

            if (~~many_flag)
                single_object = l;
            else
            {   if (and_parity) MultiAdd(l); else MultiSub(l);
                #ifdef DEBUG;
                if (parser_trace>=3)
                    print "  [Combining ", (the) l, " with list]^";
                #endif;
            }
        }
    }

!  Case 2: token is "held" (which fortunately can't take multiple objects)
!  and may generate an implicit take

    else

    {   l=NounDomain(actor,actors_location,token);       ! Same as above...
        if (l==REPARSE_CODE) return GPR_REPARSE;
        if (l==0)
        {   if (indef_possambig)
            {   ResetDescriptors(); wn = desc_wn; jump TryAgain2; }
            etype=CantSee(); jump FailToken;              ! geändert durch Max Kalus
!            etype=CantSee(); return GPR_FAIL;            ! Choose best error
        }

!  ...until it produces something not held by the actor.  Then an implicit
!  ...until it produces something not held by the actor.  Then an implicit
!  take must be tried.  If this is already happening anyway, things are too
!  confused and we have to give up (but saving the oops marker so as to get
!  it on the right word afterwards).
!  The point of this last rule is that a sequence like
!
!      > read newspaper
!      (taking the newspaper first)
!      The dwarf unexpectedly prevents you from taking the newspaper!
!
!  should not be allowed to go into an infinite repeat - read becomes
!  take then read, but take has no effect, so read becomes take then read...
!  Anyway for now all we do is record the number of the object to take.

        o=parent(l);
        if (o~=actor)
        {   if (notheld_mode==1)
            {   saved_oops=oops_from; etype=NOTHELD_PE; jump FailToken;
            }
            not_holding = l;
            #ifdef DEBUG;
            if (parser_trace>=3)
                print "  [Allowing object ", (the) l, " for now]^";
            #endif;
        }
        single_object = l;
    }

!  The following moves the word marker to just past the named object...

    wn = oops_from + match_length;

!  **** (E) ****

!  Object(s) specified now: is that the end of the list, or have we reached
!  "and", "but" and so on?  If so, create a multiple-object list if we
!  haven't already (and are allowed to).

    .NextInList;

    o=NextWord();

    if (o==AND1__WD or AND2__WD or AND3__WD or BUT1__WD or BUT2__WD or BUT3__WD
           or comma_word)
    {
        #ifdef DEBUG;
        if (parser_trace>=3) print "  [Read connective '", (address) o, "']^";
        #endif;

        if (~~token_allows_multiple)
        {   etype=MULTI_PE; jump FailToken;
        }

        if (o==BUT1__WD or BUT2__WD or BUT3__WD) and_parity = 1-and_parity;

        if (~~many_flag)
        {   multiple_object-->0 = 1;
            multiple_object-->1 = single_object;
            many_flag = true;
            #ifdef DEBUG;
            if (parser_trace>=3)
                print "  [Making new list from ", (the) single_object, "]^";
            #endif;
        }
        dont_infer = true; inferfrom=0;           ! Don't print (inferences)
        jump ObjectList;                          ! And back around
    }

    wn--;   ! Word marker back to first not-understood word

!  **** (F) ****

!  Happy or unhappy endings:

    .PassToken;

    if (many_flag)
    {   single_object = GPR_MULTIPLE;
        multi_context = token;
    }
    else
    {   if (indef_mode==1 && indef_type & PLURAL_BIT ~= 0)
        {   if (indef_wanted<100 && indef_wanted>1)
            {   multi_had=1; multi_wanted=indef_wanted;
                etype=TOOFEW_PE;
                jump FailToken;
            }
        }
    }
    return single_object;

    .FailToken;

!  If we were only guessing about it being a plural, try again but only
!  allowing singulars (so that words like "six" are not swallowed up as
!  Descriptors)

    if (allow_plurals && indef_guess_p==1)
    {   allow_plurals=false; wn=desc_wn; jump TryAgain;
    }
    return -1;
];

! ----------------------------------------------------------------------------
!  NounDomain does the most substantial part of parsing an object name.
!
!  It is given two "domains" - usually a location and then the actor who is
!  looking - and a context (i.e. token type), and returns:
!
!   0    if no match at all could be made,
!   1    if a multiple object was made,
!   k    if object k was the one decided upon,
!   REPARSE_CODE if it asked a question of the player and consequently rewrote
!        the player's input, so that the whole parser should start again
!        on the rewritten input.
!
!   In the case when it returns 1<k<REPARSE_CODE, it also sets the variable
!   length_of_noun to the number of words in the input text matched to the
!   noun.
!   In the case k=1, the multiple objects are added to multiple_object by
!   hand (not by MultiAdd, because we want to allow duplicates).
! ----------------------------------------------------------------------------

[ NounDomain domain1 domain2 context    first_word i j k l
                                        answer_words marker;

#ifdef DEBUG;
  if (parser_trace>=4)
  {   print "   [NounDomain called at word ", wn, "^";
      print "   ";
      if (indef_mode)
      {   print "seeking indefinite object: ";
          if (indef_type & OTHER_BIT)  print "other ";
          if (indef_type & MY_BIT)     print "my ";
          if (indef_type & THAT_BIT)   print "that ";
          if (indef_type & PLURAL_BIT) print "plural ";
          if (indef_type & LIT_BIT)    print "lit ";
          if (indef_type & UNLIT_BIT)  print "unlit ";
          if (indef_owner ~= 0) print "owner:", (name) indef_owner;
          new_line;
          print "   number wanted: ";
          if (indef_wanted == 100) print "all"; else print indef_wanted;
          new_line;
          print "   most likely GNAs of names: ", indef_cases, "^";
      }
      else print "seeking definite object^";
  }
#endif;

  match_length=0; number_matched=0; match_from=wn; placed_in_flag=0;

  SearchScope(domain1, domain2, context);

#ifdef DEBUG;
  if (parser_trace>=4) print "   [ND made ", number_matched, " matches]^";
#endif;

  wn=match_from+match_length;

!  If nothing worked at all, leave with the word marker skipped past the
!  first unmatched word...

  if (number_matched==0) { wn++; rfalse; }

!  Suppose that there really were some words being parsed (i.e., we did
!  not just infer).  If so, and if there was only one match, it must be
!  right and we return it...

  if (match_from <= num_words)
  {
      if (number_matched==1) {
      	  i=match_list-->0;
	  jump ND_Set_changing_gender;
      }

!  ...now suppose that there was more typing to come, i.e. suppose that
!  the user entered something beyond this noun.  If nothing ought to follow,
!  then there must be a mistake, (unless what does follow is just a full
!  stop, and or comma)

      if (wn<=num_words)
      {   i=NextWord(); wn--;
          if (i ~=  AND1__WD or AND2__WD or AND3__WD or comma_word
                 or THEN1__WD or THEN2__WD or THEN3__WD
                 or BUT1__WD or BUT2__WD or BUT3__WD)
          {   if (lookahead==ENDIT_TOKEN) rfalse;
          }
      }
  }

!  Now look for a good choice, if there's more than one choice...

  number_of_classes=0;
  
  if (number_matched==1) i=match_list-->0;
  if (number_matched>1)
  {   i=Adjudicate(context);
      if (i==-1) rfalse;
      if (i==1) rtrue;       !  Adjudicate has made a multiple
                             !  object, and we pass it on
  }

!  If i is non-zero here, one of two things is happening: either
!  (a) an inference has been successfully made that object i is
!      the intended one from the user's specification, or
!  (b) the user finished typing some time ago, but we've decided
!      on i because it's the only possible choice.
!  In either case we have to keep the pattern up to date,
!  note that an inference has been made and return.
!  (Except, we don't note which of a pile of identical objects.)

  if (i~=0)
  {   if (dont_infer) jump ND_Set_changing_gender;
      if (inferfrom==0) inferfrom=pcount;
      pattern-->pcount = i;
      jump ND_Set_changing_gender;
  }

!  If we get here, there was no obvious choice of object to make.  If in
!  fact we've already gone past the end of the player's typing (which
!  means the match list must contain every object in scope, regardless
!  of its name), then it's foolish to give an enormous list to choose
!  from - instead we go and ask a more suitable question...

  if (match_from > num_words) jump Incomplete;

!  Now we print up the question, using the equivalence classes as worked
!  out by Adjudicate() so as not to repeat ourselves on plural objects...

  if (context==CREATURE_TOKEN)
      L__M(##Miscellany, 45); else L__M(##Miscellany, 46);

  j=number_of_classes; marker=0;
  for (i=1:i<=number_of_classes:i++)
  {   
      while (((match_classes-->marker) ~= i)
             && ((match_classes-->marker) ~= -i)) marker++;
      k=match_list-->marker;

      if (match_classes-->marker > 0) print (den) k; else print (einen) k;

      if (i<j-1)  print ", ";
      if (i==j-1) print (string) OR__TX;
  }
  print "?^";

!  ...and get an answer:

  .WhichOne;
#ifdef TARGET_ZCODE;
  for (i=2:i<INPUT_BUFFER_LEN:i++) buffer2->i=' ';
#endif; ! TARGET_ZCODE
  
  answer_words=Keyboard(buffer2, parse2, 1); !Geändert durch Max Kalus

!  Conveniently, parse2-->1 is the first word in both ZCODE and GLULX.
  first_word=(parse2-->1);

!  Take care of "all", because that does something too clever here to do
!  later on:

  if (first_word == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD)
  {   
      if (context == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN
                     or MULTIINSIDE_TOKEN)
      {   l=multiple_object-->0;
          for (i=0:i<number_matched && l+i<63:i++)
          {   k=match_list-->i;
              multiple_object-->(i+1+l) = k;
          }
          multiple_object-->0 = i+l;
          rtrue;
      }
      L__M(##Miscellany, 47);
      jump WhichOne;
  }

!  If the first word of the reply can be interpreted as a verb, then
!  assume that the player has ignored the question and given a new
!  command altogether.
!  (This is one time when it's convenient that the directions are
!  not themselves verbs - thus, "north" as a reply to "Which, the north
!  or south door" is not treated as a fresh command but as an answer.)

  #ifdef LanguageIsVerb;
  if (first_word==0)
  {   j = wn; first_word=LanguageIsVerb(buffer2, parse2, 1); wn = j;
  }
  #endif;
  if (first_word ~= 0)
  {   j=first_word->#dict_par1;
      if ((0~=j&1) && (first_word ~= 'long' or 'short' or 'normal'
                                     or 'brief' or 'full' or 'verbose'))
      {   CopyBuffer(buffer, buffer2);
          return REPARSE_CODE;
      }
  }

!  Now we insert the answer into the original typed command, as
!  words additionally describing the same object
!  (eg, > take red button
!       Which one, ...
!       > music
!  becomes "take music red button".  The parser will thus have three
!  words to work from next time, not two.)

#ifdef TARGET_ZCODE;

  k = WordAddress(match_from) - buffer; l=buffer2->1+1; 
  for (j=buffer + buffer->0 - 1: j>= buffer+k+l: j--)
      j->0 = 0->(j-l);
  for (i=0:i<l:i++) buffer->(k+i) = buffer2->(2+i);
  buffer->(k+l-1) = ' ';
  buffer->1 = buffer->1 + l;
  if (buffer->1 >= (buffer->0 - 1)) buffer->1 = buffer->0;

#ifnot; ! TARGET_GLULX

  k = WordAddress(match_from) - buffer;
  l = (buffer2-->0) + 1;
  for (j=buffer+INPUT_BUFFER_LEN-1 : j >= buffer+k+l : j--)
      j->0 = j->(-l);
  for (i=0:i<l:i++) 
      buffer->(k+i) = buffer2->(WORDSIZE+i);
  buffer->(k+l-1) = ' ';
  buffer-->0 = buffer-->0 + l;
  if (buffer-->0 > (INPUT_BUFFER_LEN-WORDSIZE)) 
      buffer-->0 = (INPUT_BUFFER_LEN-WORDSIZE);

#endif; ! TARGET_

!  Having reconstructed the input, we warn the parser accordingly
!  and get out.

    for(j=0:j<5:j++)
	if (ND_answer_words -> (2*j) == 0 or match_from)
	{
	    ND_answer_words -> (2*j) = match_from;
	    break;
	}
#ifdef DEBUG;
    if (parser_trace>=5)
	print "  [ND_answer_words found/created entry at index ",j,"]^";
#endif;
    
    if (j>=5) 
    {
	print "** Runtime Error: ND_answer_words overflow!";
	j = 4;
    }
	
    ND_answer_words -> (2*j+1) = ND_answer_words -> (2*j+1) +
	answer_words;
    !Addieren, da mehrmals an der gleichen Stelle nachgefragt werden kann.
    
  return REPARSE_CODE;

!  Now we come to the question asked when the input has run out
!  and can't easily be guessed (eg, the player typed "take" and there
!  were plenty of things which might have been meant).

  .Incomplete;

  if (context==CREATURE_TOKEN)
      L__M(##Miscellany, 48); else L__M(##Miscellany, 49);

#ifdef TARGET_ZCODE;
  for (i=2:i<INPUT_BUFFER_LEN:i++) buffer2->i=' ';
#endif; ! TARGET_ZCODE
  answer_words=Keyboard(buffer2, parse2, 1); !Geändert durch Max Kalus

  first_word=(parse2-->1);
  #ifdef LanguageIsVerb;
  if (first_word==0)
  {   j = wn; first_word=LanguageIsVerb(buffer2, parse2, 1); wn = j;
  }
  #endif;

!  Once again, if the reply looks like a command, give it to the
!  parser to get on with and forget about the question...

  if (first_word ~= 0)
  {   j=first_word->#dict_par1;
      if (0~=j&1)
      {   CopyBuffer(buffer, buffer2);
          return REPARSE_CODE;
      }
  }

!  ...but if we have a genuine answer, then:
!
!  (1) we must glue in text suitable for anything that's been inferred.

  if (inferfrom ~= 0)
  {   for (j = inferfrom: j<pcount: j++)
      {   if (pattern-->j == PATTERN_NULL) continue;
#ifdef TARGET_ZCODE;
          i=2+buffer->1; (buffer->1)++; buffer->(i++) = ' ';
#ifnot; ! TARGET_GLULX
          i = WORDSIZE + buffer-->0;
          (buffer-->0)++; buffer->(i++) = ' ';
#endif; ! TARGET_
    
          if (parser_trace >= 5)
          print "[Gluing in inference with pattern code ", pattern-->j, "]^";

! Conveniently, parse2-->1 is the first word in both ZCODE and GLULX.

          parse2-->1 = 0;

          ! An inferred object.  Best we can do is glue in a pronoun.
          ! (This is imperfect, but it's very seldom needed anyway.)

          if (pattern-->j >= 2 && pattern-->j < REPARSE_CODE)
          {   PronounNotice(pattern-->j);
              for (k=1: k<=LanguagePronouns-->0: k=k+3)
                  if (pattern-->j == LanguagePronouns-->(k+2))
                  {   parse2-->1 = LanguagePronouns-->k;
                      if (parser_trace >= 5)
                      print "[Using pronoun '", (address) parse2-->1, "']^";
                      break;
                  }
          }
          else
          {   ! An inferred preposition.
              parse2-->1 = No__Dword(pattern-->j - REPARSE_CODE);
              if (parser_trace >= 5)
                  print "[Using preposition '", (address) parse2-->1, "']^";
          }
    
          ! parse2-->1 now holds the dictionary address of the word to glue in.
    
          if (parse2-->1 ~= 0)
          {   k = buffer + i;
#ifdef TARGET_ZCODE;
              @output_stream 3 k;
              print (address) parse2-->1;
              @output_stream -3;
              k = k-->0;
              for (l=i:l<i+k:l++) buffer->l = buffer->(l+2);
              i = i + k; buffer->1 = i-2;
#ifnot; ! TARGET_GLULX
              k = PrintAnyToArray(buffer+i, INPUT_BUFFER_LEN-i, parse2-->1);
              i = i + k; buffer-->0 = i - WORDSIZE;
#endif; ! TARGET_
          }
      }
  }

!  (2) we must glue the newly-typed text onto the end.

#ifdef TARGET_ZCODE;
  i=2+buffer->1; (buffer->1)++; buffer->(i++) = ' ';
  for (j=0: j<buffer2->1: i++, j++)
  {   buffer->i = buffer2->(j+2);
      (buffer->1)++;
      if (buffer->1 == INPUT_BUFFER_LEN) break;
  }    
#ifnot; ! TARGET_GLULX
  i = WORDSIZE + buffer-->0;
  (buffer-->0)++; buffer->(i++) = ' ';
  for (j=0: j<buffer2-->0: i++, j++)
  {   buffer->i = buffer2->(j+WORDSIZE);
      (buffer-->0)++;
      if (buffer-->0 == INPUT_BUFFER_LEN) break;
  }    
#endif; ! TARGET_

#ifdef TARGET_ZCODE;

!  (3) we fill up the buffer with spaces, which is unnecessary, but may
!      help incorrectly-written interpreters to cope.

  for (:i<INPUT_BUFFER_LEN:i++) buffer->i = ' ';

#endif; ! TARGET_ZCODE
 
  return REPARSE_CODE;

.ND_Set_changing_gender;
  !Wenn wir ein einzelnen Objekt vor uns haben, das SAGs (Syn. anderen
  ! Geschlechts) hat, müssen wir noch einiges tun
  if (i>1 && i<REPARSE_CODE && i provides changing_gender)
  {
      !Zuerst schauen wir, ob das erste Wort, das dieses Objekt
      !beschreibt, ein Pronomen ist
      l = wn;
      wn = match_from;
      j = NounWord();
      wn = l;

      if (j>1&&j<256)
      {
	  !Wir haben hier ein Pronomen
	  if (PowersOfTwo_TB-->(GetGNAOfObject(i)) &
	      LanguagePronouns-->(j-1))
	      !Standardgeschlecht passt, also nehmen wir es
	      i.changing_gender = 0;
	  else {
	      !Passt nicht, also Geschlecht des Synonyms nehmen,
	      !aufgrundessen dieses Pronomen überhaupt mit diesem
	      !Objekt verbunden wurde
	      i.changing_gender = LP_Changing_Gender_gender ->((j-1)/3);
	  }
	  !Wir sind dann hier fertig
	  return i;
      }
      ! Kein Pronomen, hier fängt die ganze Arbeit an:

      ! Nun haben wir das richtige Objekt und können anhand der
      ! Worte das Geschlecht bestimmen. Wir ignorieren dabei die
      ! Wörter der erste Eingabe, falls nachgefragt werden musste,
      ! da sonst z.B. folgendes passieren kann:
      !   > x tor
      !   Was meinst du, die grüne Tür oder die rote Tür?
      !   > rot
      ! Dies wird jetzt zu "x rot tor" gemacht, wenn wir "tor"
      ! nicht ignorieren, bekommen wir female und nicht default/male.
      ! (Die Frage impliziert ja schließlich Tür, nicht Tor)

      changed_gender = 0;
      l=wn-match_from;
      !Zuerst suchen wir in unsere Speicherliste, ob es bei diesem
      !Objekt nachfragen gab, und wenn ja, aus wievielen Wörtern die
      !Nachfrage besteht, da wir nur diese beachten (siehe oben)
      for (k=0:k<5:k++)
      {
	  if (ND_answer_words -> (2*k) == 0)
	      break;
	  if (ND_answer_words -> (2*k) == match_from)
	  {
	      l = ND_answer_words -> (2*k+1);
	      break;
	  }
      }
      if (i.parse_name == 0)
      {
	  ! Das Objekt hat keine parse_name Routine bzw. diese
	  ! will/kann nicht entscheiden, ob dieses Objekt gemeint ist.
	  .ND_pn_doesnt_want_to_decide;
#ifdef DEBUG;
#ifdef TARGET_ZCODE;	  
	  if (parser_trace>=3)
	      print "   [set gender: searching from word
		  #",match_from," '",(address)
		      parse-->(2*match_from-1),"' to word
			  #",match_from+l-1," '",(address)
			      parse-->(2*(match_from+l-1)-1),"'";
#ifnot; !GLULX
	  if (parser_trace>=3)
	      print "   [set gender: searching from word
		  #",match_from," '",(address)
		      parse-->(3*match_from-2),"' to word
			  #",match_from+l-1," '",(address)
			      parse-->(3*(match_from+l-1)-2),"'";
#endif;	!TARGET_
#endif; !DEBUG
	  !Refers setzt changed_gender
	  for(j=match_from:j<match_from+l:j++)
	      Refers(i,j);
      } else {
	  !Objekt hat eine parse_name Routine. Wir rufen diese auf und
	  !erwarten, dass diese ggf. changed_gender setzt und daß sie
	  !die Anzahl der gelesenen Wörter zurückgibt
#ifdef DEBUG;
	  if (parser_trace>=3)
	      print "   [set gender: calling parse_name
		  from word #",match_from," with total number of
		  words set to ",match_from+l-1;
#endif; !DEBUG
#ifdef TARGET_ZCODE;
	  j = parse->1;
	  parse->1 = match_from+l-1;
	  @push wn;
#ifnot;
	  j = parse-->0;
	  parse-->0 = match_from+l-1;
	  @copy wn sp;
#endif;
	  wn = match_from;
	  k = RunRoutines(i,parse_name);
	  
#ifdef TARGET_ZCODE;
	  parse->1 = j;
	  @pull wn;
#ifnot;
	  parse-->0 = j;
	  @copy sp wn;
#endif;
	  if (k<0)
	  {
#ifdef DEBUG;
	      if (parser_trace>=3)
		      print ". It said it doesn't want to decide. Falling
		      back to normal operation]^";
#endif;
	      jump ND_pn_doesnt_want_to_decide;
	  }
      }
      ! Jetzt müssen wir nur noch dem Objekt das neue Geschlecht
      ! 'mitteilen' und wir sind fertig
      i.changing_gender = changed_gender;
#ifdef DEBUG;
      if (parser_trace>=3){
	  print " resulted in gender set to ";
#ifdef INFIX;
	  if (changed_gender)
	      print (InfixPrintAttribute) changed_gender;
	  else print "default";
#ifnot;
	  if (changed_gender)
	      print changed_gender;
	  else print "default";
#endif; !INFIX
	  print "]^";
      }
#endif; !DEBUG
  }
  return i;
];

! ----------------------------------------------------------------------------
!  The Adjudicate routine tries to see if there is an obvious choice, when
!  faced with a list of objects (the match_list) each of which matches the
!  player's specification equally well.
!
!  To do this it makes use of the context (the token type being worked on).
!  It counts up the number of obvious choices for the given context
!  (all to do with where a candidate is, except for 6 (animate) which is to
!  do with whether it is animate or not);
!
!  if only one obvious choice is found, that is returned;
!
!  if we are in indefinite mode (don't care which) one of the obvious choices
!    is returned, or if there is no obvious choice then an unobvious one is
!    made;
!
!  at this stage, we work out whether the objects are distinguishable from
!    each other or not: if they are all indistinguishable from each other,
!    then choose one, it doesn't matter which;
!
!  otherwise, 0 (meaning, unable to decide) is returned (but remember that
!    the equivalence classes we've just worked out will be needed by other
!    routines to clear up this mess, so we can't economise on working them
!    out).
!
!  Returns -1 if an error occurred
! ----------------------------------------------------------------------------

Constant SCORE__CHOOSEOBJ = 1000;
Constant SCORE__IFGOOD = 500;
Constant SCORE__UNCONCEALED = 100;
Constant SCORE__BESTLOC = 60;
Constant SCORE__NEXTBESTLOC = 40;
Constant SCORE__NOTCOMPASS = 20;
Constant SCORE__NOTSCENERY = 10;
Constant SCORE__NOTACTOR = 5;
Constant SCORE__GNA = 1;
Constant SCORE__DIVISOR = 20;

[ Adjudicate context i j k good_flag good_ones last n flag offset sovert;

#ifdef DEBUG;
  if (parser_trace>=4)
  {   print "   [Adjudicating match list of size ", number_matched,
          " in context ", context, "^";
      print "   ";
      if (indef_mode)
      {   print "indefinite type: ";
          if (indef_type & OTHER_BIT)  print "other ";
          if (indef_type & MY_BIT)     print "my ";
          if (indef_type & THAT_BIT)   print "that ";
          if (indef_type & PLURAL_BIT) print "plural ";
          if (indef_type & LIT_BIT)    print "lit ";
          if (indef_type & UNLIT_BIT)  print "unlit ";
          if (indef_owner ~= 0) print "owner:", (name) indef_owner;
          new_line;
          print "   number wanted: ";
          if (indef_wanted == 100) print "all"; else print indef_wanted;
          new_line;
          print "   most likely GNAs of names: ", indef_cases, "^";
      }
      else print "definite object^";
  }
#endif;

  j=number_matched-1; good_ones=0; last=match_list-->0;
  for (i=0:i<=j:i++)
  {   n=match_list-->i;
      match_scores-->i = 0;

      good_flag = false;

      switch(context) {
          HELD_TOKEN, MULTIHELD_TOKEN:
              if (parent(n)==actor) good_flag = true;
          MULTIEXCEPT_TOKEN:
              if (advance_warning == -1) {
                  good_flag = true;
              } else {
                  if (n ~= advance_warning) good_flag = true;
              }
          MULTIINSIDE_TOKEN:
              if (advance_warning == -1) {
                  if (parent(n) ~= actor) good_flag = true;
              } else {
                  if (n in advance_warning) good_flag = true;
              }
          CREATURE_TOKEN: if (CreatureTest(n)==1) good_flag = true;
          default: good_flag = true;
      }

      if (good_flag) {
          match_scores-->i = SCORE__IFGOOD;
          good_ones++; last = n;
      }
  }
  if (good_ones==1) return last;

  ! If there is ambiguity about what was typed, but it definitely wasn't
  ! animate as required, then return anything; higher up in the parser
  ! a suitable error will be given.  (This prevents a question being asked.)
  !
  if (context==CREATURE_TOKEN && good_ones==0) return match_list-->0;

  if (indef_mode==0) indef_type=0;

  ScoreMatchL(context);
  if (number_matched == 0) return -1;

  if (indef_mode == 0)
  {   !  Is there now a single highest-scoring object?
      i = SingleBestGuess();
      if (i >= 0)
      {   
#ifdef DEBUG;
          if (parser_trace>=4)
              print "   Single best-scoring object returned.]^";
#endif;
          return i;
      }
  }

  if (indef_mode==1 && indef_type & PLURAL_BIT ~= 0)
  {   if (context ~= MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN
                     or MULTIINSIDE_TOKEN)
      {   etype=MULTI_PE; return -1; }
      i=0; offset=multiple_object-->0; sovert = -1;
      for (j=BestGuess():j~=-1 && i<indef_wanted
           && i+offset<63:j=BestGuess())
      {   flag=0;
          if (j hasnt concealed && j hasnt worn) flag=1;
          if (sovert == -1) sovert = bestguess_score/SCORE__DIVISOR;
          else {
              if (indef_wanted == 100
                  && bestguess_score/SCORE__DIVISOR < sovert) flag=0;
          }
          if (context==MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN
              && parent(j)~=actor) flag=0;
          if (action_to_be == ##Take or ##Remove && parent(j)==actor) flag=0;
          k=ChooseObjects(j,flag);
          if (k==1) flag=1; else { if (k==2) flag=0; }
          if (flag==1)
          {   i++; multiple_object-->(i+offset) = j;
#ifdef DEBUG;
              if (parser_trace>=4) print "   Accepting it^";
#endif;
          }
          else
          {   i=i;
#ifdef DEBUG;
              if (parser_trace>=4) print "   Rejecting it^";
#endif;
          }
      }
      if (i<indef_wanted && indef_wanted<100)
      {   etype=TOOFEW_PE; multi_wanted=indef_wanted;
          multi_had=i;
          return -1;
      }
      multiple_object-->0 = i+offset;
      multi_context=context;
#ifdef DEBUG;
      if (parser_trace>=4)
          print "   Made multiple object of size ", i, "]^";
#endif;
      return 1;
  }

  for (i=0:i<number_matched:i++) match_classes-->i=0;

  n=1;
  for (i=0:i<number_matched:i++)
      if (match_classes-->i==0)
      {   match_classes-->i=n++; flag=0;
          for (j=i+1:j<number_matched:j++)
              if (match_classes-->j==0
                  && Identical(match_list-->i, match_list-->j)==1)
              {   flag=1;
                  match_classes-->j=match_classes-->i;
              }
          if (flag==1) match_classes-->i = 1-n;
      }
  n--; number_of_classes = n;

#ifdef DEBUG;
  if (parser_trace>=4)
  {   print "   Grouped into ", n, " possibilities by name:^";
      for (i=0:i<number_matched:i++)
          if (match_classes-->i > 0)
              print "   ", (The) match_list-->i,
                  " (", match_list-->i, ")  ---  group ",
                  match_classes-->i, "^";
  }
#endif;

  if (indef_mode == 0)
  {   if (n > 1)
      {   k = -1;
          for (i=0:i<number_matched:i++)
          {   if (match_scores-->i > k)
              {   k = match_scores-->i;
                  j = match_classes-->i; j=j*j;
                  flag = 0;
              }
              else
              if (match_scores-->i == k)
              {   if ((match_classes-->i) * (match_classes-->i) ~= j)
                      flag = 1;
              }
          }
          if (flag)
          {
#ifdef DEBUG;
              if (parser_trace>=4)
                  print "   Unable to choose best group, so ask player.]^";
#endif;
              return 0;
          }
#ifdef DEBUG;
          if (parser_trace>=4)
              print "   Best choices are all from the same group.^";
#endif;          
      }
  }

!  When the player is really vague, or there's a single collection of
!  indistinguishable objects to choose from, choose the one the player
!  most recently acquired, or if the player has none of them, then
!  the one most recently put where it is.

  if (n==1) dont_infer = true;
  return BestGuess();
];

! ----------------------------------------------------------------------------
!  ReviseMulti  revises the multiple object which already exists, in the
!    light of information which has come along since then (i.e., the second
!    parameter).  It returns a parser error number, or else 0 if all is well.
!    This only ever throws things out, never adds new ones.
! ----------------------------------------------------------------------------

[ ReviseMulti second_p  i low;

#ifdef DEBUG;
  if (parser_trace>=4)
      print "   Revising multiple object list of size ", multiple_object-->0,
            " with 2nd ", (name) second_p, "^";
#endif;

  if (multi_context==MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN)
  {   for (i=1, low=0:i<=multiple_object-->0:i++)
      {   if ( (multi_context==MULTIEXCEPT_TOKEN
                && multiple_object-->i ~= second_p)
               || (multi_context==MULTIINSIDE_TOKEN
                   && multiple_object-->i in second_p))
          {   low++; multiple_object-->low = multiple_object-->i;
          }
      }
      multiple_object-->0 = low;
  }

  if (multi_context==MULTI_TOKEN && action_to_be == ##Take)
  {   for (i=1, low=0:i<=multiple_object-->0:i++)
          if (ScopeCeiling(multiple_object-->i)==ScopeCeiling(actor))
              low++;
#ifdef DEBUG;
      if (parser_trace>=4)
          print "   Token 2 plural case: number with actor ", low, "^";
#endif;
      if (take_all_rule==2 || low>0)
      {   for (i=1, low=0:i<=multiple_object-->0:i++)
          {   if (ScopeCeiling(multiple_object-->i)==ScopeCeiling(actor))
              {   low++; multiple_object-->low = multiple_object-->i;
              }
          }
          multiple_object-->0 = low;
      }
  }

  i=multiple_object-->0;
#ifdef DEBUG;
  if (parser_trace>=4)
      print "   Done: new size ", i, "^";
#endif;
  if (i==0) return NOTHING_PE;
  return 0;
];

! ----------------------------------------------------------------------------
!  ScoreMatchL  scores the match list for quality in terms of what the
!  player has vaguely asked for.  Points are awarded for conforming with
!  requirements like "my", and so on.  Remove from the match list any
!  entries which fail the basic requirements of the descriptors.
! ----------------------------------------------------------------------------

[ ScoreMatchL context its_owner its_score obj i j threshold met a_s l_s;

!  if (indef_type & OTHER_BIT ~= 0) threshold++;
  if (indef_type & MY_BIT ~= 0)    threshold++;
  if (indef_type & THAT_BIT ~= 0)  threshold++;
  if (indef_type & LIT_BIT ~= 0)   threshold++;
  if (indef_type & UNLIT_BIT ~= 0) threshold++;
  if (indef_owner ~= nothing)      threshold++;

#ifdef DEBUG;
  if (parser_trace>=4) print "   Scoring match list: indef mode ", indef_mode,
      " type ", indef_type,
      ", satisfying ", threshold, " requirements:^";
#endif;

  a_s = SCORE__NEXTBESTLOC; l_s = SCORE__BESTLOC;
  if (context == HELD_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN) {
      a_s = SCORE__BESTLOC; l_s = SCORE__NEXTBESTLOC;
  }

  for (i=0: i<number_matched: i++) {
      obj = match_list-->i; its_owner = parent(obj); its_score=0;
      met = 0; !Bugfix, Inform Patches [Match list handled incorrectly]

!      if (indef_type & OTHER_BIT ~=0
!          &&  obj~=itobj or himobj or herobj) met++;
      if (indef_type & MY_BIT ~=0  &&  its_owner==actor) met++;
      if (indef_type & THAT_BIT ~=0  &&  its_owner==actors_location) met++;
      if (indef_type & LIT_BIT ~=0  &&  obj has light) met++;
      if (indef_type & UNLIT_BIT ~=0  &&  obj hasnt light) met++;
      if (indef_owner~=0 && its_owner == indef_owner) met++;

      if (met < threshold)
      {
#ifdef DEBUG;
          if (parser_trace >= 4)
              print "   ", (The) match_list-->i,
                    " (", match_list-->i, ") in ", (the) its_owner,
                    " is rejected (doesn't match descriptors)^";
#endif;
          match_list-->i=-1;
      }
      else
      {   its_score = 0;
          if (obj hasnt concealed) its_score = SCORE__UNCONCEALED;

          if (its_owner==actor)   its_score = its_score + a_s;
          else
          if (its_owner==actors_location) its_score = its_score + l_s;
          else
          if (its_owner~=compass) its_score = its_score + SCORE__NOTCOMPASS;

          its_score = its_score + SCORE__CHOOSEOBJ * ChooseObjects(obj, 2);

          if (obj hasnt scenery) its_score = its_score + SCORE__NOTSCENERY;
          if (obj ~= actor) its_score = its_score + SCORE__NOTACTOR;

          !   A small bonus for having the correct GNA,
          !   for sorting out ambiguous articles and the like.

          if (indef_cases & (PowersOfTwo_TB-->(GetGNAOfObject(obj))))
              its_score = its_score + SCORE__GNA;

          match_scores-->i = match_scores-->i + its_score;
#ifdef DEBUG;
          if (parser_trace >= 4)
              print "     ", (The) match_list-->i,
                    " (", match_list-->i, ") in ", (the) its_owner,
                    " : ", match_scores-->i, " points^";
#endif;
      }
  }

  for (i=0:i<number_matched:i++)
  {   while (match_list-->i == -1)
      {   if (i == number_matched-1) { number_matched--; break; }
          for (j=i:j<number_matched:j++)
          {   match_list-->j = match_list-->(j+1);
              match_scores-->j = match_scores-->(j+1);              
          }
          number_matched--;
      }
  }
];

! ----------------------------------------------------------------------------
!  BestGuess makes the best guess it can out of the match list, assuming that
!  everything in the match list is textually as good as everything else;
!  however it ignores items marked as -1, and so marks anything it chooses.
!  It returns -1 if there are no possible choices.
! ----------------------------------------------------------------------------

[ BestGuess  earliest its_score best i;

  earliest=0; best=-1;
  for (i=0:i<number_matched:i++)
  {   if (match_list-->i >= 0)
      {   its_score=match_scores-->i;
          if (its_score>best) { best=its_score; earliest=i; }
      }
  }
#ifdef DEBUG;
  if (parser_trace>=4)
  {   if (best<0)
          print "   Best guess ran out of choices^";
      else
          print "   Best guess ", (the) match_list-->earliest,
                " (", match_list-->earliest, ")^";
  }
#endif;
  if (best<0) return -1;
  i=match_list-->earliest;
  match_list-->earliest=-1;
  bestguess_score = best;
  return i;
];

! ----------------------------------------------------------------------------
!  SingleBestGuess returns the highest-scoring object in the match list
!  if it is the clear winner, or returns -1 if there is no clear winner
! ----------------------------------------------------------------------------

[ SingleBestGuess  earliest its_score best i;

  earliest=-1; best=-1000;
  for (i=0:i<number_matched:i++)
  {   its_score=match_scores-->i;
      if (its_score==best) { earliest = -1; }
      if (its_score>best) { best=its_score; earliest=match_list-->i; }
  }
  bestguess_score = best;
  return earliest;
];

! ----------------------------------------------------------------------------
!  Identical decides whether or not two objects can be distinguished from
!  each other by anything the player can type.  If not, it returns true.
! ----------------------------------------------------------------------------

[ Identical o1 o2 p1 p2 n1 n2 i j flag;

  if (o1==o2) rtrue;  ! This should never happen, but to be on the safe side
  if (o1==0 || o2==0) rfalse;  ! Similarly
  if (parent(o1)==compass || parent(o2)==compass) rfalse; ! Saves time

!  What complicates things is that o1 or o2 might have a parsing routine,
!  so the parser can't know from here whether they are or aren't the same.
!  If they have different parsing routines, we simply assume they're
!  different.  If they have the same routine (which they probably got from
!  a class definition) then the decision process is as follows:
!
!     the routine is called (with self being o1, not that it matters)
!       with noun and second being set to o1 and o2, and action being set
!       to the fake action TheSame.  If it returns -1, they are found
!       identical; if -2, different; and if >=0, then the usual method
!       is used instead.

  if (o1.parse_name~=0 || o2.parse_name~=0)
  {   if (o1.parse_name ~= o2.parse_name) rfalse;
      parser_action=##TheSame; parser_one=o1; parser_two=o2;
      j=wn; i=RunRoutines(o1,parse_name); wn=j;
      if (i==-1) rtrue; if (i==-2) rfalse;
  }

!  This is the default algorithm: do they have the same words in their
!  "name" (i.e. property no. 1) properties.  (Note that the following allows
!  for repeated words and words in different orders.)

  p1 = o1.&1; n1 = (o1.#1)/WORDSIZE;
  p2 = o2.&1; n2 = (o2.#1)/WORDSIZE;

!  for (i=0:i<n1:i++) { print (address) p1-->i, " "; } new_line;
!  for (i=0:i<n2:i++) { print (address) p2-->i, " "; } new_line;

  for (i=0:i<n1:i++)
  {   flag=0;
      for (j=0:j<n2:j++)
          if (p1-->i == p2-->j) flag=1;
      if (flag==0) rfalse;
  }

  for (j=0:j<n2:j++)
  {   flag=0;
      for (i=0:i<n1:i++)
          if (p1-->i == p2-->j) flag=1;
      if (flag==0) rfalse;
  }

!  print "Which are identical!^";
  rtrue;
];

! ----------------------------------------------------------------------------
!  PrintCommand reconstructs the command as it presently reads, from
!  the pattern which has been built up
!
!  If from is 0, it starts with the verb: then it goes through the pattern.
!  The other parameter is "emptyf" - a flag: if 0, it goes up to pcount:
!  if 1, it goes up to pcount-1.
!
!  Note that verbs and prepositions are printed out of the dictionary:
!  and that since the dictionary may only preserve the first six characters
!  of a word (in a V3 game), we have to hand-code the longer words needed.
!
!  (Recall that pattern entries are 0 for "multiple object", 1 for "special
!  word", 2 to REPARSE_CODE-1 are object numbers and REPARSE_CODE+n means the
!  preposition n)
! ----------------------------------------------------------------------------

[ PrintCommand from i k spacing_flag;

  if (from==0)
  {   i=verb_word;
      if (LanguageVerb(i) == 0)
          if (PrintVerb(i) == 0)
              print (address) i, "e";	!für Deutsche Verben, MK
      from++; spacing_flag = true;
  }

  for (k=from:k<pcount:k++)
  {   i=pattern-->k;
      if (i == PATTERN_NULL) continue;
      if (spacing_flag) print (char) ' ';
      if (i==0) { print (string) THOSET__TX; jump TokenPrinted; }
      if (i==1) { print (string) THAT__TX; jump TokenPrinted; }
      if (i>=REPARSE_CODE) print (address) No__Dword(i-REPARSE_CODE);
      !Bugfix, Inform Patches [Wrong message for GO [dir] [anything]]
      else if (i in compass) print (address) (i.&1-->1); ! the direction name
           else print (den) i;
      .TokenPrinted;
      spacing_flag = true;
  }
];

! ----------------------------------------------------------------------------
!  The CantSee routine returns a good error number for the situation where
!  the last word looked at didn't seem to refer to any object in context.
!
!  The idea is that: if the actor is in a location (but not inside something
!  like, for instance, a tank which is in that location) then an attempt to
!  refer to one of the words listed as meaningful-but-irrelevant there
!  will cause "you don't need to refer to that in this game" rather than
!  "no such thing" or "what's 'it'?".
!  (The advantage of not having looked at "irrelevant" local nouns until now
!  is that it stops them from clogging up the ambiguity-resolving process.
!  Thus game objects always triumph over scenery.)
! ----------------------------------------------------------------------------

[ CantSee  i w e;
    saved_oops=oops_from;

    if (scope_token~=0) { scope_error = scope_token; return ASKSCOPE_PE; }

    wn--; w=NextWord();
    e=CANTSEE_PE;
    if (w==pronoun_word)
    {   pronoun__word=pronoun_word; pronoun__obj=pronoun_obj;
        e=ITGONE_PE;
    }
    i=actor; while (parent(i) ~= 0) i = parent(i);
    if (i has visited && Refers(i,wn-1)==1) e=SCENERY_PE;
    if (etype>e) return etype;
    return e;
];

! ----------------------------------------------------------------------------
!  The MultiAdd routine adds object "o" to the multiple-object-list.
!
!  This is only allowed to hold 63 objects at most, at which point it ignores
!  any new entries (and sets a global flag so that a warning may later be
!  printed if need be).
! ----------------------------------------------------------------------------

[ MultiAdd o i j;
  i=multiple_object-->0;
  if (i==63) { toomany_flag=1; rtrue; }
  for (j=1:j<=i:j++)
      if (o==multiple_object-->j) 
          rtrue;
  i++;
  multiple_object-->i = o;
  multiple_object-->0 = i;
];

! ----------------------------------------------------------------------------
!  The MultiSub routine deletes object "o" from the multiple-object-list.
!
!  It returns 0 if the object was there in the first place, and 9 (because
!  this is the appropriate error number in Parser()) if it wasn't.
! ----------------------------------------------------------------------------

[ MultiSub o i j k et;
  i=multiple_object-->0; et=0;
  for (j=1:j<=i:j++)
      if (o==multiple_object-->j)
      {   for (k=j:k<=i:k++)
              multiple_object-->k = multiple_object-->(k+1);
          multiple_object-->0 = --i;
          return et;
      }
  et=9; return et;
];

! ----------------------------------------------------------------------------
!  The MultiFilter routine goes through the multiple-object-list and throws
!  out anything without the given attribute "attr" set.
! ----------------------------------------------------------------------------

[ MultiFilter attr  i j o;
  .MFiltl;
  i=multiple_object-->0;
  for (j=1:j<=i:j++)
  {   o=multiple_object-->j;
      if (o hasnt attr) { MultiSub(o); jump Mfiltl; }
  }
];

! ----------------------------------------------------------------------------
!  The UserFilter routine consults the user's filter (or checks on attribute)
!  to see what already-accepted nouns are acceptable
! ----------------------------------------------------------------------------

[ UserFilter obj;

  if (token_filter > 0 && token_filter < 49)
  {   if (obj has (token_filter-1)) rtrue;
      rfalse;
  }
  noun = obj;
  return indirect(token_filter);
];

! ----------------------------------------------------------------------------
!  MoveWord copies word at2 from parse buffer b2 to word at1 in "parse"
!  (the main parse buffer)
! ----------------------------------------------------------------------------

#ifdef TARGET_ZCODE;

[ MoveWord at1 b2 at2 x y;
  x=at1*2-1; y=at2*2-1;
  parse-->x++ = b2-->y++;
  parse-->x = b2-->y;
];

#ifnot; ! TARGET_GLULX

[ 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; ! TARGET_

! ----------------------------------------------------------------------------
!  SearchScope  domain1 domain2 context
!
!  Works out what objects are in scope (possibly asking an outside routine),
!  but does not look at anything the player has typed.
! ----------------------------------------------------------------------------

[ SearchScope domain1 domain2 context i;

  i=0;
!  Everything is in scope to the debugging commands

#ifdef DEBUG;
  if (scope_reason==PARSING_REASON
      && verb_word == 'purloin' or 'tree' or 'abstract'
                       or 'gonear' or 'scope' or 'showobj')
  {
#ifdef TARGET_ZCODE;
      for (i=selfobj:i<=top_object:i++)
          if (i ofclass Object && (parent(i)==0 || parent(i) ofclass Object))
              PlaceInScope(i);
#ifnot; ! TARGET_GLULX
      objectloop (i)
          if (i ofclass Object && (parent(i)==0 || parent(i) ofclass Object))
              PlaceInScope(i);
#endif; ! TARGET_
      rtrue;
  }
#endif;

!  First, a scope token gets priority here:

  if (scope_token ~= 0)
  {   scope_stage=2;
      if (indirect(scope_token)~=0) rtrue;
  }

!  Next, call any user-supplied routine adding things to the scope,
!  which may circumvent the usual routines altogether if they return true:

  if (actor==domain1 or domain2 && InScope(actor)~=0) rtrue;

!  Pick up everything in the location except the actor's possessions;
!  then go through those.  (This ensures the actor's possessions are in
!  scope even in Darkness.)

  if (context==MULTIINSIDE_TOKEN && advance_warning ~= -1)
  {   if (IsSeeThrough(advance_warning)==1)
          ScopeWithin(advance_warning, 0, context);
  }
  else
  {   if (domain1~=0 && domain1 has supporter or container)
          ScopeWithin_O(domain1, domain1, context);
      ScopeWithin(domain1, domain2, context);
      if (domain2~=0 && domain2 has supporter or container)
          ScopeWithin_O(domain2, domain2, context);
      ScopeWithin(domain2, 0, context);
  }

!  A special rule applies:
!  in Darkness as in light, the actor is always in scope to himself.

  if (thedark == domain1 or domain2)
  {   ScopeWithin_O(actor, actor, context);
      if (parent(actor) has supporter or container)
          ScopeWithin_O(parent(actor), parent(actor), context);
  }
];

! ----------------------------------------------------------------------------
!  IsSeeThrough is used at various places: roughly speaking, it determines
!  whether o being in scope means that the contents of o are in scope.
! ----------------------------------------------------------------------------

[ IsSeeThrough o;
  if (o has supporter
      || (o has transparent)
      || (o has container && o has open))
      rtrue;
  rfalse;
];

! ----------------------------------------------------------------------------
!  PlaceInScope is provided for routines outside the library, and is not
!  called within the parser (except for debugging purposes).
! ----------------------------------------------------------------------------

[ PlaceInScope thing;
   if (scope_reason~=PARSING_REASON or TALKING_REASON)
   {   DoScopeAction(thing); rtrue; }
   wn=match_from; TryGivenObject(thing); placed_in_flag=1;
];

! ----------------------------------------------------------------------------
!  DoScopeAction
! ----------------------------------------------------------------------------

[ DoScopeAction thing s p1;
  s = scope_reason; p1=parser_one;
#ifdef DEBUG;
  if (parser_trace>=6)
  {   print "[DSA on ", (the) thing, " with reason = ", scope_reason,
      " p1 = ", parser_one, " p2 = ", parser_two, "]^";
  }
#endif;
  switch(scope_reason)
  {   REACT_BEFORE_REASON:
          if (thing.react_before==0 or NULL) return;
#ifdef DEBUG;
          if (parser_trace>=2)
          {   print "[Considering react_before for ", (the) thing, "]^"; }
#endif;
          if (parser_one==0) parser_one = RunRoutines(thing,react_before);
      REACT_AFTER_REASON:
          if (thing.react_after==0 or NULL) return;
#ifdef DEBUG;
          if (parser_trace>=2)
          {   print "[Considering react_after for ", (the) thing, "]^"; }
#endif;
          if (parser_one==0) parser_one = RunRoutines(thing,react_after);
      EACH_TURN_REASON:
          if (thing.each_turn == 0 or NULL) return;
#ifdef DEBUG;
          if (parser_trace>=2)
          {   print "[Considering each_turn for ", (the) thing, "]^"; }
#endif;
          PrintOrRun(thing, each_turn);
      TESTSCOPE_REASON:
          if (thing==parser_one) parser_two = 1;
      LOOPOVERSCOPE_REASON:
          indirect(parser_one,thing); parser_one=p1;
  }
  scope_reason = s;
];

! ----------------------------------------------------------------------------
!  ScopeWithin looks for objects in the domain which make textual sense
!  and puts them in the match list.  (However, it does not recurse through
!  the second argument.)
! ----------------------------------------------------------------------------

[ ScopeWithin domain nosearch context x y;

   if (domain==0) rtrue;

!  Special rule: the directions (interpreted as the 12 walls of a room) are
!  always in context.  (So, e.g., "examine north wall" is always legal.)
!  (Unless we're parsing something like "all", because it would just slow
!  things down then, or unless the context is "creature".)

   if (indef_mode==0 && domain==actors_location
       && scope_reason==PARSING_REASON && context~=CREATURE_TOKEN)
           ScopeWithin(compass);

!  Look through the objects in the domain, avoiding "objectloop" in case
!  movements occur, e.g. when trying each_turn.

   x = child(domain);
   while (x ~= 0)
   {   y = sibling(x);
       ScopeWithin_O(x, nosearch, context);
       x = y;
   }
];

[ ScopeWithin_O domain nosearch context i ad n;

!  multiexcept doesn't have second parameter in scope
   if (context==MULTIEXCEPT_TOKEN && domain==advance_warning) jump DontAccept;

!  If the scope reason is unusual, don't parse.

      if (scope_reason~=PARSING_REASON or TALKING_REASON)
      {   DoScopeAction(domain); jump DontAccept; }

!  "it" or "them" matches to the it-object only.  (Note that (1) this means
!  that "it" will only be understood if the object in question is still
!  in context, and (2) only one match can ever be made in this case.)

      if (match_from <= num_words)  ! If there's any text to match, that is
      {   wn=match_from;
          i=NounWord();
          if (i==1 && player==domain)  MakeMatch(domain, 1);

          if (i>=2 && i<128 && (LanguagePronouns-->i == domain))
              MakeMatch(domain, 1);
      }

!  Construing the current word as the start of a noun, can it refer to the
!  object?

      wn = match_from;
      if (TryGivenObject(domain) > 0)
          if (indef_nspec_at>0 && match_from~=indef_nspec_at)
          {   !  This case arises if the player has typed a number in
              !  which is hypothetically an indefinite descriptor:
              !  e.g. "take two clubs".  We have just checked the object
              !  against the word "clubs", in the hope of eventually finding
              !  two such objects.  But we also backtrack and check it
              !  against the words "two clubs", in case it turns out to
              !  be the 2 of Clubs from a pack of cards, say.  If it does
              !  match against "two clubs", we tear up our original
              !  assumption about the meaning of "two" and lapse back into
              !  definite mode.
          
              wn = indef_nspec_at;
              if (TryGivenObject(domain) > 0)
              {   match_from = indef_nspec_at;
                  ResetDescriptors();                  
              }
              wn = match_from;
          }

      .DontAccept;

!  Shall we consider the possessions of the current object, as well?
!  Only if it's a container (so, for instance, if a dwarf carries a
!  sword, then "drop sword" will not be accepted, but "dwarf, drop sword"
!  will).
!  Also, only if there are such possessions.
!
!  Notice that the parser can see "into" anything flagged as
!  transparent - such as a dwarf whose sword you can get at.

      if (child(domain)~=0 && domain ~= nosearch && IsSeeThrough(domain)==1)
          ScopeWithin(domain,nosearch,context);

!  Drag any extras into context

   ad = domain.&add_to_scope;
   if (ad ~= 0)
   {   
       ! Test if the property value is not an object.
#ifdef TARGET_ZCODE;
       i = (UnsignedCompare(ad-->0,top_object) > 0);
#ifnot; ! TARGET_GLULX
       i = (((ad-->0)->0) ~= $70); 
#endif; ! TARGET_

       if (i)
       {   ats_flag = 2+context;
           RunRoutines(domain, add_to_scope);
           ats_flag = 0;
       }
       else
       {   n=domain.#add_to_scope;
           for (i=0:(WORDSIZE*i)<n:i++)
               ScopeWithin_O(ad-->i,0,context);
       }
   }
];

[ AddToScope obj;
   if (ats_flag>=2)
       ScopeWithin_O(obj,0,ats_flag-2);
   if (ats_flag==1)
   {   if  (HasLightSource(obj)==1) ats_hls = 1;
   }
];

! ----------------------------------------------------------------------------
!  MakeMatch looks at how good a match is.  If it's the best so far, then
!  wipe out all the previous matches and start a new list with this one.
!  If it's only as good as the best so far, add it to the list.
!  If it's worse, ignore it altogether.
!
!  The idea is that "red panic button" is better than "red button" or "panic".
!
!  number_matched (the number of words matched) is set to the current level
!  of quality.
!
!  We never match anything twice, and keep at most 64 equally good items.
! ----------------------------------------------------------------------------

[ MakeMatch obj quality i;
#ifdef DEBUG;
   if (parser_trace>=6) print "    Match with quality ",quality,"^";
#endif;
   if (token_filter~=0 && UserFilter(obj)==0)
   {   #ifdef DEBUG;
       if (parser_trace>=6)
       {   print "    Match filtered out: token filter ", token_filter, "^";
       }
       #endif;
       rtrue;
   }
   if (quality < match_length) rtrue;
   if (quality > match_length) { match_length=quality; number_matched=0; }
   else
   {   if (2*number_matched>=MATCH_LIST_SIZE) rtrue;
       for (i=0:i<number_matched:i++)
           if (match_list-->i==obj) rtrue;
   }
   match_list-->number_matched++ = obj;
#ifdef DEBUG;
   if (parser_trace>=6) print "    Match added to list^";
#endif;
];

! ----------------------------------------------------------------------------
!  TryGivenObject tries to match as many words as possible in what has been
!  typed to the given object, obj.  If it manages any words matched at all,
!  it calls MakeMatch to say so, then returns the number of words (or 1
!  if it was a match because of inadequate input).
! ----------------------------------------------------------------------------

[ TryGivenObject obj threshold k w j;

#ifdef DEBUG;
   if (parser_trace>=5)
       print "    Trying ", (the) obj, " (", obj, ") at word ", wn, "^";
#endif;

   dict_flags_of_noun = 0;

!  If input has run out then always match, with only quality 0 (this saves
!  time).

   if (wn > num_words)
   {   if (indef_mode ~= 0)
           dict_flags_of_noun = $$01110000;  ! Reject "plural" bit
       MakeMatch(obj,0);
       #ifdef DEBUG;
       if (parser_trace>=5)
       print "    Matched (0)^";
       #endif;
       return 1;
   }

!  Ask the object to parse itself if necessary, sitting up and taking notice
!  if it says the plural was used:

   if (obj.parse_name~=0)
   {   parser_action = NULL; j=wn;
       k=RunRoutines(obj,parse_name);
       if (k>0)
       {   wn=j+k;
           .MMbyPN;

           if (parser_action == ##PluralFound)
               dict_flags_of_noun = dict_flags_of_noun | 4;

           if (dict_flags_of_noun & 4)
           {   if (~~allow_plurals) k=0;
               else
               {   if (indef_mode==0)
                   {   indef_mode=1; indef_type=0; indef_wanted=0; }
                   indef_type = indef_type | PLURAL_BIT;
                   if (indef_wanted==0) indef_wanted=100;
               }
           }

           #ifdef DEBUG;
               if (parser_trace>=5)
               {   print "    Matched (", k, ")^";
               }
           #endif;
           MakeMatch(obj,k);
           return k;
       }
       if (k==0) jump NoWordsMatch;
   }

!  The default algorithm is simply to count up how many words pass the
!  Refers test:

   parser_action = NULL;

   w = NounWord();

   if (w==1 && player==obj) { k=1; jump MMbyPN; }

   if (w>=2 && w<128 && (LanguagePronouns-->w == obj))
   {   k=1; jump MMbyPN; }

   j=--wn;
   threshold = ParseNoun(obj);
#ifdef DEBUG;
   if (threshold>=0 && parser_trace>=5)
       print "    ParseNoun returned ", threshold, "^";
#endif;
   if (threshold<0) wn++;
   if (threshold>0) { k=threshold; jump MMbyPN; }

   if (threshold==0 || Refers(obj,wn-1)==0)
   {   .NoWordsMatch;
       if (indef_mode~=0)
       {   k=0; parser_action=NULL; jump MMbyPN;
       }
       rfalse;
   }

   if (threshold<0)
   {   threshold=1;
       dict_flags_of_noun = (w->#dict_par1) & $$01110100;
       w = NextWord();
       while (Refers(obj, wn-1))
       {   threshold++;
           if (w)
               dict_flags_of_noun = dict_flags_of_noun
                                    | ((w->#dict_par1) & $$01110100);
           w = NextWord();
       }
   }

   k = threshold; jump MMbyPN;
];

! ----------------------------------------------------------------------------
!  Refers works out whether the word at number wnum can refer to the object
!  obj, returning true or false.  The standard method is to see if the
!  word is listed under "name" for the object, but this is more complex
!  in languages other than English.
! ----------------------------------------------------------------------------

[ Refers obj wnum   wd k l m;
    if (obj==0) rfalse;

    #ifdef LanguageRefers;
    k = LanguageRefers(obj,wnum); if (k>=0) return k;
    #endif;

    k = wn; wn = wnum; wd = NextWordStopped(); wn = k;

    if (parser_inflection >= 256)
    {   k = indirect(parser_inflection, obj, wd);
        if (k>=0) return k;
        m = -k;
    } else m = parser_inflection;
    k=obj.&m; l=(obj.#m)/WORDSIZE-1;
    for (m=0:m<=l:m++)
	if(wd==k-->m) {
	    	if (m<l) {
		    m = k-->(m+1);
 		if (m<256)
		    changed_gender = m;
	    }
	    rtrue;
	}
    rfalse;
];

[ WordInProperty wd obj prop k l m;
    k=obj.&prop; l=(obj.#prop)/WORDSIZE-1;
    for (m=0:m<=l:m++)
        if (wd==k-->m) rtrue;
    rfalse;
];


#ifdef TARGET_ZCODE;

[ DictionaryLookup b l i;
  for (i=0:i<l:i++) buffer2->(2+i) = b->i;
  buffer2->1 = l;
  Tokenise__(buffer2,parse2);
  return parse2-->1;
];

#ifnot; ! TARGET_GLULX

[ DictionaryLookup b l i;
  for (i=0:i<l:i++) buffer2->(WORDSIZE+i) = b->i;
  buffer2-->0 = l;
  Tokenise__(buffer2,parse2);
  return parse2-->1;
];

#endif; ! TARGET_


! ----------------------------------------------------------------------------
!  NounWord (which takes no arguments) returns:
!
!   0  if the next word is unrecognised or does not carry the "noun" bit in
!      its dictionary entry,
!   1  if a word meaning "me",
!   the index in the pronoun table (plus 2) of the value field of a pronoun,
!      if the word is a pronoun,
!   the address in the dictionary if it is a recognised noun.
!
!  The "current word" marker moves on one.
! ----------------------------------------------------------------------------

[ NounWord i j s;
   i=NextWord();
   if (i==0) rfalse;
   if (i==ME1__WD or ME2__WD or ME3__WD) return 1;
   s = LanguagePronouns-->0;
   for (j=1 : j<=s : j=j+3)
       if (i == LanguagePronouns-->j)
           return j+2;
   if ((i->#dict_par1)&128 == 0) rfalse;
   return i;
];

! ----------------------------------------------------------------------------
!  NextWord (which takes no arguments) returns:
!
!  0            if the next word is unrecognised,
!  comma_word   if a comma
!  THEN1__WD    if a full stop
!  or the dictionary address if it is recognised.
!  The "current word" marker is moved on.
!
!  NextWordStopped does the same, but returns -1 when input has run out
! ----------------------------------------------------------------------------

#ifdef TARGET_ZCODE;

[ NextWord i j;
   if (wn > parse->1) { wn++; rfalse; }
   i=wn*2-1; wn++;
   j=parse-->i;
   if (j == ',//') j=comma_word;
   if (j == './/') j=THEN1__WD;
   return j;
];   

[ NextWordStopped;
   if (wn > parse->1) { wn++; return -1; }
   return NextWord();
];

[ WordAddress wordnum;
   return buffer + parse->(wordnum*4+1);
];

[ WordLength wordnum;
   return parse->(wordnum*4);
];

#ifnot; ! TARGET_GLULX

[ NextWord i j;
   if (wn > parse-->0) { wn++; rfalse; }
   i=wn*3-2; wn++;
   j=parse-->i;
   if (j == ',//') j=comma_word;
   if (j == './/') j=THEN1__WD;
   return j;
];   

[ NextWordStopped;
   if (wn > parse-->0) { wn++; return -1; }
   return NextWord();
];

[ WordAddress wordnum;
   return buffer + parse-->(wordnum*3);
];

[ WordLength wordnum;
   return parse-->(wordnum*3-1);
];

#endif; ! TARGET_

! ----------------------------------------------------------------------------
!  TryNumber is the only routine which really does any character-level
!  parsing, since that's normally left to the Z-machine.
!  It takes word number "wordnum" and tries to parse it as an (unsigned)
!  decimal number, returning
!
!  -1000                if it is not a number
!  the number           if it has between 1 and 4 digits
!  10000                if it has 5 or more digits.
!
!  (The danger of allowing 5 digits is that Z-machine integers are only
!  16 bits long, and anyway this isn't meant to be perfect.)
!
!  Using NumberWord, it also catches "one" up to "twenty".
!
!  Note that a game can provide a ParseNumber routine which takes priority,
!  to enable parsing of odder numbers ("x45y12", say).
! ----------------------------------------------------------------------------

[ TryNumber wordnum   i j c num len mul tot d digit;

   i=wn; wn=wordnum; j=NextWord(); wn=i;
   j=NumberWord(j); if (j>=1) return j;

#ifdef TARGET_ZCODE;
   i=wordnum*4+1; j=parse->i; num=j+buffer; len=parse->(i-1);
#ifnot; ! TARGET_GLULX
   i=wordnum*3; j=parse-->i; num=j+buffer; len=parse-->(i-1);
#endif; ! TARGET_

   tot=ParseNumber(num, len);  if (tot~=0) return tot;

   if (len>=4) mul=1000;
   if (len==3) mul=100;
   if (len==2) mul=10;
   if (len==1) mul=1;

   tot=0; c=0; len=len-1;

   for (c=0:c<=len:c++)
   {   digit=num->c;
       if (digit=='0') { d=0; jump digok; }
       if (digit=='1') { d=1; jump digok; }
       if (digit=='2') { d=2; jump digok; }
       if (digit=='3') { d=3; jump digok; }
       if (digit=='4') { d=4; jump digok; }
       if (digit=='5') { d=5; jump digok; }
       if (digit=='6') { d=6; jump digok; }
       if (digit=='7') { d=7; jump digok; }
       if (digit=='8') { d=8; jump digok; }
       if (digit=='9') { d=9; jump digok; }
       return -1000;
     .digok;
       tot=tot+mul*d; mul=mul/10;
   }
   if (len>3) tot=10000;
   return tot;
];

! ----------------------------------------------------------------------------
!  GetGender returns 0 if the given animate object is female, and 1 if male
!  (not all games will want such a simple decision function!)
! ----------------------------------------------------------------------------

[ GetGender person;
   if (person hasnt female) rtrue;
   rfalse;
];

[ GetGNAOfObject obj case gender;
   if (obj hasnt animate) case = 6;
   if (obj has male) gender = male;
   if (obj has female) gender = female;
   if (obj has neuter) gender = neuter;
   if (gender == 0)
   {   if (case == 0) gender = LanguageAnimateGender;
       else gender = LanguageInanimateGender;
   }
   if (gender == female) case = case + 1;
   if (gender == neuter) case = case + 2;
   if (obj has pluralname) case = case + 3;
   return case;
];

[ GetGNAOfObjectWithGender  obj gender case;
    !Wird für ChangingGender Objekte gebraucht, um die Pronomen
    !richtig zu setzen
   if (obj hasnt animate) case = 6;
   if (gender == female) case = case + 1;
   if (gender == neuter) case = case + 2;
   if (obj has pluralname) case = case + 3;
   return case;
];

! ----------------------------------------------------------------------------
!  Converting between dictionary addresses and entry numbers
! ----------------------------------------------------------------------------

#ifdef TARGET_ZCODE;

[ Dword__No w; return (w-(0-->4 + 7))/9; ];
[ No__Dword n; return 0-->4 + 7 + 9*n; ];

#ifnot; ! TARGET_GLULX

! In Glulx, dictionary entries *are* addresses.
[ Dword__No w; return w; ];
[ No__Dword n; return n; ];

#endif; ! TARGET_

! ----------------------------------------------------------------------------
!  For copying buffers
! ----------------------------------------------------------------------------

#ifdef TARGET_ZCODE;

[ CopyBuffer bto bfrom i size;
   size=bto->0;
   for (i=1:i<=size:i++) bto->i=bfrom->i;
];

#ifnot; ! TARGET_GLULX

[ CopyBuffer bto bfrom i;
   for (i=0:i<INPUT_BUFFER_LEN:i++) 
       bto->i=bfrom->i;
];

#endif; ! TARGET_

! ----------------------------------------------------------------------------
!  Provided for use by language definition files
! ----------------------------------------------------------------------------

#ifdef TARGET_ZCODE;

[ LTI_Insert i ch  b y;

  !   Protect us from strict mode, as this isn't an array in quite the
  !   sense it expects
      b = buffer;

  !   Insert character ch into buffer at point i.

  !   Being careful not to let the buffer possibly overflow:

      y = b->1;
      if (y > b->0) y = b->0;

  !   Move the subsequent text along one character:

      for (y=y+2: y>i : y--) b->y = b->(y-1);
      b->i = ch;

  !   And the text is now one character longer:
      if (b->1 < b->0) (b->1)++;
];

#ifnot; ! TARGET_GLULX

[ LTI_Insert i ch  b y;

  !   Protect us from strict mode, as this isn't an array in quite the
  !   sense it expects
      b = buffer;

  !   Insert character ch into buffer at point i.

  !   Being careful not to let the buffer possibly overflow:

      y = b-->0;
      if (y > INPUT_BUFFER_LEN) y = INPUT_BUFFER_LEN;

  !   Move the subsequent text along one character:

      for (y=y+WORDSIZE: y>i : y--) b->y = b->(y-1);

      b->i = ch;

  !   And the text is now one character longer:
      if (b-->0 < INPUT_BUFFER_LEN)
          (b-->0)++;
];

#endif; ! TARGET_

! ============================================================================

[ PronounsSub x y c d;	!geändert von Max Kalus

  L__M(##Pronouns, 1);

  c = (LanguagePronouns-->0)/3;
  if (player ~= selfobj) c++;

  if (c==0) return L__M(##Pronouns, 4);

  for (x = 1, d = 0 : x <= LanguagePronouns-->0: x = x+3)
  {   !print "~", (address) LanguagePronouns-->x, "~ "; !auskommentiert Max Kalus
      if (LanguagePronouns-->x == 'spez.') break;
      y = LanguagePronouns-->(x+2);
      if (y == NULL) L__M(##Pronouns, 3, LanguagePronouns-->x); !verändert Max Kalus
      else { L__M(##Pronouns, 2, LanguagePronouns-->x); print (the) y; } !verändert Max Kalus
      d++;
!      if (d < c-1) print ", "; !auskommentiert Max Kalus
!      if (d == c-1) print (string) AND__TX; !auskommentiert Max Kalus
  }
  if (player ~= selfobj)
  {   print "^~", (address) ME1__WD, "~ : "; !L__M(##Pronouns, 2); !verändert Max Kalus
      c = player; player = selfobj;
      print (the) c; player = c;
  }
  ".";
];

[ SetPronoun dword value x;
  for (x = 1 : x <= LanguagePronouns-->0: x = x+3)
      if (LanguagePronouns-->x == dword)
      {   LanguagePronouns-->(x+2) = value; return;
      }
  RunTimeError(14);
];

[ PronounValue dword x;
  for (x = 1 : x <= LanguagePronouns-->0: x = x+3)
      if (LanguagePronouns-->x == dword)
          return LanguagePronouns-->(x+2);
  return 0;
];

[ ResetVagueWords obj; PronounNotice(obj); ];

#ifdef EnglishNaturalLanguage;
[ PronounOldEnglish;
   if (itobj ~= old_itobj)   SetPronoun('it', itobj);
   if (himobj ~= old_himobj) SetPronoun('him', himobj);
   if (herobj ~= old_herobj) SetPronoun('her', herobj);
   old_itobj = itobj; old_himobj = himobj; old_herobj = herobj;
];
#endif;

[ PronounNotice obj x bm bm2 i obj2;

   if (obj == player) return;

   #ifdef EnglishNaturalLanguage;
   PronounOldEnglish();
   #endif;

    
   bm = PowersOfTwo_TB-->(GetGNAOfObject(obj));
   bm2 = PowersOfTwo_TB-->(GetGNAOfObjectWithGender(obj,obj.changing_gender));

   for (x = 1 : x <= LanguagePronouns-->0: x = x+3)
       if (bm & (LanguagePronouns-->(x+1)) ~= 0)
           LanguagePronouns-->(x+2) = obj;

   !Jetzt schauen wir, ob wir noch mehr Pronomen setzen müssen, weil
   !das Objekt mit einem Synonym anderen Geschlechts (SAG) angesprochen wurde
   if (obj.changing_gender) {
       for (x = 1 : x <= LanguagePronouns-->0: x = x+3)
	   if (bm2 & (LanguagePronouns-->(x+1)) ~= 0
	       && LanguagePronouns-->(x+2) ~= obj) {
	       i = (x-1)/3;
	       !Wir haben ein Pronomen gefunden, dass nur zum Synonym
	       !passt.

	       !Geschlecht dieses Synonyms abspeichern.
	       LP_Changing_Gender_gender->i = obj.changing_gender;

	       !Altes Objekt, das durch dieses Pronomen bezeichnet
	       !wurde, sichern, aber nur, wenn es nicht auch zu einem
	       !SAG gehört
	       obj2 = LanguagePronouns-->(x+2);
	       
	       if ( obj2 == NULL || (~~obj2 provides changing_gender) ||
		   (PowersOfTwo_TB-->(GetGNAOfObject(obj2)))&(LanguagePronouns-->(x+1)) ~= 0
		   ) {
	       	   LP_Changing_Gender_prev_object --> i = obj2;
	       }	       
	       
	       LanguagePronouns-->(x+2) = obj;
	   }
   } else if (obj provides changing_gender){
       !Das Objekt wurde mit dem richtigen Geschlecht angesprochen
       
       !Alle Bezüge auf dieses Objekt mit falschem Geschlecht löschen
       for (x = 1 : x <= LanguagePronouns-->0: x = x+3)
	   if (bm & (LanguagePronouns-->(x+1)) == 0 &&
	       LanguagePronouns-->(x+2) == obj) {
	       i = (x-1)/3;
	       LanguagePronouns-->(x+2) =
		   LP_Changing_Gender_prev_object --> i;

	       LP_Changing_Gender_prev_object --> i = NULL;
	   }
   }

   #ifdef EnglishNaturalLanguage;
   itobj  = PronounValue('it');  old_itobj  = itobj;
   himobj = PronounValue('him'); old_himobj = himobj;
   herobj = PronounValue('her'); old_herobj = herobj;
   #endif;
];

! ============================================================================
!  End of the parser proper: the remaining routines are its front end.
! ----------------------------------------------------------------------------

Object InformLibrary "(Inform Library)"
  with play
       [ i j k l;
#ifdef TARGET_ZCODE;
       standard_interpreter = $32-->0;
       transcript_mode = ((0-->8) & 1);
#ifnot; ! TARGET_GLULX
       GGInitialise();
#endif; ! TARGET_

       ChangeDefault(cant_go, CANTGO__TX);

#ifdef TARGET_ZCODE;
       buffer->0 = INPUT_BUFFER_LEN;
       buffer2->0 = INPUT_BUFFER_LEN;
       buffer3->0 = INPUT_BUFFER_LEN;
       parse->0 = 64;
       parse2->0 = 64;
#endif; ! TARGET_ZCODE
       
       real_location = thedark;
       player = selfobj; actor = player;
    
#ifdef TARGET_ZCODE;
       top_object = #largest_object-255;
#endif; ! TARGET_ZCODE
       selfobj.capacity = MAX_CARRIED; ! ### change?
       #ifdef LanguageInitialise;
       LanguageInitialise();
       #endif;
       new_line;
       j=Initialise();
       last_score = score;
       move player to location;
       while (parent(location)~=0) location=parent(location);
       real_location = location;
       objectloop (i in player) give i moved ~concealed;
    
       if (j~=2) Banner();

       MoveFloatingObjects();
       lightflag=OffersLight(parent(player));
       if (lightflag==0) { real_location=location; location=thedark; }
       <Look>;
    
       for (i=1:i<=100:i++) j=random(i);

       #ifdef EnglishNaturalLanguage;
       old_itobj = itobj; old_himobj = himobj; old_herobj = herobj;
       #endif;
    
       while (~~deadflag)
       {   
           #ifdef EnglishNaturalLanguage;
               PronounOldEnglish();
               old_itobj = PronounValue('it');
               old_himobj = PronounValue('him');
               old_herobj = PronounValue('her');
           #endif;

           .very__late__error;

           if (score ~= last_score)
           {   if (notify_mode==1) NotifyTheScore(); last_score=score; }

           .late__error;

           inputobjs-->0 = 0; inputobjs-->1 = 0;
           inputobjs-->2 = 0; inputobjs-->3 = 0; meta=false;
    
           !  The Parser writes its results into inputobjs and meta,
           !  a flag indicating a "meta-verb".  This can only be set for
           !  commands by the player, not for orders to others.
    
           InformParser.parse_input(inputobjs);
    
           action=inputobjs-->0;

           !  --------------------------------------------------------------

           !  Reverse "give fred biscuit" into "give biscuit to fred"
    
           if (action==##GiveR or ##ShowR)
           {   i=inputobjs-->2; inputobjs-->2=inputobjs-->3; inputobjs-->3=i;
               if (action==##GiveR) action=##Give; else action=##Show;
           }
    
           !  Convert "P, tell me about X" to "ask P about X"
    
           if (action==##Tell && inputobjs-->2==player && actor~=player)
           {   inputobjs-->2=actor; actor=player; action=##Ask;
           }
    
           !  Convert "ask P for X" to "P, give X to me"
    
           if (action==##AskFor && inputobjs-->2~=player && actor==player)
           {   actor=inputobjs-->2; inputobjs-->2=inputobjs-->3;
               inputobjs-->3=player; action=##Give;
           }
    
           !  For old, obsolete code: special_word contains the topic word
           !  in conversation
    
           if (action==##Ask or ##Tell or ##Answer)
               special_word = special_number1;

           !  --------------------------------------------------------------
    
           multiflag=false; onotheld_mode=notheld_mode; notheld_mode=false;
           !  For implicit taking and multiple object detection
    
          .begin__action;
           inp1 = 0; inp2 = 0; i=inputobjs-->1;
           if (i>=1) inp1=inputobjs-->2;
           if (i>=2) inp2=inputobjs-->3;
    
           !  inp1 and inp2 hold: object numbers, or 0 for "multiple object",
           !  or 1 for "a number or dictionary address"
    
           if (inp1 == 1) noun = special_number1; else noun = inp1;
           if (inp2 == 1)
           {   if (inp1 == 1) second = special_number2;
               else second = special_number1;
           } else second = inp2;

           !  --------------------------------------------------------------
    
           if (actor~=player)
           {   
           !  The player's "orders" property can refuse to allow conversation
           !  here, by returning true.  If not, the order is sent to the
           !  other person's "orders" property.  If that also returns false,
           !  then: if it was a misunderstood command anyway, it is converted
           !  to an Answer action (thus "floyd, grrr" ends up as
           !  "say grrr to floyd").  If it was a good command, it is finally
           !  offered to the Order: part of the other person's "life"
           !  property, the old-fashioned way of dealing with conversation.
    
               j=RunRoutines(player,orders);
               if (j==0)
               {   j=RunRoutines(actor,orders);
                   if (j==0)
                   {   if (action==##NotUnderstood)
                       {   inputobjs-->3=actor; actor=player; action=##Answer;
                           jump begin__action;
                       }
                       if (RunLife(actor,##Order)==0) L__M(##Order,1,actor);
                   }
               }
               jump turn__end;
           }

           !  --------------------------------------------------------------
           !  Generate the action...

           if ((i==0)
               || (i==1 && inp1 ~= 0)
               || (i==2 && inp1 ~= 0 && inp2 ~= 0))
           {   self.begin_action(action, noun, second, 0);
               jump turn__end;
           }

           !  ...unless a multiple object must be substituted.  First:
           !  (a) check the multiple list isn't empty;
           !  (b) warn the player if it has been cut short because too long;
           !  (c) generate a sequence of actions from the list
           !      (stopping in the event of death or movement away).

           multiflag = true;
           j=multiple_object-->0;
           if (j==0) { L__M(##Miscellany,2); jump late__error; }
           if (toomany_flag)
           {   toomany_flag = false; L__M(##Miscellany,1); }
           i=location;
           for (k=1:k<=j:k++)
           {   if (deadflag) break;
               if (location ~= i)
               {   L__M(##Miscellany, 51);
                   break;
               }
               l = multiple_object-->k;
               PronounNotice(l);
               print (_en) l, ": ";
               if (inp1 == 0)
               {   inp1 = l; self.begin_action(action, l, second, 0); inp1 = 0;
               }
               else
               {   inp2 = l; self.begin_action(action, noun, l, 0); inp2 = 0;
               }
           }

           !  --------------------------------------------------------------
    
           .turn__end;
    
           !  No time passes if either (i) the verb was meta, or
           !  (ii) we've only had the implicit take before the "real"
           !  action to follow.
    
           if (notheld_mode==1) { NoteObjectAcquisitions(); continue; }
           if (meta) continue;
           if (~~deadflag) self.end_turn_sequence();
       }

           if (deadflag~=2) AfterLife();
           if (deadflag==0) jump very__late__error;
    
           print "^^    ";
#ifdef TARGET_ZCODE;
           #IFV5; style bold; #ENDIF;
#ifnot; ! TARGET_GLULX
           glk($0086, 5); ! set alert style
#endif; ! TARGET_
           print "***";
           if (deadflag==1) L__M(##Miscellany,3);
           if (deadflag==2) L__M(##Miscellany,4);
           if (deadflag>2)  { print " "; DeathMessage(); print " "; }
           print "***";
#ifdef TARGET_ZCODE;
           #IFV5; style roman; #ENDIF;
#ifnot; ! TARGET_GLULX
           glk($0086, 0); ! set normal style
#endif; ! TARGET_
           print "^^^";
           ScoreSub();
           DisplayStatus();
           AfterGameOver();
       ],

       end_turn_sequence
       [ i j;

           turns++;
           if (the_time~=NULL)
           {   if (time_rate>=0) the_time=the_time+time_rate;
               else
               {   time_step--;
                   if (time_step==0)
                   {   the_time++;
                       time_step = -time_rate;
                   }
               }
               the_time=the_time % 1440;
           }

           #IFDEF DEBUG;
           if (debug_flag & 4 ~= 0)
           {   for (i=0: i<active_timers: i++)
               {   j=the_timers-->i;
                   if (j~=0)
                   {   print (name) (j&~WORD_HIGHBIT), ": ";
                       if (j & WORD_HIGHBIT) print "daemon";
                       else
                       {   print "timer with ",
                                 j.time_left, " turns to go"; }
                       new_line;
                   }
               }
           }
           #ENDIF;

           for (i=0: i<active_timers: i++)
           {   if (deadflag) return;
               j=the_timers-->i;
               if (j~=0)
               {   if (j & WORD_HIGHBIT) RunRoutines(j&~WORD_HIGHBIT,daemon);
                   else
                   {   if (j.time_left==0)
                       {   StopTimer(j);
                           RunRoutines(j,time_out);
                       }
                       else
                           j.time_left=j.time_left-1;
                   }
               }
           }
           if (deadflag) return;

           scope_reason=EACH_TURN_REASON; verb_word=0;
           DoScopeAction(location);
           SearchScope(ScopeCeiling(player), player, 0);
           scope_reason=PARSING_REASON;

           if (deadflag) return;

           TimePasses();

           if (deadflag) return;

           AdjustLight();

           if (deadflag) return;

           NoteObjectAcquisitions();
       ],

       begin_action
       [ a n s source   sa sn ss;
           sa = action; sn = noun; ss = second;
           action = a; noun = n; second = s;
           #IFDEF DEBUG;
           if (debug_flag & 2 ~= 0) TraceAction(source);
           #IFNOT;
           source = 0;
           #ENDIF;
           #IFTRUE Grammar__Version == 1;
           if ((meta || BeforeRoutines()==false) && action<256)
               ActionPrimitive();
           #IFNOT;
           if ((meta || BeforeRoutines()==false) && action<4096)
               ActionPrimitive();
           #ENDIF;
           action = sa; noun = sn; second = ss;
       ],
  has  proper;

#ifdef TARGET_ZCODE;

[ ActionPrimitive;
  indirect(#actions_table-->action);
];
       
#ifnot; ! TARGET_GLULX

[ ActionPrimitive;
  indirect(#actions_table-->(action+1));
];
       
#endif; ! TARGET_

[ AfterGameOver i;
   .RRQPL;
   L__M(##Miscellany,5);
   .RRQL;
   print "> ";
#ifdef TARGET_ZCODE;
   #IFV3; read buffer parse; #ENDIF;
   temp_global=0;
   #IFV5; read buffer parse DrawStatusLine; #ENDIF;
#ifnot; ! TARGET_GLULX
   KeyboardPrimitive(buffer, parse);
#endif; ! TARGET_
   i=parse-->1;
   if (i==QUIT1__WD or QUIT2__WD) 
   {
#ifdef TARGET_ZCODE;
       quit;
#ifnot; ! TARGET_GLULX
       quit;
#endif; ! TARGET_
   }
   if (i==RESTART__WD)
   {
#ifdef TARGET_ZCODE;
       @restart;
#ifnot; ! TARGET_GLULX
       @restart;
#endif; ! TARGET_
   }
   if (i==RESTORE__WD)      { RestoreSub(); jump RRQPL; }
   if (i==FULLSCORE1__WD or FULLSCORE2__WD && TASKS_PROVIDED==0)
   {   new_line; FullScoreSub(); jump RRQPL; }
   if (deadflag==2 && i==AMUSING1__WD or AMUSING2__WD && AMUSING_PROVIDED==0) !Patch OMU
   {   new_line; Amusing(); jump RRQPL; }
   #IFV5;
   if (i==UNDO1__WD or UNDO2__WD or UNDO3__WD or UNDO4__WD)  !Patch OMU
   {   if (undo_flag==0)
       {   L__M(##Miscellany,6);
           jump RRQPL;
       }
       if (undo_flag==1) jump UndoFailed2;
#ifdef TARGET_ZCODE;
       @restore_undo i;
#ifnot; ! TARGET_GLULX
       @restoreundo i;
       i = (~~i);
#endif; ! TARGET_
       if (i==0)
       {   .UndoFailed2; L__M(##Miscellany,7);
       }
       jump RRQPL;
   }
   #ENDIF;
   L__M(##Miscellany,8);
   jump RRQL;
];

[ R_Process a i j s1 s2;
   s1 = inp1; s2 = inp2;
   inp1 = i; inp2 = j; InformLibrary.begin_action(a, i, j, 1);
   inp1 = s1; inp2 = s2;
];

[ NoteObjectAcquisitions i;
  objectloop (i in player && i hasnt moved)
  {   give i moved;
      if (i has scored)
      {   score = score + OBJECT_SCORE;
          things_score = things_score + OBJECT_SCORE;
      }
  }
];

! ----------------------------------------------------------------------------

[ TestScope obj act a al sr x y;
  x=parser_one; y=parser_two;
  parser_one=obj; parser_two=0; a=actor; al=actors_location;
  sr=scope_reason; scope_reason=TESTSCOPE_REASON;
  if (act==0) actor=player; else actor=act;
  actors_location=ScopeCeiling(actor);
  SearchScope(actors_location,actor,0); scope_reason=sr; actor=a;
  actors_location=al; parser_one=x; x=parser_two; parser_two=y;
  return x;
];

[ LoopOverScope routine act x y a al;
  x = parser_one; y=scope_reason; a=actor; al=actors_location;
  parser_one=routine; if (act==0) actor=player; else actor=act;
  actors_location=ScopeCeiling(actor);
  scope_reason=LOOPOVERSCOPE_REASON;
  SearchScope(actors_location,actor,0);
  parser_one=x; scope_reason=y; actor=a; actors_location=al;
];

[ BeforeRoutines;
  if (GamePreRoutine()~=0) rtrue;
  if (RunRoutines(player,orders)~=0) rtrue;
  if (location~=0 && RunRoutines(location,before)~=0) rtrue;
  scope_reason=REACT_BEFORE_REASON; parser_one=0;
  SearchScope(ScopeCeiling(player),player,0); scope_reason=PARSING_REASON;
  if (parser_one~=0) rtrue;
  if (inp1>1 && RunRoutines(inp1,before)~=0) rtrue;
  rfalse;
];

[ AfterRoutines;
  scope_reason=REACT_AFTER_REASON; parser_one=0;
  SearchScope(ScopeCeiling(player),player,0); scope_reason=PARSING_REASON;
  if (parser_one~=0) rtrue;
  if (location~=0 && RunRoutines(location,after)~=0) rtrue;
  if (inp1>1 && RunRoutines(inp1,after)~=0) rtrue;
  return GamePostRoutine();
];

[ RunLife a j;
#IFDEF DEBUG;
   if (debug_flag & 2 ~= 0) TraceAction(2, j);
#ENDIF;
   reason_code = j; return RunRoutines(a,life);
];

[ ZRegion addr;
  switch(metaclass(addr))       ! Left over from Inform 5
  {   nothing: return 0;
      Object, Class: return 1;
      Routine: return 2;
      String: return 3;
  }
];

[ PrintOrRun obj prop flag;
  if (obj.#prop > WORDSIZE) return RunRoutines(obj,prop);
  if (obj.prop==NULL) rfalse;
  switch(metaclass(obj.prop))
  {   Class, Object, nothing: return RunTimeError(2,obj,prop);
      String: print (string) obj.prop; if (flag==0) new_line; rtrue;
      Routine: return RunRoutines(obj,prop);
  }
];

[ ValueOrRun obj prop;
  !### this is entirely unlikely to work. Does anyone care?
  if (obj.prop < 256) return obj.prop;
  return RunRoutines(obj, prop);
];

[ RunRoutines obj prop;
   if (obj == thedark
       && prop ~= initial or short_name or description) obj=real_location;
   if (obj.&prop == 0) rfalse;
   return obj.prop();
];

#ifdef TARGET_ZCODE;

[ ChangeDefault prop val a b;
   ! Use assembly-language here because -S compilation won't allow this:
   @loadw 0 5 -> a;
   b = prop-1;
   @storew a b val;
];

#endif; ! TARGET_ZCODE

#ifdef TARGET_GLULX;

[ ChangeDefault prop val;
   ! Use assembly-language here because -S compilation won't allow this:
   ! #cpv__start-->prop = val;
   @astore #cpv__start prop val;
];

#endif; ! TARGET_GLULX

! ----------------------------------------------------------------------------

[ StartTimer obj timer i;
   for (i=0:i<active_timers:i++)
       if (the_timers-->i==obj) rfalse;
   for (i=0:i<active_timers:i++)
       if (the_timers-->i==0) jump FoundTSlot;
   i=active_timers++;
   if (i >= MAX_TIMERS) { RunTimeError(4); return; }
   .FoundTSlot;
   if (obj.&time_left==0) { RunTimeError(5,obj); return; }
   the_timers-->i=obj; obj.time_left=timer;
];

[ StopTimer obj i;
   for (i=0:i<active_timers:i++)
       if (the_timers-->i==obj) jump FoundTSlot2;
   rfalse;
   .FoundTSlot2;
   if (obj.&time_left==0) { RunTimeError(5,obj); return; }
   the_timers-->i=0; obj.time_left=0;
];

[ StartDaemon obj i;
   for (i=0:i<active_timers:i++)
       if (the_timers-->i == WORD_HIGHBIT + obj)
           rfalse;
   for (i=0:i<active_timers:i++)
       if (the_timers-->i==0) jump FoundTSlot3;
   i=active_timers++;
   if (i >= MAX_TIMERS) RunTimeError(4);
   .FoundTSlot3;
   the_timers-->i = WORD_HIGHBIT + obj;
];

[ StopDaemon obj i;
   for (i=0:i<active_timers:i++)
       if (the_timers-->i == WORD_HIGHBIT + obj) jump FoundTSlot4;
   rfalse;
   .FoundTSlot4;
   the_timers-->i=0;
];

! ----------------------------------------------------------------------------

[ DisplayStatus;
   if ((0->1)&2 == 0)	!Bugfix, Inform Patches [Implementing a clock]
   {   sline1=score; sline2=turns; }
   else
   {   sline1=the_time/60; sline2=the_time%60; }
];

[ SetTime t s;
   the_time=t; time_rate=s; time_step=0;
   if (s<0) time_step=0-s;
];

[ NotifyTheScore;
#ifdef TARGET_GLULX;
   glk($0086, 6); ! set note style
#endif; ! TARGET_GLULX
   print "^[";  L__M(##Miscellany, 50, score-last_score);  print ".]^";
#ifdef TARGET_GLULX;
   glk($0086, 0); ! set normal style
#endif; ! TARGET_GLULX
];

! ----------------------------------------------------------------------------

[ AdjustLight flag i;
   i=lightflag;
   lightflag=OffersLight(parent(player));

   if (i==0 && lightflag==1)
   {   location=real_location; if (flag==0) <Look>;
   }

   if (i==1 && lightflag==0)
   {   real_location=location; location=thedark;
       if (flag==0) { NoteArrival();
                      return L__M(##Miscellany, 9); }
   }
   if (i==0 && lightflag==0) location=thedark;
];

[ OffersLight i j;
   if (i==0) rfalse;
   if (i has light) rtrue;
   objectloop (j in i)
       if (HasLightSource(j)==1) rtrue;
   if (i has container)
   {   if (i has open || i has transparent)
           return OffersLight(parent(i));
   }
   else
   {   if (i has enterable || i has transparent || i has supporter)
           return OffersLight(parent(i));
   }
   rfalse;
];

[ HidesLightSource obj;
    if (obj == player) rfalse;
    if (obj has transparent or supporter) rfalse;
    if (obj has container) return obj hasnt open;
    return obj hasnt enterable;
];

[ HasLightSource i j ad;
   if (i==0) rfalse;
   if (i has light) rtrue;
   if (i has enterable || IsSeeThrough(i)==1)
       if (~~(HidesLightSource(i)))
           objectloop (j in i)
               if (HasLightSource(j)==1) rtrue;
   ad = i.&add_to_scope;
   if (parent(i)~=0 && ad ~= 0)
   {   if (metaclass(ad-->0) == Routine)
       {   ats_hls = 0; ats_flag = 1;
           RunRoutines(i, add_to_scope);
           ats_flag = 0; if (ats_hls == 1) rtrue;
       }
       else
       {   for (j=0:(WORDSIZE*j)<i.#add_to_scope:j++)
               if (HasLightSource(ad-->j)==1) rtrue;
       }
   }
   rfalse;
];

[ ChangePlayer obj flag i;
!  if (obj.&number==0) return RunTimeError(7,obj);
  if (actor==player) actor=obj;
  give player ~transparent ~concealed;
  i=obj; while(parent(i)~=0) { if (i has animate) give i transparent;
                               i=parent(i); }
  if (player==selfobj) player.short_name=FORMER__TX;

  player=obj;

  if (player==selfobj) player.short_name=NULL;
  give player transparent concealed animate proper;
  i=player; while(parent(i)~=0) i=parent(i); location=i;
  real_location=location;
  MoveFloatingObjects();
  lightflag=OffersLight(parent(player));
  if (lightflag==0) location=thedark;
  print_player_flag=flag;
];

! ----------------------------------------------------------------------------

#IFDEF DEBUG;
#ifdef TARGET_ZCODE;
[ DebugParameter w x n l;
  x=0-->4; x=x+(x->0)+1; l=x->0; n=(x+1)-->0; x=w-(x+3);
  print w;
  if (w>=1 && w<=top_object) print " (", (name) w, ")";
  if (x%l==0 && (x/l)<n) print " ('", (address) w, "')";
];
[ DebugAction a anames;
#iftrue Grammar__Version==1;
  if (a>=256) { print "<fake action ", a-256, ">"; return; }
#ifnot;
  if (a>=4096) { print "<fake action ", a-4096, ">"; return; }
#endif;
  anames = #identifiers_table;
  anames = anames + 2*(anames-->0) + 2*48;
  print (string) anames-->a;
];
[ DebugAttribute a anames;
  if (a<0 || a>=48) print "<invalid attribute ", a, ">";
  else
  {   anames = #identifiers_table; anames = anames + 2*(anames-->0);
      print (string) anames-->a;
  }
];
#ifnot; ! TARGET_GLULX
[ DebugParameter w endmem;
  print w;
  @getmemsize endmem;
  if (w >= 1 && w < endmem) {
    if (w->0 >= $70 && w->0 < $7F) print " (", (name) w, ")";
    if (w->0 >= $60 && w->0 < $6F) print " ('", (address) w, "')";
  }
];
[ DebugAction a str;
  if (a>=4096) { print "<fake action ", a-4096, ">"; return; }
  if (a<0 || a>=#identifiers_table-->7) print "<invalid action ", a, ">";
  else {
    str = #identifiers_table-->6;
    str = str-->a;
    if (str) print (string) str;
    else print "<unnamed action ", a, ">";
  }
];
[ DebugAttribute a str;
  if (a<0 || a>=NUM_ATTR_BYTES*8) print "<invalid attribute ", a, ">";
  else {
    str = #identifiers_table-->4;
    str = str-->a;
    if (str) print (string) str;
    else print "<unnamed attribute ", a, ">";
  }
];
#endif; ! TARGET_
[ TraceAction source ar;
  if (source<2) print "[ Action ", (DebugAction) action;
  else
  {   if (ar==##Order)
          print "[ Order to ", (name) actor, ": ", (DebugAction) action;
      else
          print "[ Life rule ", (DebugAction) ar;
  }
  if (noun~=0)   print " with noun ", (DebugParameter) noun;
  if (second~=0) print " and second ", (DebugParameter) second;
  if (source==0) print " ";
  if (source==1) print " (from < > statement) ";
  print "]^";
];
[ DebugToken token;
  AnalyseToken(token);
  switch(found_ttype)
  {   ILLEGAL_TT: print "<illegal token number ", token, ">";
      ELEMENTARY_TT:
      switch(found_tdata)
      {   NOUN_TOKEN:        print "noun";
          HELD_TOKEN:        print "held";
          MULTI_TOKEN:       print "multi";
          MULTIHELD_TOKEN:   print "multiheld";
          MULTIEXCEPT_TOKEN: print "multiexcept";
          MULTIINSIDE_TOKEN: print "multiinside";
          CREATURE_TOKEN:    print "creature";
          SPECIAL_TOKEN:     print "special";
          NUMBER_TOKEN:      print "number";
          TOPIC_TOKEN:       print "topic";
          ENDIT_TOKEN:       print "END";
      }
      PREPOSITION_TT:
          print "'", (address) found_tdata, "'";
      ROUTINE_FILTER_TT:
      #ifdef INFIX; print "noun=", (InfixPrintPA) found_tdata;
      #ifnot; print "noun=Routine(", found_tdata, ")"; #endif;
      ATTR_FILTER_TT:
          print (DebugAttribute) found_tdata;
      SCOPE_TT:
      #ifdef INFIX; print "scope=", (InfixPrintPA) found_tdata;
      #ifnot; print "scope=Routine(", found_tdata, ")"; #endif;
      GPR_TT:
      #ifdef INFIX; print (InfixPrintPA) found_tdata;
      #ifnot; print "Routine(", found_tdata, ")"; #endif;
  }
];
[ DebugGrammarLine pcount;
  print " * ";
  for (:line_token-->pcount ~= ENDIT_TOKEN:pcount++)
  {   if ((line_token-->pcount)->0 & $10) print "/ ";
      print (DebugToken) line_token-->pcount, " ";
  }
  print "-> ", (DebugAction) action_to_be;
  if (action_reversed) print " reverse";
];
#ifdef TARGET_ZCODE;
[ ShowVerbSub address lines da meta i j;
    if (((noun->#dict_par1) & 1) == 0)
      "Try typing ~showverb~ and then the name of a verb.";
    meta=((noun->#dict_par1) & 2)/2;
    i = $ff-(noun->#dict_par2);
    address = (0-->7)-->i;
    lines = address->0;
    address++;
    print "Verb ";
    if (meta) print "meta ";
    da = 0-->4;
    for (j=0:j < (da+5)-->0:j++)
        if (da->(j*9 + 14) == $ff-i)
            print "'", (address) (da + 9*j + 7), "' ";
    new_line;
    if (lines == 0) "has no grammar lines.";
    for (:lines > 0:lines--)
    {   address = UnpackGrammarLine(address);
        print "    "; DebugGrammarLine(); new_line;
    }
];
#ifnot; ! TARGET_GLULX
[ ShowVerbSub address lines i j meta wd dictlen entrylen;
  if (noun == 0 || ((noun->#dict_par1) & 1) == 0)
    "Try typing ~showverb~ and then the name of a verb.";
  meta=((noun->#dict_par1) & 2)/2;
  i = $ff-(noun->#dict_par2);
  address = (#grammar_table)-->(i+1);
  lines = address->0;
  address++;
  print "Verb ";
  if (meta) print "meta ";
  dictlen = #dictionary_table-->0;
  entrylen = DICT_WORD_SIZE + 7;
  for (j=0:j<dictlen:j++) {
    wd = #dictionary_table + WORDSIZE + entrylen*j;
    if (wd->#dict_par2 == $ff-i)
      print "'", (address) wd, "' ";
  }
  new_line;
  if (lines == 0) "has no grammar lines.";
  for (:lines > 0:lines--) {
    address = UnpackGrammarLine(address);
    print "    "; DebugGrammarLine(); new_line;
  }
];
#endif; ! TARGET_
[ ShowobjSub c f l a n x numattr;
   if (noun==0) noun=location;
   objectloop (c ofclass Class) if (noun ofclass c) { f++; l=c; }
   if (f == 1) print (name) l, " ~"; else print "Object ~";
   print (name) noun, "~ (", noun, ")";
   if (parent(noun)~=0) print " in ~", (name) parent(noun), "~";
   new_line;
   if (f > 1)
   {   print "  class ";
       objectloop (c ofclass Class) if (noun ofclass c) print (name) c, " ";
       new_line;
   }
#ifdef TARGET_ZCODE;
   numattr = 48;
#ifnot; ! TARGET_GLULX
   numattr = NUM_ATTR_BYTES * 8;
#endif; ! TARGET_
   for (a=0,f=0:a<numattr:a++) if (noun has a) f=1;
   if (f)
   {   print "  has ";
       for (a=0:a<numattr:a++) if (noun has a) print (DebugAttribute) a, " ";
       new_line;
   }
   if (noun ofclass Class) return;

   f=0; 
#ifdef TARGET_ZCODE;
   l = #identifiers_table-->0;
#ifnot; ! TARGET_GLULX
   l = INDIV_PROP_START + #identifiers_table-->3;
#endif; ! TARGET_
   for (a=1:a<=l:a++)
   {   if ((a~=2 or 3) && noun.&a)
       {   if (f==0) { print "  with "; f=1; }
           print (property) a;
           n = noun.#a;
           for (c=0:WORDSIZE*c<n:c++)
           {   print " ";
               x = (noun.&a)-->c;
               if (a==name) {
		     if(x>255 || x<0)
		         print "'", (address) x, "'";
                     else {
#ifdef INFIX;
	                 print (InfixPrintAttribute) x;
#ifnot;
		         print "(gender)";
#endif;
		     }
               }
               else
               {   if (a==number or capacity or time_left)
                       print x;
                   else
                   {   switch(x)
                       {   NULL: print "NULL";
                           0: print "0";
                           1: print "1";
                           default:
                           switch(metaclass(x))
                           {   Class, Object: print (name) x;
                               String: print "~", (string) x, "~";
                               Routine: print "[...]";
                           }
                           print " (", x, ")";
                       }
                   }
               }
           }
           print ",^       ";
       }
   }
!   if (f==1) new_line;
];
#ENDIF;

! ----------------------------------------------------------------------------
!  Except in Version 3, the DrawStatusLine routine does just that: this is
!  provided explicitly so that it can be Replace'd to change the style, and
!  as written it emulates the ordinary Standard game status line, which is
!  drawn in hardware
! ----------------------------------------------------------------------------

#ifdef TARGET_ZCODE;

#IFV5;
[ DrawStatusLine width posa posb;
   @split_window 1; @set_window 1; @set_cursor 1 1; style reverse;
   width = 0->33; posa = width-26; posb = width-13;
   spaces width;
   @set_cursor 1 2;
   if (location == thedark) print (name) location;
   else
   {   FindVisibilityLevels();
       if (visibility_ceiling == location)
           print (name) location;
       else print (The) visibility_ceiling;
   }
   if ((0->1)&2 == 0)
   {   if (width > 76)
       {   @set_cursor 1 posa; print (string) SCORE__TX, sline1;
           @set_cursor 1 posb; print (string) MOVES__TX, sline2;
       }
       if (width > 63 && width <= 76)
       {   @set_cursor 1 posb; print sline1, "/", sline2;
       }
   }
   else
   {   @set_cursor 1 posa;
       print (string) TIME__TX;
       LanguageTimeOfDay(sline1, sline2);
   }
   @set_cursor 1 1; style roman; @set_window 0;
];
#ENDIF;

#endif; ! TARGET_ZCODE

#ifdef TARGET_GLULX;

[ StatusLineHeight hgt parwin;
  if (gg_statuswin == 0)
    return;
  if (hgt == gg_statuswin_cursize)
    return;
  parwin = glk($0029, gg_statuswin); ! window_get_parent
  glk($0026, parwin, $12, hgt, 0); ! window_set_arrangement
  gg_statuswin_cursize = hgt;
];

[ DrawStatusLine width height posa posb;
    ! If we have no status window, we must not try to redraw it.
    if (gg_statuswin == 0)
        return;

    ! If there is no player location, we shouldn't try either.
    if (location == nothing || parent(player) == nothing)
        return;

    glk($002F, gg_statuswin); ! set_window
    StatusLineHeight(gg_statuswin_size);

    glk($0025, gg_statuswin, gg_arguments, gg_arguments+4); ! window_get_size
    width = gg_arguments-->0;
    height = gg_arguments-->1;
    posa = width-26; posb = width-13;

    glk($002A, gg_statuswin); ! window_clear

    glk($002B, gg_statuswin, 1, 0); ! window_move_cursor
    if (location == thedark) {
        print (name) location;
    }
    else {
        FindVisibilityLevels();
        if (visibility_ceiling == location)
            print (name) location;
        else 
            print (The) visibility_ceiling;
    }

    if (width > 66) {
        glk($002B, gg_statuswin, posa-1, 0); ! window_move_cursor
        print (string) SCORE__TX, sline1;
        glk($002B, gg_statuswin, posb-1, 0); ! window_move_cursor
        print (string) MOVES__TX, sline2;
    }
    if (width > 53 && width <= 66) {
        glk($002B, gg_statuswin, posb-1, 0); ! window_move_cursor
        print sline1, "/", sline2;
    }

    glk($002F, gg_mainwin); ! set_window
];

[ Box__Routine maxwid arr ix lines lastnl parwin;
    maxwid = 0; ! squash compiler warning
    lines = arr-->0;

    if (gg_quotewin == 0) {
        gg_arguments-->0 = lines;
        ix = InitGlkWindow(GG_QUOTEWIN_ROCK);
        if (ix == 0) 
            gg_quotewin = glk($0023, gg_mainwin, $12, lines, 3, 
                GG_QUOTEWIN_ROCK); ! window_open
    }
    else {
        parwin = glk($0029, gg_quotewin); ! window_get_parent
        glk($0026, parwin, $12, lines, 0); ! window_set_arrangement
    }

    lastnl = true;
    if (gg_quotewin) {
        glk($002A, gg_quotewin); ! window_clear
        glk($002F, gg_quotewin); ! set_window
        lastnl = false;
    }

    ! If gg_quotewin is zero here, the quote just appears in the story window.

    glk($0086, 7); ! set blockquote style
    for (ix=0 : ix<lines : ix++) {
        print (string) arr-->(ix+1);
        if (ix < lines-1 || lastnl) new_line;
    }
    glk($0086, 0); ! set normal style

    if (gg_quotewin) {
        glk($002F, gg_mainwin); ! set_window
    }
];

#endif; ! TARGET_GLULX

#ifdef TARGET_GLULX;

[ GGInitialise res;
    @gestalt 4 2 res; ! Test if this interpreter has Glk.
    if (res == 0) {
      ! Without Glk, we're entirely screwed.
      quit;
    }
    ! Set the VM's I/O system to be Glk.
    @setiosys 2 0;

    ! First, we must go through all the Glk objects that exist, and see
    ! if we created any of them. One might think this strange, since the
    ! program has just started running, but remember that the player might 
    ! have just typed "restart".
    GGRecoverObjects();

    res = InitGlkWindow(0);
    if (res ~= 0)
        return;

    ! Now, gg_mainwin and gg_storywin might already be set. If not, set them.

    if (gg_mainwin == 0) {
        ! Open the story window.
        res = InitGlkWindow(GG_MAINWIN_ROCK);
        if (res == 0)
            gg_mainwin = glk($0023, 0, 0, 0, 3, GG_MAINWIN_ROCK); ! window_open
        if (gg_mainwin == 0) {
            ! If we can't even open one window, there's no point in going on.
            quit;
        }
    }
    else {
        ! There was already a story window. We should erase it.
        glk($002A, gg_mainwin); ! window_clear
    }

    if (gg_statuswin == 0) {
        res = InitGlkWindow(GG_STATUSWIN_ROCK);
        if (res == 0) {
            gg_statuswin_cursize = gg_statuswin_size;
            gg_statuswin = glk($0023, gg_mainwin, $12, gg_statuswin_cursize, 
                4, GG_STATUSWIN_ROCK); ! window_open
        }
    }
    ! It's possible that the status window couldn't be opened, in which case
    ! gg_statuswin is now zero. We must allow for that later on.

    glk($002F, gg_mainwin); ! set_window

    InitGlkWindow(1);
];

[ GGRecoverObjects id;
    ! If GGRecoverObjects() has been called, all these stored IDs are
    ! invalid, so we start by clearing them all out. 
    ! (In fact, after a restoreundo, some of them may still be good.
    ! For simplicity, though, we assume the general case.)
    gg_mainwin = 0;
    gg_statuswin = 0;
    gg_quotewin = 0;
    gg_scriptfref = 0;
    gg_scriptstr = 0;
    gg_savestr = 0;
    gg_statuswin_cursize = 0;
#IFDEF DEBUG;
    gg_commandstr = 0;
    gg_command_reading = false;
#ENDIF;
    ! Also tell the game to clear its object references.
    IdentifyGlkObject(0);

    id = glk($0040, 0, gg_arguments); ! stream_iterate
    while (id) {
        switch (gg_arguments-->0) {
            GG_SAVESTR_ROCK: gg_savestr = id;
            GG_SCRIPTSTR_ROCK: gg_scriptstr = id;
#IFDEF DEBUG;
            GG_COMMANDWSTR_ROCK: gg_commandstr = id;
                                 gg_command_reading = false;
            GG_COMMANDRSTR_ROCK: gg_commandstr = id;
                                 gg_command_reading = true;
#ENDIF;
            default: IdentifyGlkObject(1, 1, id, gg_arguments-->0); 
        }
        id = glk($0040, id, gg_arguments); ! stream_iterate
    }

    id = glk($0020, 0, gg_arguments); ! window_iterate
    while (id) {
        switch (gg_arguments-->0) {
            GG_MAINWIN_ROCK: gg_mainwin = id;
            GG_STATUSWIN_ROCK: gg_statuswin = id;
            GG_QUOTEWIN_ROCK: gg_quotewin = id;
            default: IdentifyGlkObject(1, 0, id, gg_arguments-->0); 
        }
        id = glk($0020, id, gg_arguments); ! window_iterate
    }

    id = glk($0064, 0, gg_arguments); ! fileref_iterate
    while (id) {
        switch (gg_arguments-->0) {
            GG_SCRIPTFREF_ROCK: gg_scriptfref = id;
            default: IdentifyGlkObject(1, 2, id, gg_arguments-->0); 
        }
        id = glk($0064, id, gg_arguments); ! fileref_iterate
    }

    ! Tell the game to tie up any loose ends.
    IdentifyGlkObject(2);
];

! This is a trivial function which just prints a number, in decimal
! digits. It may be useful as a stub to pass to PrintAnything.
[ DecimalNumber num;
    print num;
];

! This somewhat obfuscated function will print anything.
! It handles strings, functions (with optional arguments), objects,
! object properties (with optional arguments), and dictionary words.
! It does *not* handle plain integers, but you can use
! DecimalNumber or EnglishNumber to handle that case.
!
! Calling:                           Is equivalent to:
! -------                            ----------------
! PrintAnything()                    <nothing printed>
! PrintAnything(0)                   <nothing printed>
! PrintAnything("string");           print (string) "string";
! PrintAnything('word')              print (address) 'word';
! PrintAnything(obj)                 print (name) obj;
! PrintAnything(obj, prop)           obj.prop();
! PrintAnything(obj, prop, args...)  obj.prop(args...);
! PrintAnything(func)                func();
! PrintAnything(func, args...)       func(args...);
! 
[ PrintAnything _vararg_count obj mclass;
    if (_vararg_count == 0)
        return;
    @copy sp obj;
    _vararg_count--;
    if (obj == 0)
        return;
   
    if (obj->0 == $60) {
        ! Dictionary word. Metaclass() can't catch this case, so we do
        ! it manually.
        print (address) obj;
        return;
    }

    mclass = metaclass(obj);
    switch (mclass) {
        nothing:
            return;
        String:
            print (string) obj;
            return;
        Routine:
            ! Call the function with all the arguments which are already
            ! on the stack.
            @call obj _vararg_count 0;
            return;
        Object:
            if (_vararg_count == 0) {
                print (name) obj;
            }
            else {
                ! Push the object back onto the stack, and call the
                ! veneer routine that handles obj.prop() calls.
                @copy obj sp;
                _vararg_count++;
                @call CA__Pr _vararg_count 0;
            }
            return;
    }
];

! This does the same as PrintAnything, but the output is sent to a
! byte array in memory. The first two arguments must be the array
! address and length; the following arguments are interpreted as 
! for PrintAnything. The return value is the number of characters
! output.
! If the output is longer than the array length given, the extra 
! characters are discarded, so the array does not overflow. 
! (However, the return value is the total length of the output, 
! including discarded characters.)

[ PrintAnyToArray _vararg_count arr arrlen str oldstr len;

   @copy sp arr;
   @copy sp arrlen;
   _vararg_count = _vararg_count - 2;

   oldstr = glk($0048); ! stream_get_current
   str = glk($0043, arr, arrlen, 1, 0); ! stream_open_memory
   if (str == 0)
       return 0;

   glk($0047, str); ! stream_set_current

   @call PrintAnything _vararg_count 0;

   glk($0047, oldstr); ! stream_set_current
   @copy $ffffffff sp;
   @copy str sp;
   @glk $0044 2 0; ! stream_close
   @copy sp len;
   @copy sp 0;

   return len;
];

! And this calls PrintAnyToArray on a particular array, jiggering
! the result to be a Glulx C-style ($E0) string.

Constant GG_ANYTOSTRING_LEN 66;
Array AnyToStrArr --> GG_ANYTOSTRING_LEN;
[ ChangeAnyToCString _vararg_count ix len;
   ix = GG_ANYTOSTRING_LEN-2;
   @copy ix sp;
   ix = AnyToStrArr+1;
   @copy ix sp;
   ix = _vararg_count+2;
   @call PrintAnyToArray ix len;
   AnyToStrArr->0 = $E0;
   if (len >= GG_ANYTOSTRING_LEN)
     len = GG_ANYTOSTRING_LEN-1;
   AnyToStrArr->(len+1) = 0;
   return AnyToStrArr;
];

#endif; ! TARGET_GLULX

#ifv5;
Array StorageForShortName --> 161;
#endif;

[ PrefaceByArticle o acode pluralise  i artform findout;

   if (o provides articles)
   {   print (string) (o.&articles)-->(acode+short_name_case*LanguageCases),
           " ";
       if (pluralise) return;
       print (PSN__) o; return;
   }

   i = GetGNAOfObject(o);
   if (pluralise)
   {   if (i<3 || (i>=6 && i<9)) i = i + 3;
   }
   i = LanguageGNAsToArticles-->i;

   artform = LanguageArticles
             + 3*WORDSIZE*LanguageContractionForms*(short_name_case + i*LanguageCases);

#iftrue LanguageContractionForms == 2;
   if (artform-->acode ~= artform-->(acode+3)) findout = true;
#endif;
#iftrue LanguageContractionForms == 3;
   if (artform-->acode ~= artform-->(acode+3)) findout = true;
   if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
#endif;
#iftrue LanguageContractionForms == 4;
   if (artform-->acode ~= artform-->(acode+3)) findout = true;
   if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
   if (artform-->(acode+6) ~= artform-->(acode+9)) findout = true;
#endif;
#iftrue LanguageContractionForms > 4;
   findout = true;
#endif;

#ifdef TARGET_ZCODE;
   if (standard_interpreter ~= 0 && findout)
   {   StorageForShortName-->0 = 160;
       @output_stream 3 StorageForShortName;
       if (pluralise) print (number) pluralise; else print (PSN__) o;
       @output_stream -3;
       acode = acode + 3*LanguageContraction(StorageForShortName + 2);
   }
#ifnot; ! TARGET_GLULX
   if (findout)
   {   if (pluralise)
           PrintAnyToArray(StorageForShortName, 160, EnglishNumber, pluralise);
       else
           PrintAnyToArray(StorageForShortName, 160, PSN__, o);
       acode = acode + 3*LanguageContraction(StorageForShortName);
   }
#endif; ! TARGET_

   print (string) artform-->acode;
   if (pluralise) return;
   print (PSN__) o;
];

[ PSN__ o;
   if (o==0) { print (string) NOTHING__TX; rtrue; }
   switch(metaclass(o))
   {   Routine: print "<routine ", o, ">"; rtrue;
       String:  print "<string ~", (string) o, "~>"; rtrue;
       nothing: print "<illegal object number ", o, ">"; rtrue;
   }
   if (o==player) { print (string) YOURSELF__TX; rtrue; }
   #ifdef LanguagePrintShortName;
   if (LanguagePrintShortName(o)) rtrue;
   #endif;
   if (indef_mode && o.&short_name_indef~=0
       && PrintOrRun(o, short_name_indef, 1)~=0) rtrue;
   if (o.&short_name~=0 && PrintOrRun(o,short_name,1)~=0) rtrue;
   print (object) o;
];

[ Indefart o i;
   i = indef_mode; indef_mode = true;
   if (o has proper) { indef_mode = NULL; print (PSN__) o; return; }
   if (o provides article)
   {   PrintOrRun(o,article,1); print " ", (PSN__) o; indef_mode = i; return;
   }
   PrefaceByArticle(o, 2); indef_mode = i;
];
[ Defart o i;
   i = indef_mode; indef_mode = false;
   if (o has proper)
   { indef_mode = NULL; print (PSN__) o; indef_mode = i; return; }
   PrefaceByArticle(o, 1); indef_mode = i;
];
[ CDefart o i;
   i = indef_mode; indef_mode = false;
   if (o has proper)
   { indef_mode = NULL; print (PSN__) o; indef_mode = i; return; }
   PrefaceByArticle(o, 0); indef_mode = i;
];

[ PrintShortName o i;
   i = indef_mode; indef_mode = NULL;
   PSN__(o); indef_mode = i;
];

[ EnglishNumber n; LanguageNumber(n); ];

[ NumberWord o i n;
  n = LanguageNumbers-->0;
  for (i=1:i<=n:i=i+2)
      if (o == LanguageNumbers-->i)
          return LanguageNumbers-->(i+1);
  return 0;
];

[ RandomEntry tab;
  if (tab-->0==0) return RunTimeError(8);
  return tab-->(random(tab-->0));
];

! ----------------------------------------------------------------------------
!  Useful routine: unsigned comparison (for addresses in Z-machine)
!    Returns 1 if x>y, 0 if x=y, -1 if x<y
! ----------------------------------------------------------------------------

[ UnsignedCompare x y u v;
  if (x==y) return 0;
  if (x<0 && y>=0) return 1;
  if (x>=0 && y<0) return -1;
  u = x&~WORD_HIGHBIT; v= y&~WORD_HIGHBIT;
  if (u>v) return 1;
  return -1;
];

#ifdef NITFOL_HOOKS;          ! Code contributed by Evin Robertson
#ifdef TARGET_GLULX;          ! Might be nice for Z-machine games too,
                              ! but I'm not going to try to make this work
                              ! given #ifdef funniness.

Array magic_array -->         ! This is so nitfol can do typo correction /
                              ! automapping / debugging on Glulx games
    $6e66726d $4d616763 $ff0010 ! Goes to 'NfrmMagc'  10 refers to length
    Magic_Global_Dispatch__
    DI__check_word            ! DI__check_word(buf, length)
    PrintShortName
    WV__Pr RV__Pr CA__Pr      ! obj.prop = x; x = obj.prop; obj.prop(x)
    RA__Pr RL__Pr RA__Sc      ! obj.&prop; obj.#prop; class::prop
    OP__Pr OC__Cl             ! obj provides prop; obj ofclass class
    OB__Move OB__Remove
    OB__Parent__ OB__Child__ OB__Sibling__  ! No explicit veneer for these
    ;

[ OB__Parent__ obj;
    return parent(obj);
];

[ OB__Child__ obj;
    return child(obj);
];

[ OB__Sibling__ obj;
    return sibling(obj);
];

[ Magic_Global_Dispatch__ glbl;
    switch(glbl) {
     0: if(location==TheDark) return real_location;
        return location;
     1: return Player;
     -1: return CompassDirection::number; ! Silliness to make exist RA__Sc
                                          ! Should never be called.
    }
    return magic_array;       ! Silences a warning.
];

[ DI__check_word buf wlen  ix val res dictlen entrylen;
    ! Just like in Tokenise__.  In fact, Tokenise__ could call this if
    ! it wanted, instead of doing this itself.
    if (wlen > DICT_WORD_SIZE)
        wlen = DICT_WORD_SIZE;
    for (ix=0 : ix < wlen : ix++) {
        gg_tokenbuf->ix = glk($00A0, buf->ix);
    }
    for ( : ix < DICT_WORD_SIZE : ix++) {
        gg_tokenbuf->ix = 0;
    }
    val = #dictionary_table + WORDSIZE;
    entrylen = DICT_WORD_SIZE + 7;
    @binarysearch gg_tokenbuf DICT_WORD_SIZE val entrylen dictlen 1 1 res;
    return res;
];

#endif;
#endif;

! ----------------------------------------------------------------------------
