Import v6.12.5 of the Inform 6 standard library
[informlib.git] / parser.h
1 ! ==============================================================================
2 !   PARSER:  Front end to parser.
3 !
4 !   Supplied for use with Inform 6 -- Release 6.12.5 -- Serial number 210605
5 !
6 !   Copyright Graham Nelson 1993-2004 and David Griffith 2012-2021
7 !
8 !   This file is free software: you can redistribute it and/or modify
9 !   it under the terms of the GNU Affero General Public License as
10 !   published by the Free Software Foundation, either version 3 of the
11 !   License, or (at your option) any later version.
12 !
13 !   This file is distributed in the hope that it will be useful, but
14 !   WITHOUT ANY WARRANTY; without even the implied warranty of
15 !   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 !   Affero General Public License for more details.
17 !
18 !   You should have received a copy of the GNU Affero General Public
19 !   License along with this program. If not, see
20 !   https://gnu.org/licenses/
21 !
22 !   In your game file, Include three library files in this order:
23 !       Include "parser";
24 !       Include "verblib";
25 !       Include "grammar";
26 !
27 ! ------------------------------------------------------------------------------
28 !   Inclusion of "linklpa" (which defines properties and attributes)
29 !   Global variables, constants and arrays
30 !       1: outside of the parser
31 !       2: used within the parser
32 !   Inclusion of natural language definition file
33 !       (which creates a compass and direction-objects)
34 !   Darkness and player objects
35 !   Definition of grammar token numbering system used by Inform
36 !
37 !   The InformParser object
38 !       keyboard reading
39 !       level 0: outer shell, conversation, errors
40 !             1: grammar lines
41 !             2: tokens
42 !             3: object lists
43 !             4: scope and ambiguity resolving
44 !             5: object comparisons
45 !             6: word comparisons
46 !             7: reading words and moving tables about
47 !       pronoun management
48 !
49 !   The InformLibrary object
50 !       main game loop
51 !       action processing
52 !       end of turn sequence
53 !       scope looping, before/after sequence, sending messages out
54 !       timers, daemons, time of day, score notification
55 !       light and darkness
56 !       changing player personality
57 !       tracing code (only present if DEBUG is set)
58 !
59 !   Status line printing, menu display
60 !   Printing object names with articles
61 !   Miscellaneous utility routines
62 !   Game banner, "version" verb, run-time errors
63 ! ==============================================================================
64
65 System_file;
66
67 #Ifndef LIBRARY_STAGE;  ! This file is the first one to define LIBRARY_STAGE.
68                 ! "This file already included" <=> "LIBRARY_STAGE exists"
69
70 ! ------------------------------------------------------------------------------
71
72 #Ifndef VN_1633;
73 Message fatalerror "*** Library 6.12.x needs Inform v6.33 or later to work ***";
74 #Endif; ! VN_
75
76 Include "version";
77
78 Constant BEFORE_PARSER   10;
79 Constant AFTER_PARSER    20;
80 Constant AFTER_VERBLIB   30;
81 Constant AFTER_GRAMMAR   40;
82
83 Constant LIBRARY_STAGE = BEFORE_PARSER;
84
85 Default COMMENT_CHARACTER '*';
86
87 #Ifdef INFIX;
88 Default DEBUG 0;
89 #Endif; ! INFIX
90
91 #Ifndef WORDSIZE;                   ! compiling with Z-code only compiler
92 Default TARGET_ZCODE 0;
93 Constant WORDSIZE 2;
94 #Endif; ! WORDSIZE
95
96 #Ifdef TARGET_ZCODE;                ! offsets into Z-machine header
97
98 Constant HDR_ZCODEVERSION  $00;     ! byte
99 Constant HDR_TERPFLAGS     $01;     ! byte
100 Constant HDR_GAMERELEASE   $02;     ! word
101 Constant HDR_HIGHMEMORY    $04;     ! word
102 Constant HDR_INITIALPC     $06;     ! word
103 Constant HDR_DICTIONARY    $08;     ! word
104 Constant HDR_OBJECTS       $0A;     ! word
105 Constant HDR_GLOBALS       $0C;     ! word
106 Constant HDR_STATICMEMORY  $0E;     ! word
107 Constant HDR_GAMEFLAGS     $10;     ! word
108 Constant HDR_GAMESERIAL    $12;     ! six ASCII characters
109 Constant HDR_ABBREVIATIONS $18;     ! word
110 Constant HDR_FILELENGTH    $1A;     ! word
111 Constant HDR_CHECKSUM      $1C;     ! word
112 Constant HDR_TERPNUMBER    $1E;     ! byte
113 Constant HDR_TERPVERSION   $1F;     ! byte
114 Constant HDR_SCREENHLINES  $20;     ! byte
115 Constant HDR_SCREENWCHARS  $21;     ! byte
116 Constant HDR_SCREENWUNITS  $22;     ! word
117 Constant HDR_SCREENHUNITS  $24;     ! word
118 Constant HDR_FONTWUNITS    $26;     ! byte
119 Constant HDR_FONTHUNITS    $27;     ! byte
120 Constant HDR_ROUTINEOFFSET $28;     ! word
121 Constant HDR_STRINGOFFSET  $2A;     ! word
122 Constant HDR_BGCOLOUR      $2C;     ! byte
123 Constant HDR_FGCOLOUR      $2D;     ! byte
124 Constant HDR_TERMCHARS     $2E;     ! word
125 Constant HDR_PIXELSTO3     $30;     ! word
126 Constant HDR_TERPSTANDARD  $32;     ! two bytes
127 Constant HDR_ALPHABET      $34;     ! word
128 Constant HDR_EXTENSION     $36;     ! word
129 Constant HDR_UNUSED        $38;     ! two words
130 Constant HDR_INFORMVERSION $3C;     ! four ASCII characters
131
132 #Ifnot; ! TARGET_GLULX              ! offsets into Glulx header and start of ROM
133
134 Constant HDR_MAGICNUMBER   $00;     ! long word
135 Constant HDR_GLULXVERSION  $04;     ! long word
136 Constant HDR_RAMSTART      $08;     ! long word
137 Constant HDR_EXTSTART      $0C;     ! long word
138 Constant HDR_ENDMEM        $10;     ! long word
139 Constant HDR_STACKSIZE     $14;     ! long word
140 Constant HDR_STARTFUNC     $18;     ! long word
141 Constant HDR_DECODINGTBL   $1C;     ! long word
142 Constant HDR_CHECKSUM      $20;     ! long word
143 Constant ROM_INFO          $24;     ! four ASCII characters
144 Constant ROM_MEMORYLAYOUT  $28;     ! long word
145 Constant ROM_INFORMVERSION $2C;     ! four ASCII characters
146 Constant ROM_COMPVERSION   $30;     ! four ASCII characters
147 Constant ROM_GAMERELEASE   $34;     ! short word
148 Constant ROM_GAMESERIAL    $36;     ! six ASCII characters
149
150 Include "infglk";
151
152 #Endif; ! TARGET_
153
154 Include "linklpa";
155
156 Fake_Action LetGo;
157 Fake_Action Receive;
158 Fake_Action ThrownAt;
159 Fake_Action Order;
160 Fake_Action TheSame;
161 Fake_Action PluralFound;
162 Fake_Action ListMiscellany;
163 Fake_Action Miscellany;
164 Fake_Action RunTimeError;
165 Fake_Action Prompt;
166 Fake_Action NotUnderstood;
167 Fake_Action Going;
168
169 #Ifdef NO_PLACES;
170 Fake_Action Places;
171 Fake_Action Objects;
172 #Endif; ! NO_PLACES
173
174 ! ------------------------------------------------------------------------------
175
176 [ Main; InformLibrary.play(); ];
177
178 ! ------------------------------------------------------------------------------
179
180 #Ifdef COLOR;
181 Constant COLOUR;
182 #Endif;
183
184 #Ifdef COLOUR;
185 Global clr_on = 1;
186 #Ifnot;
187 Global clr_on = 0;
188 #Endif;
189
190 ! ------------------------------------------------------------------------------
191 !   Global variables and their associated Constant and Array declarations
192 ! ------------------------------------------------------------------------------
193
194 Global location = InformLibrary;    ! Must be first global defined
195 Global sline1;                      ! Must be second
196 Global sline2;                      ! Must be third
197                                     ! (for status line display)
198
199 ! ------------------------------------------------------------------------------
200 !   Z-Machine and interpreter issues
201 ! ------------------------------------------------------------------------------
202
203 #Ifdef TARGET_ZCODE;
204 Global top_object;                  ! Largest valid number of any tree object
205 ! ### these globals are not meaningful... well, maybe standard_interpreter,
206 ! but I'll decide that later (AP).
207 Constant INDIV_PROP_START 64;       ! Equivalent of a Glulx constant
208
209 #Endif; ! TARGET_ZCODE
210
211 Global standard_interpreter;        ! The version number of the Z-Machine Standard which the
212                                     ! interpreter claims to support, in form (upper byte).(lower)
213
214 Global undo_flag;                   ! Can the interpreter provide "undo"?
215 Global just_undone;                 ! Can't have two successive UNDOs
216
217 Global transcript_mode;             ! true when game scripting is on
218
219 #Ifdef TARGET_ZCODE;
220 Global xcommsdir;                   ! true if command recording is on
221 #Endif; ! TARGET_ZCODE
222
223 #Ifdef TARGET_GLULX;
224 Constant GG_MAINWIN_ROCK     201;
225 Constant GG_STATUSWIN_ROCK   202;
226 Constant GG_QUOTEWIN_ROCK    203;
227 Constant GG_SAVESTR_ROCK     301;
228 Constant GG_SCRIPTSTR_ROCK   302;
229 Constant GG_COMMANDWSTR_ROCK 303;
230 Constant GG_COMMANDRSTR_ROCK 304;
231 Constant GG_SCRIPTFREF_ROCK  401;
232 Array gg_event --> 4;
233 #Ifdef VN_1630;
234 Array gg_arguments buffer 28;
235 #Ifnot;
236 Array gg_arguments --> 8;
237 #Endif; ! VN_
238 Global gg_mainwin = 0;
239 Global gg_statuswin = 0;
240 Global gg_quotewin = 0;
241 Global gg_scriptfref = 0;
242 Global gg_scriptstr = 0;
243 Global gg_savestr = 0;
244 Global gg_commandstr = 0;
245 Global gg_command_reading = 0;      ! true if gg_commandstr is being replayed
246 #Endif; ! TARGET_GLULX
247
248 Global gg_statuswin_cursize = 0;
249 Global gg_statuswin_size = 1;
250
251 ! ------------------------------------------------------------------------------
252 !   Time and score
253 !   (for linkage reasons, the task_* arrays are created not here but in verblib.h)
254 ! ------------------------------------------------------------------------------
255
256 #Ifndef sys_statusline_flag;
257 Global sys_statusline_flag = 0;     ! non-zero if status line displays time
258 #Endif;
259
260 #Ifndef START_MOVE;
261 Constant START_MOVE 0;               ! Traditionally 0 for Infocom, 1 for Inform
262 #Endif;
263
264 Global turns = START_MOVE;          ! Number of turns of play so far
265 Global the_time = NULL;             ! Current time (in minutes since midnight)
266 Global time_rate = 1;               ! How often time is updated
267 Global time_step;                   ! By how much
268
269 #Ifndef MAX_TIMERS;
270 Constant MAX_TIMERS  32;            ! Max number timers/daemons active at once
271 #Endif; ! MAX_TIMERS
272 Array  the_timers  --> MAX_TIMERS;
273 Global active_timers;               ! Number of timers/daemons actives
274
275 Global score;                       ! The current score
276 Global last_score;                  ! Score last turn (for testing for changes)
277 Global notify_mode = true;          ! Score notification
278 Global places_score;                ! Contribution to score made by visiting
279 Global things_score;                ! Contribution made by acquisition
280
281 ! ------------------------------------------------------------------------------
282 !   The player
283 ! ------------------------------------------------------------------------------
284
285 Global player;                      ! Which object the human is playing through
286 Global deadflag;                    ! Normally 0, or false; 1 for dead
287                                     ! 2 for victorious, and higher numbers
288                                     ! represent exotic forms of death
289
290 ! ------------------------------------------------------------------------------
291 !   Light and room descriptions
292 ! ------------------------------------------------------------------------------
293
294 Global lightflag = true;            ! Is there currently light to see by?
295 Global real_location;               ! When in darkness, location = thedark
296                                     ! and this holds the real location
297 Global prev_location;               ! The previous value of real_location
298 Global visibility_ceiling;          ! Highest object in tree visible from the
299                                     ! player's point of view (usually the room,
300                                     ! sometimes darkness, sometimes a closed
301                                     ! non-transparent container).
302
303 Global lookmode = 2;                ! 1=brief, 2=verbose, 3=superbrief
304                                     ! room descriptions
305 Global print_player_flag;           ! If set, print something like "(as Fred)"
306                                     ! in room descriptions, to reveal whom the
307                                     ! player is playing through
308 Global lastdesc;                    ! Value of location at time of most recent
309                                     ! room description printed out
310
311 ! ------------------------------------------------------------------------------
312 !   List writing  (style bits are defined as Constants in "verblibm.h")
313 ! ------------------------------------------------------------------------------
314
315 Global c_style;                     ! Current list-writer style
316 Global lt_value;                    ! Common value of list_together
317 Global listing_together;            ! Object number of one member of a group
318                                     ! being listed together
319 Global listing_size;                ! Size of such a group
320 Global wlf_indent;                  ! Current level of indentation printed by
321                                     ! WriteListFrom()
322
323 Global inventory_stage = 1;         ! 1 or 2 according to the context in which
324                                     ! "invent" routines of objects are called
325 Global inventory_style;             ! List-writer style currently used while
326                                     ! printing inventories
327
328 Global objects_style;               ! List-writer style currently used while
329 Global places_style;                ! printing objects handled or places visited
330
331 ! ------------------------------------------------------------------------------
332 !   Menus and printing
333 ! ------------------------------------------------------------------------------
334
335 Global pretty_flag = true;          ! Use character graphics, or plain text?
336 Global menu_nesting;                ! Level of nesting (0 = root menu)
337 Global menu_item;                   ! These are used in communicating
338 Global item_width = 8;              ! with the menu-creating routines
339 Global item_name = "---";
340
341 Global lm_n;                        ! Parameters used by LibraryMessages
342 Global lm_o;                        ! mechanism
343 Global lm_s;
344
345 #Ifdef DEBUG;
346 Constant DEBUG_MESSAGES $0001;
347 Constant DEBUG_ACTIONS  $0002;
348 Constant DEBUG_TIMERS   $0004;
349 Constant DEBUG_CHANGES  $0008;
350 Constant DEBUG_VERBOSE  $0080;
351 Global debug_flag;                  ! Bitmap of flags for tracing actions,
352                                     ! calls to object routines, etc.
353 Global x_scope_count;               ! Used in printing a list of everything
354 #Endif; ! DEBUG                     ! in scope
355
356 ! five for colour control
357 ! see http://www.inform-fiction.org/patches/L61007.html
358 ! To enable colour define a constant or Global: COLOR or COLOUR
359 !Global clr_on;                      ! has colour been enabled by the player?
360 #Ifdef COLOUR;
361 Global clr_fg = 1;                  ! foreground colour
362 Global clr_bg = 1;                  ! background colour
363 Global clr_fgstatus = 1;            ! foreground colour of statusline
364 Global clr_bgstatus = 1;            ! background colour of statusline
365 #Endif; ! COLOUR
366 Global statuswin_current;           ! if writing to top window
367
368 Constant CLR_CURRENT 0;
369 Constant CLR_DEFAULT 1;
370 Constant CLR_BLACK   2;
371 Constant CLR_RED     3;
372 Constant CLR_GREEN   4;
373 Constant CLR_YELLOW  5;
374 Constant CLR_BLUE    6;
375 Constant CLR_MAGENTA 7;
376 Constant CLR_CYAN    8;
377 Constant CLR_WHITE   9;
378 Constant CLR_PURPLE  7;
379 Constant CLR_AZURE   8;
380
381 #Ifdef TARGET_GLULX;
382 Array GlulxColourValues
383     --> (-2)
384         (-1)
385         $000000
386         $EF0000
387         $00D600
388         $EFEF00
389         $006BB5
390         $FF00FF
391         $00EFEF
392         $FFFFFF;
393 #Endif; ! TARGET_GLULX
394
395 Constant WIN_ALL     0;
396 Constant WIN_STATUS  1;
397 Constant WIN_MAIN    2;
398
399 ! ------------------------------------------------------------------------------
400 !   Action processing
401 ! ------------------------------------------------------------------------------
402
403 Global action;                      ! Action currently being asked to perform
404 Global inp1;                        ! 0 (nothing), 1 (number) or first noun
405 Global inp2;                        ! 0 (nothing), 1 (number) or second noun
406 Global noun;                        ! First noun or numerical value
407 Global second;                      ! Second noun or numerical value
408
409 Global keep_silent;                 ! If true, attempt to perform the action
410                                     ! silently (e.g. for implicit takes,
411                                     ! implicit opening of unlocked doors)
412
413 Global reason_code;                 ! Reason for calling a "life" rule
414                                     ! (an action or fake such as ##Kiss)
415
416 Global receive_action;              ! Either ##PutOn or ##Insert, whichever is
417                                     ! action being tried when an object's
418                                     ! "before" rule is checking "Receive"
419
420 Global no_implicit_actions;         ! Don't implicitly do things.
421
422 ! ==============================================================================
423 !   Parser variables: first, for communication to the parser
424 ! ------------------------------------------------------------------------------
425
426 Global parser_trace = 0;            ! Set this to 1 to make the parser trace
427                                     ! tokens and lines
428 Global parser_action;               ! For the use of the parser when calling
429 Global parser_one;                  ! user-supplied routines
430 Global parser_two;                  !
431 Array  inputobjs       --> 16;      ! For parser to write its results in
432 Global parser_inflection;           ! A property (usually "name") to find
433                                     ! object names in
434 Global parser_inflection_func;      ! Programmer sets this to true when
435                                     ! parser_infection is a function
436
437 ! ------------------------------------------------------------------------------
438 !   Parser output
439 ! ------------------------------------------------------------------------------
440
441 Global actor;                       ! Person asked to do something
442 Global actors_location;             ! Like location, but for the actor
443 Global meta;                        ! Verb is a meta-command (such as "save")
444
445 #Ifdef INFIX;
446 Global infix_verb;                  ! Verb is an Infix command
447 #Endif;
448
449 Array  multiple_object --> 64;      ! List of multiple parameters
450 Global multiflag;                   ! Multiple-object flag passed to actions
451                                     ! Also used to prevent misleading MULTI_PE
452 Global toomany_flag;                ! Flag for "multiple match too large"
453                                     ! (e.g. if "take all" took over 100 things)
454
455 Global special_word;                ! Dictionary address for "special" token
456 Global special_number;              ! Number typed for "special" token
457 Global parsed_number;               ! For user-supplied parsing routines
458 Global consult_from;                ! Word that a "consult" topic starts on
459 Global consult_words;               ! ...and number of words in topic
460 Global asking_player;               ! True during disambiguation question
461
462 ! ------------------------------------------------------------------------------
463 !   Implicit taking
464 ! ------------------------------------------------------------------------------
465
466 Global notheld_mode;                ! To do with implicit taking
467 Global onotheld_mode;               !     "old copy of notheld_mode", ditto
468 Global not_holding;                 ! Object to be automatically taken as an
469                                     ! implicit command
470 Array  kept_results --> 16;         ! Delayed command (while the take happens)
471
472 ! ------------------------------------------------------------------------------
473 !   Error numbers when parsing a grammar line
474 ! ------------------------------------------------------------------------------
475
476 Global etype;                       ! Error number on current line
477 Global best_etype;                  ! Preferred error number so far
478 Global nextbest_etype;              ! Preferred one, if ASKSCOPE_PE disallowed
479
480 Constant STUCK_PE     = 1;
481 Constant UPTO_PE      = 2;
482 Constant NUMBER_PE    = 3;
483 Constant CANTSEE_PE   = 4;
484 Constant TOOLIT_PE    = 5;
485 Constant NOTHELD_PE   = 6;
486 Constant MULTI_PE     = 7;
487 Constant MMULTI_PE    = 8;
488 Constant VAGUE_PE     = 9;
489 Constant EXCEPT_PE    = 10;
490 Constant ANIMA_PE     = 11;
491 Constant VERB_PE      = 12;
492 Constant SCENERY_PE   = 13;
493 Constant ITGONE_PE    = 14;
494 Constant JUNKAFTER_PE = 15;
495 Constant TOOFEW_PE    = 16;
496 Constant NOTHING_PE   = 17;
497 Constant ASKSCOPE_PE  = 18;
498
499 ! ------------------------------------------------------------------------------
500 !   Pattern-matching against a single grammar line
501 ! ------------------------------------------------------------------------------
502
503 Array pattern --> 32;               ! For the current pattern match
504 Global pcount;                      ! and a marker within it
505 Array pattern2 --> 32;              ! And another, which stores the best match
506 Global pcount2;                     ! so far
507 Constant PATTERN_NULL = $ffff;      ! Entry for a token producing no text
508
509 Array  line_ttype-->32;             ! For storing an analysed grammar line
510 Array  line_tdata-->32;
511 Array  line_token-->32;
512
513 Global parameters;                  ! Parameters (objects) entered so far
514 Global nsns;                        ! Number of special_numbers entered so far
515 Global special_number1;             ! First number, if one was typed
516 Global special_number2;             ! Second number, if two were typed
517
518 ! ------------------------------------------------------------------------------
519 !   Inferences and looking ahead
520 ! ------------------------------------------------------------------------------
521
522 Global params_wanted;               ! Number of parameters needed
523                                     ! (which may change in parsing)
524
525 Global inferfrom;                   ! The point from which the rest of the
526                                     ! command must be inferred
527 Global inferword;                   ! And the preposition inferred
528 Global dont_infer;                  ! Another dull flag
529 Global no_infer_message = false;    ! Use in ChooseObjects to suppress
530                                     ! an inference message.
531
532 Global action_to_be;                ! (If the current line were accepted.)
533 Global action_reversed;             ! (Parameters would be reversed in order.)
534 Global advance_warning;             ! What a later-named thing will be
535
536 ! ------------------------------------------------------------------------------
537 !   At the level of individual tokens now
538 ! ------------------------------------------------------------------------------
539
540 Global found_ttype;                 ! Used to break up tokens into type
541 Global found_tdata;                 ! and data (by AnalyseToken)
542 Global token_filter;                ! For noun filtering by user routines
543
544 Global length_of_noun;              ! Set by NounDomain to no of words in noun
545
546 #Ifdef TARGET_ZCODE;
547 Constant REPARSE_CODE = 10000;      ! Signals "reparse the text" as a reply
548                                     ! from NounDomain
549 #Ifnot; ! TARGET_GLULX
550 Constant REPARSE_CODE = $40000000;  ! The parser rather gunkily adds addresses
551                                     ! to REPARSE_CODE for some purposes and
552                                     ! expects the result to be greater than
553                                     ! REPARSE_CODE (signed comparison).
554                                     ! So Glulx Inform is limited to a single
555                                     ! gigabyte of storage, for the moment.
556 #Endif; ! TARGET_
557
558 Global lookahead;                   ! The token after the one now being matched
559
560 Global multi_mode;                  ! Multiple mode
561 Global multi_wanted;                ! Number of things needed in multitude
562 Global multi_had;                   ! Number of things actually found
563 Global multi_context;               ! What token the multi-obj was accepted for
564
565 Global indef_mode;                  ! "Indefinite" mode - ie, "take a brick"
566                                     ! is in this mode
567 Global indef_type;                  ! Bit-map holding types of specification
568 Global indef_wanted;                ! Number of items wanted (100 for all)
569 Global indef_guess_p;               ! Plural-guessing flag
570 Global indef_owner;                 ! Object which must hold these items
571 Global indef_cases;                 ! Possible gender and numbers of them
572 Global indef_possambig;             ! Has a possibly dangerous assumption
573                                     ! been made about meaning of a descriptor?
574 Global indef_nspec_at;              ! Word at which a number like "two" was
575                                     ! parsed (for backtracking)
576 Global allow_plurals;               ! Whether plurals presently allowed or not
577
578 Global take_all_rule;               ! Slightly different rules apply to
579                                     ! "take all" than other uses of multiple
580                                     ! objects, to make adjudication produce
581                                     ! more pragmatically useful results
582                                     ! (Not a flag: possible values 0, 1, 2)
583
584 Global dict_flags_of_noun;          ! Of the noun currently being parsed
585                                     ! (a bitmap in #dict_par1 format)
586 Constant DICT_VERB $01;
587 Constant DICT_META $02;
588 Constant DICT_PLUR $04;
589 Constant DICT_PREP $08;
590 Constant DICT_X654 $70;
591 Constant DICT_NOUN $80;
592
593 Global pronoun_word;                ! Records which pronoun ("it", "them", ...)
594                                     ! caused an error
595 Global pronoun_obj;                 ! And what obj it was thought to refer to
596 Global pronoun__word;               ! Saved value
597 Global pronoun__obj;                ! Saved value
598
599 ! ------------------------------------------------------------------------------
600 !   Searching through scope and parsing "scope=Routine" grammar tokens
601 ! ------------------------------------------------------------------------------
602
603 Constant PARSING_REASON       = 0;  ! Possible reasons for searching scope
604 Constant TALKING_REASON       = 1;
605 Constant EACH_TURN_REASON     = 2;
606 Constant REACT_BEFORE_REASON  = 3;
607 Constant REACT_AFTER_REASON   = 4;
608 Constant LOOPOVERSCOPE_REASON = 5;
609 Constant TESTSCOPE_REASON     = 6;
610
611 Global scope_reason = PARSING_REASON; ! Current reason for searching scope
612
613 Global scope_token;                 ! For "scope=Routine" grammar tokens
614 Global scope_error;
615 Global scope_stage;                 ! 1, 2 then 3
616
617 Global ats_flag = 0;                ! For AddToScope routines
618 Global ats_hls;                     !
619
620 Global placed_in_flag;              ! To do with PlaceInScope
621
622 ! ------------------------------------------------------------------------------
623 !   The match list of candidate objects for a given token
624 ! ------------------------------------------------------------------------------
625
626 Constant MATCH_LIST_SIZE = 64;
627 Array  match_list    --> MATCH_LIST_SIZE; ! An array of matched objects so far
628 Array  match_classes --> MATCH_LIST_SIZE; ! An array of equivalence classes for them
629 Array  match_scores --> MATCH_LIST_SIZE;  ! An array of match scores for them
630 Global number_matched;              ! How many items in it?  (0 means none)
631 Global number_of_classes;           ! How many equivalence classes?
632 Global match_length;                ! How many words long are these matches?
633 Global saved_ml;
634 Global match_from;                  ! At what word of the input do they begin?
635 Global bestguess_score;             ! What did the best-guess object score?
636
637 ! ------------------------------------------------------------------------------
638 !   Low level textual manipulation
639 ! ------------------------------------------------------------------------------
640
641 #Ifdef TARGET_ZCODE;
642
643 ! 'buffer' holds the input line as typed by the player
644 !
645 !       buffer->0                     INPUT_BUFFER_LEN - WORDSIZE
646 !       buffer->1                     Number of characters input by player
647 !       buffer->2 ... buffer->121     The actual characters
648 !       buffer->122                   Spare byte to allow for 'terp bugs
649 !
650 ! 'parse' holds the result of parsing that line into dictionary words
651 !
652 !       parse->0                      MAX_BUFFER_WORDS
653 !       parse->1                      Number of words input by player
654 !
655 !       parse-->1                     Dictionary addr of first input word
656 !       parse->4                      Number of characters in the word
657 !       parse->5                      Start position in 'buffer' of the word
658 !
659 !       parse-->3  parse->8,9         Same data for second input word
660 !       ...
661 !       parse-->29 parse->60,61       Same data for MAX_BUFFER_WORDS input word
662 !       parse->62,63,64               Spare bytes (not sure why)
663
664
665 Constant INPUT_BUFFER_LEN = WORDSIZE + 120;  ! 120 is limit on input chars
666 Constant MAX_BUFFER_WORDS = 15;              ! Limit on input words
667
668 Array  buffer  -> INPUT_BUFFER_LEN + 1;      ! For main line of input
669 Array  buffer2 -> INPUT_BUFFER_LEN + 1;      ! For supplementary questions
670 Array  buffer3 -> INPUT_BUFFER_LEN + 1;      ! Retaining input for "AGAIN"
671
672 #Ifdef VN_1630;
673 Array  parse   buffer (MAX_BUFFER_WORDS * 4) + 3;   ! Parsed data from 'buffer'
674 Array  parse2  buffer (MAX_BUFFER_WORDS * 4) + 3;   ! Parsed data from 'buffer2'
675 #Ifnot;
676 Array  parse   -> 2 + (MAX_BUFFER_WORDS * 4) + 3;
677 Array  parse2  -> 2 + (MAX_BUFFER_WORDS * 4) + 3;
678 #Endif; ! VN_
679
680 #Ifnot; ! TARGET_GLULX
681
682 ! 'buffer' holds the input line as typed by the player
683 !
684 !       buffer-->0                    Number of characters input by player
685 !       buffer->4 ... buffer->259     The actual characters
686 !
687 ! 'parse' holds the result of parsing that line into dictionary words
688 !
689 !       parse-->0                     Number of words input by player
690 !
691 !       parse-->1                     Dictionary addr of first input word
692 !       parse-->2                     Number of characters in the word
693 !       parse-->3                     Start position in 'buffer' of the word
694 !
695 !       parse-->4,5,6                 Same data for second input word
696 !       ...
697 !       parse-->58,59,60              Same data for MAX_BUFFER_WORDS input word
698
699 Constant INPUT_BUFFER_LEN = WORDSIZE + 256;    ! 256 is limit on input chars
700 Constant MAX_BUFFER_WORDS = 20;                ! Limit on input words
701
702 #Ifdef VN_1630;
703 Array  buffer  buffer (INPUT_BUFFER_LEN-WORDSIZE);  ! For main line of input
704 Array  buffer2 buffer (INPUT_BUFFER_LEN-WORDSIZE);  ! For supplementary questions
705 Array  buffer3 buffer (INPUT_BUFFER_LEN-WORDSIZE);  ! Retaining input for "AGAIN"
706 #Ifnot;
707 Array  buffer  -> INPUT_BUFFER_LEN;
708 Array  buffer2 -> INPUT_BUFFER_LEN;
709 Array  buffer3 -> INPUT_BUFFER_LEN;
710 #Endif; ! VN_
711 Array  parse   --> 1 + (MAX_BUFFER_WORDS * 3);      ! Parsed data from 'buffer'
712 Array  parse2  --> 1 + (MAX_BUFFER_WORDS * 3);      ! Parsed data from 'buffer2'
713
714 #Endif; ! TARGET_
715
716 Constant comma_word = 'comma,';     ! An "untypeable word" used to substitute
717                                     ! for commas in parse buffers
718
719 Global wn;                          ! Word number within "parse" (from 1)
720 Global num_words;                   ! Number of words typed
721 Global num_desc;                    ! Number of descriptors typed
722 Global verb_word;                   ! Verb word (eg, take in "take all" or
723                                     ! "dwarf, take all") - address in dict
724 Global verb_wordnum;                ! its number in typing order (eg, 1 or 3)
725 Global usual_grammar_after;         ! Point from which usual grammar is parsed (it may vary from the
726                                     ! above if user's routines match multi-word verbs)
727
728 Global oops_from;                   ! The "first mistake" word number
729 Global saved_oops;                  ! Used in working this out
730
731 Constant OOPS_WORKSPACE_LEN 64;     ! Used temporarily by "oops" routine
732 Array  oops_workspace -> OOPS_WORKSPACE_LEN;
733
734 Global held_back_mode;              ! Flag: is there some input from last time
735 Global hb_wn;                       ! left over?  (And a save value for wn.)
736                                     ! (Used for full stops and "then".)
737
738 Global caps_mode;                   ! Keep track of (The) with 'proper' caps
739 Global print_anything_result;       ! Return value from a PrintAny() routine
740 Global initial_lookmode;            ! Default, or set in Initialise()
741 Global before_first_turn;           ! True until after initial LOOK
742
743 ! ----------------------------------------------------------------------------
744
745 Array PowersOfTwo_TB                ! Used in converting case numbers to case
746   --> $$100000000000                ! bitmaps
747       $$010000000000
748       $$001000000000
749       $$000100000000
750       $$000010000000
751       $$000001000000
752       $$000000100000
753       $$000000010000
754       $$000000001000
755       $$000000000100
756       $$000000000010
757       $$000000000001;
758
759 ! ============================================================================
760 !  Constants, and one variable, needed for the language definition file
761 ! ----------------------------------------------------------------------------
762
763 Constant POSSESS_PK  = $100;
764 Constant DEFART_PK   = $101;
765 Constant INDEFART_PK = $102;
766 Global short_name_case;
767
768 Global dict_start;
769 Global dict_entry_size;
770 Global dict_end;
771
772 ! ----------------------------------------------------------------------------
773
774 Include "language__";               ! The natural language definition, whose filename is taken from
775                                     ! the ICL language_name variable
776
777 ! ----------------------------------------------------------------------------
778
779 #Ifndef LanguageCases;
780 Constant LanguageCases = 1;
781 #Endif; ! LanguageCases
782
783 ! ------------------------------------------------------------------------------
784 !   Pronouns support for the cruder (library 6/2 and earlier) version:
785 !   only needed in English
786 ! ------------------------------------------------------------------------------
787
788 #Ifdef EnglishNaturalLanguage;
789 Global itobj = NULL;                ! The object which is currently "it"
790 Global himobj = NULL;               ! The object which is currently "him"
791 Global herobj = NULL;               ! The object which is currently "her"
792
793 Global old_itobj = NULL;            ! The object which is currently "it"
794 Global old_himobj = NULL;           ! The object which is currently "him"
795 Global old_herobj = NULL;           ! The object which is currently "her"
796 #Endif; ! EnglishNaturalLanguage
797
798 ! ============================================================================
799 ! For present and past tenses
800 ! ----------------------------------------------------------------------------
801 Constant PRESENT_TENSE 0;
802 Constant PAST_TENSE    1;
803
804 ! ============================================================================
805 ! For InformLibrary.actor_act() to control what happens when it aborts.
806 ! ----------------------------------------------------------------------------
807 Constant ACTOR_ACT_OK 0;
808 Constant ACTOR_ACT_ABORT_NOTUNDERSTOOD 1;
809 Constant ACTOR_ACT_ABORT_ORDER 2;
810
811 ! ============================================================================
812 ! "Darkness" is not really a place: but it has to be an object so that the
813 !  location-name on the status line can be "Darkness".
814 ! ----------------------------------------------------------------------------
815
816 Object  thedark "(darkness object)"
817   with  initial 0,
818         short_name DARKNESS__TX,
819         description [;  return L__M(##Miscellany, 17); ];
820
821 ! If you want to use the third-person of the narrative voice, you will
822 ! need to replace this selfobj with your own.
823 Class  SelfClass
824   with name ',a' ',b' ',c' ',d' ',e',
825         short_name  YOURSELF__TX,
826         description [;  return L__M(##Miscellany, 19); ],
827         before NULL,
828         after NULL,
829         life NULL,
830         each_turn NULL,
831         time_out NULL,
832         describe NULL,
833         article THE__TX,
834         add_to_scope 0,
835         capacity 100,
836         parse_name 0,
837         orders 0,
838         number 0,
839         narrative_voice 2,
840         narrative_tense PRESENT_TENSE,
841         nameless true,
842         posture 0,
843         before_implicit [;Take: return 2;],
844   has   concealed animate proper transparent;
845
846 SelfClass selfobj "(self object)";
847
848 ! ============================================================================
849 !  The definition of the token-numbering system used by Inform.
850 ! ----------------------------------------------------------------------------
851
852 Constant ILLEGAL_TT         = 0;    ! Types of grammar token: illegal
853 Constant ELEMENTARY_TT      = 1;    !     (one of those below)
854 Constant PREPOSITION_TT     = 2;    !     e.g. 'into'
855 Constant ROUTINE_FILTER_TT  = 3;    !     e.g. noun=CagedCreature
856 Constant ATTR_FILTER_TT     = 4;    !     e.g. edible
857 Constant SCOPE_TT           = 5;    !     e.g. scope=Spells
858 Constant GPR_TT             = 6;    !     a general parsing routine
859
860 Constant NOUN_TOKEN         = 0;    ! The elementary grammar tokens, and
861 Constant HELD_TOKEN         = 1;    ! the numbers compiled by Inform to
862 Constant MULTI_TOKEN        = 2;    ! encode them
863 Constant MULTIHELD_TOKEN    = 3;
864 Constant MULTIEXCEPT_TOKEN  = 4;
865 Constant MULTIINSIDE_TOKEN  = 5;
866 Constant CREATURE_TOKEN     = 6;
867 Constant SPECIAL_TOKEN      = 7;
868 Constant NUMBER_TOKEN       = 8;
869 Constant TOPIC_TOKEN        = 9;
870
871
872 Constant GPR_FAIL           = -1;   ! Return values from General Parsing
873 Constant GPR_PREPOSITION    = 0;    ! Routines
874 Constant GPR_NUMBER         = 1;
875 Constant GPR_MULTIPLE       = 2;
876 Constant GPR_REPARSE        = REPARSE_CODE;
877 Constant GPR_NOUN           = $ff00;
878 Constant GPR_HELD           = $ff01;
879 Constant GPR_MULTI          = $ff02;
880 Constant GPR_MULTIHELD      = $ff03;
881 Constant GPR_MULTIEXCEPT    = $ff04;
882 Constant GPR_MULTIINSIDE    = $ff05;
883 Constant GPR_CREATURE       = $ff06;
884
885 Constant ENDIT_TOKEN        = 15;   ! Value used to mean "end of grammar line"
886
887 #Iftrue (Grammar__Version == 1);
888
889 [ AnalyseToken token m;
890     found_tdata = token;
891     if (token < 0)   { found_ttype = ILLEGAL_TT; return; }
892     if (token <= 8)  { found_ttype = ELEMENTARY_TT; return; }
893     if (token < 15)  { found_ttype = ILLEGAL_TT; return; }
894     if (token == 15) { found_ttype = ELEMENTARY_TT; return; }
895     if (token < 48)  { found_ttype = ROUTINE_FILTER_TT;
896                        found_tdata = token - 16;
897                        return;
898     }
899     if (token < 80)  { found_ttype = GPR_TT;
900                        found_tdata = #preactions_table-->(token-48);
901                        return;
902     }
903     if (token < 128) { found_ttype = SCOPE_TT;
904                        found_tdata = #preactions_table-->(token-80);
905                        return;
906     }
907     if (token < 180) { found_ttype = ATTR_FILTER_TT;
908                        found_tdata = token - 128;
909                        return;
910     }
911
912     found_ttype = PREPOSITION_TT;
913     m = #adjectives_table;
914     for (::) {
915         if (token == m-->1) { found_tdata = m-->0; return; }
916         m = m+4;
917     }
918     m = #adjectives_table; RunTimeError(1);
919     found_tdata = m;
920 ];
921
922 [ UnpackGrammarLine line_address i m;
923     for (i=0 : i<32 : i++) {
924         line_token-->i = ENDIT_TOKEN;
925         line_ttype-->i = ELEMENTARY_TT;
926         line_tdata-->i = ENDIT_TOKEN;
927     }
928     for (i=0 : i<=5 : i++) {
929         line_token-->i = line_address->(i+1);
930         AnalyseToken(line_token-->i);
931         if ((found_ttype == ELEMENTARY_TT) && (found_tdata == NOUN_TOKEN)
932            && (m == line_address->0)) {
933             line_token-->i = ENDIT_TOKEN;
934             break;
935         }
936         line_ttype-->i = found_ttype;
937         line_tdata-->i = found_tdata;
938         if (found_ttype ~= PREPOSITION_TT) m++;
939     }
940     action_to_be = line_address->7;
941     action_reversed = false;
942     params_wanted = line_address->0;
943     return line_address + 8;
944 ];
945
946 #Ifnot; ! Grammar__Version == 2
947
948 [ AnalyseToken token;
949     if (token == ENDIT_TOKEN) {
950         found_ttype = ELEMENTARY_TT;
951         found_tdata = ENDIT_TOKEN;
952         return;
953     }
954     found_ttype = (token->0) & $$1111;
955     found_tdata = (token+1)-->0;
956 ];
957
958 #Ifdef TARGET_ZCODE;
959
960 [ UnpackGrammarLine line_address i;
961     for (i=0 : i<32 : i++) {
962         line_token-->i = ENDIT_TOKEN;
963         line_ttype-->i = ELEMENTARY_TT;
964         line_tdata-->i = ENDIT_TOKEN;
965     }
966     action_to_be = 256*(line_address->0) + line_address->1;
967     action_reversed = ((action_to_be & $400) ~= 0);
968     action_to_be = action_to_be & $3ff;
969     line_address--;
970     params_wanted = 0;
971     for (i=0 : : i++) {
972         line_address = line_address + 3;
973         if (line_address->0 == ENDIT_TOKEN) break;
974         line_token-->i = line_address;
975         AnalyseToken(line_address);
976         if (found_ttype ~= PREPOSITION_TT) params_wanted++;
977         line_ttype-->i = found_ttype;
978         line_tdata-->i = found_tdata;
979     }
980     return line_address + 1;
981 ];
982
983 #Ifnot; ! TARGET_GLULX
984
985 [ UnpackGrammarLine line_address i;
986     for (i=0 : i<32 : i++) {
987         line_token-->i = ENDIT_TOKEN;
988         line_ttype-->i = ELEMENTARY_TT;
989         line_tdata-->i = ENDIT_TOKEN;
990     }
991     @aloads line_address 0 action_to_be;
992     action_reversed = (((line_address->2) & 1) ~= 0);
993     line_address = line_address - 2;
994     params_wanted = 0;
995     for (i=0 : : i++) {
996         line_address = line_address + 5;
997         if (line_address->0 == ENDIT_TOKEN) break;
998         line_token-->i = line_address;
999         AnalyseToken(line_address);
1000         if (found_ttype ~= PREPOSITION_TT) params_wanted++;
1001         line_ttype-->i = found_ttype;
1002         line_tdata-->i = found_tdata;
1003     }
1004     return line_address + 1;
1005 ];
1006
1007 #Endif; ! TARGET_
1008 #Endif; ! Grammar__Version
1009
1010 ! To protect against a bug in early versions of the "Zip" interpreter:
1011 ! Of course, in Glulx, this routine actually performs work.
1012
1013 #Ifdef TARGET_ZCODE;
1014
1015 [ Tokenise__ b p; b->(2 + b->1) = 0; @tokenise b p; ];
1016
1017 #Ifnot; ! TARGET_GLULX
1018
1019 Array gg_tokenbuf -> DICT_WORD_SIZE;
1020
1021 [ GGWordCompare str1 str2 ix jx;
1022     for (ix=0 : ix<DICT_WORD_SIZE : ix++) {
1023         jx = (str1->ix) - (str2->ix);
1024         if (jx ~= 0) return jx;
1025     }
1026     return 0;
1027 ];
1028
1029 [ Tokenise__ buf tab
1030     cx numwords len bx ix wx wpos wlen val res dictlen entrylen;
1031     len = buf-->0;
1032     buf = buf+WORDSIZE;
1033
1034     ! First, split the buffer up into words. We use the standard Infocom
1035     ! list of word separators (comma, period, double-quote).
1036
1037     cx = 0;
1038     numwords = 0;
1039     while (cx < len) {
1040         while (cx < len && buf->cx == ' ') cx++;
1041         if (cx >= len) break;
1042         bx = cx;
1043         if (buf->cx == '.' or ',' or '"') cx++;
1044         else {
1045             while (cx < len && buf->cx ~= ' ' or '.' or ',' or '"') cx++;
1046         }
1047         tab-->(numwords*3+2) = (cx-bx);
1048         tab-->(numwords*3+3) = WORDSIZE+bx;
1049         numwords++;
1050         if (numwords >= MAX_BUFFER_WORDS) break;
1051     }
1052     tab-->0 = numwords;
1053
1054     ! Now we look each word up in the dictionary.
1055
1056     dictlen = #dictionary_table-->0;
1057     entrylen = DICT_WORD_SIZE + 7;
1058
1059     for (wx=0 : wx<numwords : wx++) {
1060         wlen = tab-->(wx*3+2);
1061         wpos = tab-->(wx*3+3);
1062
1063         ! Copy the word into the gg_tokenbuf array, clipping to DICT_WORD_SIZE
1064         ! characters and lower case.
1065         if (wlen > DICT_WORD_SIZE) wlen = DICT_WORD_SIZE;
1066         cx = wpos - WORDSIZE;
1067         for (ix=0 : ix<wlen : ix++) gg_tokenbuf->ix = glk_char_to_lower(buf->(cx+ix));
1068         for (: ix<DICT_WORD_SIZE : ix++) gg_tokenbuf->ix = 0;
1069
1070         val = #dictionary_table + WORDSIZE;
1071         @binarysearch gg_tokenbuf DICT_WORD_SIZE val entrylen dictlen 1 1 res;
1072         tab-->(wx*3+1) = res;
1073     }
1074 ];
1075
1076 #Endif; ! TARGET_
1077
1078 ! ============================================================================
1079 !   The InformParser object abstracts the front end of the parser.
1080 !
1081 !   InformParser.parse_input(results)
1082 !   returns only when a sensible request has been made, and puts into the
1083 !   "results" buffer:
1084 !
1085 !   --> 0 = The action number
1086 !   --> 1 = Number of parameters
1087 !   --> 2, 3, ... = The parameters (object numbers), but
1088 !                   0 means "put the multiple object list here"
1089 !                   1 means "put one of the special numbers here"
1090 !
1091 ! ----------------------------------------------------------------------------
1092
1093 Object  InformParser "(Inform Parser)"
1094   with  parse_input [ results; Parser__parse(results); ],
1095   has   proper;
1096
1097 ! ----------------------------------------------------------------------------
1098 !   The Keyboard routine actually receives the player's words,
1099 !   putting the words in "a_buffer" and their dictionary addresses in
1100 !   "a_table".  It is assumed that the table is the same one on each
1101 !   (standard) call.
1102 !
1103 !   It can also be used by miscellaneous routines in the game to ask
1104 !   yes-no questions and the like, without invoking the rest of the parser.
1105 !
1106 !   Return the number of words typed
1107 ! ----------------------------------------------------------------------------
1108
1109 #Ifdef TARGET_ZCODE;
1110
1111 [ GetNthChar a_buffer n i;
1112     for (i = 0: a_buffer->(2+i) == ' ': i++) {
1113         if (i > a_buffer->(1)) return false;
1114     }
1115     return a_buffer->(2+i+n);
1116 ];
1117
1118 [ KeyboardPrimitive  a_buffer a_table;
1119     read a_buffer a_table;
1120
1121     #Iftrue (#version_number == 6);
1122     @output_stream -1;
1123     @loadb a_buffer 1 -> sp;
1124     @add a_buffer 2 -> sp;
1125     @print_table sp sp;
1126     new_line;
1127     @output_stream 1;
1128     #Endif;
1129 ];
1130
1131 [ KeyCharPrimitive win  key;
1132     if (win) @set_window win;
1133     @read_char 1 -> key;
1134     return key;
1135 ];
1136
1137 [ KeyTimerInterrupt;
1138     rtrue;
1139 ];
1140
1141 [ KeyDelay tenths  key;
1142     @read_char 1 tenths KeyTimerInterrupt -> key;
1143     return key;
1144 ];
1145
1146 #Ifnot; ! TARGET_GLULX
1147
1148 [ GetNthChar a_buffer n i;
1149     for (i = 0: a_buffer->(4+i) == ' ': i++) {
1150         if (i > a_buffer->(1)) return false;
1151     }
1152     return a_buffer->(4+i+n);
1153 ];
1154
1155 [ KeyCharPrimitive win nostat done res ix jx ch;
1156     jx = ch; ! squash compiler warnings
1157     if (win == 0) win = gg_mainwin;
1158     if (gg_commandstr ~= 0 && gg_command_reading ~= false) {
1159         ! get_line_stream
1160         done = glk_get_line_stream(gg_commandstr, gg_arguments, 31);
1161         if (done == 0) {
1162             glk_stream_close(gg_commandstr, 0);
1163             gg_commandstr = 0;
1164             gg_command_reading = false;
1165             ! fall through to normal user input.
1166         }
1167         else {
1168             ! Trim the trailing newline
1169             if (gg_arguments->(done-1) == 10) done = done-1;
1170             res = gg_arguments->0;
1171             if (res == '\') {
1172                 res = 0;
1173                 for (ix=1 : ix<done : ix++) {
1174                     ch = gg_arguments->ix;
1175                     if (ch >= '0' && ch <= '9') {
1176                         @shiftl res 4 res;
1177                         res = res + (ch-'0');
1178                     }
1179                     else if (ch >= 'a' && ch <= 'f') {
1180                         @shiftl res 4 res;
1181                         res = res + (ch+10-'a');
1182                     }
1183                     else if (ch >= 'A' && ch <= 'F') {
1184                         @shiftl res 4 res;
1185                         res = res + (ch+10-'A');
1186                     }
1187                 }
1188             }
1189         jump KCPContinue;
1190         }
1191     }
1192     done = false;
1193     glk_request_char_event(win);
1194     while (~~done) {
1195         glk_select(gg_event);
1196         switch (gg_event-->0) {
1197           5: ! evtype_Arrange
1198             if (nostat) {
1199                 glk_cancel_char_event(win);
1200                 res = $80000000;
1201                 done = true;
1202                 break;
1203             }
1204             DrawStatusLine();
1205           2: ! evtype_CharInput
1206             if (gg_event-->1 == win) {
1207                 res = gg_event-->2;
1208                 done = true;
1209                 }
1210         }
1211         ix = HandleGlkEvent(gg_event, 1, gg_arguments);
1212         if (ix == 0) ix = LibraryExtensions.RunWhile(ext_handleglkevent, 0, gg_event, 1, gg_arguments);
1213         if (ix == 2) {
1214             res = gg_arguments-->0;
1215             done = true;
1216         }
1217         else if (ix == -1) {
1218             done = false;
1219         }
1220     }
1221     if (gg_commandstr ~= 0 && gg_command_reading == false) {
1222         if (res < 32 || res >= 256 || (res == '\' or ' ')) {
1223             glk_put_char_stream(gg_commandstr, '\');
1224             done = 0;
1225             jx = res;
1226             for (ix=0 : ix<8 : ix++) {
1227                 @ushiftr jx 28 ch;
1228                 @shiftl jx 4 jx;
1229                 ch = ch & $0F;
1230                 if (ch ~= 0 || ix == 7) done = 1;
1231                 if (done) {
1232                     if (ch >= 0 && ch <= 9) ch = ch + '0';
1233                     else                    ch = (ch - 10) + 'A';
1234                     glk_put_char_stream(gg_commandstr, ch);
1235                 }
1236             }
1237         }
1238         else {
1239             glk_put_char_stream(gg_commandstr, res);
1240         }
1241         glk_put_char_stream(gg_commandstr, 10);
1242     }
1243   .KCPContinue;
1244     return res;
1245 ];
1246
1247 [ KeyDelay tenths  key done ix;
1248     glk_request_char_event(gg_mainwin);
1249     glk_request_timer_events(tenths*100);
1250     while (~~done) {
1251         glk_select(gg_event);
1252         ix = HandleGlkEvent(gg_event, 1, gg_arguments);
1253         if (ix == 0) ix = LibraryExtensions.RunWhile(ext_handleglkevent, 0, gg_event, 1, gg_arguments);
1254         if (ix == 2) {
1255             key = gg_arguments-->0;
1256             done = true;
1257         }
1258         else if (ix >= 0 && gg_event-->0 == 1 or 2) {
1259             key = gg_event-->2;
1260             done = true;
1261         }
1262     }
1263     glk_cancel_char_event(gg_mainwin);
1264     glk_request_timer_events(0);
1265     return key;
1266 ];
1267
1268 [ KeyboardPrimitive  a_buffer a_table done ix;
1269     if (gg_commandstr ~= 0 && gg_command_reading ~= false) {
1270         ! get_line_stream
1271         done = glk_get_line_stream(gg_commandstr, a_buffer+WORDSIZE, (INPUT_BUFFER_LEN-WORDSIZE)-1);
1272         if (done == 0) {
1273             glk_stream_close(gg_commandstr, 0);
1274             gg_commandstr = 0;
1275             gg_command_reading = false;
1276             ! L__M(##CommandsRead, 5); would come after prompt
1277             ! fall through to normal user input.
1278         }
1279         else {
1280             ! Trim the trailing newline
1281             if ((a_buffer+WORDSIZE)->(done-1) == 10) done = done-1;
1282             a_buffer-->0 = done;
1283             glk_set_style(style_Input);
1284             glk_put_buffer(a_buffer+WORDSIZE, done);
1285             glk_set_style(style_Normal);
1286             print "^";
1287             jump KPContinue;
1288         }
1289     }
1290     done = false;
1291     glk_request_line_event(gg_mainwin, a_buffer+WORDSIZE, INPUT_BUFFER_LEN-WORDSIZE, 0);
1292     while (~~done) {
1293         glk_select(gg_event);
1294         switch (gg_event-->0) {
1295           5: ! evtype_Arrange
1296             DrawStatusLine();
1297           3: ! evtype_LineInput
1298             if (gg_event-->1 == gg_mainwin) {
1299                 a_buffer-->0 = gg_event-->2;
1300                 done = true;            }
1301         }
1302         ix = HandleGlkEvent(gg_event, 0, a_buffer);
1303         if (ix == 0) ix = LibraryExtensions.RunWhile(ext_handleglkevent, 0, gg_event, 0, a_buffer);
1304         if (ix == 2) done = true;
1305         else if (ix == -1) done = false;
1306     }
1307     if (gg_commandstr ~= 0 && gg_command_reading == false) {
1308         ! put_buffer_stream
1309
1310         glk_put_buffer_stream(gg_commandstr, a_buffer+WORDSIZE, a_buffer-->0);
1311         glk_put_char_stream(gg_commandstr, 10);
1312     }
1313   .KPContinue;
1314     Tokenise__(a_buffer,a_table);
1315     ! It's time to close any quote window we've got going.
1316     if (gg_quotewin) {
1317         glk_window_close(gg_quotewin, 0);
1318         gg_quotewin = 0;
1319     }
1320 ];
1321
1322 #Endif; ! TARGET_
1323
1324 [ Keyboard  a_buffer a_table  nw i w w2 x1 x2;
1325     DisplayStatus();
1326
1327   .FreshInput;
1328
1329     ! Save the start of the buffer, in case "oops" needs to restore it
1330     ! to the previous time's buffer
1331
1332     for (i=0 : i<OOPS_WORKSPACE_LEN : i++) oops_workspace->i = a_buffer->i;
1333
1334     ! In case of an array entry corruption that shouldn't happen, but would be
1335     ! disastrous if it did:
1336
1337     #Ifdef TARGET_ZCODE;
1338     a_buffer->0 = INPUT_BUFFER_LEN - WORDSIZE;
1339     a_table->0  = MAX_BUFFER_WORDS; ! Allow to split input into this many words
1340     #Endif; ! TARGET_
1341
1342     ! Print the prompt, and read in the words and dictionary addresses
1343
1344     L__M(##Prompt);
1345     if (AfterPrompt() == 0) LibraryExtensions.RunAll(ext_afterprompt);
1346     #IfV5;
1347     DrawStatusLine();
1348     #Endif; ! V5
1349
1350     KeyboardPrimitive(a_buffer, a_table);
1351     nw = NumberWords(a_table);
1352
1353     ! If the line was blank, get a fresh line
1354     if (nw == 0) {
1355         L__M(##Miscellany, 10);
1356         jump FreshInput;
1357     }
1358
1359     ! Unless the opening word was "oops", return
1360     ! Conveniently, a_table-->1 is the first word in both ZCODE and GLULX.
1361
1362     w = a_table-->1;
1363     if (w == OOPS1__WD or OOPS2__WD or OOPS3__WD) jump DoOops;
1364
1365     if (a_buffer->WORDSIZE == COMMENT_CHARACTER) {
1366         #Ifdef TARGET_ZCODE;
1367         if ((HDR_GAMEFLAGS-->0) & $0001 || xcommsdir)
1368                                            L__M(##Miscellany, 54);
1369         else                               L__M(##Miscellany, 55);
1370         #Ifnot; ! TARGET_GLULX
1371         if (gg_scriptstr || gg_commandstr) L__M(##Miscellany, 54);
1372         else                               L__M(##Miscellany, 55);
1373         #Endif; ! TARGET_
1374
1375         jump FreshInput;
1376     }
1377
1378     #IfV5;
1379     ! Undo handling
1380
1381     if ((w == UNDO1__WD or UNDO2__WD or UNDO3__WD) && (nw==1)) {
1382         i = PerformUndo();
1383         if (i == 0) jump FreshInput;
1384     }
1385     #Ifdef TARGET_ZCODE;
1386     @save_undo i;
1387     #Ifnot; ! TARGET_GLULX
1388     @saveundo i;
1389     if (i == -1) {
1390         GGRecoverObjects();
1391         i = 2;
1392     }
1393     else  i = (~~i);
1394     #Endif; ! TARGET_
1395     just_undone = 0;
1396     undo_flag = 2;
1397     if (i == -1) undo_flag = 0;
1398     if (i == 0) undo_flag = 1;
1399     if (i == 2) {
1400         RestoreColours();
1401         #Ifdef TARGET_ZCODE;
1402         style bold;
1403         #Ifnot; ! TARGET_GLULX
1404         glk_set_style(style_Subheader);
1405         #Endif; ! TARGET_
1406         print (name) location, "^";
1407         #Ifdef TARGET_ZCODE;
1408         style roman;
1409         #Ifnot; ! TARGET_GLULX
1410         glk_set_style(style_Normal);
1411         #Endif; ! TARGET_
1412         L__M(##Miscellany, 13);
1413         just_undone = 1;
1414         jump FreshInput;
1415     }
1416     #Endif; ! V5
1417
1418     return nw;
1419
1420   .DoOops;
1421     if (oops_from == 0) {
1422         L__M(##Miscellany, 14);
1423         jump FreshInput;
1424     }
1425     if (nw == 1) {
1426         L__M(##Miscellany, 15);
1427         jump FreshInput;
1428     }
1429     if (nw > 2) {
1430         L__M(##Miscellany, 16);
1431         jump FreshInput;
1432     }
1433
1434     ! So now we know: there was a previous mistake, and the player has
1435     ! attempted to correct a single word of it.
1436
1437     for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer2->i = a_buffer->i;
1438     #Ifdef TARGET_ZCODE;
1439     x1 = a_table->9;  ! Start of word following "oops"
1440     x2 = a_table->8;  ! Length of word following "oops"
1441     #Ifnot; ! TARGET_GLULX
1442     x1 = a_table-->6; ! Start of word following "oops"
1443     x2 = a_table-->5; ! Length of word following "oops"
1444     #Endif; ! TARGET_
1445
1446     ! Repair the buffer to the text that was in it before the "oops"
1447     ! was typed:
1448
1449     for (i=0 : i < OOPS_WORKSPACE_LEN : i++) a_buffer->i = oops_workspace->i;
1450     Tokenise__(a_buffer, a_table);
1451
1452     ! Work out the position in the buffer of the word to be corrected:
1453
1454     #Ifdef TARGET_ZCODE;
1455     w = a_table->(4*oops_from + 1); ! Start of word to go
1456     w2 = a_table->(4*oops_from);    ! Length of word to go
1457     #Ifnot; ! TARGET_GLULX
1458     w = a_table-->(3*oops_from);      ! Start of word to go
1459     w2 = a_table-->(3*oops_from - 1); ! Length of word to go
1460     #Endif; ! TARGET_
1461
1462 #Ifdef OOPS_CHECK;
1463     print "[~";
1464     for (i=0 : i<w2 : i++) for (i=0 : i<w2 : i++) print (char)a_buffer->(i+w);
1465     print "~ --> ~";
1466 #Endif;
1467
1468     ! Write spaces over the word to be corrected:
1469
1470     for (i=0 : i<w2 : i++) a_buffer->(i+w) = ' ';
1471
1472     if (w2 < x2) {
1473         ! If the replacement is longer than the original, move up...
1474         for (i=INPUT_BUFFER_LEN-1 : i>=w+x2 : i--)
1475             a_buffer->i = a_buffer->(i-x2+w2);
1476
1477         ! ...increasing buffer size accordingly.
1478         SetKeyBufLength(GetKeyBufLength(a_buffer) + (x2-w2), a_buffer);
1479     }
1480
1481     ! Write the correction in:
1482
1483     for (i=0 : i<x2 : i++) {
1484         a_buffer->(i+w) = buffer2->(i+x1);
1485 #Ifdef OOPS_CHECK;
1486         print (char) buffer2->(i+x1);
1487 #Endif;
1488     }
1489
1490 #Ifdef OOPS_CHECK;
1491         print "~]^^";
1492 #Endif;
1493
1494     Tokenise__(a_buffer, a_table);
1495     nw=NumberWords(a_table);
1496
1497 !    saved_ml = 0;
1498     return nw;
1499 ]; ! end of Keyboard
1500
1501 [ PerformUndo i;
1502     if (turns == START_MOVE) { L__M(##Miscellany, 11); return 0; }
1503     if (undo_flag == 0) {      L__M(##Miscellany, 6); return 0; }
1504     if (undo_flag == 1) {      L__M(##Miscellany, 7); return 0; }
1505     #Ifdef TARGET_ZCODE;
1506     @restore_undo i;
1507     #Ifnot; ! TARGET_GLULX
1508     @restoreundo i;
1509     i = (~~i);
1510     #Endif; ! TARGET_
1511     if (i == 0) {    L__M(##Miscellany, 7); return 0; }
1512     L__M(##Miscellany, 6);
1513     return 1;
1514 ];
1515
1516 ! ==========================
1517 ! Taken from I7's Parser.i6t
1518 ! ==========================
1519
1520 [ DictionaryWordToVerbNum dword verbnum;
1521 #Ifdef TARGET_ZCODE;
1522     verbnum = $ff-(dword->#dict_par2);
1523 #Ifnot; ! GLULX
1524     dword = dword + #dict_par2 - 1;
1525     @aloads dword 0 verbnum;
1526     verbnum = $ffff-verbnum;
1527 #Endif;
1528     return verbnum;
1529 ];
1530
1531 ! ==========================
1532
1533
1534 ! ----------------------------------------------------------------------------
1535 !   To simplify the picture a little, a rough map of the main routine:
1536 !
1537 !   (A) Get the input, do "oops" and "again"
1538 !   (B) Is it a direction, and so an implicit "go"?  If so go to (K)
1539 !   (C) Is anyone being addressed?
1540 !   (D) Get the verb: try all the syntax lines for that verb
1541 !   (E) Break down a syntax line into analysed tokens
1542 !   (F) Look ahead for advance warning for multiexcept/multiinside
1543 !   (G) Parse each token in turn (calling ParseToken to do most of the work)
1544 !   (H) Cheaply parse otherwise unrecognised conversation and return
1545 !   (I) Print best possible error message
1546 !   (J) Retry the whole lot
1547 !   (K) Last thing: check for "then" and further instructions(s), return.
1548 !
1549 !   The strategic points (A) to (K) are marked in the commentary.
1550 !
1551 !   Note that there are three different places where a return can happen.
1552 ! ----------------------------------------------------------------------------
1553
1554 [ Parser__parse  results   syntax line num_lines line_address i j k
1555                            token l m line_etype vw;
1556
1557     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1558     !
1559     ! A: Get the input, do "oops" and "again"
1560     !
1561     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1562
1563     ! Firstly, in "not held" mode, we still have a command left over from last
1564     ! time (eg, the user typed "eat biscuit", which was parsed as "take biscuit"
1565     ! last time, with "eat biscuit" tucked away until now).  So we return that.
1566
1567     if (notheld_mode == 1) {
1568         for (i=0 : i<8 : i++) results-->i = kept_results-->i;
1569         notheld_mode = 0;
1570         rtrue;
1571     }
1572
1573     if (held_back_mode ~= 0) {
1574         held_back_mode = 0;
1575         Tokenise__(buffer, parse);
1576         jump ReParse;
1577     }
1578
1579   .ReType;
1580
1581     Keyboard(buffer, parse);
1582
1583 #Ifdef INFIX;
1584     ! An Infix verb is a special kind of meta verb.  We mark them here.
1585     if (GetNthChar(buffer, 0) == ';')
1586         infix_verb = true;
1587     else
1588         infix_verb = false;
1589 #Endif;
1590
1591   .ReParse;
1592
1593     parser_inflection = name;
1594     parser_inflection_func = false;
1595
1596     ! Initially assume the command is aimed at the player, and the verb
1597     ! is the first word
1598
1599     num_words = NumberWords();
1600     wn = 1;
1601
1602     #Ifdef LanguageToInformese;
1603     LanguageToInformese();
1604     #IfV5;
1605     ! Re-tokenise:
1606     Tokenise__(buffer,parse);
1607     #Endif; ! V5
1608     #Endif; ! LanguageToInformese
1609
1610     if (BeforeParsing() == false) {
1611         LibraryExtensions.ext_number_1 = wn;    ! Set "between calls" functionality to restore wn each pass
1612         LibraryExtensions.BetweenCalls = LibraryExtensions.RestoreWN;
1613         LibraryExtensions.RunWhile(ext_beforeparsing, false);
1614         LibraryExtensions.BetweenCalls = 0;     ! Turn off "between calls" functionality
1615      }
1616     num_words = NumberWords();
1617
1618     k=0;
1619     #Ifdef DEBUG;
1620     if (parser_trace >= 2) {
1621         print "[ ";
1622         for (i=0 : i<num_words : i++) {
1623
1624             j = WordValue(i+1);
1625             k = WordAddress(i+1);
1626             l = WordLength(i+1);
1627             print "~"; for (m=0 : m<l : m++) print (char) k->m; print "~ ";
1628
1629             if (j == 0) print "?";
1630             else {
1631                 #Ifdef TARGET_ZCODE;
1632                 if (UnsignedCompare(j, HDR_DICTIONARY-->0) >= 0 &&
1633                     UnsignedCompare(j, HDR_HIGHMEMORY-->0) < 0)
1634                      print (address) j;
1635                 else print j;
1636                 #Ifnot; ! TARGET_GLULX
1637                 if (j->0 == $60) print (address) j;
1638                 else print j;
1639                 #Endif; ! TARGET_
1640             }
1641             if (i ~= num_words-1) print " / ";
1642         }
1643         print " ]^";
1644     }
1645     #Endif; ! DEBUG
1646     verb_wordnum = 1;
1647     actor = player;
1648     actors_location = ScopeCeiling(player);
1649     usual_grammar_after = 0;
1650
1651   .AlmostReParse;
1652
1653     scope_token = 0;
1654     action_to_be = NULL;
1655
1656     ! Begin from what we currently think is the verb word
1657
1658   .BeginCommand;
1659
1660     wn = verb_wordnum;
1661     verb_word = NextWordStopped();
1662
1663     ! If there's no input here, we must have something like "person,".
1664
1665     if (verb_word == -1) {
1666         best_etype = STUCK_PE;
1667         jump GiveError;
1668     }
1669
1670     ! Now try for "again" or "g", which are special cases: don't allow "again" if nothing
1671     ! has previously been typed; simply copy the previous text across
1672
1673     if (verb_word == AGAIN2__WD or AGAIN3__WD) verb_word = AGAIN1__WD;
1674     if (verb_word == AGAIN1__WD) {
1675         if (actor ~= player) {
1676             L__M(##Miscellany, 20);
1677             jump ReType;
1678         }
1679         if (GetKeyBufLength(buffer3) == 0) {
1680             L__M(##Miscellany, 21);
1681             jump ReType;
1682         }
1683
1684         if (WordAddress(verb_wordnum) == buffer + WORDSIZE) { ! not held back
1685             ! splice rest of buffer onto end of buffer3
1686             i = GetKeyBufLength(buffer3);
1687             while (buffer3 -> (i + WORDSIZE - 1) == ' ' or '.')
1688                 i--;
1689             j = i - WordLength(verb_wordnum);  ! amount to move buffer up by
1690             if (j > 0) {
1691                 for (m=INPUT_BUFFER_LEN-1 : m>=WORDSIZE+j : m--)
1692                     buffer->m = buffer->(m-j);
1693                 SetKeyBufLength(GetKeyBufLength()+j);
1694                 }
1695             for (m=WORDSIZE : m<WORDSIZE+i : m++) buffer->m = buffer3->m;
1696             if (j < 0) for (:m<WORDSIZE+i-j : m++) buffer->m = ' ';
1697          }
1698         else
1699             for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer->i = buffer3->i;
1700         jump ReParse;
1701     }
1702
1703     ! Save the present input in case of an "again" next time
1704
1705     if (verb_word ~= AGAIN1__WD)
1706         for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer3->i = buffer->i;
1707
1708     if (usual_grammar_after == 0) {
1709         j = verb_wordnum;
1710         i = RunRoutines(actor, grammar);
1711         #Ifdef DEBUG;
1712         if (parser_trace >= 2 && actor.grammar ~= 0 or NULL)
1713             print " [Grammar property returned ", i, "]^";
1714         #Endif; ! DEBUG
1715
1716         #Ifdef TARGET_ZCODE;
1717         if ((i ~= 0 or 1) &&
1718             (UnsignedCompare(i, dict_start) < 0 ||
1719              UnsignedCompare(i, dict_end) >= 0 ||
1720              (i - dict_start) % dict_entry_size ~= 0)) {
1721             usual_grammar_after = j;
1722             i=-i;
1723         }
1724
1725         #Ifnot; ! TARGET_GLULX
1726         if (i < 0) { usual_grammar_after = j; i=-i; }
1727         #Endif;
1728
1729         if (i == 1) {
1730             results-->0 = action;
1731             results-->1 = 0;            ! Number of parameters
1732             results-->2 = noun;
1733             results-->3 = second;
1734             if (noun) results-->1 = 1;
1735             if (second) results-->1 = 2;
1736             rtrue;
1737         }
1738         if (i ~= 0) { verb_word = i; wn--; verb_wordnum--; }
1739         else { wn = verb_wordnum; verb_word = NextWord(); }
1740     }
1741     else usual_grammar_after = 0;
1742
1743     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1744     !
1745     ! B: Is it a direction, and so an implicit "go"?  If so go to (K)
1746     !
1747     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1748
1749     #Ifdef LanguageIsVerb;
1750     if (verb_word == 0) {
1751         i = wn; verb_word = LanguageIsVerb(buffer, parse, verb_wordnum);
1752         wn = i;
1753     }
1754     #Endif; ! LanguageIsVerb
1755
1756     ! If the first word is not listed as a verb, it must be a direction
1757     ! or the name of someone to talk to
1758
1759     if (verb_word == 0 || ((verb_word->#dict_par1) & DICT_VERB) == 0) {
1760
1761         ! So is the first word an object contained in the special object "Compass"
1762         ! (i.e., a direction)?  This needs use of NounDomain, a routine which
1763         ! does the object matching, returning the object number, or 0 if none found,
1764         ! or REPARSE_CODE if it has restructured the parse table so the whole parse
1765         ! must be begun again...
1766
1767         wn = verb_wordnum; indef_mode = false; token_filter = 0;
1768         l = NounDomain(Compass, 0, NOUN_TOKEN);
1769         if (l == REPARSE_CODE) jump ReParse;
1770
1771         ! If it is a direction, send back the results:
1772         ! action=GoSub, no of arguments=1, argument 1=the direction.
1773
1774         if (l ~= 0) {
1775             results-->0 = ##Go;
1776             action_to_be = ##Go;
1777             results-->1 = 1;
1778             results-->2 = l;
1779             jump LookForMore;
1780         }
1781
1782     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1783     !
1784     ! C: Is anyone being addressed?
1785     !
1786     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1787
1788         ! Only check for a comma (a "someone, do something" command) if we are
1789         ! not already in the middle of one.  (This simplification stops us from
1790         ! worrying about "robot, wizard, you are an idiot", telling the robot to
1791         ! tell the wizard that she is an idiot.)
1792
1793         if (actor == player) {
1794             for (j=2 : j<=num_words : j++) {
1795                 i=NextWord();
1796                 if (i == comma_word) jump Conversation;
1797             }
1798         }
1799         vw = verb_word;
1800         verb_word = UnknownVerb(vw);
1801         if (verb_word == false) verb_word = LibraryExtensions.RunWhile(ext_unknownverb, false, vw);
1802         if (verb_word) jump VerbAccepted;
1803         best_etype = VERB_PE;
1804         jump GiveError;
1805
1806         ! NextWord nudges the word number wn on by one each time, so we've now
1807         ! advanced past a comma.  (A comma is a word all on its own in the table.)
1808
1809       .Conversation;
1810
1811         j = wn - 1;
1812         if (j == 1) {
1813             L__M(##Miscellany, 22);
1814             jump ReType;
1815         }
1816
1817         ! Use NounDomain (in the context of "animate creature") to see if the
1818         ! words make sense as the name of someone held or nearby
1819
1820         wn = 1; lookahead = HELD_TOKEN;
1821         scope_reason = TALKING_REASON;
1822         l = NounDomain(player,actors_location,CREATURE_TOKEN);
1823         scope_reason = PARSING_REASON;
1824         if (l == REPARSE_CODE) jump ReParse;
1825         if (l == 0) {
1826             L__M(##Miscellany, 23);
1827             jump ReType;
1828         }
1829
1830       .Conversation2;
1831
1832         ! The object addressed must at least be "talkable" if not actually "animate"
1833         ! (the distinction allows, for instance, a microphone to be spoken to,
1834         ! without the parser thinking that the microphone is human).
1835
1836         if (l hasnt animate && l hasnt talkable) {
1837             L__M(##Miscellany, 24, l);
1838             jump ReType;
1839         }
1840
1841         ! Check that there aren't any mystery words between the end of the person's
1842         ! name and the comma (eg, throw out "dwarf sdfgsdgs, go north").
1843
1844         if (wn ~= j) {
1845             L__M(##Miscellany, 25);
1846             jump ReType;
1847         }
1848
1849         ! The player has now successfully named someone.  Adjust "him", "her", "it":
1850
1851         PronounNotice(l);
1852
1853         ! Set the global variable "actor", adjust the number of the first word,
1854         ! and begin parsing again from there.
1855
1856         verb_wordnum = j + 1;
1857
1858         ! Stop things like "me, again":
1859
1860         if (l == player) {
1861             wn = verb_wordnum;
1862             if (NextWordStopped() == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) {
1863                 L__M(##Miscellany, 20);
1864                 jump ReType;
1865             }
1866         }
1867
1868         actor = l;
1869         actors_location = ScopeCeiling(l);
1870         #Ifdef DEBUG;
1871         if (parser_trace >= 1)
1872             print "[Actor is ", (the) actor, " in ", (name) actors_location, "]^";
1873         #Endif; ! DEBUG
1874         jump BeginCommand;
1875
1876     } ! end of first-word-not-a-verb
1877
1878     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1879     !
1880     ! D: Get the verb: try all the syntax lines for that verb
1881     !
1882     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1883
1884   .VerbAccepted;
1885
1886     ! We now definitely have a verb, not a direction, whether we got here by the
1887     ! "take ..." or "person, take ..." method.  Get the meta flag for this verb:
1888
1889     meta = (verb_word->#dict_par1) & DICT_META;
1890
1891     ! You can't order other people to "full score" for you, and so on...
1892
1893     if (meta && actor ~= player) {
1894         best_etype = VERB_PE;
1895         meta = false;
1896         jump GiveError;
1897     }
1898
1899     ! Now let i be the corresponding verb number, stored in the dictionary entry
1900     ! (in a peculiar 255-n fashion for traditional Infocom reasons)...
1901
1902     i = DictionaryWordToVerbNum(verb_word);
1903
1904     ! ...then look up the i-th entry in the verb table, whose address is at word
1905     ! 7 in the Z-machine (in the header), so as to get the address of the syntax
1906     ! table for the given verb...
1907
1908     #Ifdef TARGET_ZCODE;
1909     syntax = (HDR_STATICMEMORY-->0)-->i;
1910     #Ifnot; ! TARGET_GLULX
1911     syntax = (#grammar_table)-->(i+1);
1912     #Endif; ! TARGET_
1913
1914     ! ...and then see how many lines (ie, different patterns corresponding to the
1915     ! same verb) are stored in the parse table...
1916
1917     num_lines = (syntax->0) - 1;
1918
1919     ! ...and now go through them all, one by one.
1920     ! To prevent pronoun_word 0 being misunderstood,
1921
1922     pronoun_word = NULL; pronoun_obj = NULL;
1923
1924     #Ifdef DEBUG;
1925     if (parser_trace >= 1) print "[Parsing for the verb '", (address) verb_word, "' (", num_lines+1, " lines)]^";
1926     #Endif; ! DEBUG
1927
1928     best_etype = STUCK_PE; nextbest_etype = STUCK_PE;
1929     multiflag = false; saved_oops = 0;
1930
1931     ! "best_etype" is the current failure-to-match error - it is by default
1932     ! the least informative one, "don't understand that sentence".
1933     ! "nextbest_etype" remembers the best alternative to having to ask a
1934     ! scope token for an error message (i.e., the best not counting ASKSCOPE_PE).
1935     ! multiflag is used here to prevent inappropriate MULTI_PE errors
1936     ! in addition to its unrelated duties passing information to action routines
1937
1938     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1939     !
1940     ! E: Break down a syntax line into analysed tokens
1941     !
1942     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1943
1944     line_address = syntax + 1;
1945
1946     for (line=0 : line<=num_lines : line++) {
1947
1948         for (i=0 : i<32 : i++) {
1949             line_token-->i = ENDIT_TOKEN;
1950             line_ttype-->i = ELEMENTARY_TT;
1951             line_tdata-->i = ENDIT_TOKEN;
1952         }
1953
1954         ! Unpack the syntax line from Inform format into three arrays; ensure that
1955         ! the sequence of tokens ends in an ENDIT_TOKEN.
1956
1957         line_address = UnpackGrammarLine(line_address);
1958
1959         #Ifdef DEBUG;
1960         if (parser_trace >= 1) {
1961             if (parser_trace >= 2) new_line;
1962             print "[line ", line; DebugGrammarLine();
1963             print "]^";
1964         }
1965         #Endif; ! DEBUG
1966
1967         ! We aren't in "not holding" or inferring modes, and haven't entered
1968         ! any parameters on the line yet, or any special numbers; the multiple
1969         ! object is still empty.
1970
1971         token_filter = 0;
1972         not_holding = 0;
1973         inferfrom = 0;
1974         parameters = 0;
1975         nsns = 0; special_word = 0; special_number = 0;
1976         multiple_object-->0 = 0;
1977         multi_context = 0;
1978         etype = STUCK_PE; line_etype = 100;
1979
1980         ! Put the word marker back to just after the verb
1981
1982         wn = verb_wordnum+1;
1983
1984     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1985     !
1986     ! F: Look ahead for advance warning for multiexcept/multiinside
1987     !
1988     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1989
1990         ! There are two special cases where parsing a token now has to be
1991         ! affected by the result of parsing another token later, and these
1992         ! two cases (multiexcept and multiinside tokens) are helped by a quick
1993         ! look ahead, to work out the future token now.  We can only carry this
1994         ! out in the simple (but by far the most common) case:
1995         !
1996         !     multiexcept <one or more prepositions> noun
1997         !
1998         ! and similarly for multiinside.
1999
2000         advance_warning = NULL; indef_mode = false;
2001         for (i=0,m=false,pcount=0 : line_token-->pcount ~= ENDIT_TOKEN : pcount++) {
2002             scope_token = 0;
2003
2004             if (line_ttype-->pcount ~= PREPOSITION_TT) i++;
2005
2006             if (line_ttype-->pcount == ELEMENTARY_TT) {
2007                 if (line_tdata-->pcount == MULTI_TOKEN) m = true;
2008                 if (line_tdata-->pcount == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN  && i == 1) {
2009                     ! First non-preposition is "multiexcept" or
2010                     ! "multiinside", so look ahead.
2011
2012                     #Ifdef DEBUG;
2013                     if (parser_trace >= 2) print " [Trying look-ahead]^";
2014                     #Endif; ! DEBUG
2015
2016                     ! We need this to be followed by 1 or more prepositions.
2017
2018                     pcount++;
2019                     if (line_ttype-->pcount == PREPOSITION_TT) {
2020                         ! skip ahead to a preposition word in the input
2021                         do {
2022                             l = NextWord();
2023                         } until ((wn > num_words) ||
2024                                  (l && (l->#dict_par1) & DICT_PREP ~= 0));
2025
2026                         if (wn > num_words) {
2027                             #Ifdef DEBUG;
2028                             if (parser_trace >= 2)
2029                                 print " [Look-ahead aborted: prepositions missing]^";
2030                             #Endif;
2031                             jump EmptyLine;
2032                         }
2033
2034                         do {
2035                             if (PrepositionChain(l, pcount) ~= -1) {
2036                                 ! advance past the chain
2037                                 if ((line_token-->pcount)->0 & $20 ~= 0) {
2038                                     pcount++;
2039                                     while ((line_token-->pcount ~= ENDIT_TOKEN) &&
2040                                            ((line_token-->pcount)->0 & $10 ~= 0))
2041                                         pcount++;
2042                                 } else {
2043                                     pcount++;
2044                                 }
2045                             } else {
2046                                 ! try to find another preposition word
2047                                 do {
2048                                     l = NextWord();
2049                                 } until ((wn >= num_words) ||
2050                                          (l && (l->#dict_par1) & 8 ~= 0));
2051
2052                                 if (l && (l->#dict_par1) & 8) continue;
2053
2054                                 ! lookahead failed
2055                                 #Ifdef DEBUG;
2056                                 if (parser_trace >= 2)
2057                                     print " [Look-ahead aborted: prepositions don't match]^";
2058                                 #Endif;
2059                                 jump LineFailed;
2060                             }
2061                             l = NextWord();
2062                         } until (line_ttype-->pcount ~= PREPOSITION_TT);
2063
2064                         .EmptyLine;
2065                         ! put back the non-preposition we just read
2066                         wn--;
2067
2068                         if ((line_ttype-->pcount == ELEMENTARY_TT) && (line_tdata-->pcount == NOUN_TOKEN)) {
2069                             l = Descriptors();  ! skip past THE etc
2070                             if (l~=0) etype=l;  ! don't allow multiple objects
2071                             l = NounDomain(actors_location, actor, NOUN_TOKEN);
2072
2073                             #Ifdef DEBUG;
2074                             if (parser_trace >= 2) {
2075                                 print " [Advanced to ~noun~ token: ";
2076                                 if (l == REPARSE_CODE) print "re-parse request]^";
2077                                 if (l == 1) print "but multiple found]^";
2078                                 if (l == 0) print "error ", etype, "]^";
2079                                 if (l >= 2) print (the) l, "]^";
2080                             }
2081                             #Endif; ! DEBUG
2082                             if (l == REPARSE_CODE) jump ReParse;
2083                             if (l >= 2) advance_warning = l;
2084                         }
2085                     }
2086                     break;
2087                 }
2088             }
2089         }
2090
2091         ! Slightly different line-parsing rules will apply to "take multi", to
2092         ! prevent "take all" behaving correctly but misleadingly when there's
2093         ! nothing to take.
2094
2095         take_all_rule = 0;
2096         if (m && params_wanted == 1 && action_to_be == ##Take)
2097             take_all_rule = 1;
2098
2099         ! And now start again, properly, forearmed or not as the case may be.
2100         ! As a precaution, we clear all the variables again (they may have been
2101         ! disturbed by the call to NounDomain, which may have called outside
2102         ! code, which may have done anything!).
2103
2104         not_holding = 0;
2105         inferfrom = 0;
2106         inferword = 0;
2107         parameters = 0;
2108         nsns = 0; special_word = 0; special_number = 0;
2109         multiple_object-->0 = 0;
2110         etype = STUCK_PE; line_etype = 100;
2111         wn = verb_wordnum+1;
2112
2113     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2114     !
2115     ! G: Parse each token in turn (calling ParseToken to do most of the work)
2116     !
2117     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2118
2119         ! "Pattern" gradually accumulates what has been recognised so far,
2120         ! so that it may be reprinted by the parser later on
2121
2122         for (pcount=1 : : pcount++) {
2123             pattern-->pcount = PATTERN_NULL; scope_token = 0;
2124
2125             token = line_token-->(pcount-1);
2126             lookahead = line_token-->pcount;
2127
2128             #Ifdef DEBUG;
2129             if (parser_trace >= 2)
2130                 print " [line ", line, " token ", pcount, " word ", wn, " : ", (DebugToken) token,
2131                   "]^";
2132             #Endif; ! DEBUG
2133
2134             if (token ~= ENDIT_TOKEN) {
2135                 scope_reason = PARSING_REASON;
2136                 parser_inflection = name;
2137                 parser_inflection_func = false;
2138                 AnalyseToken(token);
2139
2140                 if (action_to_be == ##AskTo && found_ttype == ELEMENTARY_TT &&
2141                     found_tdata == TOPIC_TOKEN && line_etype == 100) {
2142                     if (actor ~= player) {
2143                         best_etype = VERB_PE; jump GiveError;
2144                     }
2145                     l = inputobjs-->2;
2146                     wn--;
2147                     j = wn;
2148                     jump Conversation2;
2149                 }
2150
2151                 l = ParseToken__(found_ttype, found_tdata, pcount-1, token);
2152                 while (l<-200) l = ParseToken__(ELEMENTARY_TT, l + 256);
2153                 scope_reason = PARSING_REASON;
2154
2155                 if (l == GPR_PREPOSITION) {
2156                     if (found_ttype~=PREPOSITION_TT && (found_ttype~=ELEMENTARY_TT ||
2157                         found_tdata~=TOPIC_TOKEN)) params_wanted--;
2158                     l = true;
2159                 }
2160                 else
2161                     if (l < 0) l = false;
2162                     else
2163                         if (l ~= GPR_REPARSE) {
2164                             if (l == GPR_NUMBER) {
2165                                 if (nsns == 0) special_number1 = parsed_number;
2166                                 else special_number2 = parsed_number;
2167                                 nsns++; l = 1;
2168                             }
2169                             if (l == GPR_MULTIPLE) l = 0;
2170                             results-->(parameters+2) = l;
2171                             parameters++;
2172                             pattern-->pcount = l;
2173                             l = true;
2174                         }
2175
2176                 #Ifdef DEBUG;
2177                 if (parser_trace >= 3) {
2178                     print "  [token resulted in ";
2179                     if (l == REPARSE_CODE) print "re-parse request]^";
2180                     if (l == 0) print "failure with error type ", etype, "]^";
2181                     if (l == 1) print "success]^";
2182                 }
2183                 #Endif; ! DEBUG
2184
2185                 if (l == REPARSE_CODE) jump ReParse;
2186                 if (l == false) {
2187                     if (etype < line_etype) line_etype = etype;
2188                     if (etype == STUCK_PE || wn >= num_words) break;
2189                 }
2190             }
2191             else {
2192
2193                 ! If the player has entered enough already but there's still
2194                 ! text to wade through: store the pattern away so as to be able to produce
2195                 ! a decent error message if this turns out to be the best we ever manage,
2196                 ! and in the mean time give up on this line
2197
2198                 ! However, if the superfluous text begins with a comma or "then" then
2199                 ! take that to be the start of another instruction
2200
2201                 if (line_etype < 100) break;
2202                 if (wn <= num_words) {
2203                     l = NextWord();
2204                     if (l == THEN1__WD or THEN2__WD or THEN3__WD or comma_word or AND1__WD) {
2205                         held_back_mode = 1; hb_wn = wn-1;
2206                     }
2207                     else {
2208                         for (m=0 : m<32 : m++) pattern2-->m = pattern-->m;
2209                         pcount2 = pcount;
2210                         etype = UPTO_PE;
2211                         break;
2212                     }
2213                 }
2214
2215                 ! Now, we may need to revise the multiple object because of the single one
2216                 ! we now know (but didn't when the list was drawn up).
2217
2218                 if (parameters >= 1 && results-->2 == 0) {
2219                     l = ReviseMulti(results-->3);
2220                     if (l ~= 0) { etype = l; results-->0 = action_to_be; break; }
2221                 }
2222                 if (parameters >= 2 && results-->3 == 0) {
2223                     l = ReviseMulti(results-->2);
2224                     if (l ~= 0) { etype = l; break; }
2225                 }
2226
2227                 ! To trap the case of "take all" inferring only "yourself" when absolutely
2228                 ! nothing else is in the vicinity...
2229
2230                 if (take_all_rule == 2 && results-->2 == actor) {
2231                     best_etype = NOTHING_PE;
2232                     jump GiveError;
2233                 }
2234
2235                 #Ifdef DEBUG;
2236                 if (parser_trace >= 1) print "[Line successfully parsed]^";
2237                 #Endif; ! DEBUG
2238
2239                 ! The line has successfully matched the text.  Declare the input error-free...
2240
2241                 oops_from = 0;
2242
2243                 ! ...explain any inferences made (using the pattern)...
2244                 ! This is where the parser replies (the sword) if things aren't clear.
2245                 if (inferfrom ~= 0 && no_infer_message == false) {
2246                     print "("; PrintCommand(inferfrom); print ")^";
2247                 }
2248                 no_infer_message = false;
2249
2250                 ! ...copy the action number, and the number of parameters...
2251
2252                 results-->0 = action_to_be;
2253                 results-->1 = parameters;
2254
2255                 ! ...reverse first and second parameters if need be...
2256
2257                 if (action_reversed && parameters == 2) {
2258                     i = results-->2; results-->2 = results-->3;
2259                     results-->3 = i;
2260                     if (nsns == 2) {
2261                         i = special_number1; special_number1 = special_number2;
2262                         special_number2 = i;
2263                     }
2264                 }
2265
2266                 ! ...and to reset "it"-style objects to the first of these parameters, if
2267                 ! there is one (and it really is an object)...
2268
2269                 if (parameters > 0 && results-->2 >= 2)
2270                     PronounNotice(results-->2);
2271
2272                 ! ...and worry about the case where an object was allowed as a parameter
2273                 ! even though the player wasn't holding it and should have been: in this
2274                 ! event, keep the results for next time round, go into "not holding" mode,
2275                 ! and for now tell the player what's happening and return a "take" request
2276                 ! instead...
2277
2278                 if (not_holding ~= 0 && actor == player) {
2279                     action = ##Take;
2280                     i = RunRoutines(not_holding, before_implicit);
2281                     ! i = 0: Take the object, tell the player (default)
2282                     ! i = 1: Take the object, don't tell the player
2283                     ! i = 2: don't Take the object, continue
2284                     ! i = 3: don't Take the object, don't continue
2285                     if (i > 2 || no_implicit_actions) { best_etype = NOTHELD_PE; jump GiveError; }
2286                     ! perform the implicit Take
2287                     if (i < 2) {
2288                         if (i ~= 1)     ! and tell the player
2289                             L__M(##Miscellany, 26, not_holding);
2290                         notheld_mode = 1;
2291                         for (i=0 : i<8 : i++) kept_results-->i = results-->i;
2292                         results-->0 = ##Take;
2293                         results-->1 = 1;
2294                         results-->2 = not_holding;
2295                     }
2296                 }
2297
2298                 ! (Notice that implicit takes are only generated for the player, and not
2299                 ! for other actors.  This avoids entirely logical, but misleading, text
2300                 ! being printed.)
2301
2302                 ! ...and return from the parser altogether, having successfully matched
2303                 ! a line.
2304
2305                 if (held_back_mode == 1) {
2306                     wn=hb_wn;
2307                     jump LookForMore;
2308                 }
2309                 rtrue;
2310
2311             } ! end of if(token ~= ENDIT_TOKEN) else
2312         } ! end of for(pcount++)
2313
2314         .LineFailed;
2315         ! The line has failed to match.
2316         ! We continue the outer "for" loop, trying the next line in the grammar.
2317
2318         if (line_etype < 100) etype = line_etype;
2319         if (etype > best_etype) best_etype = etype;
2320         if (etype ~= ASKSCOPE_PE && etype > nextbest_etype) nextbest_etype = etype;
2321
2322         ! ...unless the line was something like "take all" which failed because
2323         ! nothing matched the "all", in which case we stop and give an error now.
2324
2325         if (take_all_rule == 2 && etype==NOTHING_PE) break;
2326
2327     } ! end of for(line++)
2328
2329     ! The grammar is exhausted: every line has failed to match.
2330
2331     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2332     !
2333     ! H: Cheaply parse otherwise unrecognised conversation and return
2334     !
2335     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2336
2337   .GiveError;
2338
2339     etype = best_etype;
2340
2341     ! Errors are handled differently depending on who was talking.
2342     ! If the command was addressed to somebody else (eg, "dwarf, sfgh") then
2343     ! it is taken as conversation which the parser has no business in disallowing.
2344
2345     if (actor ~= player) {
2346         if (usual_grammar_after ~= 0) {
2347             verb_wordnum = usual_grammar_after;
2348             jump AlmostReParse;
2349         }
2350         wn = verb_wordnum;
2351         special_word = NextWord();
2352         if (special_word == comma_word) {
2353             special_word = NextWord();
2354             verb_wordnum++;
2355         }
2356         special_number = TryNumber(verb_wordnum);
2357         results-->0 = ##NotUnderstood;
2358         results-->1 = 2;
2359         results-->2 = 1; special_number1 = special_word;
2360         results-->3 = actor;
2361         consult_from = verb_wordnum; consult_words = num_words-consult_from+1;
2362         rtrue;
2363     }
2364
2365     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2366     !
2367     ! I: Print best possible error message
2368     !
2369     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2370
2371     ! If the player was the actor (eg, in "take dfghh") the error must be
2372     ! printed, and fresh input called for.  In four cases the oops word
2373     ! must be jiggled (where oops_from is set to something).
2374
2375     if (ParserError(etype)) jump ReType;
2376     if (LibraryExtensions.RunWhile(ext_parsererror, false, etype)) jump ReType;
2377     pronoun_word = pronoun__word; pronoun_obj = pronoun__obj;
2378
2379     if (etype == STUCK_PE) {    L__M(##Miscellany, 27); oops_from = 1; }
2380     if (etype == UPTO_PE) {     L__M(##Miscellany, 28);
2381         for (m=0 : m<32 : m++) pattern-->m = pattern2-->m;
2382         pcount = pcount2; PrintCommand(0); L__M(##Miscellany, 56);
2383         oops_from = wn-1;
2384     }
2385     if (etype == NUMBER_PE)     L__M(##Miscellany, 29);
2386     if (etype == CANTSEE_PE) {  L__M(##Miscellany, 30); oops_from=saved_oops;}
2387     if (etype == TOOLIT_PE)     L__M(##Miscellany, 31);
2388     if (etype == NOTHELD_PE) {  L__M(##Miscellany, 32, not_holding); oops_from=saved_oops; }
2389     if (etype == MULTI_PE)      L__M(##Miscellany, 33);
2390     if (etype == MMULTI_PE)     L__M(##Miscellany, 34);
2391     if (etype == VAGUE_PE)      L__M(##Miscellany, 35, pronoun_word);
2392     if (etype == EXCEPT_PE)     L__M(##Miscellany, 36);
2393     if (etype == ANIMA_PE)      L__M(##Miscellany, 37);
2394     if (etype == VERB_PE)       L__M(##Miscellany, 38);
2395     if (etype == SCENERY_PE)    L__M(##Miscellany, 39);
2396     if (etype == ITGONE_PE) {
2397         if (pronoun_obj == NULL)
2398                                 L__M(##Miscellany, 35, pronoun_word);
2399         else                    L__M(##Miscellany, 40, pronoun_word, pronoun_obj);
2400     }
2401     if (etype == JUNKAFTER_PE)  L__M(##Miscellany, 41);
2402     if (etype == TOOFEW_PE)     L__M(##Miscellany, 42, multi_had);
2403     if (etype == NOTHING_PE) {
2404         if (results-->0 == ##Remove && results-->3 ofclass Object) {
2405             noun = results-->3; ! ensure valid for messages
2406             if (noun has animate) L__M(##Miscellany, 44, verb_word);
2407             else if (noun hasnt container or supporter) L__M(##Insert, 2, noun);
2408             else if (noun has container && noun hasnt open) L__M(##Take, 9, noun);
2409             else if (children(noun)==0) L__M(##Search, 6, noun);
2410             else results-->0 = 0;
2411         }
2412         if (results-->0 ~= ##Remove) {
2413             if (multi_wanted == 100)    L__M(##Miscellany, 43);
2414             else {
2415                 #Ifdef NO_TAKE_ALL;
2416                 if (take_all_rule == 2) L__M(##Miscellany, 59);
2417                 else                    L__M(##Miscellany, 44, verb_word);
2418                 #Ifnot;
2419                 L__M(##Miscellany, 44, verb_word);
2420                 #Endif; ! NO_TAKE_ALL
2421             }
2422         }
2423     }
2424     if (etype == ASKSCOPE_PE) {
2425         scope_stage = 3;
2426         if (scope_error() == -1) {
2427             best_etype = nextbest_etype;
2428             jump GiveError;
2429         }
2430     }
2431
2432     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2433     !
2434     ! J: Retry the whole lot
2435     !
2436     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2437
2438     ! And go (almost) right back to square one...
2439
2440     jump ReType;
2441
2442     ! ...being careful not to go all the way back, to avoid infinite repetition
2443     ! of a deferred command causing an error.
2444
2445
2446     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2447     !
2448     ! K: Last thing: check for "then" and further instructions(s), return.
2449     !
2450     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2451
2452     ! At this point, the return value is all prepared, and we are only looking
2453     ! to see if there is a "then" followed by subsequent instruction(s).
2454
2455   .LookForMore;
2456
2457     if (wn > num_words) rtrue;
2458
2459     i = NextWord();
2460     if (i == THEN1__WD or THEN2__WD or THEN3__WD or comma_word or AND1__WD) {
2461         if (wn > num_words) {
2462            held_back_mode = false;
2463            return;
2464         }
2465         i = WordAddress(verb_wordnum);
2466         j = WordAddress(wn);
2467         for (: i<j : i++) i->0 = ' ';
2468         i = NextWord();
2469         if (i == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) {
2470             ! Delete the words "then again" from the again buffer,
2471             ! in which we have just realised that it must occur:
2472             ! prevents an infinite loop on "i. again"
2473
2474             i = WordAddress(wn-2)-buffer;
2475             if (wn > num_words) j = INPUT_BUFFER_LEN-1;
2476             else j = WordAddress(wn)-buffer;
2477             for (: i<j : i++) buffer3->i = ' ';
2478         }
2479         Tokenise__(buffer,parse);
2480         held_back_mode = true;
2481         return;
2482     }
2483     best_etype = UPTO_PE;
2484     jump GiveError;
2485
2486 ]; ! end of Parser__parse
2487
2488 [ ScopeCeiling person act;
2489   act = parent(person);
2490   if (act == 0) return person;
2491   if (person == player && location == thedark) return thedark;
2492   while (parent(act)~=0 && (act has transparent || act has supporter ||
2493                            (act has container && act has open)))
2494       act = parent(act);
2495   return act;
2496 ];
2497
2498 ! ----------------------------------------------------------------------------
2499 !  Descriptors()
2500 !
2501 !  Handles descriptive words like "my", "his", "another" and so on.
2502 !  Skips "the", and leaves wn pointing to the first misunderstood word.
2503 !
2504 !  Allowed to set up for a plural only if allow_p is set
2505 !
2506 !  Returns error number, or 0 if no error occurred
2507 ! ----------------------------------------------------------------------------
2508
2509 Constant OTHER_BIT  =   1;     !  These will be used in Adjudicate()
2510 Constant MY_BIT     =   2;     !  to disambiguate choices
2511 Constant THAT_BIT   =   4;
2512 Constant PLURAL_BIT =   8;
2513 Constant LIT_BIT    =  16;
2514 Constant UNLIT_BIT  =  32;
2515
2516 [ ResetDescriptors;
2517     indef_mode = 0; indef_type = 0; indef_wanted = 0; indef_guess_p = 0;
2518     indef_possambig = false;
2519     indef_owner = nothing;
2520     indef_cases = $$111111111111;
2521     indef_nspec_at = 0;
2522 ];
2523
2524 [ Descriptors  allows_multiple o x y flag cto type m n;
2525     ResetDescriptors();
2526     if (wn > num_words) return 0;
2527     m = wn;
2528     for (flag=true : flag :) {
2529         o = NextWordStopped(); flag = false;
2530        for (x=1 : x<=LanguageDescriptors-->0 : x=x+4) {
2531             if (o == LanguageDescriptors-->x) {
2532                 ! Attempt to compensate for her-her confusion.
2533                 for (y = 1 : y<=LanguagePronouns-->0 : y=y+2) {
2534                     if (o == LanguagePronouns-->y)
2535                         jump PersonalPronoun;
2536                 }
2537                 flag = true;
2538                 type = LanguageDescriptors-->(x+2);
2539                 if (type ~= DEFART_PK) indef_mode = true;
2540                 indef_possambig = true;
2541                 indef_cases = indef_cases & (LanguageDescriptors-->(x+1));
2542                 if (type == POSSESS_PK) {
2543                     cto = LanguageDescriptors-->(x+3);
2544                     switch (cto) {
2545                       0: indef_type = indef_type | MY_BIT;
2546                       1: indef_type = indef_type | THAT_BIT;
2547                       default:
2548                         indef_owner = PronounValue(cto);
2549                         if (indef_owner == NULL) indef_owner = InformParser;
2550                     }
2551                 }
2552                 if (type == light)  indef_type = indef_type | LIT_BIT;
2553                 if (type == -light) indef_type = indef_type | UNLIT_BIT;
2554             }
2555         }
2556         .PersonalPronoun;
2557
2558         if (o == OTHER1__WD or OTHER2__WD or OTHER3__WD) {
2559             indef_mode = 1; flag = 1;
2560             indef_type = indef_type | OTHER_BIT;
2561         }
2562         if (o == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) {
2563             indef_mode = 1; flag = 1; indef_wanted = 100;
2564             if (take_all_rule == 1) take_all_rule = 2;
2565             indef_type = indef_type | PLURAL_BIT;
2566         }
2567         if (allow_plurals && allows_multiple) {
2568             n = TryNumber(wn-1);
2569             if (n == 1) { indef_mode = 1; flag = 1; indef_wanted = 1; }
2570             if (n > 1) {
2571                 indef_guess_p = 1;
2572                 indef_mode = 1; flag = 1; indef_wanted = n;
2573                 indef_nspec_at = wn-1;
2574                 indef_type = indef_type | PLURAL_BIT;
2575             }
2576         }
2577         if (flag == 1 && NextWordStopped() ~= OF1__WD or OF2__WD or OF3__WD or OF4__WD)
2578             wn--;  ! Skip 'of' after these
2579     }
2580     wn--;
2581     num_desc = wn - m;
2582     return 0;
2583 ];
2584
2585 ! ----------------------------------------------------------------------------
2586 !  CreatureTest: Will this person do for a "creature" token?
2587 ! ----------------------------------------------------------------------------
2588
2589 [ CreatureTest obj;
2590     if (actor ~= player) rtrue;
2591     if (obj has animate) rtrue;
2592     if (obj hasnt talkable) rfalse;
2593     if (action_to_be == ##Ask or ##Answer or ##Tell or ##AskFor or ##AskTo) rtrue;
2594     rfalse;
2595 ];
2596
2597 [ PrepositionChain wd index;
2598     if (line_tdata-->index == wd) return wd;
2599     if ((line_token-->index)->0 & $20 == 0) return -1;
2600     do {
2601         if (line_tdata-->index == wd) return wd;
2602         index++;
2603     } until ((line_token-->index == ENDIT_TOKEN) || (((line_token-->index)->0 & $10) == 0));
2604     return -1;
2605 ];
2606
2607 ! ----------------------------------------------------------------------------
2608 !  ParseToken(type, data):
2609 !      Parses the given token, from the current word number wn, with exactly
2610 !      the specification of a general parsing routine.
2611 !      (Except that for "topic" tokens and prepositions, you need to supply
2612 !      a position in a valid grammar line as third argument.)
2613 !
2614 !  Returns:
2615 !    GPR_REPARSE  for "reconstructed input, please re-parse from scratch"
2616 !    GPR_PREPOSITION  for "token accepted with no result"
2617 !    $ff00 + x    for "please parse ParseToken(ELEMENTARY_TT, x) instead"
2618 !    0            for "token accepted, result is the multiple object list"
2619 !    1            for "token accepted, result is the number in parsed_number"
2620 !    object num   for "token accepted with this object as result"
2621 !    -1           for "token rejected"
2622 !
2623 !  (A)            Analyse the token; handle all tokens not involving
2624 !                 object lists and break down others into elementary tokens
2625 !  (B)            Begin parsing an object list
2626 !  (C)            Parse descriptors (articles, pronouns, etc.) in the list
2627 !  (D)            Parse an object name
2628 !  (E)            Parse connectives ("and", "but", etc.) and go back to (C)
2629 !  (F)            Return the conclusion of parsing an object list
2630 ! ----------------------------------------------------------------------------
2631
2632 [ ParseToken given_ttype given_tdata token_n x y;
2633     x = lookahead; lookahead = NOUN_TOKEN;
2634     y = ParseToken__(given_ttype,given_tdata,token_n);
2635     if (y == GPR_REPARSE) Tokenise__(buffer,parse);
2636     lookahead = x; return y;
2637 ];
2638
2639 [ ParseToken__ given_ttype given_tdata token_n
2640              token l o i j k and_parity single_object desc_wn many_flag
2641              token_allows_multiple prev_indef_wanted;
2642
2643     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2644     !
2645     ! A: Analyse token; handle all not involving object lists, break down others
2646     !
2647     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2648
2649     token_filter = 0;
2650
2651     switch (given_ttype) {
2652       ELEMENTARY_TT:
2653         switch (given_tdata) {
2654           SPECIAL_TOKEN:
2655             l = TryNumber(wn);
2656             special_word = NextWord();
2657             #Ifdef DEBUG;
2658             if (l ~= -1000)
2659                 if (parser_trace >= 3) print "  [Read special as the number ", l, "]^";
2660             #Endif; ! DEBUG
2661             if (l == -1000) {
2662                 #Ifdef DEBUG;
2663                 if (parser_trace >= 3) print "  [Read special word at word number ", wn, "]^";
2664                 #Endif; ! DEBUG
2665                 l = special_word;
2666             }
2667             parsed_number = l; return GPR_NUMBER;
2668
2669           NUMBER_TOKEN:
2670             l=TryNumber(wn++);
2671             if (l == -1000) { etype = NUMBER_PE; return GPR_FAIL; }
2672             #Ifdef DEBUG;
2673             if (parser_trace>=3) print "  [Read number as ", l, "]^";
2674             #Endif; ! DEBUG
2675             parsed_number = l; return GPR_NUMBER;
2676
2677           CREATURE_TOKEN:
2678             if (action_to_be == ##Answer or ##Ask or ##AskFor or ##AskTo or ##Tell)
2679                 scope_reason = TALKING_REASON;
2680
2681           TOPIC_TOKEN:
2682             consult_from = wn;
2683             if ((line_ttype-->(token_n+1) ~= PREPOSITION_TT) &&
2684                (line_token-->(token_n+1) ~= ENDIT_TOKEN))
2685                 RunTimeError(13);
2686             do o = NextWordStopped();
2687             until (o == -1 || PrepositionChain(o, token_n+1) ~= -1);
2688             wn--;
2689             consult_words = wn-consult_from;
2690             if (consult_words == 0) return GPR_FAIL;
2691             if (action_to_be == ##Ask or ##Answer or ##Tell) {
2692                 o = wn; wn = consult_from; parsed_number = NextWord();
2693                 #Ifdef EnglishNaturalLanguage;
2694                 if (parsed_number == 'the' && consult_words > 1) parsed_number=NextWord();
2695                 #Endif; ! EnglishNaturalLanguage
2696                 wn = o; return 1;
2697             }
2698             if (o==-1 && (line_ttype-->(token_n+1) == PREPOSITION_TT))
2699                 return GPR_FAIL;    ! don't infer if required preposition is absent
2700             return GPR_PREPOSITION;
2701         }
2702
2703       PREPOSITION_TT:
2704         #Iffalse (Grammar__Version == 1);
2705         ! Is it an unnecessary alternative preposition, when a previous choice
2706         ! has already been matched?
2707         if ((token->0) & $10) return GPR_PREPOSITION;
2708         #Endif; ! Grammar__Version
2709
2710         ! If we've run out of the player's input, but still have parameters to
2711         ! specify, we go into "infer" mode, remembering where we are and the
2712         ! preposition we are inferring...
2713
2714         if (wn > num_words) {
2715             if (inferfrom==0 && parameters<params_wanted) {
2716                 inferfrom = pcount; inferword = token;
2717                 pattern-->pcount = REPARSE_CODE + Dword__No(given_tdata);
2718             }
2719
2720             ! If we are not inferring, then the line is wrong...
2721
2722             if (inferfrom == 0) return -1;
2723
2724             ! If not, then the line is right but we mark in the preposition...
2725
2726             pattern-->pcount = REPARSE_CODE + Dword__No(given_tdata);
2727             return GPR_PREPOSITION;
2728         }
2729
2730         o = NextWord();
2731
2732         pattern-->pcount = REPARSE_CODE + Dword__No(o);
2733
2734         ! Whereas, if the player has typed something here, see if it is the
2735         ! required preposition... if it's wrong, the line must be wrong,
2736         ! but if it's right, the token is passed (jump to finish this token).
2737
2738         if (o == given_tdata) return GPR_PREPOSITION;
2739         #Iffalse (Grammar__Version == 1);
2740         if (PrepositionChain(o, token_n) ~= -1) return GPR_PREPOSITION;
2741         #Endif; ! Grammar__Version
2742         return -1;
2743
2744       GPR_TT:
2745         l = given_tdata();
2746         #Ifdef DEBUG;
2747         if (parser_trace >= 3) print "  [Outside parsing routine returned ", l, "]^";
2748         #Endif; ! DEBUG
2749         return l;
2750
2751       SCOPE_TT:
2752         scope_token = given_tdata;
2753         scope_stage = 1;
2754         l = scope_token();
2755         #Ifdef DEBUG;
2756         if (parser_trace >= 3) print "  [Scope routine returned multiple-flag of ", l, "]^";
2757         #Endif; ! DEBUG
2758         if (l == 1) given_tdata = MULTI_TOKEN; else given_tdata = NOUN_TOKEN;
2759
2760       ATTR_FILTER_TT:
2761         token_filter = 1 + given_tdata;
2762         given_tdata = NOUN_TOKEN;
2763
2764       ROUTINE_FILTER_TT:
2765         token_filter = given_tdata;
2766         given_tdata = NOUN_TOKEN;
2767
2768     } ! end of switch(given_ttype)
2769
2770     token = given_tdata;
2771
2772     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2773     !
2774     ! B: Begin parsing an object list
2775     !
2776     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2777
2778     ! There are now three possible ways we can be here:
2779     !     parsing an elementary token other than "special" or "number";
2780     !     parsing a scope token;
2781     !     parsing a noun-filter token (either by routine or attribute).
2782     !
2783     ! In each case, token holds the type of elementary parse to
2784     ! perform in matching one or more objects, and
2785     ! token_filter is 0 (default), an attribute + 1 for an attribute filter
2786     ! or a routine address for a routine filter.
2787
2788     token_allows_multiple = false;
2789     if (token == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN)
2790         token_allows_multiple = true;
2791
2792     many_flag = false; and_parity = true; dont_infer = false;
2793
2794     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2795     !
2796     ! C: Parse descriptors (articles, pronouns, etc.) in the list
2797     !
2798     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2799
2800     ! We expect to find a list of objects next in what the player's typed.
2801
2802   .ObjectList;
2803
2804     #Ifdef DEBUG;
2805     if (parser_trace >= 3) print "  [Object list from word ", wn, "]^";
2806     #Endif; ! DEBUG
2807
2808     ! Take an advance look at the next word: if it's "it" or "them", and these
2809     ! are unset, set the appropriate error number and give up on the line
2810     ! (if not, these are still parsed in the usual way - it is not assumed
2811     ! that they still refer to something in scope)
2812
2813     o = NextWord(); wn--;
2814
2815     pronoun_word = NULL; pronoun_obj = NULL;
2816     l = PronounValue(o);
2817     if (l ~= 0) {
2818         pronoun_word = o; pronoun_obj = l;
2819         if (l == NULL) {
2820             ! Don't assume this is a use of an unset pronoun until the
2821             ! descriptors have been checked, because it might be an
2822             ! article (or some such) instead
2823
2824             for (l=1 : l<=LanguageDescriptors-->0 : l=l+4)
2825                 if (o == LanguageDescriptors-->l) jump AssumeDescriptor;
2826             pronoun__word = pronoun_word; pronoun__obj = pronoun_obj;
2827             etype = VAGUE_PE; return GPR_FAIL;
2828         }
2829     }
2830
2831   .AssumeDescriptor;
2832
2833     if (o == ME1__WD or ME2__WD or ME3__WD) { pronoun_word = o; pronoun_obj = player; }
2834
2835     allow_plurals = true;
2836     desc_wn = wn;
2837
2838   .TryAgain;
2839
2840     ! First, we parse any descriptive words (like "the", "five" or "every"):
2841     l = Descriptors(token_allows_multiple);
2842     if (l ~= 0) { etype = l; return GPR_FAIL; }
2843
2844   .TryAgain2;
2845
2846     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2847     !
2848     ! D: Parse an object name
2849     !
2850     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2851
2852     ! This is an actual specified object, and is therefore where a typing error
2853     ! is most likely to occur, so we set:
2854
2855     oops_from = wn;
2856
2857     ! So, two cases.  Case 1: token not equal to "held"
2858     ! but we may well be dealing with multiple objects
2859
2860     ! In either case below we use NounDomain, giving it the token number as
2861     ! context, and two places to look: among the actor's possessions, and in the
2862     ! present location.  (Note that the order depends on which is likeliest.)
2863     if (token ~= HELD_TOKEN) {
2864         i = multiple_object-->0;
2865         #Ifdef DEBUG;
2866         if (parser_trace >= 3) print "  [Calling NounDomain on location and actor]^";
2867         #Endif; ! DEBUG
2868         l = NounDomain(actors_location, actor, token);
2869         if (l == REPARSE_CODE) return l;                  ! Reparse after Q&A
2870
2871         if (indef_wanted == 100 && l == 0 && number_matched == 0)
2872             l = 1;  ! ReviseMulti if TAKE ALL FROM empty container
2873
2874         if (token_allows_multiple && ~~multiflag) {
2875             if (best_etype==MULTI_PE) best_etype=STUCK_PE;
2876             multiflag = true;
2877         }
2878         if (l == 0) {
2879             if (indef_possambig) {
2880                 saved_ml = match_length;
2881                 ResetDescriptors();
2882                 wn = desc_wn;
2883                 jump TryAgain2;
2884             }
2885             if ((etype ~=TOOFEW_PE && etype ~= VAGUE_PE) && (multiflag || etype ~= MULTI_PE))
2886                 etype = CantSee();
2887             jump FailToken;
2888         } ! Choose best error
2889
2890         #Ifdef DEBUG;
2891         if (parser_trace >= 3) {
2892             if (l > 1) print "  [NounDomain returned ", (the) l, "]^";
2893             else {
2894                 print "  [NounDomain appended to the multiple object list:^";
2895                 k = multiple_object-->0;
2896                 for (j=i+1 : j<=k : j++)
2897                     print "  Entry ", j, ": ", (The) multiple_object-->j,
2898                           " (", multiple_object-->j, ")^";
2899                 print "  List now has size ", k, "]^";
2900             }
2901         }
2902         #Endif; ! DEBUG
2903
2904         if (l == 1) {
2905             if (~~many_flag) many_flag = true;
2906             else {                                ! Merge with earlier ones
2907                 k = multiple_object-->0;            ! (with either parity)
2908                 multiple_object-->0 = i;
2909                 for (j=i+1 : j<=k : j++) {
2910                     if (and_parity) MultiAdd(multiple_object-->j);
2911                     else            MultiSub(multiple_object-->j);
2912                 }
2913                 #Ifdef DEBUG;
2914                 if (parser_trace >= 3) print "  [Merging ", k-i, " new objects to the ", i, " old ones]^";
2915                 #Endif; ! DEBUG
2916             }
2917         }
2918         else {
2919             ! A single object was indeed found
2920
2921             if (match_length == 0 && indef_possambig) {
2922                 ! So the answer had to be inferred from no textual data,
2923                 ! and we know that there was an ambiguity in the descriptor
2924                 ! stage (such as a word which could be a pronoun being
2925                 ! parsed as an article or possessive).  It's worth having
2926                 ! another go.
2927
2928                 ResetDescriptors();
2929                 wn = desc_wn;
2930                 jump TryAgain2;
2931             }
2932
2933             if (token == CREATURE_TOKEN && CreatureTest(l) == 0) {
2934                 etype = ANIMA_PE;
2935                 jump FailToken;
2936             } !  Animation is required
2937
2938             if (~~many_flag) single_object = l;
2939             else {
2940                 if (and_parity) MultiAdd(l); else MultiSub(l);
2941                 #Ifdef DEBUG;
2942                 if (parser_trace >= 3) print "  [Combining ", (the) l, " with list]^";
2943                 #Endif; ! DEBUG
2944             }
2945         }
2946
2947     } else {
2948     ! Case 2: token is "held" (which fortunately can't take multiple objects)
2949     ! and may generate an implicit take
2950         l = NounDomain(actor,actors_location,token);       ! Same as above...
2951         if (l == REPARSE_CODE) return GPR_REPARSE;
2952         if (l == 0) {
2953             if (indef_possambig) {
2954                 ResetDescriptors();
2955                 wn = desc_wn;
2956                 jump TryAgain2;
2957             }
2958             etype = CantSee(); jump FailToken;            ! Choose best error
2959         }
2960
2961         ! ...until it produces something not held by the actor.  Then an implicit
2962         ! take must be tried.  If this is already happening anyway, things are too
2963         ! confused and we have to give up (but saving the oops marker so as to get
2964         ! it on the right word afterwards).
2965         ! The point of this last rule is that a sequence like
2966         !
2967         !     > read newspaper
2968         !     (taking the newspaper first)
2969         !     The dwarf unexpectedly prevents you from taking the newspaper!
2970         !
2971         ! should not be allowed to go into an infinite repeat - read becomes
2972         ! take then read, but take has no effect, so read becomes take then read...
2973         ! Anyway for now all we do is record the number of the object to take.
2974
2975         o = parent(l);
2976
2977         if (o ~= actor) {
2978             if (notheld_mode == 1) {
2979                 saved_oops = oops_from;
2980                 etype = NOTHELD_PE;
2981                 jump FailToken;
2982             }
2983             not_holding = l;
2984             #Ifdef DEBUG;
2985             if (parser_trace >= 3) print "  [Allowing object ", (the) l, " for now]^";
2986             #Endif; ! DEBUG
2987         }
2988         single_object = l;
2989     } ! end of if (token ~= HELD_TOKEN) else
2990
2991     ! The following moves the word marker to just past the named object...
2992
2993     wn = oops_from + match_length;
2994
2995     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2996     !
2997     ! E: Parse connectives ("and", "but", etc.) and go back to (C)
2998     !
2999     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3000
3001     ! Object(s) specified now: is that the end of the list, or have we reached
3002     ! "and", "but" and so on?  If so, create a multiple-object list if we
3003     ! haven't already (and are allowed to).
3004
3005   .NextInList;
3006
3007     o = NextWord();
3008
3009     if (o == AND1__WD or AND2__WD or AND3__WD or BUT1__WD or BUT2__WD or BUT3__WD or comma_word) {
3010
3011         #Ifdef DEBUG;
3012         if (parser_trace >= 3) print "  [Read connective '", (address) o, "']^";
3013         #Endif; ! DEBUG
3014
3015         k = NextWord();
3016         if (k ~= AND1__WD) wn--;  ! allow Serial commas in input
3017         if (k > 0 && (k->#dict_par1) & (DICT_NOUN+DICT_VERB) == DICT_VERB) {
3018             wn--; ! player meant 'THEN'
3019             jump PassToken;
3020         }
3021         if (~~token_allows_multiple) {
3022             if (multiflag) jump PassToken; ! give UPTO_PE error
3023             etype=MULTI_PE;
3024             jump FailToken;
3025         }
3026
3027         if (o == BUT1__WD or BUT2__WD or BUT3__WD) and_parity = 1-and_parity;
3028
3029         if (~~many_flag) {
3030             multiple_object-->0 = 1;
3031             multiple_object-->1 = single_object;
3032             many_flag = true;
3033             #Ifdef DEBUG;
3034             if (parser_trace >= 3) print "  [Making new list from ", (the) single_object, "]^";
3035             #Endif; ! DEBUG
3036         }
3037         dont_infer = true; inferfrom=0;           ! Don't print (inferences)
3038         jump ObjectList;                          ! And back around
3039     }
3040
3041     wn--;   ! Word marker back to first not-understood word
3042
3043     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3044     !
3045     ! F: Return the conclusion of parsing an object list
3046     !
3047     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3048
3049     ! Happy or unhappy endings:
3050
3051   .PassToken;
3052
3053     if (many_flag) {
3054         single_object = GPR_MULTIPLE;
3055         multi_context = token;
3056     }
3057     else {
3058         if (indef_mode == 1 && indef_type & PLURAL_BIT ~= 0) {
3059             if (indef_wanted < 100 && indef_wanted > 1) {
3060                 multi_had = 1; multi_wanted = indef_wanted;
3061                 etype = TOOFEW_PE;
3062                 jump FailToken;
3063             }
3064         }
3065     }
3066     return single_object;
3067
3068   .FailToken;
3069
3070     ! If we were only guessing about it being a plural, try again but only
3071     ! allowing singulars (so that words like "six" are not swallowed up as
3072     ! Descriptors)
3073
3074     if (allow_plurals && indef_guess_p == 1) {
3075         #Ifdef DEBUG;
3076         if (parser_trace >= 4) print "   [Retrying singulars after failure ", etype, "]^";
3077         #Endif;
3078         prev_indef_wanted = indef_wanted;
3079         allow_plurals = false;
3080         wn = desc_wn;
3081         jump TryAgain;
3082     }
3083
3084     if ((indef_wanted > 0 || prev_indef_wanted > 0) && (~~multiflag)) etype = MULTI_PE;
3085
3086     return GPR_FAIL;
3087
3088 ]; ! end of ParseToken__
3089
3090 ! ----------------------------------------------------------------------------
3091 !  NounDomain does the most substantial part of parsing an object name.
3092 !
3093 !  It is given two "domains" - usually a location and then the actor who is
3094 !  looking - and a context (i.e. token type), and returns:
3095 !
3096 !   0    if no match at all could be made,
3097 !   1    if a multiple object was made,
3098 !   k    if object k was the one decided upon,
3099 !   REPARSE_CODE if it asked a question of the player and consequently rewrote
3100 !        the player's input, so that the whole parser should start again
3101 !        on the rewritten input.
3102 !
3103 !   In the case when it returns 1<k<REPARSE_CODE, it also sets the variable
3104 !   length_of_noun to the number of words in the input text matched to the
3105 !   noun.
3106 !   In the case k=1, the multiple objects are added to multiple_object by
3107 !   hand (not by MultiAdd, because we want to allow duplicates).
3108 ! ----------------------------------------------------------------------------
3109
3110 [ NounDomain domain1 domain2 context    first_word i j k l
3111                                         answer_words;
3112     #Ifdef DEBUG;
3113     if (parser_trace >= 4) {
3114         print "   [NounDomain called at word ", wn, "]^";
3115         print "   ";
3116         if (indef_mode) {
3117             print "seeking indefinite object: ";
3118             if (indef_type & OTHER_BIT)  print "other ";
3119             if (indef_type & MY_BIT)     print "my ";
3120             if (indef_type & THAT_BIT)   print "that ";
3121             if (indef_type & PLURAL_BIT) print "plural ";
3122             if (indef_type & LIT_BIT)    print "lit ";
3123             if (indef_type & UNLIT_BIT)  print "unlit ";
3124             if (indef_owner ~= 0) print "owner:", (name) indef_owner;
3125             new_line;
3126             print "   number wanted: ";
3127             if (indef_wanted == 100) print "all"; else print indef_wanted;
3128             new_line;
3129             print "   most likely GNAs of names: ", indef_cases, "^";
3130         }
3131         else print "seeking definite object^";
3132     }
3133     #Endif; ! DEBUG
3134
3135     match_length = 0; number_matched = 0; match_from = wn; placed_in_flag = 0;
3136
3137     SearchScope(domain1, domain2, context);
3138
3139     #Ifdef DEBUG;
3140     if (parser_trace >= 4) print "   [NounDomain made ", number_matched, " matches]^";
3141     #Endif; ! DEBUG
3142
3143     wn = match_from+match_length;
3144
3145     ! If nothing worked at all, leave with the word marker skipped past the
3146     ! first unmatched word...
3147
3148     if (number_matched == 0) { wn++; rfalse; }
3149
3150     ! Suppose that there really were some words being parsed (i.e., we did
3151     ! not just infer).  If so, and if there was only one match, it must be
3152     ! right and we return it...
3153     if (match_from <= num_words) {
3154         if (number_matched == 1) {
3155             i=match_list-->0;
3156             if (indef_mode) {
3157                 if ((indef_type & LIT_BIT) && i hasnt light) rfalse;
3158                 if ((indef_type & UNLIT_BIT) && i has light) rfalse;
3159             }
3160             return i;
3161         }
3162
3163         ! ...now suppose that there was more typing to come, i.e. suppose that
3164         ! the user entered something beyond this noun.  If nothing ought to follow,
3165         ! then there must be a mistake, (unless what does follow is just a full
3166         ! stop, and or comma)
3167
3168         if (wn <= num_words) {
3169             i = NextWord(); wn--;
3170             if (i ~=  AND1__WD or AND2__WD or AND3__WD or comma_word
3171                    or THEN1__WD or THEN2__WD or THEN3__WD
3172                    or BUT1__WD or BUT2__WD or BUT3__WD) {
3173                 if (lookahead == ENDIT_TOKEN) rfalse;
3174             }
3175         }
3176     }
3177
3178     ! Now look for a good choice, if there's more than one choice...
3179
3180     number_of_classes = 0;
3181
3182     if (match_length == 0 && indef_mode && indef_wanted > 0 && indef_wanted < 100)
3183         number_matched = 0;  ! ask question for 'take three'
3184     if (number_matched == 1) i = match_list-->0;
3185     if (number_matched > 1) {
3186         i = Adjudicate(context);
3187         if (i == -1) rfalse;
3188         if (i == 1) rtrue;       !  Adjudicate has made a multiple
3189                                  !  object, and we pass it on
3190     }
3191
3192     ! If i is non-zero here, one of two things is happening: either
3193     ! (a) an inference has been successfully made that object i is
3194     !     the intended one from the user's specification, or
3195     ! (b) the user finished typing some time ago, but we've decided
3196     !     on i because it's the only possible choice.
3197     ! In either case we have to keep the pattern up to date,
3198     ! note that an inference has been made and return.
3199     ! (Except, we don't note which of a pile of identical objects.)
3200
3201     if (i ~= 0) {
3202         if (dont_infer) return i;
3203         if (inferfrom == 0) inferfrom=pcount;
3204         pattern-->pcount = i;
3205         return i;
3206     }
3207
3208     ! If we get here, there was no obvious choice of object to make.  If in
3209     ! fact we've already gone past the end of the player's typing (which
3210     ! means the match list must contain every object in scope, regardless
3211     ! of its name), then it's foolish to give an enormous list to choose
3212     ! from - instead we go and ask a more suitable question...
3213
3214     if (match_from > num_words) jump Incomplete;
3215     return AskPlayer(context);
3216
3217     ! Now we come to the question asked when the input has run out
3218     ! and can't easily be guessed (eg, the player typed "take" and there
3219     ! were plenty of things which might have been meant).
3220
3221   .Incomplete;
3222
3223     if (best_etype == NOTHING_PE && pattern-->1 == 0) rfalse; ! for DROP when empty-handed
3224     if (context == CREATURE_TOKEN) L__M(##Miscellany, 48, actor);
3225     else                           L__M(##Miscellany, 49, actor);
3226
3227     #Ifdef TARGET_ZCODE;
3228     for (i=2 : i<INPUT_BUFFER_LEN : i++) buffer2->i = ' ';
3229     #Endif; ! TARGET_ZCODE
3230     answer_words = Keyboard(buffer2, parse2);
3231
3232     first_word = WordValue(1, parse2);
3233     #Ifdef LanguageIsVerb;
3234     if (first_word == 0) {
3235         j = wn; first_word = LanguageIsVerb(buffer2, parse2, 1); wn = j;
3236     }
3237     #Endif; ! LanguageIsVerb
3238
3239     ! Once again, if the reply looks like a command, give it to the
3240     ! parser to get on with and forget about the question...
3241
3242     ! Once again, if the reply looks like a command
3243     ! (that is, VERB ... or XXX,VERB ...), give it to the parser to get
3244     ! on with and forget about the question...
3245
3246     if (first_word) {
3247         if ((first_word->#dict_par1) & DICT_VERB) {
3248             CopyBuffer(buffer, buffer2);
3249             return REPARSE_CODE;
3250         }
3251         if (NumberWords(parse2) > 2) {
3252             j = WordValue(2, parse2);
3253             k = WordValue(3, parse2);
3254             if (j == ',//' && k && (k->#dict_par1) & DICT_VERB) {
3255                 CopyBuffer(buffer, buffer2);
3256                 return REPARSE_CODE;
3257             }
3258         }
3259     }
3260
3261     ! ...but if we have a genuine answer, then:
3262     !
3263     ! (1) we must glue in text suitable for anything that's been inferred.
3264
3265     if (inferfrom ~= 0) {
3266         for (j=inferfrom : j<pcount : j++) {
3267             if (pattern-->j == PATTERN_NULL) continue;
3268             i = WORDSIZE + GetKeyBufLength();
3269             SetKeyBufLength(i-WORDSIZE + 1);
3270             buffer->(i++) = ' ';
3271
3272             #Ifdef DEBUG;
3273             if (parser_trace >= 5) print "[Gluing in inference with pattern code ", pattern-->j, "]^";
3274             #Endif; ! DEBUG
3275
3276             ! Conveniently, parse2-->1 is the first word in both ZCODE and GLULX.
3277
3278             parse2-->1 = 0;
3279
3280             ! An inferred object.  Best we can do is glue in a pronoun.
3281             ! (This is imperfect, but it's very seldom needed anyway.)
3282
3283             if (pattern-->j >= 2 && pattern-->j < REPARSE_CODE) {
3284                 ! was the inference made from some noun words?
3285                 ! In which case, we can infer again.
3286                 if ((WordValue(NumberWords())->#dict_par1) & DICT_NOUN) continue;
3287                 PronounNotice(pattern-->j);
3288                 for (k=1 : k<=LanguagePronouns-->0 : k=k+3)
3289                     if (pattern-->j == LanguagePronouns-->(k+2)) {
3290                         parse2-->1 = LanguagePronouns-->k;
3291                         #Ifdef DEBUG;
3292                         if (parser_trace >= 5) print "[Using pronoun '", (address) parse2-->1, "']^";
3293                         #Endif; ! DEBUG
3294                         break;
3295                     }
3296             }
3297             else {
3298                 ! An inferred preposition.
3299                 parse2-->1 = No__Dword(pattern-->j - REPARSE_CODE);
3300                 #Ifdef DEBUG;
3301                 if (parser_trace >= 5) print "[Using preposition '", (address) parse2-->1, "']^";
3302                 #Endif; ! DEBUG
3303             }
3304
3305             ! parse2-->1 now holds the dictionary address of the word to glue in.
3306
3307             if (parse2-->1 ~= 0) {
3308                 k = buffer + i;
3309                 #Ifdef TARGET_ZCODE;
3310                 @output_stream 3 k;
3311                 print (address) parse2-->1;
3312                 @output_stream -3;
3313                 k = k-->0;
3314                 for (l=i : l<i+k : l++) buffer->l = buffer->(l+2);
3315                 #Ifnot; ! TARGET_GLULX
3316                 k = PrintAnyToArray(buffer+i, INPUT_BUFFER_LEN-i, parse2-->1);
3317                 l=l; ! suppress compiler warning
3318                 #Endif; ! TARGET_
3319                 i = i + k; SetKeyBufLength(i-WORDSIZE);
3320             }
3321         }
3322     }
3323
3324     ! (2) we must glue the newly-typed text onto the end.
3325
3326     i = WORDSIZE + GetKeyBufLength();
3327     buffer->(i++) = ' ';
3328     SetKeyBufLength(GetKeyBufLength()+1);
3329     for (j=0 : j<GetKeyBufLength(buffer2) : i++,j++) {
3330         buffer->i = buffer2->(j+WORDSIZE);
3331         SetKeyBufLength(GetKeyBufLength()+1);
3332         if (i-WORDSIZE == INPUT_BUFFER_LEN-1) break;
3333     }
3334
3335     ! (3) we fill up the buffer with spaces, which is unnecessary, but may
3336     !     help incorrectly-written interpreters to cope.
3337
3338     #Ifdef TARGET_ZCODE;
3339     for (: i<INPUT_BUFFER_LEN : i++) buffer->i = ' ';
3340     #Endif; ! TARGET_ZCODE
3341
3342     return REPARSE_CODE;
3343
3344 ]; ! end of NounDomain
3345
3346
3347 [ AskPlayer context  i j k l first_word answer_words marker;
3348     ! Now we print up the question, using the equivalence classes as worked
3349     ! out by Adjudicate() so as not to repeat ourselves on plural objects...
3350
3351     asking_player = true;
3352     if (context == CREATURE_TOKEN) L__M(##Miscellany, 45);
3353     else                           L__M(##Miscellany, 46);
3354
3355     j = number_of_classes; marker = 0;
3356     for (i=1 : i<=number_of_classes : i++) {
3357         while (((match_classes-->marker) ~= i) && ((match_classes-->marker) ~= -i)) marker++;
3358         k = match_list-->marker;
3359
3360         if (match_classes-->marker > 0) print (the) k; else print (a) k;
3361
3362         if (i < j-1)  print (string) COMMA__TX;
3363         if (i == j-1) print (SerialComma) j, (string) OR__TX;
3364     }
3365     L__M(##Miscellany, 57);
3366
3367     ! ...and get an answer:
3368
3369   .WhichOne;
3370     #Ifdef TARGET_ZCODE;
3371     for (i=WORDSIZE : i<INPUT_BUFFER_LEN : i++) buffer2->i = ' ';
3372     #Endif; ! TARGET_ZCODE
3373     answer_words = Keyboard(buffer2, parse2);
3374
3375     first_word = WordValue(1, parse2);
3376     asking_player = false;
3377
3378     ! Take care of "all", because that does something too clever here to do
3379     ! later on:
3380
3381     if (first_word == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) {
3382         if (context == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) {
3383             l = multiple_object-->0;
3384             for (i=0 : i<number_matched && l+i<63 : i++) {
3385                 k = match_list-->i;
3386                 multiple_object-->(i+1+l) = k;
3387             }
3388             multiple_object-->0 = i+l;
3389             rtrue;
3390         }
3391         L__M(##Miscellany, 47);
3392         jump WhichOne;
3393     }
3394
3395     ! If the first word of the reply can be interpreted as a verb, then
3396     ! assume that the player has ignored the question and given a new
3397     ! command altogether.
3398     ! (This is one time when it's convenient that the directions are
3399     ! not themselves verbs - thus, "north" as a reply to "Which, the north
3400     ! or south door" is not treated as a fresh command but as an answer.)
3401
3402     #Ifdef LanguageIsVerb;
3403     if (first_word == 0) {
3404         j = wn; first_word = LanguageIsVerb(buffer2, parse2, 1); wn = j;
3405     }
3406     #Endif; ! LanguageIsVerb
3407     if (first_word) {
3408         if (((first_word->#dict_par1) & DICT_VERB) && ~~LanguageVerbMayBeName(first_word)) {
3409             CopyBuffer(buffer, buffer2);
3410             return REPARSE_CODE;
3411         }
3412         if (NumberWords(parse2) > 2) {
3413             j = WordValue(2, parse2);
3414             k = WordValue(3, parse2);
3415             if (j == ',//' && k && (k->#dict_par1) & DICT_VERB) {
3416                 CopyBuffer(buffer, buffer2);
3417                 return REPARSE_CODE;
3418             }
3419         }
3420     }
3421
3422     ! Now we insert the answer into the original typed command, as
3423     ! words additionally describing the same object
3424     ! (eg, > take red button
3425     !      Which one, ...
3426     !      > music
3427     ! becomes "take music red button".  The parser will thus have three
3428     ! words to work from next time, not two.)
3429
3430     k = WordAddress(match_from) - buffer;
3431     l = GetKeyBufLength(buffer2) +1;
3432     for (j=buffer + INPUT_BUFFER_LEN - 1 : j>=buffer+k+l : j--) j->0 = j->(-l);
3433     for (i=0 : i<l : i++) buffer->(k+i) = buffer2->(WORDSIZE+i);
3434     buffer->(k+l-1) = ' ';
3435     SetKeyBufLength(GetKeyBufLength() + l);
3436
3437     ! Having reconstructed the input, we warn the parser accordingly
3438     ! and get out.
3439
3440     return REPARSE_CODE;
3441 ];
3442
3443
3444 ! ----------------------------------------------------------------------------
3445 !  The Adjudicate routine tries to see if there is an obvious choice, when
3446 !  faced with a list of objects (the match_list) each of which matches the
3447 !  player's specification equally well.
3448 !
3449 !  To do this it makes use of the context (the token type being worked on).
3450 !  It counts up the number of obvious choices for the given context
3451 !  (all to do with where a candidate is, except for 6 (animate) which is to
3452 !  do with whether it is animate or not);
3453 !
3454 !  if only one obvious choice is found, that is returned;
3455 !
3456 !  if we are in indefinite mode (don't care which) one of the obvious choices
3457 !    is returned, or if there is no obvious choice then an unobvious one is
3458 !    made;
3459 !
3460 !  at this stage, we work out whether the objects are distinguishable from
3461 !    each other or not: if they are all indistinguishable from each other,
3462 !    then choose one, it doesn't matter which;
3463 !
3464 !  otherwise, 0 (meaning, unable to decide) is returned (but remember that
3465 !    the equivalence classes we've just worked out will be needed by other
3466 !    routines to clear up this mess, so we can't economise on working them
3467 !    out).
3468 !
3469 !  Returns -1 if an error occurred
3470 ! ----------------------------------------------------------------------------
3471
3472 Constant SCORE__CHOOSEOBJ   = 1000;
3473 Constant SCORE__IFGOOD      = 500;
3474 Constant SCORE__UNCONCEALED = 100;
3475 Constant SCORE__BESTLOC     = 60;
3476 Constant SCORE__NEXTBESTLOC = 40;
3477 Constant SCORE__NOTCOMPASS  = 20;
3478 Constant SCORE__NOTSCENERY  = 10;
3479 Constant SCORE__NOTACTOR    = 5;
3480 Constant SCORE__GNA         = 1;
3481 Constant SCORE__DIVISOR     = 20;
3482
3483 [ Adjudicate context i j k good_flag good_ones last n flag offset sovert;
3484     #Ifdef DEBUG;
3485     if (parser_trace >= 4) {
3486         print "   [Adjudicating match list of size ", number_matched, " in context ", context, "]^";
3487         print "   ";
3488         if (indef_mode) {
3489             print "indefinite type: ";
3490             if (indef_type & OTHER_BIT)  print "other ";
3491             if (indef_type & MY_BIT)     print "my ";
3492             if (indef_type & THAT_BIT)   print "that ";
3493             if (indef_type & PLURAL_BIT) print "plural ";
3494             if (indef_type & LIT_BIT)    print "lit ";
3495             if (indef_type & UNLIT_BIT)  print "unlit ";
3496             if (indef_owner ~= 0) print "owner:", (name) indef_owner;
3497             new_line;
3498             print "   number wanted: ";
3499             if (indef_wanted == 100) print "all"; else print indef_wanted;
3500             new_line;
3501             print "   most likely GNAs of names: ", indef_cases, "^";
3502         }
3503         else print "definite object^";
3504     }
3505     #Endif; ! DEBUG
3506
3507     j = number_matched-1; good_ones = 0; last = match_list-->0;
3508     for (i=0 : i<=j : i++) {
3509         n = match_list-->i;
3510         match_scores-->i = 0;
3511
3512         good_flag = false;
3513
3514         switch (context) {
3515           HELD_TOKEN, MULTIHELD_TOKEN:
3516             if (parent(n) == actor) good_flag = true;
3517           MULTIEXCEPT_TOKEN:
3518             if (advance_warning == -1) {
3519                 good_flag = true;
3520             }
3521             else {
3522                 if (n ~= advance_warning) good_flag = true;
3523             }
3524           MULTIINSIDE_TOKEN:
3525             if (advance_warning == -1) {
3526                 if (parent(n) ~= actor) good_flag = true;
3527             }
3528             else {
3529                 if (n in advance_warning) good_flag = true;
3530             }
3531           CREATURE_TOKEN:
3532             if (CreatureTest(n) == 1) good_flag = true;
3533           default:
3534             good_flag = true;
3535         }
3536
3537         if (good_flag) {
3538             match_scores-->i = SCORE__IFGOOD;
3539             good_ones++; last = n;
3540         }
3541     }
3542     if (good_ones == 1) return last;
3543
3544     ! If there is ambiguity about what was typed, but it definitely wasn't
3545     ! animate as required, then return anything; higher up in the parser
3546     ! a suitable error will be given.  (This prevents a question being asked.)
3547
3548     if (context == CREATURE_TOKEN && good_ones == 0) return match_list-->0;
3549
3550     if (indef_mode == 0) indef_type=0;
3551
3552     ScoreMatchL(context);
3553     if (number_matched == 0) return -1;
3554
3555     if (indef_mode == 1 && indef_type & PLURAL_BIT ~= 0) {
3556         if (context ~= MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN
3557                      or MULTIINSIDE_TOKEN) {
3558             etype = MULTI_PE;
3559             return -1;
3560         }
3561         i = 0; offset = multiple_object-->0; sovert = -1;
3562         for (j=BestGuess() : j~=-1 && i<indef_wanted && i+offset<63 : j=BestGuess()) {
3563             flag = 1;
3564             if (j has concealed or worn) flag = 0;
3565             if (sovert == -1) sovert = bestguess_score/SCORE__DIVISOR;
3566             else {
3567                 if (indef_wanted == 100 && bestguess_score/SCORE__DIVISOR < sovert)
3568                     flag = 0;
3569             }
3570             if (context == MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN && parent(j) ~= actor)
3571                 flag = 0;
3572             #Ifdef TRADITIONAL_TAKE_ALL;
3573             if (action_to_be == ##Take or ##Remove && parent(j) == actor)
3574                 flag = 0;
3575             #Ifnot;
3576             if (action_to_be == ##Take or ##Remove &&
3577                (j has animate or scenery or static || parent(j) == actor))
3578                 flag = 0;
3579             #Endif; ! TRADITIONAL_TAKE_ALL
3580             #Ifdef NO_TAKE_ALL;
3581             if (take_all_rule == 2 && match_length == 0) flag = 0;
3582             #Endif; ! NO_TAKE_ALL
3583             n = ChooseObjects(j, flag);
3584             if (n == 0) n = LibraryExtensions.RunWhile(ext_chooseobjects, 0, j, flag);
3585             switch (n) {
3586               2: flag = 0;  ! forcing rejection
3587               1: flag = 1;  ! forcing acceptance
3588              !0:            ! going with parser's decision
3589             }
3590             if (flag == 1) {
3591                 i++; multiple_object-->(i+offset) = j;
3592                 #Ifdef DEBUG;
3593                 if (parser_trace >= 4) print "   Accepting it^";
3594                 #Endif; ! DEBUG
3595             }
3596             else {
3597                 i = i;
3598                 #Ifdef DEBUG;
3599                 if (parser_trace >= 4) print "   Rejecting it^";
3600                 #Endif; ! DEBUG
3601             }
3602         }
3603         if (i < indef_wanted && indef_wanted < 100) {
3604             etype = TOOFEW_PE; multi_wanted = indef_wanted;
3605             multi_had=i;
3606             return -1;
3607         }
3608         multiple_object-->0 = i+offset;
3609         multi_context = context;
3610         #Ifdef DEBUG;
3611         if (parser_trace >= 4)
3612             print "   Made multiple object of size ", i, "]^";
3613         #Endif; ! DEBUG
3614         return 1;
3615     }
3616
3617     for (i=0 : i<number_matched : i++) match_classes-->i = 0;
3618
3619     n = 1;
3620     for (i=0 : i<number_matched : i++)
3621         if (match_classes-->i == 0) {
3622             match_classes-->i = n++; flag = 0;
3623             for (j=i+1 : j<number_matched : j++)
3624                 if (match_classes-->j == 0 && Identical(match_list-->i, match_list-->j) == 1) {
3625                     flag=1;
3626                     match_classes-->j = match_classes-->i;
3627                 }
3628             if (flag == 1) match_classes-->i = 1-n;
3629         }
3630      n--; number_of_classes = n;
3631
3632     #Ifdef DEBUG;
3633     if (parser_trace >= 4) {
3634         print "   Grouped into ", n, " possibilities by name:^";
3635         for (i=0 : i<number_matched : i++)
3636             if (match_classes-->i > 0)
3637                 print "   ", (The) match_list-->i, " (", match_list-->i, ")  ---  group ",
3638                   match_classes-->i, "^";
3639     }
3640     #Endif; ! DEBUG
3641     if (n == 1) dont_infer = true;
3642
3643     if (indef_mode == 0) {
3644         !  Is there now a single highest-scoring object?
3645         i = SingleBestGuess();
3646         if (i >= 0) {
3647
3648             #Ifdef DEBUG;
3649             if (parser_trace >= 4) print "   Single best-scoring object returned.]^";
3650             #Endif; ! DEBUG
3651             return i;
3652         }
3653     }
3654
3655     if (indef_mode == 0) {
3656         if (n > 1) {
3657             k = -1;
3658             for (i=0 : i<number_matched : i++) {
3659                 if (match_scores-->i > k) {
3660                     k = match_scores-->i;
3661                     j = match_classes-->i; j = j*j;
3662                     flag = 0;
3663                 }
3664                 else
3665                     if (match_scores-->i == k) {
3666                         if ((match_classes-->i) * (match_classes-->i) ~= j)
3667                             flag = 1;
3668                     }
3669             }
3670
3671         if (flag) {
3672             if(match_length == 1) {
3673                 k = 0;
3674                 for (i=1 : i<=number_of_classes : i++) {
3675                     while ((match_classes-->k) ~= i or -i) k++;
3676                     j = match_list-->k;
3677
3678                     if(MatchWord(j, match_from)) {
3679                         no_infer_message = true;
3680                         return j;
3681                     }
3682                 }
3683             }
3684             #Ifdef DEBUG;
3685             if (parser_trace >= 4) print "   Unable to choose best group, so ask player.]^";
3686             #Endif; ! DEBUG
3687             return 0;
3688         }
3689         #Ifdef DEBUG;
3690         if (parser_trace >= 4) print "   Best choices are all from the same group.^";
3691         #Endif; ! DEBUG
3692         }
3693     }
3694
3695     !  When the player is really vague, or there's a single collection of
3696     !  indistinguishable objects to choose from, choose the one the player
3697     !  most recently acquired, or if the player has none of them, then
3698     !  the one most recently put where it is.
3699
3700     return BestGuess();
3701
3702 ]; ! Adjudicate
3703
3704 ! ----------------------------------------------------------------------------
3705 !  ReviseMulti  revises the multiple object which already exists, in the
3706 !    light of information which has come along since then (i.e., the second
3707 !    parameter).  It returns a parser error number, or else 0 if all is well.
3708 !    This only ever throws things out, never adds new ones.
3709 ! ----------------------------------------------------------------------------
3710
3711 [ ReviseMulti second_p  i low;
3712     #Ifdef DEBUG;
3713     if (parser_trace >= 4) print "   Revising multiple object list of size ", multiple_object-->0,
3714       " with 2nd ", (name) second_p, "^";
3715     #Endif; ! DEBUG
3716
3717     if (multi_context == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) {
3718         for (i=1,low=0 : i<=multiple_object-->0 : i++) {
3719             if ( (multi_context==MULTIEXCEPT_TOKEN && multiple_object-->i ~= second_p) ||
3720                  (multi_context==MULTIINSIDE_TOKEN && multiple_object-->i in second_p)) {
3721                 low++;
3722                 multiple_object-->low = multiple_object-->i;
3723             }
3724         }
3725         multiple_object-->0 = low;
3726     }
3727
3728     if (multi_context == MULTI_TOKEN && action_to_be == ##Take) {
3729         for (i=1,low=0 : i<=multiple_object-->0 : i++)
3730             if (ScopeCeiling(multiple_object-->i)==ScopeCeiling(actor)) low++;
3731         #Ifdef DEBUG;
3732         if (parser_trace >= 4) print "   Token 2 plural case: number with actor ", low, "^";
3733         #Endif; ! DEBUG
3734         if (take_all_rule == 2 || low > 0) {
3735             for (i=1,low=0 : i<=multiple_object-->0 : i++) {
3736                 if (ScopeCeiling(multiple_object-->i) == ScopeCeiling(actor)) {
3737                     low++;
3738                     multiple_object-->low = multiple_object-->i;
3739                 }
3740             }
3741             multiple_object-->0 = low;
3742         }
3743     }
3744
3745     i = multiple_object-->0;
3746     #Ifdef DEBUG;
3747     if (parser_trace >= 4) print "   Done: new size ", i, "^";
3748     #Endif; ! DEBUG
3749     if (i == 0) return NOTHING_PE;
3750     return 0;
3751 ];
3752
3753 ! ----------------------------------------------------------------------------
3754 !  ScoreMatchL  scores the match list for quality in terms of what the
3755 !  player has vaguely asked for.  Points are awarded for conforming with
3756 !  requirements like "my", and so on.  Remove from the match list any
3757 !  entries which fail the basic requirements of the descriptors.
3758 ! ----------------------------------------------------------------------------
3759
3760 [ ScoreMatchL context its_owner its_score obj i j threshold met a_s l_s;
3761 !   if (indef_type & OTHER_BIT ~= 0) threshold++;
3762     if (indef_type & MY_BIT ~= 0)    threshold++;
3763     if (indef_type & THAT_BIT ~= 0)  threshold++;
3764     if (indef_type & LIT_BIT ~= 0)   threshold++;
3765     if (indef_type & UNLIT_BIT ~= 0) threshold++;
3766     if (indef_owner ~= nothing)      threshold++;
3767
3768     #Ifdef DEBUG;
3769     if (parser_trace >= 4) print "   Scoring match list: indef mode ", indef_mode, " type ",
3770       indef_type, ", satisfying ", threshold, " requirements:^";
3771     #Endif; ! DEBUG
3772
3773     if (action_to_be ~= ##Take)
3774         a_s = SCORE__NEXTBESTLOC;
3775     l_s = SCORE__BESTLOC;
3776     if (context == HELD_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN) {
3777         a_s = SCORE__BESTLOC; l_s = SCORE__NEXTBESTLOC;
3778     }
3779
3780     for (i=0 : i<number_matched : i++) {
3781         obj = match_list-->i; its_owner = parent(obj); its_score=0; met=0;
3782
3783         !      if (indef_type & OTHER_BIT ~= 0
3784         !          &&  obj ~= itobj or himobj or herobj) met++;
3785         if (indef_type & MY_BIT ~= 0 && its_owner == actor) met++;
3786         if (indef_type & THAT_BIT ~= 0 && its_owner == actors_location) met++;
3787         if (indef_type & LIT_BIT ~= 0 && obj has light) met++;
3788         if (indef_type & UNLIT_BIT ~= 0 && obj hasnt light) met++;
3789         if (indef_owner ~= 0 && its_owner == indef_owner) met++;
3790
3791         if (met < threshold) {
3792             #Ifdef DEBUG;
3793             if (parser_trace >= 4) print "   ", (The) match_list-->i, " (", match_list-->i, ") in ",
3794               (the) its_owner, " is rejected (doesn't match descriptors)^";
3795             #Endif; ! DEBUG
3796             match_list-->i = -1;
3797         }
3798         else {
3799             its_score = 0;
3800             if (obj hasnt concealed) its_score = SCORE__UNCONCEALED;
3801
3802             if (its_owner == actor) its_score = its_score + a_s;
3803             else
3804                 if (its_owner == actors_location) its_score = its_score + l_s;
3805                 else {
3806                     #Ifdef TRADITIONAL_TAKE_ALL;
3807                     if (its_owner ~= Compass) its_score = its_score + SCORE__NOTCOMPASS;
3808                     #Ifnot;
3809                     if (its_owner ~= Compass)
3810                         if (take_all_rule && its_owner &&
3811                             its_owner has static or scenery &&
3812                               (its_owner has supporter ||
3813                               (its_owner has container && its_owner has open)))
3814                             its_score = its_score + l_s;
3815                     else
3816                             its_score = its_score + SCORE__NOTCOMPASS;
3817                     #Endif; ! TRADITIONAL_TAKE_ALL
3818                 }
3819             j = ChooseObjects(obj, 2);
3820             if (j == 0) j = LibraryExtensions.RunAll(ext_chooseobjects, obj, 2);
3821             its_score = its_score + SCORE__CHOOSEOBJ * j;
3822
3823             if (obj hasnt scenery) its_score = its_score + SCORE__NOTSCENERY;
3824             if (obj ~= actor) its_score = its_score + SCORE__NOTACTOR;
3825
3826             !   A small bonus for having the correct GNA,
3827             !   for sorting out ambiguous articles and the like.
3828
3829             if (indef_cases & (PowersOfTwo_TB-->(GetGNAOfObject(obj))))
3830                 its_score = its_score + SCORE__GNA;
3831
3832             match_scores-->i = match_scores-->i + its_score;
3833             #Ifdef DEBUG;
3834             if (parser_trace >= 4) print "     ", (The) match_list-->i, " (", match_list-->i,
3835               ") in ", (the) its_owner, " : ", match_scores-->i, " points^";
3836             #Endif; ! DEBUG
3837         }
3838      }
3839
3840     for (i=0 : i<number_matched : i++) {
3841         while (match_list-->i == -1) {
3842             if (i == number_matched-1) { number_matched--; break; }
3843             for (j=i : j<number_matched-1 : j++) {
3844                 match_list-->j = match_list-->(j+1);
3845                 match_scores-->j = match_scores-->(j+1);
3846             }
3847             number_matched--;
3848         }
3849     }
3850 ];
3851
3852 ! ----------------------------------------------------------------------------
3853 !  BestGuess makes the best guess it can out of the match list, assuming that
3854 !  everything in the match list is textually as good as everything else;
3855 !  however it ignores items marked as -1, and so marks anything it chooses.
3856 !  It returns -1 if there are no possible choices.
3857 ! ----------------------------------------------------------------------------
3858
3859 [ BestGuess  earliest its_score best i;
3860     earliest = 0; best = -1;
3861     for (i=0 : i<number_matched : i++) {
3862         if (match_list-->i >= 0) {
3863             its_score = match_scores-->i;
3864             if (its_score > best) { best = its_score; earliest = i; }
3865         }
3866     }
3867     #Ifdef DEBUG;
3868     if (parser_trace >= 4)
3869       if (best < 0) print "   Best guess ran out of choices^";
3870       else print "   Best guess ", (the) match_list-->earliest, " (", match_list-->earliest, ")^";
3871     #Endif; ! DEBUG
3872     if (best < 0) return -1;
3873     i = match_list-->earliest;
3874     match_list-->earliest = -1;
3875     bestguess_score = best;
3876     return i;
3877 ];
3878
3879 ! ----------------------------------------------------------------------------
3880 !  SingleBestGuess returns the highest-scoring object in the match list
3881 !  if it is the clear winner, or returns -1 if there is no clear winner
3882 ! ----------------------------------------------------------------------------
3883
3884 [ SingleBestGuess  earliest its_score best i;
3885     earliest = -1; best = -1000;
3886     for (i=0 : i<number_matched : i++) {
3887         its_score = match_scores-->i;
3888         if (its_score == best) earliest = -1;
3889         if (its_score > best) { best = its_score; earliest = match_list-->i; }
3890     }
3891     bestguess_score = best;
3892     return earliest;
3893 ];
3894
3895 ! ----------------------------------------------------------------------------
3896 !  Identical decides whether or not two objects can be distinguished from
3897 !  each other by anything the player can type.  If not, it returns true.
3898 ! ----------------------------------------------------------------------------
3899
3900 [ Identical o1 o2 p1 p2 n1 n2 i j flag;
3901     if (o1 == o2) rtrue;  ! This should never happen, but to be on the safe side
3902     if (o1 == 0 || o2 == 0) rfalse;  ! Similarly
3903     if (parent(o1) == Compass || parent(o2) == Compass) rfalse; ! Saves time
3904
3905     !  What complicates things is that o1 or o2 might have a parsing routine,
3906     !  so the parser can't know from here whether they are or aren't the same.
3907     !  If they have different parsing routines, we simply assume they're
3908     !  different.  If they have the same routine (which they probably got from
3909     !  a class definition) then the decision process is as follows:
3910     !
3911     !     the routine is called (with self being o1, not that it matters)
3912     !       with noun and second being set to o1 and o2, and action being set
3913     !       to the fake action TheSame.  If it returns -1, they are found
3914     !       identical; if -2, different; and if >=0, then the usual method
3915     !       is used instead.
3916
3917     if (o1.parse_name ~= 0 || o2.parse_name ~= 0) {
3918       if (o1.parse_name ~= o2.parse_name) rfalse;
3919       parser_action = ##TheSame; parser_one = o1; parser_two = o2;
3920       j = wn; i = RunRoutines(o1,parse_name); wn = j;
3921       if (i == -1) rtrue;
3922       if (i == -2) rfalse;
3923     }
3924
3925     !  This is the default algorithm: do they have the same words in their
3926     !  "name" (i.e. property no. 1) properties.  (Note that the following allows
3927     !  for repeated words and words in different orders.)
3928
3929     p1 = o1.&1; n1 = (o1.#1)/WORDSIZE;
3930     p2 = o2.&1; n2 = (o2.#1)/WORDSIZE;
3931
3932     !  for (i=0 : i<n1 : i++) { print (address) p1-->i, " "; } new_line;
3933     !  for (i=0 : i<n2 : i++) { print (address) p2-->i, " "; } new_line;
3934
3935     for (i=0 : i<n1 : i++) {
3936         flag = 0;
3937         for (j=0 : j<n2 : j++)
3938             if (p1-->i == p2-->j) flag = 1;
3939         if (flag == 0) rfalse;
3940     }
3941
3942     for (j=0 : j<n2 : j++) {
3943         flag = 0;
3944         for (i=0 : i<n1 : i++)
3945             if (p1-->i == p2-->j) flag = 1;
3946         if (flag == 0) rfalse;
3947     }
3948
3949     !  print "Which are identical!^";
3950     rtrue;
3951 ];
3952
3953
3954 ! ----------------------------------------------------------------------------
3955 !  MatchWord matches a word to the name of an object.  This is used to assume
3956 !  that an object simply named "key" is meant when the player types TAKE KEY
3957 !  rather than TAKE BRASS KEY.
3958 ! ----------------------------------------------------------------------------
3959
3960 [ MatchWord p_obj p_word_no       len_name len_input start_input i;
3961
3962     p_word_no--;
3963
3964     PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, p_obj);
3965     len_name = StorageForShortName --> 0;
3966
3967     if(len_name > 9) {
3968         for(i = 9 : i < len_name : i++)
3969             if(StorageForShortName -> (WORDSIZE + i) == 32) rfalse;
3970         len_name = 9;
3971     }
3972
3973 #Ifdef TARGET_ZCODE;
3974     start_input = (parse + 2) -> (4 * p_word_no + 3);
3975     len_input = (parse + 2) -> (4 * p_word_no + 2);
3976     if(len_input > 9) len_input = 9;
3977     if(len_name ~= len_input) rfalse;
3978     for(i = 0 : i < len_name : i++) {
3979         if(StorageForShortName -> (WORDSIZE + i) ~= buffer -> (start_input + i))
3980             rfalse;
3981     }
3982 #Ifnot;  ! TARGET_GLULX
3983     start_input = parse --> (((p_word_no+1) * 3)) - 2;
3984     len_input = parse --> ((p_word_no+1) * 3 - 1);
3985     if(len_input > 9) len_input = 9;
3986     if(len_name ~= len_input) rfalse;
3987     for(i = 0 : i < len_name : i++) {
3988         if(StorageForShortName -> (WORDSIZE + i) ~= buffer -> (start_input + i + 2))
3989             rfalse;
3990     }
3991 #Endif;  ! TARGET_
3992
3993     rtrue;
3994 ];
3995
3996
3997 ! ----------------------------------------------------------------------------
3998 !  PrintCommand reconstructs the command as it presently reads, from
3999 !  the pattern which has been built up
4000 !
4001 !  If from is 0, it starts with the verb: then it goes through the pattern.
4002 !  The other parameter is "emptyf" - a flag: if 0, it goes up to pcount:
4003 !  if 1, it goes up to pcount-1.
4004 !
4005 !  Note that verbs and prepositions are printed out of the dictionary:
4006 !  and that since the dictionary may only preserve the first six characters
4007 !  of a word (in a V3 game), we have to hand-code the longer words needed.
4008 !
4009 !  (Recall that pattern entries are 0 for "multiple object", 1 for "special
4010 !  word", 2 to REPARSE_CODE-1 are object numbers and REPARSE_CODE+n means the
4011 !  preposition n)
4012 ! ----------------------------------------------------------------------------
4013
4014 [ PrintCommand from i k spacing_flag;
4015     #Ifdef LanguageCommand;
4016     LanguageCommand(from);
4017     i = k = spacing_flag = 0;   ! suppress warning
4018     #Ifnot;
4019     if (from == 0) {
4020         i = verb_word;
4021         if (LanguageVerb(i) == 0 && PrintVerb(i) == false && LibraryExtensions.RunWhile(ext_printverb, false, i) == 0)
4022             print (address) i;
4023         from++; spacing_flag = true;
4024     }
4025
4026     for (k=from : k<pcount : k++) {
4027         i = pattern-->k;
4028         if (i == PATTERN_NULL) continue;
4029         if (spacing_flag) print (char) ' ';
4030         if (i == 0) { print (string) THOSET__TX; jump TokenPrinted; }
4031         if (i == 1) { print (string) THAT__TX;   jump TokenPrinted; }
4032         if (i >= REPARSE_CODE)
4033             print (address) No__Dword(i-REPARSE_CODE);
4034         else
4035             if (i in Compass && LanguageVerbLikesAdverb(verb_word))
4036                 LanguageDirection (i.door_dir); ! the direction name as adverb
4037             else
4038                 print (the) i;
4039       .TokenPrinted;
4040         spacing_flag = true;
4041     }
4042     #Endif; ! LanguageCommand
4043 ];
4044
4045 ! ----------------------------------------------------------------------------
4046 !  The CantSee routine returns a good error number for the situation where
4047 !  the last word looked at didn't seem to refer to any object in context.
4048 !
4049 !  The idea is that: if the actor is in a location (but not inside something
4050 !  like, for instance, a tank which is in that location) then an attempt to
4051 !  refer to one of the words listed as meaningful-but-irrelevant there
4052 !  will cause "you don't need to refer to that in this game" rather than
4053 !  "no such thing" or "what's 'it'?".
4054 !  (The advantage of not having looked at "irrelevant" local nouns until now
4055 !  is that it stops them from clogging up the ambiguity-resolving process.
4056 !  Thus game objects always triumph over scenery.)
4057 ! ----------------------------------------------------------------------------
4058
4059 [ CantSee  i w e;
4060     if (scope_token ~= 0) {
4061         scope_error = scope_token;
4062         return ASKSCOPE_PE;
4063     }
4064
4065     wn--; w = NextWord();
4066     e = CANTSEE_PE;
4067     if (w == pronoun_word) {
4068         pronoun__word = pronoun_word; pronoun__obj = pronoun_obj;
4069         e = ITGONE_PE;
4070     }
4071     i = actor; while (parent(i) ~= 0) i = parent(i);
4072
4073     wn--;
4074     if (i has visited && Refers(i,wn) == 1) e = SCENERY_PE;
4075     else {
4076         Descriptors();  ! skip past THE etc
4077         if (i has visited && Refers(i,wn) == 1) e = SCENERY_PE;
4078     }
4079
4080     if (saved_ml)
4081         saved_oops = num_desc + match_from + saved_ml;
4082     else
4083         saved_oops = num_desc + match_from + match_length;
4084
4085     wn++;
4086     return e;
4087 ];
4088
4089 ! ----------------------------------------------------------------------------
4090 !  The MultiAdd routine adds object "o" to the multiple-object-list.
4091 !
4092 !  This is only allowed to hold 63 objects at most, at which point it ignores
4093 !  any new entries (and sets a global flag so that a warning may later be
4094 !  printed if need be).
4095 ! ----------------------------------------------------------------------------
4096
4097 [ MultiAdd o i j;
4098     i = multiple_object-->0;
4099     if (i == 63) { toomany_flag = 1; rtrue; }
4100     for (j=1 : j<=i : j++)
4101         if (o == multiple_object-->j) rtrue;
4102     i++;
4103     multiple_object-->i = o;
4104     multiple_object-->0 = i;
4105 ];
4106
4107 ! ----------------------------------------------------------------------------
4108 !  The MultiSub routine deletes object "o" from the multiple-object-list.
4109 !
4110 !  It returns 0 if the object was there in the first place, and 9 (because
4111 !  this is the appropriate error number in Parser()) if it wasn't.
4112 ! ----------------------------------------------------------------------------
4113
4114 [ MultiSub o i j k et;
4115     i = multiple_object-->0; et = 0;
4116     for (j=1 : j<=i : j++)
4117         if (o == multiple_object-->j) {
4118             for (k=j : k<=i : k++)
4119                 multiple_object-->k = multiple_object-->(k+1);
4120             multiple_object-->0 = --i;
4121             return et;
4122         }
4123     et = 9; return et;
4124 ];
4125
4126 ! ----------------------------------------------------------------------------
4127 !  The MultiFilter routine goes through the multiple-object-list and throws
4128 !  out anything without the given attribute "attr" set.
4129 ! ----------------------------------------------------------------------------
4130
4131 [ MultiFilter attr  i j o;
4132
4133   .MFiltl;
4134
4135     i = multiple_object-->0;
4136     for (j=1 : j<=i : j++) {
4137         o = multiple_object-->j;
4138         if (o hasnt attr) {
4139             MultiSub(o);
4140             jump Mfiltl;
4141         }
4142     }
4143 ];
4144
4145 ! ----------------------------------------------------------------------------
4146 !  The UserFilter routine consults the user's filter (or checks on attribute)
4147 !  to see what already-accepted nouns are acceptable
4148 ! ----------------------------------------------------------------------------
4149
4150 [ UserFilter obj;
4151     if (token_filter > 0 && token_filter < 49) {
4152         if (obj has (token_filter-1)) rtrue;
4153         rfalse;
4154     }
4155     noun = obj;
4156     return token_filter();
4157 ];
4158
4159 ! ----------------------------------------------------------------------------
4160 !  MoveWord copies word at2 from parse buffer b2 to word at1 in "parse"
4161 !  (the main parse buffer)
4162 ! ----------------------------------------------------------------------------
4163
4164 #Ifdef TARGET_ZCODE;
4165
4166 [ MoveWord at1 b2 at2 x y;
4167     x = at1*2-1; y = at2*2-1;
4168     parse-->x++ = b2-->y++;
4169     parse-->x = b2-->y;
4170 ];
4171
4172 #Ifnot; ! TARGET_GLULX
4173
4174 [ MoveWord at1 b2 at2 x y;
4175     x = at1*3-2; y = at2*3-2;
4176     parse-->x++ = b2-->y++;
4177     parse-->x++ = b2-->y++;
4178     parse-->x = b2-->y;
4179 ];
4180
4181 #Endif; ! TARGET_
4182
4183 ! ----------------------------------------------------------------------------
4184 !  SearchScope  domain1 domain2 context
4185 !
4186 !  Works out what objects are in scope (possibly asking an outside routine),
4187 !  but does not look at anything the player has typed.
4188 ! ----------------------------------------------------------------------------
4189
4190 [ SearchScope domain1 domain2 context i is;
4191     i = 0;
4192     !  Everything is in scope to the debugging commands
4193
4194     #Ifdef DEBUG;
4195     if (scope_reason == PARSING_REASON
4196         && LanguageVerbIsDebugging(verb_word)) {
4197
4198         #Ifdef TARGET_ZCODE;
4199         for (i=selfobj : i<=top_object : i++)
4200             if (i ofclass Object && (parent(i) == 0 || parent(i) ofclass Object))
4201                 PlaceInScope(i);
4202         #Ifnot; ! TARGET_GLULX
4203         objectloop (i)
4204             if (i ofclass Object && (parent(i) == 0 || parent(i) ofclass Object))
4205                 PlaceInScope(i);
4206         #Endif; ! TARGET_
4207         rtrue;
4208     }
4209     #Endif; ! DEBUG
4210
4211     ! First, a scope token gets priority here:
4212
4213     if (scope_token ~= 0) {
4214         scope_stage = 2;
4215         if (scope_token()) rtrue;
4216     }
4217
4218     ! Pick up everything in the location except the actor's possessions;
4219     ! then go through those.  (This ensures the actor's possessions are in
4220     ! scope even in Darkness.)
4221
4222     if (context == MULTIINSIDE_TOKEN && advance_warning ~= -1) {
4223         if (IsSeeThrough(advance_warning) == 1)
4224            ScopeWithin(advance_warning, 0, context);
4225     }
4226     else {
4227         ! Next, call any user-supplied routine adding things to the scope,
4228         ! which may circumvent the usual routines altogether
4229         ! if they return true:
4230
4231         if (actor == domain1 or domain2) {
4232             is = InScope(actor);
4233             if (is == false) is = LibraryExtensions.RunWhile(ext_inscope, false, actor);
4234             if (is) rtrue;
4235         }
4236
4237         if (domain1 ~= 0 && domain1 has supporter or container)
4238             ScopeWithin_O(domain1, domain1, context);
4239         ScopeWithin(domain1, domain2, context);
4240         if (domain2 ~= 0 && domain2 has supporter or container)
4241             ScopeWithin_O(domain2, domain2, context);
4242         ScopeWithin(domain2, 0, context);
4243     }
4244
4245     ! A special rule applies:
4246     ! in Darkness as in light, the actor is always in scope to himself.
4247
4248     if (thedark == domain1 or domain2) {
4249         ScopeWithin_O(actor, actor, context);
4250         if (parent(actor) has supporter or container)
4251             ScopeWithin_O(parent(actor), parent(actor), context);
4252     }
4253 ];
4254
4255 ! ----------------------------------------------------------------------------
4256 !  IsSeeThrough is used at various places: roughly speaking, it determines
4257 !  whether o being in scope means that the contents of o are in scope.
4258 ! ----------------------------------------------------------------------------
4259
4260 [ IsSeeThrough o;
4261     if (o has supporter or transparent ||
4262        (o has container && o has open))
4263         rtrue;
4264     rfalse;
4265 ];
4266
4267 ! ----------------------------------------------------------------------------
4268 !  PlaceInScope is provided for routines outside the library, and is not
4269 !  called within the parser (except for debugging purposes).
4270 ! ----------------------------------------------------------------------------
4271
4272 [ PlaceInScope thing;
4273     if (scope_reason~=PARSING_REASON or TALKING_REASON) {
4274         DoScopeAction(thing); rtrue;
4275     }
4276     wn = match_from; TryGivenObject(thing); placed_in_flag = 1;
4277 ];
4278
4279 ! ----------------------------------------------------------------------------
4280 !  DoScopeAction
4281 ! ----------------------------------------------------------------------------
4282
4283 [ DoScopeAction thing s p1;
4284     s = scope_reason; p1 = parser_one;
4285     #Ifdef DEBUG;
4286     if (parser_trace >= 6)
4287         print "[DSA on ", (the) thing, " with reason = ", scope_reason,
4288             " p1 = ", parser_one, " p2 = ", parser_two, "]^";
4289     #Endif; ! DEBUG
4290     switch (scope_reason) {
4291       REACT_BEFORE_REASON:
4292         if (thing.react_before == 0 or NULL) return;
4293         #Ifdef DEBUG;
4294         if (parser_trace >= 2)
4295               print "[Considering react_before for ", (the) thing, "]^";
4296         #Endif; ! DEBUG
4297         if (parser_one == 0) parser_one = RunRoutines(thing, react_before);
4298       REACT_AFTER_REASON:
4299         if (thing.react_after == 0 or NULL) return;
4300         #Ifdef DEBUG;
4301         if (parser_trace >= 2)
4302             print "[Considering react_after for ", (the) thing, "]^";
4303         #Endif; ! DEBUG
4304         if (parser_one == 0) parser_one = RunRoutines(thing, react_after);
4305       EACH_TURN_REASON:
4306         if (thing.each_turn == 0) return;
4307         #Ifdef DEBUG;
4308         if (parser_trace >= 2)
4309               print "[Considering each_turn for ", (the) thing, "]^";
4310         #Endif; ! DEBUG
4311         PrintOrRun(thing, each_turn);
4312       TESTSCOPE_REASON:
4313         if (thing == parser_one) parser_two = 1;
4314       LOOPOVERSCOPE_REASON:
4315         parser_one(thing);
4316         parser_one=p1;
4317     }
4318     scope_reason = s;
4319 ];
4320
4321 ! ----------------------------------------------------------------------------
4322 !  ScopeWithin looks for objects in the domain which make textual sense
4323 !  and puts them in the match list.  (However, it does not recurse through
4324 !  the second argument.)
4325 ! ----------------------------------------------------------------------------
4326
4327 [ ScopeWithin domain nosearch context x y;
4328     if (domain == 0) rtrue;
4329
4330     ! Special rule: the directions (interpreted as the 12 walls of a room) are
4331     ! always in context.  (So, e.g., "examine north wall" is always legal.)
4332     ! (Unless we're parsing something like "all", because it would just slow
4333     ! things down then, or unless the context is "creature".)
4334
4335     if (indef_mode==0 && domain==actors_location
4336         && scope_reason==PARSING_REASON && context~=CREATURE_TOKEN)
4337             ScopeWithin(Compass);
4338
4339     ! Look through the objects in the domain, avoiding "objectloop" in case
4340     ! movements occur, e.g. when trying each_turn.
4341
4342     x = child(domain);
4343     while (x ~= 0) {
4344         y = sibling(x);
4345         ScopeWithin_O(x, nosearch, context);
4346         x = y;
4347     }
4348 ];
4349
4350 [ ScopeWithin_O domain nosearch context i ad n;
4351
4352     ! If the scope reason is unusual, don't parse.
4353
4354     if (scope_reason ~= PARSING_REASON or TALKING_REASON) {
4355         DoScopeAction(domain);
4356         jump DontAccept;
4357     }
4358
4359     ! "it" or "them" matches to the it-object only.  (Note that (1) this means
4360     ! that "it" will only be understood if the object in question is still
4361     ! in context, and (2) only one match can ever be made in this case.)
4362
4363     if (match_from <= num_words) {  ! If there's any text to match, that is
4364         wn = match_from;
4365         i = NounWord();
4366         if (i == 1 && player == domain) MakeMatch(domain, 1);
4367         if (i >= 2 && i < 128 && (LanguagePronouns-->i == domain)) MakeMatch(domain, 1);
4368     }
4369
4370     ! Construing the current word as the start of a noun, can it refer to the
4371     ! object?
4372
4373     wn = match_from;
4374     if (TryGivenObject(domain) > 0)
4375         if (indef_nspec_at > 0 && match_from ~= indef_nspec_at) {
4376             ! This case arises if the player has typed a number in
4377             ! which is hypothetically an indefinite descriptor:
4378             ! e.g. "take two clubs".  We have just checked the object
4379             ! against the word "clubs", in the hope of eventually finding
4380             ! two such objects.  But we also backtrack and check it
4381             ! against the words "two clubs", in case it turns out to
4382             ! be the 2 of Clubs from a pack of cards, say.  If it does
4383             ! match against "two clubs", we tear up our original
4384             ! assumption about the meaning of "two" and lapse back into
4385             ! definite mode.
4386
4387             wn = indef_nspec_at;
4388             if (TryGivenObject(domain) > 0) {
4389                 match_from = indef_nspec_at;
4390                 ResetDescriptors();
4391             }
4392             wn = match_from;
4393         }
4394
4395   .DontAccept;
4396
4397     ! Shall we consider the possessions of the current object, as well?
4398     ! Only if it's a container (so, for instance, if a dwarf carries a
4399     ! sword, then "drop sword" will not be accepted, but "dwarf, drop sword"
4400     ! will).
4401     ! Also, only if there are such possessions.
4402     !
4403     ! Notice that the parser can see "into" anything flagged as
4404     ! transparent - such as a dwarf whose sword you can get at.
4405
4406     if (child(domain) ~= 0 && domain ~= nosearch && IsSeeThrough(domain) == 1)
4407         ScopeWithin(domain,nosearch,context);
4408
4409     ! Drag any extras into context
4410
4411     ad = domain.&add_to_scope;
4412     if (ad ~= 0) {
4413
4414         ! Test if the property value is not an object.
4415         #Ifdef TARGET_ZCODE;
4416         i = (UnsignedCompare(ad-->0, top_object) > 0);
4417         #Ifnot; ! TARGET_GLULX
4418         i = (((ad-->0)->0) ~= $70);
4419         #Endif; ! TARGET_
4420
4421         if (i) {
4422             ats_flag = 2+context;
4423             RunRoutines(domain, add_to_scope);
4424             ats_flag = 0;
4425         }
4426         else {
4427             n = domain.#add_to_scope;
4428             for (i=0 : (WORDSIZE*i)<n : i++)
4429                 if (ad-->i)
4430                     ScopeWithin_O(ad-->i, 0, context);
4431         }
4432     }
4433 ];
4434
4435 [ AddToScope obj;
4436     if (ats_flag >= 2)
4437         ScopeWithin_O(obj, 0, ats_flag-2);
4438     if (ats_flag == 1) {
4439         if  (HasLightSource(obj)==1) ats_hls = 1;
4440     }
4441 ];
4442
4443 ! ----------------------------------------------------------------------------
4444 !  MakeMatch looks at how good a match is.  If it's the best so far, then
4445 !  wipe out all the previous matches and start a new list with this one.
4446 !  If it's only as good as the best so far, add it to the list.
4447 !  If it's worse, ignore it altogether.
4448 !
4449 !  The idea is that "red panic button" is better than "red button" or "panic".
4450 !
4451 !  number_matched (the number of words matched) is set to the current level
4452 !  of quality.
4453 !
4454 !  We never match anything twice, and keep at most 64 equally good items.
4455 ! ----------------------------------------------------------------------------
4456
4457 [ MakeMatch obj quality i;
4458     #Ifdef DEBUG;
4459     if (parser_trace >= 6) print "    Match with quality ",quality,"^";
4460     #Endif; ! DEBUG
4461     if (token_filter ~= 0 && UserFilter(obj) == 0) {
4462         #Ifdef DEBUG;
4463         if (parser_trace >= 6) print "    Match filtered out: token filter ", token_filter, "^";
4464         #Endif; ! DEBUG
4465         rtrue;
4466     }
4467     if (quality < match_length) rtrue;
4468     if (quality > match_length) { match_length = quality; number_matched = 0; }
4469     else {
4470         if (number_matched >= MATCH_LIST_SIZE) rtrue;
4471         for (i=0 : i<number_matched : i++)
4472             if (match_list-->i == obj) rtrue;
4473     }
4474     match_list-->number_matched++ = obj;
4475     #Ifdef DEBUG;
4476     if (parser_trace >= 6) print "    Match added to list^";
4477     #Endif; ! DEBUG
4478 ];
4479
4480 ! ----------------------------------------------------------------------------
4481 !  TryGivenObject tries to match as many words as possible in what has been
4482 !  typed to the given object, obj.  If it manages any words matched at all,
4483 !  it calls MakeMatch to say so, then returns the number of words (or 1
4484 !  if it was a match because of inadequate input).
4485 ! ----------------------------------------------------------------------------
4486
4487 [ TryGivenObject obj threshold k w j;
4488     #Ifdef DEBUG;
4489     if (parser_trace >= 5) print "    Trying ", (the) obj, " (", obj, ") at word ", wn, "^";
4490     #Endif; ! DEBUG
4491
4492     dict_flags_of_noun = 0;
4493
4494     !  If input has run out then always match, with only quality 0 (this saves
4495     !  time).
4496
4497     if (wn > num_words) {
4498         if (indef_mode ~= 0)
4499             dict_flags_of_noun = DICT_X654;  ! Reject "plural" bit
4500         MakeMatch(obj,0);
4501         #Ifdef DEBUG;
4502         if (parser_trace >= 5) print "    Matched (0)^";
4503         #Endif; ! DEBUG
4504         return 1;
4505     }
4506
4507     !  Ask the object to parse itself if necessary, sitting up and taking notice
4508     !  if it says the plural was used:
4509
4510     if (obj.parse_name~=0) {
4511         parser_action = NULL; j=wn;
4512         k = RunRoutines(obj,parse_name);
4513         if (k > 0) {
4514             wn=j+k;
4515
4516           .MMbyPN;
4517
4518             if (parser_action == ##PluralFound)
4519                 dict_flags_of_noun = dict_flags_of_noun | DICT_PLUR;
4520
4521             if (dict_flags_of_noun & DICT_PLUR) {
4522                 if (~~allow_plurals) k = 0;
4523                 else {
4524                     if (indef_mode == 0) {
4525                         indef_mode = 1; indef_type = 0; indef_wanted = 0;
4526                     }
4527                     indef_type = indef_type | PLURAL_BIT;
4528                     if (indef_wanted == 0) indef_wanted = 100;
4529                 }
4530             }
4531
4532             #Ifdef DEBUG;
4533             if (parser_trace >= 5) print "    Matched (", k, ")^";
4534             #Endif; ! DEBUG
4535             MakeMatch(obj,k);
4536             return k;
4537         }
4538         if (k == 0) jump NoWordsMatch;
4539         wn = j;
4540     }
4541
4542     ! The default algorithm is simply to count up how many words pass the
4543     ! Refers test:
4544
4545     parser_action = NULL;
4546
4547     w = NounWord();
4548
4549     if (w == 1 && player == obj) { k=1; jump MMbyPN; }
4550
4551     if (w >= 2 && w < 128 && (LanguagePronouns-->w == obj)) { k = 1; jump MMbyPN; }
4552
4553     j = --wn;
4554     threshold = ParseNoun(obj);
4555     if (threshold == -1) {
4556         LibraryExtensions.ext_number_1 = wn;    ! Set the "between calls" functionality to
4557         LibraryExtensions.BetweenCalls = LibraryExtensions.RestoreWN;
4558         threshold = LibraryExtensions.RunWhile(ext_parsenoun, -1, obj);
4559         LibraryExtensions.BetweenCalls = 0;     ! Turn off the "between calls" functionality
4560     }
4561     #Ifdef DEBUG;
4562     if (threshold >= 0 && parser_trace >= 5) print "    ParseNoun returned ", threshold, "^";
4563     #Endif; ! DEBUG
4564     if (threshold < 0) wn++;
4565     if (threshold > 0) { k = threshold; jump MMbyPN; }
4566
4567     if (threshold == 0 || Refers(obj,wn-1) == 0) {
4568       .NoWordsMatch;
4569         if (indef_mode ~= 0) {
4570             k = 0; parser_action = NULL;
4571             jump MMbyPN;
4572         }
4573         rfalse;
4574     }
4575
4576     if (threshold < 0) {
4577         threshold = 1;
4578         dict_flags_of_noun = (w->#dict_par1) & (DICT_X654+DICT_PLUR);!$$01110100;
4579         w = NextWord();
4580         while (Refers(obj, wn-1)) {
4581             threshold++;
4582             if (w)
4583                dict_flags_of_noun = dict_flags_of_noun | ((w->#dict_par1) & (DICT_X654+DICT_PLUR));
4584             w = NextWord();
4585         }
4586     }
4587
4588     k = threshold;
4589     jump MMbyPN;
4590 ];
4591
4592 ! ----------------------------------------------------------------------------
4593 !  Refers works out whether the word at number wnum can refer to the object
4594 !  obj, returning true or false.  The standard method is to see if the
4595 !  word is listed under "name" for the object, but this is more complex
4596 !  in languages other than English.
4597 ! ----------------------------------------------------------------------------
4598
4599 [ Refers obj wnum   wd k l m;
4600     if (obj == 0 || wnum <= 0) rfalse;
4601
4602     #Ifdef LanguageRefers;
4603     k = LanguageRefers(obj,wnum); if (k >= 0) return k;
4604     #Endif; ! LanguageRefers
4605
4606     k = wn; wn = wnum; wd = NextWordStopped(); wn = k;
4607
4608     if (parser_inflection_func) {
4609         k = parser_inflection(obj, wd);
4610         if (k >= 0) return k;
4611         m = -k;
4612     }
4613     else
4614         m = parser_inflection;
4615
4616     k = obj.&m; l = (obj.#m)/WORDSIZE-1;
4617     for (m=0 : m<=l : m++)
4618         if (wd == k-->m) rtrue;
4619     rfalse;
4620 ];
4621
4622 [ WordInProperty wd obj prop k l m;
4623     k = obj.&prop; l = (obj.#prop)/WORDSIZE-1;
4624     for (m=0 : m<=l : m++)
4625         if (wd == k-->m) rtrue;
4626     rfalse;
4627 ];
4628
4629 [ DictionaryLookup b l i;
4630     for (i=0 : i<l : i++) buffer2->(WORDSIZE+i) = b->i;
4631     SetKeyBufLength(l, buffer2);
4632     Tokenise__(buffer2, parse2);
4633     return parse2-->1;
4634 ];
4635
4636 ! ----------------------------------------------------------------------------
4637 !  NounWord (which takes no arguments) returns:
4638 !
4639 !   0  if the next word is unrecognised or does not carry the "noun" bit in
4640 !      its dictionary entry,
4641 !   1  if a word meaning "me",
4642 !   the index in the pronoun table (plus 2) of the value field of a pronoun,
4643 !      if the word is a pronoun,
4644 !   the address in the dictionary if it is a recognised noun.
4645 !
4646 !  The "current word" marker moves on one.
4647 ! ----------------------------------------------------------------------------
4648
4649 [ NounWord i j s;
4650     i = NextWord();
4651     if (i == 0) rfalse;
4652     if (i == ME1__WD or ME2__WD or ME3__WD) return 1;
4653     s = LanguagePronouns-->0;
4654     for (j=1 : j<=s : j=j+3)
4655         if (i == LanguagePronouns-->j)
4656             return j+2;
4657     if ((i->#dict_par1) & DICT_NOUN == 0) rfalse;
4658     return i;
4659 ];
4660
4661 ! ----------------------------------------------------------------------------
4662 !  NextWord (which takes no arguments) returns:
4663 !
4664 !  0            if the next word is unrecognised,
4665 !  comma_word   if a comma
4666 !  THEN1__WD    if a full stop
4667 !  or the dictionary address if it is recognised.
4668 !  The "current word" marker is moved on.
4669 !
4670 !  NextWordStopped does the same, but returns -1 when input has run out
4671 ! ----------------------------------------------------------------------------
4672
4673 #Ifdef TARGET_ZCODE;
4674
4675 [ NextWord i j;
4676     if (wn <= 0 || wn > parse->1) { wn++; rfalse; }
4677     i = wn*2-1; wn++;
4678     j = parse-->i;
4679     if (j == ',//') j = comma_word;
4680     if (j == './/') j = THEN1__WD;
4681     return j;
4682 ];
4683
4684 [ NextWordStopped;
4685     if (wn > parse->1) { wn++; return -1; }
4686     return NextWord();
4687 ];
4688
4689 [ WordAddress wordnum p b;  ! Absolute addr of 'wordnum' string in buffer
4690     if (p==0) p=parse;
4691     if (b==0) b=buffer;
4692     return b + p->(wordnum*4+1);
4693 ];
4694
4695 [ WordLength wordnum p;     ! Length of 'wordnum' string in buffer
4696     if (p==0) p=parse;
4697     return p->(wordnum*4);
4698 ];
4699
4700 [ WordValue wordnum p;      ! Dictionary value of 'wordnum' string in buffer
4701     if (p==0) p=parse;
4702     return p-->(wordnum*2-1);
4703 ];
4704
4705 [ NumberWords p;            ! Number of parsed strings in buffer
4706     if (p==0) p=parse;
4707     return p->1;
4708 ];
4709
4710 [ GetKeyBufLength b;        ! Number of typed chars in buffer
4711     if (b==0) b=buffer;
4712     return b->1;
4713 ];
4714
4715 [ SetKeyBufLength n b;      ! Update number of typed chars in buffer
4716     if (b==0) b=buffer;
4717     if (n > INPUT_BUFFER_LEN-WORDSIZE) n=INPUT_BUFFER_LEN-WORDSIZE;
4718     b->1 = n;
4719 ];
4720
4721 #Ifnot; ! TARGET_GLULX
4722
4723 [ NextWord i j;
4724     if (wn <= 0 || wn > parse-->0) { wn++; rfalse; }
4725     i = wn*3-2; wn++;
4726     j = parse-->i;
4727     if (j == ',//') j=comma_word;
4728     if (j == './/') j=THEN1__WD;
4729     return j;
4730 ];
4731
4732 [ NextWordStopped;
4733     if (wn > parse-->0) {
4734         wn++;
4735         return -1;
4736     }
4737     return NextWord();
4738 ];
4739
4740 [ WordAddress wordnum p b;  ! Absolute addr of 'wordnum' string in buffer
4741     if (p==0) p=parse;
4742     if (b==0) b=buffer;
4743     return b + p-->(wordnum*3);
4744 ];
4745
4746 [ WordLength wordnum p;     ! Length of 'wordnum' string in buffer
4747     if (p==0) p=parse;
4748     return p-->(wordnum*3-1);
4749 ];
4750
4751 [ WordValue wordnum p;      ! Dictionary value of 'wordnum' string in buffer
4752     if (p==0) p=parse;
4753     return p-->(wordnum*3-2);
4754 ];
4755
4756 [ NumberWords p;            ! Number of parsed strings in buffer
4757     if (p==0) p=parse;
4758     return p-->0;
4759 ];
4760
4761 [ GetKeyBufLength b;        ! Number of typed chars in buffer
4762     if (b==0) b=buffer;
4763     return b-->0;
4764 ];
4765
4766 [ SetKeyBufLength n b;      ! Update number of typed chars in buffer
4767     if (b==0) b=buffer;
4768     if (n > INPUT_BUFFER_LEN-WORDSIZE) n=INPUT_BUFFER_LEN-WORDSIZE;
4769     b-->0 = n;
4770 ];
4771
4772 #Endif; ! TARGET_
4773
4774 ! ----------------------------------------------------------------------------
4775 !  TryNumber is the only routine which really does any character-level
4776 !  parsing, since that's normally left to the Z-machine.
4777 !  It takes word number "wordnum" and tries to parse it as an (unsigned)
4778 !  decimal number, returning
4779 !
4780 !  -1000                if it is not a number
4781 !  the number           if it has between 1 and 4 digits
4782 !  10000                if it has 5 or more digits.
4783 !
4784 !  (The danger of allowing 5 digits is that Z-machine integers are only
4785 !  16 bits long, and anyway this isn't meant to be perfect.)
4786 !
4787 !  Using NumberWord, it also catches "one" up to "twenty".
4788 !
4789 !  Note that a game can provide a ParseNumber routine which takes priority,
4790 !  to enable parsing of odder numbers ("x45y12", say).
4791 ! ----------------------------------------------------------------------------
4792
4793 [ TryNumber wordnum   i j c num len mul tot d digit;
4794     i = wn; wn = wordnum; j = NextWord(); wn = i;
4795     j = NumberWord(j);
4796     if (j >= 1) return j;
4797
4798     num = WordAddress(wordnum); len = WordLength(wordnum);
4799
4800     tot = ParseNumber(num, len);
4801     if (tot == 0) tot = LibraryExtensions.RunWhile(ext_parsenumber, 0, num, len);
4802
4803     if (tot ~= 0) return tot;
4804
4805     if (len >= 4) mul=1000;
4806     if (len == 3) mul=100;
4807     if (len == 2) mul=10;
4808     if (len == 1) mul=1;
4809
4810     tot = 0; c = 0;
4811
4812     for (c = 0 : c < len : c++) {
4813         digit=num->c;
4814         if (digit == '0') { d = 0; jump digok; }
4815         if (digit == '1') { d = 1; jump digok; }
4816         if (digit == '2') { d = 2; jump digok; }
4817         if (digit == '3') { d = 3; jump digok; }
4818         if (digit == '4') { d = 4; jump digok; }
4819         if (digit == '5') { d = 5; jump digok; }
4820         if (digit == '6') { d = 6; jump digok; }
4821         if (digit == '7') { d = 7; jump digok; }
4822         if (digit == '8') { d = 8; jump digok; }
4823         if (digit == '9') { d = 9; jump digok; }
4824         return -1000;
4825      .digok;
4826         tot = tot+mul*d; mul = mul/10;
4827     }
4828     if (len > 4) tot=10000;
4829     return tot;
4830 ];
4831
4832 ! ----------------------------------------------------------------------------
4833 !  AnyNumber is a general parsing routine which accepts binary, hexadecimal
4834 !  and decimal numbers up to the full Zcode/Glulx ranges.
4835 ! ----------------------------------------------------------------------------
4836
4837 #Ifdef TARGET_ZCODE;                ! decimal range is -32768 to 32767
4838 Constant MAX_DECIMAL_SIZE 5;
4839 Constant MAX_DECIMAL_BASE 3276;
4840 #Ifnot; ! TARGET_GLULX              ! decimal range is -2147483648 to 2147483647
4841 Constant MAX_DECIMAL_SIZE 10;
4842 Constant MAX_DECIMAL_BASE 214748364;
4843 #Endif; ! TARGET_
4844
4845 [ AnyNumber
4846     wa we sign base digit digit_count num;
4847
4848     wa = WordAddress(wn); we = wa + WordLength(wn);
4849     sign = 1; base = 10;
4850     if     (wa->0 == '-') { sign = -1; wa++; }
4851     else {
4852         if (wa->0 == '$') { base = 16; wa++; }
4853         if (wa->0 == '$') { base = 2;  wa++; }
4854     }
4855     if (wa >= we) return GPR_FAIL;  ! no digits after -/$
4856     while (wa->0 == '0') wa++;      ! skip leading zeros
4857     for (num=0,digit_count=1 : wa<we : wa++,digit_count++) {
4858         switch (wa->0) {
4859           '0' to '9': digit = wa->0 - '0';
4860           'A' to 'F': digit = wa->0 - 'A' + 10;
4861           'a' to 'f': digit = wa->0 - 'a' + 10;
4862           default:    return GPR_FAIL;
4863         }
4864         if (digit >= base) return GPR_FAIL;
4865         switch (base) {
4866           16:     if (digit_count > 2*WORDSIZE)  return GPR_FAIL;
4867           2:      if (digit_count > 8*WORDSIZE)  return GPR_FAIL;
4868           10:
4869             if (digit_count >  MAX_DECIMAL_SIZE) return GPR_FAIL;
4870             if (digit_count == MAX_DECIMAL_SIZE) {
4871                 if (num >  MAX_DECIMAL_BASE)     return GPR_FAIL;
4872                 if (num == MAX_DECIMAL_BASE) {
4873                     if (sign == 1  && digit > 7) return GPR_FAIL;
4874                     if (sign == -1 && digit > 8) return GPR_FAIL;
4875                 }
4876             }
4877         }
4878         num = base*num + digit;
4879     }
4880    parsed_number = num * sign;
4881     wn++;
4882     return GPR_NUMBER;
4883 ];
4884
4885 ! ----------------------------------------------------------------------------
4886 !  GetGender returns 0 if the given animate object is female, and 1 if male
4887 !  (not all games will want such a simple decision function!)
4888 ! ----------------------------------------------------------------------------
4889
4890 [ GetGender person;
4891     if (person hasnt female) rtrue;
4892     rfalse;
4893 ];
4894
4895 [ GetGNAOfObject obj case gender;
4896     if (obj hasnt animate) case = 6;
4897     if (obj has male) gender = male;
4898     if (obj has female) gender = female;
4899     if (obj has neuter) gender = neuter;
4900     if (gender == 0) {
4901         if (case == 0) gender = LanguageAnimateGender;
4902         else gender = LanguageInanimateGender;
4903     }
4904     if (gender == female)   case = case + 1;
4905     if (gender == neuter)   case = case + 2;
4906     if (obj has pluralname) case = case + 3;
4907     return case;
4908 ];
4909
4910 ! ----------------------------------------------------------------------------
4911 !  Converting between dictionary addresses and entry numbers
4912 ! ----------------------------------------------------------------------------
4913
4914 #Ifdef TARGET_ZCODE;
4915
4916 [ Dword__No w; return (w-(HDR_DICTIONARY-->0 + 7))/9; ];
4917 [ No__Dword n; return HDR_DICTIONARY-->0 + 7 + 9*n; ];
4918
4919 #Ifnot; ! TARGET_GLULX
4920
4921 ! In Glulx, dictionary entries *are* addresses.
4922 [ Dword__No w; return w; ];
4923 [ No__Dword n; return n; ];
4924
4925 #Endif; ! TARGET_
4926
4927 ! ----------------------------------------------------------------------------
4928 !  For copying buffers
4929 ! ----------------------------------------------------------------------------
4930
4931 #Ifdef TARGET_ZCODE;
4932
4933 [ CopyBuffer bto bfrom i size;
4934     size = bto->0;
4935     for (i=1 : i<=size : i++) bto->i = bfrom->i;
4936 ];
4937
4938 #Ifnot; ! TARGET_GLULX
4939
4940 [ CopyBuffer bto bfrom i;
4941     for (i=0 : i<INPUT_BUFFER_LEN : i++) bto->i = bfrom->i;
4942 ];
4943
4944 #Endif; ! TARGET_
4945
4946 ! ----------------------------------------------------------------------------
4947 !  Provided for use by language definition files
4948 ! ----------------------------------------------------------------------------
4949
4950 #Ifdef TARGET_ZCODE;
4951
4952 [ LTI_Insert i ch  b y;
4953
4954     ! Protect us from strict mode, as this isn't an array in quite the
4955     ! sense it expects
4956     b = buffer;
4957
4958     ! Insert character ch into buffer at point i.
4959     ! Being careful not to let the buffer possibly overflow:
4960     y = b->1;
4961     if (y > b->0) y = b->0;
4962
4963     ! Move the subsequent text along one character:
4964     for (y=y+2 : y>i : y--) b->y = b->(y-1);
4965     b->i = ch;
4966
4967     ! And the text is now one character longer:
4968     if (b->1 < b->0) (b->1)++;
4969 ];
4970
4971 #Ifnot; ! TARGET_GLULX
4972
4973 [ LTI_Insert i ch  b y;
4974
4975     ! Protect us from strict mode, as this isn't an array in quite the
4976     ! sense it expects
4977     b = buffer;
4978
4979     ! Insert character ch into buffer at point i.
4980     ! Being careful not to let the buffer possibly overflow:
4981     y = b-->0;
4982     if (y > INPUT_BUFFER_LEN) y = INPUT_BUFFER_LEN;
4983
4984     ! Move the subsequent text along one character:
4985     for (y=y+WORDSIZE : y>i : y--) b->y = b->(y-1);
4986     b->i = ch;
4987
4988     ! And the text is now one character longer:
4989     if (b-->0 < INPUT_BUFFER_LEN) (b-->0)++;
4990 ];
4991
4992 #Endif; ! TARGET_
4993
4994 ! ============================================================================
4995
4996 [ PronounsSub x y c d;
4997     L__M(##Pronouns, 1);
4998
4999     c = (LanguagePronouns-->0)/3;
5000     if (player ~= selfobj) c++;
5001
5002     if (c == 0) return L__M(##Pronouns, 4);
5003
5004     for (x=1,d=0 : x<=LanguagePronouns-->0 : x=x+3) {
5005         print "~", (address) LanguagePronouns-->x, "~ ";
5006         y = LanguagePronouns-->(x+2);
5007         if (y == NULL) L__M(##Pronouns, 3);
5008         else {
5009             L__M(##Pronouns, 2);
5010             print (the) y;
5011         }
5012         d++;
5013         if (d < c-1) print (string) COMMA__TX;
5014         if (d == c-1) print (SerialComma) c, (string) AND__TX;
5015     }
5016     if (player ~= selfobj) {
5017         print "~", (address) ME1__WD, "~ "; L__M(##Pronouns, 2);
5018         c = player; player = selfobj;
5019         print (the) c; player = c;
5020     }
5021     L__M(##Pronouns, 5);
5022 ];
5023
5024 [ SetPronoun dword value x;
5025     for (x=1 : x<=LanguagePronouns-->0 : x=x+3)
5026         if (LanguagePronouns-->x == dword) {
5027             LanguagePronouns-->(x+2) = value; return;
5028         }
5029     RunTimeError(12);
5030 ];
5031
5032 [ PronounValue dword x;
5033     for (x=1 : x<=LanguagePronouns-->0 : x=x+3)
5034         if (LanguagePronouns-->x == dword)
5035             return LanguagePronouns-->(x+2);
5036     return 0;
5037 ];
5038
5039 [ ResetVagueWords obj; PronounNotice(obj); ];
5040
5041 #Ifdef EnglishNaturalLanguage;
5042
5043 [ PronounOldEnglish;
5044     if (itobj ~= old_itobj)   SetPronoun('it', itobj);
5045     if (himobj ~= old_himobj) SetPronoun('him', himobj);
5046     if (herobj ~= old_herobj) SetPronoun('her', herobj);
5047     old_itobj = itobj; old_himobj = himobj; old_herobj = herobj;
5048 ];
5049
5050 #Endif; !EnglishNaturalLanguage
5051
5052 [ PronounNotice obj x bm;
5053     if (obj == player) return;
5054
5055     #Ifdef EnglishNaturalLanguage;
5056     PronounOldEnglish();
5057     #Endif; ! EnglishNaturalLanguage
5058
5059     bm = PowersOfTwo_TB-->(GetGNAOfObject(obj));
5060
5061     for (x=1 : x<=LanguagePronouns-->0 : x=x+3)
5062         if (bm & (LanguagePronouns-->(x+1)) ~= 0)
5063             LanguagePronouns-->(x+2) = obj;
5064
5065     #Ifdef EnglishNaturalLanguage;
5066     itobj  = PronounValue('it');  old_itobj  = itobj;
5067     himobj = PronounValue('him'); old_himobj = himobj;
5068     herobj = PronounValue('her'); old_herobj = herobj;
5069     #Endif; ! EnglishNaturalLanguage
5070 ];
5071
5072 ! ============================================================================
5073 !  End of the parser proper: the remaining routines are its front end.
5074 ! ----------------------------------------------------------------------------
5075
5076 Object  InformLibrary "(Inform Library)"
5077   with  play [ i j k l;
5078
5079             #Ifdef TARGET_ZCODE;
5080             ZZInitialise();
5081             #Ifnot; ! TARGET_GLULX
5082             GGInitialise();
5083             #Endif; ! TARGET_
5084
5085             GamePrologue();
5086             while (~~deadflag) {    ! everything happens in this loop
5087
5088                 #Ifdef EnglishNaturalLanguage;
5089                 PronounOldEnglish();
5090                 old_itobj = PronounValue('it');
5091                 old_himobj = PronounValue('him');
5092                 old_herobj = PronounValue('her');
5093                 #Endif; ! EnglishNaturalLanguage
5094
5095               .very__late__error;
5096
5097                 if (score ~= last_score) {
5098                     if (notify_mode == 1) NotifyTheScore();
5099                     last_score = score;
5100                 }
5101
5102               .late__error;
5103
5104                 inputobjs-->0 = 0; inputobjs-->1 = 0;
5105                 inputobjs-->2 = 0; inputobjs-->3 = 0; meta = false;
5106
5107                 ! The Parser writes its results into inputobjs and meta,
5108                 ! a flag indicating a "meta-verb".  This can only be set for
5109                 ! commands by the player, not for orders to others.
5110
5111                 InformParser.parse_input(inputobjs);
5112
5113                 action = inputobjs-->0;
5114
5115                 ! --------------------------------------------------------------
5116
5117                 ! Reverse "give fred biscuit" into "give biscuit to fred"
5118
5119                 if (action == ##GiveR or ##ShowR) {
5120                     i = inputobjs-->2; inputobjs-->2 = inputobjs-->3; inputobjs-->3 = i;
5121                     if (action == ##GiveR) action = ##Give; else action = ##Show;
5122                 }
5123
5124                 ! Convert "P, tell me about X" to "ask P about X"
5125
5126                 if (action == ##Tell && inputobjs-->2 == player && actor ~= player) {
5127                     inputobjs-->2 = actor; actor = player; action = ##Ask;
5128                 }
5129
5130                 ! Convert "ask P for X" to "P, give X to me"
5131
5132                 if (action == ##AskFor && inputobjs-->2 ~= player && actor == player) {
5133                     actor = inputobjs-->2; inputobjs-->2 = inputobjs-->3;
5134                     inputobjs-->3 = player; action = ##Give;
5135                 }
5136
5137                 ! For old, obsolete code: special_word contains the topic word
5138                 ! in conversation
5139
5140                 if (action == ##Ask or ##Tell or ##Answer)
5141                     special_word = special_number1;
5142
5143                 !  --------------------------------------------------------------
5144
5145                 multiflag = false; onotheld_mode = notheld_mode; notheld_mode = false;
5146                 ! For implicit taking and multiple object detection
5147
5148               .begin__action;
5149                 inp1 = 0; inp2 = 0; i = inputobjs-->1;
5150                 if (i >= 1) inp1 = inputobjs-->2;
5151                 if (i >= 2) inp2 = inputobjs-->3;
5152
5153                 ! inp1 and inp2 hold: object numbers, or 0 for "multiple object",
5154                 ! or 1 for "a number or dictionary address"
5155
5156                 if (inp1 == 1) noun = special_number1; else noun = inp1;
5157                 if (inp2 == 1) {
5158                     if (inp1 == 1) second = special_number2;
5159                     else           second = special_number1;
5160                 }
5161                 else second = inp2;
5162
5163                 !  -------------------------------------------------------------
5164
5165 !print "inp1:          ", (name) inp1, "^";
5166 !print "inp2:          ", (name) inp2, "^";
5167 !print "inputobjs-->1: ", (name) inputobjs-->1, "^";
5168 !print "inputobjs-->2: ", (name) inputobjs-->2, "^";
5169 !print "inputobjs-->3: ", (name) inputobjs-->3, "^";
5170 !print "noun:          ", (name) noun, "^";
5171 !print "second:        ", (name) second, "^";
5172 !print "actor:         ", (name) actor, "^";
5173 !print "---^";
5174
5175                 ! --------------------------------------------------------------
5176                 ! Generate the action...
5177
5178                 if ((i == 0) ||
5179                     (i == 1 && inp1 ~= 0) ||
5180                     (i == 2 && inp1 ~= 0 && inp2 ~= 0)) {
5181
5182                     if (actor ~= player) {
5183                          switch (self.actor_act(actor, action, noun, second)) {
5184                              ACTOR_ACT_ABORT_NOTUNDERSTOOD: jump begin__action;
5185                              default: jump turn__end;
5186                          }
5187
5188                     }
5189
5190
5191                     self.begin_action(action, noun, second, 0);
5192                     jump turn__end;
5193                 }
5194
5195                 ! ...unless a multiple object must be substituted.  First:
5196                 ! (a) check the multiple list isn't empty;
5197                 ! (b) warn the player if it has been cut short because too long;
5198                 ! (c) generate a sequence of actions from the list
5199                 !     (stopping in the event of death or movement away).
5200
5201                 multiflag = true;
5202                 j = multiple_object-->0;
5203                 if (j == 0) {
5204                     L__M(##Miscellany, 2);
5205                     jump late__error;
5206                 }
5207                 if (toomany_flag) {
5208                     toomany_flag = false;
5209                     L__M(##Miscellany, 1);
5210                 }
5211                 i = location;
5212                 for (k=1 : k<=j : k++) {
5213                     if (deadflag) break;
5214                     if (location ~= i) {
5215                         L__M(##Miscellany, 51);
5216                         break;
5217                     }
5218                     l = multiple_object-->k;
5219                     PronounNotice(l);
5220                     print (name) l, (string) COLON__TX, " ";
5221                     if (inp1 == 0) {
5222                         inp1 = l;
5223                         switch (self.actor_act(actor, action, l, second)) {
5224                           ACTOR_ACT_ABORT_NOTUNDERSTOOD: jump begin__action;
5225                           ACTOR_ACT_ABORT_ORDER: jump turn__end;
5226                         }
5227                         inp1 = 0;
5228                     }
5229                     else {
5230                         inp2 = l;
5231                         if (self.actor_act(actor, action, noun, l) == ACTOR_ACT_ABORT_NOTUNDERSTOOD)
5232                             jump begin__action;
5233                         inp2 = 0;
5234                     }
5235                 }
5236
5237                 ! --------------------------------------------------------------
5238
5239               .turn__end;
5240
5241                 ! No time passes if either (i) the verb was meta, or
5242                 ! (ii) we've only had the implicit take before the "real"
5243                 ! action to follow.
5244
5245                 if (notheld_mode == 1) { NoteObjectAcquisitions(); continue; }
5246                 if (meta) continue;
5247                 if (~~deadflag) self.end_turn_sequence();
5248                 else if (START_MOVE ~= 1) turns++;
5249             } ! end of while()
5250
5251             if (deadflag ~= 2 && AfterLife() == false)
5252                  LibraryExtensions.RunAll(ext_afterlife);
5253             if (deadflag == 0) jump very__late__error;
5254             GameEpilogue();
5255         ], ! end of 'play' property
5256
5257         end_turn_sequence [;
5258             AdvanceWorldClock();        if (deadflag) return;
5259             RunTimersAndDaemons();      if (deadflag) return;
5260             RunEachTurnProperties();    if (deadflag) return;
5261             if (TimePasses() == 0)  LibraryExtensions.RunAll(ext_timepasses);
5262                                         if (deadflag) return;
5263             AdjustLight();              if (deadflag) return;
5264             NoteObjectAcquisitions();
5265         ],
5266
5267         ! The first time we call self.actor_act(), set the fifth
5268         ! parameter to true.  Details can be seen in
5269         ! 1d95759b1bd6674509d6cf9161e6db3da3831f10 and in Issues #23 and
5270         ! #30 (Mantis 1813 and 1885)
5271         actor_act [ p a n s ft  j sp sa sn ss;
5272             sp = actor; actor = p;
5273             if (p ~= player) {
5274                 ! The player's "orders" property can refuse to allow
5275                 ! conversation here, by returning true.  If not, the order is
5276                 ! sent to the other person's "orders" property.  If that also
5277                 ! returns false, then: if it was a misunderstood command
5278                 ! anyway, it is converted to an Answer action (thus
5279                 ! "floyd, grrr" ends up as "say grrr to floyd").  If it was a
5280                 ! good command, it is finally offered to the Order: part of
5281                 ! the other person's "life" property, the old-fashioned
5282                 ! way of dealing with conversation.
5283
5284                 sa = action; sn = noun; ss = second;
5285                 action = a; noun = n; second = s;
5286                 j = RunRoutines(player, orders);
5287                 if (j == 0) {
5288                     j = RunRoutines(actor, orders);
5289                     if (j == 0) {
5290                         if (action == ##NotUnderstood) {
5291                             inputobjs-->3 = actor; actor = player; action = ##Answer;
5292                             ! abort, not resetting action globals
5293                             return ACTOR_ACT_ABORT_NOTUNDERSTOOD;
5294                         }
5295                         if (RunLife(actor, ##Order) == 0) {
5296                             L__M(##Order, 1, actor);
5297                             if (~~ft)
5298                                 return ACTOR_ACT_ABORT_ORDER;
5299                         }
5300                     }
5301                 }
5302                 action = sa; noun = sn; second = ss;
5303                 if (ft)
5304                     return ACTOR_ACT_ABORT_ORDER;
5305             }
5306             else
5307                 if (~~ft)
5308                     self.begin_action(a, n, s, 0);
5309
5310             actor = sp;
5311             return ACTOR_ACT_OK;
5312         ],
5313
5314         begin_action [ a n s source   sa sn ss;
5315             sa = action; sn = noun; ss = second;
5316             action = a; noun = n; second = s;
5317             #Ifdef DEBUG;
5318             if (debug_flag & DEBUG_ACTIONS) TraceAction(source);
5319             #Ifnot;
5320             source = 0;
5321             #Endif; ! DEBUG
5322
5323             #Iftrue (Grammar__Version == 1);
5324             if ((meta || BeforeRoutines() == false) && action < 256) {
5325                 #Ifdef INFIX;
5326                 if (infix_verb) {
5327                     if (BeforeRoutines() == false)
5328                         ActionPrimitive();
5329                 } else ActionPrimitive();
5330                 #Ifnot;
5331                 ActionPrimitive();
5332                 #Endif;
5333             }
5334             #Ifnot;
5335             if ((meta || BeforeRoutines() == false) && action < 4096) {
5336                 #Ifdef INFIX;
5337                 if (infix_verb) {
5338                     if (BeforeRoutines() == false)
5339                         ActionPrimitive();
5340                 } else ActionPrimitive();
5341                 #Ifnot;
5342                 ActionPrimitive();
5343                 #Endif;
5344             }
5345             #Endif; ! Grammar__Version
5346             action = sa; noun = sn; second = ss;
5347         ],
5348   has   proper;
5349
5350 ! ----------------------------------------------------------------------------
5351 ! Routines called before and after main 'play' loop
5352
5353 [ GamePrologue i j;
5354     before_first_turn = true;
5355     for (i=1 : i<=100 : i++) j = random(i);
5356
5357     ChangeDefault(cant_go, CANTGO__TX);
5358
5359     real_location = thedark;
5360     player = selfobj; actor = player;
5361     selfobj.capacity = MAX_CARRIED; ! ### change?
5362
5363     #Ifdef LanguageInitialise;
5364     LanguageInitialise();
5365     #Endif; ! LanguageInitialise
5366
5367     #Ifdef EnglishNaturalLanguage;
5368     old_itobj = itobj; old_himobj = himobj; old_herobj = herobj;
5369     #Endif;! EnglishNaturalLanguage
5370
5371     new_line;
5372     LibraryExtensions.RunAll(ext_initialise);
5373     j = Initialise();
5374     last_score = score;
5375     initial_lookmode = lookmode;
5376
5377     objectloop (i in player) give i moved ~concealed;
5378     move player to location;
5379     while (parent(location)) location = parent(location);
5380     real_location = location;
5381     MoveFloatingObjects();
5382
5383     actor = player; ! resync, because player may have been changed in Initialise()
5384     actors_location = location;
5385
5386     lightflag = OffersLight(parent(player));
5387     if (lightflag == 0) location = thedark;
5388
5389     if (j ~= 2) Banner();
5390 #Ifndef NOINITIAL_LOOK;
5391     <Look>;
5392 #Endif;
5393     before_first_turn = false;
5394 ];
5395
5396 [ GameEpilogue;
5397     if (score ~= last_score) {
5398         if (notify_mode == 1) NotifyTheScore();
5399         last_score = score;
5400     }
5401     print "^^    ";
5402     #Ifdef TARGET_ZCODE;
5403     #IfV5; style bold; #Endif; ! V5
5404     #Ifnot; ! TARGET_GLULX
5405     glk_set_style(style_Alert);
5406     #Endif; ! TARGET_
5407     print "***";
5408     switch (deadflag) {
5409       1:        L__M(##Miscellany, 3);
5410       2:        L__M(##Miscellany, 4);
5411       default:  print " ";
5412                 if (DeathMessage() == false)
5413                     LibraryExtensions.RunAll(ext_deathmessage);
5414                 print " ";
5415     }
5416     print "***";
5417     #Ifdef TARGET_ZCODE;
5418     #IfV5; style roman; #Endif; ! V5
5419     #Ifnot; ! TARGET_GLULX
5420     glk_set_style(style_Normal);
5421     #Endif; ! TARGET_
5422     print "^^";
5423     #Ifndef NO_SCORE;
5424     print "^";
5425     #Endif; ! NO_SCORE
5426     Epilogue();
5427     ScoreSub();
5428     DisplayStatus();
5429     AfterGameOver();
5430 ];
5431
5432 ! ----------------------------------------------------------------------------
5433 ! Routines called at end of each turn
5434
5435 [ AdvanceWorldClock;
5436     turns++;
5437     if (the_time ~= NULL) {
5438         if (time_rate >= 0) the_time=the_time+time_rate;
5439         else {
5440             time_step--;
5441             if (time_step == 0) {
5442                 the_time++;
5443                 time_step = -time_rate;
5444             }
5445         }
5446         the_time = the_time % 1440;
5447     }
5448 ];
5449
5450 [ RunTimersAndDaemons i j;
5451     #Ifdef DEBUG;
5452     if (debug_flag & DEBUG_TIMERS) {
5453         for (i=0 : i<active_timers : i++) {
5454             j = the_timers-->i;
5455             if (j ~= 0) {
5456                 print (name) (j&~WORD_HIGHBIT), ": ";
5457                 if (j & WORD_HIGHBIT) print "daemon";
5458                 else
5459                     print "timer with ", j.time_left, " turns to go";
5460                 new_line;
5461             }
5462         }
5463     }
5464     #Endif; ! DEBUG
5465
5466     for (i=0 : i<active_timers : i++) {
5467         if (deadflag) return;
5468         j = the_timers-->i;
5469         if (j ~= 0) {
5470             if (j & WORD_HIGHBIT) RunRoutines(j&~WORD_HIGHBIT, daemon);
5471             else {
5472                 if (j.time_left == 0) {
5473                     StopTimer(j);
5474                     RunRoutines(j, time_out);
5475                 }
5476                 else
5477                     j.time_left = j.time_left-1;
5478             }
5479         }
5480     }
5481 ];
5482
5483 [ RunEachTurnProperties;
5484     scope_reason = EACH_TURN_REASON; verb_word = 0;
5485     DoScopeAction(location);
5486     SearchScope(ScopeCeiling(player), player, 0);
5487     scope_reason = PARSING_REASON;
5488 ];
5489
5490 ! ----------------------------------------------------------------------------
5491
5492 #Ifdef TARGET_ZCODE;
5493
5494 [ ActionPrimitive; (#actions_table-->action)(); ];
5495
5496 #Ifnot; ! TARGET_GLULX
5497
5498 [ ActionPrimitive; (#actions_table-->(action+1))(); ];
5499
5500 #Endif; ! TARGET_
5501
5502 [ AfterGameOver i amus_ret;
5503
5504   .RRQPL;
5505
5506     L__M(##Miscellany,5);
5507
5508   .RRQL;
5509
5510     L__M(##Prompt);
5511     #Ifdef TARGET_ZCODE;
5512     #IfV3; read buffer parse; #Endif; ! V3
5513     temp_global=0;
5514     #IfV5; read buffer parse DrawStatusLine; #Endif; ! V5
5515     #Ifnot; ! TARGET_GLULX
5516     KeyboardPrimitive(buffer, parse);
5517     #Endif; ! TARGET_
5518     i = parse-->1;
5519     if (i == QUIT1__WD or QUIT2__WD) {
5520         #Ifdef TARGET_ZCODE;
5521         quit;
5522         #Ifnot; ! TARGET_GLULX
5523         quit;
5524         #Endif; ! TARGET_
5525     }
5526     if (i == RESTART__WD) {
5527         #Ifdef TARGET_ZCODE;
5528         @restart;
5529         #Ifnot; ! TARGET_GLULX
5530         @restart;
5531         #Endif; ! TARGET_
5532     }
5533     if (i == RESTORE__WD) {
5534         RestoreSub();
5535         jump RRQPL;
5536     }
5537     if (i == FULLSCORE1__WD or FULLSCORE2__WD && TASKS_PROVIDED==0) {
5538         new_line; FullScoreSub();
5539         jump RRQPL;
5540     }
5541     if (deadflag == 2 && i == AMUSING__WD) {
5542         amus_ret = false;
5543         if (AMUSING_PROVIDED == 0) {
5544             new_line;
5545             amus_ret = Amusing();
5546         }
5547         if (amus_ret == false) LibraryExtensions.RunAll(ext_amusing);
5548         jump RRQPL;
5549     }
5550     #IfV5;
5551     if (i == UNDO1__WD or UNDO2__WD or UNDO3__WD) {
5552         i = PerformUndo();
5553         if (i == 0) jump RRQPL;
5554     }
5555     #Endif; ! V5
5556     L__M(##Miscellany, 8);
5557     jump RRQL;
5558 ];
5559
5560 [ NoteObjectAcquisitions i;
5561     objectloop (i in player)
5562         if (i hasnt moved) {
5563             give i moved;
5564             if (i has scored) {
5565                 score = score + OBJECT_SCORE;
5566                 things_score = things_score + OBJECT_SCORE;
5567             }
5568         }
5569 ];
5570
5571 ! ----------------------------------------------------------------------------
5572 ! R_Process is invoked by the <...> and <<...>> statements, whose syntax is:
5573 !   <action [noun] [second]>                ! traditional
5574 !   <action [noun] [second], actor>         ! introduced at compiler 6.33
5575
5576 [ R_Process a n s p
5577     s1 s2 s3;
5578     s1 = inp1; s2 = inp2; s3 = actor;
5579     inp1 = n; inp2 = s; if (p) actor = p; else actor = player;
5580     InformLibrary.begin_action(a, n, s, 1);
5581     inp1 = s1; inp2 = s2; actor = s3;
5582 ];
5583
5584 ! ----------------------------------------------------------------------------
5585
5586 [ TestScope obj act a al sr x y;
5587     x = parser_one; y = parser_two;
5588     parser_one = obj; parser_two = 0; a = actor; al = actors_location;
5589     sr = scope_reason; scope_reason = TESTSCOPE_REASON;
5590     if (act == 0) actor = player; else actor = act;
5591     actors_location = ScopeCeiling(actor);
5592     SearchScope(actors_location, actor, 0); scope_reason = sr; actor = a;
5593     actors_location = al; parser_one = x; x = parser_two; parser_two = y;
5594     return x;
5595 ];
5596
5597 [ LoopOverScope routine act x y a al;
5598     x = parser_one; y = scope_reason; a = actor; al = actors_location;
5599     parser_one = routine;
5600     if (act == 0) actor = player; else actor = act;
5601     actors_location = ScopeCeiling(actor);
5602     scope_reason = LOOPOVERSCOPE_REASON;
5603     SearchScope(actors_location, actor, 0);
5604     parser_one = x; scope_reason = y; actor = a; actors_location = al;
5605 ];
5606
5607 [ BeforeRoutines rv;
5608     if (GamePreRoutine()) rtrue;
5609     if (rv == false) rv=LibraryExtensions.RunWhile(ext_gamepreroutine, 0);
5610     if (rv) rtrue;
5611     if (RunRoutines(player, orders)) rtrue;
5612     scope_reason = REACT_BEFORE_REASON; parser_one = 0;
5613     SearchScope(ScopeCeiling(player), player, 0);
5614     scope_reason = PARSING_REASON;
5615     if (parser_one) rtrue;
5616     if (location && RunRoutines(location, before)) rtrue;
5617     if (inp1 > 1 && RunRoutines(inp1, before)) rtrue;
5618     rfalse;
5619 ];
5620
5621 [ AfterRoutines rv;
5622     scope_reason = REACT_AFTER_REASON; parser_one = 0;
5623     SearchScope(ScopeCeiling(player), player, 0); scope_reason = PARSING_REASON;
5624     if (parser_one) rtrue;
5625     if (location && RunRoutines(location, after)) rtrue;
5626     if (inp1 > 1 && RunRoutines(inp1, after)) rtrue;
5627     rv = GamePostRoutine();
5628     if (rv == false) rv=LibraryExtensions.RunWhile(ext_gamepostroutine, false);
5629     return rv;
5630 ];
5631
5632 [ RunLife a j;
5633     #Ifdef DEBUG;
5634     if (debug_flag & DEBUG_ACTIONS) TraceAction(2, j);
5635     #Endif; ! DEBUG
5636     reason_code = j; return RunRoutines(a,life);
5637 ];
5638
5639 [ ZRegion addr;
5640     switch (metaclass(addr)) {      ! Left over from Inform 5
5641       nothing:          return 0;
5642       Object, Class:    return 1;
5643       Routine:          return 2;
5644       String:           return 3;
5645     }
5646 ];
5647
5648 [ PrintOrRun obj prop flag;
5649     if (obj.#prop > WORDSIZE) return RunRoutines(obj, prop);
5650     if (obj.prop == NULL) rfalse;
5651     switch (metaclass(obj.prop)) {
5652       Class, Object, nothing:
5653         return RunTimeError(2,obj,prop);
5654       String:
5655         print (string) obj.prop;
5656         if (flag == 0) new_line;
5657         rtrue;
5658       Routine:
5659         return RunRoutines(obj, prop);
5660     }
5661 ];
5662
5663 [ PrintOrRunVar var flag;
5664     switch (metaclass(var)) {
5665       Object:
5666         print (name) var;
5667       String:
5668         print (string) var;
5669         if (flag == 0) new_line;
5670       Routine:
5671         return var();
5672       default:
5673         print (char) '(', var, (char) ')';
5674     }
5675     rtrue;
5676 ];
5677
5678 [ ValueOrRun obj prop;
5679   !### this is entirely unlikely to work. Does anyone care? (AP)
5680   ! Well, it's certainly used three times in verblibm.h (RF)
5681   ! Update: now not used (RF)
5682     if (obj.prop < 256) return obj.prop;
5683     return RunRoutines(obj, prop);
5684 ];
5685
5686 [ RunRoutines obj prop;
5687     if (obj == thedark && prop ~= initial or short_name or description) obj = real_location;
5688     if (obj.&prop == 0 && prop >= INDIV_PROP_START) rfalse;
5689     return obj.prop();
5690 ];
5691
5692 #Ifdef TARGET_ZCODE;
5693
5694 [ ChangeDefault prop val a b;
5695     ! Use assembly-language here because -S compilation won't allow this:
5696     @loadw 0 5 -> a;
5697     b = prop-1;
5698     @storew a b val;
5699 ];
5700
5701 #Ifnot; ! TARGET_GLULX
5702
5703 [ ChangeDefault prop val;
5704     ! Use assembly-language here because -S compilation won't allow this:
5705     ! #cpv__start-->prop = val;
5706     @astore #cpv__start prop val;
5707 ];
5708
5709 #Endif; ! TARGET_
5710
5711 ! ----------------------------------------------------------------------------
5712
5713 [ StartTimer obj timer i;
5714     for (i=0 : i<active_timers : i++)
5715         if (the_timers-->i == obj) rfalse;
5716     for (i=0 : i<active_timers : i++)
5717         if (the_timers-->i == 0) jump FoundTSlot;
5718     i = active_timers++;
5719     if (i >= MAX_TIMERS) { RunTimeError(4); return; }
5720   .FoundTSlot;
5721     if (obj.&time_left == 0) { RunTimeError(5, obj, time_left); return; }
5722     the_timers-->i = obj; obj.time_left = timer;
5723 ];
5724
5725 [ StopTimer obj i;
5726     for (i=0 : i<active_timers : i++)
5727         if (the_timers-->i == obj) jump FoundTSlot2;
5728     rfalse;
5729   .FoundTSlot2;
5730     if (obj.&time_left == 0) { RunTimeError(5, obj, time_left); return; }
5731     the_timers-->i = 0; obj.time_left = 0;
5732 ];
5733
5734 [ StartDaemon obj i;
5735     for (i=0 : i<active_timers : i++)
5736         if (the_timers-->i == WORD_HIGHBIT + obj) rfalse;
5737     for (i=0 : i<active_timers : i++)
5738         if (the_timers-->i == 0) jump FoundTSlot3;
5739     i = active_timers++;
5740     if (i >= MAX_TIMERS) RunTimeError(4);
5741   .FoundTSlot3;
5742     the_timers-->i = WORD_HIGHBIT + obj;
5743 ];
5744
5745 [ StopDaemon obj i;
5746     for (i=0 : i<active_timers : i++)
5747         if (the_timers-->i == WORD_HIGHBIT + obj) jump FoundTSlot4;
5748     rfalse;
5749   .FoundTSlot4;
5750     the_timers-->i = 0;
5751 ];
5752
5753 ! ----------------------------------------------------------------------------
5754
5755 [ DisplayStatus;
5756     if (sys_statusline_flag == 0) { sline1 = score; sline2 = turns; }
5757     else { sline1 = the_time/60; sline2 = the_time%60;}
5758 ];
5759
5760 [ SetTime t s;
5761     the_time = t; time_rate = s; time_step = 0;
5762     if (s < 0) time_step = 0-s;
5763 ];
5764
5765 [ NotifyTheScore;
5766     #Ifdef TARGET_GLULX;
5767     glk_set_style(style_Note);
5768     #Endif; ! TARGET_GLULX
5769     print "^[";  L__M(##Miscellany, 50, score-last_score);  print ".]^";
5770     #Ifdef TARGET_GLULX;
5771     glk_set_style(style_Normal);
5772     #Endif; ! TARGET_GLULX
5773 ];
5774
5775 ! ----------------------------------------------------------------------------
5776
5777 [ AdjustLight flag i;
5778     i = lightflag;
5779     lightflag = OffersLight(parent(player));
5780
5781     if (i == 0 && lightflag == 1) {
5782         location = real_location;
5783         if (flag == 0) <Look>;
5784     }
5785
5786     if (i == 1 && lightflag == 0) {
5787         real_location = location; location = thedark;
5788         if (flag == 0) {
5789             NoteArrival();
5790             return L__M(##Miscellany, 9);
5791         }
5792     }
5793     if (i == 0 && lightflag == 0) location = thedark;
5794 ];
5795
5796 [ OffersLight i j;
5797     if (i == 0) rfalse;
5798     if (i has light) rtrue;
5799     objectloop (j in i)
5800         if (HasLightSource(j) == 1) rtrue;
5801     if (i has container) {
5802         if (i has open || i has transparent)
5803             return OffersLight(parent(i));
5804     }
5805     else {
5806         if (i has enterable || i has transparent || i has supporter)
5807             return OffersLight(parent(i));
5808     }
5809     rfalse;
5810 ];
5811
5812 [ HidesLightSource obj;
5813     if (obj == player) rfalse;
5814     if (obj has transparent or supporter) rfalse;
5815     if (obj has container) return (obj hasnt open);
5816     return (obj hasnt enterable);
5817 ];
5818
5819 [ HasLightSource i j ad;
5820     if (i == 0) rfalse;
5821     if (i has light) rtrue;
5822     if (i has enterable || IsSeeThrough(i) == 1)
5823         if (~~(HidesLightSource(i)))
5824             objectloop (j in i)
5825                 if (HasLightSource(j) == 1) rtrue;
5826     ad = i.&add_to_scope;
5827     if (parent(i) ~= 0 && ad ~= 0) {
5828         if (metaclass(ad-->0) == Routine) {
5829             ats_hls = 0; ats_flag = 1;
5830             RunRoutines(i, add_to_scope);
5831             ats_flag = 0; if (ats_hls == 1) rtrue;
5832         }
5833         else {
5834             for (j=0 : (WORDSIZE*j)<i.#add_to_scope : j++)
5835                 if (HasLightSource(ad-->j) == 1) rtrue;
5836         }
5837     }
5838     rfalse;
5839 ];
5840
5841 [ ChangePlayer obj flag i;
5842 !   if (obj.&number == 0) return RunTimeError(7, obj);
5843
5844     if (obj == nothing) obj = selfobj;
5845     if (actor == player) actor=obj;
5846     give player ~transparent ~concealed;
5847     i = obj; while (parent(i) ~= 0) {
5848         if (i has animate) give i transparent;
5849         i = parent(i);
5850     }
5851     if (player == selfobj && player provides nameless && player.nameless == true) {
5852         if (player provides narrative_voice) {
5853             if (player.narrative_voice == 1) {
5854                 player.short_name = MYFORMER__TX;
5855                 (player.&name)-->0 = 'my';
5856                 (player.&name)-->1 = 'former';
5857                 (player.&name)-->2 = 'self';
5858             } else if (player.narrative_voice == 2) {
5859                 player.short_name = FORMER__TX;
5860                 (player.&name)-->0 = 'my';
5861                 (player.&name)-->1 = 'former';
5862                 (player.&name)-->2 = 'self';
5863             }
5864         }
5865     }
5866
5867
5868     player = obj;
5869
5870     give player transparent concealed animate;
5871     i = player; while (parent(i) ~= 0) i = parent(i);
5872     location = i; real_location = location;
5873     if (parent(player) == 0) return RunTimeError(10);
5874     MoveFloatingObjects();
5875     lightflag = OffersLight(parent(player));
5876     if (lightflag == 0) location = thedark;
5877     print_player_flag = flag;
5878 ];
5879
5880 ! ----------------------------------------------------------------------------
5881
5882 #Ifdef DEBUG;
5883
5884 #Ifdef TARGET_ZCODE;
5885
5886 [ DebugParameter w;
5887     print w;
5888     if (w >= 1 && w <= top_object) print " (", (name) w, ")";
5889     if (UnsignedCompare(w, dict_start) >= 0 &&
5890             UnsignedCompare(w, dict_end) < 0 &&
5891             (w - dict_start) % dict_entry_size == 0)
5892         print " ('", (address) w, "')";
5893 ];
5894
5895 [ DebugAction a anames;
5896     #Iftrue (Grammar__Version == 1);
5897     if (a >= 256) { print "<fake action ", a-256, ">"; return; }
5898     #Ifnot;
5899     if (a >= 4096) { print "<fake action ", a-4096, ">"; return; }
5900     #Endif; ! Grammar__Version
5901     anames = #identifiers_table;
5902     anames = anames + 2*(anames-->0) + 2*48;
5903     print (string) anames-->a;
5904 ];
5905
5906 [ DebugAttribute a anames;
5907     if (a < 0 || a >= 48) print "<invalid attribute ", a, ">";
5908     else {
5909         anames = #identifiers_table; anames = anames + 2*(anames-->0);
5910         print (string) anames-->a;
5911     }
5912 ];
5913
5914 #Ifnot; ! TARGET_GLULX
5915
5916 [ DebugParameter w endmem;
5917     print w;
5918     @getmemsize endmem;
5919     if (w >= 1 && w < endmem) {
5920         if (w->0 >= $70 && w->0 < $7F) print " (", (name) w, ")";
5921         if (w->0 >= $60 && w->0 < $6F) print " ('", (address) w, "')";
5922     }
5923 ];
5924
5925 [ DebugAction a str;
5926     if (a >= 4096) { print "<fake action ", a-4096, ">"; return; }
5927     if (a < 0 || a >= #identifiers_table-->7) print "<invalid action ", a, ">";
5928     else {
5929         str = #identifiers_table-->6;
5930         str = str-->a;
5931         if (str) print (string) str; else print "<unnamed action ", a, ">";
5932     }
5933 ];
5934
5935 [ DebugAttribute a str;
5936     if (a < 0 || a >= NUM_ATTR_BYTES*8) print "<invalid attribute ", a, ">";
5937     else {
5938         str = #identifiers_table-->4;
5939         str = str-->a;
5940         if (str) print (string) str; else print "<unnamed attribute ", a, ">";
5941     }
5942 ];
5943
5944 #Endif; ! TARGET_
5945
5946 [ TraceAction source ar;
5947     if (source < 2) print "[ Action ", (DebugAction) action;
5948     else {
5949         if (ar == ##Order)
5950             print "[ Order to ", (name) actor, ": ", (DebugAction) action;
5951         else
5952             print "[ Life rule ", (DebugAction) ar;
5953     }
5954     if (noun ~= 0)   print " with noun ", (DebugParameter) noun;
5955     if (second ~= 0) print " and second ", (DebugParameter) second;
5956     if (source == 0) print " ";
5957     if (source == 1) print " (from < > statement) ";
5958     print "]^";
5959 ];
5960
5961 [ DebugToken token;
5962     AnalyseToken(token);
5963     switch (found_ttype) {
5964       ILLEGAL_TT:
5965         print "<illegal token number ", token, ">";
5966       ELEMENTARY_TT:
5967         switch (found_tdata) {
5968           NOUN_TOKEN:           print "noun";
5969           HELD_TOKEN:           print "held";
5970           MULTI_TOKEN:          print "multi";
5971           MULTIHELD_TOKEN:      print "multiheld";
5972           MULTIEXCEPT_TOKEN:    print "multiexcept";
5973           MULTIINSIDE_TOKEN:    print "multiinside";
5974           CREATURE_TOKEN:       print "creature";
5975           SPECIAL_TOKEN:        print "special";
5976           NUMBER_TOKEN:         print "number";
5977           TOPIC_TOKEN:          print "topic";
5978           ENDIT_TOKEN:          print "END";
5979         }
5980       PREPOSITION_TT:
5981         print "'", (address) found_tdata, "'";
5982       ROUTINE_FILTER_TT:
5983         #Ifdef INFIX;
5984         print "noun=", (InfixPrintPA) found_tdata;
5985         #Ifnot;
5986         print "noun=Routine(", found_tdata, ")";
5987         #Endif; ! INFIX
5988       ATTR_FILTER_TT:
5989         print (DebugAttribute) found_tdata;
5990       SCOPE_TT:
5991         #Ifdef INFIX;
5992         print "scope=", (InfixPrintPA) found_tdata;
5993         #Ifnot;
5994         print "scope=Routine(", found_tdata, ")";
5995         #Endif; ! INFIX
5996       GPR_TT:
5997         #Ifdef INFIX;
5998         print (InfixPrintPA) found_tdata;
5999         #Ifnot;
6000         print "Routine(", found_tdata, ")";
6001         #Endif; ! INFIX
6002     }
6003 ];
6004
6005 [ DebugGrammarLine pcount;
6006     print " * ";
6007     for (: line_token-->pcount ~= ENDIT_TOKEN : pcount++) {
6008         if ((line_token-->pcount)->0 & $10) print "/ ";
6009         print (DebugToken) line_token-->pcount, " ";
6010     }
6011     print "-> ", (DebugAction) action_to_be;
6012     if (action_reversed) print " reverse";
6013 ];
6014
6015 #Ifdef TARGET_ZCODE;
6016
6017 [ ShowVerbSub grammar lines j;
6018     if (noun == 0 || ((noun->#dict_par1) & DICT_VERB) == 0)
6019         "Try typing ~showverb~ and then the name of a verb.";
6020     print "Verb";
6021     if ((noun->#dict_par1) & DICT_META) print " meta";
6022     for (j=dict_start : j<dict_end : j=j+dict_entry_size)
6023         if (j->#dict_par2 == noun->#dict_par2)
6024             print " '", (address) j, "'";
6025     new_line;
6026     grammar = (HDR_STATICMEMORY-->0)-->($ff-(noun->#dict_par2));
6027     lines = grammar->0;
6028     grammar++;
6029     if (lines == 0) "has no grammar lines.";
6030     for (: lines>0 : lines--) {
6031         grammar = UnpackGrammarLine(grammar);
6032         print "    "; DebugGrammarLine(); new_line;
6033     }
6034 ];
6035
6036 #Ifnot; ! TARGET_GLULX
6037
6038 [ ShowVerbSub grammar lines i j wd dictlen entrylen;
6039     if (noun == 0 || ((noun->#dict_par1) & DICT_VERB) == 0)
6040         "Try typing ~showverb~ and then the name of a verb.";
6041     print "Verb";
6042     if ((noun->#dict_par1) & DICT_META) print " meta";
6043     dictlen = #dictionary_table-->0;
6044     entrylen = DICT_WORD_SIZE + 7;
6045     for (j=0 : j<dictlen : j++) {
6046         wd = #dictionary_table + WORDSIZE + entrylen*j;
6047         if (wd->#dict_par2 == noun->#dict_par2)
6048             print " '", (address) wd, "'";
6049     }
6050     new_line;
6051     i = DictionaryWordToVerbNum(noun);
6052     grammar = (#grammar_table)-->(i+1);
6053     lines = grammar->0;
6054     grammar++;
6055     if (lines == 0) "has no grammar lines.";
6056     for (: lines>0 : lines--) {
6057         grammar = UnpackGrammarLine(grammar);
6058         print "    "; DebugGrammarLine(); new_line;
6059     }
6060 ];
6061
6062 #Endif; ! TARGET_
6063
6064 [ ShowObjSub c f l a n x numattr;
6065     if (noun == 0) noun = location;
6066     objectloop (c ofclass Class) if (noun ofclass c) { f++; l=c; }
6067     if (f == 1) print (name) l, " ~"; else print "Object ~";
6068     print (name) noun, "~ (", noun, ")";
6069     if (parent(noun)) print " in ~", (name) parent(noun), "~ (", parent(noun), ")";
6070     new_line;
6071     if (f > 1) {
6072         print "  class ";
6073         objectloop (c ofclass Class) if (noun ofclass c) print (name) c, " ";
6074         new_line;
6075     }
6076     #Ifdef TARGET_ZCODE;
6077     numattr = 48;
6078     #Ifnot; ! TARGET_GLULX
6079     numattr = NUM_ATTR_BYTES * 8;
6080     #Endif; ! TARGET_
6081     for (a=0,f=0 : a<numattr : a++) if (noun has a) f=1;
6082     if (f) {
6083         print "  has ";
6084         for (a=0 : a<numattr : a++) if (noun has a) print (DebugAttribute) a, " ";
6085         new_line;
6086     }
6087     if (noun ofclass Class) return;
6088
6089     f=0;
6090     #Ifdef TARGET_ZCODE;
6091     l = #identifiers_table-->0;
6092     #Ifnot; ! TARGET_GLULX
6093     l = INDIV_PROP_START + #identifiers_table-->3;
6094     #Endif; ! TARGET_
6095     for (a=1 : a<=l : a++) {
6096         if ((a ~= 2 or 3) && noun.&a) {
6097             if (f == 0) { print "  with "; f=1; }
6098             print (property) a;
6099             n = noun.#a;
6100             for (c=0 : WORDSIZE*c<n : c++) {
6101                 print " ";
6102                 x = (noun.&a)-->c;
6103                 if (a == name) print "'", (address) x, "'";
6104                 else {
6105                     if (a == number or capacity or time_left) print x;
6106                     else {
6107                         switch (x) {
6108                           NULL: print "NULL";
6109                           0:    print "0";
6110                           1:    print "1";
6111                           default:
6112                             switch (metaclass(x)) {
6113                               Class, Object:
6114                                 print (name) x;
6115                               String:
6116                                 print "~", (string) x, "~";
6117                               Routine:
6118                                 print "[...]";
6119                            }
6120                            print " (", x, ")";
6121                         }
6122                     }
6123                 }
6124             }
6125             print ",^       ";
6126         }
6127     }
6128 !   if (f==1) new_line;
6129 ];
6130
6131 [ ShowDictSub_helper x; print (address) x; ];
6132 [ ShowDictSub
6133     dp el ne   f x y z;
6134
6135     #Ifdef TARGET_ZCODE;
6136     dp = HDR_DICTIONARY-->0;             ! start of dictionary
6137     dp = dp + dp->0 + 1;                 ! skip over word-separators table
6138     el = dp->0;  dp = dp + 1;            ! entry length
6139     ne = dp-->0; dp = dp + WORDSIZE;     ! number of entries
6140     #Ifnot; ! TARGET_GLULX;
6141     dp = #dictionary_table;              ! start of dictionary
6142     el = DICT_WORD_SIZE + 7;             ! entry length
6143     ne = dp-->0; dp = dp + WORDSIZE;     ! number of entries
6144     #Endif; ! TARGET_
6145                                          ! dp now at first entry
6146     wn = 2; x = NextWordStopped();
6147     switch (x) {
6148       0:
6149         "That word isn't in the dictionary.";
6150       -1:
6151         ;                                ! show all entries
6152       THEN1__WD:
6153         dp = './/'; ne = 1;              ! show '.'
6154       COMMA_WORD:
6155         dp = ',//'; ne = 1;              ! show ','
6156       default:
6157         dp = x; ne = 1; f = true;        ! show specified entry, plus associated objects
6158     }
6159     for ( : ne-- : dp=dp+el) {
6160         print (address) dp, " --> ";
6161         x = dp->#dict_par1;              ! flag bits
6162         y = PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, ShowDictSub_helper, dp) + WORDSIZE;
6163         for (z=WORDSIZE : z<y : z++) {
6164             !if (x & DICT_NOUN) StorageForShortName->z = UpperCase(StorageForShortName->z);
6165             if (y > WORDSIZE+1 && StorageForShortName->z == ' ' or '.' or ',') x = x | $8000;
6166             print (char) StorageForShortName->z;
6167         }
6168         print " --> ";
6169         if (x == 0)
6170             print " no flags";
6171         else {
6172             !if (x & $0040)     print " BIT_6";
6173             !if (x & $0020)     print " BIT_5";
6174             !if (x & $0010)     print " BIT_4";
6175             if (x & $8000)     print " UNTYPEABLE";
6176             if (x & DICT_NOUN) print " noun";
6177             if (x & DICT_PLUR) print "+plural";
6178             if (x & DICT_VERB) print " verb";
6179             if (x & DICT_META) print "+meta";
6180             if (x & DICT_PREP) print " preposition";
6181             if (f && (x & DICT_NOUN)) {
6182                 print " --> refers to these objects:";
6183                 objectloop (x)
6184                     if (WordInProperty(dp, x, name)) print "^  ", (name) x, " (", x, ")";
6185             }
6186         }
6187         new_line;
6188     }
6189 ];
6190
6191 #Endif; ! DEBUG
6192
6193 ! ----------------------------------------------------------------------------
6194 !  Miscellaneous display routines used by DrawStatusLine and available for
6195 !  user.  Most of these vary according to which machine is being compiled to
6196 ! ----------------------------------------------------------------------------
6197
6198 #Ifdef TARGET_ZCODE;
6199
6200 [ ClearScreen window;
6201     switch (window) {
6202       WIN_ALL:    @erase_window -1;
6203       WIN_STATUS: @erase_window 1;
6204       WIN_MAIN:   @erase_window 0;
6205     }
6206 ];
6207
6208 #Iftrue (#version_number == 6);
6209 [ MoveCursorV6 line column  charw;  ! 1-based postion on text grid
6210     @get_wind_prop 1 13 -> charw; ! font size
6211     charw = charw & $FF;
6212     line = 1 + charw*(line-1);
6213     column = 1 + charw*(column-1);
6214     @set_cursor line column;
6215 ];
6216 #Endif;
6217
6218 #Ifndef MoveCursor;
6219 [ MoveCursor line column;  ! 1-based postion on text grid
6220     if (~~statuswin_current) {
6221         @set_window 1;
6222         #Ifdef COLOUR;
6223         if (clr_on && clr_bgstatus > 1)
6224             @set_colour clr_fgstatus clr_bgstatus;
6225         else
6226         #Endif; ! COLOUR
6227             style reverse;
6228     }
6229     if (line == 0) { line = 1; column = 1; }
6230     #Iftrue (#version_number == 6);
6231     MoveCursorV6(line, column);
6232     #Ifnot;
6233     @set_cursor line column;
6234     #Endif;
6235     statuswin_current = true;
6236 ];
6237 #Endif;
6238
6239 [ MainWindow;
6240     if (statuswin_current) {
6241 #Ifdef COLOUR;
6242         if (clr_on && clr_bgstatus > 1) @set_colour clr_fg clr_bg;
6243         else
6244 #Endif; ! COLOUR
6245             style roman;
6246         @set_window 0;
6247         }
6248     statuswin_current = false;
6249 ];
6250
6251 #Iftrue (#version_number == 6);
6252 [ ScreenWidth  width charw;
6253     @get_wind_prop 1 3 -> width;
6254     @get_wind_prop 1 13 -> charw;
6255     charw = charw & $FF;
6256     if (charw == 0) return width;
6257     return (width+charw-1) / charw;
6258 ];
6259 #Ifnot;
6260 [ ScreenWidth;
6261     return (HDR_SCREENWCHARS->0);
6262 ];
6263 #Endif;
6264
6265 [ ScreenHeight;
6266     return (HDR_SCREENHLINES->0);
6267 ];
6268
6269 #Iftrue (#version_number == 6);
6270 [ StatusLineHeight height  wx wy x y charh;
6271     ! Split the window. Standard 1.0 interpreters should keep the window 0
6272     ! cursor in the same absolute position, but older interpreters,
6273     ! including Infocom's don't - they keep the window 0 cursor in the
6274     ! same position relative to its origin. We therefore compensate
6275     ! manually.
6276     @get_wind_prop 0 0 -> wy; @get_wind_prop 0 1 -> wx;
6277     @get_wind_prop 0 13 -> charh; @log_shift charh $FFF8 -> charh;
6278     @get_wind_prop 0 4 -> y; @get_wind_prop 0 5 -> x;
6279     height = height * charh;
6280     @split_window height;
6281     y = y - height + wy - 1;
6282     if (y < 1) y = 1;
6283     x = x + wx - 1;
6284     @set_cursor y x 0;
6285     gg_statuswin_cursize = height;
6286 ];
6287 #Ifnot;
6288 [ StatusLineHeight height;
6289     if (gg_statuswin_cursize ~= height)
6290         @split_window height;
6291     gg_statuswin_cursize = height;
6292 ];
6293 #Endif;
6294
6295 #Ifdef COLOUR;
6296 [ SetColour f b window;
6297     if (window == 0) {  ! if setting both together, set reverse
6298         clr_fgstatus = b;
6299         clr_bgstatus = f;
6300     }
6301     if (window == 1) {
6302         clr_fgstatus = f;
6303         clr_bgstatus = b;
6304     }
6305     if (window == 0 or 2) {
6306         clr_fg = f;
6307         clr_bg = b;
6308     }
6309     if (clr_on) {
6310         if (statuswin_current)
6311             @set_colour clr_fgstatus clr_bgstatus;
6312         else
6313             @set_colour clr_fg clr_bg;
6314     }
6315 ];
6316 #Endif; ! COLOUR
6317
6318
6319 #Ifnot; ! TARGET_GLULX
6320
6321 [ ClearScreen window;
6322     if (window == WIN_ALL or WIN_MAIN) {
6323         glk_window_clear(gg_mainwin);
6324         if (gg_quotewin) {
6325             glk_window_close(gg_quotewin, 0);
6326             gg_quotewin = 0;
6327         }
6328     }
6329     if (gg_statuswin && window == WIN_ALL or WIN_STATUS)
6330         glk_window_clear(gg_statuswin);
6331 ];
6332
6333 [ MoveCursor line column;  ! 0-based postion on text grid
6334     if (gg_statuswin) {
6335         glk_set_window(gg_statuswin);
6336     }
6337     if (line == 0) { line = 1; column = 1; }
6338     glk_window_move_cursor(gg_statuswin, column-1, line-1);
6339     statuswin_current=1;
6340 ];
6341
6342 [ MainWindow;
6343     glk_set_window(gg_mainwin);
6344     statuswin_current=0;
6345 ];
6346
6347 [ MakeColourWord c;
6348     if (c > 9) return c;
6349     c = c-2;
6350     return $ff0000*(c&1) + $ff00*(c&2 ~= 0) + $ff*(c&4 ~= 0);
6351 ];
6352
6353 [ ScreenWidth  id;
6354     id=gg_mainwin;
6355     if (gg_statuswin && statuswin_current) id = gg_statuswin;
6356     glk_window_get_size(id, gg_arguments, 0);
6357     return gg_arguments-->0;
6358 ];
6359
6360 [ ScreenHeight;
6361     glk_window_get_size(gg_mainwin, 0, gg_arguments);
6362     return gg_arguments-->0;
6363 ];
6364
6365 #Ifdef COLOUR;
6366 [ SetColour f b window;
6367     if (window == 0) {  ! if setting both together, set reverse
6368         clr_fgstatus = b;
6369         clr_bgstatus = f;
6370     }
6371     if (window == 1) {
6372         clr_fgstatus = f;
6373         clr_bgstatus = b;
6374     }
6375     if (window == 0 or 2) {
6376         clr_fg = f;
6377         clr_bg = b;
6378     }
6379     if (clr_on) {
6380         if (glk_gestalt(gestalt_GarglkText, 0)) {
6381             if (f >= 0 && f < 10) {
6382                 f = GlulxColourValues-->f;
6383             }
6384             else {
6385                 f = -2;
6386             }
6387             if (b >= 0 && b < 10) {
6388                 b = GlulxColourValues-->b;
6389             }
6390             else {
6391                 b = -2;
6392             }
6393             garglk_set_zcolors(f, b);
6394         }
6395     }
6396 ];
6397 #Endif; ! COLOUR
6398 #Endif; ! TARGET_
6399
6400 #Ifndef COLOUR;
6401 [ SetColour f b window doclear;
6402     f = b = window = doclear = 0;
6403 ];
6404 #Endif;
6405
6406 [ SetClr f b w;
6407     SetColour (f, b, w);
6408 ];
6409
6410 [ RestoreColours;    ! L61007, L61113
6411     gg_statuswin_cursize = -1;    ! Force window split in StatusLineHeight()
6412     #Ifdef COLOUR;
6413     if (clr_on) { ! check colour has been used
6414         SetColour(clr_fg, clr_bg, 2); ! make sure both sets of variables are restored
6415         SetColour(clr_fgstatus, clr_bgstatus, 1, true);
6416         ClearScreen();
6417     }
6418     #Endif;
6419     #Ifdef TARGET_ZCODE;
6420     #Iftrue (#version_number == 6); ! request screen update
6421     (0-->8) = (0-->8) | $$00000100;
6422     #Endif;
6423     #Endif; ! TARGET_
6424 ];
6425
6426 ! ----------------------------------------------------------------------------
6427 !  Except in Version 3, the DrawStatusLine routine does just that: this is
6428 !  provided explicitly so that it can be Replace'd to change the style, and
6429 !  as written it emulates the ordinary Standard game status line, which is
6430 !  drawn in hardware
6431 ! ----------------------------------------------------------------------------
6432
6433 #Ifdef TARGET_ZCODE;
6434
6435 #IfV5;
6436
6437 #Iftrue (#version_number == 6);
6438 [ DrawStatusLine width x charw scw mvw;
6439     HDR_GAMEFLAGS-->0 = (HDR_GAMEFLAGS-->0) & ~$0004;
6440
6441     StatusLineHeight(gg_statuswin_size);
6442     ! Now clear the window. This isn't totally trivial. Our approach is to
6443     ! select the fixed space font, measure its width, and print an appropriate
6444     ! number of spaces. We round up if the screen isn't a whole number of
6445     ! characters wide, and rely on window 1 being set to clip by default.
6446     MoveCursor(1, 1);
6447     @set_font 4 -> x;
6448     width = ScreenWidth();
6449     spaces width;
6450     ! Back to standard font for the display. We use output_stream 3 to
6451     ! measure the space required, the aim being to get 50 characters
6452     ! worth of space for the location name.
6453     MoveCursor(1, 2);
6454     @set_font 1 -> x;
6455     if (location == thedark)
6456         print (name) location;
6457     else {
6458         FindVisibilityLevels();
6459         if (visibility_ceiling == location) print (name) location;
6460         else                                print (The) visibility_ceiling;
6461     }
6462     @get_wind_prop 1 3 -> width;
6463     @get_wind_prop 1 13 -> charw;
6464     charw = charw & $FF;
6465     @output_stream 3 StorageForShortName;
6466     print (string) SCORE__TX, "00000";
6467     @output_stream -3; scw = HDR_PIXELSTO3-->0 + charw;
6468     @output_stream 3 StorageForShortName;
6469     print (string) MOVES__TX, "00000";
6470     @output_stream -3; mvw = HDR_PIXELSTO3-->0 + charw;
6471     if (width - scw - mvw >= 50*charw) {
6472         x = 1+width-scw-mvw;
6473         @set_cursor 1 x; print (string) SCORE__TX, sline1;
6474         x = x+scw;
6475         @set_cursor 1 x; print (string) MOVES__TX, sline2;
6476     }
6477     else {
6478         @output_stream 3 StorageForShortName;
6479         print "00000/00000";
6480         @output_stream -3; scw = HDR_PIXELSTO3-->0 + charw;
6481         if (width - scw >= 50*charw) {
6482             x = 1+width-scw;
6483             @set_cursor 1 x; print sline1, "/", sline2;
6484         }
6485     }
6486     ! Reselect roman, as Infocom's interpreters interpreters go funny
6487     ! if reverse is selected twice.
6488     MainWindow();
6489 ];
6490
6491 #Endif; ! #version_number == 6
6492
6493 #Endif; ! V5
6494
6495 #Endif; ! TARGET_ZCODE
6496
6497 #Ifndef DrawStatusLine;
6498 [ DrawStatusLine width posa posb posc;
6499     #Ifdef TARGET_GLULX;
6500     ! If we have no status window, we must not try to redraw it.
6501     if (gg_statuswin == 0)
6502         return;
6503     #Endif;
6504
6505     ! If there is no player location, we shouldn't try to draw status window
6506     if (location == nothing || parent(player) == nothing)
6507         return;
6508
6509     StatusLineHeight(gg_statuswin_size);
6510     MoveCursor(1, 1);
6511
6512     width = ScreenWidth();
6513     posa = width-26;
6514     posb = width-13;
6515     posc = width-5;
6516     spaces width;
6517
6518     MoveCursor(1, 2);
6519     if (location == thedark) {
6520         print (name) location;
6521     }
6522     else {
6523         FindVisibilityLevels();
6524         if (visibility_ceiling == location)
6525             print (name) location;
6526         else
6527             print (The) visibility_ceiling;
6528     }
6529
6530     if (sys_statusline_flag && width > 53) {
6531         MoveCursor(1, posa);
6532         print (string) TIME__TX;
6533         LanguageTimeOfDay(sline1, sline2);
6534     }
6535     else {
6536         if (width > 66) {
6537             #Ifndef NO_SCORE;
6538             MoveCursor(1, posa);
6539             print (string) SCORE__TX, sline1;
6540             #Endif;
6541             MoveCursor(1, posb);
6542             print (string) MOVES__TX, sline2;
6543         }
6544         if (width > 53 && width <= 66) {
6545             MoveCursor(1, posb);
6546             #Ifdef NO_SCORE;
6547             print (string) MOVES__TX, sline2;
6548             #Ifnot;
6549             print sline1, "/", sline2;
6550             #Endif;
6551         }
6552         if (width < 53) {
6553            MoveCursor(1, posc);
6554            #Ifdef NO_SCORE;
6555            print (string) MOVES_S__TX, sline2;
6556            #Ifnot;
6557            print sline1, "/", sline2;
6558            #Endif;
6559         }
6560     }
6561
6562     MainWindow(); ! set_window
6563 ];
6564 #Endif;
6565
6566 #Ifdef TARGET_GLULX;
6567
6568 [ StatusLineHeight hgt parwin;
6569     if (gg_statuswin == 0) return;
6570     if (hgt == gg_statuswin_cursize) return;
6571     parwin = glk_window_get_parent(gg_statuswin);
6572     glk_window_set_arrangement(parwin, $12, hgt, 0);
6573     gg_statuswin_cursize = hgt;
6574 ];
6575
6576 [ Box__Routine maxwid arr ix lines lastnl parwin;
6577     maxwid = 0; ! squash compiler warning
6578     lines = arr-->0;
6579
6580     if (gg_quotewin == 0) {
6581         gg_arguments-->0 = lines;
6582         ix = InitGlkWindow(GG_QUOTEWIN_ROCK);
6583         if (ix == false) ix = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, GG_QUOTEWIN_ROCK);
6584         if (ix == false)
6585             gg_quotewin = glk_window_open(gg_mainwin, $12, lines, 3,
6586                 GG_QUOTEWIN_ROCK); ! window_open
6587     }
6588     else {
6589         parwin = glk_window_get_parent(gg_quotewin);
6590         glk_window_set_arrangement(parwin, $12, lines, 0);
6591     }
6592
6593     lastnl = true;
6594     if (gg_quotewin) {
6595         glk_window_clear(gg_quotewin);
6596         glk_set_window(gg_quotewin);
6597         lastnl = false;
6598     }
6599
6600     ! If gg_quotewin is zero here, the quote just appears in the story window.
6601
6602     glk_set_style(style_BlockQuote);
6603     for (ix=0 : ix<lines : ix++) {
6604         print (string) arr-->(ix+1);
6605         if (ix < lines-1 || lastnl) new_line;
6606     }
6607     glk_set_style(style_Normal);
6608
6609     if (gg_quotewin) {
6610         glk_set_window(gg_mainwin);
6611     }
6612 ];
6613
6614 #Endif; ! TARGET_GLULX
6615
6616
6617 #Ifdef TARGET_ZCODE;
6618
6619 [ ZZInitialise;
6620     standard_interpreter = HDR_TERPSTANDARD-->0;
6621    transcript_mode = ((HDR_GAMEFLAGS-->0) & $0001);
6622     sys_statusline_flag = ( (HDR_TERPFLAGS->0) & $0002 ) / 2;
6623     top_object = #largest_object-255;
6624
6625     dict_start = HDR_DICTIONARY-->0;
6626     dict_entry_size = dict_start->(dict_start->0 + 1);
6627     dict_start = dict_start + dict_start->0 + 4;
6628     dict_end = dict_start + (dict_start - 2)-->0 * dict_entry_size;
6629     #Ifdef DEBUG;
6630     if (dict_start > 0 && dict_end < 0 &&
6631       ((-dict_start) - dict_end) % dict_entry_size == 0)
6632         print "** Warning: grammar properties might not work correctly **^";
6633     #Endif; ! DEBUG
6634
6635     buffer->0  = INPUT_BUFFER_LEN - WORDSIZE;
6636     buffer2->0 = INPUT_BUFFER_LEN - WORDSIZE;
6637     buffer3->0 = INPUT_BUFFER_LEN - WORDSIZE;
6638     parse->0   = MAX_BUFFER_WORDS;
6639     parse2->0  = MAX_BUFFER_WORDS;
6640 ];
6641
6642 #Ifnot; ! TARGET_GLULX;
6643
6644 [ GGInitialise res;
6645     @gestalt 4 2 res; ! Test if this interpreter has Glk.
6646     if (res == 0) {
6647       ! Without Glk, we're entirely screwed.
6648       quit;
6649     }
6650     ! Set the VM's I/O system to be Glk.
6651     @setiosys 2 0;
6652
6653     ! First, we must go through all the Glk objects that exist, and see
6654     ! if we created any of them. One might think this strange, since the
6655     ! program has just started running, but remember that the player might
6656     ! have just typed "restart".
6657
6658     GGRecoverObjects();
6659     res = InitGlkWindow(0);
6660     if (res == false) res = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, 0);
6661     if (res) return;
6662
6663     ! Now, gg_mainwin and gg_storywin might already be set. If not, set them.
6664
6665     if (gg_mainwin == 0) {
6666         ! Open the story window.
6667         res = InitGlkWindow(GG_MAINWIN_ROCK);
6668         if (res == false) res = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, GG_MAINWIN_ROCK);
6669         if (res == false)
6670             gg_mainwin = glk_window_open(0, 0, 0, 3, GG_MAINWIN_ROCK);
6671         if (gg_mainwin == 0) {
6672             ! If we can't even open one window, there's no point in going on.
6673             quit;
6674         }
6675     }
6676     else {
6677         ! There was already a story window. We should erase it.
6678         glk_window_clear(gg_mainwin);
6679     }
6680
6681     if (gg_statuswin == 0) {
6682         res = InitGlkWindow(GG_STATUSWIN_ROCK);
6683         if (res == false) res = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, GG_STATUSWIN_ROCK);
6684         if (res == false) {
6685             gg_statuswin_cursize = gg_statuswin_size;
6686             gg_statuswin = glk_window_open(gg_mainwin, $12,
6687                 gg_statuswin_cursize, 4, GG_STATUSWIN_ROCK);
6688         }
6689     }
6690     ! It's possible that the status window couldn't be opened, in which case
6691     ! gg_statuswin is now zero. We must allow for that later on.
6692
6693     glk_set_window(gg_mainwin);
6694
6695     if (InitGlkWindow(1) == false) LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, 1);
6696 ];
6697
6698 [ GGRecoverObjects id;
6699     ! If GGRecoverObjects() has been called, all these stored IDs are
6700     ! invalid, so we start by clearing them all out.
6701     ! (In fact, after a restoreundo, some of them may still be good.
6702     ! For simplicity, though, we assume the general case.)
6703     gg_mainwin = 0;
6704     gg_statuswin = 0;
6705     gg_quotewin = 0;
6706     gg_scriptfref = 0;
6707     gg_scriptstr = 0;
6708     gg_savestr = 0;
6709     gg_statuswin_cursize = 0;
6710     gg_commandstr = 0;
6711     gg_command_reading = false;
6712     ! Also tell the game to clear its object references.
6713     if (IdentifyGlkObject(0) == false) LibraryExtensions.RunWhile(ext_identifyglkobject, 0, 0);
6714
6715     id = glk_stream_iterate(0, gg_arguments);
6716     while (id) {
6717         switch (gg_arguments-->0) {
6718             GG_SAVESTR_ROCK: gg_savestr = id;
6719             GG_SCRIPTSTR_ROCK: gg_scriptstr = id;
6720             GG_COMMANDWSTR_ROCK: gg_commandstr = id;
6721                                  gg_command_reading = false;
6722             GG_COMMANDRSTR_ROCK: gg_commandstr = id;
6723                                  gg_command_reading = true;
6724             default: if (IdentifyGlkObject(1, 1, id, gg_arguments-->0) == false)
6725                          LibraryExtensions.RunWhile(ext_identifyglkobject, false, 1, 1, id, gg_arguments-->0);
6726         }
6727         id = glk_stream_iterate(id, gg_arguments);
6728     }
6729
6730     id = glk_window_iterate(0, gg_arguments);
6731     while (id) {
6732         switch (gg_arguments-->0) {
6733             GG_MAINWIN_ROCK: gg_mainwin = id;
6734             GG_STATUSWIN_ROCK: gg_statuswin = id;
6735             GG_QUOTEWIN_ROCK: gg_quotewin = id;
6736             default: if (IdentifyGlkObject(1, 0, id, gg_arguments-->0) == false)
6737                         LibraryExtensions.RunWhile(ext_identifyglkobject, false, 1, 0, id, gg_arguments-->0);
6738         }
6739         id = glk_window_iterate(id, gg_arguments);
6740     }
6741
6742     id = glk_fileref_iterate(0, gg_arguments);
6743     while (id) {
6744         switch (gg_arguments-->0) {
6745             GG_SCRIPTFREF_ROCK: gg_scriptfref = id;
6746             default: if (IdentifyGlkObject(1, 2, id, gg_arguments-->0) == false)
6747                         LibraryExtensions.RunWhile(ext_identifyglkobject, false, 1, 2, id, gg_arguments-->0);
6748         }
6749         id = glk_fileref_iterate(id, gg_arguments);
6750     }
6751
6752     ! Tell the game to tie up any loose ends.
6753     if (IdentifyGlkObject(2) == false)
6754         LibraryExtensions.RunWhile(ext_identifyglkobject, 0, 2);
6755 ];
6756
6757 ! This somewhat obfuscated function will print anything.
6758 ! It handles strings, functions (with optional arguments), objects,
6759 ! object properties (with optional arguments), and dictionary words.
6760 ! It does *not* handle plain integers, but you can use
6761 ! DecimalNumber or EnglishNumber to handle that case.
6762 !
6763 ! Calling:                           Is equivalent to:
6764 ! -------                            ----------------
6765 ! PrintAnything()                    <nothing printed>
6766 ! PrintAnything(0)                   <nothing printed>
6767 ! PrintAnything("string");           print (string) "string";
6768 ! PrintAnything('word')              print (address) 'word';
6769 ! PrintAnything(obj)                 print (name) obj;
6770 ! PrintAnything(obj, prop)           obj.prop();
6771 ! PrintAnything(obj, prop, args...)  obj.prop(args...);
6772 ! PrintAnything(func)                func();
6773 ! PrintAnything(func, args...)       func(args...);
6774
6775 [ PrintAnything _vararg_count obj mclass;
6776     print_anything_result = 0;
6777     if (_vararg_count == 0) return;
6778     @copy sp obj;
6779     _vararg_count--;
6780     if (obj == 0) return;
6781
6782     if (obj->0 == $60) {
6783         ! Dictionary word. Metaclass() can't catch this case, so we do
6784         ! it manually.
6785         print (address) obj;
6786         return;
6787     }
6788
6789     mclass = metaclass(obj);
6790     switch (mclass) {
6791       nothing:
6792         return;
6793       String:
6794         print (string) obj;
6795         return;
6796       Routine:
6797         ! Call the function with all the arguments which are already
6798         ! on the stack.
6799         @call obj _vararg_count print_anything_result;
6800         return;
6801       Object:
6802         if (_vararg_count == 0) {
6803             print (name) obj;
6804         }
6805         else {
6806             ! Push the object back onto the stack, and call the
6807             ! veneer routine that handles obj.prop() calls.
6808             @copy obj sp;
6809             _vararg_count++;
6810             @call CA__Pr _vararg_count print_anything_result;
6811         }
6812         return;
6813     }
6814 ];
6815
6816 ! This does the same as PrintAnything, but the output is sent to a
6817 ! byte array in memory. The first two arguments must be the array
6818 ! address and length; the following arguments are interpreted as
6819 ! for PrintAnything. The return value is the number of characters
6820 ! output.
6821 ! If the output is longer than the array length given, the extra
6822 ! characters are discarded, so the array does not overflow.
6823 ! (However, the return value is the total length of the output,
6824 ! including discarded characters.)
6825
6826 [ PrintAnyToArray _vararg_count arr arrlen str oldstr len;
6827     @copy sp arr;
6828     @copy sp arrlen;
6829     _vararg_count = _vararg_count - 2;
6830
6831     oldstr = glk_stream_get_current(); ! stream_get_current
6832     str = glk_stream_open_memory(arr, arrlen, 1, 0);
6833     if (str == 0) return 0;
6834
6835     glk_stream_set_current(str);
6836
6837     @call PrintAnything _vararg_count 0;
6838
6839     glk_stream_set_current(oldstr);
6840     @copy $ffffffff sp;
6841     @copy str sp;
6842     @glk $0044 2 0;
6843     @copy sp len;
6844     @copy sp 0;
6845
6846     return len;
6847 ];
6848
6849 ! And this calls PrintAnyToArray on a particular array, jiggering
6850 ! the result to be a Glulx C-style ($E0) string.
6851
6852 Constant GG_ANYTOSTRING_LEN 66;
6853 Array AnyToStrArr -> GG_ANYTOSTRING_LEN+1;
6854
6855 [ ChangeAnyToCString _vararg_count ix len;
6856     ix = GG_ANYTOSTRING_LEN-2;
6857     @copy ix sp;
6858     ix = AnyToStrArr+1;
6859     @copy ix sp;
6860     ix = _vararg_count+2;
6861     @call PrintAnyToArray ix len;
6862     AnyToStrArr->0 = $E0;
6863     if (len >= GG_ANYTOSTRING_LEN)
6864         len = GG_ANYTOSTRING_LEN-1;
6865     AnyToStrArr->(len+1) = 0;
6866     return AnyToStrArr;
6867 ];
6868
6869 #Endif; ! TARGET_
6870
6871 ! This is a trivial function which just prints a number, in decimal
6872 ! digits. It may be useful as a stub to pass to PrintAnything.
6873
6874 [ DecimalNumber num; print num; ];
6875
6876 #Ifndef SHORTNAMEBUF_LEN;               ! Can't use 'Default', unfortunately,
6877 Constant SHORTNAMEBUF_LEN 160;          ! but this is functionally equivalent
6878 #Endif;
6879
6880 #IfV5;
6881 #Ifdef VN_1630;
6882 Array StorageForShortName buffer SHORTNAMEBUF_LEN;
6883 #Ifnot;
6884 Array StorageForShortName -> WORDSIZE + SHORTNAMEBUF_LEN;
6885 #Endif; ! VN_1630
6886
6887 #Endif; ! V5
6888
6889 #Ifdef TARGET_ZCODE;
6890
6891 ! Platform-independent way of printing strings, routines and properties
6892 ! to a buffer (defined as length word followed by byte characters).
6893
6894 [ PrintToBuffer buf len a b c d e;
6895     print_anything_result = 0;
6896     @output_stream 3 buf;
6897     switch (metaclass(a)) {
6898       String:
6899         print (string) a;
6900       Routine:
6901         print_anything_result = a(b, c, d, e);
6902       Object,Class:
6903         if (b)
6904             print_anything_result = PrintOrRun(a, b, true);
6905         else
6906             print (name) a;
6907     }
6908     @output_stream -3;
6909     if (buf-->0 > len) RunTimeError(14, len, "in PrintToBuffer()");
6910     return buf-->0;
6911 ];
6912
6913 #Ifnot; ! TARGET_GLULX
6914
6915 [ PrintToBuffer buf len a b c d e;
6916     if (b) {
6917         if (metaclass(a) == Object && a.#b == WORDSIZE
6918             && metaclass(a.b) == String)
6919             buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a.b);
6920         else
6921             buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a, b, c, d, e);
6922     }
6923     else
6924         buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a);
6925     if (buf-->0 > len) buf-->0 = len;
6926     return buf-->0;
6927 ];
6928
6929 #Endif; ! TARGET_
6930
6931 ! Print contents of buffer (defined as length word followed by byte characters).
6932 ! no_break == 1: omit trailing newline.
6933 ! set_case == 1: capitalise first letter;
6934 !          == 2: capitalise first letter, remainder lower case;
6935 !          == 3: all lower case;
6936 !          == 4: all upper case.
6937 ! centred == 1:  add leading spaces.
6938
6939 [ PrintFromBuffer buf no_break set_case centred
6940     i j k;
6941     j = (buf-->0) - 1;
6942     if (buf->(j+WORDSIZE) ~= 10 or 13) j++;     ! trim any trailing newline
6943     if (centred) {
6944         k = (ScreenWidth() - j) / 2;
6945         if (k>0) spaces k;
6946     }
6947     for (i=0 : i<j : i++) {
6948         k = buf->(WORDSIZE+i);
6949         switch (set_case) {
6950           0:    break;
6951           1:    if (i) set_case = 0;
6952                 else   k = UpperCase(k);
6953           2:    if (i) k = LowerCase(k);
6954                 else   k = UpperCase(k);
6955           3:           k = LowerCase(k);
6956           4:           k = UpperCase(k);
6957         }
6958         print (char) k;
6959     }
6960     if (no_break == false) new_line;
6961     return j;
6962 ];
6963
6964 ! None of the following functions should be called for zcode if the
6965 ! output exceeds the size of the buffer.
6966
6967 [ StringSize a b c d e;
6968     PrintToBuffer(StorageForShortName, 160, a, b, c, d, e);
6969     return StorageForShortName-->0;
6970 ];
6971
6972 [ PrintCapitalised a b no_break no_caps centred;
6973     if (metaclass(a) == Routine or String || b == 0 || metaclass(a.b) == Routine or String)
6974         PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, a, b);
6975     else
6976         if (a.b == NULL) rfalse;
6977         else             return RunTimeError(2, a, b);
6978     if (no_caps == 0 or 1) no_caps = ~~no_caps;
6979     PrintFromBuffer(StorageForShortName, no_break, no_caps, centred);
6980     return print_anything_result;
6981 ];
6982
6983 [ Centre a b;
6984     PrintCapitalised(a, b, false, true, true);
6985 ];
6986
6987 [ Cap str no_caps;
6988     if (no_caps) print (string) str;
6989     else         PrintCapitalised(str,0,true);
6990 ];
6991
6992 [ PrefaceByArticle o acode pluralise capitalise  i artform findout artval;
6993     if (o provides articles) {
6994         artval=(o.&articles)-->(acode+short_name_case*LanguageCases);
6995         if (capitalise)
6996             print (Cap) artval;
6997         else
6998             print (string) artval;
6999         if (pluralise) return;
7000         print (PSN__) o; return;
7001     }
7002
7003     i = GetGNAOfObject(o);
7004     if (pluralise) {
7005         if (i < 3 || (i >= 6 && i < 9)) i = i + 3;
7006     }
7007     i = LanguageGNAsToArticles-->i;
7008
7009     artform = LanguageArticles
7010         + 3*WORDSIZE*LanguageContractionForms*(short_name_case + i*LanguageCases);
7011
7012     #Iftrue (LanguageContractionForms == 2);
7013     if (artform-->acode ~= artform-->(acode+3)) findout = true;
7014     #Endif; ! LanguageContractionForms
7015     #Iftrue (LanguageContractionForms == 3);
7016     if (artform-->acode ~= artform-->(acode+3)) findout = true;
7017     if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
7018     #Endif; ! LanguageContractionForms
7019     #Iftrue (LanguageContractionForms == 4);
7020     if (artform-->acode ~= artform-->(acode+3)) findout = true;
7021     if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
7022     if (artform-->(acode+6) ~= artform-->(acode+9)) findout = true;
7023     #Endif; ! LanguageContractionForms
7024     #Iftrue (LanguageContractionForms > 4);
7025     findout = true;
7026     #Endif; ! LanguageContractionForms
7027
7028     #Ifdef TARGET_ZCODE;
7029     if (standard_interpreter && findout) {
7030         StorageForShortName-->0 = SHORTNAMEBUF_LEN;
7031         @output_stream 3 StorageForShortName;
7032         if (pluralise) print (number) pluralise; else print (PSN__) o;
7033         @output_stream -3;
7034         acode = acode + 3*LanguageContraction(StorageForShortName + 2);
7035     }
7036     #Ifnot; ! TARGET_GLULX
7037     if (findout) {
7038         if (pluralise)
7039             PrintAnyToArray(StorageForShortName, SHORTNAMEBUF_LEN, EnglishNumber, pluralise);
7040         else
7041             PrintAnyToArray(StorageForShortName, SHORTNAMEBUF_LEN, PSN__, o);
7042         acode = acode + 3*LanguageContraction(StorageForShortName);
7043     }
7044     #Endif; ! TARGET_
7045
7046     Cap (artform-->acode, ~~capitalise); ! print article
7047     if (pluralise) return;
7048     print (PSN__) o;
7049 ];
7050
7051 [ PSN__ o;
7052     if (o == 0) { print (string) NOTHING__TX; rtrue; }
7053     switch (metaclass(o)) {
7054       Routine:  print "<routine ", o, ">"; rtrue;
7055       String:   print "<string ~", (string) o, "~>"; rtrue;
7056       nothing:  print "<illegal object number ", o, ">"; rtrue;
7057     }
7058     #Ifdef LanguagePrintShortName;
7059     if (LanguagePrintShortName(o)) rtrue;
7060     #Endif; ! LanguagePrintShortName
7061     if (indef_mode && o.&short_name_indef ~= 0 && PrintOrRun(o, short_name_indef, 1) ~= 0) rtrue;
7062     if (o.&short_name ~= 0 && PrintOrRun(o, short_name, 1) ~= 0) rtrue;
7063     print (object) o;
7064 ];
7065
7066 [ Indefart o saveIndef;
7067     saveIndef = indef_mode; indef_mode = true; caps_mode = false;
7068     if (o has proper) {
7069         indef_mode = NULL;
7070         print (PSN__) o;
7071         indef_mode = saveIndef;
7072         return;
7073     }
7074
7075     if (o provides article) {
7076         PrintOrRun(o, article, 1);
7077         print " ", (PSN__) o;
7078         indef_mode = saveIndef;
7079         return;
7080     }
7081     PrefaceByArticle(o, 2);
7082     indef_mode = saveIndef;
7083 ];
7084
7085 [ CInDefArt o saveIndef saveCaps;
7086     saveIndef = indef_mode; indef_mode = true;
7087     saveCaps = caps_mode; caps_mode = true;
7088
7089     if (o has proper) {
7090         indef_mode = NULL;
7091         PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, PSN__, o);
7092         PrintFromBuffer(StorageForShortName, true, caps_mode);
7093         caps_mode = saveCaps;
7094         return;
7095     }
7096
7097     if (o provides article) {
7098         PrintCapitalised(o, article, true);
7099         print " ", (PSN__) o;
7100         indef_mode = saveIndef;
7101         caps_mode = saveCaps;
7102         return;
7103     }
7104     PrefaceByArticle(o, 2, 0, 1);
7105     caps_mode = saveCaps;
7106     indef_mode = saveIndef;
7107 ];
7108
7109 [ Defart o saveIndef;
7110     saveIndef = indef_mode;
7111     indef_mode = false;
7112     caps_mode = false;
7113     if ((~~o ofclass Object) || o has proper) {
7114         indef_mode = NULL;
7115         if (o == player) {
7116             if (player provides narrative_voice) {
7117                 switch (player.narrative_voice) {
7118                   1:  print (string) MYSELF__TX;
7119                   2:  print (string) YOURSELF__TX;
7120                   3:  print (PSN__) o;
7121                   default: RunTimeError(16, player.narrative_voice);
7122                 }
7123             }
7124             else ThatOrThose(player);
7125         } else {
7126             print (PSN__) o;
7127         }
7128         indef_mode = saveIndef;
7129         return;
7130     }
7131     PrefaceByArticle(o, 1);
7132     indef_mode = saveIndef;
7133 ];
7134
7135 [ CDefart o saveIndef saveCaps;
7136     saveIndef = indef_mode; indef_mode = false;
7137     saveCaps = caps_mode; caps_mode = true;
7138     if (~~o ofclass Object) {
7139         indef_mode = NULL; print (PSN__) o;
7140     }
7141     else
7142         if (o has proper) {
7143             indef_mode = NULL;
7144             PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, PSN__, o);
7145             PrintFromBuffer(StorageForShortName, true, caps_mode);
7146         }
7147         else
7148             PrefaceByArticle(o, 0);
7149     indef_mode = saveIndef; caps_mode = saveCaps;
7150 ];
7151
7152 [ PrintShortName o saveIndef;
7153     saveIndef = indef_mode; indef_mode = NULL;
7154     PSN__(o); indef_mode = saveIndef;
7155 ];
7156
7157 [ EnglishNumber n; LanguageNumber(n); ];
7158
7159 [ SerialComma n;
7160     #Ifdef SERIAL_COMMAS;
7161     if (n>2) print ",";
7162     #Endif;
7163     n=0;        ! quell unused n variable warning
7164 ];
7165
7166 [ NumberWord o i n;
7167     n = LanguageNumbers-->0;
7168     for (i=1 : i<=n : i=i+2)
7169         if (o == LanguageNumbers-->i) return LanguageNumbers-->(i+1);
7170     return 0;
7171 ];
7172
7173 [ RandomEntry tab;
7174     if (tab-->0 == 0) return RunTimeError(8);
7175     return tab-->(random(tab-->0));
7176 ];
7177
7178 ! ----------------------------------------------------------------------------
7179 !  Useful routine: unsigned comparison (for addresses in Z-machine)
7180 !    Returns 1 if x>y, 0 if x=y, -1 if x<y
7181 ! ----------------------------------------------------------------------------
7182
7183 [ UnsignedCompare x y u v;
7184     if (x == y) return 0;
7185     if (x < 0 && y >= 0) return 1;
7186     if (x >= 0 && y < 0) return -1;
7187     u = x&~WORD_HIGHBIT; v= y&~WORD_HIGHBIT;
7188     if (u > v) return 1;
7189     return -1;
7190 ];
7191
7192 ! ==============================================================================
7193
7194 #Ifdef NITFOL_HOOKS;          ! Code contributed by Evin Robertson
7195 #Ifdef TARGET_GLULX;          ! Might be nice for Z-machine games too,
7196                               ! but I'm not going to try to make this work
7197                               ! given #Ifdef funniness.
7198
7199 Array magic_array -->         ! This is so nitfol can do typo correction /
7200                               ! automapping / debugging on Glulx games
7201     $6e66726d $4d616763 $ff0010 ! Goes to 'NfrmMagc'  10 refers to length
7202     Magic_Global_Dispatch__
7203     DI__check_word            ! DI__check_word(buf, length)
7204     PrintShortName
7205     WV__Pr RV__Pr CA__Pr      ! obj.prop = x; x = obj.prop; obj.prop(x)
7206     RA__Pr RL__Pr RA__Sc      ! obj.&prop; obj.#prop; class::prop
7207     OP__Pr OC__Cl             ! obj provides prop; obj ofclass class
7208     OB__Move OB__Remove
7209     OB__Parent__ OB__Child__ OB__Sibling__  ! No explicit veneer for these
7210     ;
7211
7212 [ OB__Parent__ obj; return parent(obj); ];
7213
7214 [ OB__Child__ obj; return child(obj); ];
7215
7216 [ OB__Sibling__ obj; return sibling(obj); ];
7217
7218 [ Magic_Global_Dispatch__ glbl;
7219     switch (glbl) {
7220       0:
7221         if (location == TheDark) return real_location; return location;
7222       1:
7223         return Player;
7224       -1:
7225         return CompassDirection::number; ! Silliness to make exist RA__Sc
7226                                          ! Should never be called.
7227     }
7228     return magic_array;       ! Silences a warning.
7229 ];
7230
7231 [ DI__check_word buf wlen  ix val res dictlen entrylen;
7232     ! Just like in Tokenise__.  In fact, Tokenise__ could call this if
7233     ! it wanted, instead of doing this itself.
7234     if (wlen > DICT_WORD_SIZE) wlen = DICT_WORD_SIZE;
7235     for (ix=0 : ix<wlen : ix++) {
7236         gg_tokenbuf->ix = glk_char_to_lower(buf->ix);
7237     }
7238     for (: ix<DICT_WORD_SIZE : ix++) {
7239         gg_tokenbuf->ix = 0;
7240     }
7241     val = #dictionary_table + WORDSIZE;
7242     entrylen = DICT_WORD_SIZE + 7;
7243     @binarysearch gg_tokenbuf DICT_WORD_SIZE val entrylen dictlen 1 1 res;
7244     return res;
7245 ];
7246
7247 #Endif; ! TARGET_
7248 #Endif; ! NITFOL_HOOKS
7249
7250 ! ==============================================================================
7251
7252 Object  LibraryExtensions "(Library Extensions)"
7253   with  RunAll [ prop a1 a2 a3
7254             obj rval max;
7255             objectloop (obj in self)
7256                 if (obj provides prop && obj.prop ofclass Routine) {
7257                     rval = obj.prop(a1, a2, a3);
7258                     if (rval > max) max = rval;
7259                     if (self.BetweenCalls) self.BetweenCalls();
7260                 }
7261             return max;
7262         ],
7263         RunUntil [ prop exitval a1 a2 a3
7264             obj rval;
7265             objectloop (obj in self)
7266                 if (obj provides prop && obj.prop ofclass Routine) {
7267                     rval = obj.prop(a1, a2, a3);
7268                     if (rval == exitval) return rval;
7269                     if (self.BetweenCalls) self.BetweenCalls();
7270                 }
7271             return ~~exitval;
7272         ],
7273         RunWhile [ prop exitval a1 a2 a3
7274             obj rval;
7275             objectloop (obj in self)
7276                 if (obj provides prop && obj.prop ofclass Routine) {
7277                     rval = obj.prop(a1, a2, a3);
7278                     if (rval ~= exitval) return rval;
7279                     if (self.BetweenCalls) self.BetweenCalls();
7280                 }
7281             return exitval;
7282         ],
7283
7284         ext_number_1 0, ! general temporary workspace
7285
7286         ! can be set to a function (e.g. RestoreWN) meant for execution
7287         ! after non-terminating calls to extension objects
7288         ! (via RunUntil/While/All)
7289         BetweenCalls 0,
7290         RestoreWN [; wn = self.ext_number_1; ],
7291
7292         ! Special interception points
7293         ext_messages            0,  ! Called if LibraryMessages.before()
7294                                     !    returns false
7295                                     ! Extensions run while they return false
7296
7297         ! Cross-platform entry points
7298         !                             Called/Runs
7299         ext_afterlife           0,  ! [C2/R1]
7300         ext_afterprompt         0,  ! [C2/R1]
7301         ext_amusing             0,  ! [C2/R1]
7302         ext_beforeparsing       0,  ! [C2/R2]
7303         ext_chooseobjects       0,  ! [C2/R2]
7304         ext_darktodark          0,  ! [C2/R1]
7305         ext_deathmessage        0,  ! [C2/R1]
7306         ext_gamepostroutine     0,  ! [C2/R2]
7307         ext_gamepreroutine      0,  ! [C2/R2]
7308         ext_initialise          0,  ! [C1/R1]
7309         ext_inscope             0,  ! [C2/R2]
7310         ext_lookroutine         0,  ! [C2/R1]
7311         ext_newroom             0,  ! [C2/R1]
7312         ext_objectdoesnotfit    0,  ! [C2/R2]
7313         ext_parsenoun           0,  ! [C3/R3]
7314         ext_parsenumber         0,  ! [C2/R2]
7315         ext_parsererror         0,  ! [C2/R2]
7316         ext_printrank           0,  ! [C2/R1]
7317         ext_printtaskname       0,  ! [C2/R1]
7318         ext_printverb           0,  ! [C2/R2]
7319         ext_timepasses          0,  ! [C2/R1]
7320         ext_unknownverb         0,  ! [C2/R2]
7321         ext_aftersave           0,  ! [C2/R2]
7322         ext_afterrestore        0,  ! [C2/R2]
7323         !                             [C1] = Called in all cases
7324         !                             [C2] = Called if EP is undefined, or returns false
7325         !                             [C3] = called if EP is undefined, or returns -1
7326         !                             [R1] = All extensions run
7327         !                             [R2] = Extensions run while they return false
7328         !                             [R3] = Extensions run while they return -1
7329
7330         #Ifdef TARGET_GLULX;
7331         ! Glulx entry points
7332         !                             Called:           Runs:
7333         ext_handleglkevent      0,  ! if EP undefined   while extensions return false
7334         ext_identifyglkobject   0,  ! if EP undefined   while extensions return false
7335         ext_initglkwindow       0,  ! if EP undefined   while extensions return false
7336         #Endif; ! TARGET_GLULX;
7337
7338   has   proper;
7339
7340 ! ==============================================================================
7341
7342 Undef LIBRARY_STAGE; Constant LIBRARY_STAGE = AFTER_PARSER;
7343
7344 #Ifnot;
7345 Message "Warning: 'parser' included twice; ignoring second inclusion. (Ignore this if this is on purpose.)";
7346 #Endif;
7347 ! ==============================================================================