TADS/adv3 bug: >FOO, BAR throws an error

It appears that there’s a bug in adv3’s parser.t that chucks a wobbly whenever you do something like

>FOO, BAR
[Runtime error: nil object reference
->[many lines of stack trace deleted]
]

…where “foo” and “bar” are nearly any string literal (basically anything that won’t cause the parser to complain before it throws the error). I encountered it with >SAY, [literal string] but there’s a wide universe of inputs that produce the same behavior.

Is this a known issue? My google-fu didn’t turn up anything.

Appears to affect multiple interpreters (I tested on frobtads and parchment) and appears to be present in “real” games (e.g. the distributed version of Return to Ditch Day) as well as minimalistic hello world nonsense. Compilable example:

#charset "us-ascii"
#include <adv3.h>
#include <en_us.h>

startRoom:      Room 'Void'
        "This is a featureless void. "
;
+me:    Person;

versionInfo:    GameID
        name = 'sample'
        byline = 'nobody'
        authorEmail = 'nobody <foo@bar.com>'
        desc = '[This space intentionally left blank]'
        version = '1.0'
        IFID = '12345'
;
gameMain:       GameMainDef
        initialPlayerChar = me
;

Transcript:

>foo, bar
[Runtime error: nil object reference
->/usr/local/share/frobtads/tads3/lib/adv3/parser.t, line 4002
   /usr/local/share/frobtads/tads3/lib/adv3/parser.t, line 3921
   /usr/local/share/frobtads/tads3/lib/adv3/parser.t, line 1297
   /usr/local/share/frobtads/tads3/lib/adv3/parser.t, line 1297
   /usr/local/share/frobtads/tads3/lib/adv3/parser.t, line 2000
   /usr/local/share/frobtads/tads3/lib/adv3/parser.t, line 1297
   /usr/local/share/frobtads/tads3/lib/adv3/parser.t, line 1297
   /usr/local/share/frobtads/tads3/lib/adv3/parser.t, line 1297
   /usr/local/share/frobtads/tads3/lib/adv3/parser.t, line 1316
   /usr/local/share/frobtads/tads3/lib/adv3/en_us/en_us.t, line 5186
   /usr/local/share/frobtads/tads3/lib/adv3/exec.t, line 141
   <System>
   /usr/local/share/frobtads/tads3/lib/adv3/exec.t, line 141
   /usr/local/share/frobtads/tads3/lib/adv3/actor.t, line 10512
   /usr/local/share/frobtads/tads3/lib/adv3/actor.t, line 9417
   /usr/local/share/frobtads/tads3/lib/adv3/actor.t, line 9287
   /usr/local/share/frobtads/tads3/lib/adv3/pov.t, line 156
   /usr/local/share/frobtads/tads3/lib/adv3/pov.t, line 58
   /usr/local/share/frobtads/tads3/lib/adv3/actor.t, line 9287
   /usr/local/share/frobtads/tads3/lib/adv3/report.t, line 1941
   /usr/local/share/frobtads/tads3/lib/adv3/exec.t, line 1281
   /usr/local/share/frobtads/tads3/lib/adv3/actor.t, line 9287
   /usr/local/share/frobtads/tads3/lib/adv3/events.t, line 125
   /usr/local/share/frobtads/tads3/lib/adv3/misc.t, line 589
   /usr/local/share/frobtads/tads3/lib/adv3/misc.t, line 118
   /usr/local/share/frobtads/tads3/lib/adv3/misc.t, line 659
   /usr/local/share/frobtads/tads3/lib/adv3/misc.t, line 603
   /usr/local/share/frobtads/tads3/lib/_main.t, line 217
   /usr/local/share/frobtads/tads3/lib/_main.t, line 122
   /usr/local/share/frobtads/tads3/lib/_main.t, line 31
]

The contents of the stack trace will vary depending on the compile-time options; this is on a linux box compiled with t3make -d.

Replying to myself: Yes, it appears to be a known bug. Searching for the relevant lines from parser.t turned up a post in this forum from 2015 that suggests a workaround. I’ll repeat it here for the benefit of anyone in the future arriving here via search:

modify NounPhraseWithVocab
        resolveNounsMatchName(results, resolver, matchList) {
                try {
                        return inherited(results, resolver, matchList);
                }
                catch (RuntimeError e) {
                        // Runtime error 2203 is "nil object reference."
                        if (e.errno_ == 2203) {
                                results.noVocabMatch(resolver.getAction(),
                                        getOrigText());
                                return [];
                        }
                        // It wasn't a nil object reference. Re-throw the
                        // error so that it's not lost.
                        throw e;
                }
        }
;

That’s code originally provided by @RealNC.

I’ve hit that before… without going back to do any checking or remembering, I thought that the issue was in the phase where it was trying to resolve the actor. If the word before the comma can’t resolve to an actor which could receive a command, the error happens. That’s what I seem to remember. Seems like the fix should lie in the actor resolution process?

Crosslinking with another solution @BrettW provided.

modify TryAsActorResolveResults
    unknownNounPhrase(match, resolver) { return []; }
;

Both solutions seem to result in the same user message The story doesn't understand that command. Any reason to prefer one solution over the other? If I’m following things right, Brett’s solution provides an expected return value and so avoids the error, while the former traps and masks it? The mask feels less elegant?

1 Like