BiwaScheme is an IL based Compiler-Interpreter.
Most of the opecodes are inherited from 3imp_pdf.
Registers:
is used to push a value on to the stack.
op_constant is often used with the [[op_argument]].
program:
(display "hello")
compiled:
[frame
[constant "hello"
[argument
[constant 1
[argument
[refer-global "display"
[apply]]]]]]
[halt]]
is used to push a value (typically an argument of function call) onto the stack.
["argument", <opecodes_next>]
program:
(print "hi")
compiled:
[frame
[constant "hi"
[argument
[constant 1
[argument
[refer-global "print"
[apply]]]]]]
[halt]]
is used to stop the evaluation of the program.
["test", , ]
program:
'()
compiled:
[constant 7
[test
[constant 8
[halt]]
[constant 9
[halt]]]]
Creates closure object.
["close", , , , ]
TODO: <opecodes_next> should be the last in consistency with other opecodes
op_close creates closure object onto the 'a' register.
h2. closure object
Scheme closures are represented by JS arrays with its attribute 'closure_p' assigned to true.
[body, freevar1, freevar2, ..., dotpos]
program:
(lambda () 1)
compiled:
[close
0
[constant 1
[return]]
[halt]
-1]
h2. example of reassigned freevars
Usually closure object contains actual values of the freevars:
(define (f a b)
(lambda () a b))
(f 11 22)
-> [ ["refer-free", 0, ["refer-free", 1, ["return"]]], 11, 22, -1]
When the freevar may reassigned by set!, a box (JS array contains the actual value) is stored. It will be unboxed by [[op_indirect]] (see also: [[op_indirect]], [[op_box]])
(define (f a b)
(set! a 99)
(lambda () a b))
(f 11 22)
-> [ ["refer-free", 0, ["indirect", ["refer-free", 1, ["return"]]]], [99], 22, -1]
is used to capture the continuation.
The captured continuation is invoked by nuate.
["conti", , ]
program:
(call/cc func)
compiled:
[frame
[conti 0
[argument
[constant 1
[argument
[refer-global "func"
[apply]]]]]]
[halt]]
is used to invoke a continuation.
["nuate", , ]
op_nuate takes saved stack as an argument. When op_nuate is invoked, the interpreter stack is replaced by this saved stack.
op_nuate is never generated by Compiler. It is dynamically generated by Interpreter when processing op_conti.
program:
(define cc (call/cc identity))
(cc)
compiled:
[refer-local 0
[nuate #
halt is used to stop the evaluation of the program.
program:
'()
compiled:
[constant ()
[halt]]
Pushes stack frame.
op_frame must be called before applying a function, except when it is a tail call (see also: shift.)
["frame", , ]
op_frame pushes a frame (i.e. the following values) onto stack:
Usually this frame is popped by return.
op_frame is generated by:
program:
(print 11)
compiled:
[frame // op_frame
[constant 11 // push arguments to stack
[argument
[constant 1 // push number of arguments
[argument
[refer-global "print" // load function to 'a' register
[apply]]]]]] // invoke function
[halt]]
Note that op_frame itself does not touch 'f' register. Since function arguments are evaluated between op_frame and apply and these evaluations must be done in the current frame, 'f' register is updated by apply.
Invokes Scheme closure, library function or JS function.
["apply"]
In contrast to other opecodes, op_apply takes no arguments. Instead it retrieves needed information from the stack.
Before calling op_apply, you must put these values onto stack:
h2. applicable things
Note that you cannot invoke pure JS functions directly. You can use js-call or js-invoke instead.
program:
(+ 11 22)
compiled:
[frame
[constant 22
[argument // push 22 (second argument) to stack
[constant 11
[argument // push 11 (first argument) to stack
[constant 2
[argument // push 2 (number of arguments) to stack
[refer-global "+" // load function '+' to 'a' register
[apply]]]]]]]] // invoke the function with arguments 11 and 22
[halt]]
Pops stack frame.
op_return is generated by (lambda ...) and must be called as the last opecode of function execution.
["return"]
op_return does not have
op_return pops the following values from stack:
op_return also sets x, f, c to register 'x', 'f', 'c'.
program:
((lambda () 11))
compiled:
[frame
[constant 0
[argument
[close 0
[constant 11
[return]]
[apply]
-1]]]
[halt]]
Discards the arguments for parent call from stack.
Since stack frame is not pushed by frame and popped by return for tail calls, we need to remove old arguments with op_shift.
["shift", , ]
op_shift removes arguments for parent call from stack. Suppose the following code:
(define (foo x y)
(bar "a" "b" "c"))
(foo "x" "y")
Before applying bar, op_shift ["shift", 3, ["apply"]] removes the following values. Note that first four elements will not be popped - that's why this operation is named "shift".
Also note that stack frame is not pushed by [[op_frame]] for tail calls.
program:
((lambda () (print 1))
Here (print 1) is a tail call.
compiled:
[frame
[constant 0
[argument
[close 0 // make a closure, and
[constant 1
[argument
[constant 1
[argument
[refer-global "print"
[shift 1
[apply]]]]]]]
[apply] // immediately invoke it
-1]]]
[halt]]
Without tail call optimization, this program is compiled to:
[frame
[constant 0
[argument
[close 0
[frame // push stack frame
[constant 1
[argument
[constant 1
[argument
[refer-global "print"
[apply]]]]]]
[return]] // pop stack frame
[apply]
-1]]]
[halt]]
is used to load the value of a local variable.
["refer-local", , ]
program:
(define a 99)
compiled:
[frame
[constant 7
[argument
[constant 1
[argument
[close 0
[refer-local 0
[return]]
[apply]
-1]]]]]
[halt]]
is used to load the value of a free variable.
["refer-free", , ]
program:
(let ((a "a")(b "b"))
(let ()
a)
compiled:
[frame
[constant "a"
[argument
[constant "b"
[argument
[constant 2
[argument
[close 0
[constant 0
[argument
[refer-local 1
[argument
[close 1
[refer-free 0
[return]]
[shift 0
[apply]]
-1]]]]]
[apply]
-1]]]]]]]
halt]
is used to load the value of a certain global variable.
program:
map
compiled:
[refer-global "map"
[halt]]
is used to assign a value to a global variable.
["assign-global", , ]
program:
(define a 99)
compiled:
[constant 99
[assign-global "a"
[halt]]]
is used to assign value to a variable.
["assign-local", , ]
See example of [[op_indirect]].
is used to assign a value to a free variable.
["assign-free", , ]
program:
(let ((a "a"))
(let ()
(set! a "aa")))
compiled:
[frame
[constant "a"
[argument
[constant 1
[argument
[close 0
[box 0
[constant 0
[argument
[refer-local 0
[argument
[close 1
[constant "aa"
[assign-free 0
[return]]]
[shift 0
[apply]]
-1]]]]]]
[apply]
-1]]]]]
halt]
is used to make a box for a variable which may be reassigned by set!.
["box", , ]
See example of [[op_indirect]].
is used to load the value of a variable which may reassigned by 'set!'.
["indirect", ]
program:
(let ((a "a"))
(set! a "b")
a)
compiled:
[frame
[constant "a"
[argument
[constant 1
[argument
[close 0
[box 0
[constant "b"
[assign-local 0
[refer-local 0
[indirect
[return]]]]]]
[apply]
-1]]]]]
[halt]]