[gambit-list] Reading binary files

Christian Jaeger christian at pflanze.mine.nu
Wed Dec 19 23:49:16 EST 2007


Bob McIsaac wrote:
> The puzzling comment to my humble form said : "This code assumes that
> arguments to functions are evaluated left-to-right."  But what of it? 
> There is a stream of text to be evaluated.  Choices are 1. left-right
> evaluation in one pass; 2. parsing the stream into tokens and sorting
> the tokens into a tree according to the rules of the grammar ... not
> Lispy.  Choice 1 means typing parentheses so that the evaluator can be
> simple and recursive.  ( I assume that evaluation results in a linked list)

Hm, we're not talking about nested expressions here. We're talking about 
multiple expressions given to one function. And at that point, 
regardless of whether your internal representation is lists or something 
else, the evaluator could be programmed to evaluate function arguments 
in different orders.

The problematic code is:

 >  (bitwise-ior
 >    (bitwise-and (arithmetic-shift (read-u8 INP) 24) #xff000000)
 >    (bitwise-and (arithmetic-shift (read-u8 INP) 16) #xff0000)
 >    (bitwise-and (arithmetic-shift (read-u8 INP) 8) #xff00)
 >    (bitwise-and (read-u8 INP) #xff)))

It contains four (read-u8 INP) forms; the order in which they are 
evaluated matters (because reading from a port advances the position in 
the underlying stream as a side effect).

The problem is that those forms are indirectly positioned as arguments 
to bitwise-ior. bitwise-ior is a function. Scheme dictates that the 
arguments to functions are evaluated before the function is called 
(meaning before the body of the function definition is evaluated). So 
you're guaranteed that all four (read-u8 INP) forms have been evaluated 
before the bitwise-ior call. *But* Scheme does not specify in which 
order the four arguments of bitwise-ior are evaluated. So it's very well 
possible that some Scheme implementation first evaluates (bitwise-and 
(read-u8 INP) #xff)), then (bitwise-and (arithmetic-shift (read-u8 INP) 
8) #xff00), then (bitwise-and (arithmetic-shift (read-u8 INP) 16) 
#xff0000), and at last (bitwise-and (arithmetic-shift (read-u8 INP) 24) 
#xff000000). Or the other way round. Or even in some other order or even 
in parallel. So the bytes coming from INP will end up at unspecified and 
possibly random places.

OTOH, special forms like let and let* do specify the order of evaluation 
(they are not functions). So writing

   (let* ((a (bitwise-and (arithmetic-shift (read-u8 INP) 24) #xff000000))
	 (b (bitwise-and (arithmetic-shift (read-u8 INP) 16) #xff0000))
	 (c (bitwise-and (arithmetic-shift (read-u8 INP) 8) #xff00))
	 (d (bitwise-and (read-u8 INP) #xff)))
     (bitwise-ior a b c d))

makes the order of evaluation explicit (first a, then b, then c, then d) 
and thus portable (and deterministic).

Christian.



More information about the Gambit-list mailing list