cfad47cfa3/t3compiler/tads3/lib/adv3/resolver.t

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
#charset "us-ascii"
2
3
/* 
4
 *   Copyright (c) 2000, 2006 Michael J. Roberts.  All Rights Reserved. 
5
 *   
6
 *   TADS 3 Library: Resolvers.
7
 *   
8
 *   This module defines the Resolver classes.  A Resolver is an abstract
9
 *   object that the parser uses to control the resolution of noun phrases
10
 *   to game objects.  Specialized Resolver subclasses allow noun phrases
11
 *   to be resolved differently according to their grammatical function in
12
 *   a command.  
13
 */
14
15
#include "adv3.h"
16
17
18
/* ------------------------------------------------------------------------ */
19
/*
20
 *   Basic object resolver.  An Action object creates an object resolver
21
 *   to mediate the process of resolving noun phrases to objects.
22
 *   
23
 *   A resolver encapsulates a set of object resolution rules.  In most
24
 *   cases, an action that takes only a direct object can be its own
25
 *   resolver, because it needs only one set of resolution rules; for this
26
 *   reason, this basic Resolver implementation is designed to work with
27
 *   the direct object.  Actions with multiple objects will need separate
28
 *   resolvers for each object, since they might want to use different
29
 *   rules for the different objects.  
30
 */
31
class Resolver: object
32
    construct(action, issuingActor, targetActor)
33
    {
34
        /* remember my action and actor objects */
35
        action_ = action;
36
        issuer_ = issuingActor;
37
        actor_ = targetActor;
38
39
        /* cache the scope list */
40
        cacheScopeList();
41
    }
42
43
    /* 
44
     *   Are we a sub-phrase resolver?  This should return true if we're
45
     *   being used to resolve a sub-phrase of the main phrase. 
46
     */
47
    isSubResolver = nil
48
49
    /* 
50
     *   Reset the resolver - this can be called if we are to re-use the
51
     *   same resolver to resolve a list of noun phrases again.
52
     */
53
    resetResolver()
54
    {
55
        /* forget the equivalents we've resolved so far */
56
        equivs_ = nil;
57
    }
58
59
    /* get the action we're resolving */
60
    getAction() { return action_; }
61
62
    /* get the target actor */
63
    getTargetActor() { return actor_; }
64
65
    /*
66
     *   Match an object's name.  By default, we'll call the object's own
67
     *   matchName method with the given original and adjusted token
68
     *   lists.  Subclasses can override this to call different match
69
     *   methods (such as matchNameDisambig).  
70
     */
71
    matchName(obj, origTokens, adjustedTokens)
72
    {
73
        return obj.matchName(origTokens, adjustedTokens);
74
    }
75
    
76
    /*
77
     *   Get the resolver for qualifier phrases.  By default, this simply
78
     *   returns myself, since the resolver for qualifiers is in most
79
     *   contexts the same as the main resolver.
80
     *   
81
     *   This can be overridden in contexts where the qualifier resolver
82
     *   is different from the main resolver.  In general, when a
83
     *   sub-resolver narrows the scope for resolving a phrase, such as an
84
     *   exclusion list or a disambiguation response, we will want to
85
     *   resolve qualifiers in the context of the main resolution scope
86
     *   rather than the narrowed scope.  
87
     */
88
    getQualifierResolver() { return self; }
89
90
    /*
91
     *   Get the resolver for possessive phrases.  By default, we return a
92
     *   standard possessive resolver.  This can be overridden in contexts
93
     *   wher ethe possesive resolution context is special.  
94
     */
95
    getPossessiveResolver() { return new PossessiveResolver(self); }
96
97
    /*
98
     *   Cache the scope list for this object.  By default, we cache the
99
     *   standard physical scope list for our target actor.
100
     *   
101
     *   Note that if a subclass uses completely different rules for
102
     *   determining scope, it need not store a scope_ list at all.  The
103
     *   scope_ list is purely an implementation detail of the base
104
     *   Resolver class.  A subclass can use whatever internal
105
     *   implementation it wants, as long as it overrides objInScope() and
106
     *   getScopeList() to return consistent results.
107
     */
108
    cacheScopeList()
109
    {
110
        /* cache our actor's default scope list */
111
        scope_ = actor_.scopeList();
112
    }
113
114
    /*
115
     *   Determine if an object is in scope for the purposes of object
116
     *   resolution.  By default, we'll return true if the object is in our
117
     *   cached scope list - this ensures that we produce results that are
118
     *   consistent with getScopeList().
119
     *   
120
     *   Some subclasses might want to override this method to decide on
121
     *   scope without reference to a cached scope list, for efficiency
122
     *   reasons.  For example, if a command's scope is the set of all
123
     *   objects, caching the full list would take a lot of memory; to save
124
     *   the memory, you could override cacheScopeList() to do nothing at
125
     *   all, and then override objInScope() to return true - this will
126
     *   report that every object is in scope without bothering to store a
127
     *   list of every object.
128
     *   
129
     *   Be aware that if you override objInScope(), you should ensure that
130
     *   getScopeList() yields consistent results.  In particular,
131
     *   objInScope() should return true for every object in the list
132
     *   returned by getScopeList() (although getScopeList() doesn't
133
     *   necessarily have to return every object for which objInScope() is
134
     *   true).  
135
     */
