1
! ==============================================================================
2
! PARSERM: Core of parser.
4
! Supplied for use with Inform 6 -- Release 6/11 -- Serial number 040227
6
! Copyright Graham Nelson 1993-2004 but freely usable (see manuals)
8
! This file is automatically Included in your game file by "Parser".
9
! ------------------------------------------------------------------------------
10
! Inclusion of "linklpa" (which defines properties and attributes)
11
! Global variables, constants and arrays
12
! 1: outside of the parser
13
! 2: used within the parser
14
! Inclusion of natural language definition file
15
! (which creates a compass and direction-objects)
16
! Darkness and player objects
17
! Definition of grammar token numbering system used by Inform
19
! The InformParser object
21
! level 0: outer shell, conversation, errors
25
! 4: scope and ambiguity resolving
26
! 5: object comparisons
28
! 7: reading words and moving tables about
31
! The InformLibrary object
34
! end of turn sequence
35
! scope looping, before/after sequence, sending messages out
36
! timers, daemons, time of day, score notification
38
! changing player personality
39
! tracing code (only present if DEBUG is set)
41
! Status line printing, menu display
42
! Printing object names with articles
43
! Miscellaneous utility routines
44
! Game banner, "version" verb, run-time errors
45
! ==============================================================================
51
Constant Grammar__Version 2;
55
! ------------------------------------------------------------------------------
56
! Global variables and their associated Constant and Array declarations
57
! ------------------------------------------------------------------------------
59
Global location = InformLibrary; ! Must be first global defined
60
Global sline1; ! Must be second
61
Global sline2; ! Must be third
62
! (for status line display)
63
! ------------------------------------------------------------------------------
64
! Z-Machine and interpreter issues
65
! ------------------------------------------------------------------------------
68
Global top_object; ! Largest valid number of any tree object
69
! ### these globals are not meaningful... well, maybe standard_interpreter,
70
! but I'll decide that later (AP).
71
Constant INDIV_PROP_START 64; ! Equivalent of a Glulx constant
73
#Endif; ! TARGET_ZCODE
75
Global standard_interpreter; ! The version number of the Z-Machine Standard which the
76
! interpreter claims to support, in form (upper byte).(lower)
78
Global undo_flag; ! Can the interpreter provide "undo"?
79
Global just_undone; ! Can't have two successive UNDOs
81
Global transcript_mode; ! true when game scripting is on
84
Global xcommsdir; ! true if command recording is on
85
#Endif; ! TARGET_ZCODE
88
Constant GG_MAINWIN_ROCK 201;
89
Constant GG_STATUSWIN_ROCK 202;
90
Constant GG_QUOTEWIN_ROCK 203;
91
Constant GG_SAVESTR_ROCK 301;
92
Constant GG_SCRIPTSTR_ROCK 302;
93
Constant GG_COMMANDWSTR_ROCK 303;
94
Constant GG_COMMANDRSTR_ROCK 304;
95
Constant GG_SCRIPTFREF_ROCK 401;
98
Array gg_arguments buffer 28;
100
Array gg_arguments --> 8;
102
Global gg_mainwin = 0;
103
Global gg_statuswin = 0;
104
Global gg_quotewin = 0;
105
Global gg_scriptfref = 0;
106
Global gg_scriptstr = 0;
107
Global gg_savestr = 0;
108
Global gg_commandstr = 0;
109
Global gg_command_reading = 0; ! true if gg_commandstr is being replayed
110
#Endif; ! TARGET_GLULX
112
Global gg_statuswin_cursize = 0;
113
Global gg_statuswin_size = 1;
115
! ------------------------------------------------------------------------------
117
! (for linkage reasons, the task_* arrays are created not here but in verblib.h)
118
! ------------------------------------------------------------------------------
120
#Ifndef sys_statusline_flag;
121
Global sys_statusline_flag = 0; ! non-zero if status line displays time
124
Constant START_MOVE 0; ! Traditionally 0 for Infocom, 1 for Inform
126
Global turns = START_MOVE; ! Number of turns of play so far
127
Global the_time = NULL; ! Current time (in minutes since midnight)
128
Global time_rate = 1; ! How often time is updated
129
Global time_step; ! By how much
132
Constant MAX_TIMERS 32; ! Max number timers/daemons active at once
134
Array the_timers --> MAX_TIMERS;
135
Global active_timers; ! Number of timers/daemons actives
137
Global score; ! The current score
138
Global last_score; ! Score last turn (for testing for changes)
139
Global notify_mode = true; ! Score notification
140
Global places_score; ! Contribution to score made by visiting
141
Global things_score; ! Contribution made by acquisition
143
! ------------------------------------------------------------------------------
145
! ------------------------------------------------------------------------------
147
Global player; ! Which object the human is playing through
148
Global deadflag; ! Normally 0, or false; 1 for dead; 2 for victorious, and
149
! higher numbers represent exotic forms of death
151
! ------------------------------------------------------------------------------
152
! Light and room descriptions
153
! ------------------------------------------------------------------------------
155
Global lightflag = true; ! Is there currently light to see by?
156
Global real_location; ! When in darkness, location = thedark
157
! and this holds the real location
158
Global visibility_ceiling; ! Highest object in tree visible from the player's point of view
159
! (usually the room, sometimes darkness, sometimes a closed
160
! non-transparent container).
162
Global lookmode = 1; ! 1=standard, 2=verbose, 3=brief room descs
163
Global print_player_flag; ! If set, print something like "(as Fred)" in room descriptions,
164
! to reveal whom the human is playing through
165
Global lastdesc; ! Value of location at time of most recent room description
168
! ------------------------------------------------------------------------------
169
! List writing (style bits are defined as Constants in "verblibm.h")
170
! ------------------------------------------------------------------------------
172
Global c_style; ! Current list-writer style
173
Global lt_value; ! Common value of list_together
174
Global listing_together; ! Object number of one member of a group being listed together
175
Global listing_size; ! Size of such a group
176
Global wlf_indent; ! Current level of indentation printed by WriteListFrom()
178
Global inventory_stage = 1; ! 1 or 2 according to the context in which "invent" routines
179
! of objects are called
180
Global inventory_style; ! List-writer style currently used while printing inventories
181
! ------------------------------------------------------------------------------
183
! ------------------------------------------------------------------------------
185
Global pretty_flag = true; ! Use character graphics, or plain text?
186
Global menu_nesting; ! Level of nesting (0 = root menu)
187
Global menu_item; ! These are used in communicating
188
Global item_width = 8; ! with the menu-creating routines
189
Global item_name = "---";
191
Global lm_n; ! Parameters used by LibraryMessages
192
Global lm_o; ! mechanism
195
Global debug_flag; ! Bitmap of flags for tracing actions,
196
! calls to object routines, etc.
197
Global x_scope_count; ! Used in printing a list of everything
198
#Endif; ! DEBUG ! in scope
200
! five for colour control
201
! see http://www.inform-fiction.org/patches/L61007.html
202
Global clr_fg = 1; ! foreground colour
203
Global clr_bg = 1; ! background colour
204
Global clr_fgstatus = 1; ! foreground colour of statusline
205
Global clr_bgstatus = 1; ! background colour of statusline
206
Global clr_on; ! has colour been enabled by the player?
207
Global statuswin_current; ! if writing to top window
209
Constant CLR_DEFAULT 1;
210
Constant CLR_BLACK 2;
212
Constant CLR_GREEN 4;
213
Constant CLR_YELLOW 5;
215
Constant CLR_MAGENTA 7;
217
Constant CLR_WHITE 9;
218
Constant CLR_PURPLE 7;
219
Constant CLR_AZURE 8;
222
Constant WIN_STATUS 1;
225
! ------------------------------------------------------------------------------
227
! ------------------------------------------------------------------------------
229
Global action; ! Action currently being asked to perform
230
Global inp1; ! 0 (nothing), 1 (number) or first noun
231
Global inp2; ! 0 (nothing), 1 (number) or second noun
232
Global noun; ! First noun or numerical value
233
Global second; ! Second noun or numerical value
235
Global keep_silent; ! If true, attempt to perform the action silently (e.g. for
236
! implicit takes, implicit opening of unlocked doors)
238
Global reason_code; ! Reason for calling a "life" rule
239
! (an action or fake such as ##Kiss)
241
Global receive_action; ! Either ##PutOn or ##Insert, whichever is action being tried
242
! when an object's "before" rule is checking "Receive"
244
! ==============================================================================
245
! Parser variables: first, for communication to the parser
246
! ------------------------------------------------------------------------------
248
Global parser_trace = 0; ! Set this to 1 to make the parser trace tokens and lines
249
Global parser_action; ! For the use of the parser when calling
250
Global parser_one; ! user-supplied routines
252
Array inputobjs --> 16; ! For parser to write its results in
253
Global parser_inflection; ! A property (usually "name") to find object names in
255
! ------------------------------------------------------------------------------
257
! ------------------------------------------------------------------------------
259
Global actor; ! Person asked to do something
260
Global actors_location; ! Like location, but for the actor
261
Global meta; ! Verb is a meta-command (such as "save")
263
Array multiple_object --> 64; ! List of multiple parameters
264
Global multiflag; ! Multiple-object flag passed to actions
265
! Also used to prevent misleading MULTI_PE
266
Global toomany_flag; ! Flag for "multiple match too large"
267
! (e.g. if "take all" took over 100 things)
269
Global special_word; ! Dictionary address for "special" token
270
Global special_number; ! Number typed for "special" token
271
Global parsed_number; ! For user-supplied parsing routines
272
Global consult_from; ! Word that a "consult" topic starts on
273
Global consult_words; ! ...and number of words in topic
275
! ------------------------------------------------------------------------------
277
! ------------------------------------------------------------------------------
279
Global notheld_mode; ! To do with implicit taking
280
Global onotheld_mode; ! "old copy of notheld_mode", ditto
281
Global not_holding; ! Object to be automatically taken as an
283
Array kept_results --> 16; ! Delayed command (while the take happens)
285
! ------------------------------------------------------------------------------
286
! Error numbers when parsing a grammar line
287
! ------------------------------------------------------------------------------
289
Global etype; ! Error number on current line
290
Global best_etype; ! Preferred error number so far
291
Global nextbest_etype; ! Preferred one, if ASKSCOPE_PE disallowed
293
Constant STUCK_PE = 1;
294
Constant UPTO_PE = 2;
295
Constant NUMBER_PE = 3;
296
Constant CANTSEE_PE = 4;
297
Constant TOOLIT_PE = 5;
298
Constant NOTHELD_PE = 6;
299
Constant MULTI_PE = 7;
300
Constant MMULTI_PE = 8;
301
Constant VAGUE_PE = 9;
302
Constant EXCEPT_PE = 10;
303
Constant ANIMA_PE = 11;
304
Constant VERB_PE = 12;
305
Constant SCENERY_PE = 13;
306
Constant ITGONE_PE = 14;
307
Constant JUNKAFTER_PE = 15;
308
Constant TOOFEW_PE = 16;
309
Constant NOTHING_PE = 17;
310
Constant ASKSCOPE_PE = 18;
312
! ------------------------------------------------------------------------------
313
! Pattern-matching against a single grammar line
314
! ------------------------------------------------------------------------------
316
Array pattern --> 32; ! For the current pattern match
317
Global pcount; ! and a marker within it
318
Array pattern2 --> 32; ! And another, which stores the best match
319
Global pcount2; ! so far
320
Constant PATTERN_NULL = $ffff; ! Entry for a token producing no text
322
Array line_ttype-->32; ! For storing an analysed grammar line
323
Array line_tdata-->32;
324
Array line_token-->32;
326
Global parameters; ! Parameters (objects) entered so far
327
Global nsns; ! Number of special_numbers entered so far
328
Global special_number1; ! First number, if one was typed
329
Global special_number2; ! Second number, if two were typed
331
! ------------------------------------------------------------------------------
332
! Inferences and looking ahead
333
! ------------------------------------------------------------------------------
335
Global params_wanted; ! Number of parameters needed (which may change in parsing)
337
Global inferfrom; ! The point from which the rest of the command must be inferred
338
Global inferword; ! And the preposition inferred
339
Global dont_infer; ! Another dull flag
341
Global action_to_be; ! (If the current line were accepted.)
342
Global action_reversed; ! (Parameters would be reversed in order.)
343
Global advance_warning; ! What a later-named thing will be
345
! ------------------------------------------------------------------------------
346
! At the level of individual tokens now
347
! ------------------------------------------------------------------------------
349
Global found_ttype; ! Used to break up tokens into type
350
Global found_tdata; ! and data (by AnalyseToken)
351
Global token_filter; ! For noun filtering by user routines
353
Global length_of_noun; ! Set by NounDomain to no of words in noun
356
Constant REPARSE_CODE = 10000; ! Signals "reparse the text" as a reply from NounDomain
357
#Ifnot; ! TARGET_GLULX
358
Constant REPARSE_CODE = $40000000; ! The parser rather gunkily adds addresses to REPARSE_CODE for
359
! some purposes. And expects the result to be greater than
360
! REPARSE_CODE (signed comparison). So Glulx Inform is limited
361
! to a single gigabyte of storage, for the moment.
364
Global lookahead; ! The token after the one now being matched
366
Global multi_mode; ! Multiple mode
367
Global multi_wanted; ! Number of things needed in multitude
368
Global multi_had; ! Number of things actually found
369
Global multi_context; ! What token the multi-obj was accepted for
371
Global indef_mode; ! "Indefinite" mode - ie, "take a brick"
373
Global indef_type; ! Bit-map holding types of specification
374
Global indef_wanted; ! Number of items wanted (100 for all)
375
Global indef_guess_p; ! Plural-guessing flag
376
Global indef_owner; ! Object which must hold these items
377
Global indef_cases; ! Possible gender and numbers of them
378
Global indef_possambig; ! Has a possibly dangerous assumption
379
! been made about meaning of a descriptor?
380
Global indef_nspec_at; ! Word at which a number like "two" was parsed
382
Global allow_plurals; ! Whether plurals presently allowed or not
384
Global take_all_rule; ! Slightly different rules apply to "take all" than other uses
385
! of multiple objects, to make adjudication produce more
386
! pragmatically useful results
387
! (Not a flag: possible values 0, 1, 2)
389
Global dict_flags_of_noun; ! Of the noun currently being parsed
390
! (a bitmap in #dict_par1 format)
391
Global pronoun_word; ! Records which pronoun ("it", "them", ...) caused an error
392
Global pronoun_obj; ! And what obj it was thought to refer to
393
Global pronoun__word; ! Saved value
394
Global pronoun__obj; ! Saved value
396
! ------------------------------------------------------------------------------
397
! Searching through scope and parsing "scope=Routine" grammar tokens
398
! ------------------------------------------------------------------------------
400
Constant PARSING_REASON = 0; ! Possible reasons for searching scope
401
Constant TALKING_REASON = 1;
402
Constant EACH_TURN_REASON = 2;
403
Constant REACT_BEFORE_REASON = 3;
404
Constant REACT_AFTER_REASON = 4;
405
Constant LOOPOVERSCOPE_REASON = 5;
406
Constant TESTSCOPE_REASON = 6;
408
Global scope_reason = PARSING_REASON; ! Current reason for searching scope
410
Global scope_token; ! For "scope=Routine" grammar tokens
412
Global scope_stage; ! 1, 2 then 3
414
Global ats_flag = 0; ! For AddToScope routines
417
Global placed_in_flag; ! To do with PlaceInScope
419
! ------------------------------------------------------------------------------
420
! The match list of candidate objects for a given token
421
! ------------------------------------------------------------------------------
423
Constant MATCH_LIST_SIZE = 128;
424
Array match_list --> 64; ! An array of matched objects so far
425
Array match_classes --> 64; ! An array of equivalence classes for them
426
Array match_scores --> 64; ! An array of match scores for them
427
Global number_matched; ! How many items in it? (0 means none)
428
Global number_of_classes; ! How many equivalence classes?
429
Global match_length; ! How many words long are these matches?
430
Global match_from; ! At what word of the input do they begin?
431
Global bestguess_score; ! What did the best-guess object score?
433
! ------------------------------------------------------------------------------
434
! Low level textual manipulation
435
! ------------------------------------------------------------------------------
439
Constant INPUT_BUFFER_LEN = 120; ! Length of buffer array (although we leave an extra byte
440
! to allow for interpreter bugs)
442
Array buffer -> 123; ! Buffer for parsing main line of input
444
Array parse buffer 63; ! Parse table mirroring it
445
Array parse2 buffer 63; !
447
Array parse -> 65; ! Parse table mirroring it
448
Array parse2 -> 65; !
450
Array buffer2 -> 123; ! Buffers for supplementary questions
451
Array buffer3 -> 123; ! Buffer retaining input for "again"
453
#Ifnot; ! TARGET_GLULX
455
Constant INPUT_BUFFER_LEN = 260; ! No extra byte necessary
456
Constant MAX_BUFFER_WORDS = 20;
457
Constant PARSE_BUFFER_LEN = 244; ! 4 + MAX_BUFFER_WORDS*4;
460
Array buffer buffer INPUT_BUFFER_LEN;
461
Array buffer2 buffer INPUT_BUFFER_LEN;
462
Array buffer3 buffer INPUT_BUFFER_LEN;
464
Array buffer -> INPUT_BUFFER_LEN;
465
Array buffer2 -> INPUT_BUFFER_LEN;
466
Array buffer3 -> INPUT_BUFFER_LEN;
468
Array parse --> PARSE_BUFFER_LEN/WORDSIZE;
469
Array parse2 --> PARSE_BUFFER_LEN/WORDSIZE;
473
Constant comma_word = 'comma,'; ! An "untypeable word" used to substitute
474
! for commas in parse buffers
476
Global wn; ! Word number within "parse" (from 1)
477
Global num_words; ! Number of words typed
478
Global verb_word; ! Verb word (eg, take in "take all" or
479
! "dwarf, take all") - address in dict
480
Global verb_wordnum; ! its number in typing order (eg, 1 or 3)
481
Global usual_grammar_after; ! Point from which usual grammar is parsed (it may vary from the
482
! above if user's routines match multi-word verbs)
484
Global oops_from; ! The "first mistake" word number
485
Global saved_oops; ! Used in working this out
486
Array oops_workspace -> 64; ! Used temporarily by "oops" routine
488
Global held_back_mode; ! Flag: is there some input from last time
489
Global hb_wn; ! left over? (And a save value for wn.)
490
! (Used for full stops and "then".)
492
! ----------------------------------------------------------------------------
494
Array PowersOfTwo_TB ! Used in converting case numbers to case bitmaps
508
! ============================================================================
509
! Constants, and one variable, needed for the language definition file
510
! ----------------------------------------------------------------------------
512
Constant POSSESS_PK = $100;
513
Constant DEFART_PK = $101;
514
Constant INDEFART_PK = $102;
515
Global short_name_case;
518
Global dict_entry_size;
521
! ----------------------------------------------------------------------------
523
Include "language__"; ! The natural language definition, whose filename is taken from
524
! the ICL language_name variable
526
! ----------------------------------------------------------------------------
528
#Ifndef LanguageCases;
529
Constant LanguageCases = 1;
530
#Endif; ! LanguageCases
532
! ------------------------------------------------------------------------------
533
! Pronouns support for the cruder (library 6/2 and earlier) version:
534
! only needed in English
535
! ------------------------------------------------------------------------------
537
#Ifdef EnglishNaturalLanguage;
538
Global itobj = NULL; ! The object which is currently "it"
539
Global himobj = NULL; ! The object which is currently "him"
540
Global herobj = NULL; ! The object which is currently "her"
542
Global old_itobj = NULL; ! The object which is currently "it"
543
Global old_himobj = NULL; ! The object which is currently "him"
544
Global old_herobj = NULL; ! The object which is currently "her"
545
#Endif; ! EnglishNaturalLanguage
547
! ============================================================================
548
! "Darkness" is not really a place: but it has to be an object so that the
549
! location-name on the status line can be "Darkness".
550
! ----------------------------------------------------------------------------
552
Object thedark "(darkness object)"
554
short_name DARKNESS__TX,
555
description [; return L__M(##Miscellany, 17); ];
557
Object selfobj "(self object)"
558
with short_name [; return L__M(##Miscellany, 18); ],
559
description [; return L__M(##Miscellany, 19); ],
571
before_implicit NULL,
572
has concealed animate proper transparent;
574
! ============================================================================
575
! The definition of the token-numbering system used by Inform.
576
! ----------------------------------------------------------------------------
578
Constant ILLEGAL_TT = 0; ! Types of grammar token: illegal
579
Constant ELEMENTARY_TT = 1; ! (one of those below)
580
Constant PREPOSITION_TT = 2; ! e.g. 'into'
581
Constant ROUTINE_FILTER_TT = 3; ! e.g. noun=CagedCreature
582
Constant ATTR_FILTER_TT = 4; ! e.g. edible
583
Constant SCOPE_TT = 5; ! e.g. scope=Spells
584
Constant GPR_TT = 6; ! a general parsing routine
586
Constant NOUN_TOKEN = 0; ! The elementary grammar tokens, and
587
Constant HELD_TOKEN = 1; ! the numbers compiled by Inform to
588
Constant MULTI_TOKEN = 2; ! encode them
589
Constant MULTIHELD_TOKEN = 3;
590
Constant MULTIEXCEPT_TOKEN = 4;
591
Constant MULTIINSIDE_TOKEN = 5;
592
Constant CREATURE_TOKEN = 6;
593
Constant SPECIAL_TOKEN = 7;
594
Constant NUMBER_TOKEN = 8;
595
Constant TOPIC_TOKEN = 9;
598
Constant GPR_FAIL = -1; ! Return values from General Parsing
599
Constant GPR_PREPOSITION = 0; ! Routines
600
Constant GPR_NUMBER = 1;
601
Constant GPR_MULTIPLE = 2;
602
Constant GPR_REPARSE = REPARSE_CODE;
603
Constant GPR_NOUN = $ff00;
604
Constant GPR_HELD = $ff01;
605
Constant GPR_MULTI = $ff02;
606
Constant GPR_MULTIHELD = $ff03;
607
Constant GPR_MULTIEXCEPT = $ff04;
608
Constant GPR_MULTIINSIDE = $ff05;
609
Constant GPR_CREATURE = $ff06;
611
Constant ENDIT_TOKEN = 15; ! Value used to mean "end of grammar line"
613
#Iftrue (Grammar__Version == 1);
615
[ AnalyseToken token m;
617
if (token < 0) { found_ttype = ILLEGAL_TT; return; }
618
if (token <= 8) { found_ttype = ELEMENTARY_TT; return; }
619
if (token < 15) { found_ttype = ILLEGAL_TT; return; }
620
if (token == 15) { found_ttype = ELEMENTARY_TT; return; }
621
if (token < 48) { found_ttype = ROUTINE_FILTER_TT;
622
found_tdata = token - 16;
625
if (token < 80) { found_ttype = GPR_TT;
626
found_tdata = #preactions_table-->(token-48);
629
if (token < 128) { found_ttype = SCOPE_TT;
630
found_tdata = #preactions_table-->(token-80);
633
if (token < 180) { found_ttype = ATTR_FILTER_TT;
634
found_tdata = token - 128;
638
found_ttype = PREPOSITION_TT;
639
m = #adjectives_table;
641
if (token == m-->1) { found_tdata = m-->0; return; }
644
m = #adjectives_table; RunTimeError(1);
648
[ UnpackGrammarLine line_address i m;
649
for (i=0 : i<32 : i++) {
650
line_token-->i = ENDIT_TOKEN;
651
line_ttype-->i = ELEMENTARY_TT;
652
line_tdata-->i = ENDIT_TOKEN;
654
for (i=0 : i<=5 : i++) {
655
line_token-->i = line_address->(i+1);
656
AnalyseToken(line_token-->i);
657
if ((found_ttype == ELEMENTARY_TT) && (found_tdata == NOUN_TOKEN)
658
&& (m == line_address->0)) {
659
line_token-->i = ENDIT_TOKEN;
662
line_ttype-->i = found_ttype;
663
line_tdata-->i = found_tdata;
664
if (found_ttype ~= PREPOSITION_TT) m++;
666
action_to_be = line_address->7;
667
action_reversed = false;
668
params_wanted = line_address->0;
669
return line_address + 8;
672
#Ifnot; ! Grammar__Version == 2
674
[ AnalyseToken token;
675
if (token == ENDIT_TOKEN) {
676
found_ttype = ELEMENTARY_TT;
677
found_tdata = ENDIT_TOKEN;
680
found_ttype = (token->0) & $$1111;
681
found_tdata = (token+1)-->0;
686
[ UnpackGrammarLine line_address i;
687
for (i=0 : i<32 : i++) {
688
line_token-->i = ENDIT_TOKEN;
689
line_ttype-->i = ELEMENTARY_TT;
690
line_tdata-->i = ENDIT_TOKEN;
692
action_to_be = 256*(line_address->0) + line_address->1;
693
action_reversed = ((action_to_be & $400) ~= 0);
694
action_to_be = action_to_be & $3ff;
698
line_address = line_address + 3;
699
if (line_address->0 == ENDIT_TOKEN) break;
700
line_token-->i = line_address;
701
AnalyseToken(line_address);
702
if (found_ttype ~= PREPOSITION_TT) params_wanted++;
703
line_ttype-->i = found_ttype;
704
line_tdata-->i = found_tdata;
706
return line_address + 1;
709
#Ifnot; ! TARGET_GLULX
711
[ UnpackGrammarLine line_address i;
712
for (i=0 : i<32 : i++) {
713
line_token-->i = ENDIT_TOKEN;
714
line_ttype-->i = ELEMENTARY_TT;
715
line_tdata-->i = ENDIT_TOKEN;
717
@aloads line_address 0 action_to_be;
718
action_reversed = (((line_address->2) & 1) ~= 0);
719
line_address = line_address - 2;
722
line_address = line_address + 5;
723
if (line_address->0 == ENDIT_TOKEN) break;
724
line_token-->i = line_address;
725
AnalyseToken(line_address);
726
if (found_ttype ~= PREPOSITION_TT) params_wanted++;
727
line_ttype-->i = found_ttype;
728
line_tdata-->i = found_tdata;
730
return line_address + 1;
734
#Endif; ! Grammar__Version
736
! To protect against a bug in early versions of the "Zip" interpreter:
737
! Of course, in Glulx, this routine actually performs work.
741
[ Tokenise__ b p; b->(2 + b->1) = 0; @tokenise b p; ];
743
#Ifnot; ! TARGET_GLULX
745
Array gg_tokenbuf -> DICT_WORD_SIZE;
747
[ GGWordCompare str1 str2 ix jx;
748
for (ix=0 : ix<DICT_WORD_SIZE : ix++) {
749
jx = (str1->ix) - (str2->ix);
750
if (jx ~= 0) return jx;
756
cx numwords len bx ix wx wpos wlen val res dictlen entrylen;
760
! First, split the buffer up into words. We use the standard Infocom
761
! list of word separators (comma, period, double-quote).
766
while (cx < len && buf->cx == ' ') cx++;
767
if (cx >= len) break;
769
if (buf->cx == '.' or ',' or '"') cx++;
771
while (cx < len && buf->cx ~= ' ' or '.' or ',' or '"') cx++;
773
tab-->(numwords*3+2) = (cx-bx);
774
tab-->(numwords*3+3) = WORDSIZE+bx;
776
if (numwords >= MAX_BUFFER_WORDS) break;
780
! Now we look each word up in the dictionary.
782
dictlen = #dictionary_table-->0;
783
entrylen = DICT_WORD_SIZE + 7;
785
for (wx=0 : wx<numwords : wx++) {
786
wlen = tab-->(wx*3+2);
787
wpos = tab-->(wx*3+3);
789
! Copy the word into the gg_tokenbuf array, clipping to DICT_WORD_SIZE
790
! characters and lower case.
791
if (wlen > DICT_WORD_SIZE) wlen = DICT_WORD_SIZE;
792
cx = wpos - WORDSIZE;
793
for (ix=0 : ix<wlen : ix++) gg_tokenbuf->ix = glk($00A0, buf->(cx+ix));
794
for (: ix<DICT_WORD_SIZE : ix++) gg_tokenbuf->ix = 0;
796
val = #dictionary_table + WORDSIZE;
797
@binarysearch gg_tokenbuf DICT_WORD_SIZE val entrylen dictlen 1 1 res;
798
tab-->(wx*3+1) = res;
804
! ============================================================================
805
! The InformParser object abstracts the front end of the parser.
807
! InformParser.parse_input(results)
808
! returns only when a sensible request has been made, and puts into the
811
! --> 0 = The action number
812
! --> 1 = Number of parameters
813
! --> 2, 3, ... = The parameters (object numbers), but
814
! 0 means "put the multiple object list here"
815
! 1 means "put one of the special numbers here"
817
! ----------------------------------------------------------------------------
819
Object InformParser "(Inform Parser)"
820
with parse_input [ results; Parser__parse(results); ],
823
! ----------------------------------------------------------------------------
824
! The Keyboard routine actually receives the player's words,
825
! putting the words in "a_buffer" and their dictionary addresses in
826
! "a_table". It is assumed that the table is the same one on each
829
! It can also be used by miscellaneous routines in the game to ask
830
! yes-no questions and the like, without invoking the rest of the parser.
832
! Return the number of words typed
833
! ----------------------------------------------------------------------------
837
[ KeyboardPrimitive a_buffer a_table;
838
read a_buffer a_table;
840
#Iftrue (#version_number == 6);
842
@loadb a_buffer 1 -> sp;
843
@add a_buffer 2 -> sp;
850
[ KeyCharPrimitive win key;
851
if (win) @set_window win;
860
[ KeyDelay tenths key;
861
@read_char 1 tenths KeyTimerInterrupt -> key;
865
#Ifnot; ! TARGET_GLULX
867
[ KeyCharPrimitive win nostat done res ix jx ch;
868
jx = ch; ! squash compiler warnings
869
if (win == 0) win = gg_mainwin;
870
if (gg_commandstr ~= 0 && gg_command_reading ~= false) {
872
done = glk($0091, gg_commandstr, gg_arguments, 31);
874
glk($0044, gg_commandstr, 0); ! stream_close
876
gg_command_reading = false;
877
! fall through to normal user input.
880
! Trim the trailing newline
881
if (gg_arguments->(done-1) == 10) done = done-1;
882
res = gg_arguments->0;
885
for (ix=1 : ix<done : ix++) {
886
ch = gg_arguments->ix;
887
if (ch >= '0' && ch <= '9') {
889
res = res + (ch-'0');
891
else if (ch >= 'a' && ch <= 'f') {
893
res = res + (ch+10-'a');
895
else if (ch >= 'A' && ch <= 'F') {
897
res = res + (ch+10-'A');
905
glk($00D2, win); ! request_char_event
907
glk($00C0, gg_event); ! select
908
switch (gg_event-->0) {
911
glk($00D3, win); ! cancel_char_event
917
2: ! evtype_CharInput
918
if (gg_event-->1 == win) {
923
ix = HandleGlkEvent(gg_event, 1, gg_arguments);
925
res = gg_arguments-->0;
932
if (gg_commandstr ~= 0 && gg_command_reading == false) {
933
if (res < 32 || res >= 256 || (res == '\' or ' ')) {
934
glk($0081, gg_commandstr, '\'); ! put_buffer_char
937
for (ix=0 : ix<8 : ix++) {
941
if (ch ~= 0 || ix == 7) done = 1;
943
if (ch >= 0 && ch <= 9) ch = ch + '0';
944
else ch = (ch - 10) + 'A';
945
glk($0081, gg_commandstr, ch); ! put_buffer_char
950
glk($0081, gg_commandstr, res); ! put_buffer_char
952
glk($0081, gg_commandstr, 10); ! put_char_stream (newline)
958
[ KeyDelay tenths key done ix;
959
glk($00D2, gg_mainwin); ! request_char_event
960
glk($00D6, tenths*100); ! request_timer_events
962
glk($00C0, gg_event); ! select
963
ix = HandleGlkEvent(gg_event, 1, gg_arguments);
965
key = gg_arguments-->0;
968
else if (ix >= 0 && gg_event-->0 == 1 or 2) {
973
glk($00D3, gg_mainwin); ! cancel_char_event
974
glk($00D6, 0); ! request_timer_events
978
[ KeyboardPrimitive a_buffer a_table done ix;
979
if (gg_commandstr ~= 0 && gg_command_reading ~= false) {
981
done = glk($0091, gg_commandstr, a_buffer+WORDSIZE, (INPUT_BUFFER_LEN-WORDSIZE)-1);
983
glk($0044, gg_commandstr, 0); ! stream_close
985
gg_command_reading = false;
986
! L__M(##CommandsRead, 5); would come after prompt
987
! fall through to normal user input.
990
! Trim the trailing newline
991
if ((a_buffer+WORDSIZE)->(done-1) == 10) done = done-1;
993
glk($0086, 8); ! set input style
994
glk($0084, a_buffer+WORDSIZE, done); ! put_buffer
995
glk($0086, 0); ! set normal style
1001
glk($00D0, gg_mainwin, a_buffer+WORDSIZE, INPUT_BUFFER_LEN-WORDSIZE, 0); ! request_line_event
1003
glk($00C0, gg_event); ! select
1004
switch (gg_event-->0) {
1007
3: ! evtype_LineInput
1008
if (gg_event-->1 == gg_mainwin) {
1009
a_buffer-->0 = gg_event-->2;
1013
ix = HandleGlkEvent(gg_event, 0, a_buffer);
1014
if (ix == 2) done = true;
1015
else if (ix == -1) done = false;
1017
if (gg_commandstr ~= 0 && gg_command_reading == false) {
1019
glk($0085, gg_commandstr, a_buffer+WORDSIZE, a_buffer-->0);
1020
glk($0081, gg_commandstr, 10); ! put_char_stream (newline)
1023
Tokenise__(a_buffer,a_table);
1024
! It's time to close any quote window we've got going.
1026
glk($0024, gg_quotewin, 0); ! close_window
1033
[ Keyboard a_buffer a_table nw i w w2 x1 x2;
1038
! Save the start of the buffer, in case "oops" needs to restore it
1039
! to the previous time's buffer
1041
for (i=0 : i<64 : i++) oops_workspace->i = a_buffer->i;
1043
! In case of an array entry corruption that shouldn't happen, but would be
1044
! disastrous if it did:
1046
#Ifdef TARGET_ZCODE;
1047
a_buffer->0 = INPUT_BUFFER_LEN;
1048
a_table->0 = 15; ! Allow to split input into this many words
1051
! Print the prompt, and read in the words and dictionary addresses
1058
KeyboardPrimitive(a_buffer, a_table);
1059
#Ifdef TARGET_ZCODE;
1061
#Ifnot; ! TARGET_GLULX
1065
! If the line was blank, get a fresh line
1067
L__M(##Miscellany, 10);
1071
! Unless the opening word was "oops", return
1072
! Conveniently, a_table-->1 is the first word in both ZCODE and GLULX.
1075
if (w == OOPS1__WD or OOPS2__WD or OOPS3__WD) jump DoOops;
1077
if (a_buffer->WORDSIZE == COMMENT_CHARACTER) {
1078
#Ifdef TARGET_ZCODE;
1079
if ((HDR_GAMEFLAGS-->0) & 1 || xcommsdir)
1080
L__M(##Miscellany, 54);
1081
else L__M(##Miscellany, 55);
1082
#Ifnot; ! TARGET_GLULX
1083
if (gg_scriptstr || gg_commandstr) L__M(##Miscellany, 54);
1084
else L__M(##Miscellany, 55);
1093
if ((w == UNDO1__WD or UNDO2__WD or UNDO3__WD) && (nw==1)) {
1094
if (turns == START_MOVE) {
1095
L__M(##Miscellany, 11);
1098
if (undo_flag == 0) {
1099
L__M(##Miscellany, 6);
1102
if (undo_flag == 1) jump UndoFailed;
1103
#Ifdef TARGET_ZCODE;
1105
#Ifnot; ! TARGET_GLULX
1111
L__M(##Miscellany, 7);
1115
#Ifdef TARGET_ZCODE;
1117
#Ifnot; ! TARGET_GLULX
1127
if (i == -1) undo_flag = 0;
1128
if (i == 0) undo_flag = 1;
1131
#Ifdef TARGET_ZCODE;
1133
#Ifnot; ! TARGET_GLULX
1134
glk($0086, 4); ! set subheader style
1136
print (name) location, "^";
1137
#Ifdef TARGET_ZCODE;
1139
#Ifnot; ! TARGET_GLULX
1140
glk($0086, 0); ! set normal style
1142
L__M(##Miscellany, 13);
1151
if (oops_from == 0) {
1152
L__M(##Miscellany, 14);
1156
L__M(##Miscellany, 15);
1160
L__M(##Miscellany, 16);
1164
! So now we know: there was a previous mistake, and the player has
1165
! attempted to correct a single word of it.
1167
for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer2->i = a_buffer->i;
1168
#Ifdef TARGET_ZCODE;
1169
x1 = a_table->9; ! Start of word following "oops"
1170
x2 = a_table->8; ! Length of word following "oops"
1171
#Ifnot; ! TARGET_GLULX
1172
x1 = a_table-->6; ! Start of word following "oops"
1173
x2 = a_table-->5; ! Length of word following "oops"
1176
! Repair the buffer to the text that was in it before the "oops"
1179
for (i=0 : i<64 : i++) a_buffer->i = oops_workspace->i;
1180
Tokenise__(a_buffer,a_table);
1182
! Work out the position in the buffer of the word to be corrected:
1184
#Ifdef TARGET_ZCODE;
1185
w = a_table->(4*oops_from + 1); ! Start of word to go
1186
w2 = a_table->(4*oops_from); ! Length of word to go
1187
#Ifnot; ! TARGET_GLULX
1188
w = a_table-->(3*oops_from); ! Start of word to go
1189
w2 = a_table-->(3*oops_from - 1); ! Length of word to go
1192
! Write spaces over the word to be corrected:
1194
for (i=0 : i<w2 : i++) a_buffer->(i+w) = ' ';
1197
! If the replacement is longer than the original, move up...
1198
for (i=INPUT_BUFFER_LEN-1 : i>=w+x2 : i--)
1199
a_buffer->i = a_buffer->(i-x2+w2);
1201
! ...increasing buffer size accordingly.
1202
#Ifdef TARGET_ZCODE;
1203
a_buffer->1 = (a_buffer->1) + (x2-w2);
1204
#Ifnot; ! TARGET_GLULX
1205
a_buffer-->0 = (a_buffer-->0) + (x2-w2);
1209
! Write the correction in:
1211
for (i=0 : i<x2 : i++) a_buffer->(i+w) = buffer2->(i+x1);
1213
Tokenise__(a_buffer, a_table);
1214
#Ifdef TARGET_ZCODE;
1216
#Ifnot; ! TARGET_GLULX
1221
]; ! end of Keyboard
1223
! ----------------------------------------------------------------------------
1224
! To simplify the picture a little, a rough map of the main routine:
1226
! (A) Get the input, do "oops" and "again"
1227
! (B) Is it a direction, and so an implicit "go"? If so go to (K)
1228
! (C) Is anyone being addressed?
1229
! (D) Get the verb: try all the syntax lines for that verb
1230
! (E) Break down a syntax line into analysed tokens
1231
! (F) Look ahead for advance warning for multiexcept/multiinside
1232
! (G) Parse each token in turn (calling ParseToken to do most of the work)
1233
! (H) Cheaply parse otherwise unrecognised conversation and return
1234
! (I) Print best possible error message
1235
! (J) Retry the whole lot
1236
! (K) Last thing: check for "then" and further instructions(s), return.
1238
! The strategic points (A) to (K) are marked in the commentary.
1240
! Note that there are three different places where a return can happen.
1241
! ----------------------------------------------------------------------------
1243
[ Parser__parse results syntax line num_lines line_address i j k
1246
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1248
! A: Get the input, do "oops" and "again"
1250
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1252
! Firstly, in "not held" mode, we still have a command left over from last
1253
! time (eg, the user typed "eat biscuit", which was parsed as "take biscuit"
1254
! last time, with "eat biscuit" tucked away until now). So we return that.
1256
if (notheld_mode == 1) {
1257
for (i=0 : i<8 : i++) results-->i = kept_results-->i;
1262
if (held_back_mode == 1) {
1264
Tokenise__(buffer, parse);
1270
Keyboard(buffer,parse);
1274
parser_inflection = name;
1276
! Initially assume the command is aimed at the player, and the verb
1279
#Ifdef TARGET_ZCODE;
1280
num_words = parse->1;
1281
#Ifnot; ! TARGET_GLULX
1282
num_words = parse-->0;
1286
#Ifdef LanguageToInformese;
1287
LanguageToInformese();
1290
Tokenise__(buffer,parse);
1292
#Endif; ! LanguageToInformese
1295
#Ifdef TARGET_ZCODE;
1296
num_words = parse->1;
1297
#Ifnot; ! TARGET_GLULX
1298
num_words = parse-->0;
1303
if (parser_trace >= 2) {
1305
for (i=0 : i<num_words : i++) {
1307
#Ifdef TARGET_ZCODE;
1308
j = parse-->(i*2 + 1);
1309
#Ifnot; ! TARGET_GLULX
1310
j = parse-->(i*3 + 1);
1312
k = WordAddress(i+1);
1313
l = WordLength(i+1);
1314
print "~"; for (m=0 : m<l : m++) print (char) k->m; print "~ ";
1316
if (j == 0) print "?";
1318
#Ifdef TARGET_ZCODE;
1319
if (UnsignedCompare(j, HDR_DICTIONARY-->0) >= 0 &&
1320
UnsignedCompare(j, HDR_HIGHMEMORY-->0) < 0)
1323
#Ifnot; ! TARGET_GLULX
1324
if (j->0 == $60) print (address) j;
1328
if (i ~= num_words-1) print " / ";
1335
actors_location = ScopeCeiling(player);
1336
usual_grammar_after = 0;
1341
action_to_be = NULL;
1343
! Begin from what we currently think is the verb word
1348
verb_word = NextWordStopped();
1350
! If there's no input here, we must have something like "person,".
1352
if (verb_word == -1) {
1353
best_etype = STUCK_PE;
1357
! Now try for "again" or "g", which are special cases: don't allow "again" if nothing
1358
! has previously been typed; simply copy the previous text across
1360
if (verb_word == AGAIN2__WD or AGAIN3__WD) verb_word = AGAIN1__WD;
1361
if (verb_word == AGAIN1__WD) {
1362
if (actor ~= player) {
1363
L__M(##Miscellany, 20);
1366
#Ifdef TARGET_ZCODE;
1367
if (buffer3->1 == 0) {
1368
L__M(##Miscellany, 21);
1371
#Ifnot; ! TARGET_GLULX
1372
if (buffer3-->0 == 0) {
1373
L__M(##Miscellany, 21);
1377
for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer->i = buffer3->i;
1381
! Save the present input in case of an "again" next time
1383
if (verb_word ~= AGAIN1__WD)
1384
for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer3->i = buffer->i;
1386
if (usual_grammar_after == 0) {
1388
i = RunRoutines(actor, grammar);
1390
if (parser_trace >= 2 && actor.grammar ~= 0 or NULL)
1391
print " [Grammar property returned ", i, "]^";
1394
#Ifdef TARGET_ZCODE;
1395
if ((i ~= 0 or 1) &&
1396
(UnsignedCompare(i, dict_start) < 0 ||
1397
UnsignedCompare(i, dict_end) >= 0 ||
1398
(i - dict_start) % dict_entry_size ~= 0)) {
1399
usual_grammar_after = j;
1403
#Ifnot; ! TARGET_GLULX
1404
if (i < 0) { usual_grammar_after = verb_wordnum; i=-i; }
1408
results-->0 = action;
1410
results-->2 = second;
1413
if (i ~= 0) { verb_word = i; wn--; verb_wordnum--; }
1414
else { wn = verb_wordnum; verb_word = NextWord(); }
1416
else usual_grammar_after = 0;
1418
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1420
! B: Is it a direction, and so an implicit "go"? If so go to (K)
1422
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1424
#Ifdef LanguageIsVerb;
1425
if (verb_word == 0) {
1426
i = wn; verb_word = LanguageIsVerb(buffer, parse, verb_wordnum);
1429
#Endif; ! LanguageIsVerb
1431
! If the first word is not listed as a verb, it must be a direction
1432
! or the name of someone to talk to
1434
if (verb_word == 0 || ((verb_word->#dict_par1) & 1) == 0) {
1436
! So is the first word an object contained in the special object "compass"
1437
! (i.e., a direction)? This needs use of NounDomain, a routine which
1438
! does the object matching, returning the object number, or 0 if none found,
1439
! or REPARSE_CODE if it has restructured the parse table so the whole parse
1440
! must be begun again...
1442
wn = verb_wordnum; indef_mode = false; token_filter = 0;
1443
l = NounDomain(compass, 0, 0);
1444
if (l == REPARSE_CODE) jump ReParse;
1446
! If it is a direction, send back the results:
1447
! action=GoSub, no of arguments=1, argument 1=the direction.
1451
action_to_be = ##Go;
1457
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1459
! C: Is anyone being addressed?
1461
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1463
! Only check for a comma (a "someone, do something" command) if we are
1464
! not already in the middle of one. (This simplification stops us from
1465
! worrying about "robot, wizard, you are an idiot", telling the robot to
1466
! tell the wizard that she is an idiot.)
1468
if (actor == player) {
1469
for (j=2 : j<=num_words : j++) {
1471
if (i == comma_word) jump Conversation;
1473
verb_word = UnknownVerb(verb_word);
1474
if (verb_word ~= 0) jump VerbAccepted;
1476
best_etype = VERB_PE;
1479
! NextWord nudges the word number wn on by one each time, so we've now
1480
! advanced past a comma. (A comma is a word all on its own in the table.)
1486
L__M(##Miscellany, 22);
1490
! Use NounDomain (in the context of "animate creature") to see if the
1491
! words make sense as the name of someone held or nearby
1493
wn = 1; lookahead = HELD_TOKEN;
1494
scope_reason = TALKING_REASON;
1495
l = NounDomain(player,actors_location,6);
1496
scope_reason = PARSING_REASON;
1497
if (l == REPARSE_CODE) jump ReParse;
1499
L__M(##Miscellany, 23);
1505
! The object addressed must at least be "talkable" if not actually "animate"
1506
! (the distinction allows, for instance, a microphone to be spoken to,
1507
! without the parser thinking that the microphone is human).
1509
if (l hasnt animate && l hasnt talkable) {
1510
L__M(##Miscellany, 24, l);
1514
! Check that there aren't any mystery words between the end of the person's
1515
! name and the comma (eg, throw out "dwarf sdfgsdgs, go north").
1518
L__M(##Miscellany, 25);
1522
! The player has now successfully named someone. Adjust "him", "her", "it":
1526
! Set the global variable "actor", adjust the number of the first word,
1527
! and begin parsing again from there.
1529
verb_wordnum = j + 1;
1531
! Stop things like "me, again":
1535
if (NextWordStopped() == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) {
1536
L__M(##Miscellany, 20);
1542
actors_location = ScopeCeiling(l);
1544
if (parser_trace >= 1)
1545
print "[Actor is ", (the) actor, " in ", (name) actors_location, "]^";
1549
} ! end of first-word-not-a-verb
1551
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1553
! D: Get the verb: try all the syntax lines for that verb
1555
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1559
! We now definitely have a verb, not a direction, whether we got here by the
1560
! "take ..." or "person, take ..." method. Get the meta flag for this verb:
1562
meta = ((verb_word->#dict_par1) & 2)/2;
1564
! You can't order other people to "full score" for you, and so on...
1566
if (meta == 1 && actor ~= player) {
1567
best_etype = VERB_PE;
1572
! Now let i be the corresponding verb number, stored in the dictionary entry
1573
! (in a peculiar 255-n fashion for traditional Infocom reasons)...
1575
i = $ff-(verb_word->#dict_par2);
1577
! ...then look up the i-th entry in the verb table, whose address is at word
1578
! 7 in the Z-machine (in the header), so as to get the address of the syntax
1579
! table for the given verb...
1581
#Ifdef TARGET_ZCODE;
1582
syntax = (HDR_STATICMEMORY-->0)-->i;
1583
#Ifnot; ! TARGET_GLULX
1584
syntax = (#grammar_table)-->(i+1);
1587
! ...and then see how many lines (ie, different patterns corresponding to the
1588
! same verb) are stored in the parse table...
1590
num_lines = (syntax->0) - 1;
1592
! ...and now go through them all, one by one.
1593
! To prevent pronoun_word 0 being misunderstood,
1595
pronoun_word = NULL; pronoun_obj = NULL;
1598
if (parser_trace >= 1) print "[Parsing for the verb '", (address) verb_word, "' (", num_lines+1, " lines)]^";
1601
best_etype = STUCK_PE; nextbest_etype = STUCK_PE;
1604
! "best_etype" is the current failure-to-match error - it is by default
1605
! the least informative one, "don't understand that sentence".
1606
! "nextbest_etype" remembers the best alternative to having to ask a
1607
! scope token for an error message (i.e., the best not counting ASKSCOPE_PE).
1608
! multiflag is used here to prevent inappropriate MULTI_PE errors
1609
! in addition to its unrelated duties passing information to action routines
1611
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1613
! E: Break down a syntax line into analysed tokens
1615
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1617
line_address = syntax + 1;
1619
for (line=0 : line<=num_lines : line++) {
1621
for (i=0 : i<32 : i++) {
1622
line_token-->i = ENDIT_TOKEN;
1623
line_ttype-->i = ELEMENTARY_TT;
1624
line_tdata-->i = ENDIT_TOKEN;
1627
! Unpack the syntax line from Inform format into three arrays; ensure that
1628
! the sequence of tokens ends in an ENDIT_TOKEN.
1630
line_address = UnpackGrammarLine(line_address);
1633
if (parser_trace >= 1) {
1634
if (parser_trace >= 2) new_line;
1635
print "[line ", line; DebugGrammarLine();
1640
! We aren't in "not holding" or inferring modes, and haven't entered
1641
! any parameters on the line yet, or any special numbers; the multiple
1642
! object is still empty.
1647
nsns = 0; special_word = 0; special_number = 0;
1648
multiple_object-->0 = 0;
1652
! Put the word marker back to just after the verb
1654
wn = verb_wordnum+1;
1656
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1658
! F: Look ahead for advance warning for multiexcept/multiinside
1660
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1662
! There are two special cases where parsing a token now has to be
1663
! affected by the result of parsing another token later, and these
1664
! two cases (multiexcept and multiinside tokens) are helped by a quick
1665
! look ahead, to work out the future token now. We can only carry this
1666
! out in the simple (but by far the most common) case:
1668
! multiexcept <one or more prepositions> noun
1670
! and similarly for multiinside.
1672
advance_warning = NULL; indef_mode = false;
1673
for (i=0,m=false,pcount=0 : line_token-->pcount ~= ENDIT_TOKEN : pcount++) {
1676
if (line_ttype-->pcount ~= PREPOSITION_TT) i++;
1678
if (line_ttype-->pcount == ELEMENTARY_TT) {
1679
if (line_tdata-->pcount == MULTI_TOKEN) m = true;
1680
if (line_tdata-->pcount == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN && i == 1) {
1681
! First non-preposition is "multiexcept" or
1682
! "multiinside", so look ahead.
1685
if (parser_trace >= 2) print " [Trying look-ahead]^";
1688
! We need this to be followed by 1 or more prepositions.
1691
if (line_ttype-->pcount == PREPOSITION_TT) {
1692
while (line_ttype-->pcount == PREPOSITION_TT) pcount++;
1694
if ((line_ttype-->pcount == ELEMENTARY_TT) && (line_tdata-->pcount == NOUN_TOKEN)) {
1696
! Advance past the last preposition
1698
while (wn < num_words) {
1700
if ( l && (l->#dict_par1) &8 ) { ! if preposition
1701
l = Descriptors(); ! skip past THE etc
1702
if (l~=0) etype=l; ! don't allow multiple objects
1703
l = NounDomain(actors_location, actor, NOUN_TOKEN);
1705
if (parser_trace >= 2) {
1706
print " [Advanced to ~noun~ token: ";
1707
if (l == REPARSE_CODE) print "re-parse request]^";
1708
if (l == 1) print "but multiple found]^";
1709
if (l == 0) print "error ", etype, "]^";
1710
if (l >= 2) print (the) l, "]^";
1713
if (l == REPARSE_CODE) jump ReParse;
1714
if (l >= 2) advance_warning = l;
1724
! Slightly different line-parsing rules will apply to "take multi", to
1725
! prevent "take all" behaving correctly but misleadingly when there's
1729
if (m && params_wanted == 1 && action_to_be == ##Take)
1732
! And now start again, properly, forearmed or not as the case may be.
1733
! As a precaution, we clear all the variables again (they may have been
1734
! disturbed by the call to NounDomain, which may have called outside
1735
! code, which may have done anything!).
1740
nsns = 0; special_word = 0; special_number = 0;
1741
multiple_object-->0 = 0;
1743
wn = verb_wordnum+1;
1745
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1747
! G: Parse each token in turn (calling ParseToken to do most of the work)
1749
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1751
! "Pattern" gradually accumulates what has been recognised so far,
1752
! so that it may be reprinted by the parser later on
1754
for (pcount=1 : : pcount++) {
1755
pattern-->pcount = PATTERN_NULL; scope_token = 0;
1757
token = line_token-->(pcount-1);
1758
lookahead = line_token-->pcount;
1761
if (parser_trace >= 2)
1762
print " [line ", line, " token ", pcount, " word ", wn, " : ", (DebugToken) token,
1766
if (token ~= ENDIT_TOKEN) {
1767
scope_reason = PARSING_REASON;
1768
parser_inflection = name;
1769
AnalyseToken(token);
1771
if (action_to_be == ##AskTo && found_ttype == ELEMENTARY_TT &&
1772
found_tdata == TOPIC_TOKEN)
1780
l = ParseToken__(found_ttype, found_tdata, pcount-1, token);
1781
while (l<-200) l = ParseToken__(ELEMENTARY_TT, l + 256);
1782
scope_reason = PARSING_REASON;
1784
if (l == GPR_PREPOSITION) {
1785
if (found_ttype~=PREPOSITION_TT && (found_ttype~=ELEMENTARY_TT ||
1786
found_tdata~=TOPIC_TOKEN)) params_wanted--;
1790
if (l < 0) l = false;
1792
if (l ~= GPR_REPARSE) {
1793
if (l == GPR_NUMBER) {
1794
if (nsns == 0) special_number1 = parsed_number;
1795
else special_number2 = parsed_number;
1798
if (l == GPR_MULTIPLE) l = 0;
1799
results-->(parameters+2) = l;
1801
pattern-->pcount = l;
1806
if (parser_trace >= 3) {
1807
print " [token resulted in ";
1808
if (l == REPARSE_CODE) print "re-parse request]^";
1809
if (l == 0) print "failure with error type ", etype, "]^";
1810
if (l == 1) print "success]^";
1814
if (l == REPARSE_CODE) jump ReParse;
1815
if (l == false) break;
1819
! If the player has entered enough already but there's still
1820
! text to wade through: store the pattern away so as to be able to produce
1821
! a decent error message if this turns out to be the best we ever manage,
1822
! and in the mean time give up on this line
1824
! However, if the superfluous text begins with a comma or "then" then
1825
! take that to be the start of another instruction
1827
if (wn <= num_words) {
1829
if (l == THEN1__WD or THEN2__WD or THEN3__WD or comma_word) {
1830
held_back_mode = 1; hb_wn = wn-1;
1833
for (m=0 : m<32 : m++) pattern2-->m = pattern-->m;
1840
! Now, we may need to revise the multiple object because of the single one
1841
! we now know (but didn't when the list was drawn up).
1843
if (parameters >= 1 && results-->2 == 0) {
1844
l = ReviseMulti(results-->3);
1845
if (l ~= 0) { etype = l; results-->0 = action_to_be; break; }
1847
if (parameters >= 2 && results-->3 == 0) {
1848
l = ReviseMulti(results-->2);
1849
if (l ~= 0) { etype = l; break; }
1852
! To trap the case of "take all" inferring only "yourself" when absolutely
1853
! nothing else is in the vicinity...
1855
if (take_all_rule == 2 && results-->2 == actor) {
1856
best_etype = NOTHING_PE;
1861
if (parser_trace >= 1) print "[Line successfully parsed]^";
1864
! The line has successfully matched the text. Declare the input error-free...
1868
! ...explain any inferences made (using the pattern)...
1870
if (inferfrom ~= 0) {
1871
print "("; PrintCommand(inferfrom); print ")^";
1874
! ...copy the action number, and the number of parameters...
1876
results-->0 = action_to_be;
1877
results-->1 = parameters;
1879
! ...reverse first and second parameters if need be...
1881
if (action_reversed && parameters == 2) {
1882
i = results-->2; results-->2 = results-->3;
1885
i = special_number1; special_number1 = special_number2;
1886
special_number2 = i;
1890
! ...and to reset "it"-style objects to the first of these parameters, if
1891
! there is one (and it really is an object)...
1893
if (parameters > 0 && results-->2 >= 2)
1894
PronounNotice(results-->2);
1896
! ...and worry about the case where an object was allowed as a parameter
1897
! even though the player wasn't holding it and should have been: in this
1898
! event, keep the results for next time round, go into "not holding" mode,
1899
! and for now tell the player what's happening and return a "take" request
1902
if (not_holding ~= 0 && actor == player) {
1904
i = RunRoutines(not_holding, before_implicit);
1905
! i = 0: Take the object, tell the player (default)
1906
! i = 1: Take the object, don't tell the player
1907
! i = 2: don't Take the object, continue
1908
! i = 3: don't Take the object, don't continue
1909
if (i > 2) { best_etype = NOTHELD_PE; jump GiveError; }
1910
if (i < 2) { ! perform the implicit Take
1911
if (i ~= 1) ! and tell the player
1912
L__M(##Miscellany, 26, not_holding);
1914
for (i=0 : i<8 : i++) kept_results-->i = results-->i;
1915
results-->0 = ##Take;
1917
results-->2 = not_holding;
1921
! (Notice that implicit takes are only generated for the player, and not
1922
! for other actors. This avoids entirely logical, but misleading, text
1925
! ...and return from the parser altogether, having successfully matched
1928
if (held_back_mode == 1) {
1934
} ! end of if(token ~= ENDIT_TOKEN) else
1935
} ! end of for(pcount++)
1937
! The line has failed to match.
1938
! We continue the outer "for" loop, trying the next line in the grammar.
1940
if (etype > best_etype) best_etype = etype;
1941
if (etype ~= ASKSCOPE_PE && etype > nextbest_etype) nextbest_etype = etype;
1943
! ...unless the line was something like "take all" which failed because
1944
! nothing matched the "all", in which case we stop and give an error now.
1946
if (take_all_rule == 2 && etype==NOTHING_PE) break;
1948
} ! end of for(line++)
1950
! The grammar is exhausted: every line has failed to match.
1952
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1954
! H: Cheaply parse otherwise unrecognised conversation and return
1956
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1962
! Errors are handled differently depending on who was talking.
1963
! If the command was addressed to somebody else (eg, "dwarf, sfgh") then
1964
! it is taken as conversation which the parser has no business in disallowing.
1966
if (actor ~= player) {
1967
if (usual_grammar_after ~= 0) {
1968
verb_wordnum = usual_grammar_after;
1972
special_word = NextWord();
1973
if (special_word == comma_word) {
1974
special_word = NextWord();
1977
special_number = TryNumber(verb_wordnum);
1978
results-->0 = ##NotUnderstood;
1980
results-->2 = 1; special_number1 = special_word;
1981
results-->3 = actor;
1982
consult_from = verb_wordnum; consult_words = num_words-consult_from+1;
1986
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1988
! I: Print best possible error message
1990
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1992
! If the player was the actor (eg, in "take dfghh") the error must be printed,
1993
! and fresh input called for. In three cases the oops word must be jiggled.
1995
if (ParserError(etype) ~= 0) jump ReType;
1996
pronoun_word = pronoun__word; pronoun_obj = pronoun__obj;
1998
if (etype == STUCK_PE) { L__M(##Miscellany, 27); oops_from = 1; }
1999
if (etype == UPTO_PE) { L__M(##Miscellany, 28);
2000
for (m=0 : m<32 : m++) pattern-->m = pattern2-->m;
2001
pcount = pcount2; PrintCommand(0); L__M(##Miscellany, 56);
2003
if (etype == NUMBER_PE) L__M(##Miscellany, 29);
2004
if (etype == CANTSEE_PE) { L__M(##Miscellany, 30); oops_from=saved_oops; }
2005
if (etype == TOOLIT_PE) L__M(##Miscellany, 31);
2006
if (etype == NOTHELD_PE) { L__M(##Miscellany, 32); oops_from=saved_oops; }
2007
if (etype == MULTI_PE) L__M(##Miscellany, 33);
2008
if (etype == MMULTI_PE) L__M(##Miscellany, 34);
2009
if (etype == VAGUE_PE) L__M(##Miscellany, 35);
2010
if (etype == EXCEPT_PE) L__M(##Miscellany, 36);
2011
if (etype == ANIMA_PE) L__M(##Miscellany, 37);
2012
if (etype == VERB_PE) L__M(##Miscellany, 38);
2013
if (etype == SCENERY_PE) L__M(##Miscellany, 39);
2014
if (etype == ITGONE_PE) {
2015
if (pronoun_obj == NULL)
2016
L__M(##Miscellany, 35);
2017
else L__M(##Miscellany, 40);
2019
if (etype == JUNKAFTER_PE) L__M(##Miscellany, 41);
2020
if (etype == TOOFEW_PE) L__M(##Miscellany, 42, multi_had);
2021
if (etype == NOTHING_PE) {
2022
if (results-->0 == ##Remove && results-->3 ofclass Object) {
2023
noun = results-->3; ! ensure valid for messages
2024
if (noun has animate) L__M(##Take, 6, noun);
2025
else if (noun hasnt container or supporter) L__M(##Insert, 2, noun);
2026
else if (noun has container && noun hasnt open) L__M(##Take, 9, noun);
2027
else if (children(noun)==0) L__M(##Search, 6, noun);
2028
else results-->0 = 0;
2030
if (results-->0 ~= ##Remove) {
2031
if (multi_wanted==100) L__M(##Miscellany, 43);
2032
else L__M(##Miscellany, 44);
2035
if (etype == ASKSCOPE_PE) {
2037
if (indirect(scope_error) == -1) {
2038
best_etype = nextbest_etype;
2043
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2045
! J: Retry the whole lot
2047
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2049
! And go (almost) right back to square one...
2053
! ...being careful not to go all the way back, to avoid infinite repetition
2054
! of a deferred command causing an error.
2057
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2059
! K: Last thing: check for "then" and further instructions(s), return.
2061
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2063
! At this point, the return value is all prepared, and we are only looking
2064
! to see if there is a "then" followed by subsequent instruction(s).
2068
if (wn > num_words) rtrue;
2071
if (i == THEN1__WD or THEN2__WD or THEN3__WD or comma_word) {
2072
if (wn > num_words) {
2073
held_back_mode = false;
2076
i = WordAddress(verb_wordnum);
2077
j = WordAddress(wn);
2078
for (: i<j : i++) i->0 = ' ';
2080
if (i == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) {
2081
! Delete the words "then again" from the again buffer,
2082
! in which we have just realised that it must occur:
2083
! prevents an infinite loop on "i. again"
2085
i = WordAddress(wn-2)-buffer;
2086
if (wn > num_words) j = INPUT_BUFFER_LEN-1;
2087
else j = WordAddress(wn)-buffer;
2088
for (: i<j : i++) buffer3->i = ' ';
2090
Tokenise__(buffer,parse);
2091
held_back_mode = true;
2094
best_etype = UPTO_PE;
2097
]; ! end of Parser__parse
2099
[ ScopeCeiling person act;
2100
act = parent(person);
2101
if (act == 0) return person;
2102
if (person == player && location == thedark) return thedark;
2103
while (parent(act)~=0 && (act has transparent || act has supporter ||
2104
(act has container && act has open)))
2109
! ----------------------------------------------------------------------------
2112
! Handles descriptive words like "my", "his", "another" and so on.
2113
! Skips "the", and leaves wn pointing to the first misunderstood word.
2115
! Allowed to set up for a plural only if allow_p is set
2117
! Returns error number, or 0 if no error occurred
2118
! ----------------------------------------------------------------------------
2120
Constant OTHER_BIT = 1; ! These will be used in Adjudicate()
2121
Constant MY_BIT = 2; ! to disambiguate choices
2122
Constant THAT_BIT = 4;
2123
Constant PLURAL_BIT = 8;
2124
Constant LIT_BIT = 16;
2125
Constant UNLIT_BIT = 32;
2128
indef_mode = 0; indef_type = 0; indef_wanted = 0; indef_guess_p = 0;
2129
indef_possambig = false;
2130
indef_owner = nothing;
2131
indef_cases = $$111111111111;
2135
[ Descriptors o x flag cto type n;
2137
if (wn > num_words) return 0;
2139
for (flag=true : flag :) {
2140
o = NextWordStopped(); flag = false;
2142
for (x=1 : x<=LanguageDescriptors-->0 : x=x+4)
2143
if (o == LanguageDescriptors-->x) {
2145
type = LanguageDescriptors-->(x+2);
2146
if (type ~= DEFART_PK) indef_mode = true;
2147
indef_possambig = true;
2148
indef_cases = indef_cases & (LanguageDescriptors-->(x+1));
2150
if (type == POSSESS_PK) {
2151
cto = LanguageDescriptors-->(x+3);
2153
0: indef_type = indef_type | MY_BIT;
2154
1: indef_type = indef_type | THAT_BIT;
2156
indef_owner = PronounValue(cto);
2157
if (indef_owner == NULL) indef_owner = InformParser;
2161
if (type == light) indef_type = indef_type | LIT_BIT;
2162
if (type == -light) indef_type = indef_type | UNLIT_BIT;
2165
if (o == OTHER1__WD or OTHER2__WD or OTHER3__WD) {
2166
indef_mode = 1; flag = 1;
2167
indef_type = indef_type | OTHER_BIT;
2169
if (o == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) {
2170
indef_mode = 1; flag = 1; indef_wanted = 100;
2171
if (take_all_rule == 1) take_all_rule = 2;
2172
indef_type = indef_type | PLURAL_BIT;
2174
if (allow_plurals) {
2175
n = TryNumber(wn-1);
2176
if (n == 1) { indef_mode = 1; flag = 1; }
2179
indef_mode = 1; flag = 1; indef_wanted = n;
2180
indef_nspec_at = wn-1;
2181
indef_type = indef_type | PLURAL_BIT;
2184
if (flag == 1 && NextWordStopped() ~= OF1__WD or OF2__WD or OF3__WD or OF4__WD)
2185
wn--; ! Skip 'of' after these
2191
! ----------------------------------------------------------------------------
2192
! CreatureTest: Will this person do for a "creature" token?
2193
! ----------------------------------------------------------------------------
2196
if (obj has animate) rtrue;
2197
if (obj hasnt talkable) rfalse;
2198
if (action_to_be == ##Ask or ##Answer or ##Tell or ##AskFor) rtrue;
2202
[ PrepositionChain wd index;
2203
if (line_tdata-->index == wd) return wd;
2204
if ((line_token-->index)->0 & $20 == 0) return -1;
2206
if (line_tdata-->index == wd) return wd;
2208
} until ((line_token-->index == ENDIT_TOKEN) || (((line_token-->index)->0 & $10) == 0));
2212
! ----------------------------------------------------------------------------
2213
! ParseToken(type, data):
2214
! Parses the given token, from the current word number wn, with exactly
2215
! the specification of a general parsing routine.
2216
! (Except that for "topic" tokens and prepositions, you need to supply
2217
! a position in a valid grammar line as third argument.)
2220
! GPR_REPARSE for "reconstructed input, please re-parse from scratch"
2221
! GPR_PREPOSITION for "token accepted with no result"
2222
! $ff00 + x for "please parse ParseToken(ELEMENTARY_TT, x) instead"
2223
! 0 for "token accepted, result is the multiple object list"
2224
! 1 for "token accepted, result is the number in parsed_number"
2225
! object num for "token accepted with this object as result"
2226
! -1 for "token rejected"
2228
! (A) Analyse the token; handle all tokens not involving
2229
! object lists and break down others into elementary tokens
2230
! (B) Begin parsing an object list
2231
! (C) Parse descriptors (articles, pronouns, etc.) in the list
2232
! (D) Parse an object name
2233
! (E) Parse connectives ("and", "but", etc.) and go back to (C)
2234
! (F) Return the conclusion of parsing an object list
2235
! ----------------------------------------------------------------------------
2237
[ ParseToken given_ttype given_tdata token_n x y;
2238
x = lookahead; lookahead = NOUN_TOKEN;
2239
y = ParseToken__(given_ttype,given_tdata,token_n);
2240
if (y == GPR_REPARSE) Tokenise__(buffer,parse);
2241
lookahead = x; return y;
2244
[ ParseToken__ given_ttype given_tdata token_n
2245
token l o i j k and_parity single_object desc_wn many_flag
2246
token_allows_multiple prev_indef_wanted;
2248
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2250
! A: Analyse token; handle all not involving object lists, break down others
2252
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2256
switch (given_ttype) {
2258
switch (given_tdata) {
2261
special_word = NextWord();
2264
if (parser_trace >= 3) print " [Read special as the number ", l, "]^";
2268
if (parser_trace >= 3) print " [Read special word at word number ", wn, "]^";
2272
parsed_number = l; return GPR_NUMBER;
2276
if (l == -1000) { etype = NUMBER_PE; return GPR_FAIL; }
2278
if (parser_trace>=3) print " [Read number as ", l, "]^";
2280
parsed_number = l; return GPR_NUMBER;
2283
if (action_to_be == ##Answer or ##Ask or ##AskFor or ##Tell)
2284
scope_reason = TALKING_REASON;
2288
if ((line_ttype-->(token_n+1) ~= PREPOSITION_TT) &&
2289
(line_token-->(token_n+1) ~= ENDIT_TOKEN))
2291
do o = NextWordStopped();
2292
until (o == -1 || PrepositionChain(o, token_n+1) ~= -1);
2294
consult_words = wn-consult_from;
2295
if (consult_words == 0) return GPR_FAIL;
2296
if (action_to_be == ##Ask or ##Answer or ##Tell) {
2297
o = wn; wn = consult_from; parsed_number = NextWord();
2298
#Ifdef EnglishNaturalLanguage;
2299
if (parsed_number == 'the' && consult_words > 1) parsed_number=NextWord();
2300
#Endif; ! EnglishNaturalLanguage
2303
if (o==-1 && (line_ttype-->(token_n+1) == PREPOSITION_TT))
2304
return GPR_FAIL; ! don't infer if required preposition is absent
2305
return GPR_PREPOSITION;
2309
#Iffalse (Grammar__Version == 1);
2310
! Is it an unnecessary alternative preposition, when a previous choice
2311
! has already been matched?
2312
if ((token->0) & $10) return GPR_PREPOSITION;
2313
#Endif; ! Grammar__Version
2315
! If we've run out of the player's input, but still have parameters to
2316
! specify, we go into "infer" mode, remembering where we are and the
2317
! preposition we are inferring...
2319
if (wn > num_words) {
2320
if (inferfrom==0 && parameters<params_wanted) {
2321
inferfrom = pcount; inferword = token;
2322
pattern-->pcount = REPARSE_CODE + Dword__No(given_tdata);
2325
! If we are not inferring, then the line is wrong...
2327
if (inferfrom == 0) return -1;
2329
! If not, then the line is right but we mark in the preposition...
2331
pattern-->pcount = REPARSE_CODE + Dword__No(given_tdata);
2332
return GPR_PREPOSITION;
2337
pattern-->pcount = REPARSE_CODE + Dword__No(o);
2339
! Whereas, if the player has typed something here, see if it is the
2340
! required preposition... if it's wrong, the line must be wrong,
2341
! but if it's right, the token is passed (jump to finish this token).
2343
if (o == given_tdata) return GPR_PREPOSITION;
2344
#Iffalse (Grammar__Version == 1);
2345
if (PrepositionChain(o, token_n) ~= -1) return GPR_PREPOSITION;
2346
#Endif; ! Grammar__Version
2350
l = indirect(given_tdata);
2352
if (parser_trace >= 3) print " [Outside parsing routine returned ", l, "]^";
2357
scope_token = given_tdata;
2359
l = indirect(scope_token);
2361
if (parser_trace >= 3) print " [Scope routine returned multiple-flag of ", l, "]^";
2363
if (l == 1) given_tdata = MULTI_TOKEN; else given_tdata = NOUN_TOKEN;
2366
token_filter = 1 + given_tdata;
2367
given_tdata = NOUN_TOKEN;
2370
token_filter = given_tdata;
2371
given_tdata = NOUN_TOKEN;
2373
} ! end of switch(given_ttype)
2375
token = given_tdata;
2377
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2379
! B: Begin parsing an object list
2381
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2383
! There are now three possible ways we can be here:
2384
! parsing an elementary token other than "special" or "number";
2385
! parsing a scope token;
2386
! parsing a noun-filter token (either by routine or attribute).
2388
! In each case, token holds the type of elementary parse to
2389
! perform in matching one or more objects, and
2390
! token_filter is 0 (default), an attribute + 1 for an attribute filter
2391
! or a routine address for a routine filter.
2393
token_allows_multiple = false;
2394
if (token == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN)
2395
token_allows_multiple = true;
2397
many_flag = false; and_parity = true; dont_infer = false;
2399
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2401
! C: Parse descriptors (articles, pronouns, etc.) in the list
2403
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2405
! We expect to find a list of objects next in what the player's typed.
2410
if (parser_trace >= 3) print " [Object list from word ", wn, "]^";
2413
! Take an advance look at the next word: if it's "it" or "them", and these
2414
! are unset, set the appropriate error number and give up on the line
2415
! (if not, these are still parsed in the usual way - it is not assumed
2416
! that they still refer to something in scope)
2418
o = NextWord(); wn--;
2420
pronoun_word = NULL; pronoun_obj = NULL;
2421
l = PronounValue(o);
2423
pronoun_word = o; pronoun_obj = l;
2425
! Don't assume this is a use of an unset pronoun until the
2426
! descriptors have been checked, because it might be an
2427
! article (or some such) instead
2429
for (l=1 : l<=LanguageDescriptors-->0 : l=l+4)
2430
if (o == LanguageDescriptors-->l) jump AssumeDescriptor;
2431
pronoun__word = pronoun_word; pronoun__obj = pronoun_obj;
2432
etype = VAGUE_PE; return GPR_FAIL;
2438
if (o == ME1__WD or ME2__WD or ME3__WD) { pronoun_word = o; pronoun_obj = player; }
2440
allow_plurals = true; desc_wn = wn;
2444
! First, we parse any descriptive words (like "the", "five" or "every"):
2446
if (l ~= 0) { etype = l; return GPR_FAIL; }
2450
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2452
! D: Parse an object name
2454
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2456
! This is an actual specified object, and is therefore where a typing error
2457
! is most likely to occur, so we set:
2461
! So, two cases. Case 1: token not equal to "held" (so, no implicit takes)
2462
! but we may well be dealing with multiple objects
2464
! In either case below we use NounDomain, giving it the token number as
2465
! context, and two places to look: among the actor's possessions, and in the
2466
! present location. (Note that the order depends on which is likeliest.)
2468
if (token ~= HELD_TOKEN) {
2469
i = multiple_object-->0;
2471
if (parser_trace >= 3) print " [Calling NounDomain on location and actor]^";
2473
l = NounDomain(actors_location, actor, token);
2474
if (l == REPARSE_CODE) return l; ! Reparse after Q&A
2475
if (indef_wanted == 100 && l == 0 && number_matched == 0)
2476
l = 1; ! ReviseMulti if TAKE ALL FROM empty container
2478
if (token_allows_multiple && ~~multiflag) {
2479
if (best_etype==MULTI_PE) best_etype=STUCK_PE;
2483
if (indef_possambig) {
2488
if (etype == MULTI_PE or TOOFEW_PE && multiflag) etype = STUCK_PE;
2491
} ! Choose best error
2494
if (parser_trace >= 3) {
2495
if (l > 1) print " [ND returned ", (the) l, "]^";
2497
print " [ND appended to the multiple object list:^";
2498
k = multiple_object-->0;
2499
for (j=i+1 : j<=k : j++)
2500
print " Entry ", j, ": ", (The) multiple_object-->j,
2501
" (", multiple_object-->j, ")^";
2502
print " List now has size ", k, "]^";
2508
if (~~many_flag) many_flag = true;
2509
else { ! Merge with earlier ones
2510
k = multiple_object-->0; ! (with either parity)
2511
multiple_object-->0 = i;
2512
for (j=i+1 : j<=k : j++) {
2513
if (and_parity) MultiAdd(multiple_object-->j);
2514
else MultiSub(multiple_object-->j);
2517
if (parser_trace >= 3) print " [Merging ", k-i, " new objects to the ", i, " old ones]^";
2522
! A single object was indeed found
2524
if (match_length == 0 && indef_possambig) {
2525
! So the answer had to be inferred from no textual data,
2526
! and we know that there was an ambiguity in the descriptor
2527
! stage (such as a word which could be a pronoun being
2528
! parsed as an article or possessive). It's worth having
2536
if (token == CREATURE_TOKEN && CreatureTest(l) == 0) {
2539
} ! Animation is required
2541
if (~~many_flag) single_object = l;
2543
if (and_parity) MultiAdd(l); else MultiSub(l);
2545
if (parser_trace >= 3) print " [Combining ", (the) l, " with list]^";
2553
! Case 2: token is "held" (which fortunately can't take multiple objects)
2554
! and may generate an implicit take
2556
l = NounDomain(actor,actors_location,token); ! Same as above...
2557
if (l == REPARSE_CODE) return GPR_REPARSE;
2559
if (indef_possambig) {
2564
etype = CantSee(); jump FailToken; ! Choose best error
2567
! ...until it produces something not held by the actor. Then an implicit
2568
! take must be tried. If this is already happening anyway, things are too
2569
! confused and we have to give up (but saving the oops marker so as to get
2570
! it on the right word afterwards).
2571
! The point of this last rule is that a sequence like
2574
! (taking the newspaper first)
2575
! The dwarf unexpectedly prevents you from taking the newspaper!
2577
! should not be allowed to go into an infinite repeat - read becomes
2578
! take then read, but take has no effect, so read becomes take then read...
2579
! Anyway for now all we do is record the number of the object to take.
2583
if (notheld_mode == 1) {
2584
saved_oops = oops_from;
2590
if (parser_trace >= 3) print " [Allowing object ", (the) l, " for now]^";
2594
} ! end of if (token ~= HELD_TOKEN) else
2596
! The following moves the word marker to just past the named object...
2598
wn = oops_from + match_length;
2600
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2602
! E: Parse connectives ("and", "but", etc.) and go back to (C)
2604
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2606
! Object(s) specified now: is that the end of the list, or have we reached
2607
! "and", "but" and so on? If so, create a multiple-object list if we
2608
! haven't already (and are allowed to).
2614
if (o == AND1__WD or AND2__WD or AND3__WD or BUT1__WD or BUT2__WD or BUT3__WD or comma_word) {
2617
if (parser_trace >= 3) print " [Read connective '", (address) o, "']^";
2620
if (~~token_allows_multiple) {
2621
if (multiflag) jump PassToken; ! give UPTO_PE error
2626
if (o == BUT1__WD or BUT2__WD or BUT3__WD) and_parity = 1-and_parity;
2629
multiple_object-->0 = 1;
2630
multiple_object-->1 = single_object;
2633
if (parser_trace >= 3) print " [Making new list from ", (the) single_object, "]^";
2636
dont_infer = true; inferfrom=0; ! Don't print (inferences)
2637
jump ObjectList; ! And back around
2640
wn--; ! Word marker back to first not-understood word
2642
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2644
! F: Return the conclusion of parsing an object list
2646
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2648
! Happy or unhappy endings:
2653
single_object = GPR_MULTIPLE;
2654
multi_context = token;
2657
if (indef_mode == 1 && indef_type & PLURAL_BIT ~= 0) {
2658
if (indef_wanted < 100 && indef_wanted > 1) {
2659
multi_had = 1; multi_wanted = indef_wanted;
2665
return single_object;
2669
! If we were only guessing about it being a plural, try again but only
2670
! allowing singulars (so that words like "six" are not swallowed up as
2673
if (allow_plurals && indef_guess_p == 1) {
2675
if (parser_trace >= 4) print " [Retrying singulars after failure ", etype, "]^";
2677
prev_indef_wanted = indef_wanted;
2678
allow_plurals = false;
2683
if ((indef_wanted > 0 || prev_indef_wanted > 0) && (~~multiflag)) etype = MULTI_PE;
2687
]; ! end of ParseToken__
2689
! ----------------------------------------------------------------------------
2690
! NounDomain does the most substantial part of parsing an object name.
2692
! It is given two "domains" - usually a location and then the actor who is
2693
! looking - and a context (i.e. token type), and returns:
2695
! 0 if no match at all could be made,
2696
! 1 if a multiple object was made,
2697
! k if object k was the one decided upon,
2698
! REPARSE_CODE if it asked a question of the player and consequently rewrote
2699
! the player's input, so that the whole parser should start again
2700
! on the rewritten input.
2702
! In the case when it returns 1<k<REPARSE_CODE, it also sets the variable
2703
! length_of_noun to the number of words in the input text matched to the
2705
! In the case k=1, the multiple objects are added to multiple_object by
2706
! hand (not by MultiAdd, because we want to allow duplicates).
2707
! ----------------------------------------------------------------------------
2709
[ NounDomain domain1 domain2 context first_word i j k l
2710
answer_words marker;
2712
if (parser_trace >= 4) {
2713
print " [NounDomain called at word ", wn, "^";
2716
print "seeking indefinite object: ";
2717
if (indef_type & OTHER_BIT) print "other ";
2718
if (indef_type & MY_BIT) print "my ";
2719
if (indef_type & THAT_BIT) print "that ";
2720
if (indef_type & PLURAL_BIT) print "plural ";
2721
if (indef_type & LIT_BIT) print "lit ";
2722
if (indef_type & UNLIT_BIT) print "unlit ";
2723
if (indef_owner ~= 0) print "owner:", (name) indef_owner;
2725
print " number wanted: ";
2726
if (indef_wanted == 100) print "all"; else print indef_wanted;
2728
print " most likely GNAs of names: ", indef_cases, "^";
2730
else print "seeking definite object^";
2734
match_length = 0; number_matched = 0; match_from = wn; placed_in_flag = 0;
2736
SearchScope(domain1, domain2, context);
2739
if (parser_trace >= 4) print " [ND made ", number_matched, " matches]^";
2742
wn = match_from+match_length;
2744
! If nothing worked at all, leave with the word marker skipped past the
2745
! first unmatched word...
2747
if (number_matched == 0) { wn++; rfalse; }
2749
! Suppose that there really were some words being parsed (i.e., we did
2750
! not just infer). If so, and if there was only one match, it must be
2751
! right and we return it...
2753
if (match_from <= num_words) {
2754
if (number_matched == 1) {
2759
! ...now suppose that there was more typing to come, i.e. suppose that
2760
! the user entered something beyond this noun. If nothing ought to follow,
2761
! then there must be a mistake, (unless what does follow is just a full
2762
! stop, and or comma)
2764
if (wn <= num_words) {
2765
i = NextWord(); wn--;
2766
if (i ~= AND1__WD or AND2__WD or AND3__WD or comma_word
2767
or THEN1__WD or THEN2__WD or THEN3__WD
2768
or BUT1__WD or BUT2__WD or BUT3__WD) {
2769
if (lookahead == ENDIT_TOKEN) rfalse;
2774
! Now look for a good choice, if there's more than one choice...
2776
number_of_classes = 0;
2778
if (number_matched == 1) i = match_list-->0;
2779
if (number_matched > 1) {
2780
i = Adjudicate(context);
2781
if (i == -1) rfalse;
2782
if (i == 1) rtrue; ! Adjudicate has made a multiple
2783
! object, and we pass it on
2786
! If i is non-zero here, one of two things is happening: either
2787
! (a) an inference has been successfully made that object i is
2788
! the intended one from the user's specification, or
2789
! (b) the user finished typing some time ago, but we've decided
2790
! on i because it's the only possible choice.
2791
! In either case we have to keep the pattern up to date,
2792
! note that an inference has been made and return.
2793
! (Except, we don't note which of a pile of identical objects.)
2796
if (dont_infer) return i;
2797
if (inferfrom == 0) inferfrom=pcount;
2798
pattern-->pcount = i;
2802
! If we get here, there was no obvious choice of object to make. If in
2803
! fact we've already gone past the end of the player's typing (which
2804
! means the match list must contain every object in scope, regardless
2805
! of its name), then it's foolish to give an enormous list to choose
2806
! from - instead we go and ask a more suitable question...
2808
if (match_from > num_words) jump Incomplete;
2810
! Now we print up the question, using the equivalence classes as worked
2811
! out by Adjudicate() so as not to repeat ourselves on plural objects...
2813
if (context==CREATURE_TOKEN) L__M(##Miscellany, 45);
2814
else L__M(##Miscellany, 46);
2816
j = number_of_classes; marker = 0;
2817
for (i=1 : i<=number_of_classes : i++) {
2818
while (((match_classes-->marker) ~= i) && ((match_classes-->marker) ~= -i)) marker++;
2819
k = match_list-->marker;
2821
if (match_classes-->marker > 0) print (the) k; else print (a) k;
2823
if (i < j-1) print (string) COMMA__TX;
2824
if (i == j-1) print (string) OR__TX;
2826
L__M(##Miscellany, 57);
2828
! ...and get an answer:
2831
#Ifdef TARGET_ZCODE;
2832
for (i=2 : i<INPUT_BUFFER_LEN : i++) buffer2->i = ' ';
2833
#Endif; ! TARGET_ZCODE
2834
answer_words=Keyboard(buffer2, parse2);
2836
! Conveniently, parse2-->1 is the first word in both ZCODE and GLULX.
2837
first_word = (parse2-->1);
2839
! Take care of "all", because that does something too clever here to do
2842
if (first_word == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) {
2843
if (context == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) {
2844
l = multiple_object-->0;
2845
for (i=0 : i<number_matched && l+i<63 : i++) {
2847
multiple_object-->(i+1+l) = k;
2849
multiple_object-->0 = i+l;
2852
L__M(##Miscellany, 47);
2856
! If the first word of the reply can be interpreted as a verb, then
2857
! assume that the player has ignored the question and given a new
2858
! command altogether.
2859
! (This is one time when it's convenient that the directions are
2860
! not themselves verbs - thus, "north" as a reply to "Which, the north
2861
! or south door" is not treated as a fresh command but as an answer.)
2863
#Ifdef LanguageIsVerb;
2864
if (first_word == 0) {
2865
j = wn; first_word = LanguageIsVerb(buffer2, parse2, 1); wn = j;
2867
#Endif; ! LanguageIsVerb
2868
if (first_word ~= 0) {
2869
j = first_word->#dict_par1;
2870
if ((0 ~= j&1) && ~~LanguageVerbMayBeName(first_word)) {
2871
CopyBuffer(buffer, buffer2);
2872
return REPARSE_CODE;
2876
! Now we insert the answer into the original typed command, as
2877
! words additionally describing the same object
2878
! (eg, > take red button
2881
! becomes "take music red button". The parser will thus have three
2882
! words to work from next time, not two.)
2884
#Ifdef TARGET_ZCODE;
2885
k = WordAddress(match_from) - buffer; l=buffer2->1+1;
2886
for (j=buffer + buffer->0 - 1 : j>=buffer+k+l : j--) j->0 = 0->(j-l);
2887
for (i=0 : i<l : i++) buffer->(k+i) = buffer2->(2+i);
2888
buffer->(k+l-1) = ' ';
2889
buffer->1 = buffer->1 + l;
2890
if (buffer->1 >= (buffer->0 - 1)) buffer->1 = buffer->0;
2891
#Ifnot; ! TARGET_GLULX
2892
k = WordAddress(match_from) - buffer;
2893
l = (buffer2-->0) + 1;
2894
for (j=buffer+INPUT_BUFFER_LEN-1 : j>=buffer+k+l : j--) j->0 = j->(-l);
2895
for (i=0 : i<l : i++) buffer->(k+i) = buffer2->(WORDSIZE+i);
2896
buffer->(k+l-1) = ' ';
2897
buffer-->0 = buffer-->0 + l;
2898
if (buffer-->0 > (INPUT_BUFFER_LEN-WORDSIZE)) buffer-->0 = (INPUT_BUFFER_LEN-WORDSIZE);
2901
! Having reconstructed the input, we warn the parser accordingly
2904
return REPARSE_CODE;
2906
! Now we come to the question asked when the input has run out
2907
! and can't easily be guessed (eg, the player typed "take" and there
2908
! were plenty of things which might have been meant).
2912
if (context == CREATURE_TOKEN) L__M(##Miscellany, 48);
2913
else L__M(##Miscellany, 49);
2915
#Ifdef TARGET_ZCODE;
2916
for (i=2 : i<INPUT_BUFFER_LEN : i++) buffer2->i=' ';
2917
#Endif; ! TARGET_ZCODE
2918
answer_words = Keyboard(buffer2, parse2);
2920
first_word=(parse2-->1);
2921
#Ifdef LanguageIsVerb;
2922
if (first_word==0) {
2923
j = wn; first_word=LanguageIsVerb(buffer2, parse2, 1); wn = j;
2925
#Endif; ! LanguageIsVerb
2927
! Once again, if the reply looks like a command, give it to the
2928
! parser to get on with and forget about the question...
2930
if (first_word ~= 0) {
2931
j = first_word->#dict_par1;
2933
CopyBuffer(buffer, buffer2);
2934
return REPARSE_CODE;
2938
! ...but if we have a genuine answer, then:
2940
! (1) we must glue in text suitable for anything that's been inferred.
2942
if (inferfrom ~= 0) {
2943
for (j=inferfrom : j<pcount : j++) {
2944
if (pattern-->j == PATTERN_NULL) continue;
2945
#Ifdef TARGET_ZCODE;
2946
i = 2+buffer->1; (buffer->1)++; buffer->(i++) = ' ';
2947
#Ifnot; ! TARGET_GLULX
2948
i = WORDSIZE + buffer-->0;
2949
(buffer-->0)++; buffer->(i++) = ' ';
2953
if (parser_trace >= 5) print "[Gluing in inference with pattern code ", pattern-->j, "]^";
2956
! Conveniently, parse2-->1 is the first word in both ZCODE and GLULX.
2960
! An inferred object. Best we can do is glue in a pronoun.
2961
! (This is imperfect, but it's very seldom needed anyway.)
2963
if (pattern-->j >= 2 && pattern-->j < REPARSE_CODE) {
2964
PronounNotice(pattern-->j);
2965
for (k=1 : k<=LanguagePronouns-->0 : k=k+3)
2966
if (pattern-->j == LanguagePronouns-->(k+2)) {
2967
parse2-->1 = LanguagePronouns-->k;
2969
if (parser_trace >= 5) print "[Using pronoun '", (address) parse2-->1, "']^";
2975
! An inferred preposition.
2976
parse2-->1 = No__Dword(pattern-->j - REPARSE_CODE);
2978
if (parser_trace >= 5) print "[Using preposition '", (address) parse2-->1, "']^";
2982
! parse2-->1 now holds the dictionary address of the word to glue in.
2984
if (parse2-->1 ~= 0) {
2986
#Ifdef TARGET_ZCODE;
2988
print (address) parse2-->1;
2991
for (l=i : l<i+k : l++) buffer->l = buffer->(l+2);
2992
i = i + k; buffer->1 = i-2;
2993
#Ifnot; ! TARGET_GLULX
2994
k = PrintAnyToArray(buffer+i, INPUT_BUFFER_LEN-i, parse2-->1);
2995
i = i + k; buffer-->0 = i - WORDSIZE;
3001
! (2) we must glue the newly-typed text onto the end.
3003
#Ifdef TARGET_ZCODE;
3004
i = 2+buffer->1; (buffer->1)++; buffer->(i++) = ' ';
3005
for (j=0 : j<buffer2->1 : i++,j++) {
3006
buffer->i = buffer2->(j+2);
3008
if (buffer->1 == INPUT_BUFFER_LEN) break;
3010
#Ifnot; ! TARGET_GLULX
3011
i = WORDSIZE + buffer-->0;
3012
(buffer-->0)++; buffer->(i++) = ' ';
3013
for (j=0 : j<buffer2-->0 : i++,j++) {
3014
buffer->i = buffer2->(j+WORDSIZE);
3016
if (buffer-->0 == INPUT_BUFFER_LEN) break;
3020
! (3) we fill up the buffer with spaces, which is unnecessary, but may
3021
! help incorrectly-written interpreters to cope.
3023
#Ifdef TARGET_ZCODE;
3024
for (: i<INPUT_BUFFER_LEN : i++) buffer->i = ' ';
3025
#Endif; ! TARGET_ZCODE
3027
return REPARSE_CODE;
3029
]; ! end of NounDomain
3031
! ----------------------------------------------------------------------------
3032
! The Adjudicate routine tries to see if there is an obvious choice, when
3033
! faced with a list of objects (the match_list) each of which matches the
3034
! player's specification equally well.
3036
! To do this it makes use of the context (the token type being worked on).
3037
! It counts up the number of obvious choices for the given context
3038
! (all to do with where a candidate is, except for 6 (animate) which is to
3039
! do with whether it is animate or not);
3041
! if only one obvious choice is found, that is returned;
3043
! if we are in indefinite mode (don't care which) one of the obvious choices
3044
! is returned, or if there is no obvious choice then an unobvious one is
3047
! at this stage, we work out whether the objects are distinguishable from
3048
! each other or not: if they are all indistinguishable from each other,
3049
! then choose one, it doesn't matter which;
3051
! otherwise, 0 (meaning, unable to decide) is returned (but remember that
3052
! the equivalence classes we've just worked out will be needed by other
3053
! routines to clear up this mess, so we can't economise on working them
3056
! Returns -1 if an error occurred
3057
! ----------------------------------------------------------------------------
3059
Constant SCORE__CHOOSEOBJ = 1000;
3060
Constant SCORE__IFGOOD = 500;
3061
Constant SCORE__UNCONCEALED = 100;
3062
Constant SCORE__BESTLOC = 60;
3063
Constant SCORE__NEXTBESTLOC = 40;
3064
Constant SCORE__NOTCOMPASS = 20;
3065
Constant SCORE__NOTSCENERY = 10;
3066
Constant SCORE__NOTACTOR = 5;
3067
Constant SCORE__GNA = 1;
3068
Constant SCORE__DIVISOR = 20;
3070
[ Adjudicate context i j k good_flag good_ones last n flag offset sovert;
3072
if (parser_trace >= 4) {
3073
print " [Adjudicating match list of size ", number_matched, " in context ", context, "^";
3076
print "indefinite type: ";
3077
if (indef_type & OTHER_BIT) print "other ";
3078
if (indef_type & MY_BIT) print "my ";
3079
if (indef_type & THAT_BIT) print "that ";
3080
if (indef_type & PLURAL_BIT) print "plural ";
3081
if (indef_type & LIT_BIT) print "lit ";
3082
if (indef_type & UNLIT_BIT) print "unlit ";
3083
if (indef_owner ~= 0) print "owner:", (name) indef_owner;
3085
print " number wanted: ";
3086
if (indef_wanted == 100) print "all"; else print indef_wanted;
3088
print " most likely GNAs of names: ", indef_cases, "^";
3090
else print "definite object^";
3094
j = number_matched-1; good_ones = 0; last = match_list-->0;
3095
for (i=0 : i<=j : i++) {
3097
match_scores-->i = 0;
3102
HELD_TOKEN, MULTIHELD_TOKEN:
3103
if (parent(n) == actor) good_flag = true;
3105
if (advance_warning == -1) {
3109
if (n ~= advance_warning) good_flag = true;
3112
if (advance_warning == -1) {
3113
if (parent(n) ~= actor) good_flag = true;
3116
if (n in advance_warning) good_flag = true;
3119
if (CreatureTest(n) == 1) good_flag = true;
3125
match_scores-->i = SCORE__IFGOOD;
3126
good_ones++; last = n;
3129
if (good_ones == 1) return last;
3131
! If there is ambiguity about what was typed, but it definitely wasn't
3132
! animate as required, then return anything; higher up in the parser
3133
! a suitable error will be given. (This prevents a question being asked.)
3135
if (context == CREATURE_TOKEN && good_ones == 0) return match_list-->0;
3137
if (indef_mode == 0) indef_type=0;
3139
ScoreMatchL(context);
3140
if (number_matched == 0) return -1;
3142
if (indef_mode == 0) {
3143
! Is there now a single highest-scoring object?
3144
i = SingleBestGuess();
3148
if (parser_trace >= 4) print " Single best-scoring object returned.]^";
3154
if (indef_mode == 1 && indef_type & PLURAL_BIT ~= 0) {
3155
if (context ~= MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN
3156
or MULTIINSIDE_TOKEN) {
3160
i = 0; offset = multiple_object-->0; sovert = -1;
3161
for (j=BestGuess() : j~=-1 && i<indef_wanted && i+offset<63 : j=BestGuess()) {
3163
if (j hasnt concealed && j hasnt worn) flag = 1;
3164
if (sovert == -1) sovert = bestguess_score/SCORE__DIVISOR;
3166
if (indef_wanted == 100 && bestguess_score/SCORE__DIVISOR < sovert)
3169
if (context == MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN && parent(j) ~= actor)
3171
if (action_to_be == ##Take or ##Remove && parent(j) == actor)
3173
k = ChooseObjects(j, flag);
3177
if (k == 2) flag = 0;
3180
i++; multiple_object-->(i+offset) = j;
3182
if (parser_trace >= 4) print " Accepting it^";
3188
if (parser_trace >= 4) print " Rejecting it^";
3192
if (i < indef_wanted && indef_wanted < 100) {
3193
etype = TOOFEW_PE; multi_wanted = indef_wanted;
3197
multiple_object-->0 = i+offset;
3198
multi_context = context;
3200
if (parser_trace >= 4)
3201
print " Made multiple object of size ", i, "]^";
3206
for (i=0 : i<number_matched : i++) match_classes-->i = 0;
3209
for (i=0 : i<number_matched : i++)
3210
if (match_classes-->i == 0) {
3211
match_classes-->i = n++; flag = 0;
3212
for (j=i+1 : j<number_matched : j++)
3213
if (match_classes-->j == 0 && Identical(match_list-->i, match_list-->j) == 1) {
3215
match_classes-->j = match_classes-->i;
3217
if (flag == 1) match_classes-->i = 1-n;
3219
n--; number_of_classes = n;
3222
if (parser_trace >= 4) {
3223
print " Grouped into ", n, " possibilities by name:^";
3224
for (i=0 : i<number_matched : i++)
3225
if (match_classes-->i > 0)
3226
print " ", (The) match_list-->i, " (", match_list-->i, ") --- group ",
3227
match_classes-->i, "^";
3231
if (indef_mode == 0) {
3234
for (i=0 : i<number_matched : i++) {
3235
if (match_scores-->i > k) {
3236
k = match_scores-->i;
3237
j = match_classes-->i; j = j*j;
3241
if (match_scores-->i == k) {
3242
if ((match_classes-->i) * (match_classes-->i) ~= j)
3249
if (parser_trace >= 4) print " Unable to choose best group, so ask player.]^";
3254
if (parser_trace >= 4) print " Best choices are all from the same group.^";
3259
! When the player is really vague, or there's a single collection of
3260
! indistinguishable objects to choose from, choose the one the player
3261
! most recently acquired, or if the player has none of them, then
3262
! the one most recently put where it is.
3264
if (n == 1) dont_infer = true;
3269
! ----------------------------------------------------------------------------
3270
! ReviseMulti revises the multiple object which already exists, in the
3271
! light of information which has come along since then (i.e., the second
3272
! parameter). It returns a parser error number, or else 0 if all is well.
3273
! This only ever throws things out, never adds new ones.
3274
! ----------------------------------------------------------------------------
3276
[ ReviseMulti second_p i low;
3278
if (parser_trace >= 4) print " Revising multiple object list of size ", multiple_object-->0,
3279
" with 2nd ", (name) second_p, "^";
3282
if (multi_context == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) {
3283
for (i=1,low=0 : i<=multiple_object-->0 : i++) {
3284
if ( (multi_context==MULTIEXCEPT_TOKEN && multiple_object-->i ~= second_p) ||
3285
(multi_context==MULTIINSIDE_TOKEN && multiple_object-->i in second_p)) {
3287
multiple_object-->low = multiple_object-->i;
3290
multiple_object-->0 = low;
3293
if (multi_context == MULTI_TOKEN && action_to_be == ##Take) {
3294
for (i=1,low=0 : i<=multiple_object-->0 : i++)
3295
if (ScopeCeiling(multiple_object-->i)==ScopeCeiling(actor)) low++;
3297
if (parser_trace >= 4) print " Token 2 plural case: number with actor ", low, "^";
3299
if (take_all_rule == 2 || low > 0) {
3300
for (i=1,low=0 : i<=multiple_object-->0 : i++) {
3301
if (ScopeCeiling(multiple_object-->i) == ScopeCeiling(actor)) {
3303
multiple_object-->low = multiple_object-->i;
3306
multiple_object-->0 = low;
3310
i = multiple_object-->0;
3312
if (parser_trace >= 4) print " Done: new size ", i, "^";
3314
if (i == 0) return NOTHING_PE;
3318
! ----------------------------------------------------------------------------
3319
! ScoreMatchL scores the match list for quality in terms of what the
3320
! player has vaguely asked for. Points are awarded for conforming with
3321
! requirements like "my", and so on. Remove from the match list any
3322
! entries which fail the basic requirements of the descriptors.
3323
! ----------------------------------------------------------------------------
3325
[ ScoreMatchL context its_owner its_score obj i j threshold met a_s l_s;
3326
! if (indef_type & OTHER_BIT ~= 0) threshold++;
3327
if (indef_type & MY_BIT ~= 0) threshold++;
3328
if (indef_type & THAT_BIT ~= 0) threshold++;
3329
if (indef_type & LIT_BIT ~= 0) threshold++;
3330
if (indef_type & UNLIT_BIT ~= 0) threshold++;
3331
if (indef_owner ~= nothing) threshold++;
3334
if (parser_trace >= 4) print " Scoring match list: indef mode ", indef_mode, " type ",
3335
indef_type, ", satisfying ", threshold, " requirements:^";
3338
a_s = SCORE__NEXTBESTLOC; l_s = SCORE__BESTLOC;
3339
if (context == HELD_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN) {
3340
a_s = SCORE__BESTLOC; l_s = SCORE__NEXTBESTLOC;
3343
for (i=0 : i<number_matched : i++) {
3344
obj = match_list-->i; its_owner = parent(obj); its_score=0; met=0;
3346
! if (indef_type & OTHER_BIT ~= 0
3347
! && obj ~= itobj or himobj or herobj) met++;
3348
if (indef_type & MY_BIT ~= 0 && its_owner == actor) met++;
3349
if (indef_type & THAT_BIT ~= 0 && its_owner == actors_location) met++;
3350
if (indef_type & LIT_BIT ~= 0 && obj has light) met++;
3351
if (indef_type & UNLIT_BIT ~= 0 && obj hasnt light) met++;
3352
if (indef_owner ~= 0 && its_owner == indef_owner) met++;
3354
if (met < threshold) {
3356
if (parser_trace >= 4) print " ", (The) match_list-->i, " (", match_list-->i, ") in ",
3357
(the) its_owner, " is rejected (doesn't match descriptors)^";
3359
match_list-->i = -1;
3363
if (obj hasnt concealed) its_score = SCORE__UNCONCEALED;
3365
if (its_owner == actor) its_score = its_score + a_s;
3367
if (its_owner == actors_location) its_score = its_score + l_s;
3369
if (its_owner ~= compass) its_score = its_score + SCORE__NOTCOMPASS;
3371
its_score = its_score + SCORE__CHOOSEOBJ * ChooseObjects(obj, 2);
3373
if (obj hasnt scenery) its_score = its_score + SCORE__NOTSCENERY;
3374
if (obj ~= actor) its_score = its_score + SCORE__NOTACTOR;
3376
! A small bonus for having the correct GNA,
3377
! for sorting out ambiguous articles and the like.
3379
if (indef_cases & (PowersOfTwo_TB-->(GetGNAOfObject(obj))))
3380
its_score = its_score + SCORE__GNA;
3382
match_scores-->i = match_scores-->i + its_score;
3384
if (parser_trace >= 4) print " ", (The) match_list-->i, " (", match_list-->i,
3385
") in ", (the) its_owner, " : ", match_scores-->i, " points^";
3390
for (i=0 : i<number_matched : i++) {
3391
while (match_list-->i == -1) {
3392
if (i == number_matched-1) { number_matched--; break; }
3393
for (j=i : j<number_matched : j++) {
3394
match_list-->j = match_list-->(j+1);
3395
match_scores-->j = match_scores-->(j+1);
3402
! ----------------------------------------------------------------------------
3403
! BestGuess makes the best guess it can out of the match list, assuming that
3404
! everything in the match list is textually as good as everything else;
3405
! however it ignores items marked as -1, and so marks anything it chooses.
3406
! It returns -1 if there are no possible choices.
3407
! ----------------------------------------------------------------------------
3409
[ BestGuess earliest its_score best i;
3410
earliest = 0; best = -1;
3411
for (i=0 : i<number_matched : i++) {
3412
if (match_list-->i >= 0) {
3413
its_score = match_scores-->i;
3414
if (its_score > best) { best = its_score; earliest = i; }
3418
if (parser_trace >= 4)
3419
if (best < 0) print " Best guess ran out of choices^";
3420
else print " Best guess ", (the) match_list-->earliest, " (", match_list-->earliest, ")^";
3422
if (best < 0) return -1;
3423
i = match_list-->earliest;
3424
match_list-->earliest = -1;
3425
bestguess_score = best;
3429
! ----------------------------------------------------------------------------
3430
! SingleBestGuess returns the highest-scoring object in the match list
3431
! if it is the clear winner, or returns -1 if there is no clear winner
3432
! ----------------------------------------------------------------------------
3434
[ SingleBestGuess earliest its_score best i;
3435
earliest = -1; best = -1000;
3436
for (i=0 : i<number_matched : i++) {
3437
its_score = match_scores-->i;
3438
if (its_score == best) earliest = -1;
3439
if (its_score > best) { best = its_score; earliest = match_list-->i; }
3441
bestguess_score = best;
3445
! ----------------------------------------------------------------------------
3446
! Identical decides whether or not two objects can be distinguished from
3447
! each other by anything the player can type. If not, it returns true.
3448
! ----------------------------------------------------------------------------
3450
[ Identical o1 o2 p1 p2 n1 n2 i j flag;
3451
if (o1 == o2) rtrue; ! This should never happen, but to be on the safe side
3452
if (o1 == 0 || o2 == 0) rfalse; ! Similarly
3453
if (parent(o1) == compass || parent(o2) == compass) rfalse; ! Saves time
3455
! What complicates things is that o1 or o2 might have a parsing routine,
3456
! so the parser can't know from here whether they are or aren't the same.
3457
! If they have different parsing routines, we simply assume they're
3458
! different. If they have the same routine (which they probably got from
3459
! a class definition) then the decision process is as follows:
3461
! the routine is called (with self being o1, not that it matters)
3462
! with noun and second being set to o1 and o2, and action being set
3463
! to the fake action TheSame. If it returns -1, they are found
3464
! identical; if -2, different; and if >=0, then the usual method
3467
if (o1.parse_name ~= 0 || o2.parse_name ~= 0) {
3468
if (o1.parse_name ~= o2.parse_name) rfalse;
3469
parser_action = ##TheSame; parser_one = o1; parser_two = o2;
3470
j = wn; i = RunRoutines(o1,parse_name); wn = j;
3472
if (i == -2) rfalse;
3475
! This is the default algorithm: do they have the same words in their
3476
! "name" (i.e. property no. 1) properties. (Note that the following allows
3477
! for repeated words and words in different orders.)
3479
p1 = o1.&1; n1 = (o1.#1)/WORDSIZE;
3480
p2 = o2.&1; n2 = (o2.#1)/WORDSIZE;
3482
! for (i=0 : i<n1 : i++) { print (address) p1-->i, " "; } new_line;
3483
! for (i=0 : i<n2 : i++) { print (address) p2-->i, " "; } new_line;
3485
for (i=0 : i<n1 : i++) {
3487
for (j=0 : j<n2 : j++)
3488
if (p1-->i == p2-->j) flag = 1;
3489
if (flag == 0) rfalse;
3492
for (j=0 : j<n2 : j++) {
3494
for (i=0 : i<n1 : i++)
3495
if (p1-->i == p2-->j) flag = 1;
3496
if (flag == 0) rfalse;
3499
! print "Which are identical!^";
3503
! ----------------------------------------------------------------------------
3504
! PrintCommand reconstructs the command as it presently reads, from
3505
! the pattern which has been built up
3507
! If from is 0, it starts with the verb: then it goes through the pattern.
3508
! The other parameter is "emptyf" - a flag: if 0, it goes up to pcount:
3509
! if 1, it goes up to pcount-1.
3511
! Note that verbs and prepositions are printed out of the dictionary:
3512
! and that since the dictionary may only preserve the first six characters
3513
! of a word (in a V3 game), we have to hand-code the longer words needed.
3515
! (Recall that pattern entries are 0 for "multiple object", 1 for "special
3516
! word", 2 to REPARSE_CODE-1 are object numbers and REPARSE_CODE+n means the
3518
! ----------------------------------------------------------------------------
3520
[ PrintCommand from i k spacing_flag;
3523
if (LanguageVerb(i) == 0)
3524
if (PrintVerb(i) == 0) print (address) i;
3525
from++; spacing_flag = true;
3528
for (k=from : k<pcount : k++) {
3530
if (i == PATTERN_NULL) continue;
3531
if (spacing_flag) print (char) ' ';
3532
if (i ==0 ) { print (string) THOSET__TX; jump TokenPrinted; }
3533
if (i == 1) { print (string) THAT__TX; jump TokenPrinted; }
3534
if (i >= REPARSE_CODE)
3535
print (address) No__Dword(i-REPARSE_CODE);
3537
if (i in compass && LanguageVerbLikesAdverb(verb_word))
3538
LanguageDirection (i.door_dir); ! the direction name as adverb
3542
spacing_flag = true;
3546
! ----------------------------------------------------------------------------
3547
! The CantSee routine returns a good error number for the situation where
3548
! the last word looked at didn't seem to refer to any object in context.
3550
! The idea is that: if the actor is in a location (but not inside something
3551
! like, for instance, a tank which is in that location) then an attempt to
3552
! refer to one of the words listed as meaningful-but-irrelevant there
3553
! will cause "you don't need to refer to that in this game" rather than
3554
! "no such thing" or "what's 'it'?".
3555
! (The advantage of not having looked at "irrelevant" local nouns until now
3556
! is that it stops them from clogging up the ambiguity-resolving process.
3557
! Thus game objects always triumph over scenery.)
3558
! ----------------------------------------------------------------------------
3561
saved_oops=oops_from;
3563
if (scope_token ~= 0) {
3564
scope_error = scope_token;
3568
wn--; w = NextWord();
3570
if (w == pronoun_word) {
3571
pronoun__word = pronoun_word; pronoun__obj = pronoun_obj;
3574
i = actor; while (parent(i) ~= 0) i = parent(i);
3577
if (i has visited && Refers(i,wn) == 1) e = SCENERY_PE;
3579
Descriptors(); ! skip past THE etc
3580
if (i has visited && Refers(i,wn) == 1) e = SCENERY_PE;
3583
if (etype > e) return etype;
3587
! ----------------------------------------------------------------------------
3588
! The MultiAdd routine adds object "o" to the multiple-object-list.
3590
! This is only allowed to hold 63 objects at most, at which point it ignores
3591
! any new entries (and sets a global flag so that a warning may later be
3592
! printed if need be).
3593
! ----------------------------------------------------------------------------
3596
i = multiple_object-->0;
3597
if (i == 63) { toomany_flag = 1; rtrue; }
3598
for (j=1 : j<=i : j++)
3599
if (o == multiple_object-->j) rtrue;
3601
multiple_object-->i = o;
3602
multiple_object-->0 = i;
3605
! ----------------------------------------------------------------------------
3606
! The MultiSub routine deletes object "o" from the multiple-object-list.
3608
! It returns 0 if the object was there in the first place, and 9 (because
3609
! this is the appropriate error number in Parser()) if it wasn't.
3610
! ----------------------------------------------------------------------------
3612
[ MultiSub o i j k et;
3613
i = multiple_object-->0; et = 0;
3614
for (j=1 : j<=i : j++)
3615
if (o == multiple_object-->j) {
3616
for (k=j : k<=i : k++)
3617
multiple_object-->k = multiple_object-->(k+1);
3618
multiple_object-->0 = --i;
3624
! ----------------------------------------------------------------------------
3625
! The MultiFilter routine goes through the multiple-object-list and throws
3626
! out anything without the given attribute "attr" set.
3627
! ----------------------------------------------------------------------------
3629
[ MultiFilter attr i j o;
3633
i = multiple_object-->0;
3634
for (j=1 : j<=i : j++) {
3635
o = multiple_object-->j;
3643
! ----------------------------------------------------------------------------
3644
! The UserFilter routine consults the user's filter (or checks on attribute)
3645
! to see what already-accepted nouns are acceptable
3646
! ----------------------------------------------------------------------------
3649
if (token_filter > 0 && token_filter < 49) {
3650
if (obj has (token_filter-1)) rtrue;
3654
return indirect(token_filter);
3657
! ----------------------------------------------------------------------------
3658
! MoveWord copies word at2 from parse buffer b2 to word at1 in "parse"
3659
! (the main parse buffer)
3660
! ----------------------------------------------------------------------------
3662
#Ifdef TARGET_ZCODE;
3664
[ MoveWord at1 b2 at2 x y;
3665
x = at1*2-1; y = at2*2-1;
3666
parse-->x++ = b2-->y++;
3670
#Ifnot; ! TARGET_GLULX
3672
[ MoveWord at1 b2 at2 x y;
3673
x = at1*3-2; y = at2*3-2;
3674
parse-->x++ = b2-->y++;
3675
parse-->x++ = b2-->y++;
3681
! ----------------------------------------------------------------------------
3682
! SearchScope domain1 domain2 context
3684
! Works out what objects are in scope (possibly asking an outside routine),
3685
! but does not look at anything the player has typed.
3686
! ----------------------------------------------------------------------------
3688
[ SearchScope domain1 domain2 context i;
3690
! Everything is in scope to the debugging commands
3693
if (scope_reason == PARSING_REASON
3694
&& LanguageVerbIsDebugging(verb_word)) {
3696
#Ifdef TARGET_ZCODE;
3697
for (i=selfobj : i<=top_object : i++)
3698
if (i ofclass Object && (parent(i) == 0 || parent(i) ofclass Object))
3700
#Ifnot; ! TARGET_GLULX
3702
if (i ofclass Object && (parent(i) == 0 || parent(i) ofclass Object))
3709
! First, a scope token gets priority here:
3711
if (scope_token ~= 0) {
3713
if (indirect(scope_token) ~= 0) rtrue;
3716
! Next, call any user-supplied routine adding things to the scope,
3717
! which may circumvent the usual routines altogether if they return true:
3719
if (actor == domain1 or domain2 && InScope(actor) ~= 0) rtrue;
3721
! Pick up everything in the location except the actor's possessions;
3722
! then go through those. (This ensures the actor's possessions are in
3723
! scope even in Darkness.)
3725
if (context == MULTIINSIDE_TOKEN && advance_warning ~= -1) {
3726
if (IsSeeThrough(advance_warning) == 1)
3727
ScopeWithin(advance_warning, 0, context);
3730
if (domain1 ~= 0 && domain1 has supporter or container)
3731
ScopeWithin_O(domain1, domain1, context);
3732
ScopeWithin(domain1, domain2, context);
3733
if (domain2 ~= 0 && domain2 has supporter or container)
3734
ScopeWithin_O(domain2, domain2, context);
3735
ScopeWithin(domain2, 0, context);
3738
! A special rule applies:
3739
! in Darkness as in light, the actor is always in scope to himself.
3741
if (thedark == domain1 or domain2) {
3742
ScopeWithin_O(actor, actor, context);
3743
if (parent(actor) has supporter or container)
3744
ScopeWithin_O(parent(actor), parent(actor), context);
3748
! ----------------------------------------------------------------------------
3749
! IsSeeThrough is used at various places: roughly speaking, it determines
3750
! whether o being in scope means that the contents of o are in scope.
3751
! ----------------------------------------------------------------------------
3754
if (o has supporter ||
3755
(o has transparent) ||
3756
(o has container && o has open))
3761
! ----------------------------------------------------------------------------
3762
! PlaceInScope is provided for routines outside the library, and is not
3763
! called within the parser (except for debugging purposes).
3764
! ----------------------------------------------------------------------------
3766
[ PlaceInScope thing;
3767
if (scope_reason~=PARSING_REASON or TALKING_REASON) {
3768
DoScopeAction(thing); rtrue;
3770
wn = match_from; TryGivenObject(thing); placed_in_flag = 1;
3773
! ----------------------------------------------------------------------------
3775
! ----------------------------------------------------------------------------
3777
[ DoScopeAction thing s p1;
3778
s = scope_reason; p1 = parser_one;
3780
if (parser_trace >= 6)
3781
print "[DSA on ", (the) thing, " with reason = ", scope_reason,
3782
" p1 = ", parser_one, " p2 = ", parser_two, "]^";
3784
switch (scope_reason) {
3785
REACT_BEFORE_REASON:
3786
if (thing.react_before == 0 or NULL) return;
3788
if (parser_trace >= 2)
3789
print "[Considering react_before for ", (the) thing, "]^";
3791
if (parser_one == 0) parser_one = RunRoutines(thing, react_before);
3793
if (thing.react_after == 0 or NULL) return;
3795
if (parser_trace >= 2)
3796
print "[Considering react_after for ", (the) thing, "]^";
3798
if (parser_one == 0) parser_one = RunRoutines(thing, react_after);
3800
if (thing.each_turn == 0 or NULL) return;
3802
if (parser_trace >= 2)
3803
print "[Considering each_turn for ", (the) thing, "]^";
3805
PrintOrRun(thing, each_turn);
3807
if (thing == parser_one) parser_two = 1;
3808
LOOPOVERSCOPE_REASON:
3809
indirect(parser_one, thing);
3815
! ----------------------------------------------------------------------------
3816
! ScopeWithin looks for objects in the domain which make textual sense
3817
! and puts them in the match list. (However, it does not recurse through
3818
! the second argument.)
3819
! ----------------------------------------------------------------------------
3821
[ ScopeWithin domain nosearch context x y;
3822
if (domain == 0) rtrue;
3824
! Special rule: the directions (interpreted as the 12 walls of a room) are
3825
! always in context. (So, e.g., "examine north wall" is always legal.)
3826
! (Unless we're parsing something like "all", because it would just slow
3827
! things down then, or unless the context is "creature".)
3829
if (indef_mode==0 && domain==actors_location
3830
&& scope_reason==PARSING_REASON && context~=CREATURE_TOKEN)
3831
ScopeWithin(compass);
3833
! Look through the objects in the domain, avoiding "objectloop" in case
3834
! movements occur, e.g. when trying each_turn.
3839
ScopeWithin_O(x, nosearch, context);
3844
[ ScopeWithin_O domain nosearch context i ad n;
3846
! If the scope reason is unusual, don't parse.
3848
if (scope_reason ~= PARSING_REASON or TALKING_REASON) {
3849
DoScopeAction(domain);
3853
! "it" or "them" matches to the it-object only. (Note that (1) this means
3854
! that "it" will only be understood if the object in question is still
3855
! in context, and (2) only one match can ever be made in this case.)
3857
if (match_from <= num_words) { ! If there's any text to match, that is
3860
if (i == 1 && player == domain) MakeMatch(domain, 1);
3861
if (i >= 2 && i < 128 && (LanguagePronouns-->i == domain)) MakeMatch(domain, 1);
3864
! Construing the current word as the start of a noun, can it refer to the
3868
if (TryGivenObject(domain) > 0)
3869
if (indef_nspec_at > 0 && match_from ~= indef_nspec_at) {
3870
! This case arises if the player has typed a number in
3871
! which is hypothetically an indefinite descriptor:
3872
! e.g. "take two clubs". We have just checked the object
3873
! against the word "clubs", in the hope of eventually finding
3874
! two such objects. But we also backtrack and check it
3875
! against the words "two clubs", in case it turns out to
3876
! be the 2 of Clubs from a pack of cards, say. If it does
3877
! match against "two clubs", we tear up our original
3878
! assumption about the meaning of "two" and lapse back into
3881
wn = indef_nspec_at;
3882
if (TryGivenObject(domain) > 0) {
3883
match_from = indef_nspec_at;
3891
! Shall we consider the possessions of the current object, as well?
3892
! Only if it's a container (so, for instance, if a dwarf carries a
3893
! sword, then "drop sword" will not be accepted, but "dwarf, drop sword"
3895
! Also, only if there are such possessions.
3897
! Notice that the parser can see "into" anything flagged as
3898
! transparent - such as a dwarf whose sword you can get at.
3900
if (child(domain) ~= 0 && domain ~= nosearch && IsSeeThrough(domain) == 1)
3901
ScopeWithin(domain,nosearch,context);
3903
! Drag any extras into context
3905
ad = domain.&add_to_scope;
3908
! Test if the property value is not an object.
3909
#Ifdef TARGET_ZCODE;
3910
i = (UnsignedCompare(ad-->0, top_object) > 0);
3911
#Ifnot; ! TARGET_GLULX
3912
i = (((ad-->0)->0) ~= $70);
3916
ats_flag = 2+context;
3917
RunRoutines(domain, add_to_scope);
3921
n = domain.#add_to_scope;
3922
for (i=0 : (WORDSIZE*i)<n : i++)
3924
ScopeWithin_O(ad-->i, 0, context);
3931
ScopeWithin_O(obj, 0, ats_flag-2);
3932
if (ats_flag == 1) {
3933
if (HasLightSource(obj)==1) ats_hls = 1;
3937
! ----------------------------------------------------------------------------
3938
! MakeMatch looks at how good a match is. If it's the best so far, then
3939
! wipe out all the previous matches and start a new list with this one.
3940
! If it's only as good as the best so far, add it to the list.
3941
! If it's worse, ignore it altogether.
3943
! The idea is that "red panic button" is better than "red button" or "panic".
3945
! number_matched (the number of words matched) is set to the current level
3948
! We never match anything twice, and keep at most 64 equally good items.
3949
! ----------------------------------------------------------------------------
3951
[ MakeMatch obj quality i;
3953
if (parser_trace >= 6) print " Match with quality ",quality,"^";
3955
if (token_filter ~= 0 && UserFilter(obj) == 0) {
3957
if (parser_trace >= 6) print " Match filtered out: token filter ", token_filter, "^";
3961
if (quality < match_length) rtrue;
3962
if (quality > match_length) { match_length = quality; number_matched = 0; }
3964
if (2*number_matched >= MATCH_LIST_SIZE) rtrue;
3965
for (i=0 : i<number_matched : i++)
3966
if (match_list-->i == obj) rtrue;
3968
match_list-->number_matched++ = obj;
3970
if (parser_trace >= 6) print " Match added to list^";
3974
! ----------------------------------------------------------------------------
3975
! TryGivenObject tries to match as many words as possible in what has been
3976
! typed to the given object, obj. If it manages any words matched at all,
3977
! it calls MakeMatch to say so, then returns the number of words (or 1
3978
! if it was a match because of inadequate input).
3979
! ----------------------------------------------------------------------------
3981
[ TryGivenObject obj threshold k w j;
3983
if (parser_trace >= 5) print " Trying ", (the) obj, " (", obj, ") at word ", wn, "^";
3986
dict_flags_of_noun = 0;
3988
! If input has run out then always match, with only quality 0 (this saves
3991
if (wn > num_words) {
3992
if (indef_mode ~= 0)
3993
dict_flags_of_noun = $$01110000; ! Reject "plural" bit
3996
if (parser_trace >= 5) print " Matched (0)^";
4001
! Ask the object to parse itself if necessary, sitting up and taking notice
4002
! if it says the plural was used:
4004
if (obj.parse_name~=0) {
4005
parser_action = NULL; j=wn;
4006
k = RunRoutines(obj,parse_name);
4012
if (parser_action == ##PluralFound)
4013
dict_flags_of_noun = dict_flags_of_noun | 4;
4015
if (dict_flags_of_noun & 4) {
4016
if (~~allow_plurals) k = 0;
4018
if (indef_mode == 0) {
4019
indef_mode = 1; indef_type = 0; indef_wanted = 0;
4021
indef_type = indef_type | PLURAL_BIT;
4022
if (indef_wanted == 0) indef_wanted = 100;
4027
if (parser_trace >= 5) print " Matched (", k, ")^";
4032
if (k == 0) jump NoWordsMatch;
4035
! The default algorithm is simply to count up how many words pass the
4038
parser_action = NULL;
4042
if (w == 1 && player == obj) { k=1; jump MMbyPN; }
4044
if (w >= 2 && w < 128 && (LanguagePronouns-->w == obj)) { k = 1; jump MMbyPN; }
4047
threshold = ParseNoun(obj);
4049
if (threshold >= 0 && parser_trace >= 5) print " ParseNoun returned ", threshold, "^";
4051
if (threshold < 0) wn++;
4052
if (threshold > 0) { k = threshold; jump MMbyPN; }
4054
if (threshold == 0 || Refers(obj,wn-1) == 0) {
4056
if (indef_mode ~= 0) {
4057
k = 0; parser_action = NULL;
4063
if (threshold < 0) {
4065
dict_flags_of_noun = (w->#dict_par1) & $$01110100;
4067
while (Refers(obj, wn-1)) {
4070
dict_flags_of_noun = dict_flags_of_noun | ((w->#dict_par1) & $$01110100);
4079
! ----------------------------------------------------------------------------
4080
! Refers works out whether the word at number wnum can refer to the object
4081
! obj, returning true or false. The standard method is to see if the
4082
! word is listed under "name" for the object, but this is more complex
4083
! in languages other than English.
4084
! ----------------------------------------------------------------------------
4086
[ Refers obj wnum wd k l m;
4087
if (obj == 0) rfalse;
4089
#Ifdef LanguageRefers;
4090
k = LanguageRefers(obj,wnum); if (k >= 0) return k;
4091
#Endif; ! LanguageRefers
4093
k = wn; wn = wnum; wd = NextWordStopped(); wn = k;
4095
if (parser_inflection >= 256) {
4096
k = indirect(parser_inflection, obj, wd);
4097
if (k >= 0) return k;
4101
m = parser_inflection;
4102
k = obj.&m; l = (obj.#m)/WORDSIZE-1;
4103
for (m=0 : m<=l : m++)
4104
if (wd == k-->m) rtrue;
4108
[ WordInProperty wd obj prop k l m;
4109
k = obj.∝ l = (obj.#prop)/WORDSIZE-1;
4110
for (m=0 : m<=l : m++)
4111
if (wd == k-->m) rtrue;
4115
#Ifdef TARGET_ZCODE;
4117
[ DictionaryLookup b l i;
4118
for (i=0 : i<l : i++) buffer2->(2+i) = b->i;
4120
Tokenise__(buffer2,parse2);
4124
#Ifnot; ! TARGET_GLULX
4126
[ DictionaryLookup b l i;
4127
for (i=0 : i<l : i++) buffer2->(WORDSIZE+i) = b->i;
4129
Tokenise__(buffer2,parse2);
4135
! ----------------------------------------------------------------------------
4136
! NounWord (which takes no arguments) returns:
4138
! 0 if the next word is unrecognised or does not carry the "noun" bit in
4139
! its dictionary entry,
4140
! 1 if a word meaning "me",
4141
! the index in the pronoun table (plus 2) of the value field of a pronoun,
4142
! if the word is a pronoun,
4143
! the address in the dictionary if it is a recognised noun.
4145
! The "current word" marker moves on one.
4146
! ----------------------------------------------------------------------------
4151
if (i == ME1__WD or ME2__WD or ME3__WD) return 1;
4152
s = LanguagePronouns-->0;
4153
for (j=1 : j<=s : j=j+3)
4154
if (i == LanguagePronouns-->j)
4156
if ((i->#dict_par1)&128 == 0) rfalse;
4160
! ----------------------------------------------------------------------------
4161
! NextWord (which takes no arguments) returns:
4163
! 0 if the next word is unrecognised,
4164
! comma_word if a comma
4165
! THEN1__WD if a full stop
4166
! or the dictionary address if it is recognised.
4167
! The "current word" marker is moved on.
4169
! NextWordStopped does the same, but returns -1 when input has run out
4170
! ----------------------------------------------------------------------------
4172
#Ifdef TARGET_ZCODE;
4175
if (wn > parse->1) { wn++; rfalse; }
4178
if (j == ',//') j = comma_word;
4179
if (j == './/') j = THEN1__WD;
4184
if (wn > parse->1) { wn++; return -1; }
4188
[ WordAddress wordnum; return buffer + parse->(wordnum*4+1); ];
4190
[ WordLength wordnum; return parse->(wordnum*4); ];
4192
#Ifnot; ! TARGET_GLULX
4195
if (wn > parse-->0) { wn++; rfalse; }
4198
if (j == ',//') j=comma_word;
4199
if (j == './/') j=THEN1__WD;
4204
if (wn > parse-->0) {
4211
[ WordAddress wordnum; return buffer + parse-->(wordnum*3); ];
4213
[ WordLength wordnum; return parse-->(wordnum*3-1); ];
4217
! ----------------------------------------------------------------------------
4218
! TryNumber is the only routine which really does any character-level
4219
! parsing, since that's normally left to the Z-machine.
4220
! It takes word number "wordnum" and tries to parse it as an (unsigned)
4221
! decimal number, returning
4223
! -1000 if it is not a number
4224
! the number if it has between 1 and 4 digits
4225
! 10000 if it has 5 or more digits.
4227
! (The danger of allowing 5 digits is that Z-machine integers are only
4228
! 16 bits long, and anyway this isn't meant to be perfect.)
4230
! Using NumberWord, it also catches "one" up to "twenty".
4232
! Note that a game can provide a ParseNumber routine which takes priority,
4233
! to enable parsing of odder numbers ("x45y12", say).
4234
! ----------------------------------------------------------------------------
4236
[ TryNumber wordnum i j c num len mul tot d digit;
4237
i = wn; wn = wordnum; j = NextWord(); wn = i;
4239
if (j >= 1) return j;
4241
#Ifdef TARGET_ZCODE;
4242
i = wordnum*4+1; j = parse->i; num = j+buffer; len = parse->(i-1);
4243
#Ifnot; ! TARGET_GLULX
4244
i = wordnum*3; j = parse-->i; num = j+buffer; len = parse-->(i-1);
4247
tot=ParseNumber(num, len);
4248
if (tot ~= 0) return tot;
4250
if (len >= 4) mul=1000;
4251
if (len == 3) mul=100;
4252
if (len == 2) mul=10;
4253
if (len == 1) mul=1;
4255
tot = 0; c = 0; len = len-1;
4257
for (c=0 : c<=len : c++) {
4259
if (digit == '0') { d = 0; jump digok; }
4260
if (digit == '1') { d = 1; jump digok; }
4261
if (digit == '2') { d = 2; jump digok; }
4262
if (digit == '3') { d = 3; jump digok; }
4263
if (digit == '4') { d = 4; jump digok; }
4264
if (digit == '5') { d = 5; jump digok; }
4265
if (digit == '6') { d = 6; jump digok; }
4266
if (digit == '7') { d = 7; jump digok; }
4267
if (digit == '8') { d = 8; jump digok; }
4268
if (digit == '9') { d = 9; jump digok; }
4271
tot = tot+mul*d; mul = mul/10;
4273
if (len > 3) tot=10000;
4277
! ----------------------------------------------------------------------------
4278
! GetGender returns 0 if the given animate object is female, and 1 if male
4279
! (not all games will want such a simple decision function!)
4280
! ----------------------------------------------------------------------------
4283
if (person hasnt female) rtrue;
4287
[ GetGNAOfObject obj case gender;
4288
if (obj hasnt animate) case = 6;
4289
if (obj has male) gender = male;
4290
if (obj has female) gender = female;
4291
if (obj has neuter) gender = neuter;
4293
if (case == 0) gender = LanguageAnimateGender;
4294
else gender = LanguageInanimateGender;
4296
if (gender == female) case = case + 1;
4297
if (gender == neuter) case = case + 2;
4298
if (obj has pluralname) case = case + 3;
4302
! ----------------------------------------------------------------------------
4303
! Converting between dictionary addresses and entry numbers
4304
! ----------------------------------------------------------------------------
4306
#Ifdef TARGET_ZCODE;
4308
[ Dword__No w; return (w-(HDR_DICTIONARY-->0 + 7))/9; ];
4309
[ No__Dword n; return HDR_DICTIONARY-->0 + 7 + 9*n; ];
4311
#Ifnot; ! TARGET_GLULX
4313
! In Glulx, dictionary entries *are* addresses.
4314
[ Dword__No w; return w; ];
4315
[ No__Dword n; return n; ];
4319
! ----------------------------------------------------------------------------
4320
! For copying buffers
4321
! ----------------------------------------------------------------------------
4323
#Ifdef TARGET_ZCODE;
4325
[ CopyBuffer bto bfrom i size;
4327
for (i=1 : i<=size : i++) bto->i = bfrom->i;
4330
#Ifnot; ! TARGET_GLULX
4332
[ CopyBuffer bto bfrom i;
4333
for (i=0 : i<INPUT_BUFFER_LEN : i++) bto->i = bfrom->i;
4338
! ----------------------------------------------------------------------------
4339
! Provided for use by language definition files
4340
! ----------------------------------------------------------------------------
4342
#Ifdef TARGET_ZCODE;
4344
[ LTI_Insert i ch b y;
4346
! Protect us from strict mode, as this isn't an array in quite the
4350
! Insert character ch into buffer at point i.
4351
! Being careful not to let the buffer possibly overflow:
4353
if (y > b->0) y = b->0;
4355
! Move the subsequent text along one character:
4356
for (y=y+2 : y>i : y--) b->y = b->(y-1);
4359
! And the text is now one character longer:
4360
if (b->1 < b->0) (b->1)++;
4363
#Ifnot; ! TARGET_GLULX
4365
[ LTI_Insert i ch b y;
4367
! Protect us from strict mode, as this isn't an array in quite the
4371
! Insert character ch into buffer at point i.
4372
! Being careful not to let the buffer possibly overflow:
4374
if (y > INPUT_BUFFER_LEN) y = INPUT_BUFFER_LEN;
4376
! Move the subsequent text along one character:
4377
for (y=y+WORDSIZE : y>i : y--) b->y = b->(y-1);
4380
! And the text is now one character longer:
4381
if (b-->0 < INPUT_BUFFER_LEN) (b-->0)++;
4386
! ============================================================================
4388
[ PronounsSub x y c d;
4389
L__M(##Pronouns, 1);
4391
c = (LanguagePronouns-->0)/3;
4392
if (player ~= selfobj) c++;
4394
if (c == 0) return L__M(##Pronouns, 4);
4396
for (x=1,d=0 : x<=LanguagePronouns-->0 : x=x+3) {
4397
print "~", (address) LanguagePronouns-->x, "~ ";
4398
y = LanguagePronouns-->(x+2);
4399
if (y == NULL) L__M(##Pronouns, 3);
4401
L__M(##Pronouns, 2);
4405
if (d < c-1) print (string) COMMA__TX;
4406
if (d == c-1) print (string) AND__TX;
4408
if (player ~= selfobj) {
4409
print "~", (address) ME1__WD, "~ "; L__M(##Pronouns, 2);
4410
c = player; player = selfobj;
4411
print (the) c; player = c;
4413
L__M(##Pronouns, 5);
4416
[ SetPronoun dword value x;
4417
for (x=1 : x<=LanguagePronouns-->0 : x=x+3)
4418
if (LanguagePronouns-->x == dword) {
4419
LanguagePronouns-->(x+2) = value; return;
4424
[ PronounValue dword x;
4425
for (x=1 : x<=LanguagePronouns-->0 : x=x+3)
4426
if (LanguagePronouns-->x == dword)
4427
return LanguagePronouns-->(x+2);
4431
[ ResetVagueWords obj; PronounNotice(obj); ];
4433
#Ifdef EnglishNaturalLanguage;
4435
[ PronounOldEnglish;
4436
if (itobj ~= old_itobj) SetPronoun('it', itobj);
4437
if (himobj ~= old_himobj) SetPronoun('him', himobj);
4438
if (herobj ~= old_herobj) SetPronoun('her', herobj);
4439
old_itobj = itobj; old_himobj = himobj; old_herobj = herobj;
4442
#Endif; !EnglishNaturalLanguage
4444
[ PronounNotice obj x bm;
4445
if (obj == player) return;
4447
#Ifdef EnglishNaturalLanguage;
4448
PronounOldEnglish();
4449
#Endif; ! EnglishNaturalLanguage
4451
bm = PowersOfTwo_TB-->(GetGNAOfObject(obj));
4453
for (x=1 : x<=LanguagePronouns-->0 : x=x+3)
4454
if (bm & (LanguagePronouns-->(x+1)) ~= 0)
4455
LanguagePronouns-->(x+2) = obj;
4457
#Ifdef EnglishNaturalLanguage;
4458
itobj = PronounValue('it'); old_itobj = itobj;
4459
himobj = PronounValue('him'); old_himobj = himobj;
4460
herobj = PronounValue('her'); old_herobj = herobj;
4461
#Endif; ! EnglishNaturalLanguage
4464
! ============================================================================
4465
! End of the parser proper: the remaining routines are its front end.
4466
! ----------------------------------------------------------------------------
4468
Object InformLibrary "(Inform Library)"
4469
with play [ i j k l;
4471
#Ifdef TARGET_ZCODE;
4472
standard_interpreter = HDR_TERPSTANDARD-->0;
4473
transcript_mode = ((HDR_GAMEFLAGS-->0) & 1);
4474
sys_statusline_flag = ( (HDR_TERPFLAGS->0) & 2 ) / 2;
4475
#Ifnot; ! TARGET_GLULX
4479
ChangeDefault(cant_go, CANTGO__TX);
4481
#Ifdef TARGET_ZCODE;
4482
dict_start = HDR_DICTIONARY-->0;
4483
dict_entry_size = dict_start->(dict_start->0 + 1);
4484
dict_start = dict_start + dict_start->0 + 4;
4485
dict_end = dict_start + (dict_start - 2)-->0 * dict_entry_size;
4487
if (dict_start > 0 && dict_end < 0 &&
4488
((-dict_start) - dict_end) % dict_entry_size == 0)
4489
print "** Warning: grammar properties might not work correctly **^";
4492
buffer->0 = INPUT_BUFFER_LEN;
4493
buffer2->0 = INPUT_BUFFER_LEN;
4494
buffer3->0 = INPUT_BUFFER_LEN;
4497
#Endif; ! TARGET_ZCODE
4499
real_location = thedark;
4500
player = selfobj; actor = player;
4502
#Ifdef TARGET_ZCODE;
4503
top_object = #largest_object-255;
4504
#Endif; ! TARGET_ZCODE
4505
selfobj.capacity = MAX_CARRIED; ! ### change?
4507
#Ifdef LanguageInitialise;
4508
LanguageInitialise();
4509
#Endif; ! LanguageInitialise
4512
LibraryExtensions.RunAll(ext_initialise);
4515
move player to location;
4517
while (parent(location) ~= 0) location = parent(location);
4518
real_location = location;
4520
actor = player; ! resync, because player may have been changed in initialise()
4521
actors_location = location;
4523
objectloop (i in player) give i moved ~concealed;
4525
if (j ~= 2) Banner();
4527
MoveFloatingObjects();
4528
lightflag = OffersLight(parent(player));
4529
if (lightflag == 0) {
4530
real_location = location;
4535
for (i=1 : i<=100 : i++) j = random(i);
4537
#Ifdef EnglishNaturalLanguage;
4538
old_itobj = itobj; old_himobj = himobj; old_herobj = herobj;
4539
#Endif;! EnglishNaturalLanguage
4541
while (~~deadflag) { ! everything happens in this loop
4543
#Ifdef EnglishNaturalLanguage;
4544
PronounOldEnglish();
4545
old_itobj = PronounValue('it');
4546
old_himobj = PronounValue('him');
4547
old_herobj = PronounValue('her');
4548
#Endif; ! EnglishNaturalLanguage
4552
if (score ~= last_score) {
4553
if (notify_mode == 1) NotifyTheScore();
4559
inputobjs-->0 = 0; inputobjs-->1 = 0;
4560
inputobjs-->2 = 0; inputobjs-->3 = 0; meta=false;
4562
! The Parser writes its results into inputobjs and meta,
4563
! a flag indicating a "meta-verb". This can only be set for
4564
! commands by the player, not for orders to others.
4566
InformParser.parse_input(inputobjs);
4568
action = inputobjs-->0;
4570
! --------------------------------------------------------------
4572
! Reverse "give fred biscuit" into "give biscuit to fred"
4574
if (action == ##GiveR or ##ShowR) {
4575
i = inputobjs-->2; inputobjs-->2 = inputobjs-->3; inputobjs-->3 = i;
4576
if (action == ##GiveR) action = ##Give; else action = ##Show;
4579
! Convert "P, tell me about X" to "ask P about X"
4581
if (action == ##Tell && inputobjs-->2 == player && actor ~= player) {
4582
inputobjs-->2 = actor; actor = player; action = ##Ask;
4585
! Convert "ask P for X" to "P, give X to me"
4587
if (action == ##AskFor && inputobjs-->2 ~= player && actor == player) {
4588
actor = inputobjs-->2; inputobjs-->2 = inputobjs-->3;
4589
inputobjs-->3 = player; action = ##Give;
4592
! For old, obsolete code: special_word contains the topic word
4595
if (action == ##Ask or ##Tell or ##Answer)
4596
special_word = special_number1;
4598
! --------------------------------------------------------------
4600
multiflag = false; onotheld_mode = notheld_mode; notheld_mode = false;
4601
! For implicit taking and multiple object detection
4604
inp1 = 0; inp2 = 0; i = inputobjs-->1;
4605
if (i >= 1) inp1 = inputobjs-->2;
4606
if (i >= 2) inp2 = inputobjs-->3;
4608
! inp1 and inp2 hold: object numbers, or 0 for "multiple object",
4609
! or 1 for "a number or dictionary address"
4611
if (inp1 == 1) noun = special_number1; else noun = inp1;
4613
if (inp1 == 1) second = special_number2;
4614
else second = special_number1;
4618
! --------------------------------------------------------------
4620
if (actor ~= player) {
4622
! The player's "orders" property can refuse to allow conversation
4623
! here, by returning true. If not, the order is sent to the
4624
! other person's "orders" property. If that also returns false,
4625
! then: if it was a misunderstood command anyway, it is converted
4626
! to an Answer action (thus "floyd, grrr" ends up as
4627
! "say grrr to floyd"). If it was a good command, it is finally
4628
! offered to the Order: part of the other person's "life"
4629
! property, the old-fashioned way of dealing with conversation.
4631
j = RunRoutines(player, orders);
4633
j = RunRoutines(actor, orders);
4635
if (action == ##NotUnderstood) {
4636
inputobjs-->3 = actor; actor = player; action = ##Answer;
4639
if (RunLife(actor, ##Order) == 0) L__M(##Order, 1, actor);
4645
! --------------------------------------------------------------
4646
! Generate the action...
4649
(i == 1 && inp1 ~= 0) ||
4650
(i == 2 && inp1 ~= 0 && inp2 ~= 0)) {
4651
self.begin_action(action, noun, second, 0);
4655
! ...unless a multiple object must be substituted. First:
4656
! (a) check the multiple list isn't empty;
4657
! (b) warn the player if it has been cut short because too long;
4658
! (c) generate a sequence of actions from the list
4659
! (stopping in the event of death or movement away).
4662
j = multiple_object-->0;
4664
L__M(##Miscellany, 2);
4668
toomany_flag = false;
4669
L__M(##Miscellany, 1);
4672
for (k=1 : k<=j : k++) {
4673
if (deadflag) break;
4674
if (location ~= i) {
4675
L__M(##Miscellany, 51);
4678
l = multiple_object-->k;
4680
print (name) l, ": ";
4682
inp1 = l; self.begin_action(action, l, second, 0); inp1 = 0;
4685
inp2 = l; self.begin_action(action, noun, l, 0); inp2 = 0;
4689
! --------------------------------------------------------------
4693
! No time passes if either (i) the verb was meta, or
4694
! (ii) we've only had the implicit take before the "real"
4697
if (notheld_mode == 1) { NoteObjectAcquisitions(); continue; }
4699
if (~~deadflag) self.end_turn_sequence();
4700
else if (START_MOVE ~= 1) turns++;
4704
if (deadflag ~= 2) AfterLife();
4705
if (deadflag == 0) jump very__late__error;
4708
#Ifdef TARGET_ZCODE;
4709
#IfV5; style bold; #Endif; ! V5
4710
#Ifnot; ! TARGET_GLULX
4711
glk($0086, 5); ! set alert style
4714
if (deadflag == 1) L__M(##Miscellany, 3);
4715
if (deadflag == 2) L__M(##Miscellany, 4);
4722
#Ifdef TARGET_ZCODE;
4723
#IfV5; style roman; #Endif; ! V5
4724
#Ifnot; ! TARGET_GLULX
4725
glk($0086, 0); ! set normal style
4736
], ! end of 'play' property
4738
end_turn_sequence [;
4739
AdvanceWorldClock();
4740
if (deadflag) return;
4741
RunTimersAndDaemons();
4742
if (deadflag) return;
4743
RunEachTurnProperties();
4744
if (deadflag) return;
4746
if (deadflag) return;
4748
if (deadflag) return;
4749
NoteObjectAcquisitions();
4752
begin_action [ a n s source sa sn ss;
4753
sa = action; sn = noun; ss = second;
4754
action = a; noun = n; second = s;
4756
if (debug_flag & 2 ~= 0) TraceAction(source);
4760
#Iftrue (Grammar__Version == 1);
4761
if ((meta || BeforeRoutines() == false) && action < 256)
4764
if ((meta || BeforeRoutines() == false) && action < 4096)
4766
#Endif; ! Grammar__Version
4767
action = sa; noun = sn; second = ss;
4771
[ AdvanceWorldClock;
4773
if (the_time ~= NULL) {
4774
if (time_rate >= 0) the_time=the_time+time_rate;
4777
if (time_step == 0) {
4779
time_step = -time_rate;
4782
the_time = the_time % 1440;
4786
[ RunTimersAndDaemons i j;
4788
if (debug_flag & 4 ~= 0) {
4789
for (i=0 : i<active_timers : i++) {
4792
print (name) (j&~WORD_HIGHBIT), ": ";
4793
if (j & WORD_HIGHBIT) print "daemon";
4795
print "timer with ", j.time_left, " turns to go";
4802
for (i=0 : i<active_timers : i++) {
4803
if (deadflag) return;
4806
if (j & WORD_HIGHBIT) RunRoutines(j&~WORD_HIGHBIT, daemon);
4808
if (j.time_left == 0) {
4810
RunRoutines(j, time_out);
4813
j.time_left = j.time_left-1;
4819
[ RunEachTurnProperties;
4820
scope_reason = EACH_TURN_REASON; verb_word = 0;
4821
DoScopeAction(location);
4822
SearchScope(ScopeCeiling(player), player, 0);
4823
scope_reason = PARSING_REASON;
4826
#Ifdef TARGET_ZCODE;
4828
[ ActionPrimitive; indirect(#actions_table-->action); ];
4830
#Ifnot; ! TARGET_GLULX
4832
[ ActionPrimitive; indirect(#actions_table-->(action+1)); ];
4840
L__M(##Miscellany,5);
4845
#Ifdef TARGET_ZCODE;
4846
#IfV3; read buffer parse; #Endif; ! V3
4848
#IfV5; read buffer parse DrawStatusLine; #Endif; ! V5
4849
#Ifnot; ! TARGET_GLULX
4850
KeyboardPrimitive(buffer, parse);
4853
if (i == QUIT1__WD or QUIT2__WD) {
4854
#Ifdef TARGET_ZCODE;
4856
#Ifnot; ! TARGET_GLULX
4860
if (i == RESTART__WD) {
4861
#Ifdef TARGET_ZCODE;
4863
#Ifnot; ! TARGET_GLULX
4867
if (i == RESTORE__WD) {
4871
if (i == FULLSCORE1__WD or FULLSCORE2__WD && TASKS_PROVIDED==0) {
4872
new_line; FullScoreSub();
4875
if (deadflag == 2 && i == AMUSING__WD && AMUSING_PROVIDED==0) {
4876
new_line; Amusing();
4880
if (i == UNDO1__WD or UNDO2__WD or UNDO3__WD) {
4881
if (undo_flag == 0) {
4882
L__M(##Miscellany, 6);
4885
if (undo_flag == 1) jump UndoFailed2;
4886
#Ifdef TARGET_ZCODE;
4888
#Ifnot; ! TARGET_GLULX
4894
L__M(##Miscellany, 7);
4899
L__M(##Miscellany, 8);
4903
[ R_Process a i j s1 s2;
4904
s1 = inp1; s2 = inp2;
4905
inp1 = i; inp2 = j; InformLibrary.begin_action(a, i, j, 1);
4906
inp1 = s1; inp2 = s2;
4909
[ NoteObjectAcquisitions i;
4910
objectloop (i in player)
4911
if (i hasnt moved) {
4914
score = score + OBJECT_SCORE;
4915
things_score = things_score + OBJECT_SCORE;
4920
! ----------------------------------------------------------------------------
4922
[ TestScope obj act a al sr x y;
4923
x = parser_one; y = parser_two;
4924
parser_one = obj; parser_two = 0; a = actor; al = actors_location;
4925
sr = scope_reason; scope_reason = TESTSCOPE_REASON;
4926
if (act == 0) actor = player; else actor = act;
4927
actors_location = ScopeCeiling(actor);
4928
SearchScope(actors_location, actor, 0); scope_reason = sr; actor = a;
4929
actors_location = al; parser_one = x; x = parser_two; parser_two = y;
4933
[ LoopOverScope routine act x y a al;
4934
x = parser_one; y = scope_reason; a = actor; al = actors_location;
4935
parser_one = routine;
4936
if (act == 0) actor = player; else actor = act;
4937
actors_location = ScopeCeiling(actor);
4938
scope_reason = LOOPOVERSCOPE_REASON;
4939
SearchScope(actors_location, actor, 0);
4940
parser_one = x; scope_reason = y; actor = a; actors_location = al;
4944
if (GamePreRoutine() ~= 0) rtrue;
4945
if (RunRoutines(player, orders) ~= 0) rtrue;
4946
scope_reason = REACT_BEFORE_REASON; parser_one=0;
4947
SearchScope(ScopeCeiling(player), player, 0);
4948
scope_reason = PARSING_REASON;
4949
if (parser_one ~= 0) rtrue;
4950
if (location ~= 0 && RunRoutines(location, before) ~= 0) rtrue;
4951
if (inp1 > 1 && RunRoutines(inp1, before) ~= 0) rtrue;
4956
scope_reason = REACT_AFTER_REASON; parser_one = 0;
4957
SearchScope(ScopeCeiling(player), player, 0); scope_reason = PARSING_REASON;
4958
if (parser_one ~= 0) rtrue;
4959
if (location ~= 0 && RunRoutines(location, after) ~= 0) rtrue;
4960
if (inp1 > 1 && RunRoutines(inp1, after) ~= 0) rtrue;
4961
return GamePostRoutine();
4966
if (debug_flag & 2 ~= 0) TraceAction(2, j);
4968
reason_code = j; return RunRoutines(a,life);
4972
switch (metaclass(addr)) { ! Left over from Inform 5
4974
Object, Class: return 1;
4980
[ PrintOrRun obj prop flag;
4981
if (obj.#prop > WORDSIZE) return RunRoutines(obj,prop);
4982
if (obj.prop == NULL) rfalse;
4983
switch (metaclass(obj.prop)) {
4984
Class, Object, nothing:
4985
return RunTimeError(2,obj,prop);
4987
print (string) obj.prop;
4988
if (flag == 0) new_line;
4991
return RunRoutines(obj,prop);
4995
[ PrintOrRunVar var flag;
4996
switch (metaclass(var)) {
5001
if (flag == 0) new_line;
5005
print (char) '(', var, (char) ')';
5010
[ ValueOrRun obj prop;
5011
!### this is entirely unlikely to work. Does anyone care? (AP)
5012
! Well, it's certainly used three times in verblibm.h (RF)
5013
if (obj.prop < 256) return obj.prop;
5014
return RunRoutines(obj, prop);
5017
[ RunRoutines obj prop;
5018
if (obj == thedark && prop ~= initial or short_name or description) obj = real_location;
5019
if (obj.&prop == 0 && prop >= INDIV_PROP_START) rfalse;
5023
#Ifdef TARGET_ZCODE;
5025
[ ChangeDefault prop val a b;
5026
! Use assembly-language here because -S compilation won't allow this:
5032
#Ifnot; ! TARGET_GLULX
5034
[ ChangeDefault prop val;
5035
! Use assembly-language here because -S compilation won't allow this:
5036
! #cpv__start-->prop = val;
5037
@astore #cpv__start prop val;
5042
! ----------------------------------------------------------------------------
5044
[ StartTimer obj timer i;
5045
for (i=0 : i<active_timers : i++)
5046
if (the_timers-->i == obj) rfalse;
5047
for (i=0 : i<active_timers : i++)
5048
if (the_timers-->i == 0) jump FoundTSlot;
5049
i = active_timers++;
5050
if (i >= MAX_TIMERS) { RunTimeError(4); return; }
5052
if (obj.&time_left == 0) { RunTimeError(5,obj); return; }
5053
the_timers-->i = obj; obj.time_left = timer;
5057
for (i=0 : i<active_timers : i++)
5058
if (the_timers-->i == obj) jump FoundTSlot2;
5061
if (obj.&time_left == 0) { RunTimeError(5,obj); return; }
5062
the_timers-->i = 0; obj.time_left = 0;
5065
[ StartDaemon obj i;
5066
for (i=0 : i<active_timers : i++)
5067
if (the_timers-->i == WORD_HIGHBIT + obj) rfalse;
5068
for (i=0 : i<active_timers : i++)
5069
if (the_timers-->i == 0) jump FoundTSlot3;
5070
i = active_timers++;
5071
if (i >= MAX_TIMERS) RunTimeError(4);
5073
the_timers-->i = WORD_HIGHBIT + obj;
5077
for (i=0 : i<active_timers : i++)
5078
if (the_timers-->i == WORD_HIGHBIT + obj) jump FoundTSlot4;
5084
! ----------------------------------------------------------------------------
5087
if (sys_statusline_flag == 0) { sline1 = score; sline2 = turns; }
5088
else { sline1 = the_time/60; sline2 = the_time%60;}
5092
the_time = t; time_rate = s; time_step = 0;
5093
if (s < 0) time_step = 0-s;
5097
#Ifdef TARGET_GLULX;
5098
glk($0086, 6); ! set note style
5099
#Endif; ! TARGET_GLULX
5100
print "^["; L__M(##Miscellany, 50, score-last_score); print ".]^";
5101
#Ifdef TARGET_GLULX;
5102
glk($0086, 0); ! set normal style
5103
#Endif; ! TARGET_GLULX
5106
! ----------------------------------------------------------------------------
5108
[ AdjustLight flag i;
5110
lightflag = OffersLight(parent(player));
5112
if (i == 0 && lightflag == 1) {
5113
location = real_location;
5114
if (flag == 0) <Look>;
5117
if (i == 1 && lightflag == 0) {
5118
real_location = location; location = thedark;
5121
return L__M(##Miscellany, 9);
5124
if (i == 0 && lightflag == 0) location=thedark;
5129
if (i has light) rtrue;
5131
if (HasLightSource(j) == 1) rtrue;
5132
if (i has container) {
5133
if (i has open || i has transparent)
5134
return OffersLight(parent(i));
5137
if (i has enterable || i has transparent || i has supporter)
5138
return OffersLight(parent(i));
5143
[ HidesLightSource obj;
5144
if (obj == player) rfalse;
5145
if (obj has transparent or supporter) rfalse;
5146
if (obj has container) return (obj hasnt open);
5147
return (obj hasnt enterable);
5150
[ HasLightSource i j ad;
5152
if (i has light) rtrue;
5153
if (i has enterable || IsSeeThrough(i) == 1)
5154
if (~~(HidesLightSource(i)))
5156
if (HasLightSource(j) == 1) rtrue;
5157
ad = i.&add_to_scope;
5158
if (parent(i) ~= 0 && ad ~= 0) {
5159
if (metaclass(ad-->0) == Routine) {
5160
ats_hls = 0; ats_flag = 1;
5161
RunRoutines(i, add_to_scope);
5162
ats_flag = 0; if (ats_hls == 1) rtrue;
5165
for (j=0 : (WORDSIZE*j)<i.#add_to_scope : j++)
5166
if (HasLightSource(ad-->j) == 1) rtrue;
5172
[ ChangePlayer obj flag i;
5173
! if (obj.&number == 0) return RunTimeError(7,obj);
5174
if (actor == player) actor=obj;
5175
give player ~transparent ~concealed;
5176
i = obj; while (parent(i) ~= 0) {
5177
if (i has animate) give i transparent;
5180
if (player == selfobj) player.short_name = FORMER__TX;
5184
if (player == selfobj) player.short_name = NULL;
5185
give player transparent concealed animate proper;
5186
i = player; while (parent(i) ~= 0) i = parent(i);
5187
location = i; real_location = location;
5188
if (parent(player) == 0) return RunTimeError(10);
5189
MoveFloatingObjects();
5190
lightflag = OffersLight(parent(player));
5191
if (lightflag == 0) location = thedark;
5192
print_player_flag = flag;
5195
! ----------------------------------------------------------------------------
5199
#Ifdef TARGET_ZCODE;
5203
if (w >= 1 && w <= top_object) print " (", (name) w, ")";
5204
if (UnsignedCompare(w, dict_start) >= 0 &&
5205
UnsignedCompare(w, dict_end) < 0 &&
5206
(w - dict_start) % dict_entry_size == 0)
5207
print " ('", (address) w, "')";
5210
[ DebugAction a anames;
5211
#Iftrue (Grammar__Version == 1);
5212
if (a >= 256) { print "<fake action ", a-256, ">"; return; }
5214
if (a >= 4096) { print "<fake action ", a-4096, ">"; return; }
5215
#Endif; ! Grammar__Version
5216
anames = #identifiers_table;
5217
anames = anames + 2*(anames-->0) + 2*48;
5218
print (string) anames-->a;
5221
[ DebugAttribute a anames;
5222
if (a < 0 || a >= 48) print "<invalid attribute ", a, ">";
5224
anames = #identifiers_table; anames = anames + 2*(anames-->0);
5225
print (string) anames-->a;
5229
#Ifnot; ! TARGET_GLULX
5231
[ DebugParameter w endmem;
5234
if (w >= 1 && w < endmem) {
5235
if (w->0 >= $70 && w->0 < $7F) print " (", (name) w, ")";
5236
if (w->0 >= $60 && w->0 < $6F) print " ('", (address) w, "')";
5240
[ DebugAction a str;
5241
if (a >= 4096) { print "<fake action ", a-4096, ">"; return; }
5242
if (a < 0 || a >= #identifiers_table-->7) print "<invalid action ", a, ">";
5244
str = #identifiers_table-->6;
5246
if (str) print (string) str; else print "<unnamed action ", a, ">";
5250
[ DebugAttribute a str;
5251
if (a < 0 || a >= NUM_ATTR_BYTES*8) print "<invalid attribute ", a, ">";
5253
str = #identifiers_table-->4;
5255
if (str) print (string) str; else print "<unnamed attribute ", a, ">";
5261
[ TraceAction source ar;
5262
if (source < 2) print "[ Action ", (DebugAction) action;
5265
print "[ Order to ", (name) actor, ": ", (DebugAction) action;
5267
print "[ Life rule ", (DebugAction) ar;
5269
if (noun ~= 0) print " with noun ", (DebugParameter) noun;
5270
if (second ~= 0) print " and second ", (DebugParameter) second;
5271
if (source == 0) print " ";
5272
if (source == 1) print " (from < > statement) ";
5277
AnalyseToken(token);
5278
switch (found_ttype) {
5280
print "<illegal token number ", token, ">";
5282
switch (found_tdata) {
5283
NOUN_TOKEN: print "noun";
5284
HELD_TOKEN: print "held";
5285
MULTI_TOKEN: print "multi";
5286
MULTIHELD_TOKEN: print "multiheld";
5287
MULTIEXCEPT_TOKEN: print "multiexcept";
5288
MULTIINSIDE_TOKEN: print "multiinside";
5289
CREATURE_TOKEN: print "creature";
5290
SPECIAL_TOKEN: print "special";
5291
NUMBER_TOKEN: print "number";
5292
TOPIC_TOKEN: print "topic";
5293
ENDIT_TOKEN: print "END";
5296
print "'", (address) found_tdata, "'";
5299
print "noun=", (InfixPrintPA) found_tdata;
5301
print "noun=Routine(", found_tdata, ")";
5304
print (DebugAttribute) found_tdata;
5307
print "scope=", (InfixPrintPA) found_tdata;
5309
print "scope=Routine(", found_tdata, ")";
5313
print (InfixPrintPA) found_tdata;
5315
print "Routine(", found_tdata, ")";
5320
[ DebugGrammarLine pcount;
5322
for (: line_token-->pcount ~= ENDIT_TOKEN : pcount++) {
5323
if ((line_token-->pcount)->0 & $10) print "/ ";
5324
print (DebugToken) line_token-->pcount, " ";
5326
print "-> ", (DebugAction) action_to_be;
5327
if (action_reversed) print " reverse";
5330
#Ifdef TARGET_ZCODE;
5332
[ ShowVerbSub address lines da meta i j;
5333
if (noun == 0 || ((noun->#dict_par1) & 1) == 0)
5334
"Try typing ~showverb~ and then the name of a verb.";
5335
meta = ((noun->#dict_par1) & 2)/2;
5336
i = $ff-(noun->#dict_par2);
5337
address = (HDR_STATICMEMORY-->0)-->i;
5341
if (meta) print "meta ";
5342
da = HDR_DICTIONARY-->0;
5343
for (j=0 : j<(da+5)-->0 : j++)
5344
if (da->(j*9 + 14) == $ff-i) print "'", (address) (da + 9*j + 7), "' ";
5346
if (lines == 0) "has no grammar lines.";
5347
for (: lines>0 : lines--) {
5348
address = UnpackGrammarLine(address);
5349
print " "; DebugGrammarLine(); new_line;
5353
#Ifnot; ! TARGET_GLULX
5355
[ ShowVerbSub address lines i j meta wd dictlen entrylen;
5356
if (noun == 0 || ((noun->#dict_par1) & 1) == 0)
5357
"Try typing ~showverb~ and then the name of a verb.";
5358
meta = ((noun->#dict_par1) & 2)/2;
5359
i = $ff-(noun->#dict_par2);
5360
address = (#grammar_table)-->(i+1);
5364
if (meta) print "meta ";
5365
dictlen = #dictionary_table-->0;
5366
entrylen = DICT_WORD_SIZE + 7;
5367
for (j=0 : j<dictlen : j++) {
5368
wd = #dictionary_table + WORDSIZE + entrylen*j;
5369
if (wd->#dict_par2 == $ff-i)
5370
print "'", (address) wd, "' ";
5373
if (lines == 0) "has no grammar lines.";
5374
for (: lines>0 : lines--) {
5375
address = UnpackGrammarLine(address);
5376
print " "; DebugGrammarLine(); new_line;
5382
[ ShowobjSub c f l a n x numattr;
5383
if (noun == 0) noun = location;
5384
objectloop (c ofclass Class) if (noun ofclass c) { f++; l=c; }
5385
if (f == 1) print (name) l, " ~"; else print "Object ~";
5386
print (name) noun, "~ (", noun, ")";
5387
if (parent(noun) ~= 0) print " in ~", (name) parent(noun), "~";
5391
objectloop (c ofclass Class) if (noun ofclass c) print (name) c, " ";
5394
#Ifdef TARGET_ZCODE;
5396
#Ifnot; ! TARGET_GLULX
5397
numattr = NUM_ATTR_BYTES * 8;
5399
for (a=0,f=0 : a<numattr : a++) if (noun has a) f=1;
5402
for (a=0 : a<numattr : a++) if (noun has a) print (DebugAttribute) a, " ";
5405
if (noun ofclass Class) return;
5408
#Ifdef TARGET_ZCODE;
5409
l = #identifiers_table-->0;
5410
#Ifnot; ! TARGET_GLULX
5411
l = INDIV_PROP_START + #identifiers_table-->3;
5413
for (a=1 : a<=l : a++) {
5414
if ((a ~= 2 or 3) && noun.&a) {
5415
if (f == 0) { print " with "; f=1; }
5418
for (c=0 : WORDSIZE*c<n : c++) {
5421
if (a == name) print "'", (address) x, "'";
5423
if (a == number or capacity or time_left) print x;
5430
switch (metaclass(x)) {
5434
print "~", (string) x, "~";
5446
! if (f==1) new_line;
5451
! ----------------------------------------------------------------------------
5452
! Miscellaneous display routines used by DrawStatusLine and available for
5453
! user. Most of these vary according to which machine is being compiled to
5454
! ----------------------------------------------------------------------------
5456
#Ifdef TARGET_ZCODE;
5458
[ ClearScreen window;
5460
WIN_ALL: @erase_window -1;
5461
WIN_STATUS: @erase_window 1;
5462
WIN_MAIN: @erase_window 0;
5466
#Iftrue (#version_number == 6);
5467
[ MoveCursorV6 line column charw; ! 1-based postion on text grid
5468
@get_wind_prop 1 13 -> charw; ! font size
5469
charw = charw & $FF;
5470
line = 1 + charw*(line-1);
5471
column = 1 + charw*(column-1);
5472
@set_cursor line column;
5477
[ MoveCursor line column; ! 1-based postion on text grid
5478
if (~~statuswin_current) {
5480
if (clr_on && clr_bgstatus > 1) @set_colour clr_fgstatus clr_bgstatus;
5487
#Iftrue (#version_number == 6);
5488
MoveCursorV6(line, column);
5490
@set_cursor line column;
5492
statuswin_current = true;
5497
if (statuswin_current) {
5498
if (clr_on && clr_bgstatus > 1) @set_colour clr_fg clr_bg;
5502
statuswin_current = false;
5505
#Iftrue (#version_number == 6);
5506
[ ScreenWidth width charw;
5507
@get_wind_prop 1 3 -> width;
5508
@get_wind_prop 1 13 -> charw;
5509
charw = charw & $FF;
5510
return (width+charw-1) / charw;
5514
return (HDR_SCREENWCHARS->0);
5519
return (HDR_SCREENHLINES->0);
5522
#Iftrue (#version_number == 6);
5523
[ StatusLineHeight height wx wy x y charh;
5524
! Split the window. Standard 1.0 interpreters should keep the window 0
5525
! cursor in the same absolute position, but older interpreters,
5526
! including Infocom's don't - they keep the window 0 cursor in the
5527
! same position relative to its origin. We therefore compensate
5529
@get_wind_prop 0 0 -> wy; @get_wind_prop 0 1 -> wx;
5530
@get_wind_prop 0 13 -> charh; @log_shift charh $FFF8 -> charh;
5531
@get_wind_prop 0 4 -> y; @get_wind_prop 0 5 -> x;
5532
height = height * charh;
5533
@split_window height;
5534
y = y - height + wy - 1;
5538
gg_statuswin_cursize = height;
5541
[ StatusLineHeight height;
5542
if (gg_statuswin_cursize ~= height)
5543
@split_window height;
5544
gg_statuswin_cursize = height;
5548
[ SetColour f b window;
5549
if (clr_on && f && b) {
5550
if (window == 0) { ! if setting both together, set reverse
5558
if (window == 0 or 2) {
5562
if (statuswin_current)
5563
@set_colour clr_fgstatus clr_bgstatus;
5565
@set_colour clr_fg clr_bg;
5570
#Ifnot; ! TARGET_GLULX
5572
[ ClearScreen window;
5573
if (window == WIN_ALL or WIN_MAIN) {
5574
glk($002A, gg_mainwin);
5576
glk($0024, gg_quotewin, 0); ! close_window
5580
if (gg_statuswin && window == WIN_ALL or WIN_STATUS) glk($002A, gg_statuswin);
5583
[ MoveCursor line column; ! 0-based postion on text grid
5585
glk($002F, gg_statuswin); ! set_window
5591
glk($002B, gg_statuswin, column-1, line-1); ! window_move_cursor
5592
statuswin_current=1;
5596
glk($002F, gg_mainwin); ! set_window
5597
statuswin_current=0;
5601
if (c > 9) return c;
5603
return $ff0000*(c&1) + $ff00*(c&2 ~= 0) + $ff*(c&4 ~= 0);
5608
if (gg_statuswin && statuswin_current) id = gg_statuswin;
5609
glk($0025, id, gg_arguments, 0); ! window_get_size
5610
return gg_arguments-->0;
5614
glk($0025, gg_mainwin, 0, gg_arguments); ! window_get_size
5615
return gg_arguments-->0;
5618
[ SetColour f b window doclear i fwd bwd swin;
5619
if (clr_on && f && b) {
5620
if (window) swin = 5-window; ! 4 for TextGrid, 3 for TextBuffer
5622
fwd = MakeColourWord(f);
5623
bwd = MakeColourWord(b);
5624
for (i=0 : i<=10: i++) {
5625
if (f == CLR_DEFAULT || b == CLR_DEFAULT) { ! remove style hints
5626
glk($00B1, swin, i, 7);
5627
glk($00B1, swin, i, 8);
5630
glk($00B0, swin, i, 7, fwd);
5631
glk($00B0, swin, i, 8, bwd);
5635
! Now re-open the windows to apply the hints
5636
if (gg_statuswin) glk($0024, gg_statuswin, 0); ! close_window
5638
if (doclear || ( window ~= 1 && (clr_fg ~= f || clr_bg ~= b) ) ) {
5639
glk($0024, gg_mainwin, 0);
5640
gg_mainwin = glk($0023, 0, 0, 0, 3, GG_MAINWIN_ROCK); ! window_open
5641
if (gg_scriptstr ~= 0)
5642
glk($002D, gg_mainwin, gg_scriptstr); ! window_set_echo_stream
5645
gg_statuswin = glk($0023, gg_mainwin, $12, gg_statuswin_cursize,
5646
4, GG_STATUSWIN_ROCK); ! window_open
5647
if (statuswin_current && gg_statuswin) MoveCursor(); else MainWindow();
5662
SetColour (f, b, w);
5665
[ RestoreColours; ! L61007
5666
if (clr_on) { ! check colour has been used
5667
SetColour(clr_fg, clr_bg, 2); ! make sure both sets of variables are restored
5668
SetColour(clr_fgstatus, clr_bgstatus, 1, true);
5671
#Ifdef TARGET_ZCODE;
5672
#Iftrue (#version_number == 6); ! request screen update
5673
(0-->8) = (0-->8) | $$00000100;
5678
! ----------------------------------------------------------------------------
5679
! Except in Version 3, the DrawStatusLine routine does just that: this is
5680
! provided explicitly so that it can be Replace'd to change the style, and
5681
! as written it emulates the ordinary Standard game status line, which is
5683
! ----------------------------------------------------------------------------
5685
#Ifdef TARGET_ZCODE;
5689
#Iftrue (#version_number == 6);
5690
[ DrawStatusLine width x charw scw mvw;
5691
(0-->8) = (0-->8) &~ $$00000100;
5693
StatusLineHeight(gg_statuswin_size);
5694
! Now clear the window. This isn't totally trivial. Our approach is to select the
5695
! fixed space font, measure its width, and print an appropriate
5696
! number of spaces. We round up if the screen isn't a whole number
5697
! of characters wide, and rely on window 1 being set to clip by default.
5700
width = ScreenWidth();
5702
! Back to standard font for the display. We use output_stream 3 to
5703
! measure the space required, the aim being to get 50 characters
5704
! worth of space for the location name.
5707
if (location == thedark)
5708
print (name) location;
5710
FindVisibilityLevels();
5711
if (visibility_ceiling == location) print (name) location;
5712
else print (The) visibility_ceiling;
5714
@get_wind_prop 1 3 -> width;
5715
@get_wind_prop 1 13 -> charw;
5716
charw = charw & $FF;
5717
@output_stream 3 StorageForShortName;
5718
print (string) SCORE__TX, "00000";
5719
@output_stream -3; scw = HDR_PIXELSTO3-->0 + charw;
5720
@output_stream 3 StorageForShortName;
5721
print (string) MOVES__TX, "00000";
5722
@output_stream -3; mvw = HDR_PIXELSTO3-->0 + charw;
5723
if (width - scw - mvw >= 50*charw) {
5724
x = 1+width-scw-mvw;
5725
@set_cursor 1 x; print (string) SCORE__TX, sline1;
5727
@set_cursor 1 x; print (string) MOVES__TX, sline2;
5730
@output_stream 3 StorageForShortName;
5731
print "00000/00000";
5732
@output_stream -3; scw = HDR_PIXELSTO3-->0 + charw;
5733
if (width - scw >= 50*charw) {
5735
@set_cursor 1 x; print sline1, "/", sline2;
5738
! Reselect roman, as Infocom's interpreters interpreters go funny
5739
! if reverse is selected twice.
5743
#Endif; ! #version_number == 6
5747
#Endif; ! TARGET_ZCODE
5749
#Ifndef DrawStatusLine;
5750
[ DrawStatusLine width posa posb;
5751
#Ifdef TARGET_GLULX;
5752
! If we have no status window, we must not try to redraw it.
5753
if (gg_statuswin == 0)
5757
! If there is no player location, we shouldn't try to draw status window
5758
if (location == nothing || parent(player) == nothing)
5761
StatusLineHeight(gg_statuswin_size);
5764
width = ScreenWidth();
5765
posa = width-26; posb = width-13;
5769
if (location == thedark) {
5770
print (name) location;
5773
FindVisibilityLevels();
5774
if (visibility_ceiling == location)
5775
print (name) location;
5777
print (The) visibility_ceiling;
5780
if (sys_statusline_flag && width > 53) {
5781
MoveCursor(1, posa);
5782
print (string) TIME__TX;
5783
LanguageTimeOfDay(sline1, sline2);
5788
MoveCursor(1, posa);
5789
print (string) SCORE__TX, sline1;
5791
MoveCursor(1, posb);
5792
print (string) MOVES__TX, sline2;
5795
if (width > 53 && width <= 66) {
5796
MoveCursor(1, posb);
5797
print sline1, "/", sline2;
5802
MainWindow(); ! set_window
5806
#Ifdef TARGET_GLULX;
5808
[ StatusLineHeight hgt parwin;
5809
if (gg_statuswin == 0) return;
5810
if (hgt == gg_statuswin_cursize) return;
5811
parwin = glk($0029, gg_statuswin); ! window_get_parent
5812
glk($0026, parwin, $12, hgt, 0); ! window_set_arrangement
5813
gg_statuswin_cursize = hgt;
5816
[ Box__Routine maxwid arr ix lines lastnl parwin;
5817
maxwid = 0; ! squash compiler warning
5820
if (gg_quotewin == 0) {
5821
gg_arguments-->0 = lines;
5822
ix = InitGlkWindow(GG_QUOTEWIN_ROCK);
5824
gg_quotewin = glk($0023, gg_mainwin, $12, lines, 3,
5825
GG_QUOTEWIN_ROCK); ! window_open
5828
parwin = glk($0029, gg_quotewin); ! window_get_parent
5829
glk($0026, parwin, $12, lines, 0); ! window_set_arrangement
5834
glk($002A, gg_quotewin); ! window_clear
5835
glk($002F, gg_quotewin); ! set_window
5839
! If gg_quotewin is zero here, the quote just appears in the story window.
5841
glk($0086, 7); ! set blockquote style
5842
for (ix=0 : ix<lines : ix++) {
5843
print (string) arr-->(ix+1);
5844
if (ix < lines-1 || lastnl) new_line;
5846
glk($0086, 0); ! set normal style
5849
glk($002F, gg_mainwin); ! set_window
5853
#Endif; ! TARGET_GLULX
5855
#Ifdef TARGET_GLULX;
5858
@gestalt 4 2 res; ! Test if this interpreter has Glk.
5860
! Without Glk, we're entirely screwed.
5863
! Set the VM's I/O system to be Glk.
5866
! First, we must go through all the Glk objects that exist, and see
5867
! if we created any of them. One might think this strange, since the
5868
! program has just started running, but remember that the player might
5869
! have just typed "restart".
5873
res = InitGlkWindow(0);
5874
if (res ~= 0) return;
5876
! Now, gg_mainwin and gg_storywin might already be set. If not, set them.
5878
if (gg_mainwin == 0) {
5879
! Open the story window.
5880
res = InitGlkWindow(GG_MAINWIN_ROCK);
5882
gg_mainwin = glk($0023, 0, 0, 0, 3, GG_MAINWIN_ROCK); ! window_open
5883
if (gg_mainwin == 0) {
5884
! If we can't even open one window, there's no point in going on.
5889
! There was already a story window. We should erase it.
5890
glk($002A, gg_mainwin); ! window_clear
5893
if (gg_statuswin == 0) {
5894
res = InitGlkWindow(GG_STATUSWIN_ROCK);
5896
gg_statuswin_cursize = gg_statuswin_size;
5897
gg_statuswin = glk($0023, gg_mainwin, $12, gg_statuswin_cursize,
5898
4, GG_STATUSWIN_ROCK); ! window_open
5901
! It's possible that the status window couldn't be opened, in which case
5902
! gg_statuswin is now zero. We must allow for that later on.
5904
glk($002F, gg_mainwin); ! set_window
5909
[ GGRecoverObjects id;
5910
! If GGRecoverObjects() has been called, all these stored IDs are
5911
! invalid, so we start by clearing them all out.
5912
! (In fact, after a restoreundo, some of them may still be good.
5913
! For simplicity, though, we assume the general case.)
5920
gg_statuswin_cursize = 0;
5923
gg_command_reading = false;
5925
! Also tell the game to clear its object references.
5926
IdentifyGlkObject(0);
5928
id = glk($0040, 0, gg_arguments); ! stream_iterate
5930
switch (gg_arguments-->0) {
5931
GG_SAVESTR_ROCK: gg_savestr = id;
5932
GG_SCRIPTSTR_ROCK: gg_scriptstr = id;
5934
GG_COMMANDWSTR_ROCK: gg_commandstr = id;
5935
gg_command_reading = false;
5936
GG_COMMANDRSTR_ROCK: gg_commandstr = id;
5937
gg_command_reading = true;
5939
default: IdentifyGlkObject(1, 1, id, gg_arguments-->0);
5941
id = glk($0040, id, gg_arguments); ! stream_iterate
5944
id = glk($0020, 0, gg_arguments); ! window_iterate
5946
switch (gg_arguments-->0) {
5947
GG_MAINWIN_ROCK: gg_mainwin = id;
5948
GG_STATUSWIN_ROCK: gg_statuswin = id;
5949
GG_QUOTEWIN_ROCK: gg_quotewin = id;
5950
default: IdentifyGlkObject(1, 0, id, gg_arguments-->0);
5952
id = glk($0020, id, gg_arguments); ! window_iterate
5955
id = glk($0064, 0, gg_arguments); ! fileref_iterate
5957
switch (gg_arguments-->0) {
5958
GG_SCRIPTFREF_ROCK: gg_scriptfref = id;
5959
default: IdentifyGlkObject(1, 2, id, gg_arguments-->0);
5961
id = glk($0064, id, gg_arguments); ! fileref_iterate
5964
! Tell the game to tie up any loose ends.
5965
IdentifyGlkObject(2);
5968
! This somewhat obfuscated function will print anything.
5969
! It handles strings, functions (with optional arguments), objects,
5970
! object properties (with optional arguments), and dictionary words.
5971
! It does *not* handle plain integers, but you can use
5972
! DecimalNumber or EnglishNumber to handle that case.
5974
! Calling: Is equivalent to:
5975
! ------- ----------------
5976
! PrintAnything() <nothing printed>
5977
! PrintAnything(0) <nothing printed>
5978
! PrintAnything("string"); print (string) "string";
5979
! PrintAnything('word') print (address) 'word';
5980
! PrintAnything(obj) print (name) obj;
5981
! PrintAnything(obj, prop) obj.prop();
5982
! PrintAnything(obj, prop, args...) obj.prop(args...);
5983
! PrintAnything(func) func();
5984
! PrintAnything(func, args...) func(args...);
5986
[ PrintAnything _vararg_count obj mclass;
5987
if (_vararg_count == 0) return;
5990
if (obj == 0) return;
5992
if (obj->0 == $60) {
5993
! Dictionary word. Metaclass() can't catch this case, so we do
5995
print (address) obj;
5999
mclass = metaclass(obj);
6007
! Call the function with all the arguments which are already
6009
@call obj _vararg_count 0;
6012
if (_vararg_count == 0) {
6016
! Push the object back onto the stack, and call the
6017
! veneer routine that handles obj.prop() calls.
6020
@call CA__Pr _vararg_count 0;
6026
! This does the same as PrintAnything, but the output is sent to a
6027
! byte array in memory. The first two arguments must be the array
6028
! address and length; the following arguments are interpreted as
6029
! for PrintAnything. The return value is the number of characters
6031
! If the output is longer than the array length given, the extra
6032
! characters are discarded, so the array does not overflow.
6033
! (However, the return value is the total length of the output,
6034
! including discarded characters.)
6036
[ PrintAnyToArray _vararg_count arr arrlen str oldstr len;
6039
_vararg_count = _vararg_count - 2;
6041
oldstr = glk($0048); ! stream_get_current
6042
str = glk($0043, arr, arrlen, 1, 0); ! stream_open_memory
6043
if (str == 0) return 0;
6045
glk($0047, str); ! stream_set_current
6047
@call PrintAnything _vararg_count 0;
6049
glk($0047, oldstr); ! stream_set_current
6052
@glk $0044 2 0; ! stream_close
6059
! And this calls PrintAnyToArray on a particular array, jiggering
6060
! the result to be a Glulx C-style ($E0) string.
6062
Constant GG_ANYTOSTRING_LEN 66;
6063
Array AnyToStrArr -> GG_ANYTOSTRING_LEN+1;
6065
[ ChangeAnyToCString _vararg_count ix len;
6066
ix = GG_ANYTOSTRING_LEN-2;
6070
ix = _vararg_count+2;
6071
@call PrintAnyToArray ix len;
6072
AnyToStrArr->0 = $E0;
6073
if (len >= GG_ANYTOSTRING_LEN)
6074
len = GG_ANYTOSTRING_LEN-1;
6075
AnyToStrArr->(len+1) = 0;
6079
#Endif; ! TARGET_GLULX
6081
! This is a trivial function which just prints a number, in decimal
6082
! digits. It may be useful as a stub to pass to PrintAnything.
6084
[ DecimalNumber num; print num; ];
6089
Array StorageForShortName buffer 160;
6091
Array StorageForShortName -> 160 + WORDSIZE;
6096
#Ifdef TARGET_ZCODE;
6098
! Platform-independent way of printing strings and properties to a
6099
! buffer (defined as length word followed by byte characters).
6101
[ PrintToBuffer buf len a b c;
6102
@output_stream 3 buf;
6103
switch (metaclass(a)) {
6110
PrintOrRun(a, b, true);
6115
if (buf-->0 > len) print "Error: Overflow in PrintToBuffer.^";
6119
#Ifnot; ! TARGET_GLULX
6121
[ PrintToBuffer buf len a b;
6123
if (metaclass(a) == Object && a.#b == WORDSIZE
6124
&& metaclass(a.b) == String)
6125
buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a.b);
6127
buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a, b);
6130
buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a);
6131
if (buf-->0 > len) buf-->0 = len;
6137
! None of the following functions should be called for zcode if the
6138
! output exceeds the size of the buffer.
6141
PrintToBuffer(StorageForShortName, 160, a, b);
6142
return StorageForShortName-->0;
6145
#Ifdef TARGET_ZCODE;
6151
202, 204, 212, 214, 221:
6155
158 to 160, 167 to 169, 208 to 210:
6157
186 to 190, 196 to 200:
6169
201, 203, 211, 213, 220:
6173
155 to 157, 164 to 166, 205 to 207:
6175
181 to 185, 191 to 195:
6183
#Ifnot; ! TARGET_GLULX
6185
[ LowerCase c; return glk($00A0, c); ];
6186
[ UpperCase c; return glk($00A1, c); ];
6190
[ PrintCapitalised obj prop flag nocaps centred length i width;
6191
! a variation of PrintOrRun, capitalising the first letter and returning nothing
6193
if (obj ofclass String || prop == 0) {
6194
PrintToBuffer (StorageForShortName, 160, obj);
6198
if (obj.#prop > WORDSIZE || metaclass(obj.prop) == Routine or String) {
6199
PrintToBuffer(StorageForShortName, 160, obj, prop);
6202
if (obj.prop == NULL) rfalse;
6203
else return RunTimeError(2, obj, prop);
6205
length = StorageForShortName-->0;
6206
width = ScreenWidth();
6207
if (centred && length<width)
6208
spaces ( (width-length)/2 );
6210
StorageForShortName->WORDSIZE = UpperCase(StorageForShortName->WORDSIZE);
6212
for (i=WORDSIZE: i<length+WORDSIZE: i++) print (char) StorageForShortName->i;
6214
if (flag == 0 && obj.#prop == WORDSIZE && metaclass(obj.prop) == String)
6220
PrintCapitalised(a, b, 0, 1, 1);
6224
if (nocaps) print (string) str;
6225
else PrintCapitalised(str);
6228
[ PrefaceByArticle o acode pluralise capitalise i artform findout artval;
6229
if (o provides articles) {
6230
artval=(o.&articles)-->(acode+short_name_case*LanguageCases);
6232
print (Cap) artval, " ";
6234
print (string) artval, " ";
6235
if (pluralise) return;
6236
print (PSN__) o; return;
6239
i = GetGNAOfObject(o);
6241
if (i < 3 || (i >= 6 && i < 9)) i = i + 3;
6243
i = LanguageGNAsToArticles-->i;
6245
artform = LanguageArticles
6246
+ 3*WORDSIZE*LanguageContractionForms*(short_name_case + i*LanguageCases);
6248
#Iftrue (LanguageContractionForms == 2);
6249
if (artform-->acode ~= artform-->(acode+3)) findout = true;
6250
#Endif; ! LanguageContractionForms
6251
#Iftrue (LanguageContractionForms == 3);
6252
if (artform-->acode ~= artform-->(acode+3)) findout = true;
6253
if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
6254
#Endif; ! LanguageContractionForms
6255
#Iftrue (LanguageContractionForms == 4);
6256
if (artform-->acode ~= artform-->(acode+3)) findout = true;
6257
if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
6258
if (artform-->(acode+6) ~= artform-->(acode+9)) findout = true;
6259
#Endif; ! LanguageContractionForms
6260
#Iftrue (LanguageContractionForms > 4);
6262
#Endif; ! LanguageContractionForms
6264
#Ifdef TARGET_ZCODE;
6265
if (standard_interpreter ~= 0 && findout) {
6266
StorageForShortName-->0 = 160;
6267
@output_stream 3 StorageForShortName;
6268
if (pluralise) print (number) pluralise; else print (PSN__) o;
6270
acode = acode + 3*LanguageContraction(StorageForShortName + 2);
6272
#Ifnot; ! TARGET_GLULX
6275
PrintAnyToArray(StorageForShortName, 160, EnglishNumber, pluralise);
6277
PrintAnyToArray(StorageForShortName, 160, PSN__, o);
6278
acode = acode + 3*LanguageContraction(StorageForShortName);
6282
Cap (artform-->acode, ~~capitalise); ! print article
6283
if (pluralise) return;
6288
if (o == 0) { print (string) NOTHING__TX; rtrue; }
6289
switch (metaclass(o)) {
6290
Routine: print "<routine ", o, ">"; rtrue;
6291
String: print "<string ~", (string) o, "~>"; rtrue;
6292
nothing: print "<illegal object number ", o, ">"; rtrue;
6294
if (o == player) { print (string) YOURSELF__TX; rtrue; }
6295
#Ifdef LanguagePrintShortName;
6296
if (LanguagePrintShortName(o)) rtrue;
6297
#Endif; ! LanguagePrintShortName
6298
if (indef_mode && o.&short_name_indef ~= 0 && PrintOrRun(o, short_name_indef, 1) ~= 0) rtrue;
6299
if (o.&short_name ~= 0 && PrintOrRun(o, short_name, 1) ~= 0) rtrue;
6304
i = indef_mode; indef_mode = true;
6305
if (o has proper) { indef_mode = NULL; print (PSN__) o; return; }
6306
if (o provides article) {
6307
PrintOrRun(o, article, 1); print " ", (PSN__) o; indef_mode = i;
6310
PrefaceByArticle(o, 2); indef_mode = i;
6314
i = indef_mode; indef_mode = true;
6315
if (o has proper) { indef_mode = NULL; print (PSN__) o; return; }
6316
if (o provides article) {
6317
PrintCapitalised(o, article, 1); print " ", (PSN__) o; indef_mode = i;
6320
PrefaceByArticle(o, 2, 0, 1); indef_mode = i;
6324
i = indef_mode; indef_mode = false;
6325
if ((~~o ofclass Object) || o has proper) {
6326
indef_mode = NULL; print (PSN__) o; indef_mode = i;
6329
PrefaceByArticle(o, 1); indef_mode = i;
6335
print (string) YOU__TX;
6339
i = indef_mode; indef_mode = false;
6340
if ((~~o ofclass Object) || o has proper) {
6341
indef_mode = NULL; print (PSN__) o; indef_mode = i;
6344
PrefaceByArticle(o, 0); indef_mode = i;
6347
[ PrintShortName o i;
6348
i = indef_mode; indef_mode = NULL;
6349
PSN__(o); indef_mode = i;
6352
[ EnglishNumber n; LanguageNumber(n); ];
6355
n = LanguageNumbers-->0;
6356
for (i=1 : i<=n : i=i+2)
6357
if (o == LanguageNumbers-->i) return LanguageNumbers-->(i+1);
6362
if (tab-->0 == 0) return RunTimeError(8);
6363
return tab-->(random(tab-->0));
6366
! ----------------------------------------------------------------------------
6367
! Useful routine: unsigned comparison (for addresses in Z-machine)
6368
! Returns 1 if x>y, 0 if x=y, -1 if x<y
6369
! ----------------------------------------------------------------------------
6371
[ UnsignedCompare x y u v;
6372
if (x == y) return 0;
6373
if (x < 0 && y >= 0) return 1;
6374
if (x >= 0 && y < 0) return -1;
6375
u = x&~WORD_HIGHBIT; v= y&~WORD_HIGHBIT;
6376
if (u > v) return 1;
6380
! ==============================================================================
6382
#Ifdef NITFOL_HOOKS; ! Code contributed by Evin Robertson
6383
#Ifdef TARGET_GLULX; ! Might be nice for Z-machine games too,
6384
! but I'm not going to try to make this work
6385
! given #Ifdef funniness.
6387
Array magic_array --> ! This is so nitfol can do typo correction /
6388
! automapping / debugging on Glulx games
6389
$6e66726d $4d616763 $ff0010 ! Goes to 'NfrmMagc' 10 refers to length
6390
Magic_Global_Dispatch__
6391
DI__check_word ! DI__check_word(buf, length)
6393
WV__Pr RV__Pr CA__Pr ! obj.prop = x; x = obj.prop; obj.prop(x)
6394
RA__Pr RL__Pr RA__Sc ! obj.∝ obj.#prop; class::prop
6395
OP__Pr OC__Cl ! obj provides prop; obj ofclass class
6397
OB__Parent__ OB__Child__ OB__Sibling__ ! No explicit veneer for these
6400
[ OB__Parent__ obj; return parent(obj); ];
6402
[ OB__Child__ obj; return child(obj); ];
6404
[ OB__Sibling__ obj; return sibling(obj); ];
6406
[ Magic_Global_Dispatch__ glbl;
6409
if (location == TheDark) return real_location; return location;
6413
return CompassDirection::number; ! Silliness to make exist RA__Sc
6414
! Should never be called.
6416
return magic_array; ! Silences a warning.
6419
[ DI__check_word buf wlen ix val res dictlen entrylen;
6420
! Just like in Tokenise__. In fact, Tokenise__ could call this if
6421
! it wanted, instead of doing this itself.
6422
if (wlen > DICT_WORD_SIZE) wlen = DICT_WORD_SIZE;
6423
for (ix=0 : ix<wlen : ix++) {
6424
gg_tokenbuf->ix = glk($00A0, buf->ix);
6426
for (: ix<DICT_WORD_SIZE : ix++) {
6427
gg_tokenbuf->ix = 0;
6429
val = #dictionary_table + WORDSIZE;
6430
entrylen = DICT_WORD_SIZE + 7;
6431
@binarysearch gg_tokenbuf DICT_WORD_SIZE val entrylen dictlen 1 1 res;
6436
#Endif; ! NITFOL_HOOKS
6438
! ==============================================================================
6440
Object LibraryExtensions "(Library Extensions)"
6441
with RunAll [ prop a1 a2 a3
6443
objectloop (obj in self)
6444
if (obj provides prop && obj.prop ofclass Routine) {
6445
rval = obj.prop(a1, a2, a3);
6446
if (rval > max) max = rval;
6450
RunUntil [ prop exitval a1 a2 a3
6452
objectloop (obj in self)
6453
if (obj provides prop && obj.prop ofclass Routine) {
6454
rval = obj.prop(a1, a2, a3);
6455
if (rval == exitval) return rval;
6459
RunWhile [ prop exitval a1 a2 a3
6461
objectloop (obj in self)
6462
if (obj provides prop && obj.prop ofclass Routine) {
6463
rval = obj.prop(a1, a2, a3);
6464
if (rval ~= exitval) return rval;
6472
! ==============================================================================