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