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