; femtoLisp standard library ; by Jeff Bezanson (C) 2009 ; Distributed under the BSD License (define (void) #t) ; the unspecified value (define *builtins* (vector 0 0 0 0 0 0 0 0 0 0 0 0 (lambda (x y) (eq? x y)) (lambda (x y) (eqv? x y)) (lambda (x y) (equal? x y)) (lambda (x) (atom? x)) (lambda (x) (not x)) (lambda (x) (null? x)) (lambda (x) (boolean? x)) (lambda (x) (symbol? x)) (lambda (x) (number? x)) (lambda (x) (bound? x)) (lambda (x) (pair? x)) (lambda (x) (builtin? x)) (lambda (x) (vector? x)) (lambda (x) (fixnum? x)) (lambda (x) (function? x)) (lambda (x y) (cons x y)) (lambda rest rest) (lambda (x) (car x)) (lambda (x) (cdr x)) (lambda (x y) (set-car! x y)) (lambda (x y) (set-cdr! x y)) (lambda rest (apply apply rest)) (lambda rest (apply + rest)) (lambda rest (apply - rest)) (lambda rest (apply * rest)) (lambda rest (apply / rest)) (lambda rest (apply div0 rest)) (lambda (x y) (= x y)) (lambda (x y) (< x y)) (lambda (x y) (compare x y)) (lambda rest (apply vector rest)) (lambda (x y) (aref x y)) (lambda (x y z) (aset! x y z)))) (if (not (bound? '*syntax-environment*)) (define *syntax-environment* (table))) (define (set-syntax! s v) (put! *syntax-environment* s v)) (define (symbol-syntax s) (get *syntax-environment* s #f)) (define-macro (define-macro form . body) `(set-syntax! ',(car form) (lambda ,(cdr form) ,@body))) #;(define (map1 f lst acc) (cdr (prog1 acc (while (pair? lst) (begin (set! acc (cdr (set-cdr! acc (cons (f (car lst)) ())))) (set! lst (cdr lst))))))) #;(define (mapn f lsts) (if (null? (car lsts)) () (cons (apply f (map1 car lsts (list ()))) (mapn f (map1 cdr lsts (list ())))))) #;(define (map f lst . lsts) (if (null? lsts) (map1 f lst (list ())) (mapn f (cons lst lsts)))) (define-macro (letrec binds . body) `((lambda ,(map car binds) ,.(map (lambda (b) `(set! ,@b)) binds) ,@body) ,.(map (lambda (x) (void)) binds))) (define-macro (let binds . body) (let ((lname #f)) (if (symbol? binds) (begin (set! lname binds) (set! binds (car body)) (set! body (cdr body)))) (let ((thelambda `(lambda ,(map (lambda (c) (if (pair? c) (car c) c)) binds) ,@body)) (theargs (map (lambda (c) (if (pair? c) (cadr c) (void))) binds))) (cons (if lname `(letrec ((,lname ,thelambda)) ,lname) thelambda) theargs)))) (define-macro (cond . clauses) (define (cond-clauses->if lst) (if (atom? lst) #f (let ((clause (car lst))) (if (or (eq? (car clause) 'else) (eq? (car clause) #t)) (if (null? (cdr clause)) (car clause) (cons 'begin (cdr clause))) (if (null? (cdr clause)) ; test by itself (list 'or (car clause) (cond-clauses->if (cdr lst))) ; test => expression (if (eq? (cadr clause) '=>) (if (1arg-lambda? (caddr clause)) ; test => (lambda (x) ...) (let ((var (caadr (caddr clause)))) `(let ((,var ,(car clause))) (if ,var ,(cons 'begin (cddr (caddr clause))) ,(cond-clauses->if (cdr lst))))) ; test => proc (let ((b (gensym))) `(let ((,b ,(car clause))) (if ,b (,(caddr clause) ,b) ,(cond-clauses->if (cdr lst)))))) (list 'if (car clause) (cons 'begin (cdr clause)) (cond-clauses->if (cdr lst))))))))) (cond-clauses->if clauses)) (define-macro (import . rest) `(import-procedure ,@(map (lambda (x) `(quote ,x)) rest))) ; standard procedures -------------------------------------------------------- (define (member item lst) (cond ((atom? lst) #f) ((equal? (car lst) item) lst) (#t (member item (cdr lst))))) (define (memv item lst) (cond ((atom? lst) #f) ((eqv? (car lst) item) lst) (#t (memv item (cdr lst))))) (define (assoc item lst) (cond ((atom? lst) #f) ((equal? (caar lst) item) (car lst)) (#t (assoc item (cdr lst))))) (define (assv item lst) (cond ((atom? lst) #f) ((eqv? (caar lst) item) (car lst)) (#t (assv item (cdr lst))))) (define (> a b) (< b a)) (define (nan? x) (or (equal? x +nan.0) (equal? x -nan.0))) (define (<= a b) (not (or (< b a) (and (nan? a) (nan? b))))) (define (>= a b) (not (or (< a b) (and (nan? a) (nan? b))))) (define (negative? x) (< x 0)) (define (zero? x) (= x 0)) (define (positive? x) (> x 0)) (define (even? x) (= (logand x 1) 0)) (define (odd? x) (not (even? x))) (define (identity x) x) (define (1+ n) (+ n 1)) (define (1- n) (- n 1)) (define (mod0 x y) (- x (* (div0 x y) y))) (define (div x y) (+ (div0 x y) (or (and (< x 0) (or (and (< y 0) 1) -1)) 0))) (define (mod x y) (- x (* (div x y) y))) (define (random n) (if (integer? n) (mod (rand) n) (* (rand.double) n))) (define (abs x) (if (< x 0) (- x) x)) (define (max x0 . xs) (if (null? xs) x0 (foldl (lambda (a b) (if (< a b) b a)) x0 xs))) (define (min x0 . xs) (if (null? xs) x0 (foldl (lambda (a b) (if (< a b) a b)) x0 xs))) (define (char? x) (eq? (typeof x) 'wchar)) (define (array? x) (or (vector? x) (let ((t (typeof x))) (and (pair? t) (eq? (car t) 'array))))) (define (closure? x) (and (function? x) (not (builtin? x)))) (define (caar x) (car (car x))) (define (cadr x) (car (cdr x))) (define (cdar x) (cdr (car x))) (define (cddr x) (cdr (cdr x))) (define (caaar x) (car (car (car x)))) (define (caadr x) (car (car (cdr x)))) (define (cadar x) (car (cdr (car x)))) (define (caddr x) (car (cdr (cdr x)))) (define (cdaar x) (cdr (car (car x)))) (define (cdadr x) (cdr (car (cdr x)))) (define (cddar x) (cdr (cdr (car x)))) (define (cdddr x) (cdr (cdr (cdr x)))) (define (caaaar x) (car (car (car (car x))))) (define (caaadr x) (car (car (car (cdr x))))) (define (caadar x) (car (car (cdr (car x))))) (define (caaddr x) (car (car (cdr (cdr x))))) (define (cadaar x) (car (cdr (car (car x))))) (define (cadadr x) (car (cdr (car (cdr x))))) (define (caddar x) (car (cdr (cdr (car x))))) (define (cadddr x) (car (cdr (cdr (cdr x))))) (define (cdaaar x) (cdr (car (car (car x))))) (define (cdaadr x) (cdr (car (car (cdr x))))) (define (cdadar x) (cdr (car (cdr (car x))))) (define (cdaddr x) (cdr (car (cdr (cdr x))))) (define (cddaar x) (cdr (cdr (car (car x))))) (define (cddadr x) (cdr (cdr (car (cdr x))))) (define (cdddar x) (cdr (cdr (cdr (car x))))) (define (cddddr x) (cdr (cdr (cdr (cdr x))))) (let ((*values* (list '*values*))) (set! values (lambda vs (if (and (pair? vs) (null? (cdr vs))) (car vs) (cons *values* vs)))) (set! call-with-values (lambda (producer consumer) (let ((res (producer))) (if (and (pair? res) (eq? *values* (car res))) (apply consumer (cdr res)) (consumer res)))))) ; list utilities ------------------------------------------------------------- (define (every pred lst) (or (atom? lst) (and (pred (car lst)) (every pred (cdr lst))))) (define (any pred lst) (and (pair? lst) (or (pred (car lst)) (any pred (cdr lst))))) (define (list? a) (or (null? a) (and (pair? a) (list? (cdr a))))) (define (list-tail lst n) (if (<= n 0) lst (list-tail (cdr lst) (- n 1)))) (define (list-head lst n) (if (<= n 0) () (cons (car lst) (list-head (cdr lst) (- n 1))))) (define (list-ref lst n) (car (list-tail lst n))) ; bounded length test ; use this instead of (= (length lst) n), since it avoids unnecessary ; work and always terminates. (define (length= lst n) (cond ((< n 0) #f) ((= n 0) (atom? lst)) ((atom? lst) (= n 0)) (else (length= (cdr lst) (- n 1))))) (define (length> lst n) (cond ((< n 0) lst) ((= n 0) (and (pair? lst) lst)) ((atom? lst) (< n 0)) (else (length> (cdr lst) (- n 1))))) (define (last-pair l) (if (atom? (cdr l)) l (last-pair (cdr l)))) (define (lastcdr l) (if (atom? l) l (cdr (last-pair l)))) (define (to-proper l) (cond ((null? l) l) ((atom? l) (list l)) (else (cons (car l) (to-proper (cdr l)))))) (define (map! f lst) (prog1 lst (while (pair? lst) (set-car! lst (f (car lst))) (set! lst (cdr lst))))) (define (filter pred lst) (define (filter- f lst acc) (cdr (prog1 acc (while (pair? lst) (begin (if (pred (car lst)) (set! acc (cdr (set-cdr! acc (cons (car lst) ()))))) (set! lst (cdr lst))))))) (filter- pred lst (list ()))) (define (separate pred lst) (define (separate- pred lst yes no) (let ((vals (prog1 (cons yes no) (while (pair? lst) (begin (if (pred (car lst)) (set! yes (cdr (set-cdr! yes (cons (car lst) ())))) (set! no (cdr (set-cdr! no (cons (car lst) ()))))) (set! lst (cdr lst))))))) (values (cdr (car vals)) (cdr (cdr vals))))) (separate- pred lst (list ()) (list ()))) (define (count f l) (define (count- f l n) (if (null? l) n (count- f (cdr l) (if (f (car l)) (+ n 1) n)))) (count- f l 0)) (define (nestlist f zero n) (if (<= n 0) () (cons zero (nestlist f (f zero) (- n 1))))) (define (foldr f zero lst) (if (null? lst) zero (f (car lst) (foldr f zero (cdr lst))))) (define (foldl f zero lst) (if (null? lst) zero (foldl f (f (car lst) zero) (cdr lst)))) (define (reverse- zero lst) (if (null? lst) zero (reverse- (cons (car lst) zero) (cdr lst)))) (define (reverse lst) (reverse- () lst)) (define (reverse!- prev l) (while (pair? l) (set! l (prog1 (cdr l) (set-cdr! l (prog1 prev (set! prev l)))))) prev) (define (reverse! l) (reverse!- () l)) (define (copy-tree l) (if (atom? l) l (cons (copy-tree (car l)) (copy-tree (cdr l))))) (define (delete-duplicates lst) (if (length> lst 20) (let ((t (table))) (let loop ((l lst) (acc '())) (if (atom? l) (reverse! acc) (if (has? t (car l)) (loop (cdr l) acc) (begin (put! t (car l) #t) (loop (cdr l) (cons (car l) acc))))))) (if (atom? lst) lst (let ((elt (car lst)) (tail (cdr lst))) (if (member elt tail) (delete-duplicates tail) (cons elt (delete-duplicates tail))))))) ; backquote ------------------------------------------------------------------ (define (revappend l1 l2) (reverse- l2 l1)) (define (nreconc l1 l2) (reverse!- l2 l1)) (define (self-evaluating? x) (or (and (atom? x) (not (symbol? x))) (and (constant? x) (symbol? x) (eq x (top-level-value x))))) (define-macro (quasiquote x) (bq-process x 0)) (define (splice-form? x) (or (and (pair? x) (or (eq? (car x) 'unquote-splicing) (eq? (car x) 'unquote-nsplicing) (and (eq? (car x) 'unquote) (length> x 2)))) (eq? x 'unquote))) ;; bracket without splicing (define (bq-bracket1 x d) (if (and (pair? x) (eq? (car x) 'unquote)) (if (= d 0) (cadr x) (list cons ''unquote (bq-process (cdr x) (- d 1)))) (bq-process x d))) (define (bq-bracket x d) (cond ((atom? x) (list list (bq-process x d))) ((eq? (car x) 'unquote) (if (= d 0) (cons list (cdr x)) (list list (list cons ''unquote (bq-process (cdr x) (- d 1)))))) ((eq? (car x) 'unquote-splicing) (if (= d 0) (list 'copy-list (cadr x)) (list list (list list ''unquote-splicing (bq-process (cadr x) (- d 1)))))) ((eq? (car x) 'unquote-nsplicing) (if (= d 0) (cadr x) (list list (list list ''unquote-nsplicing (bq-process (cadr x) (- d 1)))))) (else (list list (bq-process x d))))) (define (bq-process x d) (cond ((symbol? x) (list 'quote x)) ((vector? x) (let ((body (bq-process (vector->list x) d))) (if (eq? (car body) list) (cons vector (cdr body)) (list apply vector body)))) ((atom? x) x) ((eq? (car x) 'quasiquote) (list list ''quasiquote (bq-process (cadr x) (+ d 1)))) ((eq? (car x) 'unquote) (if (and (= d 0) (length= x 2)) (cadr x) (list cons ''unquote (bq-process (cdr x) (- d 1))))) ((not (any splice-form? x)) (let ((lc (lastcdr x)) (forms (map (lambda (x) (bq-bracket1 x d)) x))) (if (null? lc) (cons list forms) (if (null? (cdr forms)) (list cons (car forms) (bq-process lc d)) (nconc (cons list* forms) (list (bq-process lc d))))))) (else (let loop ((p x) (q ())) (cond ((null? p) ;; proper list (cons 'nconc (reverse! q))) ((pair? p) (cond ((eq? (car p) 'unquote) ;; (... . ,x) (cons 'nconc (nreconc q (if (= d 0) (cdr p) (list (list list ''unquote) (bq-process (cdr p) (- d 1))))))) (else (loop (cdr p) (cons (bq-bracket (car p) d) q))))) (else ;; (... . x) (cons 'nconc (reverse! (cons (bq-process p d) q))))))))) ; standard macros ------------------------------------------------------------ (define (quote-value v) (if (self-evaluating? v) v (list 'quote v))) (define-macro (let* binds . body) (if (atom? binds) `((lambda () ,@body)) `((lambda (,(caar binds)) ,@(if (pair? (cdr binds)) `((let* ,(cdr binds) ,@body)) body)) ,(cadar binds)))) (define-macro (when c . body) (list 'if c (cons 'begin body) #f)) (define-macro (unless c . body) (list 'if c #f (cons 'begin body))) (define-macro (case key . clauses) (define (vals->cond key v) (cond ((eq? v 'else) 'else) ((null? v) #f) ((symbol? v) `(eq? ,key ,(quote-value v))) ((atom? v) `(eqv? ,key ,(quote-value v))) ((null? (cdr v)) `(eqv? ,key ,(quote-value (car v)))) ((every symbol? v) `(memq ,key ',v)) (else `(memv ,key ',v)))) (let ((g (gensym))) `(let ((,g ,key)) (cond ,.(map (lambda (clause) (cons (vals->cond g (car clause)) (cdr clause))) clauses))))) (define-macro (do vars test-spec . commands) (let ((loop (gensym)) (test-expr (car test-spec)) (vars (map car vars)) (inits (map cadr vars)) (steps (map (lambda (x) (if (pair? (cddr x)) (caddr x) (car x))) vars))) `(letrec ((,loop (lambda ,vars (if ,test-expr (begin ,@(cdr test-spec)) (begin ,@commands (,loop ,.steps)))))) (,loop ,.inits)))) ; SRFI 8 (define-macro (receive formals expr . body) `(call-with-values (lambda () ,expr) (lambda ,formals ,@body))) (define-macro (let-values bindings . body) (unless (pair? bindings) (error "let-values: syntax error")) (let* ((binding (car bindings)) (names (car binding)) (form (cadr binding))) (unless (null? (cddr binding)) (error "let-values: syntax error")) (unless (null? (cdr bindings)) (error "let-values: syntax error")) `(receive ,names ,form ,@body))) (define-macro (dotimes var . body) (let ((v (car var)) (cnt (cadr var))) `(for 0 (- ,cnt 1) (lambda (,v) ,@body)))) (define (map-int f n) (if (<= n 0) () (let ((first (cons (f 0) ())) (acc ())) (set! acc first) (for 1 (- n 1) (lambda (i) (begin (set-cdr! acc (cons (f i) ())) (set! acc (cdr acc))))) first))) (define (iota n) (map-int identity n)) (define (for-each f l . lsts) (define (for-each-n f lsts) (if (pair? (car lsts)) (begin (apply f (map car lsts)) (for-each-n f (map cdr lsts))))) (if (null? lsts) (while (pair? l) (begin (f (car l)) (set! l (cdr l)))) (for-each-n f (cons l lsts))) #t) (define-macro (with-bindings binds . body) (let ((vars (map car binds)) (vals (map cadr binds)) (olds (map (lambda (x) (gensym)) binds))) `(let ,(map list olds vars) ,@(map (lambda (v val) `(set! ,v ,val)) vars vals) (unwind-protect (begin ,@body) (begin ,@(map (lambda (v old) `(set! ,v ,old)) vars olds)))))) ; exceptions ----------------------------------------------------------------- (define (error . args) (raise (cons 'error args))) (define-macro (throw tag value) `(raise (list 'thrown-value ,tag ,value))) (define-macro (catch tag expr) (let ((e (gensym))) `(trycatch ,expr (lambda (,e) (if (and (pair? ,e) (eq (car ,e) 'thrown-value) (eq (cadr ,e) ,tag)) (caddr ,e) (raise ,e)))))) (define-macro (unwind-protect expr finally) (let ((e (gensym)) (thk (gensym))) `(let ((,thk (lambda () ,finally))) (prog1 (trycatch ,expr (lambda (,e) (begin (,thk) (raise ,e)))) (,thk))))) ; debugging utilities -------------------------------------------------------- (define-macro (assert expr) `(if ,expr #t (raise '(assert-failed ,expr)))) (define traced? (letrec ((sample-traced-lambda (lambda args (begin (writeln (cons 'x args)) (apply #.apply args))))) (lambda (f) (and (closure? f) (equal? (function:code f) (function:code sample-traced-lambda)))))) (define (trace sym) (let* ((func (top-level-value sym)) (args (gensym))) (if (not (traced? func)) (set-top-level-value! sym (eval `(lambda ,args (begin (writeln (cons ',sym ,args)) (apply ',func ,args))))))) 'ok) (define (untrace sym) (let ((func (top-level-value sym))) (if (traced? func) (set-top-level-value! sym (aref (function:vals func) 2))))) (define-macro (time expr) (let ((t0 (gensym))) `(let ((,t0 (time.now))) (prog1 ,expr (display "Elapsed time: ") (display (- (time.now) ,t0)) (display " seconds") (newline))))) ; text I/O ------------------------------------------------------------------- (define (io.readline s) (io.readuntil s #\linefeed)) ; call f on a stream until the stream runs out of data (define (read-all-of f s) (let loop ((lines ()) (curr (f s))) (if (io.eof? s) (reverse! lines) (loop (cons curr lines) (f s))))) (define (io.readlines s) (read-all-of io.readline s)) (define (read-all s) (read-all-of read s)) (define (io.readall s) (let ((b (buffer))) (io.copy b s) (let ((str (io.tostring! b))) (if (and (equal? str "") (io.eof? s)) (eof-object) str)))) (define-macro (with-output-to stream . body) `(with-bindings ((*output-stream* ,stream)) ,@body)) (define-macro (with-input-from stream . body) `(with-bindings ((*input-stream* ,stream)) ,@body)) ; vector functions ----------------------------------------------------------- (define (list->vector l) (apply vector l)) (define (vector->list v) (let ((n (length v)) (l ())) (for 1 n (lambda (i) (set! l (cons (aref v (- n i)) l)))) l)) (define (vector.map f v) (let* ((n (length v)) (nv (vector.alloc n))) (for 0 (- n 1) (lambda (i) (aset! nv i (f (aref v i))))) nv)) ; table functions ------------------------------------------------------------ (define (table.pairs t) (table.foldl (lambda (k v z) (cons (cons k v) z)) () t)) (define (table.keys t) (table.foldl (lambda (k v z) (cons k z)) () t)) (define (table.values t) (table.foldl (lambda (k v z) (cons v z)) () t)) (define (table.clone t) (let ((nt (table))) (table.foldl (lambda (k v z) (put! nt k v)) () t) nt)) (define (table.invert t) (let ((nt (table))) (table.foldl (lambda (k v z) (put! nt v k)) () t) nt)) (define (table.foreach f t) (table.foldl (lambda (k v z) (begin (f k v) #t)) () t)) ; string functions ----------------------------------------------------------- (define (string.tail s n) (string.sub s (string.inc s 0 n))) (define *whitespace* (string.encode #array(wchar 9 10 11 12 13 32 133 160 5760 6158 8192 8193 8194 8195 8196 8197 8198 8199 8200 8201 8202 8232 8233 8239 8287 12288))) (define (string.trim s at-start at-end) (define (trim-start s chars i L) (if (and (< i L) (string.find chars (string.char s i))) (trim-start s chars (string.inc s i) L) i)) (define (trim-end s chars i) (if (and (> i 0) (string.find chars (string.char s (string.dec s i)))) (trim-end s chars (string.dec s i)) i)) (let ((L (length s))) (string.sub s (trim-start s at-start 0 L) (trim-end s at-end L)))) (define (string.map f s) (let ((b (buffer)) (n (length s))) (let ((i 0)) (while (< i n) (begin (io.putc b (f (string.char s i))) (set! i (string.inc s i))))) (io.tostring! b))) (define (string.rep s k) (cond ((< k 4) (cond ((<= k 0) "") ((= k 1) (string s)) ((= k 2) (string s s)) (else (string s s s)))) ((odd? k) (string s (string.rep s (- k 1)))) (else (string.rep (string s s) (/ k 2))))) (define (string.lpad s n c) (string (string.rep c (- n (string.count s))) s)) (define (string.rpad s n c) (string s (string.rep c (- n (string.count s))))) (define (write-to-string v) (let ((b (buffer))) (write v b) (io.tostring! b))) (define print-to-string write-to-string) ;; TODO: remove (define (string.join strlist sep) (if (null? strlist) "" (let ((b (buffer))) (io.write b (car strlist)) (for-each (lambda (s) (begin (io.write b sep) (io.write b s))) (cdr strlist)) (io.tostring! b)))) ; toplevel ------------------------------------------------------------------- (define (macrocall? e) (and (symbol? (car e)) (symbol-syntax (car e)))) (define (macroexpand-1 e) (if (atom? e) e (let ((f (macrocall? e))) (if f (apply f (cdr e)) e)))) (define (expand e) ; symbol resolves to toplevel; i.e. has no shadowing definition (define (top? s env) (not (or (bound? s) (assq s env)))) (define (splice-begin body) (cond ((atom? body) body) ((equal? body '((begin))) body) ((and (pair? (car body)) (eq? (caar body) 'begin)) (append (splice-begin (cdar body)) (splice-begin (cdr body)))) (else (cons (car body) (splice-begin (cdr body)))))) (define *expanded* (list '*expanded*)) (define (expand-body body env) (if (atom? body) body (let* ((body (if (top? 'begin env) (splice-begin body) body)) (def? (top? 'define env)) (dvars (if def? (get-defined-vars body) ())) (env (nconc (map list dvars) env))) (if (not def?) (map (lambda (x) (expand-in x env)) body) (let* ((ex-nondefs ; expand non-definitions (let loop ((body body)) (cond ((atom? body) body) ((and (pair? (car body)) (eq? 'define (caar body))) (cons (car body) (loop (cdr body)))) (else (let ((form (expand-in (car body) env))) (set! env (nconc (map list (get-defined-vars form)) env)) (cons (cons *expanded* form) (loop (cdr body)))))))) (body ex-nondefs)) (while (pair? body) ; now expand deferred definitions (if (not (eq? *expanded* (caar body))) (set-car! body (expand-in (car body) env)) (set-car! body (cdar body))) (set! body (cdr body))) ex-nondefs))))) (define (expand-lambda-list l env) (if (atom? l) l (cons (if (and (pair? (car l)) (pair? (cdr (car l)))) (list (caar l) (expand-in (cadar l) env)) (car l)) (expand-lambda-list (cdr l) env)))) (define (l-vars l) (cond ((atom? l) (list l)) ((pair? (car l)) (cons (caar l) (l-vars (cdr l)))) (else (cons (car l) (l-vars (cdr l)))))) (define (expand-lambda e env) (let ((formals (cadr e)) (name (lastcdr e)) (body (cddr e)) (vars (l-vars (cadr e)))) (let ((env (nconc (map list vars) env))) `(lambda ,(expand-lambda-list formals env) ,.(expand-body body env) . ,name)))) (define (expand-define e env) (if (or (null? (cdr e)) (atom? (cadr e))) (if (null? (cddr e)) e `(define ,(cadr e) ,(expand-in (caddr e) env))) (let ((formals (cdadr e)) (name (caadr e)) (body (cddr e)) (vars (l-vars (cdadr e)))) (let ((env (nconc (map list vars) env))) `(define ,(cons name (expand-lambda-list formals env)) ,.(expand-body body env)))))) (define (expand-let-syntax e env) (let ((binds (cadr e))) (cons 'begin (expand-body (cddr e) (nconc (map (lambda (bind) (list (car bind) ((compile-thunk (expand-in (cadr bind) env))) env)) binds) env))))) ; given let-syntax definition environment (menv) and environment ; at the point of the macro use (lenv), return the environment to ; expand the macro use in. TODO (define (local-expansion-env menv lenv) menv) (define (expand-in e env) (if (atom? e) e (let* ((head (car e)) (bnd (assq head env)) (default (lambda () (let loop ((e e)) (if (atom? e) e (cons (if (atom? (car e)) (car e) (expand-in (car e) env)) (loop (cdr e)))))))) (cond ((and bnd (pair? (cdr bnd))) ; local macro (expand-in (apply (cadr bnd) (cdr e)) (local-expansion-env (caddr bnd) env))) ((or bnd ; bound lexical or toplevel var (not (symbol? head)) (bound? head)) (default)) ((macrocall? e) => (lambda (f) (expand-in (apply f (cdr e)) env))) ((eq? head 'quote) e) ((eq? head 'lambda) (expand-lambda e env)) ((eq? head 'define) (expand-define e env)) ((eq? head 'let-syntax) (expand-let-syntax e env)) (else (default)))))) (expand-in e ())) (define (eval x) ((compile-thunk (expand x)))) (define (load-process x) (eval x)) (define (load filename) (let ((F (file filename :read))) (trycatch (let next (prev E v) (if (not (io.eof? F)) (next (read F) prev (load-process E)) (begin (io.close F) ; evaluate last form in almost-tail position (load-process E)))) (lambda (e) (begin (io.close F) (raise `(load-error ,filename ,e))))))) (define (sgr . ns) (let ((out (open-output-string))) (display "\x1b[" out) (unless (null? ns) (let loop ((ns ns)) (display (car ns) out) (let ((ns (cdr ns))) (unless (null? ns) (display #\; out) (loop ns))))) (display "m" out) (get-output-string out))) (define bold 1) (define (fg n) (+ 30 n)) (define (bg n) (+ 40 n)) (define black 0) (define red 1) (define green 2) (define yellow 3) (define blue 4) (define magenta 5) (define cyan 6) (define white 7) (define default-color 8) (define history '()) (define (history-push form values) (set! history (cons (cons form values) history))) (define (history-get x proc) (cond ((pair? x) (proc x)) ((eqv? #f x) (history-get 0 proc)) ((and (integer? x) (>= x 0) (< x (length history))) (history-get (list-ref history x) proc)) (else #f))) (define (history-form (x #f)) (history-get x car)) (define (history-value (x #f)) (history-get x cadr)) (define (history-values (x #f)) (history-get x cdr)) (define (history-exception (x #f)) #f) (define (repl) (define (prompt) (display (string-append (sgr bold (fg cyan)) "up>" (sgr) " ")) (io.flush *output-stream*) (let ((form (trycatch (read) (lambda (e) (io.discardbuffer *input-stream*) (raise e))))) (and (not (io.eof? *input-stream*)) (let ((value #f) (exception #f)) (trycatch (begin (set! value (load-process form)) (writeln value) (set! that value)) (lambda (e) (set! exception e) (top-level-exception-handler e))) (history-push form (list value)) (when (or (eqv? 'help form) (eqv? 'exit form)) (newline) (displayln "Type (help) for help or (exit) to exit.")) #t)))) (define (reploop) (when (trycatch (prompt) (lambda (e) (top-level-exception-handler e) #t)) (reploop))) (reploop) (newline)) (define (top-level-exception-handler e) (with-output-to *stderr* (print-exception e) (print-stack-trace (stacktrace)))) (define (print-stack-trace st) (define (find-in-f f tgt path) (let ((path (cons (function:name f) path))) (if (eq? (function:code f) (function:code tgt)) (throw 'ffound path) (let ((v (function:vals f))) (for 0 (1- (length v)) (lambda (i) (if (closure? (aref v i)) (find-in-f (aref v i) tgt path)))))))) (define (fn-name f e) (let ((p (catch 'ffound (begin (for-each (lambda (topfun) (find-in-f topfun f ())) e) #f)))) (if p (symbol (string.join (map string (reverse! p)) "/")) 'lambda))) (let ((st (reverse! (list-tail st (if *interactive* 5 4)))) (e (filter closure? (map (lambda (s) (and (bound? s) (top-level-value s))) (environment)))) (n 0)) (for-each (lambda (f) (display "#") (display n) (display " ") (writeln (cons (fn-name (aref f 0) e) (cdr (vector->list f)))) (set! n (+ n 1))) st))) (define (print-exception e) (cond ((and (pair? e) (eq? (car e) 'type-error) (length= e 4)) (display "type error: ") (display (cadr e)) (display ": expected ") (display (caddr e)) (display ", got ") (write (cadddr e))) ((and (pair? e) (eq? (car e) 'bounds-error) (length= e 4)) (display (cadr e)) (display ": index ") (display (cadddr e)) (display " out of bounds for ") (write (caddr e))) ((and (pair? e) (eq? (car e) 'unbound-error) (pair? (cdr e))) (display "eval: variable ") (display (cadr e)) (display " has no value")) ((and (pair? e) (eq? (car e) 'error)) (display "error: ") (for-each display (cdr e))) ((and (pair? e) (eq? (car e) 'load-error)) (print-exception (caddr e)) (display "in file ") (display (cadr e))) ((and (list? e) (length= e 2)) (write (car e)) (display ": ") (let ((msg (cadr e))) ((if (or (string? msg) (symbol? msg)) display write) msg))) (else (display "*** Unhandled exception: ") (write e))) (newline)) (define (simple-sort l) (if (or (null? l) (null? (cdr l))) l (let ((piv (car l))) (receive (less grtr) (separate (lambda (x) (< x piv)) (cdr l)) (nconc (simple-sort less) (list piv) (simple-sort grtr)))))) (define (sort l (less? <) (key identity)) (let sort ((l l)) (if (or (null? l) (null? (cdr l))) l (let ((piv (car l))) (receive (less grtr) (separate (lambda (x) (less? (key x) (key piv))) (cdr l)) (nconc (sort less) (list piv) (sort grtr))))))) (define (apropos-list key) (let ((key (string-downcase (cond ((string? key) key) ((symbol? key) (symbol->string key)) (else (error "Please give a string or symbol key")))))) (if (= 0 (string-length key)) '() (sort (filter (λ (sym) (string.find (string-downcase (symbol->string sym)) key)) (environment)) stringstring)))) (define (apropos . args) (let ((syms (apply apropos-list args))) (and (not (null? syms)) (for-each (λ (sym) (displayln (string-append (let ((val (symbol-value sym))) (if (procedure? val) "procedure" "variable ")) " " (symbol->string sym)))) syms) #t))) (define-macro (help . args) `(apply help* ',args)) (define (display-banner) (displayln (string-append (sgr (fg cyan)) "Up Scheme" (sgr)))) (define (system-image->buffer) (let ((out (buffer)) (excludes '(*linefeed* *directory-separator* *argv* that *print-pretty* *print-width* *print-readably* *print-level* *print-length* *os-name*))) (with-bindings ((*print-pretty* #t) (*print-readably* #t)) (let ((syms (filter (lambda (s) (and (bound? s) (not (constant? s)) (or (not (builtin? (top-level-value s))) (not (equal? (string s) ; alias of builtin (string (top-level-value s))))) (not (memq s excludes)) (not (iostream? (top-level-value s))))) (simple-sort (environment))))) (write (apply nconc (map list syms (map top-level-value syms))) out) (io.seek out 0) out)))) ; initialize globals that need to be set at load time (define (__init_globals) (if (eq? *os-name* 'windows) (begin (set! *directory-separator* "\\") (set! *linefeed* "\r\n")) (begin (set! *directory-separator* "/") (set! *linefeed* "\n"))) (set! *output-stream* *stdout*) (set! *input-stream* *stdin*) (set! *error-stream* *stderr*)) (define (__script fname) (trycatch (load fname) (lambda (e) (begin (top-level-exception-handler e) (exit 1))))) (define (__start argv) (__init_globals) (if (pair? (cdr argv)) (begin (set! *argv* (cdr argv)) (set! *interactive* #f) (__script (cadr argv))) (begin (set! *argv* argv) (set! *interactive* #t) (display-banner) (repl))) (exit 0))