136
    objInScope(obj) { return scope_.indexOf(obj) != nil; }
137
138
    /*
139
     *   Get the full list of objects in scope.  By default, this simply
140
     *   returns our cached scope list.
141
     *   
142
     *   For every object in the list that getScopeList() returns,
143
     *   objInScope() must return true.  However, getScopeList() need not
144
     *   return *all* objects that are in scope as far as objInScope() is
145
     *   concerned - it can, but a subset of in-scope objects is
146
     *   sufficient.
147
     *   
148
     *   The default implementation returns the complete set of in-scope
149
     *   objects by simply returning the cached scope list.  This is the
150
     *   same scope list that the default objInScope() checks, which
151
     *   ensures that the two methods produce consistent results.
152
     *   
153
     *   The reason that it's okay for this method to return a subset of
154
     *   in-scope objects is that the result is only used to resolve
155
     *   "wildcard" phrases in input, and such phrases don't have to expand
156
     *   to every possible object.  Examples of wildcard phrases include
157
     *   ALL, missing phrases that need default objects, and locational
158
     *   phrases ("the vase on the table" - which isn't superficially a
159
     *   wildcard, but implicitly contains one in the form of "considering
160
     *   only everything on the table").  It's perfectly reasonable for the
161
     *   parser to expand a wildcard based on what's actually in sight, in
162
     *   mind, or whatever's appropriate.  So, in cases where you define an
163
     *   especially expansive objInScope() - for example, a universal scope
164
     *   like the one TopicResolver uses - it's usually fine to use the
165
     *   default definition of getScopeList(), which returns only the
166
     *   objects that are in the smaller physical scope.  
167
     */
168
    getScopeList() { return scope_; }
169
170
    /*
171
     *   Is this a "global" scope?  By default, the scope is local: it's
172
     *   limited to what the actor can see, hear, etc.  In some cases, the
173
     *   scope is broader, and extends beyond the senses; we call those
174
     *   cases global scope.
175
     *   
176
     *   This is an advisory status only.  The caller musn't take this to
177
     *   mean that everything is in scope; objInScope() and getScopeList()
178
     *   must still be used to make the exact determination of what objects
179
     *   are in scope.  However, some noun phrase productions might wish to
180
     *   know generally whether we're in a local or global sort of scope,
181
     *   so that they can adjust their zeal at reducing ambiguity.  In
182
     *   cases of global scope, we generally want to be more inclusive of
183
     *   possible matches than in local scopes, because we have much less
184
     *   of a basis to guess about what the player might mean.
185
     */
186
    isGlobalScope = nil
187
188
    /*
189
     *   Get the binding for a reflexive third-person pronoun (himself,
190
     *   herself, itself, themselves).  By default, the reflexive binding
191
     *   is the anaphoric binding from the action - that is, it refers
192
     *   back to the preceding noun phrase in a verb phrase with multiple
193
     *   noun slots (as in ASK BOB ABOUT HIMSELF: 'himself' refers back to
194
     *   'bob', the previous noun phrase).  
195
     */
196
    getReflexiveBinding(typ) { return getAction().getAnaphoricBinding(typ); }
197
198
    /*
199
     *   Resolve a pronoun antecedent, given a pronoun selector.  This
200
     *   returns a list of ResolveInfo objects, for use in object
201
     *   resolution.  'poss' is true if this is a possessive pronoun (his,
202
     *   her, its, etc), nil if it's an ordinary, non-possessive pronoun
203
     *   (him, her, it, etc).  
204
     */
205
    resolvePronounAntecedent(typ, np, results, poss)
