Unfortunately, I’m not sure exactly what I meant by that – it’s been a couple of months and I’ve forgotten a lot of the details that were in mind at the time.
There’s not much in DM4 about ParseNoun()
. Looking it over and checking my notes, nothing is prompting memories of specific remaining issues. I may have just been hedging my bets because there was the possibility of interaction with other parts of the code with which I’m not familiar.
I did find my notes describing the exact nature of the problem with the current code, which the posted fix is intended to address. For the benefit of @DavidG, they are as follows:
After the call to ParseNoun()
from TryGivenObject()
, there’s a line
if (threshold < 0) wn++;
that advances wn
by 1 when ParseNoun()
has returned a negative number.
This doesn’t make any sense until one looks at the logic for checking .name
parameters later in the routine. There’s an odd order of operations that works out if and only if wn
was undisturbed by the call to ParseNoun()
.
The later line
if (threshold == 0 || Refers(obj,wn-1) == 0) {
compensates for the forced advancement, checking that the word under wn
as it was immediately after return from ParseNoun()
can be found in the object’s .name
property.
So far, so good. However, things get out of sync in the next block:
if (threshold < 0) {
threshold = 1;
dict_flags_of_noun = (w->#dict_par1) & (DICT_X654+DICT_PLUR);!$$01110100;
w = NextWord();
while (Refers(obj, wn-1)) {
threshold++;
if (w)
dict_flags_of_noun = dict_flags_of_noun | ((w->#dict_par1) & (DICT_X654+DICT_PLUR));
w = NextWord();
}
}
The code assumes that the previous Refers()
check cleared the first word of the noun phrase, so threshold
can be set to 1 to reflect this.
The code also assumes that dict_flags_of_noun
can be meaningfully set from the contents of local variable w
, which is not the case if ParseNoun()
has moved wn
before returning -1. The code implicitly expects that the word loaded into local variable w
(by an earlier call to NounWord()
) is the first word of the noun phrase, but note that the contents of w
have not been updated since the earlier call to NounWord()
, before the call to ParseNoun()
. (It is an OK assumption if wn
was not disturbed by ParseNoun()
.)
The code then updates w
with a call to NextWord()
. If wn
was undisturbed by ParseNoun()
, this would load the second word of the noun phrase. If wn
was moved by ParseNoun()
, then a fault is introduced. (For example, if wn
is left at the last word of input.)
Consider the case where ParseNoun()
has been written to skip the word 'your'
if the actor is the holder of an object. The command >BOB, DROP YOUR PENCIL will get a call to TryGivenObject()
with wn
set to 4 (pointing to 'your'
). The ParseNoun()
routine always returns -1 (to allow normal processing of the .name
property), but when the word is not 'your'
it rewinds wn
to put it back to the same place that it was when it was called.
After line
threshold = ParseNoun(obj);
then threshold
is -1 and wn
is 5 (because it is deliberately skipping a word). The line
if (threshold < 0) wn++;
increments wn
to 6. Then the condition in line
if (threshold == 0 || Refers(obj,wn-1) == 0) {
evaluates to false (as desired), because the second parameter of the Refers()
check evaluates to 5, and this points to 'pencil'
which will return true
for the pencil
object. But when it gets to
if (threshold < 0) {
threshold = 1;
dict_flags_of_noun = (w->#dict_par1) & (DICT_X654+DICT_PLUR);!$$01110100;
w = NextWord();
while (Refers(obj, wn-1)) {
the if
block is entered, threshold
is set to 1 (OK in this case), dict_flags_of_noun
is updated based on 'your'
(also OK in this case), but then w
is set to 0 (because wn
is at 6 before the call to NextWord()
, past end of input) and wn
is increased to 7. The while
loop check attempts to look at word 6 of input (which is not there), fails, and TryGivenObject()
returns 1. This is an insufficient number of words to consume the whole noun phrase, resulting in the parser’s confusion that is noted in the original post for this thread. (In this case, the grammar for ##Drop
fails and the command is converted to an ##Answer
action.)
For >BOB, DROP PENCIL, everything works out. ParseNoun()
leaves wn
at 4 ('pencil'
). Then wn
is incremented to 5. The first Refers(obj, 4)
check sees 'pencil'
and passes. Local variable threshold
is set to 1 to reflect this. The next call to NextWord()
returns 0, so the while
loop is skipped. TryGivenObject()
returns 1 (consuming the single word 'pencil'
), and the grammar line for ##Drop
parses OK.