ref: e4415ad27d5d9730ffa6a6407869d0401f2bfa49
dir: /flisp.c/
/* femtoLisp by Jeff Bezanson (C) 2009 Distributed under the BSD License */ #include "llt.h" #include "flisp.h" #include "operators.h" #include "cvalues.h" #include "opcodes.h" #include "types.h" #include "print.h" #include "read.h" #include "timefuncs.h" #include "equal.h" #include "hashing.h" #include "table.h" #include "iostream.h" #include "fsixel.h" typedef struct { char *name; builtin_t fptr; }builtinspec_t; __thread Fl *fl; int isbuiltin(value_t x) { int i = uintval(x); return tag(x) == TAG_FUNCTION && i < nelem(builtins) && builtins[i].name != nil; } static value_t apply_cl(uint32_t nargs); // error utilities ------------------------------------------------------------ void free_readstate(fl_readstate_t *rs) { htable_free(&rs->backrefs); htable_free(&rs->gensyms); } _Noreturn void fl_exit(int status) { fl->exiting = true; gc(0); exit(status); } #define FL_TRY \ fl_exception_context_t _ctx; int l__tr, l__ca; \ _ctx.sp = fl->SP; _ctx.frame = fl->curr_frame; _ctx.rdst = fl->readstate; _ctx.prev = fl->exctx; \ _ctx.ngchnd = fl->N_GCHND; fl->exctx = &_ctx; \ if(!setjmp(_ctx.buf)) \ for(l__tr = 1; l__tr; l__tr = 0, (void)(fl->exctx = fl->exctx->prev)) #define FL_CATCH_INC \ l__ca = 0, fl->lasterror = fl->FL_NIL, fl->throwing_frame = 0, fl->SP = _ctx.sp, fl->curr_frame = _ctx.frame #define FL_CATCH \ else \ for(l__ca = 1; l__ca; FL_CATCH_INC) #define FL_CATCH_NO_INC \ else \ for(l__ca = 1; l__ca;) void fl_savestate(fl_exception_context_t *_ctx) { _ctx->sp = fl->SP; _ctx->frame = fl->curr_frame; _ctx->rdst = fl->readstate; _ctx->prev = fl->exctx; _ctx->ngchnd = fl->N_GCHND; } void fl_restorestate(fl_exception_context_t *_ctx) { fl->lasterror = fl->FL_NIL; fl->throwing_frame = 0; fl->SP = _ctx->sp; fl->curr_frame = _ctx->frame; } _Noreturn void fl_raise(value_t e) { fl->lasterror = e; // unwind read state while(fl->readstate != fl->exctx->rdst){ free_readstate(fl->readstate); fl->readstate = fl->readstate->prev; } if(fl->throwing_frame == 0) fl->throwing_frame = fl->curr_frame; fl->N_GCHND = fl->exctx->ngchnd; fl_exception_context_t *thisctx = fl->exctx; if(fl->exctx->prev) // don't throw past toplevel fl->exctx = fl->exctx->prev; longjmp(thisctx->buf, 1); } _Noreturn void lerrorf(value_t e, char *format, ...) { char msgbuf[256]; va_list args; PUSH(e); va_start(args, format); vsnprintf(msgbuf, sizeof(msgbuf), format, args); value_t msg = string_from_cstr(msgbuf); va_end(args); e = POP(); fl_raise(fl_list2(e, msg)); } _Noreturn void type_error(char *expected, value_t got) { fl_raise(fl_listn(3, fl->TypeError, symbol(expected), got)); } _Noreturn void bounds_error(value_t arr, value_t ind) { fl_raise(fl_listn(3, fl->BoundsError, arr, ind)); } _Noreturn void unbound_error(value_t sym) { fl_raise(fl_listn(2, fl->UnboundError, sym)); } // safe cast operators -------------------------------------------------------- #define isstring fl_isstring #define SAFECAST_OP(type, ctype, cnvt) \ ctype to##type(value_t v) \ { \ if(__likely(is##type(v))) \ return (ctype)cnvt(v); \ type_error(#type, v); \ } SAFECAST_OP(cons, cons_t*, ptr) SAFECAST_OP(symbol, symbol_t*, ptr) SAFECAST_OP(fixnum, fixnum_t, numval) //SAFECAST_OP(cvalue, cvalue_t*, ptr) SAFECAST_OP(string, char*, cvalue_data) #undef isstring // symbol table --------------------------------------------------------------- int fl_is_keyword_name(char *str, size_t len) { return (str[0] == ':' || str[len-1] == ':') && str[1] != '\0'; } static symbol_t * mk_symbol(char *str) { symbol_t *sym; size_t len = strlen(str); sym = calloc(1, sizeof(*sym)-sizeof(void*) + len + 1); assert(((uintptr_t)sym & 0x7) == 0); // make sure malloc aligns 8 sym->numtype = NONNUMERIC; if(fl_is_keyword_name(str, len)){ value_t s = tagptr(sym, TAG_SYM); setc(s, s); sym->flags |= FLAG_KEYWORD; }else{ sym->binding = UNBOUND; } sym->hash = memhash32(str, len)^0xAAAAAAAA; memmove(sym->name, str, len+1); return sym; } static symbol_t ** symtab_lookup(symbol_t **ptree, char *str) { int x; while(*ptree != nil && (x = strcmp(str, (*ptree)->name)) != 0) ptree = x < 0 ? &(*ptree)->left : &(*ptree)->right; return ptree; } value_t symbol(char *str) { symbol_t **pnode; pnode = symtab_lookup(&fl->symtab, str); if(*pnode == nil) *pnode = mk_symbol(str); return tagptr(*pnode, TAG_SYM); } BUILTIN("gensym", gensym) { argcount(nargs, 0); USED(args); gensym_t *gs = alloc_words(sizeof(gensym_t)/sizeof(void*)); gs->id = fl->_gensym_ctr++; gs->binding = UNBOUND; gs->isconst = 0; gs->type = nil; return tagptr(gs, TAG_SYM); } value_t gensym(void) { return fn_builtin_gensym(nil, 0); } BUILTIN("gensym?", gensymp) { argcount(nargs, 1); return isgensym(args[0]) ? fl->FL_T : fl->FL_F; } char * uint2str(char *dest, size_t len, uint64_t num, uint32_t base) { int i = len-1; uint64_t b = (uint64_t)base; char ch; dest[i--] = '\0'; while(i >= 0){ ch = (char)(num % b); if(ch < 10) ch += '0'; else ch = ch-10+'a'; dest[i--] = ch; num /= b; if(num == 0) break; } return &dest[i+1]; } char * symbol_name(value_t v) { if(ismanaged(v)){ gensym_t *gs = (gensym_t*)ptr(v); fl->gsnameno = 1-fl->gsnameno; char *n = uint2str(fl->gsname[fl->gsnameno]+1, sizeof(fl->gsname[0])-1, gs->id, 10); *(--n) = 'g'; return n; } return ((symbol_t*)ptr(v))->name; } // conses --------------------------------------------------------------------- value_t mk_cons(void) { cons_t *c; if(__unlikely(fl->curheap > fl->lim)) gc(0); c = (cons_t*)fl->curheap; fl->curheap += sizeof(cons_t); return tagptr(c, TAG_CONS); } void * alloc_words(int n) { value_t *first; assert(n > 0); n = LLT_ALIGN(n, 2); // only allocate multiples of 2 words if(__unlikely((value_t*)fl->curheap > ((value_t*)fl->lim)+2-n)){ gc(0); while((value_t*)fl->curheap > ((value_t*)fl->lim)+2-n) gc(1); } first = (value_t*)fl->curheap; fl->curheap += (n*sizeof(value_t)); return first; } value_t alloc_vector(size_t n, int init) { if(n == 0) return fl->the_empty_vector; value_t *c = alloc_words(n+1); value_t v = tagptr(c, TAG_VECTOR); vector_setsize(v, n); if(init){ unsigned int i; for(i = 0; i < n; i++) vector_elt(v, i) = fl->FL_UNSPECIFIED; } return v; } // collector ------------------------------------------------------------------ void fl_gc_handle(value_t *pv) { if(__unlikely(fl->N_GCHND >= N_GC_HANDLES)) lerrorf(fl->MemoryError, "out of gc handles"); fl->GCHandleStack[fl->N_GCHND++] = pv; } void fl_free_gc_handles(uint32_t n) { assert(fl->N_GCHND >= n); fl->N_GCHND -= n; } value_t relocate(value_t v) { value_t a, d, nc, first, *pcdr; uintptr_t t = tag(v); if(t == TAG_CONS){ // iterative implementation allows arbitrarily long cons chains pcdr = &first; do{ if((a = car_(v)) == TAG_FWD){ *pcdr = cdr_(v); return first; } *pcdr = nc = tagptr((cons_t*)fl->curheap, TAG_CONS); fl->curheap += sizeof(cons_t); d = cdr_(v); car_(v) = TAG_FWD; cdr_(v) = nc; car_(nc) = relocate(a); pcdr = &cdr_(nc); v = d; }while(iscons(v)); *pcdr = d == fl->NIL ? fl->NIL : relocate(d); return first; } if((t&3) == 0) return v; if(!ismanaged(v)) return v; if(isforwarded(v)) return forwardloc(v); if(t == TAG_VECTOR){ // N.B.: 0-length vectors secretly have space for a first element size_t i, sz = vector_size(v); if(vector_elt(v, -1) & 0x1){ // grown vector nc = relocate(vector_elt(v, 0)); forward(v, nc); }else{ nc = tagptr(alloc_words(sz+1), TAG_VECTOR); vector_setsize(nc, sz); a = vector_elt(v, 0); forward(v, nc); if(sz > 0){ vector_elt(nc, 0) = relocate(a); for(i = 1; i < sz; i++) vector_elt(nc, i) = relocate(vector_elt(v, i)); } } return nc; } if(t == TAG_CPRIM){ cprim_t *pcp = ptr(v); size_t nw = CPRIM_NWORDS-1+NWORDS(cp_class(pcp)->size); cprim_t *ncp = alloc_words(nw); while(nw--) ((value_t*)ncp)[nw] = ((value_t*)pcp)[nw]; nc = tagptr(ncp, TAG_CPRIM); forward(v, nc); return nc; } if(t == TAG_CVALUE) return cvalue_relocate(v); if(t == TAG_FUNCTION){ function_t *fn = ptr(v); function_t *nfn = alloc_words(4); nfn->bcode = fn->bcode; nfn->vals = fn->vals; nc = tagptr(nfn, TAG_FUNCTION); forward(v, nc); nfn->env = relocate(fn->env); nfn->vals = relocate(nfn->vals); nfn->bcode = relocate(nfn->bcode); assert(!ismanaged(fn->name)); nfn->name = fn->name; return nc; } if(t == TAG_SYM){ gensym_t *gs = ptr(v); gensym_t *ng = alloc_words(sizeof(gensym_t)/sizeof(void*)); ng->id = gs->id; ng->binding = gs->binding; ng->isconst = 0; nc = tagptr(ng, TAG_SYM); forward(v, nc); if(ng->binding != UNBOUND) ng->binding = relocate(ng->binding); return nc; } return v; } value_t relocate_lispvalue(value_t v) { return relocate(v); } static void trace_globals(symbol_t *root) { while(root != nil){ if(root->binding != UNBOUND) root->binding = relocate(root->binding); trace_globals(root->left); root = root->right; } } void gc(int mustgrow) { void *temp; uint32_t i, f, top; fl_readstate_t *rs; fl->curheap = fl->tospace; if(fl->grew) fl->lim = fl->curheap+fl->heapsize*2-sizeof(cons_t); else fl->lim = fl->curheap+fl->heapsize-sizeof(cons_t); if(fl->throwing_frame > fl->curr_frame){ top = fl->throwing_frame - 4; f = fl->Stack[fl->throwing_frame-4]; }else{ top = fl->SP; f = fl->curr_frame; } while(1){ for(i = f; i < top; i++) fl->Stack[i] = relocate(fl->Stack[i]); if(f == 0) break; top = f - 4; f = fl->Stack[f-4]; } for(i = 0; i < fl->N_GCHND; i++) *fl->GCHandleStack[i] = relocate(*fl->GCHandleStack[i]); trace_globals(fl->symtab); relocate_typetable(); rs = fl->readstate; while(rs){ value_t ent; for(i = 0; i < rs->backrefs.size; i++){ ent = (value_t)rs->backrefs.table[i]; if(ent != (value_t)HT_NOTFOUND) rs->backrefs.table[i] = (void*)relocate(ent); } for(i = 0; i < rs->gensyms.size; i++){ ent = (value_t)rs->gensyms.table[i]; if(ent != (value_t)HT_NOTFOUND) rs->gensyms.table[i] = (void*)relocate(ent); } rs->source = relocate(rs->source); rs = rs->prev; } fl->lasterror = relocate(fl->lasterror); fl->memory_exception_value = relocate(fl->memory_exception_value); fl->the_empty_vector = relocate(fl->the_empty_vector); sweep_finalizers(); #ifdef VERBOSEGC printf("GC: found %d/%d live conses\n", (fl->curheap-fl->tospace)/sizeof(cons_t), fl->heapsize/sizeof(cons_t)); #endif temp = fl->tospace; fl->tospace = fl->fromspace; fl->fromspace = temp; // if we're using > 80% of the space, resize tospace so we have // more space to fill next time. if we grew tospace last time, // grow the other half of the heap this time to catch up. if(fl->grew || ((fl->lim-fl->curheap) < (int)(fl->heapsize/5)) || mustgrow){ temp = LLT_REALLOC(fl->tospace, fl->heapsize*2); if(__unlikely(temp == nil)) fl_raise(fl->memory_exception_value); fl->tospace = temp; if(fl->grew){ fl->heapsize *= 2; temp = bitvector_resize(fl->consflags, 0, fl->heapsize/sizeof(cons_t), 1); if(__unlikely(temp == nil)) fl_raise(fl->memory_exception_value); fl->consflags = (uint32_t*)temp; } fl->grew = !fl->grew; } if(fl->curheap > fl->lim) // all data was live gc(0); } static void grow_stack(void) { size_t newsz = fl->N_STACK * 2; value_t *ns = LLT_REALLOC(fl->Stack, newsz*sizeof(value_t)); if(__unlikely(ns == nil)) lerrorf(fl->MemoryError, "stack overflow"); fl->Stack = ns; fl->N_STACK = newsz; } // utils ---------------------------------------------------------------------- // apply function with n args on the stack static value_t _applyn(uint32_t n) { value_t f = fl->Stack[fl->SP-n-1]; uint32_t saveSP = fl->SP; value_t v; if(iscbuiltin(f)){ v = ((builtin_t*)ptr(f))[3](&fl->Stack[fl->SP-n], n); }else if(isfunction(f)){ v = apply_cl(n); }else if(__likely(isbuiltin(f))){ value_t tab = symbol_value(fl->builtins_table_sym); if(__unlikely(ptr(tab) == nil)) unbound_error(tab); fl->Stack[fl->SP-n-1] = vector_elt(tab, uintval(f)); v = apply_cl(n); }else{ type_error("function", f); } fl->SP = saveSP; return v; } value_t fl_apply(value_t f, value_t l) { value_t v = l; uint32_t n = fl->SP; PUSH(f); while(iscons(v)){ if(fl->SP >= fl->N_STACK) grow_stack(); PUSH(car_(v)); v = cdr_(v); } n = fl->SP - n - 1; v = _applyn(n); POPN(n+1); return v; } value_t fl_applyn(uint32_t n, value_t f, ...) { va_list ap; va_start(ap, f); size_t i; PUSH(f); while(fl->SP+n >= fl->N_STACK) grow_stack(); for(i = 0; i < n; i++){ value_t a = va_arg(ap, value_t); PUSH(a); } value_t v = _applyn(n); POPN(n+1); va_end(ap); return v; } value_t fl_listn(size_t n, ...) { va_list ap; va_start(ap, n); uint32_t si = fl->SP; size_t i; while(fl->SP+n >= fl->N_STACK) grow_stack(); for(i = 0; i < n; i++){ value_t a = va_arg(ap, value_t); PUSH(a); } cons_t *c = alloc_words(n*2); cons_t *l = c; for(i = 0; i < n; i++){ c->car = fl->Stack[si++]; c->cdr = tagptr(c+1, TAG_CONS); c++; } c[-1].cdr = fl->NIL; POPN(n); va_end(ap); return tagptr(l, TAG_CONS); } value_t fl_list2(value_t a, value_t b) { PUSH(a); PUSH(b); cons_t *c = alloc_words(4); b = POP(); a = POP(); c[0].car = a; c[0].cdr = tagptr(c+1, TAG_CONS); c[1].car = b; c[1].cdr = fl->NIL; return tagptr(c, TAG_CONS); } value_t fl_cons(value_t a, value_t b) { PUSH(a); PUSH(b); value_t c = mk_cons(); cdr_(c) = POP(); car_(c) = POP(); return c; } int fl_isnumber(value_t v) { if(isfixnum(v)) return 1; if(iscprim(v)){ cprim_t *c = ptr(v); return c->type != fl->runetype; } if(iscvalue(v)){ cvalue_t *c = ptr(v); return valid_numtype(cv_class(c)->numtype); } return 0; } // eval ----------------------------------------------------------------------- static value_t list(value_t *args, uint32_t nargs, int star) { cons_t *c; uint32_t i; value_t v; v = cons_reserve(nargs); c = ptr(v); for(i = 0; i < nargs; i++){ c->car = args[i]; c->cdr = tagptr(c+1, TAG_CONS); c++; } if(star) c[-2].cdr = c[-1].car; else c[-1].cdr = fl->NIL; return v; } static value_t copy_list(value_t L) { if(!iscons(L)) return fl->NIL; PUSH(fl->NIL); PUSH(L); value_t *plcons = &fl->Stack[fl->SP-2]; value_t *pL = &fl->Stack[fl->SP-1]; value_t c; c = mk_cons(); PUSH(c); // save first cons car_(c) = car_(*pL); cdr_(c) = fl->NIL; *plcons = c; *pL = cdr_(*pL); while(iscons(*pL)){ c = mk_cons(); car_(c) = car_(*pL); cdr_(c) = fl->NIL; cdr_(*plcons) = c; *plcons = c; *pL = cdr_(*pL); } c = POP(); // first cons POPN(2); return c; } static value_t do_trycatch(void) { uint32_t saveSP = fl->SP; value_t v = fl->NIL; value_t thunk = fl->Stack[fl->SP-2]; fl->Stack[fl->SP-2] = fl->Stack[fl->SP-1]; fl->Stack[fl->SP-1] = thunk; FL_TRY{ v = apply_cl(0); } FL_CATCH{ v = fl->Stack[saveSP-2]; PUSH(v); PUSH(fl->lasterror); v = apply_cl(1); } fl->SP = saveSP; return v; } /* argument layout on stack is |--required args--|--opt args--|--kw args--|--rest args... */ static uint32_t process_keys(value_t kwtable, uint32_t nreq, uint32_t nkw, uint32_t nopt, uint32_t bp, uint32_t nargs, int va) { uint32_t extr = nopt+nkw; uint32_t ntot = nreq+extr; value_t args[64], v = fl->NIL; uint32_t i, a = 0, nrestargs; value_t s1 = fl->Stack[fl->SP-1]; value_t s2 = fl->Stack[fl->SP-2]; value_t s4 = fl->Stack[fl->SP-4]; value_t s5 = fl->Stack[fl->SP-5]; if(__unlikely(nargs < nreq)) lerrorf(fl->ArgError, "too few arguments"); if(__unlikely(extr > nelem(args))) lerrorf(fl->ArgError, "too many arguments"); for(i = 0; i < extr; i++) args[i] = UNBOUND; for(i = nreq; i < nargs; i++){ v = fl->Stack[bp+i]; if(issymbol(v) && iskeyword((symbol_t*)ptr(v))) break; if(a >= nopt) goto no_kw; args[a++] = v; } if(i >= nargs) goto no_kw; // now process keywords uintptr_t n = vector_size(kwtable)/2; do{ i++; if(__unlikely(i >= nargs)) lerrorf(fl->ArgError, "keyword %s requires an argument", symbol_name(v)); value_t hv = fixnum(((symbol_t*)ptr(v))->hash); lltint_t lx = numval(hv); uintptr_t x = 2*((lx < 0 ? -lx : lx) % n); if(__likely(vector_elt(kwtable, x) == v)){ uintptr_t idx = numval(vector_elt(kwtable, x+1)); assert(idx < nkw); idx += nopt; if(args[idx] == UNBOUND){ // if duplicate key, keep first value args[idx] = fl->Stack[bp+i]; } }else{ lerrorf(fl->ArgError, "unsupported keyword %s", symbol_name(v)); } i++; if(i >= nargs) break; v = fl->Stack[bp+i]; }while(issymbol(v) && iskeyword((symbol_t*)ptr(v))); no_kw: nrestargs = nargs - i; if(__unlikely(!va && nrestargs > 0)) lerrorf(fl->ArgError, "too many arguments"); nargs = ntot + nrestargs; if(nrestargs) memmove(&fl->Stack[bp+ntot], &fl->Stack[bp+i], nrestargs*sizeof(value_t)); memmove(&fl->Stack[bp+nreq], args, extr*sizeof(value_t)); fl->SP = bp + nargs; assert(fl->SP < fl->N_STACK-5); PUSH(s5); PUSH(s4); PUSH(nargs); PUSH(s2); PUSH(s1); fl->curr_frame = fl->SP; return nargs; } #if BYTE_ORDER == BIG_ENDIAN #define GET_INT32(a) \ ((int32_t) \ ((((int32_t)a[0])<<0) | \ (((int32_t)a[1])<<8) | \ (((int32_t)a[2])<<16) | \ (((int32_t)a[3])<<24))) #define GET_INT16(a) \ ((int16_t) \ ((((int16_t)a[0])<<0) | \ (((int16_t)a[1])<<8))) #define PUT_INT32(a, i) (*(int32_t*)(a) = bswap_32((int32_t)(i))) #else #define GET_INT32(a) (*(int32_t*)a) #define GET_INT16(a) (*(int16_t*)a) #define PUT_INT32(a, i) (*(int32_t*)(a) = (int32_t)(i)) #endif #define OP(x) case x: #define NEXT_OP break /* stack on entry: <func> <nargs args...> caller's responsibility: - put the stack in this state - provide arg count - respect tail position - restore SP callee's responsibility: - check arg counts - allocate vararg array - push closed env, set up new environment */ static value_t apply_cl(uint32_t nargs) { uint32_t top_frame = fl->curr_frame; // frame variables uint32_t n, captured; uint32_t bp; const uint8_t *ip; fixnum_t s, hi; int tail, x; // temporary variables (not necessary to preserve across calls) uint32_t op, i, ipd; symbol_t *sym; cons_t *c; value_t *pv; value_t func, v, e; n = 0; v = 0; USED(n); USED(v); apply_cl_top: captured = 0; func = fl->Stack[fl->SP-nargs-1]; ip = cv_data((cvalue_t*)ptr(fn_bcode(func))); assert(!ismanaged((uintptr_t)ip)); i = fl->SP+GET_INT32(ip); while(i >= fl->N_STACK) grow_stack(); ip += 4; bp = fl->SP-nargs; PUSH(fn_env(func)); PUSH(fl->curr_frame); PUSH(nargs); ipd = fl->SP; fl->SP++; // ip PUSH(0); // captured? fl->curr_frame = fl->SP; op = *ip++; while(1){ switch(op){ OP(OP_LOADA0) PUSH(captured ? vector_elt(fl->Stack[bp], 0) : fl->Stack[bp]); NEXT_OP; OP(OP_LOADA1) PUSH(captured ? vector_elt(fl->Stack[bp], 1) : fl->Stack[bp+1]); NEXT_OP; OP(OP_LOADV) v = fn_vals(fl->Stack[bp-1]); assert(*ip < vector_size(v)); PUSH(vector_elt(v, *ip++)); NEXT_OP; OP(OP_BRF) ip += POP() == fl->FL_F ? GET_INT16(ip) : 2; NEXT_OP; OP(OP_POP) POPN(1); NEXT_OP; OP(OP_TCALLL) tail = 1; if(0){ OP(OP_CALLL) tail = 0; } n = GET_INT32(ip); ip += 4; if(0){ OP(OP_TCALL) tail = 1; if(0){ OP(OP_CALL) tail = 0; } n = *ip++; // nargs } do_call: fl->Stack[ipd] = (uintptr_t)ip; func = fl->Stack[fl->SP-n-1]; if(tag(func) == TAG_FUNCTION){ if(func > (N_BUILTINS<<3)){ if(tail){ fl->curr_frame = fl->Stack[fl->curr_frame-4]; for(s = -1; s < (fixnum_t)n; s++) fl->Stack[bp+s] = fl->Stack[fl->SP-n+s]; fl->SP = bp+n; } nargs = n; goto apply_cl_top; }else{ i = uintval(func); if(isbuiltin(func)){ s = builtins[i].nargs; if(s >= 0) argcount(n, s); else if(s != ANYARGS && (signed)n < -s) argcount(n, -s); // remove function arg for(s = fl->SP-n-1; s < (int)fl->SP-1; s++) fl->Stack[s] = fl->Stack[s+1]; fl->SP--; switch(i){ case OP_LIST: goto apply_list; case OP_VECTOR: goto apply_vector; case OP_APPLY: goto apply_apply; case OP_ADD: goto apply_add; case OP_SUB: goto apply_sub; case OP_MUL: goto apply_mul; case OP_DIV: goto apply_div; default: op = i; continue; } } } }else if(__likely(iscbuiltin(func))){ s = fl->SP; v = (((builtin_t*)ptr(func))[3])(&fl->Stack[fl->SP-n], n); fl->SP = s-n; fl->Stack[fl->SP-1] = v; NEXT_OP; } type_error("function", func); OP(OP_LOADGL) v = fn_vals(fl->Stack[bp-1]); v = vector_elt(v, GET_INT32(ip)); ip += 4; if(0){ OP(OP_LOADG) v = fn_vals(fl->Stack[bp-1]); assert(*ip < vector_size(v)); v = vector_elt(v, *ip); ip++; } assert(issymbol(v)); sym = (symbol_t*)ptr(v); if(__unlikely(sym->binding == UNBOUND)){ fl->Stack[ipd] = (uintptr_t)ip; unbound_error(v); } PUSH(sym->binding); NEXT_OP; OP(OP_LOADA) assert(nargs > 0); i = *ip++; if(captured){ e = fl->Stack[bp]; assert(isvector(e)); assert(i < vector_size(e)); v = vector_elt(e, i); }else{ v = fl->Stack[bp+i]; } PUSH(v); NEXT_OP; OP(OP_LOADC) s = *ip++; i = *ip++; v = fl->Stack[bp+nargs]; while(s--) v = vector_elt(v, vector_size(v)-1); assert(isvector(v)); assert(i < vector_size(v)); PUSH(vector_elt(v, i)); NEXT_OP; OP(OP_RET) v = POP(); fl->SP = fl->curr_frame; fl->curr_frame = fl->Stack[fl->SP-4]; if(fl->curr_frame == top_frame) return v; fl->SP -= 5+nargs; captured = fl->Stack[fl->curr_frame-1]; ipd = fl->curr_frame-2; ip = (uint8_t*)fl->Stack[ipd]; nargs = fl->Stack[fl->curr_frame-3]; bp = fl->curr_frame - 5 - nargs; fl->Stack[fl->SP-1] = v; NEXT_OP; OP(OP_DUP) fl->SP++; fl->Stack[fl->SP-1] = fl->Stack[fl->SP-2]; NEXT_OP; OP(OP_CAR) v = fl->Stack[fl->SP-1]; if(__unlikely(!iscons(v))){ fl->Stack[ipd] = (uintptr_t)ip; type_error("cons", v); } fl->Stack[fl->SP-1] = car_(v); NEXT_OP; OP(OP_CDR) v = fl->Stack[fl->SP-1]; if(__unlikely(!iscons(v))){ fl->Stack[ipd] = (uintptr_t)ip; type_error("cons", v); } fl->Stack[fl->SP-1] = cdr_(v); NEXT_OP; OP(OP_CLOSURE) // build a closure (lambda args body . env) if(nargs > 0 && !captured){ // save temporary environment to the heap n = nargs; pv = alloc_words(n + 2); PUSH(tagptr(pv, TAG_VECTOR)); pv[0] = fixnum(n+1); pv++; do{ pv[n] = fl->Stack[bp+n]; }while(n--); // environment representation changed; install // the new representation so everybody can see it captured = 1; fl->Stack[fl->curr_frame-1] = 1; fl->Stack[bp] = fl->Stack[fl->SP-1]; }else{ PUSH(fl->Stack[bp]); // env has already been captured; share } if(fl->curheap > fl->lim-2) gc(0); pv = (value_t*)fl->curheap; fl->curheap += 4*sizeof(value_t); e = fl->Stack[fl->SP-2]; // closure to copy assert(isfunction(e)); pv[0] = ((value_t*)ptr(e))[0]; pv[1] = ((value_t*)ptr(e))[1]; pv[2] = fl->Stack[fl->SP-1]; // env pv[3] = ((value_t*)ptr(e))[3]; POPN(1); fl->Stack[fl->SP-1] = tagptr(pv, TAG_FUNCTION); NEXT_OP; OP(OP_SETA) assert(nargs > 0); v = fl->Stack[fl->SP-1]; i = *ip++; if(captured){ e = fl->Stack[bp]; assert(isvector(e)); assert(i < vector_size(e)); vector_elt(e, i) = v; }else{ fl->Stack[bp+i] = v; } NEXT_OP; OP(OP_JMP) ip += GET_INT16(ip); NEXT_OP; OP(OP_LOADC00) PUSH(vector_elt(fl->Stack[bp+nargs], 0)); NEXT_OP; OP(OP_PAIRP) fl->Stack[fl->SP-1] = iscons(fl->Stack[fl->SP-1]) ? fl->FL_T : fl->FL_F; NEXT_OP; OP(OP_BRNE) ip += fl->Stack[fl->SP-2] != fl->Stack[fl->SP-1] ? GET_INT16(ip) : 2; POPN(2); NEXT_OP; OP(OP_LOADT) PUSH(fl->FL_T); NEXT_OP; OP(OP_LOAD0) PUSH(fixnum(0)); NEXT_OP; OP(OP_LOADC01) PUSH(vector_elt(fl->Stack[bp+nargs], 1)); NEXT_OP; OP(OP_AREF) fl->Stack[ipd] = (uintptr_t)ip; v = fl->Stack[fl->SP-2]; if(isvector(v)){ e = fl->Stack[fl->SP-1]; i = isfixnum(e) ? numval(e) : (uint32_t)toulong(e); if(__unlikely(i >= vector_size(v))) bounds_error(v, e); v = vector_elt(v, i); }else if(__likely(isarray(v))){ v = cvalue_array_aref(&fl->Stack[fl->SP-2]); }else{ type_error("sequence", v); } POPN(1); fl->Stack[fl->SP-1] = v; NEXT_OP; OP(OP_ATOMP) fl->Stack[fl->SP-1] = iscons(fl->Stack[fl->SP-1]) ? fl->FL_F : fl->FL_T; NEXT_OP; OP(OP_BRT) ip += POP() != fl->FL_F ? GET_INT16(ip) : 2; NEXT_OP; OP(OP_BRNN) ip += POP() != fl->NIL ? GET_INT16(ip) : 2; NEXT_OP; OP(OP_LOAD1) PUSH(fixnum(1)); NEXT_OP; OP(OP_LT) x = numeric_compare(fl->Stack[fl->SP-2], fl->Stack[fl->SP-1], 0, 0, 0); if(x > 1) x = numval(fl_compare(fl->Stack[fl->SP-2], fl->Stack[fl->SP-1])); POPN(1); fl->Stack[fl->SP-1] = x < 0 ? fl->FL_T : fl->FL_F; NEXT_OP; OP(OP_ADD2) fl->Stack[ipd] = (uintptr_t)ip; if(bothfixnums(fl->Stack[fl->SP-1], fl->Stack[fl->SP-2])){ s = numval(fl->Stack[fl->SP-1]) + numval(fl->Stack[fl->SP-2]); v = fits_fixnum(s) ? fixnum(s) : mk_xlong(s); }else{ v = fl_add_any(&fl->Stack[fl->SP-2], 2, 0); } POPN(1); fl->Stack[fl->SP-1] = v; NEXT_OP; OP(OP_SETCDR) v = fl->Stack[fl->SP-2]; if(__unlikely(!iscons(v))){ fl->Stack[ipd] = (uintptr_t)ip; type_error("cons", v); } cdr_(v) = fl->Stack[fl->SP-1]; POPN(1); NEXT_OP; OP(OP_LOADF) PUSH(fl->FL_F); NEXT_OP; OP(OP_CONS) if(fl->curheap > fl->lim) gc(0); c = (cons_t*)fl->curheap; fl->curheap += sizeof(cons_t); c->car = fl->Stack[fl->SP-2]; c->cdr = fl->Stack[fl->SP-1]; fl->Stack[fl->SP-2] = tagptr(c, TAG_CONS); POPN(1); NEXT_OP; OP(OP_EQ) fl->Stack[fl->SP-2] = fl->Stack[fl->SP-2] == fl->Stack[fl->SP-1] ? fl->FL_T : fl->FL_F; POPN(1); NEXT_OP; OP(OP_SYMBOLP) fl->Stack[fl->SP-1] = issymbol(fl->Stack[fl->SP-1]) ? fl->FL_T : fl->FL_F; NEXT_OP; OP(OP_NOT) fl->Stack[fl->SP-1] = fl->Stack[fl->SP-1] == fl->FL_F ? fl->FL_T : fl->FL_F; NEXT_OP; OP(OP_CADR) v = fl->Stack[fl->SP-1]; if(__unlikely(!iscons(v))){ fl->Stack[ipd] = (uintptr_t)ip; type_error("cons", v); } v = cdr_(v); if(__unlikely(!iscons(v))){ fl->Stack[ipd] = (uintptr_t)ip; type_error("cons", v); } fl->Stack[fl->SP-1] = car_(v); NEXT_OP; OP(OP_NEG) do_neg: fl->Stack[ipd] = (uintptr_t)ip; fl->Stack[fl->SP-1] = fl_neg(fl->Stack[fl->SP-1]); NEXT_OP; OP(OP_NULLP) fl->Stack[fl->SP-1] = fl->Stack[fl->SP-1] == fl->NIL ? fl->FL_T : fl->FL_F; NEXT_OP; OP(OP_BOOLEANP) v = fl->Stack[fl->SP-1]; fl->Stack[fl->SP-1] = (v == fl->FL_T || v == fl->FL_F) ? fl->FL_T : fl->FL_F; NEXT_OP; OP(OP_NUMBERP) v = fl->Stack[fl->SP-1]; fl->Stack[fl->SP-1] = fl_isnumber(v) ? fl->FL_T : fl->FL_F; NEXT_OP; OP(OP_FIXNUMP) fl->Stack[fl->SP-1] = isfixnum(fl->Stack[fl->SP-1]) ? fl->FL_T : fl->FL_F; NEXT_OP; OP(OP_BOUNDP) fl->Stack[ipd] = (uintptr_t)ip; sym = tosymbol(fl->Stack[fl->SP-1]); fl->Stack[fl->SP-1] = sym->binding == UNBOUND ? fl->FL_F : fl->FL_T; NEXT_OP; OP(OP_BUILTINP) v = fl->Stack[fl->SP-1]; fl->Stack[fl->SP-1] = (isbuiltin(v) || iscbuiltin(v)) ? fl->FL_T : fl->FL_F; NEXT_OP; OP(OP_FUNCTIONP) v = fl->Stack[fl->SP-1]; fl->Stack[fl->SP-1] = ((tag(v) == TAG_FUNCTION && (isbuiltin(v) || v>(N_BUILTINS<<3))) || iscbuiltin(v)) ? fl->FL_T : fl->FL_F; NEXT_OP; OP(OP_VECTORP) fl->Stack[fl->SP-1] = isvector(fl->Stack[fl->SP-1]) ? fl->FL_T : fl->FL_F; NEXT_OP; OP(OP_JMPL) ip += GET_INT32(ip); NEXT_OP; OP(OP_BRFL) ip += POP() == fl->FL_F ? GET_INT32(ip) : 4; NEXT_OP; OP(OP_BRTL) ip += POP() != fl->FL_F ? GET_INT32(ip) : 4; NEXT_OP; OP(OP_BRNEL) ip += fl->Stack[fl->SP-2] != fl->Stack[fl->SP-1] ? GET_INT32(ip) : 4; POPN(2); NEXT_OP; OP(OP_BRNNL) ip += POP() != fl->NIL ? GET_INT32(ip) : 4; NEXT_OP; OP(OP_BRN) ip += POP() == fl->NIL ? GET_INT16(ip) : 2; NEXT_OP; OP(OP_BRNL) ip += POP() == fl->NIL ? GET_INT32(ip) : 4; NEXT_OP; OP(OP_EQV) if(fl->Stack[fl->SP-2] == fl->Stack[fl->SP-1]) v = fl->FL_T; else if(!leafp(fl->Stack[fl->SP-2]) || !leafp(fl->Stack[fl->SP-1])) v = fl->FL_F; else v = compare_(fl->Stack[fl->SP-2], fl->Stack[fl->SP-1], 1) == 0 ? fl->FL_T : fl->FL_F; fl->Stack[fl->SP-2] = v; POPN(1); NEXT_OP; OP(OP_EQUAL) if(fl->Stack[fl->SP-2] == fl->Stack[fl->SP-1]) v = fl->FL_T; else v = compare_(fl->Stack[fl->SP-2], fl->Stack[fl->SP-1], 1) == 0 ? fl->FL_T : fl->FL_F; fl->Stack[fl->SP-2] = v; POPN(1); NEXT_OP; OP(OP_SETCAR) v = fl->Stack[fl->SP-2]; if(__unlikely(!iscons(v))){ fl->Stack[ipd] = (uintptr_t)ip; type_error("cons", v); } car_(v) = fl->Stack[fl->SP-1]; POPN(1); NEXT_OP; OP(OP_LIST) n = *ip++; apply_list: if(n > 0){ v = list(&fl->Stack[fl->SP-n], n, 0); POPN(n); PUSH(v); }else{ PUSH(fl->NIL); } NEXT_OP; OP(OP_TAPPLY) tail = 1; if(0){ OP(OP_APPLY) tail = 0; } n = *ip++; apply_apply: v = POP(); // arglist n = fl->SP-(n-2); // n-2 == # leading arguments not in the list while(iscons(v)){ if(fl->SP >= fl->N_STACK) grow_stack(); PUSH(car_(v)); v = cdr_(v); } n = fl->SP-n; goto do_call; OP(OP_ADD) n = *ip++; apply_add: s = 0; i = fl->SP-n; for(; i < fl->SP; i++){ if(isfixnum(fl->Stack[i])){ s += numval(fl->Stack[i]); if(__unlikely(!fits_fixnum(s))){ i++; goto add_ovf; } }else{ add_ovf: fl->Stack[ipd] = (uintptr_t)ip; v = fl_add_any(&fl->Stack[i], fl->SP-i, s); break; } } if(i == fl->SP) v = fixnum(s); POPN(n); PUSH(v); NEXT_OP; OP(OP_SUB) n = *ip++; apply_sub: if(n == 2) goto do_sub2; if(n == 1) goto do_neg; fl->Stack[ipd] = (uintptr_t)ip; i = fl->SP-n; // we need to pass the full arglist on to fl_add_any // so it can handle rest args properly PUSH(fl->Stack[i]); fl->Stack[i] = fixnum(0); fl->Stack[i+1] = fl_neg(fl_add_any(&fl->Stack[i], n, 0)); fl->Stack[i] = POP(); v = fl_add_any(&fl->Stack[i], 2, 0); POPN(n); PUSH(v); NEXT_OP; OP(OP_SUB2) do_sub2: if(bothfixnums(fl->Stack[fl->SP-2], fl->Stack[fl->SP-1])){ s = numval(fl->Stack[fl->SP-2]) - numval(fl->Stack[fl->SP-1]); v = fits_fixnum(s) ? fixnum(s) : mk_xlong(s); }else{ fl->Stack[ipd] = (uintptr_t)ip; fl->Stack[fl->SP-1] = fl_neg(fl->Stack[fl->SP-1]); v = fl_add_any(&fl->Stack[fl->SP-2], 2, 0); } POPN(1); fl->Stack[fl->SP-1] = v; NEXT_OP; OP(OP_MUL) n = *ip++; apply_mul: fl->Stack[ipd] = (uintptr_t)ip; v = fl_mul_any(&fl->Stack[fl->SP-n], n); POPN(n); PUSH(v); NEXT_OP; OP(OP_DIV) n = *ip++; apply_div: fl->Stack[ipd] = (uintptr_t)ip; i = fl->SP-n; if(n == 1){ fl->Stack[fl->SP-1] = fl_div2(fixnum(1), fl->Stack[i]); }else{ if(n > 2){ PUSH(fl->Stack[i]); fl->Stack[i] = fixnum(1); fl->Stack[i+1] = fl_mul_any(&fl->Stack[i], n); fl->Stack[i] = POP(); } v = fl_div2(fl->Stack[i], fl->Stack[i+1]); POPN(n); PUSH(v); } NEXT_OP; OP(OP_IDIV) fl->Stack[ipd] = (uintptr_t)ip; v = fl->Stack[fl->SP-2]; e = fl->Stack[fl->SP-1]; if(bothfixnums(v, e)){ if(e == 0) DivideByZeroError(); v = fixnum(numval(v) / numval(e)); }else{ v = fl_idiv2(v, e); } POPN(1); fl->Stack[fl->SP-1] = v; NEXT_OP; OP(OP_NUMEQ) v = fl->Stack[fl->SP-2]; e = fl->Stack[fl->SP-1]; if(bothfixnums(v, e)) v = v == e ? fl->FL_T : fl->FL_F; else{ fl->Stack[ipd] = (uintptr_t)ip; v = numeric_compare(v, e, 1, 0, 1) == 0 ? fl->FL_T : fl->FL_F; } POPN(1); fl->Stack[fl->SP-1] = v; NEXT_OP; OP(OP_COMPARE) fl->Stack[fl->SP-2] = compare_(fl->Stack[fl->SP-2], fl->Stack[fl->SP-1], 0); POPN(1); NEXT_OP; OP(OP_ARGC) n = *ip++; if(0){ OP(OP_LARGC) n = GET_INT32(ip); ip += 4; } fl->Stack[ipd] = (uintptr_t)ip; argcount(nargs, n); NEXT_OP; OP(OP_VECTOR) n = *ip++; apply_vector: v = alloc_vector(n, 0); if(n){ memmove(&vector_elt(v, 0), &fl->Stack[fl->SP-n], n*sizeof(value_t)); POPN(n); } PUSH(v); NEXT_OP; OP(OP_ASET) e = fl->Stack[fl->SP-3]; fl->Stack[ipd] = (uintptr_t)ip; if(isvector(e)){ i = tofixnum(fl->Stack[fl->SP-2]); if(__unlikely(i >= vector_size(e))) bounds_error(v, fl->Stack[fl->SP-1]); vector_elt(e, i) = (v = fl->Stack[fl->SP-1]); }else if(__likely(isarray(e))){ v = cvalue_array_aset(&fl->Stack[fl->SP-3]); }else{ type_error("sequence", e); } POPN(2); fl->Stack[fl->SP-1] = v; NEXT_OP; OP(OP_FOR) fl->Stack[ipd] = (uintptr_t)ip; s = tofixnum(fl->Stack[fl->SP-3]); hi = tofixnum(fl->Stack[fl->SP-2]); v = fl->FL_UNSPECIFIED; fl->SP += 2; n = fl->SP; for(; s <= hi; s++){ fl->Stack[fl->SP-2] = fl->Stack[fl->SP-3]; fl->Stack[fl->SP-1] = fixnum(s); v = apply_cl(1); fl->SP = n; } POPN(4); fl->Stack[fl->SP-1] = v; NEXT_OP; OP(OP_LOADNIL) PUSH(fl->NIL); NEXT_OP; OP(OP_LOADI8) s = (int8_t)*ip++; PUSH(fixnum(s)); NEXT_OP; OP(OP_LOADVL) v = fn_vals(fl->Stack[bp-1]); v = vector_elt(v, GET_INT32(ip)); ip += 4; PUSH(v); NEXT_OP; OP(OP_SETGL) v = fn_vals(fl->Stack[bp-1]); v = vector_elt(v, GET_INT32(ip)); ip += 4; if(0){ OP(OP_SETG) v = fn_vals(fl->Stack[bp-1]); assert(*ip < vector_size(v)); v = vector_elt(v, *ip); ip++; } assert(issymbol(v)); sym = (symbol_t*)ptr(v); v = fl->Stack[fl->SP-1]; if(!isconstant(sym)) sym->binding = v; NEXT_OP; OP(OP_LOADAL) assert(nargs > 0); i = GET_INT32(ip); ip += 4; v = captured ? vector_elt(fl->Stack[bp], i) : fl->Stack[bp+i]; PUSH(v); NEXT_OP; OP(OP_SETAL) assert(nargs > 0); v = fl->Stack[fl->SP-1]; i = GET_INT32(ip); ip += 4; if(captured) vector_elt(fl->Stack[bp], i) = v; else fl->Stack[bp+i] = v; NEXT_OP; OP(OP_SETC) s = *ip++; i = *ip++; v = fl->Stack[bp+nargs]; while(s--) v = vector_elt(v, vector_size(v)-1); assert(isvector(v)); assert(i < vector_size(v)); vector_elt(v, i) = fl->Stack[fl->SP-1]; NEXT_OP; OP(OP_LOADCL) s = GET_INT32(ip); ip += 4; i = GET_INT32(ip); ip += 4; v = fl->Stack[bp+nargs]; while(s--) v = vector_elt(v, vector_size(v)-1); PUSH(vector_elt(v, i)); NEXT_OP; OP(OP_SETCL) s = GET_INT32(ip); ip += 4; i = GET_INT32(ip); ip += 4; v = fl->Stack[bp+nargs]; while(s--) v = vector_elt(v, vector_size(v)-1); assert(i < vector_size(v)); vector_elt(v, i) = fl->Stack[fl->SP-1]; NEXT_OP; OP(OP_VARGC) i = *ip++; if(0){ OP(OP_LVARGC) i = GET_INT32(ip); ip += 4; } s = (fixnum_t)nargs - (fixnum_t)i; if(s > 0){ v = list(&fl->Stack[bp+i], s, 0); fl->Stack[bp+i] = v; if(s > 1){ fl->Stack[bp+i+1] = fl->Stack[bp+nargs+0]; fl->Stack[bp+i+2] = fl->Stack[bp+nargs+1]; fl->Stack[bp+i+3] = i+1; //fl->Stack[bp+i+4] = 0; fl->Stack[bp+i+5] = 0; fl->SP = bp+i+6; fl->curr_frame = fl->SP; } }else if(__unlikely(s < 0)){ fl->Stack[ipd] = (uintptr_t)ip; lerrorf(fl->ArgError, "too few arguments"); }else{ PUSH(0); fl->Stack[fl->SP-3] = i+1; fl->Stack[fl->SP-4] = fl->Stack[fl->SP-5]; fl->Stack[fl->SP-5] = fl->Stack[fl->SP-6]; fl->Stack[fl->SP-6] = fl->NIL; fl->curr_frame = fl->SP; } ipd = fl->SP-2; nargs = i+1; NEXT_OP; OP(OP_TRYCATCH) fl->Stack[ipd] = (uintptr_t)ip; v = do_trycatch(); POPN(1); fl->Stack[fl->SP-1] = v; NEXT_OP; OP(OP_OPTARGS) i = GET_INT32(ip); ip += 4; n = GET_INT32(ip); ip += 4; if(__unlikely(nargs < i)){ fl->Stack[ipd] = (uintptr_t)ip; lerrorf(fl->ArgError, "too few arguments"); } if((int32_t)n > 0){ if(__unlikely(nargs > n)){ fl->Stack[ipd] = (uintptr_t)ip; lerrorf(fl->ArgError, "too many arguments"); } }else n = -n; if(__likely(n > nargs)){ n -= nargs; fl->SP += n; fl->Stack[fl->SP-1] = fl->Stack[fl->SP-n-1]; fl->Stack[fl->SP-2] = fl->Stack[fl->SP-n-2]; fl->Stack[fl->SP-3] = nargs+n; fl->Stack[fl->SP-4] = fl->Stack[fl->SP-n-4]; fl->Stack[fl->SP-5] = fl->Stack[fl->SP-n-5]; fl->curr_frame = fl->SP; ipd = fl->SP-2; for(i = 0; i < n; i++) fl->Stack[bp+nargs+i] = UNBOUND; nargs += n; } NEXT_OP; OP(OP_BRBOUND) i = GET_INT32(ip); ip += 4; v = captured ? vector_elt(fl->Stack[bp], i) : fl->Stack[bp+i]; PUSH(v != UNBOUND ? fl->FL_T : fl->FL_F); NEXT_OP; OP(OP_KEYARGS) v = fn_vals(fl->Stack[bp-1]); v = vector_elt(v, 0); i = GET_INT32(ip); ip += 4; n = GET_INT32(ip); ip += 4; s = GET_INT32(ip); ip += 4; fl->Stack[ipd] = (uintptr_t)ip; nargs = process_keys(v, i, n, labs(s)-(i+n), bp, nargs, s<0); NEXT_OP; } op = *ip++; } } #define SWAP_INT32(a) #define SWAP_INT16(a) #include "maxstack.inc" #if BYTE_ORDER == BIG_ENDIAN #undef SWAP_INT32 #undef SWAP_INT16 #if defined(__sparc__) #define SWAP_INT32(a) \ do{ \ uint8_t *x = (void*)a, y; \ y = x[0]; x[0] = x[3]; x[3] = y; \ y = x[1]; x[1] = x[2]; x[2] = y; \ }while(0) #define SWAP_INT16(a) \ do{ \ uint8_t *x = (void*)a, y; \ y = x[0]; x[0] = x[1]; x[1] = y; \ }while(0) #else #define SWAP_INT32(a) (*(int32_t*)(a) = bswap_32(*(int32_t*)(a))) #define SWAP_INT16(a) (*(int16_t*)(a) = bswap_16(*(int16_t*)(a))) #endif #define compute_maxstack compute_maxstack_swap #include "maxstack.inc" #undef compute_maxstack #else #endif // top = top frame pointer to start at static value_t _stacktrace(uint32_t top) { value_t lst = fl->NIL; fl_gc_handle(&lst); while(top > 0){ const uint8_t *ip1 = (void*)fl->Stack[top-2]; uint32_t sz = fl->Stack[top-3]+1; uint32_t bp = top-5-sz; value_t func = fl->Stack[bp]; const uint8_t *ip0 = cv_data((cvalue_t*)ptr(fn_bcode(func))); value_t ip = ip1 - ip0 - 1; /* -1: ip1 is *after* the one that was being executed */ value_t v = alloc_vector(sz+1, 0); vector_elt(v, 0) = fixnum(ip); vector_elt(v, 1) = func; if(fl->Stack[top-1] /*captured*/){ memmove(&vector_elt(v, 2), &vector_elt(fl->Stack[bp+1], 0), (sz-1)*sizeof(value_t)); }else{ for(uint32_t i = 1; i < sz; i++){ value_t si = fl->Stack[bp+i]; // if there's an error evaluating argument defaults some slots // might be left set to UNBOUND (issue #22) vector_elt(v, i+1) = si == UNBOUND ? fl->FL_UNSPECIFIED : si; } } lst = fl_cons(v, lst); top = fl->Stack[top-4]; } fl_free_gc_handles(1); return lst; } // builtins ------------------------------------------------------------------- BUILTIN("gc", gc) { USED(args); argcount(nargs, 0); gc(0); return fl->FL_T; } BUILTIN("function", function) { if(nargs == 1 && issymbol(args[0])) return fn_builtin_builtin(args, nargs); if(nargs < 2 || nargs > 4) argcount(nargs, 2); if(__unlikely(!fl_isstring(args[0]))) type_error("string", args[0]); if(__unlikely(!isvector(args[1]))) type_error("vector", args[1]); cvalue_t *arr = (cvalue_t*)ptr(args[0]); cv_pin(arr); char *data = cv_data(arr); uint32_t ms; if((uint8_t)data[4] >= N_OPCODES){ // read syntax, shifted 48 for compact text representation size_t i, sz = cv_len(arr); for(i = 0; i < sz; i++) data[i] -= 48; #if BYTE_ORDER == BIG_ENDIAN ms = compute_maxstack((uint8_t*)data, cv_len(arr)); }else{ ms = compute_maxstack_swap((uint8_t*)data, cv_len(arr)); } #else } ms = compute_maxstack((uint8_t*)data, cv_len(arr)); #endif PUT_INT32(data, ms); function_t *fn = alloc_words(4); value_t fv = tagptr(fn, TAG_FUNCTION); fn->bcode = args[0]; fn->vals = args[1]; fn->env = fl->NIL; fn->name = fl->LAMBDA; if(nargs > 2){ if(issymbol(args[2])){ fn->name = args[2]; if(nargs > 3) fn->env = args[3]; }else{ fn->env = args[2]; if(nargs > 3){ if(__unlikely(!issymbol(args[3]))) type_error("symbol", args[3]); fn->name = args[3]; } } if(__unlikely(isgensym(fn->name))) lerrorf(fl->ArgError, "name should not be a gensym"); } return fv; } BUILTIN("function:code", function_code) { argcount(nargs, 1); value_t v = args[0]; if(__unlikely(!isclosure(v))) type_error("function", v); return fn_bcode(v); } BUILTIN("function:vals", function_vals) { argcount(nargs, 1); value_t v = args[0]; if(__unlikely(!isclosure(v))) type_error("function", v); return fn_vals(v); } BUILTIN("function:env", function_env) { argcount(nargs, 1); value_t v = args[0]; if(__unlikely(!isclosure(v))) type_error("function", v); return fn_env(v); } BUILTIN("function:name", function_name) { argcount(nargs, 1); value_t v = args[0]; if(__unlikely(!isclosure(v))) type_error("function", v); return fn_name(v); } BUILTIN("copy-list", copy_list) { argcount(nargs, 1); return copy_list(args[0]); } BUILTIN("append", append) { value_t first = fl->NIL, lst, lastcons = fl->NIL; int i; if(nargs == 0) return fl->NIL; fl_gc_handle(&first); fl_gc_handle(&lastcons); for(i = 0; i < nargs; i++){ lst = args[i]; if(iscons(lst)){ lst = copy_list(lst); if(first == fl->NIL) first = lst; else cdr_(lastcons) = lst; lastcons = tagptr((((cons_t*)fl->curheap)-1), TAG_CONS); }else if(lst != fl->NIL){ type_error("cons", lst); } } fl_free_gc_handles(2); return first; } BUILTIN("list*", liststar) { if(nargs == 1) return args[0]; if(nargs == 0) argcount(nargs, 1); return list(args, nargs, 1); } BUILTIN("stacktrace", stacktrace) { USED(args); argcount(nargs, 0); return _stacktrace(fl->throwing_frame ? fl->throwing_frame : fl->curr_frame); } BUILTIN("map", map) { if(__unlikely(nargs < 2)) lerrorf(fl->ArgError, "too few arguments"); if(!iscons(args[1])) return fl->NIL; value_t first, last, v; int64_t argSP = args-fl->Stack; assert(argSP >= 0 && argSP < fl->N_STACK); if(nargs == 2){ if(fl->SP+3 > fl->N_STACK) grow_stack(); PUSH(fl->Stack[argSP]); PUSH(car_(fl->Stack[argSP+1])); v = _applyn(1); PUSH(v); v = mk_cons(); car_(v) = POP(); cdr_(v) = fl->NIL; last = first = v; fl->Stack[argSP+1] = cdr_(fl->Stack[argSP+1]); fl_gc_handle(&first); fl_gc_handle(&last); while(iscons(fl->Stack[argSP+1])){ fl->Stack[fl->SP-2] = fl->Stack[argSP]; fl->Stack[fl->SP-1] = car_(fl->Stack[argSP+1]); v = _applyn(1); PUSH(v); v = mk_cons(); car_(v) = POP(); cdr_(v) = fl->NIL; cdr_(last) = v; last = v; fl->Stack[argSP+1] = cdr_(fl->Stack[argSP+1]); } POPN(2); fl_free_gc_handles(2); }else{ int i; while(fl->SP+nargs+1 > fl->N_STACK) grow_stack(); PUSH(fl->Stack[argSP]); for(i = 1; i < nargs; i++){ PUSH(car(fl->Stack[argSP+i])); fl->Stack[argSP+i] = cdr_(fl->Stack[argSP+i]); } v = _applyn(nargs-1); POPN(nargs); PUSH(v); v = mk_cons(); car_(v) = POP(); cdr_(v) = fl->NIL; last = first = v; fl_gc_handle(&first); fl_gc_handle(&last); while(iscons(fl->Stack[argSP+1])){ PUSH(fl->Stack[argSP]); for(i = 1; i < nargs; i++){ PUSH(car(fl->Stack[argSP+i])); fl->Stack[argSP+i] = cdr_(fl->Stack[argSP+i]); } v = _applyn(nargs-1); POPN(nargs); PUSH(v); v = mk_cons(); car_(v) = POP(); cdr_(v) = fl->NIL; cdr_(last) = v; last = v; } fl_free_gc_handles(2); } return first; } BUILTIN("sleep", fl_sleep) { if(nargs > 1) argcount(nargs, 1); double s = nargs > 0 ? todouble(args[0]) : 0; sleep_ms(s * 1000.0); return fl->FL_T; } static const builtinspec_t builtin_fns[] = { #define BUILTIN_FN(l, c){l, fn_builtin_##c}, #include "builtin_fns.h" #undef BUILTIN_FN }; // initialization ------------------------------------------------------------- void fl_init(size_t initial_heapsize) { int i; fl = LLT_ALLOC(sizeof(*fl)); memset(fl, 0, sizeof(*fl)); fl->SCR_WIDTH = 80; fl->heapsize = initial_heapsize; fl->fromspace = LLT_ALLOC(fl->heapsize); fl->tospace = LLT_ALLOC(fl->heapsize); fl->curheap = fl->fromspace; fl->lim = fl->curheap+fl->heapsize-sizeof(cons_t); fl->consflags = bitvector_new(fl->heapsize/sizeof(cons_t), 1); htable_new(&fl->printconses, 32); comparehash_init(); fl->N_STACK = 262144; fl->Stack = LLT_ALLOC(fl->N_STACK*sizeof(value_t)); fl->FL_NIL = fl->NIL = builtin(OP_THE_EMPTY_LIST); fl->FL_T = builtin(OP_BOOL_CONST_T); fl->FL_F = builtin(OP_BOOL_CONST_F); fl->FL_EOF = builtin(OP_EOF_OBJECT); fl->LAMBDA = symbol("λ"); fl->FUNCTION = symbol("function"); fl->QUOTE = symbol("quote"); fl->TRYCATCH = symbol("trycatch"); fl->BACKQUOTE = symbol("quasiquote"); fl->COMMA = symbol("unquote"); fl->COMMAAT = symbol("unquote-splicing"); fl->COMMADOT = symbol("unquote-nsplicing"); fl->IOError = symbol("io-error"); fl->ParseError = symbol("parse-error"); fl->TypeError = symbol("type-error"); fl->ArgError = symbol("arg-error"); fl->UnboundError = symbol("unbound-error"); fl->KeyError = symbol("key-error"); fl->MemoryError = symbol("memory-error"); fl->BoundsError = symbol("bounds-error"); fl->DivideError = symbol("divide-error"); fl->EnumerationError = symbol("enumeration-error"); fl->Error = symbol("error"); fl->pairsym = symbol("pair"); fl->symbolsym = symbol("symbol"); fl->fixnumsym = symbol("fixnum"); fl->vectorsym = symbol("vector"); fl->builtinsym = symbol("builtin"); fl->booleansym = symbol("boolean"); fl->nullsym = symbol("null"); fl->definesym = symbol("define"); fl->defmacrosym = symbol("define-macro"); fl->forsym = symbol("for"); fl->setqsym = symbol("set!"); fl->evalsym = symbol("eval"); fl->vu8sym = symbol("vu8"); fl->fnsym = symbol("fn"); fl->nulsym = symbol("nul"); fl->alarmsym = symbol("alarm"); fl->backspacesym = symbol("backspace"); fl->tabsym = symbol("tab"); fl->linefeedsym = symbol("linefeed"); fl->vtabsym = symbol("vtab"); fl->pagesym = symbol("page"); fl->returnsym = symbol("return"); fl->escsym = symbol("esc"); fl->spacesym = symbol("space"); fl->deletesym = symbol("delete"); fl->newlinesym = symbol("newline"); fl->tsym = symbol("t"); fl->Tsym = symbol("T"); fl->fsym = symbol("f"); fl->Fsym = symbol("F"); fl->builtins_table_sym = symbol("*builtins*"); set(fl->printprettysym = symbol("*print-pretty*"), fl->FL_T); set(fl->printreadablysym = symbol("*print-readably*"), fl->FL_T); set(fl->printwidthsym = symbol("*print-width*"), fixnum(fl->SCR_WIDTH)); set(fl->printlengthsym = symbol("*print-length*"), fl->FL_F); set(fl->printlevelsym = symbol("*print-level*"), fl->FL_F); fl->lasterror = fl->NIL; for(i = 0; i < nelem(builtins); i++){ if(builtins[i].name) setc(symbol(builtins[i].name), builtin(i)); } setc(symbol("eq"), builtin(OP_EQ)); setc(symbol("procedure?"), builtin(OP_FUNCTIONP)); setc(symbol("top-level-bound?"), builtin(OP_BOUNDP)); #if defined(__linux__) set(symbol("*os-name*"), symbol("linux")); #elif defined(__OpenBSD__) set(symbol("*os-name*"), symbol("openbsd")); #elif defined(__FreeBSD__) set(symbol("*os-name*"), symbol("freebsd")); #elif defined(__NetBSD__) set(symbol("*os-name*"), symbol("netbsd")); #elif defined(__DragonFly__) set(symbol("*os-name*"), symbol("dragonflybsd")); #elif defined(__plan9__) set(symbol("*os-name*"), symbol("plan9")); #else set(symbol("*os-name*"), symbol("unknown")); #endif fl->the_empty_vector = tagptr(alloc_words(1), TAG_VECTOR); vector_setsize(fl->the_empty_vector, 0); cvalues_init(); fl->memory_exception_value = fl_list2(fl->MemoryError, cvalue_static_cstring("out of memory")); const builtinspec_t *b; for(i = 0, b = builtin_fns; i < nelem(builtin_fns); i++, b++) setc(symbol(b->name), cbuiltin(b->name, b->fptr)); table_init(); iostream_init(); fsixel_init(); } // top level ------------------------------------------------------------------ value_t fl_toplevel_eval(value_t expr) { return fl_applyn(1, symbol_value(fl->evalsym), expr); } int fl_load_system_image(value_t sys_image_iostream) { value_t e; int saveSP; symbol_t *sym; PUSH(sys_image_iostream); saveSP = fl->SP; FL_TRY{ while(1){ e = fl_read_sexpr(fl->Stack[fl->SP-1]); if(ios_eof(value2c(ios_t*, fl->Stack[fl->SP-1]))) break; if(isfunction(e)){ // stage 0 format: series of thunks PUSH(e); (void)_applyn(0); fl->SP = saveSP; }else{ // stage 1 format: list alternating symbol/value while(iscons(e)){ sym = tosymbol(car_(e)); e = cdr_(e); (void)tocons(e); sym->binding = car_(e); e = cdr_(e); } break; } } } FL_CATCH_NO_INC{ ios_puts("fatal error during bootstrap:\n", ios_stderr); fl_print(ios_stderr, fl->lasterror); ios_putc('\n', ios_stderr); return 1; } ios_close(value2c(ios_t*, fl->Stack[fl->SP-1])); POPN(1); return 0; }