206
    {
207
        local lst;
208
        local scopeLst;
209
210
        /* check the Action for a special override for the pronoun */
211
        lst = getAction().getPronounOverride(typ);
212
213
        /* if there's no override, get the standard raw antecedent list */
214
        if (lst == nil)
215
            lst = getRawPronounAntecedent(typ);
216
217
        /* if there is no antecedent, return an empty list */
218
        if (lst != nil && lst != [])
219
        {
220
            local cur;
221
222
            /* if it's a single object, turn it into a list */
223
            if (dataType(lst) == TypeObject)
224
                lst = [lst];
225
226
            /* add any extra objects for the pronoun binding */
227
            foreach (cur in lst)
228
                lst = cur.expandPronounList(typ, lst);
229
230
            /* filter the list to keep only in-scope objects */
231
            scopeLst = new Vector(lst.length());
232
            foreach (cur in lst)
233
            {
234
                local facets;
235
                
236
                /* get the object's facets */
237
                facets = cur.getFacets();
238
239
                /* 
240
                 *   If it has any, pick the best one that's in scope.  If
241
                 *   not, keep the object only if it's in scope. 
242
                 */
243
                if (facets.length() != 0)
244
                {
245
                    local best;
246
                    
247
                    /* 
248
                     *   This object has other facets, so we want to
249
                     *   consider the other in-scope facets in case any are
250
                     *   more suitable than the original one.  For example,
251
                     *   we might have just referred to a door, and then
252
                     *   traveled through the door to an adjoining room.
253
                     *   We now want the antecedent to be the side (facet)
254
                     *   of the door that's in the new location.  
255
                     */
256
257
                    /* get the in-scope subset of the facets */
258
                    facets = (facets + cur).subset({x: objInScope(x)});
259
260
                    /* keep the best facet from the list */
261
                    best = findBestFacet(actor_, facets);
262
263
                    /* 
264
                     *   If we found a winner, use it instead of the
265
                     *   original.  
266
                     */
267
                    if (best != nil)
268
                        cur = best;
269
                }
270
271
                /* if the object is in scope, include it in the results */
272
                if (objInScope(cur))
273
                    scopeLst.append(cur);
274
            }
275
276
            /* create a list of ResolveInfo objects from the antecedents */
277
            lst = scopeLst.toList().mapAll({x: new ResolveInfo(x, 0)});
278
        }
279
280
        /* 
281
         *   If there's nothing matching in scope, try to find a default.
282
         *   Look to see if there's a unique default object matching the
283
         *   pronoun, and select it if so. 
284
         */
285
        if (lst == nil || lst == [])
286
            lst = getPronounDefault(typ, np);
287
        
288
        /* run the normal resolution list filtering on the list */
289
        lst = action_.finishResolveList(lst, whichObject, np, nil);
290
291
        /* return the result */
292
        return lst;
293
    }
294
295
    /*
296
     *   Get the "raw" pronoun antecedent list for a given pronoun
297
     *   selector.  This returns a list of objects matching the pronoun.
298
     *   The list is raw in that it is given as a list of game objects
299
     *   (not ResolveInfo objects), and it isn't filtered for scope.  
300
     */
301
    getRawPronounAntecedent(typ)
302
    {
303
        /* check for pronouns that are relative to the issuer or target */
304
        switch(typ)
305
        {
306
        case PronounMe:
307
            /*
308
             *   It's a first-person construction.  If the issuing actor is
309
             *   the player character, and we don't treat you/me as
310
             *   interchangeable, this refers to the player character only
311
             *   if the game refers to the player character in the second
312
             *   person (so, if the game calls the PC "you", the player
313
             *   calls the PC "me").  If the issuing actor isn't the player
314
             *   character, then a first-person pronoun refers to the
315
             *   command's issuer.  If we allow you/me mixing, then "me"
316
             *   always means the PC in input, no matter how the game
317
             *   refers to the PC in output.
318
             */
319
            if (issuer_.isPlayerChar
320
                && issuer_.referralPerson != SecondPerson
321
                && !gameMain.allowYouMeMixing)
322
            {
323
                /* 
324
                 *   the issuer is the player, but the game doesn't call
325
                 *   the PC "you", so "me" has no meaning 
326
                 */
327
                return [];
328
            }
329
            else
330
            {
331
                /* "me" refers to the command's issuer */
332
                return [issuer_];
333
            }
334
335
        case PronounYou:
336
            /*
337
             *   It's a second-person construction.  If the target actor is
338
             *   the player character, and we don't treat you/me as
339
             *   interchangeable, this refers to the player character only
340
             *   if the game refers to the player character in the first
341
             *   person (so, if the game calls the PC "me", then the player
342
             *   calls the PC "you").  If we allow you/me mixing, "you" is
343
             *   always the PC in input, no matter how the game refers to
344
             *   the PC in output.
345
             *   
346
             *   If the target actor isn't the player character, then a
347
             *   second-person pronoun refers to either the target actor or
348
             *   to the player character, depending on the referral person
349
             *   of the current command that's targeting the actor.  If the
350
             *   command is in the second person, then a second-person
351
             *   pronoun refers to the actor ("bob, hit you" means for Bob
352
             *   to hit himself).  If the command is in the third person,
353
             *   then a second-person pronoun is a bit weird, but probably
354
             *   refers to the player character ("tell bob to hit you"
355
             *   means for Bob to hit the PC).  
356
             */
357
            if (actor_.isPlayerChar
358
                && actor_.referralPerson != FirstPerson
359
                && !gameMain.allowYouMeMixing)
360
            {
361
                /* 
362
                 *   the target is the player character, but the game
363
                 *   doesn't call the PC "me", so "you" has no meaning in
364
                 *   this command 
365
                 */
366
                return [];
367
            }
368
            else if (actor_.commandReferralPerson == ThirdPerson)
369
            {
370
                /* 
371
                 *   we're addressing the actor in the third person, so YOU
372
                 *   probably doesn't refer to the target actor; the only
373
                 *   other real possibility is that it refers to the player
374
                 *   character 
375
                 */
376
                return [gPlayerChar];
377
            }
378
            else
379
            {
380
                /* in other cases, "you" refers to the command's target */
381
                return [actor_];
382
            }
383
384
        default:
385
            /* 
386
             *   it's not a relative pronoun, so ask the target actor for
387
             *   the antecedent based on recent commands 
388
             */
389
            return actor_.getPronounAntecedent(typ);
390
        }
391
    }
