1 ! ==============================================================================
2 ! PARSER: Front end to parser.
4 ! Supplied for use with Inform 6 -- Release 6.12.6 -- Serial number 220219
6 ! Copyright Graham Nelson 1993-2004 and David Griffith 2012-2022
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.
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.
18 ! You should have received a copy of the GNU Affero General Public
19 ! License along with this program. If not, see
20 ! https://gnu.org/licenses/
22 ! In your game file, Include three library files in this order:
27 ! ------------------------------------------------------------------------------
28 ! Inclusion of "linklpa" (which defines properties and attributes)
29 ! Global variables, constants and arrays
30 ! 1: outside of the parser
31 ! 2: used within the parser
32 ! Inclusion of natural language definition file
33 ! (which creates a compass and direction-objects)
34 ! Darkness and player objects
35 ! Definition of grammar token numbering system used by Inform
37 ! The InformParser object
39 ! level 0: outer shell, conversation, errors
43 ! 4: scope and ambiguity resolving
44 ! 5: object comparisons
46 ! 7: reading words and moving tables about
49 ! The InformLibrary object
52 ! end of turn sequence
53 ! scope looping, before/after sequence, sending messages out
54 ! timers, daemons, time of day, score notification
56 ! changing player personality
57 ! tracing code (only present if DEBUG is set)
59 ! Status line printing, menu display
60 ! Printing object names with articles
61 ! Miscellaneous utility routines
62 ! Game banner, "version" verb, run-time errors
63 ! ==============================================================================
67 #Ifndef LIBRARY_STAGE; ! This file is the first one to define LIBRARY_STAGE.
68 ! "This file already included" <=> "LIBRARY_STAGE exists"
70 ! ------------------------------------------------------------------------------
73 Message fatalerror "*** Library 6.12.x needs Inform v6.33 or later to work ***";
78 Constant BEFORE_PARSER 10;
79 Constant AFTER_PARSER 20;
80 Constant AFTER_VERBLIB 30;
81 Constant AFTER_GRAMMAR 40;
83 Constant LIBRARY_STAGE = BEFORE_PARSER;
85 Default COMMENT_CHARACTER '*';
91 #Ifndef WORDSIZE; ! compiling with Z-code only compiler
92 Default TARGET_ZCODE 0;
96 #Ifdef TARGET_ZCODE; ! offsets into Z-machine header
98 Constant HDR_ZCODEVERSION $00; ! byte
99 Constant HDR_TERPFLAGS $01; ! byte
100 Constant HDR_GAMERELEASE $02; ! word
101 Constant HDR_HIGHMEMORY $04; ! word
102 Constant HDR_INITIALPC $06; ! word
103 Constant HDR_DICTIONARY $08; ! word
104 Constant HDR_OBJECTS $0A; ! word
105 Constant HDR_GLOBALS $0C; ! word
106 Constant HDR_STATICMEMORY $0E; ! word
107 Constant HDR_GAMEFLAGS $10; ! word
108 Constant HDR_GAMESERIAL $12; ! six ASCII characters
109 Constant HDR_ABBREVIATIONS $18; ! word
110 Constant HDR_FILELENGTH $1A; ! word
111 Constant HDR_CHECKSUM $1C; ! word
112 Constant HDR_TERPNUMBER $1E; ! byte
113 Constant HDR_TERPVERSION $1F; ! byte
114 Constant HDR_SCREENHLINES $20; ! byte
115 Constant HDR_SCREENWCHARS $21; ! byte
116 Constant HDR_SCREENWUNITS $22; ! word
117 Constant HDR_SCREENHUNITS $24; ! word
118 #Iftrue (#version_number == 6);
119 Constant HDR_FONTHUNITS $26; ! byte
120 Constant HDR_FONTWUNITS $27; ! byte
122 Constant HDR_FONTWUNITS $26; ! byte
123 Constant HDR_FONTHUNITS $27; ! byte
125 Constant HDR_ROUTINEOFFSET $28; ! word
126 Constant HDR_STRINGOFFSET $2A; ! word
127 Constant HDR_BGCOLOUR $2C; ! byte
128 Constant HDR_FGCOLOUR $2D; ! byte
129 Constant HDR_TERMCHARS $2E; ! word
130 Constant HDR_PIXELSTO3 $30; ! word
131 Constant HDR_TERPSTANDARD $32; ! two bytes
132 Constant HDR_ALPHABET $34; ! word
133 Constant HDR_EXTENSION $36; ! word
134 Constant HDR_UNUSED $38; ! two words
135 Constant HDR_INFORMVERSION $3C; ! four ASCII characters
137 #Ifnot; ! TARGET_GLULX ! offsets into Glulx header and start of ROM
139 Constant HDR_MAGICNUMBER $00; ! long word
140 Constant HDR_GLULXVERSION $04; ! long word
141 Constant HDR_RAMSTART $08; ! long word
142 Constant HDR_EXTSTART $0C; ! long word
143 Constant HDR_ENDMEM $10; ! long word
144 Constant HDR_STACKSIZE $14; ! long word
145 Constant HDR_STARTFUNC $18; ! long word
146 Constant HDR_DECODINGTBL $1C; ! long word
147 Constant HDR_CHECKSUM $20; ! long word
148 Constant ROM_INFO $24; ! four ASCII characters
149 Constant ROM_MEMORYLAYOUT $28; ! long word
150 Constant ROM_INFORMVERSION $2C; ! four ASCII characters
151 Constant ROM_COMPVERSION $30; ! four ASCII characters
152 Constant ROM_GAMERELEASE $34; ! short word
153 Constant ROM_GAMESERIAL $36; ! six ASCII characters
163 Fake_Action ThrownAt;
166 Fake_Action PluralFound;
167 Fake_Action ListMiscellany;
168 Fake_Action Miscellany;
169 Fake_Action RunTimeError;
171 Fake_Action NotUnderstood;
179 ! ------------------------------------------------------------------------------
181 [ Main; InformLibrary.play(); ];
183 ! ------------------------------------------------------------------------------
195 ! ------------------------------------------------------------------------------
196 ! Global variables and their associated Constant and Array declarations
197 ! ------------------------------------------------------------------------------
199 Global location = InformLibrary; ! Must be first global defined
200 Global sline1; ! Must be second
201 Global sline2; ! Must be third
202 ! (for status line display)
204 ! ------------------------------------------------------------------------------
205 ! Z-Machine and interpreter issues
206 ! ------------------------------------------------------------------------------
209 Global top_object; ! Largest valid number of any tree object
210 ! ### these globals are not meaningful... well, maybe standard_interpreter,
211 ! but I'll decide that later (AP).
212 Constant INDIV_PROP_START 64; ! Equivalent of a Glulx constant
214 #Endif; ! TARGET_ZCODE
216 Global standard_interpreter; ! The version number of the Z-Machine Standard which the
217 ! interpreter claims to support, in form (upper byte).(lower)
219 Global undo_flag; ! Can the interpreter provide "undo"?
220 Global just_undone; ! Can't have two successive UNDOs
222 Global transcript_mode; ! true when game scripting is on
225 Global xcommsdir; ! true if command recording is on
226 #Endif; ! TARGET_ZCODE
229 Constant GG_MAINWIN_ROCK 201;
230 Constant GG_STATUSWIN_ROCK 202;
231 Constant GG_QUOTEWIN_ROCK 203;
232 Constant GG_SAVESTR_ROCK 301;
233 Constant GG_SCRIPTSTR_ROCK 302;
234 Constant GG_COMMANDWSTR_ROCK 303;
235 Constant GG_COMMANDRSTR_ROCK 304;
236 Constant GG_SCRIPTFREF_ROCK 401;
237 Array gg_event --> 4;
239 Array gg_arguments buffer 28;
241 Array gg_arguments --> 8;
243 Global gg_mainwin = 0;
244 Global gg_statuswin = 0;
245 Global gg_quotewin = 0;
246 Global gg_scriptfref = 0;
247 Global gg_scriptstr = 0;
248 Global gg_savestr = 0;
249 Global gg_commandstr = 0;
250 Global gg_command_reading = 0; ! true if gg_commandstr is being replayed
251 #Endif; ! TARGET_GLULX
253 Global gg_statuswin_cursize = 0;
254 Global gg_statuswin_size = 1;
256 ! ------------------------------------------------------------------------------
258 ! (for linkage reasons, the task_* arrays are created not here but in verblib.h)
259 ! ------------------------------------------------------------------------------
261 #Ifndef sys_statusline_flag;
262 Global sys_statusline_flag = 0; ! non-zero if status line displays time
266 Constant START_MOVE 0; ! Traditionally 0 for Infocom, 1 for Inform
269 Global turns = START_MOVE; ! Number of turns of play so far
270 Global the_time = NULL; ! Current time (in minutes since midnight)
271 Global time_rate = 1; ! How often time is updated
272 Global time_step; ! By how much
275 Constant MAX_TIMERS 32; ! Max number timers/daemons active at once
277 Array the_timers --> MAX_TIMERS;
278 Global active_timers; ! Number of timers/daemons actives
280 Global score; ! The current score
281 Global last_score; ! Score last turn (for testing for changes)
282 Global notify_mode = true; ! Score notification
283 Global places_score; ! Contribution to score made by visiting
284 Global things_score; ! Contribution made by acquisition
286 ! ------------------------------------------------------------------------------
288 ! ------------------------------------------------------------------------------
290 Global player; ! Which object the human is playing through
291 Global deadflag; ! Normally 0, or false; 1 for dead
292 ! 2 for victorious, and higher numbers
293 ! represent exotic forms of death
295 ! ------------------------------------------------------------------------------
296 ! Light and room descriptions
297 ! ------------------------------------------------------------------------------
299 Global lightflag = true; ! Is there currently light to see by?
300 Global real_location; ! When in darkness, location = thedark
301 ! and this holds the real location
302 Global prev_location; ! The previous value of real_location
303 Global visibility_ceiling; ! Highest object in tree visible from the
304 ! player's point of view (usually the room,
305 ! sometimes darkness, sometimes a closed
306 ! non-transparent container).
308 Global lookmode = 2; ! 1=brief, 2=verbose, 3=superbrief
310 Global print_player_flag; ! If set, print something like "(as Fred)"
311 ! in room descriptions, to reveal whom the
312 ! player is playing through
313 Global lastdesc; ! Value of location at time of most recent
314 ! room description printed out
316 ! ------------------------------------------------------------------------------
317 ! List writing (style bits are defined as Constants in "verblibm.h")
318 ! ------------------------------------------------------------------------------
320 Global c_style; ! Current list-writer style
321 Global lt_value; ! Common value of list_together
322 Global listing_together; ! Object number of one member of a group
323 ! being listed together
324 Global listing_size; ! Size of such a group
325 Global wlf_indent; ! Current level of indentation printed by
328 Global inventory_stage = 1; ! 1 or 2 according to the context in which
329 ! "invent" routines of objects are called
330 Global inventory_style; ! List-writer style currently used while
331 ! printing inventories
333 Global objects_style; ! List-writer style currently used while
334 Global places_style; ! printing objects handled or places visited
336 ! ------------------------------------------------------------------------------
338 ! ------------------------------------------------------------------------------
340 Global pretty_flag = true; ! Use character graphics, or plain text?
341 Global menu_nesting; ! Level of nesting (0 = root menu)
342 Global menu_item; ! These are used in communicating
343 Global item_width = 8; ! with the menu-creating routines
344 Global item_name = "---";
346 Global lm_n; ! Parameters used by LibraryMessages
347 Global lm_o; ! mechanism
351 Constant DEBUG_MESSAGES $0001;
352 Constant DEBUG_ACTIONS $0002;
353 Constant DEBUG_TIMERS $0004;
354 Constant DEBUG_CHANGES $0008;
355 Constant DEBUG_VERBOSE $0080;
356 Global debug_flag; ! Bitmap of flags for tracing actions,
357 ! calls to object routines, etc.
358 Global x_scope_count; ! Used in printing a list of everything
359 #Endif; ! DEBUG ! in scope
361 ! five for colour control
362 ! see http://www.inform-fiction.org/patches/L61007.html
363 ! To enable colour define a constant or Global: COLOR or COLOUR
364 !Global clr_on; ! has colour been enabled by the player?
366 Global clr_fg = 1; ! foreground colour
367 Global clr_bg = 1; ! background colour
368 Global clr_fgstatus = 1; ! foreground colour of statusline
369 Global clr_bgstatus = 1; ! background colour of statusline
371 Global statuswin_current; ! if writing to top window
373 Constant CLR_CURRENT 0;
374 Constant CLR_DEFAULT 1;
375 Constant CLR_BLACK 2;
377 Constant CLR_GREEN 4;
378 Constant CLR_YELLOW 5;
380 Constant CLR_MAGENTA 7;
382 Constant CLR_WHITE 9;
383 Constant CLR_PURPLE 7;
384 Constant CLR_AZURE 8;
387 Array GlulxColourValues
398 #Endif; ! TARGET_GLULX
401 Constant WIN_STATUS 1;
404 ! ------------------------------------------------------------------------------
406 ! ------------------------------------------------------------------------------
408 Global action; ! Action currently being asked to perform
409 Global inp1; ! 0 (nothing), 1 (number) or first noun
410 Global inp2; ! 0 (nothing), 1 (number) or second noun
411 Global noun; ! First noun or numerical value
412 Global second; ! Second noun or numerical value
414 Global keep_silent; ! If true, attempt to perform the action
415 ! silently (e.g. for implicit takes,
416 ! implicit opening of unlocked doors)
418 Global reason_code; ! Reason for calling a "life" rule
419 ! (an action or fake such as ##Kiss)
421 Global receive_action; ! Either ##PutOn or ##Insert, whichever is
422 ! action being tried when an object's
423 ! "before" rule is checking "Receive"
425 Global no_implicit_actions; ! Don't implicitly do things.
427 ! ==============================================================================
428 ! Parser variables: first, for communication to the parser
429 ! ------------------------------------------------------------------------------
431 Global parser_trace = 0; ! Set this to 1 to make the parser trace
433 Global parser_action; ! For the use of the parser when calling
434 Global parser_one; ! user-supplied routines
436 Array inputobjs --> 16; ! For parser to write its results in
437 Global parser_inflection; ! A property (usually "name") to find
439 Global parser_inflection_func; ! Programmer sets this to true when
440 ! parser_infection is a function
442 ! ------------------------------------------------------------------------------
444 ! ------------------------------------------------------------------------------
446 Global actor; ! Person asked to do something
447 Global actors_location; ! Like location, but for the actor
448 Global meta; ! Verb is a meta-command (such as "save")
451 Global infix_verb; ! Verb is an Infix command
454 Array multiple_object --> 64; ! List of multiple parameters
455 Global multiflag; ! Multiple-object flag passed to actions
456 ! Also used to prevent misleading MULTI_PE
457 Global toomany_flag; ! Flag for "multiple match too large"
458 ! (e.g. if "take all" took over 100 things)
460 Global special_word; ! Dictionary address for "special" token
461 Global special_number; ! Number typed for "special" token
462 Global parsed_number; ! For user-supplied parsing routines
463 Global consult_from; ! Word that a "consult" topic starts on
464 Global consult_words; ! ...and number of words in topic
465 Global asking_player; ! True during disambiguation question
467 ! ------------------------------------------------------------------------------
469 ! ------------------------------------------------------------------------------
471 Global notheld_mode; ! To do with implicit taking
472 Global onotheld_mode; ! "old copy of notheld_mode", ditto
473 Global not_holding; ! Object to be automatically taken as an
475 Array kept_results --> 16; ! Delayed command (while the take happens)
477 ! ------------------------------------------------------------------------------
478 ! Error numbers when parsing a grammar line
479 ! ------------------------------------------------------------------------------
481 Global etype; ! Error number on current line
482 Global best_etype; ! Preferred error number so far
483 Global nextbest_etype; ! Preferred one, if ASKSCOPE_PE disallowed
485 Constant STUCK_PE = 1;
486 Constant UPTO_PE = 2;
487 Constant NUMBER_PE = 3;
488 Constant CANTSEE_PE = 4;
489 Constant TOOLIT_PE = 5;
490 Constant NOTHELD_PE = 6;
491 Constant MULTI_PE = 7;
492 Constant MMULTI_PE = 8;
493 Constant VAGUE_PE = 9;
494 Constant EXCEPT_PE = 10;
495 Constant ANIMA_PE = 11;
496 Constant VERB_PE = 12;
497 Constant SCENERY_PE = 13;
498 Constant ITGONE_PE = 14;
499 Constant JUNKAFTER_PE = 15;
500 Constant TOOFEW_PE = 16;
501 Constant NOTHING_PE = 17;
502 Constant ASKSCOPE_PE = 18;
504 ! ------------------------------------------------------------------------------
505 ! Pattern-matching against a single grammar line
506 ! ------------------------------------------------------------------------------
508 Array pattern --> 32; ! For the current pattern match
509 Global pcount; ! and a marker within it
510 Array pattern2 --> 32; ! And another, which stores the best match
511 Global pcount2; ! so far
512 Constant PATTERN_NULL = $ffff; ! Entry for a token producing no text
514 Array line_ttype-->32; ! For storing an analysed grammar line
515 Array line_tdata-->32;
516 Array line_token-->32;
518 Global parameters; ! Parameters (objects) entered so far
519 Global nsns; ! Number of special_numbers entered so far
520 Global special_number1; ! First number, if one was typed
521 Global special_number2; ! Second number, if two were typed
523 ! ------------------------------------------------------------------------------
524 ! Inferences and looking ahead
525 ! ------------------------------------------------------------------------------
527 Global params_wanted; ! Number of parameters needed
528 ! (which may change in parsing)
530 Global inferfrom; ! The point from which the rest of the
531 ! command must be inferred
532 Global inferword; ! And the preposition inferred
533 Global dont_infer; ! Another dull flag
534 Global no_infer_message = false; ! Use in ChooseObjects to suppress
535 ! an inference message.
537 Global action_to_be; ! (If the current line were accepted.)
538 Global action_reversed; ! (Parameters would be reversed in order.)
539 Global advance_warning; ! What a later-named thing will be
541 ! ------------------------------------------------------------------------------
542 ! At the level of individual tokens now
543 ! ------------------------------------------------------------------------------
545 Global found_ttype; ! Used to break up tokens into type
546 Global found_tdata; ! and data (by AnalyseToken)
547 Global token_filter; ! For noun filtering by user routines
549 Global length_of_noun; ! Set by NounDomain to no of words in noun
552 Constant REPARSE_CODE = 10000; ! Signals "reparse the text" as a reply
554 #Ifnot; ! TARGET_GLULX
555 Constant REPARSE_CODE = $40000000; ! The parser rather gunkily adds addresses
556 ! to REPARSE_CODE for some purposes and
557 ! expects the result to be greater than
558 ! REPARSE_CODE (signed comparison).
559 ! So Glulx Inform is limited to a single
560 ! gigabyte of storage, for the moment.
563 Global lookahead; ! The token after the one now being matched
565 Global multi_mode; ! Multiple mode
566 Global multi_wanted; ! Number of things needed in multitude
567 Global multi_had; ! Number of things actually found
568 Global multi_context; ! What token the multi-obj was accepted for
570 Global indef_mode; ! "Indefinite" mode - ie, "take a brick"
572 Global indef_type; ! Bit-map holding types of specification
573 Global indef_wanted; ! Number of items wanted (100 for all)
574 Global indef_guess_p; ! Plural-guessing flag
575 Global indef_owner; ! Object which must hold these items
576 Global indef_cases; ! Possible gender and numbers of them
577 Global indef_possambig; ! Has a possibly dangerous assumption
578 ! been made about meaning of a descriptor?
579 Global indef_nspec_at; ! Word at which a number like "two" was
580 ! parsed (for backtracking)
581 Global allow_plurals; ! Whether plurals presently allowed or not
583 Global take_all_rule; ! Slightly different rules apply to
584 ! "take all" than other uses of multiple
585 ! objects, to make adjudication produce
586 ! more pragmatically useful results
587 ! (Not a flag: possible values 0, 1, 2)
589 Global dict_flags_of_noun; ! Of the noun currently being parsed
590 ! (a bitmap in #dict_par1 format)
591 Constant DICT_VERB $01;
592 Constant DICT_META $02;
593 Constant DICT_PLUR $04;
594 Constant DICT_PREP $08;
595 Constant DICT_X654 $70;
596 Constant DICT_NOUN $80;
598 Global pronoun_word; ! Records which pronoun ("it", "them", ...)
600 Global pronoun_obj; ! And what obj it was thought to refer to
601 Global pronoun__word; ! Saved value
602 Global pronoun__obj; ! Saved value
604 ! ------------------------------------------------------------------------------
605 ! Searching through scope and parsing "scope=Routine" grammar tokens
606 ! ------------------------------------------------------------------------------
608 Constant PARSING_REASON = 0; ! Possible reasons for searching scope
609 Constant TALKING_REASON = 1;
610 Constant EACH_TURN_REASON = 2;
611 Constant REACT_BEFORE_REASON = 3;
612 Constant REACT_AFTER_REASON = 4;
613 Constant LOOPOVERSCOPE_REASON = 5;
614 Constant TESTSCOPE_REASON = 6;
616 Global scope_reason = PARSING_REASON; ! Current reason for searching scope
618 Global scope_token; ! For "scope=Routine" grammar tokens
620 Global scope_stage; ! 1, 2 then 3
622 Global ats_flag = 0; ! For AddToScope routines
625 Global placed_in_flag; ! To do with PlaceInScope
627 ! ------------------------------------------------------------------------------
628 ! The match list of candidate objects for a given token
629 ! ------------------------------------------------------------------------------
631 Constant MATCH_LIST_SIZE = 64;
632 Array match_list --> MATCH_LIST_SIZE; ! An array of matched objects so far
633 Array match_classes --> MATCH_LIST_SIZE; ! An array of equivalence classes for them
634 Array match_scores --> MATCH_LIST_SIZE; ! An array of match scores for them
635 Global number_matched; ! How many items in it? (0 means none)
636 Global number_of_classes; ! How many equivalence classes?
637 Global match_length; ! How many words long are these matches?
639 Global match_from; ! At what word of the input do they begin?
640 Global bestguess_score; ! What did the best-guess object score?
642 ! ------------------------------------------------------------------------------
643 ! Low level textual manipulation
644 ! ------------------------------------------------------------------------------
648 ! 'buffer' holds the input line as typed by the player
650 ! buffer->0 INPUT_BUFFER_LEN - WORDSIZE
651 ! buffer->1 Number of characters input by player
652 ! buffer->2 ... buffer->121 The actual characters
653 ! buffer->122 Spare byte to allow for 'terp bugs
655 ! 'parse' holds the result of parsing that line into dictionary words
657 ! parse->0 MAX_BUFFER_WORDS
658 ! parse->1 Number of words input by player
660 ! parse-->1 Dictionary addr of first input word
661 ! parse->4 Number of characters in the word
662 ! parse->5 Start position in 'buffer' of the word
664 ! parse-->3 parse->8,9 Same data for second input word
666 ! parse-->29 parse->60,61 Same data for MAX_BUFFER_WORDS input word
667 ! parse->62,63,64 Spare bytes (not sure why)
670 Constant INPUT_BUFFER_LEN = WORDSIZE + 120; ! 120 is limit on input chars
671 Constant MAX_BUFFER_WORDS = 15; ! Limit on input words
673 Array buffer -> INPUT_BUFFER_LEN + 1; ! For main line of input
674 Array buffer2 -> INPUT_BUFFER_LEN + 1; ! For supplementary questions
675 Array buffer3 -> INPUT_BUFFER_LEN + 1; ! Retaining input for "AGAIN"
678 Array parse buffer (MAX_BUFFER_WORDS * 4) + 3; ! Parsed data from 'buffer'
679 Array parse2 buffer (MAX_BUFFER_WORDS * 4) + 3; ! Parsed data from 'buffer2'
681 Array parse -> 2 + (MAX_BUFFER_WORDS * 4) + 3;
682 Array parse2 -> 2 + (MAX_BUFFER_WORDS * 4) + 3;
685 #Ifnot; ! TARGET_GLULX
687 ! 'buffer' holds the input line as typed by the player
689 ! buffer-->0 Number of characters input by player
690 ! buffer->4 ... buffer->259 The actual characters
692 ! 'parse' holds the result of parsing that line into dictionary words
694 ! parse-->0 Number of words input by player
696 ! parse-->1 Dictionary addr of first input word
697 ! parse-->2 Number of characters in the word
698 ! parse-->3 Start position in 'buffer' of the word
700 ! parse-->4,5,6 Same data for second input word
702 ! parse-->58,59,60 Same data for MAX_BUFFER_WORDS input word
704 Constant INPUT_BUFFER_LEN = WORDSIZE + 256; ! 256 is limit on input chars
705 Constant MAX_BUFFER_WORDS = 20; ! Limit on input words
708 Array buffer buffer (INPUT_BUFFER_LEN-WORDSIZE); ! For main line of input
709 Array buffer2 buffer (INPUT_BUFFER_LEN-WORDSIZE); ! For supplementary questions
710 Array buffer3 buffer (INPUT_BUFFER_LEN-WORDSIZE); ! Retaining input for "AGAIN"
712 Array buffer -> INPUT_BUFFER_LEN;
713 Array buffer2 -> INPUT_BUFFER_LEN;
714 Array buffer3 -> INPUT_BUFFER_LEN;
716 Array parse --> 1 + (MAX_BUFFER_WORDS * 3); ! Parsed data from 'buffer'
717 Array parse2 --> 1 + (MAX_BUFFER_WORDS * 3); ! Parsed data from 'buffer2'
721 Constant comma_word = 'comma,'; ! An "untypeable word" used to substitute
722 ! for commas in parse buffers
724 Global wn; ! Word number within "parse" (from 1)
725 Global num_words; ! Number of words typed
726 Global num_desc; ! Number of descriptors typed
727 Global verb_word; ! Verb word (eg, take in "take all" or
728 ! "dwarf, take all") - address in dict
729 Global verb_wordnum; ! its number in typing order (eg, 1 or 3)
730 Global usual_grammar_after; ! Point from which usual grammar is parsed (it may vary from the
731 ! above if user's routines match multi-word verbs)
733 Global oops_from; ! The "first mistake" word number
734 Global saved_oops; ! Used in working this out
736 Constant OOPS_WORKSPACE_LEN 64; ! Used temporarily by "oops" routine
737 Array oops_workspace -> OOPS_WORKSPACE_LEN;
739 Global held_back_mode; ! Flag: is there some input from last time
740 Global hb_wn; ! left over? (And a save value for wn.)
741 ! (Used for full stops and "then".)
743 Global caps_mode; ! Keep track of (The) with 'proper' caps
744 Global print_anything_result; ! Return value from a PrintAny() routine
745 Global initial_lookmode; ! Default, or set in Initialise()
746 Global before_first_turn; ! True until after initial LOOK
748 ! ----------------------------------------------------------------------------
750 Array PowersOfTwo_TB ! Used in converting case numbers to case
751 --> $$100000000000 ! bitmaps
764 ! ============================================================================
765 ! Constants, and one variable, needed for the language definition file
766 ! ----------------------------------------------------------------------------
768 Constant POSSESS_PK = $100;
769 Constant DEFART_PK = $101;
770 Constant INDEFART_PK = $102;
771 Global short_name_case;
774 Global dict_entry_size;
777 ! ----------------------------------------------------------------------------
779 Include "language__"; ! The natural language definition, whose filename is taken from
780 ! the ICL language_name variable
782 ! ----------------------------------------------------------------------------
784 #Ifndef LanguageCases;
785 Constant LanguageCases = 1;
786 #Endif; ! LanguageCases
788 ! ------------------------------------------------------------------------------
789 ! Pronouns support for the cruder (library 6/2 and earlier) version:
790 ! only needed in English
791 ! ------------------------------------------------------------------------------
793 #Ifdef EnglishNaturalLanguage;
794 Global itobj = NULL; ! The object which is currently "it"
795 Global himobj = NULL; ! The object which is currently "him"
796 Global herobj = NULL; ! The object which is currently "her"
798 Global old_itobj = NULL; ! The object which is currently "it"
799 Global old_himobj = NULL; ! The object which is currently "him"
800 Global old_herobj = NULL; ! The object which is currently "her"
801 #Endif; ! EnglishNaturalLanguage
803 ! ============================================================================
804 ! For present and past tenses
805 ! ----------------------------------------------------------------------------
806 Constant PRESENT_TENSE 0;
807 Constant PAST_TENSE 1;
809 ! ============================================================================
810 ! For InformLibrary.actor_act() to control what happens when it aborts.
811 ! ----------------------------------------------------------------------------
812 Constant ACTOR_ACT_OK 0;
813 Constant ACTOR_ACT_ABORT_NOTUNDERSTOOD 1;
814 Constant ACTOR_ACT_ABORT_ORDER 2;
816 ! ============================================================================
817 ! "Darkness" is not really a place: but it has to be an object so that the
818 ! location-name on the status line can be "Darkness".
819 ! ----------------------------------------------------------------------------
821 Object thedark "(darkness object)"
823 short_name DARKNESS__TX,
824 description [; return L__M(##Miscellany, 17); ];
826 ! If you want to use the third-person of the narrative voice, you will
827 ! need to replace this selfobj with your own.
829 with name ',a' ',b' ',c' ',d' ',e',
830 short_name YOURSELF__TX,
831 description [; return L__M(##Miscellany, 19); ],
845 narrative_tense PRESENT_TENSE,
848 before_implicit [;Take: return 2;],
849 has concealed animate proper transparent;
851 SelfClass selfobj "(self object)";
853 ! ============================================================================
854 ! The definition of the token-numbering system used by Inform.
855 ! ----------------------------------------------------------------------------
857 Constant ILLEGAL_TT = 0; ! Types of grammar token: illegal
858 Constant ELEMENTARY_TT = 1; ! (one of those below)
859 Constant PREPOSITION_TT = 2; ! e.g. 'into'
860 Constant ROUTINE_FILTER_TT = 3; ! e.g. noun=CagedCreature
861 Constant ATTR_FILTER_TT = 4; ! e.g. edible
862 Constant SCOPE_TT = 5; ! e.g. scope=Spells
863 Constant GPR_TT = 6; ! a general parsing routine
865 Constant NOUN_TOKEN = 0; ! The elementary grammar tokens, and
866 Constant HELD_TOKEN = 1; ! the numbers compiled by Inform to
867 Constant MULTI_TOKEN = 2; ! encode them
868 Constant MULTIHELD_TOKEN = 3;
869 Constant MULTIEXCEPT_TOKEN = 4;
870 Constant MULTIINSIDE_TOKEN = 5;
871 Constant CREATURE_TOKEN = 6;
872 Constant SPECIAL_TOKEN = 7;
873 Constant NUMBER_TOKEN = 8;
874 Constant TOPIC_TOKEN = 9;
877 Constant GPR_FAIL = -1; ! Return values from General Parsing
878 Constant GPR_PREPOSITION = 0; ! Routines
879 Constant GPR_NUMBER = 1;
880 Constant GPR_MULTIPLE = 2;
881 Constant GPR_REPARSE = REPARSE_CODE;
882 Constant GPR_NOUN = $ff00;
883 Constant GPR_HELD = $ff01;
884 Constant GPR_MULTI = $ff02;
885 Constant GPR_MULTIHELD = $ff03;
886 Constant GPR_MULTIEXCEPT = $ff04;
887 Constant GPR_MULTIINSIDE = $ff05;
888 Constant GPR_CREATURE = $ff06;
890 Constant ENDIT_TOKEN = 15; ! Value used to mean "end of grammar line"
892 #Iftrue (Grammar__Version == 1);
894 [ AnalyseToken token m;
896 if (token < 0) { found_ttype = ILLEGAL_TT; return; }
897 if (token <= 8) { found_ttype = ELEMENTARY_TT; return; }
898 if (token < 15) { found_ttype = ILLEGAL_TT; return; }
899 if (token == 15) { found_ttype = ELEMENTARY_TT; return; }
900 if (token < 48) { found_ttype = ROUTINE_FILTER_TT;
901 found_tdata = token - 16;
904 if (token < 80) { found_ttype = GPR_TT;
905 found_tdata = #preactions_table-->(token-48);
908 if (token < 128) { found_ttype = SCOPE_TT;
909 found_tdata = #preactions_table-->(token-80);
912 if (token < 180) { found_ttype = ATTR_FILTER_TT;
913 found_tdata = token - 128;
917 found_ttype = PREPOSITION_TT;
918 m = #adjectives_table;
920 if (token == m-->1) { found_tdata = m-->0; return; }
923 m = #adjectives_table; RunTimeError(1);
927 [ UnpackGrammarLine line_address i m;
928 for (i=0 : i<32 : i++) {
929 line_token-->i = ENDIT_TOKEN;
930 line_ttype-->i = ELEMENTARY_TT;
931 line_tdata-->i = ENDIT_TOKEN;
933 for (i=0 : i<=5 : i++) {
934 line_token-->i = line_address->(i+1);
935 AnalyseToken(line_token-->i);
936 if ((found_ttype == ELEMENTARY_TT) && (found_tdata == NOUN_TOKEN)
937 && (m == line_address->0)) {
938 line_token-->i = ENDIT_TOKEN;
941 line_ttype-->i = found_ttype;
942 line_tdata-->i = found_tdata;
943 if (found_ttype ~= PREPOSITION_TT) m++;
945 action_to_be = line_address->7;
946 action_reversed = false;
947 params_wanted = line_address->0;
948 return line_address + 8;
951 #Ifnot; ! Grammar__Version == 2
953 [ AnalyseToken token;
954 if (token == ENDIT_TOKEN) {
955 found_ttype = ELEMENTARY_TT;
956 found_tdata = ENDIT_TOKEN;
959 found_ttype = (token->0) & $$1111;
960 found_tdata = (token+1)-->0;
965 [ UnpackGrammarLine line_address i;
966 for (i=0 : i<32 : i++) {
967 line_token-->i = ENDIT_TOKEN;
968 line_ttype-->i = ELEMENTARY_TT;
969 line_tdata-->i = ENDIT_TOKEN;
971 action_to_be = 256*(line_address->0) + line_address->1;
972 action_reversed = ((action_to_be & $400) ~= 0);
973 action_to_be = action_to_be & $3ff;
977 line_address = line_address + 3;
978 if (line_address->0 == ENDIT_TOKEN) break;
979 line_token-->i = line_address;
980 AnalyseToken(line_address);
981 if (found_ttype ~= PREPOSITION_TT) params_wanted++;
982 line_ttype-->i = found_ttype;
983 line_tdata-->i = found_tdata;
985 return line_address + 1;
988 #Ifnot; ! TARGET_GLULX
990 [ UnpackGrammarLine line_address i;
991 for (i=0 : i<32 : i++) {
992 line_token-->i = ENDIT_TOKEN;
993 line_ttype-->i = ELEMENTARY_TT;
994 line_tdata-->i = ENDIT_TOKEN;
996 @aloads line_address 0 action_to_be;
997 action_reversed = (((line_address->2) & 1) ~= 0);
998 line_address = line_address - 2;
1001 line_address = line_address + 5;
1002 if (line_address->0 == ENDIT_TOKEN) break;
1003 line_token-->i = line_address;
1004 AnalyseToken(line_address);
1005 if (found_ttype ~= PREPOSITION_TT) params_wanted++;
1006 line_ttype-->i = found_ttype;
1007 line_tdata-->i = found_tdata;
1009 return line_address + 1;
1013 #Endif; ! Grammar__Version
1015 ! To protect against a bug in early versions of the "Zip" interpreter:
1016 ! Of course, in Glulx, this routine actually performs work.
1018 #Ifdef TARGET_ZCODE;
1020 [ Tokenise__ b p; b->(2 + b->1) = 0; @tokenise b p; ];
1022 #Ifnot; ! TARGET_GLULX
1024 Array gg_tokenbuf -> DICT_WORD_SIZE;
1026 [ GGWordCompare str1 str2 ix jx;
1027 for (ix=0 : ix<DICT_WORD_SIZE : ix++) {
1028 jx = (str1->ix) - (str2->ix);
1029 if (jx ~= 0) return jx;
1034 [ Tokenise__ buf tab
1035 cx numwords len bx ix wx wpos wlen val res dictlen entrylen;
1039 ! First, split the buffer up into words. We use the standard Infocom
1040 ! list of word separators (comma, period, double-quote).
1045 while (cx < len && buf->cx == ' ') cx++;
1046 if (cx >= len) break;
1048 if (buf->cx == '.' or ',' or '"') cx++;
1050 while (cx < len && buf->cx ~= ' ' or '.' or ',' or '"') cx++;
1052 tab-->(numwords*3+2) = (cx-bx);
1053 tab-->(numwords*3+3) = WORDSIZE+bx;
1055 if (numwords >= MAX_BUFFER_WORDS) break;
1059 ! Now we look each word up in the dictionary.
1061 dictlen = #dictionary_table-->0;
1062 entrylen = DICT_WORD_SIZE + 7;
1064 for (wx=0 : wx<numwords : wx++) {
1065 wlen = tab-->(wx*3+2);
1066 wpos = tab-->(wx*3+3);
1068 ! Copy the word into the gg_tokenbuf array, clipping to DICT_WORD_SIZE
1069 ! characters and lower case.
1070 if (wlen > DICT_WORD_SIZE) wlen = DICT_WORD_SIZE;
1071 cx = wpos - WORDSIZE;
1072 for (ix=0 : ix<wlen : ix++) gg_tokenbuf->ix = glk_char_to_lower(buf->(cx+ix));
1073 for (: ix<DICT_WORD_SIZE : ix++) gg_tokenbuf->ix = 0;
1075 val = #dictionary_table + WORDSIZE;
1076 @binarysearch gg_tokenbuf DICT_WORD_SIZE val entrylen dictlen 1 1 res;
1077 tab-->(wx*3+1) = res;
1083 ! ============================================================================
1084 ! The InformParser object abstracts the front end of the parser.
1086 ! InformParser.parse_input(results)
1087 ! returns only when a sensible request has been made, and puts into the
1090 ! --> 0 = The action number
1091 ! --> 1 = Number of parameters
1092 ! --> 2, 3, ... = The parameters (object numbers), but
1093 ! 0 means "put the multiple object list here"
1094 ! 1 means "put one of the special numbers here"
1096 ! ----------------------------------------------------------------------------
1098 Object InformParser "(Inform Parser)"
1099 with parse_input [ results; Parser__parse(results); ],
1102 ! ----------------------------------------------------------------------------
1103 ! The Keyboard routine actually receives the player's words,
1104 ! putting the words in "a_buffer" and their dictionary addresses in
1105 ! "a_table". It is assumed that the table is the same one on each
1108 ! It can also be used by miscellaneous routines in the game to ask
1109 ! yes-no questions and the like, without invoking the rest of the parser.
1111 ! Return the number of words typed
1112 ! ----------------------------------------------------------------------------
1114 #Ifdef TARGET_ZCODE;
1116 [ GetNthChar a_buffer n i;
1117 for (i = 0: a_buffer->(2+i) == ' ': i++) {
1118 if (i > a_buffer->(1)) return false;
1120 return a_buffer->(2+i+n);
1123 [ KeyboardPrimitive a_buffer a_table;
1124 read a_buffer a_table;
1126 #Iftrue (#version_number == 6);
1128 @loadb a_buffer 1 -> sp;
1129 @add a_buffer 2 -> sp;
1136 [ KeyCharPrimitive win key;
1137 if (win) @set_window win;
1138 @read_char 1 -> key;
1142 [ KeyTimerInterrupt;
1146 [ KeyDelay tenths key;
1147 @read_char 1 tenths KeyTimerInterrupt -> key;
1151 #Ifnot; ! TARGET_GLULX
1153 [ GetNthChar a_buffer n i;
1154 for (i = 0: a_buffer->(4+i) == ' ': i++) {
1155 if (i > a_buffer->(1)) return false;
1157 return a_buffer->(4+i+n);
1160 [ KeyCharPrimitive win nostat done res ix jx ch;
1161 jx = ch; ! squash compiler warnings
1162 if (win == 0) win = gg_mainwin;
1163 if (gg_commandstr ~= 0 && gg_command_reading ~= false) {
1165 done = glk_get_line_stream(gg_commandstr, gg_arguments, 31);
1167 glk_stream_close(gg_commandstr, 0);
1169 gg_command_reading = false;
1170 ! fall through to normal user input.
1173 ! Trim the trailing newline
1174 if (gg_arguments->(done-1) == 10) done = done-1;
1175 res = gg_arguments->0;
1178 for (ix=1 : ix<done : ix++) {
1179 ch = gg_arguments->ix;
1180 if (ch >= '0' && ch <= '9') {
1182 res = res + (ch-'0');
1184 else if (ch >= 'a' && ch <= 'f') {
1186 res = res + (ch+10-'a');
1188 else if (ch >= 'A' && ch <= 'F') {
1190 res = res + (ch+10-'A');
1198 glk_request_char_event(win);
1200 glk_select(gg_event);
1201 switch (gg_event-->0) {
1204 glk_cancel_char_event(win);
1210 2: ! evtype_CharInput
1211 if (gg_event-->1 == win) {
1216 ix = HandleGlkEvent(gg_event, 1, gg_arguments);
1217 if (ix == 0) ix = LibraryExtensions.RunWhile(ext_handleglkevent, 0, gg_event, 1, gg_arguments);
1219 res = gg_arguments-->0;
1222 else if (ix == -1) {
1226 if (gg_commandstr ~= 0 && gg_command_reading == false) {
1227 if (res < 32 || res >= 256 || (res == '\' or ' ')) {
1228 glk_put_char_stream(gg_commandstr, '\');
1231 for (ix=0 : ix<8 : ix++) {
1235 if (ch ~= 0 || ix == 7) done = 1;
1237 if (ch >= 0 && ch <= 9) ch = ch + '0';
1238 else ch = (ch - 10) + 'A';
1239 glk_put_char_stream(gg_commandstr, ch);
1244 glk_put_char_stream(gg_commandstr, res);
1246 glk_put_char_stream(gg_commandstr, 10);
1252 [ KeyDelay tenths key done ix;
1253 glk_request_char_event(gg_mainwin);
1254 glk_request_timer_events(tenths*100);
1256 glk_select(gg_event);
1257 ix = HandleGlkEvent(gg_event, 1, gg_arguments);
1258 if (ix == 0) ix = LibraryExtensions.RunWhile(ext_handleglkevent, 0, gg_event, 1, gg_arguments);
1260 key = gg_arguments-->0;
1263 else if (ix >= 0 && gg_event-->0 == 1 or 2) {
1268 glk_cancel_char_event(gg_mainwin);
1269 glk_request_timer_events(0);
1273 [ KeyboardPrimitive a_buffer a_table done ix;
1274 if (gg_commandstr ~= 0 && gg_command_reading ~= false) {
1276 done = glk_get_line_stream(gg_commandstr, a_buffer+WORDSIZE, (INPUT_BUFFER_LEN-WORDSIZE)-1);
1278 glk_stream_close(gg_commandstr, 0);
1280 gg_command_reading = false;
1281 ! L__M(##CommandsRead, 5); would come after prompt
1282 ! fall through to normal user input.
1285 ! Trim the trailing newline
1286 if ((a_buffer+WORDSIZE)->(done-1) == 10) done = done-1;
1287 a_buffer-->0 = done;
1288 glk_set_style(style_Input);
1289 glk_put_buffer(a_buffer+WORDSIZE, done);
1290 glk_set_style(style_Normal);
1296 glk_request_line_event(gg_mainwin, a_buffer+WORDSIZE, INPUT_BUFFER_LEN-WORDSIZE, 0);
1298 glk_select(gg_event);
1299 switch (gg_event-->0) {
1302 3: ! evtype_LineInput
1303 if (gg_event-->1 == gg_mainwin) {
1304 a_buffer-->0 = gg_event-->2;
1307 ix = HandleGlkEvent(gg_event, 0, a_buffer);
1308 if (ix == 0) ix = LibraryExtensions.RunWhile(ext_handleglkevent, 0, gg_event, 0, a_buffer);
1309 if (ix == 2) done = true;
1310 else if (ix == -1) done = false;
1312 if (gg_commandstr ~= 0 && gg_command_reading == false) {
1315 glk_put_buffer_stream(gg_commandstr, a_buffer+WORDSIZE, a_buffer-->0);
1316 glk_put_char_stream(gg_commandstr, 10);
1319 Tokenise__(a_buffer,a_table);
1320 ! It's time to close any quote window we've got going.
1322 glk_window_close(gg_quotewin, 0);
1329 [ Keyboard a_buffer a_table nw i w w2 x1 x2;
1334 ! Save the start of the buffer, in case "oops" needs to restore it
1335 ! to the previous time's buffer
1337 for (i=0 : i<OOPS_WORKSPACE_LEN : i++) oops_workspace->i = a_buffer->i;
1339 ! In case of an array entry corruption that shouldn't happen, but would be
1340 ! disastrous if it did:
1342 #Ifdef TARGET_ZCODE;
1343 a_buffer->0 = INPUT_BUFFER_LEN - WORDSIZE;
1344 a_table->0 = MAX_BUFFER_WORDS; ! Allow to split input into this many words
1347 ! Print the prompt, and read in the words and dictionary addresses
1350 if (AfterPrompt() == 0) LibraryExtensions.RunAll(ext_afterprompt);
1355 KeyboardPrimitive(a_buffer, a_table);
1356 nw = NumberWords(a_table);
1358 ! If the line was blank, get a fresh line
1360 L__M(##Miscellany, 10);
1364 ! Unless the opening word was "oops", return
1365 ! Conveniently, a_table-->1 is the first word in both ZCODE and GLULX.
1368 if (w == OOPS1__WD or OOPS2__WD or OOPS3__WD) jump DoOops;
1370 if (a_buffer->WORDSIZE == COMMENT_CHARACTER) {
1371 #Ifdef TARGET_ZCODE;
1372 if ((HDR_GAMEFLAGS-->0) & $0001 || xcommsdir)
1373 L__M(##Miscellany, 54);
1374 else L__M(##Miscellany, 55);
1375 #Ifnot; ! TARGET_GLULX
1376 if (gg_scriptstr || gg_commandstr) L__M(##Miscellany, 54);
1377 else L__M(##Miscellany, 55);
1386 if ((w == UNDO1__WD or UNDO2__WD or UNDO3__WD) && (nw==1)) {
1388 if (i == 0) jump FreshInput;
1390 #Ifdef TARGET_ZCODE;
1392 #Ifnot; ! TARGET_GLULX
1402 if (i == -1) undo_flag = 0;
1403 if (i == 0) undo_flag = 1;
1406 #Ifdef TARGET_ZCODE;
1408 #Ifnot; ! TARGET_GLULX
1409 glk_set_style(style_Subheader);
1411 print (name) location, "^";
1412 #Ifdef TARGET_ZCODE;
1414 #Ifnot; ! TARGET_GLULX
1415 glk_set_style(style_Normal);
1417 L__M(##Miscellany, 13);
1426 if (oops_from == 0) {
1427 L__M(##Miscellany, 14);
1431 L__M(##Miscellany, 15);
1435 L__M(##Miscellany, 16);
1439 ! So now we know: there was a previous mistake, and the player has
1440 ! attempted to correct a single word of it.
1442 for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer2->i = a_buffer->i;
1443 #Ifdef TARGET_ZCODE;
1444 x1 = a_table->9; ! Start of word following "oops"
1445 x2 = a_table->8; ! Length of word following "oops"
1446 #Ifnot; ! TARGET_GLULX
1447 x1 = a_table-->6; ! Start of word following "oops"
1448 x2 = a_table-->5; ! Length of word following "oops"
1451 ! Repair the buffer to the text that was in it before the "oops"
1454 for (i=0 : i < OOPS_WORKSPACE_LEN : i++) a_buffer->i = oops_workspace->i;
1455 Tokenise__(a_buffer, a_table);
1457 ! Work out the position in the buffer of the word to be corrected:
1459 #Ifdef TARGET_ZCODE;
1460 w = a_table->(4*oops_from + 1); ! Start of word to go
1461 w2 = a_table->(4*oops_from); ! Length of word to go
1462 #Ifnot; ! TARGET_GLULX
1463 w = a_table-->(3*oops_from); ! Start of word to go
1464 w2 = a_table-->(3*oops_from - 1); ! Length of word to go
1469 for (i=0 : i<w2 : i++) for (i=0 : i<w2 : i++) print (char)a_buffer->(i+w);
1473 ! Write spaces over the word to be corrected:
1475 for (i=0 : i<w2 : i++) a_buffer->(i+w) = ' ';
1478 ! If the replacement is longer than the original, move up...
1479 for (i=INPUT_BUFFER_LEN-1 : i>=w+x2 : i--)
1480 a_buffer->i = a_buffer->(i-x2+w2);
1482 ! ...increasing buffer size accordingly.
1483 SetKeyBufLength(GetKeyBufLength(a_buffer) + (x2-w2), a_buffer);
1486 ! Write the correction in:
1488 for (i=0 : i<x2 : i++) {
1489 a_buffer->(i+w) = buffer2->(i+x1);
1491 print (char) buffer2->(i+x1);
1499 Tokenise__(a_buffer, a_table);
1500 nw=NumberWords(a_table);
1504 ]; ! end of Keyboard
1507 if (turns == START_MOVE) { L__M(##Miscellany, 11); return 0; }
1508 if (undo_flag == 0) { L__M(##Miscellany, 6); return 0; }
1509 if (undo_flag == 1) { L__M(##Miscellany, 7); return 0; }
1510 #Ifdef TARGET_ZCODE;
1512 #Ifnot; ! TARGET_GLULX
1516 if (i == 0) { L__M(##Miscellany, 7); return 0; }
1517 L__M(##Miscellany, 6);
1521 ! ==========================
1522 ! Taken from I7's Parser.i6t
1523 ! ==========================
1525 [ DictionaryWordToVerbNum dword verbnum;
1526 #Ifdef TARGET_ZCODE;
1527 verbnum = $ff-(dword->#dict_par2);
1529 dword = dword + #dict_par2 - 1;
1530 @aloads dword 0 verbnum;
1531 verbnum = $ffff-verbnum;
1536 ! ==========================
1539 ! ----------------------------------------------------------------------------
1540 ! To simplify the picture a little, a rough map of the main routine:
1542 ! (A) Get the input, do "oops" and "again"
1543 ! (B) Is it a direction, and so an implicit "go"? If so go to (K)
1544 ! (C) Is anyone being addressed?
1545 ! (D) Get the verb: try all the syntax lines for that verb
1546 ! (E) Break down a syntax line into analysed tokens
1547 ! (F) Look ahead for advance warning for multiexcept/multiinside
1548 ! (G) Parse each token in turn (calling ParseToken to do most of the work)
1549 ! (H) Cheaply parse otherwise unrecognised conversation and return
1550 ! (I) Print best possible error message
1551 ! (J) Retry the whole lot
1552 ! (K) Last thing: check for "then" and further instructions(s), return.
1554 ! The strategic points (A) to (K) are marked in the commentary.
1556 ! Note that there are three different places where a return can happen.
1557 ! ----------------------------------------------------------------------------
1559 [ Parser__parse results syntax line num_lines line_address i j k
1560 token l m line_etype vw;
1562 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1564 ! A: Get the input, do "oops" and "again"
1566 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1568 ! Firstly, in "not held" mode, we still have a command left over from last
1569 ! time (eg, the user typed "eat biscuit", which was parsed as "take biscuit"
1570 ! last time, with "eat biscuit" tucked away until now). So we return that.
1572 if (notheld_mode == 1) {
1573 for (i=0 : i<8 : i++) results-->i = kept_results-->i;
1578 if (held_back_mode ~= 0) {
1580 Tokenise__(buffer, parse);
1586 Keyboard(buffer, parse);
1589 ! An Infix verb is a special kind of meta verb. We mark them here.
1590 if (GetNthChar(buffer, 0) == ';')
1598 parser_inflection = name;
1599 parser_inflection_func = false;
1601 ! Initially assume the command is aimed at the player, and the verb
1604 num_words = NumberWords();
1607 #Ifdef LanguageToInformese;
1608 LanguageToInformese();
1611 Tokenise__(buffer,parse);
1613 #Endif; ! LanguageToInformese
1615 if (BeforeParsing() == false) {
1616 LibraryExtensions.ext_number_1 = wn; ! Set "between calls" functionality to restore wn each pass
1617 LibraryExtensions.BetweenCalls = LibraryExtensions.RestoreWN;
1618 LibraryExtensions.RunWhile(ext_beforeparsing, false);
1619 LibraryExtensions.BetweenCalls = 0; ! Turn off "between calls" functionality
1621 num_words = NumberWords();
1625 if (parser_trace >= 2) {
1627 for (i=0 : i<num_words : i++) {
1630 k = WordAddress(i+1);
1631 l = WordLength(i+1);
1632 print "~"; for (m=0 : m<l : m++) print (char) k->m; print "~ ";
1634 if (j == 0) print "?";
1636 #Ifdef TARGET_ZCODE;
1637 if (UnsignedCompare(j, HDR_DICTIONARY-->0) >= 0 &&
1638 UnsignedCompare(j, HDR_HIGHMEMORY-->0) < 0)
1641 #Ifnot; ! TARGET_GLULX
1642 if (j->0 == $60) print (address) j;
1646 if (i ~= num_words-1) print " / ";
1653 actors_location = ScopeCeiling(player);
1654 usual_grammar_after = 0;
1659 action_to_be = NULL;
1661 ! Begin from what we currently think is the verb word
1666 verb_word = NextWordStopped();
1668 ! If there's no input here, we must have something like "person,".
1670 if (verb_word == -1) {
1671 best_etype = STUCK_PE;
1675 ! Now try for "again" or "g", which are special cases: don't allow "again" if nothing
1676 ! has previously been typed; simply copy the previous text across
1678 if (verb_word == AGAIN2__WD or AGAIN3__WD) verb_word = AGAIN1__WD;
1679 if (verb_word == AGAIN1__WD) {
1680 if (actor ~= player) {
1681 L__M(##Miscellany, 20);
1684 if (GetKeyBufLength(buffer3) == 0) {
1685 L__M(##Miscellany, 21);
1689 if (WordAddress(verb_wordnum) == buffer + WORDSIZE) { ! not held back
1690 ! splice rest of buffer onto end of buffer3
1691 i = GetKeyBufLength(buffer3);
1692 while (buffer3 -> (i + WORDSIZE - 1) == ' ' or '.')
1694 j = i - WordLength(verb_wordnum); ! amount to move buffer up by
1696 for (m=INPUT_BUFFER_LEN-1 : m>=WORDSIZE+j : m--)
1697 buffer->m = buffer->(m-j);
1698 SetKeyBufLength(GetKeyBufLength()+j);
1700 for (m=WORDSIZE : m<WORDSIZE+i : m++) buffer->m = buffer3->m;
1701 if (j < 0) for (:m<WORDSIZE+i-j : m++) buffer->m = ' ';
1704 for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer->i = buffer3->i;
1708 ! Save the present input in case of an "again" next time
1710 if (verb_word ~= AGAIN1__WD)
1711 for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer3->i = buffer->i;
1713 if (usual_grammar_after == 0) {
1715 #Ifdef TARGET_ZCODE;
1718 i = RunRoutines(actor, grammar);
1720 if (parser_trace >= 2 && actor.grammar ~= 0 or NULL)
1721 print " [Grammar property returned ", i, "]^";
1724 #Ifdef TARGET_ZCODE;
1725 if ((i ~= 0 or 1) &&
1727 (UnsignedCompare(i, dict_start) < 0 ||
1728 UnsignedCompare(i, dict_end) >= 0 ||
1729 (i - dict_start) % dict_entry_size ~= 0))) {
1730 usual_grammar_after = j;
1734 #Ifnot; ! TARGET_GLULX
1735 if (i < 0) { usual_grammar_after = j; i=-i; }
1739 results-->0 = action;
1740 results-->1 = 0; ! Number of parameters
1742 results-->3 = second;
1743 if (noun) results-->1 = 1;
1744 if (second) results-->1 = 2;
1747 if (i ~= 0) { verb_word = i; wn--; verb_wordnum--; }
1748 else { wn = verb_wordnum; verb_word = NextWord(); }
1750 else usual_grammar_after = 0;
1752 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1754 ! B: Is it a direction, and so an implicit "go"? If so go to (K)
1756 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1758 #Ifdef LanguageIsVerb;
1759 if (verb_word == 0) {
1760 i = wn; verb_word = LanguageIsVerb(buffer, parse, verb_wordnum);
1763 #Endif; ! LanguageIsVerb
1765 ! If the first word is not listed as a verb, it must be a direction
1766 ! or the name of someone to talk to
1768 if (verb_word == 0 || ((verb_word->#dict_par1) & DICT_VERB) == 0) {
1770 ! So is the first word an object contained in the special object "Compass"
1771 ! (i.e., a direction)? This needs use of NounDomain, a routine which
1772 ! does the object matching, returning the object number, or 0 if none found,
1773 ! or REPARSE_CODE if it has restructured the parse table so the whole parse
1774 ! must be begun again...
1776 wn = verb_wordnum; indef_mode = false; token_filter = 0;
1777 l = NounDomain(Compass, 0, NOUN_TOKEN);
1778 if (l == REPARSE_CODE) jump ReParse;
1780 ! If it is a direction, send back the results:
1781 ! action=GoSub, no of arguments=1, argument 1=the direction.
1785 action_to_be = ##Go;
1791 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1793 ! C: Is anyone being addressed?
1795 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1797 ! Only check for a comma (a "someone, do something" command) if we are
1798 ! not already in the middle of one. (This simplification stops us from
1799 ! worrying about "robot, wizard, you are an idiot", telling the robot to
1800 ! tell the wizard that she is an idiot.)
1802 if (actor == player) {
1803 for (j=2 : j<=num_words : j++) {
1805 if (i == comma_word) jump Conversation;
1809 verb_word = UnknownVerb(vw);
1810 if (verb_word == false) verb_word = LibraryExtensions.RunWhile(ext_unknownverb, false, vw);
1811 if (verb_word) jump VerbAccepted;
1812 best_etype = VERB_PE;
1815 ! NextWord nudges the word number wn on by one each time, so we've now
1816 ! advanced past a comma. (A comma is a word all on its own in the table.)
1822 L__M(##Miscellany, 22);
1826 ! Use NounDomain (in the context of "animate creature") to see if the
1827 ! words make sense as the name of someone held or nearby
1829 wn = 1; lookahead = HELD_TOKEN;
1830 scope_reason = TALKING_REASON;
1831 l = NounDomain(player,actors_location,CREATURE_TOKEN);
1832 scope_reason = PARSING_REASON;
1833 if (l == REPARSE_CODE) jump ReParse;
1835 L__M(##Miscellany, 23);
1841 ! The object addressed must at least be "talkable" if not actually "animate"
1842 ! (the distinction allows, for instance, a microphone to be spoken to,
1843 ! without the parser thinking that the microphone is human).
1845 if (l hasnt animate && l hasnt talkable) {
1846 L__M(##Miscellany, 24, l);
1850 ! Check that there aren't any mystery words between the end of the person's
1851 ! name and the comma (eg, throw out "dwarf sdfgsdgs, go north").
1854 L__M(##Miscellany, 25);
1858 ! The player has now successfully named someone. Adjust "him", "her", "it":
1862 ! Set the global variable "actor", adjust the number of the first word,
1863 ! and begin parsing again from there.
1865 verb_wordnum = j + 1;
1867 ! Stop things like "me, again":
1871 if (NextWordStopped() == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) {
1872 L__M(##Miscellany, 20);
1878 actors_location = ScopeCeiling(l);
1880 if (parser_trace >= 1)
1881 print "[Actor is ", (the) actor, " in ", (name) actors_location, "]^";
1885 } ! end of first-word-not-a-verb
1887 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1889 ! D: Get the verb: try all the syntax lines for that verb
1891 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1895 ! We now definitely have a verb, not a direction, whether we got here by the
1896 ! "take ..." or "person, take ..." method. Get the meta flag for this verb:
1898 meta = (verb_word->#dict_par1) & DICT_META;
1900 ! You can't order other people to "full score" for you, and so on...
1902 if (meta && actor ~= player) {
1903 best_etype = VERB_PE;
1908 ! Now let i be the corresponding verb number, stored in the dictionary entry
1909 ! (in a peculiar 255-n fashion for traditional Infocom reasons)...
1911 i = DictionaryWordToVerbNum(verb_word);
1913 ! ...then look up the i-th entry in the verb table, whose address is at word
1914 ! 7 in the Z-machine (in the header), so as to get the address of the syntax
1915 ! table for the given verb...
1917 #Ifdef TARGET_ZCODE;
1918 syntax = (HDR_STATICMEMORY-->0)-->i;
1919 #Ifnot; ! TARGET_GLULX
1920 syntax = (#grammar_table)-->(i+1);
1923 ! ...and then see how many lines (ie, different patterns corresponding to the
1924 ! same verb) are stored in the parse table...
1926 num_lines = (syntax->0) - 1;
1928 ! ...and now go through them all, one by one.
1929 ! To prevent pronoun_word 0 being misunderstood,
1931 pronoun_word = NULL; pronoun_obj = NULL;
1934 if (parser_trace >= 1) print "[Parsing for the verb '", (address) verb_word, "' (", num_lines+1, " lines)]^";
1937 best_etype = STUCK_PE; nextbest_etype = STUCK_PE;
1938 multiflag = false; saved_oops = 0;
1940 ! "best_etype" is the current failure-to-match error - it is by default
1941 ! the least informative one, "don't understand that sentence".
1942 ! "nextbest_etype" remembers the best alternative to having to ask a
1943 ! scope token for an error message (i.e., the best not counting ASKSCOPE_PE).
1944 ! multiflag is used here to prevent inappropriate MULTI_PE errors
1945 ! in addition to its unrelated duties passing information to action routines
1947 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1949 ! E: Break down a syntax line into analysed tokens
1951 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1953 line_address = syntax + 1;
1955 for (line=0 : line<=num_lines : line++) {
1957 for (i=0 : i<32 : i++) {
1958 line_token-->i = ENDIT_TOKEN;
1959 line_ttype-->i = ELEMENTARY_TT;
1960 line_tdata-->i = ENDIT_TOKEN;
1963 ! Unpack the syntax line from Inform format into three arrays; ensure that
1964 ! the sequence of tokens ends in an ENDIT_TOKEN.
1966 line_address = UnpackGrammarLine(line_address);
1969 if (parser_trace >= 1) {
1970 if (parser_trace >= 2) new_line;
1971 print "[line ", line; DebugGrammarLine();
1976 ! We aren't in "not holding" or inferring modes, and haven't entered
1977 ! any parameters on the line yet, or any special numbers; the multiple
1978 ! object is still empty.
1984 nsns = 0; special_word = 0; special_number = 0;
1985 multiple_object-->0 = 0;
1987 etype = STUCK_PE; line_etype = 100;
1989 ! Put the word marker back to just after the verb
1991 wn = verb_wordnum+1;
1993 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1995 ! F: Look ahead for advance warning for multiexcept/multiinside
1997 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1999 ! There are two special cases where parsing a token now has to be
2000 ! affected by the result of parsing another token later, and these
2001 ! two cases (multiexcept and multiinside tokens) are helped by a quick
2002 ! look ahead, to work out the future token now. We can only carry this
2003 ! out in the simple (but by far the most common) case:
2005 ! multiexcept <one or more prepositions> noun
2007 ! and similarly for multiinside.
2009 advance_warning = NULL; indef_mode = false;
2010 for (i=0,m=false,pcount=0 : line_token-->pcount ~= ENDIT_TOKEN : pcount++) {
2013 if (line_ttype-->pcount ~= PREPOSITION_TT) i++;
2015 if (line_ttype-->pcount == ELEMENTARY_TT) {
2016 if (line_tdata-->pcount == MULTI_TOKEN) m = true;
2017 if (line_tdata-->pcount == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN && i == 1) {
2018 ! First non-preposition is "multiexcept" or
2019 ! "multiinside", so look ahead.
2022 if (parser_trace >= 2) print " [Trying look-ahead]^";
2025 ! We need this to be followed by 1 or more prepositions.
2028 if (line_ttype-->pcount == PREPOSITION_TT) {
2029 ! skip ahead to a preposition word in the input
2032 } until ((wn > num_words) ||
2033 (l && (l->#dict_par1) & DICT_PREP ~= 0));
2035 if (wn > num_words) {
2037 if (parser_trace >= 2)
2038 print " [Look-ahead aborted: prepositions missing]^";
2044 if (PrepositionChain(l, pcount) ~= -1) {
2045 ! advance past the chain
2046 if ((line_token-->pcount)->0 & $20 ~= 0) {
2048 while ((line_token-->pcount ~= ENDIT_TOKEN) &&
2049 ((line_token-->pcount)->0 & $10 ~= 0))
2055 ! try to find another preposition word
2058 } until ((wn >= num_words) ||
2059 (l && (l->#dict_par1) & 8 ~= 0));
2061 if (l && (l->#dict_par1) & 8) continue;
2065 if (parser_trace >= 2)
2066 print " [Look-ahead aborted: prepositions don't match]^";
2071 } until (line_ttype-->pcount ~= PREPOSITION_TT);
2074 ! put back the non-preposition we just read
2077 if ((line_ttype-->pcount == ELEMENTARY_TT) && (line_tdata-->pcount == NOUN_TOKEN)) {
2078 l = Descriptors(); ! skip past THE etc
2079 if (l~=0) etype=l; ! don't allow multiple objects
2080 l = NounDomain(actors_location, actor, NOUN_TOKEN);
2083 if (parser_trace >= 2) {
2084 print " [Advanced to ~noun~ token: ";
2085 if (l == REPARSE_CODE) print "re-parse request]^";
2086 if (l == 1) print "but multiple found]^";
2087 if (l == 0) print "error ", etype, "]^";
2088 if (l >= 2) print (the) l, "]^";
2091 if (l == REPARSE_CODE) jump ReParse;
2092 if (l >= 2) advance_warning = l;
2100 ! Slightly different line-parsing rules will apply to "take multi", to
2101 ! prevent "take all" behaving correctly but misleadingly when there's
2105 if (m && params_wanted == 1 && action_to_be == ##Take)
2108 ! And now start again, properly, forearmed or not as the case may be.
2109 ! As a precaution, we clear all the variables again (they may have been
2110 ! disturbed by the call to NounDomain, which may have called outside
2111 ! code, which may have done anything!).
2117 nsns = 0; special_word = 0; special_number = 0;
2118 multiple_object-->0 = 0;
2119 etype = STUCK_PE; line_etype = 100;
2120 wn = verb_wordnum+1;
2122 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2124 ! G: Parse each token in turn (calling ParseToken to do most of the work)
2126 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2128 ! "Pattern" gradually accumulates what has been recognised so far,
2129 ! so that it may be reprinted by the parser later on
2131 for (pcount=1 : : pcount++) {
2132 pattern-->pcount = PATTERN_NULL; scope_token = 0;
2134 token = line_token-->(pcount-1);
2135 lookahead = line_token-->pcount;
2138 if (parser_trace >= 2)
2139 print " [line ", line, " token ", pcount, " word ", wn, " : ", (DebugToken) token,
2143 if (token ~= ENDIT_TOKEN) {
2144 scope_reason = PARSING_REASON;
2145 parser_inflection = name;
2146 parser_inflection_func = false;
2147 AnalyseToken(token);
2149 if (action_to_be == ##AskTo && found_ttype == ELEMENTARY_TT &&
2150 found_tdata == TOPIC_TOKEN && line_etype == 100) {
2151 if (actor ~= player) {
2152 best_etype = VERB_PE; jump GiveError;
2160 l = ParseToken__(found_ttype, found_tdata, pcount-1, token);
2161 while (l<-200) l = ParseToken__(ELEMENTARY_TT, l + 256);
2162 scope_reason = PARSING_REASON;
2164 if (l == GPR_PREPOSITION) {
2165 if (found_ttype~=PREPOSITION_TT && (found_ttype~=ELEMENTARY_TT ||
2166 found_tdata~=TOPIC_TOKEN)) params_wanted--;
2170 if (l < 0) l = false;
2172 if (l ~= GPR_REPARSE) {
2173 if (l == GPR_NUMBER) {
2174 if (nsns == 0) special_number1 = parsed_number;
2175 else special_number2 = parsed_number;
2178 if (l == GPR_MULTIPLE) l = 0;
2179 results-->(parameters+2) = l;
2181 pattern-->pcount = l;
2186 if (parser_trace >= 3) {
2187 print " [token resulted in ";
2188 if (l == REPARSE_CODE) print "re-parse request]^";
2189 if (l == 0) print "failure with error type ", etype, "]^";
2190 if (l == 1) print "success]^";
2194 if (l == REPARSE_CODE) jump ReParse;
2196 if (etype < line_etype) line_etype = etype;
2197 if (etype == STUCK_PE || wn >= num_words) break;
2202 ! If the player has entered enough already but there's still
2203 ! text to wade through: store the pattern away so as to be able to produce
2204 ! a decent error message if this turns out to be the best we ever manage,
2205 ! and in the mean time give up on this line
2207 ! However, if the superfluous text begins with a comma or "then" then
2208 ! take that to be the start of another instruction
2210 if (line_etype < 100) break;
2211 if (wn <= num_words) {
2213 if (l == THEN1__WD or THEN2__WD or THEN3__WD or comma_word or AND1__WD) {
2214 held_back_mode = 1; hb_wn = wn-1;
2217 for (m=0 : m<32 : m++) pattern2-->m = pattern-->m;
2224 ! Now, we may need to revise the multiple object because of the single one
2225 ! we now know (but didn't when the list was drawn up).
2227 if (parameters >= 1 && results-->2 == 0) {
2228 l = ReviseMulti(results-->3);
2229 if (l ~= 0) { etype = l; results-->0 = action_to_be; break; }
2231 if (parameters >= 2 && results-->3 == 0) {
2232 l = ReviseMulti(results-->2);
2233 if (l ~= 0) { etype = l; break; }
2236 ! To trap the case of "take all" inferring only "yourself" when absolutely
2237 ! nothing else is in the vicinity...
2239 if (take_all_rule == 2 && results-->2 == actor) {
2240 best_etype = NOTHING_PE;
2245 if (parser_trace >= 1) print "[Line successfully parsed]^";
2248 ! The line has successfully matched the text. Declare the input error-free...
2252 ! ...explain any inferences made (using the pattern)...
2253 ! This is where the parser replies (the sword) if things aren't clear.
2254 if (inferfrom ~= 0 && no_infer_message == false) {
2255 print "("; PrintCommand(inferfrom); print ")^";
2257 no_infer_message = false;
2259 ! ...copy the action number, and the number of parameters...
2261 results-->0 = action_to_be;
2262 results-->1 = parameters;
2264 ! ...reverse first and second parameters if need be...
2266 if (action_reversed && parameters == 2) {
2267 i = results-->2; results-->2 = results-->3;
2270 i = special_number1; special_number1 = special_number2;
2271 special_number2 = i;
2275 ! ...and to reset "it"-style objects to the first of these parameters, if
2276 ! there is one (and it really is an object)...
2278 if (parameters > 0 && results-->2 >= 2)
2279 PronounNotice(results-->2);
2281 ! ...and worry about the case where an object was allowed as a parameter
2282 ! even though the player wasn't holding it and should have been: in this
2283 ! event, keep the results for next time round, go into "not holding" mode,
2284 ! and for now tell the player what's happening and return a "take" request
2287 if (not_holding ~= 0 && actor == player) {
2289 i = RunRoutines(not_holding, before_implicit);
2290 ! i = 0: Take the object, tell the player (default)
2291 ! i = 1: Take the object, don't tell the player
2292 ! i = 2: don't Take the object, continue
2293 ! i = 3: don't Take the object, don't continue
2294 if (i > 2 || no_implicit_actions) { best_etype = NOTHELD_PE; jump GiveError; }
2295 ! perform the implicit Take
2297 if (i ~= 1) ! and tell the player
2298 L__M(##Miscellany, 26, not_holding);
2300 for (i=0 : i<8 : i++) kept_results-->i = results-->i;
2301 results-->0 = ##Take;
2303 results-->2 = not_holding;
2307 ! (Notice that implicit takes are only generated for the player, and not
2308 ! for other actors. This avoids entirely logical, but misleading, text
2311 ! ...and return from the parser altogether, having successfully matched
2314 if (held_back_mode == 1) {
2320 } ! end of if(token ~= ENDIT_TOKEN) else
2321 } ! end of for(pcount++)
2324 ! The line has failed to match.
2325 ! We continue the outer "for" loop, trying the next line in the grammar.
2327 if (line_etype < 100) etype = line_etype;
2328 if (etype > best_etype) best_etype = etype;
2329 if (etype ~= ASKSCOPE_PE && etype > nextbest_etype) nextbest_etype = etype;
2331 ! ...unless the line was something like "take all" which failed because
2332 ! nothing matched the "all", in which case we stop and give an error now.
2334 if (take_all_rule == 2 && etype==NOTHING_PE) break;
2336 } ! end of for(line++)
2338 ! The grammar is exhausted: every line has failed to match.
2340 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2342 ! H: Cheaply parse otherwise unrecognised conversation and return
2344 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2350 ! Errors are handled differently depending on who was talking.
2351 ! If the command was addressed to somebody else (eg, "dwarf, sfgh") then
2352 ! it is taken as conversation which the parser has no business in disallowing.
2354 if (actor ~= player) {
2355 if (usual_grammar_after ~= 0) {
2356 verb_wordnum = usual_grammar_after;
2360 special_word = NextWord();
2361 if (special_word == comma_word) {
2362 special_word = NextWord();
2365 special_number = TryNumber(verb_wordnum);
2366 results-->0 = ##NotUnderstood;
2368 results-->2 = 1; special_number1 = special_word;
2369 results-->3 = actor;
2370 consult_from = verb_wordnum; consult_words = num_words-consult_from+1;
2374 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2376 ! I: Print best possible error message
2378 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2380 ! If the player was the actor (eg, in "take dfghh") the error must be
2381 ! printed, and fresh input called for. In four cases the oops word
2382 ! must be jiggled (where oops_from is set to something).
2384 if (ParserError(etype)) jump ReType;
2385 if (LibraryExtensions.RunWhile(ext_parsererror, false, etype)) jump ReType;
2386 pronoun_word = pronoun__word; pronoun_obj = pronoun__obj;
2388 if (etype == STUCK_PE) { L__M(##Miscellany, 27); oops_from = 1; }
2389 if (etype == UPTO_PE) { L__M(##Miscellany, 28);
2390 for (m=0 : m<32 : m++) pattern-->m = pattern2-->m;
2391 pcount = pcount2; PrintCommand(0); L__M(##Miscellany, 56);
2394 if (etype == NUMBER_PE) L__M(##Miscellany, 29);
2395 if (etype == CANTSEE_PE) { L__M(##Miscellany, 30); oops_from=saved_oops;}
2396 if (etype == TOOLIT_PE) L__M(##Miscellany, 31);
2397 if (etype == NOTHELD_PE) { L__M(##Miscellany, 32, not_holding); oops_from=saved_oops; }
2398 if (etype == MULTI_PE) L__M(##Miscellany, 33);
2399 if (etype == MMULTI_PE) L__M(##Miscellany, 34);
2400 if (etype == VAGUE_PE) L__M(##Miscellany, 35, pronoun_word);
2401 if (etype == EXCEPT_PE) L__M(##Miscellany, 36);
2402 if (etype == ANIMA_PE) L__M(##Miscellany, 37);
2403 if (etype == VERB_PE) L__M(##Miscellany, 38);
2404 if (etype == SCENERY_PE) L__M(##Miscellany, 39);
2405 if (etype == ITGONE_PE) {
2406 if (pronoun_obj == NULL)
2407 L__M(##Miscellany, 35, pronoun_word);
2408 else L__M(##Miscellany, 40, pronoun_word, pronoun_obj);
2410 if (etype == JUNKAFTER_PE) L__M(##Miscellany, 41);
2411 if (etype == TOOFEW_PE) L__M(##Miscellany, 42, multi_had);
2412 if (etype == NOTHING_PE) {
2413 if (results-->0 == ##Remove && results-->3 ofclass Object) {
2414 noun = results-->3; ! ensure valid for messages
2415 if (noun has animate) L__M(##Miscellany, 44, verb_word);
2416 else if (noun hasnt container or supporter) L__M(##Insert, 2, noun);
2417 else if (noun has container && noun hasnt open) L__M(##Take, 9, noun);
2418 else if (children(noun)==0) L__M(##Search, 6, noun);
2419 else results-->0 = 0;
2421 if (results-->0 ~= ##Remove) {
2422 if (multi_wanted == 100) L__M(##Miscellany, 43);
2425 if (take_all_rule == 2) L__M(##Miscellany, 59);
2426 else L__M(##Miscellany, 44, verb_word);
2428 L__M(##Miscellany, 44, verb_word);
2429 #Endif; ! NO_TAKE_ALL
2433 if (etype == ASKSCOPE_PE) {
2435 if (scope_error() == -1) {
2436 best_etype = nextbest_etype;
2441 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2443 ! J: Retry the whole lot
2445 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2447 ! And go (almost) right back to square one...
2451 ! ...being careful not to go all the way back, to avoid infinite repetition
2452 ! of a deferred command causing an error.
2455 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2457 ! K: Last thing: check for "then" and further instructions(s), return.
2459 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2461 ! At this point, the return value is all prepared, and we are only looking
2462 ! to see if there is a "then" followed by subsequent instruction(s).
2466 if (wn > num_words) rtrue;
2469 if (i == THEN1__WD or THEN2__WD or THEN3__WD or comma_word or AND1__WD) {
2470 if (wn > num_words) {
2471 held_back_mode = false;
2474 i = WordAddress(verb_wordnum);
2475 j = WordAddress(wn);
2476 for (: i<j : i++) i->0 = ' ';
2478 if (i == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) {
2479 ! Delete the words "then again" from the again buffer,
2480 ! in which we have just realised that it must occur:
2481 ! prevents an infinite loop on "i. again"
2483 i = WordAddress(wn-2)-buffer;
2484 if (wn > num_words) j = INPUT_BUFFER_LEN-1;
2485 else j = WordAddress(wn)-buffer;
2486 for (: i<j : i++) buffer3->i = ' ';
2488 Tokenise__(buffer,parse);
2489 held_back_mode = true;
2492 best_etype = UPTO_PE;
2495 ]; ! end of Parser__parse
2497 [ ScopeCeiling person act;
2498 act = parent(person);
2499 if (act == 0) return person;
2500 if (person == player && location == thedark) return thedark;
2501 while (parent(act)~=0 && (act has transparent || act has supporter ||
2502 (act has container && act has open)))
2507 ! ----------------------------------------------------------------------------
2510 ! Handles descriptive words like "my", "his", "another" and so on.
2511 ! Skips "the", and leaves wn pointing to the first misunderstood word.
2513 ! Allowed to set up for a plural only if allow_p is set
2515 ! Returns error number, or 0 if no error occurred
2516 ! ----------------------------------------------------------------------------
2518 Constant OTHER_BIT = 1; ! These will be used in Adjudicate()
2519 Constant MY_BIT = 2; ! to disambiguate choices
2520 Constant THAT_BIT = 4;
2521 Constant PLURAL_BIT = 8;
2522 Constant LIT_BIT = 16;
2523 Constant UNLIT_BIT = 32;
2526 indef_mode = 0; indef_type = 0; indef_wanted = 0; indef_guess_p = 0;
2527 indef_possambig = false;
2528 indef_owner = nothing;
2529 indef_cases = $$111111111111;
2533 [ Descriptors allows_multiple o x y flag cto type m n;
2535 if (wn > num_words) return 0;
2537 for (flag=true : flag :) {
2538 o = NextWordStopped(); flag = false;
2539 for (x=1 : x<=LanguageDescriptors-->0 : x=x+4) {
2540 if (o == LanguageDescriptors-->x) {
2541 ! Attempt to compensate for her-her confusion.
2542 for (y = 1 : y<=LanguagePronouns-->0 : y=y+2) {
2543 if (o == LanguagePronouns-->y)
2544 jump PersonalPronoun;
2547 type = LanguageDescriptors-->(x+2);
2548 if (type ~= DEFART_PK) indef_mode = true;
2549 indef_possambig = true;
2550 indef_cases = indef_cases & (LanguageDescriptors-->(x+1));
2551 if (type == POSSESS_PK) {
2552 cto = LanguageDescriptors-->(x+3);
2554 0: indef_type = indef_type | MY_BIT;
2555 1: indef_type = indef_type | THAT_BIT;
2557 indef_owner = PronounValue(cto);
2558 if (indef_owner == NULL) indef_owner = InformParser;
2561 if (type == light) indef_type = indef_type | LIT_BIT;
2562 if (type == -light) indef_type = indef_type | UNLIT_BIT;
2567 if (o == OTHER1__WD or OTHER2__WD or OTHER3__WD) {
2568 indef_mode = 1; flag = 1;
2569 indef_type = indef_type | OTHER_BIT;
2571 if (o == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) {
2572 indef_mode = 1; flag = 1; indef_wanted = 100;
2573 if (take_all_rule == 1) take_all_rule = 2;
2574 indef_type = indef_type | PLURAL_BIT;
2576 if (allow_plurals && allows_multiple) {
2577 n = TryNumber(wn-1);
2578 if (n == 1) { indef_mode = 1; flag = 1; indef_wanted = 1; }
2581 indef_mode = 1; flag = 1; indef_wanted = n;
2582 indef_nspec_at = wn-1;
2583 indef_type = indef_type | PLURAL_BIT;
2586 if (flag == 1 && NextWordStopped() ~= OF1__WD or OF2__WD or OF3__WD or OF4__WD)
2587 wn--; ! Skip 'of' after these
2594 ! ----------------------------------------------------------------------------
2595 ! CreatureTest: Will this person do for a "creature" token?
2596 ! ----------------------------------------------------------------------------
2599 if (actor ~= player) rtrue;
2600 if (obj has animate) rtrue;
2601 if (obj hasnt talkable) rfalse;
2602 if (action_to_be == ##Ask or ##Answer or ##Tell or ##AskFor or ##AskTo) rtrue;
2606 [ PrepositionChain wd index;
2607 if (line_tdata-->index == wd) return wd;
2608 if ((line_token-->index)->0 & $20 == 0) return -1;
2610 if (line_tdata-->index == wd) return wd;
2612 } until ((line_token-->index == ENDIT_TOKEN) || (((line_token-->index)->0 & $10) == 0));
2616 ! ----------------------------------------------------------------------------
2617 ! ParseToken(type, data):
2618 ! Parses the given token, from the current word number wn, with exactly
2619 ! the specification of a general parsing routine.
2620 ! (Except that for "topic" tokens and prepositions, you need to supply
2621 ! a position in a valid grammar line as third argument.)
2624 ! GPR_REPARSE for "reconstructed input, please re-parse from scratch"
2625 ! GPR_PREPOSITION for "token accepted with no result"
2626 ! $ff00 + x for "please parse ParseToken(ELEMENTARY_TT, x) instead"
2627 ! 0 for "token accepted, result is the multiple object list"
2628 ! 1 for "token accepted, result is the number in parsed_number"
2629 ! object num for "token accepted with this object as result"
2630 ! -1 for "token rejected"
2632 ! (A) Analyse the token; handle all tokens not involving
2633 ! object lists and break down others into elementary tokens
2634 ! (B) Begin parsing an object list
2635 ! (C) Parse descriptors (articles, pronouns, etc.) in the list
2636 ! (D) Parse an object name
2637 ! (E) Parse connectives ("and", "but", etc.) and go back to (C)
2638 ! (F) Return the conclusion of parsing an object list
2639 ! ----------------------------------------------------------------------------
2641 [ ParseToken given_ttype given_tdata token_n x y;
2642 x = lookahead; lookahead = NOUN_TOKEN;
2643 y = ParseToken__(given_ttype,given_tdata,token_n);
2644 if (y == GPR_REPARSE) Tokenise__(buffer,parse);
2645 lookahead = x; return y;
2648 [ ParseToken__ given_ttype given_tdata token_n
2649 token l o i j k and_parity single_object desc_wn many_flag
2650 token_allows_multiple prev_indef_wanted;
2652 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2654 ! A: Analyse token; handle all not involving object lists, break down others
2656 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2660 switch (given_ttype) {
2662 switch (given_tdata) {
2665 special_word = NextWord();
2668 if (parser_trace >= 3) print " [Read special as the number ", l, "]^";
2672 if (parser_trace >= 3) print " [Read special word at word number ", wn, "]^";
2676 parsed_number = l; return GPR_NUMBER;
2680 if (l == -1000) { etype = NUMBER_PE; return GPR_FAIL; }
2682 if (parser_trace>=3) print " [Read number as ", l, "]^";
2684 parsed_number = l; return GPR_NUMBER;
2687 if (action_to_be == ##Answer or ##Ask or ##AskFor or ##AskTo or ##Tell)
2688 scope_reason = TALKING_REASON;
2692 if ((line_ttype-->(token_n+1) ~= PREPOSITION_TT) &&
2693 (line_token-->(token_n+1) ~= ENDIT_TOKEN))
2695 do o = NextWordStopped();
2696 until (o == -1 || PrepositionChain(o, token_n+1) ~= -1);
2698 consult_words = wn-consult_from;
2699 if (consult_words == 0) return GPR_FAIL;
2700 if (action_to_be == ##Ask or ##Answer or ##Tell) {
2701 o = wn; wn = consult_from; parsed_number = NextWord();
2702 #Ifdef EnglishNaturalLanguage;
2703 if (parsed_number == 'the' && consult_words > 1) parsed_number=NextWord();
2704 #Endif; ! EnglishNaturalLanguage
2707 if (o==-1 && (line_ttype-->(token_n+1) == PREPOSITION_TT))
2708 return GPR_FAIL; ! don't infer if required preposition is absent
2709 return GPR_PREPOSITION;
2713 #Iffalse (Grammar__Version == 1);
2714 ! Is it an unnecessary alternative preposition, when a previous choice
2715 ! has already been matched?
2716 if ((token->0) & $10) return GPR_PREPOSITION;
2717 #Endif; ! Grammar__Version
2719 ! If we've run out of the player's input, but still have parameters to
2720 ! specify, we go into "infer" mode, remembering where we are and the
2721 ! preposition we are inferring...
2723 if (wn > num_words) {
2724 if (inferfrom==0 && parameters<params_wanted) {
2725 inferfrom = pcount; inferword = token;
2726 pattern-->pcount = REPARSE_CODE + Dword__No(given_tdata);
2729 ! If we are not inferring, then the line is wrong...
2731 if (inferfrom == 0) return -1;
2733 ! If not, then the line is right but we mark in the preposition...
2735 pattern-->pcount = REPARSE_CODE + Dword__No(given_tdata);
2736 return GPR_PREPOSITION;
2741 pattern-->pcount = REPARSE_CODE + Dword__No(o);
2743 ! Whereas, if the player has typed something here, see if it is the
2744 ! required preposition... if it's wrong, the line must be wrong,
2745 ! but if it's right, the token is passed (jump to finish this token).
2747 if (o == given_tdata) return GPR_PREPOSITION;
2748 #Iffalse (Grammar__Version == 1);
2749 if (PrepositionChain(o, token_n) ~= -1) return GPR_PREPOSITION;
2750 #Endif; ! Grammar__Version
2756 if (parser_trace >= 3) print " [Outside parsing routine returned ", l, "]^";
2761 scope_token = given_tdata;
2765 if (parser_trace >= 3) print " [Scope routine returned multiple-flag of ", l, "]^";
2767 if (l == 1) given_tdata = MULTI_TOKEN; else given_tdata = NOUN_TOKEN;
2770 token_filter = 1 + given_tdata;
2771 given_tdata = NOUN_TOKEN;
2774 token_filter = given_tdata;
2775 given_tdata = NOUN_TOKEN;
2777 } ! end of switch(given_ttype)
2779 token = given_tdata;
2781 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2783 ! B: Begin parsing an object list
2785 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2787 ! There are now three possible ways we can be here:
2788 ! parsing an elementary token other than "special" or "number";
2789 ! parsing a scope token;
2790 ! parsing a noun-filter token (either by routine or attribute).
2792 ! In each case, token holds the type of elementary parse to
2793 ! perform in matching one or more objects, and
2794 ! token_filter is 0 (default), an attribute + 1 for an attribute filter
2795 ! or a routine address for a routine filter.
2797 token_allows_multiple = false;
2798 if (token == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN)
2799 token_allows_multiple = true;
2801 many_flag = false; and_parity = true; dont_infer = false;
2803 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2805 ! C: Parse descriptors (articles, pronouns, etc.) in the list
2807 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2809 ! We expect to find a list of objects next in what the player's typed.
2814 if (parser_trace >= 3) print " [Object list from word ", wn, "]^";
2817 ! Take an advance look at the next word: if it's "it" or "them", and these
2818 ! are unset, set the appropriate error number and give up on the line
2819 ! (if not, these are still parsed in the usual way - it is not assumed
2820 ! that they still refer to something in scope)
2822 o = NextWord(); wn--;
2824 pronoun_word = NULL; pronoun_obj = NULL;
2825 l = PronounValue(o);
2827 pronoun_word = o; pronoun_obj = l;
2829 ! Don't assume this is a use of an unset pronoun until the
2830 ! descriptors have been checked, because it might be an
2831 ! article (or some such) instead
2833 for (l=1 : l<=LanguageDescriptors-->0 : l=l+4)
2834 if (o == LanguageDescriptors-->l) jump AssumeDescriptor;
2835 pronoun__word = pronoun_word; pronoun__obj = pronoun_obj;
2836 etype = VAGUE_PE; return GPR_FAIL;
2842 if (o == ME1__WD or ME2__WD or ME3__WD) { pronoun_word = o; pronoun_obj = player; }
2844 allow_plurals = true;
2849 ! First, we parse any descriptive words (like "the", "five" or "every"):
2850 l = Descriptors(token_allows_multiple);
2851 if (l ~= 0) { etype = l; return GPR_FAIL; }
2855 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2857 ! D: Parse an object name
2859 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2861 ! This is an actual specified object, and is therefore where a typing error
2862 ! is most likely to occur, so we set:
2866 ! So, two cases. Case 1: token not equal to "held"
2867 ! but we may well be dealing with multiple objects
2869 ! In either case below we use NounDomain, giving it the token number as
2870 ! context, and two places to look: among the actor's possessions, and in the
2871 ! present location. (Note that the order depends on which is likeliest.)
2872 if (token ~= HELD_TOKEN) {
2873 i = multiple_object-->0;
2875 if (parser_trace >= 3) print " [Calling NounDomain on location and actor]^";
2877 l = NounDomain(actors_location, actor, token);
2878 if (l == REPARSE_CODE) return l; ! Reparse after Q&A
2880 if (indef_wanted == 100 && l == 0 && number_matched == 0)
2881 l = 1; ! ReviseMulti if TAKE ALL FROM empty container
2883 if (token_allows_multiple && ~~multiflag) {
2884 if (best_etype==MULTI_PE) best_etype=STUCK_PE;
2888 if (indef_possambig) {
2889 saved_ml = match_length;
2894 if ((etype ~=TOOFEW_PE && etype ~= VAGUE_PE) && (multiflag || etype ~= MULTI_PE))
2897 } ! Choose best error
2900 if (parser_trace >= 3) {
2901 if (l > 1) print " [NounDomain returned ", (the) l, "]^";
2903 print " [NounDomain appended to the multiple object list:^";
2904 k = multiple_object-->0;
2905 for (j=i+1 : j<=k : j++)
2906 print " Entry ", j, ": ", (The) multiple_object-->j,
2907 " (", multiple_object-->j, ")^";
2908 print " List now has size ", k, "]^";
2914 if (~~many_flag) many_flag = true;
2915 else { ! Merge with earlier ones
2916 k = multiple_object-->0; ! (with either parity)
2917 multiple_object-->0 = i;
2918 for (j=i+1 : j<=k : j++) {
2919 if (and_parity) MultiAdd(multiple_object-->j);
2920 else MultiSub(multiple_object-->j);
2923 if (parser_trace >= 3) print " [Merging ", k-i, " new objects to the ", i, " old ones]^";
2928 ! A single object was indeed found
2930 if (match_length == 0 && indef_possambig) {
2931 ! So the answer had to be inferred from no textual data,
2932 ! and we know that there was an ambiguity in the descriptor
2933 ! stage (such as a word which could be a pronoun being
2934 ! parsed as an article or possessive). It's worth having
2942 if (token == CREATURE_TOKEN && CreatureTest(l) == 0) {
2945 } ! Animation is required
2947 if (~~many_flag) single_object = l;
2949 if (and_parity) MultiAdd(l); else MultiSub(l);
2951 if (parser_trace >= 3) print " [Combining ", (the) l, " with list]^";
2957 ! Case 2: token is "held" (which fortunately can't take multiple objects)
2958 ! and may generate an implicit take
2959 l = NounDomain(actor,actors_location,token); ! Same as above...
2960 if (l == REPARSE_CODE) return GPR_REPARSE;
2962 if (indef_possambig) {
2967 etype = CantSee(); jump FailToken; ! Choose best error
2970 ! ...until it produces something not held by the actor. Then an implicit
2971 ! take must be tried. If this is already happening anyway, things are too
2972 ! confused and we have to give up (but saving the oops marker so as to get
2973 ! it on the right word afterwards).
2974 ! The point of this last rule is that a sequence like
2977 ! (taking the newspaper first)
2978 ! The dwarf unexpectedly prevents you from taking the newspaper!
2980 ! should not be allowed to go into an infinite repeat - read becomes
2981 ! take then read, but take has no effect, so read becomes take then read...
2982 ! Anyway for now all we do is record the number of the object to take.
2987 if (notheld_mode == 1) {
2988 saved_oops = oops_from;
2994 if (parser_trace >= 3) print " [Allowing object ", (the) l, " for now]^";
2998 } ! end of if (token ~= HELD_TOKEN) else
3000 ! The following moves the word marker to just past the named object...
3002 wn = oops_from + match_length;
3004 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3006 ! E: Parse connectives ("and", "but", etc.) and go back to (C)
3008 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3010 ! Object(s) specified now: is that the end of the list, or have we reached
3011 ! "and", "but" and so on? If so, create a multiple-object list if we
3012 ! haven't already (and are allowed to).
3018 if (o == AND1__WD or AND2__WD or AND3__WD or BUT1__WD or BUT2__WD or BUT3__WD or comma_word) {
3021 if (parser_trace >= 3) print " [Read connective '", (address) o, "']^";
3025 if (k ~= AND1__WD) wn--; ! allow Serial commas in input
3026 if (k > 0 && (k->#dict_par1) & (DICT_NOUN+DICT_VERB) == DICT_VERB) {
3027 wn--; ! player meant 'THEN'
3030 if (~~token_allows_multiple) {
3031 if (multiflag) jump PassToken; ! give UPTO_PE error
3036 if (o == BUT1__WD or BUT2__WD or BUT3__WD) and_parity = 1-and_parity;
3039 multiple_object-->0 = 1;
3040 multiple_object-->1 = single_object;
3043 if (parser_trace >= 3) print " [Making new list from ", (the) single_object, "]^";
3046 dont_infer = true; inferfrom=0; ! Don't print (inferences)
3047 jump ObjectList; ! And back around
3050 wn--; ! Word marker back to first not-understood word
3052 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3054 ! F: Return the conclusion of parsing an object list
3056 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3058 ! Happy or unhappy endings:
3063 single_object = GPR_MULTIPLE;
3064 multi_context = token;
3067 if (indef_mode == 1 && indef_type & PLURAL_BIT ~= 0) {
3068 if (indef_wanted < 100 && indef_wanted > 1) {
3069 multi_had = 1; multi_wanted = indef_wanted;
3075 return single_object;
3079 ! If we were only guessing about it being a plural, try again but only
3080 ! allowing singulars (so that words like "six" are not swallowed up as
3083 if (allow_plurals && indef_guess_p == 1) {
3085 if (parser_trace >= 4) print " [Retrying singulars after failure ", etype, "]^";
3087 prev_indef_wanted = indef_wanted;
3088 allow_plurals = false;
3093 if ((indef_wanted > 0 || prev_indef_wanted > 0) && (~~multiflag)) etype = MULTI_PE;
3097 ]; ! end of ParseToken__
3099 ! ----------------------------------------------------------------------------
3100 ! NounDomain does the most substantial part of parsing an object name.
3102 ! It is given two "domains" - usually a location and then the actor who is
3103 ! looking - and a context (i.e. token type), and returns:
3105 ! 0 if no match at all could be made,
3106 ! 1 if a multiple object was made,
3107 ! k if object k was the one decided upon,
3108 ! REPARSE_CODE if it asked a question of the player and consequently rewrote
3109 ! the player's input, so that the whole parser should start again
3110 ! on the rewritten input.
3112 ! In the case when it returns 1<k<REPARSE_CODE, it also sets the variable
3113 ! length_of_noun to the number of words in the input text matched to the
3115 ! In the case k=1, the multiple objects are added to multiple_object by
3116 ! hand (not by MultiAdd, because we want to allow duplicates).
3117 ! ----------------------------------------------------------------------------
3119 [ NounDomain domain1 domain2 context first_word i j k l
3122 if (parser_trace >= 4) {
3123 print " [NounDomain called at word ", wn, "]^";
3126 print "seeking indefinite object: ";
3127 if (indef_type & OTHER_BIT) print "other ";
3128 if (indef_type & MY_BIT) print "my ";
3129 if (indef_type & THAT_BIT) print "that ";
3130 if (indef_type & PLURAL_BIT) print "plural ";
3131 if (indef_type & LIT_BIT) print "lit ";
3132 if (indef_type & UNLIT_BIT) print "unlit ";
3133 if (indef_owner ~= 0) print "owner:", (name) indef_owner;
3135 print " number wanted: ";
3136 if (indef_wanted == 100) print "all"; else print indef_wanted;
3138 print " most likely GNAs of names: ", indef_cases, "^";
3140 else print "seeking definite object^";
3144 match_length = 0; number_matched = 0; match_from = wn; placed_in_flag = 0;
3146 SearchScope(domain1, domain2, context);
3149 if (parser_trace >= 4) print " [NounDomain made ", number_matched, " matches]^";
3152 wn = match_from+match_length;
3154 ! If nothing worked at all, leave with the word marker skipped past the
3155 ! first unmatched word...
3157 if (number_matched == 0) { wn++; rfalse; }
3159 ! Suppose that there really were some words being parsed (i.e., we did
3160 ! not just infer). If so, and if there was only one match, it must be
3161 ! right and we return it...
3162 if (match_from <= num_words) {
3163 if (number_matched == 1) {
3166 if ((indef_type & LIT_BIT) && i hasnt light) rfalse;
3167 if ((indef_type & UNLIT_BIT) && i has light) rfalse;
3172 ! ...now suppose that there was more typing to come, i.e. suppose that
3173 ! the user entered something beyond this noun. If nothing ought to follow,
3174 ! then there must be a mistake, (unless what does follow is just a full
3175 ! stop, and or comma)
3177 if (wn <= num_words) {
3178 i = NextWord(); wn--;
3179 if (i ~= AND1__WD or AND2__WD or AND3__WD or comma_word
3180 or THEN1__WD or THEN2__WD or THEN3__WD
3181 or BUT1__WD or BUT2__WD or BUT3__WD) {
3182 if (lookahead == ENDIT_TOKEN) rfalse;
3187 ! Now look for a good choice, if there's more than one choice...
3189 number_of_classes = 0;
3191 if (match_length == 0 && indef_mode && indef_wanted > 0 && indef_wanted < 100)
3192 number_matched = 0; ! ask question for 'take three'
3193 if (number_matched == 1) i = match_list-->0;
3194 if (number_matched > 1) {
3195 i = Adjudicate(context);
3196 if (i == -1) rfalse;
3197 if (i == 1) rtrue; ! Adjudicate has made a multiple
3198 ! object, and we pass it on
3201 ! If i is non-zero here, one of two things is happening: either
3202 ! (a) an inference has been successfully made that object i is
3203 ! the intended one from the user's specification, or
3204 ! (b) the user finished typing some time ago, but we've decided
3205 ! on i because it's the only possible choice.
3206 ! In either case we have to keep the pattern up to date,
3207 ! note that an inference has been made and return.
3208 ! (Except, we don't note which of a pile of identical objects.)
3211 if (dont_infer) return i;
3212 if (inferfrom == 0) inferfrom=pcount;
3213 pattern-->pcount = i;
3217 ! If we get here, there was no obvious choice of object to make. If in
3218 ! fact we've already gone past the end of the player's typing (which
3219 ! means the match list must contain every object in scope, regardless
3220 ! of its name), then it's foolish to give an enormous list to choose
3221 ! from - instead we go and ask a more suitable question...
3223 if (match_from > num_words) jump Incomplete;
3224 return AskPlayer(context);
3226 ! Now we come to the question asked when the input has run out
3227 ! and can't easily be guessed (eg, the player typed "take" and there
3228 ! were plenty of things which might have been meant).
3232 if (best_etype == NOTHING_PE && pattern-->1 == 0) rfalse; ! for DROP when empty-handed
3233 if (context == CREATURE_TOKEN) L__M(##Miscellany, 48, actor);
3234 else L__M(##Miscellany, 49, actor);
3236 #Ifdef TARGET_ZCODE;
3237 for (i=2 : i<INPUT_BUFFER_LEN : i++) buffer2->i = ' ';
3238 #Endif; ! TARGET_ZCODE
3239 answer_words = Keyboard(buffer2, parse2);
3241 first_word = WordValue(1, parse2);
3242 #Ifdef LanguageIsVerb;
3243 if (first_word == 0) {
3244 j = wn; first_word = LanguageIsVerb(buffer2, parse2, 1); wn = j;
3246 #Endif; ! LanguageIsVerb
3248 ! Once again, if the reply looks like a command, give it to the
3249 ! parser to get on with and forget about the question...
3251 ! Once again, if the reply looks like a command
3252 ! (that is, VERB ... or XXX,VERB ...), give it to the parser to get
3253 ! on with and forget about the question...
3256 if ((first_word->#dict_par1) & DICT_VERB) {
3257 CopyBuffer(buffer, buffer2);
3258 return REPARSE_CODE;
3260 if (NumberWords(parse2) > 2) {
3261 j = WordValue(2, parse2);
3262 k = WordValue(3, parse2);
3263 if (j == ',//' && k && (k->#dict_par1) & DICT_VERB) {
3264 CopyBuffer(buffer, buffer2);
3265 return REPARSE_CODE;
3270 ! ...but if we have a genuine answer, then:
3272 ! (1) we must glue in text suitable for anything that's been inferred.
3274 if (inferfrom ~= 0) {
3275 for (j=inferfrom : j<pcount : j++) {
3276 if (pattern-->j == PATTERN_NULL) continue;
3277 i = WORDSIZE + GetKeyBufLength();
3278 SetKeyBufLength(i-WORDSIZE + 1);
3279 buffer->(i++) = ' ';
3282 if (parser_trace >= 5) print "[Gluing in inference with pattern code ", pattern-->j, "]^";
3285 ! Conveniently, parse2-->1 is the first word in both ZCODE and GLULX.
3289 ! An inferred object. Best we can do is glue in a pronoun.
3290 ! (This is imperfect, but it's very seldom needed anyway.)
3292 if (pattern-->j >= 2 && pattern-->j < REPARSE_CODE) {
3293 ! was the inference made from some noun words?
3294 ! In which case, we can infer again.
3295 if ((WordValue(NumberWords())->#dict_par1) & DICT_NOUN) continue;
3296 PronounNotice(pattern-->j);
3297 for (k=1 : k<=LanguagePronouns-->0 : k=k+3)
3298 if (pattern-->j == LanguagePronouns-->(k+2)) {
3299 parse2-->1 = LanguagePronouns-->k;
3301 if (parser_trace >= 5) print "[Using pronoun '", (address) parse2-->1, "']^";
3307 ! An inferred preposition.
3308 parse2-->1 = No__Dword(pattern-->j - REPARSE_CODE);
3310 if (parser_trace >= 5) print "[Using preposition '", (address) parse2-->1, "']^";
3314 ! parse2-->1 now holds the dictionary address of the word to glue in.
3316 if (parse2-->1 ~= 0) {
3318 #Ifdef TARGET_ZCODE;
3320 print (address) parse2-->1;
3323 for (l=i : l<i+k : l++) buffer->l = buffer->(l+2);
3324 #Ifnot; ! TARGET_GLULX
3325 k = PrintAnyToArray(buffer+i, INPUT_BUFFER_LEN-i, parse2-->1);
3326 l=l; ! suppress compiler warning
3328 i = i + k; SetKeyBufLength(i-WORDSIZE);
3333 ! (2) we must glue the newly-typed text onto the end.
3335 i = WORDSIZE + GetKeyBufLength();
3336 buffer->(i++) = ' ';
3337 SetKeyBufLength(GetKeyBufLength()+1);
3338 for (j=0 : j<GetKeyBufLength(buffer2) : i++,j++) {
3339 buffer->i = buffer2->(j+WORDSIZE);
3340 SetKeyBufLength(GetKeyBufLength()+1);
3341 if (i-WORDSIZE == INPUT_BUFFER_LEN-1) break;
3344 ! (3) we fill up the buffer with spaces, which is unnecessary, but may
3345 ! help incorrectly-written interpreters to cope.
3347 #Ifdef TARGET_ZCODE;
3348 for (: i<INPUT_BUFFER_LEN : i++) buffer->i = ' ';
3349 #Endif; ! TARGET_ZCODE
3351 return REPARSE_CODE;
3353 ]; ! end of NounDomain
3356 [ AskPlayer context i j k l first_word answer_words marker;
3357 ! Now we print up the question, using the equivalence classes as worked
3358 ! out by Adjudicate() so as not to repeat ourselves on plural objects...
3360 asking_player = true;
3361 if (context == CREATURE_TOKEN) L__M(##Miscellany, 45);
3362 else L__M(##Miscellany, 46);
3364 j = number_of_classes; marker = 0;
3365 for (i=1 : i<=number_of_classes : i++) {
3366 while (((match_classes-->marker) ~= i) && ((match_classes-->marker) ~= -i)) marker++;
3367 k = match_list-->marker;
3369 if (match_classes-->marker > 0) print (the) k; else print (a) k;
3371 if (i < j-1) print (string) COMMA__TX;
3372 if (i == j-1) print (SerialComma) j, (string) OR__TX;
3374 L__M(##Miscellany, 57);
3376 ! ...and get an answer:
3379 #Ifdef TARGET_ZCODE;
3380 for (i=WORDSIZE : i<INPUT_BUFFER_LEN : i++) buffer2->i = ' ';
3381 #Endif; ! TARGET_ZCODE
3382 answer_words = Keyboard(buffer2, parse2);
3384 first_word = WordValue(1, parse2);
3385 asking_player = false;
3387 ! Take care of "all", because that does something too clever here to do
3390 if (first_word == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) {
3391 if (context == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) {
3392 l = multiple_object-->0;
3393 for (i=0 : i<number_matched && l+i<63 : i++) {
3395 multiple_object-->(i+1+l) = k;
3397 multiple_object-->0 = i+l;
3400 L__M(##Miscellany, 47);
3404 ! If the first word of the reply can be interpreted as a verb, then
3405 ! assume that the player has ignored the question and given a new
3406 ! command altogether.
3407 ! (This is one time when it's convenient that the directions are
3408 ! not themselves verbs - thus, "north" as a reply to "Which, the north
3409 ! or south door" is not treated as a fresh command but as an answer.)
3411 #Ifdef LanguageIsVerb;
3412 if (first_word == 0) {
3413 j = wn; first_word = LanguageIsVerb(buffer2, parse2, 1); wn = j;
3415 #Endif; ! LanguageIsVerb
3417 if (((first_word->#dict_par1) & DICT_VERB) && ~~LanguageVerbMayBeName(first_word)) {
3418 CopyBuffer(buffer, buffer2);
3419 return REPARSE_CODE;
3421 if (NumberWords(parse2) > 2) {
3422 j = WordValue(2, parse2);
3423 k = WordValue(3, parse2);
3424 if (j == ',//' && k && (k->#dict_par1) & DICT_VERB) {
3425 CopyBuffer(buffer, buffer2);
3426 return REPARSE_CODE;
3431 ! Now we insert the answer into the original typed command, as
3432 ! words additionally describing the same object
3433 ! (eg, > take red button
3436 ! becomes "take music red button". The parser will thus have three
3437 ! words to work from next time, not two.)
3439 k = WordAddress(match_from) - buffer;
3440 l = GetKeyBufLength(buffer2) +1;
3441 for (j=buffer + INPUT_BUFFER_LEN - 1 : j>=buffer+k+l : j--) j->0 = j->(-l);
3442 for (i=0 : i<l : i++) buffer->(k+i) = buffer2->(WORDSIZE+i);
3443 buffer->(k+l-1) = ' ';
3444 SetKeyBufLength(GetKeyBufLength() + l);
3446 ! Having reconstructed the input, we warn the parser accordingly
3449 return REPARSE_CODE;
3453 ! ----------------------------------------------------------------------------
3454 ! The Adjudicate routine tries to see if there is an obvious choice, when
3455 ! faced with a list of objects (the match_list) each of which matches the
3456 ! player's specification equally well.
3458 ! To do this it makes use of the context (the token type being worked on).
3459 ! It counts up the number of obvious choices for the given context
3460 ! (all to do with where a candidate is, except for 6 (animate) which is to
3461 ! do with whether it is animate or not);
3463 ! if only one obvious choice is found, that is returned;
3465 ! if we are in indefinite mode (don't care which) one of the obvious choices
3466 ! is returned, or if there is no obvious choice then an unobvious one is
3469 ! at this stage, we work out whether the objects are distinguishable from
3470 ! each other or not: if they are all indistinguishable from each other,
3471 ! then choose one, it doesn't matter which;
3473 ! otherwise, 0 (meaning, unable to decide) is returned (but remember that
3474 ! the equivalence classes we've just worked out will be needed by other
3475 ! routines to clear up this mess, so we can't economise on working them
3478 ! Returns -1 if an error occurred
3479 ! ----------------------------------------------------------------------------
3481 Constant SCORE__CHOOSEOBJ = 1000;
3482 Constant SCORE__IFGOOD = 500;
3483 Constant SCORE__UNCONCEALED = 100;
3484 Constant SCORE__BESTLOC = 60;
3485 Constant SCORE__NEXTBESTLOC = 40;
3486 Constant SCORE__NOTCOMPASS = 20;
3487 Constant SCORE__NOTSCENERY = 10;
3488 Constant SCORE__NOTACTOR = 5;
3489 Constant SCORE__GNA = 1;
3490 Constant SCORE__DIVISOR = 20;
3492 [ Adjudicate context i j k good_flag good_ones last n flag offset sovert;
3494 if (parser_trace >= 4) {
3495 print " [Adjudicating match list of size ", number_matched, " in context ", context, "]^";
3498 print "indefinite type: ";
3499 if (indef_type & OTHER_BIT) print "other ";
3500 if (indef_type & MY_BIT) print "my ";
3501 if (indef_type & THAT_BIT) print "that ";
3502 if (indef_type & PLURAL_BIT) print "plural ";
3503 if (indef_type & LIT_BIT) print "lit ";
3504 if (indef_type & UNLIT_BIT) print "unlit ";
3505 if (indef_owner ~= 0) print "owner:", (name) indef_owner;
3507 print " number wanted: ";
3508 if (indef_wanted == 100) print "all"; else print indef_wanted;
3510 print " most likely GNAs of names: ", indef_cases, "^";
3512 else print "definite object^";
3516 j = number_matched-1; good_ones = 0; last = match_list-->0;
3517 for (i=0 : i<=j : i++) {
3519 match_scores-->i = 0;
3524 HELD_TOKEN, MULTIHELD_TOKEN:
3525 if (parent(n) == actor) good_flag = true;
3527 if (advance_warning == -1) {
3531 if (n ~= advance_warning) good_flag = true;
3534 if (advance_warning == -1) {
3535 if (parent(n) ~= actor) good_flag = true;
3538 if (n in advance_warning) good_flag = true;
3541 if (CreatureTest(n) == 1) good_flag = true;
3547 match_scores-->i = SCORE__IFGOOD;
3548 good_ones++; last = n;
3551 if (good_ones == 1) return last;
3553 ! If there is ambiguity about what was typed, but it definitely wasn't
3554 ! animate as required, then return anything; higher up in the parser
3555 ! a suitable error will be given. (This prevents a question being asked.)
3557 if (context == CREATURE_TOKEN && good_ones == 0) return match_list-->0;
3559 if (indef_mode == 0) indef_type=0;
3561 ScoreMatchL(context);
3562 if (number_matched == 0) return -1;
3564 if (indef_mode == 1 && indef_type & PLURAL_BIT ~= 0) {
3565 if (context ~= MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN
3566 or MULTIINSIDE_TOKEN) {
3570 i = 0; offset = multiple_object-->0; sovert = -1;
3571 for (j=BestGuess() : j~=-1 && i<indef_wanted && i+offset<63 : j=BestGuess()) {
3573 if (j has concealed or worn) flag = 0;
3574 if (sovert == -1) sovert = bestguess_score/SCORE__DIVISOR;
3576 if (indef_wanted == 100 && bestguess_score/SCORE__DIVISOR < sovert)
3579 if (context == MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN && parent(j) ~= actor)
3581 #Ifdef TRADITIONAL_TAKE_ALL;
3582 if (action_to_be == ##Take or ##Remove && parent(j) == actor)
3585 if (action_to_be == ##Take or ##Remove &&
3586 (j has animate or scenery or static || parent(j) == actor))
3588 #Endif; ! TRADITIONAL_TAKE_ALL
3590 if (take_all_rule == 2 && match_length == 0) flag = 0;
3591 #Endif; ! NO_TAKE_ALL
3592 n = ChooseObjects(j, flag);
3593 if (n == 0) n = LibraryExtensions.RunWhile(ext_chooseobjects, 0, j, flag);
3595 2: flag = 0; ! forcing rejection
3596 1: flag = 1; ! forcing acceptance
3597 !0: ! going with parser's decision
3600 i++; multiple_object-->(i+offset) = j;
3602 if (parser_trace >= 4) print " Accepting it^";
3608 if (parser_trace >= 4) print " Rejecting it^";
3612 if (i < indef_wanted && indef_wanted < 100) {
3613 etype = TOOFEW_PE; multi_wanted = indef_wanted;
3617 multiple_object-->0 = i+offset;
3618 multi_context = context;
3620 if (parser_trace >= 4)
3621 print " Made multiple object of size ", i, "]^";
3626 for (i=0 : i<number_matched : i++) match_classes-->i = 0;
3629 for (i=0 : i<number_matched : i++)
3630 if (match_classes-->i == 0) {
3631 match_classes-->i = n++; flag = 0;
3632 for (j=i+1 : j<number_matched : j++)
3633 if (match_classes-->j == 0 && Identical(match_list-->i, match_list-->j) == 1) {
3635 match_classes-->j = match_classes-->i;
3637 if (flag == 1) match_classes-->i = 1-n;
3639 n--; number_of_classes = n;
3642 if (parser_trace >= 4) {
3643 print " Grouped into ", n, " possibilities by name:^";
3644 for (i=0 : i<number_matched : i++)
3645 if (match_classes-->i > 0)
3646 print " ", (The) match_list-->i, " (", match_list-->i, ") --- group ",
3647 match_classes-->i, "^";
3650 if (n == 1) dont_infer = true;
3652 if (indef_mode == 0) {
3653 ! Is there now a single highest-scoring object?
3654 i = SingleBestGuess();
3658 if (parser_trace >= 4) print " Single best-scoring object returned.]^";
3664 if (indef_mode == 0) {
3667 for (i=0 : i<number_matched : i++) {
3668 if (match_scores-->i > k) {
3669 k = match_scores-->i;
3670 j = match_classes-->i; j = j*j;
3674 if (match_scores-->i == k) {
3675 if ((match_classes-->i) * (match_classes-->i) ~= j)
3681 if(match_length == 1) {
3683 for (i=1 : i<=number_of_classes : i++) {
3684 while ((match_classes-->k) ~= i or -i) k++;
3687 if(MatchWord(j, match_from)) {
3688 no_infer_message = true;
3694 if (parser_trace >= 4) print " Unable to choose best group, so ask player.]^";
3699 if (parser_trace >= 4) print " Best choices are all from the same group.^";
3704 ! When the player is really vague, or there's a single collection of
3705 ! indistinguishable objects to choose from, choose the one the player
3706 ! most recently acquired, or if the player has none of them, then
3707 ! the one most recently put where it is.
3713 ! ----------------------------------------------------------------------------
3714 ! ReviseMulti revises the multiple object which already exists, in the
3715 ! light of information which has come along since then (i.e., the second
3716 ! parameter). It returns a parser error number, or else 0 if all is well.
3717 ! This only ever throws things out, never adds new ones.
3718 ! ----------------------------------------------------------------------------
3720 [ ReviseMulti second_p i low;
3722 if (parser_trace >= 4) print " Revising multiple object list of size ", multiple_object-->0,
3723 " with 2nd ", (name) second_p, "^";
3726 if (multi_context == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) {
3727 for (i=1,low=0 : i<=multiple_object-->0 : i++) {
3728 if ( (multi_context==MULTIEXCEPT_TOKEN && multiple_object-->i ~= second_p) ||
3729 (multi_context==MULTIINSIDE_TOKEN && multiple_object-->i in second_p)) {
3731 multiple_object-->low = multiple_object-->i;
3734 multiple_object-->0 = low;
3737 if (multi_context == MULTI_TOKEN && action_to_be == ##Take) {
3738 for (i=1,low=0 : i<=multiple_object-->0 : i++)
3739 if (ScopeCeiling(multiple_object-->i)==ScopeCeiling(actor)) low++;
3741 if (parser_trace >= 4) print " Token 2 plural case: number with actor ", low, "^";
3743 if (take_all_rule == 2 || low > 0) {
3744 for (i=1,low=0 : i<=multiple_object-->0 : i++) {
3745 if (ScopeCeiling(multiple_object-->i) == ScopeCeiling(actor)) {
3747 multiple_object-->low = multiple_object-->i;
3750 multiple_object-->0 = low;
3754 i = multiple_object-->0;
3756 if (parser_trace >= 4) print " Done: new size ", i, "^";
3758 if (i == 0) return NOTHING_PE;
3762 ! ----------------------------------------------------------------------------
3763 ! ScoreMatchL scores the match list for quality in terms of what the
3764 ! player has vaguely asked for. Points are awarded for conforming with
3765 ! requirements like "my", and so on. Remove from the match list any
3766 ! entries which fail the basic requirements of the descriptors.
3767 ! ----------------------------------------------------------------------------
3769 [ ScoreMatchL context its_owner its_score obj i j threshold met a_s l_s;
3770 ! if (indef_type & OTHER_BIT ~= 0) threshold++;
3771 if (indef_type & MY_BIT ~= 0) threshold++;
3772 if (indef_type & THAT_BIT ~= 0) threshold++;
3773 if (indef_type & LIT_BIT ~= 0) threshold++;
3774 if (indef_type & UNLIT_BIT ~= 0) threshold++;
3775 if (indef_owner ~= nothing) threshold++;
3778 if (parser_trace >= 4) print " Scoring match list: indef mode ", indef_mode, " type ",
3779 indef_type, ", satisfying ", threshold, " requirements:^";
3782 if (action_to_be ~= ##Take)
3783 a_s = SCORE__NEXTBESTLOC;
3784 l_s = SCORE__BESTLOC;
3785 if (context == HELD_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN) {
3786 a_s = SCORE__BESTLOC; l_s = SCORE__NEXTBESTLOC;
3789 for (i=0 : i<number_matched : i++) {
3790 obj = match_list-->i; its_owner = parent(obj); its_score=0; met=0;
3792 ! if (indef_type & OTHER_BIT ~= 0
3793 ! && obj ~= itobj or himobj or herobj) met++;
3794 if (indef_type & MY_BIT ~= 0 && its_owner == actor) met++;
3795 if (indef_type & THAT_BIT ~= 0 && its_owner == actors_location) met++;
3796 if (indef_type & LIT_BIT ~= 0 && obj has light) met++;
3797 if (indef_type & UNLIT_BIT ~= 0 && obj hasnt light) met++;
3798 if (indef_owner ~= 0 && its_owner == indef_owner) met++;
3800 if (met < threshold) {
3802 if (parser_trace >= 4) print " ", (The) match_list-->i, " (", match_list-->i, ") in ",
3803 (the) its_owner, " is rejected (doesn't match descriptors)^";
3805 match_list-->i = -1;
3809 if (obj hasnt concealed) its_score = SCORE__UNCONCEALED;
3811 if (its_owner == actor) its_score = its_score + a_s;
3813 if (its_owner == actors_location) its_score = its_score + l_s;
3815 #Ifdef TRADITIONAL_TAKE_ALL;
3816 if (its_owner ~= Compass) its_score = its_score + SCORE__NOTCOMPASS;
3818 if (its_owner ~= Compass)
3819 if (take_all_rule && its_owner &&
3820 its_owner has static or scenery &&
3821 (its_owner has supporter ||
3822 (its_owner has container && its_owner has open)))
3823 its_score = its_score + l_s;
3825 its_score = its_score + SCORE__NOTCOMPASS;
3826 #Endif; ! TRADITIONAL_TAKE_ALL
3828 j = ChooseObjects(obj, 2);
3829 if (j == 0) j = LibraryExtensions.RunAll(ext_chooseobjects, obj, 2);
3830 its_score = its_score + SCORE__CHOOSEOBJ * j;
3832 if (obj hasnt scenery) its_score = its_score + SCORE__NOTSCENERY;
3833 if (obj ~= actor) its_score = its_score + SCORE__NOTACTOR;
3835 ! A small bonus for having the correct GNA,
3836 ! for sorting out ambiguous articles and the like.
3838 if (indef_cases & (PowersOfTwo_TB-->(GetGNAOfObject(obj))))
3839 its_score = its_score + SCORE__GNA;
3841 match_scores-->i = match_scores-->i + its_score;
3843 if (parser_trace >= 4) print " ", (The) match_list-->i, " (", match_list-->i,
3844 ") in ", (the) its_owner, " : ", match_scores-->i, " points^";
3849 for (i=0 : i<number_matched : i++) {
3850 while (match_list-->i == -1) {
3851 if (i == number_matched-1) { number_matched--; break; }
3852 for (j=i : j<number_matched-1 : j++) {
3853 match_list-->j = match_list-->(j+1);
3854 match_scores-->j = match_scores-->(j+1);
3861 ! ----------------------------------------------------------------------------
3862 ! BestGuess makes the best guess it can out of the match list, assuming that
3863 ! everything in the match list is textually as good as everything else;
3864 ! however it ignores items marked as -1, and so marks anything it chooses.
3865 ! It returns -1 if there are no possible choices.
3866 ! ----------------------------------------------------------------------------
3868 [ BestGuess earliest its_score best i;
3869 earliest = 0; best = -1;
3870 for (i=0 : i<number_matched : i++) {
3871 if (match_list-->i >= 0) {
3872 its_score = match_scores-->i;
3873 if (its_score > best) { best = its_score; earliest = i; }
3877 if (parser_trace >= 4)
3878 if (best < 0) print " Best guess ran out of choices^";
3879 else print " Best guess ", (the) match_list-->earliest, " (", match_list-->earliest, ")^";
3881 if (best < 0) return -1;
3882 i = match_list-->earliest;
3883 match_list-->earliest = -1;
3884 bestguess_score = best;
3888 ! ----------------------------------------------------------------------------
3889 ! SingleBestGuess returns the highest-scoring object in the match list
3890 ! if it is the clear winner, or returns -1 if there is no clear winner
3891 ! ----------------------------------------------------------------------------
3893 [ SingleBestGuess earliest its_score best i;
3894 earliest = -1; best = -1000;
3895 for (i=0 : i<number_matched : i++) {
3896 its_score = match_scores-->i;
3897 if (its_score == best) earliest = -1;
3898 if (its_score > best) { best = its_score; earliest = match_list-->i; }
3900 bestguess_score = best;
3904 ! ----------------------------------------------------------------------------
3905 ! Identical decides whether or not two objects can be distinguished from
3906 ! each other by anything the player can type. If not, it returns true.
3907 ! ----------------------------------------------------------------------------
3909 [ Identical o1 o2 p1 p2 n1 n2 i j flag;
3910 if (o1 == o2) rtrue; ! This should never happen, but to be on the safe side
3911 if (o1 == 0 || o2 == 0) rfalse; ! Similarly
3912 if (parent(o1) == Compass || parent(o2) == Compass) rfalse; ! Saves time
3914 ! What complicates things is that o1 or o2 might have a parsing routine,
3915 ! so the parser can't know from here whether they are or aren't the same.
3916 ! If they have different parsing routines, we simply assume they're
3917 ! different. If they have the same routine (which they probably got from
3918 ! a class definition) then the decision process is as follows:
3920 ! the routine is called (with self being o1, not that it matters)
3921 ! with noun and second being set to o1 and o2, and action being set
3922 ! to the fake action TheSame. If it returns -1, they are found
3923 ! identical; if -2, different; and if >=0, then the usual method
3926 if (o1.parse_name ~= 0 || o2.parse_name ~= 0) {
3927 if (o1.parse_name ~= o2.parse_name) rfalse;
3928 parser_action = ##TheSame; parser_one = o1; parser_two = o2;
3929 j = wn; i = RunRoutines(o1,parse_name); wn = j;
3931 if (i == -2) rfalse;
3934 ! This is the default algorithm: do they have the same words in their
3935 ! "name" (i.e. property no. 1) properties. (Note that the following allows
3936 ! for repeated words and words in different orders.)
3938 p1 = o1.&1; n1 = (o1.#1)/WORDSIZE;
3939 p2 = o2.&1; n2 = (o2.#1)/WORDSIZE;
3941 ! for (i=0 : i<n1 : i++) { print (address) p1-->i, " "; } new_line;
3942 ! for (i=0 : i<n2 : i++) { print (address) p2-->i, " "; } new_line;
3944 for (i=0 : i<n1 : i++) {
3946 for (j=0 : j<n2 : j++)
3947 if (p1-->i == p2-->j) flag = 1;
3948 if (flag == 0) rfalse;
3951 for (j=0 : j<n2 : j++) {
3953 for (i=0 : i<n1 : i++)
3954 if (p1-->i == p2-->j) flag = 1;
3955 if (flag == 0) rfalse;
3958 ! print "Which are identical!^";
3963 ! ----------------------------------------------------------------------------
3964 ! MatchWord matches a word to the name of an object. This is used to assume
3965 ! that an object simply named "key" is meant when the player types TAKE KEY
3966 ! rather than TAKE BRASS KEY.
3967 ! ----------------------------------------------------------------------------
3969 [ MatchWord p_obj p_word_no len_name len_input start_input i;
3973 PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, p_obj);
3974 len_name = StorageForShortName --> 0;
3977 for(i = 9 : i < len_name : i++)
3978 if(StorageForShortName -> (WORDSIZE + i) == 32) rfalse;
3982 #Ifdef TARGET_ZCODE;
3983 start_input = (parse + 2) -> (4 * p_word_no + 3);
3984 len_input = (parse + 2) -> (4 * p_word_no + 2);
3985 if(len_input > 9) len_input = 9;
3986 if(len_name ~= len_input) rfalse;
3987 for(i = 0 : i < len_name : i++) {
3988 if(StorageForShortName -> (WORDSIZE + i) ~= buffer -> (start_input + i))
3991 #Ifnot; ! TARGET_GLULX
3992 start_input = parse --> (((p_word_no+1) * 3)) - 2;
3993 len_input = parse --> ((p_word_no+1) * 3 - 1);
3994 if(len_input > 9) len_input = 9;
3995 if(len_name ~= len_input) rfalse;
3996 for(i = 0 : i < len_name : i++) {
3997 if(StorageForShortName -> (WORDSIZE + i) ~= buffer -> (start_input + i + 2))
4006 ! ----------------------------------------------------------------------------
4007 ! PrintCommand reconstructs the command as it presently reads, from
4008 ! the pattern which has been built up
4010 ! If from is 0, it starts with the verb: then it goes through the pattern.
4011 ! The other parameter is "emptyf" - a flag: if 0, it goes up to pcount:
4012 ! if 1, it goes up to pcount-1.
4014 ! Note that verbs and prepositions are printed out of the dictionary:
4015 ! and that since the dictionary may only preserve the first six characters
4016 ! of a word (in a V3 game), we have to hand-code the longer words needed.
4018 ! (Recall that pattern entries are 0 for "multiple object", 1 for "special
4019 ! word", 2 to REPARSE_CODE-1 are object numbers and REPARSE_CODE+n means the
4021 ! ----------------------------------------------------------------------------
4023 [ PrintCommand from i k spacing_flag;
4024 #Ifdef LanguageCommand;
4025 LanguageCommand(from);
4026 i = k = spacing_flag = 0; ! suppress warning
4030 if (LanguageVerb(i) == 0 && PrintVerb(i) == false && LibraryExtensions.RunWhile(ext_printverb, false, i) == 0)
4032 from++; spacing_flag = true;
4035 for (k=from : k<pcount : k++) {
4037 if (i == PATTERN_NULL) continue;
4038 if (spacing_flag) print (char) ' ';
4039 if (i == 0) { print (string) THOSET__TX; jump TokenPrinted; }
4040 if (i == 1) { print (string) THAT__TX; jump TokenPrinted; }
4041 if (i >= REPARSE_CODE)
4042 print (address) No__Dword(i-REPARSE_CODE);
4044 if (i in Compass && LanguageVerbLikesAdverb(verb_word))
4045 LanguageDirection (i.door_dir); ! the direction name as adverb
4049 spacing_flag = true;
4051 #Endif; ! LanguageCommand
4054 ! ----------------------------------------------------------------------------
4055 ! The CantSee routine returns a good error number for the situation where
4056 ! the last word looked at didn't seem to refer to any object in context.
4058 ! The idea is that: if the actor is in a location (but not inside something
4059 ! like, for instance, a tank which is in that location) then an attempt to
4060 ! refer to one of the words listed as meaningful-but-irrelevant there
4061 ! will cause "you don't need to refer to that in this game" rather than
4062 ! "no such thing" or "what's 'it'?".
4063 ! (The advantage of not having looked at "irrelevant" local nouns until now
4064 ! is that it stops them from clogging up the ambiguity-resolving process.
4065 ! Thus game objects always triumph over scenery.)
4066 ! ----------------------------------------------------------------------------
4069 if (scope_token ~= 0) {
4070 scope_error = scope_token;
4074 wn--; w = NextWord();
4076 if (w == pronoun_word) {
4077 pronoun__word = pronoun_word; pronoun__obj = pronoun_obj;
4080 i = actor; while (parent(i) ~= 0) i = parent(i);
4083 if (i has visited && Refers(i,wn) == 1) e = SCENERY_PE;
4085 Descriptors(); ! skip past THE etc
4086 if (i has visited && Refers(i,wn) == 1) e = SCENERY_PE;
4090 saved_oops = num_desc + match_from + saved_ml;
4092 saved_oops = num_desc + match_from + match_length;
4098 ! ----------------------------------------------------------------------------
4099 ! The MultiAdd routine adds object "o" to the multiple-object-list.
4101 ! This is only allowed to hold 63 objects at most, at which point it ignores
4102 ! any new entries (and sets a global flag so that a warning may later be
4103 ! printed if need be).
4104 ! ----------------------------------------------------------------------------
4107 i = multiple_object-->0;
4108 if (i == 63) { toomany_flag = 1; rtrue; }
4109 for (j=1 : j<=i : j++)
4110 if (o == multiple_object-->j) rtrue;
4112 multiple_object-->i = o;
4113 multiple_object-->0 = i;
4116 ! ----------------------------------------------------------------------------
4117 ! The MultiSub routine deletes object "o" from the multiple-object-list.
4119 ! It returns 0 if the object was there in the first place, and 9 (because
4120 ! this is the appropriate error number in Parser()) if it wasn't.
4121 ! ----------------------------------------------------------------------------
4123 [ MultiSub o i j k et;
4124 i = multiple_object-->0; et = 0;
4125 for (j=1 : j<=i : j++)
4126 if (o == multiple_object-->j) {
4127 for (k=j : k<=i : k++)
4128 multiple_object-->k = multiple_object-->(k+1);
4129 multiple_object-->0 = --i;
4135 ! ----------------------------------------------------------------------------
4136 ! The MultiFilter routine goes through the multiple-object-list and throws
4137 ! out anything without the given attribute "attr" set.
4138 ! ----------------------------------------------------------------------------
4140 [ MultiFilter attr i j o;
4144 i = multiple_object-->0;
4145 for (j=1 : j<=i : j++) {
4146 o = multiple_object-->j;
4154 ! ----------------------------------------------------------------------------
4155 ! The UserFilter routine consults the user's filter (or checks on attribute)
4156 ! to see what already-accepted nouns are acceptable
4157 ! ----------------------------------------------------------------------------
4160 if (token_filter > 0 && token_filter < 49) {
4161 if (obj has (token_filter-1)) rtrue;
4165 return token_filter();
4168 ! ----------------------------------------------------------------------------
4169 ! MoveWord copies word at2 from parse buffer b2 to word at1 in "parse"
4170 ! (the main parse buffer)
4171 ! ----------------------------------------------------------------------------
4173 #Ifdef TARGET_ZCODE;
4175 [ MoveWord at1 b2 at2 x y;
4176 x = at1*2-1; y = at2*2-1;
4177 parse-->x++ = b2-->y++;
4181 #Ifnot; ! TARGET_GLULX
4183 [ MoveWord at1 b2 at2 x y;
4184 x = at1*3-2; y = at2*3-2;
4185 parse-->x++ = b2-->y++;
4186 parse-->x++ = b2-->y++;
4192 ! ----------------------------------------------------------------------------
4193 ! SearchScope domain1 domain2 context
4195 ! Works out what objects are in scope (possibly asking an outside routine),
4196 ! but does not look at anything the player has typed.
4197 ! ----------------------------------------------------------------------------
4199 [ SearchScope domain1 domain2 context i is;
4201 ! Everything is in scope to the debugging commands
4204 if (scope_reason == PARSING_REASON
4205 && LanguageVerbIsDebugging(verb_word)) {
4207 #Ifdef TARGET_ZCODE;
4208 for (i=selfobj : i<=top_object : i++)
4209 if (i ofclass Object && (parent(i) == 0 || parent(i) ofclass Object))
4211 #Ifnot; ! TARGET_GLULX
4213 if (i ofclass Object && (parent(i) == 0 || parent(i) ofclass Object))
4220 ! First, a scope token gets priority here:
4222 if (scope_token ~= 0) {
4224 if (scope_token()) rtrue;
4227 ! Pick up everything in the location except the actor's possessions;
4228 ! then go through those. (This ensures the actor's possessions are in
4229 ! scope even in Darkness.)
4231 if (context == MULTIINSIDE_TOKEN && advance_warning ~= -1) {
4232 if (IsSeeThrough(advance_warning) == 1)
4233 ScopeWithin(advance_warning, 0, context);
4236 ! Next, call any user-supplied routine adding things to the scope,
4237 ! which may circumvent the usual routines altogether
4238 ! if they return true:
4240 if (actor == domain1 or domain2) {
4241 is = InScope(actor);
4242 if (is == false) is = LibraryExtensions.RunWhile(ext_inscope, false, actor);
4246 if (domain1 ~= 0 && domain1 has supporter or container)
4247 ScopeWithin_O(domain1, domain1, context);
4248 ScopeWithin(domain1, domain2, context);
4249 if (domain2 ~= 0 && domain2 has supporter or container)
4250 ScopeWithin_O(domain2, domain2, context);
4251 ScopeWithin(domain2, 0, context);
4254 ! A special rule applies:
4255 ! in Darkness as in light, the actor is always in scope to himself.
4257 if (thedark == domain1 or domain2) {
4258 ScopeWithin_O(actor, actor, context);
4259 if (parent(actor) has supporter or container)
4260 ScopeWithin_O(parent(actor), parent(actor), context);
4264 ! ----------------------------------------------------------------------------
4265 ! IsSeeThrough is used at various places: roughly speaking, it determines
4266 ! whether o being in scope means that the contents of o are in scope.
4267 ! ----------------------------------------------------------------------------
4270 if (o has supporter or transparent ||
4271 (o has container && o has open))
4276 ! ----------------------------------------------------------------------------
4277 ! PlaceInScope is provided for routines outside the library, and is not
4278 ! called within the parser (except for debugging purposes).
4279 ! ----------------------------------------------------------------------------
4281 [ PlaceInScope thing;
4282 if (scope_reason~=PARSING_REASON or TALKING_REASON) {
4283 DoScopeAction(thing); rtrue;
4285 wn = match_from; TryGivenObject(thing); placed_in_flag = 1;
4288 ! ----------------------------------------------------------------------------
4290 ! ----------------------------------------------------------------------------
4292 [ DoScopeAction thing s p1;
4293 s = scope_reason; p1 = parser_one;
4295 if (parser_trace >= 6)
4296 print "[DSA on ", (the) thing, " with reason = ", scope_reason,
4297 " p1 = ", parser_one, " p2 = ", parser_two, "]^";
4299 switch (scope_reason) {
4300 REACT_BEFORE_REASON:
4301 if (thing.react_before == 0 or NULL) return;
4303 if (parser_trace >= 2)
4304 print "[Considering react_before for ", (the) thing, "]^";
4306 if (parser_one == 0) parser_one = RunRoutines(thing, react_before);
4308 if (thing.react_after == 0 or NULL) return;
4310 if (parser_trace >= 2)
4311 print "[Considering react_after for ", (the) thing, "]^";
4313 if (parser_one == 0) parser_one = RunRoutines(thing, react_after);
4315 if (thing.each_turn == 0) return;
4317 if (parser_trace >= 2)
4318 print "[Considering each_turn for ", (the) thing, "]^";
4320 PrintOrRun(thing, each_turn);
4322 if (thing == parser_one) parser_two = 1;
4323 LOOPOVERSCOPE_REASON:
4330 ! ----------------------------------------------------------------------------
4331 ! ScopeWithin looks for objects in the domain which make textual sense
4332 ! and puts them in the match list. (However, it does not recurse through
4333 ! the second argument.)
4334 ! ----------------------------------------------------------------------------
4336 [ ScopeWithin domain nosearch context x y;
4337 if (domain == 0) rtrue;
4339 ! Special rule: the directions (interpreted as the 12 walls of a room) are
4340 ! always in context. (So, e.g., "examine north wall" is always legal.)
4341 ! (Unless we're parsing something like "all", because it would just slow
4342 ! things down then, or unless the context is "creature".)
4344 if (indef_mode==0 && domain==actors_location
4345 && scope_reason==PARSING_REASON && context~=CREATURE_TOKEN)
4346 ScopeWithin(Compass);
4348 ! Look through the objects in the domain, avoiding "objectloop" in case
4349 ! movements occur, e.g. when trying each_turn.
4354 ScopeWithin_O(x, nosearch, context);
4359 [ ScopeWithin_O domain nosearch context i ad n;
4361 ! If the scope reason is unusual, don't parse.
4363 if (scope_reason ~= PARSING_REASON or TALKING_REASON) {
4364 DoScopeAction(domain);
4368 ! "it" or "them" matches to the it-object only. (Note that (1) this means
4369 ! that "it" will only be understood if the object in question is still
4370 ! in context, and (2) only one match can ever be made in this case.)
4372 if (match_from <= num_words) { ! If there's any text to match, that is
4375 if (i == 1 && player == domain) MakeMatch(domain, 1);
4376 if (i >= 2 && i < 128 && (LanguagePronouns-->i == domain)) MakeMatch(domain, 1);
4379 ! Construing the current word as the start of a noun, can it refer to the
4383 if (TryGivenObject(domain) > 0)
4384 if (indef_nspec_at > 0 && match_from ~= indef_nspec_at) {
4385 ! This case arises if the player has typed a number in
4386 ! which is hypothetically an indefinite descriptor:
4387 ! e.g. "take two clubs". We have just checked the object
4388 ! against the word "clubs", in the hope of eventually finding
4389 ! two such objects. But we also backtrack and check it
4390 ! against the words "two clubs", in case it turns out to
4391 ! be the 2 of Clubs from a pack of cards, say. If it does
4392 ! match against "two clubs", we tear up our original
4393 ! assumption about the meaning of "two" and lapse back into
4396 wn = indef_nspec_at;
4397 if (TryGivenObject(domain) > 0) {
4398 match_from = indef_nspec_at;
4406 ! Shall we consider the possessions of the current object, as well?
4407 ! Only if it's a container (so, for instance, if a dwarf carries a
4408 ! sword, then "drop sword" will not be accepted, but "dwarf, drop sword"
4410 ! Also, only if there are such possessions.
4412 ! Notice that the parser can see "into" anything flagged as
4413 ! transparent - such as a dwarf whose sword you can get at.
4415 if (child(domain) ~= 0 && domain ~= nosearch && IsSeeThrough(domain) == 1)
4416 ScopeWithin(domain,nosearch,context);
4418 ! Drag any extras into context
4420 ad = domain.&add_to_scope;
4423 ! Test if the property value is not an object.
4424 #Ifdef TARGET_ZCODE;
4425 i = (UnsignedCompare(ad-->0, top_object) > 0);
4426 #Ifnot; ! TARGET_GLULX
4427 i = (((ad-->0)->0) ~= $70);
4431 ats_flag = 2+context;
4432 RunRoutines(domain, add_to_scope);
4436 n = domain.#add_to_scope;
4437 for (i=0 : (WORDSIZE*i)<n : i++)
4439 ScopeWithin_O(ad-->i, 0, context);
4446 ScopeWithin_O(obj, 0, ats_flag-2);
4447 if (ats_flag == 1) {
4448 if (HasLightSource(obj)==1) ats_hls = 1;
4452 ! ----------------------------------------------------------------------------
4453 ! MakeMatch looks at how good a match is. If it's the best so far, then
4454 ! wipe out all the previous matches and start a new list with this one.
4455 ! If it's only as good as the best so far, add it to the list.
4456 ! If it's worse, ignore it altogether.
4458 ! The idea is that "red panic button" is better than "red button" or "panic".
4460 ! number_matched (the number of words matched) is set to the current level
4463 ! We never match anything twice, and keep at most 64 equally good items.
4464 ! ----------------------------------------------------------------------------
4466 [ MakeMatch obj quality i;
4468 if (parser_trace >= 6) print " Match with quality ",quality,"^";
4470 if (token_filter ~= 0 && UserFilter(obj) == 0) {
4472 if (parser_trace >= 6) print " Match filtered out: token filter ", token_filter, "^";
4476 if (quality < match_length) rtrue;
4477 if (quality > match_length) { match_length = quality; number_matched = 0; }
4479 if (number_matched >= MATCH_LIST_SIZE) rtrue;
4480 for (i=0 : i<number_matched : i++)
4481 if (match_list-->i == obj) rtrue;
4483 match_list-->number_matched++ = obj;
4485 if (parser_trace >= 6) print " Match added to list^";
4489 ! ----------------------------------------------------------------------------
4490 ! TryGivenObject tries to match as many words as possible in what has been
4491 ! typed to the given object, obj. If it manages any words matched at all,
4492 ! it calls MakeMatch to say so, then returns the number of words (or 1
4493 ! if it was a match because of inadequate input).
4494 ! ----------------------------------------------------------------------------
4496 [ TryGivenObject obj threshold k w j;
4498 if (parser_trace >= 5) print " Trying ", (the) obj, " (", obj, ") at word ", wn, "^";
4501 dict_flags_of_noun = 0;
4503 ! If input has run out then always match, with only quality 0 (this saves
4506 if (wn > num_words) {
4507 if (indef_mode ~= 0)
4508 dict_flags_of_noun = DICT_X654; ! Reject "plural" bit
4511 if (parser_trace >= 5) print " Matched (0)^";
4516 ! Ask the object to parse itself if necessary, sitting up and taking notice
4517 ! if it says the plural was used:
4519 if (obj.parse_name~=0) {
4520 parser_action = NULL; j=wn;
4521 k = RunRoutines(obj,parse_name);
4527 if (parser_action == ##PluralFound)
4528 dict_flags_of_noun = dict_flags_of_noun | DICT_PLUR;
4530 if (dict_flags_of_noun & DICT_PLUR) {
4531 if (~~allow_plurals) k = 0;
4533 if (indef_mode == 0) {
4534 indef_mode = 1; indef_type = 0; indef_wanted = 0;
4536 indef_type = indef_type | PLURAL_BIT;
4537 if (indef_wanted == 0) indef_wanted = 100;
4542 if (parser_trace >= 5) print " Matched (", k, ")^";
4547 if (k == 0) jump NoWordsMatch;
4551 ! The default algorithm is simply to count up how many words pass the
4554 parser_action = NULL;
4558 if (w == 1 && player == obj) { k=1; jump MMbyPN; }
4560 if (w >= 2 && w < 128 && (LanguagePronouns-->w == obj)) { k = 1; jump MMbyPN; }
4563 threshold = ParseNoun(obj);
4564 if (threshold == -1) {
4565 LibraryExtensions.ext_number_1 = wn; ! Set the "between calls" functionality to
4566 LibraryExtensions.BetweenCalls = LibraryExtensions.RestoreWN;
4567 threshold = LibraryExtensions.RunWhile(ext_parsenoun, -1, obj);
4568 LibraryExtensions.BetweenCalls = 0; ! Turn off the "between calls" functionality
4571 if (threshold >= 0 && parser_trace >= 5) print " ParseNoun returned ", threshold, "^";
4573 ! Don't arbitrarily increase wn when ParseNoun() returns -1
4574 if (threshold > 0) {
4579 ! Check wn instead of wn - 1
4580 if (threshold == 0 || Refers(obj,wn) == 0) {
4582 if (indef_mode ~= 0) {
4583 ! Restore wn to pre-ParseNoun() state
4584 k = 0; parser_action = NULL; wn = j;
4590 if (threshold < 0) {
4591 ! Set threshold to reflect any words consumed by ParseNoun()
4593 w = NextWord(); ! Ensure w contains actual first word of noun phrase
4594 ! if ParseNoun() moved wn.
4595 dict_flags_of_noun = (w->#dict_par1) & (DICT_X654+DICT_PLUR);!$$01110100;
4596 while (Refers(obj, wn-1)) {
4599 dict_flags_of_noun = dict_flags_of_noun | ((w->#dict_par1) & (DICT_X654+DICT_PLUR));
4608 ! ----------------------------------------------------------------------------
4609 ! Refers works out whether the word at number wnum can refer to the object
4610 ! obj, returning true or false. The standard method is to see if the
4611 ! word is listed under "name" for the object, but this is more complex
4612 ! in languages other than English.
4613 ! ----------------------------------------------------------------------------
4615 [ Refers obj wnum wd k l m;
4616 if (obj == 0 || wnum <= 0) rfalse;
4618 #Ifdef LanguageRefers;
4619 k = LanguageRefers(obj,wnum); if (k >= 0) return k;
4620 #Endif; ! LanguageRefers
4622 k = wn; wn = wnum; wd = NextWordStopped(); wn = k;
4624 if (parser_inflection_func) {
4625 k = parser_inflection(obj, wd);
4626 if (k >= 0) return k;
4630 m = parser_inflection;
4632 k = obj.&m; l = (obj.#m)/WORDSIZE-1;
4633 for (m=0 : m<=l : m++)
4634 if (wd == k-->m) rtrue;
4638 [ WordInProperty wd obj prop k l m;
4639 k = obj.∝ l = (obj.#prop)/WORDSIZE-1;
4640 for (m=0 : m<=l : m++)
4641 if (wd == k-->m) rtrue;
4645 [ DictionaryLookup b l i;
4646 for (i=0 : i<l : i++) buffer2->(WORDSIZE+i) = b->i;
4647 SetKeyBufLength(l, buffer2);
4648 Tokenise__(buffer2, parse2);
4652 ! ----------------------------------------------------------------------------
4653 ! NounWord (which takes no arguments) returns:
4655 ! 0 if the next word is unrecognised or does not carry the "noun" bit in
4656 ! its dictionary entry,
4657 ! 1 if a word meaning "me",
4658 ! the index in the pronoun table (plus 2) of the value field of a pronoun,
4659 ! if the word is a pronoun,
4660 ! the address in the dictionary if it is a recognised noun.
4662 ! The "current word" marker moves on one.
4663 ! ----------------------------------------------------------------------------
4668 if (i == ME1__WD or ME2__WD or ME3__WD) return 1;
4669 s = LanguagePronouns-->0;
4670 for (j=1 : j<=s : j=j+3)
4671 if (i == LanguagePronouns-->j)
4673 if ((i->#dict_par1) & DICT_NOUN == 0) rfalse;
4677 ! ----------------------------------------------------------------------------
4678 ! NextWord (which takes no arguments) returns:
4680 ! 0 if the next word is unrecognised,
4681 ! comma_word if a comma
4682 ! THEN1__WD if a full stop
4683 ! or the dictionary address if it is recognised.
4684 ! The "current word" marker is moved on.
4686 ! NextWordStopped does the same, but returns -1 when input has run out
4687 ! ----------------------------------------------------------------------------
4689 #Ifdef TARGET_ZCODE;
4692 if (wn <= 0 || wn > parse->1) { wn++; rfalse; }
4695 if (j == ',//') j = comma_word;
4696 if (j == './/') j = THEN1__WD;
4701 if (wn > parse->1) { wn++; return -1; }
4705 [ WordAddress wordnum p b; ! Absolute addr of 'wordnum' string in buffer
4708 return b + p->(wordnum*4+1);
4711 [ WordLength wordnum p; ! Length of 'wordnum' string in buffer
4713 return p->(wordnum*4);
4716 [ WordValue wordnum p; ! Dictionary value of 'wordnum' string in buffer
4718 return p-->(wordnum*2-1);
4721 [ NumberWords p; ! Number of parsed strings in buffer
4726 [ GetKeyBufLength b; ! Number of typed chars in buffer
4731 [ SetKeyBufLength n b; ! Update number of typed chars in buffer
4733 if (n > INPUT_BUFFER_LEN-WORDSIZE) n=INPUT_BUFFER_LEN-WORDSIZE;
4737 #Ifnot; ! TARGET_GLULX
4740 if (wn <= 0 || wn > parse-->0) { wn++; rfalse; }
4743 if (j == ',//') j=comma_word;
4744 if (j == './/') j=THEN1__WD;
4749 if (wn > parse-->0) {
4756 [ WordAddress wordnum p b; ! Absolute addr of 'wordnum' string in buffer
4759 return b + p-->(wordnum*3);
4762 [ WordLength wordnum p; ! Length of 'wordnum' string in buffer
4764 return p-->(wordnum*3-1);
4767 [ WordValue wordnum p; ! Dictionary value of 'wordnum' string in buffer
4769 return p-->(wordnum*3-2);
4772 [ NumberWords p; ! Number of parsed strings in buffer
4777 [ GetKeyBufLength b; ! Number of typed chars in buffer
4782 [ SetKeyBufLength n b; ! Update number of typed chars in buffer
4784 if (n > INPUT_BUFFER_LEN-WORDSIZE) n=INPUT_BUFFER_LEN-WORDSIZE;
4790 ! ----------------------------------------------------------------------------
4791 ! TryNumber is the only routine which really does any character-level
4792 ! parsing, since that's normally left to the Z-machine.
4793 ! It takes word number "wordnum" and tries to parse it as an (unsigned)
4794 ! decimal number, returning
4796 ! -1000 if it is not a number
4797 ! the number if it has between 1 and 4 digits
4798 ! 10000 if it has 5 or more digits.
4800 ! (The danger of allowing 5 digits is that Z-machine integers are only
4801 ! 16 bits long, and anyway this isn't meant to be perfect.)
4803 ! Using NumberWord, it also catches "one" up to "twenty".
4805 ! Note that a game can provide a ParseNumber routine which takes priority,
4806 ! to enable parsing of odder numbers ("x45y12", say).
4807 ! ----------------------------------------------------------------------------
4809 [ TryNumber wordnum i j c num len mul tot d digit;
4810 i = wn; wn = wordnum; j = NextWord(); wn = i;
4812 if (j >= 1) return j;
4814 num = WordAddress(wordnum); len = WordLength(wordnum);
4816 tot = ParseNumber(num, len);
4817 if (tot == 0) tot = LibraryExtensions.RunWhile(ext_parsenumber, 0, num, len);
4819 if (tot ~= 0) return tot;
4821 if (len >= 4) mul=1000;
4822 if (len == 3) mul=100;
4823 if (len == 2) mul=10;
4824 if (len == 1) mul=1;
4828 for (c = 0 : c < len : c++) {
4830 if (digit == '0') { d = 0; jump digok; }
4831 if (digit == '1') { d = 1; jump digok; }
4832 if (digit == '2') { d = 2; jump digok; }
4833 if (digit == '3') { d = 3; jump digok; }
4834 if (digit == '4') { d = 4; jump digok; }
4835 if (digit == '5') { d = 5; jump digok; }
4836 if (digit == '6') { d = 6; jump digok; }
4837 if (digit == '7') { d = 7; jump digok; }
4838 if (digit == '8') { d = 8; jump digok; }
4839 if (digit == '9') { d = 9; jump digok; }
4842 tot = tot+mul*d; mul = mul/10;
4844 if (len > 4) tot=10000;
4848 ! ----------------------------------------------------------------------------
4849 ! AnyNumber is a general parsing routine which accepts binary, hexadecimal
4850 ! and decimal numbers up to the full Zcode/Glulx ranges.
4851 ! ----------------------------------------------------------------------------
4853 #Ifdef TARGET_ZCODE; ! decimal range is -32768 to 32767
4854 Constant MAX_DECIMAL_SIZE 5;
4855 Constant MAX_DECIMAL_BASE 3276;
4856 #Ifnot; ! TARGET_GLULX ! decimal range is -2147483648 to 2147483647
4857 Constant MAX_DECIMAL_SIZE 10;
4858 Constant MAX_DECIMAL_BASE 214748364;
4862 wa we sign base digit digit_count num;
4864 wa = WordAddress(wn); we = wa + WordLength(wn);
4865 sign = 1; base = 10;
4866 if (wa->0 == '-') { sign = -1; wa++; }
4868 if (wa->0 == '$') { base = 16; wa++; }
4869 if (wa->0 == '$') { base = 2; wa++; }
4871 if (wa >= we) return GPR_FAIL; ! no digits after -/$
4872 while (wa->0 == '0') wa++; ! skip leading zeros
4873 for (num=0,digit_count=1 : wa<we : wa++,digit_count++) {
4875 '0' to '9': digit = wa->0 - '0';
4876 'A' to 'F': digit = wa->0 - 'A' + 10;
4877 'a' to 'f': digit = wa->0 - 'a' + 10;
4878 default: return GPR_FAIL;
4880 if (digit >= base) return GPR_FAIL;
4882 16: if (digit_count > 2*WORDSIZE) return GPR_FAIL;
4883 2: if (digit_count > 8*WORDSIZE) return GPR_FAIL;
4885 if (digit_count > MAX_DECIMAL_SIZE) return GPR_FAIL;
4886 if (digit_count == MAX_DECIMAL_SIZE) {
4887 if (num > MAX_DECIMAL_BASE) return GPR_FAIL;
4888 if (num == MAX_DECIMAL_BASE) {
4889 if (sign == 1 && digit > 7) return GPR_FAIL;
4890 if (sign == -1 && digit > 8) return GPR_FAIL;
4894 num = base*num + digit;
4896 parsed_number = num * sign;
4901 ! ----------------------------------------------------------------------------
4902 ! GetGender returns 0 if the given animate object is female, and 1 if male
4903 ! (not all games will want such a simple decision function!)
4904 ! ----------------------------------------------------------------------------
4907 if (person hasnt female) rtrue;
4911 [ GetGNAOfObject obj case gender;
4912 if (obj hasnt animate) case = 6;
4913 if (obj has male) gender = male;
4914 if (obj has female) gender = female;
4915 if (obj has neuter) gender = neuter;
4917 if (case == 0) gender = LanguageAnimateGender;
4918 else gender = LanguageInanimateGender;
4920 if (gender == female) case = case + 1;
4921 if (gender == neuter) case = case + 2;
4922 if (obj has pluralname) case = case + 3;
4926 ! ----------------------------------------------------------------------------
4927 ! Converting between dictionary addresses and entry numbers
4928 ! ----------------------------------------------------------------------------
4930 #Ifdef TARGET_ZCODE;
4932 [ Dword__No w dp dh;
4933 dp = HDR_DICTIONARY-->0;
4935 dp = dp + dp->0 + 1;
4936 return (w-(HDR_DICTIONARY-->0 + dh )) / (dp->0);
4939 [ No__Dword n dp dh;
4940 dp = HDR_DICTIONARY-->0;
4942 dp = dp + dp->0 + 1;
4943 return HDR_DICTIONARY-->0 + dh + (dp->0 *n);
4946 #Ifnot; ! TARGET_GLULX
4948 ! In Glulx, dictionary entries *are* addresses.
4949 [ Dword__No w; return w; ];
4950 [ No__Dword n; return n; ];
4954 ! ----------------------------------------------------------------------------
4955 ! For copying buffers
4956 ! ----------------------------------------------------------------------------
4958 #Ifdef TARGET_ZCODE;
4960 [ CopyBuffer bto bfrom i size;
4962 for (i=1 : i<=size : i++) bto->i = bfrom->i;
4965 #Ifnot; ! TARGET_GLULX
4967 [ CopyBuffer bto bfrom i;
4968 for (i=0 : i<INPUT_BUFFER_LEN : i++) bto->i = bfrom->i;
4973 ! ----------------------------------------------------------------------------
4974 ! Provided for use by language definition files
4975 ! ----------------------------------------------------------------------------
4977 #Ifdef TARGET_ZCODE;
4979 [ LTI_Insert i ch b y;
4981 ! Protect us from strict mode, as this isn't an array in quite the
4985 ! Insert character ch into buffer at point i.
4986 ! Being careful not to let the buffer possibly overflow:
4988 if (y > b->0) y = b->0;
4990 ! Move the subsequent text along one character:
4991 for (y=y+2 : y>i : y--) b->y = b->(y-1);
4994 ! And the text is now one character longer:
4995 if (b->1 < b->0) (b->1)++;
4998 #Ifnot; ! TARGET_GLULX
5000 [ LTI_Insert i ch b y;
5002 ! Protect us from strict mode, as this isn't an array in quite the
5006 ! Insert character ch into buffer at point i.
5007 ! Being careful not to let the buffer possibly overflow:
5009 if (y > INPUT_BUFFER_LEN) y = INPUT_BUFFER_LEN;
5011 ! Move the subsequent text along one character:
5012 for (y=y+WORDSIZE : y>i : y--) b->y = b->(y-1);
5015 ! And the text is now one character longer:
5016 if (b-->0 < INPUT_BUFFER_LEN) (b-->0)++;
5021 ! ============================================================================
5023 [ PronounsSub x y c d;
5024 L__M(##Pronouns, 1);
5026 c = (LanguagePronouns-->0)/3;
5027 if (player ~= selfobj) c++;
5029 if (c == 0) return L__M(##Pronouns, 4);
5031 for (x=1,d=0 : x<=LanguagePronouns-->0 : x=x+3) {
5032 print "~", (address) LanguagePronouns-->x, "~ ";
5033 y = LanguagePronouns-->(x+2);
5034 if (y == NULL) L__M(##Pronouns, 3);
5036 L__M(##Pronouns, 2);
5040 if (d < c-1) print (string) COMMA__TX;
5041 if (d == c-1) print (SerialComma) c, (string) AND__TX;
5043 if (player ~= selfobj) {
5044 print "~", (address) ME1__WD, "~ "; L__M(##Pronouns, 2);
5045 c = player; player = selfobj;
5046 print (the) c; player = c;
5048 L__M(##Pronouns, 5);
5051 [ SetPronoun dword value x;
5052 for (x=1 : x<=LanguagePronouns-->0 : x=x+3)
5053 if (LanguagePronouns-->x == dword) {
5054 LanguagePronouns-->(x+2) = value; return;
5059 [ PronounValue dword x;
5060 for (x=1 : x<=LanguagePronouns-->0 : x=x+3)
5061 if (LanguagePronouns-->x == dword)
5062 return LanguagePronouns-->(x+2);
5066 [ ResetVagueWords obj; PronounNotice(obj); ];
5068 #Ifdef EnglishNaturalLanguage;
5070 [ PronounOldEnglish;
5071 if (itobj ~= old_itobj) SetPronoun('it', itobj);
5072 if (himobj ~= old_himobj) SetPronoun('him', himobj);
5073 if (herobj ~= old_herobj) SetPronoun('her', herobj);
5074 old_itobj = itobj; old_himobj = himobj; old_herobj = herobj;
5077 #Endif; !EnglishNaturalLanguage
5079 [ PronounNotice obj x bm;
5080 if (obj == player) return;
5082 #Ifdef EnglishNaturalLanguage;
5083 PronounOldEnglish();
5084 #Endif; ! EnglishNaturalLanguage
5086 bm = PowersOfTwo_TB-->(GetGNAOfObject(obj));
5088 for (x=1 : x<=LanguagePronouns-->0 : x=x+3)
5089 if (bm & (LanguagePronouns-->(x+1)) ~= 0)
5090 LanguagePronouns-->(x+2) = obj;
5092 #Ifdef EnglishNaturalLanguage;
5093 itobj = PronounValue('it'); old_itobj = itobj;
5094 himobj = PronounValue('him'); old_himobj = himobj;
5095 herobj = PronounValue('her'); old_herobj = herobj;
5096 #Endif; ! EnglishNaturalLanguage
5099 ! ============================================================================
5100 ! End of the parser proper: the remaining routines are its front end.
5101 ! ----------------------------------------------------------------------------
5103 Object InformLibrary "(Inform Library)"
5104 with play [ i j k l;
5106 #Ifdef TARGET_ZCODE;
5108 #Ifnot; ! TARGET_GLULX
5113 while (~~deadflag) { ! everything happens in this loop
5115 #Ifdef EnglishNaturalLanguage;
5116 PronounOldEnglish();
5117 old_itobj = PronounValue('it');
5118 old_himobj = PronounValue('him');
5119 old_herobj = PronounValue('her');
5120 #Endif; ! EnglishNaturalLanguage
5124 if (score ~= last_score) {
5125 if (notify_mode == 1) NotifyTheScore();
5131 inputobjs-->0 = 0; inputobjs-->1 = 0;
5132 inputobjs-->2 = 0; inputobjs-->3 = 0; meta = false;
5134 ! The Parser writes its results into inputobjs and meta,
5135 ! a flag indicating a "meta-verb". This can only be set for
5136 ! commands by the player, not for orders to others.
5138 InformParser.parse_input(inputobjs);
5140 action = inputobjs-->0;
5142 ! --------------------------------------------------------------
5144 ! Reverse "give fred biscuit" into "give biscuit to fred"
5146 if (action == ##GiveR or ##ShowR) {
5147 i = inputobjs-->2; inputobjs-->2 = inputobjs-->3; inputobjs-->3 = i;
5148 if (action == ##GiveR) action = ##Give; else action = ##Show;
5151 ! Convert "P, tell me about X" to "ask P about X"
5153 if (action == ##Tell && inputobjs-->2 == player && actor ~= player) {
5154 inputobjs-->2 = actor; actor = player; action = ##Ask;
5157 ! Convert "ask P for X" to "P, give X to me"
5159 if (action == ##AskFor && inputobjs-->2 ~= player && actor == player) {
5160 actor = inputobjs-->2; inputobjs-->2 = inputobjs-->3;
5161 inputobjs-->3 = player; action = ##Give;
5164 ! For old, obsolete code: special_word contains the topic word
5167 if (action == ##Ask or ##Tell or ##Answer)
5168 special_word = special_number1;
5170 ! --------------------------------------------------------------
5172 multiflag = false; onotheld_mode = notheld_mode; notheld_mode = false;
5173 ! For implicit taking and multiple object detection
5176 inp1 = 0; inp2 = 0; i = inputobjs-->1;
5177 if (i >= 1) inp1 = inputobjs-->2;
5178 if (i >= 2) inp2 = inputobjs-->3;
5180 ! inp1 and inp2 hold: object numbers, or 0 for "multiple object",
5181 ! or 1 for "a number or dictionary address"
5183 if (inp1 == 1) noun = special_number1; else noun = inp1;
5185 if (inp1 == 1) second = special_number2;
5186 else second = special_number1;
5190 ! -------------------------------------------------------------
5192 !print "inp1: ", (name) inp1, "^";
5193 !print "inp2: ", (name) inp2, "^";
5194 !print "inputobjs-->1: ", (name) inputobjs-->1, "^";
5195 !print "inputobjs-->2: ", (name) inputobjs-->2, "^";
5196 !print "inputobjs-->3: ", (name) inputobjs-->3, "^";
5197 !print "noun: ", (name) noun, "^";
5198 !print "second: ", (name) second, "^";
5199 !print "actor: ", (name) actor, "^";
5202 ! --------------------------------------------------------------
5203 ! Generate the action...
5206 (i == 1 && inp1 ~= 0) ||
5207 (i == 2 && inp1 ~= 0 && inp2 ~= 0)) {
5209 if (actor ~= player) {
5210 switch (self.actor_act(actor, action, noun, second)) {
5211 ACTOR_ACT_ABORT_NOTUNDERSTOOD: jump begin__action;
5212 default: jump turn__end;
5218 self.begin_action(action, noun, second, 0);
5222 ! ...unless a multiple object must be substituted. First:
5223 ! (a) check the multiple list isn't empty;
5224 ! (b) warn the player if it has been cut short because too long;
5225 ! (c) generate a sequence of actions from the list
5226 ! (stopping in the event of death or movement away).
5229 j = multiple_object-->0;
5231 L__M(##Miscellany, 2);
5235 toomany_flag = false;
5236 L__M(##Miscellany, 1);
5239 for (k=1 : k<=j : k++) {
5240 if (deadflag) break;
5241 if (location ~= i) {
5242 L__M(##Miscellany, 51);
5245 l = multiple_object-->k;
5247 print (name) l, (string) COLON__TX, " ";
5250 switch (self.actor_act(actor, action, l, second)) {
5251 ACTOR_ACT_ABORT_NOTUNDERSTOOD: jump begin__action;
5252 ACTOR_ACT_ABORT_ORDER: jump turn__end;
5258 if (self.actor_act(actor, action, noun, l) == ACTOR_ACT_ABORT_NOTUNDERSTOOD)
5264 ! --------------------------------------------------------------
5268 ! No time passes if either (i) the verb was meta, or
5269 ! (ii) we've only had the implicit take before the "real"
5272 if (notheld_mode == 1) { NoteObjectAcquisitions(); continue; }
5274 if (~~deadflag) self.end_turn_sequence();
5275 else if (START_MOVE ~= 1) turns++;
5278 if (deadflag ~= 2 && AfterLife() == false)
5279 LibraryExtensions.RunAll(ext_afterlife);
5280 if (deadflag == 0) jump very__late__error;
5282 ], ! end of 'play' property
5284 end_turn_sequence [;
5285 AdvanceWorldClock(); if (deadflag) return;
5286 RunTimersAndDaemons(); if (deadflag) return;
5287 RunEachTurnProperties(); if (deadflag) return;
5288 if (TimePasses() == 0) LibraryExtensions.RunAll(ext_timepasses);
5289 if (deadflag) return;
5290 AdjustLight(); if (deadflag) return;
5291 NoteObjectAcquisitions();
5294 ! The first time we call self.actor_act(), set the fifth
5295 ! parameter to true. Details can be seen in
5296 ! 1d95759b1bd6674509d6cf9161e6db3da3831f10 and in Issues #23 and
5297 ! #30 (Mantis 1813 and 1885)
5298 actor_act [ p a n s ft j sp sa sn ss;
5299 sp = actor; actor = p;
5301 ! The player's "orders" property can refuse to allow
5302 ! conversation here, by returning true. If not, the order is
5303 ! sent to the other person's "orders" property. If that also
5304 ! returns false, then: if it was a misunderstood command
5305 ! anyway, it is converted to an Answer action (thus
5306 ! "floyd, grrr" ends up as "say grrr to floyd"). If it was a
5307 ! good command, it is finally offered to the Order: part of
5308 ! the other person's "life" property, the old-fashioned
5309 ! way of dealing with conversation.
5311 sa = action; sn = noun; ss = second;
5312 action = a; noun = n; second = s;
5313 j = RunRoutines(player, orders);
5315 j = RunRoutines(actor, orders);
5317 if (action == ##NotUnderstood) {
5318 inputobjs-->3 = actor; actor = player; action = ##Answer;
5319 ! abort, not resetting action globals
5320 return ACTOR_ACT_ABORT_NOTUNDERSTOOD;
5322 if (RunLife(actor, ##Order) == 0) {
5323 L__M(##Order, 1, actor);
5325 return ACTOR_ACT_ABORT_ORDER;
5329 action = sa; noun = sn; second = ss;
5331 return ACTOR_ACT_ABORT_ORDER;
5335 self.begin_action(a, n, s, 0);
5338 return ACTOR_ACT_OK;
5341 begin_action [ a n s source sa sn ss;
5342 sa = action; sn = noun; ss = second;
5343 action = a; noun = n; second = s;
5345 if (debug_flag & DEBUG_ACTIONS) TraceAction(source);
5350 #Iftrue (Grammar__Version == 1);
5351 if ((meta || BeforeRoutines() == false) && action < 256) {
5354 if (BeforeRoutines() == false)
5356 } else ActionPrimitive();
5362 if ((meta || BeforeRoutines() == false) && action < 4096) {
5365 if (BeforeRoutines() == false)
5367 } else ActionPrimitive();
5372 #Endif; ! Grammar__Version
5373 action = sa; noun = sn; second = ss;
5377 ! ----------------------------------------------------------------------------
5378 ! Routines called before and after main 'play' loop
5381 before_first_turn = true;
5382 for (i=1 : i<=100 : i++) j = random(i);
5384 ChangeDefault(cant_go, CANTGO__TX);
5386 real_location = thedark;
5387 player = selfobj; actor = player;
5388 selfobj.capacity = MAX_CARRIED; ! ### change?
5390 #Ifdef LanguageInitialise;
5391 LanguageInitialise();
5392 #Endif; ! LanguageInitialise
5394 #Ifdef EnglishNaturalLanguage;
5395 old_itobj = itobj; old_himobj = himobj; old_herobj = herobj;
5396 #Endif;! EnglishNaturalLanguage
5399 LibraryExtensions.RunAll(ext_initialise);
5402 initial_lookmode = lookmode;
5404 objectloop (i in player) give i moved ~concealed;
5405 move player to location;
5406 while (parent(location)) location = parent(location);
5407 real_location = location;
5408 MoveFloatingObjects();
5410 actor = player; ! resync, because player may have been changed in Initialise()
5411 actors_location = location;
5413 lightflag = OffersLight(parent(player));
5414 if (lightflag == 0) location = thedark;
5416 if (j ~= 2) Banner();
5417 #Ifndef NOINITIAL_LOOK;
5420 before_first_turn = false;
5424 if (score ~= last_score) {
5425 if (notify_mode == 1) NotifyTheScore();
5429 #Ifdef TARGET_ZCODE;
5430 #IfV5; style bold; #Endif; ! V5
5431 #Ifnot; ! TARGET_GLULX
5432 glk_set_style(style_Alert);
5436 1: L__M(##Miscellany, 3);
5437 2: L__M(##Miscellany, 4);
5439 if (DeathMessage() == false)
5440 LibraryExtensions.RunAll(ext_deathmessage);
5444 #Ifdef TARGET_ZCODE;
5445 #IfV5; style roman; #Endif; ! V5
5446 #Ifnot; ! TARGET_GLULX
5447 glk_set_style(style_Normal);
5459 ! ----------------------------------------------------------------------------
5460 ! Routines called at end of each turn
5462 [ AdvanceWorldClock;
5464 if (the_time ~= NULL) {
5465 if (time_rate >= 0) the_time=the_time+time_rate;
5468 if (time_step == 0) {
5470 time_step = -time_rate;
5473 the_time = the_time % 1440;
5477 [ RunTimersAndDaemons i j;
5479 if (debug_flag & DEBUG_TIMERS) {
5480 for (i=0 : i<active_timers : i++) {
5483 print (name) (j&~WORD_HIGHBIT), ": ";
5484 if (j & WORD_HIGHBIT) print "daemon";
5486 print "timer with ", j.time_left, " turns to go";
5493 for (i=0 : i<active_timers : i++) {
5494 if (deadflag) return;
5497 if (j & WORD_HIGHBIT) RunRoutines(j&~WORD_HIGHBIT, daemon);
5499 if (j.time_left == 0) {
5501 RunRoutines(j, time_out);
5504 j.time_left = j.time_left-1;
5510 [ RunEachTurnProperties;
5511 scope_reason = EACH_TURN_REASON; verb_word = 0;
5512 DoScopeAction(location);
5513 SearchScope(ScopeCeiling(player), player, 0);
5514 scope_reason = PARSING_REASON;
5517 ! ----------------------------------------------------------------------------
5519 #Ifdef TARGET_ZCODE;
5521 [ ActionPrimitive; (#actions_table-->action)(); ];
5523 #Ifnot; ! TARGET_GLULX
5525 [ ActionPrimitive; (#actions_table-->(action+1))(); ];
5529 [ AfterGameOver i amus_ret;
5533 L__M(##Miscellany,5);
5538 #Ifdef TARGET_ZCODE;
5539 #IfV3; read buffer parse; #Endif; ! V3
5541 #IfV5; read buffer parse DrawStatusLine; #Endif; ! V5
5542 #Ifnot; ! TARGET_GLULX
5543 KeyboardPrimitive(buffer, parse);
5546 if (i == QUIT1__WD or QUIT2__WD) {
5547 #Ifdef TARGET_ZCODE;
5549 #Ifnot; ! TARGET_GLULX
5553 if (i == RESTART__WD) {
5554 #Ifdef TARGET_ZCODE;
5556 #Ifnot; ! TARGET_GLULX
5560 if (i == RESTORE__WD) {
5564 if (i == FULLSCORE1__WD or FULLSCORE2__WD && TASKS_PROVIDED==0) {
5565 new_line; FullScoreSub();
5568 if (deadflag == 2 && i == AMUSING__WD) {
5570 if (AMUSING_PROVIDED == 0) {
5572 amus_ret = Amusing();
5574 if (amus_ret == false) LibraryExtensions.RunAll(ext_amusing);
5578 if (i == UNDO1__WD or UNDO2__WD or UNDO3__WD) {
5580 if (i == 0) jump RRQPL;
5583 L__M(##Miscellany, 8);
5587 [ NoteObjectAcquisitions i;
5588 objectloop (i in player)
5589 if (i hasnt moved) {
5592 score = score + OBJECT_SCORE;
5593 things_score = things_score + OBJECT_SCORE;
5598 ! ----------------------------------------------------------------------------
5599 ! R_Process is invoked by the <...> and <<...>> statements, whose syntax is:
5600 ! <action [noun] [second]> ! traditional
5601 ! <action [noun] [second], actor> ! introduced at compiler 6.33
5605 s1 = inp1; s2 = inp2; s3 = actor;
5606 inp1 = n; inp2 = s; if (p) actor = p; else actor = player;
5607 InformLibrary.begin_action(a, n, s, 1);
5608 inp1 = s1; inp2 = s2; actor = s3;
5611 ! ----------------------------------------------------------------------------
5613 [ TestScope obj act a al sr x y;
5614 x = parser_one; y = parser_two;
5615 parser_one = obj; parser_two = 0; a = actor; al = actors_location;
5616 sr = scope_reason; scope_reason = TESTSCOPE_REASON;
5617 if (act == 0) actor = player; else actor = act;
5618 actors_location = ScopeCeiling(actor);
5619 SearchScope(actors_location, actor, 0); scope_reason = sr; actor = a;
5620 actors_location = al; parser_one = x; x = parser_two; parser_two = y;
5624 [ LoopOverScope routine act x y a al;
5625 x = parser_one; y = scope_reason; a = actor; al = actors_location;
5626 parser_one = routine;
5627 if (act == 0) actor = player; else actor = act;
5628 actors_location = ScopeCeiling(actor);
5629 scope_reason = LOOPOVERSCOPE_REASON;
5630 SearchScope(actors_location, actor, 0);
5631 parser_one = x; scope_reason = y; actor = a; actors_location = al;
5634 [ BeforeRoutines rv;
5635 if (GamePreRoutine()) rtrue;
5636 if (rv == false) rv=LibraryExtensions.RunWhile(ext_gamepreroutine, 0);
5638 if (RunRoutines(player, orders)) rtrue;
5639 scope_reason = REACT_BEFORE_REASON; parser_one = 0;
5640 SearchScope(ScopeCeiling(player), player, 0);
5641 scope_reason = PARSING_REASON;
5642 if (parser_one) rtrue;
5643 if (location && RunRoutines(location, before)) rtrue;
5644 if (inp1 > 1 && RunRoutines(inp1, before)) rtrue;
5649 scope_reason = REACT_AFTER_REASON; parser_one = 0;
5650 SearchScope(ScopeCeiling(player), player, 0); scope_reason = PARSING_REASON;
5651 if (parser_one) rtrue;
5652 if (location && RunRoutines(location, after)) rtrue;
5653 if (inp1 > 1 && RunRoutines(inp1, after)) rtrue;
5654 rv = GamePostRoutine();
5655 if (rv == false) rv=LibraryExtensions.RunWhile(ext_gamepostroutine, false);
5661 if (debug_flag & DEBUG_ACTIONS) TraceAction(2, j);
5663 reason_code = j; return RunRoutines(a,life);
5667 switch (metaclass(addr)) { ! Left over from Inform 5
5669 Object, Class: return 1;
5675 [ PrintOrRun obj prop flag;
5676 if (obj.#prop > WORDSIZE) return RunRoutines(obj, prop);
5677 if (obj.prop == NULL) rfalse;
5678 switch (metaclass(obj.prop)) {
5679 Class, Object, nothing:
5680 return RunTimeError(2,obj,prop);
5682 print (string) obj.prop;
5683 if (flag == 0) new_line;
5686 return RunRoutines(obj, prop);
5690 [ PrintOrRunVar var flag;
5691 switch (metaclass(var)) {
5696 if (flag == 0) new_line;
5700 print (char) '(', var, (char) ')';
5705 [ ValueOrRun obj prop;
5706 !### this is entirely unlikely to work. Does anyone care? (AP)
5707 ! Well, it's certainly used three times in verblibm.h (RF)
5708 ! Update: now not used (RF)
5709 if (obj.prop < 256) return obj.prop;
5710 return RunRoutines(obj, prop);
5713 [ RunRoutines obj prop;
5714 if (obj == thedark && prop ~= initial or short_name or description) obj = real_location;
5715 if (obj.&prop == 0 && prop >= INDIV_PROP_START) rfalse;
5719 #Ifdef TARGET_ZCODE;
5721 [ ChangeDefault prop val a b;
5722 ! Use assembly-language here because -S compilation won't allow this:
5728 #Ifnot; ! TARGET_GLULX
5730 [ ChangeDefault prop val;
5731 ! Use assembly-language here because -S compilation won't allow this:
5732 ! #cpv__start-->prop = val;
5733 @astore #cpv__start prop val;
5738 ! ----------------------------------------------------------------------------
5740 [ StartTimer obj timer i;
5741 for (i=0 : i<active_timers : i++)
5742 if (the_timers-->i == obj) rfalse;
5743 for (i=0 : i<active_timers : i++)
5744 if (the_timers-->i == 0) jump FoundTSlot;
5745 i = active_timers++;
5746 if (i >= MAX_TIMERS) { RunTimeError(4); return; }
5748 if (obj.&time_left == 0) { RunTimeError(5, obj, time_left); return; }
5749 the_timers-->i = obj; obj.time_left = timer;
5753 for (i=0 : i<active_timers : i++)
5754 if (the_timers-->i == obj) jump FoundTSlot2;
5757 if (obj.&time_left == 0) { RunTimeError(5, obj, time_left); return; }
5758 the_timers-->i = 0; obj.time_left = 0;
5761 [ StartDaemon obj i;
5762 for (i=0 : i<active_timers : i++)
5763 if (the_timers-->i == WORD_HIGHBIT + obj) rfalse;
5764 for (i=0 : i<active_timers : i++)
5765 if (the_timers-->i == 0) jump FoundTSlot3;
5766 i = active_timers++;
5767 if (i >= MAX_TIMERS) RunTimeError(4);
5769 the_timers-->i = WORD_HIGHBIT + obj;
5773 for (i=0 : i<active_timers : i++)
5774 if (the_timers-->i == WORD_HIGHBIT + obj) jump FoundTSlot4;
5780 ! ----------------------------------------------------------------------------
5783 if (sys_statusline_flag == 0) { sline1 = score; sline2 = turns; }
5784 else { sline1 = the_time/60; sline2 = the_time%60;}
5788 the_time = t; time_rate = s; time_step = 0;
5789 if (s < 0) time_step = 0-s;
5793 #Ifdef TARGET_GLULX;
5794 glk_set_style(style_Note);
5795 #Endif; ! TARGET_GLULX
5796 print "^["; L__M(##Miscellany, 50, score-last_score); print ".]^";
5797 #Ifdef TARGET_GLULX;
5798 glk_set_style(style_Normal);
5799 #Endif; ! TARGET_GLULX
5802 ! ----------------------------------------------------------------------------
5804 [ AdjustLight flag i;
5806 lightflag = OffersLight(parent(player));
5808 if (i == 0 && lightflag == 1) {
5809 location = real_location;
5810 if (flag == 0) <Look>;
5813 if (i == 1 && lightflag == 0) {
5814 real_location = location; location = thedark;
5817 return L__M(##Miscellany, 9);
5820 if (i == 0 && lightflag == 0) location = thedark;
5825 if (i has light) rtrue;
5827 if (HasLightSource(j) == 1) rtrue;
5828 if (i has container) {
5829 if (i has open || i has transparent)
5830 return OffersLight(parent(i));
5833 if (i has enterable || i has transparent || i has supporter)
5834 return OffersLight(parent(i));
5839 [ HidesLightSource obj;
5840 if (obj == player) rfalse;
5841 if (obj has transparent or supporter) rfalse;
5842 if (obj has container) return (obj hasnt open);
5843 return (obj hasnt enterable);
5846 [ HasLightSource i j ad;
5848 if (i has light) rtrue;
5849 if (i has enterable || IsSeeThrough(i) == 1)
5850 if (~~(HidesLightSource(i)))
5852 if (HasLightSource(j) == 1) rtrue;
5853 ad = i.&add_to_scope;
5854 if (parent(i) ~= 0 && ad ~= 0) {
5855 if (metaclass(ad-->0) == Routine) {
5856 ats_hls = 0; ats_flag = 1;
5857 RunRoutines(i, add_to_scope);
5858 ats_flag = 0; if (ats_hls == 1) rtrue;
5861 for (j=0 : (WORDSIZE*j)<i.#add_to_scope : j++)
5862 if (HasLightSource(ad-->j) == 1) rtrue;
5868 [ ChangePlayer obj flag i;
5869 ! if (obj.&number == 0) return RunTimeError(7, obj);
5871 if (obj == nothing) obj = selfobj;
5872 if (actor == player) actor=obj;
5873 give player ~transparent ~concealed;
5874 i = obj; while (parent(i) ~= 0) {
5875 if (i has animate) give i transparent;
5878 if (player == selfobj && player provides nameless && player.nameless == true) {
5879 if (player provides narrative_voice) {
5880 if (player.narrative_voice == 1) {
5881 player.short_name = MYFORMER__TX;
5882 (player.&name)-->0 = 'my';
5883 (player.&name)-->1 = 'former';
5884 (player.&name)-->2 = 'self';
5885 } else if (player.narrative_voice == 2) {
5886 player.short_name = FORMER__TX;
5887 (player.&name)-->0 = 'my';
5888 (player.&name)-->1 = 'former';
5889 (player.&name)-->2 = 'self';
5897 give player transparent concealed animate;
5898 i = player; while (parent(i) ~= 0) i = parent(i);
5899 location = i; real_location = location;
5900 if (parent(player) == 0) return RunTimeError(10);
5901 MoveFloatingObjects();
5902 lightflag = OffersLight(parent(player));
5903 if (lightflag == 0) location = thedark;
5904 print_player_flag = flag;
5907 ! ----------------------------------------------------------------------------
5911 #Ifdef TARGET_ZCODE;
5915 if (w >= 1 && w <= top_object) print " (", (name) w, ")";
5916 if (UnsignedCompare(w, dict_start) >= 0 &&
5917 UnsignedCompare(w, dict_end) < 0 &&
5918 (w - dict_start) % dict_entry_size == 0)
5919 print " ('", (address) w, "')";
5922 [ DebugAction a anames;
5923 #Iftrue (Grammar__Version == 1);
5924 if (a >= 256) { print "<fake action ", a-256, ">"; return; }
5926 if (a >= 4096) { print "<fake action ", a-4096, ">"; return; }
5927 #Endif; ! Grammar__Version
5928 anames = #identifiers_table;
5929 anames = anames + 2*(anames-->0) + 2*48;
5930 print (string) anames-->a;
5933 [ DebugAttribute a anames;
5934 if (a < 0 || a >= 48) print "<invalid attribute ", a, ">";
5936 anames = #identifiers_table; anames = anames + 2*(anames-->0);
5937 print (string) anames-->a;
5941 #Ifnot; ! TARGET_GLULX
5943 [ DebugParameter w endmem;
5946 if (w >= 1 && w < endmem) {
5947 if (w->0 >= $70 && w->0 < $7F) print " (", (name) w, ")";
5948 if (w->0 >= $60 && w->0 < $6F) print " ('", (address) w, "')";
5952 [ DebugAction a str;
5953 if (a >= 4096) { print "<fake action ", a-4096, ">"; return; }
5954 if (a < 0 || a >= #identifiers_table-->7) print "<invalid action ", a, ">";
5956 str = #identifiers_table-->6;
5958 if (str) print (string) str; else print "<unnamed action ", a, ">";
5962 [ DebugAttribute a str;
5963 if (a < 0 || a >= NUM_ATTR_BYTES*8) print "<invalid attribute ", a, ">";
5965 str = #identifiers_table-->4;
5967 if (str) print (string) str; else print "<unnamed attribute ", a, ">";
5973 [ TraceAction source ar;
5974 if (source < 2) print "[ Action ", (DebugAction) action;
5977 print "[ Order to ", (name) actor, ": ", (DebugAction) action;
5979 print "[ Life rule ", (DebugAction) ar;
5981 if (noun ~= 0) print " with noun ", (DebugParameter) noun;
5982 if (second ~= 0) print " and second ", (DebugParameter) second;
5983 if (source == 0) print " ";
5984 if (source == 1) print " (from < > statement) ";
5989 AnalyseToken(token);
5990 switch (found_ttype) {
5992 print "<illegal token number ", token, ">";
5994 switch (found_tdata) {
5995 NOUN_TOKEN: print "noun";
5996 HELD_TOKEN: print "held";
5997 MULTI_TOKEN: print "multi";
5998 MULTIHELD_TOKEN: print "multiheld";
5999 MULTIEXCEPT_TOKEN: print "multiexcept";
6000 MULTIINSIDE_TOKEN: print "multiinside";
6001 CREATURE_TOKEN: print "creature";
6002 SPECIAL_TOKEN: print "special";
6003 NUMBER_TOKEN: print "number";
6004 TOPIC_TOKEN: print "topic";
6005 ENDIT_TOKEN: print "END";
6008 print "'", (address) found_tdata, "'";
6011 print "noun=", (InfixPrintPA) found_tdata;
6013 print "noun=Routine(", found_tdata, ")";
6016 print (DebugAttribute) found_tdata;
6019 print "scope=", (InfixPrintPA) found_tdata;
6021 print "scope=Routine(", found_tdata, ")";
6025 print (InfixPrintPA) found_tdata;
6027 print "Routine(", found_tdata, ")";
6032 [ DebugGrammarLine pcount;
6034 for (: line_token-->pcount ~= ENDIT_TOKEN : pcount++) {
6035 if ((line_token-->pcount)->0 & $10) print "/ ";
6036 print (DebugToken) line_token-->pcount, " ";
6038 print "-> ", (DebugAction) action_to_be;
6039 if (action_reversed) print " reverse";
6042 #Ifdef TARGET_ZCODE;
6044 [ ShowVerbSub grammar lines j;
6045 if (noun == 0 || ((noun->#dict_par1) & DICT_VERB) == 0)
6046 "Try typing ~showverb~ and then the name of a verb.";
6048 if ((noun->#dict_par1) & DICT_META) print " meta";
6049 for (j=dict_start : j<dict_end : j=j+dict_entry_size)
6050 if (j->#dict_par2 == noun->#dict_par2)
6051 print " '", (address) j, "'";
6053 grammar = (HDR_STATICMEMORY-->0)-->($ff-(noun->#dict_par2));
6056 if (lines == 0) "has no grammar lines.";
6057 for (: lines>0 : lines--) {
6058 grammar = UnpackGrammarLine(grammar);
6059 print " "; DebugGrammarLine(); new_line;
6063 #Ifnot; ! TARGET_GLULX
6065 [ ShowVerbSub grammar lines i j wd dictlen entrylen;
6066 if (noun == 0 || ((noun->#dict_par1) & DICT_VERB) == 0)
6067 "Try typing ~showverb~ and then the name of a verb.";
6069 if ((noun->#dict_par1) & DICT_META) print " meta";
6070 dictlen = #dictionary_table-->0;
6071 entrylen = DICT_WORD_SIZE + 7;
6072 for (j=0 : j<dictlen : j++) {
6073 wd = #dictionary_table + WORDSIZE + entrylen*j;
6074 if (wd->#dict_par2 == noun->#dict_par2)
6075 print " '", (address) wd, "'";
6078 i = DictionaryWordToVerbNum(noun);
6079 grammar = (#grammar_table)-->(i+1);
6082 if (lines == 0) "has no grammar lines.";
6083 for (: lines>0 : lines--) {
6084 grammar = UnpackGrammarLine(grammar);
6085 print " "; DebugGrammarLine(); new_line;
6091 [ ShowObjSub c f l a n x numattr;
6092 if (noun == 0) noun = location;
6093 objectloop (c ofclass Class) if (noun ofclass c) { f++; l=c; }
6094 if (f == 1) print (name) l, " ~"; else print "Object ~";
6095 print (name) noun, "~ (", noun, ")";
6096 if (parent(noun)) print " in ~", (name) parent(noun), "~ (", parent(noun), ")";
6100 objectloop (c ofclass Class) if (noun ofclass c) print (name) c, " ";
6103 #Ifdef TARGET_ZCODE;
6105 #Ifnot; ! TARGET_GLULX
6106 numattr = NUM_ATTR_BYTES * 8;
6108 for (a=0,f=0 : a<numattr : a++) if (noun has a) f=1;
6111 for (a=0 : a<numattr : a++) if (noun has a) print (DebugAttribute) a, " ";
6114 if (noun ofclass Class) return;
6117 #Ifdef TARGET_ZCODE;
6118 l = #identifiers_table-->0;
6119 #Ifnot; ! TARGET_GLULX
6120 l = INDIV_PROP_START + #identifiers_table-->3;
6122 for (a=1 : a<=l : a++) {
6123 if ((a ~= 2 or 3) && noun.&a) {
6124 if (f == 0) { print " with "; f=1; }
6127 for (c=0 : WORDSIZE*c<n : c++) {
6130 if (a == name) print "'", (address) x, "'";
6132 if (a == number or capacity or time_left) print x;
6139 switch (metaclass(x)) {
6143 print "~", (string) x, "~";
6155 ! if (f==1) new_line;
6158 [ ShowDictSub_helper x; print (address) x; ];
6162 #Ifdef TARGET_ZCODE;
6163 dp = HDR_DICTIONARY-->0; ! start of dictionary
6164 dp = dp + dp->0 + 1; ! skip over word-separators table
6165 el = dp->0; dp = dp + 1; ! entry length
6166 ne = dp-->0; dp = dp + WORDSIZE; ! number of entries
6167 #Ifnot; ! TARGET_GLULX;
6168 dp = #dictionary_table; ! start of dictionary
6169 el = DICT_WORD_SIZE + 7; ! entry length
6170 ne = dp-->0; dp = dp + WORDSIZE; ! number of entries
6172 ! dp now at first entry
6173 wn = 2; x = NextWordStopped();
6176 "That word isn't in the dictionary.";
6178 ; ! show all entries
6180 dp = './/'; ne = 1; ! show '.'
6182 dp = ',//'; ne = 1; ! show ','
6184 dp = x; ne = 1; f = true; ! show specified entry, plus associated objects
6186 for ( : ne-- : dp=dp+el) {
6187 print (address) dp, " --> ";
6188 x = dp->#dict_par1; ! flag bits
6189 y = PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, ShowDictSub_helper, dp) + WORDSIZE;
6190 for (z=WORDSIZE : z<y : z++) {
6191 !if (x & DICT_NOUN) StorageForShortName->z = UpperCase(StorageForShortName->z);
6192 if (y > WORDSIZE+1 && StorageForShortName->z == ' ' or '.' or ',') x = x | $8000;
6193 print (char) StorageForShortName->z;
6199 !if (x & $0040) print " BIT_6";
6200 !if (x & $0020) print " BIT_5";
6201 !if (x & $0010) print " BIT_4";
6202 if (x & $8000) print " UNTYPEABLE";
6203 if (x & DICT_NOUN) print " noun";
6204 if (x & DICT_PLUR) print "+plural";
6205 if (x & DICT_VERB) print " verb";
6206 if (x & DICT_META) print "+meta";
6207 if (x & DICT_PREP) print " preposition";
6208 if (f && (x & DICT_NOUN)) {
6209 print " --> refers to these objects:";
6211 if (WordInProperty(dp, x, name)) print "^ ", (name) x, " (", x, ")";
6220 ! ----------------------------------------------------------------------------
6221 ! Miscellaneous display routines used by DrawStatusLine and available for
6222 ! user. Most of these vary according to which machine is being compiled to
6223 ! ----------------------------------------------------------------------------
6225 #Ifdef TARGET_ZCODE;
6227 [ ClearScreen window;
6229 WIN_ALL: @erase_window -1;
6230 WIN_STATUS: @erase_window 1;
6231 WIN_MAIN: @erase_window 0;
6235 #Iftrue (#version_number == 6);
6236 [ MoveCursorV6 line column charw; ! 1-based postion on text grid
6237 @get_wind_prop 1 13 -> charw; ! font size
6238 charw = charw & $FF;
6239 line = 1 + charw*(line-1);
6240 column = 1 + charw*(column-1);
6241 @set_cursor line column;
6246 [ MoveCursor line column; ! 1-based postion on text grid
6247 if (~~statuswin_current) {
6250 if (clr_on && clr_bgstatus > 1)
6251 @set_colour clr_fgstatus clr_bgstatus;
6256 if (line == 0) { line = 1; column = 1; }
6257 #Iftrue (#version_number == 6);
6258 MoveCursorV6(line, column);
6260 @set_cursor line column;
6262 statuswin_current = true;
6267 if (statuswin_current) {
6269 if (clr_on && clr_bgstatus > 1) @set_colour clr_fg clr_bg;
6275 statuswin_current = false;
6278 #Iftrue (#version_number == 6);
6279 [ ScreenWidth width charw;
6280 @get_wind_prop 1 3 -> width;
6281 @get_wind_prop 1 13 -> charw;
6282 charw = charw & $FF;
6283 if (charw == 0) return width;
6284 return (width+charw-1) / charw;
6288 return (HDR_SCREENWCHARS->0);
6293 return (HDR_SCREENHLINES->0);
6296 #Iftrue (#version_number == 6);
6297 [ StatusLineHeight height wx wy x y charh;
6298 ! Split the window. Standard 1.0 interpreters should keep the window 0
6299 ! cursor in the same absolute position, but older interpreters,
6300 ! including Infocom's don't - they keep the window 0 cursor in the
6301 ! same position relative to its origin. We therefore compensate
6303 @get_wind_prop 0 0 -> wy; @get_wind_prop 0 1 -> wx;
6304 @get_wind_prop 0 13 -> charh; @log_shift charh $FFF8 -> charh;
6305 @get_wind_prop 0 4 -> y; @get_wind_prop 0 5 -> x;
6306 height = height * charh;
6307 @split_window height;
6308 y = y - height + wy - 1;
6312 gg_statuswin_cursize = height;
6315 [ StatusLineHeight height;
6316 if (gg_statuswin_cursize ~= height)
6317 @split_window height;
6318 gg_statuswin_cursize = height;
6323 [ SetColour f b window;
6324 if (window == 0) { ! if setting both together, set reverse
6332 if (window == 0 or 2) {
6337 if (statuswin_current)
6338 @set_colour clr_fgstatus clr_bgstatus;
6340 @set_colour clr_fg clr_bg;
6346 #Ifnot; ! TARGET_GLULX
6348 [ ClearScreen window;
6349 if (window == WIN_ALL or WIN_MAIN) {
6350 glk_window_clear(gg_mainwin);
6352 glk_window_close(gg_quotewin, 0);
6356 if (gg_statuswin && window == WIN_ALL or WIN_STATUS)
6357 glk_window_clear(gg_statuswin);
6360 [ MoveCursor line column; ! 0-based postion on text grid
6362 glk_set_window(gg_statuswin);
6364 if (line == 0) { line = 1; column = 1; }
6365 glk_window_move_cursor(gg_statuswin, column-1, line-1);
6366 statuswin_current=1;
6370 glk_set_window(gg_mainwin);
6371 statuswin_current=0;
6375 if (c > 9) return c;
6377 return $ff0000*(c&1) + $ff00*(c&2 ~= 0) + $ff*(c&4 ~= 0);
6382 if (gg_statuswin && statuswin_current) id = gg_statuswin;
6383 glk_window_get_size(id, gg_arguments, 0);
6384 return gg_arguments-->0;
6388 glk_window_get_size(gg_mainwin, 0, gg_arguments);
6389 return gg_arguments-->0;
6393 [ SetColour f b window;
6394 if (window == 0) { ! if setting both together, set reverse
6402 if (window == 0 or 2) {
6407 if (glk_gestalt(gestalt_GarglkText, 0)) {
6408 if (f >= 0 && f < 10) {
6409 f = GlulxColourValues-->f;
6414 if (b >= 0 && b < 10) {
6415 b = GlulxColourValues-->b;
6420 garglk_set_zcolors(f, b);
6428 [ SetColour f b window doclear;
6429 f = b = window = doclear = 0;
6434 SetColour (f, b, w);
6437 [ RestoreColours; ! L61007, L61113
6438 gg_statuswin_cursize = -1; ! Force window split in StatusLineHeight()
6440 if (clr_on) { ! check colour has been used
6441 SetColour(clr_fg, clr_bg, 2); ! make sure both sets of variables are restored
6442 SetColour(clr_fgstatus, clr_bgstatus, 1, true);
6446 #Ifdef TARGET_ZCODE;
6447 #Iftrue (#version_number == 6); ! request screen update
6448 (0-->8) = (0-->8) | $$00000100;
6453 ! ----------------------------------------------------------------------------
6454 ! Except in Version 3, the DrawStatusLine routine does just that: this is
6455 ! provided explicitly so that it can be Replace'd to change the style, and
6456 ! as written it emulates the ordinary Standard game status line, which is
6458 ! ----------------------------------------------------------------------------
6460 #Ifdef TARGET_ZCODE;
6464 #Iftrue (#version_number == 6);
6465 [ DrawStatusLine width x charw scw mvw;
6466 HDR_GAMEFLAGS-->0 = (HDR_GAMEFLAGS-->0) & ~$0004;
6468 StatusLineHeight(gg_statuswin_size);
6469 ! Now clear the window. This isn't totally trivial. Our approach is to
6470 ! select the fixed space font, measure its width, and print an appropriate
6471 ! number of spaces. We round up if the screen isn't a whole number of
6472 ! characters wide, and rely on window 1 being set to clip by default.
6475 width = ScreenWidth();
6477 ! Back to standard font for the display. We use output_stream 3 to
6478 ! measure the space required, the aim being to get 50 characters
6479 ! worth of space for the location name.
6482 if (location == thedark)
6483 print (name) location;
6485 FindVisibilityLevels();
6486 if (visibility_ceiling == location) print (name) location;
6487 else print (The) visibility_ceiling;
6489 @get_wind_prop 1 3 -> width;
6490 @get_wind_prop 1 13 -> charw;
6491 charw = charw & $FF;
6492 @output_stream 3 StorageForShortName;
6493 print (string) SCORE__TX, "00000";
6494 @output_stream -3; scw = HDR_PIXELSTO3-->0 + charw;
6495 @output_stream 3 StorageForShortName;
6496 print (string) MOVES__TX, "00000";
6497 @output_stream -3; mvw = HDR_PIXELSTO3-->0 + charw;
6498 if (width - scw - mvw >= 50*charw) {
6499 x = 1+width-scw-mvw;
6500 @set_cursor 1 x; print (string) SCORE__TX, sline1;
6502 @set_cursor 1 x; print (string) MOVES__TX, sline2;
6505 @output_stream 3 StorageForShortName;
6506 print "00000/00000";
6507 @output_stream -3; scw = HDR_PIXELSTO3-->0 + charw;
6508 if (width - scw >= 50*charw) {
6510 @set_cursor 1 x; print sline1, "/", sline2;
6513 ! Reselect roman, as Infocom's interpreters interpreters go funny
6514 ! if reverse is selected twice.
6518 #Endif; ! #version_number == 6
6522 #Endif; ! TARGET_ZCODE
6524 #Ifndef DrawStatusLine;
6525 [ DrawStatusLine width posa posb posc posd pose;
6526 #Ifdef TARGET_GLULX;
6527 ! If we have no status window, we must not try to redraw it.
6528 if (gg_statuswin == 0)
6532 ! If there is no player location, we shouldn't try to draw status window
6533 if (location == nothing || parent(player) == nothing)
6536 StatusLineHeight(gg_statuswin_size);
6539 width = ScreenWidth();
6540 posa = width-26; ! For standard move/score display.
6544 posd = width-19; ! For time display.
6550 if (location == thedark) {
6551 print (name) location;
6554 FindVisibilityLevels();
6555 if (visibility_ceiling == location)
6556 print (name) location;
6558 print (The) visibility_ceiling;
6560 if (sys_statusline_flag) {
6563 MoveCursor(1, posd);
6565 MoveCursor(1, pose);
6566 print (string) TIME__TX;
6567 LanguageTimeOfDay(sline1, sline2);
6573 MoveCursor(1, posa);
6574 print (string) SCORE__TX, sline1;
6576 MoveCursor(1, posb);
6577 print (string) MOVES__TX, sline2;
6579 if (width > 53 && width <= 66) {
6580 MoveCursor(1, posb);
6582 print (string) MOVES__TX, sline2;
6584 print sline1, "/", sline2;
6588 MoveCursor(1, posc);
6590 print (string) MOVES_S__TX, sline2;
6592 print sline1, "/", sline2;
6597 MainWindow(); ! set_window
6601 #Ifdef TARGET_GLULX;
6603 [ StatusLineHeight hgt parwin;
6604 if (gg_statuswin == 0) return;
6605 if (hgt == gg_statuswin_cursize) return;
6606 parwin = glk_window_get_parent(gg_statuswin);
6607 glk_window_set_arrangement(parwin, $12, hgt, 0);
6608 gg_statuswin_cursize = hgt;
6611 [ Box__Routine maxwid arr ix lines lastnl parwin;
6612 maxwid = 0; ! squash compiler warning
6615 if (gg_quotewin == 0) {
6616 gg_arguments-->0 = lines;
6617 ix = InitGlkWindow(GG_QUOTEWIN_ROCK);
6618 if (ix == false) ix = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, GG_QUOTEWIN_ROCK);
6620 gg_quotewin = glk_window_open(gg_mainwin, $12, lines, 3,
6621 GG_QUOTEWIN_ROCK); ! window_open
6624 parwin = glk_window_get_parent(gg_quotewin);
6625 glk_window_set_arrangement(parwin, $12, lines, 0);
6630 glk_window_clear(gg_quotewin);
6631 glk_set_window(gg_quotewin);
6635 ! If gg_quotewin is zero here, the quote just appears in the story window.
6637 glk_set_style(style_BlockQuote);
6638 for (ix=0 : ix<lines : ix++) {
6639 print (string) arr-->(ix+1);
6640 if (ix < lines-1 || lastnl) new_line;
6642 glk_set_style(style_Normal);
6645 glk_set_window(gg_mainwin);
6649 #Endif; ! TARGET_GLULX
6652 #Ifdef TARGET_ZCODE;
6655 standard_interpreter = HDR_TERPSTANDARD-->0;
6656 transcript_mode = ((HDR_GAMEFLAGS-->0) & $0001);
6657 sys_statusline_flag = ( (HDR_TERPFLAGS->0) & $0002 ) / 2;
6658 top_object = #largest_object-255;
6660 dict_start = HDR_DICTIONARY-->0;
6661 dict_entry_size = dict_start->(dict_start->0 + 1);
6662 dict_start = dict_start + dict_start->0 + 4;
6663 dict_end = dict_start + (dict_start - 2)-->0 * dict_entry_size;
6665 if (dict_start > 0 && dict_end < 0 &&
6666 ((-dict_start) - dict_end) % dict_entry_size == 0)
6667 print "** Warning: grammar properties might not work correctly **^";
6670 buffer->0 = INPUT_BUFFER_LEN - WORDSIZE;
6671 buffer2->0 = INPUT_BUFFER_LEN - WORDSIZE;
6672 buffer3->0 = INPUT_BUFFER_LEN - WORDSIZE;
6673 parse->0 = MAX_BUFFER_WORDS;
6674 parse2->0 = MAX_BUFFER_WORDS;
6677 #Ifnot; ! TARGET_GLULX;
6680 @gestalt 4 2 res; ! Test if this interpreter has Glk.
6682 ! Without Glk, we're entirely screwed.
6685 ! Set the VM's I/O system to be Glk.
6688 ! First, we must go through all the Glk objects that exist, and see
6689 ! if we created any of them. One might think this strange, since the
6690 ! program has just started running, but remember that the player might
6691 ! have just typed "restart".
6694 res = InitGlkWindow(0);
6695 if (res == false) res = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, 0);
6698 ! Now, gg_mainwin and gg_storywin might already be set. If not, set them.
6700 if (gg_mainwin == 0) {
6701 ! Open the story window.
6702 res = InitGlkWindow(GG_MAINWIN_ROCK);
6703 if (res == false) res = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, GG_MAINWIN_ROCK);
6705 gg_mainwin = glk_window_open(0, 0, 0, 3, GG_MAINWIN_ROCK);
6706 if (gg_mainwin == 0) {
6707 ! If we can't even open one window, there's no point in going on.
6712 ! There was already a story window. We should erase it.
6713 glk_window_clear(gg_mainwin);
6716 if (gg_statuswin == 0) {
6717 res = InitGlkWindow(GG_STATUSWIN_ROCK);
6718 if (res == false) res = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, GG_STATUSWIN_ROCK);
6720 gg_statuswin_cursize = gg_statuswin_size;
6721 gg_statuswin = glk_window_open(gg_mainwin, $12,
6722 gg_statuswin_cursize, 4, GG_STATUSWIN_ROCK);
6725 ! It's possible that the status window couldn't be opened, in which case
6726 ! gg_statuswin is now zero. We must allow for that later on.
6728 glk_set_window(gg_mainwin);
6730 if (InitGlkWindow(1) == false) LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, 1);
6733 [ GGRecoverObjects id;
6734 ! If GGRecoverObjects() has been called, all these stored IDs are
6735 ! invalid, so we start by clearing them all out.
6736 ! (In fact, after a restoreundo, some of them may still be good.
6737 ! For simplicity, though, we assume the general case.)
6744 gg_statuswin_cursize = 0;
6746 gg_command_reading = false;
6747 ! Also tell the game to clear its object references.
6748 if (IdentifyGlkObject(0) == false) LibraryExtensions.RunWhile(ext_identifyglkobject, 0, 0);
6750 id = glk_stream_iterate(0, gg_arguments);
6752 switch (gg_arguments-->0) {
6753 GG_SAVESTR_ROCK: gg_savestr = id;
6754 GG_SCRIPTSTR_ROCK: gg_scriptstr = id;
6755 GG_COMMANDWSTR_ROCK: gg_commandstr = id;
6756 gg_command_reading = false;
6757 GG_COMMANDRSTR_ROCK: gg_commandstr = id;
6758 gg_command_reading = true;
6759 default: if (IdentifyGlkObject(1, 1, id, gg_arguments-->0) == false)
6760 LibraryExtensions.RunWhile(ext_identifyglkobject, false, 1, 1, id, gg_arguments-->0);
6762 id = glk_stream_iterate(id, gg_arguments);
6765 id = glk_window_iterate(0, gg_arguments);
6767 switch (gg_arguments-->0) {
6768 GG_MAINWIN_ROCK: gg_mainwin = id;
6769 GG_STATUSWIN_ROCK: gg_statuswin = id;
6770 GG_QUOTEWIN_ROCK: gg_quotewin = id;
6771 default: if (IdentifyGlkObject(1, 0, id, gg_arguments-->0) == false)
6772 LibraryExtensions.RunWhile(ext_identifyglkobject, false, 1, 0, id, gg_arguments-->0);
6774 id = glk_window_iterate(id, gg_arguments);
6777 id = glk_fileref_iterate(0, gg_arguments);
6779 switch (gg_arguments-->0) {
6780 GG_SCRIPTFREF_ROCK: gg_scriptfref = id;
6781 default: if (IdentifyGlkObject(1, 2, id, gg_arguments-->0) == false)
6782 LibraryExtensions.RunWhile(ext_identifyglkobject, false, 1, 2, id, gg_arguments-->0);
6784 id = glk_fileref_iterate(id, gg_arguments);
6787 ! Tell the game to tie up any loose ends.
6788 if (IdentifyGlkObject(2) == false)
6789 LibraryExtensions.RunWhile(ext_identifyglkobject, 0, 2);
6792 ! This somewhat obfuscated function will print anything.
6793 ! It handles strings, functions (with optional arguments), objects,
6794 ! object properties (with optional arguments), and dictionary words.
6795 ! It does *not* handle plain integers, but you can use
6796 ! DecimalNumber or EnglishNumber to handle that case.
6798 ! Calling: Is equivalent to:
6799 ! ------- ----------------
6800 ! PrintAnything() <nothing printed>
6801 ! PrintAnything(0) <nothing printed>
6802 ! PrintAnything("string"); print (string) "string";
6803 ! PrintAnything('word') print (address) 'word';
6804 ! PrintAnything(obj) print (name) obj;
6805 ! PrintAnything(obj, prop) obj.prop();
6806 ! PrintAnything(obj, prop, args...) obj.prop(args...);
6807 ! PrintAnything(func) func();
6808 ! PrintAnything(func, args...) func(args...);
6810 [ PrintAnything _vararg_count obj mclass;
6811 print_anything_result = 0;
6812 if (_vararg_count == 0) return;
6815 if (obj == 0) return;
6817 if (obj->0 == $60) {
6818 ! Dictionary word. Metaclass() can't catch this case, so we do
6820 print (address) obj;
6824 mclass = metaclass(obj);
6832 ! Call the function with all the arguments which are already
6834 @call obj _vararg_count print_anything_result;
6837 if (_vararg_count == 0) {
6841 ! Push the object back onto the stack, and call the
6842 ! veneer routine that handles obj.prop() calls.
6845 @call CA__Pr _vararg_count print_anything_result;
6851 ! This does the same as PrintAnything, but the output is sent to a
6852 ! byte array in memory. The first two arguments must be the array
6853 ! address and length; the following arguments are interpreted as
6854 ! for PrintAnything. The return value is the number of characters
6856 ! If the output is longer than the array length given, the extra
6857 ! characters are discarded, so the array does not overflow.
6858 ! (However, the return value is the total length of the output,
6859 ! including discarded characters.)
6861 [ PrintAnyToArray _vararg_count arr arrlen str oldstr len;
6864 _vararg_count = _vararg_count - 2;
6866 oldstr = glk_stream_get_current(); ! stream_get_current
6867 str = glk_stream_open_memory(arr, arrlen, 1, 0);
6868 if (str == 0) return 0;
6870 glk_stream_set_current(str);
6872 @call PrintAnything _vararg_count 0;
6874 glk_stream_set_current(oldstr);
6884 ! And this calls PrintAnyToArray on a particular array, jiggering
6885 ! the result to be a Glulx C-style ($E0) string.
6887 Constant GG_ANYTOSTRING_LEN 66;
6888 Array AnyToStrArr -> GG_ANYTOSTRING_LEN+1;
6890 [ ChangeAnyToCString _vararg_count ix len;
6891 ix = GG_ANYTOSTRING_LEN-2;
6895 ix = _vararg_count+2;
6896 @call PrintAnyToArray ix len;
6897 AnyToStrArr->0 = $E0;
6898 if (len >= GG_ANYTOSTRING_LEN)
6899 len = GG_ANYTOSTRING_LEN-1;
6900 AnyToStrArr->(len+1) = 0;
6906 ! This is a trivial function which just prints a number, in decimal
6907 ! digits. It may be useful as a stub to pass to PrintAnything.
6909 [ DecimalNumber num; print num; ];
6911 #Ifndef SHORTNAMEBUF_LEN; ! Can't use 'Default', unfortunately,
6912 Constant SHORTNAMEBUF_LEN 160; ! but this is functionally equivalent
6917 Array StorageForShortName buffer SHORTNAMEBUF_LEN;
6919 Array StorageForShortName -> WORDSIZE + SHORTNAMEBUF_LEN;
6924 #Ifdef TARGET_ZCODE;
6926 ! Platform-independent way of printing strings, routines and properties
6927 ! to a buffer (defined as length word followed by byte characters).
6929 [ PrintToBuffer buf len a b c d e;
6930 print_anything_result = 0;
6931 @output_stream 3 buf;
6932 switch (metaclass(a)) {
6936 print_anything_result = a(b, c, d, e);
6939 print_anything_result = PrintOrRun(a, b, true);
6944 if (buf-->0 > len) RunTimeError(14, len, "in PrintToBuffer()");
6948 #Ifnot; ! TARGET_GLULX
6950 [ PrintToBuffer buf len a b c d e;
6952 if (metaclass(a) == Object && a.#b == WORDSIZE
6953 && metaclass(a.b) == String)
6954 buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a.b);
6956 buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a, b, c, d, e);
6959 buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a);
6960 if (buf-->0 > len) buf-->0 = len;
6966 ! Print contents of buffer (defined as length word followed by byte characters).
6967 ! no_break == 1: omit trailing newline.
6968 ! set_case == 1: capitalise first letter;
6969 ! == 2: capitalise first letter, remainder lower case;
6970 ! == 3: all lower case;
6971 ! == 4: all upper case.
6972 ! centred == 1: add leading spaces.
6974 [ PrintFromBuffer buf no_break set_case centred
6977 if (buf->(j+WORDSIZE) ~= 10 or 13) j++; ! trim any trailing newline
6979 k = (ScreenWidth() - j) / 2;
6982 for (i=0 : i<j : i++) {
6983 k = buf->(WORDSIZE+i);
6986 1: if (i) set_case = 0;
6987 else k = UpperCase(k);
6988 2: if (i) k = LowerCase(k);
6989 else k = UpperCase(k);
6990 3: k = LowerCase(k);
6991 4: k = UpperCase(k);
6995 if (no_break == false) new_line;
6999 ! None of the following functions should be called for zcode if the
7000 ! output exceeds the size of the buffer.
7002 [ StringSize a b c d e;
7003 PrintToBuffer(StorageForShortName, 160, a, b, c, d, e);
7004 return StorageForShortName-->0;
7007 [ PrintCapitalised a b no_break no_caps centred;
7008 if (metaclass(a) == Routine or String || b == 0 || metaclass(a.b) == Routine or String)
7009 PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, a, b);
7011 if (a.b == NULL) rfalse;
7012 else return RunTimeError(2, a, b);
7013 if (no_caps == 0 or 1) no_caps = ~~no_caps;
7014 PrintFromBuffer(StorageForShortName, no_break, no_caps, centred);
7015 return print_anything_result;
7019 PrintCapitalised(a, b, false, true, true);
7023 if (no_caps) print (string) str;
7024 else PrintCapitalised(str,0,true);
7027 [ PrefaceByArticle o acode pluralise capitalise i artform findout artval;
7028 if (o provides articles) {
7029 artval=(o.&articles)-->(acode+short_name_case*LanguageCases);
7033 print (string) artval;
7034 if (pluralise) return;
7035 print (PSN__) o; return;
7038 i = GetGNAOfObject(o);
7040 if (i < 3 || (i >= 6 && i < 9)) i = i + 3;
7042 i = LanguageGNAsToArticles-->i;
7044 artform = LanguageArticles
7045 + 3*WORDSIZE*LanguageContractionForms*(short_name_case + i*LanguageCases);
7047 #Iftrue (LanguageContractionForms == 2);
7048 if (artform-->acode ~= artform-->(acode+3)) findout = true;
7049 #Endif; ! LanguageContractionForms
7050 #Iftrue (LanguageContractionForms == 3);
7051 if (artform-->acode ~= artform-->(acode+3)) findout = true;
7052 if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
7053 #Endif; ! LanguageContractionForms
7054 #Iftrue (LanguageContractionForms == 4);
7055 if (artform-->acode ~= artform-->(acode+3)) findout = true;
7056 if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
7057 if (artform-->(acode+6) ~= artform-->(acode+9)) findout = true;
7058 #Endif; ! LanguageContractionForms
7059 #Iftrue (LanguageContractionForms > 4);
7061 #Endif; ! LanguageContractionForms
7063 #Ifdef TARGET_ZCODE;
7064 if (standard_interpreter && findout) {
7065 StorageForShortName-->0 = SHORTNAMEBUF_LEN;
7066 @output_stream 3 StorageForShortName;
7067 if (pluralise) print (number) pluralise; else print (PSN__) o;
7069 acode = acode + 3*LanguageContraction(StorageForShortName + 2);
7071 #Ifnot; ! TARGET_GLULX
7074 PrintAnyToArray(StorageForShortName, SHORTNAMEBUF_LEN, EnglishNumber, pluralise);
7076 PrintAnyToArray(StorageForShortName, SHORTNAMEBUF_LEN, PSN__, o);
7077 acode = acode + 3*LanguageContraction(StorageForShortName);
7081 Cap (artform-->acode, ~~capitalise); ! print article
7082 if (pluralise) return;
7087 if (o == 0) { print (string) NOTHING__TX; rtrue; }
7088 switch (metaclass(o)) {
7089 Routine: print "<routine ", o, ">"; rtrue;
7090 String: print "<string ~", (string) o, "~>"; rtrue;
7091 nothing: print "<illegal object number ", o, ">"; rtrue;
7093 #Ifdef LanguagePrintShortName;
7094 if (LanguagePrintShortName(o)) rtrue;
7095 #Endif; ! LanguagePrintShortName
7096 if (indef_mode && o.&short_name_indef ~= 0 && PrintOrRun(o, short_name_indef, 1) ~= 0) rtrue;
7097 if (o.&short_name ~= 0 && PrintOrRun(o, short_name, 1) ~= 0) rtrue;
7101 [ Indefart o saveIndef;
7102 saveIndef = indef_mode; indef_mode = true; caps_mode = false;
7106 indef_mode = saveIndef;
7110 if (o provides article) {
7111 PrintOrRun(o, article, 1);
7112 print " ", (PSN__) o;
7113 indef_mode = saveIndef;
7116 PrefaceByArticle(o, 2);
7117 indef_mode = saveIndef;
7120 [ CInDefArt o saveIndef saveCaps;
7121 saveIndef = indef_mode; indef_mode = true;
7122 saveCaps = caps_mode; caps_mode = true;
7126 PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, PSN__, o);
7127 PrintFromBuffer(StorageForShortName, true, caps_mode);
7128 caps_mode = saveCaps;
7132 if (o provides article) {
7133 PrintCapitalised(o, article, true);
7134 print " ", (PSN__) o;
7135 indef_mode = saveIndef;
7136 caps_mode = saveCaps;
7139 PrefaceByArticle(o, 2, 0, 1);
7140 caps_mode = saveCaps;
7141 indef_mode = saveIndef;
7144 [ Defart o saveIndef;
7145 saveIndef = indef_mode;
7148 if ((~~o ofclass Object) || o has proper) {
7151 if (player provides narrative_voice) {
7152 switch (player.narrative_voice) {
7153 1: print (string) MYSELF__TX;
7154 2: print (string) YOURSELF__TX;
7156 default: RunTimeError(16, player.narrative_voice);
7159 else ThatOrThose(player);
7163 indef_mode = saveIndef;
7166 PrefaceByArticle(o, 1);
7167 indef_mode = saveIndef;
7170 [ CDefart o saveIndef saveCaps;
7171 saveIndef = indef_mode; indef_mode = false;
7172 saveCaps = caps_mode; caps_mode = true;
7173 if (~~o ofclass Object) {
7174 indef_mode = NULL; print (PSN__) o;
7179 PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, PSN__, o);
7180 PrintFromBuffer(StorageForShortName, true, caps_mode);
7183 PrefaceByArticle(o, 0);
7184 indef_mode = saveIndef; caps_mode = saveCaps;
7187 [ PrintShortName o saveIndef;
7188 saveIndef = indef_mode; indef_mode = NULL;
7189 PSN__(o); indef_mode = saveIndef;
7192 [ EnglishNumber n; LanguageNumber(n); ];
7195 #Ifdef SERIAL_COMMAS;
7198 n=0; ! quell unused n variable warning
7202 n = LanguageNumbers-->0;
7203 for (i=1 : i<=n : i=i+2)
7204 if (o == LanguageNumbers-->i) return LanguageNumbers-->(i+1);
7209 if (tab-->0 == 0) return RunTimeError(8);
7210 return tab-->(random(tab-->0));
7213 ! ----------------------------------------------------------------------------
7214 ! Useful routine: unsigned comparison (for addresses in Z-machine)
7215 ! Returns 1 if x>y, 0 if x=y, -1 if x<y
7216 ! ----------------------------------------------------------------------------
7218 [ UnsignedCompare x y u v;
7219 if (x == y) return 0;
7220 if (x < 0 && y >= 0) return 1;
7221 if (x >= 0 && y < 0) return -1;
7222 u = x&~WORD_HIGHBIT; v= y&~WORD_HIGHBIT;
7223 if (u > v) return 1;
7227 ! ==============================================================================
7229 #Ifdef NITFOL_HOOKS; ! Code contributed by Evin Robertson
7230 #Ifdef TARGET_GLULX; ! Might be nice for Z-machine games too,
7231 ! but I'm not going to try to make this work
7232 ! given #Ifdef funniness.
7234 Array magic_array --> ! This is so nitfol can do typo correction /
7235 ! automapping / debugging on Glulx games
7236 $6e66726d $4d616763 $ff0010 ! Goes to 'NfrmMagc' 10 refers to length
7237 Magic_Global_Dispatch__
7238 DI__check_word ! DI__check_word(buf, length)
7240 WV__Pr RV__Pr CA__Pr ! obj.prop = x; x = obj.prop; obj.prop(x)
7241 RA__Pr RL__Pr RA__Sc ! obj.∝ obj.#prop; class::prop
7242 OP__Pr OC__Cl ! obj provides prop; obj ofclass class
7244 OB__Parent__ OB__Child__ OB__Sibling__ ! No explicit veneer for these
7247 [ OB__Parent__ obj; return parent(obj); ];
7249 [ OB__Child__ obj; return child(obj); ];
7251 [ OB__Sibling__ obj; return sibling(obj); ];
7253 [ Magic_Global_Dispatch__ glbl;
7256 if (location == TheDark) return real_location; return location;
7260 return CompassDirection::number; ! Silliness to make exist RA__Sc
7261 ! Should never be called.
7263 return magic_array; ! Silences a warning.
7266 [ DI__check_word buf wlen ix val res dictlen entrylen;
7267 ! Just like in Tokenise__. In fact, Tokenise__ could call this if
7268 ! it wanted, instead of doing this itself.
7269 if (wlen > DICT_WORD_SIZE) wlen = DICT_WORD_SIZE;
7270 for (ix=0 : ix<wlen : ix++) {
7271 gg_tokenbuf->ix = glk_char_to_lower(buf->ix);
7273 for (: ix<DICT_WORD_SIZE : ix++) {
7274 gg_tokenbuf->ix = 0;
7276 val = #dictionary_table + WORDSIZE;
7277 entrylen = DICT_WORD_SIZE + 7;
7278 @binarysearch gg_tokenbuf DICT_WORD_SIZE val entrylen dictlen 1 1 res;
7283 #Endif; ! NITFOL_HOOKS
7285 ! ==============================================================================
7287 Object LibraryExtensions "(Library Extensions)"
7288 with RunAll [ prop a1 a2 a3
7290 objectloop (obj in self)
7291 if (obj provides prop && obj.prop ofclass Routine) {
7292 rval = obj.prop(a1, a2, a3);
7293 if (rval > max) max = rval;
7294 if (self.BetweenCalls) self.BetweenCalls();
7298 RunUntil [ prop exitval a1 a2 a3
7300 objectloop (obj in self)
7301 if (obj provides prop && obj.prop ofclass Routine) {
7302 rval = obj.prop(a1, a2, a3);
7303 if (rval == exitval) return rval;
7304 if (self.BetweenCalls) self.BetweenCalls();
7308 RunWhile [ prop exitval a1 a2 a3
7310 objectloop (obj in self)
7311 if (obj provides prop && obj.prop ofclass Routine) {
7312 rval = obj.prop(a1, a2, a3);
7313 if (rval ~= exitval) return rval;
7314 if (self.BetweenCalls) self.BetweenCalls();
7319 ext_number_1 0, ! general temporary workspace
7321 ! can be set to a function (e.g. RestoreWN) meant for execution
7322 ! after non-terminating calls to extension objects
7323 ! (via RunUntil/While/All)
7325 RestoreWN [; wn = self.ext_number_1; ],
7327 ! Special interception points
7328 ext_messages 0, ! Called if LibraryMessages.before()
7330 ! Extensions run while they return false
7332 ! Cross-platform entry points
7334 ext_afterlife 0, ! [C2/R1]
7335 ext_afterprompt 0, ! [C2/R1]
7336 ext_amusing 0, ! [C2/R1]
7337 ext_beforeparsing 0, ! [C2/R2]
7338 ext_chooseobjects 0, ! [C2/R2]
7339 ext_darktodark 0, ! [C2/R1]
7340 ext_deathmessage 0, ! [C2/R1]
7341 ext_gamepostroutine 0, ! [C2/R2]
7342 ext_gamepreroutine 0, ! [C2/R2]
7343 ext_initialise 0, ! [C1/R1]
7344 ext_inscope 0, ! [C2/R2]
7345 ext_lookroutine 0, ! [C2/R1]
7346 ext_newroom 0, ! [C2/R1]
7347 ext_objectdoesnotfit 0, ! [C2/R2]
7348 ext_parsenoun 0, ! [C3/R3]
7349 ext_parsenumber 0, ! [C2/R2]
7350 ext_parsererror 0, ! [C2/R2]
7351 ext_printrank 0, ! [C2/R1]
7352 ext_printtaskname 0, ! [C2/R1]
7353 ext_printverb 0, ! [C2/R2]
7354 ext_timepasses 0, ! [C2/R1]
7355 ext_unknownverb 0, ! [C2/R2]
7356 ext_aftersave 0, ! [C2/R2]
7357 ext_afterrestore 0, ! [C2/R2]
7358 ! [C1] = Called in all cases
7359 ! [C2] = Called if EP is undefined, or returns false
7360 ! [C3] = called if EP is undefined, or returns -1
7361 ! [R1] = All extensions run
7362 ! [R2] = Extensions run while they return false
7363 ! [R3] = Extensions run while they return -1
7365 #Ifdef TARGET_GLULX;
7366 ! Glulx entry points
7368 ext_handleglkevent 0, ! if EP undefined while extensions return false
7369 ext_identifyglkobject 0, ! if EP undefined while extensions return false
7370 ext_initglkwindow 0, ! if EP undefined while extensions return false
7371 #Endif; ! TARGET_GLULX;
7375 ! ==============================================================================
7377 Undef LIBRARY_STAGE; Constant LIBRARY_STAGE = AFTER_PARSER;
7380 Message "Warning: 'parser' included twice; ignoring second inclusion. (Ignore this if this is on purpose.)";
7382 ! ==============================================================================