At 4:42 AM -0500 on 12/5/99, [EMAIL PROTECTED] wrote:
> Why don't you include a symbol table at the beginning of the compiled
>code so that whenever the DO command is used, the symbol table can be checked
>to generate a new variable? Of course, the symbol table will have to be
>loaded into memory, because it can change while executing DO commands, but
>the extra overhead strikes me as trivial, considering that the DO and SEND
>commands will add overhead no matter what you do.
I do have a symbol table of sorts. Consider the following script:
do "put 1 into a"
do "put 1 into b"
put 1+3*random(5) into c
return a*b^c
First step: Parse. Second step: Bytecode. Third step: Compile. Compiling
will generate a symbol table (as you suggest -- it always has). Remember
that in general, I can't know the effect of a 'do' until it is actuall
gotten to. So output will look like this:
push.s 0 ; push "put 1 into a" onto stack
call 0 ; handler call to 'do'
push.s 2 ; push "put 1 into b" onto stack
call 0 ; handler call to 'do'
pushi #5 ; random's parameter
call 1 ; function call to 'random'
pop 0 ; load result into variable 0, at this point a temp
muli 0, 0, #3 ; multiply by 3
addi 2, 0, #1 ; add one, assign to c.
exp 3, 1, 2 ; b^c; slot 3 is a temp
mul 3, 3, 0 ; multiply by a (which is no longer a temp!)
push 3 ; push function result
end ; return to caller
Here's the variable table, at the end of the compile (it changes during
compile)
0 => 'a'
1 => 'b'
2 => 'c'
3 => '.TMP1'
[ note: They'd actually be in a different order. Irrelevant. ]
And the string table:
0 => 'put 1 into a'
1 => 'put 1 into b'
And the function table:
0 => �DO�
1 => �random�
Now, consider the execution of this code. To follow, build hyurself a
variable table on a sheet of paper, which looks something like this:
slot # | 0 | 1 | 2 | 3 |
--------|-------|-------|-------|-------|
| | | | |
V | | | | |
| | | | |
A | | | | |
| | | | |
L | | | | |
| | | | |
U | | | | |
| | | | |
E | | | | |
| | | | |
As you trace through the script, fill in the chart. When a value changes,
just write the new value below the old one. We won't worry about the stack,
because in this example it is not important:
push.s 0 ; push "put 1 into a" onto stack
call 0 ; handler call to 'do'
Now, remember, this goes through parse->bytecode->compile. Look in the
variable table, above. 'a' is 0. So place a 1 in column 0.
push.s 2 ; push "put 1 into b" onto stack
call 0 ; handler call to 'do'
'b' is 1, so place a 1 in column 1.
pushi #5 ; random's parameter
call 1 ; function call to 'random'
pop 0 ; load result into variable 0, at this point a temp
Place any old number between 1 and 5 into column 0.
muli 0, 0, #3 ; multiply by 3
Now multiply the value in column 0 by 3.
addi 2, 0, #1 ; add one
Now add one to that value, and place the result in column 2.
exp 3, 1, 2 ; b^c; slot 3 is a temp
Take the value of column 1 to the column 3 power, and put the result into
column 3.
mul 3, 3, 0 ; multiply by a (which is no longer a temp!)
Multiply column 3 by column 0, place result int column 3
push 3 ; push function result
end ; return to caller
The last value in column three is the result. We expected 1, and (in my
choice of random, 4) got 12. Oops.