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 site is no longer supported; information may be out of date.
Maintained as a historical archive by the Interactive Fiction Technology Foundation. Copyright 1993-2018 IFTF, CC-BY-SA unless otherwise noted.
This page was originally managed by Graham Nelson (graham@gnelson.demon.co.uk) assisted by C Knight.