Consulting a topic

What am I doing wrong (or is this a bug?)

A testproject:

Constant Story "TEST - LIBRARY";
Constant Headline "^A test^";
Release 1;

Include "Parser";
Include "VerbLib";

[ Initialise; ;    location = Library; ];

Object   Library "Library"    
  with  name 'library',
        description "This is the library",
  has   light;

Object -> book "Telephone book"      
  with  name 'book' 'telephone',
        before [ w ;
            Consult:
                wn = consult_from;
                w = NextWord();
                if( w == 'me' or 'myself' or 'self' ) {
                    "111-1234567";
                }
                if( w == 'peter' )
                    "222-7654321";
                "There is no listing.";
        ],
        description "Just an ordinary telephone book.",
        article "the",
  has  ;

Include "Grammar";

Generates this transcript:

TEST - LIBRARY
A test
Release 1 / Serial number 220902 / Inform v Library v6.12.6 

Library
This is the library

You can see the Telephone book here.

get book
Taken.

read it
Just an ordinary telephone book.

look up me in book
111-1234567

look up peter in book
222-7654321

look up me in book. look up peter in book
222-7654321
222-7654321

To me it seems like consult_from points to wrong word in the last exchange.

3 Likes

It looks like consult_from and consult_words are being set correctly by the parser, but then the command buffer is being wiped of the first command and then retokenised prior to action processing, so the original dictionary word being pointed to by consult_from is no longer in either the parse array or the command buffer when you try to use it in your code. Because the two commands have the same structure, your code picks up the topic word in the second command.

The confusion of entries isn’t a given, but the confusion of words is. Entering commands >LOOK UP ME IN BOOK. JUMP. LOOK UP PETER IN BOOK. leaves the word 'look' in position 3 after retokenisation.

I’m not sure that it’s possible to get access to the topic word(s) of the first ##Consult action in this scenario without modification of the parser.

To expand on some details:

The initial setting of consult_from and consult_words occurs during Parser section G, which calls ParseToken__() as part of its work. In ParseToken__() section A:

      TOPIC_TOKEN:
        consult_from = wn;  ! *** sets consult_from ***
        if ((line_ttype-->(token_n+1) ~= PREPOSITION_TT) &&
           (line_token-->(token_n+1) ~= ENDIT_TOKEN))
            RunTimeError(13); ! "A 'topic' token can only be followed by a preposition"
        do o = NextWordStopped(); ! advance past potentially meaningful words
        until (o == -1 || PrepositionChain(o, token_n+1) ~= -1);
        wn--;
        consult_words = wn-consult_from;  ! *** sets consult_words ***
        if (consult_words == 0) return GPR_FAIL;
        if (action_to_be == ##Ask or ##Answer or ##Tell) { ! for some verbs: set noun as dictionary word, stripping one leading 'the' 
            o = wn; wn = consult_from; parsed_number = NextWord();
            #Ifdef EnglishNaturalLanguage;
            if (parsed_number == 'the' && consult_words > 1) parsed_number=NextWord();
            #Endif; ! EnglishNaturalLanguage
            wn = o; return 1;
        }
        if (o==-1 && (line_ttype-->(token_n+1) == PREPOSITION_TT))
            return GPR_FAIL;    ! don't infer if required preposition is absent
        return GPR_PREPOSITION; ! signal successful parsing of topic

Parsing continues as normal until the command has been fully parsed. Then Parser section G takes notice that the player’s command includes multiple commands:

            ! However, if the superfluous text begins with a comma or "then" then
            ! take that to be the start of another instruction

            if (wn <= num_words) {
                l = NextWord();
                if (l == THEN1__WD or THEN2__WD or THEN3__WD or comma_word) {
                    held_back_mode = 1; hb_wn = wn-1; ! *** sets held_back_mode flag ***
                }
                else {
                    for (m=0 : m<32 : m++) pattern2-->m = pattern-->m;
                    pcount2 = pcount;
                    etype = UPTO_PE;
                    break;
                }
            }

