How to print the textual equivalent of an enumerated kind of value in I6?

I feel there must be a simple way of doing this, but I can’t work it out.

Example:

Lab is a room.
The lab rat is an animal in the Lab. 
coat-colour is a kind of value. Some coat-colours are white, grey, black.
An animal has a  coat-colour.  The coat-colour of the rat is black.

When play begins: 
	say "Zoinks! There is a ";
	describe the rat;
	say " here![paragraph break]";
	
To say the coat-colour of (o - an animal)(this is coat-describing):
	say "[coat-colour of o]".

To describe (o - an animal): (- Describe({o}); -)

Include (-
[ Describe o;
((+ coat-describing +)-->1)(o);print " ", (name) o;]
;
-)

((+ coat-describing +)-->1)(o) is a hacky work-around to access a specifically defined I7 routine to print the rat’s coat-colour, but I’m wondering if it is possible to directly access the I6 routine that does this:

[ E67 
    value ! Implied call parameter
    ;
    switch(value) {
        I128_white: print "white";
        I129_grey: print "grey";
        I130_black: print "black";
        default: print "<illegal coat-colour>";
    }
];

The numerical value of the rat’s coat-colour (in this case, 3) can be readily obtained (if o is the rat) from o.(+ coat-colour +) but the difficulty is that I’m not aware of an I7 (+ ...+) expansion that will expand to the name of this arbitrarily named (E67) routine that prints the textual equivalent, or a the .... translates into I6 as ... phrase that will define the name of this I6 routine from I7 code.

EDIT: PS it’s not that dropping into I6 achieves anything useful in this example, it’s just a simple illustration of the principle

In 6M62, there is a routine PrintKindValuePair() that seems to be intended as a master switch for printing kinds of value as text. However, to use it you need to supply both the kind ID and the value as parameters. While the value will be stored in a property, the kind ID is not directly programmatically accessible so far as I can tell.

but, some experimentation shows that:

  • There is an array property_metadata that stores, among other things, the printed names of properties.
  • There seems to be a definite offset between the ordinal position in which a property appears in this table and the corresponding kind ID. (EDIT: Wrong.)

So it is possible in a very klugey way to determine the correct kind ID at run-time and then use it the way that you want. EDIT: (Proceed only for amusement value. This approach does not work.)

"There Should Be a Better Way"

Lab is a room.

The lab rat is an animal in the Lab. 
coat-colour is a kind of value. Some coat-colours are white, grey, black.
An animal has a  coat-colour.  The coat-colour of the rat is black.
furriness is a kind of value. Some furrinesses are hairless, short-haired, long-haired. 
An animal has a furriness. The furriness of the rat is short-haired.

To say the/-- indirect (P - property) of (R - animal):
    (- PrintKindValuePair(PropSearch(property_metadata-->(valued_property_offsets-->{P})), {R}.{P}); -). 

Include (-

Constant NUM_PROPS = {-value:NUMBER_CREATED(property)};

Constant MAX_STRING_CMP = 100;
Array strcmp1 buffer MAX_STRING_CMP;
Array strcmp2 buffer MAX_STRING_CMP;

[ CmpString t1 t2   l1 l2 i saf;
    l1 = VM_PrintToBuffer(strcmp1, MAX_STRING_CMP, t1);
    l2 = VM_PrintToBuffer(strcmp2, MAX_STRING_CMP, t2);
    if (l1 ~= l2) rfalse;
    while (i < l1) {
	    if ((strcmp1->(i+WORDSIZE)) ~= (strcmp2->(i+WORDSIZE))) rfalse;
	    i++;
    }
    rtrue;
];

[ PropSearch t i c v ;
    ! print "NUM_PROPS = ", NUM_PROPS, "^";
    ! print "Searching for ~", (string) t, "~^";
    while (c < NUM_PROPS - 65) { ! empirically-determined value
	    if (property_metadata-->i ofclass String) {
		    c++;
		    if (CmpString(property_metadata-->i, t)) return c + 10; ! empirically-determined value
	    }
	    i++;
    }
];

-).

For printing a locale paragraph about the lab rat:
    say "[The lab rat] is [indirect coat-colour of lab rat] and [indirect furriness of lab rat]."

This hack is dependent on using the kind of value as a nameless property. I have no idea whether the same kind of stunt can be managed in 10.1.

This was the best I could manage in v10.

to say kind-value (n - a number) and (v - a number): (- PrintKindValuePair({n},{v}); -).

To repeat with (i - nonexisting number variable) running through the kinds begin -- end loop:
    (- for ( {i} = (OBJECT_TY-1) : KindAtomic({i}) && ({i} >= 0) : {i}-- ) -).

To decide what number is the weak id of coat colour (this is weak-id):
repeat with k running through the kinds begin;
  if "[kind-value k and 1]" exactly matches the text "[first value of coat-colour]", decide on k;
end repeat;

Include (-
[ PrintByKind n i;
for (i = 1 : i <= KOVDomainSize(n) : i++ ) {
  PrintKindValuePair(n,i);
  print " ";
}
print "^";
];
-)

To print by num (n - a number): (- PrintByKind({n}); -).


when play begins:
  let x be weak id of coat colour;
  print by num x;

