[I6] Weird bugs when using no_implicit_actions

This is a problem with Inform 6, so please don’t give me Inform 7 solutions.

I hate implicit actions because they give so many stupid responses. I always use no_implicit_actions, but I have just struck some really weird bugs that have me baffled.

I want to be able to put an object in a container without having to first pick up the object. Let’s say the container is a bucket and I want to use the bucket to scoop up some acid or lava or even just water. You can’t get these types of objects before putting them in a container. I have a fill routine for the container. I just want PUT OBJECT IN CONTAINER to be diverted to <<Fill second noun>>. Here’s some sample code:

Include "parser";
Include "verblib";

[ Initialise;
  no_implicit_actions = true;
  location = stream;
];

Object stream "Stream"
with
  description "You are standing by a steam.",
has light;

Object water "water" stream
with
  name 'water',
  article "some",
  before
  [;
    Insert:
      print "Inserting...^";
      <<Fill second noun>>;
  ],
has;

Object rocks "rocks" stream
with
  name 'rocks//p',
  before
  [;
    Insert:
      print "Inserting...^";
      <<Fill second noun>>;
  ],
has pluralname;

Object bucket "bucket" stream
with
  name 'bucket',
  description "It's a metal bucket.",
  before
  [;
    Fill:
      move second to self;
      "You fill the bucket with ", (the)second, ".";
  ],
has container open;

Include "grammar";

Extend 'fill' first
  * noun 'with' noun -> Fill;

When I PUT ROCKS IN BUCKET, it says “I don’t understand that sentence.”, even though there’s nothing wrong with it.

When I PUT WATER IN BUCKET, it says “You aren’t holding that!”

When I get the bucket and the water, then PUT IT IN BUCKET, it works fine. When I FILL BUCKET WITH ROCKS or FILL BUCKET WITH WATER, it works fine. So my questions are:

  1. Why do PUT ROCKS and PUT WATER give different responses?

  2. Why don’t the two before:Insert routines get executed when you PUT WATER IN BUCKET or PUT ROCKS IN BUCKET when those objects aren’t held? The debugging print statements are never executed, so the parser is clearly deciding to issue one of two different error responses and abort before the before routine can be executed.

I haven’t used the 6.12 library (which introduced no_implicit_actions), so I can’t explain all of what’s going on. However, it seems clear that your bucket before:Fill handler never triggers an Insert action. It moves the object directly to the bucket. So there’s no reason for the before:Insert handler to run.

You wouldn’t want before:Insert to run anyhow! It calls the <<Fill second noun>> action. If the Fill action called the Insert action too, that would be an infinite loop and your interpreter would crash.

(If I’m misunderstanding the question, I apologize.)

1 Like

I have edited question 2 to try and make it a bit clearer.

Your observations are correct. FILL BUCKET WITH WATER runs the bucket’s before:Fill handler and everything works fine. That’s not the issue. When you PUT WATER IN BUCKET, it should run the water’s before:Insert handler which diverts to the bucket’s before:Fill handler, but the water’s before:Insert handler is not run if the water is not held (and the water can never be held in a real game, but not shown in the demo code). Instead, the parser prints one of two inconsistent error responses. Why does it do this at all and why are the error responses different for the water object and the rocks object? It just doesn’t make sense.

Based on a hunch and what I’ve observed in my tests, I think I might have found the problem. It’s a bug with the handling of the multiexcept token. As the grammar for PUT uses multiexcept, I tried replacing it with the following:

Extend 'put' first
  * noun 'in'/'inside'/'into' noun -> Insert;

And now everything works as it should. I can PUT WATER IN BUCKET, my water’s before:Insert routine is fired, this correctly diverts to the bucket’s before:Fill routine and everything is hunky dory. Ditto for PUT ROCKS IN BUCKET. The only downside is that I can no longer put multiple items in the bucket, not that I would want to anyway. Can the library maintainers please take a look at this issue, as I’m pretty sure it’s a bug?

Take a look at the before_implicit property. It works for me.

