ref: 672558d30fdfffa9f3ede3c7b671d28285407437
parent: b9a1be78a090a3d57e6da9b10a247c8292726068
author: JeffBezanson <jeff.bezanson@gmail.com>
date: Tue Apr 14 20:12:01 EDT 2009
bytecode vm is now working, off by default various bug fixes language changes: • constant symbols no longer shadow everything • eval* removed • vararg lists always allocated on entry, dotted argument lists not preserved new applyn() entry point
--- a/femtolisp/builtins.c
+++ b/femtolisp/builtins.c
@@ -129,7 +129,7 @@
return symbol(cvalue_data(args[0]));
}
-extern value_t LAMBDA;
+extern value_t LAMBDA, COMPILEDLAMBDA;
static value_t fl_setsyntax(value_t *args, u_int32_t nargs)
{
@@ -142,7 +142,8 @@
sym->syntax = 0;
}
else {
- if (!iscons(args[1]) || car_(args[1])!=LAMBDA)
+ if (!iscons(args[1]) || (car_(args[1])!=LAMBDA &&
+ car_(args[1])!=COMPILEDLAMBDA))
type_error("set-syntax!", "function", args[1]);
sym->syntax = args[1];
}
--- a/femtolisp/compiler.lsp
+++ b/femtolisp/compiler.lsp
@@ -8,13 +8,14 @@
(define Instructions
(make-enum-table
- [:nop :dup :pop :call :jmp :brf :brt :jmp.l :brf.l :brt.l :ret
+ [:nop :dup :pop :call :tcall :jmp :brf :brt :jmp.l :brf.l :brt.l :ret
+ :tapply
:eq? :eqv? :equal? :atom? :not :null? :boolean? :symbol?
:number? :bound? :pair? :builtin? :vector? :fixnum?
:cons :list :car :cdr :set-car! :set-cdr!
- :eval :eval* :apply
+ :eval :apply
:+ :- :* :/ :< :compare
@@ -24,7 +25,7 @@
:loadg :loada :loadc :loadg.l
:setg :seta :setc :setg.l
- :closure :trycatch :tcall :tapply :argc :vargc]))
+ :closure :trycatch :argc :vargc]))
(define arg-counts
(table :eq? 2 :eqv? 2
@@ -37,10 +38,9 @@
:cons 2 :car 1
:cdr 1 :set-car! 2
:set-cdr! 2 :eval 1
- :eval* 1 :apply 2
- :< 2 :for 3
- :compare 2 :aref 2
- :aset! 3))
+ :apply 2 :< 2
+ :for 3 :compare 2
+ :aref 2 :aset! 3))
(define 1/Instructions (table.invert Instructions))
@@ -181,11 +181,11 @@
`(closed ,lev ,i))
(lookup-sym s
(cdr env)
- (if (null? curr) lev (+ lev 1))
+ (if (or arg? (null? curr)) lev (+ lev 1))
#f)))))
(define (compile-sym g env s Is)
- (let ((loc (lookup-sym s env -1 #t)))
+ (let ((loc (lookup-sym s env 0 #t)))
(case (car loc)
(arg (emit g (aref Is 0) (cadr loc)))
(closed (emit g (aref Is 1) (cadr loc) (caddr loc)))
@@ -199,13 +199,13 @@
(cond-clauses->if (cdr form)))
(define (cond-clauses->if lst)
(if (atom? lst)
- lst
- (let ((clause (car lst)))
- (if (eq? (car clause) 'else)
- (cons 'begin (cdr clause))
- `(if ,(car clause)
- ,(cons 'begin (cdr clause))
- ,(cond-clauses->if (cdr lst)))))))
+ #f
+ (let ((clause (car lst)))
+ (if (eq? (car clause) 'else)
+ (cons 'begin (cdr clause))
+ `(if ,(car clause)
+ ,(cons 'begin (cdr clause))
+ ,(cond-clauses->if (cdr lst)))))))
(define (compile-if g env tail? x)
(let ((elsel (make-label g))
@@ -241,11 +241,12 @@
(define (compile-while g env cond body)
(let ((top (make-label g))
(end (make-label g)))
+ (compile-in g env #f #f)
(mark-label g top)
(compile-in g env #f cond)
(emit g :brf end)
- (compile-in g env #f body)
(emit g :pop)
+ (compile-in g env #f body)
(emit g :jmp top)
(mark-label g end)))
@@ -365,12 +366,12 @@
(cond (compile-in g env tail? (cond->if x)))
(if (compile-if g env tail? x))
(begin (compile-begin g env tail? (cdr x)))
- (prog1 (compile-prog1 g env tail? x))
+ (prog1 (compile-prog1 g env x))
(lambda (begin (emit g :loadv (compile-f env x))
(emit g :closure)))
(and (compile-and g env tail? (cdr x)))
(or (compile-or g env tail? (cdr x)))
- (while (compile-while g env (cadr x) (caddr x)))
+ (while (compile-while g env (cadr x) (cons 'begin (cddr x))))
(set! (compile-in g env #f (caddr x))
(compile-sym g env (cadr x) [:seta :setc :setg]))
(trycatch (compile-in g env #f `(lambda () ,(cadr x)))
@@ -383,13 +384,14 @@
(args (cadr f)))
(if (null? (lastcdr args))
(emit g :argc (length args))
- (emit g :vargc (length args)))
+ (emit g :vargc (if (atom? args) 0 (length args))))
(compile-in g (cons (to-proper args) env) #t (caddr f))
(emit g :ret)
`(compiled-lambda ,args ,(bytecode g))))
-(define (compile x)
- (bytecode (compile-in (make-code-emitter) () #t x)))
+(define (compile f) (compile-f () f))
+
+(define (compile-thunk expr) (compile `(lambda () ,expr)))
(define (ref-uint32-LE a i)
(+ (ash (aref a (+ i 0)) 0)
--- a/femtolisp/cps.lsp
+++ b/femtolisp/cps.lsp
@@ -8,7 +8,7 @@
(cond-clauses->if (cdr form)))
(define (cond-clauses->if lst)
(if (atom? lst)
- lst
+ #f
(let ((clause (car lst)))
`(if ,(car clause)
,(cond-body (cdr clause))
@@ -22,13 +22,13 @@
,(begin->cps (cdr forms) k)))))))
(define-macro (lambda/cc args body)
- `(set-car! (lambda ,args ,body) 'lambda/cc))
+ `(cons 'lambda/cc (lambda ,args ,body)))
; a utility used at run time to dispatch a call with or without
; the continuation argument, depending on the function
(define (funcall/cc f k . args)
(if (and (pair? f) (eq (car f) 'lambda/cc))
- (apply f (cons k args))
+ (apply (cdr f) (cons k args))
(k (apply f args))))
(define *funcall/cc-names*
(list->vector
@@ -38,7 +38,7 @@
(let ((name (aref *funcall/cc-names* (length args))))
`(define (,name f k ,@args)
(if (and (pair? f) (eq (car f) 'lambda/cc))
- (f k ,@args)
+ ((cdr f) k ,@args)
(k (f ,@args))))))
(def-funcall/cc-n ())
(def-funcall/cc-n (a0))
--- a/femtolisp/flisp.c
+++ b/femtolisp/flisp.c
@@ -55,7 +55,7 @@
static char *builtin_names[] =
{ // special forms
"quote", "cond", "if", "and", "or", "while", "lambda",
- "trycatch", "%apply", "set!", "prog1", "begin",
+ "trycatch", "%apply", "%applyn", "set!", "prog1", "begin",
// predicates
"eq?", "eqv?", "equal?", "atom?", "not", "null?", "boolean?", "symbol?",
@@ -65,7 +65,7 @@
"cons", "list", "car", "cdr", "set-car!", "set-cdr!",
// execution
- "eval", "eval*", "apply",
+ "eval", "apply",
// arithmetic
"+", "-", "*", "/", "<", "compare",
@@ -96,7 +96,7 @@
value_t definesym, defmacrosym, forsym, labelsym, printprettysym, setqsym;
value_t printwidthsym, tsym, Tsym, fsym, Fsym, booleansym, nullsym, elsesym;
-static value_t eval_sexpr(value_t e, value_t *penv, int tail);
+static value_t eval_sexpr(value_t e, value_t *penv, int tail, uint32_t envsz);
static value_t apply_cl(uint32_t nargs);
static value_t *alloc_words(int n);
static value_t relocate(value_t v);
@@ -467,7 +467,7 @@
}
}
-static value_t special_apply_form;
+static value_t special_apply_form, special_applyn_form;
static value_t apply1_args;
static value_t memory_exception_value;
@@ -502,6 +502,7 @@
}
lasterror = relocate(lasterror);
special_apply_form = relocate(special_apply_form);
+ special_applyn_form = relocate(special_applyn_form);
apply1_args = relocate(apply1_args);
memory_exception_value = relocate(memory_exception_value);
@@ -551,10 +552,31 @@
value_t apply1(value_t f, value_t a0)
{
- car_(apply1_args) = a0;
- return apply(f, apply1_args);
+ PUSH(f);
+ PUSH(a0);
+ PUSH(fixnum(1));
+ value_t v = toplevel_eval(special_applyn_form);
+ POPN(3);
+ return v;
}
+value_t applyn(uint32_t n, value_t f, ...)
+{
+ va_list ap;
+ va_start(ap, f);
+ size_t i;
+
+ PUSH(f);
+ for(i=0; i < n; i++) {
+ value_t a = va_arg(ap, value_t);
+ PUSH(a);
+ }
+ PUSH(fixnum(n));
+ value_t v = toplevel_eval(special_applyn_form);
+ POPN(n+2);
+ return v;
+}
+
value_t listn(size_t n, ...)
{
va_list ap;
@@ -634,23 +656,21 @@
// eval -----------------------------------------------------------------------
/*
- take the final cdr as an argument so the list builtin can give
- the same result as (lambda x x).
-
- however, there is still one interesting difference.
+ there is one interesting difference between this and (lambda x x).
(eq a (apply list a)) is always false for nonempty a, while
(eq a (apply (lambda x x) a)) is always true. the justification for this
is that a vararg lambda often needs to recur by applying itself to the
tail of its argument list, so copying the list would be unacceptable.
*/
-static void list(value_t *pv, uint32_t nargs, value_t *plastcdr)
+static value_t list(value_t *args, uint32_t nargs)
{
cons_t *c;
uint32_t i;
- *pv = cons_reserve(nargs);
- c = (cons_t*)ptr(*pv);
- for(i=SP-nargs; i < SP; i++) {
- c->car = Stack[i];
+ value_t v;
+ v = cons_reserve(nargs);
+ c = (cons_t*)ptr(v);
+ for(i=0; i < nargs; i++) {
+ c->car = args[i];
c->cdr = tagptr(c+1, TAG_CONS);
c++;
}
@@ -657,17 +677,18 @@
if (nargs > MAX_ARGS)
(c-2)->cdr = (c-1)->car;
else
- (c-1)->cdr = *plastcdr;
+ (c-1)->cdr = NIL;
+ return v;
}
-#define eval(e) (selfevaluating(e) ? (e) : eval_sexpr((e),penv,0))
-#define topeval(e, env) (selfevaluating(e) ? (e) : eval_sexpr((e),env,1))
+#define eval(e) (selfevaluating(e) ? (e) : eval_sexpr((e),penv,0,envsz))
+#define topeval(e, env) (selfevaluating(e) ? (e) : eval_sexpr((e),env,1,2))
#define tail_eval(xpr) do { \
if (selfevaluating(xpr)) { SP=saveSP; return (xpr); } \
else { e=(xpr); goto eval_top; } } while (0)
/* eval a list of expressions, giving a list of the results */
-static value_t evlis(value_t *pv, value_t *penv)
+static value_t evlis(value_t *pv, value_t *penv, uint32_t envsz)
{
PUSH(NIL);
PUSH(NIL);
@@ -680,7 +701,7 @@
v = mk_cons();
car_(v) = Stack[SP-1];
cdr_(v) = NIL;
- (void)POP();
+ POPN(1);
if (*rest == NIL)
Stack[SP-2] = v;
else
@@ -688,7 +709,7 @@
*rest = v;
v = *pv = cdr_(*pv);
}
- (void)POP();
+ POPN(1);
return POP();
}
@@ -698,7 +719,7 @@
is active until this function returns. Any return past this function
must free the new segment.
*/
-static value_t new_stackseg(value_t e, value_t *penv, int tail)
+static value_t new_stackseg(value_t e, value_t *penv, int tail, uint32_t envsz)
{
stackseg_t s;
@@ -713,7 +734,7 @@
value_t v = NIL;
int err = 0;
FL_TRY {
- v = eval_sexpr(e, penv, tail);
+ v = eval_sexpr(e, penv, tail, envsz);
}
FL_CATCH {
err = 1;
@@ -727,7 +748,7 @@
return v;
}
-static value_t do_trycatch(value_t expr, value_t *penv)
+static value_t do_trycatch(value_t expr, value_t *penv, uint32_t envsz)
{
value_t v;
@@ -748,6 +769,23 @@
return v;
}
+static value_t do_trycatch2()
+{
+ value_t v;
+ value_t thunk = Stack[SP-2];
+ Stack[SP-2] = Stack[SP-1];
+ Stack[SP-1] = thunk;
+
+ FL_TRY {
+ v = apply_cl(0);
+ }
+ FL_CATCH {
+ Stack[SP-1] = lasterror;
+ v = apply_cl(1);
+ }
+ return v;
+}
+
/* stack setup on entry:
n n+1 ...
+-----+-----+-----+-----+-----+-----+-----+-----+
@@ -764,12 +802,12 @@
penv[-1] tells you the environment size, from LL through CLO, as a fixnum.
*/
-static value_t eval_sexpr(value_t e, value_t *penv, int tail)
+static value_t eval_sexpr(value_t e, value_t *penv, int tail, uint32_t envsz)
{
value_t f, v, *pv, *lenv;
cons_t *c;
symbol_t *sym;
- uint32_t saveSP, bp, envsz, nargs;
+ uint32_t saveSP, bp, nargs;
int i, noeval=0;
fixnum_t s, lo, hi;
int64_t accum;
@@ -783,7 +821,6 @@
eval_top:
if (issymbol(e)) {
sym = (symbol_t*)ptr(e);
- if (sym->syntax == TAG_CONST) { SP=saveSP; return sym->binding; }
while (1) {
v = *penv++;
while (iscons(v)) {
@@ -803,7 +840,7 @@
return v;
}
if (__unlikely(SP >= (N_STACK-MAX_ARGS-4))) {
- v = new_stackseg(e, penv, tail);
+ v = new_stackseg(e, penv, tail, envsz);
SP = saveSP;
return v;
}
@@ -811,15 +848,13 @@
v = car_(e);
PUSH(cdr_(e));
if (selfevaluating(v)) f=v;
- else if (issymbol(v) && (f=((symbol_t*)ptr(v))->syntax)) {
+ else if (issymbol(v) && (f=((symbol_t*)ptr(v))->syntax) && f!=TAG_CONST) {
// handle special syntax forms
if (isspecial(f))
goto apply_special;
- else if (f == TAG_CONST)
- f = ((symbol_t*)ptr(v))->binding;
else {
- noeval = 2;
PUSH(f);
+ noeval = 2;
v = Stack[bp];
goto move_args;
}
@@ -830,7 +865,7 @@
// evaluate argument list, placing arguments on stack
while (iscons(v)) {
if (SP-bp-2 == MAX_ARGS) {
- v = evlis(&Stack[bp], penv);
+ v = evlis(&Stack[bp], penv, envsz);
PUSH(v);
break;
}
@@ -885,7 +920,6 @@
if (*penv != NIL) {
// save temporary environment to the heap
lenv = penv;
- envsz = numval(penv[-1]);
pv = alloc_words(envsz + 1);
PUSH(tagptr(pv, TAG_VECTOR));
pv[0] = fixnum(envsz);
@@ -1019,7 +1053,7 @@
v = POP();
break;
case F_TRYCATCH:
- v = do_trycatch(car(Stack[bp]), penv);
+ v = do_trycatch(car(Stack[bp]), penv, envsz);
break;
// ordinary functions
@@ -1043,11 +1077,10 @@
v = tagptr(c, TAG_CONS);
break;
case F_LIST:
- if (nargs) {
- Stack[bp] = v;
- list(&v, nargs, &Stack[bp]);
- }
- // else v is already set to the final cdr, which is the result
+ if (nargs)
+ v = list(&Stack[SP-nargs], nargs);
+ else
+ v = NIL;
break;
case F_CAR:
argcount("car", nargs, 1);
@@ -1296,15 +1329,14 @@
argcount("eval", nargs, 1);
e = Stack[SP-1];
if (selfevaluating(e)) { SP=saveSP; return e; }
+ envsz = 2;
if (tail) {
assert((ulong_t)(penv-Stack)<N_STACK);
- penv[-1] = fixnum(2);
penv[0] = NIL;
penv[1] = NIL;
SP = (penv-Stack) + 2;
}
else {
- PUSH(fixnum(2));
PUSH(NIL);
PUSH(NIL);
tail = 1;
@@ -1311,12 +1343,6 @@
penv = &Stack[SP-2];
}
goto eval_top;
- case F_EVALSTAR:
- argcount("eval*", nargs, 1);
- e = Stack[SP-1];
- if (selfevaluating(e)) { SP=saveSP; return e; }
- POPN(3);
- goto eval_top;
case F_FOR:
argcount("for", nargs, 3);
lo = tofixnum(Stack[SP-3], "for");
@@ -1323,25 +1349,32 @@
hi = tofixnum(Stack[SP-2], "for");
f = Stack[SP-1];
v = car(cdr(f));
- if (!iscons(v) || !iscons(cdr_(cdr_(f))) || cdr_(v) != NIL)
+ if (!iscons(v) || !iscons(cdr_(cdr_(f))) || cdr_(v) != NIL ||
+ car_(f) != LAMBDA)
lerror(ArgError, "for: expected 1 argument lambda");
f = cdr_(f);
PUSH(f); // save function cdr
- SP += 4; // make space
- Stack[SP-4] = fixnum(3); // env size
+ SP += 3; // make space
Stack[SP-1] = cdr_(cdr_(f)); // cloenv
v = FL_F;
for(s=lo; s <= hi; s++) {
- f = Stack[SP-5];
+ f = Stack[SP-4];
Stack[SP-3] = car_(f); // lambda list
Stack[SP-2] = fixnum(s); // argument value
v = car_(cdr_(f));
- if (!selfevaluating(v)) v = eval_sexpr(v, &Stack[SP-3], 0);
+ if (!selfevaluating(v)) v = eval_sexpr(v, &Stack[SP-3], 0, 3);
}
break;
+ case F_SPECIAL_APPLYN:
+ POPN(4);
+ v = POP();
+ nargs = numval(v);
+ bp = SP-nargs-2;
+ f = Stack[bp+1];
+ goto do_apply;
case F_SPECIAL_APPLY:
- f = Stack[bp-5];
- v = Stack[bp-4];
+ f = Stack[bp-4];
+ v = Stack[bp-3];
PUSH(f);
PUSH(v);
nargs = 2;
@@ -1348,7 +1381,7 @@
// falls through!!
case F_APPLY:
argcount("apply", nargs, 2);
- v = Stack[bp] = Stack[SP-1]; // second arg is new arglist
+ v = Stack[SP-1]; // second arg is new arglist
f = Stack[bp+1] = Stack[SP-2]; // first arg is new function
POPN(2); // pop apply's args
move_args:
@@ -1373,11 +1406,19 @@
return v;
}
f = Stack[bp+1];
+ assert(SP > bp+1);
if (__likely(iscons(f))) {
if (car_(f) == COMPILEDLAMBDA) {
- v = apply_cl(nargs);
- SP = saveSP;
- return v;
+ e = apply_cl(nargs);
+ if (noeval == 2) {
+ if (selfevaluating(e)) { SP=saveSP; return(e); }
+ noeval = 0;
+ goto eval_top;
+ }
+ else {
+ SP = saveSP;
+ return e;
+ }
}
// apply lambda expression
f = Stack[bp+1] = cdr_(f);
@@ -1397,7 +1438,7 @@
else {
v = NIL;
if (i > 0) {
- list(&v, i, &NIL);
+ v = list(&Stack[SP-i], i);
if (nargs > MAX_ARGS) {
c = (cons_t*)curheap;
(c-2)->cdr = (c-1)->car;
@@ -1412,12 +1453,10 @@
if (selfevaluating(e)) { SP=saveSP; return(e); }
PUSH(cdr_(f)); // add closed environment
Stack[bp+1] = car_(Stack[bp+1]); // put lambda list
- envsz = SP - bp - 1;
if (noeval == 2) {
// macro: evaluate body in lambda environment
- Stack[bp] = fixnum(envsz);
- e = eval_sexpr(e, &Stack[bp+1], 1);
+ e = eval_sexpr(e, &Stack[bp+1], 1, SP - bp - 1);
if (selfevaluating(e)) { SP=saveSP; return(e); }
noeval = 0;
// macro: evaluate expansion in calling environment
@@ -1424,9 +1463,9 @@
goto eval_top;
}
else {
+ envsz = SP - bp - 1;
if (tail) {
// ok to overwrite environment
- penv[-1] = fixnum(envsz);
for(i=0; i < (int)envsz; i++)
penv[i] = Stack[bp+1+i];
SP = (penv-Stack)+envsz;
@@ -1433,7 +1472,6 @@
goto eval_top;
}
else {
- Stack[bp] = fixnum(envsz);
penv = &Stack[bp+1];
tail = 1;
goto eval_top;
@@ -1460,6 +1498,7 @@
- check arg counts
- allocate vararg array
- push closed env, set up new environment
+ - restore SP
** need 'copyenv' instruction that moves env to heap, installs
heap version as the current env, and pushes the result vector.
@@ -1469,8 +1508,8 @@
*/
static value_t apply_cl(uint32_t nargs)
{
- uint32_t i, n, ip, bp, envsz;
- fixnum_t s;
+ uint32_t i, n, ip, bp, envsz, saveSP=SP;
+ fixnum_t s, lo, hi;
int64_t accum;
uint8_t op, *code;
value_t func, v, bcode, x, e, ftl;
@@ -1480,34 +1519,15 @@
apply_cl_top:
func = Stack[SP-nargs-1];
+ assert(iscons(func));
+ assert(iscons(cdr_(func)));
+ assert(iscons(cdr_(cdr_(func))));
ftl = cdr_(cdr_(func));
bcode = car_(ftl);
code = cv_data((cvalue_t*)ptr(car_(bcode)));
- i = code[1];
- if (nargs < i)
+ assert(!ismanaged((uptrint_t)code));
+ if (nargs < code[1])
lerror(ArgError, "apply: too few arguments");
- if (code[0] == OP_VARGC) {
- s = (fixnum_t)nargs - (fixnum_t)i;
- v = NIL;
- if (s > 0) {
- list(&v, s, &NIL);
- if (nargs > MAX_ARGS) {
- c = (cons_t*)curheap;
- (c-2)->cdr = (c-1)->car;
- }
- // reload movable pointers
- func = Stack[SP-nargs-1];
- ftl = cdr_(cdr_(func));
- bcode = car_(ftl);
- code = cv_data((cvalue_t*)ptr(car_(bcode)));
- }
- Stack[SP-s] = v;
- SP -= (s-1);
- nargs = i+1;
- }
- else if (nargs > i) {
- lerror(ArgError, "apply: too many arguments");
- }
bp = SP-nargs;
x = cdr_(ftl); // cloenv
@@ -1514,16 +1534,48 @@
Stack[bp-1] = car_(cdr_(func)); // lambda list
penv = &Stack[bp-1];
PUSH(x);
+ // must keep a reference to the bcode object while executing it
+ PUSH(bcode);
PUSH(cdr_(bcode));
pvals = &Stack[SP-1];
- ip = 2;
+ ip = 0;
while (1) {
op = code[ip++];
+ dispatch:
switch (op) {
+ case OP_ARGC:
+ if (nargs > code[ip++]) {
+ lerror(ArgError, "apply: too many arguments");
+ }
+ break;
+ case OP_VARGC:
+ i = code[ip++];
+ s = (fixnum_t)nargs - (fixnum_t)i;
+ v = NIL;
+ if (s > 0) {
+ v = list(&Stack[bp+i], s);
+ if (nargs > MAX_ARGS) {
+ c = (cons_t*)curheap;
+ (c-2)->cdr = (c-1)->car;
+ }
+ Stack[bp+i] = v;
+ Stack[bp+i+1] = Stack[bp+nargs];
+ Stack[bp+i+2] = Stack[bp+nargs+1];
+ Stack[bp+i+3] = Stack[bp+nargs+2];
+ }
+ else {
+ PUSH(NIL);
+ Stack[SP-1] = Stack[SP-2];
+ Stack[SP-2] = Stack[SP-3];
+ Stack[SP-3] = Stack[SP-4];
+ Stack[SP-4] = NIL;
+ }
+ nargs = i+1;
+ break;
case OP_NOP: break;
case OP_DUP: v = Stack[SP-1]; PUSH(v); break;
- case OP_POP: (void)POP(); break;
+ case OP_POP: POPN(1); break;
case OP_TCALL:
case OP_CALL:
i = code[ip++]; // nargs
@@ -1534,9 +1586,13 @@
if (uintval(func) > N_BUILTINS) {
v = ((builtin_t)ptr(func))(&Stack[SP-i], i);
}
+ else {
+ PUSH(fixnum(i));
+ v = toplevel_eval(special_applyn_form);
+ }
}
- else {
- if (iscons(func) && car_(func) == COMPILEDLAMBDA) {
+ else if (iscons(func)) {
+ if (car_(func) == COMPILEDLAMBDA) {
if (op == OP_TCALL) {
for(s=-1; s < (fixnum_t)i; s++)
Stack[bp+s] = Stack[SP-i+s];
@@ -1548,7 +1604,14 @@
v = apply_cl(i);
}
}
+ else {
+ PUSH(fixnum(i));
+ v = toplevel_eval(special_applyn_form);
+ }
}
+ else {
+ type_error("apply", "function", func);
+ }
SP = s-i-1;
PUSH(v);
break;
@@ -1574,11 +1637,11 @@
if (v != FL_F) ip = *(uint32_t*)&code[ip];
else ip += 4;
break;
- case OP_RET: v = POP(); return v;
+ case OP_RET: v = POP(); SP = saveSP; return v;
case OP_EQ:
Stack[SP-2] = ((Stack[SP-2] == Stack[SP-1]) ? FL_T : FL_F);
- POP(); break;
+ POPN(1); break;
case OP_EQV:
if (Stack[SP-2] == Stack[SP-1]) {
v = FL_T;
@@ -1590,7 +1653,7 @@
v = (numval(compare(Stack[SP-2], Stack[SP-1]))==0) ?
FL_T : FL_F;
}
- Stack[SP-2] = v; POP();
+ Stack[SP-2] = v; POPN(1);
break;
case OP_EQUAL:
if (Stack[SP-2] == Stack[SP-1]) {
@@ -1603,7 +1666,7 @@
v = (numval(compare(Stack[SP-2], Stack[SP-1]))==0) ?
FL_T : FL_F;
}
- Stack[SP-2] = v; POP();
+ Stack[SP-2] = v; POPN(1);
break;
case OP_PAIRP:
Stack[SP-1] = (iscons(Stack[SP-1]) ? FL_T : FL_F); break;
@@ -1643,7 +1706,7 @@
c->car = Stack[SP-2];
c->cdr = Stack[SP-1];
Stack[SP-2] = tagptr(c, TAG_CONS);
- POP(); break;
+ POPN(1); break;
case OP_CAR:
c = tocons(Stack[SP-1], "car");
Stack[SP-1] = c->car;
@@ -1654,13 +1717,16 @@
break;
case OP_SETCAR:
car(Stack[SP-2]) = Stack[SP-1];
- POP(); break;
+ POPN(1); break;
case OP_SETCDR:
cdr(Stack[SP-2]) = Stack[SP-1];
- POP(); break;
+ POPN(1); break;
case OP_LIST:
i = code[ip++];
- list(&v, i, &NIL);
+ if (i > 0)
+ v = list(&Stack[SP-i], i);
+ else
+ v = NIL;
POPN(i);
PUSH(v);
break;
@@ -1668,7 +1734,6 @@
v = toplevel_eval(POP());
PUSH(v);
break;
- case OP_EVALSTAR:
case OP_TAPPLY:
case OP_APPLY:
@@ -1691,7 +1756,7 @@
n = code[ip++];
i = SP-n;
if (n > MAX_ARGS) goto add_ovf;
- for (; i < (int)SP; i++) {
+ for (; i < SP; i++) {
if (__likely(isfixnum(Stack[i]))) {
s += numval(Stack[i]);
if (__unlikely(!fits_fixnum(s))) {
@@ -1725,7 +1790,7 @@
if (__likely(bothfixnums(Stack[i], Stack[i+1]))) {
s = numval(Stack[i]) - numval(Stack[i+1]);
if (__likely(fits_fixnum(s))) {
- POP();
+ POPN(1);
Stack[SP-1] = fixnum(s);
break;
}
@@ -1752,7 +1817,7 @@
n = code[ip++];
i = SP-n;
if (n > MAX_ARGS) goto mul_ovf;
- for (; i < (int)SP; i++) {
+ for (; i < SP; i++) {
if (__likely(isfixnum(Stack[i]))) {
accum *= numval(Stack[i]);
}
@@ -1798,12 +1863,12 @@
v = (numval(compare(Stack[SP-2], Stack[SP-1])) < 0) ?
FL_T : FL_F;
}
- POP();
+ POPN(1);
Stack[SP-1] = v;
break;
case OP_COMPARE:
Stack[SP-2] = compare(Stack[SP-2], Stack[SP-1]);
- POP();
+ POPN(1);
break;
case OP_VECTOR:
@@ -1841,7 +1906,7 @@
else {
type_error("aref", "sequence", v);
}
- POP();
+ POPN(1);
Stack[SP-1] = v;
break;
case OP_ASET:
@@ -1862,6 +1927,19 @@
Stack[SP-1] = v;
break;
case OP_FOR:
+ lo = tofixnum(Stack[SP-3], "for");
+ hi = tofixnum(Stack[SP-2], "for");
+ //f = Stack[SP-1];
+ v = FL_F;
+ SP += 2;
+ for(s=lo; s <= hi; s++) {
+ Stack[SP-2] = Stack[SP-3];
+ Stack[SP-1] = fixnum(s);
+ v = apply_cl(1);
+ }
+ POPN(4);
+ Stack[SP-1] = v;
+ break;
case OP_LOADT: PUSH(FL_T); break;
case OP_LOADF: PUSH(FL_F); break;
@@ -1869,19 +1947,22 @@
case OP_LOAD0: PUSH(fixnum(0)); break;
case OP_LOAD1: PUSH(fixnum(1)); break;
case OP_LOADV:
+ assert(code[ip] < vector_size(*pvals));
v = vector_elt(*pvals, code[ip]); ip++;
PUSH(v);
break;
case OP_LOADVL:
- v = vector_elt(*pvals, *(uint32_t*)code[ip]); ip+=4;
+ v = vector_elt(*pvals, *(uint32_t*)&code[ip]); ip+=4;
PUSH(v);
break;
case OP_LOADGL:
- v = vector_elt(*pvals, *(uint32_t*)code[ip]); ip+=4;
+ v = vector_elt(*pvals, *(uint32_t*)&code[ip]); ip+=4;
goto do_loadg;
case OP_LOADG:
+ assert(code[ip] < vector_size(*pvals));
v = vector_elt(*pvals, code[ip]); ip++;
do_loadg:
+ assert(issymbol(v));
sym = (symbol_t*)ptr(v);
if (sym->binding == UNBOUND)
raise(list2(UnboundError, v));
@@ -1889,11 +1970,13 @@
break;
case OP_SETGL:
- v = vector_elt(*pvals, *(uint32_t*)code[ip]); ip+=4;
+ v = vector_elt(*pvals, *(uint32_t*)&code[ip]); ip+=4;
goto do_setg;
case OP_SETG:
+ assert(code[ip] < vector_size(*pvals));
v = vector_elt(*pvals, code[ip]); ip++;
do_setg:
+ assert(issymbol(v));
sym = (symbol_t*)ptr(v);
v = Stack[SP-1];
if (sym->syntax != TAG_CONST)
@@ -1901,20 +1984,32 @@
break;
case OP_LOADA:
+ assert(nargs > 0);
i = code[ip++];
- if (penv[0] == NIL)
+ if (penv[0] == NIL) {
+ assert(isvector(penv[1]));
+ assert(i+1 < vector_size(penv[1]));
v = vector_elt(penv[1], i+1);
- else
+ }
+ else {
+ assert(bp+i < SP);
v = Stack[bp+i];
+ }
PUSH(v);
break;
case OP_SETA:
+ assert(nargs > 0);
v = Stack[SP-1];
i = code[ip++];
- if (penv[0] == NIL)
+ if (penv[0] == NIL) {
+ assert(isvector(penv[1]));
+ assert(i+1 < vector_size(penv[1]));
vector_elt(penv[1], i+1) = v;
- else
+ }
+ else {
+ assert(bp+i < SP);
Stack[bp+i] = v;
+ }
break;
case OP_LOADC:
case OP_SETC:
@@ -1932,6 +2027,8 @@
}
while (s--)
v = vector_elt(v, vector_size(v)-1);
+ assert(isvector(v));
+ assert(i < vector_size(v));
if (op == OP_SETC)
vector_elt(v, i) = Stack[SP-1];
else
@@ -1969,11 +2066,14 @@
//if (!iscons(e=cdr_(e))) goto notpair;
c->car = car_(e); //body
c->cdr = Stack[SP-1]; //env
- POP();
+ POPN(1);
Stack[SP-1] = v;
break;
case OP_TRYCATCH:
+ v = do_trycatch2();
+ POPN(1);
+ Stack[SP-1] = v;
break;
}
}
@@ -2049,10 +2149,11 @@
set(printwidthsym=symbol("*print-width*"), fixnum(SCR_WIDTH));
lasterror = NIL;
special_apply_form = fl_cons(builtin(F_SPECIAL_APPLY), NIL);
+ special_applyn_form = fl_cons(builtin(F_SPECIAL_APPLYN), NIL);
apply1_args = fl_cons(NIL, NIL);
i = 0;
while (isspecial(builtin(i))) {
- if (i != F_SPECIAL_APPLY)
+ if (i != F_SPECIAL_APPLY && i != F_SPECIAL_APPLYN)
((symbol_t*)ptr(symbol(builtin_names[i])))->syntax = builtin(i);
i++;
}
@@ -2096,7 +2197,6 @@
{
value_t v;
uint32_t saveSP = SP;
- PUSH(fixnum(2));
PUSH(NIL);
PUSH(NIL);
v = topeval(expr, &Stack[SP-2]);
@@ -2111,7 +2211,7 @@
for(i=argc-1; i >= 0; i--) {
PUSH(cvalue_static_cstring(argv[i]));
Stack[SP-2] = fl_cons(Stack[SP-1], Stack[SP-2]);
- (void)POP();
+ POPN(1);
}
return POP();
}
@@ -2149,7 +2249,7 @@
v = toplevel_eval(e);
}
ios_close(value2c(ios_t*,Stack[SP-1]));
- (void)POP();
+ POPN(1);
PUSH(symbol_value(symbol("__start")));
PUSH(argv_list(argc, argv));
--- a/femtolisp/flisp.h
+++ b/femtolisp/flisp.h
@@ -117,7 +117,7 @@
enum {
// special forms
F_QUOTE=0, F_COND, F_IF, F_AND, F_OR, F_WHILE, F_LAMBDA,
- F_TRYCATCH, F_SPECIAL_APPLY, F_SETQ, F_PROG1, F_BEGIN,
+ F_TRYCATCH, F_SPECIAL_APPLY, F_SPECIAL_APPLYN, F_SETQ, F_PROG1, F_BEGIN,
// functions
F_EQ, F_EQV, F_EQUAL, F_ATOM, F_NOT, F_NULL, F_BOOLEANP, F_SYMBOLP,
@@ -124,7 +124,7 @@
F_NUMBERP, F_BOUNDP, F_CONSP, F_BUILTINP, F_VECTORP, F_FIXNUMP,
F_CONS, F_LIST, F_CAR, F_CDR, F_SETCAR, F_SETCDR,
- F_EVAL, F_EVALSTAR, F_APPLY,
+ F_EVAL, F_APPLY,
F_ADD, F_SUB, F_MUL, F_DIV, F_LT, F_COMPARE,
F_VECTOR, F_AREF, F_ASET, F_FOR,
@@ -141,6 +141,7 @@
value_t toplevel_eval(value_t expr);
value_t apply(value_t f, value_t l);
value_t apply1(value_t f, value_t a0);
+value_t applyn(uint32_t n, value_t f, ...);
value_t load_file(char *fname);
/* object model manipulation */
--- /dev/null
+++ b/femtolisp/opcodes.h
@@ -1,0 +1,26 @@
+#ifndef __OPCODES_H_
+#define __OPCODES_H_
+
+enum {
+ OP_NOP=0, OP_DUP, OP_POP, OP_CALL, OP_TCALL, OP_JMP, OP_BRF, OP_BRT,
+ OP_JMPL, OP_BRFL, OP_BRTL, OP_RET, OP_TAPPLY,
+
+ OP_EQ, OP_EQV, OP_EQUAL, OP_ATOMP, OP_NOT, OP_NULLP, OP_BOOLEANP,
+ OP_SYMBOLP, OP_NUMBERP, OP_BOUNDP, OP_PAIRP, OP_BUILTINP, OP_VECTORP,
+ OP_FIXNUMP,
+
+ OP_CONS, OP_LIST, OP_CAR, OP_CDR, OP_SETCAR, OP_SETCDR,
+ OP_EVAL, OP_APPLY,
+
+ OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_LT, OP_COMPARE,
+
+ OP_VECTOR, OP_AREF, OP_ASET, OP_FOR,
+
+ OP_LOADT, OP_LOADF, OP_LOADNIL, OP_LOAD0, OP_LOAD1, OP_LOADV, OP_LOADVL,
+ OP_LOADG, OP_LOADA, OP_LOADC, OP_LOADGL, OP_SETG, OP_SETA, OP_SETC,
+ OP_SETGL,
+
+ OP_CLOSURE, OP_TRYCATCH, OP_ARGC, OP_VARGC
+};
+
+#endif
--- a/femtolisp/system.lsp
+++ b/femtolisp/system.lsp
@@ -105,7 +105,8 @@
(define (char? x) (eq? (typeof x) 'wchar))
(define (function? x)
(or (builtin? x)
- (and (pair? x) (eq (car x) 'lambda))))
+ (and (pair? x) (or (eq (car x) 'lambda)
+ (eq (car x) 'compiled-lambda)))))
(define procedure? function?)
(define (caar x) (car (car x)))
@@ -642,6 +643,8 @@
(define (expand x) (macroexpand x))
+(define (load-process x) (eval (expand x)))
+
(define (load filename)
(let ((F (file filename :read)))
(trycatch
@@ -649,15 +652,18 @@
(if (not (io.eof? F))
(next (read F)
prev
- (eval (expand E)))
+ (load-process E))
(begin (io.close F)
; evaluate last form in almost-tail position
- (eval (expand E)))))
+ (load-process E))))
(lambda (e)
(begin
(io.close F)
(raise `(load-error ,filename ,e)))))))
+;(load (string *install-dir* *directory-separator* "compiler.lsp"))
+;(define (load-process x) ((compile-thunk (expand x))))
+
(define *banner* (string.tail "
; _
; |_ _ _ |_ _ | . _ _
@@ -679,7 +685,7 @@
#t))))
(define (reploop)
(when (trycatch (and (prompt) (newline))
- print-exception)
+ (lambda (e) (print-exception e)))
(begin (newline)
(reploop))))
(reploop)
--- a/femtolisp/table.c
+++ b/femtolisp/table.c
@@ -168,7 +168,6 @@
value_t fl_table_foldl(value_t *args, uint32_t nargs)
{
argcount("table.foldl", nargs, 3);
- PUSH(listn(3, NIL, NIL, NIL));
htable_t *h = totable(args[2], "table.foldl");
size_t i, n = h->size;
void **table = h->table;
@@ -175,11 +174,10 @@
value_t c;
for(i=0; i < n; i+=2) {
if (table[i+1] != HT_NOTFOUND) {
- c = Stack[SP-1];
- car_(c) = (value_t)table[i];
- car_(cdr_(c)) = (value_t)table[i+1];
- car_(cdr_(cdr_(c))) = args[1];
- args[1] = apply(args[0], c);
+ args[1] = applyn(3, args[0],
+ (value_t)table[i],
+ (value_t)table[i+1],
+ args[1]);
// reload pointer
h = (htable_t*)cv_data((cvalue_t*)ptr(args[2]));
if (h->size != n)
@@ -187,7 +185,6 @@
table = h->table;
}
}
- (void)POP();
return args[1];
}
--- a/femtolisp/todo
+++ b/femtolisp/todo
@@ -1012,3 +1012,20 @@
struct _fltype_t *artype; // (array this)
int marked;
} fltype_t;
+
+-----------------------------------------------------------------------------
+
+new evaluator todo:
+
+- need builtin = to handle nans properly, fix equal? on nans
+- builtin quasi-opaque function type
+ fields: signature, maxstack, bcode, vals, cloenv
+ function->vector
+- make (for ...) a special form
+- trycatch should require 2nd arg to be a lambda expression
+- maxstack calculation, replace Stack with C stack, alloca
+ - stack traces and better debugging support
+- lambda lifting
+- let optimization
+- have macroexpand use its own global syntax table
+- be able to create/load an image file