[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