Near the end of Parser section G, after the first command has been fully parsed, the code checks to see whether held_back_mode has been set, and, if so, do more work:

            ! ...and return from the parser altogether, having successfully matched
            ! a line.

            if (held_back_mode == 1) { ! *** flag still set ***
                wn=hb_wn;
                jump LookForMore;
            }
            rtrue;

This moves to Parser section K, which does not account for the case in your example code:

.LookForMore;

if (wn > num_words) rtrue;

i = NextWord();
if (i == THEN1__WD or THEN2__WD or THEN3__WD or comma_word) {
    if (wn > num_words) {
       held_back_mode = false;
       return;
    }
    i = WordAddress(verb_wordnum);
    j = WordAddress(wn);
    for (: i<j : i++) i->0 = ' '; ! *** wipes first command from command text buffer ***
    i = NextWord();
    if (i == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) {
        ! Delete the words "then again" from the again buffer,
        ! in which we have just realised that it must occur:
        ! prevents an infinite loop on "i. again"

        i = WordAddress(wn-2)-buffer;
        if (wn > num_words) j = INPUT_BUFFER_LEN-1;
        else j = WordAddress(wn)-buffer;
        for (: i<j : i++) buffer3->i = ' ';
    }
    Tokenise__(buffer,parse);
    held_back_mode = true;
    return;
}
best_etype = UPTO_PE;
jump GiveError;

and that’s where the root problem lies.

The only way to permanently solve this that I can see is to defer wiping the text buffer until after action processing, which would probably mean making this functionality part of Parser section A and adding some globals to preserve information between calls to the parser.

It appears that there is a related issue when the topic is placed at the end of the command grammar. In such a case, it seems that the parser doesn’t notice that a THEN*__WD is in the topic, so it just includes later commands in the parsed topic, e.g. >CONSULT BOOK ABOUT ME. CONSULT BOOK ABOUT PETER. is treated as a single command with consult_from == 4 and consult_words == 7.

Here is an almost totally untested proof of concept demonstrating that in theory these can be solved. WARNING: In all likelihood the quick and rough surgery performed here has created new problems.

helpboard.inf (52.4 KB)

However, it seems to work for your scenario:

>CONSULT BOOK ABOUT ME. CONSULT BOOK ABOUT PETER.
111-1234567
222-7654321

>LOOK UP ME IN BOOK. LOOK UP PETER IN BOOK.
111-1234567
222-7654321

>LOOK UP ME IN BOOK. JUMP. CONSULT BOOK ABOUT PETER.
111-1234567
You jump on the spot, fruitlessly.
222-7654321
3 Likes

Today I wondered whether your issue persists in the I7 6M62 version of the parser. What I found was:

  1. The functionality for wiping the first command has already been moved to Parser section A, so the bug from your test scenario does not occur, i.e. >LOOK UP ME IN BOOK. LOOK UP PETER IN BOOK. works correctly.
  2. The issue with creating “run-on” topics when the topic is the last part of the grammar still occurs there, i.e. >CONSULT BOOK ABOUT ME. CONSULT BOOK ABOUT PETER. does not work there, either.

@DavidG, you may want to review the changes to Parser Letter A and Parser Letter K in the 6M62 parser code to incorporate the fix for #1 into StdLib 6.12.

1 Like

Great work! It seem like I stumbled over a real issue (no a biggie, but anyway…). Its been like this at least since 6.11 (it worked in 5.12) but I noticed it when I playtested the recreated Curses!-code. The original is in 5.12 and the recreated uses 6.11.

Could you file it as an issue at the GitLab-project (you have much better grasp of the problem)?

I don’t have a GitLab account, but I will pass the word along.

1 Like

Thanks. I’m on it. (edit) Nope. As @fredrik pointed out, this does not refer to the problem.

1 Like

I found a small problem with the solution to the below:

I noticed it when play-testing Curses! and have the following exchange:

>say odd.
I only understood you as far as wanting to say.

