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