Yet another Scheme interpreter
A few months ago, I created a new interpreter based on the separation of Scheme execution into interpreter selection, interpreter execution, and code transformation.
The apply procedure, as implemented, knows of only two types of procedures, the machine/native code procedures that provide the foundation for the Scheme system, and the S-expression procedure, for which we can define a traditional S-expression evaluator/interpreter.
The eval procedure, as implemented, is an S-expression interpreter. It cannot interpret executable code in any other form.
The apply procedure is the interpreter selector. In this role, the procedure provides the necessary data marshalling services — making this the natural place to include a foreign function interface (FFI). The ultimate interpreter is the hardware that is called the processor, CPU, and other synonymous names.
Just recently I modified a Scheme version of eval to go further — it performs a code transformation (macro expansion) before proceeding with interpretation. Because of the code transformation step, it’s clear that eval is not necessarily the interpreter — the code transformation could actually include a compilation to native code or to pseudo-code. The Anatomy of Lisp pointedly demonstrates this by providing more than one version of S-expression interpreters, each with a different set of formal arguments, and then providing definitions of eval that call each version of the “real” interpreter.
One of the happy results of this version is that it doesn’t matter if the interpreted code is in CPS or not. Not even for the machine language procedures, the m-proc. The trick is that apply does not stack a return address — that is done by the recursive argument evaluation performed by the eval interpreter. Thus a return from an m-proc will usually return to where the interpreter is inserting the return value into an argument list. The ultimate return point is the REPL. To “tail call’ back into the Scheme system, the m-proc merely needs to provide the appropriate apply arguments and then jump to the machine coded apply routine.
To enable growth of the system, I added a save procedure to create an image file, and provided an option to load an image on startup. To retain definitions, I added a procedure to place a name-value binding in the “top level” environment.