392
393
    /*
394
     *   Determine if "all" is allowed for the noun phrase we're resolving.
395
     *   By default, we'll just ask the action.  
396
     */
397
    allowAll()
398
    {
399
        /* ask the action to determine whether or not "all" is allowed */
400
        return action_.actionAllowsAll;
401
    }
402
403
    /*
404
     *   Get the "all" list - this is the list of objects that we should
405
     *   use when the object of the command is the special word "all".
406
     *   We'll ask the action to resolve 'all' for the direct object,
407
     *   since we are by default a direct object resolver.
408
     */
409
    getAll(np)
410
    {
411
        /* 
412
         *   ask the action to resolve 'all' for the direct object, and
413
         *   then filter the list and return the result 
414
         */
415
        return filterAll(action_.getAllDobj(actor_, getScopeList()),
416
                         DirectObject, np);
417
    }
418
419
    /*
420
     *   Filter an 'all' list to remove things that don't belong.  We
421
     *   always remove the actor executing the command, as well as any
422
     *   objects explicitly marked as hidden from 'all' lists.
423
     *   
424
     *   Returns a ResolveInfo list, with each entry marked with the
425
     *   MatchedAll flag.  
426
     */
427
    filterAll(lst, whichObj, np)
428
    {
429
        local result;
430
431
        /* set up a vector to hold the result */
432
        result = new Vector(lst.length());
433
434
        /* 
435
         *   run through the list and include elements that we don't want
436
         *   to exclude 
437
         */
438
        foreach (local cur in lst)
439
        {
440
            /* 
441
             *   if this item isn't the actor, and isn't marked for
442
             *   exclusion from 'all' lists in general, include it 
443
             */
444
            if (cur != actor_ && !cur.hideFromAll(getAction()))
445
                result.append(cur);
446
        }
447
448
        /* 
449
         *   create a ResolveInfo for each object, with the 'MatchedAll'
450
         *   flag set for each object 
451
         */
452
        result.applyAll({x: new ResolveInfo(x, MatchedAll)});
453
454
        /* run through the list and apply each object's own filtering */
455
        result = getAction().finishResolveList(result, whichObject, np, nil);
456
457
        /* return the result as a list */
458
        return result.toList();
459
    }
460
461
    /*
462
     *   Get the list of potential default objects.  This is simply the
463
     *   basic 'all' list, not filtered for exclusion with hideFromAll.  
464
     */
465
    getAllDefaults()
466
    {
467
        local lst;
468
        
469
        /* ask the action to resolve 'all' for the direct object */
470
        lst = action_.getAllDobj(actor_, getScopeList());
471
472
        /* return the results as ResolveInfo objects */
473
        return lst.mapAll({x: new ResolveInfo(x, 0)});
474
    }
475
476
    /*
477
     *   Filter an ambiguous list of objects ('lst') resolving to a noun
478
     *   phrase.  If the objects in the list vary in the degree of
479
     *   suitability for the command, returns a list consisting only of the
480
     *   most suitable objects.  If the objects are all equally suitable -
481
     *   or equally unsuitable - the whole list should be returned
482
     *   unchanged.
483
     *   
484
     *   'requiredNum' is the number of objects required in the final list
485
     *   by the caller; if the result list is larger than this, the caller
486
     *   will consider the results ambiguous.
487
     *   
488
     *   'np' is the noun phrase production that we're resolving.  This is
489
     *   usually a subclass of NounPhraseProd.
490
     *   
491
     *   This routine does NOT perform any interactive disambiguation, but
492
     *   is merely a first attempt at reducing the number of matching
493
     *   objects by removing the obviously unsuitable ones.
494
     *   
495
     *   For example, for an "open" command, if the list consists of one
496
     *   object that's open and one object that's currently closed, the
497
     *   result list should include only the closed one, since it is
498
     *   obvious that the one that's already open does not need to be
499
     *   opened again.  On the other hand, if the list consists only of
500
     *   open objects, they should all be returned, since they're all
501
     *   equally unsuitable.
502
     *   
503
     *   It is not necessary to reduce the list to a single entry; it is
504
     *   adequate merely to reduce the ambiguity by removing any items that
505
     *   are clearly less suitable than the survivors.  
506
     */
507
    filterAmbiguousNounPhrase(lst, requiredNum, np)
508
    {
509
        return withGlobals(
510
            {:action_.filterAmbiguousDobj(lst, requiredNum, np)});
511
    }
512
513
    /*
514
     *   Filter an ambiguous noun phrase list using the strength of
515
     *   possessive qualification, if any.  If we have subsets at
516
     *   different possessive strengths, choose the strongest subset that
517
     *   has at least the required number of objects. 
518
     */
