A portable, embeddable Scheme implementation based on a register-oriented bytecode vm
I have many years of work experiences in game programming, especially backend. Lua is so pupular in the game industry, and also in many other areas. I like SICP very much, and have read the article The Roots Of Lisp and the book "Hackers and Painters". Lisp is highly praised.
I want to have a scheme implementation that's like lua, a vm register and bytecode-based with line level debug info
It could be easily embed to many host programs, only need to copy some .h/.cpp files to compile together, and write stubs
- Bytecode compiler with register-based instruction set
- tail-call optimization
- First-class continuations (call/cc) multi-short, unlimited, use the method copy-on-write of the callframes. delimited continuation will be supported in the near future too
- Flatten box value(inspired by Lua) no chain, box the stack value if captured by inner lambda
- Stack segment technique for continuation capture and chain walking, inspired by Chez Scheme
- Two-halves incremental generational GC (partially implemented, not used currently)
- syntax-ruleshygienic macros custom ellipsis is also supported
- Number tower bignum is supported too
- Line-level debug info with source location tracking
- Lua-like embeddability and portability: platform-independent, easy to integrate into C/C++ host applications (windows, linux, mac, ios, android, any platform where there is a c99/c++98 compiliance compiler)
- Full tests passed with r4rstest (pass SCHEME_STD_R4RS=1) and r5rs-tests
- dynamic-wind support netsted with call/cc, jump to(out) dynamic-wind's thunk/body allow to jump out dynamic-wind's before and after, but jumping into is not forbidden
- values+call-with-values
- no transcript-on / transcript-off
- no eval but has vm->evalstr
- hash-table (inspired by lua) (make-hash-table) (hash-table-ref table key) (hash-table-set! table key val) (hash-table-for-each (lambda (key val) xxx) table)
-1. to test scheme code, please refer to r4rstest.scm, r5rs-tests.scm, aftertest.scm in the project
- copy *.cpp/h to your project except main.cpp
- include header file
#include "vm.h"
using namespace Scheme;
- init vm
use default malloc/free
VM vm;
or use custom malloc/free
static void * myalloc(void *ptr, size_t nsize) {
if (nsize == 0) {
myfree(ptr);
return NULL;
}
else
return myrealloc(ptr, nsize);
}
VM vm(myalloc);
- load file
vm.loadfile("filename.scm");
- write stubs
// (lambda (a b) xxx)
static ValueT scm_stub_my_add(VM* vm, ValueT* a, ValueT* b)
{
return scm_make_int(vm, scm_get_int(vm, a) + scm_get_int(vm, b));
}
// (lambda rest xxx)
static ValueT scm_stub_my_sum(VM* vm, ValueT* rest)
{
scm_int t = 0;
ValueT* p = rest;
while (!scm_is_null(vm, p))
{
t += scm_get_int(vm, scm_car(vm, p));
p = scm_cdr(vm, p);
}
return scm_make_int(vm, t);
}
const RegCProc myext[] = {
RegCProc("my-add", scm_stub_my_add), // (lambda (a) xxx)
RegCProc("my-sum", scm_stub_my_sum, true), // (lambda (a . rest) xxx)
RegCProc(NULL, -1)
};
regcfunc(vm, myext);
- call stubs from scheme
(my-add 2 3)
(my-sum 1 2 3 4)
- eval scheme from c/c++
ValueT val = vm->evalstr("(+ 1 2)")
int iv = scm_get_int(vm, &val);
// iv = 3
- call lambda from c/c++
ValueT* argv[] = {xxx};
ValueT val = vm->call("f", argv, n)
- R5RS with no extra setting(default)
- R4RS with the macro SCHEME_STD_R4RS(mainly about the SYMBOL upcase conversion)
Optimization and features are endless
MIT