These are the stages of compilation:
The first compilation stage reads the input
from a file, from a string,
or from the interactive command interpreter.
The result is one or more Scheme forms (S-expressions), usually lists.
If reading commands interactively, only a single form is read;
if reading from a file or string, all the forms are read until
end-of-file or end-of-string; in either case, the result is treated as the
body of a dummy function (i.e.
a ModuleBody
).
The source form is rewritten into an
Expression
object, specifically a ModuleExp
.
This stage handles macro expansion and lexical name binding.
It figures out which local variables are “captured”
by an inner function, and hence need to be heap-allocated.
(Other variables are stack-allocated in the Java local variable frame.)
Various optimizations are done, including selection of
closure representations.
The resulting ModuleExp
is compiled into
one or more byte-coded classes.
This is done by invoking the virtual compile
method recursively on the Expression
s, which generates
instructions (using the bytecode
package) to evaluate the
expression and leave the result on the Java operand stack.
At the end we ask the bytecode
package to write out the resulting
classes and methods. They can be written to a file (for
future use), or into byte arrays in memory.
The compiled bytecodes are loaded into the Kawa
run-time. In the case of code that is compiled and then
immediately executed, the compiled code can be immediately
turned into Java classes using the Java ClassLoader
feature.
(That is how the read-eval-print loop works.)
An instance of the compiled sub-class of ModuleBody
is created and run
, which normally produces
various side-effects.