femtolisp/femtolisp/system.lsp

585 lines
16 KiB
Plaintext
Raw Normal View History

; -*- scheme -*-
2008-06-30 21:54:22 -04:00
; femtoLisp standard library
; by Jeff Bezanson (C) 2009
; Distributed under the BSD License
2008-06-30 21:54:22 -04:00
(set-constant! 'eq eq?)
(set-constant! 'eqv eqv?)
(set-constant! 'equal equal?)
(set-constant! 'rplaca set-car!)
(set-constant! 'rplacd set-cdr!)
(set-constant! 'char? (lambda (x) (eq? (typeof x) 'wchar)))
2008-06-30 21:54:22 -04:00
; convert a sequence of body statements to a single expression.
; this allows define, defun, defmacro, let, etc. to contain multiple
; body expressions as in Common Lisp.
(set! f-body (lambda (e)
2009-02-08 22:22:31 -05:00
(cond ((atom? e) #f)
2008-06-30 21:54:22 -04:00
((eq (cdr e) ()) (car e))
(#t (cons 'begin e)))))
2008-06-30 21:54:22 -04:00
(set-syntax! 'define-macro
(lambda (form . body)
(list 'set-syntax! (list 'quote (car form))
(list 'lambda (cdr form) (f-body body)))))
(define-macro (define form . body)
(if (symbol? form)
(list 'set! form (car body))
(list 'set! (car form) (list 'lambda (cdr form) (f-body body)))))
2008-06-30 21:54:22 -04:00
(define *output-stream* *stdout*)
(define *input-stream* *stdin*)
(define (print . args)
(apply io.print (cons *output-stream* args)))
(define (princ . args)
(apply io.princ (cons *output-stream* args)))
(define (set s v) (eval (list 'set! s (list 'quote v))))
2008-06-30 21:54:22 -04:00
(define (map f lst)
(if (atom? lst) lst
(cons (f (car lst)) (map f (cdr lst)))))
2008-06-30 21:54:22 -04:00
(define-macro (label name fn)
(list (list 'lambda (list name) (list 'set! name fn)) #f))
(define-macro (let binds . body)
2009-02-05 22:41:24 -05:00
((lambda (lname)
(begin
(if (symbol? binds)
(begin (set! lname binds)
(set! binds (car body))
(set! body (cdr body))))
((lambda (thelambda theargs)
(cons (if lname
(list 'label lname thelambda)
thelambda)
theargs))
(list 'lambda
(map (lambda (c) (if (pair? c) (car c) c)) binds)
(f-body body))
(map (lambda (c) (if (pair? c) (cadr c) #f)) binds))))
#f))
2008-06-30 21:54:22 -04:00
(define (nconc . lsts)
(cond ((null? lsts) ())
((null? (cdr lsts)) (car lsts))
((null? (car lsts)) (apply nconc (cdr lsts)))
(#t (prog1 (car lsts)
(rplacd (last (car lsts))
(apply nconc (cdr lsts)))))))
2008-06-30 21:54:22 -04:00
(define (append . lsts)
(cond ((null? lsts) ())
((null? (cdr lsts)) (car lsts))
(#t ((label append2 (lambda (l d)
(if (null? l) d
(cons (car l)
(append2 (cdr l) d)))))
(car lsts) (apply append (cdr lsts))))))
2008-06-30 21:54:22 -04:00
(define (member item lst)
(cond ((atom? lst) #f)
((equal (car lst) item) lst)
(#t (member item (cdr lst)))))
(define (memq item lst)
(cond ((atom? lst) #f)
((eq (car lst) item) lst)
(#t (memq 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 (macrocall? e) (and (symbol? (car e))
(symbol-syntax (car e))))
(define (function? x)
(or (builtin? x)
(and (pair? x) (eq (car x) 'lambda))))
(define procedure? function?)
2008-06-30 21:54:22 -04:00
(define (macroexpand-1 e)
(if (atom? e) e
(let ((f (macrocall? e)))
(if f (apply f (cdr e))
e))))
2008-06-30 21:54:22 -04:00
; convert to proper list, i.e. remove "dots", and append
(define (append.2 l tail)
(cond ((null? l) tail)
((atom? l) (cons l tail))
(#t (cons (car l) (append.2 (cdr l) tail)))))
2008-06-30 21:54:22 -04:00
(define (cadr x) (car (cdr x)))
;(set! *special-forms* '(quote cond if and or while lambda trycatch
; set! begin))
2008-07-11 22:58:55 -04:00
(define (macroexpand e)
2008-06-30 21:54:22 -04:00
((label mexpand
(lambda (e env f)
(begin
(while (and (pair? e)
2008-06-30 21:54:22 -04:00
(not (member (car e) env))
(set! f (macrocall? e)))
(set! e (apply f (cdr e))))
(cond ((and (pair? e)
2008-07-11 22:58:55 -04:00
(not (eq (car e) 'quote)))
(let ((newenv
(if (and (eq (car e) 'lambda)
(pair? (cdr e)))
2008-07-11 22:58:55 -04:00
(append.2 (cadr e) env)
env)))
(map (lambda (x) (mexpand x newenv ())) e)))
;((and (symbol? e) (constant? e)) (eval e))
;((and (symbol? e)
2008-07-11 22:58:55 -04:00
; (not (member e *special-forms*))
; (not (member e env))) (cons '%top e))
(#t e)))))
e () ()))
(define-macro (define form . body)
(if (symbol? form)
(list 'set! form (car body))
(list 'set! (car form)
(macroexpand (list 'lambda (cdr form) (f-body body))))))
(define-macro (define-macro form . body)
(list 'set-syntax! (list 'quote (car form))
(macroexpand (list 'lambda (cdr form) (f-body body)))))
(define macroexpand (macroexpand macroexpand))
(define (delete-duplicates lst)
(if (atom? lst)
lst
(let ((elt (car lst))
(tail (cdr lst)))
(if (member elt tail)
(delete-duplicates tail)
(cons elt
(delete-duplicates tail))))))
(define (get-defined-vars- expr)
(cond ((atom? expr) ())
((and (eq? (car expr) 'define)
(pair? (cdr expr)))
(or (and (symbol? (cadr expr))
(list (cadr expr)))
(and (pair? (cadr expr))
(symbol? (caadr expr))
(list (caadr expr)))
()))
((eq? (car expr) 'begin)
(apply append (map get-defined-vars- (cdr expr))))
(else ())))
(define (get-defined-vars expr)
(delete-duplicates (get-defined-vars- expr)))
; redefine f-body to support internal defines
(define f-body- f-body)
(define (f-body e)
((lambda (B)
((lambda (V)
(if (null? V)
B
(cons (list 'lambda V B) (map (lambda (x) #f) V))))
(get-defined-vars B)))
(f-body- e)))
2009-02-05 22:41:24 -05:00
(define = eqv)
(define eql eqv)
2008-06-30 21:54:22 -04:00
(define (/= a b) (not (equal a b)))
(define != /=)
(define (> a b) (< b a))
(define (<= a b) (not (< b a)))
(define (>= a b) (not (< a b)))
(define (1+ n) (+ n 1))
(define (1- n) (- n 1))
(define (mod x y) (- x (* (/ x y) y)))
(define remainder mod)
2008-06-30 21:54:22 -04:00
(define (abs x) (if (< x 0) (- x) x))
(define (identity x) x)
(define K prog1) ; K combinator ;)
(define begin0 prog1)
2008-06-30 21:54:22 -04:00
(define (caar x) (car (car 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 (cadddr x) (car (cdr (cdr (cdr x)))))
2008-06-30 21:54:22 -04:00
(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 (every pred lst)
(or (atom? lst)
2008-06-30 21:54:22 -04:00
(and (pred (car lst))
(every pred (cdr lst)))))
(define (any pred lst)
(and (pair? lst)
2008-06-30 21:54:22 -04:00
(or (pred (car lst))
(any pred (cdr lst)))))
(define (listp a) (or (null? a) (pair? a)))
(define (list? a) (or (null? a) (and (pair? a) (list? (cdr a)))))
2008-06-30 21:54:22 -04:00
(define (nthcdr lst n)
2008-06-30 21:54:22 -04:00
(if (<= n 0) lst
(nthcdr (cdr lst) (- n 1))))
(define list-tail nthcdr)
2008-06-30 21:54:22 -04:00
(define (list-ref lst n)
(car (nthcdr lst n)))
2008-06-30 21:54:22 -04:00
(define (list* . l)
(if (atom? (cdr l))
2008-06-30 21:54:22 -04:00
(car l)
(cons (car l) (apply list* (cdr l)))))
2008-06-30 21:54:22 -04:00
(define (nlist* . l)
(if (atom? (cdr l))
2008-06-30 21:54:22 -04:00
(car l)
(rplacd l (apply nlist* (cdr l)))))
2008-06-30 21:54:22 -04:00
(define (lastcdr l)
(if (atom? l) l
(lastcdr (cdr l))))
2008-06-30 21:54:22 -04:00
(define (last l)
(cond ((atom? l) l)
((atom? (cdr l)) l)
(#t (last (cdr l)))))
(define last-pair last)
2008-06-30 21:54:22 -04:00
(define (map! f lst)
2008-06-30 21:54:22 -04:00
(prog1 lst
(while (pair? lst)
(rplaca lst (f (car lst)))
(set! lst (cdr lst)))))
2008-06-30 21:54:22 -04:00
(define (mapcar f . lsts)
2008-06-30 21:54:22 -04:00
((label mapcar-
(lambda (lsts)
(cond ((null? lsts) (f))
((atom? (car lsts)) (car lsts))
(#t (cons (apply f (map car lsts))
(mapcar- (map cdr lsts)))))))
2008-06-30 21:54:22 -04:00
lsts))
(define (transpose M) (apply mapcar (cons list M)))
2008-06-30 21:54:22 -04:00
(define (filter pred lst) (filter- pred lst ()))
(define (filter- pred lst accum)
(cond ((null? lst) accum)
((pred (car lst))
(filter- pred (cdr lst) (cons (car lst) accum)))
(#t
(filter- pred (cdr lst) accum))))
(define (separate pred lst) (separate- pred lst () ()))
(define (separate- pred lst yes no)
(cond ((null? lst) (cons yes no))
((pred (car lst))
(separate- pred (cdr lst) (cons (car lst) yes) no))
(#t
(separate- pred (cdr lst) yes (cons (car lst) no)))))
2008-06-30 21:54:22 -04:00
(define (foldr f zero lst)
(if (null? lst) zero
2008-06-30 21:54:22 -04:00
(f (car lst) (foldr f zero (cdr lst)))))
(define (foldl f zero lst)
(if (null? lst) zero
2008-06-30 21:54:22 -04:00
(foldl f (f (car lst) zero) (cdr lst))))
(define (reverse lst) (foldl cons () lst))
2008-06-30 21:54:22 -04:00
(define (copy-list l)
(if (atom? l) l
2008-06-30 21:54:22 -04:00
(cons (car l)
(copy-list (cdr l)))))
(define (copy-tree l)
(if (atom? l) l
2008-06-30 21:54:22 -04:00
(cons (copy-tree (car l))
(copy-tree (cdr l)))))
(define (nreverse l)
(let ((prev ()))
(while (pair? l)
(set! l (prog1 (cdr l)
(rplacd l (prog1 prev
(set! prev l))))))
2008-06-30 21:54:22 -04:00
prev))
(define-macro (let* binds . body)
2008-06-30 21:54:22 -04:00
(cons (list 'lambda (map car binds)
(f-body
(nconc (map (lambda (b) (cons 'set! b)) binds)
body)))
(map (lambda (x) #f) binds)))
(set-syntax! 'letrec (symbol-syntax 'let*))
2008-06-30 21:54:22 -04:00
(define-macro (when c . body) (list 'if c (f-body body) #f))
(define-macro (unless c . body) (list 'if c #f (f-body body)))
2008-06-30 21:54:22 -04:00
(define-macro (dotimes var . body)
2008-06-30 21:54:22 -04:00
(let ((v (car var))
(cnt (cadr var)))
`(for 0 (- ,cnt 1)
(lambda (,v) ,(f-body body)))))
2008-06-30 21:54:22 -04:00
(define (map-int f n)
2008-06-30 21:54:22 -04:00
(if (<= n 0)
()
(let ((first (cons (f 0) ()))
(acc ()))
(set! acc first)
(for 1 (- n 1)
(lambda (i)
(begin (rplacd acc (cons (f i) ()))
(set! acc (cdr acc)))))
first)))
2008-06-30 21:54:22 -04:00
(define (iota n) (map-int identity n))
(define ι iota)
2008-06-30 21:54:22 -04:00
(define (error . args) (raise (cons 'error args)))
2008-06-30 21:54:22 -04:00
(define-macro (throw tag value) `(raise (list 'thrown-value ,tag ,value)))
(define-macro (catch tag expr)
2008-06-30 21:54:22 -04:00
(let ((e (gensym)))
`(trycatch ,expr
(lambda (,e) (if (and (pair? ,e)
2008-06-30 21:54:22 -04:00
(eq (car ,e) 'thrown-value)
(eq (cadr ,e) ,tag))
(caddr ,e)
(raise ,e))))))
2008-06-30 21:54:22 -04:00
(define-macro (unwind-protect expr finally)
2008-06-30 21:54:22 -04:00
(let ((e (gensym)))
`(prog1 (trycatch ,expr
(lambda (,e) (begin ,finally (raise ,e))))
,finally)))
2008-06-30 21:54:22 -04:00
(define (revappend l1 l2) (nconc (reverse l1) l2))
(define (nreconc l1 l2) (nconc (nreverse l1) l2))
2008-06-30 21:54:22 -04:00
(define (list-to-vector l) (apply vector l))
(define (vector-to-list v)
(let ((n (length v))
(l ()))
(for 1 n
(lambda (i)
(set! l (cons (aref v (- n i)) l))))
2008-06-30 21:54:22 -04:00
l))
(define (self-evaluating? x)
(or (and (atom? x)
(not (symbol? x)))
(and (constant? x)
(eq x (eval x)))))
2008-06-30 21:54:22 -04:00
; backquote
(define-macro (backquote x) (bq-process x))
2008-06-30 21:54:22 -04:00
(define (splice-form? x)
(or (and (pair? x) (or (eq (car x) '*comma-at*)
2008-06-30 21:54:22 -04:00
(eq (car x) '*comma-dot*)))
(eq x '*comma*)))
(define (bq-process x)
(cond ((self-evaluating? x)
(if (vector? x)
2008-06-30 21:54:22 -04:00
(let ((body (bq-process (vector-to-list x))))
(if (eq (car body) 'list)
(cons vector (cdr body))
(list apply vector body)))
x))
((atom? x) (list 'quote x))
2008-06-30 21:54:22 -04:00
((eq (car x) 'backquote) (bq-process (bq-process (cadr x))))
((eq (car x) '*comma*) (cadr x))
((not (any splice-form? x))
2008-06-30 21:54:22 -04:00
(let ((lc (lastcdr x))
(forms (map bq-bracket1 x)))
(if (null? lc)
2008-06-30 21:54:22 -04:00
(cons 'list forms)
(nconc (cons 'nlist* forms) (list (bq-process lc))))))
(#t (let ((p x) (q ()))
(while (and (pair? p)
(not (eq (car p) '*comma*)))
(set! q (cons (bq-bracket (car p)) q))
(set! p (cdr p)))
(let ((forms
(cond ((pair? p) (nreconc q (list (cadr p))))
((null? p) (nreverse q))
(#t (nreconc q (list (bq-process p)))))))
(if (null? (cdr forms))
(car forms)
(cons 'nconc forms)))))))
2008-06-30 21:54:22 -04:00
(define (bq-bracket x)
(cond ((atom? x) (list list (bq-process x)))
((eq (car x) '*comma*) (list list (cadr x)))
2008-06-30 21:54:22 -04:00
((eq (car x) '*comma-at*) (list 'copy-list (cadr x)))
((eq (car x) '*comma-dot*) (cadr x))
(#t (list list (bq-process x)))))
2008-06-30 21:54:22 -04:00
; bracket without splicing
(define (bq-bracket1 x)
(if (and (pair? x) (eq (car x) '*comma*))
2008-06-30 21:54:22 -04:00
(cadr x)
(bq-process x)))
2008-06-30 21:54:22 -04:00
(define-macro (assert expr) `(if ,expr #t (raise '(assert-failed ,expr))))
2008-06-30 21:54:22 -04:00
(define-macro (time expr)
2008-06-30 21:54:22 -04:00
(let ((t0 (gensym)))
`(let ((,t0 (time.now)))
(prog1
,expr
(princ "Elapsed time: " (- (time.now) ,t0) " seconds\n")))))
(define (display x) (princ x) #t)
(define (vu8 . elts) (apply array (cons 'uint8 elts)))
(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))
(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))
2009-02-05 22:41:24 -05:00
(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 (load filename)
(let ((F (file filename :read)))
(trycatch
(prog1
(let next (E v)
(if (not (io.eof? F))
(next (read F)
(eval E))
v))
(io.close F))
(lambda (e)
(begin
(io.close F)
(raise `(load-error ,filename ,e)))))))
(define (string.tail s n)
(string.sub s (string.inc s 0 n) (sizeof s)))
(define *banner* (string.tail "
; _
; |_ _ _ |_ _ | . _ _
; | (-||||_(_)|__|_)|_)
;-------------------|----------------------------------------------------------
" 1))
(define (repl)
(define (prompt)
(princ "> ") (io.flush *output-stream*)
(let ((v (trycatch (read)
(lambda (e) (begin (io.discardbuffer *input-stream*)
(raise e))))))
(and (not (io.eof? *input-stream*))
(let ((V (eval v)))
(print V)
(set! that V)
#t))))
(define (reploop)
(when (trycatch (and (prompt) (princ "\n"))
print-exception)
(begin (princ "\n")
(reploop))))
(reploop)
(princ "\n"))
(define (print-exception e)
(cond ((and (pair? e)
(eq? (car e) 'type-error)
(= (length e) 4))
(io.princ *stderr* "type-error: ")
(io.print *stderr* (cadr e))
(io.princ *stderr* ": expected ")
(io.print *stderr* (caddr e))
(io.princ *stderr* ", got ")
(io.print *stderr* (cadddr e)))
((and (pair? e)
(eq? (car e) 'unbound-error)
(pair? (cdr e)))
(io.princ *stderr*
"unbound-error: eval: variable " (cadr e)
" has no value"))
((and (pair? e)
(eq? (car e) 'error))
(io.princ *stderr* "error: ")
(apply io.princ (cons *stderr* (cdr e))))
((and (pair? e)
(eq? (car e) 'load-error))
(print-exception (caddr e))
(io.princ *stderr* "in file " (cadr e)))
((and (list? e)
(= (length e) 2))
(io.princ *stderr* (car e) ": " (cadr e)))
(else (io.princ *stderr* "*** Unhandled exception: ")
(io.print *stderr* e)))
(io.princ *stderr* "\n")
#t)
(define (__script fname)
(trycatch (load fname)
(lambda (e) (begin (print-exception e)
(exit 1)))))
(define (__start . argv)
(if (pair? (cdr argv))
(begin (set! *argv* (cdr argv))
(__script (cadr argv)))
(begin (set! *argv* argv)
(princ *banner*)
(repl)))
(exit 0))