>say odd
(to Madame Sosostris, famous clairvoyante)
Madame Sosostris picks up the cards and deals them onto the table. She frowns in concentration as the Grim Reaper, the Fool and then the Drowned Sailor show. She blanches with alarm, then recovers and jabbers something about not taking the cards too literally, the images being symbolic and so on. As rapidly as she possibly can, she warns you to fear death by water and then hustles you out onto the street.

Unreal City
This is a city side street, but as if seen through the grey of despair. People stream by, some of whom you almost recognise, as if dead. The street runs east-west, and to the north is a doorway into a grubby tenement building.

The period at the end of the sentence breaks something. It can also be observed in the helpboard.inf example:

Library
This is the library

You can see the Telephone book here.

>say book
(to yourself)
There is no reply.

>say book.
I didn't understand that sentence.

>say book to me.get it.
There is no reply.
Taken.

>say book. drop it.
I didn't understand that sentence.

EDIT: It’s part of an overall problem where a period breaks “infering” and it’s not specific to the “topic”-fix. It just surfaced when the first fix was in.

Library
This is the library

You can see the Telephone book here.

>get
(out of the Telephone book)
You aren't in the Telephone book.

>get.
You can't see any such thing.

EDIT 2: This is from a recent Inform7 game:

Of Their Shadows Deep
An Interactive Fiction by Amanda Walker
Release 1 / Serial number 220630 / Inform 7 build 6M62 (I6/v6.34 lib 6/12N) 
 
Winter Woods
Bare trees are tangled with greenbriar hanging in thorny curtains. Birds call. They flash bright against the naked branches: cardinal screams red; goldfinch blazes sun. The rocky path limned with ice is at the base of the winding stone steps that lead north and up to your home; or you can follow the path down and south into the ravine. 
 
There's a torn piece of white paper caught on a low branch of a tree, fluttering in the chilly breeze. 
 
>get paper
Taken. Your heart leaps as you scan the torn piece of paper. It's covered with her lost words. You came here for firewood, but maybe you can find what is lost, give language back to her. The paper reads:
 
o, i hunt. 
silken, deadly,
such intent darkness
treading so softly,
eyes bright,
soft companion,
little tiger. 
 
What am I? 
 
Every time you see a riddle like this one, answer it with SAY answer. The answer will always be a single word. You can type HINT if you need one.
 
>read
(the torn piece of paper with some writing on it)
Your heart leaps as you scan the torn piece of paper. It's covered with her lost words. You came here for firewood, but maybe you can find what is lost, give language back to her. The paper reads:
 
o, i hunt. 
silken, deadly,
such intent darkness
treading so softly,
eyes bright,
soft companion,
little tiger. 
 
What am I? 
 
>read.
You don't see that here.

This seems to be a different issue than the one you link to though.

After scrutinizing this behavior for a while, my guess is that it would count as “working as designed,” even though the effect appears to be inconsistent.

The (to yourself) happens when the parser is trying to infer a missing object. This “infer mode” is only entered when the parser has run out of words in the player’s command. When the command is >SAY BOOK. the parser still sees the period as a word left to be processed, so it doesn’t think that it has run out of words, so it doesn’t try to infer.

It seemed like it should be easy enough to enable inference even when a command-separating word is encountered, but it was a long chase through the thickets to find the places that had to be changed… and who knows what other monsters have been disturbed in the process? Use at your own risk, and note that the unusual interpretation of >GET. still happens – I didn’t look into the cause but suspect that it has to do with the ordering of grammar lines.

topic-problems.inf.zip (15.2 KB)

Thank you for your work. I can’t imagine that it’s workong as intended, even if it’s working as designed. I can’t see any reason that you should get different behaviour if you type

say odd. get card.

instead of

say odd
get card

On the other hand, it’s a very minor inconvenience and maybe not worth rebuilding the whole parser to solve.

Inform has always had strange behavior around periods, because it assumes they’ll always be used the same way as “then”—as command separators, not command terminators. It’s also never coped very well with the American convention of putting dots after abbreviations, like “Mr. Green” instead of “Mr Green”.