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