[I6] Array access and increments

The first line of code doesn’t work, the second two do. Incrementing in the middle of an array access works for zcode, so why don’t two of them in a row work for glulx?

ch = (ITA-->1)++->0 * 256 + (ITA-->1)++->0;

ch = (ITA-->1)->0 * 256 + (ITA-->1)->1;
ITA-->1 = ITA-->1 + 2;

Full code example:

[code]Include (-
Array ITA --> 3;
! If you don’t pass in a block, set to the next linked block
[ UpdateBlkStruct array block headersize;
if ( block == 0 )
block = (array–>0)–>BLK_NEXT;
headersize = BLK_DATA_OFFSET;
array–>0 = block;
array–>1 = block + headersize;
array–>2 = block + BlkSize( block );
[ INDEXED_TEXT_TY_Say indt ch i dsize;
if ((indt==0) || (BlkType(indt) ~= INDEXED_TEXT_TY)) return;

UpdateBlkStruct( ITA, indt );

dsize = BlkValueExtent(indt);
for (i=0:i<dsize:i++) {

	!ch = BlkValueRead(indt, i);
		ch = (ITA-->1)++->0;
	#ifnot; ! TARGET_ZCODE
		!ch = (ITA-->1)++->0 * 256 + (ITA-->1)++->0;
		ch = (ITA-->1)->0 * 256 + (ITA-->1)->1;
		ITA-->1 = ITA-->1 + 2;
	!if ( ITA-->1 == ITA-->2 ) UpdateBlkStruct( ITA );
	if (ch == 0) break;
	print (char) ch;
	#ifnot; ! TARGET_ZCODE

-) instead of “Printing” in “IndexedText.i6t”.

There is a room.
When play begins:
let T be indexed text;
let T be “Hello World!”;
say T; say line break;

Any ideas?

[Edit: Meh, never mind, I’ll just use the working code. It might even involve fewer instructions.

But I’d still love to figure out why it doesn’t work!]

Looks like a stack mishap. The assembly is multiplying the wrong stack value by 256.

It makes me nervous to see ++ so close beside a --> and a ->, because which one it is associated with changes the meaning of ++.
(I.e., I don’t know either.)

I tried a simpler I6 example:

Array ITA --> 1 2 3 4 5 6 7 8;
Array bytearray -> $33 $44 $55 $66;

[ RunTest ch;
ITA–>1 = bytearray;
ch = (ITA–>1)+±>0 * 256 + (ITA–>1)+±>0;
print "Ref ", ITA–>1, "; ch ", ch, “^”;

So, the first thing is that this behaves the same in Zcode and Glulx. I don’t think you were alleging a difference there, but just to be clear, the same answer comes out.

The answer this prints is 17459, which is $4433, which explains what’s going on: the + operator on that line is being evaluated right-to-left. I’m afraid that’s the way Inform is. (I think C guarantees left-to-right evaluation for binary operators, but I’m not actually sure of that. Inform is backwards.)

If you break it up into two lines, you’ll get what I guess you probably want:

ch = (ITA-->1)++->0 * 256;
ch = ch + (ITA-->1)++->0;

(Before anybody asks, the && and || operators are evaluated left-side-first, so they can short-circuit properly. These are exceptions to the rule.)

I didn’t guess that is what was happening. Weird.

Is | also evaluated right to left?

All other operators except comma are. Even function arguments are evaluated right to left. This way, subexpressions can be evaluated onto the stack instead of using precious temporary variables.

There’s no reason why going left to right couldn’t use the stack too. That’s what I’d always thought it did.

It’s not impossible, but it does require temporary storage.

x=foo(a(), b(), c()); can be compiled right to left as:

call c -> sp
call b -> sp
call a -> sp
call foo sp sp sp -> x

But left to right would be:

call a -> tmp1
call b -> tmp2
call c -> sp
call foo tmp1 tmp2 sp -> x

Likewise, x=a()/b(); right to left is:

call b -> sp
call a -> sp
div sp sp -> x

But left to right would be:

call a -> tmp1
call b -> sp
div tmp1 sp -> x

ZILF evaluates left to right using local variables for temporary storage when necessary; it’s possible to run out of locals that way, so there’s quite a bit of logic to detect cases where the order of evaluation doesn’t matter. Inform has a different philosophy.

You’re right, evaluating in that order does make function calls simpler (or anything where more than one stack value is needed at a time). I suspect for many calculations there is only one value being pulled from the stack per opcode. Inform’s way is simpler, though unintuitive. I don’t remember ever reading about this in the DM4.

I had a rethink and the whole issue can be fixed by using the right glulx opcodes!

@aload ITA 1 sp; @aloads sp 0 ch; ITA-->1 = ITA-->1 + 2;

Now I just wish Inform had macros…

FWIW, this sort of code invokes undefined behavior in C and C++, so I would naturally expect its behavior to be undefined (or at least unspecified) in Inform as well. You’re modifying (ITA–>1) twice in the same expression, and it’s not clear which old value should be used where. Sounds like the Z-code compiler picks one answer and the Glulx compiler picks the other. I don’t think anything’s going wrong here.

Totally off-topic, but today I learned that Javascript has well-defined yet craazy order-of-evaluation rules: stackoverflow.com/questions/5944 … -operators

What’s crazy about JS? Chrome had a bug, which has since been fixed. And I’m used to JS, so I didn’t know that it was undefined in C. Now that I know that Inform is the reverse of what I’d expect I can account for it.

Oh and the compiler is the same in both, I just didn’t have code that showed it for the Z-Machine.

According to that SO thread, the (slightly) crazy behavior is part of the spec: x is called before y in both “x() < y()” and “x() > y()”, but in one case the result of x() will be converted to a primitive first, through a separate function call, and in the other the result of y() will be converted first.

Hmm, I’m not sure if the conversion to a primitive is something that could make a difference to user code (i.e., does it involve calling .valueOf, toString etc?). I would guess that it could not. In any case the the examples in that SO page don’t work anymore, so either they’re bugs that have been fixed or new bugs have been introduced. :slight_smile:

Yeah, ECMAScript 5 fixed the “abstract relational comparison algorithm” (page 78 of this PDF) to add the “LeftFirst” parameter, so they can still define x>y in terms of y<x without accidentally flipping the order in which x and y get converted to primitives.

I think Claudiu’s examples here (which override the .valueOf method in order to observe the wackiness) used to work in ECMAScript 3 (JavaScript circa 2009), but they definitely don’t work anymore in ECMAScript 5 (JavaScript 1.8.5, circa 2010).

Okay, so the JavaScript situation is a lot less crazy than I thought it was at first. At least in this case. :slight_smile:

But back to Inform: even though there is only one implementation of Inform (and likely will ever be), it still feels like a bad idea to write code like “return ++i * ++i / ++i” that relies on an undocumented feature of that implementation. (I checked the DM4. Order of evaluation of side effects is undocumented. But it’s not specifically called out as undefined, either.) So I’m glad you were induced to write a cleaner clearer version instead. :slight_smile:

I’ve been working on a new implementation of Inform, actually. Part of that will entail making formal specs and/or unit tests to document quirks like this.

(BTW, right-to-left evaluation is documented in the compiler source code, but I guess that’s the only place.)