Object water "water" stream
with
  name 'water',
  article "some",
  before_implicit [;
    Take: if (action_to_be == ##Insert) return 1;
    return 3;
  ],
  before
  [;
    Insert:
      print "Inserting...^";
      <<Fill second noun>>;
  ],
has;

put water in bucket
Inserting…
You fill the bucket with the water.

I’ve tried without:

Extend 'put' first
  * noun 'in'/'inside'/'into' noun -> Insert;
1 Like

I might try that. I seem to recall having some trouble with before_implicit once before, but this might be a good time to revisit it. Do you know if it will work with the global no_implicit_actions = true? I’m wondering which one takes priority.

In this case, it seems to be working (I didn’t remove the no_implicit_actions = true). I had a little look, a while ago, at the no_implicit_actions, before_implicit property and so on combinations, and it was a real brainteaser, but David Griffith had done a few fixes, so it must work better.

I don’t understand why this works, but it does. I found the piece of code in parser.h that is doing the tests for before_implicit and no_implicit_actions prior to doing the implicit take. I changed my water and rocks objects in the sample code so that they can’t be taken under normal circumstances, expecting the implicit take to fail, but it didn’t fail. Go figure.

Unfortunately, this stuff is not documented anywhere (as far as I know), apart from in the release notes. I think the lesson to take away from this is that if you are using the global no_implicit_actions = true and you want to bypass this in specific situations, you can’t just divert one action to another, but must use before_implicit to allow the implicit action then do the diversion.

Damn. Just when I thought I had a hacky solution that worked, I tried applying it to a real game and it no longer works. I’m back where I started.

There must surely be a library maintainer out there who knows the rationale behind all this implicit stuff and how to get it working properly.

All I want to do is divert PUT WATER IN BUCKET to FILL BUCKET WITH WATER because the water cannot be held. Is that too much to ask?

Include "parser";
Include "verblib";

[ Initialise;
  no_implicit_actions = true;
  location = stream;
];

Object stream "Stream"
with
  description "You are standing by a steam.",
has light;

Object water "water" stream
with
  name 'water',
  article "some",
  before_implicit [;
    Take: if (action_to_be == ##Insert) return 1;
    return 3;
  ],
has;

Object bucket "bucket" stream
with
  name 'bucket',
  description "It's a metal bucket.",
has container open;

Include "grammar";

Extend 'fill' first
  * noun 'with' noun -> Insert reverse;
>fill bucket with water
You put the water into the bucket.

>fill bucket with water
The water is already here.

>put water in bucket   
You put the water into the bucket.

>put water in bucket
You put the water into the bucket.

There’s still a few things to worry about!

Thanks for trying to help, but the problem is a little deeper than I implied in my simple example. It’s actually the age old problem of global water vs local water. Let’s say I have a stream. The stream contains global water which is scenery. I can’t take the global water, as it trickles through my fingers. I need to put it in a bucket. The water in the bucket is the local water. I can FILL BUCKET WITH WATER, GET WATER WITH BUCKET and so on, no problem. (They basically make sure that the bucket is empty and move the local water to the bucket.) The only problem is that I think players are extremely likely to try PUT WATER IN BUCKET. This is an Insert action, but the library won’t let me divert an Insert action to a Fill action. The before_implicit doesn’t help, because that will implicitly get the global water, not the local water. As I write this, I’m wondering whether I should allow the implicit take of the global water, then swap the global water for the local water in the Fill routine. I don’t know whether that will work, because the global water is scenery, the local water isn’t.

All this stuff used to work in the old days prior to the introduction of implicit actions in version 6.12.

And no one has answered question 1 regarding the different error responses for the rocks and the water. None of this makes sense.

I have reverted to the grammar changes for ‘put’, ‘insert’ and related verbs. Now everything works as intended. I still can’t explain why the ‘noun’ token works, but the ‘multiexcept’ token doesn’t.

Multiexcept is a tricky beast. Does the Library still need my attention on this?

I think so. I’ve identified three issues related to no_implicit_actions:

  1. The noun token works where the multiexcept token doesn’t work. Multiexcept should effectively build a list of nouns and treat each one in turn. It should not abort prematurely in situations where the noun token does not abort.
  2. If an object provides a before handler for Insert, it should be run. The library should not abort prematurely before the before handler has a chance to run.
  3. If the library ignores the requirements of 1 and 2 above, it should not provide two different error responses for different objects for no apparent reason.

I’ve tried to make sense of the library myself (specifically parser.h), but the really long functions, spaghetti-like code (use of jumps), untidy formatting and extensive use of obscure global variables makes it extremely difficult to work out what’s going on. As you’re more familiar with the source code than I am, I’m sure you’ll at least have a fighting chance of identifying (and fixing?) the problems. Thanks if you can.

I filed a new issue for this at https://gitlab.com/DavidGriffith/inform6lib/-/issues/92

I’m not sure what the problem is with this. If you add

        before [;
        Fill:
                move second to self;
                "You fill the bucket with ", (the)second, ".";
        ],

to the bucket, then things seem to work okay. Can you give me some examples of what’s expected and what actually happens when the above before property is used?

You’re right. I haven’t used Inform 6 in a while and I’m a little rusty.
What confused me with “>put water in the bucket” action the second time, was the lack of the message: “(first get the water out of the bucket)”. But this is the normal before_implicit operation with 1 as return parameter (1 to try the TAKE without sending the message first).
But this wasn’t, I think, Garry’s original problem; check with him.

I may have been a little hasty in agreeing with you.
The addition of this code does not seem necessary:

        before [;
        Fill:
                move second to self;
                "You fill the bucket with ", (the)second, ".";
        ],

What’s a bit weird is that Fill and Put, which are both directed to Insert, don’t behave the same way.

It’s probably best to read the original query. There’s nothing wrong with Fill. This behaves as it should. The problem is when you try to put an object in a container when you are not holding the object. This should run the Insert action, but it never gets there. Instead, you get one of two really weird (and inappropriate) responses.

Copy, paste and compile the original code and follow the instructions I gave. It’s a standalone program and clearly illustrates the problems raised.

From grammar.h:

Verb 'put'
    * multiexcept 'in'/'inside'/'into' noun     -> Insert
    * multiexcept 'on'/'onto' noun              -> PutOn
    * 'on' held                                 -> Wear
    * 'down' multiheld                          -> Drop
    * multiheld 'down'                          -> Drop;

Rather than noun 'with' noun, If I extend the FILL with noun 'with' multiexcept, then FILL BUCKET WITH WATER appears to behave the same as `PUT WATER IN BUCKET.

Extend 'fill' first
  * noun 'with' multiexcept -> Insert reverse

Given this, I’ll mark it as complete.