Sun Jan 3 09:19:15 CET 2010

More VM

Unifying primitives and abstractions.  Maybe this can be done by
making a closure wrapper for each primitive.

So let's make a closure constructor first.

Done: nb argument checking.

Next: distinguish primitives from abstractions.  The problem is that
this can't be done at compile time.

So, something needs to indicate this at run time.  Either the apply
function dispatches on type, or all values in function position are
assumed to be closures.

The latter won't work: since values can come from anywhere, they need
to be checked for type.  OK.

Next: Make vm-compile understand the basic forms in the other VM.
This mainly requires `let'.  Maybe the VM's let1 schould be extended
to support parallel evaluation.  Otoh, if it is possible to write
`let' in terms of `let*' it might be interesting to pursue that route,
as it's useful for the Scheme -> EX compiler.

Alternatively, can the current compile.scm be converted to C?  The
task is finding the sweet spot somewhere.

let* -> let can be achieved by renaming all variables in the let, and
undoing the rename at a reference point.

Problem however is then properly threading the rename environment.
I'm fixing the compiler such that `%let' is no longer recognized as a
primitive form, but both `let*' and `let' compile directly to op-let1
using a shared traversal function.

However, it might be simpler to implement let directly in the VM:
continuation frames are created anyway.

Here's the let -> let* code (with a rename bug for the expressions).

         ((eq? tag 'let*)
          (let ((bindings (car args))
                (body (cdr args)))
             ((null? bindings) (comp `(begin ,@body)))
             ((null? (cdr bindings))
              (comp `(%let ,@(car bindings)
                           (begin ,@body))))
              (comp `(%let ,@(car bindings)
                           (let* ,(cdr bindings) ,@body)))))))
         ((eq? tag 'let)
          (let ((bindings (car args))
                (body (cdr args)))
            ;; Rename variables + keep translation records.
            (let ((names (map car bindings))
                  (exprs (map cadr bindings))
                  (new-names (map gensym bindings)))
              (compile `(let* ,(map list new-names exprs) ,@body)
                       (append (map cons names new-names)

OK: let (made it a VM opcode).

Next: self-hosting compiler.  Re-arrange the code such that it becomes
possible to write the macro expander and the compiler in the VM, then
switch VMs.

Next: Toplevel environment & boxes.

Boxes are not be necessary yet as there is still an environment at run
time.  set! can be implemented by updating this in-place.

OK: assignments

Next: ribbed index.  -> Probably won't work since vectors can't be
updated non-destructively.  However, closures could be packed lazily.
I.e. if the 'ref' function can handle nested vectors like it now does

#(a b c d e f #(h i j k l '()))

Anyway, that's for later.

Next: toplevel.

Next: match form.

It might be a lot simpler to use the `match' form for writing the
compiler.  The effort needed to implement this might pay off well.

For toplevel, it might be simplest to have the compile operation
extend the toplevel as well.  I.e. it adds slots for undefined
variables, and links those slots into the code.

Or better still, add a dynamic binding opcode that can be
short-cicuited into an unbox opcode at first use?