519
    filterPossRank(lst, num)
520
    {
521
        local sub1 = lst.subset({x: x.possRank_ >= 1});
522
        local sub2 = lst.subset({x: x.possRank_ >= 2});
523
524
        /* 
525
         *   sub2 is the subset with rank 2; if this meets our needs,
526
         *   return it.  If sub2 doesn't meet our needs, then check to see
527
         *   if sub1 does; sub1 is the subset with rank 1 or higher.  If
528
         *   neither subset meets our needs, use the original list.  
529
         */
530
        if (sub2.length() >= num)
531
            return sub2;
532
        else if (sub1.length() >= num)
533
            return sub1;
534
        else
535
            return lst;
536
    }
537
538
    /*
539
     *   Filter a list of ambiguous matches ('lst') for a noun phrase, to
540
     *   reduce each set of equivalent items to a single such item, if
541
     *   desired.  If no equivalent reduction is desired for this type of
542
     *   resolver, this can simply return the original list.
543
     *   
544
     *   'np' is the noun phrase production that we're resolving.  This is
545
     *   usually a subclass of NounPhraseProd.  
546
     */
547
    filterAmbiguousEquivalents(lst, np)
548
    {
549
        /* if we have only one item, there's obviously nothing redundant */
550
        if (lst.length() == 1)
551
            return lst;
552
        
553
        /* scan the list, looking for equivalents */
554
        for (local i = 1, local len = lst.length() ; i <= len ; ++i)
555
        {
556
            /* 
557
             *   if this item is marked as equivalent, check for others
558
             *   like it 
559
             */
560
            if (lst[i].obj_.isEquivalent)
561
            {
562
                /*
563
                 *   If this object is in our list of previously-used
564
                 *   equivalents, and we have more equivalents to this
565
                 *   object in our list, then omit this one, so that we
566
                 *   keep a different equivalent this time.  This way, if
567
                 *   we have a noun list such as "take coin and coin",
568
                 *   we'll return different equivalent items for each
569
                 *   equivalent noun phrase. 
570
                 */
571
                if (equivs_ != nil
572
                    && equivs_.indexOf(lst[i].obj_) != nil
573
                    && lst.lastIndexWhich(
574
                       {x: x.obj_.isVocabEquivalent(lst[i].obj_)}) > i)
575
                {
576
                    /* 
577
                     *   we've already returned this one, and we have
578
                     *   another equivalent later in the list that we can
579
                     *   use instead this time - remove this one from the
580
                     *   list 
581
                     */
582
                    lst = lst.removeElementAt(i);
583
584
                    /* adjust the our counters for the removal */
585
                    --len;
586
                    --i;
587
                }
588
                else
589
                {
590
                    /*
591
                     *   We've decided to keep this element, either
592
                     *   because we haven't already returned it as a match
593
                     *   for this noun phrase, or because it's the last
594
                     *   one of its kind.  Add it to the list of
595
                     *   equivalents we've previously returned. 
596
                     */
597
                    if (equivs_ == nil)
598
                        equivs_ = new Vector(10);
599
                    equivs_.append(lst[i].obj_);
600
601
                    /* 
602
                     *   check each object at a higher index to see if
603
                     *   it's equivalent to this one 
604
                     */
605
                    for (local j = i + 1 ; j <= len ; ++j)
606
                    {
607
                        /* check this object */
608
                        if (lst[i].obj_.isVocabEquivalent(lst[j].obj_))
609
                        {
610
                            /* they match - remove the other one */
611
                            lst = lst.removeElementAt(j);
612
                            
613
                            /* reduce the list length accordingly */
614
                            --len;
615
                            
616
                            /* back up our scanning index as well */
617
                            --j;
618
                        }
619
                    }
620
                }
621
            }
622
        }
623
624
        /* return the updated list */
625
        return lst;
626
    }
627
628
    /*
629
     *   Filter a plural phrase to reduce the set to the logical subset, if
630
     *   possible.  If there is no logical subset, simply return the
631
     *   original set.
632
     *   
633
     *   'np' is the noun phrase we're resolving; this is usually a
634
     *   subclass of PluralProd.  
635
     */
636
    filterPluralPhrase(lst, np)
637
    {
638
        return withGlobals({:action_.filterPluralDobj(lst, np)});
639
    }
640
641
    /*
642
     *   Select a resolution for an indefinite noun phrase ("a coin"),
643
     *   given a list of possible matches.  The matches will be given to
644
     *   us sorted from most likely to least likely, as done by
645
     *   filterAmbiguousNounPhrase().
646
     *   
647
     *   By default, we simply select the first 'n' items from the list
648
     *   (which are the most likely matches), because in most contexts, an
649
     *   indefinite noun phrase means that we should arbitrarily select
650
     *   any matching object.  This can be overridden for contexts in
651
     *   which indefinite noun phrases must be handled differently.  
652
     */
