;--------------------------------------------------------------------------------------------- ; ; Stack-Based Model compiler/vm, derived from ; ; Three Implementation Models for Scheme ; TR87-0ll ; 1987 ; R. Kent Dybvig ; ; https://www.cs.unc.edu/techreports/87-011.pdf ; ; ;--------------------------------------------------------------------------------------------- (load "n.sf") ;--------------------------------------------------------------------------------------------- ; Utils ;--------------------------------------------------------------------------------------------- (define set-member? (lambda (x s) (cond [(null? s) #f] [(eq? x (car s)) #t] [else (set-member? x (cdr s))]))) (define set-cons (lambda (x s) (if (set-member? x s) s (cons x s)))) (define set-union (lambda (s1 s2) (if (null? s1) s2 (set-union (cdr s1) (set-cons (car s1) s2))))) (define set-minus (lambda (s1 s2) (if (null? s1) '() (if (set-member? (car s1) s2) (set-minus (cdr s1) s2) (cons (car s1) (set-minus (cdr s1) s2)))))) (define set-intersect (lambda (s1 s2) (if (null? s1) '() (if (set-member? (car s1) s2) (cons (car s1) (set-intersect (cdr s1) s2)) (set-intersect (cdr s1) s2))))) (define-syntax record-case (syntax-rules (else) [(record-case (pa . ir) clause ...) (let ([id (pa . ir)]) (record-case id clause ...))] [(record-case id) 'record-case-miss] [(record-case id [else exp ...]) (begin exp ...)] [(record-case id [key ids exp ...] clause ...) (if (eq? (car id) 'key) (apply (lambda ids exp ...) (cdr id)) (record-case id clause ...))])) (define syntax-match? (lambda (pat exp) (or (eq? pat '*) (equal? exp pat) (and (pair? pat) (cond [(and (eq? (car pat) '$) (pair? (cdr pat)) (null? (cddr pat))) (eq? exp (cadr pat))] [(and (pair? (cdr pat)) (eq? (cadr pat) '...) (null? (cddr pat))) (let ([pat (car pat)]) (define (f lst) (or (null? lst) (and (pair? lst) (syntax-match? pat (car lst)) (f (cdr lst))))) (f exp))] [else (and (pair? exp) (syntax-match? (car pat) (car exp)) (syntax-match? (cdr pat) (cdr exp)))]))))) ; unique symbol generator (poor man's version) (define gensym (let ([gsc 0]) (lambda args ; (), (symbol), or (#f) for gsc reset (set! gsc (fx+ gsc 1)) (if (null? args) (string->symbol (string-append "#" (fixnum->string gsc 10))) (if (symbol? (car args)) (string->symbol (string-append (symbol->string (car args)) (string-append "#" (fixnum->string gsc 10)))) (set! gsc 0)))))) (define posq (lambda (x l) (let loop ([l l] [n 0]) (cond [(null? l) #f] [(eq? x (car l)) n] [else (loop (cdr l) (fx+ n 1))])))) (define list-diff (lambda (l t) (if (or (null? l) (eq? l t)) '() (cons (car l) (list-diff (cdr l) t))))) (define (pair* x . more) (let loop ([x x] [rest more]) (if (null? rest) x (cons x (loop (car rest) (cdr rest)))))) (define (andmap p l) (if (pair? l) (and (p (car l)) (andmap p (cdr l))) #t)) (define (list1? x) (and (pair? x) (null? (cdr x)))) (define (list1+? x) (and (pair? x) (list? (cdr x)))) (define (list2? x) (and (pair? x) (list1? (cdr x)))) (define (list2+? x) (and (pair? x) (list1+? (cdr x)))) (define integrable? (%prim "{ /* define integrable? */ static obj c[] = { obj_from_objptr(vmcases+8) }; $return objptr(c); }")) (define lookup-integrable (%prim "{ /* define lookup-integrable */ static obj c[] = { obj_from_objptr(vmcases+9) }; $return objptr(c); }")) (define integrable-type (%prim "{ /* define integrable-type */ static obj c[] = { obj_from_objptr(vmcases+10) }; $return objptr(c); }")) (define integrable-global (%prim "{ /* define integrable-global */ static obj c[] = { obj_from_objptr(vmcases+11) }; $return objptr(c); }")) (define integrable-code (%prim "{ /* define integrable-code */ static obj c[] = { obj_from_objptr(vmcases+12) }; $return objptr(c); }")) ;--------------------------------------------------------------------------------------------- ; Syntax of the Scheme Core language ;--------------------------------------------------------------------------------------------- ; -> (quote ) ; -> (ref ) ; -> (set! ) ; -> (set& ) ; -> (lambda ) where -> ( ...) | ( ... . ) | ; -> (lambda* ( ) ...) where -> ( ) ; -> (syntax-lambda ( ...) ) ; -> (letcc ) ; -> (withcc ) ; -> (begin ...) ; -> (if ) ; -> (call ...) ; -> (integrable ...) where is an index in the integrables table ; NB: (begin) is legit, returns unspecified value ; on top level, these two extra core forms are legal: ; -> (define ) ; -> (define-syntax ) (define idslist? (lambda (x) (cond [(null? x) #t] [(pair? x) (and (id? (car x)) (idslist? (cdr x)))] [else (id? x)]))) (define normalize-arity (lambda (arity) (if (and (list2? arity) (fixnum? (car arity)) (boolean? (cadr arity))) arity (let loop ([cnt 0] [l arity]) (cond [(pair? l) (loop (fx+ 1 cnt) (cdr l))] [(null? l) (list cnt #f)] [else (list cnt #t)]))))) ; convention for 'flattened' is to put rest arg if any at the front (define flatten-idslist (lambda (ilist) (if (list? ilist) ilist (let loop ([l ilist] [r '()]) (cond [(pair? l) (loop (cdr l) (cons (car l) r))] [else (if (null? l) (reverse! r) (cons l (reverse! r)))]))))) (define idslist-req-count (lambda (ilist) (if (pair? ilist) (fx+ 1 (idslist-req-count (cdr ilist))) 0))) ;--------------------------------------------------------------------------------------------- ; Macro transformer (from Scheme to Scheme Core) derived from Al Petrofsky's EIOD 1.17 ;--------------------------------------------------------------------------------------------- ; An environment is a procedure that accepts any identifier and ; returns a denotation. The denotation of an unbound identifier is ; its name (as a symbol). A bound identifier's denotation is its ; binding, which is a pair of the current value and the identifier's ; name (needed by quote). Biding's value can be changed later. ; Special forms are either a symbol naming a builtin, or a transformer procedure ; that takes two arguments: a macro use and the environment of the macro use. ; -> | ; -> | ; -> ( . ) ; -> | ; -> | ; -> syntax | quote | set! | set& | begin | if | lambda | ; lambda* | syntax-lambda | letcc | withcc | body | ; define | define-syntax ; top-level only ; -> (define-inline (val-core? val) (pair? val)) (define-inline (val-special? val) (not (pair? val))) (define-inline (binding? x) (pair? x)) (define-inline (make-binding s v) (cons s v)) (define-inline (binding-val bnd) (cdr bnd)) (define-inline (binding-special? bnd) (val-special? (cdr bnd))) (define-inline (binding-sym bnd) (car bnd)) (define-inline (binding-set-val! bnd val) (set-cdr! bnd val)) (define-inline (find-top-binding s blist) (assq s blist)) (define (new-id den) (define p (list den)) (lambda () p)) (define (old-den id) (car (id))) (define (id? x) (or (symbol? x) (procedure? x))) (define (id->sym id) (if (symbol? id) id (den->sym (old-den id)))) (define (den->sym den) (if (symbol? den) den (binding-sym den))) (define (empty-xenv id) (if (symbol? id) id (old-den id))) (define (extend-xenv env id bnd) (lambda (i) (if (eq? id i) bnd (env i)))) (define (add-binding key val env) ; adds as-is (extend-xenv env key (make-binding (id->sym key) val))) (define (add-var var val env) ; adds renamed var as (extend-xenv env var (make-binding (id->sym var) (list 'ref val)))) (define (x-error msg . args) (error* (string-append "transformer: " msg) args)) ; xform receives Scheme s-expressions and returns either Core Scheme ; (always a pair) or special-form, which is either a builtin (a symbol) or ; a transformer (a procedure). Appos? flag is true when the context can ; allow xform to return a transformer; otherwise, only is accepted. (define (xform appos? sexp env) (cond [(id? sexp) (let ([hval (xform-ref sexp env)]) (cond [appos? hval] [(integrable? hval) ; integrable id-syntax (list 'ref (integrable-global hval))] [(procedure? hval) ; id-syntax (xform appos? (hval sexp env) env)] [(not (pair? hval)) (x-error "improper use of syntax form" hval)] [else hval]))] [(not (pair? sexp)) (xform-quote (list sexp) env)] [else (let* ([head (car sexp)] [tail (cdr sexp)] [hval (xform #t head env)]) (case hval [(syntax) (car tail)] ; internal use only [(quote) (xform-quote tail env)] [(set!) (xform-set! tail env)] [(set&) (xform-set& tail env)] [(begin) (xform-begin tail env)] [(if) (xform-if tail env)] [(lambda) (xform-lambda tail env)] [(lambda*) (xform-lambda* tail env)] [(syntax-lambda) (xform-syntax-lambda tail env)] [(letcc) (xform-letcc tail env)] [(withcc) (xform-withcc tail env)] [(body) (xform-body tail env)] [(define) (xform-define tail env)] [(define-syntax) (xform-define-syntax tail env)] [(syntax-length) (xform-syntax-length tail env)] [(syntax-error) (xform-syntax-error tail env)] [else (if (integrable? hval) (xform-integrable hval tail env) (if (procedure? hval) (xform appos? (hval sexp env) env) (xform-call hval tail env)))]))])) (define (xform-sexp->datum sexp) (let conv ([sexp sexp]) (cond [(id? sexp) (id->sym sexp)] [(pair? sexp) (cons (conv (car sexp)) (conv (cdr sexp)))] [(vector? sexp) (list->vector (map conv (vector->list sexp)))] [else sexp]))) (define (xform-ref id env) (let ([den (env id)]) (cond [(symbol? den) (list 'ref den)] [else (binding-val den)]))) (define (xform-quote tail env) (if (list1? tail) (list 'quote (xform-sexp->datum (car tail))) (x-error "improper quote form" (cons 'quote tail)))) (define (xform-syntax-length tail env) (if (and (list1? tail) (list? (car tail))) (list 'quote (length (car tail))) (x-error "improper syntax-length form" (cons 'syntax-length tail)))) (define (xform-syntax-error tail env) (let ([args (map xform-sexp->datum tail)]) (if (and (list1+? args) (string? (car args))) (apply x-error args) (x-error "improper syntax-error form" (cons 'syntax-error tail))))) (define (xform-set! tail env) (if (and (list2? tail) (id? (car tail))) (let ([den (env (car tail))] [xexp (xform #f (cadr tail) env)]) (cond [(symbol? den) (list 'set! den xexp)] [(binding-special? den) (binding-set-val! den xexp) '(begin)] [else (let ([val (binding-val den)]) (if (eq? (car val) 'ref) (list 'set! (cadr val) xexp) (x-error "set! to non-identifier form")))])) (x-error "improper set! form" (cons 'set! tail)))) (define (xform-set& tail env) (if (list1? tail) (let ([den (env (car tail))]) (cond [(symbol? den) (list 'set& den)] [(binding-special? den) (x-error "set& of a non-variable")] [else (let ([val (binding-val den)]) (if (eq? (car val) 'ref) (list 'set& (cadr val)) (x-error "set& of a non-variable")))])) (x-error "improper set& form" (cons 'set& tail)))) (define (xform-begin tail env) ; top-level (if (list? tail) (let ([xexps (map (lambda (sexp) (xform #f sexp env)) tail)]) (if (and (pair? xexps) (null? (cdr xexps))) (car xexps) ; (begin x) => x (cons 'begin xexps))) (x-error "improper begin form" (cons 'begin! tail)))) (define (xform-if tail env) (if (list? tail) (let ([xexps (map (lambda (sexp) (xform #f sexp env)) tail)]) (case (length xexps) [(2) (cons 'if (append xexps '((begin))))] [(3) (cons 'if xexps)] [else (x-error "malformed if form" (cons 'if tail))])) (x-error "improper if form" (cons 'if tail)))) (define (xform-call xexp tail env) (if (list? tail) (let ([xexps (map (lambda (sexp) (xform #f sexp env)) tail)]) (if (and (null? xexps) (eq? (car xexp) 'lambda) (null? (cadr xexp))) (caddr xexp) ; ((let () x)) => x (pair* 'call xexp xexps))) (x-error "improper application" (cons xexp tail)))) (define (integrable-argc-match? igt n) (case igt [(#\0) (= n 0)] [(#\1) (= n 1)] [(#\2) (= n 2)] [(#\3) (= n 3)] [(#\p) (>= n 0)] [(#\m) (>= n 1)] [(#\c) (>= n 2)] [(#\x) (>= n 1)] [(#\u) (<= 0 n 1)] [(#\b) (<= 1 n 2)] [(#\t) (<= 2 n 3)] [(#\#) (>= n 0)] [(#\@) #f] [else #f])) (define (xform-integrable ig tail env) (if (integrable-argc-match? (integrable-type ig) (length tail)) (cons 'integrable (cons ig (map (lambda (sexp) (xform #f sexp env)) tail))) (xform-call (list 'ref (integrable-global ig)) tail env))) (define (xform-lambda tail env) (if (and (list1+? tail) (idslist? (car tail))) (let loop ([vars (car tail)] [ienv env] [ipars '()]) (cond [(pair? vars) (let* ([var (car vars)] [nvar (gensym (id->sym var))]) (loop (cdr vars) (add-var var nvar ienv) (cons nvar ipars)))] [(null? vars) (list 'lambda (reverse ipars) (xform-body (cdr tail) ienv))] [else ; improper (let* ([var vars] [nvar (gensym (id->sym var))] [ienv (add-var var nvar ienv)]) (list 'lambda (append (reverse ipars) nvar) (xform-body (cdr tail) ienv)))])) (x-error "improper lambda body" (cons 'lambda tail)))) (define (xform-lambda* tail env) (if (list? tail) (cons 'lambda* (map (lambda (aexp) (if (and (list2? aexp) (or (and (list2? (car aexp)) (fixnum? (caar aexp)) (boolean? (cadar aexp))) (idslist? (car aexp)))) (list (normalize-arity (car aexp)) (xform #f (cadr aexp) env)) (x-error "improper lambda* clause" aexp))) tail)) (x-error "improper lambda* form" (cons 'lambda* tail)))) (define (xform-syntax-lambda tail env) (if (and (list2+? tail) (andmap id? (car tail))) (let ([vars (car tail)] [macenv env] [forms (cdr tail)]) ; return a transformer that wraps xformed body in (syntax ...) (lambda (use useenv) (if (and (list1+? use) (fx=? (length vars) (length (cdr use)))) (let loop ([vars vars] [exps (cdr use)] [env macenv]) (if (null? vars) (list 'syntax (xform-body forms env)) (loop (cdr vars) (cdr exps) (add-binding (car vars) (xform #t (car exps) useenv) env)))) (x-error "invalif syntax-lambda application" use)))) (x-error "improper syntax-lambda body" (cons 'syntax-lambda tail)))) (define (xform-letcc tail env) (if (and (list2+? tail) (id? (car tail))) (let* ([var (car tail)] [nvar (gensym (id->sym var))]) (list 'letcc nvar (xform-body (cdr tail) (add-var var nvar env)))) (x-error "improper letcc form" (cons 'letcc tail)))) (define (xform-withcc tail env) (if (list2+? tail) (list 'withcc (xform #f (car tail) env) (xform-body (cdr tail) env)) (x-error "improper withcc form" (cons 'withcc tail)))) (define (xform-body tail env) (cond [(null? tail) (list 'begin)] [(list1? tail) ; can't have defines there (xform #f (car tail) env)] [(not (list? tail)) (x-error "improper body form" (cons 'body tail))] [else (let loop ([env env] [ids '()] [inits '()] [nids '()] [body tail]) (if (and (pair? body) (pair? (car body))) (let ([first (car body)] [rest (cdr body)]) (let* ([head (car first)] [tail (cdr first)] [hval (xform #t head env)]) (case hval [(begin) ; internal (if (list? tail) (loop env ids inits nids (append tail rest)) (x-error "improper begin form" first))] [(define) ; internal (cond [(and (list2? tail) (null? (car tail))) ; idless (let ([init (cadr tail)]) (loop env (cons #f ids) (cons init inits) (cons #f nids) rest))] [(and (list2? tail) (id? (car tail))) (let* ([id (car tail)] [init (cadr tail)] [nid (gensym (id->sym id))] [env (add-var id nid env)]) (loop env (cons id ids) (cons init inits) (cons nid nids) rest))] [(and (list2+? tail) (pair? (car tail)) (id? (caar tail)) (idslist? (cdar tail))) (let* ([id (caar tail)] [lambda-id (new-id (make-binding 'lambda 'lambda))] [init (cons lambda-id (cons (cdar tail) (cdr tail)))] [nid (gensym (id->sym id))] [env (add-var id nid env)]) (loop env (cons id ids) (cons init inits) (cons nid nids) rest))] [else (x-error "improper define form" first)])] [(define-syntax) ; internal (if (and (list2? tail) (id? (car tail))) (let* ([id (car tail)] [init (cadr tail)] [env (add-binding id '(undefined) env)]) (loop env (cons id ids) (cons init inits) (cons #t nids) rest)) (x-error "improper define-syntax form" first))] [else (if (procedure? hval) (loop env ids inits nids (cons (hval first env) rest)) (xform-labels (reverse ids) (reverse inits) (reverse nids) body env))]))) (xform-labels (reverse ids) (reverse inits) (reverse nids) body env)))])) (define (xform-labels ids inits nids body env) (let loop ([ids ids] [inits inits] [nids nids] [sets '()] [lids '()]) (cond [(null? ids) (let* ([xexps (append (reverse sets) (map (lambda (x) (xform #f x env)) body))] [xexp (if (list1? xexps) (car xexps) (cons 'begin xexps))]) (if (null? lids) xexp (pair* 'call (list 'lambda (reverse lids) xexp) (map (lambda (lid) '(begin)) lids))))] [(not (car ids)) ; idless define (loop (cdr ids) (cdr inits) (cdr nids) (cons (xform #f (car inits) env) sets) lids)] [(symbol? (car nids)) ; define (loop (cdr ids) (cdr inits) (cdr nids) (cons (xform-set! (list (car ids) (car inits)) env) sets) (cons (car nids) lids))] [else ; define-syntax (binding-set-val! (env (car ids)) (xform #t (car inits) env)) (loop (cdr ids) (cdr inits) (cdr nids) sets lids)]))) (define (xform-define tail env) ; top-level (cond [(and (list2? tail) (null? (car tail))) ; idless (xform #f (cadr tail) env)] [(and (list2? tail) (id? (car tail))) (list 'define (id->sym (car tail)) (xform #f (cadr tail) env))] [(and (list2+? tail) (pair? (car tail)) (id? (caar tail)) (idslist? (cdar tail))) (list 'define (id->sym (caar tail)) (xform-lambda (cons (cdar tail) (cdr tail)) env))] [else (x-error "improper define form" (cons 'define tail))])) (define (xform-define-syntax tail env) ; top-level (if (and (list2? tail) (id? (car tail))) (list 'define-syntax (id->sym (car tail)) (xform #t (cadr tail) env)) (x-error "improper define-syntax form" (cons 'define-syntax tail)))) ; ellipsis denotation is used for comparisons only (define denotation-of-default-ellipsis (make-binding '... (lambda (sexp env) (x-error "improper use of ..." sexp)))) (define *transformers* (list (make-binding 'syntax 'syntax) (make-binding 'define 'define) (make-binding 'define-syntax 'define-syntax) (make-binding 'quote 'quote) (make-binding 'set! 'set!) (make-binding 'set& 'set&) (make-binding 'lambda 'lambda) (make-binding 'lambda* 'lambda*) (make-binding 'syntax-lambda 'syntax-lambda) (make-binding 'syntax-length 'syntax-length) (make-binding 'syntax-error 'syntax-error) (make-binding 'letcc 'letcc) (make-binding 'withcc 'withcc) (make-binding 'begin 'begin) (make-binding 'if 'if) (make-binding 'body 'body) denotation-of-default-ellipsis)) (define (top-transformer-env id) (let ([bnd (find-top-binding id *transformers*)]) (cond [(binding? bnd) ; special case: syntax-rules in sexp form (left by init) (let ([val (binding-val bnd)]) (if (and (pair? val) (eq? (car val) 'syntax-rules)) (binding-set-val! bnd (transform #t val)))) bnd] [(symbol? id) (let ([bnd (make-binding id (or (lookup-integrable id) (list 'ref id)))]) (set! *transformers* (cons bnd *transformers*)) bnd)] [else (old-den id)]))) (define (install-transformer! s t) (binding-set-val! (top-transformer-env s) t)) (define (install-transformer-rules! s ell lits rules) (install-transformer! s (syntax-rules* top-transformer-env ell lits rules))) (define (transform appos? sexp . optenv) ; (gensym #f) ; reset gs counter to make results reproducible (xform appos? sexp (if (null? optenv) top-transformer-env (car optenv)))) ; 'syntax-rules' transformer produces another transformer from the rules (define (syntax-rules* mac-env ellipsis pat-literals rules) (define (pat-literal? id) (memq id pat-literals)) (define (not-pat-literal? id) (not (pat-literal? id))) (define (ellipsis-pair? x) (and (pair? x) (ellipsis? (car x)))) (define (ellipsis? x) (if ellipsis (eq? x ellipsis) (and (id? x) (eq? (mac-env x) denotation-of-default-ellipsis)))) ; List-ids returns a list of the non-ellipsis ids in a ; pattern or template for which (pred? id) is true. If ; include-scalars is false, we only include ids that are ; within the scope of at least one ellipsis. (define (list-ids x include-scalars pred?) (let collect ([x x] [inc include-scalars] [l '()]) (cond [(id? x) (if (and inc (pred? x)) (cons x l) l)] [(vector? x) (collect (vector->list x) inc l)] [(pair? x) (if (ellipsis-pair? (cdr x)) (collect (car x) #t (collect (cddr x) inc l)) (collect (car x) inc (collect (cdr x) inc l)))] [else l]))) ; Returns #f or an alist mapping each pattern var to a part of ; the input. Ellipsis vars are mapped to lists of parts (or ; lists of lists ...). (define (match-pattern pat use use-env) (call-with-current-continuation (lambda (return) (define (fail) (return #f)) (let match ([pat pat] [sexp use] [bindings '()]) (define (continue-if condition) (if condition bindings (fail))) (cond [(id? pat) (if (pat-literal? pat) (continue-if (and (id? sexp) (eq? (use-env sexp) (mac-env pat)))) (cons (cons pat sexp) bindings))] [(vector? pat) (or (vector? sexp) (fail)) (match (vector->list pat) (vector->list sexp) bindings)] [(not (pair? pat)) (continue-if (equal? pat sexp))] [(ellipsis-pair? (cdr pat)) (let* ([tail-len (length (cddr pat))] [sexp-len (if (list? sexp) (length sexp) (fail))] [seq-len (fx- sexp-len tail-len)] [sexp-tail (begin (if (negative? seq-len) (fail)) (list-tail sexp seq-len))] [seq (reverse (list-tail (reverse sexp) tail-len))] [vars (list-ids (car pat) #t not-pat-literal?)]) (define (match1 sexp) (map cdr (match (car pat) sexp '()))) (append (apply map (cons list (cons vars (map match1 seq)))) (match (cddr pat) sexp-tail bindings)))] [(pair? sexp) (match (car pat) (car sexp) (match (cdr pat) (cdr sexp) bindings))] [else (fail)]))))) (define (expand-template pat tmpl top-bindings) ; New-literals is an alist mapping each literal id in the ; template to a fresh id for inserting into the output. It ; might have duplicate entries mapping an id to two different ; fresh ids, but that's okay because when we go to retrieve a ; fresh id, assq will always retrieve the first one. (define new-literals (map (lambda (id) (cons id (new-id (mac-env id)))) (list-ids tmpl #t (lambda (id) (not (assq id top-bindings)))))) (define ellipsis-vars (list-ids pat #f not-pat-literal?)) (define (list-ellipsis-vars subtmpl) (list-ids subtmpl #t (lambda (id) (memq id ellipsis-vars)))) (let expand ([tmpl tmpl] [bindings top-bindings]) (let expand-part ([tmpl tmpl]) (cond [(id? tmpl) (cdr (or (assq tmpl bindings) (assq tmpl top-bindings) (assq tmpl new-literals)))] [(vector? tmpl) (list->vector (expand-part (vector->list tmpl)))] [(and (pair? tmpl) (ellipsis-pair? (cdr tmpl))) (let ([vars-to-iterate (list-ellipsis-vars (car tmpl))]) (define (lookup var) (cdr (assq var bindings))) (define (expand-using-vals . vals) (expand (car tmpl) (map cons vars-to-iterate vals))) (if (null? vars-to-iterate) ; ellipsis following non-repeatable part is an error, but we don't care (cons (expand-part (car tmpl)) (expand-part (cddr tmpl))) ; repeat once ; correct use of ellipsis (let ([val-lists (map lookup vars-to-iterate)]) (append (apply map (cons expand-using-vals val-lists)) (expand-part (cddr tmpl))))))] [(pair? tmpl) (cons (expand-part (car tmpl)) (expand-part (cdr tmpl)))] [else tmpl])))) (lambda (use use-env) (let loop ([rules rules]) (if (null? rules) (x-error "invalid syntax" use)) (let* ([rule (car rules)] [pat (car rule)] [tmpl (cadr rule)]) (cond [(match-pattern pat use use-env) => (lambda (bindings) (expand-template pat tmpl bindings))] [else (loop (cdr rules))]))))) (install-transformer! 'syntax-rules (lambda (sexp env) (define syntax-id (new-id (make-binding 'syntax 'syntax))) ; sexp can be either (if (id? (cadr sexp)) ; (_ ellipsis (litname ...) . rules) (list syntax-id (syntax-rules* env (cadr sexp) (caddr sexp) (cdddr sexp))) ; or (_ (litname ...) . rules) (list syntax-id (syntax-rules* env #f (cadr sexp) (cddr sexp)))))) ;--------------------------------------------------------------------------------------------- ; Runtime ;--------------------------------------------------------------------------------------------- (%localdef "#include \"i.h\"") (define *globals* '()) (define global-location (lambda (sym) (let ([p (assq sym *globals*)]) (if (pair? p) (cdr p) (let ([loc (box 'undefined)]) (set! *globals* (cons (cons sym loc) *globals*)) loc))))) (define-syntax index-global unbox) (define-syntax index-set-global! set-box!) (define *dynamic-state* (list #f)) ; for dynamic-wind (define *current-input* #f) (define *current-output* #f) (define *current-error* #f) ;--------------------------------------------------------------------------------------------- ; String representation of S-expressions and code arguments ;--------------------------------------------------------------------------------------------- (define (c-error msg . args) (error* (string-append "compiler: " msg) args)) (define (write-serialized-char x port) (cond [(or (char=? x #\%) (char=? x #\") (char=? x #\\) (char? x #\~)) (write-char #\% port) (let ([s (fixnum->string (char->integer x) 16)]) (if (fx=? (string-length s) 1) (write-char #\0 port)) (write-string s port))] [else (write-char x port)])) (define (write-serialized-byte x port) (let ([s (fixnum->string x 16)]) (if (fx=? (string-length s) 1) (write-char #\0 port)) (write-string s port))) (define (write-serialized-size n port) (write-string (fixnum->string n 10) port) (write-char #\: port)) (define (write-serialized-element x port) (write-serialized-sexp x port) (write-char #\; port)) (define (write-serialized-sexp x port) (cond [(eq? x #f) (write-char #\f port)] [(eq? x #t) (write-char #\t port)] [(eq? x '()) (write-char #\n port)] [(char? x) (write-char #\c port) (write-serialized-char x port)] [(number? x) (write-char (if (exact? x) #\i #\j) port) (write-string (number->string x 10) port)] [(list? x) (write-char #\l port) (write-serialized-size (length x) port) (do ([x x (cdr x)]) [(null? x)] (write-serialized-element (car x) port))] [(pair? x) (write-char #\p port) (write-serialized-element (car x) port) (write-serialized-element (cdr x) port)] [(vector? x) (write-char #\v port) (write-serialized-size (vector-length x) port) (do ([i 0 (fx+ i 1)]) [(fx=? i (vector-length x))] (write-serialized-element (vector-ref x i) port))] [(string? x) (write-char #\s port) (write-serialized-size (string-length x) port) (do ([i 0 (fx+ i 1)]) [(fx=? i (string-length x))] (write-serialized-char (string-ref x i) port))] [(bytevector? x) (write-char #\b port) (write-serialized-size (bytevector-length x) port) (do ([i 0 (fx+ i 1)]) [(fx=? i (bytevector-length x))] (write-serialized-byte (bytevector-u8-ref x i) port))] [(symbol? x) (write-char #\y port) (let ([x (symbol->string x)]) (write-serialized-size (string-length x) port) (do ([i 0 (fx+ i 1)]) [(fx=? i (string-length x))] (write-serialized-char (string-ref x i) port)))] [else (c-error "cannot encode literal" x)])) (define (write-serialized-arg arg port) (if (and (number? arg) (exact? arg) (fx<=? 0 arg) (fx<=? arg 9)) (write-char (string-ref "0123456789" arg) port) (begin (write-char #\( port) (write-serialized-sexp arg port) (write-char #\) port)))) ;--------------------------------------------------------------------------------------------- ; Compiler producing serialized code ;--------------------------------------------------------------------------------------------- (define find-free* (lambda (x* b) (if (null? x*) '() (set-union (find-free (car x*) b) (find-free* (cdr x*) b))))) (define find-free (lambda (x b) (record-case x [quote (obj) '()] [ref (id) (if (set-member? id b) '() (list id))] [set! (id exp) (set-union (if (set-member? id b) '() (list id)) (find-free exp b))] [set& (id) (if (set-member? id b) '() (list id))] [lambda (idsi exp) (find-free exp (set-union (flatten-idslist idsi) b))] [lambda* clauses (find-free* (map cadr clauses) b)] [letcc (kid exp) (find-free exp (set-union (list kid) b))] [withcc (kexp exp) (set-union (find-free kexp b) (find-free exp b))] [if (test then else) (set-union (find-free test b) (set-union (find-free then b) (find-free else b)))] [begin exps (find-free* exps b)] [integrable (ig . args) (find-free* args b)] [call (exp . args) (set-union (find-free exp b) (find-free* args b))] [define tail (c-error "misplaced define form" x)]))) (define find-sets* (lambda (x* v) (if (null? x*) '() (set-union (find-sets (car x*) v) (find-sets* (cdr x*) v))))) (define find-sets (lambda (x v) (record-case x [quote (obj) '()] [ref (id) '()] [set! (id x) (set-union (if (set-member? id v) (list id) '()) (find-sets x v))] [set& (id) (if (set-member? id v) (list id) '())] [lambda (idsi exp) (find-sets exp (set-minus v (flatten-idslist idsi)))] [lambda* clauses (find-sets* (map cadr clauses) v)] [letcc (kid exp) (find-sets exp (set-minus v (list kid)))] [withcc (kexp exp) (set-union (find-sets kexp v) (find-sets exp v))] [begin exps (find-sets* exps v)] [if (test then else) (set-union (find-sets test v) (set-union (find-sets then v) (find-sets else v)))] [integrable (ig . args) (find-sets* args v)] [call (exp . args) (set-union (find-sets exp v) (find-sets* args v))] [define tail (c-error "misplaced define form" x)]))) (define codegen ; x: Scheme Core expression to compile ; l: local var list (with #f placeholders for nonvar slots) ; f: free var list ; s: set! var set ; g: global var set ; k: #f: x goes to ac, N: x is to be returned after (sdrop n) ; port: output code goes here (lambda (x l f s g k port) (record-case x [quote (obj) (case obj [(#t) (write-char #\t port)] [(#f) (write-char #\f port)] [(()) (write-char #\n port)] [else (write-char #\' port) (write-serialized-arg obj port)]) (when k (write-char #\] port) (write-serialized-arg k port))] [ref (id) (cond [(posq id l) => ; local (lambda (n) (write-char #\. port) (write-serialized-arg n port) (if (set-member? id s) (write-char #\^ port)))] [(posq id f) => ; free (lambda (n) (write-char #\: port) (write-serialized-arg n port) (if (set-member? id s) (write-char #\^ port)))] [else ; global (write-char #\@ port) (write-serialized-arg id port)]) (when k (write-char #\] port) (write-serialized-arg k port))] [set! (id x) (codegen x l f s g #f port) (cond [(posq id l) => ; local (lambda (n) (write-char #\. port) (write-char #\! port) (write-serialized-arg n port))] [(posq id f) => ; free (lambda (n) (write-char #\: port) (write-char #\! port) (write-serialized-arg n port))] [else ; global (write-char #\@ port) (write-char #\! port) (write-serialized-arg id port)]) (when k (write-char #\] port) (write-serialized-arg k port))] [set& (id) (cond [(posq id l) => ; local (lambda (n) (write-char #\. port) (write-serialized-arg n port))] [(posq id f) => ; free (lambda (n) (write-char #\: port) (write-serialized-arg n port))] [else ; global (write-char #\` port) (write-serialized-arg id port)]) (when k (write-char #\] port) (write-serialized-arg k port))] [begin exps (let loop ([xl exps]) (when (pair? xl) (let ([k (if (pair? (cdr xl)) #f k)]) (codegen (car xl) l f s g k port) (loop (cdr xl))))) (when (and k (null? exps)) (write-char #\] port) (write-serialized-arg k port))] [if (test then else) (codegen test l f s g #f port) (write-char #\? port) (write-char #\{ port) (codegen then l f s g k port) (write-char #\} port) (cond [k ; tail call: 'then' arm exits, so br around is not needed (codegen else l f s g k port)] [(equal? else '(begin)) ; non-tail with void 'else' arm ] ; no code needed -- ac retains #f from failed test [else ; non-tail with 'else' expression; needs br (write-char #\{ port) (codegen else l f s g k port) (write-char #\} port)])] [lambda (idsi exp) (let* ([ids (flatten-idslist idsi)] [free (set-minus (find-free exp ids) g)] [sets (find-sets exp ids)]) (do ([free (reverse free) (cdr free)] [l l (cons #f l)]) [(null? free)] ; note: called with empty set! var list ; to make sure no dereferences are generated (codegen (list 'ref (car free)) l f '() g #f port) (write-char #\, port)) (write-char #\& port) (write-serialized-arg (length free) port) (write-char #\{ port) (cond [(list? idsi) (write-char #\% port) (write-serialized-arg (length idsi) port)] [else (write-char #\% port) (write-char #\! port) (write-serialized-arg (idslist-req-count idsi) port)]) (do ([ids ids (cdr ids)] [n 0 (fx+ n 1)]) [(null? ids)] (when (set-member? (car ids) sets) (write-char #\# port) (write-serialized-arg n port))) (codegen exp ids free (set-union sets (set-intersect s free)) g (length ids) port) (write-char #\} port)) (when k (write-char #\] port) (write-serialized-arg k port))] [lambda* clauses (do ([clauses (reverse clauses) (cdr clauses)] [l l (cons #f l)]) [(null? clauses)] (codegen (cadr (car clauses)) l f s g #f port) (write-char #\% port) (write-char #\x port) (write-char #\, port)) (write-char #\& port) (write-serialized-arg (length clauses) port) (write-char #\{ port) (do ([clauses clauses (cdr clauses)] [i 0 (fx+ i 1)]) [(null? clauses)] (let* ([arity (caar clauses)] [cnt (car arity)] [rest? (cadr arity)]) (write-char #\| port) (if rest? (write-char #\! port)) (write-serialized-arg cnt port) (write-serialized-arg i port))) (write-char #\% port) (write-char #\% port) (write-char #\} port) (when k (write-char #\] port) (write-serialized-arg k port))] [letcc (kid exp) (let* ([ids (list kid)] [sets (find-sets exp ids)] [news (set-union (set-minus s ids) sets)]) (cond [k ; tail position with k locals on stack to be disposed of (write-char #\k port) (write-serialized-arg k port) (write-char #\, port) (when (set-member? kid sets) (write-char #\# port) (write-char #\0 port)) ; stack map here: kid on top (codegen exp (cons kid l) f news g (fx+ k 1) port)] [else ; non-tail position (write-char #\$ port) (write-char #\{ port) (write-char #\k port) (write-char #\0 port) (write-char #\, port) (when (set-member? kid sets) (write-char #\# port) (write-char #\0 port)) ; stack map here: kid on top, two-slot frame under it (codegen exp (cons kid (cons #f (cons #f l))) f news g #f port) (write-char #\_ port) (write-serialized-arg 3 port) (write-char #\} port)]))] [withcc (kexp exp) (cond [(memq (car exp) '(quote ref lambda)) ; exp is a constant, return it (codegen exp l f s g #f port) (write-char #\, port) ; stack map after: k on top (codegen kexp (cons #f l) f s g #f port) (write-char #\w port) (write-char #\! port)] [else ; exp is not a constant, thunk it and call it from k (codegen (list 'lambda '() exp) l f s g #f port) (write-char #\, port) ; stack map after: k on top (codegen kexp (cons #f l) f s g #f port) (write-char #\w port)])] [integrable (ig . args) (let ([igty (integrable-type ig)] [igc0 (integrable-code ig 0)]) (case igty [(#\0 #\1 #\2 #\3) ; 1st arg in a, others on stack (do ([args (reverse args) (cdr args)] [l l (cons #f l)]) [(null? args)] (codegen (car args) l f s g #f port) (unless (null? (cdr args)) (write-char #\, port))) (write-string igc0 port)] [(#\p) ; (length args) >= 0 (if (null? args) (let ([igc1 (integrable-code ig 1)]) (write-string igc1 port)) (let ([opc (fx- (length args) 1)]) (do ([args (reverse args) (cdr args)] [l l (cons #f l)]) [(null? args)] (codegen (car args) l f s g #f port) (unless (null? (cdr args)) (write-char #\, port))) (do ([i 0 (fx+ i 1)]) [(fx>=? i opc)] (write-string igc0 port))))] [(#\m) ; (length args) >= 1 (if (null? (cdr args)) (let ([igc1 (integrable-code ig 1)]) (codegen (car args) l f s g #f port) (write-string igc1 port)) (let ([opc (fx- (length args) 1)]) (do ([args (reverse args) (cdr args)] [l l (cons #f l)]) [(null? args)] (codegen (car args) l f s g #f port) (unless (null? (cdr args)) (write-char #\, port))) (do ([i 0 (fx+ i 1)]) [(fx>=? i opc)] (write-string igc0 port))))] [(#\c) ; (length args) >= 2 (let ([opc (fx- (length args) 1)] [args (reverse args)]) (codegen (car args) l f s g #f port) (write-char #\, port) (do ([args (cdr args) (cdr args)] [l (cons #f l) (cons #f (cons #f l))]) [(null? args)] (codegen (car args) l f s g #f port) (unless (null? (cdr args)) (write-char #\, port) (write-char #\, port))) (do ([i 0 (fx+ i 1)]) [(fx>=? i opc)] (unless (fxzero? i) (write-char #\; port)) (write-string igc0 port)))] [(#\x) ; (length args) >= 1 (let ([opc (fx- (length args) 1)]) (do ([args (reverse args) (cdr args)] [l l (cons #f l)]) [(null? args)] (codegen (car args) l f s g #f port) (unless (null? (cdr args)) (write-char #\, port))) (do ([i 0 (fx+ i 1)]) [(fx>=? i opc)] (write-string igc0 port)))] [(#\u) ; 0 <= (length args) <= 1 (if (null? args) (write-string (integrable-code ig 1) port) (codegen (car args) l f s g #f port)) (write-string igc0 port)] [(#\b) ; 1 <= (length args) <= 2 (if (null? (cdr args)) (write-string (integrable-code ig 1) port) (codegen (cadr args) l f s g #f port)) (write-char #\, port) (codegen (car args) (cons #f l) f s g #f port) (write-string igc0 port)] [(#\t) ; 2 <= (length args) <= 3 (if (null? (cddr args)) (write-string (integrable-code ig 1) port) (codegen (caddr args) l f s g #f port)) (write-char #\, port) (codegen (cadr args) (cons #f l) f s g #f port) (write-char #\, port) (codegen (car args) (cons #f (cons #f l)) f s g #f port) (write-string igc0 port)] [(#\#) ; (length args) >= 0 (do ([args (reverse args) (cdr args)] [l l (cons #f l)]) [(null? args)] (codegen (car args) l f s g #f port) (write-char #\, port)) (write-string igc0 port) (write-serialized-arg (length args) port)] [else (c-error "unsupported integrable type" igty)])) (when k (write-char #\] port) (write-serialized-arg k port))] [call (exp . args) (cond [(and (eq? (car exp) 'lambda) (list? (cadr exp)) (fx=? (length args) (length (cadr exp)))) ; let-like call; compile as special lambda + call combo (do ([args (reverse args) (cdr args)] [l l (cons #f l)]) [(null? args)] (codegen (car args) l f s g #f port) (write-char #\, port)) (let* ([ids (cadr exp)] [exp (caddr exp)] [sets (find-sets exp ids)] [news (set-union (set-minus s ids) sets)] [newl (append ids l)]) ; with real names (do ([ids ids (cdr ids)] [n 0 (fx+ n 1)]) [(null? ids)] (when (set-member? (car ids) sets) (write-char #\# port) (write-serialized-arg n port))) (if k (codegen exp newl f news g (fx+ k (length args)) port) (begin (codegen exp newl f news g #f port) (write-char #\_ port) (write-serialized-arg (length args) port))))] [k ; tail call with k elements under arguments (do ([args (reverse args) (cdr args)] [l l (cons #f l)]) [(null? args) (codegen exp l f s g #f port)] (codegen (car args) l f s g #f port) (write-char #\, port)) (write-char #\[ port) (write-serialized-arg k port) (write-serialized-arg (length args) port)] [else ; non-tail call; 'save' puts 2 extra elements on the stack! (write-char #\$ port) (write-char #\{ port) (do ([args (reverse args) (cdr args)] [l (cons #f (cons #f l)) (cons #f l)]) [(null? args) (codegen exp l f s g #f port)] (codegen (car args) l f s g #f port) (write-char #\, port)) (write-char #\[ port) (write-serialized-arg 0 port) (write-serialized-arg (length args) port) (write-char #\} port)])] [define tail (c-error "misplaced define form" x)]))) (define (compile-to-string x) (let ([p (open-output-string)]) (codegen x '() '() '() (find-free x '()) #f p) (get-output-string p))) ;--------------------------------------------------------------------------------------------- ; Code deserializer and Evaluator (use built-ins) ;--------------------------------------------------------------------------------------------- (define execute-thunk-closure (%prim "{ /* define execute-thunk-closure */ static obj c[] = { obj_from_objptr(vmcases+0) }; $return objptr(c); }")) (define make-closure (%prim "{ /* define make-closure */ static obj c[] = { obj_from_objptr(vmcases+1) }; $return objptr(c); }")) (define execute (lambda (code) (execute-thunk-closure (make-closure code)))) (define decode-sexp (%prim "{ /* define decode-sexp */ static obj c[] = { obj_from_objptr(vmcases+2) }; $return objptr(c); }")) (define decode (%prim "{ /* define decode */ static obj c[] = { obj_from_objptr(vmcases+3) }; $return objptr(c); }")) (define (evaluate x) (execute (decode (compile-to-string (transform #f x))))) ;--------------------------------------------------------------------------------------------- ; File processor (Scheme => Serialized code) ;--------------------------------------------------------------------------------------------- (define *hide-refs* '()) (define (display-code cstr oport) (let loop ([i 0] [l (string-length cstr)]) (let ([r (fx- l i)]) (cond [(<= r 70) (display " \"" oport) (display (substring cstr i l)) (display "\"," oport)] [else (display " \"" oport) (display (substring cstr i (fx+ i 70))) (display "\"\n" oport) (loop (fx+ i 70) l)])))) (define (process-syntax id xval oport) (newline oport) (display " \"S\", \"" oport) (display id oport) (display "\",\n" oport) (let ([p (open-output-string)]) (write-serialized-sexp xval p) (display-code (get-output-string p) oport) (newline oport))) (define (process-alias id oldid oport) (newline oport) (display " \"A\", \"" oport) (display id oport) (display "\"," oport) (display " \"" oport) (display oldid oport) (display "\",\n" oport)) (define (process-command xval oport) (define cstr (compile-to-string xval)) (newline oport) (display " \"C\", 0,\n" oport) (display-code cstr oport) (newline oport)) (define (process-define id xlam oport) (define cstr (compile-to-string xlam)) (let ([len (string-length cstr)]) (cond [(and (eq? (car xlam) 'lambda) (> len 4) (char=? (string-ref cstr 0) #\&) (char=? (string-ref cstr 1) #\0) (char=? (string-ref cstr 2) #\{) (char=? (string-ref cstr (fx- len 1)) #\})) (newline oport) (display " \"P\", \"" oport) (display id oport) (display "\",\n" oport) (display-code (substring cstr 3 (fx- len 1)) oport) (newline oport)] [else (process-command (list 'set! id xlam) oport)]))) (define (scan-top-form x) (cond [(and (list2? x) (eq? (car x) 'load) (string? (cadr x))) (let ([iport (open-input-file (cadr x))]) (let loop ([x (read iport)]) (unless (eof-object? x) (scan-top-form x) (loop (read iport)))) (close-input-port iport))] [(pair? x) (let ([hval (transform #t (car x))]) (cond [(eq? hval 'begin) (for-each scan-top-form (cdr x))] [(eq? hval 'define-syntax) (let ([xval (transform #t (caddr x))]) (install-transformer! (cadr x) xval))] [(procedure? hval) (scan-top-form (hval x top-transformer-env))]))])) (define (process-top-form x oport) (cond [(and (list2? x) (eq? (car x) 'load) (string? (cadr x))) (let ([iport (open-input-file (cadr x))]) (let loop ([x (read iport)]) (unless (eof-object? x) (scan-top-form x) (loop (read iport)))) (close-input-port iport))] [(pair? x) (let ([hval (transform #t (car x))]) (cond [(eq? hval 'begin) (let loop ([x* (cdr x)]) (when (pair? x*) (process-top-form (car x*) oport) (loop (cdr x*))))] [(eq? hval 'define-syntax) (let ([xval (transform #t (caddr x))]) (install-transformer! (cadr x) xval) (unless (memq (cadr x) *hide-refs*) (if (symbol? (caddr x)) (process-alias (cadr x) (caddr x) oport) (process-syntax (cadr x) (caddr x) oport))))] [(eq? hval 'define) (let* ([dval (transform #f x)] [xval (caddr dval)]) (process-define (cadr dval) xval oport))] [(procedure? hval) (process-top-form (hval x top-transformer-env) oport)] [else (process-command (transform #f x) oport)]))] [else (process-command (transform #f x) oport)])) (define (path-strip-directory filename) (let loop ([l (reverse (string->list filename))] [r '()]) (cond [(null? l) (list->string r)] [(memv (car l) '(#\\ #\/ #\:)) (list->string r)] [else (loop (cdr l) (cons (car l) r))]))) (define (path-strip-extension filename) (let ([l (reverse (string->list filename))]) (let ([r (memv #\. l)]) (if r (list->string (reverse (cdr r))) filename)))) (define (module-name filename) (path-strip-extension (path-strip-directory filename))) (define (process-file fname) (define iport (open-input-file fname)) (define oport (current-output-port)) (define mname (module-name fname)) (display "/* " oport) (display mname oport) (display ".c -- generated via skint -c " oport) (display (path-strip-directory fname) oport) (display " */" oport) (newline oport) (newline oport) (display "char *" oport) (display mname oport) (display "_code[] = {" oport) (newline oport) (let loop ([x (read iport)]) (unless (eof-object? x) (process-top-form x oport) (loop (read iport)))) (display "\n 0, 0, 0\n};\n" oport) (close-input-port iport)) ;--------------------------------------------------------------------------------------------- ; Initial environment ;--------------------------------------------------------------------------------------------- ; adapter code for continuation closures produced by letcc (define continuation-adapter-code #f) ; inited via (decode "k!...") in i.c ; adapter closure for values/call-with-values pair (define callmv-adapter-closure (make-closure (decode "K5"))) (define install-global-lambdas (%prim "{ /* define install-global-lambdas */ static obj c[] = { obj_from_objptr(vmcases+6) }; $return objptr(c); }")) (install-global-lambdas) (define initialize-modules (%prim "{ /* define initialize-modules */ static obj c[] = { obj_from_objptr(vmcases+7) }; $return objptr(c); }")) (initialize-modules) ;--------------------------------------------------------------------------------------------- ; Tests ;--------------------------------------------------------------------------------------------- (define test1 '(let () (define (sort-list obj pred) (define (loop l) (if (and (pair? l) (pair? (cdr l))) (split l '() '()) l)) (define (split l one two) (if (pair? l) (split (cdr l) two (cons (car l) one)) (merge (loop one) (loop two)))) (define (merge one two) (cond [(null? one) two] [(pred (car two) (car one)) (cons (car two) (merge (cdr two) one))] [else (cons (car one) (merge (cdr one) two))])) (loop obj)) (sort-list '("one" "two" "three" "four" "five" "six" "seven" "eight" "nine" "ten" "eleven" "twelve") string ; ("eight" "eleven" "five" "four" "nine" "one" "seven" "six" "ten" "three" "twelve" "two") ; ; (evaluate test2) => ; 70 ; ; (evaluate test3) => ; 92 ; ; (evaluate test4) => ; 3628800 ; ; (evaluate test5) => ; (3628800 3628800 3628800 3628800) ; ;--------------------------------------------------------------------------------------------- ; REPL ;--------------------------------------------------------------------------------------------- (define *verbose* #f) (define *reset* #f) (define (error* msg args) (if (procedure? *reset*) (let ([p (current-error-port)]) (display msg p) (newline p) (for-each (lambda (arg) (write arg p) (newline p)) args) (*reset* #f)) (apply error (cons msg args)))) (define (run-tests) (define start (current-jiffy)) (display "Running tests ...") (newline) (write (evaluate test1)) (newline) (write (evaluate test2)) (newline) (write (evaluate test3)) (newline) (write (evaluate test4)) (newline) (write (evaluate test5)) (newline) (display "Elapsed time: ") (write (* 1000 (/ (- (current-jiffy) start) (jiffies-per-second)))) (display " ms.") (newline)) (define (repl-eval x) (letcc catch (set! *reset* catch) (let ([xexp (transform #f x)]) (when *verbose* (display "TRANSFORM =>") (newline) (write xexp) (newline)) (unless (pair? xexp) (x-error "unexpected transformed output" xexp)) (if (eq? (car xexp) 'define) (set-car! xexp 'set!)) (when *verbose* (display "COMPILE-TO-STRING =>") (newline)) (let ([cstr (compile-to-string xexp)] [start #f]) (when *verbose* (display cstr) (newline) (display "DECODE+EXECUTE =>") (newline) (set! start (current-jiffy))) (let* ([thunk (decode cstr)] [res (execute thunk)]) (unless (eq? res (void)) (write res) (newline))) (when *verbose* (display "Elapsed time: ") (write (* 1000 (/ (- (current-jiffy) start) (jiffies-per-second)))) (display " ms.") (newline)))))) (define (repl-eval-top-form x) (cond [(and (list2? x) (eq? (car x) 'load) (string? (cadr x))) (let ([iport (open-input-file (cadr x))]) (repl-from-port iport) (close-input-port iport))] [(pair? x) (let ([hval (transform #t (car x))]) (cond [(eq? hval 'begin) (let loop ([x* (cdr x)]) (when (pair? x*) (repl-eval-top-form (car x*)) (loop (cdr x*))))] [(eq? hval 'define-syntax) (let ([xval (transform #t (caddr x))]) (install-transformer! (cadr x) xval))] [(procedure? hval) (repl-eval-top-form (hval x top-transformer-env))] [else (repl-eval x)]))] [else (repl-eval x)])) (define (repl-read iport) (when (eq? iport (current-input-port)) (display "\nskint> ")) (read iport)) (define (repl-from-port iport) (let loop ([x (repl-read iport)]) (unless (eof-object? x) (repl-eval-top-form x) (loop (repl-read iport))))) (define (repl-file fname) (define iport (open-input-file fname)) (repl-from-port iport) (close-input-port iport)) (define (benchmark-file fname) (define iport (open-input-file fname)) (unless (syntax-match? '(load "libl.sf") (read iport)) (error "unexpected benchmark file format" fname)) (repl-from-port iport) (repl-eval-top-form '(main #f)) (close-input-port iport)) (define (run-repl) (repl-from-port (current-input-port))) (define (main argv) (let ([args (cdr (command-line))]) (cond [(syntax-match? '("-c" *) args) (process-file (cadr args))] [(syntax-match? '("-l" *) args) (repl-file (cadr args))] [(syntax-match? '("-b" *) args) (benchmark-file (cadr args))] [(syntax-match? '("-t") args) (run-tests)] [(syntax-match? '("-v") args) (set! *verbose* #t) (run-repl)] [else (run-repl)])))