The first task, as always, is simply to learn the
bare mechanics of producing a
by means of an assembler.
stack: makeAssembler --> *asm* stack: makeFunction --> *fun* stack: "my-function" --> *fun*$s.name stack: *asm* reset stack: 0 *fun* *asm* finishAssembly --> *cfn* stack: *cfn* stack: #<c-fn my-function>
That's it! Our first compiled function. If we disassembled it, we would see:
constants: code bytes: 00: 2e code disassembly: 000: 2e RETURN
which is the simplest function possible in Muq: No constants and code limited to a single one-byte RETURN bytecode.
Let's go over the example in detail.
makeAssembler --> *asm*
The above creates our own assembler. It records internally
everything we tell it about what we want in the final
compiledFunction, and then when we're done
describing it, actually builds the
makeFunction --> *fun*
The above creates our
function. Do not confuse
function objects with
functioncontains book-keeping information of interest to humans, such as the source code in text form, the compiler used to compile the source, the date created, and the names of any local variables used by the function. A
functionhas a full compliment of propdirs, and hence may be decorated with any extra information you like. A
functionwill normally spend most of its life on disk, being fetched into memory only if some human wishes to inspect the source code or such.
compiledFunctionis very specialized, bare-bones object containing all and only the information needed by the bytecode interpreter to execute function, namely the bytecodes for the instructions, and any constants needed by those bytecodes. (Well... and an 'owner' field for accounting purposes, and a pointer to the corresponding
functionobject.) No creation date, no propdirs, no name. 99% lean muscle. You will almost never have cause to do anything with a
compile-functionexcept to call it or to fetch the pointer to the corresponding
function, where all the human-readable stuff is.
(Note that there may be more than one
compiledFunction pointing to a given
function. This is most common when implementing
lispStyle lambda closures, scheme-style promises, or
functional programming stuff. For generic Algolic sorts of
code, however, there will normally be just one
compiledFunction for each
"my-function" --> *fun*$s.name
We name the function "my-function". This is optional; The default name is "_".
A single assembler may be used to assemble many functions; In a production compiler, it is much more efficient to re-use assemblers than to create and discard a new one for each function assembled.
reset to prepare our assembler to compile a
In this particular example, the assembler was freshly created and hence already reset, so we could actually have skipped this step, but we include it for didactic completeness.
nil 0 *fun* *asm* finishAssembly --> *cfn*
finishAssembly command triggers construction
by the assembler of the desired
*fun* argument is used by the assembler to
cfn$s.source slot in the resulting
compiledFunction to the desired
object: For security and reliability reasons, Muq doesn't
let anything much but an assembler tinker with the contents
compiledFunction, so you can't do this
What were the NIL and
0 parameters for? We cover this
in the next section.
Go to the first, previous, next, last section, table of contents.