653
    selectIndefinite(results, lst, requiredNumber)
654
    {
655
        /* 
656
         *   arbitrarily choose the first 'requiredNumber' item(s) from
657
         *   the list 
658
         */
659
        return lst.sublist(1, requiredNumber);
660
    }
661
662
    /*
663
     *   Get the default object or objects for this phrase.  Returns a list
664
     *   of ResolveInfo objects if a default is available, or nil if no
665
     *   default is available.  This routine does not interact with the
666
     *   user; it should merely determine if the command implies a default
667
     *   strongly enough to assume it without asking the user.
668
     *   
669
     *   By default, we ask the action for a default direct object.
670
     *   Resolver subclasses should override this as appropriate for the
671
     *   specific objects they're used to resolve.  
672
     */
673
    getDefaultObject(np)
674
    {
675
        /* ask the action to provide a default direct object */
676
        return withGlobals({:action_.getDefaultDobj(np, self)});
677
    }
678
679
    /*
680
     *   Resolve a noun phrase involving unknown words, if possible.  If
681
     *   it is not possible to resolve such a phrase, return nil;
682
     *   otherwise, return a list of resolved objects.  This routine does
683
     *   not interact with the user - "oops" prompting is handled
684
     *   separately.
685
     *   
686
     *   'tokList' is the token list for the phrase, in the canonical
687
     *   format as returned from the tokenizer.  Each element of 'tokList'
688
     *   is a sublist representing one token.
689
     *   
690
     *   Note that this routine allows for specialized unknown word
691
     *   resolution separately from the more general matchName mechanism.
692
     *   The purpose of this method is to allow the specific type of
693
     *   resolver to deal with unknown words specially, rather than using
694
     *   the matchName mechanism.  This routine is called as a last
695
     *   resort, only after the matchName mechanism fails to find any
696
     *   matches.  
697
     */
698
    resolveUnknownNounPhrase(tokList)
699
    {
700
        /* by default, we can't resolve an unknown noun phrase */
701
        return nil;
702
    }
703
704
    /*
705
     *   Execute a callback function in the global context of our actor
706
     *   and action - we'll set gActor and gAction to our own stored actor
707
     *   and action values, then call the callback, then restore the old
708
     *   globals. 
709
     */
710
    withGlobals(func)
711
    {
712
        /* invoke the function with our action and actor in the globals */
713
        return withParserGlobals(issuer_, actor_, action_, func);
714
    }
715
716
    /* the role played by this object, if any */
717
    whichObject = DirectObject
718
719
    /*
720
     *   Get an indication of which object we're resolving, for message
721
     *   generation purposes.  By default, we'll indicate direct object;
722
     *   this should be overridden for resolvers of indirect and other
723
     *   types of objects.  
724
     */
725
    whichMessageObject = DirectObject
726
727
    /* 
728
     *   The cached scope list, if we have one.  Note that this is an
729
     *   internal implementation detail of the base class; subclasses can
730
     *   dispense with the cached scope list if they define their own
731
     *   objInScope() and getScopeList() overrides.
732
     *   
733
     *   Note that any subclasses (including Actions) that make changes to
734
     *   this list MUST ensure that the result only contains unique
735
     *   entries.  The library assumes in several places that there are no
736
     *   duplicate entries in the list; subtle problems can occur if the
737
     *   list contains any duplicates.  
738
     */
739
    scope_ = []
740
741
    /* my action */
742
    action_ = nil
743
744
    /* the issuing actor */
745
    issuer_ = nil
746
747
    /* the target actor object */
748
    actor_ = nil
749
750
    /* 
751
     *   List of equivalent objects we've resolved so far.  We use this to
752
     *   try to return different equivalent objects when multiple noun
753
     *   phrases refer to the same set of equivalents. 
754
     */
755
    equivs_ = nil
756
;
757
758
/* ------------------------------------------------------------------------ */
759
/*
760
 *   Proxy Resolver - this is used to create resolvers that refer methods
761
 *   not otherwise overridden back to an underlying resolver 
762
 */
763
class ProxyResolver: object
764
    construct(origResolver)
765
    {
766
        /* remember my underlying resolver */
767
        self.origResolver = origResolver;
768
    }
769
770
    /* delegate methods we don't override to the underlying resolver */
771
    propNotDefined(prop, [args])
772
    {
773
        /* delegate the call to the original resolver */
774
        return origResolver.(prop)(args...);
775
    }
776
777
    /* base our possessive resolver on the proxy */
778
    getPossessiveResolver() { return new PossessiveResolver(self); }
779
;
780
781
/* ------------------------------------------------------------------------ */
782
/*
783
 *   Basic resolver for indirect objects 
784
 */
785
class IobjResolver: Resolver
786
    /* 
787
     *   we resolve indirect objects for message generation purposes
788
     */
789
    whichObject = IndirectObject
790
    whichMessageObject = IndirectObject
791
792
    /* resolve 'all' for the indirect object */
793
    getAll(np)
