ref: a70379d7e4b822f532fb0a8ccdd1624a90b64a68
dir: /src/compiler.sl/
;;;; built-in compiler ;; code generation state, constant tables, bytecode encoding (def (make-code-emitter) (vec NIL (table) 0 NIL 0)) (defmacro (bcode:code b) `(aref ,b 0)) (defmacro (bcode:ctable b) `(aref ,b 1)) (defmacro (bcode:nconst b) `(aref ,b 2)) (defmacro (bcode:cenv b) `(aref ,b 3)) (defmacro (bcode:sp b) `(aref ,b 4)) (defmacro (bcode:stack b n) `(aset! ,b 4 (+ (bcode:sp ,b) ,n))) ;; get an index for a referenced value in a bytecode object (def (bcode:indexfor b v) (let ((const-to-idx (bcode:ctable b)) (nconst (bcode:nconst b))) (if (has? const-to-idx v) (get const-to-idx v) (begin (put! const-to-idx v nconst) (prog1 nconst (aset! b 2 (+ nconst 1))))))) (def (emit e inst . args) (def (load? i) (member i '(load0 load1 loadt loadf loadnil loadvoid))) ; FIXME no load immediate here yet (let ((bc (aref e 0))) (if (not args) (if (and (eq? inst 'car) (eq? (car bc) 'cdr)) (set-car! bc 'cadr) (cond ((and (eq? inst 'pop) (load? (car bc))) (aset! e 0 (cdr bc))) (else (aset! e 0 (cons inst bc))))) (begin (when (memq inst '(loadv loadg setg)) (set! args (list (bcode:indexfor e (car args))))) (let ((longform (assq inst '((loadv loadv.l) (loadg loadg.l) (setg setg.l) (loada loada.l) (seta seta.l) (box box.l))))) (when (and longform (> (car args) 255)) (set! inst (cadr longform)))) (let ((longform (assq inst '((loadc loadc.l))))) (when (and longform (> (car args) 255)) (set! inst (cadr longform)))) (if (eq? inst 'loada) (cond ((equal? args '(0)) (set! inst 'loada0) (set! args NIL)) ((equal? args '(1)) (set! inst 'loada1) (set! args NIL)))) (if (eq? inst 'loadc) (cond ((equal? args '(0)) (set! inst 'loadc0) (set! args NIL)) ((equal? args '(1)) (set! inst 'loadc1) (set! args NIL)))) (let ((lasti (car bc))) (cond ((and (eq? inst 'brn) (cond ((eq? lasti 'not) (aset! e 0 (cons (car args) (cons 'brnn (cdr bc))))) ((eq? lasti 'eq?) (aset! e 0 (cons (car args) (cons 'brne (cdr bc))))) (else NIL)))) (else (aset! e 0 (nreconc (cons inst args) bc))))))) e)) (defmacro (make-label e) `(gensym)) (defmacro (mark-label e l) `(emit ,e 'label ,l)) ;; convert symbolic bytecode representation to a byte array. ;; labels are fixed-up. (def (encode-byte-code e) (let* ((cl (reverse! e)) (v (list->vec cl)) (long? (>= (+ (length v) ;; 1 byte for each entry, plus... ;; at most half the entries in this vector can be ;; instructions accepting 32-bit arguments (* 3 (div0 (length v) 2))) 65536))) (let ((n (length v)) (i 0) (label-to-loc (table)) (fixup-to-label (table)) (bcode (buffer)) (vi NIL) (nxt NIL)) (while (< i n) (set! vi (aref v i)) (if (eq? vi 'label) (begin (put! label-to-loc (aref v (+ i 1)) (sizeof bcode)) (set! i (+ i 2))) (begin (io-write bcode (get Instructions (if long? (case vi (jmp 'jmp.l) (brne 'brne.l) (brnn 'brnn.l) (brn 'brn.l) (else vi)) vi))) (set! i (+ i 1)) (set! nxt (and (< i n) (aref v i))) (cond ((memq vi '(jmp brne brnn brn)) (put! fixup-to-label (sizeof bcode) nxt) (io-write bcode ((if long? s32 s16) 0)) (set! i (+ i 1))) ((eq? vi 'brbound) (io-write bcode (s32 nxt)) (set! i (+ i 1))) ((num? nxt) (case vi ((loadv.l loadg.l setg.l loada.l seta.l argc.l vargc.l call.l tcall.l loadc.l box.l) (io-write bcode (s32 nxt)) (set! i (+ i 1))) ((optargs keyargs) ; 2 s32 args (io-write bcode (s32 nxt)) (set! i (+ i 1)) (io-write bcode (s32 (aref v i))) (set! i (+ i 1)) (when (eq? vi 'keyargs) (io-write bcode (s32 (aref v i))) (set! i (+ i 1)))) (else ; other number arguments are always u8 (io-write bcode (u8 nxt)) (set! i (+ i 1))))) (else NIL))))) (for-each (λ (addr labl) (io-seek bcode addr) (io-write bcode ((if long? s32 s16) (- (get label-to-loc labl) addr)))) fixup-to-label) (io->str bcode)))) (def (const-to-idx-vec e) (let ((cvec (vec-alloc (bcode:nconst e)))) (for-each (λ (val idx) (aset! cvec idx val)) (bcode:ctable e)) cvec)) ;; variables (def (vinfo symbol heap? index) (list symbol heap? index)) (def vinfo:sym car) (def vinfo:heap? cadr) (def vinfo:index caddr) (def (quoted? e) (eq? (car e) 'quote)) (def (capture-var! g s) (let ((ce (bcode:cenv g))) (let ((n (index-of s ce 0))) (or n (prog1 (length ce) (aset! g 3 (nconc ce (list s)))))))) (def (index-of item lst start) (cond ((not lst) NIL) ((eq? item (car lst)) start) (else (index-of item (cdr lst) (+ start 1))))) (def (in-env? s env) (and (cons? env) (or (assq s (car env)) (in-env? s (cdr env))))) (def (lookup-sym s env lev) (if (not env) 'global (let* ((curr (car env)) (vi (assq s curr))) (if vi (cons lev vi) (lookup-sym s (cdr env) (+ lev 1)))))) (def (printable? x) (not (or (io? x) (void? x) (eof-object? x)))) (def (compile-sym g env s deref) (let ((loc (lookup-sym s env 0))) (cond ((eq? loc 'global) (if (and (const? s) (printable? (top-level-value s))) (emit g 'loadv (top-level-value s)) (emit g 'loadg s))) ((= (car loc) 0) (emit g 'loada (vinfo:index (cdr loc))) (when (and deref (vinfo:heap? (cdr loc))) (emit g 'car))) (else (emit g 'loadc (capture-var! g s)) (when (and deref (vinfo:heap? (cdr loc))) (emit g 'car)))))) (def (compile-aset! g env args) (let ((nref (- (length args) 2))) (cond ((= nref 1) (compile-app g env NIL (cons 'aset! args))) ((> nref 1) (compile-app g env NIL (cons 'aref (list-head args nref))) (let ((nargs (compile-arglist g env (list-tail args nref)))) (bcode:stack g (- nargs)) (emit g 'aset!))) (else (argc-error 'aset! 3))))) (def (compile-set! g env s rhs) (let ((loc (lookup-sym s env 0))) (if (eq? loc 'global) (begin (compile-in g env NIL rhs) (emit g 'setg s)) (let ((arg? (= (car loc) 0))) (let ((h? (vinfo:heap? (cdr loc))) (idx (if arg? (vinfo:index (cdr loc)) (capture-var! g s)))) (if h? (begin (emit g (if arg? 'loada 'loadc) idx) (bcode:stack g 1) (compile-in g env NIL rhs) (bcode:stack g -1) (emit g 'set-car!)) (begin (compile-in g env NIL rhs) (unless arg? (error (str "internal error: misallocated var " s))) (emit g 'seta idx)))))))) (def (box-vars g env) (let loop ((e env)) (when (cons? e) (if (cadr (car e)) (emit g 'box (caddr (car e)))) (loop (cdr e))))) ;; control flow (def (compile-if g env tail? x) (let ((thenl (make-label g)) (elsel (make-label g)) (endl (make-label g)) (test (cadr x)) (then (caddr x)) (else (if (cons? (cdddr x)) (cadddr x) NIL))) (cond ((eq? test T) (compile-in g env tail? then)) ((not test) (compile-in g env tail? else)) (else (compile-in g env NIL test elsel) (emit g 'brn elsel) (mark-label g thenl) (compile-in g env tail? then) (if tail? (emit g 'ret) (emit g 'jmp endl)) (mark-label g elsel) (compile-in g env tail? else) (mark-label g endl))))) (def (compile-begin g env tail? forms) (cond ((atom? forms) (compile-in g env tail? (void))) ((atom? (cdr forms)) (compile-in g env tail? (car forms))) (else (compile-in g env NIL (car forms)) (emit g 'pop) (compile-begin g env tail? (cdr forms))))) (def (compile-prog1 g env x) (compile-in g env NIL (cadr x)) (when (cons? (cddr x)) (bcode:stack g 1) (compile-begin g env NIL (cddr x)) (emit g 'pop) (bcode:stack g -1))) (def (compile-while g env cond body) (let ((top (make-label g)) (end (make-label g))) (compile-in g env NIL (void)) (bcode:stack g 1) (mark-label g top) (compile-in g env NIL cond) (emit g 'brn end) (emit g 'pop) (bcode:stack g -1) (compile-in g env NIL body) (emit g 'jmp top) (mark-label g end))) (def (1arg-lambda? func) (and (cons? func) (eq? (car func) 'λ) (length= (cadr func) 1))) (def (compile-short-circuit g env tail? forms default branch outl) (cond ((atom? forms) (compile-in g env tail? default outl)) ((atom? (cdr forms)) (compile-in g env tail? (car forms) outl)) (else (let ((end (or outl (make-label g)))) (compile-in g env NIL (car forms) outl) (bcode:stack g 1) (unless outl (emit g 'dup)) (emit g branch end) (bcode:stack g -1) (unless outl (emit g 'pop)) (compile-short-circuit g env tail? (cdr forms) default branch outl) (unless outl (mark-label g end)))))) (def (compile-and g env tail? forms outl) (compile-short-circuit g env tail? forms T 'brn outl)) (def (compile-or g env tail? forms) (compile-short-circuit g env tail? forms NIL 'brnn NIL)) ;; calls (def (compile-arglist g env lst) (for-each (λ (a) (compile-in g env NIL a) (bcode:stack g 1)) lst) (length lst)) (def (argc-error head count) (error "compile error: " head " expects " count (if (= count 1) " argument." " arguments."))) (def builtin->instruction (let ((b2i (table num? 'num? cons 'cons fixnum? 'fixnum? equal? 'equal? eq? 'eq? sym? 'sym? div0 'div0 builtin? 'builtin? aset! 'aset! - '- not 'not apply 'apply atom? 'atom? nan? 'nan? set-cdr! 'set-cdr! / '/ fn? 'fn? vec 'vec list 'list bound? 'bound? < '< * '* cdr 'cdr cadr 'cadr + '+ eqv? 'eqv? compare 'compare aref 'aref set-car! 'set-car! car 'car for 'for cons? 'cons? = '= vec? 'vec?))) (λ (b) (get b2i b NIL)))) (def (compile-builtin-call g env tail? x head b nargs) (def (num-compare) (if (= nargs 0) (argc-error b 1) (emit g b nargs))) (let ((count (get arg-counts b NIL))) (when (and count (not (length= (cdr x) count))) (argc-error b count)) (case b ; handle special cases of vararg builtins (list (if (= nargs 0) (emit g 'loadnil) (emit g b nargs))) (< (num-compare)) (= (num-compare)) (+ (cond ((= nargs 0) (emit g 'load0)) ((= nargs 2) (emit g 'add2)) (else (emit g b nargs)))) (- (cond ((= nargs 0) (argc-error b 1)) ((= nargs 1) (emit g 'neg)) ((= nargs 2) (emit g 'sub2)) (else (emit g b nargs)))) (* (if (= nargs 0) (emit g 'load1) (emit g b nargs))) (/ (if (= nargs 0) (argc-error b 1) (emit g b nargs))) (vec (if (= nargs 0) (emit g 'loadv #()) (emit g b nargs))) (apply (if (< nargs 2) (argc-error b 2) (emit g (if tail? 'tapply 'apply) nargs))) (aref (cond ((= nargs 2) (emit g 'aref2)) ((> nargs 2) (emit g b (- nargs 3))) (else (argc-error b 2)))) (else (emit g b))))) (def (inlineable? form) (let ((lam (car form))) (and (cons? lam) (eq? (car lam) 'λ) (list? (cadr lam)) (every sym? (cadr lam)) (not (length> (cadr lam) 255)) (length= (cadr lam) (length (cdr form)))))) ;; compile call to lambda in head position, inlined (def (compile-let g env tail? form) (let ((lam (car form)) (args (cdr form)) (sp (bcode:sp g))) (let ((vars (cadr lam)) (n (compile-arglist g env args))) (let ((newvars (vars-to-env vars (complex-bindings (caddr lam) vars) sp))) (box-vars g newvars) (let ((newenv (cons (nconc newvars (car env)) (cdr env)))) (compile-in g newenv tail? (caddr lam)) (bcode:stack g (- n)) (when (and (> n 0) (not tail?)) (emit g 'shift n))))))) (def (compile-app g env tail? x) (let ((head (car x))) (let ((head (if (and (sym? head) (not (in-env? head env)) (bound? head) (builtin? (top-level-value head))) (top-level-value head) head))) (if (length> (cdr x) 255) ;; more than 255 arguments, need long versions of instructions (begin (compile-in g env NIL head) (bcode:stack g 1) (let ((nargs (compile-arglist g env (cdr x)))) (bcode:stack g (- nargs)) (emit g (if tail? 'tcall.l 'call.l) nargs))) (let ((b (and (builtin? head) (builtin->instruction head)))) (if (and (eq? head 'cadr) (not (in-env? head env)) (equal? (top-level-value 'cadr) cadr) (length= x 2)) (begin (compile-in g env NIL (cadr x)) (emit g 'cadr)) (if (and (cons? head) (eq? (car head) 'λ) (inlineable? x)) (compile-let g env tail? x) (begin (unless b (compile-in g env NIL head) (bcode:stack g 1)) (let ((nargs (compile-arglist g env (cdr x)))) (bcode:stack g (- nargs)) (unless b (bcode:stack g -1)) (if b (compile-builtin-call g env tail? x head b nargs) (emit g (if tail? 'tcall 'call) nargs))))))))))) ;; lambda, main compilation loop (def (fits-i8 x) (and (fixnum? x) (>= 127 x -128))) (def (compile-in g env tail? x (outl NIL)) (cond ((sym? x) (compile-sym g env x T)) ((atom? x) (cond ((eq? x 0) (emit g 'load0)) ((eq? x 1) (emit g 'load1)) ((eq? x T) (emit g 'loadt)) ((not x) (emit g 'loadnil)) ((void? x) (emit g 'loadvoid)) ((fits-i8 x) (emit g 'loadi8 x)) (else (emit g 'loadv x)))) ((eq? (car x) 'aset!) (compile-aset! g env (cdr x))) ((or (not (sym? (car x))) (bound? (car x)) (in-env? (car x) env)) (compile-app g env tail? x)) (else (case (car x) (quote (if (self-evaluating? (cadr x)) (compile-in g env tail? (cadr x)) (emit g 'loadv (cadr x)))) (if (compile-if g env tail? x)) (begin (compile-begin g env tail? (cdr x))) (prog1 (compile-prog1 g env x)) (λ (receive (the-f cenv) (compile-f- env x) (begin (emit g 'loadv the-f) (when cenv (for-each (λ (var) (compile-sym g env var NIL)) cenv) (emit g 'closure (length cenv)))))) (and (compile-and g env tail? (cdr x) outl)) (or (compile-or g env tail? (cdr x))) (while (compile-while g env (cadr x) (cons 'begin (cddr x)))) (return (compile-in g env T (cadr x)) (emit g 'ret)) (set! (let* ((name (cadr x)) (doc+value (separate-doc-from-body (cddr x))) (doc (car (car doc+value))) (value (cdr doc+value))) (unless (sym? name) (error "set!: name must be a symbol")) (when doc (sym-set-doc name doc (and (cons? (car value)) (eq? (car (car value)) 'λ) (lambda:vars (car value))))) (compile-set! g env name (car value)))) (trycatch (compile-in g env NIL `(λ () ,(cadr x))) (unless (1arg-lambda? (caddr x)) (error "trycatch: second form must be a 1-argument lambda")) (compile-in g env NIL (caddr x)) (emit g 'trycatch)) (else (compile-app g env tail? x)))))) ;; optional and keyword args (def (keyword-arg? x) (and (cons? x) (keyword? (car x)))) (def (keyword->sym k) (if (keyword? k) (sym (let ((s (str k))) (str-sub s 1 (str-length s)))) k)) (def (lambda-vars l) (def (check-formals l o opt kw) (cond ((or (not l) (sym? l)) T) ((and (cons? l) (sym? (car l))) (if (or opt kw) (error "compile error: invalid argument list " o ": optional arguments must come after required") (check-formals (cdr l) o opt kw))) ((and (cons? l) (cons? (car l))) (unless (and (length= (car l) 2) (sym? (caar l))) (error "compile error: invalid optional argument " (car l) " in list " o)) (if (keyword? (caar l)) (check-formals (cdr l) o opt T) (if kw (error "compile error: invalid argument list " o ": keyword arguments must come last.") (check-formals (cdr l) o T kw)))) ((cons? l) (error "compile error: invalid formal argument " (car l) " in list " o)) (else (if (eq? l o) (error "compile error: invalid argument list " o) (error "compile error: invalid formal argument " l " in list " o))))) (check-formals l l NIL NIL) (map (λ (s) (if (cons? s) (keyword->sym (car s)) s)) (to-proper l))) (def (emit-optional-arg-inits g env opta vars i) ; i is the lexical var index of the opt arg to process next (when (cons? opta) (let ((nxt (make-label g))) (emit g 'brbound i) (emit g 'brnn nxt) (compile-in g (extend-env env (list-head vars i) NIL) NIL (cadar opta)) (emit g 'seta i) (emit g 'pop) (mark-label g nxt) (emit-optional-arg-inits g env (cdr opta) vars (+ i 1))))) ;; define (def (expand-define x) ;; expand a single `define` expression to `set!` (let* ((form (cadr x)) (body (if (cons? (cddr x)) (cddr x) (if (sym? form) #.void (error "compile error: invalid syntax " (print-to-str x)))))) (if (sym? form) `(#.void (set! ,form ,(car body))) `(#.void (set! ,(car form) (λ ,(cdr form) ,@body . ,(car form))))))) (def get-defined-vars (letrec ((get-defined-vars- (λ (expr) (cond ((atom? expr) NIL) ((and (eq? (car expr) 'def) (cons? (cdr expr))) (or (and (sym? (cadr expr)) (list (cadr expr))) (and (cons? (cadr expr)) (sym? (caadr expr)) (list (caadr expr))))) ((eq? (car expr) 'begin) (apply nconc (map get-defined-vars- (cdr expr)))) (else NIL))))) (λ (expr) (delete-duplicates (get-defined-vars- expr))))) (def (lower-define e) ;; convert lambda to one body expression and process internal defines (def (λ-body e) (let* ((B (if (cons? (cddr e)) (if (cons? (cdddr e)) (cons 'begin (cddr e)) (caddr e)) (void))) (V (get-defined-vars B)) (new-B (lower-define B))) (if V (cons `(λ ,V ,new-B) (map void V)) new-B))) (cond ((or (atom? e) (quoted? e)) e) ((eq? (car e) 'def) (lower-define (expand-define e))) ((eq? (car e) 'λ) `(λ ,(cadr e) ,(λ-body e) . ,(lastcdr e))) (else (map lower-define e)))) ;; closure analysis (def (lambda:body e) (caddr e)) (def (lambda:vars e) (lambda-vars (cadr e))) (def (diff s1 s2) (cond ((not s1) NIL) ((memq (car s1) s2) (diff (cdr s1) s2)) (else (cons (car s1) (diff (cdr s1) s2))))) ;; bindings that are both captured and set!'d (def (complex-bindings- e vars head nested capt setd) (cond ((not vars) NIL) ((sym? e) (when (and nested (memq e vars)) (put! capt e T))) ((or (atom? e) (quoted? e)) NIL) ((eq? (car e) 'set!) (when (memq (cadr e) vars) (put! setd (cadr e) T) (if nested (put! capt (cadr e) T))) (complex-bindings- (caddr e) vars NIL nested capt setd)) ((eq? (car e) 'λ) (complex-bindings- (lambda:body e) (diff vars (lambda:vars e)) NIL (or (not head) nested) capt setd)) (else (cons (complex-bindings- (car e) vars (inlineable? e) nested capt setd) (map (λ (x) (complex-bindings- x vars NIL nested capt setd)) (cdr e)))))) (def (complex-bindings e vars) (let ((capt (table)) (setd (table))) (complex-bindings- e vars NIL NIL capt setd) (filter (λ (x) (has? capt x)) (table-keys setd)))) (def (vars-to-env vars cb offs) (map (λ (var i) (vinfo var (not (not (memq var cb))) (+ i offs))) vars (iota (length vars)))) (def (extend-env env vars cb) (cons (vars-to-env vars cb 0) env)) ;; main entry points (def (compile f) (compile-f NIL (lower-define f))) (def (compile-thunk expr) ;; to eval a top-level expression we need to avoid internal define (compile-f NIL `(λ () ,(lower-define expr)))) (def (compile-f env f) (receive (ff ignore) (compile-f- env f) ff)) (def (compile-f- env f) (def (any-duplicate-kw kw) (let ((k (caar kw)) (rest (cdr kw))) (when rest (or (any (λ (next) (and (eq? k (car next)) k)) rest) (any-duplicate-kw rest))))) ;; compile lambda expression, assuming defines already lowered (let ((g (make-code-emitter)) (args (cadr f)) (atail (lastcdr (cadr f))) (vars (lambda:vars f)) (opta (filter cons? (cadr f))) (last (lastcdr f))) (let* ((name (if (not last) 'λ last)) (nargs (if (atom? args) 0 (length args))) (nreq (- nargs (length opta))) (kwa (filter keyword-arg? opta)) (dupkw (any-duplicate-kw kwa))) (when dupkw (error "compile error: duplicate keyword " dupkw)) ;; emit argument checking prologue (when opta (if (not kwa) (emit g 'optargs nreq (if atail (- nargs) nargs)) (begin (bcode:indexfor g (make-perfect-hash-table (map cons (map car kwa) (iota (length kwa))))) (emit g 'keyargs nreq (length kwa) (if atail (- nargs) nargs)))) (emit-optional-arg-inits g env opta vars nreq)) (cond ((> nargs 255) (emit g (if atail 'vargc.l 'argc.l) nargs)) (atail (emit g 'vargc nargs)) ((not opta) (emit g 'argc nargs))) (let ((newenv (extend-env env vars (complex-bindings (lambda:body f) vars)))) (box-vars g (car newenv)) ;; set initial stack pointer (aset! g 4 (+ (length vars) 4)) ;; compile body and return (compile-in g newenv T (lambda:body f)) (emit g 'ret) (values (fn (encode-byte-code (bcode:code g)) (const-to-idx-vec g) name) (bcode:cenv g)))))) ;; disassembler (def (ref-s32-LE a i) (s32 (+ (ash (aref a (+ i 0)) 0) (ash (aref a (+ i 1)) 8) (ash (aref a (+ i 2)) 16) (ash (aref a (+ i 3)) 24)))) (def (ref-s16-LE a i) (s16 (+ (ash (aref a (+ i 0)) 0) (ash (aref a (+ i 1)) 8)))) (def (hex5 n) (str-lpad (num->str n 16) 5 #\0)) (def (fn-disasm f (ip NIL) . lev?) (when (not lev?) (fn-disasm f ip 0) (newline) (return (void))) (let ((lev (car lev?)) (code (fn-code f)) (vals (fn-vals f))) (def (print-val v) (if (and (fn? v) (not (builtin? v))) (begin (newline) (fn-disasm v NIL (+ lev 1))) (print v))) (def (print-inst inst s sz) (princ (if (and ip (= lev 0) (>= ip (1- s)) (< ip (+ s sz))) " >" " ") (hex5 (- s 1)) ": " inst " ")) (let ((i 0) (N (length code))) (while (< i N) ; find key whose value matches the current byte (let ((inst (table-foldl (λ (k v z) (or z (and (= v (aref code i)) k))) NIL Instructions))) (when (> i 0) (newline)) (dotimes (xx lev) (princ "\t")) (set! i (+ i 1)) (case inst ((loadv.l loadg.l setg.l) (print-inst inst i 4) (print-val (aref vals (ref-s32-LE code i))) (set! i (+ i 4))) ((loadv loadg setg) (print-inst inst i 1) (print-val (aref vals (aref code i))) (set! i (+ i 1))) ((loada seta loadc call tcall list + - * / < = vec argc vargc loadi8 apply tapply closure box shift aref) (print-inst inst i 1) (princ (num->str (+ (aref code i) (if (eq? inst 'aref) 3 0)))) (set! i (+ i 1))) ((loada.l seta.l loadc.l argc.l vargc.l call.l tcall.l box.l) (print-inst inst i 4) (princ (num->str (ref-s32-LE code i))) (set! i (+ i 4))) ((optargs keyargs) (print-inst inst i (+ 8 (if (eq? inst 'keyargs) 4 0))) (princ (num->str (ref-s32-LE code i)) " ") (set! i (+ i 4)) (princ (num->str (ref-s32-LE code i))) (set! i (+ i 4)) (when (eq? inst 'keyargs) (princ " ") (princ (num->str (ref-s32-LE code i)) " ") (set! i (+ i 4)))) ((brbound) (print-inst inst i 4) (princ (num->str (ref-s32-LE code i)) " ") (set! i (+ i 4))) ((jmp brne brnn brn) (print-inst inst i 2) (princ "@" (hex5 (+ i (ref-s16-LE code i)))) (set! i (+ i 2))) ((jmp.l brne.l brnn.l brn.l) (print-inst inst i 4) (princ "@" (hex5 (+ i (ref-s32-LE code i)))) (set! i (+ i 4))) (else (print-inst inst i 0)))))))) ; From SRFI 89 by Marc Feeley (http://srfi.schemers.org/srfi-89/srfi-89.html) ; Copyright (C) Marc Feeley 2006. All Rights Reserved. ; ; "alist" is a list of pairs of the form "(keyword . value)" ; The result is a perfect hash-table represented as a vector of ; length 2*N, where N is the hash modulus. If the keyword K is in ; the hash-table it is at index ; ; X = (* 2 ($hash-keyword K N)) ; ; and the associated value is at index X+1. (def (make-perfect-hash-table alist) (def ($hash-keyword key n) (mod0 (abs (hash key)) n)) (let loop1 ((n (length alist))) (let ((v (vec-alloc (* 2 n) NIL))) (let loop2 ((lst alist)) (if (cons? lst) (let ((key (caar lst))) (let ((x (* 2 ($hash-keyword key n)))) (if (aref v x) (loop1 (+ n 1)) (begin (aset! v x key) (aset! v (+ x 1) (cdar lst)) (loop2 (cdr lst)))))) v)))))