Inform - Support - Source

Back to List

Inventory
Complete

Plain
Coloured
Gaudy

Browsing parserm.h

This is the complete source code of the example game parserm.inf.

0001  ! ----------------------------------------------------------------------------
0002  !  PARSERM:  Core of parser.
0003  !
0004  !  Supplied for use with Inform 6                         Serial number 991113
0005  !                                                                 Release 6/10
0006  !  (c) Graham Nelson 1993, 1994, 1995, 1996, 1997, 1998, 1999
0007  !      but freely usable (see manuals)
0008  ! ----------------------------------------------------------------------------
0009  !  Inclusion of "linklpa"
0010  !                   (which defines properties and attributes)
0011  !  Global variables, constants and arrays
0012  !                1: outside of the parser
0013  !                2: used within the parser
0014  !  Inclusion of natural language definition file
0015  !                   (which creates a compass and direction-objects)
0016  !  Darkness and player objects
0017  !  Definition of grammar token numbering system used by Inform
0018  !
0019  !  The InformParser object
0020  !          keyboard reading
0021  !          level 0: outer shell, conversation, errors
0022  !                1: grammar lines
0023  !                2: tokens
0024  !                3: object lists
0025  !                4: scope and ambiguity resolving
0026  !                5: object comparisons
0027  !                6: word comparisons
0028  !                7: reading words and moving tables about
0029  !          pronoun management
0030  !
0031  !  The InformLibrary object
0032  !          main game loop
0033  !          action processing
0034  !          end of turn sequence
0035  !          scope looping, before/after sequence, sending messages out
0036  !          timers, daemons, time of day, score notification
0037  !          light and darkness
0038  !          changing player personality
0039  !          tracing code (only present if DEBUG is set)
0040  !
0041  !  Status line printing, menu display
0042  !  Printing object names with articles
0043  !  Miscellaneous utility routines
0044  !  Game banner, "version" verb, run-time errors
0045  ! ----------------------------------------------------------------------------
0046   
0047  System_file;
0048  Constant NULL = $ffff;
0049   
0050  IFDEF MODULE_MODE;
0051  Constant DEBUG;
0052  Constant Grammar__Version 2;
0053  Include "linklpa";
0054  ENDIF;
0055   
0056  ! ============================================================================
0057  !   Global variables and their associated Constant and Array declarations
0058  ! ----------------------------------------------------------------------------
0059  Global location = InformLibrary;     ! Must be first global defined
0060  Global sline1;                       ! Must be second
0061  Global sline2;                       ! Must be third
0062                                       ! (for status line display)
0063  ! ------------------------------------------------------------------------------
0064  !   Z-Machine and interpreter issues
0065  ! ------------------------------------------------------------------------------
0066  Global top_object;                   ! Largest valid number of any tree object
0067  Global standard_interpreter;         ! The version number of the Z-Machine
0068                                       ! Standard which the interpreter claims
0069                                       ! to support, in form (upper byte).(lower)
0070  Global undo_flag;                    ! Can the interpreter provide "undo"?
0071  Global just_undone;                  ! Can't have two successive UNDOs
0072  Global transcript_mode;              ! true when game scripting is on
0073  IFDEF DEBUG;
0074  Global xcommsdir;                    ! true if command recording is on
0075  ENDIF;
0076  ! ------------------------------------------------------------------------------
0077  !   Time and score
0078  ! (for linkage reasons, the task_* arrays are created not here but in verblib.h)
0079  ! ------------------------------------------------------------------------------
0080  Global turns = 1;                    ! Number of turns of play so far
0081  Global the_time = NULL;              ! Current time (in minutes since midnight)
0082  Global time_rate = 1;                ! How often time is updated
0083  Global time_step;                    ! By how much
0084   
0085  #ifndef MAX_TIMERS;
0086  Constant MAX_TIMERS  32;             ! Max number timers/daemons active at once
0087  #endif;
0088  Array  the_timers  --> MAX_TIMERS;
0089  Global active_timers;                ! Number of timers/daemons actives
0090   
0091  Global score;                        ! The current score
0092  Global last_score;                   ! Score last turn (for testing for changes)
0093  Global notify_mode = true;           ! Score notification
0094  Global places_score;                 ! Contribution to score made by visiting
0095  Global things_score;                 ! Contribution made by acquisition
0096  ! ------------------------------------------------------------------------------
0097  !   The player
0098  ! ------------------------------------------------------------------------------
0099  Global player;                       ! Which object the human is playing through
0100  Global deadflag;                     ! Normally 0, or false; 1 for dead;
0101                                       ! 2 for victorious, and higher numbers
0102                                       ! represent exotic forms of death
0103  ! ------------------------------------------------------------------------------
0104  !   Light and room descriptions
0105  ! ------------------------------------------------------------------------------
0106  Global lightflag = true;             ! Is there currently light to see by?
0107  Global real_location;                ! When in darkness, location = thedark
0108                                       ! and this holds the real location
0109  Global visibility_ceiling;           ! Highest object in tree visible from
0110                                       ! the player's point of view (usually
0111                                       ! the room, sometimes darkness, sometimes
0112                                       ! a closed non-transparent container).
0113   
0114  Global lookmode = 1;                 ! 1=standard, 2=verbose, 3=brief room descs
0115  Global print_player_flag;            ! If set, print something like "(as Fred)"
0116                                       ! in room descriptions, to reveal whom
0117                                       ! the human is playing through
0118  Global lastdesc;                     ! Value of location at time of most recent
0119                                       ! room description printed out
0120  ! ------------------------------------------------------------------------------
0121  !   List writing  (style bits are defined as Constants in "verblibm.h")
0122  ! ------------------------------------------------------------------------------
0123  Global c_style;                      ! Current list-writer style
0124  Global lt_value;                     ! Common value of list_together
0125  Global listing_together;             ! Object number of one member of a group
0126                                       ! being listed together
0127  Global listing_size;                 ! Size of such a group
0128  Global wlf_indent;                   ! Current level of indentation printed by
0129                                       ! WriteListFrom routine
0130   
0131  Global inventory_stage = 1;          ! 1 or 2 according to the context in which
0132                                       ! "invent" routines of objects are called
0133  Global inventory_style;              ! List-writer style currently used while
0134                                       ! printing inventories
0135  ! ------------------------------------------------------------------------------
0136  !   Menus and printing
0137  ! ------------------------------------------------------------------------------
0138  Global pretty_flag = true;           ! Use character graphics, or plain text?
0139  Global menu_nesting;                 ! Level of nesting (0 = root menu)
0140  Global menu_item;                    ! These are used in communicating
0141  Global item_width = 8;               ! with the menu-creating routines
0142  Global item_name = "---";
0143   
0144  Global lm_n;                         ! Parameters used by LibraryMessages
0145  Global lm_o;                         ! mechanism
0146   
0147  IFDEF DEBUG;
0148  Global debug_flag;                   ! Bitmap of flags for tracing actions,
0149                                       ! calls to object routines, etc.
0150  Global x_scope_count;                ! Used in printing a list of everything
0151                                       ! in scope
0152  ENDIF;
0153  ! ------------------------------------------------------------------------------
0154  !   Action processing
0155  ! ------------------------------------------------------------------------------
0156  Global action;                       ! Action currently being asked to perform
0157  Global inp1;                         ! 0 (nothing), 1 (number) or first noun
0158  Global inp2;                         ! 0 (nothing), 1 (number) or second noun
0159  Global noun;                         ! First noun or numerical value
0160  Global second;                       ! Second noun or numerical value
0161   
0162  Global keep_silent;                  ! If true, attempt to perform the action
0163                                       ! silently (e.g. for implicit takes,
0164                                       ! implicit opening of unlocked doors)
0165   
0166  Global reason_code;                  ! Reason for calling a "life" rule
0167                                       ! (an action or fake such as ##Kiss)
0168   
0169  Global receive_action;               ! Either ##PutOn or ##Insert, whichever
0170                                       ! is action being tried when an object's
0171                                       ! "before" rule is checking "Receive"
0172  ! ==============================================================================
0173  !   Parser variables: first, for communication to the parser
0174  ! ------------------------------------------------------------------------------
0175  Global parser_trace = 0;             ! Set this to 1 to make the parser trace
0176                                       ! tokens and lines
0177  Global parser_action;                ! For the use of the parser when calling
0178  Global parser_one;                   ! user-supplied routines
0179  Global parser_two;                   !
0180  Array  inputobjs       --> 16;       ! For parser to write its results in
0181  Global parser_inflection;            ! A property (usually "name") to find
0182                                       ! object names in
0183  ! ------------------------------------------------------------------------------
0184  !   Parser output
0185  ! ------------------------------------------------------------------------------
0186  Global actor;                        ! Person asked to do something
0187  Global actors_location;              ! Like location, but for the actor
0188  Global meta;                         ! Verb is a meta-command (such as "save")
0189   
0190  Array  multiple_object --> 64;       ! List of multiple parameters
0191  Global multiflag;                    ! Multiple-object flag
0192  Global toomany_flag;                 ! Flag for "multiple match too large"
0193                                       ! (e.g. if "take all" took over 100 things)
0194   
0195  Global special_word;                 ! Dictionary address for "special" token
0196  Global special_number;               ! Number typed for "special" token
0197  Global parsed_number;                ! For user-supplied parsing routines
0198  Global consult_from;                 ! Word that a "consult" topic starts on
0199  Global consult_words;                ! ...and number of words in topic
0200  ! ------------------------------------------------------------------------------
0201  !   Implicit taking
0202  ! ------------------------------------------------------------------------------
0203  Global notheld_mode;                 ! To do with implicit taking
0204  Global onotheld_mode;                !     "old copy of notheld_mode", ditto
0205  Global not_holding;                  ! Object to be automatically taken as an
0206                                       ! implicit command
0207  Array  kept_results --> 16;          ! Delayed command (while the take happens)
0208  ! ------------------------------------------------------------------------------
0209  !   Error numbers when parsing a grammar line
0210  ! ------------------------------------------------------------------------------
0211  Global etype;                        ! Error number on current line
0212  Global best_etype;                   ! Preferred error number so far
0213  Global nextbest_etype;               ! Preferred one, if ASKSCOPE_PE disallowed
0214   
0215  Constant STUCK_PE     = 1;
0216  Constant UPTO_PE      = 2;
0217  Constant NUMBER_PE    = 3;
0218  Constant CANTSEE_PE   = 4;
0219  Constant TOOLIT_PE    = 5;
0220  Constant NOTHELD_PE   = 6;
0221  Constant MULTI_PE     = 7;
0222  Constant MMULTI_PE    = 8;
0223  Constant VAGUE_PE     = 9;
0224  Constant EXCEPT_PE    = 10;
0225  Constant ANIMA_PE     = 11;
0226  Constant VERB_PE      = 12;
0227  Constant SCENERY_PE   = 13;
0228  Constant ITGONE_PE    = 14;
0229  Constant JUNKAFTER_PE = 15;
0230  Constant TOOFEW_PE    = 16;
0231  Constant NOTHING_PE   = 17;
0232  Constant ASKSCOPE_PE  = 18;
0233  ! ------------------------------------------------------------------------------
0234  !   Pattern-matching against a single grammar line
0235  ! ------------------------------------------------------------------------------
0236  Array pattern --> 32;                ! For the current pattern match
0237  Global pcount;                       ! and a marker within it
0238  Array pattern2 --> 32;               ! And another, which stores the best match
0239  Global pcount2;                      ! so far
0240  Constant PATTERN_NULL = $ffff;       ! Entry for a token producing no text
0241   
0242  Array  line_ttype-->32;              ! For storing an analysed grammar line
0243  Array  line_tdata-->32;
0244  Array  line_token-->32;
0245   
0246  Global parameters;                   ! Parameters (objects) entered so far
0247  Global nsns;                         ! Number of special_numbers entered so far
0248  Global special_number1;              ! First number, if one was typed
0249  Global special_number2;              ! Second number, if two were typed
0250  ! ------------------------------------------------------------------------------
0251  !   Inferences and looking ahead
0252  ! ------------------------------------------------------------------------------
0253  Global params_wanted;                ! Number of parameters needed
0254                                       ! (which may change in parsing)
0255   
0256  Global inferfrom;                    ! The point from which the rest of the
0257                                       ! command must be inferred
0258  Global inferword;                    ! And the preposition inferred
0259  Global dont_infer;                   ! Another dull flag
0260   
0261  Global action_to_be;                 ! (If the current line were accepted.)
0262  Global action_reversed;              ! (Parameters would be reversed in order.)
0263  Global advance_warning;              ! What a later-named thing will be
0264  ! ------------------------------------------------------------------------------
0265  !   At the level of individual tokens now
0266  ! ------------------------------------------------------------------------------
0267  Global found_ttype;                  ! Used to break up tokens into type
0268  Global found_tdata;                  ! and data (by AnalyseToken)
0269  Global token_filter;                 ! For noun filtering by user routines
0270   
0271  Global length_of_noun;               ! Set by NounDomain to no of words in noun
0272  Constant REPARSE_CODE = 10000;       ! Signals "reparse the text" as a reply
0273                                       ! from NounDomain
0274   
0275  Global lookahead;                    ! The token after the one now being matched
0276   
0277  Global multi_mode;                   ! Multiple mode
0278  Global multi_wanted;                 ! Number of things needed in multitude
0279  Global multi_had;                    ! Number of things actually found
0280  Global multi_context;                ! What token the multi-obj was accepted for
0281   
0282  Global indef_mode;                   ! "Indefinite" mode - ie, "take a brick"
0283                                       ! is in this mode
0284  Global indef_type;                   ! Bit-map holding types of specification
0285  Global indef_wanted;                 ! Number of items wanted (100 for all)
0286  Global indef_guess_p;                ! Plural-guessing flag
0287  Global indef_owner;                  ! Object which must hold these items
0288  Global indef_cases;                  ! Possible gender and numbers of them
0289  Global indef_possambig;              ! Has a possibly dangerous assumption
0290                                       ! been made about meaning of a descriptor?
0291  Global indef_nspec_at;               ! Word at which a number like "two" was
0292                                       ! parsed (for backtracking)
0293  Global allow_plurals;                ! Whether plurals presently allowed or not
0294   
0295  Global take_all_rule;                ! Slightly different rules apply to
0296                                       ! "take all" than other uses of multiple
0297                                       ! objects, to make adjudication produce
0298                                       ! more pragmatically useful results
0299                                       ! (Not a flag: possible values 0, 1, 2)
0300   
0301  Global dict_flags_of_noun;           ! Of the noun currently being parsed
0302                                       ! (a bitmap in #dict_par1 format)
0303  Global pronoun_word;                 ! Records which pronoun ("it", "them", ...)
0304                                       ! caused an error
0305  Global pronoun_obj;                  ! And what obj it was thought to refer to
0306  Global pronoun__word;                ! Saved value
0307  Global pronoun__obj;                 ! Saved value
0308  ! ------------------------------------------------------------------------------
0309  !   Searching through scope and parsing "scope=Routine" grammar tokens
0310  ! ------------------------------------------------------------------------------
0311  Constant PARSING_REASON       = 0;   ! Possible reasons for searching scope
0312  Constant TALKING_REASON       = 1;
0313  Constant EACH_TURN_REASON     = 2;
0314  Constant REACT_BEFORE_REASON  = 3;
0315  Constant REACT_AFTER_REASON   = 4;
0316  Constant LOOPOVERSCOPE_REASON = 5;
0317  Constant TESTSCOPE_REASON     = 6;
0318   
0319  Global scope_reason = PARSING_REASON; ! Current reason for searching scope
0320   
0321  Global scope_token;                  ! For "scope=Routine" grammar tokens
0322  Global scope_error;
0323  Global scope_stage;                  ! 1, 2 then 3
0324   
0325  Global ats_flag = 0;                 ! For AddToScope routines
0326  Global ats_hls;                      !
0327   
0328  Global placed_in_flag;               ! To do with PlaceInScope
0329   
0330  ! ------------------------------------------------------------------------------
0331  !   The match list of candidate objects for a given token
0332  ! ------------------------------------------------------------------------------
0333  Constant MATCH_LIST_SIZE = 128;
0334  Array  match_list    --> 64;         ! An array of matched objects so far
0335  Array  match_classes --> 64;         ! An array of equivalence classes for them
0336  Array  match_scores --> 64;          ! An array of match scores for them
0337  Global number_matched;               ! How many items in it?  (0 means none)
0338  Global number_of_classes;            ! How many equivalence classes?
0339  Global match_length;                 ! How many words long are these matches?
0340  Global match_from;                   ! At what word of the input do they begin?
0341  Global bestguess_score;              ! What did the best-guess object score?
0342  ! ------------------------------------------------------------------------------
0343  !   Low level textual manipulation
0344  ! ------------------------------------------------------------------------------
0345  Array  buffer    -> 121;             ! Buffer for parsing main line of input
0346  Array  parse     -> 65;              ! Parse table mirroring it
0347  Array  buffer2   -> 121;             ! Buffers for supplementary questions
0348  Array  parse2    -> 65;              !
0349  Array  buffer3   -> 121;             ! Buffer retaining input for "again"
0350   
0351  Constant comma_word = 'comma,';      ! An "untypeable word" used to substitute
0352                                       ! for commas in parse buffers
0353   
0354  Global wn;                           ! Word number within "parse" (from 1)
0355  Global num_words;                    ! Number of words typed
0356  Global verb_word;                    ! Verb word (eg, take in "take all" or
0357                                       ! "dwarf, take all") - address in dict
0358  Global verb_wordnum;                 ! its number in typing order (eg, 1 or 3)
0359  Global usual_grammar_after;          ! Point from which usual grammar is parsed
0360                                       ! (it may vary from the above if user's
0361                                       ! routines match multi-word verbs)
0362   
0363  Global oops_from;                    ! The "first mistake" word number
0364  Global saved_oops;                   ! Used in working this out
0365  Array  oops_workspace -> 64;         ! Used temporarily by "oops" routine
0366   
0367  Global held_back_mode;               ! Flag: is there some input from last time
0368  Global hb_wn;                        ! left over?  (And a save value for wn.)
0369                                       ! (Used for full stops and "then".)
0370  ! ----------------------------------------------------------------------------
0371  Array PowersOfTwo_TB                 ! Used in converting case numbers to
0372    --> $$100000000000                 ! case bitmaps
0373        $$010000000000
0374        $$001000000000
0375        $$000100000000
0376        $$000010000000
0377        $$000001000000
0378        $$000000100000
0379        $$000000010000
0380        $$000000001000
0381        $$000000000100
0382        $$000000000010
0383        $$000000000001;
0384  ! ============================================================================
0385   
0386   
0387  ! ============================================================================
0388  !  Constants, and one variable, needed for the language definition file
0389  ! ----------------------------------------------------------------------------
0390  Constant POSSESS_PK  = $100;
0391  Constant DEFART_PK   = $101;
0392  Constant INDEFART_PK = $102;
0393  Global short_name_case;
0394  ! ----------------------------------------------------------------------------
0395  Include "language__";                !  The natural language definition,
0396                                       !  whose filename is taken from the ICL
0397                                       !  language_name variable
0398  ! ----------------------------------------------------------------------------
0399  #ifndef LanguageCases;
0400  Constant LanguageCases = 1;
0401  #endif;
0402  ! ------------------------------------------------------------------------------
0403  !   Pronouns support for the cruder (library 6/2 and earlier) version:
0404  !   only needed in English
0405  ! ------------------------------------------------------------------------------
0406  #ifdef EnglishNaturalLanguage;
0407  Global itobj = NULL;                 ! The object which is currently "it"
0408  Global himobj = NULL;                ! The object which is currently "him"
0409  Global herobj = NULL;                ! The object which is currently "her"
0410   
0411  Global old_itobj = NULL;             ! The object which is currently "it"
0412  Global old_himobj = NULL;            ! The object which is currently "him"
0413  Global old_herobj = NULL;            ! The object which is currently "her"
0414  #endif;
0415  ! ============================================================================
0416   
0417   
0418  ! ============================================================================
0419  ! "Darkness" is not really a place: but it has to be an object so that the
0420  !  location-name on the status line can be "Darkness".
0421  ! ----------------------------------------------------------------------------
0422  Object thedark "(darkness object)"
0423    with initial 0,
0424         short_name DARKNESS__TX,
0425         description
0426         [;  return L__M(##Miscellany, 17);
0427         ];
0428  Object selfobj "(self object)"
0429    with short_name
0430         [;  return L__M(##Miscellany, 18);
0431         ],
0432         description
0433         [;  return L__M(##Miscellany, 19);
0434         ],
0435         before NULL,   after NULL,    life NULL,    each_turn NULL,
0436         time_out NULL, describe NULL,
0437         capacity 100, parse_name 0,
0438         orders 0, number 0,
0439    has  concealed animate proper transparent;
0440   
0441  ! ============================================================================
0442  !  The definition of the token-numbering system used by Inform.
0443  ! ----------------------------------------------------------------------------
0444   
0445  Constant ILLEGAL_TT        = 0;      ! Types of grammar token: illegal
0446  Constant ELEMENTARY_TT     = 1;      !     (one of those below)
0447  Constant PREPOSITION_TT    = 2;      !     e.g. 'into'
0448  Constant ROUTINE_FILTER_TT = 3;      !     e.g. noun=CagedCreature
0449  Constant ATTR_FILTER_TT    = 4;      !     e.g. edible
0450  Constant SCOPE_TT          = 5;      !     e.g. scope=Spells
0451  Constant GPR_TT            = 6;      !     a general parsing routine
0452   
0453  Constant NOUN_TOKEN        = 0;      ! The elementary grammar tokens, and
0454  Constant HELD_TOKEN        = 1;      ! the numbers compiled by Inform to
0455  Constant MULTI_TOKEN       = 2;      ! encode them
0456  Constant MULTIHELD_TOKEN   = 3;
0457  Constant MULTIEXCEPT_TOKEN = 4;
0458  Constant MULTIINSIDE_TOKEN = 5;
0459  Constant CREATURE_TOKEN    = 6;
0460  Constant SPECIAL_TOKEN     = 7;
0461  Constant NUMBER_TOKEN      = 8;
0462  Constant TOPIC_TOKEN       = 9;
0463   
0464   
0465  Constant GPR_FAIL          = -1;     ! Return values from General Parsing
0466  Constant GPR_PREPOSITION   = 0;      ! Routines
0467  Constant GPR_NUMBER        = 1;
0468  Constant GPR_MULTIPLE      = 2;
0469  Constant GPR_REPARSE       = REPARSE_CODE;
0470  Constant GPR_NOUN          = $ff00;
0471  Constant GPR_HELD          = $ff01;
0472  Constant GPR_MULTI         = $ff02;
0473  Constant GPR_MULTIHELD     = $ff03;
0474  Constant GPR_MULTIEXCEPT   = $ff04;
0475  Constant GPR_MULTIINSIDE   = $ff05;
0476  Constant GPR_CREATURE      = $ff06;
0477   
0478  Constant ENDIT_TOKEN       = 15;     ! Value used to mean "end of grammar line"
0479   
0480  #Iftrue Grammar__Version == 1;
0481  [ AnalyseToken token m;
0482   
0483      found_tdata = token;
0484   
0485      if (token < 0)   { found_ttype = ILLEGAL_TT; return; }
0486      if (token <= 8)  { found_ttype = ELEMENTARY_TT; return; }
0487      if (token < 15)  { found_ttype = ILLEGAL_TT; return; }
0488      if (token == 15) { found_ttype = ELEMENTARY_TT; return; }
0489      if (token < 48)  { found_ttype = ROUTINE_FILTER_TT;
0490                         found_tdata = token - 16;
0491                         return; }
0492      if (token < 80)  { found_ttype = GPR_TT;
0493                         found_tdata = #preactions_table-->(token-48);
0494                         return; }
0495      if (token < 128) { found_ttype = SCOPE_TT;
0496                         found_tdata = #preactions_table-->(token-80);
0497                         return; }
0498      if (token < 180) { found_ttype = ATTR_FILTER_TT;
0499                         found_tdata = token - 128;
0500                         return; }
0501   
0502      found_ttype = PREPOSITION_TT;
0503      m=#adjectives_table;
0504      for (::)
0505      {   if (token==m-->1) { found_tdata = m-->0; return; }
0506          m=m+4;
0507      }
0508      m=#adjectives_table; RunTimeError(1);
0509      found_tdata = m;
0510  ];
0511  [ UnpackGrammarLine line_address i m;
0512    for (i = 0 : i < 32 : i++)
0513    {   line_token-->i = ENDIT_TOKEN;
0514        line_ttype-->i = ELEMENTARY_TT;
0515        line_tdata-->i = ENDIT_TOKEN;
0516    }
0517    for (i = 0: i <= 5 :i++)
0518    {   line_token-->i = line_address->(i+1);
0519        AnalyseToken(line_token-->i);
0520        if ((found_ttype == ELEMENTARY_TT) && (found_tdata == NOUN_TOKEN)
0521            && (m == line_address->0))
0522        {   line_token-->i = ENDIT_TOKEN;
0523            break;
0524        }
0525        line_ttype-->i = found_ttype;
0526        line_tdata-->i = found_tdata;
0527        if (found_ttype ~= PREPOSITION_TT) m++;
0528    }
0529    action_to_be = line_address->7;
0530    action_reversed = false;
0531    params_wanted = line_address->0;
0532    return line_address + 8;
0533  ];
0534  #Ifnot;
0535  [ AnalyseToken token;
0536   
0537      if (token == ENDIT_TOKEN)
0538      {   found_ttype = ELEMENTARY_TT;
0539          found_tdata = ENDIT_TOKEN;
0540          return;
0541      }
0542   
0543      found_ttype = (token->0) & $$1111;
0544      found_tdata = (token+1)-->0;
0545  ];
0546  [ UnpackGrammarLine line_address i;
0547    for (i = 0 : i < 32 : i++)
0548    {   line_token-->i = ENDIT_TOKEN;
0549        line_ttype-->i = ELEMENTARY_TT;
0550        line_tdata-->i = ENDIT_TOKEN;
0551    }
0552    action_to_be = 256*(line_address->0) + line_address->1;
0553    action_reversed = ((action_to_be & $400) ~= 0);
0554    action_to_be = action_to_be & $3ff;
0555    line_address--;
0556    params_wanted = 0;
0557    for (i=0::i++)
0558    {   line_address = line_address + 3;
0559        if (line_address->0 == ENDIT_TOKEN) break;
0560        line_token-->i = line_address;
0561        AnalyseToken(line_address);
0562        if (found_ttype ~= PREPOSITION_TT) params_wanted++;
0563        line_ttype-->i = found_ttype;
0564        line_tdata-->i = found_tdata;
0565    }
0566    return line_address + 1;
0567  ];
0568  #Endif;
0569   
0570  !  To protect against a bug in early versions of the "Zip" interpreter:
0571   
0572  [ Tokenise__ b p; b->(2 + b->1) = 0; @tokenise b p; ];
0573   
0574  ! ============================================================================
0575  !  The InformParser object abstracts the front end of the parser.
0576  !
0577  !  InformParser.parse_input(results)
0578  !  returns only when a sensible request has been made, and puts into the
0579  !  "results" buffer:
0580  !
0581  !  --> 0 = The action number
0582  !  --> 1 = Number of parameters
0583  !  --> 2, 3, ... = The parameters (object numbers), but
0584  !                  0 means "put the multiple object list here"
0585  !                  1 means "put one of the special numbers here"
0586  !
0587  ! ----------------------------------------------------------------------------
0588   
0589  Object InformParser "(Inform Parser)"
0590    with parse_input
0591         [ results; Parser__parse(results);
0592         ], has proper;
0593   
0594  ! ----------------------------------------------------------------------------
0595  !  The Keyboard routine actually receives the player's words,
0596  !  putting the words in "a_buffer" and their dictionary addresses in
0597  !  "a_table".  It is assumed that the table is the same one on each
0598  !  (standard) call.
0599  !
0600  !  It can also be used by miscellaneous routines in the game to ask
0601  !  yes-no questions and the like, without invoking the rest of the parser.
0602  !
0603  !  Return the number of words typed
0604  ! ----------------------------------------------------------------------------
0605   
0606  [ KeyboardPrimitive  a_buffer a_table;
0607    read a_buffer a_table;
0608  ];
0609  [ Keyboard  a_buffer a_table  nw i w w2 x1 x2;
0610   
0611      DisplayStatus();
0612      .FreshInput;
0613   
0614  !  Save the start of the buffer, in case "oops" needs to restore it
0615  !  to the previous time's buffer
0616   
0617      for (i=0:i<64:i++) oops_workspace->i = a_buffer->i;
0618   
0619  !  In case of an array entry corruption that shouldn't happen, but would be
0620  !  disastrous if it did:
0621   
0622     a_buffer->0 = 120;
0623     a_table->0 = 15;  ! Allow to split input into this many words
0624   
0625  !  Print the prompt, and read in the words and dictionary addresses
0626   
0627      L__M(##Prompt);
0628      AfterPrompt();
0629      #IFV5; DrawStatusLine(); #ENDIF;
0630      KeyboardPrimitive(a_buffer, a_table);
0631      nw=a_table->1;
0632   
0633  !  If the line was blank, get a fresh line
0634      if (nw == 0)
0635      { L__M(##Miscellany,10); jump FreshInput; }
0636   
0637  !  Unless the opening word was "oops", return
0638   
0639      w=a_table-->1;
0640      if (w == OOPS1__WD or OOPS2__WD or OOPS3__WD) jump DoOops;
0641   
0642  #IFV5;
0643  !  Undo handling
0644   
0645      if ((w == UNDO1__WD or UNDO2__WD or UNDO3__WD) && (parse->1==1))
0646      {   if (turns==1)
0647          {   L__M(##Miscellany,11); jump FreshInput;
0648          }
0649          if (undo_flag==0)
0650          {   L__M(##Miscellany,6); jump FreshInput;
0651          }
0652          if (undo_flag==1) jump UndoFailed;
0653          if (just_undone==1)
0654          {   L__M(##Miscellany,12); jump FreshInput;
0655          }
0656          @restore_undo i;
0657          if (i==0)
0658          {   .UndoFailed;
0659              L__M(##Miscellany,7);
0660          }
0661          jump FreshInput;
0662      }
0663      @save_undo i;
0664      just_undone=0;
0665      undo_flag=2;
0666      if (i==-1) undo_flag=0;
0667      if (i==0) undo_flag=1;
0668      if (i==2)
0669      {   style bold;
0670          print (name) location, "^";
0671          style roman;
0672          L__M(##Miscellany,13);
0673          just_undone=1;
0674          jump FreshInput;
0675      }
0676  #ENDIF;
0677   
0678      return nw;
0679   
0680      .DoOops;
0681      if (oops_from == 0)
0682      {   L__M(##Miscellany,14); jump FreshInput; }
0683      if (nw == 1)
0684      {   L__M(##Miscellany,15); jump FreshInput; }
0685      if (nw > 2)
0686      {   L__M(##Miscellany,16); jump FreshInput; }
0687   
0688  !  So now we know: there was a previous mistake, and the player has
0689  !  attempted to correct a single word of it.
0690   
0691      for (i=0:i<=120:i++) buffer2->i = a_buffer->i;
0692      x1 = a_table->9; ! Start of word following "oops"
0693      x2 = a_table->8; ! Length of word following "oops"
0694   
0695  !  Repair the buffer to the text that was in it before the "oops"
0696  !  was typed:
0697   
0698      for (i=0:i<64:i++) a_buffer->i = oops_workspace->i;
0699      Tokenise__(a_buffer,a_table);
0700   
0701  !  Work out the position in the buffer of the word to be corrected:
0702   
0703      w = a_table->(4*oops_from + 1); ! Start of word to go
0704      w2 = a_table->(4*oops_from);    ! Length of word to go
0705   
0706  !  Write spaces over the word to be corrected:
0707   
0708      for (i=0:i<w2:i++) a_buffer->(i+w) = ' ';
0709   
0710      if (w2 < x2)
0711      {   ! If the replacement is longer than the original, move up...
0712   
0713          for (i=120:i>=w+x2:i--)
0714              a_buffer->i = a_buffer->(i-x2+w2);
0715   
0716          ! ...increasing buffer size accordingly.
0717   
0718          a_buffer->1 = (a_buffer->1) + (x2-w2);
0719      }
0720   
0721  !  Write the correction in:
0722   
0723      for (i=0:i<x2:i++) a_buffer->(i+w) = buffer2->(i+x1);
0724   
0725      Tokenise__(a_buffer,a_table);
0726      nw=a_table->1;
0727   
0728      return nw;
0729  ];
0730   
0731  ! ----------------------------------------------------------------------------
0732  !  To simplify the picture a little, a rough map of the main routine:
0733  !
0734  !  (A)    Get the input, do "oops" and "again"
0735  !  (B)    Is it a direction, and so an implicit "go"?  If so go to (K)
0736  !  (C)    Is anyone being addressed?
0737  !  (D)    Get the verb: try all the syntax lines for that verb
0738  !  (E)    Break down a syntax line into analysed tokens
0739  !  (F)    Look ahead for advance warning for multiexcept/multiinside
0740  !  (G)    Parse each token in turn (calling ParseToken to do most of the work)
0741  !  (H)    Cheaply parse otherwise unrecognised conversation and return
0742  !  (I)    Print best possible error message
0743  !  (J)    Retry the whole lot
0744  !  (K)    Last thing: check for "then" and further instructions(s), return.
0745  !
0746  !  The strategic points (A) to (K) are marked in the commentary.
0747  !
0748  !  Note that there are three different places where a return can happen.
0749  ! ----------------------------------------------------------------------------
0750   
0751  [ Parser__parse  results   syntax line num_lines line_address i j k
0752                             token l m;
0753   
0754  !  **** (A) ****
0755   
0756  !  Firstly, in "not held" mode, we still have a command left over from last
0757  !  time (eg, the user typed "eat biscuit", which was parsed as "take biscuit"
0758  !  last time, with "eat biscuit" tucked away until now).  So we return that.
0759   
0760      if (notheld_mode==1)
0761      {   for (i=0:i<8:i++) results-->i=kept_results-->i;
0762          notheld_mode=0; rtrue;
0763      }
0764   
0765      if (held_back_mode==1)
0766      {   held_back_mode=0;
0767          Tokenise__(buffer,parse);
0768          jump ReParse;
0769      }
0770   
0771    .ReType;
0772   
0773      Keyboard(buffer,parse);
0774   
0775    .ReParse;
0776   
0777      parser_inflection = name;
0778   
0779  !  Initially assume the command is aimed at the player, and the verb
0780  !  is the first word
0781   
0782      num_words=parse->1;
0783      wn=1;
0784  #ifdef LanguageToInformese;
0785      LanguageToInformese();
0786  #ifv5;
0787  !   Re-tokenise:
0788      Tokenise__(buffer,parse);
0789  #endif;
0790  #endif;
0791   
0792      BeforeParsing();
0793      num_words=parse->1;
0794   
0795      k=0;
0796  #ifdef DEBUG;
0797      if (parser_trace>=2)
0798      {   print "[ ";
0799          for (i=0:i<num_words:i++)
0800          {   j=parse-->(i*2 + 1);
0801              k=WordAddress(i+1);
0802              l=WordLength(i+1);
0803              print "~"; for (m=0:m<l:m++) print (char) k->m; print "~ ";
0804   
0805              if (j == 0) print "?";
0806              else
0807              {   if (UnsignedCompare(j, 0-->4)>=0
0808                      && UnsignedCompare(j, 0-->2)<0) print (address) j;
0809                  else print j;
0810              }
0811              if (i ~= num_words-1) print " / ";
0812          }
0813          print " ]^";
0814      }
0815  #endif;
0816      verb_wordnum=1;
0817      actor=player;
0818      actors_location = ScopeCeiling(player);
0819      usual_grammar_after = 0;
0820   
0821    .AlmostReParse;
0822   
0823      scope_token = 0;
0824      action_to_be = NULL;
0825   
0826  !  Begin from what we currently think is the verb word
0827   
0828    .BeginCommand;
0829      wn=verb_wordnum;
0830      verb_word = NextWordStopped();
0831   
0832  !  If there's no input here, we must have something like
0833  !  "person,".
0834   
0835      if (verb_word==-1)
0836      {   best_etype = STUCK_PE; jump GiveError; }
0837   
0838  !  Now try for "again" or "g", which are special cases:
0839  !  don't allow "again" if nothing has previously been typed;
0840  !  simply copy the previous text across
0841   
0842      if (verb_word==AGAIN2__WD or AGAIN3__WD) verb_word=AGAIN1__WD;
0843      if (verb_word==AGAIN1__WD)
0844      {   if (actor~=player)
0845          {   L__M(##Miscellany,20); jump ReType; }
0846          if (buffer3->1==0)
0847          {   L__M(##Miscellany,21); jump ReType; }
0848          for (i=0:i<120:i++) buffer->i=buffer3->i;
0849          jump ReParse;
0850      }
0851   
0852  !  Save the present input in case of an "again" next time
0853   
0854      if (verb_word~=AGAIN1__WD)
0855          for (i=0:i<120:i++) buffer3->i=buffer->i;
0856   
0857      if (usual_grammar_after==0)
0858      {   i = RunRoutines(actor, grammar);
0859          #ifdef DEBUG;
0860          if (parser_trace>=2 && actor.grammar~=0 or NULL)
0861              print " [Grammar property returned ", i, "]^";
0862          #endif;
0863          if (i<0) { usual_grammar_after = verb_wordnum; i=-i; }
0864          if (i==1)
0865          {   results-->0 = action;
0866              results-->1 = noun;
0867              results-->2 = second;
0868              rtrue;
0869          }
0870          if (i~=0) { verb_word = i; wn--; verb_wordnum--; }
0871          else
0872          {   wn = verb_wordnum; verb_word=NextWord();
0873          }
0874      }
0875      else usual_grammar_after=0;
0876   
0877  !  **** (B) ****
0878   
0879      #ifdef LanguageIsVerb;
0880      if (verb_word==0)
0881      {   i = wn; verb_word=LanguageIsVerb(buffer, parse, verb_wordnum);
0882          wn = i;
0883      }
0884      #endif;
0885   
0886  !  If the first word is not listed as a verb, it must be a direction
0887  !  or the name of someone to talk to
0888   
0889      if (verb_word==0 || ((verb_word->#dict_par1) & 1) == 0)
0890      {   
0891   
0892  !  So is the first word an object contained in the special object "compass"
0893  !  (i.e., a direction)?  This needs use of NounDomain, a routine which
0894  !  does the object matching, returning the object number, or 0 if none found,
0895  !  or REPARSE_CODE if it has restructured the parse table so the whole parse
0896  !  must be begun again...
0897   
0898          wn=verb_wordnum; indef_mode = false; token_filter = 0;
0899          l=NounDomain(compass,0,0); if (l==REPARSE_CODE) jump ReParse;
0900   
0901  !  If it is a direction, send back the results:
0902  !  action=GoSub, no of arguments=1, argument 1=the direction.
0903   
0904          if (l~=0)
0905          {   results-->0 = ##Go;
0906              action_to_be = ##Go;
0907              results-->1 = 1;
0908              results-->2 = l;
0909              jump LookForMore;
0910          }
0911   
0912  !  **** (C) ****
0913   
0914  !  Only check for a comma (a "someone, do something" command) if we are
0915  !  not already in the middle of one.  (This simplification stops us from
0916  !  worrying about "robot, wizard, you are an idiot", telling the robot to
0917  !  tell the wizard that she is an idiot.)
0918   
0919          if (actor==player)
0920          {   for (j=2:j<=num_words:j++)
0921              {   i=NextWord(); if (i==comma_word) jump Conversation;
0922              }
0923   
0924              verb_word=UnknownVerb(verb_word);
0925              if (verb_word~=0) jump VerbAccepted;
0926          }
0927   
0928          best_etype=VERB_PE; jump GiveError;
0929   
0930  !  NextWord nudges the word number wn on by one each time, so we've now
0931  !  advanced past a comma.  (A comma is a word all on its own in the table.)
0932   
0933        .Conversation;
0934          j=wn-1;
0935          if (j==1) { L__M(##Miscellany,22); jump ReType; }
0936   
0937  !  Use NounDomain (in the context of "animate creature") to see if the
0938  !  words make sense as the name of someone held or nearby
0939   
0940          wn=1; lookahead=HELD_TOKEN;
0941          scope_reason = TALKING_REASON;
0942          l=NounDomain(player,actors_location,6);
0943          scope_reason = PARSING_REASON;
0944          if (l==REPARSE_CODE) jump ReParse;
0945   
0946          if (l==0) { L__M(##Miscellany,23); jump ReType; }
0947   
0948  !  The object addressed must at least be "talkable" if not actually "animate"
0949  !  (the distinction allows, for instance, a microphone to be spoken to,
0950  !  without the parser thinking that the microphone is human).
0951   
0952          if (l hasnt animate && l hasnt talkable)
0953          {   L__M(##Miscellany, 24, l); jump ReType; }
0954   
0955  !  Check that there aren't any mystery words between the end of the person's
0956  !  name and the comma (eg, throw out "dwarf sdfgsdgs, go north").
0957   
0958          if (wn~=j)
0959          {   L__M(##Miscellany, 25); jump ReType; }
0960   
0961  !  The player has now successfully named someone.  Adjust "him", "her", "it":
0962   
0963          PronounNotice(l);
0964   
0965  !  Set the global variable "actor", adjust the number of the first word,
0966  !  and begin parsing again from there.
0967   
0968          verb_wordnum=j+1;
0969   
0970  !  Stop things like "me, again":
0971   
0972          if (l == player)
0973          {   wn = verb_wordnum;
0974              if (NextWordStopped() == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD)
0975              {   L__M(##Miscellany,20); jump ReType;
0976              }
0977          }
0978   
0979          actor=l;
0980          actors_location=ScopeCeiling(l);
0981          #ifdef DEBUG;
0982          if (parser_trace>=1)
0983              print "[Actor is ", (the) actor, " in ",
0984                  (name) actors_location, "]^";
0985          #endif;
0986          jump BeginCommand;
0987      }
0988   
0989  !  **** (D) ****
0990   
0991     .VerbAccepted;
0992   
0993  !  We now definitely have a verb, not a direction, whether we got here by the
0994  !  "take ..." or "person, take ..." method.  Get the meta flag for this verb:
0995   
0996      meta=((verb_word->#dict_par1) & 2)/2;
0997   
0998  !  You can't order other people to "full score" for you, and so on...
0999   
1000      if (meta==1 && actor~=player)
1001      {   best_etype=VERB_PE; meta=0; jump GiveError; }
1002   
1003  !  Now let i be the corresponding verb number, stored in the dictionary entry
1004  !  (in a peculiar 255-n fashion for traditional Infocom reasons)...
1005   
1006      i=$ff-(verb_word->#dict_par2);
1007   
1008  !  ...then look up the i-th entry in the verb table, whose address is at word
1009  !  7 in the Z-machine (in the header), so as to get the address of the syntax
1010  !  table for the given verb...
1011   
1012      syntax=(0-->7)-->i;
1013   
1014  !  ...and then see how many lines (ie, different patterns corresponding to the
1015  !  same verb) are stored in the parse table...
1016   
1017      num_lines=(syntax->0)-1;
1018   
1019  !  ...and now go through them all, one by one.
1020  !  To prevent pronoun_word 0 being misunderstood,
1021   
1022     pronoun_word=NULL; pronoun_obj=NULL;
1023   
1024     #ifdef DEBUG;
1025     if (parser_trace>=1)
1026     {    print "[Parsing for the verb '", (address) verb_word,
1027                "' (", num_lines+1, " lines)]^";
1028     }
1029     #endif;
1030   
1031     best_etype=STUCK_PE; nextbest_etype=STUCK_PE;
1032   
1033  !  "best_etype" is the current failure-to-match error - it is by default
1034  !  the least informative one, "don't understand that sentence".
1035  !  "nextbest_etype" remembers the best alternative to having to ask a
1036  !  scope token for an error message (i.e., the best not counting ASKSCOPE_PE).
1037   
1038   
1039  !  **** (E) ****
1040   
1041      line_address = syntax + 1;
1042   
1043      for (line=0:line<=num_lines:line++)
1044      {   
1045          for (i = 0 : i < 32 : i++)
1046          {   line_token-->i = ENDIT_TOKEN;
1047              line_ttype-->i = ELEMENTARY_TT;
1048              line_tdata-->i = ENDIT_TOKEN;
1049          }
1050   
1051  !  Unpack the syntax line from Inform format into three arrays; ensure that
1052  !  the sequence of tokens ends in an ENDIT_TOKEN.
1053   
1054          line_address = UnpackGrammarLine(line_address);
1055              
1056          #ifdef DEBUG;
1057          if (parser_trace >= 1)
1058          {   if (parser_trace >= 2) new_line;
1059              print "[line ", line; DebugGrammarLine();
1060              print "]^";
1061          }
1062          #endif;
1063   
1064  !  We aren't in "not holding" or inferring modes, and haven't entered
1065  !  any parameters on the line yet, or any special numbers; the multiple
1066  !  object is still empty.
1067   
1068          not_holding=0;
1069          inferfrom=0;
1070          parameters=0;
1071          nsns=0; special_word=0; special_number=0;
1072          multiple_object-->0 = 0;
1073          multi_context = 0;
1074          etype=STUCK_PE;
1075   
1076  !  Put the word marker back to just after the verb
1077   
1078          wn=verb_wordnum+1;
1079   
1080  !  **** (F) ****
1081  !  There are two special cases where parsing a token now has to be
1082  !  affected by the result of parsing another token later, and these
1083  !  two cases (multiexcept and multiinside tokens) are helped by a quick
1084  !  look ahead, to work out the future token now.  We can only carry this
1085  !  out in the simple (but by far the most common) case:
1086  !
1087  !      multiexcept  noun
1088  !
1089  !  and similarly for multiinside.
1090   
1091          advance_warning = NULL; indef_mode = false;
1092          for (i=0,m=false,pcount=0:line_token-->pcount ~= ENDIT_TOKEN:pcount++)
1093          {   scope_token = 0;
1094   
1095              if (line_ttype-->pcount ~= PREPOSITION_TT) i++;
1096   
1097              if (line_ttype-->pcount == ELEMENTARY_TT)
1098              {   if (line_tdata-->pcount == MULTI_TOKEN) m=true;
1099                  if (line_tdata-->pcount
1100                      == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN  && i==1)
1101                  {   !   First non-preposition is "multiexcept" or
1102                      !   "multiinside", so look ahead.
1103   
1104                      #ifdef DEBUG;
1105                      if (parser_trace>=2) print " [Trying look-ahead]^";
1106                      #endif;
1107   
1108                      !   We need this to be followed by 1 or more prepositions.
1109   
1110                      pcount++;
1111                      if (line_ttype-->pcount == PREPOSITION_TT)
1112                      {   while (line_ttype-->pcount == PREPOSITION_TT)
1113                              pcount++;
1114   
1115                          if ((line_ttype-->pcount == ELEMENTARY_TT)
1116                              && (line_tdata-->pcount == NOUN_TOKEN))
1117                          {
1118                              !  Advance past the last preposition
1119   
1120                              while (wn <= num_words)
1121                              {   if (NextWord() == line_tdata-->(pcount-1))
1122                                  {   l = NounDomain(actors_location, actor,
1123                                              NOUN_TOKEN);
1124                                      #ifdef DEBUG;
1125                                      if (parser_trace>=2)
1126                                      {   print " [Advanced to ~noun~ token: ";
1127                                          if (l==REPARSE_CODE)
1128                                              print "re-parse request]^";
1129                                          if (l==1) print "but multiple found]^";
1130                                          if (l==0) print "error ", etype, "]^";
1131                                          if (l>=2) print (the) l, "]^";
1132                                      }
1133                                      #endif;
1134                                      if (l==REPARSE_CODE) jump ReParse;
1135                                      if (l>=2) advance_warning = l;
1136                                  }
1137                              }
1138                          }
1139                      }
1140                      break;
1141                  }
1142              }
1143          }
1144   
1145  !  Slightly different line-parsing rules will apply to "take multi", to
1146  !  prevent "take all" behaving correctly but misleadingly when there's
1147  !  nothing to take.
1148   
1149          take_all_rule = 0;
1150          if (m && params_wanted==1 && action_to_be==##Take)
1151              take_all_rule = 1;
1152   
1153  !  And now start again, properly, forearmed or not as the case may be.
1154  !  As a precaution, we clear all the variables again (they may have been
1155  !  disturbed by the call to NounDomain, which may have called outside
1156  !  code, which may have done anything!).
1157   
1158          not_holding=0;
1159          inferfrom=0;
1160          parameters=0;
1161          nsns=0; special_word=0; special_number=0;
1162          multiple_object-->0 = 0;
1163          etype=STUCK_PE;
1164          wn=verb_wordnum+1;
1165   
1166  !  **** (G) ****
1167  !  "Pattern" gradually accumulates what has been recognised so far,
1168  !  so that it may be reprinted by the parser later on
1169   
1170          for (pcount=1::pcount++)
1171          {   pattern-->pcount = PATTERN_NULL; scope_token=0;
1172   
1173              token = line_token-->(pcount-1);
1174              lookahead = line_token-->pcount;
1175   
1176              #ifdef DEBUG;
1177              if (parser_trace >= 2)
1178                 print " [line ", line, " token ", pcount, " word ", wn, " : ",
1179                       (DebugToken) token, "]^";
1180              #endif;
1181   
1182              if (token ~= ENDIT_TOKEN)
1183              {   scope_reason = PARSING_REASON;
1184                  parser_inflection = name;
1185                  AnalyseToken(token);
1186                  l = ParseToken__(found_ttype, found_tdata, pcount-1, token);
1187                  while (l<-200) l = ParseToken__(ELEMENTARY_TT, l + 256);
1188                  scope_reason = PARSING_REASON;
1189   
1190                  if (l==GPR_PREPOSITION)
1191                  {   if (found_ttype~=PREPOSITION_TT
1192                          && (found_ttype~=ELEMENTARY_TT
1193                              || found_tdata~=TOPIC_TOKEN)) params_wanted--;
1194                      l = true;
1195                  }
1196                  else
1197                  if (l<0) l = false;
1198                  else
1199                  if (l~=GPR_REPARSE)
1200                  {   if (l==GPR_NUMBER)
1201                      {   if (nsns==0) special_number1=parsed_number;
1202                          else special_number2=parsed_number;
1203                          nsns++; l = 1;
1204                      }
1205                      if (l==GPR_MULTIPLE) l = 0;
1206                      results-->(parameters+2) = l;
1207                      parameters++;
1208                      pattern-->pcount = l;
1209                      l = true;
1210                  }
1211   
1212                  #ifdef DEBUG;
1213                  if (parser_trace >= 3)
1214                  {   print "  [token resulted in ";
1215                      if (l==REPARSE_CODE) print "re-parse request]^";
1216                      if (l==0) print "failure with error type ", etype, "]^";
1217                      if (l==1) print "success]^";
1218                  }
1219                  #endif;
1220   
1221                  if (l==REPARSE_CODE) jump ReParse;
1222                  if (l==false) break;
1223              }
1224              else
1225              {
1226   
1227  !  If the player has entered enough already but there's still
1228  !  text to wade through: store the pattern away so as to be able to produce
1229  !  a decent error message if this turns out to be the best we ever manage,
1230  !  and in the mean time give up on this line
1231   
1232  !  However, if the superfluous text begins with a comma or "then" then
1233  !  take that to be the start of another instruction
1234   
1235                  if (wn <= num_words)
1236                  {   l=NextWord();
1237                      if (l==THEN1__WD or THEN2__WD or THEN3__WD or comma_word)
1238                      {   held_back_mode=1; hb_wn=wn-1; }
1239                      else
1240                      {   for (m=0:m<32:m++) pattern2-->m=pattern-->m;
1241                          pcount2=pcount;
1242                          etype=UPTO_PE; break;
1243                      }
1244                  }
1245   
1246  !  Now, we may need to revise the multiple object because of the single one
1247  !  we now know (but didn't when the list was drawn up).
1248   
1249                  if (parameters>=1 && results-->2 == 0)
1250                  {   l=ReviseMulti(results-->3);
1251                      if (l~=0) { etype=l; break; }
1252                  }
1253                  if (parameters>=2 && results-->3 == 0)
1254                  {   l=ReviseMulti(results-->2);
1255                      if (l~=0) { etype=l; break; }
1256                  }
1257   
1258  !  To trap the case of "take all" inferring only "yourself" when absolutely
1259  !  nothing else is in the vicinity...
1260   
1261                  if (take_all_rule==2 && results-->2 == actor)
1262                  {   best_etype = NOTHING_PE; jump GiveError;
1263                  }
1264   
1265                  #ifdef DEBUG;
1266                  if (parser_trace>=1)
1267                      print "[Line successfully parsed]^";
1268                  #endif;
1269   
1270  !  The line has successfully matched the text.  Declare the input error-free...
1271   
1272                  oops_from = 0;
1273   
1274  !  ...explain any inferences made (using the pattern)...
1275   
1276                  if (inferfrom~=0)
1277                  {   print "("; PrintCommand(inferfrom); print ")^";
1278                  }
1279   
1280  !  ...copy the action number, and the number of parameters...
1281   
1282                  results-->0 = action_to_be;
1283                  results-->1 = parameters;
1284   
1285  !  ...reverse first and second parameters if need be...
1286   
1287                  if (action_reversed && parameters==2)
1288                  {   i = results-->2; results-->2 = results-->3;
1289                      results-->3 = i;
1290                      if (nsns == 2)
1291                      {   i = special_number1; special_number1=special_number2;
1292                          special_number2=i;
1293                      }
1294                  }
1295   
1296  !  ...and to reset "it"-style objects to the first of these parameters, if
1297  !  there is one (and it really is an object)...
1298   
1299                  if (parameters > 0 && results-->2 >= 2)
1300                      PronounNotice(results-->2);
1301   
1302  !  ...and worry about the case where an object was allowed as a parameter
1303  !  even though the player wasn't holding it and should have been: in this
1304  !  event, keep the results for next time round, go into "not holding" mode,
1305  !  and for now tell the player what's happening and return a "take" request
1306  !  instead...
1307   
1308                  if (not_holding~=0 && actor==player)
1309                  {   notheld_mode=1;
1310                      for (i=0:i<8:i++) kept_results-->i = results-->i;
1311                      results-->0 = ##Take;
1312                      results-->1 = 1;
1313                      results-->2 = not_holding;
1314                      L__M(##Miscellany, 26, not_holding);
1315                  }
1316   
1317  !  (Notice that implicit takes are only generated for the player, and not
1318  !  for other actors.  This avoids entirely logical, but misleading, text
1319  !  being printed.)
1320   
1321  !  ...and return from the parser altogether, having successfully matched
1322  !  a line.
1323   
1324                  if (held_back_mode==1) { wn=hb_wn; jump LookForMore; }
1325                  rtrue;
1326              }
1327          }
1328   
1329  !  The line has failed to match.
1330  !  We continue the outer "for" loop, trying the next line in the grammar.
1331   
1332          if (etype>best_etype) best_etype=etype;
1333          if (etype~=ASKSCOPE_PE && etype>nextbest_etype) nextbest_etype=etype;
1334   
1335  !  ...unless the line was something like "take all" which failed because
1336  !  nothing matched the "all", in which case we stop and give an error now.
1337   
1338          if (take_all_rule == 2 && etype==NOTHING_PE) break;
1339     }
1340   
1341  !  The grammar is exhausted: every line has failed to match.
1342   
1343  !  **** (H) ****
1344   
1345    .GiveError;
1346          etype=best_etype;
1347   
1348  !  Errors are handled differently depending on who was talking.
1349   
1350  !  If the command was addressed to somebody else (eg, "dwarf, sfgh") then
1351  !  it is taken as conversation which the parser has no business in disallowing.
1352   
1353      if (actor~=player)
1354      {   if (usual_grammar_after>0)
1355          {   verb_wordnum = usual_grammar_after;
1356              jump AlmostReParse;
1357          }
1358          wn=verb_wordnum;
1359          special_word=NextWord();
1360          if (special_word==comma_word)
1361          {   special_word=NextWord();
1362              verb_wordnum++;
1363          }
1364          special_number=TryNumber(verb_wordnum);
1365          results-->0=##NotUnderstood;
1366          results-->1=2;
1367          results-->2=1; special_number1=special_word;
1368          results-->3=actor;
1369          consult_from = verb_wordnum; consult_words = num_words-consult_from+1;
1370          rtrue;
1371      }
1372   
1373  !  **** (I) ****
1374   
1375  !  If the player was the actor (eg, in "take dfghh") the error must be printed,
1376  !  and fresh input called for.  In three cases the oops word must be jiggled.
1377   
1378      if (ParserError(etype)~=0) jump ReType;
1379      pronoun_word = pronoun__word; pronoun_obj = pronoun__obj;
1380   
1381      if (etype==STUCK_PE)   { L__M(##Miscellany, 27); oops_from=1; }
1382      if (etype==UPTO_PE)    { L__M(##Miscellany, 28);
1383                               for (m=0:m<32:m++) pattern-->m = pattern2-->m;
1384                               pcount=pcount2; PrintCommand(0); print ".^";
1385                             }
1386      if (etype==NUMBER_PE)  L__M(##Miscellany, 29);
1387      if (etype==CANTSEE_PE) { L__M(##Miscellany, 30); oops_from=saved_oops; }
1388      if (etype==TOOLIT_PE)  L__M(##Miscellany, 31);
1389      if (etype==NOTHELD_PE) { L__M(##Miscellany, 32); oops_from=saved_oops; }
1390      if (etype==MULTI_PE)   L__M(##Miscellany, 33);
1391      if (etype==MMULTI_PE)  L__M(##Miscellany, 34);
1392      if (etype==VAGUE_PE)   L__M(##Miscellany, 35);
1393      if (etype==EXCEPT_PE)  L__M(##Miscellany, 36);
1394      if (etype==ANIMA_PE)   L__M(##Miscellany, 37);
1395      if (etype==VERB_PE)    L__M(##Miscellany, 38);
1396      if (etype==SCENERY_PE) L__M(##Miscellany, 39);
1397      if (etype==ITGONE_PE)
1398      {   if (pronoun_obj == NULL) L__M(##Miscellany, 35);
1399                              else L__M(##Miscellany, 40);
1400      }
1401      if (etype==JUNKAFTER_PE) L__M(##Miscellany, 41);
1402      if (etype==TOOFEW_PE)  L__M(##Miscellany, 42, multi_had);
1403      if (etype==NOTHING_PE) { if (multi_wanted==100) L__M(##Miscellany, 43);
1404                               else L__M(##Miscellany, 44);  }
1405   
1406      if (etype==ASKSCOPE_PE)
1407      {   scope_stage=3;
1408          if (indirect(scope_error)==-1)
1409          {   best_etype=nextbest_etype; jump GiveError;  }
1410      }
1411   
1412  !  **** (J) ****
1413   
1414  !  And go (almost) right back to square one...
1415   
1416      jump ReType;
1417   
1418  !  ...being careful not to go all the way back, to avoid infinite repetition
1419  !  of a deferred command causing an error.
1420   
1421   
1422  !  **** (K) ****
1423   
1424  !  At this point, the return value is all prepared, and we are only looking
1425  !  to see if there is a "then" followed by subsequent instruction(s).
1426      
1427     .LookForMore;
1428   
1429     if (wn>num_words) rtrue;
1430   
1431     i=NextWord();
1432     if (i==THEN1__WD or THEN2__WD or THEN3__WD or comma_word)
1433     {   if (wn>num_words)
1434         {   held_back_mode = false; return; }
1435         i = WordAddress(verb_wordnum);
1436         j = WordAddress(wn);
1437         for (:i<j:i++) i->0 = ' ';
1438         i = NextWord();
1439         if (i==AGAIN1__WD or AGAIN2__WD or AGAIN3__WD)
1440         {   !   Delete the words "then again" from the again buffer,
1441             !   in which we have just realised that it must occur:
1442             !   prevents an infinite loop on "i. again"
1443   
1444             i = WordAddress(wn-2)-buffer;
1445             if (wn > num_words) j = 119; else j = WordAddress(wn)-buffer;
1446             for (:i<j:i++) buffer3->i = ' ';
1447         }
1448         Tokenise__(buffer,parse); held_back_mode = true; return;
1449     }
1450     best_etype=UPTO_PE; jump GiveError;
1451  ];
1452   
1453  [ ScopeCeiling person act;
1454    act = parent(person); if (act == 0) return person;
1455    if (person == player && location == thedark) return thedark;
1456    while (parent(act)~=0
1457           && (act has transparent || act has supporter
1458               || (act has container && act has open)))
1459        act = parent(act);
1460    return act;
1461  ];
1462   
1463  ! ----------------------------------------------------------------------------
1464  !  Descriptors()
1465  !
1466  !  Handles descriptive words like "my", "his", "another" and so on.
1467  !  Skips "the", and leaves wn pointing to the first misunderstood word.
1468  !
1469  !  Allowed to set up for a plural only if allow_p is set
1470  !
1471  !  Returns error number, or 0 if no error occurred
1472  ! ----------------------------------------------------------------------------
1473   
1474  Constant OTHER_BIT  =   1;     !  These will be used in Adjudicate()
1475  Constant MY_BIT     =   2;     !  to disambiguate choices
1476  Constant THAT_BIT   =   4;
1477  Constant PLURAL_BIT =   8;
1478  Constant LIT_BIT    =  16;
1479  Constant UNLIT_BIT  =  32;
1480   
1481  [ ResetDescriptors;
1482     indef_mode=0; indef_type=0; indef_wanted=0; indef_guess_p=0;
1483     indef_possambig = false;
1484     indef_owner = nothing;
1485     indef_cases = $$111111111111;
1486     indef_nspec_at = 0;
1487  ];
1488   
1489  [ Descriptors allow_multiple  o x flag cto type n;
1490   
1491     ResetDescriptors();
1492     if (wn > num_words) return 0;
1493   
1494     for (flag=true:flag:)
1495     {   o=NextWordStopped(); flag=false;
1496   
1497         for (x=1:x<=LanguageDescriptors-->0:x=x+4)
1498             if (o == LanguageDescriptors-->x)
1499             {   flag = true;
1500                 type = LanguageDescriptors-->(x+2);
1501                 if (type ~= DEFART_PK) indef_mode = true;
1502                 indef_possambig = true;
1503                 indef_cases = indef_cases & (LanguageDescriptors-->(x+1));
1504   
1505                 if (type == POSSESS_PK)
1506                 {   cto = LanguageDescriptors-->(x+3);
1507                     switch(cto)
1508                     {  0: indef_type = indef_type | MY_BIT;
1509                        1: indef_type = indef_type | THAT_BIT;
1510                        default: indef_owner = PronounValue(cto);
1511                          if (indef_owner == NULL) indef_owner = InformParser;
1512                     }
1513                 }
1514   
1515                 if (type == light)
1516                     indef_type = indef_type | LIT_BIT;
1517                 if (type == -light)
1518                     indef_type = indef_type | UNLIT_BIT;
1519             }
1520   
1521         if (o==OTHER1__WD or OTHER2__WD or OTHER3__WD)
1522                              { indef_mode=1; flag=1;
1523                                indef_type = indef_type | OTHER_BIT; }
1524         if (o==ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD)
1525                              { indef_mode=1; flag=1; indef_wanted=100;
1526                                if (take_all_rule == 1)
1527                                    take_all_rule = 2;
1528                                indef_type = indef_type | PLURAL_BIT; }
1529         if (allow_plurals && allow_multiple)
1530         {   n=TryNumber(wn-1);
1531             if (n==1)        { indef_mode=1; flag=1; }
1532             if (n>1)         { indef_guess_p=1;
1533                                indef_mode=1; flag=1; indef_wanted=n;
1534                                indef_nspec_at=wn-1;
1535                                indef_type = indef_type | PLURAL_BIT; }
1536         }
1537         if (flag==1
1538             && NextWordStopped() ~= OF1__WD or OF2__WD or OF3__WD or OF4__WD)
1539             wn--;  ! Skip 'of' after these
1540     }
1541     wn--;
1542     if ((indef_wanted > 0) && (~~allow_multiple)) return MULTI_PE;
1543     return 0;
1544  ];
1545   
1546  ! ----------------------------------------------------------------------------
1547  !  CreatureTest: Will this person do for a "creature" token?
1548  ! ----------------------------------------------------------------------------
1549   
1550  [ CreatureTest obj;
1551    if (obj has animate) rtrue;
1552    if (obj hasnt talkable) rfalse;
1553    if (action_to_be == ##Ask or ##Answer or ##Tell or ##AskFor) rtrue;
1554    rfalse;
1555  ];
1556   
1557  [ PrepositionChain wd index;
1558    if (line_tdata-->index == wd) return wd;
1559    if ((line_token-->index)->0 & $20 == 0) return -1;
1560    do
1561    {   if (line_tdata-->index == wd) return wd;
1562        index++;
1563    }
1564    until ((line_token-->index == ENDIT_TOKEN)
1565           || (((line_token-->index)->0 & $10) == 0));
1566    return -1;
1567  ];
1568   
1569  ! ----------------------------------------------------------------------------
1570  !  ParseToken(type, data):
1571  !      Parses the given token, from the current word number wn, with exactly
1572  !      the specification of a general parsing routine.
1573  !      (Except that for "topic" tokens and prepositions, you need to supply
1574  !      a position in a valid grammar line as third argument.)
1575  !
1576  !  Returns:
1577  !    GPR_REPARSE  for "reconstructed input, please re-parse from scratch"
1578  !    GPR_PREPOSITION  for "token accepted with no result"
1579  !    $ff00 + x    for "please parse ParseToken(ELEMENTARY_TT, x) instead"
1580  !    0            for "token accepted, result is the multiple object list"
1581  !    1            for "token accepted, result is the number in parsed_number"
1582  !    object num   for "token accepted with this object as result"
1583  !    -1           for "token rejected"
1584  !
1585  !  (A)            Analyse the token; handle all tokens not involving
1586  !                 object lists and break down others into elementary tokens
1587  !  (B)            Begin parsing an object list
1588  !  (C)            Parse descriptors (articles, pronouns, etc.) in the list
1589  !  (D)            Parse an object name
1590  !  (E)            Parse connectives ("and", "but", etc.) and go back to (C)
1591  !  (F)            Return the conclusion of parsing an object list
1592  ! ----------------------------------------------------------------------------
1593   
1594  [ ParseToken given_ttype given_tdata token_n x y;
1595    x = lookahead; lookahead = NOUN_TOKEN;
1596    y = ParseToken__(given_ttype,given_tdata,token_n);
1597    if (y == GPR_REPARSE) Tokenise__(buffer,parse);
1598    lookahead = x; return y;
1599  ];
1600   
1601  [ ParseToken__ given_ttype given_tdata token_n
1602               token l o i j k and_parity single_object desc_wn many_flag
1603               token_allows_multiple;
1604   
1605  !  **** (A) ****
1606   
1607     token_filter = 0;
1608   
1609     switch(given_ttype)
1610     {   ELEMENTARY_TT:
1611             switch(given_tdata)
1612             {   SPECIAL_TOKEN:
1613                     l=TryNumber(wn);
1614                     special_word=NextWord();
1615                     #ifdef DEBUG;
1616                     if (l~=-1000)
1617                         if (parser_trace>=3)
1618                             print "  [Read special as the number ", l, "]^";
1619                     #endif;
1620                     if (l==-1000)
1621                     {   #ifdef DEBUG;
1622                         if (parser_trace>=3)
1623                           print "  [Read special word at word number ", wn, "]^";
1624                         #endif;
1625                         l = special_word;
1626                     }
1627                     parsed_number = l; return GPR_NUMBER;
1628   
1629                 NUMBER_TOKEN:
1630                     l=TryNumber(wn++);
1631                     if (l==-1000) { etype=NUMBER_PE; return GPR_FAIL; }
1632                     #ifdef DEBUG;
1633                     if (parser_trace>=3) print "  [Read number as ", l, "]^";
1634                     #endif;
1635                     parsed_number = l; return GPR_NUMBER;
1636   
1637                 CREATURE_TOKEN:
1638                     if (action_to_be==##Answer or ##Ask or ##AskFor or ##Tell)
1639                         scope_reason = TALKING_REASON;
1640   
1641                 TOPIC_TOKEN:
1642                     consult_from = wn;
1643                     if ((line_ttype-->(token_n+1) ~= PREPOSITION_TT)
1644                         && (line_token-->(token_n+1) ~= ENDIT_TOKEN))
1645                         RunTimeError(13);
1646                     do o=NextWordStopped();
1647                     until (o==-1 || PrepositionChain(o, token_n+1) ~= -1);
1648                     wn--;
1649                     consult_words = wn-consult_from;
1650                     if (consult_words==0) return GPR_FAIL;
1651                     if (action_to_be==##Ask or ##Answer or ##Tell)
1652                     {   o=wn; wn=consult_from; parsed_number=NextWord();
1653                         #IFDEF EnglishNaturalLanguage;
1654                         if (parsed_number=='the' && consult_words>1)
1655                             parsed_number=NextWord();
1656                         #ENDIF;
1657                         wn=o; return 1;
1658                     }
1659                     return GPR_PREPOSITION;
1660             }
1661   
1662         PREPOSITION_TT:
1663             #Iffalse Grammar__Version==1;
1664  !  Is it an unnecessary alternative preposition, when a previous choice
1665  !  has already been matched?
1666             if ((token->0) & $10) return GPR_PREPOSITION;
1667             #Endif;
1668   
1669  !  If we've run out of the player's input, but still have parameters to
1670  !  specify, we go into "infer" mode, remembering where we are and the
1671  !  preposition we are inferring...
1672   
1673             if (wn > num_words)
1674             {   if (inferfrom==0 && parameters<params_wanted)
1675                 {   inferfrom = pcount; inferword = token;
1676                     pattern-->pcount = REPARSE_CODE + Dword__No(given_tdata);
1677                 }
1678   
1679  !  If we are not inferring, then the line is wrong...
1680   
1681                 if (inferfrom==0) return -1;
1682   
1683  !  If not, then the line is right but we mark in the preposition...
1684   
1685                 pattern-->pcount = REPARSE_CODE + Dword__No(given_tdata);
1686                 return GPR_PREPOSITION;
1687             }
1688   
1689             o = NextWord();
1690   
1691             pattern-->pcount = REPARSE_CODE + Dword__No(o);
1692   
1693  !  Whereas, if the player has typed something here, see if it is the
1694  !  required preposition... if it's wrong, the line must be wrong,
1695  !  but if it's right, the token is passed (jump to finish this token).
1696   
1697             if (o == given_tdata) return GPR_PREPOSITION;
1698             #Iffalse Grammar__Version==1;
1699             if (PrepositionChain(o, token_n) ~= -1)
1700                 return GPR_PREPOSITION;
1701             #Endif;
1702             return -1;
1703   
1704         GPR_TT:
1705             l=indirect(given_tdata);
1706             #ifdef DEBUG;
1707             if (parser_trace>=3)
1708                 print "  [Outside parsing routine returned ", l, "]^";
1709             #endif;
1710             return l;
1711   
1712         SCOPE_TT:
1713             scope_token = given_tdata;
1714             scope_stage = 1;
1715             l = indirect(scope_token);
1716             #ifdef DEBUG;
1717             if (parser_trace>=3)
1718                 print "  [Scope routine returned multiple-flag of ", l, "]^";
1719             #endif;
1720             if (l==1) given_tdata = MULTI_TOKEN; else given_tdata = NOUN_TOKEN;
1721   
1722         ATTR_FILTER_TT:
1723             token_filter = 1 + given_tdata;
1724             given_tdata = NOUN_TOKEN;
1725   
1726         ROUTINE_FILTER_TT:
1727             token_filter = given_tdata;
1728             given_tdata = NOUN_TOKEN;
1729     }
1730   
1731     token = given_tdata;
1732   
1733  !  **** (B) ****
1734   
1735  !  There are now three possible ways we can be here:
1736  !      parsing an elementary token other than "special" or "number";
1737  !      parsing a scope token;
1738  !      parsing a noun-filter token (either by routine or attribute).
1739  !
1740  !  In each case, token holds the type of elementary parse to
1741  !  perform in matching one or more objects, and
1742  !  token_filter is 0 (default), an attribute + 1 for an attribute filter
1743  !  or a routine address for a routine filter.
1744   
1745     token_allows_multiple = false;
1746     if (token == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN
1747                  or MULTIINSIDE_TOKEN) token_allows_multiple = true;
1748   
1749     many_flag = false; and_parity = true; dont_infer = false;
1750   
1751  !  **** (C) ****
1752  !  We expect to find a list of objects next in what the player's typed.
1753   
1754    .ObjectList;
1755   
1756     #ifdef DEBUG;
1757     if (parser_trace>=3) print "  [Object list from word ", wn, "]^";
1758     #endif;
1759   
1760  !  Take an advance look at the next word: if it's "it" or "them", and these
1761  !  are unset, set the appropriate error number and give up on the line
1762  !  (if not, these are still parsed in the usual way - it is not assumed
1763  !  that they still refer to something in scope)
1764   
1765      o=NextWord(); wn--;
1766   
1767      pronoun_word = NULL; pronoun_obj = NULL;
1768      l = PronounValue(o);
1769      if (l ~= 0)
1770      {   pronoun_word = o; pronoun_obj = l;
1771          if (l == NULL)
1772          {   !   Don't assume this is a use of an unset pronoun until the
1773              !   descriptors have been checked, because it might be an
1774              !   article (or some such) instead
1775   
1776              for (l=1:l<=LanguageDescriptors-->0:l=l+4)
1777                  if (o == LanguageDescriptors-->l) jump AssumeDescriptor;
1778              pronoun__word=pronoun_word; pronoun__obj=pronoun_obj;
1779              etype=VAGUE_PE; return GPR_FAIL;
1780          }
1781      }
1782   
1783      .AssumeDescriptor;
1784   
1785      if (o==ME1__WD or ME2__WD or ME3__WD)
1786      {   pronoun_word = o; pronoun_obj = player;
1787      }
1788   
1789      allow_plurals = true; desc_wn = wn;
1790   
1791      .TryAgain;
1792  !   First, we parse any descriptive words (like "the", "five" or "every"):
1793      l = Descriptors(token_allows_multiple);
1794      if (l~=0) { etype=l; return GPR_FAIL; }
1795   
1796      .TryAgain2;
1797   
1798  !  **** (D) ****
1799   
1800  !  This is an actual specified object, and is therefore where a typing error
1801  !  is most likely to occur, so we set:
1802   
1803      oops_from = wn;
1804   
1805  !  So, two cases.  Case 1: token not equal to "held" (so, no implicit takes)
1806  !  but we may well be dealing with multiple objects
1807   
1808  !  In either case below we use NounDomain, giving it the token number as
1809  !  context, and two places to look: among the actor's possessions, and in the
1810  !  present location.  (Note that the order depends on which is likeliest.)
1811   
1812      if (token ~= HELD_TOKEN)
1813      {   i=multiple_object-->0;
1814          #ifdef DEBUG;
1815          if (parser_trace>=3)
1816              print "  [Calling NounDomain on location and actor]^";
1817          #endif;
1818          l=NounDomain(actors_location, actor, token);
1819          if (l==REPARSE_CODE) return l;                  ! Reparse after Q&A
1820          if (l==0) {   if (indef_possambig)
1821                        {   ResetDescriptors(); wn = desc_wn; jump TryAgain2; }
1822                        etype=CantSee(); jump FailToken; } ! Choose best error
1823   
1824          #ifdef DEBUG;
1825          if (parser_trace>=3)
1826          {   if (l>1)
1827                  print "  [ND returned ", (the) l, "]^";
1828              else
1829              {   print "  [ND appended to the multiple object list:^";
1830                  k=multiple_object-->0;
1831                  for (j=i+1:j<=k:j++)
1832                      print "  Entry ", j, ": ", (The) multiple_object-->j,
1833                            " (", multiple_object-->j, ")^";
1834                  print "  List now has size ", k, "]^";
1835              }
1836          }
1837          #endif;
1838   
1839          if (l==1)
1840          {   if (~~many_flag)
1841              {   many_flag = true;
1842              }
1843              else                                  ! Merge with earlier ones
1844              {   k=multiple_object-->0;            ! (with either parity)
1845                  multiple_object-->0 = i;
1846                  for (j=i+1:j<=k:j++)
1847                  {   if (and_parity) MultiAdd(multiple_object-->j);
1848                      else MultiSub(multiple_object-->j);
1849                  }
1850                  #ifdef DEBUG;
1851                  if (parser_trace>=3)
1852                      print "  [Merging ", k-i, " new objects to the ",
1853                          i, " old ones]^";
1854                  #endif;
1855              }
1856          }
1857          else
1858          {   ! A single object was indeed found
1859   
1860              if (match_length == 0 && indef_possambig)
1861              {   !   So the answer had to be inferred from no textual data,
1862                  !   and we know that there was an ambiguity in the descriptor
1863                  !   stage (such as a word which could be a pronoun being
1864                  !   parsed as an article or possessive).  It's worth having
1865                  !   another go.
1866   
1867                  ResetDescriptors(); wn = desc_wn; jump TryAgain2;
1868              }
1869          
1870              if (token==CREATURE_TOKEN && CreatureTest(l)==0)
1871              {   etype=ANIMA_PE; jump FailToken; } !  Animation is required
1872   
1873              if (~~many_flag)
1874                  single_object = l;
1875              else
1876              {   if (and_parity) MultiAdd(l); else MultiSub(l);
1877                  #ifdef DEBUG;
1878                  if (parser_trace>=3)
1879                      print "  [Combining ", (the) l, " with list]^";
1880                  #endif;
1881              }
1882          }
1883      }
1884   
1885  !  Case 2: token is "held" (which fortunately can't take multiple objects)
1886  !  and may generate an implicit take
1887   
1888      else
1889   
1890      {   l=NounDomain(actor,actors_location,token);       ! Same as above...
1891          if (l==REPARSE_CODE) return GPR_REPARSE;
1892          if (l==0)
1893          {   if (indef_possambig)
1894              {   ResetDescriptors(); wn = desc_wn; jump TryAgain2; }
1895              etype=CantSee(); return GPR_FAIL;            ! Choose best error
1896          }
1897   
1898  !  ...until it produces something not held by the actor.  Then an implicit
1899  !  take must be tried.  If this is already happening anyway, things are too
1900  !  confused and we have to give up (but saving the oops marker so as to get
1901  !  it on the right word afterwards).
1902  !  The point of this last rule is that a sequence like
1903  !
1904  !      > read newspaper
1905  !      (taking the newspaper first)
1906  !      The dwarf unexpectedly prevents you from taking the newspaper!
1907  !
1908  !  should not be allowed to go into an infinite repeat - read becomes
1909  !  take then read, but take has no effect, so read becomes take then read...
1910  !  Anyway for now all we do is record the number of the object to take.
1911   
1912          o=parent(l);
1913          if (o~=actor)
1914          {   if (notheld_mode==1)
1915              {   saved_oops=oops_from; etype=NOTHELD_PE; jump FailToken;
1916              }
1917              not_holding = l;
1918              #ifdef DEBUG;
1919              if (parser_trace>=3)
1920                  print "  [Allowing object ", (the) l, " for now]^";
1921              #endif;
1922          }
1923          single_object = l;
1924      }
1925   
1926  !  The following moves the word marker to just past the named object...
1927   
1928      wn = oops_from + match_length;
1929   
1930  !  **** (E) ****
1931   
1932  !  Object(s) specified now: is that the end of the list, or have we reached
1933  !  "and", "but" and so on?  If so, create a multiple-object list if we
1934  !  haven't already (and are allowed to).
1935   
1936      .NextInList;
1937   
1938      o=NextWord();
1939   
1940      if (o==AND1__WD or AND2__WD or AND3__WD or BUT1__WD or BUT2__WD or BUT3__WD
1941             or comma_word)
1942      {
1943          #ifdef DEBUG;
1944          if (parser_trace>=3) print "  [Read connective '", (address) o, "']^";
1945          #endif;
1946   
1947          if (~~token_allows_multiple)
1948          {   etype=MULTI_PE; jump FailToken;
1949          }
1950   
1951          if (o==BUT1__WD or BUT2__WD or BUT3__WD) and_parity = 1-and_parity;
1952   
1953          if (~~many_flag)
1954          {   multiple_object-->0 = 1;
1955              multiple_object-->1 = single_object;
1956              many_flag = true;
1957              #ifdef DEBUG;
1958              if (parser_trace>=3)
1959                  print "  [Making new list from ", (the) single_object, "]^";
1960              #endif;
1961          }
1962          dont_infer = true; inferfrom=0;           ! Don't print (inferences)
1963          jump ObjectList;                          ! And back around
1964      }
1965   
1966      wn--;   ! Word marker back to first not-understood word
1967   
1968  !  **** (F) ****
1969   
1970  !  Happy or unhappy endings:
1971   
1972      .PassToken;
1973   
1974      if (many_flag)
1975      {   single_object = GPR_MULTIPLE;
1976          multi_context = token;
1977      }
1978      else
1979      {   if (indef_mode==1 && indef_type & PLURAL_BIT ~= 0)
1980          {   if (indef_wanted<100 && indef_wanted>1)
1981              {   multi_had=1; multi_wanted=indef_wanted;
1982                  etype=TOOFEW_PE;
1983                  jump FailToken;
1984              }
1985          }
1986      }
1987      return single_object;
1988   
1989      .FailToken;
1990   
1991  !  If we were only guessing about it being a plural, try again but only
1992  !  allowing singulars (so that words like "six" are not swallowed up as
1993  !  Descriptors)
1994   
1995      if (allow_plurals && indef_guess_p==1)
1996      {   allow_plurals=false; wn=desc_wn; jump TryAgain;
1997      }
1998      return -1;
1999  ];
2000   
2001  ! ----------------------------------------------------------------------------
2002  !  NounDomain does the most substantial part of parsing an object name.
2003  !
2004  !  It is given two "domains" - usually a location and then the actor who is
2005  !  looking - and a context (i.e. token type), and returns:
2006  !
2007  !   0    if no match at all could be made,
2008  !   1    if a multiple object was made,
2009  !   k    if object k was the one decided upon,
2010  !   REPARSE_CODE if it asked a question of the player and consequently rewrote
2011  !        the player's input, so that the whole parser should start again
2012  !        on the rewritten input.
2013  !
2014  !   In the case when it returns 1
2015  !   length_of_noun to the number of words in the input text matched to the
2016  !   noun.
2017  !   In the case k=1, the multiple objects are added to multiple_object by
2018  !   hand (not by MultiAdd, because we want to allow duplicates).
2019  ! ----------------------------------------------------------------------------
2020   
2021  [ NounDomain domain1 domain2 context    first_word i j k l
2022                                          answer_words marker;
2023   
2024  #ifdef DEBUG;
2025    if (parser_trace>=4)
2026    {   print "   [NounDomain called at word ", wn, "^";
2027        print "   ";
2028        if (indef_mode)
2029        {   print "seeking indefinite object: ";
2030            if (indef_type & OTHER_BIT)  print "other ";
2031            if (indef_type & MY_BIT)     print "my ";
2032            if (indef_type & THAT_BIT)   print "that ";
2033            if (indef_type & PLURAL_BIT) print "plural ";
2034            if (indef_type & LIT_BIT)    print "lit ";
2035            if (indef_type & UNLIT_BIT)  print "unlit ";
2036            if (indef_owner ~= 0) print "owner:", (name) indef_owner;
2037            new_line;
2038            print "   number wanted: ";
2039            if (indef_wanted == 100) print "all"; else print indef_wanted;
2040            new_line;
2041            print "   most likely GNAs of names: ", indef_cases, "^";
2042        }
2043        else print "seeking definite object^";
2044    }
2045  #endif;
2046   
2047    match_length=0; number_matched=0; match_from=wn; placed_in_flag=0;
2048   
2049    SearchScope(domain1, domain2, context);
2050   
2051  #ifdef DEBUG;
2052    if (parser_trace>=4) print "   [ND made ", number_matched, " matches]^";
2053  #endif;
2054   
2055    wn=match_from+match_length;
2056   
2057  !  If nothing worked at all, leave with the word marker skipped past the
2058  !  first unmatched word...
2059   
2060    if (number_matched==0) { wn++; rfalse; }
2061   
2062  !  Suppose that there really were some words being parsed (i.e., we did
2063  !  not just infer).  If so, and if there was only one match, it must be
2064  !  right and we return it...
2065   
2066    if (match_from <= num_words)
2067    {   if (number_matched==1) { i=match_list-->0; return i; }
2068   
2069  !  ...now suppose that there was more typing to come, i.e. suppose that
2070  !  the user entered something beyond this noun.  If nothing ought to follow,
2071  !  then there must be a mistake, (unless what does follow is just a full
2072  !  stop, and or comma)
2073   
2074        if (wn<=num_words)
2075        {   i=NextWord(); wn--;
2076            if (i ~=  AND1__WD or AND2__WD or AND3__WD or comma_word
2077                   or THEN1__WD or THEN2__WD or THEN3__WD
2078                   or BUT1__WD or BUT2__WD or BUT3__WD)
2079            {   if (lookahead==ENDIT_TOKEN) rfalse;
2080            }
2081        }
2082    }
2083   
2084  !  Now look for a good choice, if there's more than one choice...
2085   
2086    number_of_classes=0;
2087    
2088    if (number_matched==1) i=match_list-->0;
2089    if (number_matched>1)
2090    {   i=Adjudicate(context);
2091        if (i==-1) rfalse;
2092        if (i==1) rtrue;       !  Adjudicate has made a multiple
2093                               !  object, and we pass it on
2094    }
2095   
2096  !  If i is non-zero here, one of two things is happening: either
2097  !  (a) an inference has been successfully made that object i is
2098  !      the intended one from the user's specification, or
2099  !  (b) the user finished typing some time ago, but we've decided
2100  !      on i because it's the only possible choice.
2101  !  In either case we have to keep the pattern up to date,
2102  !  note that an inference has been made and return.
2103  !  (Except, we don't note which of a pile of identical objects.)
2104   
2105    if (i~=0)
2106    {   if (dont_infer) return i;
2107        if (inferfrom==0) inferfrom=pcount;
2108        pattern-->pcount = i;
2109        return i;
2110    }
2111   
2112  !  If we get here, there was no obvious choice of object to make.  If in
2113  !  fact we've already gone past the end of the player's typing (which
2114  !  means the match list must contain every object in scope, regardless
2115  !  of its name), then it's foolish to give an enormous list to choose
2116  !  from - instead we go and ask a more suitable question...
2117   
2118    if (match_from > num_words) jump Incomplete;
2119   
2120  !  Now we print up the question, using the equivalence classes as worked
2121  !  out by Adjudicate() so as not to repeat ourselves on plural objects...
2122   
2123    if (context==CREATURE_TOKEN)
2124        L__M(##Miscellany, 45); else L__M(##Miscellany, 46);
2125   
2126    j=number_of_classes; marker=0;
2127    for (i=1:i<=number_of_classes:i++)
2128    {   
2129        while (((match_classes-->marker) ~= i)
2130               && ((match_classes-->marker) ~= -i)) marker++;
2131        k=match_list-->marker;
2132   
2133        if (match_classes-->marker > 0) print (the) k; else print (a) k;
2134   
2135        if (i<j-1)  print ", ";
2136        if (i==j-1) print (string) OR__TX;
2137    }
2138    print "?^";
2139   
2140  !  ...and get an answer:
2141   
2142    .WhichOne;
2143    for (i=2:i<120:i++) buffer2->i=' ';
2144    answer_words=Keyboard(buffer2, parse2);
2145   
2146    first_word=(parse2-->1);
2147   
2148  !  Take care of "all", because that does something too clever here to do
2149  !  later on:
2150   
2151    if (first_word == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD)
2152    {   
2153        if (context == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN
2154                       or MULTIINSIDE_TOKEN)
2155        {   l=multiple_object-->0;
2156            for (i=0:i<number_matched && l+i<63:i++)
2157            {   k=match_list-->i;
2158                multiple_object-->(i+1+l) = k;
2159            }
2160            multiple_object-->0 = i+l;
2161            rtrue;
2162        }
2163        L__M(##Miscellany, 47);
2164        jump WhichOne;
2165    }
2166   
2167  !  If the first word of the reply can be interpreted as a verb, then
2168  !  assume that the player has ignored the question and given a new
2169  !  command altogether.
2170  !  (This is one time when it's convenient that the directions are
2171  !  not themselves verbs - thus, "north" as a reply to "Which, the north
2172  !  or south door" is not treated as a fresh command but as an answer.)
2173   
2174    #ifdef LanguageIsVerb;
2175    if (first_word==0)
2176    {   j = wn; first_word=LanguageIsVerb(buffer2, parse2, 1); wn = j;
2177    }
2178    #endif;
2179    if (first_word ~= 0)
2180    {   j=first_word->#dict_par1;
2181        if ((0~=j&1) && (first_word ~= 'long' or 'short' or 'normal'
2182                                       or 'brief' or 'full' or 'verbose'))
2183        {   CopyBuffer(buffer, buffer2);
2184            return REPARSE_CODE;
2185        }
2186    }
2187   
2188  !  Now we insert the answer into the original typed command, as
2189  !  words additionally describing the same object
2190  !  (eg, > take red button
2191  !       Which one, ...
2192  !       > music
2193  !  becomes "take music red button".  The parser will thus have three
2194  !  words to work from next time, not two.)
2195   
2196    k = WordAddress(match_from) - buffer; l=buffer2->1+1;
2197    for (j=buffer + buffer->0 - 1: j>= buffer+k+l: j--)
2198        j->0 = 0->(j-l);
2199    for (i=0:i<l:i++) buffer->(k+i) = buffer2->(2+i);
2200    buffer->(k+l-1) = ' ';
2201    buffer->1 = buffer->1 + l;
2202    if (buffer->1 >= (buffer->0 - 1)) buffer->1 = buffer->0;
2203   
2204  !  Having reconstructed the input, we warn the parser accordingly
2205  !  and get out.
2206   
2207    return REPARSE_CODE;
2208   
2209  !  Now we come to the question asked when the input has run out
2210  !  and can't easily be guessed (eg, the player typed "take" and there
2211  !  were plenty of things which might have been meant).
2212   
2213    .Incomplete;
2214   
2215    if (context==CREATURE_TOKEN)
2216        L__M(##Miscellany, 48); else L__M(##Miscellany, 49);
2217   
2218    for (i=2:i<120:i++) buffer2->i=' ';
2219    answer_words=Keyboard(buffer2, parse2);
2220   
2221    first_word=(parse2-->1);
2222    #ifdef LanguageIsVerb;
2223    if (first_word==0)
2224    {   j = wn; first_word=LanguageIsVerb(buffer2, parse2, 1); wn = j;
2225    }
2226    #endif;
2227   
2228  !  Once again, if the reply looks like a command, give it to the
2229  !  parser to get on with and forget about the question...
2230   
2231    if (first_word ~= 0)
2232    {   j=first_word->#dict_par1;
2233        if (0~=j&1)
2234        {   CopyBuffer(buffer, buffer2);
2235            return REPARSE_CODE;
2236        }
2237    }
2238   
2239  !  ...but if we have a genuine answer, then:
2240  !
2241  !  (1) we must glue in text suitable for anything that's been inferred.
2242   
2243    if (inferfrom ~= 0)
2244    {   for (j = inferfrom: j<pcount: j++)
2245        {   if (pattern-->j == PATTERN_NULL) continue;
2246            i=2+buffer->1; (buffer->1)++; buffer->(i++) = ' ';
2247      
2248            if (parser_trace >= 5)
2249            print "[Gluing in inference with pattern code ", pattern-->j, "]^";
2250   
2251            parse2-->1 = 0;
2252   
2253            ! An inferred object.  Best we can do is glue in a pronoun.
2254            ! (This is imperfect, but it's very seldom needed anyway.)
2255      
2256            if (pattern-->j >= 2 && pattern-->j < REPARSE_CODE)
2257            {   PronounNotice(pattern-->j);
2258                for (k=1: k<=LanguagePronouns-->0: k=k+3)
2259                    if (pattern-->j == LanguagePronouns-->(k+2))
2260                    {   parse2-->1 = LanguagePronouns-->k;
2261                        if (parser_trace >= 5)
2262                        print "[Using pronoun '", (address) parse2-->1, "']^";
2263                        break;
2264                    }
2265            }
2266            else
2267            {   ! An inferred preposition.
2268                parse2-->1 = No__Dword(pattern-->j - REPARSE_CODE);
2269                if (parser_trace >= 5)
2270                    print "[Using preposition '", (address) parse2-->1, "']^";
2271            }
2272      
2273            ! parse2-->1 now holds the dictionary address of the word to glue in.
2274      
2275            if (parse2-->1 ~= 0)
2276            {   k = buffer + i;
2277                @output_stream 3 k;
2278                print (address) parse2-->1;
2279                @output_stream -3;
2280                k = k-->0;
2281                for (l=i:l<i+k:l++) buffer->l = buffer->(l+2);
2282                i = i + k; buffer->1 = i-2;
2283            }
2284        }
2285    }
2286   
2287  !  (2) we must glue the newly-typed text onto the end.
2288   
2289    i=2+buffer->1; (buffer->1)++; buffer->(i++) = ' ';
2290    for (j=0: j<buffer2->1: i++, j++)
2291    {   buffer->i = buffer2->(j+2);
2292        (buffer->1)++;
2293        if (buffer->1 == 120) break;
2294    }    
2295   
2296  !  (3) we fill up the buffer with spaces, which is unnecessary, but may
2297  !      help incorrectly-written interpreters to cope.
2298   
2299    for (:i<120:i++) buffer->i = ' ';
2300   
2301    return REPARSE_CODE;
2302  ];
2303   
2304  ! ----------------------------------------------------------------------------
2305  !  The Adjudicate routine tries to see if there is an obvious choice, when
2306  !  faced with a list of objects (the match_list) each of which matches the
2307  !  player's specification equally well.
2308  !
2309  !  To do this it makes use of the context (the token type being worked on).
2310  !  It counts up the number of obvious choices for the given context
2311  !  (all to do with where a candidate is, except for 6 (animate) which is to
2312  !  do with whether it is animate or not);
2313  !
2314  !  if only one obvious choice is found, that is returned;
2315  !
2316  !  if we are in indefinite mode (don't care which) one of the obvious choices
2317  !    is returned, or if there is no obvious choice then an unobvious one is
2318  !    made;
2319  !
2320  !  at this stage, we work out whether the objects are distinguishable from
2321  !    each other or not: if they are all indistinguishable from each other,
2322  !    then choose one, it doesn't matter which;
2323  !
2324  !  otherwise, 0 (meaning, unable to decide) is returned (but remember that
2325  !    the equivalence classes we've just worked out will be needed by other
2326  !    routines to clear up this mess, so we can't economise on working them
2327  !    out).
2328  !
2329  !  Returns -1 if an error occurred
2330  ! ----------------------------------------------------------------------------
2331  Constant SCORE__CHOOSEOBJ = 1000;
2332  Constant SCORE__IFGOOD = 500;
2333  Constant SCORE__UNCONCEALED = 100;
2334  Constant SCORE__BESTLOC = 60;
2335  Constant SCORE__NEXTBESTLOC = 40;
2336  Constant SCORE__NOTCOMPASS = 20;
2337  Constant SCORE__NOTSCENERY = 10;
2338  Constant SCORE__NOTACTOR = 5;
2339  Constant SCORE__GNA = 1;
2340  Constant SCORE__DIVISOR = 20;
2341   
2342  [ Adjudicate context i j k good_flag good_ones last n flag offset sovert;
2343   
2344  #ifdef DEBUG;
2345    if (parser_trace>=4)
2346    {   print "   [Adjudicating match list of size ", number_matched,
2347            " in context ", context, "^";
2348        print "   ";
2349        if (indef_mode)
2350        {   print "indefinite type: ";
2351            if (indef_type & OTHER_BIT)  print "other ";
2352            if (indef_type & MY_BIT)     print "my ";
2353            if (indef_type & THAT_BIT)   print "that ";
2354            if (indef_type & PLURAL_BIT) print "plural ";
2355            if (indef_type & LIT_BIT)    print "lit ";
2356            if (indef_type & UNLIT_BIT)  print "unlit ";
2357            if (indef_owner ~= 0) print "owner:", (name) indef_owner;
2358            new_line;
2359            print "   number wanted: ";
2360            if (indef_wanted == 100) print "all"; else print indef_wanted;
2361            new_line;
2362            print "   most likely GNAs of names: ", indef_cases, "^";
2363        }
2364        else print "definite object^";
2365    }
2366  #endif;
2367   
2368    j=number_matched-1; good_ones=0; last=match_list-->0;
2369    for (i=0:i<=j:i++)
2370    {   n=match_list-->i;
2371        match_scores-->i = 0;
2372   
2373        good_flag = false;
2374   
2375        switch(context) {
2376            HELD_TOKEN, MULTIHELD_TOKEN:
2377                if (parent(n)==actor) good_flag = true;
2378            MULTIEXCEPT_TOKEN:
2379                if (advance_warning == -1) {
2380                    good_flag = true;
2381                } else {
2382                    if (n ~= advance_warning) good_flag = true;
2383                }
2384            MULTIINSIDE_TOKEN:
2385                if (advance_warning == -1) {
2386                    if (parent(n) ~= actor) good_flag = true;
2387                } else {
2388                    if (n in advance_warning) good_flag = true;
2389                }
2390            CREATURE_TOKEN: if (CreatureTest(n)==1) good_flag = true;
2391            default: good_flag = true;
2392        }
2393   
2394        if (good_flag) {
2395            match_scores-->i = SCORE__IFGOOD;
2396            good_ones++; last = n;
2397        }
2398    }
2399    if (good_ones==1) return last;
2400   
2401    ! If there is ambiguity about what was typed, but it definitely wasn't
2402    ! animate as required, then return anything; higher up in the parser
2403    ! a suitable error will be given.  (This prevents a question being asked.)
2404    !
2405    if (context==CREATURE_TOKEN && good_ones==0) return match_list-->0;
2406   
2407    if (indef_mode==0) indef_type=0;
2408   
2409    ScoreMatchL(context);
2410    if (number_matched == 0) return -1;
2411   
2412    if (indef_mode == 0)
2413    {   !  Is there now a single highest-scoring object?
2414        i = SingleBestGuess();
2415        if (i >= 0)
2416        {   
2417  #ifdef DEBUG;
2418            if (parser_trace>=4)
2419                print "   Single best-scoring object returned.]^";
2420  #endif;
2421            return i;
2422        }
2423    }
2424   
2425    if (indef_mode==1 && indef_type & PLURAL_BIT ~= 0)
2426    {   if (context ~= MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN
2427                       or MULTIINSIDE_TOKEN)
2428        {   etype=MULTI_PE; return -1; }
2429        i=0; offset=multiple_object-->0; sovert = -1;
2430        for (j=BestGuess():j~=-1 && i<indef_wanted
2431             && i+offset<63:j=BestGuess())
2432        {   flag=0;
2433            if (j hasnt concealed && j hasnt worn) flag=1;
2434            if (sovert == -1) sovert = bestguess_score/SCORE__DIVISOR;
2435            else {
2436                if (indef_wanted == 100
2437                    && bestguess_score/SCORE__DIVISOR < sovert) flag=0;
2438            }
2439            if (context==MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN
2440                && parent(j)~=actor) flag=0;
2441            if (action_to_be == ##Take or ##Remove && parent(j)==actor) flag=0;
2442            k=ChooseObjects(j,flag);
2443            if (k==1) flag=1; else { if (k==2) flag=0; }
2444            if (flag==1)
2445            {   i++; multiple_object-->(i+offset) = j;
2446  #ifdef DEBUG;
2447                if (parser_trace>=4) print "   Accepting it^";
2448  #endif;
2449            }
2450            else
2451            {   i=i;
2452  #ifdef DEBUG;
2453                if (parser_trace>=4) print "   Rejecting it^";
2454  #endif;
2455            }
2456        }
2457        if (i<indef_wanted && indef_wanted<100)
2458        {   etype=TOOFEW_PE; multi_wanted=indef_wanted;
2459            multi_had=i;
2460            return -1;
2461        }
2462        multiple_object-->0 = i+offset;
2463        multi_context=context;
2464  #ifdef DEBUG;
2465        if (parser_trace>=4)
2466            print "   Made multiple object of size ", i, "]^";
2467  #endif;
2468        return 1;
2469    }
2470   
2471    for (i=0:i<number_matched:i++) match_classes-->i=0;
2472   
2473    n=1;
2474    for (i=0:i<number_matched:i++)
2475        if (match_classes-->i==0)
2476        {   match_classes-->i=n++; flag=0;
2477            for (j=i+1:j<number_matched:j++)
2478                if (match_classes-->j==0
2479                    && Identical(match_list-->i, match_list-->j)==1)
2480                {   flag=1;
2481                    match_classes-->j=match_classes-->i;
2482                }
2483            if (flag==1) match_classes-->i = 1-n;
2484        }
2485    n--; number_of_classes = n;
2486   
2487  #ifdef DEBUG;
2488    if (parser_trace>=4)
2489    {   print "   Grouped into ", n, " possibilities by name:^";
2490        for (i=0:i<number_matched:i++)
2491            if (match_classes-->i > 0)
2492                print "   ", (The) match_list-->i,
2493                    " (", match_list-->i, ")  ---  group ",
2494                    match_classes-->i, "^";
2495    }
2496  #endif;
2497   
2498    if (indef_mode == 0)
2499    {   if (n > 1)
2500        {   k = -1;
2501            for (i=0:i<number_matched:i++)
2502            {   if (match_scores-->i > k)
2503                {   k = match_scores-->i;
2504                    j = match_classes-->i; j=j*j;
2505                    flag = 0;
2506                }
2507                else
2508                if (match_scores-->i == k)
2509                {   if ((match_classes-->i) * (match_classes-->i) ~= j)
2510                        flag = 1;
2511                }
2512            }
2513            if (flag)
2514            {
2515  #ifdef DEBUG;
2516                if (parser_trace>=4)
2517                    print "   Unable to choose best group, so ask player.]^";
2518  #endif;
2519                return 0;
2520            }
2521  #ifdef DEBUG;
2522            if (parser_trace>=4)
2523                print "   Best choices are all from the same group.^";
2524  #endif;          
2525        }
2526    }
2527   
2528  !  When the player is really vague, or there's a single collection of
2529  !  indistinguishable objects to choose from, choose the one the player
2530  !  most recently acquired, or if the player has none of them, then
2531  !  the one most recently put where it is.
2532   
2533    if (n==1) dont_infer = true;
2534    return BestGuess();
2535  ];
2536   
2537  ! ----------------------------------------------------------------------------
2538  !  ReviseMulti  revises the multiple object which already exists, in the
2539  !    light of information which has come along since then (i.e., the second
2540  !    parameter).  It returns a parser error number, or else 0 if all is well.
2541  !    This only ever throws things out, never adds new ones.
2542  ! ----------------------------------------------------------------------------
2543   
2544  [ ReviseMulti second_p  i low;
2545   
2546  #ifdef DEBUG;
2547    if (parser_trace>=4)
2548        print "   Revising multiple object list of size ", multiple_object-->0,
2549              " with 2nd ", (name) second_p, "^";
2550  #endif;
2551   
2552    if (multi_context==MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN)
2553    {   for (i=1, low=0:i<=multiple_object-->0:i++)
2554        {   if ( (multi_context==MULTIEXCEPT_TOKEN
2555                  && multiple_object-->i ~= second_p)
2556                 || (multi_context==MULTIINSIDE_TOKEN
2557                     && multiple_object-->i in second_p))
2558            {   low++; multiple_object-->low = multiple_object-->i;
2559            }
2560        }
2561        multiple_object-->0 = low;
2562    }
2563   
2564    if (multi_context==MULTI_TOKEN && action_to_be == ##Take)
2565    {   for (i=1, low=0:i<=multiple_object-->0:i++)
2566            if (ScopeCeiling(multiple_object-->i)==ScopeCeiling(actor))
2567                low++;
2568  #ifdef DEBUG;
2569        if (parser_trace>=4)
2570            print "   Token 2 plural case: number with actor ", low, "^";
2571  #endif;
2572        if (take_all_rule==2 || low>0)
2573        {   for (i=1, low=0:i<=multiple_object-->0:i++)
2574            {   if (ScopeCeiling(multiple_object-->i)==ScopeCeiling(actor))
2575                {   low++; multiple_object-->low = multiple_object-->i;
2576                }
2577            }
2578            multiple_object-->0 = low;
2579        }
2580    }
2581   
2582    i=multiple_object-->0;
2583  #ifdef DEBUG;
2584    if (parser_trace>=4)
2585        print "   Done: new size ", i, "^";
2586  #endif;
2587    if (i==0) return NOTHING_PE;
2588    return 0;
2589  ];
2590   
2591  ! ----------------------------------------------------------------------------
2592  !  ScoreMatchL  scores the match list for quality in terms of what the
2593  !  player has vaguely asked for.  Points are awarded for conforming with
2594  !  requirements like "my", and so on.  Remove from the match list any
2595  !  entries which fail the basic requirements of the descriptors.
2596  ! ----------------------------------------------------------------------------
2597   
2598  [ ScoreMatchL context its_owner its_score obj i j threshold met a_s l_s;
2599   
2600  !  if (indef_type & OTHER_BIT ~= 0) threshold++;
2601    if (indef_type & MY_BIT ~= 0)    threshold++;
2602    if (indef_type & THAT_BIT ~= 0)  threshold++;
2603    if (indef_type & LIT_BIT ~= 0)   threshold++;
2604    if (indef_type & UNLIT_BIT ~= 0) threshold++;
2605    if (indef_owner ~= nothing)      threshold++;
2606   
2607  #ifdef DEBUG;
2608    if (parser_trace>=4) print "   Scoring match list: indef mode ", indef_mode,
2609        " type ", indef_type,
2610        ", satisfying ", threshold, " requirements:^";
2611  #endif;
2612   
2613    a_s = SCORE__NEXTBESTLOC; l_s = SCORE__BESTLOC;
2614    if (context == HELD_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN) {
2615        a_s = SCORE__BESTLOC; l_s = SCORE__NEXTBESTLOC;
2616    }
2617   
2618    for (i=0: i<number_matched: i++) {
2619        obj = match_list-->i; its_owner = parent(obj); its_score=0;
2620   
2621  !      if (indef_type & OTHER_BIT ~=0
2622  !          &&  obj~=itobj or himobj or herobj) met++;
2623        if (indef_type & MY_BIT ~=0  &&  its_owner==actor) met++;
2624        if (indef_type & THAT_BIT ~=0  &&  its_owner==actors_location) met++;
2625        if (indef_type & LIT_BIT ~=0  &&  obj has light) met++;
2626        if (indef_type & UNLIT_BIT ~=0  &&  obj hasnt light) met++;
2627        if (indef_owner~=0 && its_owner == indef_owner) met++;
2628   
2629        if (met < threshold)
2630        {
2631  #ifdef DEBUG;
2632            if (parser_trace >= 4)
2633                print "   ", (The) match_list-->i,
2634                      " (", match_list-->i, ") in ", (the) its_owner,
2635                      " is rejected (doesn't match descriptors)^";
2636  #endif;
2637            match_list-->i=-1;
2638        }
2639        else
2640        {   its_score = 0;
2641            if (obj hasnt concealed) its_score = SCORE__UNCONCEALED;
2642   
2643            if (its_owner==actor)   its_score = its_score + a_s;
2644            else
2645            if (its_owner==actors_location) its_score = its_score + l_s;
2646            else
2647            if (its_owner~=compass) its_score = its_score + SCORE__NOTCOMPASS;
2648   
2649            its_score = its_score + SCORE__CHOOSEOBJ * ChooseObjects(obj, 2);
2650   
2651            if (obj hasnt scenery) its_score = its_score + SCORE__NOTSCENERY;
2652            if (obj ~= actor) its_score = its_score + SCORE__NOTACTOR;
2653   
2654            !   A small bonus for having the correct GNA,
2655            !   for sorting out ambiguous articles and the like.
2656   
2657            if (indef_cases & (PowersOfTwo_TB-->(GetGNAOfObject(obj))))
2658                its_score = its_score + SCORE__GNA;
2659   
2660            match_scores-->i = match_scores-->i + its_score;
2661  #ifdef DEBUG;
2662            if (parser_trace >= 4)
2663                print "     ", (The) match_list-->i,
2664                      " (", match_list-->i, ") in ", (the) its_owner,
2665                      " : ", match_scores-->i, " points^";
2666  #endif;
2667        }
2668    }
2669   
2670    for (i=0:i<number_matched:i++)
2671    {   while (match_list-->i == -1)
2672        {   if (i == number_matched-1) { number_matched--; break; }
2673            for (j=i:j<number_matched:j++)
2674            {   match_list-->j = match_list-->(j+1);
2675                match_scores-->j = match_scores-->(j+1);              
2676            }
2677            number_matched--;
2678        }
2679    }
2680  ];
2681   
2682  ! ----------------------------------------------------------------------------
2683  !  BestGuess makes the best guess it can out of the match list, assuming that
2684  !  everything in the match list is textually as good as everything else;
2685  !  however it ignores items marked as -1, and so marks anything it chooses.
2686  !  It returns -1 if there are no possible choices.
2687  ! ----------------------------------------------------------------------------
2688   
2689  [ BestGuess  earliest its_score best i;
2690   
2691    earliest=0; best=-1;
2692    for (i=0:i<number_matched:i++)
2693    {   if (match_list-->i >= 0)
2694        {   its_score=match_scores-->i;
2695            if (its_score>best) { best=its_score; earliest=i; }
2696        }
2697    }
2698  #ifdef DEBUG;
2699    if (parser_trace>=4)
2700    {   if (best<0)
2701            print "   Best guess ran out of choices^";
2702        else
2703            print "   Best guess ", (the) match_list-->earliest,
2704                  " (", match_list-->earliest, ")^";
2705    }
2706  #endif;
2707    if (best<0) return -1;
2708    i=match_list-->earliest;
2709    match_list-->earliest=-1;
2710    bestguess_score = best;
2711    return i;
2712  ];
2713   
2714  ! ----------------------------------------------------------------------------
2715  !  SingleBestGuess returns the highest-scoring object in the match list
2716  !  if it is the clear winner, or returns -1 if there is no clear winner
2717  ! ----------------------------------------------------------------------------
2718   
2719  [ SingleBestGuess  earliest its_score best i;
2720   
2721    earliest=-1; best=-1000;
2722    for (i=0:i<number_matched:i++)
2723    {   its_score=match_scores-->i;
2724        if (its_score==best) { earliest = -1; }
2725        if (its_score>best) { best=its_score; earliest=match_list-->i; }
2726    }
2727    bestguess_score = best;
2728    return earliest;
2729  ];
2730   
2731  ! ----------------------------------------------------------------------------
2732  !  Identical decides whether or not two objects can be distinguished from
2733  !  each other by anything the player can type.  If not, it returns true.
2734  ! ----------------------------------------------------------------------------
2735   
2736  [ Identical o1 o2 p1 p2 n1 n2 i j flag;
2737   
2738    if (o1==o2) rtrue;  ! This should never happen, but to be on the safe side
2739    if (o1==0 || o2==0) rfalse;  ! Similarly
2740    if (parent(o1)==compass || parent(o2)==compass) rfalse; ! Saves time
2741   
2742  !  What complicates things is that o1 or o2 might have a parsing routine,
2743  !  so the parser can't know from here whether they are or aren't the same.
2744  !  If they have different parsing routines, we simply assume they're
2745  !  different.  If they have the same routine (which they probably got from
2746  !  a class definition) then the decision process is as follows:
2747  !
2748  !     the routine is called (with self being o1, not that it matters)
2749  !       with noun and second being set to o1 and o2, and action being set
2750  !       to the fake action TheSame.  If it returns -1, they are found
2751  !       identical; if -2, different; and if >=0, then the usual method
2752  !       is used instead.
2753   
2754    if (o1.parse_name~=0 || o2.parse_name~=0)
2755    {   if (o1.parse_name ~= o2.parse_name) rfalse;
2756        parser_action=##TheSame; parser_one=o1; parser_two=o2;
2757        j=wn; i=RunRoutines(o1,parse_name); wn=j;
2758        if (i==-1) rtrue; if (i==-2) rfalse;
2759    }
2760   
2761  !  This is the default algorithm: do they have the same words in their
2762  !  "name" (i.e. property no. 1) properties.  (Note that the following allows
2763  !  for repeated words and words in different orders.)
2764   
2765    p1 = o1.&1; n1 = (o1.#1)/2;
2766    p2 = o2.&1; n2 = (o2.#1)/2;
2767   
2768  !  for (i=0:ii, " "; } new_line;
2769  !  for (i=0:ii, " "; } new_line;
2770   
2771    for (i=0:i<n1:i++)
2772    {   flag=0;
2773        for (j=0:j<n2:j++)
2774            if (p1-->i == p2-->j) flag=1;
2775        if (flag==0) rfalse;
2776    }
2777   
2778    for (j=0:j<n2:j++)
2779    {   flag=0;
2780        for (i=0:i<n1:i++)
2781            if (p1-->i == p2-->j) flag=1;
2782        if (flag==0) rfalse;
2783    }
2784   
2785  !  print "Which are identical!^";
2786    rtrue;
2787  ];
2788   
2789  ! ----------------------------------------------------------------------------
2790  !  PrintCommand reconstructs the command as it presently reads, from
2791  !  the pattern which has been built up
2792  !
2793  !  If from is 0, it starts with the verb: then it goes through the pattern.
2794  !  The other parameter is "emptyf" - a flag: if 0, it goes up to pcount:
2795  !  if 1, it goes up to pcount-1.
2796  !
2797  !  Note that verbs and prepositions are printed out of the dictionary:
2798  !  and that since the dictionary may only preserve the first six characters
2799  !  of a word (in a V3 game), we have to hand-code the longer words needed.
2800  !
2801  !  (Recall that pattern entries are 0 for "multiple object", 1 for "special
2802  !  word", 2 to REPARSE_CODE-1 are object numbers and REPARSE_CODE+n means the
2803  !  preposition n)
2804  ! ----------------------------------------------------------------------------
2805   
2806  [ PrintCommand from i k spacing_flag;
2807   
2808    if (from==0)
2809    {   i=verb_word;
2810        if (LanguageVerb(i) == 0)
2811            if (PrintVerb(i) == 0)
2812                print (address) i;
2813        from++; spacing_flag = true;
2814    }
2815   
2816    for (k=from:k<pcount:k++)
2817    {   i=pattern-->k;
2818        if (i == PATTERN_NULL) continue;
2819        if (spacing_flag) print (char) ' ';
2820        if (i==0) { print (string) THOSET__TX; jump TokenPrinted; }
2821        if (i==1) { print (string) THAT__TX; jump TokenPrinted; }
2822        if (i>=REPARSE_CODE) print (address) No__Dword(i-REPARSE_CODE);
2823        else print (the) i;
2824        .TokenPrinted;
2825        spacing_flag = true;
2826    }
2827  ];
2828   
2829  ! ----------------------------------------------------------------------------
2830  !  The CantSee routine returns a good error number for the situation where
2831  !  the last word looked at didn't seem to refer to any object in context.
2832  !
2833  !  The idea is that: if the actor is in a location (but not inside something
2834  !  like, for instance, a tank which is in that location) then an attempt to
2835  !  refer to one of the words listed as meaningful-but-irrelevant there
2836  !  will cause "you don't need to refer to that in this game" rather than
2837  !  "no such thing" or "what's 'it'?".
2838  !  (The advantage of not having looked at "irrelevant" local nouns until now
2839  !  is that it stops them from clogging up the ambiguity-resolving process.
2840  !  Thus game objects always triumph over scenery.)
2841  ! ----------------------------------------------------------------------------
2842   
2843  [ CantSee  i w e;
2844      saved_oops=oops_from;
2845   
2846      if (scope_token~=0) { scope_error = scope_token; return ASKSCOPE_PE; }
2847   
2848      wn--; w=NextWord();
2849      e=CANTSEE_PE;
2850      if (w==pronoun_word)
2851      {   pronoun__word=pronoun_word; pronoun__obj=pronoun_obj;
2852          e=ITGONE_PE;
2853      }
2854      i=actor; while (parent(i) ~= 0) i = parent(i);
2855      if (i has visited && Refers(i,wn-1)==1) e=SCENERY_PE;
2856      if (etype>e) return etype;
2857      return e;
2858  ];
2859   
2860  ! ----------------------------------------------------------------------------
2861  !  The MultiAdd routine adds object "o" to the multiple-object-list.
2862  !
2863  !  This is only allowed to hold 63 objects at most, at which point it ignores
2864  !  any new entries (and sets a global flag so that a warning may later be
2865  !  printed if need be).
2866  ! ----------------------------------------------------------------------------
2867   
2868  [ MultiAdd o i j;
2869    i=multiple_object-->0;
2870    if (i==63) { toomany_flag=1; rtrue; }
2871    for (j=1:j<=i:j++)
2872        if (o==multiple_object-->j) 
2873            rtrue;
2874    i++;
2875    multiple_object-->i = o;
2876    multiple_object-->0 = i;
2877  ];
2878   
2879  ! ----------------------------------------------------------------------------
2880  !  The MultiSub routine deletes object "o" from the multiple-object-list.
2881  !
2882  !  It returns 0 if the object was there in the first place, and 9 (because
2883  !  this is the appropriate error number in Parser()) if it wasn't.
2884  ! ----------------------------------------------------------------------------
2885   
2886  [ MultiSub o i j k et;
2887    i=multiple_object-->0; et=0;
2888    for (j=1:j<=i:j++)
2889        if (o==multiple_object-->j)
2890        {   for (k=j:k<=i:k++)
2891                multiple_object-->k = multiple_object-->(k+1);
2892            multiple_object-->0 = --i;
2893            return et;
2894        }
2895    et=9; return et;
2896  ];
2897   
2898  ! ----------------------------------------------------------------------------
2899  !  The MultiFilter routine goes through the multiple-object-list and throws
2900  !  out anything without the given attribute "attr" set.
2901  ! ----------------------------------------------------------------------------
2902   
2903  [ MultiFilter attr  i j o;
2904    .MFiltl;
2905    i=multiple_object-->0;
2906    for (j=1:j<=i:j++)
2907    {   o=multiple_object-->j;
2908        if (o hasnt attr) { MultiSub(o); jump Mfiltl; }
2909    }
2910  ];
2911   
2912  ! ----------------------------------------------------------------------------
2913  !  The UserFilter routine consults the user's filter (or checks on attribute)
2914  !  to see what already-accepted nouns are acceptable
2915  ! ----------------------------------------------------------------------------
2916   
2917  [ UserFilter obj;
2918   
2919    if (token_filter > 0 && token_filter < 49)
2920    {   if (obj has (token_filter-1)) rtrue;
2921        rfalse;
2922    }
2923    noun = obj;
2924    return indirect(token_filter);
2925  ];
2926   
2927  ! ----------------------------------------------------------------------------
2928  !  MoveWord copies word at2 from parse buffer b2 to word at1 in "parse"
2929  !  (the main parse buffer)
2930  ! ----------------------------------------------------------------------------
2931   
2932  [ MoveWord at1 b2 at2 x y;
2933    x=at1*2-1; y=at2*2-1;
2934    parse-->x++ = b2-->y++;
2935    parse-->x = b2-->y;
2936  ];
2937   
2938  ! ----------------------------------------------------------------------------
2939  !  SearchScope  domain1 domain2 context
2940  !
2941  !  Works out what objects are in scope (possibly asking an outside routine),
2942  !  but does not look at anything the player has typed.
2943  ! ----------------------------------------------------------------------------
2944   
2945  [ SearchScope domain1 domain2 context i;
2946   
2947    i=0;
2948  !  Everything is in scope to the debugging commands
2949   
2950  #ifdef DEBUG;
2951    if (scope_reason==PARSING_REASON
2952        && verb_word == 'purloin' or 'tree' or 'abstract'
2953                         or 'gonear' or 'scope' or 'showobj')
2954    {   for (i=selfobj:i<=top_object:i++)
2955            if (i ofclass Object && (parent(i)==0 || parent(i) ofclass Object))
2956                PlaceInScope(i);
2957        rtrue;
2958    }
2959  #endif;
2960   
2961  !  First, a scope token gets priority here:
2962   
2963    if (scope_token ~= 0)
2964    {   scope_stage=2;
2965        if (indirect(scope_token)~=0) rtrue;
2966    }
2967   
2968  !  Next, call any user-supplied routine adding things to the scope,
2969  !  which may circumvent the usual routines altogether if they return true:
2970   
2971    if (actor==domain1 or domain2 && InScope(actor)~=0) rtrue;
2972   
2973  !  Pick up everything in the location except the actor's possessions;
2974  !  then go through those.  (This ensures the actor's possessions are in
2975  !  scope even in Darkness.)
2976   
2977    if (context==MULTIINSIDE_TOKEN && advance_warning ~= -1)
2978    {   if (IsSeeThrough(advance_warning)==1)
2979            ScopeWithin(advance_warning, 0, context);
2980    }
2981    else
2982    {   if (domain1~=0 && domain1 has supporter or container)
2983            ScopeWithin_O(domain1, domain1, context);
2984        ScopeWithin(domain1, domain2, context);
2985        if (domain2~=0 && domain2 has supporter or container)
2986            ScopeWithin_O(domain2, domain2, context);
2987        ScopeWithin(domain2, 0, context);
2988    }
2989   
2990  !  A special rule applies:
2991  !  in Darkness as in light, the actor is always in scope to himself.
2992   
2993    if (thedark == domain1 or domain2)
2994    {   ScopeWithin_O(actor, actor, context);
2995        if (parent(actor) has supporter or container)
2996            ScopeWithin_O(parent(actor), parent(actor), context);
2997    }
2998  ];
2999   
3000  ! ----------------------------------------------------------------------------
3001  !  IsSeeThrough is used at various places: roughly speaking, it determines
3002  !  whether o being in scope means that the contents of o are in scope.
3003  ! ----------------------------------------------------------------------------
3004   
3005  [ IsSeeThrough o;
3006    if (o has supporter
3007        || (o has transparent)
3008        || (o has container && o has open))
3009        rtrue;
3010    rfalse;
3011  ];
3012   
3013  ! ----------------------------------------------------------------------------
3014  !  PlaceInScope is provided for routines outside the library, and is not
3015  !  called within the parser (except for debugging purposes).
3016  ! ----------------------------------------------------------------------------
3017   
3018  [ PlaceInScope thing;
3019     if (scope_reason~=PARSING_REASON or TALKING_REASON)
3020     {   DoScopeAction(thing); rtrue; }
3021     wn=match_from; TryGivenObject(thing); placed_in_flag=1;
3022  ];
3023   
3024  ! ----------------------------------------------------------------------------
3025  !  DoScopeAction
3026  ! ----------------------------------------------------------------------------
3027   
3028  [ DoScopeAction thing s p1;
3029    s = scope_reason; p1=parser_one;
3030  #ifdef DEBUG;
3031    if (parser_trace>=6)
3032    {   print "[DSA on ", (the) thing, " with reason = ", scope_reason,
3033        " p1 = ", parser_one, " p2 = ", parser_two, "]^";
3034    }
3035  #endif;
3036    switch(scope_reason)
3037    {   REACT_BEFORE_REASON:
3038            if (thing.react_before==0 or NULL) return;
3039  #ifdef DEBUG;
3040            if (parser_trace>=2)
3041            {   print "[Considering react_before for ", (the) thing, "]^"; }
3042  #endif;
3043            if (parser_one==0) parser_one = RunRoutines(thing,react_before);
3044        REACT_AFTER_REASON:
3045            if (thing.react_after==0 or NULL) return;
3046  #ifdef DEBUG;
3047            if (parser_trace>=2)
3048            {   print "[Considering react_after for ", (the) thing, "]^"; }
3049  #endif;
3050            if (parser_one==0) parser_one = RunRoutines(thing,react_after);
3051        EACH_TURN_REASON:
3052            if (thing.each_turn == 0 or NULL) return;
3053  #ifdef DEBUG;
3054            if (parser_trace>=2)
3055            {   print "[Considering each_turn for ", (the) thing, "]^"; }
3056  #endif;
3057            PrintOrRun(thing, each_turn);
3058        TESTSCOPE_REASON:
3059            if (thing==parser_one) parser_two = 1;
3060        LOOPOVERSCOPE_REASON:
3061            indirect(parser_one,thing); parser_one=p1;
3062    }
3063    scope_reason = s;
3064  ];
3065   
3066  ! ----------------------------------------------------------------------------
3067  !  ScopeWithin looks for objects in the domain which make textual sense
3068  !  and puts them in the match list.  (However, it does not recurse through
3069  !  the second argument.)
3070  ! ----------------------------------------------------------------------------
3071   
3072  [ ScopeWithin domain nosearch context x y;
3073   
3074     if (domain==0) rtrue;
3075   
3076  !  Special rule: the directions (interpreted as the 12 walls of a room) are
3077  !  always in context.  (So, e.g., "examine north wall" is always legal.)
3078  !  (Unless we're parsing something like "all", because it would just slow
3079  !  things down then, or unless the context is "creature".)
3080   
3081     if (indef_mode==0 && domain==actors_location
3082         && scope_reason==PARSING_REASON && context~=CREATURE_TOKEN)
3083             ScopeWithin(compass);
3084   
3085  !  Look through the objects in the domain, avoiding "objectloop" in case
3086  !  movements occur, e.g. when trying each_turn.
3087   
3088     x = child(domain);
3089     while (x ~= 0)
3090     {   y = sibling(x);
3091         ScopeWithin_O(x, nosearch, context);
3092         x = y;
3093     }
3094  ];
3095   
3096  [ ScopeWithin_O domain nosearch context i ad n;
3097   
3098  !  multiexcept doesn't have second parameter in scope
3099     if (context==MULTIEXCEPT_TOKEN && domain==advance_warning) jump DontAccept;
3100   
3101  !  If the scope reason is unusual, don't parse.
3102   
3103        if (scope_reason~=PARSING_REASON or TALKING_REASON)
3104        {   DoScopeAction(domain); jump DontAccept; }
3105   
3106  !  "it" or "them" matches to the it-object only.  (Note that (1) this means
3107  !  that "it" will only be understood if the object in question is still
3108  !  in context, and (2) only one match can ever be made in this case.)
3109   
3110        if (match_from <= num_words)  ! If there's any text to match, that is
3111        {   wn=match_from;
3112            i=NounWord();
3113            if (i==1 && player==domain)  MakeMatch(domain, 1);
3114   
3115            if (i>=2 && i<128 && (LanguagePronouns-->i == domain))
3116                MakeMatch(domain, 1);
3117        }
3118   
3119  !  Construing the current word as the start of a noun, can it refer to the
3120  !  object?
3121   
3122        wn = match_from;
3123        if (TryGivenObject(domain) > 0)
3124            if (indef_nspec_at>0 && match_from~=indef_nspec_at)
3125            {   !  This case arises if the player has typed a number in
3126                !  which is hypothetically an indefinite descriptor:
3127                !  e.g. "take two clubs".  We have just checked the object
3128                !  against the word "clubs", in the hope of eventually finding
3129                !  two such objects.  But we also backtrack and check it
3130                !  against the words "two clubs", in case it turns out to
3131                !  be the 2 of Clubs from a pack of cards, say.  If it does
3132                !  match against "two clubs", we tear up our original
3133                !  assumption about the meaning of "two" and lapse back into
3134                !  definite mode.
3135            
3136                wn = indef_nspec_at;
3137                if (TryGivenObject(domain) > 0)
3138                {   match_from = indef_nspec_at;
3139                    ResetDescriptors();                  
3140                }
3141                wn = match_from;
3142            }
3143   
3144        .DontAccept;
3145   
3146  !  Shall we consider the possessions of the current object, as well?
3147  !  Only if it's a container (so, for instance, if a dwarf carries a
3148  !  sword, then "drop sword" will not be accepted, but "dwarf, drop sword"
3149  !  will).
3150  !  Also, only if there are such possessions.
3151  !
3152  !  Notice that the parser can see "into" anything flagged as
3153  !  transparent - such as a dwarf whose sword you can get at.
3154   
3155        if (child(domain)~=0 && domain ~= nosearch && IsSeeThrough(domain)==1)
3156            ScopeWithin(domain,nosearch,context);
3157   
3158  !  Drag any extras into context
3159   
3160     ad = domain.&add_to_scope;
3161     if (ad ~= 0)
3162     {   if (UnsignedCompare(ad-->0,top_object) > 0)
3163         {   ats_flag = 2+context;
3164             RunRoutines(domain, add_to_scope);
3165             ats_flag = 0;
3166         }
3167         else
3168         {   n=domain.#add_to_scope;
3169             for (i=0:(2*i)<n:i++)
3170                 ScopeWithin_O(ad-->i,0,context);
3171         }
3172     }
3173  ];
3174   
3175  [ AddToScope obj;
3176     if (ats_flag>=2)
3177         ScopeWithin_O(obj,0,ats_flag-2);
3178     if (ats_flag==1)
3179     {   if  (HasLightSource(obj)==1) ats_hls = 1;
3180     }
3181  ];
3182   
3183  ! ----------------------------------------------------------------------------
3184  !  MakeMatch looks at how good a match is.  If it's the best so far, then
3185  !  wipe out all the previous matches and start a new list with this one.
3186  !  If it's only as good as the best so far, add it to the list.
3187  !  If it's worse, ignore it altogether.
3188  !
3189  !  The idea is that "red panic button" is better than "red button" or "panic".
3190  !
3191  !  number_matched (the number of words matched) is set to the current level
3192  !  of quality.
3193  !
3194  !  We never match anything twice, and keep at most 64 equally good items.
3195  ! ----------------------------------------------------------------------------
3196   
3197  [ MakeMatch obj quality i;
3198  #ifdef DEBUG;
3199     if (parser_trace>=6) print "    Match with quality ",quality,"^";
3200  #endif;
3201     if (token_filter~=0 && UserFilter(obj)==0)
3202     {   #ifdef DEBUG;
3203         if (parser_trace>=6)
3204         {   print "    Match filtered out: token filter ", token_filter, "^";
3205         }
3206         #endif;
3207         rtrue;
3208     }
3209     if (quality < match_length) rtrue;
3210     if (quality > match_length) { match_length=quality; number_matched=0; }
3211     else
3212     {   if (2*number_matched>=MATCH_LIST_SIZE) rtrue;
3213         for (i=0:i<number_matched:i++)
3214             if (match_list-->i==obj) rtrue;
3215     }
3216     match_list-->number_matched++ = obj;
3217  #ifdef DEBUG;
3218     if (parser_trace>=6) print "    Match added to list^";
3219  #endif;
3220  ];
3221   
3222  ! ----------------------------------------------------------------------------
3223  !  TryGivenObject tries to match as many words as possible in what has been
3224  !  typed to the given object, obj.  If it manages any words matched at all,
3225  !  it calls MakeMatch to say so, then returns the number of words (or 1
3226  !  if it was a match because of inadequate input).
3227  ! ----------------------------------------------------------------------------
3228   
3229  [ TryGivenObject obj threshold k w j;
3230   
3231  #ifdef DEBUG;
3232     if (parser_trace>=5)
3233         print "    Trying ", (the) obj, " (", obj, ") at word ", wn, "^";
3234  #endif;
3235   
3236     dict_flags_of_noun = 0;
3237   
3238  !  If input has run out then always match, with only quality 0 (this saves
3239  !  time).
3240   
3241     if (wn > num_words)
3242     {   if (indef_mode ~= 0)
3243             dict_flags_of_noun = $$01110000;  ! Reject "plural" bit
3244         MakeMatch(obj,0);
3245         #ifdef DEBUG;
3246         if (parser_trace>=5)
3247         print "    Matched (0)^";
3248         #endif;
3249         return 1;
3250     }
3251   
3252  !  Ask the object to parse itself if necessary, sitting up and taking notice
3253  !  if it says the plural was used:
3254   
3255     if (obj.parse_name~=0)
3256     {   parser_action = NULL; j=wn;
3257         k=RunRoutines(obj,parse_name);
3258         if (k>0)
3259         {   wn=j+k;
3260             .MMbyPN;
3261   
3262             if (parser_action == ##PluralFound)
3263                 dict_flags_of_noun = dict_flags_of_noun | 4;
3264   
3265             if (dict_flags_of_noun & 4)
3266             {   if (~~allow_plurals) k=0;
3267                 else
3268                 {   if (indef_mode==0)
3269                     {   indef_mode=1; indef_type=0; indef_wanted=0; }
3270                     indef_type = indef_type | PLURAL_BIT;
3271                     if (indef_wanted==0) indef_wanted=100;
3272                 }
3273             }
3274   
3275             #ifdef DEBUG;
3276                 if (parser_trace>=5)
3277                 {   print "    Matched (", k, ")^";
3278                 }
3279             #endif;
3280             MakeMatch(obj,k);
3281             return k;
3282         }
3283         if (k==0) jump NoWordsMatch;
3284     }
3285   
3286  !  The default algorithm is simply to count up how many words pass the
3287  !  Refers test:
3288   
3289     parser_action = NULL;
3290   
3291     w = NounWord();
3292   
3293     if (w==1 && player==obj) { k=1; jump MMbyPN; }
3294   
3295     if (w>=2 && w<128 && (LanguagePronouns-->w == obj))
3296     {   k=1; jump MMbyPN; }
3297   
3298     j=--wn;
3299     threshold = ParseNoun(obj);
3300  #ifdef DEBUG;
3301     if (threshold>=0 && parser_trace>=5)
3302         print "    ParseNoun returned ", threshold, "^";
3303  #endif;
3304     if (threshold<0) wn++;
3305     if (threshold>0) { k=threshold; jump MMbyPN; }
3306   
3307     if (threshold==0 || Refers(obj,wn-1)==0)
3308     {   .NoWordsMatch;
3309         if (indef_mode~=0)
3310         {   k=0; parser_action=NULL; jump MMbyPN;
3311         }
3312         rfalse;
3313     }
3314   
3315     if (threshold<0)
3316     {   threshold=1;
3317         dict_flags_of_noun = (w->#dict_par1) & $$01110100;
3318         w = NextWord();
3319         while (Refers(obj, wn-1))
3320         {   threshold++;
3321             if (w)
3322                 dict_flags_of_noun = dict_flags_of_noun
3323                                      | ((w->#dict_par1) & $$01110100);
3324             w = NextWord();
3325         }
3326     }
3327   
3328     k = threshold; jump MMbyPN;
3329  ];
3330   
3331  ! ----------------------------------------------------------------------------
3332  !  Refers works out whether the word at number wnum can refer to the object
3333  !  obj, returning true or false.  The standard method is to see if the
3334  !  word is listed under "name" for the object, but this is more complex
3335  !  in languages other than English.
3336  ! ----------------------------------------------------------------------------
3337   
3338  [ Refers obj wnum   wd k l m;
3339      if (obj==0) rfalse;
3340   
3341      #ifdef LanguageRefers;
3342      k = LanguageRefers(obj,wnum); if (k>=0) return k;
3343      #endif;
3344   
3345      k = wn; wn = wnum; wd = NextWordStopped(); wn = k;
3346   
3347      if (parser_inflection >= 256)
3348      {   k = indirect(parser_inflection, obj, wd);
3349          if (k>=0) return k;
3350          m = -k;
3351      } else m = parser_inflection;
3352      k=obj.&m; l=(obj.#m)/2-1;
3353      for (m=0:m<=l:m++)
3354          if (wd==k-->m) rtrue;
3355      rfalse;
3356  ];
3357   
3358  [ WordInProperty wd obj prop k l m;
3359      k=obj.&prop; l=(obj.#prop)/2-1;
3360      for (m=0:m<=l:m++)
3361          if (wd==k-->m) rtrue;
3362      rfalse;
3363  ];
3364   
3365  [ DictionaryLookup b l i;
3366    for (i=0:i<l:i++) buffer2->(2+i) = b->i;
3367    buffer2->1 = l;
3368    Tokenise__(buffer2,parse2);
3369    return parse2-->1;
3370  ];
3371   
3372  ! ----------------------------------------------------------------------------
3373  !  NounWord (which takes no arguments) returns:
3374  !
3375  !   0  if the next word is unrecognised or does not carry the "noun" bit in
3376  !      its dictionary entry,
3377  !   1  if a word meaning "me",
3378  !   the index in the pronoun table (plus 2) of the value field of a pronoun,
3379  !      if the word is a pronoun,
3380  !   the address in the dictionary if it is a recognised noun.
3381  !
3382  !  The "current word" marker moves on one.
3383  ! ----------------------------------------------------------------------------
3384   
3385  [ NounWord i j s;
3386     i=NextWord();
3387     if (i==0) rfalse;
3388     if (i==ME1__WD or ME2__WD or ME3__WD) return 1;
3389     s = LanguagePronouns-->0;
3390     for (j=1 : j<=s : j=j+3)
3391         if (i == LanguagePronouns-->j)
3392             return j+2;
3393     if ((i->#dict_par1)&128 == 0) rfalse;
3394     return i;
3395  ];
3396   
3397  ! ----------------------------------------------------------------------------
3398  !  NextWord (which takes no arguments) returns:
3399  !
3400  !  0            if the next word is unrecognised,
3401  !  comma_word   if a comma
3402  !  THEN1__WD    if a full stop
3403  !  or the dictionary address if it is recognised.
3404  !  The "current word" marker is moved on.
3405  !
3406  !  NextWordStopped does the same, but returns -1 when input has run out
3407  ! ----------------------------------------------------------------------------
3408   
3409  [ NextWord i j;
3410     if (wn > parse->1) { wn++; rfalse; }
3411     i=wn*2-1; wn++;
3412     j=parse-->i;
3413     if (j == ',//') j=comma_word;
3414     if (j == './/') j=THEN1__WD;
3415     return j;
3416  ];   
3417   
3418  [ NextWordStopped;
3419     if (wn > parse->1) { wn++; return -1; }
3420     return NextWord();
3421  ];
3422   
3423  [ WordAddress wordnum;
3424     return buffer + parse->(wordnum*4+1);
3425  ];
3426   
3427  [ WordLength wordnum;
3428     return parse->(wordnum*4);
3429  ];
3430   
3431  ! ----------------------------------------------------------------------------
3432  !  TryNumber is the only routine which really does any character-level
3433  !  parsing, since that's normally left to the Z-machine.
3434  !  It takes word number "wordnum" and tries to parse it as an (unsigned)
3435  !  decimal number, returning
3436  !
3437  !  -1000                if it is not a number
3438  !  the number           if it has between 1 and 4 digits
3439  !  10000                if it has 5 or more digits.
3440  !
3441  !  (The danger of allowing 5 digits is that Z-machine integers are only
3442  !  16 bits long, and anyway this isn't meant to be perfect.)
3443  !
3444  !  Using NumberWord, it also catches "one" up to "twenty".
3445  !
3446  !  Note that a game can provide a ParseNumber routine which takes priority,
3447  !  to enable parsing of odder numbers ("x45y12", say).
3448  ! ----------------------------------------------------------------------------
3449   
3450  [ TryNumber wordnum   i j c num len mul tot d digit;
3451   
3452     i=wn; wn=wordnum; j=NextWord(); wn=i;
3453     j=NumberWord(j); if (j>=1) return j;
3454   
3455     i=wordnum*4+1; j=parse->i; num=j+buffer; len=parse->(i-1);
3456   
3457     tot=ParseNumber(num, len);  if (tot~=0) return tot;
3458   
3459     if (len>=4) mul=1000;
3460     if (len==3) mul=100;
3461     if (len==2) mul=10;
3462     if (len==1) mul=1;
3463   
3464     tot=0; c=0; len=len-1;
3465   
3466     for (c=0:c<=len:c++)
3467     {   digit=num->c;
3468         if (digit=='0') { d=0; jump digok; }
3469         if (digit=='1') { d=1; jump digok; }
3470         if (digit=='2') { d=2; jump digok; }
3471         if (digit=='3') { d=3; jump digok; }
3472         if (digit=='4') { d=4; jump digok; }
3473         if (digit=='5') { d=5; jump digok; }
3474         if (digit=='6') { d=6; jump digok; }
3475         if (digit=='7') { d=7; jump digok; }
3476         if (digit=='8') { d=8; jump digok; }
3477         if (digit=='9') { d=9; jump digok; }
3478         return -1000;
3479       .digok;
3480         tot=tot+mul*d; mul=mul/10;
3481     }
3482     if (len>3) tot=10000;
3483     return tot;
3484  ];
3485   
3486  ! ----------------------------------------------------------------------------
3487  !  GetGender returns 0 if the given animate object is female, and 1 if male
3488  !  (not all games will want such a simple decision function!)
3489  ! ----------------------------------------------------------------------------
3490   
3491  [ GetGender person;
3492     if (person hasnt female) rtrue;
3493     rfalse;
3494  ];
3495   
3496  [ GetGNAOfObject obj case gender;
3497     if (obj hasnt animate) case = 6;
3498     if (obj has male) gender = male;
3499     if (obj has female) gender = female;
3500     if (obj has neuter) gender = neuter;
3501     if (gender == 0)
3502     {   if (case == 0) gender = LanguageAnimateGender;
3503         else gender = LanguageInanimateGender;
3504     }
3505     if (gender == female) case = case + 1;
3506     if (gender == neuter) case = case + 2;
3507     if (obj has pluralname) case = case + 3;
3508     return case;
3509  ];
3510   
3511  ! ----------------------------------------------------------------------------
3512  !  Converting between dictionary addresses and entry numbers
3513  ! ----------------------------------------------------------------------------
3514   
3515  [ Dword__No w; return (w-(0-->4 + 7))/9; ];
3516  [ No__Dword n; return 0-->4 + 7 + 9*n; ];
3517   
3518  ! ----------------------------------------------------------------------------
3519  !  For copying buffers
3520  ! ----------------------------------------------------------------------------
3521   
3522  [ CopyBuffer bto bfrom i size;
3523     size=bto->0;
3524     for (i=1:i<=size:i++) bto->i=bfrom->i;
3525  ];
3526   
3527  ! ----------------------------------------------------------------------------
3528  !  Provided for use by language definition files
3529  ! ----------------------------------------------------------------------------
3530   
3531  [ LTI_Insert i ch  b y;
3532   
3533    !   Protect us from strict mode, as this isn't an array in quite the
3534    !   sense it expects
3535        b = buffer;
3536   
3537    !   Insert character ch into buffer at point i.
3538   
3539    !   Being careful not to let the buffer possibly overflow:
3540   
3541        y = b->1;
3542        if (y > b->0) y = b->0;
3543   
3544    !   Move the subsequent text along one character:
3545   
3546        for (y=y+2: y>i : y--) b->y = b->(y-1);
3547        b->i = ch;
3548   
3549    !   And the text is now one character longer:
3550        if (b->1 < b->0) (b->1)++;
3551  ];
3552   
3553  ! ============================================================================
3554   
3555  [ PronounsSub x y c d;
3556   
3557    L__M(##Pronouns, 1);
3558   
3559    c = (LanguagePronouns-->0)/3;
3560    if (player ~= selfobj) c++;
3561   
3562    if (c==0) return L__M(##Pronouns, 4);
3563   
3564    for (x = 1, d = 0 : x <= LanguagePronouns-->0: x = x+3)
3565    {   print "~", (address) LanguagePronouns-->x, "~ ";
3566        y = LanguagePronouns-->(x+2);
3567        if (y == NULL) L__M(##Pronouns, 3);
3568        else { L__M(##Pronouns, 2); print (the) y; }
3569        d++;
3570        if (d < c-1) print ", ";
3571        if (d == c-1) print (string) AND__TX;
3572    }
3573    if (player ~= selfobj)
3574    {   print "~", (address) ME1__WD, "~ "; L__M(##Pronouns, 2);
3575        c = player; player = selfobj;
3576        print (the) c; player = c;
3577    }
3578    ".";
3579  ];
3580   
3581  [ SetPronoun dword value x;
3582    for (x = 1 : x <= LanguagePronouns-->0: x = x+3)
3583        if (LanguagePronouns-->x == dword)
3584        {   LanguagePronouns-->(x+2) = value; return;
3585        }
3586    RunTimeError(14);
3587  ];
3588   
3589  [ PronounValue dword x;
3590    for (x = 1 : x <= LanguagePronouns-->0: x = x+3)
3591        if (LanguagePronouns-->x == dword)
3592            return LanguagePronouns-->(x+2);
3593    return 0;
3594  ];
3595   
3596  [ ResetVagueWords obj; PronounNotice(obj); ];
3597   
3598  #ifdef EnglishNaturalLanguage;
3599  [ PronounOldEnglish;
3600     if (itobj ~= old_itobj)   SetPronoun('it', itobj);
3601     if (himobj ~= old_himobj) SetPronoun('him', himobj);
3602     if (herobj ~= old_herobj) SetPronoun('her', herobj);
3603     old_itobj = itobj; old_himobj = himobj; old_herobj = herobj;
3604  ];
3605  #endif;
3606   
3607  [ PronounNotice obj x bm;
3608   
3609     if (obj == player) return;
3610   
3611     #ifdef EnglishNaturalLanguage;
3612     PronounOldEnglish();
3613     #endif;
3614   
3615     bm = PowersOfTwo_TB-->(GetGNAOfObject(obj));
3616   
3617     for (x = 1 : x <= LanguagePronouns-->0: x = x+3)
3618         if (bm & (LanguagePronouns-->(x+1)) ~= 0)
3619             LanguagePronouns-->(x+2) = obj;
3620   
3621     #ifdef EnglishNaturalLanguage;
3622     itobj  = PronounValue('it');  old_itobj  = itobj;
3623     himobj = PronounValue('him'); old_himobj = himobj;
3624     herobj = PronounValue('her'); old_herobj = herobj;
3625     #endif;
3626  ];
3627   
3628  ! ============================================================================
3629  !  End of the parser proper: the remaining routines are its front end.
3630  ! ----------------------------------------------------------------------------
3631   
3632  Object InformLibrary "(Inform Library)"
3633    with play
3634         [ i j k l;
3635         standard_interpreter = $32-->0;
3636         transcript_mode = ((0-->8) & 1);
3637         ChangeDefault(cant_go, CANTGO__TX);
3638   
3639         buffer->0 = 120;
3640         buffer2->0 = 120;
3641         buffer3->0 = 120;
3642         parse->0 = 64;
3643         parse2->0 = 64;
3644         
3645         real_location = thedark;
3646         player = selfobj; actor = player;
3647      
3648         top_object = #largest_object-255;
3649         selfobj.capacity = MAX_CARRIED;
3650         #ifdef LanguageInitialise;
3651         LanguageInitialise();
3652         #endif;
3653         new_line;
3654         j=Initialise();
3655         last_score = score;
3656         move player to location;
3657         while (parent(location)~=0) location=parent(location);
3658         real_location = location;
3659         objectloop (i in player) give i moved ~concealed;
3660      
3661         if (j~=2) Banner();
3662   
3663         MoveFloatingObjects();
3664         lightflag=OffersLight(parent(player));
3665         if (lightflag==0) { real_location=location; location=thedark; }
3666         <Look>;
3667      
3668         for (i=1:i<=100:i++) j=random(i);
3669   
3670         #ifdef EnglishNaturalLanguage;
3671         old_itobj = itobj; old_himobj = himobj; old_herobj = herobj;
3672         #endif;
3673      
3674         while (~~deadflag)
3675         {   
3676             #ifdef EnglishNaturalLanguage;
3677                 PronounOldEnglish();
3678                 old_itobj = PronounValue('it');
3679                 old_himobj = PronounValue('him');
3680                 old_herobj = PronounValue('her');
3681             #endif;
3682   
3683             .very__late__error;
3684   
3685             if (score ~= last_score)
3686             {   if (notify_mode==1) NotifyTheScore(); last_score=score; }
3687   
3688             .late__error;
3689   
3690             inputobjs-->0 = 0; inputobjs-->1 = 0;
3691             inputobjs-->2 = 0; inputobjs-->3 = 0; meta=false;
3692      
3693             !  The Parser writes its results into inputobjs and meta,
3694             !  a flag indicating a "meta-verb".  This can only be set for
3695             !  commands by the player, not for orders to others.
3696      
3697             InformParser.parse_input(inputobjs);
3698      
3699             action=inputobjs-->0;
3700   
3701             !  --------------------------------------------------------------
3702   
3703             !  Reverse "give fred biscuit" into "give biscuit to fred"
3704      
3705             if (action==##GiveR or ##ShowR)
3706             {   i=inputobjs-->2; inputobjs-->2=inputobjs-->3; inputobjs-->3=i;
3707                 if (action==##GiveR) action=##Give; else action=##Show;
3708             }
3709      
3710             !  Convert "P, tell me about X" to "ask P about X"
3711      
3712             if (action==##Tell && inputobjs-->2==player && actor~=player)
3713             {   inputobjs-->2=actor; actor=player; action=##Ask;
3714             }
3715      
3716             !  Convert "ask P for X" to "P, give X to me"
3717      
3718             if (action==##AskFor && inputobjs-->2~=player && actor==player)
3719             {   actor=inputobjs-->2; inputobjs-->2=inputobjs-->3;
3720                 inputobjs-->3=player; action=##Give;
3721             }
3722      
3723             !  For old, obsolete code: special_word contains the topic word
3724             !  in conversation
3725      
3726             if (action==##Ask or ##Tell or ##Answer)
3727                 special_word = special_number1;
3728   
3729             !  --------------------------------------------------------------
3730      
3731             multiflag=false; onotheld_mode=notheld_mode; notheld_mode=false;
3732             !  For implicit taking and multiple object detection
3733      
3734            .begin__action;
3735             inp1 = 0; inp2 = 0; i=inputobjs-->1;
3736             if (i>=1) inp1=inputobjs-->2;
3737             if (i>=2) inp2=inputobjs-->3;
3738      
3739             !  inp1 and inp2 hold: object numbers, or 0 for "multiple object",
3740             !  or 1 for "a number or dictionary address"
3741      
3742             if (inp1 == 1) noun = special_number1; else noun = inp1;
3743             if (inp2 == 1)
3744             {   if (inp1 == 1) second = special_number2;
3745                 else second = special_number1;
3746             } else second = inp2;
3747   
3748             !  --------------------------------------------------------------
3749      
3750             if (actor~=player)
3751             {   
3752             !  The player's "orders" property can refuse to allow conversation
3753             !  here, by returning true.  If not, the order is sent to the
3754             !  other person's "orders" property.  If that also returns false,
3755             !  then: if it was a misunderstood command anyway, it is converted
3756             !  to an Answer action (thus "floyd, grrr" ends up as
3757             !  "say grrr to floyd").  If it was a good command, it is finally
3758             !  offered to the Order: part of the other person's "life"
3759             !  property, the old-fashioned way of dealing with conversation.
3760      
3761                 j=RunRoutines(player,orders);
3762                 if (j==0)
3763                 {   j=RunRoutines(actor,orders);
3764                     if (j==0)
3765                     {   if (action==##NotUnderstood)
3766                         {   inputobjs-->3=actor; actor=player; action=##Answer;
3767                             jump begin__action;
3768                         }
3769                         if (RunLife(actor,##Order)==0) L__M(##Order,1,actor);
3770                     }
3771                 }
3772                 jump turn__end;
3773             }
3774   
3775             !  --------------------------------------------------------------
3776             !  Generate the action...
3777   
3778             if ((i==0)
3779                 || (i==1 && inp1 ~= 0)
3780                 || (i==2 && inp1 ~= 0 && inp2 ~= 0))
3781             {   self.begin_action(action, noun, second, 0);
3782                 jump turn__end;
3783             }
3784   
3785             !  ...unless a multiple object must be substituted.  First:
3786             !  (a) check the multiple list isn't empty;
3787             !  (b) warn the player if it has been cut short because too long;
3788             !  (c) generate a sequence of actions from the list
3789             !      (stopping in the event of death or movement away).
3790   
3791             multiflag = true;
3792             j=multiple_object-->0;
3793             if (j==0) { L__M(##Miscellany,2); jump late__error; }
3794             if (toomany_flag)
3795             {   toomany_flag = false; L__M(##Miscellany,1); }
3796             i=location;
3797             for (k=1:k<=j:k++)
3798             {   if (deadflag) break;
3799                 if (location ~= i)
3800                 {   L__M(##Miscellany, 51);
3801                     break;
3802                 }
3803                 l = multiple_object-->k;
3804                 PronounNotice(l);
3805                 print (name) l, ": ";
3806                 if (inp1 == 0)
3807                 {   inp1 = l; self.begin_action(action, l, second, 0); inp1 = 0;
3808                 }
3809                 else
3810                 {   inp2 = l; self.begin_action(action, noun, l, 0); inp2 = 0;
3811                 }
3812             }
3813   
3814             !  --------------------------------------------------------------
3815      
3816             .turn__end;
3817      
3818             !  No time passes if either (i) the verb was meta, or
3819             !  (ii) we've only had the implicit take before the "real"
3820             !  action to follow.
3821      
3822             if (notheld_mode==1) { NoteObjectAcquisitions(); continue; }
3823             if (meta) continue;
3824             if (~~deadflag) self.end_turn_sequence();
3825         }
3826   
3827             if (deadflag~=2) AfterLife();
3828             if (deadflag==0) jump very__late__error;
3829      
3830             print "^^    ";
3831             #IFV5; style bold; #ENDIF;
3832             print "***";
3833             if (deadflag==1) L__M(##Miscellany,3);
3834             if (deadflag==2) L__M(##Miscellany,4);
3835             if (deadflag>2)  { print " "; DeathMessage(); print " "; }
3836             print "***";
3837             #IFV5; style roman; #ENDIF;
3838             print "^^^";
3839             ScoreSub();
3840             DisplayStatus();
3841             AfterGameOver();
3842         ],
3843   
3844         end_turn_sequence
3845         [ i j;
3846   
3847             turns++;
3848             if (the_time~=NULL)
3849             {   if (time_rate>=0) the_time=the_time+time_rate;
3850                 else
3851                 {   time_step--;
3852                     if (time_step==0)
3853                     {   the_time++;
3854                         time_step = -time_rate;
3855                     }
3856                 }
3857                 the_time=the_time % 1440;
3858             }
3859   
3860             #IFDEF DEBUG;
3861             if (debug_flag & 4 ~= 0)
3862             {   for (i=0: i<active_timers: i++)
3863                 {   j=the_timers-->i;
3864                     if (j~=0)
3865                     {   print (name) (j&$7fff), ": ";
3866                         if (j & $8000) print "daemon";
3867                         else
3868                         {   print "timer with ",
3869                                   j.time_left, " turns to go"; }
3870                         new_line;
3871                     }
3872                 }
3873             }
3874             #ENDIF;
3875   
3876             for (i=0: i<active_timers: i++)
3877             {   if (deadflag) return;
3878                 j=the_timers-->i;
3879                 if (j~=0)
3880                 {   if (j & $8000) RunRoutines(j&$7fff,daemon);
3881                     else
3882                     {   if (j.time_left==0)
3883                         {   StopTimer(j);
3884                             RunRoutines(j,time_out);
3885                         }
3886                         else
3887                             j.time_left=j.time_left-1;
3888                     }
3889                 }
3890             }
3891             if (deadflag) return;
3892   
3893             scope_reason=EACH_TURN_REASON; verb_word=0;
3894             DoScopeAction(location);
3895             SearchScope(ScopeCeiling(player), player, 0);
3896             scope_reason=PARSING_REASON;
3897   
3898             if (deadflag) return;
3899   
3900             TimePasses();
3901   
3902             if (deadflag) return;
3903   
3904             AdjustLight();
3905   
3906             if (deadflag) return;
3907   
3908             NoteObjectAcquisitions();
3909         ],
3910   
3911         begin_action
3912         [ a n s source   sa sn ss;
3913             sa = action; sn = noun; ss = second;
3914             action = a; noun = n; second = s;
3915             #IFDEF DEBUG;
3916             if (debug_flag & 2 ~= 0) TraceAction(source);
3917             #IFNOT;
3918             source = 0;
3919             #ENDIF;
3920             #IFTRUE Grammar__Version == 1;
3921             if ((meta || BeforeRoutines()==false) && action<256)
3922                 ActionPrimitive();
3923             #IFNOT;
3924             if ((meta || BeforeRoutines()==false) && action<4096)
3925                 ActionPrimitive();
3926             #ENDIF;
3927             action = sa; noun = sn; second = ss;
3928         ],
3929    has  proper;
3930   
3931  [ ActionPrimitive;
3932    indirect(#actions_table-->action);
3933  ];
3934         
3935  [ AfterGameOver i;
3936     .RRQPL;
3937     L__M(##Miscellany,5);
3938     .RRQL;
3939     print "> ";
3940     #IFV3; read buffer parse; #ENDIF;
3941     temp_global=0;
3942     #IFV5; read buffer parse DrawStatusLine; #ENDIF;
3943     i=parse-->1;
3944     if (i==QUIT1__WD or QUIT2__WD) quit;
3945     if (i==RESTART__WD)      @restart;
3946     if (i==RESTORE__WD)      { RestoreSub(); jump RRQPL; }
3947     if (i==FULLSCORE1__WD or FULLSCORE2__WD && TASKS_PROVIDED==0)
3948     {   new_line; FullScoreSub(); jump RRQPL; }
3949     if (deadflag==2 && i==AMUSING__WD && AMUSING_PROVIDED==0)
3950     {   new_line; Amusing(); jump RRQPL; }
3951     #IFV5;
3952     if (i==UNDO1__WD or UNDO2__WD or UNDO3__WD)
3953     {   if (undo_flag==0)
3954         {   L__M(##Miscellany,6);
3955             jump RRQPL;
3956         }
3957         if (undo_flag==1) jump UndoFailed2;
3958         @restore_undo i;
3959         if (i==0)
3960         {   .UndoFailed2; L__M(##Miscellany,7);
3961         }
3962         jump RRQPL;
3963     }
3964     #ENDIF;
3965     L__M(##Miscellany,8);
3966     jump RRQL;
3967  ];
3968   
3969  [ R_Process a i j s1 s2;
3970     s1 = inp1; s2 = inp2;
3971     inp1 = i; inp2 = j; InformLibrary.begin_action(a, i, j, 1);
3972     inp1 = s1; inp2 = s2;
3973  ];
3974   
3975  [ NoteObjectAcquisitions i;
3976    objectloop (i in player && i hasnt moved)
3977    {   give i moved;
3978        if (i has scored)
3979        {   score = score + OBJECT_SCORE;
3980            things_score = things_score + OBJECT_SCORE;
3981        }
3982    }
3983  ];
3984   
3985  ! ----------------------------------------------------------------------------
3986   
3987  [ TestScope obj act a al sr x y;
3988    x=parser_one; y=parser_two;
3989    parser_one=obj; parser_two=0; a=actor; al=actors_location;
3990    sr=scope_reason; scope_reason=TESTSCOPE_REASON;
3991    if (act==0) actor=player; else actor=act;
3992    actors_location=ScopeCeiling(actor);
3993    SearchScope(actors_location,actor,0); scope_reason=sr; actor=a;
3994    actors_location=al; parser_one=x; x=parser_two; parser_two=y;
3995    return x;
3996  ];
3997   
3998  [ LoopOverScope routine act x y a al;
3999    x = parser_one; y=scope_reason; a=actor; al=actors_location;
4000    parser_one=routine; if (act==0) actor=player; else actor=act;
4001    actors_location=ScopeCeiling(actor);
4002    scope_reason=LOOPOVERSCOPE_REASON;
4003    SearchScope(actors_location,actor,0);
4004    parser_one=x; scope_reason=y; actor=a; actors_location=al;
4005  ];
4006   
4007  [ BeforeRoutines;
4008    if (GamePreRoutine()~=0) rtrue;
4009    if (RunRoutines(player,orders)~=0) rtrue;
4010    if (location~=0 && RunRoutines(location,before)~=0) rtrue;
4011    scope_reason=REACT_BEFORE_REASON; parser_one=0;
4012    SearchScope(ScopeCeiling(player),player,0); scope_reason=PARSING_REASON;
4013    if (parser_one~=0) rtrue;
4014    if (inp1>1 && RunRoutines(inp1,before)~=0) rtrue;
4015    rfalse;
4016  ];
4017   
4018  [ AfterRoutines;
4019    scope_reason=REACT_AFTER_REASON; parser_one=0;
4020    SearchScope(ScopeCeiling(player),player,0); scope_reason=PARSING_REASON;
4021    if (parser_one~=0) rtrue;
4022    if (location~=0 && RunRoutines(location,after)~=0) rtrue;
4023    if (inp1>1 && RunRoutines(inp1,after)~=0) rtrue;
4024    return GamePostRoutine();
4025  ];
4026   
4027  [ RunLife a j;
4028  #IFDEF DEBUG;
4029     if (debug_flag & 2 ~= 0) TraceAction(2, j);
4030  #ENDIF;
4031     reason_code = j; return RunRoutines(a,life);
4032  ];
4033   
4034  [ ZRegion addr;
4035    switch(metaclass(addr))       ! Left over from Inform 5
4036    {   nothing: return 0;
4037        Object, Class: return 1;
4038        Routine: return 2;
4039        String: return 3;
4040    }
4041  ];
4042   
4043  [ PrintOrRun obj prop flag;
4044    if (obj.#prop > 2) return RunRoutines(obj,prop);
4045    if (obj.prop==NULL) rfalse;
4046    switch(metaclass(obj.prop))
4047    {   Class, Object, nothing: return RunTimeError(2,obj,prop);
4048        String: print (string) obj.prop; if (flag==0) new_line; rtrue;
4049        Routine: return RunRoutines(obj,prop);
4050    }
4051  ];
4052   
4053  [ ValueOrRun obj prop;
4054    if (obj.prop < 256) return obj.prop;
4055    return RunRoutines(obj, prop);
4056  ];
4057   
4058  [ RunRoutines obj prop;
4059     if (obj == thedark
4060         && prop ~= initial or short_name or description) obj=real_location;
4061     if (obj.&prop == 0) rfalse;
4062     return obj.prop();
4063  ];
4064   
4065  [ ChangeDefault prop val a b;
4066     ! Use assembly-language here because -S compilation won't allow this:
4067     @loadw 0 5 -> a;
4068     b = prop-1;
4069     @storew a b val;
4070  ];
4071   
4072  ! ----------------------------------------------------------------------------
4073   
4074  [ StartTimer obj timer i;
4075     for (i=0:i<active_timers:i++)
4076         if (the_timers-->i==obj) rfalse;
4077     for (i=0:i<active_timers:i++)
4078         if (the_timers-->i==0) jump FoundTSlot;
4079     i=active_timers++;
4080     if (i >= MAX_TIMERS) { RunTimeError(4); return; }
4081     .FoundTSlot;
4082     if (obj.&time_left==0) { RunTimeError(5,obj); return; }
4083     the_timers-->i=obj; obj.time_left=timer;
4084  ];
4085   
4086  [ StopTimer obj i;
4087     for (i=0:i<active_timers:i++)
4088         if (the_timers-->i==obj) jump FoundTSlot2;
4089     rfalse;
4090     .FoundTSlot2;
4091     if (obj.&time_left==0) { RunTimeError(5,obj); return; }
4092     the_timers-->i=0; obj.time_left=0;
4093  ];
4094   
4095  [ StartDaemon obj i;
4096     for (i=0:i<active_timers:i++)
4097         if (the_timers-->i == $8000 + obj)
4098             rfalse;
4099     for (i=0:i<active_timers:i++)
4100         if (the_timers-->i==0) jump FoundTSlot3;
4101     i=active_timers++;
4102     if (i >= MAX_TIMERS) RunTimeError(4);
4103     .FoundTSlot3;
4104     the_timers-->i = $8000 + obj;
4105  ];
4106   
4107  [ StopDaemon obj i;
4108     for (i=0:i<active_timers:i++)
4109         if (the_timers-->i == $8000 + obj) jump FoundTSlot4;
4110     rfalse;
4111     .FoundTSlot4;
4112     the_timers-->i=0;
4113  ];
4114   
4115  ! ----------------------------------------------------------------------------
4116   
4117  [ DisplayStatus;
4118     if (the_time==NULL)
4119     {   sline1=score; sline2=turns; }
4120     else
4121     {   sline1=the_time/60; sline2=the_time%60; }
4122  ];
4123   
4124  [ SetTime t s;
4125     the_time=t; time_rate=s; time_step=0;
4126     if (s<0) time_step=0-s;
4127  ];
4128   
4129  [ NotifyTheScore;
4130     print "^[";  L__M(##Miscellany, 50, score-last_score);  print ".]^";
4131  ];
4132   
4133  ! ----------------------------------------------------------------------------
4134   
4135  [ AdjustLight flag i;
4136     i=lightflag;
4137     lightflag=OffersLight(parent(player));
4138   
4139     if (i==0 && lightflag==1)
4140     {   location=real_location; if (flag==0) <Look>;
4141     }
4142   
4143     if (i==1 && lightflag==0)
4144     {   real_location=location; location=thedark;
4145         if (flag==0) { NoteArrival();
4146                        return L__M(##Miscellany, 9); }
4147     }
4148     if (i==0 && lightflag==0) location=thedark;
4149  ];
4150   
4151  [ OffersLight i j;
4152     if (i==0) rfalse;
4153     if (i has light) rtrue;
4154     objectloop (j in i)
4155         if (HasLightSource(j)==1) rtrue;
4156     if (i has container)
4157     {   if (i has open || i has transparent)
4158             return OffersLight(parent(i));
4159     }
4160     else
4161     {   if (i has enterable || i has transparent || i has supporter)
4162             return OffersLight(parent(i));
4163     }
4164     rfalse;
4165  ];
4166   
4167  [ HidesLightSource obj;
4168      if (obj == player) rfalse;
4169      if (obj has transparent or supporter) rfalse;
4170      if (obj has container) return obj hasnt open;
4171      return obj hasnt enterable;
4172  ];
4173   
4174  [ HasLightSource i j ad;
4175     if (i==0) rfalse;
4176     if (i has light) rtrue;
4177     if (i has enterable || IsSeeThrough(i)==1)
4178         if (~~(HidesLightSource(i)))
4179             objectloop (j in i)
4180                 if (HasLightSource(j)==1) rtrue;
4181     ad = i.&add_to_scope;
4182     if (parent(i)~=0 && ad ~= 0)
4183     {   if (metaclass(ad-->0) == Routine)
4184         {   ats_hls = 0; ats_flag = 1;
4185             RunRoutines(i, add_to_scope);
4186             ats_flag = 0; if (ats_hls == 1) rtrue;
4187         }
4188         else
4189         {   for (j=0:(2*j)<i.#add_to_scope:j++)
4190                 if (HasLightSource(ad-->j)==1) rtrue;
4191         }
4192     }
4193     rfalse;
4194  ];
4195   
4196  [ ChangePlayer obj flag i;
4197  !  if (obj.&number==0) return RunTimeError(7,obj);
4198    if (actor==player) actor=obj;
4199    give player ~transparent ~concealed;
4200    i=obj; while(parent(i)~=0) { if (i has animate) give i transparent;
4201                                 i=parent(i); }
4202    if (player==selfobj) player.short_name=FORMER__TX;
4203   
4204    player=obj;
4205   
4206    if (player==selfobj) player.short_name=NULL;
4207    give player transparent concealed animate proper;
4208    i=player; while(parent(i)~=0) i=parent(i); location=i;
4209    real_location=location;
4210    MoveFloatingObjects();
4211    lightflag=OffersLight(parent(player));
4212    if (lightflag==0) location=thedark;
4213    print_player_flag=flag;
4214  ];
4215   
4216  ! ----------------------------------------------------------------------------
4217   
4218  #IFDEF DEBUG;
4219  [ DebugParameter w x n l;
4220    x=0-->4; x=x+(x->0)+1; l=x->0; n=(x+1)-->0; x=w-(x+3);
4221    print w;
4222    if (w>=1 && w<=top_object) print " (", (name) w, ")";
4223    if (x%l==0 && (x/l)<n) print " ('", (address) w, "')";
4224  ];
4225  [ DebugAction a anames;
4226  #iftrue Grammar__Version==1;
4227    if (a>=256) { print ", a-256, ">"; return; }
4228  #ifnot;
4229    if (a>=4096) { print ", a-4096, ">"; return; }
4230  #endif;
4231    anames = #identifiers_table;
4232    anames = anames + 2*(anames-->0) + 2*48;
4233    print (string) anames-->a;
4234  ];
4235  [ DebugAttribute a anames;
4236    if (a<0 || a>=48) print ", a, ">";
4237    else
4238    {   anames = #identifiers_table; anames = anames + 2*(anames-->0);
4239        print (string) anames-->a;
4240    }
4241  ];
4242  [ TraceAction source ar;
4243    if (source<2) print "[ Action ", (DebugAction) action;
4244    else
4245    {   if (ar==##Order)
4246            print "[ Order to ", (name) actor, ": ", (DebugAction) action;
4247        else
4248            print "[ Life rule ", (DebugAction) ar;
4249    }
4250    if (noun~=0)   print " with noun ", (DebugParameter) noun;
4251    if (second~=0) print " and second ", (DebugParameter) second;
4252    if (source==0) print " ";
4253    if (source==1) print " (from < > statement) ";
4254    print "]^";
4255  ];
4256  [ DebugToken token;
4257    AnalyseToken(token);
4258    switch(found_ttype)
4259    {   ILLEGAL_TT: print ", token, ">";
4260        ELEMENTARY_TT:
4261        switch(found_tdata)
4262        {   NOUN_TOKEN:        print "noun";
4263            HELD_TOKEN:        print "held";
4264            MULTI_TOKEN:       print "multi";
4265            MULTIHELD_TOKEN:   print "multiheld";
4266            MULTIEXCEPT_TOKEN: print "multiexcept";
4267            MULTIINSIDE_TOKEN: print "multiinside";
4268            CREATURE_TOKEN:    print "creature";
4269            SPECIAL_TOKEN:     print "special";
4270            NUMBER_TOKEN:      print "number";
4271            TOPIC_TOKEN:       print "topic";
4272            ENDIT_TOKEN:       print "END";
4273        }
4274        PREPOSITION_TT:
4275            print "'", (address) found_tdata, "'";
4276        ROUTINE_FILTER_TT:
4277        #ifdef INFIX; print "noun=", (InfixPrintPA) found_tdata;
4278        #ifnot; print "noun=Routine(", found_tdata, ")"; #endif;
4279        ATTR_FILTER_TT:
4280            print (DebugAttribute) found_tdata;
4281        SCOPE_TT:
4282        #ifdef INFIX; print "scope=", (InfixPrintPA) found_tdata;
4283        #ifnot; print "scope=Routine(", found_tdata, ")"; #endif;
4284        GPR_TT:
4285        #ifdef INFIX; print (InfixPrintPA) found_tdata;
4286        #ifnot; print "Routine(", found_tdata, ")"; #endif;
4287    }
4288  ];
4289  [ DebugGrammarLine pcount;
4290    print " * ";
4291    for (:line_token-->pcount ~= ENDIT_TOKEN:pcount++)
4292    {   if ((line_token-->pcount)->0 & $10) print "/ ";
4293        print (DebugToken) line_token-->pcount, " ";
4294    }
4295    print "-> ", (DebugAction) action_to_be;
4296    if (action_reversed) print " reverse";
4297  ];
4298  [ ShowVerbSub address lines da meta i j;
4299      if (((noun->#dict_par1) & 1) == 0)
4300        "Try typing ~showverb~ and then the name of a verb.";
4301      meta=((noun->#dict_par1) & 2)/2;
4302      i = $ff-(noun->#dict_par2);
4303      address = (0-->7)-->i;
4304      lines = address->0;
4305      address++;
4306      print "Verb ";
4307      if (meta) print "meta ";
4308      da = 0-->4;
4309      for (j=0:j < (da+5)-->0:j++)
4310          if (da->(j*9 + 14) == $ff-i)
4311              print "'", (address) (da + 9*j + 7), "' ";
4312      new_line;
4313      if (lines == 0) "has no grammar lines.";
4314      for (:lines > 0:lines--)
4315      {   address = UnpackGrammarLine(address);
4316          print "    "; DebugGrammarLine(); new_line;
4317      }
4318  ];
4319  [ ShowobjSub c f l a n x;
4320     if (noun==0) noun=location;
4321     objectloop (c ofclass Class) if (noun ofclass c) { f++; l=c; }
4322     if (f == 1) print (name) l, " ~"; else print "Object ~";
4323     print (name) noun, "~ (", noun, ")";
4324     if (parent(noun)~=0) print " in ~", (name) parent(noun), "~";
4325     new_line;
4326     if (f > 1)
4327     {   print "  class ";
4328         objectloop (c ofclass Class) if (noun ofclass c) print (name) c, " ";
4329         new_line;
4330     }
4331     for (a=0,f=0:a<48:a++) if (noun has a) f=1;
4332     if (f)
4333     {   print "  has ";
4334         for (a=0:a<48:a++) if (noun has a) print (DebugAttribute) a, " ";
4335         new_line;
4336     }
4337     if (noun ofclass Class) return;
4338   
4339     f=0; l = #identifiers_table-->0;
4340     for (a=1:a<=l:a++)
4341     {   if ((a~=2 or 3) && noun.&a)
4342         {   if (f==0) { print "  with "; f=1; }
4343             print (property) a;
4344             n = noun.#a;
4345             for (c=0:2*c<n:c++)
4346             {   print " ";
4347                 x = (noun.&a)-->c;
4348                 if (a==name) print "'", (address) x, "'";
4349                 else
4350                 {   if (a==number or capacity or time_left)
4351                         print x;
4352                     else
4353                     {   switch(x)
4354                         {   NULL: print "NULL";
4355                             0: print "0";
4356                             1: print "1";
4357                             default:
4358                             switch(metaclass(x))
4359                             {   Class, Object: print (name) x;
4360                                 String: print "~", (string) x, "~";
4361                                 Routine: print "[...]";
4362                             }
4363                             print " (", x, ")";
4364                         }
4365                     }
4366                 }
4367             }
4368             print ",^       ";
4369         }
4370     }
4371  !   if (f==1) new_line;
4372  ];
4373  #ENDIF;
4374   
4375  ! ----------------------------------------------------------------------------
4376  !  Except in Version 3, the DrawStatusLine routine does just that: this is
4377  !  provided explicitly so that it can be Replace'd to change the style, and
4378  !  as written it emulates the ordinary Standard game status line, which is
4379  !  drawn in hardware
4380  ! ----------------------------------------------------------------------------
4381   
4382  #IFV5;
4383  [ DrawStatusLine width posa posb;
4384     @split_window 1; @set_window 1; @set_cursor 1 1; style reverse;
4385     width = 0->33; posa = width-26; posb = width-13;
4386     spaces width;
4387     @set_cursor 1 2;
4388     if (location == thedark) print (name) location;
4389     else
4390     {   FindVisibilityLevels();
4391         if (visibility_ceiling == location)
4392             print (name) location;
4393         else print (The) visibility_ceiling;
4394     }
4395     if ((0->1)&2 == 0)
4396     {   if (width > 76)
4397         {   @set_cursor 1 posa; print (string) SCORE__TX, sline1;
4398             @set_cursor 1 posb; print (string) MOVES__TX, sline2;
4399         }
4400         if (width > 63 && width <= 76)
4401         {   @set_cursor 1 posb; print sline1, "/", sline2;
4402         }
4403     }
4404     else
4405     {   @set_cursor 1 posa;
4406         print (string) TIME__TX;
4407         LanguageTimeOfDay(sline1, sline2);
4408     }
4409     @set_cursor 1 1; style roman; @set_window 0;
4410  ];
4411  #ENDIF;
4412   
4413  #ifv5;
4414  Array StorageForShortName --> 161;
4415  #endif;
4416   
4417  [ PrefaceByArticle o acode pluralise  i artform findout;
4418   
4419     if (o provides articles)
4420     {   print (string) (o.&articles)-->(acode+short_name_case*LanguageCases),
4421             " ";
4422         if (pluralise) return;
4423         print (PSN__) o; return;
4424     }
4425   
4426     i = GetGNAOfObject(o);
4427     if (pluralise)
4428     {   if (i<3 || (i>=6 && i<9)) i = i + 3;
4429     }
4430     i = LanguageGNAsToArticles-->i;
4431   
4432     artform = LanguageArticles
4433               + 6*LanguageContractionForms*(short_name_case + i*LanguageCases);
4434   
4435  #iftrue LanguageContractionForms == 2;
4436     if (artform-->acode ~= artform-->(acode+3)) findout = true;
4437  #endif;
4438  #iftrue LanguageContractionForms == 3;
4439     if (artform-->acode ~= artform-->(acode+3)) findout = true;
4440     if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
4441  #endif;
4442  #iftrue LanguageContractionForms == 4;
4443     if (artform-->acode ~= artform-->(acode+3)) findout = true;
4444     if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
4445     if (artform-->(acode+6) ~= artform-->(acode+9)) findout = true;
4446  #endif;
4447  #iftrue LanguageContractionForms > 4;
4448     findout = true;
4449  #endif;
4450     if (standard_interpreter ~= 0 && findout)
4451     {   StorageForShortName-->0 = 160;
4452         @output_stream 3 StorageForShortName;
4453         if (pluralise) print (number) pluralise; else print (PSN__) o;
4454         @output_stream -3;
4455         acode = acode + 3*LanguageContraction(StorageForShortName + 2);
4456     }
4457   
4458     print (string) artform-->acode;
4459     if (pluralise) return;
4460     print (PSN__) o;
4461  ];
4462   
4463  [ PSN__ o;
4464     if (o==0) { print (string) NOTHING__TX; rtrue; }
4465     switch(metaclass(o))
4466     {   Routine: print ", o, ">"; rtrue;
4467         String:  print "<string ~", (string) o, "~>"; rtrue;
4468         nothing: print ", o, ">"; rtrue;
4469     }
4470     if (o==player) { print (string) YOURSELF__TX; rtrue; }
4471     #ifdef LanguagePrintShortName;
4472     if (LanguagePrintShortName(o)) rtrue;
4473     #endif;
4474     if (indef_mode && o.&short_name_indef~=0
4475         && PrintOrRun(o, short_name_indef, 1)~=0) rtrue;
4476     if (o.&short_name~=0 && PrintOrRun(o,short_name,1)~=0) rtrue;
4477     @print_obj o;
4478  ];
4479   
4480  [ Indefart o i;
4481     i = indef_mode; indef_mode = true;
4482     if (o has proper) { indef_mode = NULL; print (PSN__) o; return; }
4483     if (o provides article)
4484     {   PrintOrRun(o,article,1); print " ", (PSN__) o; indef_mode = i; return;
4485     }
4486     PrefaceByArticle(o, 2); indef_mode = i;
4487  ];
4488  [ Defart o i;
4489     i = indef_mode; indef_mode = false;
4490     if (o has proper)
4491     { indef_mode = NULL; print (PSN__) o; indef_mode = i; return; }
4492     PrefaceByArticle(o, 1); indef_mode = i;
4493  ];
4494  [ CDefart o i;
4495     i = indef_mode; indef_mode = false;
4496     if (o has proper)
4497     { indef_mode = NULL; print (PSN__) o; indef_mode = i; return; }
4498     PrefaceByArticle(o, 0); indef_mode = i;
4499  ];
4500   
4501  [ PrintShortName o i;
4502     i = indef_mode; indef_mode = NULL;
4503     PSN__(o); indef_mode = i;
4504  ];
4505   
4506  [ EnglishNumber n; LanguageNumber(n); ];
4507   
4508  [ NumberWord o i n;
4509    n = LanguageNumbers-->0;
4510    for (i=1:i<=n:i=i+2)
4511        if (o == LanguageNumbers-->i)
4512            return LanguageNumbers-->(i+1);
4513    return 0;
4514  ];
4515   
4516  [ RandomEntry tab;
4517    if (tab-->0==0) return RunTimeError(8);
4518    return tab-->(random(tab-->0));
4519  ];
4520   
4521  ! ----------------------------------------------------------------------------
4522  !  Useful routine: unsigned comparison (for addresses in Z-machine)
4523  !    Returns 1 if x>y, 0 if x=y, -1 if x
4524  ! ----------------------------------------------------------------------------
4525   
4526  [ UnsignedCompare x y u v;
4527    if (x==y) return 0;
4528    if (x<0 && y>=0) return 1;
4529    if (x>=0 && y<0) return -1;
4530    u = x&$7fff; v= y&$7fff;
4531    if (u>v) return 1;
4532    return -1;
4533  ];
4534   
4535  ! ----------------------------------------------------------------------------


Last updated 27 February 2004. This web site has not been fully supported since April 2008. Information may be out of date. This page was originally managed by Graham Nelson (graham@gnelson.demon.co.uk) assisted by C Knight.