794
    {
795
        /* 
796
         *   ask the action to resolve 'all' for the indirect object, and
797
         *   then filter the list and return the result 
798
         */
799
        return filterAll(action_.getAllIobj(actor_, getScopeList()),
800
                         IndirectObject, np);
801
    }
802
803
    /* get all possible default objects */
804
    getAllDefaults()
805
    {
806
        local lst;
807
        
808
        /* ask the action to resolve 'all' for the indirect object */
809
        lst = action_.getAllIobj(actor_, getScopeList());
810
811
        /* return the results as ResolveInfo objects */
812
        return lst.mapAll({x: new ResolveInfo(x, 0)});
813
    }
814
815
    /* filter an ambiguous noun phrase */
816
    filterAmbiguousNounPhrase(lst, requiredNum, np)
817
    {
818
        return withGlobals(
819
            {:action_.filterAmbiguousIobj(lst, requiredNum, np)});
820
    }
821
822
    /*
823
     *   Filter a plural phrase to reduce the set to the logical subset,
824
     *   if possible.  If there is no logical subset, simply return the
825
     *   original set.  
826
     */
827
    filterPluralPhrase(lst, np)
828
    {
829
        return withGlobals({:action_.filterPluralIobj(lst, np)});
830
    }
831
832
    /*
833
     *   Get the default object or objects for this phrase.  Since we
834
     *   resolve indirect objects, we'll ask the action for a default
835
     *   indirect object.  
836
     */
837
    getDefaultObject(np)
838
    {
839
        /* ask the action to provide a default indirect object */
840
        return withGlobals({:action_.getDefaultIobj(np, self)});
841
    }
842
;
843
844
/* ------------------------------------------------------------------------ */
845
/*
846
 *   Basic topic qualifier resolver.  This can be used to resolve qualifier
847
 *   phrases (such as possessives or locationals) within topic phrases.
848
 */
849
class TopicQualifierResolver: Resolver
850
    getAll(np)
851
    {
852
        /* 'all' doesn't make sense as a qualifier; return an empty list */
853
        return [];
854
    }
855
856
    getAllDefaults()
857
    {
858
        /* we don't need defaults for a qualifier */
859
        return [];
860
    }
861
862
    filterAmbiguousNounPhrase(lst, requiredNum, np)
863
    {
864
        /* we have no basis for any filtering; return the list unchanged */
865
        return lst;
866
    }
867
868
    filterPluralPhrase(lst, np)
869
    {
870
        /* we have no basis for any filtering */
871
        return lst;
872
    }
873
874
    getDefaultObject(np)
875
    {
876
        /* have have no way to pick a default */
877
        return nil;
878
    }
879
;
880
881
/* ------------------------------------------------------------------------ */
882
/*
883
 *   Actor Resolver.  We use this to resolve the actor to whom a command
884
 *   is directed: the actor must be in scope for the player character.  
885
 */
886
class ActorResolver: Resolver
887
    construct(issuingActor)
888
    {
889
        /* remember the issuing actor */
890
        actor_ = issuingActor;
891
892
        /* 
893
         *   Use our pseudo-action for "command actor" - this represents
894
         *   the intermediate step where the issuing actor is doing
895
         *   whatever physical activity is needed (such as talking) to give
896
         *   the command to the target actor.  This isn't a real action;
897
         *   it's just an implied intermediate step in the overall action.
898
         *   We need this mostly because there are assumptions elsewhere in
899
         *   the resolution process that there's a valid Action object
900
         *   available.  
901
         */
902
        action_ = CommandActorAction;
903
904
        /* ...and the action needs an actor */
905
        action_.actor_ = actor_;
906
907
        /* cache the scope list for the actor who issued the command */
908
        cacheScopeList();
909
    }
910
911
    /*
912
     *   Get the "all" list - this is the list of objects that we should
913
     *   use when the object of the command is the special word "all".  By
914
     *   default, we'll return everything in scope.  
915
     */
916
    getAll(np)
917
    {
918
        /* we can't address 'all' */
919
        throw new ParseFailureException(&cannotAddressMultiple);
920
    }
921
922
    /* get the default object list */
923
    getAllDefaults()
924
    {
925
        /* there are no default actors */
926
        return [];
927
    }
928
929
    /*
930
     *   Filter an ambiguous list of objects.  We will filter according to
931
     *   which objects are most logical as targets of commands.  
932
     */
933
    filterAmbiguousNounPhrase(lst, requiredNum, np)