It relies on no other property it enounters before coat-colour having the same text for the printed version of its first value, i.e., “white” for coat-colour. It’s a pretty horrible hack I wouldn’t want to rely on.

Edited to add: and it would still rely on calling to I7 so is just a worse way to accomplish what you already had…

Ha! You are a brave & brilliant explorer, Mr Otis!

I too came across this routine, but baulked from plunging into the jungle in an attempt to exploit it like you have.

This hack may perhaps be TOO empirical: I compiled this code under the new IDE but as 6M62 with the following amusing result:

There Should Be a Better Way
An Interactive Fiction
Release 1 / Serial number 220917 / Inform 7 build 6M62 (I6/v6.41 lib 6/12N) SD

Lab
The lab rat is feminine gender and 2.

>

Another valiant effort! I wonder what the collective noun for horrible hacks is? In any case, we now have three of them!

Sometimes “brilliant” in the same sense as flaming wreckage, it seems.

Sadly, what you report is the same result that I get for a fresh project in 6M62. The test project I was using before had some other stuff in it, including a different kind of value not attached to a nameless property, and this changed the required offset.

Because the routines for printing the specific values (e.g. E67()) are related to the order of declaration of the kinds of values, and because not every kind of value is necessarily used as the basis of a property, a single fixed-size offset will always be unreliable in the general case. (A kind of value not used as a property can be declared between two others that are used as properties, so for the two used as properties the first would use offset N but the second N+1.)

As @Draconis put it a few years back (see Programatically printing all properties associated with object?), this attempt was “definitely hubris.”

Perhaps somewhere in the compiler code there are secret calls that can be used to get at these hidden kind ID values.

1 Like

Once hacking the compiler itself is on the table (which it is for v10), we should be able to add the hooks to achieve of lot of things whose aspiration used to be solely the realm of hubris.

1 Like

Peter’s original code is simple and supported.

Y’all may find nifty options, but it won’t be simpler than where you started.

1 Like

To get the kind value for use with PrintKindValuePair you can use {-strong-kind:K}.

I haven’t looked into how it could help with this specific issue yet.

So I remember this area of difficulty now. Enum printing routines (like E67) are only referenced in PrintKindValuePair, but the indexes that function uses, the kind numbers, are not stored anywhere. But in an I6 phrase definition you can get a kind number with {-strong-kind:K}, which you can use like this:

To describe (o - an animal):
	(- Describe({o}, {-strong-kind:coat-colour}); -).

Include (-
[ Describe o coat_colour_kov;
	PrintKindValuePair(coat_colour_kov, o.(+ coat-colour +));
	print " ", (name) o;
];
-).

I’m actually surprised this works, as I thought the (+ coat-colour +) substitution would turn into the kind of value, and not the anonymously named property, but it actually does return the property as desired. If there is no anonymously named property, that substitution will turn into (true), so be careful.

If you only need to access a few properties like this then this should be a workable solution. But if you need to do a lot of it then you should stick to I7 code if possible.

1 Like

Well, it allows this simplification of my original hack, at the expense of introducing a mildly deprecated usage:

Lab is a room.
The lab rat is an animal in the Lab. 
coat-colour is a kind of value. Some coat-colours are white, grey, black.
An animal has a  coat-colour.  The coat-colour of the rat is black.

When play begins: 
	say "Zoinks! There is a ";
	describe the rat;
	say " here![paragraph break]";
	

To describe (o - an animal): (- Describe({-weak-kind:coat-colour},{o}); -).


Include (-
[ Describe k o;
PrintKindValuePair(k, o.(+ coat-colour +));print " ", (name) o;]
;
-)

EDIT: Snap!

Although, property expansions don’t work reliably in 10.1.2 (compilation fails) so you need to use 'The … property translates into inter as “…” ’ technique

Thanks all for the input. My current conclusion is that @zarf called it right- there may after all not be a simpler, less hacky, more reliable or future-proofed method than the one I first thought of…

@Zed, what’s been sought in the forum questions about this sort of thing is some link between property ID values and kind ID values that survives at the I6 level. As we discussed elsewhere, the whole property_data subsystem has been replaced in 10.1, otherwise it would probably be simplest to add it there immediately following the property name string in each subarray. It seems like the comparable I6 output in 10.1 has made the situation worse, as each property seems to get its own special array now. Perhaps something to output a master array of all property arrays, linked with their kinds? As in:

Array master_property_list table ... <built-in properties and their kinds> ... A_P_coat_colour 58 A_P_furriness 59;

in which 58 and 59 would be the kind IDs for the kinds of values used by those properties.

I haven’t spotted anything like that in v10.1 I6 output, but I haven’t spent that much time looking, either.

Peter’s original code is simple and supported.

I guess I’m overstating things to say that ((+ coat-describing +)-->1)(o); is the supported way to call an I7 phrase value from I6. It’s been stable for a long time, though.

https://ganelson.github.io/inform/imperative-module/3-cls.html:

The closure array consists of three words: the strong kind ID, the address of the function, and the text of the name.

(Do we this as “documented behavior”? For a typical software project we’d say, well, that’s an internal comment, not a promise; next release could be different. As a literate program, Inform publishes that information in a formal way – but without exactly saying “This is documentation.” Beware relying on old software habits that don’t take this into account.)

2 Likes