I6: Using an expression as a property

I’m trying to implement possession of objects in the Inform 6 Library. Ideally this would involve code like this:

Object -> Sword
    with name "sword",
    owner player,
    description [;
        Possessive(self.owner, true, true);
        print " precious ", (name) self, ".^";
    ];

The problem with this is that the Inform6 compiler doesn’t like seeing an expression as a property. Now, if we have owner George, then it’ll work. But not with player because that’s a Global which must be computed first to figure out that it’s really George. Attempting to ignore this by modifying the Inform6 compiler to ignore such an illegal thing leads to this when attempting to examine the sword:

>EXAMINE SWORD
[** Programming error: tried to test "has" or "hasnt" of nothing **]

[** Programming error: tried to test "has" or "hasnt" of nothing **]

[** Programming error: tried to test "has" or "hasnt" of nothing **]

[** Programming error: tried to test "has" or "hasnt" of nothing **]
Its sword.
>

At least with this instance, the error results from the final line in RunRoutines() in parser.h.

[ RunRoutines obj prop;
    if (obj == thedark && prop ~= initial or short_name or description) obj = real_location;
    if (obj.&prop == 0 && prop >= INDIV_PROP_START) rfalse;
    return obj.prop();
];

That last line attempts to run a property of the object. But with owner player, that property is actually an expression. With the compiler altered like this, a property that’s actually an expression will always render as nothing.

So the big question is this: What prevents the compiler from generating code to evaluate the expression to determine what’s actually being called?

Where do you want this code to go?

The Inform convention for an object property which contains code is

owner [; return player; ]

This gives you a property which contains a routine address. Then, again by convention, you can write Sword.owner() to invoke the routine.

You could decide that a property could contain either an object reference or a routine address. Then Sword.owner() will either return the object itself or invoke the routine.

But there’s no facility in the VM (or veneer) for Sword.owner, with no parens, to invoke user code. That’s a pure property table lookup.

3 Likes

Perhaps the main thing I want to do with this is to change ownership of an object by changing that object’s owner property. For instance, Sword.owner = Herman;. This works, but not if I want to give the sword to the current PC (see above trouble). Offhand, I think perhaps Sword.owner(Herman); or Sword.owner(player); may work, but I’m not able to do any tests for a few days.

The problem in your code is that the value of “player” isn’t known at compile time. It’s a variable, which means it may vary over time, as the program executes. The Inform compiler needs to boil all property values down to (possibly lists of) 16-bit values which are just that - 16-bit values, not expressions.

If you know that player will in fact always be set to the object cretin, you can use cretin instead of player, and it will work fine. If you know player will vary over the course of the game, you can use Andrews suggestion.

1 Like

There shouldn’t be any problem doing

sword.owner = player;

You just couldn’t set it at compile time.

1 Like

I don’t see what you mean by “cannot set it at compile time”. You mean when writing the object, I cannot do what I did in my first post, but somewhere else, like in Initialize() is fine?

Pretty much. Same reason int i = 24; works both inside and outside a function in C, but int i = j; only works inside a function. The first is an initialization (done at compile time), the second is an assignment (done at run time).

Doing

[ Initialise; sword.owner = player; ];

is different from defining the property as

owner [; return player; ]

…and using an obj.owner() idiom to fetch it. Just to be clear that we’re talking about different cases.

Yeah. I’m casting about for the proper way to go about implementing this.