934
    {
935
        local likelyCnt;
936
937
        /* give each object in the list a chance to filter the list */
938
        lst = getAction().finishResolveList(lst, ActorObject,
939
                                            np, requiredNum);
940
        
941
        /*
942
         *   Run through the list and see how many objects are likely
943
         *   command targets.  
944
         */
945
        likelyCnt = 0;
946
        foreach (local cur in lst)
947
        {
948
            /* if it's a likely command target, count it */
949
            if (cur.obj_.isLikelyCommandTarget)
950
                ++likelyCnt;
951
        }
952
953
        /* 
954
         *   If some of the targets are likely and others aren't, and we
955
         *   have at least the required number of likely targets, keep
956
         *   only the likely ones.  If they're all likely or all unlikely,
957
         *   it doesn't help us because we still have no basis for
958
         *   choosing some over others; if removing unlikely ones would
959
         *   not give us enough to meet the minimum number required it
960
         *   also doesn't help, because we don't have a basis for
961
         *   selecting as many as are needed.  
962
         */
963
        if (likelyCnt != 0 && likelyCnt != lst.length()
964
            && likelyCnt >= requiredNum)
965
        {
966
            /* 
967
             *   we have a useful subset of likely ones - filter the list
968
             *   down to the likely subset 
969
             */
970
            lst = lst.subset({cur: cur.obj_.isLikelyCommandTarget});
971
        }
972
973
        /* return the result */
974
        return lst;
975
    }
976
977
    /*
978
     *   Filter a plural list 
979
     */
980
    filterPluralPhrase(lst, np)
981
    {
982
        /* 
983
         *   Use the same filtering that we use for ambiguous nouns.  This
984
         *   simply reduces the set to the likely command targets if any
985
         *   are likely command targets. 
986
         */
987
        return filterAmbiguousNounPhrase(lst, 1, np);
988
    }
989
990
    /* get a default object */
991
    getDefaultObject(np)
992
    {
993
        /* there is never a default for the target actor */
994
        return nil;
995
    }
996
997
    /* resolve a noun phrase involving unknown words */
998
    resolveUnknownNounPhrase(tokList)
999
    {
1000
        /* we can't resolve an unknown noun phrase used as an actor target */
1001
        return nil;
1002
    }
1003
1004
    /* 
1005
     *   Get a raw pronoun antecedent list.  Since we are resolving the
1006
     *   target actor, pronouns are relative to the issuing actor.  
1007
     */
1008
    getRawPronounAntecedent(typ)
1009
    {
1010
        /* check for pronouns that are relative to the issuer */
1011
        switch(typ)
1012
        {
1013
        case PronounMe:
1014
            /*
1015
             *   It's a first-person construction.  If the issuing actor
1016
             *   is the player character, and the PC is in the second
1017
             *   person, this refers to the player character (the game
1018
             *   calls the PC "you", so the player calls the PC "me").  If
1019
             *   the issuing actor is an NPC, this is unconditionally the
1020
             *   PC.  
1021
             */
1022
            if (actor_.isPlayerChar && actor_.referralPerson != SecondPerson)
1023
                return [];
1024
            else
1025
                return [actor_];
1026
1027
        case PronounYou:
1028
            /*
1029
             *   It's a second-person construction.  If the issuer is the
1030
             *   player character, and the player character is in the
1031
             *   first person, this refers to the player character (the
1032
             *   game calls the PC "me", so the player calls the PC
1033
             *   "you").  If the issuer isn't the player character, "you"
1034
             *   has no meaning.  
1035
             */
1036
            if (!actor_.isPlayerChar || actor_.referralPerson != FirstPerson)
1037
                return [];
1038
            else
1039
                return [actor_];
1040
1041
        default:
1042
            /* 
1043
             *   it's not a relative pronoun, so ask the issuing actor for
1044
             *   the antecedent based on recent commands 
1045
             */
1046
            return actor_.getPronounAntecedent(typ);
1047
        }
1048
    }
1049
1050
    /* we resolve target actors */
1051
    whichObject = ActorObject
1052
    whichMessageObject = ActorObject
1053
;
1054
1055
/*
1056
 *   A pseudo-action for "command actor."  This represents the act of one
1057
 *   actor (usually the PC) giving a command to another, as in "BOB, GO
1058
 *   NORTH".  This isn't a real action that the player can type; it's just
1059
 *   an internal construct that we use to represent the partially resolved
1060
 *   action, when we know that we're addressing another actor but we're
1061
 *   still working on figuring out what we're saying.  
1062
 */
1063
class CommandActorAction: Action
1064
;
1065
1066
/* ------------------------------------------------------------------------ */
1067
/*
1068
 *   A possessive resolver is a proxy to a main resolver that considers an
1069
 *   object in scope if (a) it's in scope in the base resolver, or (b) the
1070
 *   object is known to the actor. 
1071
 */
1072
class PossessiveResolver: ProxyResolver
1073
    objInScope(obj)
1074
    {
1075
        /* 
1076
         *   An object is in scope for the purposes of a possessive phrase
1077
         *   if it's in scope in the base resolver, or it's known to the
1078
         *   actor.  An object is only in scope for a possessive qualifier
1079
         *   phrase if its canResolvePossessive property is true.  
1080
         */
1081
        return (obj.canResolvePossessive
1082
                && (origResolver.objInScope(obj)
1083
                    || origResolver.getTargetActor().knowsAbout(obj)));
1084
    }
1085
1086
    /* this is a sub-resolver */
1087
    isSubResolver = true
1088
;
1089