ref: 0c9010a117357e14ef92c79e9b892af9202327be
parent: 1f81d56b897746fc8af241f0df38495718b798e5
author: JeffBezanson <[email protected]>
date: Mon Jun 30 21:54:22 EDT 2008
import femtolisp source
--- /dev/null
+++ b/femtolisp/100x100.lsp
@@ -1,0 +1,1 @@
+'#0=(#198=(#197=(#196=(#195=(#194=(#193=(#192=(#191=(#190=(#189=(#188=(#187=(#186=(#185=(#184=(#183=(#182=(#181=(#180=(#179=(#178=(#177=(#176=(#175=(#174=(#173=(#172=(#171=(#170=(#169=(#168=(#167=(#166=(#165=(#164=(#163=(#162=(#161=(#160=(#159=(#158=(#157=(#156=(#155=(#154=(#153=(#152=(#151=(#150=(#149=(#148=(#147=(#146=(#145=(#144=(#143=(#142=(#141=(#140=(#139=(#138=(#137=(#136=(#135=(#134=(#133=(#132=(#131=(#130=(#129=(#128=(#127=(#126=(#125=(#124=(#123=(#122=(#121=(#120=(#119=(#118=(#117=(#116=(#115=(#114=(#113=(#112=(#111=(#110=(#109=(#108=(#107=(#106=(#105=(#104=(#103=(#102=(#101=(#100=(#0# . #1=(#9999=(#9998=(#9997=(#9996=(#9995=(#9994=(#9993=(#9992=(#9991=(#9990=(#9989=(#9988=(#9987=(#9986=(#9985=(#9984=(#9983=(#9982=(#9981=(#9980=(#9979=(#9978=(#9977=(#9976=(#9975=(#9974=(#9973=(#9972=(#9971=(#9970=(#9969=(#9968=(#9967=(#9966=(#9965=(#9964=(#9963=(#9962=(#9961=(#9960=(#9959=(#9958=(#9957=(#9956=(#9955=(#9954=(#9953=(#9952=(#9951=(#9950=(#9949=(#9948=(#9947=(#9946=(#9945=(#9944=(#9943=(#9942=(#9941=(#9940=(#9939=(#9938=(#9937=(#9936=(#9935=(#9934=(#9933=(#9932=(#9931=(#9930=(#9929=(#9928=(#9927=(#9926=(#9925=(#9924=(#9923=(#9922=(#9921=(#9920=(#9919=(#9918=(#9917=(#9916=(#9915=(#9914=(#9913=(#9912=(#9911=(#9910=(#9909=(#9908=(#9907=(#9906=(#9905=(#9904=(#9903=(#9902=(#9901=(#1# . #2=(#9900=(#9899=(#9898=(#9897=(#9896=(#9895=(#9894=(#9893=(#9892=(#9891=(#9890=(#9889=(#9888=(#9887=(#9886=(#9885=(#9884=(#9883=(#9882=(#9881=(#9880=(#9879=(#9878=(#9877=(#9876=(#9875=(#9874=(#9873=(#9872=(#9871=(#9870=(#9869=(#9868=(#9867=(#9866=(#9865=(#9864=(#9863=(#9862=(#9861=(#9860=(#9859=(#9858=(#9857=(#9856=(#9855=(#9854=(#9853=(#9852=(#9851=(#9850=(#9849=(#9848=(#9847=(#9846=(#9845=(#9844=(#9843=(#9842=(#9841=(#9840=(#9839=(#9838=(#9837=(#9836=(#9835=(#9834=(#9833=(#9832=(#9831=(#9830=(#9829=(#9828=(#9827=(#9826=(#9825=(#9824=(#9823=(#9822=(#9821=(#9820=(#9819=(#9818=(#9817=(#9816=(#9815=(#9814=(#9813=(#9812=(#9811=(#9810=(#9809=(#9808=(#9807=(#9806=(#9805=(#9804=(#9803=(#9802=(#2# . #3=(#9801=(#9800=(#9799=(#9798=(#9797=(#9796=(#9795=(#9794=(#9793=(#9792=(#9791=(#9790=(#9789=(#9788=(#9787=(#9786=(#9785=(#9784=(#9783=(#9782=(#9781=(#9780=(#9779=(#9778=(#9777=(#9776=(#9775=(#9774=(#9773=(#9772=(#9771=(#9770=(#9769=(#9768=(#9767=(#9766=(#9765=(#9764=(#9763=(#9762=(#9761=(#9760=(#9759=(#9758=(#9757=(#9756=(#9755=(#9754=(#9753=(#9752=(#9751=(#9750=(#9749=(#9748=(#9747=(#9746=(#9745=(#9744=(#9743=(#9742=(#9741=(#9740=(#9739=(#9738=(#9737=(#9736=(#9735=(#9734=(#9733=(#9732=(#9731=(#9730=(#9729=(#9728=(#9727=(#9726=(#9725=(#9724=(#9723=(#9722=(#9721=(#9720=(#9719=(#9718=(#9717=(#9716=(#9715=(#9714=(#9713=(#9712=(#9711=(#9710=(#9709=(#9708=(#9707=(#9706=(#9705=(#9704=(#9703=(#3# . #4=(#9702=(#9701=(#9700=(#9699=(#9698=(#9697=(#9696=(#9695=(#9694=(#9693=(#9692=(#9691=(#9690=(#9689=(#9688=(#9687=(#9686=(#9685=(#9684=(#9683=(#9682=(#9681=(#9680=(#9679=(#9678=(#9677=(#9676=(#9675=(#9674=(#9673=(#9672=(#9671=(#9670=(#9669=(#9668=(#9667=(#9666=(#9665=(#9664=(#9663=(#9662=(#9661=(#9660=(#9659=(#9658=(#9657=(#9656=(#9655=(#9654=(#9653=(#9652=(#9651=(#9650=(#9649=(#9648=(#9647=(#9646=(#9645=(#9644=(#9643=(#9642=(#9641=(#9640=(#9639=(#9638=(#9637=(#9636=(#9635=(#9634=(#9633=(#9632=(#9631=(#9630=(#9629=(#9628=(#9627=(#9626=(#9625=(#9624=(#9623=(#9622=(#9621=(#9620=(#9619=(#9618=(#9617=(#9616=(#9615=(#9614=(#9613=(#9612=(#9611=(#9610=(#9609=(#9608=(#9607=(#9606=(#9605=(#9604=(#4# . #5=(#9603=(#9602=(#9601=(#9600=(#9599=(#9598=(#9597=(#9596=(#9595=(#9594=(#9593=(#9592=(#9591=(#9590=(#9589=(#9588=(#9587=(#9586=(#9585=(#9584=(#9583=(#9582=(#9581=(#9580=(#9579=(#9578=(#9577=(#9576=(#9575=(#9574=(#9573=(#9572=(#9571=(#9570=(#9569=(#9568=(#9567=(#9566=(#9565=(#9564=(#9563=(#9562=(#9561=(#9560=(#9559=(#9558=(#9557=(#9556=(#9555=(#9554=(#9553=(#9552=(#9551=(#9550=(#9549=(#9548=(#9547=(#9546=(#9545=(#9544=(#9543=(#9542=(#9541=(#9540=(#9539=(#9538=(#9537=(#9536=(#9535=(#9534=(#9533=(#9532=(#9531=(#9530=(#9529=(#9528=(#9527=(#9526=(#9525=(#9524=(#9523=(#9522=(#9521=(#9520=(#9519=(#9518=(#9517=(#9516=(#9515=(#9514=(#9513=(#9512=(#9511=(#9510=(#9509=(#9508=(#9
\ No newline at end of file
--- /dev/null
+++ b/femtolisp/Makefile
@@ -1,0 +1,41 @@
+CC = gcc
+
+NAME = flisp
+SRCS = $(NAME).c builtins.c equal.c
+OBJS = $(SRCS:%.c=%.o)
+DOBJS = $(SRCS:%.c=%.do)
+EXENAME = $(NAME)
+LLT = llt/libllt.a
+
+FLAGS = -Wall -Wextra -Wno-strict-aliasing -I./llt $(CFLAGS)
+LIBS = $(LLT) -lm
+
+DEBUGFLAGS = -g -DDEBUG $(FLAGS)
+SHIPFLAGS = -O3 -DNDEBUG -fomit-frame-pointer $(FLAGS)
+
+default: release test
+
+test:
+ ./flisp unittest.lsp
+
+%.o: %.c
+ $(CC) $(SHIPFLAGS) -c $< -o $@
+%.do: %.c
+ $(CC) $(DEBUGFLAGS) -c $< -o $@
+
+flisp.o: flisp.c cvalues.c flisp.h print.c read.c
+flisp.do: flisp.c cvalues.c flisp.h print.c read.c
+
+$(LLT):
+ cd llt && make
+
+debug: $(DOBJS) $(LIBS)
+ $(CC) $(DEBUGFLAGS) $(DOBJS) -o $(EXENAME) $(LIBS)
+
+release: $(OBJS) $(LIBS)
+ $(CC) $(SHIPFLAGS) $(OBJS) -o $(EXENAME) $(LIBS)
+
+clean:
+ rm -f *.o
+ rm -f *.do
+ rm -f $(EXENAME)
--- /dev/null
+++ b/femtolisp/ast/asttools.lsp
@@ -1,0 +1,97 @@
+; utilities for AST processing
+
+(define (symconcat s1 s2)
+ (intern (string s1 s2)))
+
+(define (list-adjoin item lst)
+ (if (member item lst)
+ lst
+ (cons item lst)))
+
+(define (index-of item lst start)
+ (cond ((null lst) nil)
+ ((eq item (car lst)) start)
+ (T (index-of item (cdr lst) (+ start 1)))))
+
+(define (each f l)
+ (if (null l) l
+ (progn (f (car l))
+ (each f (cdr l)))))
+
+(define (maptree-pre f tr)
+ (let ((new-t (f tr)))
+ (if (consp new-t)
+ (map (lambda (e) (maptree-pre f e)) new-t)
+ new-t)))
+
+(define (maptree-post f tr)
+ (if (not (consp tr))
+ (f tr)
+ (let ((new-t (map (lambda (e) (maptree-post f e)) tr)))
+ (f new-t))))
+
+; collapse forms like (&& (&& (&& (&& a b) c) d) e) to (&& a b c d e)
+(define (flatten-left-op op e)
+ (maptree-post (lambda (node)
+ (if (and (consp node)
+ (eq (car node) op)
+ (consp (cdr node))
+ (consp (cadr node))
+ (eq (caadr node) op))
+ (cons op
+ (append (cdadr node) (cddr node)))
+ node))
+ e))
+
+; convert all local variable references to (lexref rib slot name)
+; where rib is the nesting level and slot is the stack slot#
+; name is just there for reference
+; this assumes lambda is the only remaining naming form
+(define (lookup-var v env lev)
+ (if (null env) v
+ (let ((i (index-of v (car env) 0)))
+ (if i (list 'lexref lev i v)
+ (lookup-var v (cdr env) (+ lev 1))))))
+(define (lvc- e env)
+ (cond ((symbolp e) (lookup-var e env 0))
+ ((consp e)
+ (if (eq (car e) 'quote)
+ e
+ (let* ((newvs (and (eq (car e) 'lambda) (cadr e)))
+ (newenv (if newvs (cons newvs env) env)))
+ (if newvs
+ (cons 'lambda
+ (cons (cadr e)
+ (map (lambda (se) (lvc- se newenv))
+ (cddr e))))
+ (map (lambda (se) (lvc- se env)) e)))))
+ (T e)))
+(define (lexical-var-conversion e)
+ (lvc- e ()))
+
+; convert let to lambda
+(define (let-expand e)
+ (maptree-post (lambda (n)
+ (if (and (consp n) (eq (car n) 'let))
+ `((lambda ,(map car (cadr n)) ,@(cddr n))
+ ,@(map cadr (cadr n)))
+ n))
+ e))
+
+; flatten op with any associativity
+(defmacro flatten-all-op (op e)
+ `(pattern-expand
+ (pattern-lambda (,op (-- l ...) (-- inner (,op ...)) (-- r ...))
+ (cons ',op (append l (cdr inner) r)))
+ ,e))
+
+(defmacro pattern-lambda (pat body)
+ (let* ((args (patargs pat))
+ (expander `(lambda ,args ,body)))
+ `(lambda (expr)
+ (let ((m (match ',pat expr)))
+ (if m
+ ; matches; perform expansion
+ (apply ,expander (map (lambda (var) (cdr (or (assoc var m) '(0 . nil))))
+ ',args))
+ nil)))))
--- /dev/null
+++ b/femtolisp/ast/asttools.scm
@@ -1,0 +1,88 @@
+; utilities for AST processing
+
+(define (symconcat s1 s2)
+ (string->symbol (string-append (symbol->string s1)
+ (symbol->string s2))))
+
+(define (list-adjoin item lst)
+ (if (memq item lst)
+ lst
+ (cons item lst)))
+
+(define (index-of item lst start)
+ (cond ((null? lst) #f)
+ ((eq? item (car lst)) start)
+ (else (index-of item (cdr lst) (+ start 1)))))
+
+(define (map! f l)
+ (define (map!- f l start)
+ (if (pair? l)
+ (begin (set-car! l (f (car l)))
+ (map!- f (cdr l) start))
+ start))
+ (map!- f l l))
+
+(define (each f l)
+ (if (null? l) l
+ (begin (f (car l))
+ (each f (cdr l)))))
+
+(define (maptree-pre f t)
+ (let ((new-t (f t)))
+ (if (pair? new-t)
+ (map (lambda (e) (maptree-pre f e)) new-t)
+ new-t)))
+
+(define (maptree-post f t)
+ (if (not (pair? t))
+ (f t)
+ (let ((new-t (map (lambda (e) (maptree-post f e)) t)))
+ (f new-t))))
+
+; collapse forms like (&& (&& (&& (&& a b) c) d) e) to (&& a b c d e)
+(define (flatten-left-op op e)
+ (maptree-post (lambda (node)
+ (if (and (pair? node)
+ (eq? (car node) op)
+ (pair? (cdr node))
+ (pair? (cadr node))
+ (eq? (caadr node) op))
+ (cons op
+ (append (cdadr node) (cddr node)))
+ node))
+ e))
+
+; convert all local variable references to (lexref rib slot name)
+; where rib is the nesting level and slot is the stack slot#
+; name is just there for reference
+; this assumes lambda is the only remaining naming form
+(define (lexical-var-conversion e)
+ (define (lookup-var v env lev)
+ (if (null? env) v
+ (let ((i (index-of v (car env) 0)))
+ (if i (list 'lexref lev i v)
+ (lookup-var v (cdr env) (+ lev 1))))))
+ (define (lvc- e env)
+ (cond ((symbol? e) (lookup-var e env 0))
+ ((pair? e)
+ (if (eq? (car e) 'quote)
+ e
+ (let* ((newvs (and (eq? (car e) 'lambda) (cadr e)))
+ (newenv (if newvs (cons newvs env) env)))
+ (if newvs
+ (cons 'lambda
+ (cons (cadr e)
+ (map (lambda (se) (lvc- se newenv))
+ (cddr e))))
+ (map (lambda (se) (lvc- se env)) e)))))
+ (else e)))
+ (lvc- e ()))
+
+; convert let to lambda
+(define (let-expand e)
+ (maptree-post (lambda (n)
+ (if (and (pair? n) (eq? (car n) 'let))
+ `((lambda ,(map car (cadr n)) ,@(cddr n))
+ ,@(map cadr (cadr n)))
+ n))
+ e))
--- /dev/null
+++ b/femtolisp/ast/match.lsp
@@ -1,0 +1,181 @@
+; tree regular expression pattern matching
+; by Jeff Bezanson
+
+(define (unique lst)
+ (if (null lst)
+ ()
+ (cons (car lst)
+ (filter (lambda (x) (not (eq x (car lst))))
+ (unique (cdr lst))))))
+
+; list of special pattern symbols that cannot be variable names
+(define metasymbols '(_ ...))
+
+; expression tree pattern matching
+; matches expr against pattern p and returns an assoc list ((var . expr) (var . expr) ...)
+; mapping variables to captured subexpressions, or #f if no match.
+; when a match succeeds, __ is always bound to the whole matched expression.
+;
+; p is an expression in the following pattern language:
+;
+; _ match anything, not captured
+; <func> any scheme function; matches if (func expr) returns #t
+; <var> match anything and capture as <var>. future occurrences of <var> in the pattern
+; must match the same thing.
+; (head <p1> <p2> etc) match an s-expr with 'head' matched literally, and the rest of the
+; subpatterns matched recursively.
+; (-/ <ex>) match <ex> literally
+; (-^ <p>) complement of pattern <p>
+; (-- <var> <p>) match <p> and capture as <var> if match succeeds
+;
+; regular match constructs:
+; ... match any number of anything
+; (-$ <p1> <p2> etc) match any of subpatterns <p1>, <p2>, etc
+; (-* <p>) match any number of <p>
+; (-? <p>) match 0 or 1 of <p>
+; (-+ <p>) match at least 1 of <p>
+; all of these can be wrapped in (-- var ) for capturing purposes
+; This is NP-complete. Be careful.
+;
+(define (match- p expr state)
+ (cond ((symbolp p)
+ (cond ((eq p '_) state)
+ (T
+ (let ((capt (assoc p state)))
+ (if capt
+ (and (equal expr (cdr capt)) state)
+ (cons (cons p expr) state))))))
+
+ ((functionp p)
+ (and (p expr) state))
+
+ ((consp p)
+ (cond ((eq (car p) '-/) (and (equal (cadr p) expr) state))
+ ((eq (car p) '-^) (and (not (match- (cadr p) expr state)) state))
+ ((eq (car p) '--)
+ (and (match- (caddr p) expr state)
+ (cons (cons (cadr p) expr) state)))
+ ((eq (car p) '-$) ; greedy alternation for toplevel pattern
+ (match-alt (cdr p) () (list expr) state nil 1))
+ (T
+ (and (consp expr)
+ (equal (car p) (car expr))
+ (match-seq (cdr p) (cdr expr) state (length (cdr expr)))))))
+
+ (T
+ (and (equal p expr) state))))
+
+; match an alternation
+(define (match-alt alt prest expr state var L)
+ (if (null alt) nil ; no alternatives left
+ (let ((subma (match- (car alt) (car expr) state)))
+ (or (and subma
+ (match-seq prest (cdr expr)
+ (if var
+ (cons (cons var (car expr))
+ subma)
+ subma)
+ (- L 1)))
+ (match-alt (cdr alt) prest expr state var L)))))
+
+; match generalized kleene star (try consuming min to max)
+(define (match-star- p prest expr state var min max L sofar)
+ (cond ; case 0: impossible to match
+ ((> min max) nil)
+ ; case 1: only allowed to match 0 subexpressions
+ ((= max 0) (match-seq prest expr
+ (if var (cons (cons var (reverse sofar)) state)
+ state)
+ L))
+ ; case 2: must match at least 1
+ ((> min 0)
+ (and (match- p (car expr) state)
+ (match-star- p prest (cdr expr) state var (- min 1) (- max 1) (- L 1)
+ (cons (car expr) sofar))))
+ ; otherwise, must match either 0 or between 1 and max subexpressions
+ (T
+ (or (match-star- p prest expr state var 0 0 L sofar)
+ (match-star- p prest expr state var 1 max L sofar)))))
+(define (match-star p prest expr state var min max L)
+ (match-star- p prest expr state var min max L ()))
+
+; match sequences of expressions
+(define (match-seq p expr state L)
+ (cond ((not state) nil)
+ ((null p) (if (null expr) state nil))
+ (T
+ (let ((subp (car p))
+ (var nil))
+ (if (and (consp subp)
+ (eq (car subp) '--))
+ (progn (setq var (cadr subp))
+ (setq subp (caddr subp)))
+ nil)
+ (let ((head (if (consp subp) (car subp) ())))
+ (cond ((eq subp '...)
+ (match-star '_ (cdr p) expr state var 0 L L))
+ ((eq head '-*)
+ (match-star (cadr subp) (cdr p) expr state var 0 L L))
+ ((eq head '-+)
+ (match-star (cadr subp) (cdr p) expr state var 1 L L))
+ ((eq head '-?)
+ (match-star (cadr subp) (cdr p) expr state var 0 1 L))
+ ((eq head '-$)
+ (match-alt (cdr subp) (cdr p) expr state var L))
+ (T
+ (and (consp expr)
+ (match-seq (cdr p) (cdr expr)
+ (match- (car p) (car expr) state)
+ (- L 1))))))))))
+
+(define (match p expr) (match- p expr (list (cons '__ expr))))
+
+; given a pattern p, return the list of capturing variables it uses
+(define (patargs- p)
+ (cond ((and (symbolp p)
+ (not (member p metasymbols)))
+ (list p))
+
+ ((consp p)
+ (if (eq (car p) '-/)
+ ()
+ (unique (apply append (map patargs- (cdr p))))))
+
+ (T ())))
+(define (patargs p)
+ (cons '__ (patargs- p)))
+
+; try to transform expr using a pattern-lambda from plist
+; returns the new expression, or expr if no matches
+(define (apply-patterns plist expr)
+ (if (null plist) expr
+ (if (functionp plist)
+ (let ((enew (plist expr)))
+ (if (not enew)
+ expr
+ enew))
+ (let ((enew ((car plist) expr)))
+ (if (not enew)
+ (apply-patterns (cdr plist) expr)
+ enew)))))
+
+; top-down fixed-point macroexpansion. this is a typical algorithm,
+; but it may leave some structure that matches a pattern unexpanded.
+; the advantage is that non-terminating cases cannot arise as a result
+; of expression composition. in other words, if the outer loop terminates
+; on all inputs for a given set of patterns, then the whole algorithm
+; terminates. pattern sets that violate this should be easier to detect,
+; for example
+; (pattern-lambda (/ 2 3) '(/ 3 2)), (pattern-lambda (/ 3 2) '(/ 2 3))
+; TODO: ignore quoted expressions
+(define (pattern-expand plist expr)
+ (if (not (consp expr))
+ expr
+ (let ((enew (apply-patterns plist expr)))
+ (if (eq enew expr)
+ ; expr didn't change; move to subexpressions
+ (cons (car expr)
+ (map (lambda (subex) (pattern-expand plist subex)) (cdr expr)))
+ ; expr changed; iterate
+
+ (pattern-expand plist enew)))))
--- /dev/null
+++ b/femtolisp/ast/match.scm
@@ -1,0 +1,181 @@
+; tree regular expression pattern matching
+; by Jeff Bezanson
+
+(define (unique lst)
+ (if (null? lst)
+ ()
+ (cons (car lst)
+ (filter (lambda (x) (not (eq? x (car lst))))
+ (unique (cdr lst))))))
+
+; list of special pattern symbols that cannot be variable names
+(define metasymbols '(_ ...))
+
+; expression tree pattern matching
+; matches expr against pattern p and returns an assoc list ((var . expr) (var . expr) ...)
+; mapping variables to captured subexpressions, or #f if no match.
+; when a match succeeds, __ is always bound to the whole matched expression.
+;
+; p is an expression in the following pattern language:
+;
+; _ match anything, not captured
+; <func> any scheme function; matches if (func expr) returns #t
+; <var> match anything and capture as <var>. future occurrences of <var> in the pattern
+; must match the same thing.
+; (head <p1> <p2> etc) match an s-expr with 'head' matched literally, and the rest of the
+; subpatterns matched recursively.
+; (-/ <ex>) match <ex> literally
+; (-^ <p>) complement of pattern <p>
+; (-- <var> <p>) match <p> and capture as <var> if match succeeds
+;
+; regular match constructs:
+; ... match any number of anything
+; (-$ <p1> <p2> etc) match any of subpatterns <p1>, <p2>, etc
+; (-* <p>) match any number of <p>
+; (-? <p>) match 0 or 1 of <p>
+; (-+ <p>) match at least 1 of <p>
+; all of these can be wrapped in (-- var ) for capturing purposes
+; This is NP-complete. Be careful.
+;
+(define (match- p expr state)
+ (cond ((symbol? p)
+ (cond ((eq? p '_) state)
+ (else
+ (let ((capt (assq p state)))
+ (if capt
+ (and (equal? expr (cdr capt)) state)
+ (cons (cons p expr) state))))))
+
+ ((procedure? p)
+ (and (p expr) state))
+
+ ((pair? p)
+ (cond ((eq? (car p) '-/) (and (equal? (cadr p) expr) state))
+ ((eq? (car p) '-^) (and (not (match- (cadr p) expr state)) state))
+ ((eq? (car p) '--)
+ (and (match- (caddr p) expr state)
+ (cons (cons (cadr p) expr) state)))
+ ((eq? (car p) '-$) ; greedy alternation for toplevel pattern
+ (match-alt (cdr p) () (list expr) state #f 1))
+ (else
+ (and (pair? expr)
+ (equal? (car p) (car expr))
+ (match-seq (cdr p) (cdr expr) state (length (cdr expr)))))))
+
+ (else
+ (and (equal? p expr) state))))
+
+; match an alternation
+(define (match-alt alt prest expr state var L)
+ (if (null? alt) #f ; no alternatives left
+ (let ((subma (match- (car alt) (car expr) state)))
+ (or (and subma
+ (match-seq prest (cdr expr)
+ (if var
+ (cons (cons var (car expr))
+ subma)
+ subma)
+ (- L 1)))
+ (match-alt (cdr alt) prest expr state var L)))))
+
+; match generalized kleene star (try consuming min to max)
+(define (match-star p prest expr state var min max L)
+ (define (match-star- p prest expr state var min max L sofar)
+ (cond ; case 0: impossible to match
+ ((> min max) #f)
+ ; case 1: only allowed to match 0 subexpressions
+ ((= max 0) (match-seq prest expr
+ (if var (cons (cons var (reverse sofar)) state)
+ state)
+ L))
+ ; case 2: must match at least 1
+ ((> min 0)
+ (and (match- p (car expr) state)
+ (match-star- p prest (cdr expr) state var (- min 1) (- max 1) (- L 1)
+ (cons (car expr) sofar))))
+ ; otherwise, must match either 0 or between 1 and max subexpressions
+ (else
+ (or (match-star- p prest expr state var 0 0 L sofar)
+ (match-star- p prest expr state var 1 max L sofar)))))
+
+ (match-star- p prest expr state var min max L ()))
+
+; match sequences of expressions
+(define (match-seq p expr state L)
+ (cond ((not state) #f)
+ ((null? p) (if (null? expr) state #f))
+ (else
+ (let ((subp (car p))
+ (var #f))
+ (if (and (pair? subp)
+ (eq? (car subp) '--))
+ (begin (set! var (cadr subp))
+ (set! subp (caddr subp)))
+ #f)
+ (let ((head (if (pair? subp) (car subp) ())))
+ (cond ((eq? subp '...)
+ (match-star '_ (cdr p) expr state var 0 L L))
+ ((eq? head '-*)
+ (match-star (cadr subp) (cdr p) expr state var 0 L L))
+ ((eq? head '-+)
+ (match-star (cadr subp) (cdr p) expr state var 1 L L))
+ ((eq? head '-?)
+ (match-star (cadr subp) (cdr p) expr state var 0 1 L))
+ ((eq? head '-$)
+ (match-alt (cdr subp) (cdr p) expr state var L))
+ (else
+ (and (pair? expr)
+ (match-seq (cdr p) (cdr expr)
+ (match- (car p) (car expr) state)
+ (- L 1))))))))))
+
+(define (match p expr) (match- p expr (list (cons '__ expr))))
+
+; given a pattern p, return the list of capturing variables it uses
+(define (patargs p)
+ (define (patargs- p)
+ (cond ((and (symbol? p)
+ (not (member p metasymbols)))
+ (list p))
+
+ ((pair? p)
+ (if (eq? (car p) '-/)
+ ()
+ (unique (apply append (map patargs- (cdr p))))))
+
+ (else ())))
+ (cons '__ (patargs- p)))
+
+; try to transform expr using a pattern-lambda from plist
+; returns the new expression, or expr if no matches
+(define (apply-patterns plist expr)
+ (if (null? plist) expr
+ (if (procedure? plist)
+ (let ((enew (plist expr)))
+ (if (not enew)
+ expr
+ enew))
+ (let ((enew ((car plist) expr)))
+ (if (not enew)
+ (apply-patterns (cdr plist) expr)
+ enew)))))
+
+; top-down fixed-point macroexpansion. this is a typical algorithm,
+; but it may leave some structure that matches a pattern unexpanded.
+; the advantage is that non-terminating cases cannot arise as a result
+; of expression composition. in other words, if the outer loop terminates
+; on all inputs for a given set of patterns, then the whole algorithm
+; terminates. pattern sets that violate this should be easier to detect,
+; for example
+; (pattern-lambda (/ 2 3) '(/ 3 2)), (pattern-lambda (/ 3 2) '(/ 2 3))
+; TODO: ignore quoted expressions
+(define (pattern-expand plist expr)
+ (if (not (pair? expr))
+ expr
+ (let ((enew (apply-patterns plist expr)))
+ (if (eq? enew expr)
+ ; expr didn't change; move to subexpressions
+ (cons (car expr)
+ (map (lambda (subex) (pattern-expand plist subex)) (cdr expr)))
+ ; expr changed; iterate
+ (pattern-expand plist enew)))))
--- /dev/null
+++ b/femtolisp/ast/out.lsp
@@ -1,0 +1,3 @@
+1201386230.6766149997711182
+(r-expressions (r-call library MASS) (r-call dyn.load "starp.so") (<- ppcommand (lambda (...) (let nil (r-block (r-call .Call "ppcommand" (r-call list r-dotdotdot)))))) (<- ppvcommand (lambda (va) (let nil (r-block (r-call .Call "ppcommand" va))))) (<- ppinvoke ppcommand) (<- pploadconfig (lambda (fileName) (let nil (r-block (r-call .Call "pploadconfig" fileName))))) (<- ppconnect (lambda (numProcs machines) (let ((machines nil) (numProcs nil)) (r-block (when (missing numProcs) (<- numProcs nil)) (when (missing machines) (<- machines nil)) (r-call .Call "ppconnect" (r-call list numProcs machines)))))) (<- ppgetlogpath (lambda nil (let nil (r-block (r-call .Call "ppgetlogpath"))))) (<- ppgetlog (lambda nil (let nil (r-block (r-call .Call "ppgetlog"))))) (<- ppshowdashboard (lambda nil (let nil (r-block (r-call .Call "ppshowdashboard"))))) (<- pphidedashboard (lambda nil (let nil (r-block (r-call .Call "pphidedashboard"))))) (<- revealargs (lambda (dots) (let nil (r-block (r-call .Call "_revealArgs" dots))))) (<- listargs (lambda (...) (let nil (r-block (r-call revealargs (r-call get "...")))))) (<- ppping (lambda nil (let nil (r-block (r-call ppcommand "ppping"))))) (<- ppver (lambda nil (let nil (r-block (r-call ppcommand "pp_ver"))))) (<- STARPDIST "../../../linkdist") (<- STARPPLATFORM "ia32_linux") (r-call .Call "_setstarpdist" STARPDIST) (r-call .Call "_setstarpplat" STARPPLATFORM) (r-call pploadconfig (r-call paste STARPDIST "/config/starpd.properties" (*named* sep ""))) (<- dimdis (lambda (v) (let nil (r-block (if (r-call == (r-call r-index (r-call class v) 1) "dlayoutn") (return (r-call as.numeric (r-call r-index (r-call class v) 2)))) (if (r-call ! (r-call is.null v)) (r-block (for i (r-call : (r-call length v) 1) (if (r-call > (r-call r-aref v i) 1) (return i)))) (r-block (return 1))) (return (r-call length v)))))) (<- is.scalar (lambda (x) (let nil (r-block (&& (|\|\|| (r-call == (r-call mode x) "numeric") (r-call == (r-call mode x) "complex")) (r-call is.null (r-call (r-call .Primitive "dim") x)) (r-call == (r-call length x) 1)))))) (<- p 1) (r-block (ref= #:g0 (r-call c "dlayout" "numeric")) (<- p (r-call class p #:g0)) #:g0) (<- darray (lambda (id shape distribution isreal) (let ((d nil) (distribution nil) (shape nil)) (r-block (<- shape (r-call as.array shape)) (<- distribution (r-call + distribution 1)) (r-block (ref= #:g1 (r-call append "dlayoutn" (r-call toString distribution) (r-call class shape))) (<- shape (r-call class shape #:g1)) #:g1) (<- d (r-call list (*named* id id) (*named* shape shape) (*named* isreal isreal) (*named* logical *r-false*) nil nil)) (r-block (<- d (r-call class d "darray")) "darray") d)))) (<- darraydist (lambda (da) (let nil (r-block (r-call as.numeric (r-call r-aref (r-call class (r-call r-aref da (index-in-strlist shape (r-call attr da "names")))) 2)))))) (<- is.darray (lambda (x) (let nil (r-block (r-call == (r-call r-index (r-call class x) 1) "darray"))))) (<- is.nd (lambda (x) (let nil (r-block (r-call != (r-call length (r-call dim x)) 2))))) (<- is.darraynd (lambda (x) (let nil (r-block (&& (r-call is.darray x) (r-call is.nd x)))))) (<- is.dlayout (lambda (x) (let nil (r-block (r-call any (r-call == (r-call class x) "dlayout")))))) (<- vdim (lambda (x) (let nil (r-block (if (r-call is.vector x) (r-call length x) (r-call dim x)))))) (<- |[[.dlayoutn| (<- |[.dlayoutn| (lambda (dl n) (let ((didi nil) (r nil) (dd nil)) (r-block (<- dd (r-call as.numeric (r-call r-aref (r-call class dl) 2))) (if (r-call == (r-call length n) 1) (r-block (if (r-call == n dd) (r-call * (r-call r-index (r-call as.vector dl) n) p) (r-call r-index (r-call as.vector dl) n))) (r-block (<- r (r-call r-index (r-call as.numeric dl) n)) (<- didi (r-call dimdis r)) (for i (r-call : 1 (r-call length n)) (r-block (if (r-call == (r-call r-aref n i) dd) (r-block (<- didi i) (break))))) (r-block (ref= #:g2 (r-call append "dlayoutn" (r-call toString didi) (r-call class r))) (<- r (r-call class r #:g2)) #:g2) (return r)))))))) (<- print.darray (lambda (d ...) (let ((shs nil) (sh nil)) (r-block (<- sh (r-call as.
\ No newline at end of file
+1201386230.8069550991058350
--- /dev/null
+++ b/femtolisp/ast/plambda-js.scm
@@ -1,0 +1,23 @@
+; pattern-lambda syntax for jscheme
+
+; pattern-lambda abstraction
+; this is a generalization of lambda:
+;
+; ((pattern-lambda p body) expr)
+; Matches expr against p. If no match, return #null. If match succeeds, evaluate body
+; with variables in p bound to whatever they matched in expr.
+;
+; EXAMPLE: Recognize adding any expression x to itself, replace with 2*x.
+; (define selfadd (pattern-lambda (+ x x) `(* 2 ,x)))
+; Then (selfadd '(+ (foo bar) (foo bar))) returns (* 2 (foo bar))
+;
+(define-macro (pattern-lambda pat body)
+ (let* ((args (patargs pat))
+ (expander `(lambda ,args ,body)))
+ `(lambda (expr)
+ (let ((m (match ',pat expr)))
+ (if m
+ ; matches; perform expansion
+ (apply ,expander (map (lambda (var) (cdr (or (assq var m) '(0 . #f))))
+ ',args))
+ #f)))))
binary files /dev/null b/femtolisp/ast/rpasses.exe differ
--- /dev/null
+++ b/femtolisp/ast/rpasses.lsp
@@ -1,0 +1,121 @@
+(load '|match.lsp|)
+(load '|asttools.lsp|)
+
+(define missing-arg-tag '*r-missing*)
+
+; tree inspection utils
+
+(define (assigned-var e)
+ (and (consp e)
+ (or (eq (car e) '<-) (eq (car e) 'ref=))
+ (symbolp (cadr e))
+ (cadr e)))
+
+(define (func-argnames f)
+ (let ((argl (cadr f)))
+ (if (eq argl '*r-null*) ()
+ (map cadr argl))))
+
+; transformations
+
+(define (dollarsign-transform e)
+ (pattern-expand
+ (pattern-lambda ($ lhs name)
+ (let* ((g (if (not (consp lhs)) lhs (gensym)))
+ (n (if (symbolp name)
+ name ;(symbol->string name)
+ name))
+ (expr `(r-call
+ r-aref ,g (index-in-strlist ,n (r-call attr ,g "names")))))
+ (if (not (consp lhs))
+ expr
+ `(r-block (ref= ,g ,lhs) ,expr))))
+ e))
+
+; lower r expressions of the form f(lhs,...) <- rhs
+; TODO: if there are any special forms that can be f in this expression,
+; they need to be handled separately. For example a$b can be lowered
+; to an index assignment (by dollarsign-transform), after which
+; this transform applies. I don't think there are any others though.
+(define (fancy-assignment-transform e)
+ (pattern-expand
+ (pattern-lambda (-$ (<- (r-call f lhs ...) rhs)
+ (<<- (r-call f lhs ...) rhs))
+ (let ((g (if (consp rhs) (gensym) rhs))
+ (op (car __)))
+ `(r-block ,@(if (consp rhs) `((ref= ,g ,rhs)) ())
+ (,op ,lhs (r-call ,(symconcat f '<-) ,@(cddr (cadr __)) ,g))
+ ,g)))
+ e))
+
+; map an arglist with default values to appropriate init code
+; function(x=blah) { ... } gets
+; if (missing(x)) x = blah
+; added to its body
+(define (gen-default-inits arglist)
+ (map (lambda (arg)
+ (let ((name (cadr arg))
+ (default (caddr arg)))
+ `(when (missing ,name)
+ (<- ,name ,default))))
+ (filter (lambda (arg) (not (eq (caddr arg) missing-arg-tag))) arglist)))
+
+; convert r function expressions to lambda
+(define (normalize-r-functions e)
+ (maptree-post (lambda (n)
+ (if (and (consp n) (eq (car n) 'function))
+ `(lambda ,(func-argnames n)
+ (r-block ,@(gen-default-inits (cadr n))
+ ,@(if (and (consp (caddr n))
+ (eq (car (caddr n)) 'r-block))
+ (cdr (caddr n))
+ (list (caddr n)))))
+ n))
+ e))
+
+(define (find-assigned-vars n)
+ (let ((vars ()))
+ (maptree-pre (lambda (s)
+ (if (not (consp s)) s
+ (cond ((eq (car s) 'lambda) nil)
+ ((eq (car s) '<-)
+ (setq vars (list-adjoin (cadr s) vars))
+ (cddr s))
+ (T s))))
+ n)
+ vars))
+
+; introduce let based on assignment statements
+(define (letbind-locals e)
+ (maptree-post (lambda (n)
+ (if (and (consp n) (eq (car n) 'lambda))
+ (let ((vars (find-assigned-vars (cddr n))))
+ `(lambda ,(cadr n) (let ,(map (lambda (v) (list v ()))
+ vars)
+ ,@(cddr n))))
+ n))
+ e))
+
+(define (compile-ish e)
+ (letbind-locals
+ (normalize-r-functions
+ (fancy-assignment-transform
+ (dollarsign-transform
+ (flatten-all-op && (flatten-all-op \|\| e)))))))
+
+;(trace map)
+;(pretty-print (compile-ish *input*))
+; (time-call (lambda () (compile-ish *input*)) 1)
+;)
+(define (main)
+ (progn
+ (define *input* (read))
+ ;(define t0 ((java.util.Date:new):getTime))
+ (clock)
+ (compile-ish *input*)
+ (clock)
+ ;(define t1 ((java.util.Date:new):getTime))
+))
+
+(main)
--- /dev/null
+++ b/femtolisp/ast/rpasses.scm
@@ -1,0 +1,206 @@
+(include "iscutil.scm")
+(include "match.scm")
+(include "asttools.scm")
+;(load "plambda-js.scm")
+;(load "plambda-chez.scm")
+
+;(pretty-print *input*)
+
+#|
+Overall phases:
+I. s-expr output
+II. tree normalization
+ 1. control construct normalization, flattening. various restructuring.
+ 2. transformations that might add variables
+ 3. local variable detection
+III. var/func attribute analysis
+IV. argument normalization
+V. type inference
+ 1. split each function into generic/non-generic versions. the generic
+ one resolves generic funcs to calls to a lookup routine that tries
+ to find stuff like `diag<-.darray`. the other one assumes everything
+ is handled by a builtin R function with a known t-function
+ 2. inference
+VI. code generation
+
+Useful R lowering passes:
+
+- control construct normalization
+ . convert while/repeat/various for forms/break/next to while/break
+ . convert switch to nested if
+
+- local variable detection
+ . classify vars as (1) definitely local, (2) possibly-local, (3) free
+ . collect all local or possibly-local vars and wrap the body with
+ (let ((g0 (upvalue 'var1))
+ (g1 (upvalue 'var2)))
+ <body>)
+
+ where (upvalue x) is either (get-global x) or (captured-var n i)
+ for definitely-local, start as null instead of upvalue
+
+ then we have to rename var1 to g0 everywhere inside that.
+ for the vast majority of functions that don't attempt to modify parent-scope
+ locals, pure-functional closure conversion would work.
+
+ utility for this: fold-along-cfg
+ . after this the tree is ready for typical lexical scope analysis
+
+ (- closure conversion/deBruijn indices)
+
+- argument normalization for call to known function
+ . convert lambda arglist to plain list of symbols
+ . move default initializers into body as `(when (eq? ,argname 'missing) ,assign)
+ . at call site sort args to correct positions, add explicit missing
+ . if call target unknown insert call to match.args or whatever
+
+- r-block, ||, && flattening
+
+- fancy assignment transformation:
+ f(v) <- rhs, (<- (r-call f v) rhs)
+ performs:
+ (begin (<- v (r-call f<- v rhs))
+ rhs)
+
+- (<- a b) becomes (ref= a (lazy-copy b))
+ arguments to functions are wrapped in lazy-copy at the call site, so we can
+ omit the copy (1) for functions marked as pass-by-ref, (2) where user indicated
+ pass-by-ref, (3) for arguments which are strictly-allocating expressions,
+ (4) for user functions proven to be ref-safe and thus marked as case (1)
+
+Useful analyses:
+
+- prove function strictness!!
+ . strict functions need to open with (if (promise? arg) (force arg) arg) for each
+ arg, in case they are called indirectly.
+- prove global variables constant (esp. function names)
+ . prove builtins redefined/constant
+- need dictionary of builtin properties (pure/strict/t-functions/etc.)
+- useful but very general types:
+ single: has length 1 and no attrs (implies simple)
+ simple: has default class attributes
+ array: has dim attribute only
+ distributed: starp array
+ numeric
+|#
+
+
+(define missing-arg-tag '*r-missing*)
+
+; tree inspection utils
+
+(define (assigned-var e)
+ (and (pair? e)
+ (or (eq? (car e) '<-) (eq? (car e) 'ref=))
+ (symbol? (cadr e))
+ (cadr e)))
+
+(define (func-argnames f)
+ (let ((argl (cadr f)))
+ (if (eq? argl '*r-null*) ()
+ (map cadr argl))))
+
+; transformations
+
+(define (dollarsign-transform e)
+ (pattern-expand
+ (pattern-lambda ($ lhs name)
+ (let* ((g (if (not (pair? lhs)) lhs (gensym)))
+ (n (if (symbol? name)
+ (symbol->string name)
+ name))
+ (expr `(r-call
+ r-aref ,g (index-in-strlist ,n (r-call attr ,g "names")))))
+ (if (not (pair? lhs))
+ expr
+ `(r-block (ref= ,g ,lhs) ,expr))))
+ e))
+
+; lower r expressions of the form f(lhs,...) <- rhs
+; TODO: if there are any special forms that can be f in this expression,
+; they need to be handled separately. For example a$b can be lowered
+; to an index assignment (by dollarsign-transform), after which
+; this transform applies. I don't think there are any others though.
+(define (fancy-assignment-transform e)
+ (pattern-expand
+ (pattern-lambda (-$ (<- (r-call f lhs ...) rhs)
+ (<<- (r-call f lhs ...) rhs))
+ (let ((g (if (pair? rhs) (gensym) rhs))
+ (op (car __)))
+ `(r-block ,@(if (pair? rhs) `((ref= ,g ,rhs)) ())
+ (,op ,lhs (r-call ,(symconcat f '<-) ,@(cddr (cadr __)) ,g))
+ ,g)))
+ e))
+
+; map an arglist with default values to appropriate init code
+; function(x=blah) { ... } gets
+; if (missing(x)) x = blah
+; added to its body
+(define (gen-default-inits arglist)
+ (map (lambda (arg)
+ (let ((name (cadr arg))
+ (default (caddr arg)))
+ `(when (missing ,name)
+ (<- ,name ,default))))
+ (filter (lambda (arg) (not (eq? (caddr arg) missing-arg-tag))) arglist)))
+
+; convert r function expressions to lambda
+(define (normalize-r-functions e)
+ (maptree-post (lambda (n)
+ (if (and (pair? n) (eq? (car n) 'function))
+ `(lambda ,(func-argnames n)
+ (r-block ,@(gen-default-inits (cadr n))
+ ,@(if (and (pair? (caddr n))
+ (eq? (car (caddr n)) 'r-block))
+ (cdr (caddr n))
+ (list (caddr n)))))
+ n))
+ e))
+
+(define (find-assigned-vars n)
+ (let ((vars ()))
+ (maptree-pre (lambda (s)
+ (if (not (pair? s)) s
+ (cond ((eq? (car s) 'lambda) #f)
+ ((eq? (car s) '<-)
+ (set! vars (list-adjoin (cadr s) vars))
+ (cddr s))
+ (else s))))
+ n)
+ vars))
+
+; introduce let based on assignment statements
+(define (letbind-locals e)
+ (maptree-post (lambda (n)
+ (if (and (pair? n) (eq? (car n) 'lambda))
+ (let ((vars (find-assigned-vars (cddr n))))
+ `(lambda ,(cadr n) (let ,(map list
+ vars
+ (map (lambda (x) '()) vars))
+ ,@(cddr n))))
+ n))
+ e))
+
+(define (compile-ish e)
+ (letbind-locals
+ (normalize-r-functions
+ (fancy-assignment-transform
+ (dollarsign-transform
+ (flatten-all-op && (flatten-all-op || e)))))))
+
+;(trace map)
+;(pretty-print (compile-ish *input*))
+; (time-call (lambda () (compile-ish *input*)) 1)
+;)
+(define (main)
+ (begin
+ (define *input* (read))
+ (define t0 ((java.util.Date:new):getTime))
+ (compile-ish *input*)
+ (define t1 ((java.util.Date:new):getTime))
+ (display "milliseconds: ")
+ (display (- t1 t0))
+ (newline)))
+
+(main)
--- /dev/null
+++ b/femtolisp/ast/starpR.lsp
@@ -1,0 +1,120 @@
+(r-expressions
+ (r-call library \M\A\S\S)
+ (r-call dyn.load "starp.so")
+ (<- ppcommand (function ((*named* ... *r-missing*)) (r-call .\Call "ppcommand" (r-call list r-dotdotdot)) ()))
+ (<- ppvcommand (function ((*named* va *r-missing*)) (r-call .\Call "ppcommand" va) ()))
+ (<- ppinvoke ppcommand)
+ (<- pploadconfig (function ((*named* fileName *r-missing*)) (r-call .\Call "pploadconfig" file\Name) ()))
+ (<- ppconnect (function ((*named* numProcs ()) (*named* machines ())) (r-call .\Call "ppconnect" (r-call list num\Procs machines)) ()))
+ (<- ppgetlogpath (function () (r-call .\Call "ppgetlogpath") ()))
+ (<- ppgetlog (function () (r-call .\Call "ppgetlog") ()))
+ (<- ppshowdashboard (function () (r-call .\Call "ppshowdashboard") ()))
+ (<- pphidedashboard (function () (r-call .\Call "pphidedashboard") ()))
+ (<- revealargs (function ((*named* dots *r-missing*)) (r-call .\Call "_revealArgs" dots) ()))
+ (<- listargs (function ((*named* ... *r-missing*)) (r-call revealargs (r-call get "...")) ()))
+ (<- ppping (function () (r-call ppcommand "ppping") ()))
+ (<- ppver (function () (r-call ppcommand "pp_ver") ()))
+ (<- \S\T\A\R\P\D\I\S\T "../../../linkdist")
+ (<- \S\T\A\R\P\P\L\A\T\F\O\R\M "ia32_linux")
+ (r-call .\Call "_setstarpdist" \S\T\A\R\P\D\I\S\T)
+ (r-call .\Call "_setstarpplat" \S\T\A\R\P\P\L\A\T\F\O\R\M)
+ (r-call pploadconfig (r-call paste \S\T\A\R\P\D\I\S\T "/config/starpd.properties" (*named* sep "")))
+ (<- dimdis (function ((*named* v *r-missing*)) (r-block (if (r-call == (r-call r-index (r-call class v) 1) "dlayoutn") (return (r-call as.numeric (r-call r-index (r-call class v) 2)))) (if (r-call ! (r-call is.null v)) (r-block (for i (r-call : (r-call length v) 1) (if (r-call > (r-call r-aref v i) 1) (return i)))) (r-block (return 1))) (return (r-call length v))) ()))
+ (<- is.scalar (function ((*named* x *r-missing*)) (&& (&& (\|\| (r-call == (r-call mode x) "numeric") (r-call == (r-call mode x) "complex")) (r-call is.null (r-call (r-call .\Primitive "dim") x))) (r-call == (r-call length x) 1)) ()))
+ (<- p 1)
+ (<- (r-call class p) (r-call c "dlayout" "numeric"))
+ (<- darray (function ((*named* id *r-missing*) (*named* shape *r-missing*) (*named* distribution *r-missing*) (*named* isreal *r-missing*)) (r-block (<- shape (r-call as.array shape)) (<- distribution (r-call + distribution 1)) (<- (r-call class shape) (r-call append "dlayoutn" (r-call to\String distribution) (r-call class shape))) (<- d (r-call list (*named* id id) (*named* shape shape) (*named* isreal isreal) (*named* logical *r-false*) () ())) (<- (r-call class d) "darray") d) ()))
+ (<- darraydist (function ((*named* da *r-missing*)) (r-call as.numeric (r-call r-aref (r-call class ($ da shape)) 2)) ()))
+ (<- is.darray (function ((*named* x *r-missing*)) (r-call == (r-call r-index (r-call class x) 1) "darray") ()))
+ (<- is.nd (function ((*named* x *r-missing*)) (r-call != (r-call length (r-call dim x)) 2) ()))
+ (<- is.darraynd (function ((*named* x *r-missing*)) (&& (r-call is.darray x) (r-call is.nd x)) ()))
+ (<- is.dlayout (function ((*named* x *r-missing*)) (r-call any (r-call == (r-call class x) "dlayout")) ()))
+ (<- vdim (function ((*named* x *r-missing*)) (if (r-call is.vector x) (r-call length x) (r-call dim x)) ()))
+ (<- \[\[.dlayoutn (<- \[.dlayoutn (function ((*named* dl *r-missing*) (*named* n *r-missing*)) (r-block (<- dd (r-call as.numeric (r-call r-aref (r-call class dl) 2))) (if (r-call == (r-call length n) 1) (r-block (if (r-call == n dd) (r-call * (r-call r-index (r-call as.vector dl) n) p) (r-call r-index (r-call as.vector dl) n))) (r-block (<- r (r-call r-index (r-call as.numeric dl) n)) (<- didi (r-call dimdis r)) (for i (r-call : 1 (r-call length n)) (r-block (if (r-call == (r-call r-aref n i) dd) (r-block (<- didi i) (break))))) (<- (r-call class r) (r-call append "dlayoutn" (r-call to\String didi) (r-call class r))) (return r)))) ())))
+ (<- print.darray (function ((*named* d *r-missing*) (*named* ... *r-missing*)) (r-block (<- sh (r-call as.vector ($ d shape))) (<- shs (r-call deparse sh)) (if (r-call > (r-call length sh) 1) (r-block (<- shs (r-call substring shs 2))) (r-block (<- shs (r-call paste "(" shs ")" (*named* sep ""))))) (r-call print.default (r-call paste "<darray id:" ($ d id) " shape:" shs " distribution:" (r-call r-aref (r-call class ($ d shape)) 2) ">" (*named* sep "")) (*named* quote *r-false*)) (r-call invisible d)) ()))
+ (<- validdist (function ((*named* dims *r-missing*) (*named* dd *r-missing*)) (r-block (if (\|\| (r-call > dd (r-call length dims)) (r-call == (r-call r-aref dims dd) 1)) (return (r-call dimdis (r-call as.vector dims)))) (return dd)) ()))
+ (<- dim.darray (function ((*named* x *r-missing*)) ($ x shape) ()))
+ (<- dim<-.darray (function ((*named* x *r-missing*) (*named* value *r-missing*)) (r-block (if (r-call == (r-call r-index (r-call class value) 1) "dlayoutn") (r-block (<- dd (r-call as.numeric (r-call r-index (r-call class value) 2)))) (<- dd (r-call darraydist x))) (<- dd (r-call validdist value dd)) (if (&& (r-call == (r-call length value) 2) (r-call == (r-call length ($ x shape)) 2)) (r-block (r-call ppcommand "ppdense_reshape" x (r-call r-aref value 1) (r-call - dd 1))) (r-block (<- d (r-call ppcommand "ppdensend_reshape" x (r-call length value) (r-call as.real value) (r-call - dd 1))) (if (r-call == (r-call length ($ d shape)) 2) (r-call ppcommand "ppdensend_clobber_singletons_and_demote" d)) d))) ()))
+ (<- length.darray (function ((*named* d *r-missing*)) (r-call prod ($ d shape)) ()))
+ (<- ppzeros (function ((*named* dims *r-missing*)) (r-block (if (r-call == (r-call length dims) 2) (r-call ppcommand "ppdense_zeros" (r-call r-aref dims 1) (r-call r-aref dims 2) (r-call dimdis dims)) (r-call ppcommand "ppdensend_add" (r-call - (r-call dimdis dims) 1) (r-call as.real dims) 1 "zeros"))) ()))
+ (<- ppones (function ((*named* dims *r-missing*)) (r-block (if (r-call == (r-call length dims) 2) (r-call ppcommand "ppdense_ones" (r-call r-aref dims 1) (r-call r-aref dims 2) (r-call dimdis dims) 1) (r-call ppcommand "ppdensend_add" (r-call - (r-call dimdis dims) 1) (r-call as.real dims) 1 "ones"))) ()))
+ (<- pprand (function ((*named* dims *r-missing*)) (r-block (if (r-call == (r-call length dims) 2) (r-call ppcommand "ppdense_rand" (r-call r-aref dims 1) (r-call r-aref dims 2) (r-call dimdis dims)) (r-call ppcommand "ppdensend_add" (r-call - (r-call dimdis dims) 1) (r-call as.real dims) 1 "rand"))) ()))
+ (<- ppback (function ((*named* m *r-missing*) (*named* dist (r-call dimdis (r-call dim m))) (*named* allowScalar *r-false*)) (r-block (if (\|\| (r-call is.darray m) (r-call == (r-call length m) 0)) (return m)) (<- lg (r-call is.logical m)) (if (&& (r-call ! (r-call is.complex m)) (r-call ! (r-call is.real m))) (r-block (if (r-call is.vector m) (<- m (r-call as.real m)) (<- m (r-call dim<- (r-call as.real m) (r-call dim m)))))) (if (r-call is.scalar m) (r-block (if allow\Scalar (return (r-call ppcommand "ppdensend_ppback_scalar" m))) (return m))) (if (r-call ! (missing dist)) (<- dist (r-call validdist dist))) (if (&& (r-call ! (r-call is.vector m)) (r-call == (r-call length (r-call dim m)) 2)) (<- d (r-call ppcommand "pp_dense_ppback" m (r-call r-index (r-call dim m) 1) (r-call r-index (r-call dim m) 2) dist)) (<- d (r-call ppcommand "ppdensend_ppback" (r-call - dist 1) (r-call as.real (r-call vdim m)) (r-call is.real m) m))) (if lg (<- ($ d logical) *r-true*)) d) ()))
+ (<- ppfront (function ((*named* da *r-missing*)) (r-block (if (r-call ! (r-call is.darray da)) (return da)) (if (r-call == (r-call length ($ da shape)) 2) (r-block (<- l (r-call ppcommand "ppdense_ppfront" da)) (if ($ da logical) (<- m (r-call as.logical (r-call r-aref l 1))) (<- m (r-call r-aref l 1))) (<- (r-call dim m) (r-call c (r-call r-aref l 2) (r-call r-aref l 3)))) (r-block (<- m (r-call ppcommand "ppdensend_ppfront" da)) (if ($ da logical) (<- m (r-call as.logical m))) (<- (r-call dim m) (r-call as.vector ($ da shape))))) m) ()))
+ (<- vector (function ((*named* mode "logical") (*named* length 0)) (r-call \Use\Method "vector" length) ()))
+ (<- vector.default (r-call .\Primitive "vector"))
+ (<- vector.dlayout (function ((*named* mode "logical") (*named* length 0)) (r-block (<- d (r-call ppzeros (r-call c 1 length))) (if (r-call == mode "logical") (<- ($ d logical) *r-true*)) d) ()))
+ (<- double (function ((*named* length 0)) (r-call vector "double" length) ()))
+ (<- logical (function ((*named* length 0)) (r-call vector "logical" length) ()))
+ (<- c (function ((*named* ... *r-missing*)) (r-block (<- args (r-call list r-dotdotdot)) (<- v (r-call (r-call .\Primitive "c") r-dotdotdot)) (<- l (r-call length args)) (if (r-call == l 0) (return v)) (for i (r-call : 1 l) (if (r-call is.dlayout (r-call r-aref args i)) (r-block (<- (r-call class v) (r-call append "dlayoutn" (r-call to\String i) (r-call class v))) (return v)))) v) ()))
+ (<- rep (function ((*named* x *r-missing*) (*named* times 1) (*named* length.out \N\A) (*named* each 1)) (r-block (if (r-call is.darray x) (r-block (<- (r-call dim x) (r-call c 1 (r-call length x))) (if (\|\| (&& (missing length.out) (r-call > (r-call length times) 1)) (r-call > each 1)) (<- x (r-call ppfront x)))) (if (r-call ! (\|\| (r-call is.dlayout times) (&& (r-call ! (missing length.out)) (r-call is.dlayout length.out)))) (r-block (return (r-call (r-call .\Primitive "rep") x (*named* times times) (*named* length.out length.out) (*named* each each)))))) (if (r-call > each 1) (r-block (<- x (r-call (r-call .\Primitive "rep") x (*named* each each))))) (if (missing length.out) (r-block (if (r-call > (r-call length times) 1) (r-block (<- x (r-call (r-call .\Primitive "rep") x (*named* times times))) (<- times 1)))) (r-block (<- times (r-call ceiling (r-call / length.out (r-call length x)))))) (if (r-call == (r-call length x) 1) (r-block (return (r-call * (r-call ppones (r-call r-aref times 1)) (r-call r-aref x 1))))) (<- x (r-call ppback (r-call as.2d x))) (<- out (r-call ppcommand "ppdense_repmat" x 1 (r-call r-aref times 1) 1)) (if (&& (r-call ! (missing length.out)) (r-call != (r-call r-aref (r-call dim out) 2) length.out)) (r-block (<- out (r-call ppcommand "ppdense_subsref_col" out (r-call as.realarray (r-call : 1 length.out)))))) (<- (r-call dim out) (r-call length out)) (return out)) ()))
+ (<- globalbinding (function ((*named* sym *r-missing*)) (r-call eval (r-call as.name sym) (*named* envir (r-call globalenv))) ()))
+ (<- boundp (function ((*named* sym *r-missing*)) (return (r-call != (r-call class (r-call try (r-call globalbinding sym) (*named* silent *r-true*))) "try-error")) ()))
+ (<- redefining (function ((*named* sym *r-missing*)) (r-block (<- name (r-call deparse (substitute sym))) (<- rname (r-call paste "R" name (*named* sep ""))) (if (r-call ! (r-call boundp rname)) (r-call assign rname (r-call globalbinding name) (*named* envir (r-call globalenv))))) ()))
+ (r-call redefining array)
+ (<- array (function ((*named* data \N\A) (*named* dim (r-call length data)) (*named* dimnames ())) (r-block (<- dd *r-false*) (if (r-call == (r-call r-index (r-call class dim) 1) "dlayoutn") (<- dd (r-call as.numeric (r-call r-index (r-call class dim) 2)))) (if (r-call is.darray data) (r-block (if (r-call != (r-call length data) (r-call prod dim)) (r-block (<- data (r-call rep data (*named* length.out (r-call prod dim)))))) (if (r-call all (r-call == dim (r-call as.vector ($ data shape)))) (return data)) (return (r-call dim<-.darray data dim))) (r-block (if dd (r-block (<- data (r-call rep data (*named* length.out (r-call * (r-call prod dim) p)))) (return (r-call dim<-.darray data dim))) (r-block (r-call \Rarray data dim dimnames)))))) ()))
+ (r-call redefining matrix)
+ (<- matrix (function ((*named* data \N\A) (*named* nrow 1) (*named* ncol 1) (*named* byrow *r-false*) (*named* dimnames ())) (r-block (<- l (r-call length data)) (if (missing nrow) (r-block (if (r-call ! (missing ncol)) (<- nrow (r-call / l ncol)) (r-block (<- nrow l) (<- ncol 1)))) (if (missing ncol) (<- ncol (r-call / l nrow)))) (<- m (r-call array data (r-call c nrow ncol) dimnames)) (if byrow (r-call t m) m)) ()))
+ (<- t.darray (function ((*named* da *r-missing*)) (r-block (if (\|\| (r-call == (r-call darraydist da) 1) (r-call == (r-call darraydist da) 2)) (r-call ppcommand "ppdense_transpose" da 0) (r-call ppcommand "pppblas_trans" da))) ()))
+ (<- runif (function ((*named* n *r-missing*) (*named* min 0) (*named* max 1)) (r-block (if (r-call is.dlayout n) (r-call pprand n) (r-call .\Internal (r-call runif n min max)))) ()))
+ (r-call redefining diag)
+ (<- diag (function ((*named* da *r-missing*) (*named* nrow *r-missing*) (*named* ncol n)) (r-block (if (r-call is.darray da) (r-block (if (r-call == (r-call length ($ da shape)) 1) (r-block (<- da (r-call as.2d da)))) (if (r-call == (r-call length ($ da shape)) 2) (r-block (if (r-call == (r-call r-index ($ da shape) 1) 1) (return (r-call ppcommand "ppdense_diagv" da 0)) (if (r-call == (r-call r-index ($ da shape) 2) 1) (return (r-call ppcommand "ppdense_diagv" (r-call t da) 0)))))) (r-call t (r-call ppcommand "ppdense_diag" da 0))) (r-call \Rdiag da))) ()))
+ (<- dbinaryop (function ((*named* code *r-missing*) (*named* scalarcode *r-missing*) (*named* bscalarcode *r-missing*) (*named* ndcode *r-missing*) (*named* a *r-missing*) (*named* b *r-missing*)) (r-block (if (r-call is.scalar a) (r-block (if (r-call is.nd b) (r-call ppcommand "ppdensend_s_binary_operator" a b ndcode *r-true*) (r-call ppcommand "ppdense_scalar_op" scalarcode a b))) (if (r-call is.scalar b) (r-block (if (r-call is.nd a) (r-call ppcommand "ppdensend_binary_operator_s" a b ndcode *r-true*) (r-call ppcommand "ppdense_scalar_op" bscalarcode b a))) (r-block (if (r-call ! (r-call is.darray a)) (<- a (r-call ppback a))) (if (r-call ! (r-call is.darray b)) (<- b (r-call ppback b))) (if (\|\| (r-call is.nd a) (r-call is.nd b)) (r-call ppcommand "ppdensend_binary_operator" a b ndcode *r-true*) (r-call ppcommand "ppdense_binary_op" code a b)))))) ()))
+ (<- +.darray (function ((*named* a *r-missing*) (*named* b *r-missing*)) (r-call dbinaryop 1 1 1 2 a b) ()))
+ (<- *.darray (function ((*named* a *r-missing*) (*named* b *r-missing*)) (r-call dbinaryop 3 3 3 3 a b) ()))
+ (<- /.darray (function ((*named* a *r-missing*) (*named* b *r-missing*)) (r-call dbinaryop 4 4 5 6 a b) ()))
+ (<- ^.darray (function ((*named* a *r-missing*) (*named* b *r-missing*)) (r-call dbinaryop 7 10 11 19 a b) ()))
+ (<- mkdlogicalop (function ((*named* c *r-missing*) (*named* sc *r-missing*) (*named* bsc *r-missing*) (*named* ndcode *r-missing*)) (r-block (function ((*named* a *r-missing*) (*named* b *r-missing*)) (r-block (<- da (r-call dbinaryop c sc bsc ndcode a b)) (<- ($ da logical) *r-true*) da) ())) ()))
+ (<- <.darray (r-call mkdlogicalop 14 16 17 15))
+ (<- >.darray (r-call mkdlogicalop 15 17 16 17))
+ (<- ==.darray (r-call mkdlogicalop 18 20 20 13))
+ (<- !=.darray (r-call mkdlogicalop 19 21 21 14))
+ (<- <=.darray (r-call mkdlogicalop 16 18 19 18))
+ (<- >=.darray (r-call mkdlogicalop 17 19 18 16))
+ (<- &.darray (function ((*named* a *r-missing*) (*named* b *r-missing*)) (r-block (if (r-call is.darray a) (<- other b) (r-block (<- other a) (<- a b))) (if (r-call is.scalar other) (r-block (if other (return (r-call ppcopy a)) (return (r-call ppzeros (r-call dim a)))))) (<- da (r-call dbinaryop 11 (r-call - 1) (r-call - 1) 9 a b)) (<- ($ da logical) *r-true*) da) ()))
+ (<- \|.darray (function ((*named* a *r-missing*) (*named* b *r-missing*)) (r-block (if (r-call is.darray a) (<- other b) (r-block (<- other a) (<- a b))) (if (r-call is.scalar other) (r-block (if other (return (r-call ppones (r-call dim a))) (return (r-call ppcopy a))))) (<- da (r-call dbinaryop 12 (r-call - 1) (r-call - 1) 10 a b)) (<- ($ da logical) *r-true*) da) ()))
+ (<- !.darray (function ((*named* a *r-missing*)) (r-block (if (r-call is.nd a) (r-block (<- da (r-call ppcommand "ppdensend_not" a))) (r-block (<- da (r-call ppcommand "ppdense_unary_op" 2 a)))) (<- ($ da logical) *r-true*) da) ()))
+ (<- %*% (function ((*named* a *r-missing*) (*named* b *r-missing*)) (r-block (if (r-call is.darray a) (r-block (if (r-call is.darray b) (r-block (r-call ppcommand "pppblas_gemm" a b)) (r-block (r-call ppcommand "pppblas_gemm" a (r-call ppback b))))) (if (r-call is.darray b) (r-block (r-call ppcommand "pppblas_gemm" (r-call ppback a) b)) (r-call (r-call .\Primitive "%*%") a b)))) ()))
+ (<- -.darray (function ((*named* a *r-missing*) (*named* b *r-missing*)) (r-block (if (missing b) (if (r-call is.nd a) (r-block (<- b a) (<- a 0)) (r-block (return (r-call ppcommand "ppdense_unary_op" 13 a))))) (if (r-call is.scalar b) (r-call dbinaryop 1 1 1 4 (r-call - b) a) (r-call dbinaryop 2 2 2 4 a b))) ()))
+ (<- ppreduce (function ((*named* da *r-missing*) (*named* axis *r-missing*) (*named* allfunc *r-missing*) (*named* axisfunc *r-missing*) (*named* ndcode *r-missing*) (*named* islogical *r-false*)) (r-block (<- nd (r-call length ($ da shape))) (if (r-call == nd 2) (r-block (if (r-call ! axis) (r-call ppcommand allfunc da) (r-block (<- res (r-call ppcommand axisfunc da axis)) (if (r-call is.list res) (<- res (r-call r-aref res 1))) (return res)))) (r-block (if (r-call ! axis) (r-block (<- (r-call dim da) (r-call length da)) (<- axis 1))) (<- res (r-call ppcommand "ppdensend_reduce" da ndcode (r-call - axis 1))) (if (&& islogical (r-call is.darray res)) (<- ($ res logical) *r-true*)) (return res)))) ()))
+ (<- any.darray (function ((*named* da *r-missing*) (*named* axis *r-false*) (*named* na.rm *r-false*)) (r-block (if (r-call == (r-call length ($ da shape)) 2) (r-block (if (r-call ! axis) (r-block (return (r-call > (r-call ppcommand "ppbase_nnz" da) 0))) (r-block (if (r-call == (r-call r-index ($ da shape) axis) 1) (return (r-call != da 0)) (r-block (<- res (r-call ppcommand "ppdense_any" da axis)) (<- ($ res logical) *r-true*) (return res)))))) (r-block (r-call ppreduce da axis "" "" 5 *r-true*)))) ()))
+ (<- all.darray (function ((*named* da *r-missing*) (*named* axis *r-false*) (*named* na.rm *r-false*)) (r-block (if (r-call == (r-call length ($ da shape)) 2) (r-block (if (r-call ! axis) (r-block (return (r-call == (r-call ppcommand "ppbase_nnz" da) (r-call length da)))) (r-block (if (r-call == (r-call r-index ($ da shape) axis) 1) (return (r-call != da 0)) (r-block (<- res (r-call ppcommand "ppdense_all" da axis)) (<- ($ res logical) *r-true*) (return res)))))) (r-block (r-call ppreduce da axis "" "" 6 *r-true*)))) ()))
+ (<- sum (function ((*named* ... *r-missing*) (*named* na.rm *r-false*) (*named* axis *r-false*)) (r-block (<- l (r-call list r-dotdotdot)) (if (r-call == (r-call length l) 0) (return 0)) (<- da (r-call r-aref l 1)) (if (r-call is.darray da) (r-call ppreduce da axis "ppdense_sumv" "ppdense_sum" 2) (r-call (r-call .\Primitive "sum") r-dotdotdot (*named* na.rm na.rm)))) ()))
+ (<- prod (function ((*named* ... *r-missing*) (*named* na.rm *r-false*) (*named* axis *r-false*)) (r-block (<- l (r-call list r-dotdotdot)) (if (r-call == (r-call length l) 0) (return 1)) (<- da (r-call r-aref l 1)) (if (r-call is.darray da) (r-call ppreduce da axis "ppdense_prodv" "ppdense_prod" 3) (r-call (r-call .\Primitive "prod") r-dotdotdot (*named* na.rm na.rm)))) ()))
+ (<- min (function ((*named* ... *r-missing*) (*named* na.rm *r-false*) (*named* axis *r-false*)) (r-block (<- l (r-call list r-dotdotdot)) (if (r-call == (r-call length l) 0) (return \Inf)) (<- da (r-call r-aref l 1)) (if (r-call is.darray da) (r-call ppreduce da axis "ppdense_minv" "ppdense_min" 8) (r-call (r-call .\Primitive "min") r-dotdotdot (*named* na.rm na.rm)))) ()))
+ (<- max (function ((*named* ... *r-missing*) (*named* na.rm *r-false*) (*named* axis *r-false*)) (r-block (<- l (r-call list r-dotdotdot)) (if (r-call == (r-call length l) 0) (return (r-call - \Inf))) (<- da (r-call r-aref l 1)) (if (r-call is.darray da) (r-call ppreduce da axis "ppdense_maxv" "ppdense_max" 7) (r-call (r-call .\Primitive "max") r-dotdotdot (*named* na.rm na.rm)))) ()))
+ (<- ppcopy (function ((*named* d *r-missing*) (*named* dist 2)) (r-block (if (\|\| (missing dist) (r-call == dist (r-call darraydist d))) (return (r-call ppcommand "ppbase_createMatrixCopy" d)) (return (r-call ppcommand "ppbase_createMatrixCopyRedist" d dist)))) ()))
+ (<- as.realarray (function ((*named* x *r-missing*)) (r-call as.array (r-call as.real x)) ()))
+ (<- as.1d (function ((*named* x *r-missing*)) (r-block (<- (r-call dim x) (r-call length x)) (return x)) ()))
+ (<- as.2d (function ((*named* x *r-missing*)) (r-block (<- (r-call dim x) (r-call c 1 (r-call length x))) (return x)) ()))
+ (<- as.real2d (function ((*named* x *r-missing*)) (r-block (<- x (r-call as.real x)) (<- (r-call dim x) (r-call c 1 (r-call length x))) (return x)) ()))
+ (<- to\Index\Vec2d (function ((*named* i *r-missing*) (*named* con *r-missing*)) (r-block (if (r-call == (r-call length i) 0) (r-block (return (r-call ppcommand "ppdense_zeros" 1 0 1)))) (return (r-call ppback (r-call as.2d i) (*named* allowScalar *r-true*)))) ()))
+ (<- to\Index\Vec (function ((*named* i *r-missing*) (*named* con *r-missing*)) (r-block (if (r-call == (r-call length i) 0) (r-block (return (r-call ppcommand "ppdensend_add" 0 0 1 "zeros")))) (return (r-call ppback i (*named* allowScalar *r-true*)))) ()))
+ (<- to\Num\Index (function ((*named* i *r-missing*)) (r-block (if (r-call ! (r-call is.darray i)) (r-block (if (r-call is.logical i) (r-block (<- \N (r-call : 1 (r-call length i))) (<- i (r-call r-index \N i)))) (return i)) (if (r-call ! ($ i logical)) (r-block (return i)))) (if (r-call != (r-call length (r-call dim i)) 2) (<- (r-call dim i) (r-call c 1 (r-call length i)))) (<- i (r-call r-aref (r-call ppcommand "ppdense_find" i 1 0 0) 1)) (<- (r-call dim i) (r-call length i)) i) ()))
+ (<- expand\Linear\Index (function ((*named* shape *r-missing*) (*named* i *r-missing*)) (r-block (<- out (r-call numeric (r-call length shape))) (for n (r-call : 1 (r-call length shape)) (r-block (<- (r-call r-aref out n) (r-call + (r-call %% (r-call - i 1) (r-call r-index shape n)) 1)) (<- i (r-call + (r-call %/% (r-call - i 1) (r-call r-index shape n)) 1)))) out) ()))
+ (<- to\Linear\Index (function ((*named* shape *r-missing*) (*named* iv *r-missing*)) (r-call + (r-call sum (r-call * (r-call - iv 1) (r-call cumprod (r-call r-index (r-call c 1 shape) (r-call - (r-call - (r-call length shape)) 1))))) 1) ()))
+ (<- to\Linear\Indexes (function ((*named* shape *r-missing*) (*named* im *r-missing*)) (r-block (<- ds (r-call t (r-call array (r-call cumprod (r-call r-index (r-call c 1 shape) (r-call - (r-call - (r-call length shape)) 1))) (r-call rev (r-call dim im))))) (r-call as.1d (r-call + (r-call apply (r-call * (r-call - im 1) ds) 1 sum) 1))) ()))
+ (<- starpcolon (quote :missingarg:))
+ (<- is.colon (function ((*named* x *r-missing*)) (r-call identical x starpcolon) ()))
+ (<- normalize\Indexes (function ((*named* shape *r-missing*) (*named* idxs *r-missing*)) (r-block (<- li (r-call length idxs)) (<- out (r-call vector "list" li)) (if (r-call == li 0) (return out) (if (&& (r-call > li 1) (r-call != li (r-call length shape))) (r-call stop "wrong number of subscripts"))) (for n (r-call : 1 li) (r-block (<- i (r-call r-aref idxs n)) (if (r-call == (r-call length (r-call dim i)) 2) (r-block (<- i (r-call to\Linear\Indexes shape i)) (if (r-call == (r-call length i) 1) (<- i (r-call as.real (r-call as.vector (r-call ppfront i)))))) (if (r-call ! (r-call is.colon i)) (r-block (if (r-call > (r-call length (r-call dim i)) 2) (r-block (<- i (r-call as.1d i)))) (<- lg (\|\| (r-call is.logical i) (&& (r-call is.darray i) ($ i logical)))) (if (&& lg (r-call == li 1)) (<- i (r-call rep i (*named* length.out (r-call prod shape))))) (<- i (r-call to\Num\Index i)) (if (r-call ! lg) (r-block (<- nonz (r-call != i 0)) (if (r-call ! (r-call is.darray nonz)) (r-block (<- i (r-call r-index i nonz))) (r-block (<- where (r-call r-aref (r-call ppcommand "ppdense_find" (r-call as.2d i) 1 0 0) 1)) (<- i (r-call ppcommand "ppdense_subsref_dcol" i where)))))) (if (r-call == (r-call length i) 1) (<- i (r-call as.real (r-call as.vector (r-call ppfront i))))) (if (&& (r-call is.scalar i) (r-call < i 0)) (r-block (<- i (r-call r-index (r-call : 1 (r-call r-index shape n)) i))))))) (<- (r-call r-aref out n) i))) out) ()))
+ (<- index\Sizes (function ((*named* d *r-missing*) (*named* idxs *r-missing*)) (r-block (<- n (r-call length idxs)) (<- whichcolons (r-call logical n)) (<- lens (r-call numeric n)) (for i (r-call : 1 n) (r-block (if (r-call is.colon (r-call r-aref idxs i)) (r-block (<- (r-call r-index whichcolons i) *r-true*) (<- (r-call r-index lens i) (r-call r-index (r-call dim d) i))) (<- (r-call r-index lens i) (r-call length (r-call r-aref idxs i)))))) (r-call list lens whichcolons)) ()))
+ (<- \[.darray (function ((*named* d *r-missing*) (*named* ... *r-missing*)) (r-block (<- n (r-call nargs)) (if (r-call == n 1) (return d)) (<- idxs (r-call normalize\Indexes (r-call dim d) (r-call revealargs (r-call get "...")))) (<- tmp (r-call index\Sizes d idxs)) (<- lens (r-call r-aref tmp 1)) (<- whichcolons (r-call r-aref tmp 2)) (if (r-call == (r-call prod lens) 0) (r-block (return (r-call array 0 (r-call r-index lens (r-call != lens 1)))))) (if (r-call all whichcolons) (return (r-call ppcopy d))) (if (r-call == n 2) (r-block (if (r-call == (r-call length (r-call dim d)) 2) (<- x (r-call ppcommand "ppdense_subsref_idx" d (r-call ppback (r-call as.2d (r-call r-aref idxs 1))))) (<- x (r-call ppcommand "ppdensend_subsref_idx_dist" d (r-call ppback (r-call r-aref idxs 1) (*named* allowScalar *r-true*))))) (if (r-call == (r-call length (r-call r-aref idxs 1)) 1) (return (r-call ppfront x)) (return x))) (if (r-call == n 3) (r-block (<- r (r-call r-aref idxs 1)) (<- c (r-call r-aref idxs 2)) (if (&& (r-call is.scalar r) (r-call is.scalar c)) (r-block (return (r-call ppcommand "ppdense_viewelement" d r c)))) (if (r-call is.colon r) (r-block (if (r-call is.darray c) (<- a (r-call ppcommand "ppdense_subsref_dcol" d c)) (<- a (r-call ppcommand "ppdense_subsref_col" d (r-call as.realarray c))))) (if (r-call is.colon c) (r-block (if (r-call is.darray r) (<- a (r-call ppcommand "ppdense_subsref_drow" d r)) (<- a (r-call ppcommand "ppdense_subsref_row" d (r-call as.realarray r))))) (r-block (if (r-call ! (r-call is.darray r)) (<- r (r-call as.realarray r))) (if (r-call ! (r-call is.darray c)) (<- c (r-call as.realarray c))) (<- a (r-call ppcommand "ppdense_subsref_rowcol" d r c))))) (if (r-call == (r-call sum (r-call > lens 1)) 1) (return (r-call as.1d a))) (return a)))) (if (r-call all (r-call lapply idxs is.scalar)) (r-block (return (r-call ppcommand "ppdensend_subsref_scalar" d (r-call as.numeric idxs))))) (if (r-call == (r-call sum whichcolons) (r-call - (r-call length (r-call dim d)) 1)) (r-block (<- slice (r-call as.realarray (r-call r-aref (r-call r-index idxs (r-call ! whichcolons)) 1))) (<- slicepos (r-call r-index (r-call : 1 (r-call length idxs)) (r-call ! whichcolons))) (if (r-call == slicepos (r-call darraydist d)) (r-block (if (r-call > (r-call length slice) 1) (r-block (<- (r-call dim slice) (r-call c (r-call length slice) 1)) (<- slice (r-call ppback slice)))) (<- result (r-call ppcommand "ppdensend_subsref_extract_slices_dist" d slice))) (r-block (<- result (r-call ppcommand "ppdensend_subsref_extract_slices_local" d (r-call - slicepos 1) slice))))) (r-block (<- idxs (r-call lapply idxs (function ((*named* i *r-missing*)) (if (r-call is.colon i) (r-call - 1) (r-call as.realarray i)) ()))) (<- al (r-call append "ppdensend_subsref_element_list" (r-call append 0 idxs))) (<- (r-call r-aref al 2) d) (<- result (r-call ppvcommand al)) (if (r-call == (r-call sum (r-call > lens 1)) 1) (return (r-call as.1d result))))) (return result)) ()))
+ (<- \[<-.darray (function ((*named* d *r-missing*) (*named* ... *r-missing*)) (r-block (<- n (r-call nargs)) (<- arglist (r-call revealargs (r-call get "..."))) (<- rhs (r-call r-aref arglist (r-call - n 1))) (<- idxs (r-call normalize\Indexes (r-call dim d) (r-call r-index arglist (r-call + (r-call - n) 1)))) (if (&& (r-call == (r-call length idxs) 1) (r-call is.colon (r-call r-aref idxs 1))) (r-block (<- idxs (r-call rep (r-call list starpcolon) (*named* length.out (r-call length (r-call dim d))))) (<- n (r-call + 2 (r-call length (r-call dim d)))))) (<- tmp (r-call index\Sizes d idxs)) (<- lens (r-call r-aref tmp 1)) (<- whichcolons (r-call r-aref tmp 2)) (if (r-call == (r-call prod lens) 0) (r-block (return d))) (if (r-call ! (r-call is.scalar rhs)) (r-block (if (&& (r-call != (r-call length rhs) (r-call prod lens)) (r-call > (r-call prod lens) 1)) (r-block (<- rhs (r-call rep rhs (*named* length.out (r-call prod lens)))))) (if (r-call is.darray rhs) (r-block (if (r-call == (r-call length (r-call dim rhs)) 1) (<- rhs (r-call as.2d rhs)))) (r-block (<- rhs (r-call as.array rhs)) (if (r-call == (r-call length (r-call dim rhs)) 1) (<- rhs (r-call as.2d rhs))) (<- rhs (r-call ppback rhs)))))) (if (r-call == (r-call length (r-call dim d)) 2) (r-block (if (r-call all whichcolons) (if (r-call is.scalar rhs) (r-call ppcommand "ppdense_setall" d rhs) (r-call ppcommand "ppdense_copyall" rhs d)) (if (r-call == n 3) (r-block (if (r-call is.scalar rhs) (r-call ppcommand "ppdense_subsasgn_idx_s" d (r-call ppback (r-call as.2d (r-call r-aref idxs 1))) rhs) (r-call ppcommand "ppdense_subsasgn_idx" d (r-call ppback (r-call as.2d (r-call r-aref idxs 1))) (r-call ppback rhs)))) (r-block (<- r (r-call r-aref idxs 1)) (<- c (r-call r-aref idxs 2)) (if (&& (r-call is.scalar r) (r-call is.scalar c)) (r-block (if (r-call ! (r-call is.scalar rhs)) (r-call stop "expected scalar value")) (r-call ppcommand "ppdense_setelement" d r c rhs)) (if (r-call is.colon r) (r-block (if (r-call is.darray c) (r-block (if (r-call is.scalar rhs) (r-call ppcommand "ppdense_subsasgn_dcol_s" d c rhs) (r-call ppcommand "ppdense_subsasgn_dcol" d c rhs))) (r-block (if (r-call is.scalar rhs) (r-call ppcommand "ppdense_subsasgn_col_s" d (r-call as.real2d c) rhs) (r-call ppcommand "ppdense_subsasgn_col" d (r-call as.real2d c) rhs))))) (if (r-call is.colon c) (r-block (if (r-call is.darray r) (r-block (if (r-call is.scalar rhs) (r-call ppcommand "ppdense_subsasgn_drow_s" d r rhs) (r-call ppcommand "ppdense_subsasgn_drow" d r rhs))) (r-block (if (r-call is.scalar rhs) (r-call ppcommand "ppdense_subsasgn_row_s" d (r-call as.real2d r) rhs) (r-call ppcommand "ppdense_subsasgn_row" d (r-call as.real2d r) rhs))))) (r-block (if (r-call ! (r-call is.darray r)) (<- r (r-call as.realarray r))) (if (r-call ! (r-call is.darray c)) (<- c (r-call as.realarray c))) (if (r-call is.scalar rhs) (r-call ppcommand "ppdense_subsasgn_rowcol_s" d r c rhs) (r-call ppcommand "ppdense_subsasgn_rowcol" d r c rhs))))))))) (return d)) (r-block (if (r-call == n 3) (r-call ppcommand "ppdensend_subsasgn_idx" d (r-call ppback (r-call as.2d (r-call r-aref idxs 1))) rhs) (if (r-call all (r-call lapply idxs is.scalar)) (r-block (r-call ppcommand "ppdensend_subsasgn_scalar" d (r-call as.numeric idxs) rhs)) (if (r-call == (r-call sum whichcolons) (r-call - (r-call length (r-call dim d)) 1)) (r-block (<- slice (r-call as.realarray (r-call r-aref (r-call r-index idxs (r-call ! whichcolons)) 1))) (<- slicepos (r-call r-index (r-call : 1 (r-call length idxs)) (r-call ! whichcolons))) (r-call ppcommand "ppdensend_subsasgn_slice" d (r-call - slicepos 1) slice rhs)) (r-block (<- idxs (r-call lapply idxs (function ((*named* i *r-missing*)) (if (r-call is.colon i) (r-call - 1) (r-call as.realarray i)) ()))) (<- al (r-call append "ppdensend_subsasgn_tuple" (r-call append 0 (r-call append idxs 0)))) (<- (r-call r-aref al 2) d) (<- (r-call r-aref al (r-call length al)) rhs) (r-call ppvcommand al))))))) d) ()))
+ (<- unaryops (r-call list (r-call list "ceiling" 9 "ceil") (r-call list "round" 10) (r-call list "floor" 11) (r-call list "sign" 14) (r-call list "abs" 15) (r-call list "sqrt" 16 *r-false*) (r-call list "exp" 17) (r-call list "log10" 19) (r-call list "log2" 20) (r-call list "Conj" 8 *r-false*) (r-call list "sin" 21) (r-call list "cos" 22) (r-call list "tan" 23)))
+ (<- mkunaryop (function ((*named* code *r-missing*) (*named* oldf *r-missing*) (*named* ndname *r-missing*)) (r-block (r-call force code) (r-call force oldf) (if (r-call is.character ndname) (r-block (<- ndname (r-call paste "ppdensend_" ndname (*named* sep ""))) (function ((*named* x *r-missing*)) (r-block (if (r-call is.darray x) (r-block (if (r-call == (r-call length ($ x shape)) 2) (r-call ppcommand "ppdense_unary_op" code x) (r-call ppcommand ndname x))) (r-call oldf x))) ())) (r-block (function ((*named* x *r-missing*)) (r-block (if (r-call is.darray x) (r-call ppcommand "ppdense_unary_op" code x) (r-call oldf x))) ())))) ()))
+ (for i unaryops (r-block (<- ppname (r-call as.name (r-call r-aref i 1))) (<- \Rf (r-call eval ppname)) (if (r-call == (r-call length i) 2) (<- ndn (r-call r-aref i 1)) (<- ndn (r-call r-aref i 3))) (r-call assign (r-call as.character ppname) (r-call mkunaryop (r-call r-aref i 2) \Rf ndn) (*named* envir (r-call globalenv)))))
+ (r-call redefining chol)
+ (<- chol (function ((*named* m *r-missing*)) (r-block (if (r-call is.darray m) (r-block (<- l (r-call ppcommand "ppscalapack_chol" m)) (if (r-call > (r-call r-aref l 1) 0) (r-call stop "chol: not positive definite.")) (return (r-call r-aref l 2)))) (r-call \Rchol m)) ()))
+ (r-call redefining ginv)
+ (<- ginv (function ((*named* m *r-missing*)) (r-block (if (r-call is.darray m) (r-block (<- l (r-call ppcommand "ppscalapack_inv" m)) (return (r-call r-aref l 1)))) (r-call \Rginv m)) ()))
+ (r-call redefining eigen)
+ (<- eigen (function ((*named* x *r-missing*) (*named* symmetric *r-missing*) (*named* only.values *r-false*) (*named* EISPACK *r-false*)) (r-block (if (r-call ! (r-call is.darray x)) (return (r-call \Reigen x symmetric only.values \E\I\S\P\A\C\K))) (if only.values (<- vl 0) (<- vl 1)) (if (&& (r-call ! (missing symmetric)) symmetric) (r-block (<- res (r-call ppcommand "ppscalapack_eig_sym" x vl))) (r-block (<- res (r-call ppcommand "ppscalapack_eig" x vl)))) (<- out (r-call list (*named* values ()) (*named* vectors ()))) (if only.values (r-block (<- ($ out values) (r-call t res))) (r-block (if (&& (r-call ! (missing symmetric)) symmetric) (r-block (<- ($ out values) (r-call t (r-call r-aref res 2)))) (r-block (<- ($ out values) (r-call diag (r-call r-aref res 2))))) (<- ($ out vectors) (r-call r-aref res 1)))) out) ()))
+ (r-call redefining apply)
+ (<- apply (function ((*named* d *r-missing*) (*named* axis *r-missing*) (*named* f *r-missing*)) (r-block (if (r-call ! (r-call is.darray d)) (return (r-call \Rapply d axis f))) (<- axis (r-call + axis 1)) (if (r-call identical f sum) (r-call t (r-call ppcommand "ppdense_sum" d axis)) (r-call stop "starp: unsupported operation"))) ()))
+ (r-call redefining diag<-)
+ (<- diag<- (function ((*named* d *r-missing*) (*named* value *r-missing*)) (r-block (if (r-call ! (r-call is.darray d)) (r-block (if (r-call is.darray value) (<- value (r-call ppfront value))) (return (r-call \Rdiag<- d value)))) (if (r-call != (r-call length (r-call dim d)) 2) (r-call stop "starp diag<-: only supported for 2d")) (<- n (r-call min (r-call dim d))) (<- idxs (r-call ppcommand "ppdense_makeRange" 1 (r-call + (r-call r-index (r-call dim d) 1) 1) (r-call + (r-call * (r-call - n 1) (r-call r-index (r-call dim d) 1)) n))) (if (r-call is.scalar value) (r-block (r-call ppcommand "ppdense_subsasgn_idx_s" d idxs value)) (if (r-call != (r-call length value) n) (r-block (r-call stop "diag<-: replacement diagonal has wrong length")) (r-block (r-call ppcommand "ppdense_subsasgn_idx" d idxs (r-call ppback (r-call as.2d value)))))) d) ()))
+ (<- engine\Arg (function ((*named* arg *r-missing*)) (r-block (<- arg (r-call tolower arg)) (if (r-call != arg "") (r-block (if (r-call != arg "c") (r-call stop "unknown engine specified")))) (return arg)) ()))
+ (<- pploadcenginemodule (function ((*named* filename *r-missing*) (*named* name "")) (r-block (<- res (r-call ppcommand "ppemode2_evaluate" "c" "ppevalc_builtin:load_module" 1 0 filename name)) (return (r-call r-aref (r-call ppcommand "ppemode2_getelement" (r-call r-index (r-call r-aref res 1) 1) 0) 2))) ()))
+ (<- ppunloadcenginemodule (function ((*named* name *r-missing*)) (r-block (r-call ppcommand "ppemode2_evaluate" "c" "ppevalc_builtin:remove_module" 1 0 name) *r-true*) ()))
+ (<- pploadpackage (function ((*named* filename *r-missing*) (*named* name "") (*named* engine "")) (r-block (<- engine (r-call engine\Arg engine)) (if (r-call == engine "c") (r-call pploadcenginemodule filename (*named* name name)) (r-block (<- out (r-call ppcommand "ppbase_loadUserPackage" filename name)) (if (r-call > (r-call length out) 1) (r-block (r-call warning (r-call r-index out 2)) (return (r-call r-index out 1)))) (return out)))) ()))
+ (<- ppunloadpackage (function ((*named* name *r-missing*) (*named* engine "")) (r-block (<- engine (r-call engine\Arg engine)) (if (r-call == engine "c") (r-call ppunloadcenginemodule name) (r-call ppcommand "ppbase_removeUserPackage" name)) *r-true*) ())))
--- /dev/null
+++ b/femtolisp/ast/system.lsp
@@ -1,0 +1,511 @@
+; femtoLisp standard library
+; by Jeff Bezanson
+; Public Domain
+
+(set 'list (lambda args args))
+
+(set 'setq (macro (name val)
+ (list set (list 'quote name) val)))
+
+(setq sp '| |)
+(setq nl '|
+|)
+
+; 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.
+(setq f-body (lambda (e)
+ (cond ((atom e) e)
+ ((eq (cdr e) ()) (car e))
+ (T (cons 'progn e)))))
+
+(setq defmacro
+ (macro (name args . body)
+ (list 'setq name (list 'macro args (f-body body)))))
+
+; support both CL defun and Scheme-style define
+(defmacro defun (name args . body)
+ (list 'setq name (list 'lambda args (f-body body))))
+
+(defmacro define (name . body)
+ (if (symbolp name)
+ (list 'setq name (car body))
+ (cons 'defun (cons (car name) (cons (cdr name) body)))))
+
+(defun identity (x) x)
+(setq null not)
+(defun consp (x) (not (atom x)))
+
+(defun map (f lst)
+ (if (atom lst) lst
+ (cons (f (car lst)) (map f (cdr lst)))))
+
+(defmacro let (binds . body)
+ (cons (list 'lambda
+ (map (lambda (c) (if (consp c) (car c) c)) binds)
+ (f-body body))
+ (map (lambda (c) (if (consp c) (cadr c) nil)) binds)))
+
+(defun nconc lsts
+ (cond ((null lsts) ())
+ ((null (cdr lsts)) (car lsts))
+ (T ((lambda (l d) (if (null l) d
+ (prog1 l
+ (while (consp (cdr l)) (set 'l (cdr l)))
+ (rplacd l d))))
+ (car lsts) (apply nconc (cdr lsts))))))
+
+(defun 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))))))
+
+(defun member (item lst)
+ (cond ((atom lst) ())
+ ((equal (car lst) item) lst)
+ (T (member item (cdr lst)))))
+
+(defun macrop (e) (and (consp e) (eq (car e) 'macro) e))
+(defun macrocallp (e) (and (symbolp (car e))
+ (boundp (car e))
+ (macrop (eval (car e)))))
+(defun macroapply (m args) (apply (cons 'lambda (cdr m)) args))
+
+(defun macroexpand-1 (e)
+ (if (atom e) e
+ (let ((f (macrocallp e)))
+ (if f (macroapply f (cdr e))
+ e))))
+
+; convert to proper list, i.e. remove "dots", and append
+(defun append.2 (l tail)
+ (cond ((null l) tail)
+ ((atom l) (cons l tail))
+ (T (cons (car l) (append.2 (cdr l) tail)))))
+
+(define (cadr x) (car (cdr x)))
+
+(defun macroexpand (e)
+ ((label mexpand
+ (lambda (e env f)
+ (progn
+ (while (and (consp e)
+ (not (member (car e) env))
+ (set 'f (macrocallp e)))
+ (set 'e (macroapply f (cdr e))))
+ (if (and (consp e)
+ (not (eq (car e) 'quote)))
+ (let ((newenv
+ (if (and (or (eq (car e) 'lambda) (eq (car e) 'macro))
+ (consp (cdr e)))
+ (append.2 (cadr e) env)
+ env)))
+ (map (lambda (x) (mexpand x newenv nil)) e))
+ e))))
+ e nil nil))
+
+; uncomment this to macroexpand functions at definition time.
+; makes typical code ~25% faster, but only works for defun expressions
+; at the top level.
+(defmacro defun (name args . body)
+ (list 'setq name (list 'lambda args (macroexpand (f-body body)))))
+
+; same thing for macros. enabled by default because macros are usually
+; defined at the top level.
+(defmacro defmacro (name args . body)
+ (list 'setq name (list 'macro args (macroexpand (f-body body)))))
+
+(setq = eq)
+(setq eql eq)
+(define (/= a b) (not (eq 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 (abs x) (if (< x 0) (- x) x))
+(define (truncate x) x)
+(setq K prog1) ; K combinator ;)
+(define (funcall f . args) (apply f args))
+(define (symbol-function sym) (eval sym))
+(define (symbol-value sym) (eval sym))
+
+(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 (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 (equal a b)
+ (if (and (consp a) (consp b))
+ (and (equal (car a) (car b))
+ (equal (cdr a) (cdr b)))
+ (eq a b)))
+
+; compare imposes an ordering on all values. yields -1 for a<b,
+; 0 for a==b, and 1 for a>b. lists are compared up to the first
+; point of difference.
+(defun compare (a b)
+ (cond ((eq a b) 0)
+ ((or (atom a) (atom b)) (if (< a b) -1 1))
+ (T (let ((c (compare (car a) (car b))))
+ (if (not (eq c 0))
+ c
+ (compare (cdr a) (cdr b)))))))
+
+(defun every (pred lst)
+ (or (atom lst)
+ (and (pred (car lst))
+ (every pred (cdr lst)))))
+
+(defun any (pred lst)
+ (and (consp lst)
+ (or (pred (car lst))
+ (any pred (cdr lst)))))
+
+(defun listp (a) (or (eq a ()) (consp a)))
+
+(defun length (l)
+ (if (null l) 0
+ (+ 1 (length (cdr l)))))
+
+(defun nthcdr (n lst)
+ (if (<= n 0) lst
+ (nthcdr (- n 1) (cdr lst))))
+
+(defun list-ref (lst n)
+ (car (nthcdr n lst)))
+
+(defun list* l
+ (if (atom (cdr l))
+ (car l)
+ (cons (car l) (apply list* (cdr l)))))
+
+(defun nlist* l
+ (if (atom (cdr l))
+ (car l)
+ (rplacd l (apply nlist* (cdr l)))))
+
+(defun lastcdr (l)
+ (if (atom l) l
+ (lastcdr (cdr l))))
+
+(defun last (l)
+ (cond ((atom l) l)
+ ((atom (cdr l)) l)
+ (T (last (cdr l)))))
+
+(defun map! (f lst)
+ (prog1 lst
+ (while (consp lst)
+ (rplaca lst (f (car lst)))
+ (set 'lst (cdr lst)))))
+
+(defun mapcar (f . lsts)
+ ((label mapcar-
+ (lambda (lsts)
+ (cond ((null lsts) (f))
+ ((atom (car lsts)) (car lsts))
+ (T (cons (apply f (map car lsts))
+ (mapcar- (map cdr lsts)))))))
+ lsts))
+
+(defun transpose (M) (apply mapcar (cons list M)))
+
+(defun filter (pred lst)
+ (cond ((null lst) ())
+ ((not (pred (car lst))) (filter pred (cdr lst)))
+ (T (cons (car lst) (filter pred (cdr lst))))))
+
+(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 lst) (foldl cons nil lst))
+
+(defun reduce (f zero lst)
+ (if (null lst) zero
+ (reduce f (f zero (car lst)) (cdr lst))))
+
+(define (copy-list l)
+ (if (atom l) l
+ (cons (car l)
+ (copy-list (cdr l)))))
+(define (copy-tree l)
+ (if (atom l) l
+ (cons (copy-tree (car l))
+ (copy-tree (cdr l)))))
+
+(define (assoc item lst)
+ (cond ((atom lst) ())
+ ((eq (caar lst) item) (car lst))
+ (T (assoc item (cdr lst)))))
+
+(define (nreverse l)
+ (let ((prev nil))
+ (while (consp l)
+ (set 'l (prog1 (cdr l)
+ (rplacd l (prog1 prev
+ (set 'prev l))))))
+ prev))
+
+(defmacro let* (binds . body)
+ (cons (list 'lambda (map car binds)
+ (cons 'progn
+ (nconc (map (lambda (b) (cons 'setq b)) binds)
+ body)))
+ (map (lambda (x) nil) binds)))
+
+(defmacro labels (binds . body)
+ (cons (list 'lambda (map car binds)
+ (cons 'progn
+ (nconc (map (lambda (b)
+ (list 'setq (car b) (cons 'lambda (cdr b))))
+ binds)
+ body)))
+ (map (lambda (x) nil) binds)))
+
+(defmacro when (c . body) (list if c (f-body body) nil))
+(defmacro unless (c . body) (list if c nil (f-body body)))
+
+(defmacro dotimes (var . body)
+ (let ((v (car var))
+ (cnt (cadr var)))
+ (list 'let (list (list v 0))
+ (list 'while (list < v cnt)
+ (list prog1 (f-body body) (list 'setq v (list + v 1)))))))
+
+(defun map-int (f n)
+ (let ((acc nil))
+ (dotimes (i n)
+ (setq acc (cons (f i) acc)))
+ (nreverse acc)))
+
+(defun error args (raise (cons 'error args)))
+
+(defmacro throw (tag value) `(raise (list 'thrown-value ,tag ,value)))
+(defmacro catch (tag expr)
+ (let ((e (gensym)))
+ `(trycatch ,expr
+ (lambda (,e) (if (and (consp ,e)
+ (eq (car ,e) 'thrown-value)
+ (eq (cadr ,e) ,tag))
+ (caddr ,e)
+ (raise ,e))))))
+
+(defmacro unwind-protect (expr finally)
+ (let ((e (gensym)))
+ `(prog1 (trycatch ,expr
+ (lambda (,e) (progn ,finally (raise ,e))))
+ ,finally)))
+
+; (try expr
+; (catch (type-error e) . exprs)
+; (catch (io-error e) . exprs)
+; (catch (e) . exprs)
+; (finally . exprs))
+(defmacro try (expr . forms)
+ (let* ((e (gensym))
+ (reraised (gensym))
+ (final (f-body (cdr (or (assoc 'finally forms) '(())))))
+ (catches (filter (lambda (f) (eq (car f) 'catch)) forms))
+ (catchblock `(cond
+ ,.(map (lambda (catc)
+ (let* ((specific (cdr (cadr catc)))
+ (extype (caadr catc))
+ (var (if specific (car specific)
+ extype))
+ (todo (cddr catc)))
+ `(,(if specific
+ ; exception matching logic
+ `(or (eq ,e ',extype)
+ (and (consp ,e)
+ (eq (car ,e)
+ ',extype)))
+ T); (catch (e) ...), match anything
+ (let ((,var ,e)) ,@todo))))
+ catches)
+ (T (raise ,e))))) ; no matches, reraise
+ (if final
+ (if catches
+ ; form with both catch and finally
+ `(prog1 (trycatch ,expr
+ (lambda (,e)
+ (trycatch ,catchblock
+ (lambda (,reraised)
+ (progn ,final
+ (raise ,reraised))))))
+ ,final)
+ ; finally only; same as unwind-protect
+ `(prog1 (trycatch ,expr (lambda (,e)
+ (progn ,final (raise ,e))))
+ ,final))
+ ; catch, no finally
+ `(trycatch ,expr (lambda (,e) ,catchblock)))))
+
+; property lists
+(setq *plists* nil)
+
+(defun symbol-plist (sym)
+ (cdr (or (assoc sym *plists*) '(()))))
+
+(defun set-symbol-plist (sym lst)
+ (let ((p (assoc sym *plists*)))
+ (if (null p) ; sym has no plist yet
+ (setq *plists* (cons (cons sym lst) *plists*))
+ (rplacd p lst))))
+
+(defun get (sym prop)
+ (let ((pl (symbol-plist sym)))
+ (if pl
+ (let ((pr (member prop pl)))
+ (if pr (cadr pr) nil))
+ nil)))
+
+(defun put (sym prop val)
+ (let ((p (assoc sym *plists*)))
+ (if (null p) ; sym has no plist yet
+ (setq *plists* (cons (list sym prop val) *plists*))
+ (let ((pr (member prop p)))
+ (if (null pr) ; sym doesn't have this property yet
+ (rplacd p (cons prop (cons val (cdr p))))
+ (rplaca (cdr pr) val)))))
+ val)
+
+; setf
+; expands (setf (place x ...) v) to (mutator (f x ...) v)
+; (mutator (identity x ...) v) is interpreted as (mutator x ... v)
+(setq *setf-place-list*
+ ; place mutator f
+ '((car rplaca identity)
+ (cdr rplacd identity)
+ (caar rplaca car)
+ (cadr rplaca cdr)
+ (cdar rplacd car)
+ (cddr rplacd cdr)
+ (caaar rplaca caar)
+ (caadr rplaca cadr)
+ (cadar rplaca cdar)
+ (caddr rplaca cddr)
+ (cdaar rplacd caar)
+ (cdadr rplacd cadr)
+ (cddar rplacd cdar)
+ (cdddr rplacd cddr)
+ (get put identity)
+ (aref aset identity)
+ (symbol-function set identity)
+ (symbol-value set identity)
+ (symbol-plist set-symbol-plist identity)))
+
+(defun setf-place-mutator (place val)
+ (if (symbolp place)
+ (list 'setq place val)
+ (let ((mutator (assoc (car place) *setf-place-list*)))
+ (if (null mutator)
+ (error '|setf: unknown place | (car place))
+ (if (eq (caddr mutator) 'identity)
+ (cons (cadr mutator) (append (cdr place) (list val)))
+ (list (cadr mutator)
+ (cons (caddr mutator) (cdr place))
+ val))))))
+
+(defmacro setf args
+ (f-body
+ ((label setf-
+ (lambda (args)
+ (if (null args)
+ nil
+ (cons (setf-place-mutator (car args) (cadr args))
+ (setf- (cddr args))))))
+ args)))
+
+(defun revappend (l1 l2) (nconc (reverse l1) l2))
+(defun nreconc (l1 l2) (nconc (nreverse l1) l2))
+
+(defun list-to-vector (l) (apply vector l))
+(defun vector-to-list (v)
+ (let ((i (- (length v) 1))
+ (l nil))
+ (while (>= i 0)
+ (setq l (cons (aref v i) l))
+ (setq i (- i 1)))
+ l))
+
+(defun self-evaluating-p (x)
+ (or (eq x nil)
+ (eq x T)
+ (and (atom x)
+ (not (symbolp x)))))
+
+(defun functionp (x)
+ (or (builtinp x)
+ (and (consp x) (eq (car x) 'lambda))))
+
+; backquote
+(defmacro backquote (x) (bq-process x))
+
+(defun splice-form-p (x)
+ (or (and (consp x) (or (eq (car x) '*comma-at*)
+ (eq (car x) '*comma-dot*)))
+ (eq x '*comma*)))
+
+(defun bq-process (x)
+ (cond ((self-evaluating-p x)
+ (if (vectorp x)
+ (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))
+ ((eq (car x) 'backquote) (bq-process (bq-process (cadr x))))
+ ((eq (car x) '*comma*) (cadr x))
+ ((not (any splice-form-p x))
+ (let ((lc (lastcdr x))
+ (forms (map bq-bracket1 x)))
+ (if (null lc)
+ (cons 'list forms)
+ (nconc (cons 'nlist* forms) (list (bq-process lc))))))
+ (T (let ((p x) (q ()))
+ (while (and (consp p)
+ (not (eq (car p) '*comma*)))
+ (setq q (cons (bq-bracket (car p)) q))
+ (setq p (cdr p)))
+ (let ((forms
+ (cond ((consp 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)))))))
+
+(defun bq-bracket (x)
+ (cond ((atom x) (list cons (bq-process x) nil))
+ ((eq (car x) '*comma*) (list cons (cadr x) nil))
+ ((eq (car x) '*comma-at*) (list 'copy-list (cadr x)))
+ ((eq (car x) '*comma-dot*) (cadr x))
+ (T (list cons (bq-process x) nil))))
+
+; bracket without splicing
+(defun bq-bracket1 (x)
+ (if (and (consp x) (eq (car x) '*comma*))
+ (cadr x)
+ (bq-process x)))
+
+(defmacro assert (expr) `(if ,expr T (raise '(assert-failed ,expr))))
--- /dev/null
+++ b/femtolisp/attic/flutils.c
@@ -1,0 +1,59 @@
+typedef struct {
+ size_t n, maxsize;
+ unsigned long *items;
+} ltable_t;
+
+void ltable_init(ltable_t *t, size_t n)
+{
+ t->n = 0;
+ t->maxsize = n;
+ t->items = (unsigned long*)malloc(n * sizeof(unsigned long));
+}
+
+void ltable_clear(ltable_t *t)
+{
+ t->n = 0;
+}
+
+void ltable_insert(ltable_t *t, unsigned long item)
+{
+ unsigned long *p;
+
+ if (t->n == t->maxsize) {
+ p = realloc(t->items, (t->maxsize*2)*sizeof(unsigned long));
+ if (p == NULL) return;
+ t->items = p;
+ t->maxsize *= 2;
+ }
+ t->items[t->n++] = item;
+}
+
+#define LT_NOTFOUND ((int)-1)
+
+int ltable_lookup(ltable_t *t, unsigned long item)
+{
+ int i;
+ for(i=0; i < (int)t->n; i++)
+ if (t->items[i] == item)
+ return i;
+ return LT_NOTFOUND;
+}
+
+void ltable_adjoin(ltable_t *t, unsigned long item)
+{
+ if (ltable_lookup(t, item) == LT_NOTFOUND)
+ ltable_insert(t, item);
+}
+
+char *snprintf_gensym_id(char *nbuf, size_t n, u_int32_t g)
+{
+ size_t i=n-1;
+
+ nbuf[i--] = '\0';
+ do {
+ nbuf[i--] = '0' + g%10;
+ g/=10;
+ } while (g && i);
+ nbuf[i] = 'g';
+ return &nbuf[i];
+}
--- /dev/null
+++ b/femtolisp/attic/plists.lsp
@@ -1,0 +1,28 @@
+; property lists. they really suck.
+(setq *plists* nil)
+
+(defun symbol-plist (sym)
+ (cdr (or (assoc sym *plists*) '(()))))
+
+(defun set-symbol-plist (sym lst)
+ (let ((p (assoc sym *plists*)))
+ (if (null p) ; sym has no plist yet
+ (setq *plists* (cons (cons sym lst) *plists*))
+ (rplacd p lst))))
+
+(defun get (sym prop)
+ (let ((pl (symbol-plist sym)))
+ (if pl
+ (let ((pr (member prop pl)))
+ (if pr (cadr pr) nil))
+ nil)))
+
+(defun put (sym prop val)
+ (let ((p (assoc sym *plists*)))
+ (if (null p) ; sym has no plist yet
+ (setq *plists* (cons (list sym prop val) *plists*))
+ (let ((pr (member prop p)))
+ (if (null pr) ; sym doesn't have this property yet
+ (rplacd p (cons prop (cons val (cdr p))))
+ (rplaca (cdr pr) val)))))
+ val)
--- /dev/null
+++ b/femtolisp/attic/s.c
@@ -1,0 +1,212 @@
+#include <stdio.h>
+
+struct _b {
+ char a;
+ short b:9;
+};
+
+struct _bb {
+ char a;
+ int :0;
+ int b:10;
+ int :0;
+ int b0:10;
+ int :0;
+ int b1:10;
+ int :0;
+ int b2:10;
+ int :0;
+ int b4:30;
+ char c;
+};
+
+union _cc {
+ struct {
+ char a;
+ int b:1; // bit 8
+ int b1:1; // bit 9
+ int b2:24; // bits 32..55
+ char c;
+ };
+ unsigned long long ull;
+};
+
+union _cc2 {
+ struct {
+ char a;
+ int b:24; // bit 8
+ int b1:1;
+ int b2:1;
+ char c;
+ };
+ unsigned long long ull;
+};
+
+union _dd {
+ struct {
+ int a0:10;
+ int a1:10;
+ int a2:10;
+ int a3:10;
+ int a4:10;
+ };
+ struct {
+ unsigned long long ull;
+ };
+};
+
+struct _ee {
+ short s:9;
+ short j:9;
+ char c;
+};
+
+typedef long long int int64_t;
+typedef unsigned long long int uint64_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef char int8_t;
+typedef unsigned char uint8_t;
+
+#define lomask(type,n) (type)((((type)1)<<(n))-1)
+
+uint64_t get_u_bitfield(char *ptr, int typesz, int boffs, int blen)
+{
+ uint64_t i8;
+ uint32_t i4;
+ uint16_t i2;
+ uint8_t i1;
+
+ switch (typesz) {
+ case 8:
+ i8 = *(uint64_t*)ptr;
+ return (i8>>boffs) & lomask(uint64_t,blen);
+ case 4:
+ i4 = *(uint32_t*)ptr;
+ return (i4>>boffs) & lomask(uint32_t,blen);
+ case 2:
+ i2 = *(uint16_t*)ptr;
+ return (i2>>boffs) & lomask(uint16_t,blen);
+ case 1:
+ i1 = *(uint8_t*)ptr;
+ return (i1>>boffs) & lomask(uint8_t,blen);
+ }
+ //error
+ return 0;
+}
+
+int64_t get_s_bitfield(char *ptr, int typesz, int boffs, int blen)
+{
+ int64_t i8;
+ int32_t i4;
+ int16_t i2;
+ int8_t i1;
+
+ switch (typesz) {
+ case 8:
+ i8 = *(int64_t*)ptr;
+ return (i8<<(64-boffs-blen))>>(64-blen);
+ case 4:
+ i4 = *(int32_t*)ptr;
+ return (i4<<(32-boffs-blen))>>(32-blen);
+ case 2:
+ i2 = *(int16_t*)ptr;
+ return (i2<<(16-boffs-blen))>>(16-blen);
+ case 1:
+ i1 = *(int8_t*)ptr;
+ return (i1<<(8-boffs-blen))>>(8-blen);
+ }
+ //error
+ return 0;
+}
+
+void set_bitfield(char *ptr, int typesz, int boffs, int blen, uint64_t v)
+{
+ uint64_t i8, m8;
+ uint32_t i4, m4;
+ uint16_t i2, m2;
+ uint8_t i1, m1;
+
+ switch (typesz) {
+ case 8:
+ m8 = lomask(uint64_t,blen)<<boffs;
+ i8 = *(uint64_t*)ptr;
+ *(uint64_t*)ptr = (i8&~m8) | ((v<<boffs)&m8);
+ break;
+ case 4:
+ m4 = lomask(uint32_t,blen)<<boffs;
+ i4 = *(uint32_t*)ptr;
+ *(uint32_t*)ptr = (i4&~m4) | ((v<<boffs)&m4);
+ break;
+ case 2:
+ m2 = lomask(uint16_t,blen)<<boffs;
+ i2 = *(uint16_t*)ptr;
+ *(uint16_t*)ptr = (i2&~m2) | ((v<<boffs)&m2);
+ break;
+ case 1:
+ m1 = lomask(uint8_t,blen)<<boffs;
+ i1 = *(uint8_t*)ptr;
+ *(uint8_t*)ptr = (i1&~m1) | ((v<<boffs)&m1);
+ break;
+ }
+}
+
+int main()
+{
+ union _cc2 c;
+ union _dd d;
+ printf("%d\n", sizeof(struct _b));
+
+ printf("%d\n", sizeof(d));
+ //printf("%d\n\n", sizeof(struct _bb));
+
+ //printf("%d\n", (char*)&b.b - (char*)&b);
+ //printf("%d\n", (char*)&b.c - (char*)&b);
+ //printf("%d\n", (char*)&b.e - (char*)&b);
+
+ c.ull = 0;
+ d.ull = 0;
+ //d.ull2 = 0;
+
+ d.a0 = d.a1 = d.a2 = d.a3 = d.a4 = 1;
+ printf("0x%016llx\n", d.ull);
+ unsigned long long m = 1;
+ int bn = 0;
+ while (m) {
+ if (d.ull & m)
+ printf("bit %d set\n", bn);
+ bn++;
+ m<<=1;
+ }
+ //printf("%016x\n", d.ull2);
+
+
+ c.a = 1;
+ c.b = 1;
+ c.c = 1;
+ printf("0x%016llx\n", c.ull);
+ bn=0;m=1;
+ while (m) {
+ if (c.ull & m)
+ printf("bit %d set\n", bn);
+ bn++;
+ m<<=1;
+ }
+
+ return 0;
+}
+
+/*
+ offset/alignment rules for bit fields:
+
+ - alignment for whole struct is still the most strict of any of the
+ named types, regardless of bit fields. (i.e. just take the bit field
+ widths away and compute struct alignment normally)
+
+ - a bit field cannot cross a word boundary of its declared type
+
+ - otherwise pack bit fields as tightly as possible
+
+ */
--- /dev/null
+++ b/femtolisp/attic/system-old.lsp
@@ -1,0 +1,25 @@
+(define (equal a b)
+ (if (and (consp a) (consp b))
+ (and (equal (car a) (car b))
+ (equal (cdr a) (cdr b)))
+ (eq a b)))
+
+; compare imposes an ordering on all values. yields -1 for a<b,
+; 0 for a==b, and 1 for a>b. lists are compared up to the first
+; point of difference.
+(defun compare (a b)
+ (cond ((eq a b) 0)
+ ((or (atom a) (atom b)) (if (< a b) -1 1))
+ (T (let ((c (compare (car a) (car b))))
+ (if (not (eq c 0))
+ c
+ (compare (cdr a) (cdr b)))))))
+
+(defun length (l)
+ (if (null l) 0
+ (+ 1 (length (cdr l)))))
+
+(define (assoc item lst)
+ (cond ((atom lst) ())
+ ((eq (caar lst) item) (car lst))
+ (T (assoc item (cdr lst)))))
--- /dev/null
+++ b/femtolisp/attic/trash.c
@@ -1,0 +1,117 @@
+value_t prim_types[32];
+value_t *prim_sym_addrs[] = {
+ &int8sym, &uint8sym, &int16sym, &uint16sym, &int32sym, &uint32sym,
+ &int64sym, &uint64sym, &charsym, &ucharsym, &shortsym, &ushortsym,
+ &intsym, &uintsym, &longsym, &ulongsym,
+ &lispvaluesym };
+#define N_PRIMSYMS (sizeof(prim_sym_addrs) / sizeof(value_t*))
+
+static value_t cv_type(cvalue_t *cv)
+{
+ if (cv->flags.prim) {
+ return prim_types[cv->flags.primtype];
+ }
+ return cv->type;
+}
+
+
+ double t0,t1;
+ int i;
+ int32_t i32;
+ char s8;
+ ulong_t c8=3;
+ t0 = clock(); //0.058125017
+ set_secret_symtag(ulongsym,TAG_UINT32);
+ set_secret_symtag(int8sym,TAG_INT8);
+ for(i=0; i < 8000000; i++) {
+ cnvt_to_int32(&i32, &s8, int8sym);
+ c8+=c8;
+ s8+=s8;
+ }
+ t1 = clock();
+ printf("%d. that took %.16f\n", i32, t1-t0);
+
+
+#define int_converter(type) \
+static int cnvt_to_##type(type##_t *i, void *data, value_t type) \
+{ \
+ if (type==int32sym) *i = *(int32_t*)data; \
+ else if (type==charsym) *i = *(char*)data; \
+ else if (type==ulongsym) *i = *(ulong*)data; \
+ else if (type==uint32sym) *i = *(uint32_t*)data; \
+ else if (type==int8sym) *i = *(int8_t*)data; \
+ else if (type==uint8sym) *i = *(uint8_t*)data; \
+ else if (type==int64sym) *i = *(int64_t*)data; \
+ else if (type==uint64sym) *i = *(uint64_t*)data; \
+ else if (type==wcharsym) *i = *(wchar_t*)data; \
+ else if (type==longsym) *i = *(long*)data; \
+ else if (type==int16sym) *i = *(int16_t*)data; \
+ else if (type==uint16sym) *i = *(uint16_t*)data; \
+ else \
+ return 1; \
+ return 0; \
+}
+int_converter(int32)
+int_converter(uint32)
+int_converter(int64)
+int_converter(uint64)
+
+#ifdef BITS64
+#define cnvt_to_ulong(i,d,t) cnvt_to_uint64(i,d,t)
+#else
+#define cnvt_to_ulong(i,d,t) cnvt_to_uint32(i,d,t)
+#endif
+
+long intabs(long n)
+{
+ long s = n>>(NBITS-1); // either -1 or 0
+ return (n^s) - s;
+}
+
+value_t fl_inv(value_t b)
+{
+ int_t bi;
+ int tb;
+ void *bptr=NULL;
+ cvalue_t *cv;
+
+ if (isfixnum(b)) {
+ bi = numval(b);
+ if (bi == 0)
+ goto inv_error;
+ else if (bi == 1)
+ return fixnum(1);
+ else if (bi == -1)
+ return fixnum(-1);
+ return fixnum(0);
+ }
+ else if (iscvalue(b)) {
+ cv = (cvalue_t*)ptr(b);
+ tb = cv_numtype(cv);
+ if (tb <= T_DOUBLE)
+ bptr = cv_data(cv);
+ }
+ if (bptr == NULL)
+ type_error("/", "number", b);
+
+ if (tb == T_FLOAT)
+ return mk_double(1.0/(double)*(float*)bptr);
+ if (tb == T_DOUBLE)
+ return mk_double(1.0 / *(double*)bptr);
+
+ if (tb == T_UINT64) {
+ if (*(uint64_t*)bptr > 1)
+ return fixnum(0);
+ else if (*(uint64_t*)bptr == 1)
+ return fixnum(1);
+ goto inv_error;
+ }
+ int64_t b64 = conv_to_int64(bptr, tb);
+ if (b64 == 0) goto inv_error;
+ else if (b64 == 1) return fixnum(1);
+ else if (b64 == -1) return fixnum(-1);
+
+ return fixnum(0);
+ inv_error:
+ lerror(DivideError, "/: division by zero");
+}
--- /dev/null
+++ b/femtolisp/builtins.c
@@ -1,0 +1,582 @@
+/*
+ Extra femtoLisp builtin functions
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <errno.h>
+#include "llt.h"
+#include "flisp.h"
+
+size_t llength(value_t v)
+{
+ size_t n = 0;
+ while (iscons(v)) {
+ n++;
+ v = cdr_(v);
+ }
+ return n;
+}
+
+value_t list_nth(value_t l, size_t n)
+{
+ while (n && iscons(l)) {
+ l = cdr_(l);
+ n--;
+ }
+ if (iscons(l)) return car_(l);
+ return NIL;
+}
+
+value_t fl_print(value_t *args, u_int32_t nargs)
+{
+ unsigned i;
+ for (i=0; i < nargs; i++)
+ print(stdout, args[i], 0);
+ fputc('\n', stdout);
+ return nargs ? args[nargs-1] : NIL;
+}
+
+value_t fl_princ(value_t *args, u_int32_t nargs)
+{
+ unsigned i;
+ for (i=0; i < nargs; i++)
+ print(stdout, args[i], 1);
+ return nargs ? args[nargs-1] : NIL;
+}
+
+value_t fl_read(value_t *args, u_int32_t nargs)
+{
+ (void)args;
+ argcount("read", nargs, 0);
+ return read_sexpr(stdin);
+}
+
+value_t fl_load(value_t *args, u_int32_t nargs)
+{
+ argcount("load", nargs, 1);
+ return load_file(tostring(args[0], "load"));
+}
+
+value_t fl_exit(value_t *args, u_int32_t nargs)
+{
+ if (nargs > 0)
+ exit(tofixnum(args[0], "exit"));
+ exit(0);
+ return NIL;
+}
+
+extern value_t LAMBDA;
+
+value_t fl_setsyntax(value_t *args, u_int32_t nargs)
+{
+ argcount("set-syntax", nargs, 2);
+ symbol_t *sym = tosymbol(args[0], "set-syntax");
+ if (sym->syntax && (sym->syntax == TAG_CONST || isspecial(sym->syntax)))
+ lerror(ArgError, "set-syntax: cannot define syntax for %s",
+ symbol_name(args[0]));
+ if (args[1] == NIL) {
+ sym->syntax = 0;
+ }
+ else {
+ if (!iscons(args[1]) || car_(args[1])!=LAMBDA)
+ type_error("set-syntax", "function", args[1]);
+ sym->syntax = args[1];
+ }
+ return args[1];
+}
+
+value_t fl_symbolsyntax(value_t *args, u_int32_t nargs)
+{
+ argcount("symbol-syntax", nargs, 1);
+ symbol_t *sym = tosymbol(args[0], "symbol-syntax");
+ // must avoid returning built-in syntax expanders, because they
+ // don't behave like functions (they take their arguments directly
+ // from the form rather than from the stack of evaluated arguments)
+ if (sym->syntax == TAG_CONST || isspecial(sym->syntax))
+ return NIL;
+ return sym->syntax;
+}
+
+static void syntax_env_assoc_list(symbol_t *root, value_t *pv)
+{
+ while (root != NULL) {
+ if (root->syntax && root->syntax != TAG_CONST &&
+ !isspecial(root->syntax)) {
+ PUSH(fl_cons(tagptr(root,TAG_SYM), root->syntax));
+ *pv = fl_cons(POP(), *pv);
+ }
+ syntax_env_assoc_list(root->left, pv);
+ root = root->right;
+ }
+}
+static void global_env_assoc_list(symbol_t *root, value_t *pv)
+{
+ while (root != NULL) {
+ if (root->binding != UNBOUND) {
+ PUSH(fl_cons(tagptr(root,TAG_SYM), root->binding));
+ *pv = fl_cons(POP(), *pv);
+ }
+ global_env_assoc_list(root->left, pv);
+ root = root->right;
+ }
+}
+
+extern symbol_t *symtab;
+
+value_t fl_syntax_env(value_t *args, u_int32_t nargs)
+{
+ (void)args;
+ argcount("syntax-environment", nargs, 0);
+ PUSH(NIL);
+ syntax_env_assoc_list(symtab, &Stack[SP-1]);
+ return POP();
+}
+value_t fl_global_env(value_t *args, u_int32_t nargs)
+{
+ (void)args;
+ argcount("environment", nargs, 0);
+ PUSH(NIL);
+ global_env_assoc_list(symtab, &Stack[SP-1]);
+ return POP();
+}
+
+value_t fl_constantp(value_t *args, u_int32_t nargs)
+{
+ argcount("constantp", nargs, 1);
+ if (issymbol(args[0]))
+ return (isconstant(args[0]) ? T : NIL);
+ if (iscons(args[0]))
+ return NIL;
+ return T;
+}
+
+value_t fl_fixnum(value_t *args, u_int32_t nargs)
+{
+ argcount("fixnum", nargs, 1);
+ if (isfixnum(args[0]))
+ return args[0];
+ if (iscvalue(args[0])) {
+ cvalue_t *cv = (cvalue_t*)ptr(args[0]);
+ long i;
+ if (cv->flags.cstring) {
+ char *pend;
+ errno = 0;
+ i = strtol(cv_data(cv), &pend, 0);
+ if (*pend != '\0' || errno!=0)
+ lerror(ArgError, "fixnum: invalid string");
+ return fixnum(i);
+ }
+ else if (valid_numtype(cv_numtype(cv))) {
+ i = conv_to_long(cv_data(cv), cv_numtype(cv));
+ return fixnum(i);
+ }
+ }
+ lerror(ArgError, "fixnum: cannot convert argument");
+}
+
+value_t fl_truncate(value_t *args, u_int32_t nargs)
+{
+ argcount("truncate", nargs, 1);
+ if (isfixnum(args[0]))
+ return args[0];
+ if (iscvalue(args[0])) {
+ cvalue_t *cv = (cvalue_t*)ptr(args[0]);
+ void *data = cv_data(cv);
+ numerictype_t nt = cv_numtype(cv);
+ if (valid_numtype(nt)) {
+ double d;
+ if (nt == T_FLOAT)
+ d = (double)*(float*)data;
+ else if (nt == T_DOUBLE)
+ d = *(double*)data;
+ else
+ return args[0];
+ if (d > 0)
+ return return_from_uint64((uint64_t)d);
+ return return_from_int64((int64_t)d);
+ }
+ }
+ type_error("truncate", "number", args[0]);
+}
+
+value_t fl_vector_alloc(value_t *args, u_int32_t nargs)
+{
+ fixnum_t i;
+ value_t f, v;
+ if (nargs == 0)
+ lerror(ArgError, "vector.alloc: too few arguments");
+ i = tofixnum(args[0], "vector.alloc");
+ if (i < 0)
+ lerror(ArgError, "vector.alloc: invalid size");
+ if (nargs == 2)
+ f = args[1];
+ else
+ f = NIL;
+ v = alloc_vector((unsigned)i, f==NIL);
+ if (f != NIL) {
+ int k;
+ for(k=0; k < i; k++)
+ vector_elt(v,k) = f;
+ }
+ return v;
+}
+
+int isstring(value_t v)
+{
+ return (iscvalue(v) && ((cvalue_t*)ptr(v))->flags.cstring);
+}
+
+value_t fl_intern(value_t *args, u_int32_t nargs)
+{
+ argcount("intern", nargs, 1);
+ if (!isstring(args[0]))
+ type_error("intern", "string", args[0]);
+ return symbol(cvalue_data(args[0]));
+}
+
+value_t fl_stringp(value_t *args, u_int32_t nargs)
+{
+ argcount("stringp", nargs, 1);
+ return isstring(args[0]) ? T : NIL;
+}
+
+value_t fl_string_length(value_t *args, u_int32_t nargs)
+{
+ argcount("string.length", nargs, 1);
+ if (!isstring(args[0]))
+ type_error("string.length", "string", args[0]);
+ size_t len = cv_len((cvalue_t*)ptr(args[0]));
+ return size_wrap(u8_charnum(cvalue_data(args[0]), len));
+}
+
+value_t fl_string_reverse(value_t *args, u_int32_t nargs)
+{
+ argcount("string.reverse", nargs, 1);
+ if (!isstring(args[0]))
+ type_error("string.reverse", "string", args[0]);
+ size_t len = cv_len((cvalue_t*)ptr(args[0]));
+ value_t ns = cvalue_string(len);
+ u8_reverse(cvalue_data(ns), cvalue_data(args[0]), len);
+ return ns;
+}
+
+value_t fl_string_encode(value_t *args, u_int32_t nargs)
+{
+ argcount("string.encode", nargs, 1);
+ if (iscvalue(args[0])) {
+ cvalue_t *cv = (cvalue_t*)ptr(args[0]);
+ value_t t = cv_type(cv);
+ if (iscons(t) && car_(t) == arraysym &&
+ iscons(cdr_(t)) && car_(cdr_(t)) == wcharsym) {
+ size_t nc = cv_len(cv) / sizeof(uint32_t);
+ uint32_t *ptr = (uint32_t*)cv_data(cv);
+ size_t nbytes = u8_codingsize(ptr, nc);
+ value_t str = cvalue_string(nbytes);
+ ptr = cv_data((cvalue_t*)ptr(args[0])); // relocatable pointer
+ u8_toutf8(cvalue_data(str), nbytes, ptr, nc);
+ return str;
+ }
+ }
+ type_error("string.encode", "wide character array", args[0]);
+}
+
+value_t fl_string_decode(value_t *args, u_int32_t nargs)
+{
+ int term=0;
+ if (nargs == 2) {
+ term = (POP() != NIL);
+ nargs--;
+ }
+ argcount("string.decode", nargs, 1);
+ if (!isstring(args[0]))
+ type_error("string.decode", "string", args[0]);
+ cvalue_t *cv = (cvalue_t*)ptr(args[0]);
+ char *ptr = (char*)cv_data(cv);
+ size_t nb = cv_len(cv);
+ size_t nc = u8_charnum(ptr, nb);
+ size_t newsz = nc*sizeof(uint32_t);
+ if (term) newsz += sizeof(uint32_t);
+ value_t wcstr = cvalue(symbol_value(wcstringtypesym), newsz);
+ ptr = cv_data((cvalue_t*)ptr(args[0])); // relocatable pointer
+ uint32_t *pwc = cvalue_data(wcstr);
+ u8_toucs(pwc, nc, ptr, nb);
+ if (term) pwc[nc] = 0;
+ return wcstr;
+}
+
+value_t fl_string(value_t *args, u_int32_t nargs)
+{
+ value_t cv, t;
+ u_int32_t i;
+ size_t len, sz = 0;
+ cvalue_t *temp;
+ char *data;
+ wchar_t wc;
+
+ for(i=0; i < nargs; i++) {
+ if (issymbol(args[i])) {
+ sz += strlen(symbol_name(args[i]));
+ continue;
+ }
+ else if (iscvalue(args[i])) {
+ temp = (cvalue_t*)ptr(args[i]);
+ t = cv_type(temp);
+ if (t == charsym) {
+ sz++;
+ continue;
+ }
+ else if (t == wcharsym) {
+ wc = *(wchar_t*)cv_data(temp);
+ sz += u8_charlen(wc);
+ continue;
+ }
+ else if (temp->flags.cstring) {
+ sz += cv_len(temp);
+ continue;
+ }
+ }
+ lerror(ArgError, "string: expected string, symbol or character");
+ }
+ cv = cvalue_string(sz);
+ char *ptr = cvalue_data(cv);
+ for(i=0; i < nargs; i++) {
+ if (issymbol(args[i])) {
+ char *name = symbol_name(args[i]);
+ while (*name) *ptr++ = *name++;
+ }
+ else {
+ temp = (cvalue_t*)ptr(args[i]);
+ t = cv_type(temp);
+ data = cvalue_data(args[i]);
+ if (t == charsym) {
+ *ptr++ = *(char*)data;
+ }
+ else if (t == wcharsym) {
+ ptr += u8_wc_toutf8(ptr, *(wchar_t*)data);
+ }
+ else {
+ len = cv_len(temp);
+ memcpy(ptr, data, len);
+ ptr += len;
+ }
+ }
+ }
+ return cv;
+}
+
+value_t fl_string_split(value_t *args, u_int32_t nargs)
+{
+ argcount("string.split", nargs, 2);
+ char *s = tostring(args[0], "string.split");
+ char *delim = tostring(args[1], "string.split");
+ size_t len = cv_len((cvalue_t*)ptr(args[0]));
+ size_t dlen = cv_len((cvalue_t*)ptr(args[1]));
+ PUSH(NIL);
+ size_t ssz, tokend=0, tokstart=0, i=0;
+ value_t c=NIL;
+ size_t junk;
+ do {
+ // find and allocate next token
+ tokstart = tokend = i;
+ while (i < len &&
+ !u8_memchr(delim, u8_nextmemchar(s, &i), dlen, &junk))
+ tokend = i;
+ ssz = tokend - tokstart;
+ PUSH(c); // save previous cons cell
+ c = fl_cons(cvalue_string(ssz), NIL);
+
+ // we've done allocation; reload movable pointers
+ s = cv_data((cvalue_t*)ptr(args[0]));
+ delim = cv_data((cvalue_t*)ptr(args[1]));
+
+ if (ssz) memcpy(cv_data((cvalue_t*)ptr(car_(c))), &s[tokstart], ssz);
+
+ // link new cell
+ if (Stack[SP-1] == NIL) {
+ Stack[SP-2] = c; // first time, save first cons
+ (void)POP();
+ }
+ else {
+ ((cons_t*)ptr(POP()))->cdr = c;
+ }
+
+ // note this tricky condition: if the string ends with a
+ // delimiter, we need to go around one more time to add an
+ // empty string. this happens when (i==len && tokend<i)
+ } while (i < len || (i==len && (tokend!=i)));
+ return POP();
+}
+
+value_t fl_string_sub(value_t *args, u_int32_t nargs)
+{
+ argcount("string.sub", nargs, 3);
+ char *s = tostring(args[0], "string.sub");
+ size_t len = cv_len((cvalue_t*)ptr(args[0]));
+ size_t i1, i2;
+ i1 = toulong(args[1], "string.sub");
+ if (i1 > len)
+ bounds_error("string.sub", args[0], args[1]);
+ i2 = toulong(args[2], "string.sub");
+ if (i2 > len)
+ bounds_error("string.sub", args[0], args[2]);
+ if (i2 <= i1)
+ return cvalue_string(0);
+ value_t ns = cvalue_string(i2-i1);
+ memcpy(cv_data((cvalue_t*)ptr(ns)), &s[i1], i2-i1);
+ return ns;
+}
+
+value_t fl_time_now(value_t *args, u_int32_t nargs)
+{
+ argcount("time.now", nargs, 0);
+ (void)args;
+ return mk_double(clock_now());
+}
+
+static double value_to_double(value_t a, char *fname)
+{
+ if (isfixnum(a))
+ return (double)numval(a);
+ if (iscvalue(a)) {
+ cvalue_t *cv = (cvalue_t*)ptr(a);
+ numerictype_t nt = cv_numtype(cv);
+ if (valid_numtype(nt))
+ return conv_to_double(cv_data(cv), nt);
+ }
+ type_error(fname, "number", a);
+}
+
+static value_t return_from_cstr(char *str)
+{
+ size_t n = strlen(str);
+ value_t v = cvalue_string(n);
+ memcpy(cvalue_data(v), str, n);
+ return v;
+}
+
+value_t fl_time_string(value_t *args, uint32_t nargs)
+{
+ argcount("time.string", nargs, 1);
+ double t = value_to_double(args[0], "time.string");
+ char buf[64];
+ timestring(t, buf, sizeof(buf));
+ return return_from_cstr(buf);
+}
+
+value_t fl_path_cwd(value_t *args, uint32_t nargs)
+{
+ if (nargs > 1)
+ argcount("path.cwd", nargs, 1);
+ if (nargs == 0) {
+ char buf[1024];
+ get_cwd(buf, sizeof(buf));
+ return return_from_cstr(buf);
+ }
+ char *ptr = tostring(args[0], "path.cwd");
+ if (set_cwd(ptr))
+ lerror(IOError, "could not cd to %s", ptr);
+ return T;
+}
+
+value_t fl_os_getenv(value_t *args, uint32_t nargs)
+{
+ argcount("os.getenv", nargs, 1);
+ char *name = tostring(args[0], "os.getenv");
+ char *val = getenv(name);
+ if (val == NULL) return NIL;
+ if (*val == 0)
+ return symbol_value(emptystringsym);
+ return cvalue_pinned_cstring(val);
+}
+
+value_t fl_os_setenv(value_t *args, uint32_t nargs)
+{
+ argcount("os.setenv", nargs, 2);
+ char *name = tostring(args[0], "os.setenv");
+ int result;
+ if (args[1] == NIL) {
+ result = unsetenv(name);
+ }
+ else {
+ char *val = tostring(args[1], "os.setenv");
+ result = setenv(name, val, 1);
+ }
+ if (result != 0)
+ lerror(ArgError, "os.setenv: invalid environment variable");
+ return T;
+}
+
+value_t fl_rand(value_t *args, u_int32_t nargs)
+{
+ (void)args;
+ (void)nargs;
+ return fixnum(random()&0x1fffffff);
+}
+value_t fl_rand32(value_t *args, u_int32_t nargs)
+{
+ (void)args;
+ (void)nargs;
+ return mk_uint32(random());
+}
+value_t fl_rand64(value_t *args, u_int32_t nargs)
+{
+ (void)args;
+ (void)nargs;
+ return mk_uint64(((uint64_t)random())<<32 | ((uint64_t)random()));
+}
+value_t fl_randd(value_t *args, u_int32_t nargs)
+{
+ (void)args;
+ (void)nargs;
+ return mk_double(rand_double());
+}
+
+void builtins_init()
+{
+ set(symbol("set-syntax"), guestfunc(fl_setsyntax));
+ set(symbol("symbol-syntax"), guestfunc(fl_symbolsyntax));
+ set(symbol("syntax-environment"), guestfunc(fl_syntax_env));
+ set(symbol("environment"), guestfunc(fl_global_env));
+ set(symbol("constantp"), guestfunc(fl_constantp));
+
+ set(symbol("print"), guestfunc(fl_print));
+ set(symbol("princ"), guestfunc(fl_princ));
+ set(symbol("read"), guestfunc(fl_read));
+ set(symbol("load"), guestfunc(fl_load));
+ set(symbol("exit"), guestfunc(fl_exit));
+ set(symbol("intern"), guestfunc(fl_intern));
+ set(symbol("fixnum"), guestfunc(fl_fixnum));
+ set(symbol("truncate"), guestfunc(fl_truncate));
+
+ set(symbol("vector.alloc"), guestfunc(fl_vector_alloc));
+
+ set(symbol("string"), guestfunc(fl_string));
+ set(symbol("stringp"), guestfunc(fl_stringp));
+ set(symbol("string.length"), guestfunc(fl_string_length));
+ set(symbol("string.split"), guestfunc(fl_string_split));
+ set(symbol("string.sub"), guestfunc(fl_string_sub));
+ set(symbol("string.reverse"), guestfunc(fl_string_reverse));
+ set(symbol("string.encode"), guestfunc(fl_string_encode));
+ set(symbol("string.decode"), guestfunc(fl_string_decode));
+
+ set(symbol("time.now"), guestfunc(fl_time_now));
+ set(symbol("time.string"), guestfunc(fl_time_string));
+
+ set(symbol("rand"), guestfunc(fl_rand));
+ set(symbol("rand.uint32"), guestfunc(fl_rand32));
+ set(symbol("rand.uint64"), guestfunc(fl_rand64));
+ set(symbol("rand.double"), guestfunc(fl_randd));
+
+ set(symbol("path.cwd"), guestfunc(fl_path_cwd));
+
+ set(symbol("os.getenv"), guestfunc(fl_os_getenv));
+ set(symbol("os.setenv"), guestfunc(fl_os_setenv));
+}
--- /dev/null
+++ b/femtolisp/color.lsp
@@ -1,0 +1,94 @@
+; uncomment for compatibility with CL
+;(defun mapp (f l) (mapcar f l))
+;(defmacro define (name &rest body)
+; (if (symbolp name)
+; (list 'setq name (car body))
+; (list 'defun (car name) (cdr name) (cons 'progn body))))
+
+; dictionaries ----------------------------------------------------------------
+(define (dict-new) ())
+
+(define (dict-extend dl key value)
+ (cond ((null dl) (list (cons key value)))
+ ((equal key (caar dl)) (cons (cons key value) (cdr dl)))
+ (T (cons (car dl) (dict-extend (cdr dl) key value)))))
+
+(define (dict-lookup dl key)
+ (cond ((null dl) ())
+ ((equal key (caar dl)) (cdar dl))
+ (T (dict-lookup (cdr dl) key))))
+
+(define (dict-keys dl) (map (symbol-function 'car) dl))
+
+; graphs ----------------------------------------------------------------------
+(define (graph-empty) (dict-new))
+
+(define (graph-connect g n1 n2)
+ (dict-extend
+ (dict-extend g n2 (cons n1 (dict-lookup g n2)))
+ n1
+ (cons n2 (dict-lookup g n1))))
+
+(define (graph-adjacent? g n1 n2) (member n2 (dict-lookup g n1)))
+
+(define (graph-neighbors g n) (dict-lookup g n))
+
+(define (graph-nodes g) (dict-keys g))
+
+(define (graph-add-node g n1) (dict-extend g n1 ()))
+
+(define (graph-from-edges edge-list)
+ (if (null edge-list)
+ (graph-empty)
+ (graph-connect (graph-from-edges (cdr edge-list))
+ (caar edge-list)
+ (cdar edge-list))))
+
+; graph coloring --------------------------------------------------------------
+(define (node-colorable? g coloring node-to-color color-of-node)
+ (not (member
+ color-of-node
+ (map
+ (lambda (n)
+ (let ((color-pair (assoc n coloring)))
+ (if (consp color-pair) (cdr color-pair) nil)))
+ (graph-neighbors g node-to-color)))))
+
+(define (try-each f lst)
+ (if (null lst) nil
+ (let ((ret (funcall f (car lst))))
+ (if ret ret (try-each f (cdr lst))))))
+
+(define (color-node g coloring colors uncolored-nodes color)
+ (cond
+ ((null uncolored-nodes) coloring)
+ ((node-colorable? g coloring (car uncolored-nodes) color)
+ (let ((new-coloring
+ (cons (cons (car uncolored-nodes) color) coloring)))
+ (try-each (lambda (c)
+ (color-node g new-coloring colors (cdr uncolored-nodes) c))
+ colors)))))
+
+(define (color-graph g colors)
+ (if (null colors)
+ (null (graph-nodes g))
+ (color-node g () colors (graph-nodes g) (car colors))))
+
+(define (color-pairs pairs colors)
+ (color-graph (graph-from-edges pairs) colors))
+
+; queens ----------------------------------------------------------------------
+(defun can-attack (x y)
+ (let ((x1 (mod x 5))
+ (y1 (truncate (/ x 5)))
+ (x2 (mod y 5))
+ (y2 (truncate (/ y 5))))
+ (or (= x1 x2) (= y1 y2) (= (abs (- y2 y1)) (abs (- x2 x1))))))
+
+(defun generate-5x5-pairs ()
+ (let ((result nil))
+ (dotimes (x 25)
+ (dotimes (y 25)
+ (if (and (/= x y) (can-attack x y))
+ (setq result (cons (cons x y) result)) nil)))
+ result))
--- /dev/null
+++ b/femtolisp/cvalues.c
@@ -1,0 +1,1368 @@
+#define MAX_INL_SIZE 96
+#ifdef BITS64
+#define NWORDS(sz) (((sz)+7)>>3)
+#else
+#define NWORDS(sz) (((sz)+3)>>2)
+#endif
+
+static int struct_aligns[8] = {
+ sizeof(struct { char a; int8_t i; }),
+ sizeof(struct { char a; int16_t i; }),
+ sizeof(struct { char a; char i[3]; }),
+ sizeof(struct { char a; int32_t i; }),
+ sizeof(struct { char a; char i[5]; }),
+ sizeof(struct { char a; char i[6]; }),
+ sizeof(struct { char a; char i[7]; }),
+ sizeof(struct { char a; int64_t i; }) };
+static int ALIGN2, ALIGN4, ALIGN8;
+
+typedef void (*cvinitfunc_t)(value_t*, u_int32_t, void*, void*);
+
+value_t int8sym, uint8sym, int16sym, uint16sym, int32sym, uint32sym;
+value_t int64sym, uint64sym;
+value_t longsym, ulongsym, charsym, wcharsym;
+value_t floatsym, doublesym;
+value_t gftypesym, lispvaluesym, stringtypesym, wcstringtypesym;
+value_t emptystringsym;
+
+value_t structsym, arraysym, enumsym, cfunctionsym, voidsym, pointersym;
+value_t unionsym;
+
+value_t autoreleasesym, typeofsym, sizeofsym;
+
+static void cvalue_init(value_t type, value_t *vs, u_int32_t nv, void *dest);
+
+void cvalue_print(FILE *f, value_t v, int princ);
+// exported guest functions
+value_t cvalue_new(value_t *args, u_int32_t nargs);
+value_t cvalue_sizeof(value_t *args, u_int32_t nargs);
+value_t cvalue_typeof(value_t *args, u_int32_t nargs);
+
+// compute the size of the metadata object for a cvalue
+static size_t cv_nwords(cvalue_t *cv)
+{
+ if (cv->flags.prim) {
+ if (cv->flags.inlined)
+ return 2 + NWORDS(cv->flags.inllen);
+ return 3;
+ }
+ if (cv->flags.inlined) {
+ size_t s = 3 + NWORDS(cv->flags.inllen + cv->flags.cstring);
+ return (s < 5) ? 5 : s;
+ }
+ return 5;
+}
+
+void *cv_data(cvalue_t *cv)
+{
+ if (cv->flags.prim) {
+ if (cv->flags.inlined) {
+ return &((cprim_t*)cv)->data;
+ }
+ return ((cprim_t*)cv)->data;
+ }
+ else if (cv->flags.inlined) {
+ return &cv->data;
+ }
+ return cv->data;
+}
+
+void *cvalue_data(value_t v)
+{
+ return cv_data((cvalue_t*)ptr(v));
+}
+
+static void autorelease(cvalue_t *cv)
+{
+ cv->flags.autorelease = 1;
+ // TODO: add to finalizer list
+}
+
+value_t cvalue(value_t type, size_t sz)
+{
+ cvalue_t *pcv;
+
+ if (issymbol(type)) {
+ cprim_t *pcp;
+ pcp = (cprim_t*)alloc_words(2 + NWORDS(sz));
+ pcp->flagbits = INITIAL_FLAGS;
+ pcp->flags.inllen = sz;
+ pcp->flags.inlined = 1;
+ pcp->flags.prim = 1;
+ pcp->type = type;
+ return tagptr(pcp, TAG_BUILTIN);
+ }
+ PUSH(type);
+ if (sz <= MAX_INL_SIZE) {
+ size_t nw = 3 + NWORDS(sz);
+ pcv = (cvalue_t*)alloc_words((nw < 5) ? 5 : nw);
+ pcv->flagbits = INITIAL_FLAGS;
+ pcv->flags.inllen = sz;
+ pcv->flags.inlined = 1;
+ }
+ else {
+ pcv = (cvalue_t*)alloc_words(5);
+ pcv->flagbits = INITIAL_FLAGS;
+ pcv->flags.inlined = 0;
+ pcv->data = malloc(sz);
+ pcv->len = sz;
+ autorelease(pcv);
+ }
+ pcv->deps = NIL;
+ pcv->type = POP();
+ return tagptr(pcv, TAG_BUILTIN);
+}
+
+value_t cvalue_from_data(value_t type, void *data, size_t sz)
+{
+ cvalue_t *pcv;
+ value_t cv;
+ cv = cvalue(type, sz);
+ pcv = (cvalue_t*)ptr(cv);
+ memcpy(cv_data(pcv), data, sz);
+ return cv;
+}
+
+// this effectively dereferences a pointer
+// just like *p in C, it only removes a level of indirection from the type,
+// it doesn't copy any data.
+// this method of creating a cvalue only allocates metadata.
+// ptr is user-managed; we don't autorelease it unless the
+// user explicitly calls (autorelease ) on the result of this function.
+// 'parent' is an optional cvalue that this pointer is known to point
+// into; UNBOUND if none.
+value_t cvalue_from_ref(value_t type, void *ptr, size_t sz, value_t parent)
+{
+ cvalue_t *pcv;
+ value_t cv;
+
+ PUSH(parent);
+ PUSH(type);
+ pcv = (cvalue_t*)alloc_words(5);
+ pcv->flagbits = INITIAL_FLAGS;
+ pcv->flags.inlined = 0;
+ pcv->data = ptr;
+ pcv->len = sz;
+ pcv->deps = NIL;
+ pcv->type = POP();
+ parent = POP();
+ if (parent != UNBOUND) {
+ // TODO: add dependency
+ }
+ cv = tagptr(pcv, TAG_BUILTIN);
+ return cv;
+}
+
+value_t cvalue_string(size_t sz)
+{
+ value_t cv;
+ char *data;
+ cvalue_t *pcv;
+
+ if (sz == 0)
+ return symbol_value(emptystringsym);
+ // secretly allocate space for 1 more byte, hide a NUL there so
+ // any string will always be NUL terminated.
+ cv = cvalue(symbol_value(stringtypesym), sz+1);
+ pcv = (cvalue_t*)ptr(cv);
+ data = cv_data(pcv);
+ data[sz] = '\0';
+ if (pcv->flags.inlined)
+ pcv->flags.inllen = sz;
+ else
+ pcv->len = sz;
+ pcv->flags.cstring = 1;
+ return cv;
+}
+
+value_t cvalue_pinned_cstring(char *str)
+{
+ value_t v = cvalue_from_ref(symbol_value(stringtypesym), str, strlen(str),
+ UNBOUND);
+ ((cvalue_t*)ptr(v))->flags.cstring = 1;
+ return v;
+}
+
+// convert to malloc representation (fixed address)
+/*
+static void cv_pin(cvalue_t *cv)
+{
+ if (!cv->flags.inlined)
+ return;
+ size_t sz = cv->flags.inllen;
+ void *data = malloc(sz);
+ cv->flags.inlined = 0;
+ // TODO: handle flags.cstring
+ if (cv->flags.prim) {
+ memcpy(data, (void*)(&((cprim_t*)cv)->data), sz);
+ ((cprim_t*)cv)->data = data;
+ }
+ else {
+ memcpy(data, (void*)(&cv->data), sz);
+ cv->data = data;
+ }
+ autorelease(cv);
+}
+*/
+
+static int64_t strtoi64(char *str, char *fname)
+{
+ char *pend;
+ int64_t i;
+ errno = 0;
+ i = strtoll(str, &pend, 0);
+ if (*pend != '\0' || errno) lerror(ArgError, "%s: invalid string", fname);
+ return i;
+}
+static uint64_t strtoui64(char *str, char *fname)
+{
+ char *pend;
+ uint64_t i;
+ errno = 0;
+ i = strtoull(str, &pend, 0);
+ if (*pend != '\0' || errno) lerror(ArgError, "%s: invalid string", fname);
+ return i;
+}
+static double strtodouble(char *str, char *fname)
+{
+ char *pend;
+ double d;
+ errno = 0;
+ d = strtod(str, &pend);
+ if (*pend != '\0' || errno) lerror(ArgError, "%s: invalid string", fname);
+ return d;
+}
+
+#define num_ctor(typenam, cnvt, tag, fromstr) \
+static void cvalue_##typenam##_init(value_t *args, u_int32_t nargs, \
+ void *dest, void *data) \
+{ \
+ typenam##_t n=0; \
+ (void)data; \
+ if (nargs) { \
+ if (iscvalue(args[0])) { \
+ cvalue_t *cv = (cvalue_t*)ptr(args[0]); \
+ void *p = cv_data(cv); \
+ if (valid_numtype(cv_numtype(cv))) { \
+ n = (typenam##_t)conv_to_##cnvt(p, cv_numtype(cv)); \
+ } \
+ else if (cv->flags.cstring) { \
+ n = fromstr(p, #typenam); \
+ } \
+ else if (cv_len(cv) == sizeof(typenam##_t)) { \
+ n = *(typenam##_t*)p; \
+ } \
+ else { \
+ type_error(#typenam, "number", args[0]); \
+ } \
+ } \
+ else { \
+ n = tofixnum(args[0], #typenam); \
+ } \
+ } \
+ *((typenam##_t*)dest) = n; \
+} \
+value_t cvalue_##typenam(value_t *args, u_int32_t nargs) \
+{ \
+ value_t cv = cvalue(typenam##sym, sizeof(typenam##_t)); \
+ ((cprim_t*)ptr(cv))->flags.numtype = tag; \
+ cvalue_##typenam##_init(args, nargs, &((cprim_t*)ptr(cv))->data, 0); \
+ return cv; \
+} \
+value_t mk_##typenam(typenam##_t n) \
+{ \
+ value_t cv = cvalue(typenam##sym, sizeof(typenam##_t)); \
+ ((cprim_t*)ptr(cv))->flags.numtype = tag; \
+ *(typenam##_t*)&((cprim_t*)ptr(cv))->data = n; \
+ return cv; \
+}
+
+num_ctor(int8, int32, T_INT8, strtoi64)
+num_ctor(uint8, uint32, T_UINT8, strtoui64)
+num_ctor(int16, int32, T_INT16, strtoi64)
+num_ctor(uint16, uint32, T_UINT16, strtoui64)
+num_ctor(int32, int32, T_INT32, strtoi64)
+num_ctor(uint32, uint32, T_UINT32, strtoui64)
+num_ctor(int64, int64, T_INT64, strtoi64)
+num_ctor(uint64, uint64, T_UINT64, strtoui64)
+num_ctor(char, uint32, T_UINT8, strtoui64)
+num_ctor(wchar, int32, T_INT32, strtoi64)
+#ifdef BITS64
+num_ctor(long, int64, T_INT64, strtoi64)
+num_ctor(ulong, uint64, T_UINT64, strtoui64)
+#else
+num_ctor(long, int32, T_INT32, strtoi64)
+num_ctor(ulong, uint32, T_UINT32, strtoui64)
+#endif
+num_ctor(float, double, T_FLOAT, strtodouble)
+num_ctor(double, double, T_DOUBLE, strtodouble)
+
+value_t size_wrap(size_t sz)
+{
+ if (fits_fixnum(sz))
+ return fixnum(sz);
+ assert(sizeof(void*) == sizeof(size_t));
+ return mk_ulong(sz);
+}
+
+size_t toulong(value_t n, char *fname)
+{
+ if (isfixnum(n))
+ return numval(n);
+ if (iscvalue(n)) {
+ cvalue_t *cv = (cvalue_t*)ptr(n);
+ if (valid_numtype(cv_numtype(cv))) {
+ return conv_to_ulong(cv_data(cv), cv_numtype(cv));
+ }
+ }
+ type_error(fname, "number", n);
+ return 0;
+}
+
+static void cvalue_enum_init(value_t *args, u_int32_t nargs, void *dest,
+ void *data)
+{
+ int n=0;
+ value_t syms;
+
+ (void)data;
+ argcount("enum", nargs, 2);
+ syms = args[0];
+ if (!iscons(syms))
+ type_error("enum", "cons", syms);
+ if (issymbol(args[1])) {
+ while (iscons(syms)) {
+ if (car_(syms) == args[1]) {
+ *(int*)dest = n;
+ return;
+ }
+ n++;
+ syms = cdr_(syms);
+ }
+ lerror(ArgError, "enum: invalid enum value");
+ }
+ if (isfixnum(args[1])) {
+ n = (int)numval(args[1]);
+ }
+ else if (iscvalue(args[1])) {
+ cvalue_t *cv = (cvalue_t*)ptr(args[1]);
+ if (!valid_numtype(cv_numtype(cv)))
+ type_error("enum", "number", args[1]);
+ n = conv_to_int32(cv_data(cv), cv_numtype(cv));
+ }
+ if ((unsigned)n >= llength(syms))
+ lerror(ArgError, "enum: value out of range");
+ *(int*)dest = n;
+}
+
+value_t cvalue_enum(value_t *args, u_int32_t nargs)
+{
+ argcount("enum", nargs, 2);
+ value_t cv = cvalue(list2(enumsym, args[0]), 4);
+ ((cvalue_t*)ptr(cv))->flags.numtype = T_INT32;
+ cvalue_enum_init(args, nargs, cv_data((cvalue_t*)ptr(cv)), NULL);
+ return cv;
+}
+
+static void cvalue_array_init(value_t *args, u_int32_t nargs, void *dest,
+ void *data)
+{
+ size_t cnt=0, elsize, i;
+ value_t *init = NULL;
+ int junk;
+
+ if (data != 0)
+ elsize = (size_t)data; // already computed by constructor
+ else
+ elsize = ctype_sizeof(args[0], &junk);
+ char *out = (char*)dest;
+
+ if (nargs == 2) {
+ if (isvector(args[1]) || iscons(args[1]) || args[1]==NIL)
+ init = &args[1];
+ else
+ cnt = toulong(args[1], "array");
+ }
+ else if (nargs == 3) {
+ cnt = toulong(args[1], "array");
+ init = &args[2];
+ }
+ else {
+ argcount("array", nargs, 2);
+ }
+ if (init) {
+ if (isvector(*init)) {
+ if (cnt && vector_size(*init) != cnt)
+ lerror(ArgError, "array: size mismatch");
+ cnt = vector_size(*init);
+ for(i=0; i < cnt; i++) {
+ cvalue_init(args[0], &vector_elt(*init, i), 1, out);
+ out += elsize;
+ }
+ return;
+ }
+ else if (iscons(*init) || *init==NIL) {
+ for(i=0; i < cnt || cnt==0; i++) {
+ if (!iscons(*init)) {
+ if (cnt != 0)
+ lerror(ArgError, "array: size mismatch");
+ else
+ break;
+ }
+ cvalue_init(args[0], &car_(*init), 1, out);
+ out += elsize;
+ *init = cdr_(*init);
+ }
+ return;
+ }
+ else if (iscvalue(*init)) {
+ cvalue_t *cv = (cvalue_t*)ptr(*init);
+ size_t tot = cnt*elsize;
+ if (tot == cv_len(cv)) {
+ if (tot) memcpy(out, cv_data(cv), tot);
+ return;
+ }
+ }
+ else {
+ type_error("array", "cons", *init);
+ }
+ lerror(ArgError, "array: invalid size");
+ }
+}
+
+static size_t predict_arraylen(value_t *args, u_int32_t nargs, size_t *elsz)
+{
+ int junk;
+ size_t cnt;
+
+ if (nargs < 2)
+ argcount("array", nargs, 2);
+ *elsz = ctype_sizeof(args[0], &junk);
+ if (isvector(args[1])) {
+ cnt = vector_size(args[1]);
+ }
+ else if (iscons(args[1])) {
+ cnt = llength(args[1]);
+ }
+ else if (args[1] == NIL) {
+ cnt = 0;
+ }
+ else {
+ cnt = toulong(args[1], "array");
+ }
+ return cnt;
+}
+
+static value_t alloc_array(value_t type, size_t sz)
+{
+ value_t cv;
+ if (car_(cdr_(type)) == charsym) {
+ cv = cvalue_string(sz);
+ ((cvalue_t*)ptr(cv))->type = type;
+ }
+ else {
+ cv = cvalue(type, sz);
+ }
+ return cv;
+}
+
+value_t cvalue_array(value_t *args, u_int32_t nargs)
+{
+ size_t elsize, cnt, sz;
+
+ cnt = predict_arraylen(args, nargs, &elsize);
+ sz = elsize * cnt;
+
+ value_t cv = alloc_array(listn(3, arraysym, args[0], size_wrap(cnt)), sz);
+ cvalue_array_init(args, nargs, cv_data((cvalue_t*)ptr(cv)), (void*)elsize);
+ return cv;
+}
+
+// NOTE: v must be an array
+size_t cvalue_arraylen(value_t v)
+{
+ cvalue_t *cv = (cvalue_t*)ptr(v);
+ value_t type = cv_type(cv);
+
+ if (iscons(cdr_(cdr_(type)))) {
+ return toulong(car_(cdr_(cdr_(type))), "length");
+ }
+ // incomplete array type
+ int junk;
+ value_t eltype = car_(cdr_(type));
+ size_t elsize = ctype_sizeof(eltype, &junk);
+ return elsize ? cv_len(cv)/elsize : 0;
+}
+
+value_t cvalue_relocate(value_t v)
+{
+ size_t nw;
+ cvalue_t *cv = (cvalue_t*)ptr(v);
+ cvalue_t *nv;
+ value_t ncv;
+
+ if (cv->flags.moved)
+ return cv->type;
+ nw = cv_nwords(cv);
+ if (!cv->flags.islispfunction) {
+ nv = (cvalue_t*)alloc_words(nw);
+ memcpy(nv, cv, nw*sizeof(value_t));
+ ncv = tagptr(nv, TAG_BUILTIN);
+ cv->type = ncv;
+ cv->flags.moved = 1;
+ }
+ else {
+ // guestfunctions are permanent objects, unmanaged
+ nv = cv;
+ ncv = v;
+ }
+ nv->type = relocate(nv->type);
+ return ncv;
+}
+
+size_t cvalue_struct_offs(value_t type, value_t field, int computeTotal,
+ int *palign)
+{
+ value_t fld = car(cdr_(type));
+ size_t fsz, ssz = 0;
+ int al;
+ *palign = 0;
+
+ while (iscons(fld)) {
+ fsz = ctype_sizeof(car(cdr(car_(fld))), &al);
+
+ ssz = ALIGN(ssz, al);
+ if (al > *palign)
+ *palign = al;
+
+ if (!computeTotal && field==car_(car_(fld))) {
+ // found target field
+ return ssz;
+ }
+
+ ssz += fsz;
+ fld = cdr_(fld);
+ }
+ return ALIGN(ssz, *palign);
+}
+
+static size_t cvalue_union_size(value_t type, int *palign)
+{
+ value_t fld = car(cdr_(type));
+ size_t fsz, usz = 0;
+ int al;
+ *palign = 0;
+
+ while (iscons(fld)) {
+ fsz = ctype_sizeof(car(cdr(car_(fld))), &al);
+ if (al > *palign) *palign = al;
+ if (fsz > usz) usz = fsz;
+ fld = cdr_(fld);
+ }
+ return ALIGN(usz, *palign);
+}
+
+// *palign is an output argument giving the alignment required by type
+size_t ctype_sizeof(value_t type, int *palign)
+{
+ if (type == int8sym || type == uint8sym || type == charsym) {
+ *palign = 1;
+ return 1;
+ }
+ if (type == int16sym || type == uint16sym) {
+ *palign = ALIGN2;
+ return 2;
+ }
+ if (type == int32sym || type == uint32sym || type == wcharsym ||
+ type == floatsym) {
+ *palign = ALIGN4;
+ return 4;
+ }
+ if (type == int64sym || type == uint64sym || type == doublesym) {
+ *palign = ALIGN8;
+ return 8;
+ }
+ if (type == longsym || type == ulongsym) {
+#ifdef BITS64
+ *palign = ALIGN8;
+ return 8;
+#else
+ *palign = ALIGN4;
+ return 4;
+#endif
+ }
+ if (iscons(type)) {
+ value_t hed = car_(type);
+ if (hed == pointersym || hed == cfunctionsym || hed == lispvaluesym) {
+ *palign = struct_aligns[sizeof(void*)-1];
+ return sizeof(void*);
+ }
+ if (hed == arraysym) {
+ value_t t = car(cdr_(type));
+ if (!iscons(cdr_(cdr_(type))))
+ lerror(ArgError, "sizeof: incomplete type");
+ value_t n = car_(cdr_(cdr_(type)));
+ size_t sz = toulong(n, "sizeof");
+ return sz * ctype_sizeof(t, palign);
+ }
+ else if (hed == structsym) {
+ return cvalue_struct_offs(type, NIL, 1, palign);
+ }
+ else if (hed == unionsym) {
+ return cvalue_union_size(type, palign);
+ }
+ else if (hed == enumsym) {
+ *palign = ALIGN4;
+ return 4;
+ }
+ }
+ lerror(ArgError, "sizeof: invalid c type");
+ return 0;
+}
+
+value_t cvalue_sizeof(value_t *args, u_int32_t nargs)
+{
+ cvalue_t *cv;
+ argcount("sizeof", nargs, 1);
+ if (iscvalue(args[0])) {
+ cv = (cvalue_t*)ptr(args[0]);
+ return size_wrap(cv_len(cv));
+ }
+ int a;
+ return size_wrap(ctype_sizeof(args[0], &a));
+}
+
+value_t cvalue_typeof(value_t *args, u_int32_t nargs)
+{
+ argcount("typeof", nargs, 1);
+ switch(tag(args[0])) {
+ case TAG_CONS: return conssym;
+ case TAG_NUM: return fixnumsym;
+ case TAG_SYM: return symbolsym;
+ case TAG_BUILTIN:
+ if (isbuiltin(args[0]))
+ return builtinsym;
+ if (discriminateAsVector(args[0]))
+ return vectorsym;
+ }
+ return cv_type((cvalue_t*)ptr(args[0]));
+}
+
+value_t cvalue_copy(value_t v)
+{
+ assert(iscvalue(v));
+ PUSH(v);
+ cvalue_t *cv = (cvalue_t*)ptr(v);
+ size_t nw = cv_nwords(cv);
+ value_t *pnv = alloc_words(nw);
+ v = POP(); cv = (cvalue_t*)ptr(v);
+ memcpy(pnv, cv, nw * sizeof(value_t));
+ if (!cv->flags.inlined) {
+ size_t len = cv_len(cv);
+ if (cv->flags.cstring) len++;
+ void *data = malloc(len);
+ memcpy(data, cv_data(cv), len);
+ if (cv->flags.prim)
+ ((cprim_t*)pnv)->data = data;
+ else
+ ((cvalue_t*)pnv)->data = data;
+ autorelease((cvalue_t*)pnv);
+ }
+
+ return tagptr(pnv, TAG_BUILTIN);
+}
+
+static void cvalue_init(value_t type, value_t *vs, u_int32_t nv, void *dest)
+{
+ cvinitfunc_t f;
+ unsigned int i, na=0;
+
+ if (issymbol(type)) {
+ f = ((symbol_t*)ptr(type))->dlcache;
+ }
+ else if (!iscons(type)) {
+ f = NULL;
+ lerror(ArgError, "c-value: invalid c type");
+ }
+ else {
+ value_t head = car_(type);
+ f = ((symbol_t*)ptr(head))->dlcache;
+ type = cdr_(type);
+ while (iscons(type)) {
+ PUSH(car_(type));
+ na++;
+ type = cdr_(type);
+ }
+ }
+ for(i=0; i < nv; i++)
+ PUSH(vs[i]);
+ na += nv;
+ f(&Stack[SP-na], na, dest, NULL);
+ POPN(na);
+}
+
+static numerictype_t sym_to_numtype(value_t type)
+{
+ if (type == int8sym)
+ return T_INT8;
+ else if (type == uint8sym || type == charsym)
+ return T_UINT8;
+ else if (type == int16sym)
+ return T_INT16;
+ else if (type == uint16sym)
+ return T_UINT16;
+#ifdef BITS64
+ else if (type == int32sym || type == wcharsym)
+#else
+ else if (type == int32sym || type == wcharsym || type == longsym)
+#endif
+ return T_INT32;
+#ifdef BITS64
+ else if (type == uint32sym)
+#else
+ else if (type == uint32sym || type == ulongsym)
+#endif
+ return T_UINT32;
+#ifdef BITS64
+ else if (type == int64sym || type == longsym)
+#else
+ else if (type == int64sym)
+#endif
+ return T_INT64;
+#ifdef BITS64
+ else if (type == uint64sym || type == ulongsym)
+#else
+ else if (type == uint64sym)
+#endif
+ return T_UINT64;
+ assert(false);
+ return N_NUMTYPES;
+}
+
+// (new type . args)
+// this provides (1) a way to allocate values with a shared type for
+// efficiency, (2) a uniform interface for allocating cvalues of any
+// type, including user-defined.
+value_t cvalue_new(value_t *args, u_int32_t nargs)
+{
+ if (nargs < 1)
+ argcount("c-value", nargs, 1);
+ value_t type = args[0];
+ value_t cv;
+ if (iscons(type) && car_(type) == arraysym) {
+ // special case to handle incomplete array types bla[]
+ size_t elsz;
+ value_t c = cdr_(type);
+ int na=0;
+ while (iscons(c)) {
+ PUSH(car_(c));
+ c = cdr_(c);
+ na++;
+ }
+ if (nargs > 1) {
+ PUSH(args[1]);
+ na++;
+ }
+ size_t cnt = predict_arraylen(&Stack[SP-na], na, &elsz);
+ cv = alloc_array(type, elsz * cnt);
+ cvalue_array_init(&Stack[SP-na], na, cv_data((cvalue_t*)ptr(cv)),
+ (void*)elsz);
+ POPN(na);
+ }
+ else {
+ int junk;
+ cv = cvalue(type, ctype_sizeof(type, &junk));
+ if (issymbol(type)) {
+ ((cvalue_t*)ptr(cv))->flags.numtype = sym_to_numtype(type);
+ }
+ cvalue_init(type, &args[1], nargs-1, cv_data((cvalue_t*)ptr(cv)));
+ }
+ return cv;
+}
+
+// NOTE: this only compares lexicographically; it ignores numeric formats
+value_t cvalue_compare(value_t a, value_t b)
+{
+ cvalue_t *ca = (cvalue_t*)ptr(a);
+ cvalue_t *cb = (cvalue_t*)ptr(b);
+ char *adata = cv_data(ca);
+ char *bdata = cv_data(cb);
+ size_t asz = cv_len(ca);
+ size_t bsz = cv_len(cb);
+ size_t minsz = asz < bsz ? asz : bsz;
+ int diff = memcmp(adata, bdata, minsz);
+ if (diff == 0) {
+ if (asz > bsz)
+ return fixnum(1);
+ else if (asz < bsz)
+ return fixnum(-1);
+ }
+ return fixnum(diff);
+}
+
+static void check_addr_args(char *fname, size_t typesize, value_t *args,
+ void **data, ulong_t *index)
+{
+ size_t sz;
+ if (!iscvalue(args[0]))
+ type_error(fname, "cvalue", args[0]);
+ *data = cv_data((cvalue_t*)ptr(args[0]));
+ sz = cv_len((cvalue_t*)ptr(args[0]));
+ cvalue_t *cv = (cvalue_t*)ptr(args[1]);
+ if (isfixnum(args[1]))
+ *index = numval(args[1]);
+ else if (!iscvalue(args[1]) || !valid_numtype(cv_numtype(cv)))
+ type_error(fname, "number", args[1]);
+ else
+ *index = conv_to_ulong(cv_data(cv), cv_numtype(cv));
+ if (*index > sz - typesize)
+ bounds_error(fname, args[0], args[1]);
+}
+
+value_t cvalue_get_int8(value_t *args, u_int32_t nargs)
+{
+ void *data; ulong_t index;
+ argcount("get-int8", nargs, 2);
+ check_addr_args("get-int8", sizeof(int8_t), args, &data, &index);
+ return fixnum(((int8_t*)data)[index]);
+}
+
+value_t cvalue_set_int8(value_t *args, u_int32_t nargs)
+{
+ void *data; ulong_t index; int32_t val=0;
+ argcount("set-int8", nargs, 3);
+ check_addr_args("set-int8", sizeof(int8_t), args, &data, &index);
+ cvalue_t *cv = (cvalue_t*)ptr(args[2]);
+ if (isfixnum(args[2]))
+ val = numval(args[2]);
+ else if (!iscvalue(args[2]) || !valid_numtype(cv_numtype(cv)))
+ type_error("set-int8", "number", args[2]);
+ else
+ val = conv_to_int32(cv_data(cv), cv_numtype(cv));
+ ((int8_t*)data)[index] = val;
+ return args[2];
+}
+
+value_t guestfunc(guestfunc_t f)
+{
+ value_t gf = cvalue(symbol_value(gftypesym), sizeof(void*));
+ ((cvalue_t*)ptr(gf))->data = f;
+ ((cvalue_t*)ptr(gf))->flags.islispfunction = 1;
+ size_t nw = cv_nwords((cvalue_t*)ptr(gf));
+ // directly-callable values are assumed not to move for
+ // evaluator performance, so put guestfunction metadata on the
+ // unmanaged heap
+ cvalue_t *buf = malloc(nw * sizeof(value_t));
+ memcpy(buf, ptr(gf), nw*sizeof(value_t));
+ return tagptr(buf, TAG_BUILTIN);
+}
+
+#define cv_intern(tok) tok##sym = symbol(#tok)
+#define ctor_cv_intern(tok) cv_intern(tok); set(tok##sym, guestfunc(cvalue_##tok))
+#define symbol_dlcache(s) (((symbol_t*)ptr(s))->dlcache)
+#define cache_initfunc(tok) symbol_dlcache(tok##sym) = &cvalue_##tok##_init
+
+void cvalues_init()
+{
+ int i;
+
+ // compute struct field alignment required for primitives of sizes 1-8
+ for(i=0; i < 8; i++)
+ struct_aligns[i] -= (i+1);
+ ALIGN2 = struct_aligns[1];
+ ALIGN4 = struct_aligns[3];
+ ALIGN8 = struct_aligns[7];
+
+ cv_intern(uint32);
+ cv_intern(pointer);
+ cfunctionsym = symbol("c-function");
+ cv_intern(lispvalue);
+ gftypesym = symbol("*guest-function-type*");
+ setc(gftypesym, listn(3, cfunctionsym, lispvaluesym,
+ list2(list2(pointersym, lispvaluesym), uint32sym)));
+ set(uint32sym, guestfunc(cvalue_uint32));
+
+ ctor_cv_intern(int8);
+ ctor_cv_intern(uint8);
+ ctor_cv_intern(int16);
+ ctor_cv_intern(uint16);
+ ctor_cv_intern(int32);
+ ctor_cv_intern(int64);
+ ctor_cv_intern(uint64);
+ ctor_cv_intern(char);
+ ctor_cv_intern(wchar);
+ ctor_cv_intern(long);
+ ctor_cv_intern(ulong);
+ ctor_cv_intern(float);
+ ctor_cv_intern(double);
+
+ ctor_cv_intern(array);
+ ctor_cv_intern(enum);
+ cv_intern(struct);
+ cv_intern(union);
+ cv_intern(void);
+ set(symbol("c-value"), guestfunc(cvalue_new));
+ set(symbol("get-int8"), guestfunc(cvalue_get_int8));
+ set(symbol("set-int8"), guestfunc(cvalue_set_int8));
+
+ cv_intern(autorelease);
+ ctor_cv_intern(typeof);
+ ctor_cv_intern(sizeof);
+
+ // set up references to the init functions for each primitive type.
+ // this is used for fast access in constructors for compound types
+ // like arrays that need to initialize (but not allocate) elements.
+ cache_initfunc(int8);
+ cache_initfunc(uint8);
+ cache_initfunc(int16);
+ cache_initfunc(uint16);
+ cache_initfunc(int32);
+ cache_initfunc(uint32);
+ cache_initfunc(int64);
+ cache_initfunc(uint64);
+ cache_initfunc(char);
+ cache_initfunc(wchar);
+ cache_initfunc(long);
+ cache_initfunc(ulong);
+ cache_initfunc(float);
+ cache_initfunc(double);
+
+ cache_initfunc(array);
+ cache_initfunc(enum);
+
+ stringtypesym = symbol("*string-type*");
+ setc(stringtypesym, list2(arraysym, charsym));
+
+ wcstringtypesym = symbol("*wcstring-type*");
+ setc(wcstringtypesym, list2(arraysym, wcharsym));
+
+ emptystringsym = symbol("*empty-string*");
+ setc(emptystringsym, cvalue_pinned_cstring(""));
+}
+
+#define RETURN_NUM_AS(var, type) return(mk_##type((type##_t)var))
+
+value_t return_from_uint64(uint64_t Uaccum)
+{
+ if (fits_fixnum(Uaccum)) {
+ return fixnum((fixnum_t)Uaccum);
+ }
+ if (Uaccum > (uint64_t)S64_MAX) {
+ RETURN_NUM_AS(Uaccum, uint64);
+ }
+ else if (Uaccum > (uint64_t)UINT_MAX) {
+ RETURN_NUM_AS(Uaccum, int64);
+ }
+ else if (Uaccum > (uint64_t)INT_MAX) {
+ RETURN_NUM_AS(Uaccum, uint32);
+ }
+ RETURN_NUM_AS(Uaccum, int32);
+}
+
+value_t return_from_int64(int64_t Saccum)
+{
+ if (fits_fixnum(Saccum)) {
+ return fixnum((fixnum_t)Saccum);
+ }
+ if (Saccum > (int64_t)UINT_MAX || Saccum < (int64_t)INT_MIN) {
+ RETURN_NUM_AS(Saccum, int64);
+ }
+ else if (Saccum > (int64_t)INT_MAX) {
+ RETURN_NUM_AS(Saccum, uint32);
+ }
+ RETURN_NUM_AS(Saccum, int32);
+}
+
+value_t fl_add_any(value_t *args, u_int32_t nargs, fixnum_t carryIn)
+{
+ uint64_t Uaccum=0;
+ int64_t Saccum = carryIn;
+ double Faccum=0;
+ uint32_t i;
+
+ for(i=0; i < nargs; i++) {
+ if (isfixnum(args[i])) {
+ Saccum += numval(args[i]);
+ continue;
+ }
+ else if (iscvalue(args[i])) {
+ cvalue_t *cv = (cvalue_t*)ptr(args[i]);
+ void *a = cv_data(cv);
+ int64_t i64;
+ switch(cv_numtype(cv)) {
+ case T_INT8: Saccum += *(int8_t*)a; break;
+ case T_UINT8: Saccum += *(uint8_t*)a; break;
+ case T_INT16: Saccum += *(int16_t*)a; break;
+ case T_UINT16: Saccum += *(uint16_t*)a; break;
+ case T_INT32: Saccum += *(int32_t*)a; break;
+ case T_UINT32: Saccum += *(uint32_t*)a; break;
+ case T_INT64:
+ i64 = *(int64_t*)a;
+ if (i64 > 0)
+ Uaccum += (uint64_t)i64;
+ else
+ Saccum += i64;
+ break;
+ case T_UINT64: Uaccum += *(uint64_t*)a; break;
+ case T_FLOAT: Faccum += *(float*)a; break;
+ case T_DOUBLE: Faccum += *(double*)a; break;
+ default:
+ goto add_type_error;
+ }
+ continue;
+ }
+ add_type_error:
+ type_error("+", "number", args[i]);
+ }
+ if (Faccum != 0) {
+ Faccum += Uaccum;
+ Faccum += Saccum;
+ return mk_double(Faccum);
+ }
+ else if (Saccum < 0) {
+ uint64_t negpart = (uint64_t)(-Saccum);
+ if (negpart > Uaccum) {
+ Saccum += (int64_t)Uaccum;
+ // return value in Saccum
+ if (Saccum >= INT_MIN) {
+ if (fits_fixnum(Saccum)) {
+ return fixnum((fixnum_t)Saccum);
+ }
+ RETURN_NUM_AS(Saccum, int32);
+ }
+ RETURN_NUM_AS(Saccum, int64);
+ }
+ Uaccum -= negpart;
+ }
+ else {
+ Uaccum += (uint64_t)Saccum;
+ }
+ // return value in Uaccum
+ return return_from_uint64(Uaccum);
+}
+
+value_t fl_neg(value_t n)
+{
+ if (isfixnum(n)) {
+ return fixnum(-numval(n));
+ }
+ else if (iscvalue(n)) {
+ cvalue_t *cv = (cvalue_t*)ptr(n);
+ void *a = cv_data(cv);
+ uint32_t ui32;
+ int32_t i32;
+ int64_t i64;
+ switch(cv_numtype(cv)) {
+ case T_INT8: return fixnum(-(int32_t)*(int8_t*)a);
+ case T_UINT8: return fixnum(-(int32_t)*(uint8_t*)a);
+ case T_INT16: return fixnum(-(int32_t)*(int16_t*)a);
+ case T_UINT16: return fixnum(-(int32_t)*(uint16_t*)a);
+ case T_INT32:
+ i32 = *(int32_t*)a;
+ if (i32 == (int32_t)BIT31)
+ return mk_uint32((uint32_t)BIT31);
+ return mk_int32(-i32);
+ case T_UINT32:
+ ui32 = *(uint32_t*)a;
+ if (ui32 <= ((uint32_t)INT_MAX)+1) return mk_int32(-(int32_t)ui32);
+ return mk_int64(-(int64_t)ui32);
+ case T_INT64:
+ i64 = *(int64_t*)a;
+ if (i64 == (int64_t)BIT63)
+ return mk_uint64((uint64_t)BIT63);
+ return mk_int64(-i64);
+ case T_UINT64: return mk_int64(-(int64_t)*(uint64_t*)a);
+ case T_FLOAT: return mk_float(-*(float*)a);
+ case T_DOUBLE: return mk_double(-*(double*)a);
+ break;
+ }
+ }
+ type_error("-", "number", n);
+}
+
+value_t fl_mul_any(value_t *args, u_int32_t nargs, int64_t Saccum)
+{
+ uint64_t Uaccum=1;
+ double Faccum=1;
+ uint32_t i;
+
+ for(i=0; i < nargs; i++) {
+ if (isfixnum(args[i])) {
+ Saccum *= numval(args[i]);
+ continue;
+ }
+ else if (iscvalue(args[i])) {
+ cvalue_t *cv = (cvalue_t*)ptr(args[i]);
+ void *a = cv_data(cv);
+ int64_t i64;
+ switch(cv_numtype(cv)) {
+ case T_INT8: Saccum *= *(int8_t*)a; break;
+ case T_UINT8: Saccum *= *(uint8_t*)a; break;
+ case T_INT16: Saccum *= *(int16_t*)a; break;
+ case T_UINT16: Saccum *= *(uint16_t*)a; break;
+ case T_INT32: Saccum *= *(int32_t*)a; break;
+ case T_UINT32: Saccum *= *(uint32_t*)a; break;
+ case T_INT64:
+ i64 = *(int64_t*)a;
+ if (i64 > 0)
+ Uaccum *= (uint64_t)i64;
+ else
+ Saccum *= i64;
+ break;
+ case T_UINT64: Uaccum *= *(uint64_t*)a; break;
+ case T_FLOAT: Faccum *= *(float*)a; break;
+ case T_DOUBLE: Faccum *= *(double*)a; break;
+ default:
+ goto mul_type_error;
+ }
+ continue;
+ }
+ mul_type_error:
+ type_error("*", "number", args[i]);
+ }
+ if (Faccum != 1) {
+ Faccum *= Uaccum;
+ Faccum *= Saccum;
+ return mk_double(Faccum);
+ }
+ else if (Saccum < 0) {
+ Saccum *= (int64_t)Uaccum;
+ if (Saccum >= INT_MIN) {
+ if (fits_fixnum(Saccum)) {
+ return fixnum((fixnum_t)Saccum);
+ }
+ RETURN_NUM_AS(Saccum, int32);
+ }
+ RETURN_NUM_AS(Saccum, int64);
+ }
+ else {
+ Uaccum *= (uint64_t)Saccum;
+ }
+ return return_from_uint64(Uaccum);
+}
+
+value_t fl_div2(value_t a, value_t b)
+{
+ double da, db;
+ int_t ai, bi;
+ int ta, tb;
+ void *aptr=NULL, *bptr=NULL;
+ cvalue_t *cv;
+
+ if (isfixnum(a)) {
+ ai = numval(a);
+ aptr = &ai;
+ ta = T_FIXNUM;
+ }
+ else if (iscvalue(a)) {
+ cv = (cvalue_t*)ptr(a);
+ ta = cv_numtype(cv);
+ if (ta <= T_DOUBLE)
+ aptr = cv_data(cv);
+ }
+ if (aptr == NULL)
+ type_error("/", "number", a);
+ if (isfixnum(b)) {
+ bi = numval(b);
+ bptr = &bi;
+ tb = T_FIXNUM;
+ }
+ else if (iscvalue(b)) {
+ cv = (cvalue_t*)ptr(b);
+ tb = cv_numtype(cv);
+ if (tb <= T_DOUBLE)
+ bptr = cv_data(cv);
+ }
+ if (bptr == NULL)
+ type_error("/", "number", b);
+
+ if (ta == T_FLOAT) {
+ db = conv_to_double(bptr, tb);
+ da = (double)*(float*)aptr / db;
+ return mk_double(da);
+ }
+ if (ta == T_DOUBLE) {
+ db = conv_to_double(bptr, tb);
+ da = *(double*)aptr / db;
+ return mk_double(da);
+ }
+ if (tb == T_FLOAT) {
+ da = conv_to_double(aptr, ta);
+ da /= (double)*(float*)bptr;
+ return mk_double(da);
+ }
+ if (tb == T_DOUBLE) {
+ da = conv_to_double(aptr, ta);
+ da /= *(double*)bptr;
+ return mk_double(da);
+ }
+
+ int64_t a64, b64;
+
+ if (ta == T_UINT64) {
+ if (tb == T_UINT64) {
+ if (*(uint64_t*)bptr == 0) goto div_error;
+ return return_from_uint64(*(uint64_t*)aptr / *(uint64_t*)bptr);
+ }
+ b64 = conv_to_int64(bptr, tb);
+ if (b64 < 0) {
+ return return_from_int64(-(int64_t)(*(uint64_t*)aptr /
+ (uint64_t)(-b64)));
+ }
+ if (b64 == 0)
+ goto div_error;
+ return return_from_uint64(*(uint64_t*)aptr / (uint64_t)b64);
+ }
+ if (tb == T_UINT64) {
+ if (*(uint64_t*)bptr == 0) goto div_error;
+ a64 = conv_to_int64(aptr, ta);
+ if (a64 < 0) {
+ return return_from_int64(-((int64_t)((uint64_t)(-a64) /
+ *(uint64_t*)bptr)));
+ }
+ return return_from_uint64((uint64_t)a64 / *(uint64_t*)bptr);
+ }
+
+ b64 = conv_to_int64(bptr, tb);
+ if (b64 == 0) goto div_error;
+
+ return return_from_int64(conv_to_int64(aptr, ta) / b64);
+ div_error:
+ lerror(DivideError, "/: division by zero");
+}
+
+static void *int_data_ptr(value_t a, int *pnumtype, char *fname)
+{
+ cvalue_t *cv;
+ if (iscvalue(a)) {
+ cv = (cvalue_t*)ptr(a);
+ *pnumtype = cv_numtype(cv);
+ if (*pnumtype < T_FLOAT)
+ return cv_data(cv);
+ }
+ type_error(fname, "integer", a);
+ return NULL;
+}
+
+value_t fl_bitwise_not(value_t a)
+{
+ cvalue_t *cv;
+ int ta;
+ void *aptr;
+
+ if (iscvalue(a)) {
+ cv = (cvalue_t*)ptr(a);
+ ta = cv_numtype(cv);
+ aptr = cv_data(cv);
+ switch (ta) {
+ case T_INT8: return mk_int8(~*(int8_t *)aptr);
+ case T_UINT8: return mk_uint8(~*(uint8_t *)aptr);
+ case T_INT16: return mk_int16(~*(int16_t *)aptr);
+ case T_UINT16: return mk_uint16(~*(uint16_t*)aptr);
+ case T_INT32: return mk_int32(~*(int32_t *)aptr);
+ case T_UINT32: return mk_uint32(~*(uint32_t*)aptr);
+ case T_INT64: return mk_int64(~*(int64_t *)aptr);
+ case T_UINT64: return mk_uint64(~*(uint64_t*)aptr);
+ }
+ }
+ type_error("~", "integer", a);
+ return NIL;
+}
+
+#define BITSHIFT_OP(name, op) \
+value_t fl_##name(value_t a, int n) \
+{ \
+ cvalue_t *cv; \
+ int ta; \
+ void *aptr; \
+ if (iscvalue(a)) { \
+ cv = (cvalue_t*)ptr(a); \
+ ta = cv_numtype(cv); \
+ aptr = cv_data(cv); \
+ switch (ta) { \
+ case T_INT8: return mk_int8((*(int8_t *)aptr) op n); \
+ case T_UINT8: return mk_uint8((*(uint8_t *)aptr) op n); \
+ case T_INT16: return mk_int16((*(int16_t *)aptr) op n); \
+ case T_UINT16: return mk_uint16((*(uint16_t*)aptr) op n); \
+ case T_INT32: return mk_int32((*(int32_t *)aptr) op n); \
+ case T_UINT32: return mk_uint32((*(uint32_t*)aptr) op n); \
+ case T_INT64: return mk_int64((*(int64_t *)aptr) op n); \
+ case T_UINT64: return mk_uint64((*(uint64_t*)aptr) op n); \
+ } \
+ } \
+ type_error(#op, "integer", a); \
+ return NIL; \
+}
+BITSHIFT_OP(shl,<<)
+BITSHIFT_OP(shr,>>)
+
+value_t fl_bitwise_op(value_t a, value_t b, int opcode, char *fname)
+{
+ int_t ai, bi;
+ int ta, tb, itmp;
+ void *aptr=NULL, *bptr=NULL, *ptmp;
+ int64_t b64;
+
+ if (isfixnum(a)) {
+ ta = T_FIXNUM;
+ ai = numval(a);
+ aptr = &ai;
+ bptr = int_data_ptr(b, &tb, fname);
+ }
+ else {
+ aptr = int_data_ptr(a, &ta, fname);
+ if (isfixnum(b)) {
+ tb = T_FIXNUM;
+ bi = numval(b);
+ bptr = &bi;
+ }
+ else {
+ bptr = int_data_ptr(b, &tb, fname);
+ }
+ }
+ if (ta < tb) {
+ itmp = ta; ta = tb; tb = itmp;
+ ptmp = aptr; aptr = bptr; bptr = ptmp;
+ }
+ // now a's type is larger than or same as b's
+ b64 = conv_to_int64(bptr, tb);
+ switch (opcode) {
+ case 0:
+ switch (ta) {
+ case T_INT8: return mk_int8( *(int8_t *)aptr & (int8_t )b64);
+ case T_UINT8: return mk_uint8( *(uint8_t *)aptr & (uint8_t )b64);
+ case T_INT16: return mk_int16( *(int16_t*)aptr & (int16_t )b64);
+ case T_UINT16: return mk_uint16(*(uint16_t*)aptr & (uint16_t)b64);
+ case T_INT32: return mk_int32( *(int32_t*)aptr & (int32_t )b64);
+ case T_UINT32: return mk_uint32(*(uint32_t*)aptr & (uint32_t)b64);
+ case T_INT64: return mk_int64( *(int64_t*)aptr & (int64_t )b64);
+ case T_UINT64: return mk_uint64(*(uint64_t*)aptr & (uint64_t)b64);
+ }
+ break;
+ case 1:
+ switch (ta) {
+ case T_INT8: return mk_int8( *(int8_t *)aptr | (int8_t )b64);
+ case T_UINT8: return mk_uint8( *(uint8_t *)aptr | (uint8_t )b64);
+ case T_INT16: return mk_int16( *(int16_t*)aptr | (int16_t )b64);
+ case T_UINT16: return mk_uint16(*(uint16_t*)aptr | (uint16_t)b64);
+ case T_INT32: return mk_int32( *(int32_t*)aptr | (int32_t )b64);
+ case T_UINT32: return mk_uint32(*(uint32_t*)aptr | (uint32_t)b64);
+ case T_INT64: return mk_int64( *(int64_t*)aptr | (int64_t )b64);
+ case T_UINT64: return mk_uint64(*(uint64_t*)aptr | (uint64_t)b64);
+ }
+ break;
+ case 2:
+ switch (ta) {
+ case T_INT8: return mk_int8( *(int8_t *)aptr ^ (int8_t )b64);
+ case T_UINT8: return mk_uint8( *(uint8_t *)aptr ^ (uint8_t )b64);
+ case T_INT16: return mk_int16( *(int16_t*)aptr ^ (int16_t )b64);
+ case T_UINT16: return mk_uint16(*(uint16_t*)aptr ^ (uint16_t)b64);
+ case T_INT32: return mk_int32( *(int32_t*)aptr ^ (int32_t )b64);
+ case T_UINT32: return mk_uint32(*(uint32_t*)aptr ^ (uint32_t)b64);
+ case T_INT64: return mk_int64( *(int64_t*)aptr ^ (int64_t )b64);
+ case T_UINT64: return mk_uint64(*(uint64_t*)aptr ^ (uint64_t)b64);
+ }
+ }
+ assert(0);
+ return NIL;
+}
--- /dev/null
+++ b/femtolisp/dict.lsp
@@ -1,0 +1,51 @@
+; dictionary as binary tree
+
+(defun dict () ())
+
+; node representation ((k . v) L R)
+(defun dict-peek (d key nf)
+ (if (null d) nf
+ (let ((c (compare key (caar d))))
+ (cond ((= c 0) (cdar d))
+ ((< c 0) (dict-peek (cadr d) key nf))
+ (T (dict-peek (caddr d) key nf))))))
+
+(defun dict-get (d key) (dict-peek d key nil))
+
+(defun dict-put (d key v)
+ (if (null d) (list (cons key v) (dict) (dict))
+ (let ((c (compare key (caar d))))
+ (cond ((= c 0) (list (cons key v) (cadr d) (caddr d)))
+ ((< c 0) (list (car d)
+ (dict-put (cadr d) key v)
+ (caddr d)))
+ (T (list (car d)
+ (cadr d)
+ (dict-put (caddr d) key v)))))))
+
+; mutable dictionary
+(defun dict-nput (d key v)
+ (if (null d) (list (cons key v) (dict) (dict))
+ (let ((c (compare key (caar d))))
+ (cond ((= c 0) (rplacd (car d) v))
+ ((< c 0) (setf (cadr d) (dict-nput (cadr d) key v)))
+ (T (setf (caddr d) (dict-nput (caddr d) key v))))
+ d)))
+
+(defun dict-collect (f d)
+ (if (null d) ()
+ (cons (f (caar d) (cdar d)) (nconc (dict-collect f (cadr d))
+ (dict-collect f (caddr d))))))
+
+(defun dict-keys (d) (dict-collect K d))
+(defun dict-pairs (d) (dict-collect cons d))
+
+(defun dict-each (f d)
+ (if (null d) ()
+ (progn (f (caar d) (cdar d))
+ (dict-each f (cadr d))
+ (dict-each f (caddr d)))))
+
+(defun alist-to-dict (a)
+ (foldl (lambda (p d) (dict-put d (car p) (cdr p)))
+ (dict) a))
--- /dev/null
+++ b/femtolisp/equal.c
@@ -1,0 +1,253 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+#include "llt.h"
+#include "flisp.h"
+
+// comparable with ==
+#define eq_comparable(a,b) (!(((a)|(b))&0x1))
+
+// is it a leaf? (i.e. does not lead to other values)
+static inline int leafp(value_t a)
+{
+ return (!iscons(a) && !isvector(a));
+}
+
+static value_t eq_class(ptrhash_t *table, value_t key)
+{
+ value_t c = (value_t)ptrhash_get(table, (void*)key);
+ if (c == (value_t)PH_NOTFOUND)
+ return NIL;
+ if (c == key)
+ return c;
+ return eq_class(table, c);
+}
+
+static void eq_union(ptrhash_t *table, value_t a, value_t b,
+ value_t c, value_t cb)
+{
+ value_t ca = (c==NIL ? a : c);
+ if (cb != NIL)
+ ptrhash_put(table, (void*)cb, (void*)ca);
+ ptrhash_put(table, (void*)a, (void*)ca);
+ ptrhash_put(table, (void*)b, (void*)ca);
+}
+
+// a is a fixnum, b is a cvalue
+static int compare_num_cvalue(value_t a, value_t b)
+{
+ cvalue_t *bcv = (cvalue_t*)ptr(b);
+ numerictype_t bt;
+ if (valid_numtype(bt=cv_numtype(bcv))) {
+ fixnum_t ia = numval(a);
+ void *bptr = cv_data(bcv);
+ if (cmp_eq(&ia, T_FIXNUM, bptr, bt))
+ return 0;
+ if (cmp_lt(&ia, T_FIXNUM, bptr, bt))
+ return -1;
+ }
+ else {
+ return -1;
+ }
+ return 1;
+}
+
+static value_t bounded_compare(value_t a, value_t b, int bound);
+static value_t cyc_compare(value_t a, value_t b, ptrhash_t *table);
+
+static value_t bounded_vector_compare(value_t a, value_t b, int bound)
+{
+ size_t la = vector_size(a);
+ size_t lb = vector_size(b);
+ size_t m, i;
+ m = la < lb ? la : lb;
+ for (i = 0; i < m; i++) {
+ value_t d = bounded_compare(vector_elt(a,i), vector_elt(b,i), bound-1);
+ if (d==NIL || numval(d)!=0) return d;
+ }
+ if (la < lb) return fixnum(-1);
+ if (la > lb) return fixnum(1);
+ return fixnum(0);
+}
+
+// strange comparisons are resolved arbitrarily but consistently.
+// ordering: number < builtin < cvalue < vector < symbol < cons
+static value_t bounded_compare(value_t a, value_t b, int bound)
+{
+ value_t d;
+
+ compare_top:
+ if (a == b) return fixnum(0);
+ if (bound <= 0)
+ return NIL;
+ switch (tag(a)) {
+ case TAG_NUM:
+ if (isfixnum(b)) {
+ return (numval(a) < numval(b)) ? fixnum(-1) : fixnum(1);
+ }
+ if (iscvalue(b)) {
+ return fixnum(compare_num_cvalue(a, b));
+ }
+ return fixnum(-1);
+ case TAG_SYM:
+ if (tag(b) < TAG_SYM) return fixnum(1);
+ if (tag(b) > TAG_SYM) return fixnum(-1);
+ return fixnum(strcmp(symbol_name(a), symbol_name(b)));
+ case TAG_BUILTIN:
+ if (tag(b) > TAG_BUILTIN) return fixnum(-1);
+ if (tag(b) == TAG_BUILTIN) {
+ if (uintval(a) < N_BUILTINS || uintval(b) < N_BUILTINS) {
+ return (uintval(a) < uintval(b)) ? fixnum(-1) : fixnum(1);
+ }
+ if (discriminateAsVector(a)) {
+ if (discriminateAsVector(b))
+ return bounded_vector_compare(a, b, bound);
+ return fixnum(1);
+ }
+ if (discriminateAsVector(b))
+ return fixnum(-1);
+ assert(iscvalue(a));
+ assert(iscvalue(b));
+ cvalue_t *acv=(cvalue_t*)ptr(a), *bcv=(cvalue_t*)ptr(b);
+ numerictype_t at, bt;
+ if (valid_numtype(at=cv_numtype(acv)) &&
+ valid_numtype(bt=cv_numtype(bcv))) {
+ void *aptr = cv_data(acv);
+ void *bptr = cv_data(bcv);
+ if (cmp_eq(aptr, at, bptr, bt))
+ return fixnum(0);
+ if (cmp_lt(aptr, at, bptr, bt))
+ return fixnum(-1);
+ return fixnum(1);
+ }
+ return cvalue_compare(a, b);
+ }
+ assert(isfixnum(b));
+ return fixnum(-compare_num_cvalue(b, a));
+ case TAG_CONS:
+ if (tag(b) < TAG_CONS) return fixnum(1);
+ d = bounded_compare(car_(a), car_(b), bound-1);
+ if (numval(d) != 0) return d;
+ a = cdr_(a); b = cdr_(b);
+ bound--;
+ goto compare_top;
+ }
+ return NIL;
+}
+
+static value_t cyc_vector_compare(value_t a, value_t b, ptrhash_t *table)
+{
+ size_t la = vector_size(a);
+ size_t lb = vector_size(b);
+ size_t m, i;
+ value_t d, xa, xb, ca, cb;
+
+ // first try to prove them different with no recursion
+ m = la < lb ? la : lb;
+ for (i = 0; i < m; i++) {
+ xa = vector_elt(a,i);
+ xb = vector_elt(b,i);
+ if (leafp(xa) || leafp(xb)) {
+ d = bounded_compare(xa, xb, 1);
+ if (numval(d)!=0) return d;
+ }
+ else if (tag(xa) < tag(xb)) {
+ return fixnum(-1);
+ }
+ else if (tag(xa) > tag(xb)) {
+ return fixnum(1);
+ }
+ }
+
+ ca = eq_class(table, a);
+ cb = eq_class(table, b);
+ if (ca!=NIL && ca==cb)
+ return fixnum(0);
+
+ eq_union(table, a, b, ca, cb);
+
+ for (i = 0; i < m; i++) {
+ xa = vector_elt(a,i);
+ xb = vector_elt(b,i);
+ if (!leafp(xa) && !leafp(xb)) {
+ d = cyc_compare(xa, xb, table);
+ if (numval(d)!=0)
+ return d;
+ }
+ }
+
+ if (la < lb) return fixnum(-1);
+ if (la > lb) return fixnum(1);
+ return fixnum(0);
+}
+
+static value_t cyc_compare(value_t a, value_t b, ptrhash_t *table)
+{
+ if (a==b)
+ return fixnum(0);
+ if (iscons(a)) {
+ if (iscons(b)) {
+ value_t aa = car_(a); value_t da = cdr_(a);
+ value_t ab = car_(b); value_t db = cdr_(b);
+ value_t d, ca, cb;
+ if (leafp(aa) || leafp(ab)) {
+ d = bounded_compare(aa, ab, 1);
+ if (numval(d)!=0) return d;
+ }
+ else if (tag(aa) < tag(ab))
+ return fixnum(-1);
+ else if (tag(aa) > tag(ab))
+ return fixnum(1);
+ if (leafp(da) || leafp(db)) {
+ d = bounded_compare(da, db, 1);
+ if (numval(d)!=0) return d;
+ }
+ else if (tag(da) < tag(db))
+ return fixnum(-1);
+ else if (tag(da) > tag(db))
+ return fixnum(1);
+
+ ca = eq_class(table, a);
+ cb = eq_class(table, b);
+ if (ca!=NIL && ca==cb)
+ return fixnum(0);
+
+ eq_union(table, a, b, ca, cb);
+ d = cyc_compare(aa, ab, table);
+ if (numval(d)!=0) return d;
+ return cyc_compare(da, db, table);
+ }
+ else {
+ return fixnum(1);
+ }
+ }
+ else if (isvector(a) && isvector(b)) {
+ return cyc_vector_compare(a, b, table);
+ }
+ return bounded_compare(a, b, 1);
+}
+
+value_t compare(value_t a, value_t b)
+{
+ ptrhash_t h;
+ value_t guess = bounded_compare(a, b, 2048);
+ if (guess != NIL)
+ return guess;
+
+ ptrhash_new(&h, 512);
+ guess = cyc_compare(a, b, &h);
+ ptrhash_free(&h);
+ return guess;
+}
+
+/*
+ optimizations:
+ - use hash updates instead of calling lookup then insert. i.e. get the
+ bp once and use it twice.
+ - preallocate hash table and call reset() instead of new/free
+ - specialized version for equal (unordered comparison)
+ - less redundant tag checking, 3-bit tags
+*/
--- /dev/null
+++ b/femtolisp/equal.scm
@@ -1,0 +1,68 @@
+; Terminating equal predicate
+; by Jeff Bezanson
+;
+; This version only considers pairs and simple atoms.
+
+; equal?, with bounded recursion. returns 0 if we suspect
+; nontermination, otherwise #t or #f for the correct answer.
+(define (bounded-equal a b N)
+ (cond ((<= N 0) 0)
+ ((and (pair? a) (pair? b))
+ (let ((as
+ (bounded-equal (car a) (car b) (- N 1))))
+ (if (number? as)
+ 0
+ (and as
+ (bounded-equal (cdr a) (cdr b) (- N 1))))))
+ (else (eq? a b))))
+
+; union-find algorithm
+
+; find equivalence class of a cons cell, or #f if not yet known
+; the root of a class is a cons that is its own class
+(define (class table key)
+ (let ((c (hashtable-ref table key #f)))
+ (if (or (not c) (eq? c key))
+ c
+ (class table c))))
+
+; move a and b to the same equivalence class, given c and cb
+; as the current values of (class table a) and (class table b)
+; Note: this is not quite optimal. We blindly pick 'a' as the
+; root of the new class, but we should pick whichever class is
+; larger.
+(define (union! table a b c cb)
+ (let ((ca (if c c a)))
+ (if cb
+ (hashtable-set! table cb ca))
+ (hashtable-set! table a ca)
+ (hashtable-set! table b ca)))
+
+; cyclic equal. first, attempt to compare a and b as best
+; we can without recurring. if we can't prove them different,
+; set them equal and move on.
+(define (cyc-equal a b table)
+ (cond ((eq? a b) #t)
+ ((not (and (pair? a) (pair? b))) (eq? a b))
+ (else
+ (let ((aa (car a)) (da (cdr a))
+ (ab (car b)) (db (cdr b)))
+ (cond ((or (not (eq? (atom? aa) (atom? ab)))
+ (not (eq? (atom? da) (atom? db)))) #f)
+ ((and (atom? aa)
+ (not (eq? aa ab))) #f)
+ ((and (atom? da)
+ (not (eq? da db))) #f)
+ (else
+ (let ((ca (class table a))
+ (cb (class table b)))
+ (if (and ca cb (eq? ca cb))
+ #t
+ (begin (union! table a b ca cb)
+ (and (cyc-equal aa ab table)
+ (cyc-equal da db table)))))))))))
+
+(define (equal a b)
+ (let ((guess (bounded-equal a b 2048)))
+ (if (boolean? guess) guess
+ (cyc-equal a b (make-eq-hashtable)))))
--- /dev/null
+++ b/femtolisp/flisp.c
@@ -1,0 +1,1471 @@
+/*
+ femtoLisp
+
+ a minimal interpreter for a minimal lisp dialect
+
+ this lisp dialect uses lexical scope and self-evaluating lambda.
+ it supports 30-bit integers, symbols, conses, and full macros.
+ it is case-sensitive.
+ it features a simple compacting copying garbage collector.
+ it uses a Scheme-style evaluation rule where any expression may appear in
+ head position as long as it evaluates to a function.
+ it uses Scheme-style varargs (dotted formal argument lists)
+ lambdas can have only 1 body expression; use (progn ...) for multiple
+ expressions. this is due to the closure representation
+ (lambda args body . env)
+
+ This is a fork of femtoLisp with advanced reading and printing facilities:
+ * circular structure can be printed and read
+ * #. read macro for eval-when-read and correctly printing builtins
+ * read macros for backquote
+ * symbol character-escaping printer
+
+ * new print algorithm
+ 1. traverse & tag all conses to be printed. when you encounter a cons
+ that is already tagged, add it to a table to give it a #n# index
+ 2. untag a cons when printing it. if cons is in the table, print
+ "#n=" before it in the car, " . #n=" in the cdr. if cons is in the
+ table but already untagged, print #n# in car or " . #n#" in the cdr.
+ * read macros for #n# and #n= using the same kind of table
+ * also need a table of read labels to translate from input indexes to
+ normalized indexes (0 for first label, 1 for next, etc.)
+ * read macro #. for eval-when-read. use for printing builtins, e.g. "#.eq"
+
+ The value of this extra complexity, and what makes this fork worthy of
+ the femtoLisp brand, is that the interpreter is fully "closed" in the
+ sense that all representable values can be read and printed.
+
+ This is a fully fleshed-out lisp built up from femtoLisp. It has all the
+ remaining features needed to be taken seriously:
+ * vectors
+ * exceptions
+ * gensyms (can be usefully read back in, too)
+ * #| multiline comments |#
+ * generic compare function
+ * cvalues system providing C data types and a C FFI
+ * constructor notation for nicely printing arbitrary values
+ * cyclic equal
+ * strings
+ - hash tables
+
+ by Jeff Bezanson
+ Public Domain
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <ctype.h>
+#include <wctype.h>
+#include <sys/types.h>
+#include <locale.h>
+#include <limits.h>
+#include <errno.h>
+#include "llt.h"
+#include "flisp.h"
+
+static char *builtin_names[] =
+ { "quote", "cond", "if", "and", "or", "while", "lambda", "label",
+ "trycatch", "progn",
+
+ "eq", "atom", "not", "symbolp", "numberp", "boundp", "consp",
+ "builtinp", "vectorp", "fixnump", "equal",
+ "cons", "car", "cdr", "rplaca", "rplacd",
+ "eval", "apply", "set", "prog1", "raise",
+ "+", "-", "*", "/", "<", "~", "&", "!", "$",
+ "vector", "aref", "aset", "length", "assoc", "compare" };
+
+static char *stack_bottom;
+#define PROCESS_STACK_SIZE (2*1024*1024)
+#define N_STACK 98304
+value_t Stack[N_STACK];
+u_int32_t SP = 0;
+
+value_t NIL, T, LAMBDA, LABEL, QUOTE, VECTOR, IF, TRYCATCH;
+value_t BACKQUOTE, COMMA, COMMAAT, COMMADOT;
+value_t IOError, ParseError, TypeError, ArgError, UnboundError, MemoryError;
+value_t DivideError, BoundsError, Error;
+value_t conssym, symbolsym, fixnumsym, vectorsym, builtinsym;
+
+static value_t eval_sexpr(value_t e, value_t *penv, int tail, u_int32_t envend);
+static value_t *alloc_words(int n);
+static value_t relocate(value_t v);
+static void do_print(FILE *f, value_t v, int princ);
+
+typedef struct _readstate_t {
+ ptrhash_t backrefs;
+ ptrhash_t gensyms;
+ struct _readstate_t *prev;
+} readstate_t;
+static readstate_t *readstate = NULL;
+
+static void free_readstate(readstate_t *rs)
+{
+ ptrhash_free(&rs->backrefs);
+ ptrhash_free(&rs->gensyms);
+}
+
+static unsigned char *fromspace;
+static unsigned char *tospace;
+static unsigned char *curheap;
+static unsigned char *lim;
+static u_int32_t heapsize = 256*1024;//bytes
+static u_int32_t *consflags;
+static u_int32_t printlabel;
+
+// error utilities ------------------------------------------------------------
+
+// saved execution state for an unwind target
+typedef struct _ectx_t {
+ jmp_buf buf;
+ u_int32_t sp;
+ readstate_t *rdst;
+ struct _ectx_t *prev;
+} exception_context_t;
+
+static exception_context_t *ctx = NULL;
+static value_t lasterror;
+static char lerrorbuf[512];
+
+#define FL_TRY \
+ exception_context_t _ctx; int l__tr, l__ca; \
+ _ctx.sp=SP; _ctx.rdst=readstate; _ctx.prev=ctx; \
+ ctx = &_ctx; \
+ if (!setjmp(_ctx.buf)) \
+ for (l__tr=1; l__tr; l__tr=0, (void)(ctx->prev && (ctx=ctx->prev)))
+
+#define FL_CATCH \
+ else \
+ for (l__ca=1; l__ca; l__ca=0, lerrorbuf[0]='\0', lasterror=NIL)
+
+void raise(value_t e)
+{
+ if (e != lasterror) {
+ lasterror = e;
+ lerrorbuf[0] = '\0'; // overwriting exception; clear error buf
+ }
+ // unwind read state
+ while (readstate != ctx->rdst) {
+ free_readstate(readstate);
+ readstate = readstate->prev;
+ }
+ SP = ctx->sp;
+ exception_context_t *thisctx = ctx;
+ if (ctx->prev) // don't throw past toplevel
+ ctx = ctx->prev;
+ longjmp(thisctx->buf, 1);
+}
+
+void lerror(value_t e, char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vsnprintf(lerrorbuf, sizeof(lerrorbuf), format, args);
+ va_end(args);
+
+ lasterror = e;
+ raise(e);
+}
+
+void type_error(char *fname, char *expected, value_t got)
+{
+ raise(listn(4, TypeError, symbol(fname), symbol(expected), got));
+}
+
+void bounds_error(char *fname, value_t arr, value_t ind)
+{
+ lerror(listn(3, BoundsError, arr, ind), "%s: index out of bounds", fname);
+}
+
+// safe cast operators --------------------------------------------------------
+
+#define SAFECAST_OP(type,ctype,cnvt) \
+ctype to##type(value_t v, char *fname) \
+{ \
+ if (is##type(v)) \
+ return (ctype)cnvt(v); \
+ type_error(fname, #type, v); \
+ return (ctype)0; \
+}
+SAFECAST_OP(cons, cons_t*, ptr)
+SAFECAST_OP(symbol,symbol_t*,ptr)
+SAFECAST_OP(fixnum,fixnum_t, numval)
+SAFECAST_OP(cvalue,cvalue_t*,ptr)
+SAFECAST_OP(string,char*, cvalue_data)
+
+// symbol table ---------------------------------------------------------------
+
+symbol_t *symtab = NULL;
+
+static symbol_t *mk_symbol(char *str)
+{
+ symbol_t *sym;
+
+ sym = (symbol_t*)malloc(sizeof(symbol_t) - sizeof(void*) + strlen(str)+1);
+ sym->left = sym->right = NULL;
+ sym->binding = UNBOUND;
+ sym->syntax = 0;
+ strcpy(&sym->name[0], str);
+ return sym;
+}
+
+static symbol_t **symtab_lookup(symbol_t **ptree, char *str)
+{
+ int x;
+
+ while(*ptree != NULL) {
+ x = strcmp(str, (*ptree)->name);
+ if (x == 0)
+ return ptree;
+ if (x < 0)
+ ptree = &(*ptree)->left;
+ else
+ ptree = &(*ptree)->right;
+ }
+ return ptree;
+}
+
+value_t symbol(char *str)
+{
+ symbol_t **pnode;
+
+ pnode = symtab_lookup(&symtab, str);
+ if (*pnode == NULL)
+ *pnode = mk_symbol(str);
+ return tagptr(*pnode, TAG_SYM);
+}
+
+typedef struct {
+ value_t binding; // global value binding
+ value_t syntax; // syntax environment entry
+ void *dlcache; // dlsym address
+ u_int32_t id;
+} gensym_t;
+
+static u_int32_t _gensym_ctr=0;
+// two static buffers for gensym printing so there can be two
+// gensym names available at a time, mostly for compare()
+static char gsname[2][16];
+static int gsnameno=0;
+value_t gensym(value_t *args, u_int32_t nargs)
+{
+ (void)args;
+ (void)nargs;
+ gensym_t *gs = (gensym_t*)alloc_words(sizeof(gensym_t)/sizeof(void*));
+ gs->id = _gensym_ctr++;
+ gs->binding = UNBOUND;
+ gs->syntax = 0;
+ return tagptr(gs, TAG_SYM);
+}
+
+value_t fl_gensym()
+{
+ return gensym(NULL, 0);
+}
+
+static char *snprintf_gensym_id(char *nbuf, size_t n, u_int32_t g)
+{
+ size_t i=n-1;
+
+ nbuf[i--] = '\0';
+ do {
+ nbuf[i--] = '0' + g%10;
+ g/=10;
+ } while (g && i);
+ nbuf[i] = 'g';
+ return &nbuf[i];
+}
+
+char *symbol_name(value_t v)
+{
+ if (ismanaged(v)) {
+ gensym_t *gs = (gensym_t*)ptr(v);
+ gsnameno = 1-gsnameno;
+ return snprintf_gensym_id(gsname[gsnameno], sizeof(gsname[0]), gs->id);
+ }
+ return ((symbol_t*)ptr(v))->name;
+}
+
+// conses ---------------------------------------------------------------------
+
+void gc(int mustgrow);
+
+static value_t mk_cons(void)
+{
+ cons_t *c;
+
+ if (curheap > lim)
+ gc(0);
+ c = (cons_t*)curheap;
+ curheap += sizeof(cons_t);
+ return tagptr(c, TAG_CONS);
+}
+
+static value_t *alloc_words(int n)
+{
+ value_t *first;
+
+ // the minimum allocation is a 2-word block
+ if (n < 2) n = 2;
+ if ((value_t*)curheap > ((value_t*)lim)+2-n) {
+ gc(0);
+ while ((value_t*)curheap > ((value_t*)lim)+2-n) {
+ gc(1);
+ }
+ }
+ first = (value_t*)curheap;
+ curheap += (n*sizeof(value_t));
+ return first;
+}
+
+// allocate n consecutive conses
+#define cons_reserve(n) tagptr(alloc_words((n)*2), TAG_CONS)
+
+#define cons_index(c) (((cons_t*)ptr(c))-((cons_t*)fromspace))
+#define ismarked(c) bitvector_get(consflags, cons_index(c))
+#define mark_cons(c) bitvector_set(consflags, cons_index(c), 1)
+#define unmark_cons(c) bitvector_set(consflags, cons_index(c), 0)
+
+value_t alloc_vector(size_t n, int init)
+{
+ value_t *c = alloc_words(n+1);
+ value_t v = tagptr(c, TAG_BUILTIN);
+ vector_setsize(v, n);
+ if (init) {
+ unsigned int i;
+ for(i=0; i < n; i++)
+ vector_elt(v, i) = NIL;
+ }
+ return v;
+}
+
+// print ----------------------------------------------------------------------
+
+static int isnumtok(char *tok, value_t *pval);
+static int symchar(char c);
+
+#include "print.c"
+
+// cvalues --------------------------------------------------------------------
+
+#include "cvalues.c"
+
+// collector ------------------------------------------------------------------
+
+static value_t relocate(value_t v)
+{
+ value_t a, d, nc, first, *pcdr;
+
+ if (isfixnum(v))
+ return(v);
+ else if (iscons(v)) {
+ // iterative implementation allows arbitrarily long cons chains
+ pcdr = &first;
+ do {
+ if ((a=car_(v)) == UNBOUND) {
+ *pcdr = cdr_(v);
+ return first;
+ }
+ *pcdr = nc = mk_cons();
+ d = cdr_(v);
+ car_(v) = UNBOUND; cdr_(v) = nc;
+ car_(nc) = relocate(a);
+ pcdr = &cdr_(nc);
+ v = d;
+ } while (iscons(v));
+ *pcdr = (d==NIL) ? NIL : relocate(d);
+
+ return first;
+ }
+ else if (isvectorish(v)) {
+ if (discriminateAsVector(v)) {
+ // 0-length vectors secretly have space for a first element
+ if (vector_elt(v,0) == UNBOUND)
+ return vector_elt(v,-1);
+ size_t i, newsz, sz = vector_size(v);
+ newsz = sz;
+ if (vector_elt(v,-1) & 0x1)
+ newsz += vector_grow_amt(sz);
+ nc = alloc_vector(newsz, 0);
+ a = vector_elt(v,0);
+ vector_elt(v,0) = UNBOUND;
+ vector_elt(v,-1) = nc;
+ i = 0;
+ if (sz > 0) {
+ vector_elt(nc,0) = relocate(a); i++;
+ for(; i < sz; i++)
+ vector_elt(nc,i) = relocate(vector_elt(v,i));
+ }
+ for(; i < newsz; i++)
+ vector_elt(nc,i) = NIL;
+ return nc;
+ }
+ else {
+ return cvalue_relocate(v);
+ }
+ }
+ else if (ismanaged(v)) {
+ assert(issymbol(v));
+ gensym_t *gs = (gensym_t*)ptr(v);
+ if (gs->id == 0xffffffff)
+ return gs->binding;
+ gensym_t *ng = (gensym_t*)alloc_words(sizeof(gensym_t)/sizeof(void*));
+ *ng = *gs;
+ gs->id = 0xffffffff;
+ nc = tagptr(ng, TAG_SYM);
+ gs->binding = nc;
+ if (ng->binding != UNBOUND)
+ ng->binding = relocate(ng->binding);
+ return nc;
+ }
+ return v;
+}
+
+static void trace_globals(symbol_t *root)
+{
+ while (root != NULL) {
+ root->binding = relocate(root->binding);
+ if (iscons(root->syntax))
+ root->syntax = relocate(root->syntax);
+ trace_globals(root->left);
+ root = root->right;
+ }
+}
+
+void gc(int mustgrow)
+{
+ static int grew = 0;
+ void *temp;
+ u_int32_t i;
+ readstate_t *rs;
+
+ curheap = tospace;
+ lim = curheap+heapsize-sizeof(cons_t);
+
+ for (i=0; i < SP; i++)
+ Stack[i] = relocate(Stack[i]);
+ trace_globals(symtab);
+ rs = readstate;
+ while (rs) {
+ for(i=0; i < rs->backrefs.size; i++)
+ rs->backrefs.table[i] = (void*)relocate((value_t)rs->backrefs.table[i]);
+ for(i=0; i < rs->gensyms.size; i++)
+ rs->gensyms.table[i] = (void*)relocate((value_t)rs->gensyms.table[i]);
+ rs = rs->prev;
+ }
+ lasterror = relocate(lasterror);
+#ifdef VERBOSEGC
+ printf("gc found %d/%d live conses\n",
+ (curheap-tospace)/sizeof(cons_t), heapsize/sizeof(cons_t));
+#endif
+ temp = tospace;
+ tospace = fromspace;
+ fromspace = temp;
+
+ // if we're using > 80% of the space, resize tospace so we have
+ // more space to fill next time. if we grew tospace last time,
+ // grow the other half of the heap this time to catch up.
+ if (grew || ((lim-curheap) < (int)(heapsize/5)) || mustgrow) {
+ temp = realloc(tospace, grew ? heapsize : heapsize*2);
+ if (temp == NULL)
+ lerror(MemoryError, "out of memory");
+ tospace = temp;
+ if (!grew) {
+ heapsize*=2;
+ }
+ else {
+ temp = bitvector_resize(consflags, heapsize/sizeof(cons_t), 1);
+ if (temp == NULL)
+ lerror(MemoryError, "out of memory");
+ consflags = (u_int32_t*)temp;
+ }
+ grew = !grew;
+ }
+ if (curheap > lim) // all data was live
+ gc(0);
+}
+
+// utils ----------------------------------------------------------------------
+
+value_t apply(value_t f, value_t l)
+{
+ PUSH(f);
+ PUSH(l);
+ value_t e = cons_reserve(5);
+ value_t x = e;
+ car_(e) = builtin(F_APPLY);
+ cdr_(e) = tagptr(((cons_t*)ptr(e))+1, TAG_CONS); e = cdr_(e);
+ // TODO: consider quoting this if it's a lambda expression
+ car_(e) = Stack[SP-2];
+ cdr_(e) = tagptr(((cons_t*)ptr(e))+1, TAG_CONS); e = cdr_(e);
+ car_(e) = tagptr(((cons_t*)ptr(e))+1, TAG_CONS);
+ cdr_(e) = NIL;
+ e = car_(e);
+ car_(e) = QUOTE;
+ cdr_(e) = tagptr(((cons_t*)ptr(e))+1, TAG_CONS); e = cdr_(e);
+ car_(e) = Stack[SP-1];
+ cdr_(e) = NIL;
+ POPN(2);
+ return toplevel_eval(x);
+}
+
+value_t listn(size_t n, ...)
+{
+ va_list ap;
+ va_start(ap, n);
+ u_int32_t si = SP;
+ size_t i;
+
+ for(i=0; i < n; i++) {
+ value_t a = va_arg(ap, value_t);
+ PUSH(a);
+ }
+ cons_t *c = (cons_t*)alloc_words(n*2);
+ cons_t *l = c;
+ for(i=0; i < n; i++) {
+ c->car = Stack[si++];
+ c->cdr = tagptr(c+1, TAG_CONS);
+ c++;
+ }
+ (c-1)->cdr = NIL;
+
+ POPN(n);
+ va_end(ap);
+ return tagptr(l, TAG_CONS);
+}
+
+value_t list2(value_t a, value_t b)
+{
+ PUSH(a);
+ PUSH(b);
+ cons_t *c = (cons_t*)alloc_words(4);
+ b = POP();
+ a = POP();
+ c[0].car = a;
+ c[0].cdr = tagptr(c+1, TAG_CONS);
+ c[1].car = b;
+ c[1].cdr = NIL;
+ return tagptr(c, TAG_CONS);
+}
+
+value_t fl_cons(value_t a, value_t b)
+{
+ PUSH(a);
+ PUSH(b);
+ value_t c = mk_cons();
+ cdr_(c) = POP();
+ car_(c) = POP();
+ return c;
+}
+
+// NOTE: this is NOT an efficient operation. it is only used by the
+// reader; vectors should not generally be resized.
+// vector_grow requires at least 1 and up to 3 garbage collections!
+static value_t vector_grow(value_t v)
+{
+ size_t s = vector_size(v);
+ size_t d = vector_grow_amt(s);
+ PUSH(v);
+ // first allocate enough space to guarantee the heap will be big enough
+ // for the new vector
+ alloc_words(d);
+ // setting low bit of vector's size acts as a flag to the collector
+ // to grow this vector as it is relocated
+ ((size_t*)ptr(Stack[SP-1]))[0] |= 0x1;
+ gc(0);
+ return POP();
+}
+
+extern value_t compare(value_t a, value_t b);
+
+int isnumber(value_t v)
+{
+ return (isfixnum(v) ||
+ (iscvalue(v) &&
+ valid_numtype(cv_numtype((cvalue_t*)ptr(v)))));
+}
+
+// read -----------------------------------------------------------------------
+
+#include "read.c"
+
+// eval -----------------------------------------------------------------------
+
+// return a cons element of v whose car is item
+static value_t assoc(value_t item, value_t v)
+{
+ value_t bind;
+
+ while (iscons(v)) {
+ bind = car_(v);
+ if (iscons(bind) && car_(bind) == item)
+ return bind;
+ v = cdr_(v);
+ }
+ return NIL;
+}
+
+#define eval(e) ((tag(e)<0x2) ? (e) : eval_sexpr((e),penv,0,envend))
+#define topeval(e, env) ((tag(e)<0x2) ? (e) : eval_sexpr((e),env,1,SP))
+#define tail_eval(xpr) do { SP = saveSP; \
+ if (tag(xpr)<0x2) { return (xpr); } \
+ else { e=(xpr); goto eval_top; } } while (0)
+
+static value_t do_trycatch(value_t expr, value_t *penv, u_int32_t envend)
+{
+ value_t v;
+
+ FL_TRY {
+ v = eval(expr);
+ }
+ FL_CATCH {
+ v = cdr_(Stack[SP-1]);
+ if (!iscons(v)) {
+ v = NIL; // 1-argument form
+ }
+ else {
+ Stack[SP-1] = car_(v);
+ value_t quoted = list2(QUOTE, lasterror);
+ expr = list2(Stack[SP-1], quoted);
+ v = eval(expr);
+ }
+ }
+ return v;
+}
+
+/* stack setup on entry:
+ n n+1 ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+
+ | SYM | VAL | SYM | VAL | CLO | | | |
+ +-----+-----+-----+-----+-----+-----+-----+-----+
+ ^ ^ ^
+ | | |
+ penv envend SP (who knows where)
+
+ sym is an argument name and val is its binding. CLO is a closed-up
+ environment vector (which can be empty, i.e. NIL).
+ CLO is always there, but there might be zero SYM/VAL pairs.
+
+ if tail==1, you are allowed (indeed encouraged) to overwrite this
+ environment, otherwise you have to put any new environment on the top
+ of the stack.
+*/
+static value_t eval_sexpr(value_t e, value_t *penv, int tail, u_int32_t envend)
+{
+ value_t f, v, asym, *pv, *argsyms, *body, *lenv, *argenv;
+ cons_t *c;
+ symbol_t *sym;
+ u_int32_t saveSP;
+ int i, nargs, noeval=0;
+ fixnum_t s;
+ cvalue_t *cv;
+ int64_t accum;
+
+ eval_top:
+ if (issymbol(e)) {
+ sym = (symbol_t*)ptr(e);
+ if (sym->syntax == TAG_CONST) return sym->binding;
+ while (1) {
+ if (tag(*penv) == TAG_BUILTIN)
+ penv = &vector_elt(*penv, 0);
+ if (*penv == e)
+ return penv[1];
+ else if (*penv == NIL)
+ break;
+ penv+=2;
+ }
+ if ((v = sym->binding) == UNBOUND) // 3. global env
+ raise(list2(UnboundError, e));
+ return v;
+ }
+ if ((unsigned)(char*)&nargs < (unsigned)stack_bottom || SP>=(N_STACK-100))
+ lerror(MemoryError, "eval: stack overflow");
+ saveSP = SP;
+ v = car_(e);
+ PUSH(cdr_(e));
+ if (tag(v)<0x2) f=v;
+ else if (issymbol(v) && (f=((symbol_t*)ptr(v))->syntax)) {
+ // handle special syntax forms
+ if (isspecial(f))
+ goto apply_special;
+ else if (f == TAG_CONST)
+ f = ((symbol_t*)ptr(v))->binding;
+ else
+ noeval = 2;
+ }
+ else f = eval_sexpr(v, penv, 0, envend);
+ v = Stack[saveSP];
+ if (tag(f) == TAG_BUILTIN) {
+ // handle builtin function
+ // evaluate argument list, placing arguments on stack
+ while (iscons(v)) {
+ v = eval(car_(v));
+ PUSH(v);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ apply_builtin:
+ nargs = SP - saveSP - 1;
+ apply_special:
+ switch (uintval(f)) {
+ // special forms
+ case F_QUOTE:
+ if (!iscons(Stack[saveSP]))
+ lerror(ArgError, "quote: expected argument");
+ v = car_(Stack[saveSP]);
+ break;
+ case F_LAMBDA:
+ // build a closure (lambda args body . env)
+ if (issymbol(*penv) && *penv != NIL) {
+ // save temporary environment to the heap
+ // find out how much space we need
+ nargs = ((int)(&Stack[envend] - penv - 1));
+ lenv = penv;
+ pv = alloc_words(nargs + 2);
+ PUSH(tagptr(pv, TAG_BUILTIN));
+ pv[0] = (nargs+1)<<2;
+ pv++;
+ while (nargs--)
+ *pv++ = *penv++;
+ // final element points to existing cloenv
+ *pv = Stack[envend-1];
+ // environment representation changed; install
+ // the new representation so everybody can see it
+ *lenv = Stack[SP-1];
+ }
+ else {
+ PUSH(*penv); // env has already been captured; share
+ }
+ c = (cons_t*)ptr(v=cons_reserve(3));
+ c->car = LAMBDA;
+ c->cdr = tagptr(c+1, TAG_CONS); c++;
+ c->car = car(Stack[saveSP]); //argsyms
+ c->cdr = tagptr(c+1, TAG_CONS); c++;
+ c->car = car(cdr_(Stack[saveSP])); //body
+ c->cdr = Stack[SP-1]; //env
+ break;
+ case F_LABEL:
+ // the syntax of label is (label name (lambda args body))
+ // nothing else is guaranteed to work
+ PUSH(car(Stack[saveSP]));
+ PUSH(car(cdr_(Stack[saveSP])));
+ body = &Stack[SP-1];
+ *body = eval(*body); // evaluate lambda
+ pv = alloc_words(4);
+ pv[0] = 3<<2; // vector size 3
+ // add [name fn] to front of function's environment
+ pv[1] = Stack[SP-2]; // name
+ pv[2] = v = *body; // lambda
+ f = cdr(cdr(v));
+ pv[3] = cdr(f);
+ cdr_(f) = tagptr(pv, TAG_BUILTIN);
+ break;
+ case F_IF:
+ v = car(Stack[saveSP]);
+ if (eval(v) != NIL)
+ v = car(cdr_(Stack[saveSP]));
+ else
+ v = car(cdr(cdr_(Stack[saveSP])));
+ tail_eval(v);
+ break;
+ case F_COND:
+ pv = &Stack[saveSP]; v = NIL;
+ while (iscons(*pv)) {
+ c = tocons(car_(*pv), "cond");
+ v = eval(c->car);
+ if (v != NIL) {
+ *pv = cdr_(car_(*pv));
+ // evaluate body forms
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ v = eval(car_(*pv));
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv));
+ }
+ break;
+ }
+ *pv = cdr_(*pv);
+ }
+ break;
+ case F_AND:
+ pv = &Stack[saveSP]; v = T;
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ if ((v=eval(car_(*pv))) == NIL) {
+ SP = saveSP; return NIL;
+ }
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv));
+ }
+ break;
+ case F_OR:
+ pv = &Stack[saveSP]; v = NIL;
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ if ((v=eval(car_(*pv))) != NIL) {
+ SP = saveSP; return v;
+ }
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv));
+ }
+ break;
+ case F_WHILE:
+ PUSH(cdr(Stack[saveSP]));
+ body = &Stack[SP-1];
+ PUSH(*body);
+ Stack[saveSP] = car_(Stack[saveSP]);
+ value_t *cond = &Stack[saveSP];
+ PUSH(NIL);
+ pv = &Stack[SP-1];
+ while (eval(*cond) != NIL) {
+ *body = Stack[SP-2];
+ while (iscons(*body)) {
+ *pv = eval(car_(*body));
+ *body = cdr_(*body);
+ }
+ }
+ v = *pv;
+ break;
+ case F_PROGN:
+ // return last arg
+ pv = &Stack[saveSP]; v = NIL;
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ v = eval(car_(*pv));
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv));
+ }
+ break;
+ case F_TRYCATCH:
+ v = do_trycatch(car(Stack[saveSP]), penv, envend);
+ break;
+
+ // ordinary functions
+ case F_SET:
+ argcount("set", nargs, 2);
+ e = Stack[SP-2];
+ while (1) {
+ if (tag(*penv) == TAG_BUILTIN)
+ penv = &vector_elt(*penv, 0);
+ if (*penv == e) {
+ penv[1] = Stack[SP-1];
+ SP=saveSP; return penv[1];
+ }
+ else if (*penv == NIL)
+ break;
+ penv+=2;
+ }
+ sym = tosymbol(e, "set");
+ v = Stack[SP-1];
+ if (sym->syntax != TAG_CONST)
+ sym->binding = v;
+ break;
+ case F_BOUNDP:
+ argcount("boundp", nargs, 1);
+ sym = tosymbol(Stack[SP-1], "boundp");
+ v = (sym->binding == UNBOUND) ? NIL : T;
+ break;
+ case F_EQ:
+ argcount("eq", nargs, 2);
+ v = ((Stack[SP-2] == Stack[SP-1]) ? T : NIL);
+ break;
+ case F_CONS:
+ argcount("cons", nargs, 2);
+ if (curheap > lim)
+ gc(0);
+ c = (cons_t*)curheap;
+ curheap += sizeof(cons_t);
+ c->car = Stack[SP-2];
+ c->cdr = Stack[SP-1];
+ v = tagptr(c, TAG_CONS);
+ break;
+ case F_CAR:
+ argcount("car", nargs, 1);
+ v = car(Stack[SP-1]);
+ break;
+ case F_CDR:
+ argcount("cdr", nargs, 1);
+ v = cdr(Stack[SP-1]);
+ break;
+ case F_RPLACA:
+ argcount("rplaca", nargs, 2);
+ car(v=Stack[SP-2]) = Stack[SP-1];
+ break;
+ case F_RPLACD:
+ argcount("rplacd", nargs, 2);
+ cdr(v=Stack[SP-2]) = Stack[SP-1];
+ break;
+ case F_VECTOR:
+ v = alloc_vector(nargs, 0);
+ memcpy(&vector_elt(v,0), &Stack[saveSP+1], nargs*sizeof(value_t));
+ break;
+ case F_LENGTH:
+ argcount("length", nargs, 1);
+ if (isvectorish(Stack[SP-1])) {
+ if (discriminateAsVector(Stack[SP-1])) {
+ v = fixnum(vector_size(Stack[SP-1]));
+ break;
+ }
+ else {
+ cv = (cvalue_t*)ptr(Stack[SP-1]);
+ v = cv_type(cv);
+ if (iscons(v) && car_(v) == arraysym) {
+ v = size_wrap(cvalue_arraylen(Stack[SP-1]));
+ break;
+ }
+ else if (v == charsym) {
+ v = fixnum(1);
+ break;
+ }
+ else if (v == wcharsym) {
+ v = fixnum(u8_charlen(*(wchar_t*)cv_data(cv)));
+ break;
+ }
+ }
+ }
+ else if (Stack[SP-1] == NIL) {
+ v = fixnum(0); break;
+ }
+ else if (iscons(Stack[SP-1])) {
+ v = fixnum(llength(Stack[SP-1])); break;
+ }
+ type_error("length", "sequence", Stack[SP-1]);
+ break;
+ case F_AREF:
+ argcount("aref", nargs, 2);
+ v = Stack[SP-2];
+ i = tofixnum(Stack[SP-1], "aref");
+ if (isvector(v)) {
+ if ((unsigned)i >= vector_size(v))
+ bounds_error("aref", v, Stack[SP-1]);
+ v = vector_elt(v, i);
+ }
+ else {
+ // TODO other sequence types?
+ type_error("aref", "sequence", v);
+ }
+ break;
+ case F_ASET:
+ argcount("aset", nargs, 3);
+ e = Stack[SP-3];
+ i = tofixnum(Stack[SP-2], "aset");
+ if (isvector(e)) {
+ if ((unsigned)i >= vector_size(e))
+ bounds_error("aref", v, Stack[SP-1]);
+ vector_elt(e, i) = (v=Stack[SP-1]);
+ }
+ else {
+ type_error("aset", "sequence", e);
+ }
+ break;
+ case F_ATOM:
+ argcount("atom", nargs, 1);
+ v = ((!iscons(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_CONSP:
+ argcount("consp", nargs, 1);
+ v = (iscons(Stack[SP-1]) ? T : NIL);
+ break;
+ case F_SYMBOLP:
+ argcount("symbolp", nargs, 1);
+ v = ((issymbol(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_NUMBERP:
+ argcount("numberp", nargs, 1);
+ v = ((isfixnum(Stack[SP-1]) ||
+ (iscvalue(Stack[SP-1]) &&
+ valid_numtype(cv_numtype((cvalue_t*)ptr(Stack[SP-1]))) ))
+ ? T : NIL);
+ break;
+ case F_FIXNUMP:
+ argcount("fixnump", nargs, 1);
+ v = ((isfixnum(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_BUILTINP:
+ argcount("builtinp", nargs, 1);
+ v = (isbuiltin(Stack[SP-1]) ||
+ (iscvalue(Stack[SP-1]) &&
+ ((cvalue_t*)ptr(Stack[SP-1]))->flags.islispfunction))? T:NIL;
+ break;
+ case F_VECTORP:
+ argcount("vectorp", nargs, 1);
+ v = ((isvector(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_NOT:
+ argcount("not", nargs, 1);
+ v = ((Stack[SP-1] == NIL) ? T : NIL);
+ break;
+ case F_ADD:
+ s = 0;
+ for (i=saveSP+1; i < (int)SP; i++) {
+ if (isfixnum(Stack[i])) {
+ s += numval(Stack[i]);
+ if (!fits_fixnum(s)) {
+ i++;
+ goto add_ovf;
+ }
+ }
+ else {
+ add_ovf:
+ v = fl_add_any(&Stack[i], SP-i, s);
+ SP = saveSP;
+ return v;
+ }
+ }
+ v = fixnum(s);
+ break;
+ case F_SUB:
+ if (nargs < 1) lerror(ArgError, "-: too few arguments");
+ i = saveSP+1;
+ if (nargs == 1) {
+ if (isfixnum(Stack[i]))
+ v = fixnum(-numval(Stack[i]));
+ else
+ v = fl_neg(Stack[i]);
+ break;
+ }
+ if (nargs == 2) {
+ if (bothfixnums(Stack[i], Stack[i+1])) {
+ s = numval(Stack[i]) - numval(Stack[i+1]);
+ if (fits_fixnum(s)) {
+ v = fixnum(s);
+ break;
+ }
+ Stack[i+1] = fixnum(-numval(Stack[i+1]));
+ }
+ else {
+ Stack[i+1] = fl_neg(Stack[i+1]);
+ }
+ }
+ else {
+ Stack[i+1] = fl_neg(fl_add_any(&Stack[i+1], nargs-1, 0));
+ }
+ v = fl_add_any(&Stack[i], 2, 0);
+ break;
+ case F_MUL:
+ accum = 1;
+ for (i=saveSP+1; i < (int)SP; i++) {
+ if (isfixnum(Stack[i])) {
+ accum *= numval(Stack[i]);
+ }
+ else {
+ v = fl_mul_any(&Stack[i], SP-i, accum);
+ SP = saveSP;
+ return v;
+ }
+ }
+ if (fits_fixnum(accum))
+ v = fixnum(accum);
+ else
+ v = return_from_int64(accum);
+ break;
+ case F_DIV:
+ if (nargs < 1) lerror(ArgError, "/: too few arguments");
+ i = saveSP+1;
+ if (nargs == 1) {
+ v = fl_div2(fixnum(1), Stack[i]);
+ }
+ else {
+ if (nargs > 2)
+ Stack[i+1] = fl_mul_any(&Stack[i+1], nargs-1, 1);
+ v = fl_div2(Stack[i], Stack[i+1]);
+ }
+ break;
+ case F_BNOT:
+ argcount("~", nargs, 1);
+ if (isfixnum(Stack[SP-1]))
+ v = fixnum(~numval(Stack[SP-1]));
+ else
+ v = fl_bitwise_not(Stack[SP-1]);
+ break;
+ case F_BAND:
+ argcount("&", nargs, 2);
+ if (bothfixnums(Stack[SP-1], Stack[SP-2]))
+ v = Stack[SP-1] & Stack[SP-2];
+ else
+ v = fl_bitwise_op(Stack[SP-2], Stack[SP-1], 0, "&");
+ break;
+ case F_BOR:
+ argcount("!", nargs, 2);
+ if (bothfixnums(Stack[SP-1], Stack[SP-2]))
+ v = Stack[SP-1] | Stack[SP-2];
+ else
+ v = fl_bitwise_op(Stack[SP-2], Stack[SP-1], 1, "!");
+ break;
+ case F_BXOR:
+ argcount("$", nargs, 2);
+ if (bothfixnums(Stack[SP-1], Stack[SP-2]))
+ v = fixnum(numval(Stack[SP-1]) ^ numval(Stack[SP-2]));
+ else
+ v = fl_bitwise_op(Stack[SP-2], Stack[SP-1], 2, "$");
+ break;
+ case F_COMPARE:
+ argcount("compare", nargs, 2);
+ v = compare(Stack[SP-2], Stack[SP-1]);
+ break;
+ case F_LT:
+ argcount("<", nargs, 2);
+ if (bothfixnums(Stack[SP-2], Stack[SP-1])) {
+ v = (numval(Stack[SP-2]) < numval(Stack[SP-1])) ? T : NIL;
+ }
+ else {
+ v = (numval(compare(Stack[SP-2], Stack[SP-1])) < 0) ? T : NIL;
+ }
+ break;
+ case F_EQUAL:
+ argcount("equal", nargs, 2);
+ if (!((Stack[SP-2] | Stack[SP-1])&0x1)) {
+ v = (Stack[SP-2] == Stack[SP-1]) ? T : NIL;
+ }
+ else {
+ v = (compare(Stack[SP-2], Stack[SP-1])==0) ? T : NIL;
+ }
+ break;
+ case F_EVAL:
+ argcount("eval", nargs, 1);
+ v = Stack[SP-1];
+ if (tag(v)<0x2) { SP=saveSP; return v; }
+ if (tail) {
+ *penv = NIL;
+ envend = SP = (u_int32_t)(penv-&Stack[0]) + 1;
+ e=v; goto eval_top;
+ }
+ else {
+ PUSH(NIL);
+ v = eval_sexpr(v, &Stack[SP-1], 1, SP);
+ }
+ break;
+ case F_RAISE:
+ argcount("raise", nargs, 1);
+ raise(Stack[SP-1]);
+ break;
+ case F_PROG1:
+ // return first arg
+ if (nargs < 1) lerror(ArgError, "prog1: too few arguments");
+ v = Stack[saveSP+1];
+ break;
+ case F_ASSOC:
+ argcount("assoc", nargs, 2);
+ v = assoc(Stack[SP-2], Stack[SP-1]);
+ break;
+ case F_APPLY:
+ argcount("apply", nargs, 2);
+ v = Stack[saveSP] = Stack[SP-1]; // second arg is new arglist
+ f = Stack[SP-2]; // first arg is new function
+ POPN(2); // pop apply's args
+ if (tag(f) == TAG_BUILTIN) {
+ assert(!isspecial(f));
+ // unpack arglist onto the stack
+ while (iscons(v)) {
+ PUSH(car_(v));
+ v = cdr_(v);
+ }
+ goto apply_builtin;
+ }
+ noeval = 1;
+ goto apply_lambda;
+ default:
+ cv = (cvalue_t*)ptr(f);
+ if (!discriminateAsVector(f) && cv->flags.islispfunction) {
+ v = ((guestfunc_t)cv->data)(&Stack[saveSP+1], nargs);
+ }
+ else {
+ goto apply_lambda; // trigger type error
+ }
+ }
+ SP = saveSP;
+ return v;
+ }
+ apply_lambda:
+ if (iscons(f)) {
+ // apply lambda or macro expression
+ PUSH(cdr(cdr_(f)));
+ PUSH(car_(cdr_(f)));
+ argsyms = &Stack[SP-1];
+ argenv = &Stack[SP]; // argument environment starts now
+ // build a calling environment for the lambda
+ // the environment is the argument binds on top of the captured
+ // environment
+ while (iscons(v)) {
+ // bind args
+ if (!iscons(*argsyms)) {
+ if (*argsyms == NIL)
+ lerror(ArgError, "apply: too many arguments");
+ break;
+ }
+ asym = car_(*argsyms);
+ if (asym==NIL || !issymbol(asym))
+ lerror(ArgError, "apply: invalid formal argument");
+ v = car_(v);
+ if (!noeval) {
+ v = eval(v);
+ }
+ PUSH(asym);
+ PUSH(v);
+ *argsyms = cdr_(*argsyms);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ if (*argsyms != NIL) {
+ if (issymbol(*argsyms)) {
+ PUSH(*argsyms);
+ PUSH(Stack[saveSP]);
+ if (!noeval) {
+ // this version uses collective allocation. about 7-10%
+ // faster for lists with > 2 elements, but uses more
+ // stack space
+ i = SP;
+ while (iscons(Stack[saveSP])) {
+ PUSH(eval(car_(Stack[saveSP])));
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ nargs = SP-i;
+ if (nargs) {
+ Stack[i-1] = cons_reserve(nargs);
+ c = (cons_t*)ptr(Stack[i-1]);
+ for(; i < (int)SP; i++) {
+ c->car = Stack[i];
+ c->cdr = tagptr(c+1, TAG_CONS);
+ c++;
+ }
+ (c-1)->cdr = Stack[saveSP];
+ POPN(nargs);
+ }
+ }
+ }
+ else if (iscons(*argsyms)) {
+ lerror(ArgError, "apply: too few arguments");
+ }
+ }
+ PUSH(cdr(Stack[saveSP+1])); // add cloenv to new environment
+ e = car_(Stack[saveSP+1]);
+ // macro: evaluate expansion in the calling environment
+ if (noeval == 2) {
+ if (tag(e)<0x2) ;
+ else e = eval_sexpr(e, argenv, 1, SP);
+ SP = saveSP;
+ if (tag(e)<0x2) return(e);
+ noeval = 0;
+ goto eval_top;
+ }
+ else {
+ if (tag(e)<0x2) { SP=saveSP; return(e); }
+ if (tail) {
+ noeval = 0;
+ // ok to overwrite environment
+ nargs = (int)(&Stack[SP] - argenv);
+ for(i=0; i < nargs; i++)
+ penv[i] = argenv[i];
+ envend = SP = (u_int32_t)((penv+nargs) - &Stack[0]);
+ goto eval_top;
+ }
+ else {
+ v = eval_sexpr(e, argenv, 1, SP);
+ SP = saveSP;
+ return v;
+ }
+ }
+ // not reached
+ }
+ type_error("apply", "function", f);
+ return NIL;
+}
+
+// initialization -------------------------------------------------------------
+
+extern void builtins_init();
+
+void lisp_init(void)
+{
+ int i;
+
+ llt_init();
+
+ fromspace = malloc(heapsize);
+ tospace = malloc(heapsize);
+ curheap = fromspace;
+ lim = curheap+heapsize-sizeof(cons_t);
+ consflags = bitvector_new(heapsize/sizeof(cons_t), 1);
+ ptrhash_new(&printconses, 32);
+
+ NIL = symbol("nil"); setc(NIL, NIL);
+ T = symbol("T"); setc(T, T);
+ LAMBDA = symbol("lambda");
+ LABEL = symbol("label");
+ QUOTE = symbol("quote");
+ VECTOR = symbol("vector");
+ TRYCATCH = symbol("trycatch");
+ BACKQUOTE = symbol("backquote");
+ COMMA = symbol("*comma*");
+ COMMAAT = symbol("*comma-at*");
+ COMMADOT = symbol("*comma-dot*");
+ IOError = symbol("io-error");
+ ParseError = symbol("parse-error");
+ TypeError = symbol("type-error");
+ ArgError = symbol("arg-error");
+ UnboundError = symbol("unbound-error");
+ MemoryError = symbol("memory-error");
+ BoundsError = symbol("bounds-error");
+ DivideError = symbol("divide-error");
+ Error = symbol("error");
+ conssym = symbol("cons");
+ symbolsym = symbol("symbol");
+ fixnumsym = symbol("fixnum");
+ vectorsym = symbol("vector");
+ builtinsym = symbol("builtin");
+ lasterror = NIL;
+ lerrorbuf[0] = '\0';
+ i = 0;
+ while (isspecial(builtin(i))) {
+ ((symbol_t*)ptr(symbol(builtin_names[i])))->syntax = builtin(i);
+ i++;
+ }
+ for (; i < N_BUILTINS; i++) {
+ setc(symbol(builtin_names[i]), builtin(i));
+ }
+
+#ifdef LINUX
+ set(symbol("os.name"), symbol("linux"));
+#elif defined(WIN32) || defined(WIN64)
+ set(symbol("os.name"), symbol("win32"));
+#elif defined(MACOSX)
+ set(symbol("os.name"), symbol("macos"));
+#else
+ set(symbol("os.name"), symbol("unknown"));
+#endif
+
+ cvalues_init();
+ set(symbol("gensym"), guestfunc(gensym));
+ builtins_init();
+}
+
+// repl -----------------------------------------------------------------------
+
+value_t toplevel_eval(value_t expr)
+{
+ value_t v;
+ u_int32_t saveSP = SP;
+ PUSH(NIL);
+ v = topeval(expr, &Stack[SP-1]);
+ SP = saveSP;
+ return v;
+}
+
+static void print_toplevel_exception()
+{
+ if (iscons(lasterror) && car_(lasterror) == TypeError &&
+ llength(lasterror) == 4) {
+ fprintf(stderr, "type-error: ");
+ print(stderr, car_(cdr_(lasterror)), 1);
+ fprintf(stderr, ": expected ");
+ print(stderr, car_(cdr_(cdr_(lasterror))), 1);
+ fprintf(stderr, ", got ");
+ print(stderr, car_(cdr_(cdr_(cdr_(lasterror)))), 0);
+ }
+ else if (iscons(lasterror) && car_(lasterror) == UnboundError &&
+ iscons(cdr_(lasterror))) {
+ fprintf(stderr, "unbound-error: eval: variable %s has no value",
+ (symbol_name(car_(cdr_(lasterror)))));
+ }
+ else if (iscons(lasterror) && car_(lasterror) == Error) {
+ value_t v = cdr_(lasterror);
+ fprintf(stderr, "error: ");
+ while (iscons(v)) {
+ print(stderr, car_(v), 1);
+ v = cdr_(v);
+ }
+ }
+ else {
+ if (lasterror != NIL) {
+ if (!lerrorbuf[0])
+ fprintf(stderr, "*** Unhandled exception: ");
+ print(stderr, lasterror, 0);
+ if (lerrorbuf[0])
+ fprintf(stderr, ": ");
+ }
+ }
+
+ if (lerrorbuf[0])
+ fprintf(stderr, "%s", lerrorbuf);
+}
+
+value_t load_file(char *fname)
+{
+ value_t volatile e, v=NIL;
+ FILE * volatile f = fopen(fname, "r");
+ if (f == NULL) lerror(IOError, "file \"%s\" not found", fname);
+ FL_TRY {
+ while (1) {
+ e = read_sexpr(f);
+ //print(stdout,e,0); printf("\n");
+ if (feof(f)) break;
+ v = toplevel_eval(e);
+ }
+ }
+ FL_CATCH {
+ fclose(f);
+ size_t msglen = strlen(lerrorbuf);
+ snprintf(&lerrorbuf[msglen], sizeof(lerrorbuf)-msglen,
+ "\nin file \"%s\"", fname);
+ lerrorbuf[sizeof(lerrorbuf)-1] = '\0';
+ raise(lasterror);
+ }
+ fclose(f);
+ return v;
+}
+
+static value_t argv_list(int argc, char *argv[])
+{
+ int i;
+ PUSH(NIL);
+ if (argc > 1) { argc--; argv++; }
+ for(i=argc-1; i >= 0; i--)
+ Stack[SP-1] = fl_cons(cvalue_pinned_cstring(argv[i]), Stack[SP-1]);
+ return POP();
+}
+
+int locale_is_utf8;
+
+int main(int argc, char *argv[])
+{
+ value_t v;
+
+ locale_is_utf8 = u8_is_locale_utf8(setlocale(LC_ALL, ""));
+
+ stack_bottom = ((char*)&v) - PROCESS_STACK_SIZE;
+ lisp_init();
+ set(symbol("argv"), argv_list(argc, argv));
+ FL_TRY {
+ // install toplevel exception handler
+ }
+ FL_CATCH {
+ print_toplevel_exception();
+
+ lerrorbuf[0] = '\0';
+ lasterror = NIL;
+ fprintf(stderr, "\n\n");
+ goto repl;
+ }
+ load_file("system.lsp");
+ if (argc > 1) { load_file(argv[1]); return 0; }
+ printf("; _ \n");
+ printf("; |_ _ _ |_ _ | . _ _\n");
+ printf("; | (-||||_(_)|__|_)|_)\n");
+ printf(";-------------------|----------------------------------------------------------\n\n");
+ repl:
+ while (1) {
+ printf("> ");
+ v = read_sexpr(stdin);
+ if (feof(stdin)) break;
+ print(stdout, v=toplevel_eval(v), 0);
+ set(symbol("that"), v);
+ printf("\n\n");
+ }
+ printf("\n");
+ return 0;
+}
--- /dev/null
+++ b/femtolisp/flisp.h
@@ -1,0 +1,235 @@
+#ifndef _FLISP_H_
+#define _FLISP_H_
+
+typedef uptrint_t value_t;
+typedef int_t fixnum_t;
+#ifdef BITS64
+#define T_FIXNUM T_INT64
+#else
+#define T_FIXNUM T_INT32
+#endif
+
+typedef struct {
+ value_t car;
+ value_t cdr;
+} cons_t;
+
+typedef struct _symbol_t {
+ value_t binding; // global value binding
+ value_t syntax; // syntax environment entry
+ void *dlcache; // dlsym address
+ // below fields are private
+ struct _symbol_t *left;
+ struct _symbol_t *right;
+ union {
+ char name[1];
+ void *_pad; // ensure field aligned to pointer size
+ };
+} symbol_t;
+
+#define TAG_NUM 0x0
+#define TAG_BUILTIN 0x1
+#define TAG_SYM 0x2
+#define TAG_CONS 0x3
+#define UNBOUND ((value_t)TAG_SYM) // an invalid symbol pointer
+#define TAG_CONST ((value_t)-2) // in sym->syntax for constants
+#define tag(x) ((x)&0x3)
+#define ptr(x) ((void*)((x)&(~(value_t)0x3)))
+#define tagptr(p,t) (((value_t)(p)) | (t))
+#define fixnum(x) ((value_t)((x)<<2))
+#define numval(x) (((fixnum_t)(x))>>2)
+#define fits_fixnum(x) (((x)>>29) == 0 || (~((x)>>29)) == 0)
+#define fits_bits(x,b) (((x)>>(b-1)) == 0 || (~((x)>>(b-1))) == 0)
+#define uintval(x) (((unsigned int)(x))>>2)
+#define builtin(n) tagptr((((int)n)<<2), TAG_BUILTIN)
+#define iscons(x) (tag(x) == TAG_CONS)
+#define issymbol(x) (tag(x) == TAG_SYM)
+#define isfixnum(x) (tag(x) == TAG_NUM)
+#define bothfixnums(x,y) (tag((x)|(y)) == TAG_NUM)
+#define isbuiltin(x) ((tag(x) == TAG_BUILTIN) && uintval(x) < N_BUILTINS)
+#define isvectorish(x) ((tag(x) == TAG_BUILTIN) && uintval(x) > N_BUILTINS)
+#define isvector(x) (isvectorish(x) && !(((value_t*)ptr(x))[0] & 0x2))
+#define iscvalue(x) (isvectorish(x) && (((value_t*)ptr(x))[0] & 0x2))
+// distinguish a vector from a cvalue
+#define discriminateAsVector(x) (!(((value_t*)ptr(x))[0] & 0x2))
+#define vector_size(v) (((size_t*)ptr(v))[0]>>2)
+#define vector_setsize(v,n) (((size_t*)ptr(v))[0] = ((n)<<2))
+#define vector_elt(v,i) (((value_t*)ptr(v))[1+(i)])
+#define vector_grow_amt(x) ((x)<8 ? 4 : 6*((x)>>3))
+// functions ending in _ are unsafe, faster versions
+#define car_(v) (((cons_t*)ptr(v))->car)
+#define cdr_(v) (((cons_t*)ptr(v))->cdr)
+#define car(v) (tocons((v),"car")->car)
+#define cdr(v) (tocons((v),"cdr")->cdr)
+#define set(s, v) (((symbol_t*)ptr(s))->binding = (v))
+#define setc(s, v) do { ((symbol_t*)ptr(s))->syntax = TAG_CONST; \
+ ((symbol_t*)ptr(s))->binding = (v); } while (0)
+#define isconstant(s) (((symbol_t*)ptr(s))->syntax == TAG_CONST)
+#define symbol_value(s) (((symbol_t*)ptr(s))->binding)
+#define ismanaged(v) ((((unsigned char*)ptr(v)) >= fromspace) && \
+ (((unsigned char*)ptr(v)) < fromspace+heapsize))
+
+extern value_t Stack[];
+extern u_int32_t SP;
+#define PUSH(v) (Stack[SP++] = (v))
+#define POP() (Stack[--SP])
+#define POPN(n) (SP-=(n))
+
+enum {
+ // special forms
+ F_QUOTE=0, F_COND, F_IF, F_AND, F_OR, F_WHILE, F_LAMBDA, F_LABEL,
+ F_TRYCATCH, F_PROGN,
+ // functions
+ F_EQ, F_ATOM, F_NOT, F_SYMBOLP, F_NUMBERP, F_BOUNDP, F_CONSP,
+ F_BUILTINP, F_VECTORP, F_FIXNUMP, F_EQUAL,
+ F_CONS, F_CAR, F_CDR, F_RPLACA, F_RPLACD,
+ F_EVAL, F_APPLY, F_SET, F_PROG1, F_RAISE,
+ F_ADD, F_SUB, F_MUL, F_DIV, F_LT, F_BNOT, F_BAND, F_BOR, F_BXOR,
+ F_VECTOR, F_AREF, F_ASET, F_LENGTH, F_ASSOC, F_COMPARE,
+ N_BUILTINS
+};
+#define isspecial(v) (uintval(v) <= (unsigned int)F_PROGN)
+
+extern value_t NIL, T;
+
+/* read, eval, print main entry points */
+value_t read_sexpr(FILE *f);
+void print(FILE *f, value_t v, int princ);
+value_t toplevel_eval(value_t expr);
+value_t apply(value_t f, value_t l);
+value_t load_file(char *fname);
+
+/* object model manipulation */
+value_t fl_cons(value_t a, value_t b);
+value_t list2(value_t a, value_t b);
+value_t listn(size_t n, ...);
+value_t symbol(char *str);
+value_t fl_gensym();
+char *symbol_name(value_t v);
+value_t alloc_vector(size_t n, int init);
+size_t llength(value_t v);
+value_t list_nth(value_t l, size_t n);
+value_t compare(value_t a, value_t b);
+
+/* safe casts */
+cons_t *tocons(value_t v, char *fname);
+symbol_t *tosymbol(value_t v, char *fname);
+fixnum_t tofixnum(value_t v, char *fname);
+char *tostring(value_t v, char *fname);
+
+/* error handling */
+void lerror(value_t e, char *format, ...) __attribute__ ((__noreturn__));
+void raise(value_t e) __attribute__ ((__noreturn__));
+void type_error(char *fname, char *expected, value_t got) __attribute__ ((__noreturn__));
+void bounds_error(char *fname, value_t arr, value_t ind) __attribute__ ((__noreturn__));
+extern value_t ArgError, IOError;
+static inline void argcount(char *fname, int nargs, int c)
+{
+ if (nargs != c)
+ lerror(ArgError,"%s: too %s arguments", fname, nargs<c ? "few":"many");
+}
+
+/* c interface */
+#define INL_SIZE_NBITS 16
+typedef struct {
+ unsigned two:2;
+ unsigned moved:1;
+ unsigned numtype:4;
+ unsigned inllen:INL_SIZE_NBITS;
+ unsigned cstring:1;
+ unsigned unused:4;
+ unsigned prim:1;
+ unsigned inlined:1;
+ unsigned islispfunction:1;
+ unsigned autorelease:1;
+#ifdef BITS64
+ unsigned pad:32;
+#endif
+} cvflags_t;
+
+// initial flags have two==0x2 (type tag) and numtype==0xf
+#ifdef BITFIELD_BIG_ENDIAN
+# ifdef BITS64
+# define INITIAL_FLAGS 0x9e00000000000000UL
+# else
+# define INITIAL_FLAGS 0x9e000000
+# endif
+#else
+# ifdef BITS64
+# define INITIAL_FLAGS 0x000000000000007aUL
+# else
+# define INITIAL_FLAGS 0x0000007a
+# endif
+#endif
+
+typedef struct {
+ union {
+ cvflags_t flags;
+ unsigned long flagbits;
+ };
+ value_t type;
+ value_t deps;
+ // fields below are absent in inline-allocated values
+ void *data;
+ size_t len; // length of *data in bytes
+ //cvtable_t *vtable;
+} cvalue_t;
+
+typedef struct {
+ union {
+ cvflags_t flags;
+ unsigned long flagbits;
+ };
+ value_t type;
+ void *data;
+} cprim_t;
+
+#define cv_len(c) ((c)->flags.inlined ? (c)->flags.inllen : (c)->len)
+#define cv_type(c) ((c)->type)
+#define cv_numtype(c) ((c)->flags.numtype)
+
+#define valid_numtype(v) ((v) < N_NUMTYPES)
+
+/* C type names corresponding to cvalues type names */
+typedef unsigned long ulong;
+typedef unsigned int uint;
+typedef unsigned char uchar;
+typedef char char_t;
+typedef long long_t;
+typedef unsigned long ulong_t;
+typedef double double_t;
+typedef float float_t;
+
+typedef value_t (*guestfunc_t)(value_t*, u_int32_t);
+
+extern value_t int8sym, uint8sym, int16sym, uint16sym, int32sym, uint32sym;
+extern value_t int64sym, uint64sym, shortsym, ushortsym;
+extern value_t intsym, uintsym, longsym, ulongsym, charsym, ucharsym, wcharsym;
+extern value_t structsym, arraysym, enumsym, cfunctionsym, voidsym, pointersym;
+extern value_t stringtypesym, wcstringtypesym, emptystringsym;
+extern value_t unionsym, floatsym, doublesym, lispvaluesym;
+
+value_t cvalue(value_t type, size_t sz);
+size_t ctype_sizeof(value_t type, int *palign);
+void *cvalue_data(value_t v);
+void *cv_data(cvalue_t *cv);
+value_t cvalue_copy(value_t v);
+value_t cvalue_from_data(value_t type, void *data, size_t sz);
+value_t cvalue_from_ref(value_t type, void *ptr, size_t sz, value_t parent);
+value_t guestfunc(guestfunc_t f);
+size_t cvalue_arraylen(value_t v);
+value_t size_wrap(size_t sz);
+size_t toulong(value_t n, char *fname);
+value_t cvalue_string(size_t sz);
+value_t cvalue_pinned_cstring(char *str);
+int isstring(value_t v);
+int isnumber(value_t v);
+value_t cvalue_compare(value_t a, value_t b);
+
+value_t mk_double(double_t n);
+value_t mk_uint32(uint32_t n);
+value_t mk_uint64(uint64_t n);
+value_t return_from_uint64(uint64_t Uaccum);
+value_t return_from_int64(int64_t Saccum);
+
+#endif
--- /dev/null
+++ b/femtolisp/pisum.lsp
@@ -1,0 +1,8 @@
+(defun pisum ()
+ (dotimes (j 500)
+ ((label sumloop
+ (lambda (i sum)
+ (if (> i 10000)
+ sum
+ (sumloop (+ i 1) (+ sum (/ (* i i)))))))
+ 1.0 0.0)))
--- /dev/null
+++ b/femtolisp/print.c
@@ -1,0 +1,570 @@
+static ptrhash_t printconses;
+
+static int HPOS, VPOS;
+static void outc(char c, FILE *f)
+{
+ fputc(c, f);
+ HPOS++;
+}
+static void outs(char *s, FILE *f)
+{
+ fputs(s, f);
+ HPOS += u8_strwidth(s);
+}
+static void outindent(int n, FILE *f)
+{
+ fputc('\n', f);
+ VPOS++;
+ HPOS = n;
+ while (n >= 8) {
+ fputc('\t', f);
+ n -= 8;
+ }
+ while (n) {
+ fputc(' ', f);
+ n--;
+ }
+}
+
+static void print_traverse(value_t v)
+{
+ value_t *bp;
+ while (iscons(v)) {
+ if (ismarked(v)) {
+ bp = (value_t*)ptrhash_bp(&printconses, (void*)v);
+ if (*bp == (value_t)PH_NOTFOUND)
+ *bp = fixnum(printlabel++);
+ return;
+ }
+ mark_cons(v);
+ print_traverse(car_(v));
+ v = cdr_(v);
+ }
+ if (!ismanaged(v) || issymbol(v))
+ return;
+ if (isvectorish(v)) {
+ if (ismarked(v)) {
+ bp = (value_t*)ptrhash_bp(&printconses, (void*)v);
+ if (*bp == (value_t)PH_NOTFOUND)
+ *bp = fixnum(printlabel++);
+ return;
+ }
+ if (discriminateAsVector(v)) {
+ mark_cons(v);
+ unsigned int i;
+ for(i=0; i < vector_size(v); i++)
+ print_traverse(vector_elt(v,i));
+ }
+ else {
+ cvalue_t *cv = (cvalue_t*)ptr(v);
+ // don't consider shared references to ""
+ if (!cv->flags.cstring || cv_len(cv)!=0)
+ mark_cons(v);
+ }
+ }
+}
+
+static void print_symbol_name(FILE *f, char *name)
+{
+ int i, escape=0, charescape=0;
+
+ if ((name[0] == '\0') ||
+ (name[0] == '.' && name[1] == '\0') ||
+ (name[0] == '#') ||
+ isnumtok(name, NULL))
+ escape = 1;
+ i=0;
+ while (name[i]) {
+ if (!symchar(name[i])) {
+ escape = 1;
+ if (name[i]=='|' || name[i]=='\\') {
+ charescape = 1;
+ break;
+ }
+ }
+ i++;
+ }
+ if (escape) {
+ if (charescape) {
+ outc('|', f);
+ i=0;
+ while (name[i]) {
+ if (name[i]=='|' || name[i]=='\\')
+ outc('\\', f);
+ outc(name[i], f);
+ i++;
+ }
+ outc('|', f);
+ }
+ else {
+ outc('|', f);
+ outs(name, f);
+ outc('|', f);
+ }
+ }
+ else {
+ outs(name, f);
+ }
+}
+
+/*
+ The following implements a simple pretty-printing algorithm. This is
+ an unlimited-width approach that doesn't require an extra pass.
+ It uses some heuristics to guess whether an expression is "small",
+ and avoids wrapping symbols across lines. The result is high
+ performance and nice output for typical code. Quality is poor for
+ pathological or deeply-nested expressions, but those are difficult
+ to print anyway.
+*/
+static inline int tinyp(value_t v)
+{
+ return (issymbol(v) || isfixnum(v) || isbuiltin(v));
+}
+
+static int smallp(value_t v)
+{
+ if (tinyp(v)) return 1;
+ if (isnumber(v)) return 1;
+ if (iscons(v)) {
+ if (tinyp(car_(v)) && (tinyp(cdr_(v)) ||
+ (iscons(cdr_(v)) && tinyp(car_(cdr_(v))) &&
+ cdr_(cdr_(v))==NIL)))
+ return 1;
+ return 0;
+ }
+ if (isvector(v)) {
+ size_t s = vector_size(v);
+ return (s == 0 || (tinyp(vector_elt(v,0)) &&
+ (s == 1 || (s == 2 &&
+ tinyp(vector_elt(v,1))))));
+ }
+ return 0;
+}
+
+static int specialindent(value_t v)
+{
+ // indent these forms 2 spaces, not lined up with the first argument
+ if (v == LAMBDA || v == TRYCATCH)
+ return 2;
+ return -1;
+}
+
+static int lengthestimate(value_t v)
+{
+ // get the width of an expression if we can do so cheaply
+ if (issymbol(v))
+ return u8_strwidth(symbol_name(v));
+ return -1;
+}
+
+static int allsmallp(value_t v)
+{
+ int n = 1;
+ while (iscons(v)) {
+ if (!smallp(car_(v)))
+ return 0;
+ v = cdr_(v);
+ n++;
+ if (n > 25)
+ return n;
+ }
+ return n;
+}
+
+static int indentevery(value_t v)
+{
+ // indent before every subform of a special form, unless every
+ // subform is "small"
+ value_t c = car_(v);
+ if (c == LAMBDA)
+ return 0;
+ value_t f;
+ if (issymbol(c) && (f=((symbol_t*)ptr(c))->syntax) && isspecial(f))
+ return !allsmallp(cdr_(v));
+ return 0;
+}
+
+static int blockindent(value_t v)
+{
+ // in this case we switch to block indent mode, where the head
+ // is no longer considered special:
+ // (a b c d e
+ // f g h i j)
+ return (allsmallp(v) > 9);
+}
+
+static void print_pair(FILE *f, value_t v, int princ)
+{
+ value_t cd;
+ char *op = NULL;
+ if (iscons(cdr_(v)) && cdr_(cdr_(v)) == NIL &&
+ !ptrhash_has(&printconses, (void*)cdr_(v)) &&
+ (((car_(v) == QUOTE) && (op = "'")) ||
+ ((car_(v) == BACKQUOTE) && (op = "`")) ||
+ ((car_(v) == COMMA) && (op = ",")) ||
+ ((car_(v) == COMMAAT) && (op = ",@")) ||
+ ((car_(v) == COMMADOT) && (op = ",.")))) {
+ // special prefix syntax
+ unmark_cons(v);
+ unmark_cons(cdr_(v));
+ outs(op, f);
+ do_print(f, car_(cdr_(v)), princ);
+ return;
+ }
+ int startpos = HPOS;
+ outc('(', f);
+ int newindent=HPOS, blk=blockindent(v);
+ int lastv, n=0, si, ind=0, est, always=0, nextsmall;
+ if (!blk) always = indentevery(v);
+ value_t head = car_(v);
+ while (1) {
+ lastv = VPOS;
+ unmark_cons(v);
+ do_print(f, car_(v), princ);
+ cd = cdr_(v);
+ if (!iscons(cd) || ptrhash_has(&printconses, (void*)cd)) {
+ if (cd != NIL) {
+ outs(" . ", f);
+ do_print(f, cd, princ);
+ }
+ outc(')', f);
+ break;
+ }
+
+ if (princ || (head == LAMBDA && n == 0)) {
+ // never break line before lambda-list or in princ
+ ind = 0;
+ }
+ else {
+ est = lengthestimate(car_(cd));
+ nextsmall = smallp(car_(cd));
+ ind = (((n > 0) &&
+ ((!nextsmall && HPOS>28) || (VPOS > lastv))) ||
+
+ ((VPOS > lastv) && (!nextsmall || n==0)) ||
+
+ (HPOS > 50 && !nextsmall) ||
+
+ (HPOS > 74) ||
+
+ (est!=-1 && (HPOS+est > 78)) ||
+
+ (head == LAMBDA && !nextsmall) ||
+
+ (n > 0 && always));
+ }
+
+ if (ind) {
+ outindent(newindent, f);
+ }
+ else {
+ outc(' ', f);
+ if (n==0) {
+ // set indent level after printing head
+ si = specialindent(head);
+ if (si != -1)
+ newindent = startpos + si;
+ else if (!blk)
+ newindent = HPOS;
+ }
+ }
+ n++;
+ v = cd;
+ }
+}
+
+void cvalue_print(FILE *f, value_t v, int princ);
+
+static void do_print(FILE *f, value_t v, int princ)
+{
+ value_t label;
+ char *name;
+
+ switch (tag(v)) {
+ case TAG_NUM: HPOS+=fprintf(f, "%ld", numval(v)); break;
+ case TAG_SYM:
+ name = symbol_name(v);
+ if (princ)
+ outs(name, f);
+ else if (v == NIL)
+ outs("()", f);
+ else if (ismanaged(v)) {
+ outs("#:", f);
+ outs(name, f);
+ }
+ else
+ print_symbol_name(f, name);
+ break;
+ case TAG_BUILTIN:
+ if (isbuiltin(v)) {
+ outs("#.", f);
+ outs(builtin_names[uintval(v)], f);
+ break;
+ }
+ if (!ismanaged(v)) {
+ assert(iscvalue(v));
+ cvalue_print(f, v, princ); break;
+ }
+ case TAG_CONS:
+ if ((label=(value_t)ptrhash_get(&printconses, (void*)v)) !=
+ (value_t)PH_NOTFOUND) {
+ if (!ismarked(v)) {
+ HPOS+=fprintf(f, "#%ld#", numval(label));
+ return;
+ }
+ HPOS+=fprintf(f, "#%ld=", numval(label));
+ }
+ if (isvector(v)) {
+ outc('[', f);
+ int newindent = HPOS, est;
+ unmark_cons(v);
+ int i, sz = vector_size(v);
+ for(i=0; i < sz; i++) {
+ do_print(f, vector_elt(v,i), princ);
+ if (i < sz-1) {
+ if (princ) {
+ outc(' ', f);
+ }
+ else {
+ est = lengthestimate(vector_elt(v,i+1));
+ if (HPOS > 74 || (est!=-1 && (HPOS+est > 78)) ||
+ (HPOS > 40 && !smallp(vector_elt(v,i+1))))
+ outindent(newindent, f);
+ else
+ outc(' ', f);
+ }
+ }
+ }
+ outc(']', f);
+ break;
+ }
+ if (iscvalue(v)) {
+ unmark_cons(v);
+ cvalue_print(f, v, princ);
+ break;
+ }
+ print_pair(f, v, princ);
+ break;
+ }
+}
+
+void print_string(FILE *f, char *str, size_t sz)
+{
+ char buf[512];
+ size_t i = 0;
+
+ outc('"', f);
+ while (i < sz) {
+ u8_escape(buf, sizeof(buf), str, &i, sz, 1, 0);
+ outs(buf, f);
+ }
+ outc('"', f);
+}
+
+static numerictype_t sym_to_numtype(value_t type);
+
+// 'weak' means we don't need to accurately reproduce the type, so
+// for example #int32(0) can be printed as just 0. this is used
+// printing in a context where a type is already implied, e.g. inside
+// an array.
+static void cvalue_printdata(FILE *f, void *data, size_t len, value_t type,
+ int princ, int weak)
+{
+ int64_t tmp=0;
+
+ if (type == charsym) {
+ // print chars as characters when possible
+ unsigned char ch = *(unsigned char*)data;
+ if (princ)
+ outc(ch, f);
+ else if (weak)
+ HPOS+=fprintf(f, "%hhu", ch);
+ else if (isprint(ch))
+ HPOS+=fprintf(f, "#\\%c", ch);
+ else
+ HPOS+=fprintf(f, "#char(%hhu)", ch);
+ }
+ /*
+ else if (type == ucharsym) {
+ uchar ch = *(uchar*)data;
+ if (princ)
+ outc(ch, f);
+ else {
+ if (!weak)
+ fprintf(f, "#uchar(");
+ fprintf(f, "%hhu", ch);
+ if (!weak)
+ outs(")", f);
+ }
+ }
+ */
+ else if (type == wcharsym) {
+ uint32_t wc = *(uint32_t*)data;
+ char seq[8];
+ if (weak)
+ HPOS+=fprintf(f, "%d", (int)wc);
+ else if (princ || (iswprint(wc) && wc>0x7f)) {
+ // reader only reads #\c syntax as wchar if the code is >0x7f
+ size_t nb = u8_toutf8(seq, sizeof(seq), &wc, 1);
+ seq[nb] = '\0';
+ // TODO: better multibyte handling
+ if (!princ) outs("#\\", f);
+ outs(seq, f);
+ }
+ else {
+ HPOS+=fprintf(f, "#%s(%d)", symbol_name(type), (int)wc);
+ }
+ }
+ else if (type == int64sym
+#ifdef BITS64
+ || type == longsym
+#endif
+ ) {
+ int64_t i64 = *(int64_t*)data;
+ if (fits_fixnum(i64) || princ) {
+ if (weak || princ)
+ HPOS+=fprintf(f, "%lld", i64);
+ else
+ HPOS+=fprintf(f, "#%s(%lld)", symbol_name(type), i64);
+ }
+ else
+ HPOS+=fprintf(f, "#%s(0x%08x%08x)", symbol_name(type),
+ (uint32_t)(i64>>32),
+ (uint32_t)(i64));
+ }
+ else if (type == uint64sym
+#ifdef BITS64
+ || type == ulongsym
+#endif
+ ) {
+ uint64_t ui64 = *(uint64_t*)data;
+ if (fits_fixnum(ui64) || princ) {
+ if (weak || princ)
+ HPOS+=fprintf(f, "%llu", ui64);
+ else
+ HPOS+=fprintf(f, "#%s(%llu)", symbol_name(type), ui64);
+ }
+ else
+ HPOS+=fprintf(f, "#%s(0x%08x%08x)", symbol_name(type),
+ (uint32_t)(ui64>>32),
+ (uint32_t)(ui64));
+ }
+ else if (type == lispvaluesym) {
+ // TODO
+ }
+ else if (type == floatsym || type == doublesym) {
+ char buf[64];
+ double d;
+ if (type == floatsym) d = (double)*(float*)data;
+ else d = *(double*)data;
+ snprint_real(buf, sizeof(buf), d, 0, 16, 3, 10);
+ if (weak || princ || (type==doublesym && strpbrk(buf, ".eE"))) {
+ outs(buf, f);
+ }
+ else {
+ if (!DFINITE(d))
+ HPOS+=fprintf(f, "#%s(\"%s\")", symbol_name(type), buf);
+ else
+ HPOS+=fprintf(f, "#%s(%s)", symbol_name(type), buf);
+ }
+ }
+ else if (issymbol(type)) {
+ // handle other integer prims. we know it's smaller than 64 bits
+ // at this point, so int64 is big enough to capture everything.
+ tmp = conv_to_int64(data, sym_to_numtype(type));
+ if (fits_fixnum(tmp) || princ) {
+ if (weak || princ)
+ HPOS+=fprintf(f, "%lld", tmp);
+ else
+ HPOS+=fprintf(f, "#%s(%lld)", symbol_name(type), tmp);
+ }
+ else
+ HPOS+=fprintf(f, "#%s(0x%08x)", symbol_name(type),
+ (uint32_t)(tmp&0xffffffff));
+ }
+ else if (iscons(type)) {
+ if (car_(type) == arraysym) {
+ value_t eltype = car(cdr_(type));
+ size_t cnt, elsize;
+ if (iscons(cdr_(cdr_(type)))) {
+ cnt = toulong(car_(cdr_(cdr_(type))), "length");
+ elsize = cnt ? len/cnt : 0;
+ }
+ else {
+ // incomplete array type
+ int junk;
+ elsize = ctype_sizeof(eltype, &junk);
+ cnt = elsize ? len/elsize : 0;
+ }
+ if (eltype == charsym) {
+ if (princ) {
+ fwrite(data, 1, len, f);
+ }
+ else {
+ print_string(f, (char*)data, len);
+ }
+ return;
+ }
+ else if (eltype == wcharsym) {
+ // TODO wchar
+ }
+ else {
+ }
+ size_t i;
+ if (!weak) {
+ outs("#array(", f);
+ do_print(f, eltype, princ);
+ outc(' ', f);
+ }
+ outc('[', f);
+ for(i=0; i < cnt; i++) {
+ cvalue_printdata(f, data, elsize, eltype, princ, 1);
+ if (i < cnt-1)
+ outc(' ', f);
+ data += elsize;
+ }
+ outc(']', f);
+ if (!weak)
+ outc(')', f);
+ }
+ else if (car_(type) == enumsym) {
+ value_t sym = list_nth(car(cdr_(type)), *(size_t*)data);
+ if (!weak) {
+ outs("#enum(", f);
+ do_print(f, car(cdr_(type)), princ);
+ outc(' ', f);
+ }
+ if (sym == NIL) {
+ cvalue_printdata(f, data, len, int32sym, princ, 1);
+ }
+ else {
+ do_print(f, sym, princ);
+ }
+ if (!weak)
+ outc(')', f);
+ }
+ }
+}
+
+void cvalue_print(FILE *f, value_t v, int princ)
+{
+ cvalue_t *cv = (cvalue_t*)ptr(v);
+ void *data = cv_data(cv);
+
+ if (cv->flags.islispfunction) {
+ HPOS+=fprintf(f, "#<guestfunction @0x%08lx>",
+ (unsigned long)*(guestfunc_t*)data);
+ return;
+ }
+
+ cvalue_printdata(f, data, cv_len(cv), cv_type(cv), princ, 0);
+}
+
+void print(FILE *f, value_t v, int princ)
+{
+ ptrhash_reset(&printconses, 32);
+ printlabel = 0;
+ print_traverse(v);
+ HPOS = VPOS = 0;
+ do_print(f, v, princ);
+}
--- /dev/null
+++ b/femtolisp/printcases.lsp
@@ -1,0 +1,21 @@
+macroexpand
+append
+bq-process
+(syntax-environment)
+
+(symbol-syntax 'try)
+
+(map-int (lambda (x) `(a b c d e)) 90)
+
+(list-to-vector (map-int (lambda (x) `(a b c d e)) 90))
+
+'((lambda (x y) (if (< x y) x y)) (a b c) (d e f) 2 3 (r t y))
+
+'((lambda (x y) (if (< x y) x yffffffffffffffffffff)) (a b c) (d e f) 2 3 (r t y))
+
+'((lambda (x y) (if (< x y) x y)) (a b c) (d (e zz zzz) f) 2 3 (r t y))
+
+'((23 . a) (9 . a) (22 . b) (17 . d) (14 . d) (8 . b) (21 . e)
+ (19 . b) (16 . c) (13 . c) (11 . b) (7 . e) (24 . c) (20 . d)
+ (18 . e) (15 . a) (12 . a) (10 . e) (6 . d) (5 . c) (4 . e)
+ (3 . d) (2 . c) (0 . b) (1 . a))
--- /dev/null
+++ b/femtolisp/read.c
@@ -1,0 +1,542 @@
+enum {
+ TOK_NONE, TOK_OPEN, TOK_CLOSE, TOK_DOT, TOK_QUOTE, TOK_SYM, TOK_NUM,
+ TOK_BQ, TOK_COMMA, TOK_COMMAAT, TOK_COMMADOT,
+ TOK_SHARPDOT, TOK_LABEL, TOK_BACKREF, TOK_SHARPQUOTE, TOK_SHARPOPEN,
+ TOK_OPENB, TOK_CLOSEB, TOK_SHARPSYM, TOK_GENSYM, TOK_DOUBLEQUOTE
+};
+
+// defines which characters are ordinary symbol characters.
+// exceptions are '.', which is an ordinary symbol character
+// unless it's the only character in the symbol, and '#', which is
+// an ordinary symbol character unless it's the first character.
+static int symchar(char c)
+{
+ static char *special = "()[]'\";`,\\|";
+ return (!isspace(c) && !strchr(special, c));
+}
+
+static int isnumtok(char *tok, value_t *pval)
+{
+ char *end;
+ int64_t i64;
+ uint64_t ui64;
+ double d;
+ if (*tok == '\0')
+ return 0;
+ if (!((tok[0]=='0' && tok[1]=='x') || // these formats are always integer
+ (tok[0]=='0' && isdigit(tok[1]))) &&
+ strpbrk(tok, ".eE")) {
+ d = strtod(tok, &end);
+ if (*end == '\0') {
+ if (pval) *pval = mk_double(d);
+ return 1;
+ }
+ }
+ if (isdigit(tok[0]) || tok[0]=='-' || tok[0]=='+') {
+ if (tok[0]=='-') {
+ i64 = strtoll(tok, &end, 0);
+ if (pval) *pval = return_from_int64(i64);
+ }
+ else {
+ ui64 = strtoull(tok, &end, 0);
+ if (pval) *pval = return_from_uint64(ui64);
+ }
+ if (*end == '\0')
+ return 1;
+ }
+ return 0;
+}
+
+static u_int32_t toktype = TOK_NONE;
+static value_t tokval;
+static char buf[256];
+
+static char nextchar(FILE *f)
+{
+ int ch;
+ char c;
+
+ do {
+ ch = fgetc(f);
+ if (ch == EOF)
+ return 0;
+ c = (char)ch;
+ if (c == ';') {
+ // single-line comment
+ do {
+ ch = fgetc(f);
+ if (ch == EOF)
+ return 0;
+ } while ((char)ch != '\n');
+ c = (char)ch;
+ }
+ } while (isspace(c));
+ return c;
+}
+
+static void take(void)
+{
+ toktype = TOK_NONE;
+}
+
+static void accumchar(char c, int *pi)
+{
+ buf[(*pi)++] = c;
+ if (*pi >= (int)(sizeof(buf)-1))
+ lerror(ParseError, "read: token too long");
+}
+
+// return: 1 if escaped (forced to be symbol)
+static int read_token(FILE *f, char c, int digits)
+{
+ int i=0, ch, escaped=0, issym=0, first=1;
+
+ while (1) {
+ if (!first) {
+ ch = fgetc(f);
+ if (ch == EOF)
+ goto terminate;
+ c = (char)ch;
+ }
+ first = 0;
+ if (c == '|') {
+ issym = 1;
+ escaped = !escaped;
+ }
+ else if (c == '\\') {
+ issym = 1;
+ ch = fgetc(f);
+ if (ch == EOF)
+ goto terminate;
+ accumchar((char)ch, &i);
+ }
+ else if (!escaped && !(symchar(c) && (!digits || isdigit(c)))) {
+ break;
+ }
+ else {
+ accumchar(c, &i);
+ }
+ }
+ ungetc(c, f);
+ terminate:
+ buf[i++] = '\0';
+ return issym;
+}
+
+static u_int32_t peek(FILE *f)
+{
+ char c, *end;
+ fixnum_t x;
+ int ch;
+
+ if (toktype != TOK_NONE)
+ return toktype;
+ c = nextchar(f);
+ if (feof(f)) return TOK_NONE;
+ if (c == '(') {
+ toktype = TOK_OPEN;
+ }
+ else if (c == ')') {
+ toktype = TOK_CLOSE;
+ }
+ else if (c == '[') {
+ toktype = TOK_OPENB;
+ }
+ else if (c == ']') {
+ toktype = TOK_CLOSEB;
+ }
+ else if (c == '\'') {
+ toktype = TOK_QUOTE;
+ }
+ else if (c == '`') {
+ toktype = TOK_BQ;
+ }
+ else if (c == '"') {
+ toktype = TOK_DOUBLEQUOTE;
+ }
+ else if (c == '#') {
+ ch = fgetc(f);
+ if (ch == EOF)
+ lerror(ParseError, "read: invalid read macro");
+ if ((char)ch == '.') {
+ toktype = TOK_SHARPDOT;
+ }
+ else if ((char)ch == '\'') {
+ toktype = TOK_SHARPQUOTE;
+ }
+ else if ((char)ch == '\\') {
+ u_int32_t cval = u8_fgetc(f);
+ if (cval == UEOF)
+ lerror(ParseError, "read: end of input in character constant");
+ toktype = TOK_NUM;
+ tokval = fixnum(cval);
+ if (cval > 0x7f) {
+ tokval = cvalue_wchar(&tokval, 1);
+ }
+ else {
+ tokval = cvalue_char(&tokval, 1);
+ }
+ }
+ else if ((char)ch == '(') {
+ toktype = TOK_SHARPOPEN;
+ }
+ else if ((char)ch == '<') {
+ lerror(ParseError, "read: unreadable object");
+ }
+ else if (isdigit((char)ch)) {
+ read_token(f, (char)ch, 1);
+ c = (char)fgetc(f);
+ if (c == '#')
+ toktype = TOK_BACKREF;
+ else if (c == '=')
+ toktype = TOK_LABEL;
+ else
+ lerror(ParseError, "read: invalid label");
+ errno = 0;
+ x = strtol(buf, &end, 10);
+ if (*end != '\0' || errno)
+ lerror(ParseError, "read: invalid label");
+ tokval = fixnum(x);
+ }
+ else if ((char)ch == '!') {
+ // #! single line comment for shbang script support
+ do {
+ ch = fgetc(f);
+ } while (ch != EOF && (char)ch != '\n');
+ return peek(f);
+ }
+ else if ((char)ch == '|') {
+ // multiline comment
+ while (1) {
+ ch = fgetc(f);
+ hashpipe_got:
+ if (ch == EOF)
+ lerror(ParseError, "read: eof within comment");
+ if ((char)ch == '|') {
+ ch = fgetc(f);
+ if ((char)ch == '#')
+ break;
+ goto hashpipe_got;
+ }
+ }
+ // this was whitespace, so keep peeking
+ return peek(f);
+ }
+ else if ((char)ch == ':') {
+ // gensym
+ ch = fgetc(f);
+ if ((char)ch == 'g')
+ ch = fgetc(f);
+ read_token(f, (char)ch, 0);
+ errno = 0;
+ x = strtol(buf, &end, 10);
+ if (*end != '\0' || buf[0] == '\0' || errno)
+ lerror(ParseError, "read: invalid gensym label");
+ toktype = TOK_GENSYM;
+ tokval = fixnum(x);
+ }
+ else if (symchar((char)ch)) {
+ read_token(f, ch, 0);
+ toktype = TOK_SHARPSYM;
+ tokval = symbol(buf);
+ c = nextchar(f);
+ if (c != '(') {
+ take();
+ lerror(ParseError, "read: expected argument list for %s",
+ symbol_name(tokval));
+ }
+ }
+ else {
+ lerror(ParseError, "read: unknown read macro");
+ }
+ }
+ else if (c == ',') {
+ toktype = TOK_COMMA;
+ ch = fgetc(f);
+ if (ch == EOF)
+ return toktype;
+ if ((char)ch == '@')
+ toktype = TOK_COMMAAT;
+ else if ((char)ch == '.')
+ toktype = TOK_COMMADOT;
+ else
+ ungetc((char)ch, f);
+ }
+ else {
+ if (!read_token(f, c, 0)) {
+ if (buf[0]=='.' && buf[1]=='\0') {
+ return (toktype=TOK_DOT);
+ }
+ else {
+ errno = 0;
+ if (isnumtok(buf, &tokval)) {
+ if (errno)
+ lerror(ParseError,"read: overflow in numeric constant");
+ return (toktype=TOK_NUM);
+ }
+ }
+ }
+ toktype = TOK_SYM;
+ tokval = symbol(buf);
+ }
+ return toktype;
+}
+
+static value_t do_read_sexpr(FILE *f, value_t label);
+
+static value_t read_vector(FILE *f, value_t label, u_int32_t closer)
+{
+ value_t v=alloc_vector(4, 1), elt;
+ u_int32_t i=0;
+ PUSH(v);
+ if (label != UNBOUND)
+ ptrhash_put(&readstate->backrefs, (void*)label, (void*)v);
+ while (peek(f) != closer) {
+ if (feof(f))
+ lerror(ParseError, "read: unexpected end of input");
+ if (i >= vector_size(v))
+ Stack[SP-1] = vector_grow(v);
+ elt = do_read_sexpr(f, UNBOUND);
+ v = Stack[SP-1];
+ vector_elt(v,i) = elt;
+ i++;
+ }
+ take();
+ vector_setsize(v, i);
+ return POP();
+}
+
+static value_t read_string(FILE *f)
+{
+ char *buf, *temp;
+ char eseq[10];
+ size_t i=0, j, sz = 64, ndig;
+ int c;
+ value_t s;
+ u_int32_t wc;
+
+ buf = malloc(sz);
+ while (1) {
+ if (i >= sz-4) { // -4: leaves room for longest utf8 sequence
+ sz *= 2;
+ temp = realloc(buf, sz);
+ if (temp == NULL) {
+ free(buf);
+ lerror(ParseError, "read: out of memory reading string");
+ }
+ buf = temp;
+ }
+ c = fgetc(f);
+ if (c == EOF) {
+ free(buf);
+ lerror(ParseError, "read: unexpected end of input in string");
+ }
+ if (c == '"')
+ break;
+ else if (c == '\\') {
+ c = fgetc(f);
+ if (c == EOF) {
+ free(buf);
+ lerror(ParseError, "read: end of input in escape sequence");
+ }
+ j=0;
+ if (octal_digit(c)) {
+ do {
+ eseq[j++] = c;
+ c = fgetc(f);
+ } while (octal_digit(c) && j<3 && (c!=EOF));
+ if (c!=EOF) ungetc(c, f);
+ eseq[j] = '\0';
+ wc = strtol(eseq, NULL, 8);
+ i += u8_wc_toutf8(&buf[i], wc);
+ }
+ else if ((c=='x' && (ndig=2)) ||
+ (c=='u' && (ndig=4)) ||
+ (c=='U' && (ndig=8))) {
+ wc = c;
+ c = fgetc(f);
+ while (hex_digit(c) && j<ndig && (c!=EOF)) {
+ eseq[j++] = c;
+ c = fgetc(f);
+ }
+ if (c!=EOF) ungetc(c, f);
+ eseq[j] = '\0';
+ if (j) wc = strtol(eseq, NULL, 16);
+ i += u8_wc_toutf8(&buf[i], wc);
+ }
+ else if (c == 'n')
+ buf[i++] = '\n';
+ else if (c == 't')
+ buf[i++] = '\t';
+ else if (c == 'r')
+ buf[i++] = '\r';
+ else if (c == 'b')
+ buf[i++] = '\b';
+ else if (c == 'f')
+ buf[i++] = '\f';
+ else if (c == 'v')
+ buf[i++] = '\v';
+ else if (c == 'a')
+ buf[i++] = '\a';
+ else
+ buf[i++] = c;
+ }
+ else {
+ buf[i++] = c;
+ }
+ }
+ s = cvalue_string(i);
+ memcpy(cvalue_data(s), buf, i);
+ free(buf);
+ return s;
+}
+
+// build a list of conses. this is complicated by the fact that all conses
+// can move whenever a new cons is allocated. we have to refer to every cons
+// through a handle to a relocatable pointer (i.e. a pointer on the stack).
+static void read_list(FILE *f, value_t *pval, value_t label)
+{
+ value_t c, *pc;
+ u_int32_t t;
+
+ PUSH(NIL);
+ pc = &Stack[SP-1]; // to keep track of current cons cell
+ t = peek(f);
+ while (t != TOK_CLOSE) {
+ if (feof(f))
+ lerror(ParseError, "read: unexpected end of input");
+ c = mk_cons(); car_(c) = cdr_(c) = NIL;
+ if (iscons(*pc)) {
+ cdr_(*pc) = c;
+ }
+ else {
+ *pval = c;
+ if (label != UNBOUND)
+ ptrhash_put(&readstate->backrefs, (void*)label, (void*)c);
+ }
+ *pc = c;
+ c = do_read_sexpr(f,UNBOUND); // must be on separate lines due to
+ car_(*pc) = c; // undefined evaluation order
+
+ t = peek(f);
+ if (t == TOK_DOT) {
+ take();
+ c = do_read_sexpr(f,UNBOUND);
+ cdr_(*pc) = c;
+ t = peek(f);
+ if (feof(f))
+ lerror(ParseError, "read: unexpected end of input");
+ if (t != TOK_CLOSE)
+ lerror(ParseError, "read: expected ')'");
+ }
+ }
+ take();
+ (void)POP();
+}
+
+// label is the backreference we'd like to fix up with this read
+static value_t do_read_sexpr(FILE *f, value_t label)
+{
+ value_t v, sym, oldtokval, *head;
+ value_t *pv;
+ u_int32_t t;
+
+ t = peek(f);
+ take();
+ switch (t) {
+ case TOK_CLOSE:
+ lerror(ParseError, "read: unexpected ')'");
+ case TOK_CLOSEB:
+ lerror(ParseError, "read: unexpected ']'");
+ case TOK_DOT:
+ lerror(ParseError, "read: unexpected '.'");
+ case TOK_SYM:
+ case TOK_NUM:
+ return tokval;
+ case TOK_COMMA:
+ head = &COMMA; goto listwith;
+ case TOK_COMMAAT:
+ head = &COMMAAT; goto listwith;
+ case TOK_COMMADOT:
+ head = &COMMADOT; goto listwith;
+ case TOK_BQ:
+ head = &BACKQUOTE; goto listwith;
+ case TOK_QUOTE:
+ head = "E;
+ listwith:
+ v = cons_reserve(2);
+ car_(v) = *head;
+ cdr_(v) = tagptr(((cons_t*)ptr(v))+1, TAG_CONS);
+ car_(cdr_(v)) = cdr_(cdr_(v)) = NIL;
+ PUSH(v);
+ if (label != UNBOUND)
+ ptrhash_put(&readstate->backrefs, (void*)label, (void*)v);
+ v = do_read_sexpr(f,UNBOUND);
+ car_(cdr_(Stack[SP-1])) = v;
+ return POP();
+ case TOK_SHARPQUOTE:
+ // femtoLisp doesn't need symbol-function, so #' does nothing
+ return do_read_sexpr(f, label);
+ case TOK_OPEN:
+ PUSH(NIL);
+ read_list(f, &Stack[SP-1], label);
+ return POP();
+ case TOK_SHARPSYM:
+ // constructor notation
+ sym = tokval;
+ PUSH(NIL);
+ read_list(f, &Stack[SP-1], UNBOUND);
+ v = POP();
+ return apply(sym, v);
+ case TOK_OPENB:
+ return read_vector(f, label, TOK_CLOSEB);
+ case TOK_SHARPOPEN:
+ return read_vector(f, label, TOK_CLOSE);
+ case TOK_SHARPDOT:
+ // eval-when-read
+ // evaluated expressions can refer to existing backreferences, but they
+ // cannot see pending labels. in other words:
+ // (... #2=#.#0# ... ) OK
+ // (... #2=#.(#2#) ... ) DO NOT WANT
+ v = do_read_sexpr(f,UNBOUND);
+ return toplevel_eval(v);
+ case TOK_LABEL:
+ // create backreference label
+ if (ptrhash_has(&readstate->backrefs, (void*)tokval))
+ lerror(ParseError, "read: label %ld redefined", numval(tokval));
+ oldtokval = tokval;
+ v = do_read_sexpr(f, tokval);
+ ptrhash_put(&readstate->backrefs, (void*)oldtokval, (void*)v);
+ return v;
+ case TOK_BACKREF:
+ // look up backreference
+ v = (value_t)ptrhash_get(&readstate->backrefs, (void*)tokval);
+ if (v == (value_t)PH_NOTFOUND)
+ lerror(ParseError, "read: undefined label %ld", numval(tokval));
+ return v;
+ case TOK_GENSYM:
+ pv = (value_t*)ptrhash_bp(&readstate->gensyms, (void*)tokval);
+ if (*pv == (value_t)PH_NOTFOUND)
+ *pv = gensym(NULL, 0);
+ return *pv;
+ case TOK_DOUBLEQUOTE:
+ return read_string(f);
+ }
+ return NIL;
+}
+
+value_t read_sexpr(FILE *f)
+{
+ value_t v;
+ readstate_t state;
+ state.prev = readstate;
+ ptrhash_new(&state.backrefs, 16);
+ ptrhash_new(&state.gensyms, 16);
+ readstate = &state;
+
+ v = do_read_sexpr(f, UNBOUND);
+
+ readstate = state.prev;
+ free_readstate(&state);
+ return v;
+}
--- /dev/null
+++ b/femtolisp/site/doc
@@ -1,0 +1,62 @@
+1. Syntax
+
+symbols
+numbers
+conses and vectors
+comments
+special prefix tokens: ' ` , ,@ ,.
+other read macros: #. #' #\ #< #n= #n# #: #ctor
+builtins
+
+2. Data and execution models
+
+3. Primitive functions
+
+eq atom not set prog1 progn
+symbolp numberp builtinp consp vectorp boundp
++ - * / <
+apply eval
+
+4. Special forms
+
+quote if lambda macro while label cond and or
+
+5. Data structures
+
+cons car cdr rplaca rplacd list
+alloc vector aref aset length
+
+6. Other functions
+
+read, print, princ, load, exit
+equal, compare
+gensym
+
+7. Exceptions
+
+trycatch raise
+
+8. Cvalues
+
+introduction
+type representations
+constructors
+access
+memory management concerns
+ccall
+
+
+If deliberate 50% heap utilization seems wasteful, consider:
+
+- malloc has per-object overhead. for small allocations you might use
+ much more space than you think.
+- any non-moving memory manager (whether malloc or a collector) can
+ waste arbitrary amounts of memory through fragmentation.
+
+With a copying collector, you agree to give up 50% of your memory
+up front, in exchange for significant benefits:
+
+- really fast allocation
+- heap compaction, improving locality and possibly speeding up computation
+- collector performance O(1) in number of dead objects, essential for
+ maximal performance on generational workloads
--- /dev/null
+++ b/femtolisp/site/doc.html
@@ -1,0 +1,428 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
+<title>femtoLisp</title>
+</head>
+<body bgcolor="#fcfcfc"> <!-"#fcfcc8">
+<img src="flbanner.jpg">
+
+<table border=0 width="100%" cellpadding=0 cellspacing=0>
+<tr><td bgcolor="#2d3f5f" height=4></table>
+
+<h1>0. Argument</h1>
+This Lisp has the following characteristics and goals:
+
+<ul>
+<li>Lisp-1 evaluation rule (ala Scheme)
+<li>Self-evaluating lambda (i.e. <tt>'(lambda (x) x)</tt> is callable)
+<li>Full Common Lisp-style macros
+<li>Dotted lambda lists for rest arguments (ala Scheme)
+<li>Symbols have one binding
+<li>Builtin functions are constants
+<li><em>All</em> values are printable and readable
+<li>Case-sensitive symbol names
+<li>Only the minimal core built-in (i.e. written in C), but
+ enough to provide a practical level of performance
+<li>Very short (but not necessarily simple...) implementation
+<li>Generally use Common Lisp operator names
+<li>Nothing excessively weird or fancy
+</ul>
+
+<h1>1. Syntax</h1>
+<h2>1.1. Symbols</h2>
+Any character string can be a symbol name, including the empty string. In
+general, text between whitespace is read as a symbol except in the following
+cases:
+<ul>
+<li>The text begins with <tt>#</tt>
+<li>The text consists of a single period <tt>.</tt>
+<li>The text contains one of the special characters <tt>()[]';`,\|</tt>
+<li>The text is a valid number
+<li>The text is empty
+</ul>
+In these cases the symbol can be written by surrounding it with <tt>| |</tt>
+characters, or by escaping individual characters within the symbol using
+backslash <tt>\</tt>. Note that <tt>|</tt> and <tt>\</tt> must always be
+preceded with a backslash when writing a symbol name.
+
+<h2>1.2. Numbers</h2>
+
+A number consists of an optional + or - sign followed by one of the following
+sequences:
+<ul>
+<li><tt>NNN...</tt> where N is a decimal digit
+<li><tt>0xNNN...</tt> where N is a hexadecimal digit
+<li><tt>0NNN...</tt> where N is an octal digit
+</ul>
+femtoLisp provides 30-bit integers, and it is an error to write a constant
+less than -2<sup>29</sup> or greater than 2<sup>29</sup>-1.
+
+<h2>1.3. Conses and vectors</h2>
+
+The text <tt>(a b c)</tt> parses to the structure
+<tt>(cons a (cons b (cons c nil)))</tt> where a, b, and c are arbitrary
+expressions.
+<p>
+The text <tt>(a . b)</tt> parses to the structure
+<tt>(cons a b)</tt> where a and b are arbitrary expressions.
+<p>
+The text <tt>()</tt> reads as the symbol <tt>nil</tt>.
+<p>
+The text <tt>[a b c]</tt> parses to a vector of expressions a, b, and c.
+The syntax <tt>#(a b c)</tt> has the same meaning.
+
+
+<h2>1.4. Comments</h2>
+
+Text between a semicolon <tt>;</tt> and the next end-of-line is skipped.
+Text between <tt>#|</tt> and <tt>|#</tt> is also skipped.
+
+<h2>1.5. Prefix tokens</h2>
+
+There are five special prefix tokens which parse as follows:<p>
+<tt>'a</tt> is equivalent to <tt>(quote a)</tt>.<br>
+<tt>`a</tt> is equivalent to <tt>(backquote a)</tt>.<br>
+<tt>,a</tt> is equivalent to <tt>(*comma* a)</tt>.<br>
+<tt>,@a</tt> is equivalent to <tt>(*comma-at* a)</tt>.<br>
+<tt>,.a</tt> is equivalent to <tt>(*comma-dot* a)</tt>.
+
+
+<h2>1.6. Other read macros</h2>
+
+femtoLisp provides a few "read macros" that let you accomplish interesting
+tricks for textually representing data structures.
+
+<table border=1>
+<tr>
+<td>sequence<td>meaning
+<tr>
+<td><tt>#.e</tt><td>evaluate expression <tt>e</tt> and behave as if e's
+ value had been written in place of e
+<tr>
+<td><tt>#\c</tt><td><tt>c</tt> is a character; read as its Unicode value
+<tr>
+<td><tt>#n=e</tt><td>read <tt>e</tt> and label it as <tt>n</tt>, where n
+ is a decimal number
+<tr>
+<td><tt>#n#</tt><td>read as the identically-same value previously labeled
+ <tt>n</tt>
+<tr>
+<td><tt>#:gNNN or #:NNN</tt><td>read a gensym. NNN is a hexadecimal
+ constant. future occurrences of the same <tt>#:</tt> sequence will read to
+ the identically-same gensym
+<tr>
+<td><tt>#sym(...)</tt><td>reads to the result of evaluating
+ <tt>(apply sym '(...))</tt>
+<tr>
+<td><tt>#<</tt><td>triggers an error
+<tr>
+<td><tt>#'</tt><td>ignored; provided for compatibility
+<tr>
+<td><tt>#!</tt><td>single-line comment, for script execution support
+<tr>
+<td><tt>"str"</tt><td>UTF-8 character string; may contain newlines.
+ <tt>\</tt> is the escape character. All C escape sequences are supported, plus
+ <tt>\u</tt> and <tt>\U</tt> for unicode values.
+</table>
+When a read macro involves persistent state (e.g. label assignments), that
+state is valid only within the closest enclosing call to <tt>read</tt>.
+
+
+<h2>1.7. Builtins</h2>
+
+Builtin functions are represented as opaque constants. Every builtin
+function is the value of some constant symbol, so the builtin <tt>eq</tt>,
+for example, can be written as <tt>#.eq</tt> ("the value of symbol eq").
+Note that <tt>eq</tt> itself is still an ordinary symbol, except that its
+value cannot be changed.
+<p>
+
+<table border=0 width="100%" cellpadding=0 cellspacing=0>
+<tr><td bgcolor="#2d3f5f" height=4></table>
+
+
+<h1>2. Data and execution models</h1>
+
+
+
+
+<table border=0 width="100%" cellpadding=0 cellspacing=0>
+<tr><td bgcolor="#2d3f5f" height=4></table>
+
+
+<h1>3. Primitive functions</h1>
+
+
+eq atom not set prog1 progn
+symbolp numberp builtinp consp vectorp boundp
++ - * / <
+apply eval
+
+
+<table border=0 width="100%" cellpadding=0 cellspacing=0>
+<tr><td bgcolor="#2d3f5f" height=4></table>
+
+<h1>4. Special forms</h1>
+
+quote if lambda macro while label cond and or
+
+
+<table border=0 width="100%" cellpadding=0 cellspacing=0>
+<tr><td bgcolor="#2d3f5f" height=4></table>
+
+<h1>5. Data structures</h1>
+
+cons car cdr rplaca rplacd list
+alloc vector aref aset length
+
+
+<table border=0 width="100%" cellpadding=0 cellspacing=0>
+<tr><td bgcolor="#2d3f5f" height=4></table>
+
+<h1>6. Other functions</h1>
+
+read print princ load exit
+equal compare
+gensym
+
+
+<table border=0 width="100%" cellpadding=0 cellspacing=0>
+<tr><td bgcolor="#2d3f5f" height=4></table>
+
+<h1>7. Exceptions</h1>
+
+trycatch raise
+
+
+<table border=0 width="100%" cellpadding=0 cellspacing=0>
+<tr><td bgcolor="#2d3f5f" height=4></table>
+
+<h1>8. Cvalues</h1>
+
+<h2>8.1. Introduction</h2>
+
+femtoLisp allows you to use the full range of C data types on
+dynamically-typed Lisp values. The motivation for this feature is that
+useful
+interpreters must provide a large library of routines in C for dealing
+with "real world" data like text and packed numeric arrays, and I would
+rather not write yet another such library. Instead, all the
+required data representations and primitives are provided so that such
+features could be implemented in, or at least described in, Lisp.
+<p>
+The cvalues capability makes it easier to call C from Lisp by providing
+ways to construct whatever arguments your C routines might require, and ways
+to decipher whatever values your C routines might return. Here are some
+things you can do with cvalues:
+<ul>
+<li>Call native C functions from Lisp without wrappers
+<li>Wrap C functions in pure Lisp, automatically inheriting some degree
+ of type safety
+<li>Use Lisp functions as callbacks from C code
+<li>Use the Lisp garbage collector to reclaim malloc'd storage
+<li>Annotate C pointers with size information for bounds checking or
+ serialization
+<li>Attach symbolic type information to a C data structure, allowing it to
+ inherit Lisp services such as printing a readable representation
+<li>Add datatypes like strings to Lisp
+<li>Use more efficient represenations for your Lisp programs' data
+</ul>
+<p>
+femtoLisp's "cvalues" is inspired in part by Python's "ctypes" package.
+Lisp doesn't really have first-class types the way Python does, but it does
+have values, hence my version is called "cvalues".
+
+<h2>8.2. Type representations</h2>
+
+The core of cvalues is a language for describing C data types as
+symbolic expressions:
+
+<ul>
+<li>Primitive types are symbols <tt>int8, uint8, int16, uint16, int32, uint32,
+int64, uint64, char, wchar, long, ulong, float, double, void</tt>
+<li>Arrays <tt>(array TYPE SIZE)</tt>, where TYPE is another C type and
+SIZE is either a Lisp number or a C ulong. SIZE can be omitted to
+represent incomplete C array types like "int a[]". As in C, the size may
+only be omitted for the top level of a nested array; all array
+<em>element</em> types
+must have explicit sizes. Examples:
+<ul>
+ <tt>int a[][2][3]</tt> is <tt>(array (array (array int32 3) 2))</tt><br>
+ <tt>int a[4][]</tt> would be <tt>(array (array int32) 4)</tt>, but this is
+ invalid.
+</ul>
+<li>Pointer <tt>(pointer TYPE)</tt>
+<li>Struct <tt>(struct ((NAME TYPE) (NAME TYPE) ...))</tt>
+<li>Union <tt>(union ((NAME TYPE) (NAME TYPE) ...))</tt>
+<li>Enum <tt>(enum (NAME NAME ...))</tt>
+<li>Function <tt>(c-function RET-TYPE (ARG-TYPE ARG-TYPE ...))</tt>
+</ul>
+
+A cvalue can be constructed using <tt>(c-value TYPE arg)</tt>, where
+<tt>arg</tt> is some Lisp value. The system will try to convert the Lisp
+value to the specified type. In many cases this will work better if some
+components of the provided Lisp value are themselves cvalues.
+
+<p>
+Note the function type is called "c-function" to avoid confusion, since
+functions are such a prevalent concept in Lisp.
+
+<p>
+The function <tt>sizeof</tt> returns the size (in bytes) of a cvalue or a
+c type. Every cvalue has a size, but incomplete types will cause
+<tt>sizeof</tt> to raise an error. The function <tt>typeof</tt> returns
+the type of a cvalue.
+
+<p>
+You are probably wondering how 32- and 64-bit integers are constructed from
+femtoLisp's 30-bit integers. The answer is that larger integers are
+constructed from multiple Lisp numbers 16 bits at a time, in big-endian
+fashion. In fact, the larger numeric types are the only cvalues
+types whose constructors accept multiple arguments. Examples:
+<ul>
+<pre>
+(c-value 'int32 0xdead 0xbeef) ; make 0xdeadbeef
+(c-value 'uint64 0x1001 0x8000 0xffff) ; make 0x000010018000ffff
+</pre>
+</ul>
+As you can see, missing zeros are padded in from the left.
+
+
+<h2>8.3. Constructors</h2>
+
+For convenience, a specialized constructor is provided for each
+class of C type (primitives, pointer, array, struct, union, enum,
+and c-function).
+For example:
+<ul>
+<pre>
+(uint32 0xcafe 0xd00d)
+(int32 -4)
+(char #\w)
+(array 'int8 [1 1 2 3 5 8])
+</pre>
+</ul>
+
+These forms can be slightly less efficient than <tt>(c-value ...)</tt>
+because in many cases they will allocate a new type for the new value.
+For example, the fourth expression must create the type
+<tt>(array int8 6)</tt>.
+
+<p>
+Notice that calls to these constructors strongly resemble
+the types of the values they create. This relationship can be expressed
+formally as follows:
+
+<pre>
+(define (c-allocate type)
+ (if (atom type)
+ (apply (eval type) ())
+ (apply (eval (car type)) (cdr type))))
+</pre>
+
+This function produces an instance of the given type by
+invoking the appropriate constructor. Primitive types (whose representations
+are symbols) can be constructed with zero arguments. For other types,
+the only required arguments are those present in the type representation.
+Any arguments after those are initializers. Using
+<tt>(cdr type)</tt> as the argument list provides only required arguments,
+so the value you get will not be initialized.
+
+<p>
+The builtin <tt>c-value</tt> function is similar to this one, except that it
+lets you pass initializers.
+
+<p>
+Cvalue constructors are generally permissive; they do the best they
+can with whatever you pass in. For example:
+
+<ul>
+<pre>
+(c-value '(array int8 1)) ; ok, full type provided
+(c-value '(array int8)) ; error, no size information
+(c-value '(array int8) [0 1]) ; ok, size implied by initializer
+</pre>
+</ul>
+
+<p>
+ccopy, c2lisp
+
+<h2>8.4. Pointers, arrays, and strings</h2>
+
+Pointer types are provided for completeness and C interoperability, but
+they should not generally be used from Lisp. femtoLisp doesn't know
+anything about a pointer except the raw address and the (alleged) type of the
+value it points to. Arrays are much more useful. They behave like references
+as in C, but femtoLisp tracks their sizes and performs bounds checking.
+
+<p>
+Arrays are used to allocate strings. All strings share
+the incomplete array type <tt>(array char)</tt>:
+
+<pre>
+> (c-value '(array char) [#\h #\e #\l #\l #\o])
+"hello"
+
+> (sizeof that)
+5
+</pre>
+
+<tt>sizeof</tt> reveals that the size is known even though it is not
+reflected in the type (as is always the case with incomplete array types).
+
+<p>
+Since femtoLisp tracks the sizes of all values, there is no need for NUL
+terminators. Strings are just arrays of bytes, and may contain zero bytes
+throughout. However, C functions require zero-terminated strings. To
+solve this problem, femtoLisp allocates magic strings that actually have
+space for one more byte than they appear to. The hidden extra byte is
+always zero. This guarantees that a C function operating on the string
+will never overrun its allocated space.
+
+<p>
+Such magic strings are produced by double-quoted string literals, and by
+any explicit string-constructing function (such as <tt>string</tt>).
+
+<p>
+Unfortunately you still need to be careful, because it is possible to
+allocate a non-magic character array with no terminator. The "hello"
+string above is an example of this, since it was constructed from an
+explicit vector of characters.
+Such an array would cause problems if passed to a function expecting a
+C string.
+
+<p>
+deref
+
+<h2>8.5. Access</h2>
+
+cref,cset,byteref,byteset,ccopy
+
+<h2>8.6. Memory management concerns</h2>
+
+autorelease
+
+
+<h2>8.7. Guest functions</h2>
+
+Functions written in C but designed to operate on Lisp values are
+known here as "guest functions". Although they are foreign, they live in
+Lisp's house and so live by its rules. Guest functions are what you
+use to write interpreter extensions, for example to implement a function
+like <tt>assoc</tt> in C for performance.
+
+<p>
+Guest functions must have a particular signature:
+<pre>
+value_t func(value_t *args, uint32_t nargs);
+</pre>
+Guest functions must also be aware of the femtoLisp API and garbage
+collector.
+
+
+<h2>8.8. Native functions</h2>
+
+</body>
+</html>
binary files /dev/null b/femtolisp/site/flbanner.jpg differ
binary files /dev/null b/femtolisp/site/flbanner.xcf differ
binary files /dev/null b/femtolisp/site/flbanner2.jpg differ
binary files /dev/null b/femtolisp/site/home.gif differ
--- /dev/null
+++ b/femtolisp/site/index.html
@@ -1,0 +1,206 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
+<title>femtoLisp</title>
+</head>
+<body>
+<h1>femtoLisp</h1>
+<hr>
+femtoLisp is an elegant Lisp implementation. Its goal is to be a
+reasonably efficient and capable interpreter with the shortest, simplest
+code possible. As its name implies, it is small (10<sup>-15</sup>).
+Right now it is just 1000 lines of C (give or take). It would make a great
+teaching example, or a useful system anywhere a very small Lisp is wanted.
+It is also a useful basis for developing other interpreters or related
+languages.
+
+
+<h2>The language implemented</h2>
+
+femtoLisp tries to be a generic, simple Lisp dialect, influenced by McCarthy's
+original.
+
+<ul>
+<li>Types: cons, symbol, 30-bit integer, builtin function
+<li>Self-evaluating lambda, macro, and label forms
+<li>Full Common Lisp-style macros
+<li>Case-sensitive symbol names
+<li>Scheme-style evaluation rule where any expression may appear in head
+ position as long as it evaluates to a callable
+<li>Scheme-style formal argument lists (dotted lists for varargs)
+<li>Transparent closure representation <tt>(lambda args body . env)</tt>
+<li>A lambda body may contain only one form. Use explicit <tt>progn</tt> for
+ multiple forms. Included macros, however, allow <tt>defun</tt>,
+ <tt>let</tt>, etc. to accept multiple body forms.
+<li>Builtin function names are constants and cannot be redefined.
+<li>Symbols have one binding, as in Scheme.
+</ul>
+<b>Builtin special forms:</b><br>
+<tt>quote, cond, if, and, or, lambda, macro, label, while, progn, prog1</tt>
+<p>
+<b>Builtin functions:</b><br>
+<tt>eq, atom, not, symbolp, numberp, boundp, cons, car, cdr,
+ read, eval, print, load, set,
+ +, -, *, /, <, apply, rplaca, rplacd</tt>
+<p>
+<b>Included library functions and macros:</b><br>
+<tt>
+setq, setf, defmacro, defun, define, let, let*, labels, dotimes,
+macroexpand-1, macroexpand, backquote,
+
+null, consp, builtinp, self-evaluating-p, listp, eql, equal, every, any,
+when, unless,
+
+=, !=, >, <=, >=, compare, mod, abs, identity,
+
+list, list*, length, last, nthcdr, lastcdr, list-ref, reverse, nreverse,
+assoc, member, append, nconc, copy-list, copy-tree, revappend, nreconc,
+
+mapcar, filter, reduce, map-int,
+
+symbol-plist, set-symbol-plist, put, get
+</tt>
+<p>
+<a href="system.lsp">system.lsp</a>
+
+
+<h2>The implementation</h2>
+
+<ul>
+<li>Compacting copying garbage collector (<tt>O(1)</tt> in number of dead
+ objects)
+<li>Tagged pointers for efficient type checking and fast integers
+<li>Tail-recursive evaluator (tail calls use no stack space)
+<li>Minimally-consing <tt>apply</tt>
+<li>Interactive and script execution modes
+</ul>
+<p>
+<a href="lisp.c">lisp.c</a>
+
+
+<h2>femtoLisp2</h2>
+
+This version includes robust reading and printing capabilities for
+circular structures and escaped symbol names. It adds read and print support
+for the Common Lisp read-macros <tt>#., #n#,</tt> and <tt>#n=</tt>.
+This allows builtins to be printed in a readable fashion as e.g.
+"<tt>#.eq</tt>".
+<p>
+The net result is that the interpreter achieves a highly satisfying property
+of closure under I/O. In other words, every representable Lisp value can be
+read and printed.
+<p>
+The traditional builtin <tt>label</tt> provides a purely-functional,
+non-circular way
+to write an anonymous recursive function. In femtoLisp2 you can
+achieve the same effect "manually" using nothing more than the reader:
+<br>
+<tt>#0=(lambda (x) (if (<= x 0) 1 (* x (#0# (- x 1)))))</tt>
+<p>
+femtoLisp2 has the following extra features and optimizations:
+<ul>
+<li> builtin functions <tt>error, exit,</tt> and <tt>princ</tt>
+<li> read support for backquote expressions
+<li> delayed environment consing
+<li> collective allocation of cons chains
+</ul>
+Those two optimizations are a Big Deal.
+<p>
+<a href="lisp2.c">lisp2.c</a> (uses <a href="flutils.c">flutils.c</a>)
+
+
+<h2>Performance</h2>
+
+femtoLisp's performance is surprising. It is faster than most
+interpreters, and it is usually within a factor of 2-5 of compiled CLISP.
+
+<table border=1>
+<tr>
+<td colspan=3><center><b>solve 5 queens problem 100x</b></center></td>
+<tr>
+<td> <td>interpreted<td>compiled
+<tr>
+<td>CLISP <td>4.02 sec <td>0.68 sec
+<tr>
+<td>femtoLisp2<td>2.62 sec <td>2.03 sec**
+<tr>
+<td>femtoLisp <td>6.02 sec <td>5.64 sec**
+<tr>
+
+<td colspan=3><center><b>recursive fib(34)</b></center></td>
+<tr>
+<td> <td>interpreted<td>compiled
+<tr>
+<td>CLISP <td>23.12 sec <td>4.04 sec
+<tr>
+<td>femtoLisp2<td>4.71 sec <td>n/a
+<tr>
+<td>femtoLisp <td>7.25 sec <td>n/a
+<tr>
+
+</table>
+** femtoLisp is not a compiler; in this context "compiled" means macros
+were pre-expanded.
+
+
+<h2>"Installation"</h2>
+
+Here is a <a href="Makefile">Makefile</a>. Type <tt>make</tt> to build
+femtoLisp, <tt>make NAME=lisp2</tt> to build femtoLisp2.
+
+
+<h2>Tail recursion</h2>
+The femtoLisp evaluator is tail-recursive, following the idea in
+<a href="http://library.readscheme.org/servlets/cite.ss?pattern=Ste-76b">
+Lambda: The Ultimate Declarative</a> (should be required reading
+for all schoolchildren).
+<p>
+The femtoLisp source provides a simple concrete example showing why a function
+call is best viewed as a "renaming plus goto" rather than as a set of stack
+operations.
+<p>
+Here is the non-tail-recursive evaluator code to evaluate the body of a
+lambda (function), from <a href="lisp-nontail.c">lisp-nontail.c</a>:
+<pre>
+ PUSH(*lenv); // preserve environment on stack
+ lenv = &Stack[SP-1];
+ v = eval(*body, lenv);
+ POP();
+ return v;
+</pre>
+(Note that because of the copying garbage collector, values are referenced
+through relocatable handles.)
+<p>
+Superficially, the call to <tt>eval</tt> is not a tail call, because work
+remains after it returns—namely, popping the environment off the stack.
+In other words, the control stack must be saved and restored to allow us to
+eventually restore the environment stack. However, restoring the environment
+stack is the <i>only</i> work to be done. Yet after this point the old
+environment is not used! So restoring the environment stack isn't
+necessary, therefore restoring the control stack isn't either.
+<p>
+This perspective makes proper tail recursion seem like more than an
+alternate design or optimization. It seems more correct.
+<p>
+Here is the corrected, tail-recursive version of the code:
+<pre>
+ SP = saveSP; // restore stack completely
+ e = *body; // reassign arguments
+ *penv = *lenv;
+ goto eval_top;
+</pre>
+<tt>penv</tt> is a pointer to the old environment, which we overwrite.
+(Notice that the variable <tt>penv</tt> does not even appear in the first code
+example.)
+So where is the environment saved and restored, if not here? The answer
+is that the burden is shifted to the caller; a caller to <tt>eval</tt> must
+expect that its environment might be overwritten, and take steps to save it
+if it will be needed further after the call. In practice, this means
+the environment is saved and restored around the evaluation of
+arguments, rather than around function applications. Hence <tt>(f x)</tt>
+might be a tail call to <tt>f</tt>, but <tt>(+ y (f x))</tt> is not.
+
+</body>
+</html>
binary files /dev/null b/femtolisp/site/software.gif differ
binary files /dev/null b/femtolisp/site/source.gif differ
binary files /dev/null b/femtolisp/site/text.gif differ
--- /dev/null
+++ b/femtolisp/system.lsp
@@ -1,0 +1,466 @@
+; femtoLisp standard library
+; by Jeff Bezanson
+; Public Domain
+
+(set 'list (lambda args args))
+
+(set-syntax 'setq (lambda (name val)
+ (list set (list 'quote name) val)))
+
+; 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.
+(setq f-body (lambda (e)
+ (cond ((atom e) e)
+ ((eq (cdr e) ()) (car e))
+ (T (cons 'progn e)))))
+
+(set-syntax 'defmacro
+ (lambda (name args . body)
+ (list 'set-syntax (list 'quote name)
+ (list 'lambda args (f-body body)))))
+
+; support both CL defun and Scheme-style define
+(defmacro defun (name args . body)
+ (list 'setq name (list 'lambda args (f-body body))))
+
+(defmacro define (name . body)
+ (if (symbolp name)
+ (list 'setq name (car body))
+ (cons 'defun (cons (car name) (cons (cdr name) body)))))
+
+(defun identity (x) x)
+(setq null not)
+
+(defun map (f lst)
+ (if (atom lst) lst
+ (cons (f (car lst)) (map f (cdr lst)))))
+
+(defmacro let (binds . body)
+ (cons (list 'lambda
+ (map (lambda (c) (if (consp c) (car c) c)) binds)
+ (f-body body))
+ (map (lambda (c) (if (consp c) (cadr c) nil)) binds)))
+
+(defun nconc lsts
+ (cond ((null lsts) ())
+ ((null (cdr lsts)) (car lsts))
+ (T ((lambda (l d) (if (null l) d
+ (prog1 l
+ (while (consp (cdr l)) (set 'l (cdr l)))
+ (rplacd l d))))
+ (car lsts) (apply nconc (cdr lsts))))))
+
+(defun 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))))))
+
+(defun member (item lst)
+ (cond ((atom lst) ())
+ ((equal (car lst) item) lst)
+ (T (member item (cdr lst)))))
+
+(defun macrocallp (e) (and (symbolp (car e))
+ (symbol-syntax (car e))))
+
+(defun functionp (x)
+ (or (builtinp x)
+ (and (consp x) (eq (car x) 'lambda))))
+
+(defun macroexpand-1 (e)
+ (if (atom e) e
+ (let ((f (macrocallp e)))
+ (if f (apply f (cdr e))
+ e))))
+
+; convert to proper list, i.e. remove "dots", and append
+(defun append.2 (l tail)
+ (cond ((null l) tail)
+ ((atom l) (cons l tail))
+ (T (cons (car l) (append.2 (cdr l) tail)))))
+
+(define (cadr x) (car (cdr x)))
+
+(defun macroexpand (e)
+ ((label mexpand
+ (lambda (e env f)
+ (progn
+ (while (and (consp e)
+ (not (member (car e) env))
+ (set 'f (macrocallp e)))
+ (set 'e (apply f (cdr e))))
+ (if (and (consp e)
+ (not (eq (car e) 'quote)))
+ (let ((newenv
+ (if (and (eq (car e) 'lambda)
+ (consp (cdr e)))
+ (append.2 (cadr e) env)
+ env)))
+ (map (lambda (x) (mexpand x newenv nil)) e))
+ e))))
+ e nil nil))
+
+; uncomment this to macroexpand functions at definition time.
+; makes typical code ~25% faster, but only works for defun expressions
+; at the top level.
+(defmacro defun (name args . body)
+ (list 'setq name (list 'lambda args (macroexpand (f-body body)))))
+
+; same thing for macros. enabled by default because macros are usually
+; defined at the top level.
+(defmacro defmacro (name args . body)
+ (list 'set-syntax (list 'quote name)
+ (list 'lambda args (macroexpand (f-body body)))))
+
+(setq = equal)
+(setq eql equal)
+(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 (abs x) (if (< x 0) (- x) x))
+(setq K prog1) ; K combinator ;)
+(define (funcall f . args) (apply f args))
+(define (symbol-value sym) (eval sym))
+(define symbol-function symbol-value)
+(define (terpri) (princ "\n") nil)
+
+(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 (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))))
+
+(defun every (pred lst)
+ (or (atom lst)
+ (and (pred (car lst))
+ (every pred (cdr lst)))))
+
+(defun any (pred lst)
+ (and (consp lst)
+ (or (pred (car lst))
+ (any pred (cdr lst)))))
+
+(defun listp (a) (or (eq a ()) (consp a)))
+
+(defun nthcdr (n lst)
+ (if (<= n 0) lst
+ (nthcdr (- n 1) (cdr lst))))
+
+(defun list-ref (lst n)
+ (car (nthcdr n lst)))
+
+(defun list* l
+ (if (atom (cdr l))
+ (car l)
+ (cons (car l) (apply list* (cdr l)))))
+
+(defun nlist* l
+ (if (atom (cdr l))
+ (car l)
+ (rplacd l (apply nlist* (cdr l)))))
+
+(defun lastcdr (l)
+ (if (atom l) l
+ (lastcdr (cdr l))))
+
+(defun last (l)
+ (cond ((atom l) l)
+ ((atom (cdr l)) l)
+ (T (last (cdr l)))))
+
+(defun map! (f lst)
+ (prog1 lst
+ (while (consp lst)
+ (rplaca lst (f (car lst)))
+ (set 'lst (cdr lst)))))
+
+(defun mapcar (f . lsts)
+ ((label mapcar-
+ (lambda (lsts)
+ (cond ((null lsts) (f))
+ ((atom (car lsts)) (car lsts))
+ (T (cons (apply f (map car lsts))
+ (mapcar- (map cdr lsts)))))))
+ lsts))
+
+(defun transpose (M) (apply mapcar (cons list M)))
+
+(defun filter (pred lst)
+ (cond ((null lst) ())
+ ((pred (car lst)) (cons (car lst) (filter pred (cdr lst))))
+ (T (filter pred (cdr lst)))))
+
+(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 lst) (foldl cons nil lst))
+
+(defun reduce (f zero lst)
+ (if (null lst) zero
+ (reduce f (f zero (car lst)) (cdr lst))))
+
+(define (copy-list l)
+ (if (atom l) l
+ (cons (car l)
+ (copy-list (cdr l)))))
+(define (copy-tree l)
+ (if (atom l) l
+ (cons (copy-tree (car l))
+ (copy-tree (cdr l)))))
+
+(define (nreverse l)
+ (let ((prev nil))
+ (while (consp l)
+ (set 'l (prog1 (cdr l)
+ (rplacd l (prog1 prev
+ (set 'prev l))))))
+ prev))
+
+(defmacro let* (binds . body)
+ (cons (list 'lambda (map car binds)
+ (cons 'progn
+ (nconc (map (lambda (b) (cons 'setq b)) binds)
+ body)))
+ (map (lambda (x) nil) binds)))
+
+(defmacro labels (binds . body)
+ (cons (list 'lambda (map car binds)
+ (cons 'progn
+ (nconc (map (lambda (b)
+ (list 'setq (car b) (cons 'lambda (cdr b))))
+ binds)
+ body)))
+ (map (lambda (x) nil) binds)))
+
+(defmacro when (c . body) (list 'if c (f-body body) nil))
+(defmacro unless (c . body) (list 'if c nil (f-body body)))
+
+(defmacro dotimes (var . body)
+ (let ((v (car var))
+ (cnt (cadr var)))
+ (list 'let (list (list v 0))
+ (list 'while (list < v cnt)
+ (list prog1 (f-body body) (list 'setq v (list + v 1)))))))
+
+(defun map-int (f n)
+ (if (<= n 0)
+ ()
+ (let ((first (cons (f 0) nil)))
+ ((label map-int-
+ (lambda (acc i n)
+ (if (= i n)
+ first
+ (progn (rplacd acc (cons (f i) nil))
+ (map-int- (cdr acc) (+ i 1) n)))))
+ first 1 n))))
+
+(defun iota (n) (map-int identity n))
+
+(defun error args (raise (cons 'error args)))
+
+(defmacro throw (tag value) `(raise (list 'thrown-value ,tag ,value)))
+(defmacro catch (tag expr)
+ (let ((e (gensym)))
+ `(trycatch ,expr
+ (lambda (,e) (if (and (consp ,e)
+ (eq (car ,e) 'thrown-value)
+ (eq (cadr ,e) ,tag))
+ (caddr ,e)
+ (raise ,e))))))
+
+(defmacro unwind-protect (expr finally)
+ (let ((e (gensym)))
+ `(prog1 (trycatch ,expr
+ (lambda (,e) (progn ,finally (raise ,e))))
+ ,finally)))
+
+; (try expr
+; (catch (type-error e) . exprs)
+; (catch (io-error e) . exprs)
+; (catch (e) . exprs)
+; (finally . exprs))
+(defmacro try (expr . forms)
+ (let* ((e (gensym))
+ (reraised (gensym))
+ (final (f-body (cdr (or (assoc 'finally forms) '(())))))
+ (catches (filter (lambda (f) (eq (car f) 'catch)) forms))
+ (catchblock `(cond
+ ,.(map (lambda (catc)
+ (let* ((specific (cdr (cadr catc)))
+ (extype (caadr catc))
+ (var (if specific (car specific)
+ extype))
+ (todo (cddr catc)))
+ `(,(if specific
+ ; exception matching logic
+ `(or (eq ,e ',extype)
+ (and (consp ,e)
+ (eq (car ,e)
+ ',extype)))
+ T); (catch (e) ...), match anything
+ (let ((,var ,e)) (progn ,@todo)))))
+ catches)
+ (T (raise ,e))))) ; no matches, reraise
+ (if final
+ (if catches
+ ; form with both catch and finally
+ `(prog1 (trycatch ,expr
+ (lambda (,e)
+ (trycatch ,catchblock
+ (lambda (,reraised)
+ (progn ,final
+ (raise ,reraised))))))
+ ,final)
+ ; finally only; same as unwind-protect
+ `(prog1 (trycatch ,expr (lambda (,e)
+ (progn ,final (raise ,e))))
+ ,final))
+ ; catch, no finally
+ `(trycatch ,expr (lambda (,e) ,catchblock)))))
+
+; setf
+; expands (setf (place x ...) v) to (mutator (f x ...) v)
+; (mutator (identity x ...) v) is interpreted as (mutator x ... v)
+(setq *setf-place-list*
+ ; place mutator f
+ '((car rplaca identity)
+ (cdr rplacd identity)
+ (caar rplaca car)
+ (cadr rplaca cdr)
+ (cdar rplacd car)
+ (cddr rplacd cdr)
+ (caaar rplaca caar)
+ (caadr rplaca cadr)
+ (cadar rplaca cdar)
+ (caddr rplaca cddr)
+ (cdaar rplacd caar)
+ (cdadr rplacd cadr)
+ (cddar rplacd cdar)
+ (cdddr rplacd cddr)
+ (get put identity)
+ (aref aset identity)
+ (symbol-function set identity)
+ (symbol-value set identity)
+ (symbol-plist set-symbol-plist identity)
+ (symbol-syntax set-syntax identity)))
+
+(defun setf-place-mutator (place val)
+ (if (symbolp place)
+ (list 'setq place val)
+ (let ((mutator (assoc (car place) *setf-place-list*)))
+ (if (null mutator)
+ (error '|setf: unknown place | (car place))
+ (if (eq (caddr mutator) 'identity)
+ (cons (cadr mutator) (append (cdr place) (list val)))
+ (list (cadr mutator)
+ (cons (caddr mutator) (cdr place))
+ val))))))
+
+(defmacro setf args
+ (f-body
+ ((label setf-
+ (lambda (args)
+ (if (null args)
+ nil
+ (cons (setf-place-mutator (car args) (cadr args))
+ (setf- (cddr args))))))
+ args)))
+
+(defun revappend (l1 l2) (nconc (reverse l1) l2))
+(defun nreconc (l1 l2) (nconc (nreverse l1) l2))
+
+(defun list-to-vector (l) (apply vector l))
+(defun vector-to-list (v)
+ (let ((i (- (length v) 1))
+ (l nil))
+ (while (>= i 0)
+ (setq l (cons (aref v i) l))
+ (setq i (- i 1)))
+ l))
+
+(defun self-evaluating-p (x)
+ (or (eq x nil)
+ (eq x T)
+ (and (atom x)
+ (not (symbolp x)))))
+
+; backquote
+(defmacro backquote (x) (bq-process x))
+
+(defun splice-form-p (x)
+ (or (and (consp x) (or (eq (car x) '*comma-at*)
+ (eq (car x) '*comma-dot*)))
+ (eq x '*comma*)))
+
+(defun bq-process (x)
+ (cond ((self-evaluating-p x)
+ (if (vectorp x)
+ (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))
+ ((eq (car x) 'backquote) (bq-process (bq-process (cadr x))))
+ ((eq (car x) '*comma*) (cadr x))
+ ((not (any splice-form-p x))
+ (let ((lc (lastcdr x))
+ (forms (map bq-bracket1 x)))
+ (if (null lc)
+ (cons 'list forms)
+ (nconc (cons 'nlist* forms) (list (bq-process lc))))))
+ (T (let ((p x) (q ()))
+ (while (and (consp p)
+ (not (eq (car p) '*comma*)))
+ (setq q (cons (bq-bracket (car p)) q))
+ (setq p (cdr p)))
+ (let ((forms
+ (cond ((consp 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)))))))
+
+(defun bq-bracket (x)
+ (cond ((atom x) (list cons (bq-process x) nil))
+ ((eq (car x) '*comma*) (list cons (cadr x) nil))
+ ((eq (car x) '*comma-at*) (list 'copy-list (cadr x)))
+ ((eq (car x) '*comma-dot*) (cadr x))
+ (T (list cons (bq-process x) nil))))
+
+; bracket without splicing
+(defun bq-bracket1 (x)
+ (if (and (consp x) (eq (car x) '*comma*))
+ (cadr x)
+ (bq-process x)))
+
+(defmacro assert (expr) `(if ,expr T (raise '(assert-failed ,expr))))
+
+(defmacro time (expr)
+ (let ((t0 (gensym)))
+ `(let ((,t0 (time.now)))
+ (prog1
+ ,expr
+ (princ "Elapsed time: " (- (time.now) ,t0) " seconds\n")))))
--- /dev/null
+++ b/femtolisp/tcolor.lsp
@@ -1,0 +1,11 @@
+; color for performance
+
+(load "color.lsp")
+
+; 100x color 5 queens
+(setq Q (generate-5x5-pairs))
+(defun ct ()
+ (setq C (color-pairs Q '(a b c d e)))
+ (dotimes (n 99) (color-pairs Q '(a b c d e))))
+(time (ct))
+(print C)
--- /dev/null
+++ b/femtolisp/test.lsp
@@ -1,0 +1,194 @@
+; make label self-evaluating, but evaluating the lambda in the process
+;(defmacro labl (name f)
+; (list list ''labl (list 'quote name) f))
+
+(defmacro labl (name f)
+ `(let (,name) (set ',name ,f)))
+
+;(define (reverse lst)
+; ((label rev-help (lambda (lst result)
+; (if (null lst) result
+; (rev-help (cdr lst) (cons (car lst) result)))))
+; lst nil))
+
+(define (append- . lsts)
+ ((label append-h
+ (lambda (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) (append-h (cdr lsts)))))))
+ lsts))
+
+;(princ 'Hello '| | 'world! "\n")
+;(filter (lambda (x) (not (< x 0))) '(1 -1 -2 5 10 -8 0))
+(define (fib n) (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
+;(princ (time (fib 34)) "\n")
+;(dotimes (i 20000) (map-int (lambda (x) (list 'quote x)) 8))
+;(dotimes (i 40000) (append '(a b) '(1 2 3 4) nil '(c) nil '(5 6)))
+;(dotimes (i 80000) (list 1 2 3 4 5))
+;(setq a (map-int identity 10000))
+;(dotimes (i 200) (rfoldl cons nil a))
+
+; iterative filter
+(defun ifilter (pred lst)
+ ((label f (lambda (accum lst)
+ (cond ((null lst) (nreverse accum))
+ ((not (pred (car lst))) (f accum (cdr lst)))
+ (T (f (cons (car lst) accum) (cdr lst))))))
+ nil lst))
+
+(defun sort (l)
+ (if (or (null l) (null (cdr l))) l
+ (let ((piv (car l)))
+ (nconc (sort (filter (lambda (x) (<= x piv)) (cdr l)))
+ (list piv)
+ (sort (filter (lambda (x) (> x piv)) (cdr l)))))))
+
+;(setq r (map-int (lambda (x) (mod (+ (* x 9421) 12345) 1024)) 1000))
+;(sort r)
+
+(defmacro dotimes (var . body)
+ (let ((v (car var))
+ (cnt (cadr var)))
+ `(let ((,v 0))
+ (while (< ,v ,cnt)
+ (prog1
+ ,(f-body body)
+ (setq ,v (+ ,v 1)))))))
+
+(defmacro labl (name fn)
+ (list (list lambda (cons name nil) (list 'setq name fn)) nil))
+
+;(dotimes (n 5000) (macroexpand '(dotimes (i 100) body1 body2)))
+
+(define (square x) (* x x))
+(define (evenp x) (= x (* (/ x 2) 2)))
+(define (expt b p)
+ (cond ((= p 0) 1)
+ ((= b 0) 0)
+ ((evenp p) (square (expt b (/ p 2))))
+ (T (* b (expt b (- p 1))))))
+
+(define (gcd a b)
+ (cond ((= a 0) b)
+ ((= b 0) a)
+ ((< a b) (gcd a (- b a)))
+ (T (gcd b (- a b)))))
+
+; like eval-when-compile
+(defmacro literal (expr)
+ (let ((v (eval expr)))
+ (if (self-evaluating-p v) v (list quote v))))
+
+(defun cardepth (l)
+ (if (atom l) 0
+ (+ 1 (cardepth (car l)))))
+
+(defun nestlist (f zero n)
+ (if (<= n 0) ()
+ (cons zero (nestlist f (f zero) (- n 1)))))
+
+(defun mapl (f . lsts)
+ ((label mapl-
+ (lambda (lsts)
+ (if (null (car lsts)) ()
+ (progn (apply f lsts) (mapl- (map cdr lsts))))))
+ lsts))
+
+; test to see if a symbol begins with :
+(defun keywordp (s)
+ (and (>= s '|:|) (<= s '|:~|)))
+
+; swap the cars and cdrs of every cons in a structure
+(defun swapad (c)
+ (if (atom c) c
+ (rplacd c (K (swapad (car c))
+ (rplaca c (swapad (cdr c)))))))
+
+(defun without (x l)
+ (filter (lambda (e) (not (eq e x))) l))
+
+(defun conscount (c)
+ (if (consp c) (+ 1
+ (conscount (car c))
+ (conscount (cdr c)))
+ 0))
+
+; _ Welcome to
+; (_ _ _ |_ _ | . _ _ 2
+; | (-||||_(_)|__|_)|_)
+; ==================|==
+
+;[` _ ,_ |- | . _ 2
+;| (/_||||_()|_|_\|)
+; |
+
+(defmacro while- (test . forms)
+ `((label -loop- (lambda ()
+ (if ,test
+ (progn ,@forms
+ (-loop-))
+ nil)))))
+
+; this would be a cool use of thunking to handle 'finally' clauses, but
+; this code doesn't work in the case where the user manually re-raises
+; inside a catch block. one way to handle it would be to replace all
+; their uses of 'raise' with '*_try_finally_raise_*' which calls the thunk.
+; (try expr
+; (catch (TypeError e) . exprs)
+; (catch (IOError e) . exprs)
+; (finally . exprs))
+(defmacro try (expr . forms)
+ (let ((final (f-body (cdr (or (assoc 'finally forms) '(())))))
+ (body (foldr
+ ; create a function to check for and handle one exception
+ ; type, and pass off control to the next when no match
+ (lambda (catc next)
+ (let ((var (cadr (cadr catc)))
+ (extype (caadr catc))
+ (todo (f-body (cddr catc))))
+ `(lambda (,var)
+ (if (or (eq ,var ',extype)
+ (and (consp ,var)
+ (eq (car ,var) ',extype)))
+ ,todo
+ (,next ,var)))))
+
+ ; default function; no matches so re-raise
+ '(lambda (e) (progn (*_try_finally_thunk_*) (raise e)))
+
+ ; make list of catch forms
+ (filter (lambda (f) (eq (car f) 'catch)) forms))))
+ `(let ((*_try_finally_thunk_* (lambda () ,final)))
+ (prog1 (attempt ,expr ,body)
+ (*_try_finally_thunk_*)))))
+
+(defun map (f lst)
+ (if (atom lst) lst
+ (cons (funcall f (car lst)) (map f (cdr lst)))))
+
+(define Y
+ (lambda (f)
+ ((lambda (h)
+ (f (lambda (x) ((h h) x))))
+ (lambda (h)
+ (f (lambda (x) ((h h) x)))))))
+
+(defmacro debug ()
+ (let ((g (gensym)))
+ `(progn (princ "Debug REPL:\n")
+ (let ((,g (read)))
+ (while (not (eq ,g 'quit))
+ (prog1
+ (print (trycatch (apply '(macro x x) ,g)
+ identity))
+ (setq ,g (read))))))))
+
+(defun tt () (time (dotimes (i 500000) (* 0x1fffffff 1) )))
+(tt)
+(tt)
+(tt)
--- /dev/null
+++ b/femtolisp/tiny/Makefile
@@ -1,0 +1,22 @@
+CC = gcc
+
+NAME = lisp
+SRC = $(NAME).c
+EXENAME = $(NAME)
+
+FLAGS = -Wall -Wextra
+LIBS =
+
+DEBUGFLAGS = -g -DDEBUG $(FLAGS)
+SHIPFLAGS = -O3 -fomit-frame-pointer $(FLAGS)
+
+default: release
+
+debug: $(SRC)
+ $(CC) $(DEBUGFLAGS) $(SRC) -o $(EXENAME) $(LIBS)
+
+release: $(SRC)
+ $(CC) $(SHIPFLAGS) $(SRC) -o $(EXENAME) $(LIBS)
+
+clean:
+ rm -f $(EXENAME)
--- /dev/null
+++ b/femtolisp/tiny/eval1
@@ -1,0 +1,390 @@
+value_t eval_sexpr(value_t e, value_t *penv)
+{
+ value_t f, v, bind, headsym, asym, labl=0, *pv, *argsyms, *body, *lenv;
+ value_t *rest;
+ cons_t *c;
+ symbol_t *sym;
+ u_int32_t saveSP;
+ int i, nargs, noeval=0;
+ number_t s, n;
+
+ if (issymbol(e)) {
+ sym = (symbol_t*)ptr(e);
+ if (sym->constant != UNBOUND) return sym->constant;
+ v = *penv;
+ while (iscons(v)) {
+ bind = car_(v);
+ if (iscons(bind) && car_(bind) == e)
+ return cdr_(bind);
+ v = cdr_(v);
+ }
+ if ((v = sym->binding) == UNBOUND)
+ lerror("eval: error: variable %s has no value\n", sym->name);
+ return v;
+ }
+ if ((unsigned)(char*)&nargs < (unsigned)stack_bottom || SP>=(N_STACK-100))
+ lerror("eval: error: stack overflow\n");
+ saveSP = SP;
+ PUSH(e);
+ f = eval(car_(e), penv);
+ if (isbuiltin(f)) {
+ // handle builtin function
+ if (!isspecial(f)) {
+ // evaluate argument list, placing arguments on stack
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ while (iscons(v)) {
+ v = eval(car_(v), penv);
+ PUSH(v);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ }
+ apply_builtin:
+ nargs = SP - saveSP - 1;
+ switch (intval(f)) {
+ // special forms
+ case F_QUOTE:
+ v = cdr_(Stack[saveSP]);
+ if (!iscons(v))
+ lerror("quote: error: expected argument\n");
+ v = car_(v);
+ break;
+ case F_MACRO:
+ case F_LAMBDA:
+ v = Stack[saveSP];
+ if (*penv != NIL) {
+ // build a closure (lambda args body . env)
+ v = cdr_(v);
+ PUSH(car(v));
+ argsyms = &Stack[SP-1];
+ PUSH(car(cdr_(v)));
+ body = &Stack[SP-1];
+ v = cons_(intval(f)==F_LAMBDA ? &LAMBDA : &MACRO,
+ cons(argsyms, cons(body, penv)));
+ }
+ break;
+ case F_LABEL:
+ v = Stack[saveSP];
+ if (*penv != NIL) {
+ v = cdr_(v);
+ PUSH(car(v)); // name
+ pv = &Stack[SP-1];
+ PUSH(car(cdr_(v))); // function
+ body = &Stack[SP-1];
+ *body = eval(*body, penv); // evaluate lambda
+ v = cons_(&LABEL, cons(pv, cons(body, &NIL)));
+ }
+ break;
+ case F_IF:
+ v = car(cdr_(Stack[saveSP]));
+ if (eval(v, penv) != NIL)
+ v = car(cdr_(cdr_(Stack[saveSP])));
+ else
+ v = car(cdr(cdr_(cdr_(Stack[saveSP]))));
+ v = eval(v, penv);
+ break;
+ case F_COND:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ while (iscons(*pv)) {
+ c = tocons(car_(*pv), "cond");
+ if ((v=eval(c->car, penv)) != NIL) {
+ *pv = cdr_(car_(*pv));
+ // evaluate body forms
+ while (iscons(*pv)) {
+ v = eval(car_(*pv), penv);
+ *pv = cdr_(*pv);
+ }
+ break;
+ }
+ *pv = cdr_(*pv);
+ }
+ break;
+ case F_AND:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = T;
+ while (iscons(*pv)) {
+ if ((v=eval(car_(*pv), penv)) == NIL)
+ break;
+ *pv = cdr_(*pv);
+ }
+ break;
+ case F_OR:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ while (iscons(*pv)) {
+ if ((v=eval(car_(*pv), penv)) != NIL)
+ break;
+ *pv = cdr_(*pv);
+ }
+ break;
+ case F_WHILE:
+ PUSH(car(cdr(cdr_(Stack[saveSP]))));
+ body = &Stack[SP-1];
+ Stack[saveSP] = car_(cdr_(Stack[saveSP]));
+ value_t *cond = &Stack[saveSP];
+ PUSH(NIL); pv = &Stack[SP-1];
+ while (eval(*cond, penv) != NIL)
+ *pv = eval(*body, penv);
+ v = *pv;
+ break;
+ case F_PROGN:
+ // return last arg
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ while (iscons(*pv)) {
+ v = eval(car_(*pv), penv);
+ *pv = cdr_(*pv);
+ }
+ break;
+
+ // ordinary functions
+ case F_SET:
+ argcount("set", nargs, 2);
+ e = Stack[SP-2];
+ v = *penv;
+ while (iscons(v)) {
+ bind = car_(v);
+ if (iscons(bind) && car_(bind) == e) {
+ cdr_(bind) = (v=Stack[SP-1]);
+ SP=saveSP; return v;
+ }
+ v = cdr_(v);
+ }
+ tosymbol(e, "set")->binding = (v=Stack[SP-1]);
+ break;
+ case F_BOUNDP:
+ argcount("boundp", nargs, 1);
+ if (tosymbol(Stack[SP-1], "boundp")->binding == UNBOUND)
+ v = NIL;
+ else
+ v = T;
+ break;
+ case F_EQ:
+ argcount("eq", nargs, 2);
+ v = ((Stack[SP-2] == Stack[SP-1]) ? T : NIL);
+ break;
+ case F_CONS:
+ argcount("cons", nargs, 2);
+ v = mk_cons();
+ car_(v) = Stack[SP-2];
+ cdr_(v) = Stack[SP-1];
+ break;
+ case F_CAR:
+ argcount("car", nargs, 1);
+ v = car(Stack[SP-1]);
+ break;
+ case F_CDR:
+ argcount("cdr", nargs, 1);
+ v = cdr(Stack[SP-1]);
+ break;
+ case F_RPLACA:
+ argcount("rplaca", nargs, 2);
+ car(v=Stack[SP-2]) = Stack[SP-1];
+ break;
+ case F_RPLACD:
+ argcount("rplacd", nargs, 2);
+ cdr(v=Stack[SP-2]) = Stack[SP-1];
+ break;
+ case F_ATOM:
+ argcount("atom", nargs, 1);
+ v = ((!iscons(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_SYMBOLP:
+ argcount("symbolp", nargs, 1);
+ v = ((issymbol(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_NUMBERP:
+ argcount("numberp", nargs, 1);
+ v = ((isnumber(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_ADD:
+ s = 0;
+ for (i=saveSP+1; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "+");
+ s += n;
+ }
+ v = number(s);
+ break;
+ case F_SUB:
+ if (nargs < 1)
+ lerror("-: error: too few arguments\n");
+ i = saveSP+1;
+ s = (nargs==1) ? 0 : tonumber(Stack[i++], "-");
+ for (; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "-");
+ s -= n;
+ }
+ v = number(s);
+ break;
+ case F_MUL:
+ s = 1;
+ for (i=saveSP+1; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "*");
+ s *= n;
+ }
+ v = number(s);
+ break;
+ case F_DIV:
+ if (nargs < 1)
+ lerror("/: error: too few arguments\n");
+ i = saveSP+1;
+ s = (nargs==1) ? 1 : tonumber(Stack[i++], "/");
+ for (; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "/");
+ if (n == 0)
+ lerror("/: error: division by zero\n");
+ s /= n;
+ }
+ v = number(s);
+ break;
+ case F_LT:
+ argcount("<", nargs, 2);
+ if (tonumber(Stack[SP-2],"<") < tonumber(Stack[SP-1],"<"))
+ v = T;
+ else
+ v = NIL;
+ break;
+ case F_NOT:
+ argcount("not", nargs, 1);
+ v = ((Stack[SP-1] == NIL) ? T : NIL);
+ break;
+ case F_EVAL:
+ argcount("eval", nargs, 1);
+ v = eval(Stack[SP-1], &NIL);
+ break;
+ case F_PRINT:
+ for (i=saveSP+1; i < (int)SP; i++)
+ print(stdout, v=Stack[i]);
+ break;
+ case F_READ:
+ argcount("read", nargs, 0);
+ v = read_sexpr(stdin);
+ break;
+ case F_LOAD:
+ argcount("load", nargs, 1);
+ v = load_file(tosymbol(Stack[SP-1], "load")->name);
+ break;
+ case F_PROG1:
+ // return first arg
+ if (nargs < 1)
+ lerror("prog1: error: too few arguments\n");
+ v = Stack[saveSP+1];
+ break;
+ case F_APPLY:
+ // unpack a list onto the stack
+ argcount("apply", nargs, 2);
+ v = Stack[saveSP] = Stack[SP-1]; // second arg is new arglist
+ f = Stack[SP-2]; // first arg is new function
+ POPN(2); // pop apply's args
+ if (isbuiltin(f)) {
+ if (isspecial(f))
+ lerror("apply: error: cannot apply special operator "
+ "%s\n", builtin_names[intval(f)]);
+ while (iscons(v)) {
+ PUSH(car_(v));
+ v = cdr_(v);
+ }
+ goto apply_builtin;
+ }
+ noeval = 1;
+ goto apply_lambda;
+ }
+ SP = saveSP;
+ return v;
+ }
+ else {
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ apply_lambda:
+ if (iscons(f)) {
+ headsym = car_(f);
+ if (headsym == LABEL) {
+ // (label name (lambda ...)) behaves the same as the lambda
+ // alone, except with name bound to the whole label expression
+ labl = f;
+ f = car(cdr(cdr_(labl)));
+ headsym = car(f);
+ }
+ // apply lambda or macro expression
+ PUSH(cdr(cdr(cdr_(f))));
+ lenv = &Stack[SP-1];
+ PUSH(car_(cdr_(f)));
+ argsyms = &Stack[SP-1];
+ PUSH(car_(cdr_(cdr_(f))));
+ body = &Stack[SP-1];
+ if (labl) {
+ // add label binding to environment
+ PUSH(labl);
+ PUSH(car_(cdr_(labl)));
+ *lenv = cons_(cons(&Stack[SP-1], &Stack[SP-2]), lenv);
+ POPN(3);
+ v = Stack[saveSP]; // refetch arglist
+ }
+ if (headsym == MACRO)
+ noeval = 1;
+ else if (headsym != LAMBDA)
+ lerror("apply: error: head must be lambda, macro, or label\n");
+ // build a calling environment for the lambda
+ // the environment is the argument binds on top of the captured
+ // environment
+ while (iscons(v)) {
+ // bind args
+ if (!iscons(*argsyms)) {
+ if (*argsyms == NIL)
+ lerror("apply: error: too many arguments\n");
+ break;
+ }
+ asym = car_(*argsyms);
+ if (!issymbol(asym))
+ lerror("apply: error: formal argument not a symbol\n");
+ v = car_(v);
+ if (!noeval) v = eval(v, penv);
+ PUSH(v);
+ *lenv = cons_(cons(&asym, &Stack[SP-1]), lenv);
+ POPN(2);
+ *argsyms = cdr_(*argsyms);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ if (*argsyms != NIL) {
+ if (issymbol(*argsyms)) {
+ if (noeval) {
+ *lenv = cons_(cons(argsyms, &Stack[saveSP]), lenv);
+ }
+ else {
+ PUSH(NIL);
+ PUSH(NIL);
+ rest = &Stack[SP-1];
+ // build list of rest arguments
+ // we have to build it forwards, which is tricky
+ while (iscons(v)) {
+ v = eval(car_(v), penv);
+ PUSH(v);
+ v = cons_(&Stack[SP-1], &NIL);
+ POP();
+ if (iscons(*rest))
+ cdr_(*rest) = v;
+ else
+ Stack[SP-2] = v;
+ *rest = v;
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ *lenv = cons_(cons(argsyms, &Stack[SP-2]), lenv);
+ }
+ }
+ else if (iscons(*argsyms)) {
+ lerror("apply: error: too few arguments\n");
+ }
+ }
+ SP = saveSP; // free temporary stack space
+ PUSH(*lenv); // preserve environment on stack
+ lenv = &Stack[SP-1];
+ v = eval(*body, lenv);
+ POP();
+ // macro: evaluate expansion in the calling environment
+ if (headsym == MACRO)
+ return eval(v, penv);
+ return v;
+ }
+ type_error("apply", "function", f);
+ return NIL;
+}
--- /dev/null
+++ b/femtolisp/tiny/eval2
@@ -1,0 +1,407 @@
+value_t eval_sexpr(value_t e, value_t *penv)
+{
+ value_t f, v, bind, headsym, asym, labl=0, *pv, *argsyms, *body, *lenv;
+ value_t *rest;
+ cons_t *c;
+ symbol_t *sym;
+ u_int32_t saveSP;
+ int i, nargs, noeval=0;
+ number_t s, n;
+
+ if (issymbol(e)) {
+ sym = (symbol_t*)ptr(e);
+ if (sym->constant != UNBOUND) return sym->constant;
+ v = *penv;
+ while (iscons(v)) {
+ bind = car_(v);
+ if (iscons(bind) && car_(bind) == e)
+ return cdr_(bind);
+ v = cdr_(v);
+ }
+ if ((v = sym->binding) == UNBOUND)
+ lerror("eval: error: variable %s has no value\n", sym->name);
+ return v;
+ }
+ if ((unsigned)(char*)&nargs < (unsigned)stack_bottom || SP>=(N_STACK-100))
+ lerror("eval: error: stack overflow\n");
+ saveSP = SP;
+ PUSH(e);
+ f = eval(car_(e), penv);
+ if (isbuiltin(f)) {
+ // handle builtin function
+ if (!isspecial(f)) {
+ // evaluate argument list, placing arguments on stack
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ while (iscons(v)) {
+ v = eval(car_(v), penv);
+ PUSH(v);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ }
+ apply_builtin:
+ nargs = SP - saveSP - 1;
+ switch (intval(f)) {
+ // special forms
+ case F_QUOTE:
+ v = cdr_(Stack[saveSP]);
+ if (!iscons(v))
+ lerror("quote: error: expected argument\n");
+ v = car_(v);
+ break;
+ case F_MACRO:
+ case F_LAMBDA:
+ v = Stack[saveSP];
+ if (*penv != NIL) {
+ // build a closure (lambda args body . env)
+ v = cdr_(v);
+ PUSH(car(v));
+ argsyms = &Stack[SP-1];
+ PUSH(car(cdr_(v)));
+ body = &Stack[SP-1];
+ v = cons_(intval(f)==F_LAMBDA ? &LAMBDA : &MACRO,
+ cons(argsyms, cons(body, penv)));
+ }
+ break;
+ case F_LABEL:
+ v = Stack[saveSP];
+ if (*penv != NIL) {
+ v = cdr_(v);
+ PUSH(car(v)); // name
+ pv = &Stack[SP-1];
+ PUSH(car(cdr_(v))); // function
+ body = &Stack[SP-1];
+ *body = eval(*body, penv); // evaluate lambda
+ v = cons_(&LABEL, cons(pv, cons(body, &NIL)));
+ }
+ break;
+ case F_IF:
+ v = car(cdr_(Stack[saveSP]));
+ if (eval(v, penv) != NIL)
+ v = car(cdr_(cdr_(Stack[saveSP])));
+ else
+ v = car(cdr(cdr_(cdr_(Stack[saveSP]))));
+ v = eval(v, penv);
+ break;
+ case F_COND:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ while (iscons(*pv)) {
+ c = tocons(car_(*pv), "cond");
+ if ((v=eval(c->car, penv)) != NIL) {
+ *pv = cdr_(car_(*pv));
+ // evaluate body forms
+ while (iscons(*pv)) {
+ v = eval(car_(*pv), penv);
+ *pv = cdr_(*pv);
+ }
+ break;
+ }
+ *pv = cdr_(*pv);
+ }
+ break;
+ case F_AND:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = T;
+ while (iscons(*pv)) {
+ if ((v=eval(car_(*pv), penv)) == NIL)
+ break;
+ *pv = cdr_(*pv);
+ }
+ break;
+ case F_OR:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ while (iscons(*pv)) {
+ if ((v=eval(car_(*pv), penv)) != NIL)
+ break;
+ *pv = cdr_(*pv);
+ }
+ break;
+ case F_WHILE:
+ PUSH(car(cdr(cdr_(Stack[saveSP]))));
+ body = &Stack[SP-1];
+ Stack[saveSP] = car_(cdr_(Stack[saveSP]));
+ value_t *cond = &Stack[saveSP];
+ PUSH(NIL); pv = &Stack[SP-1];
+ while (eval(*cond, penv) != NIL)
+ *pv = eval(*body, penv);
+ v = *pv;
+ break;
+ case F_PROGN:
+ // return last arg
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ while (iscons(*pv)) {
+ v = eval(car_(*pv), penv);
+ *pv = cdr_(*pv);
+ }
+ break;
+
+ // ordinary functions
+ case F_SET:
+ argcount("set", nargs, 2);
+ e = Stack[SP-2];
+ v = *penv;
+ while (iscons(v)) {
+ bind = car_(v);
+ if (iscons(bind) && car_(bind) == e) {
+ cdr_(bind) = (v=Stack[SP-1]);
+ SP=saveSP; return v;
+ }
+ v = cdr_(v);
+ }
+ tosymbol(e, "set")->binding = (v=Stack[SP-1]);
+ break;
+ case F_BOUNDP:
+ argcount("boundp", nargs, 1);
+ if (tosymbol(Stack[SP-1], "boundp")->binding == UNBOUND)
+ v = NIL;
+ else
+ v = T;
+ break;
+ case F_EQ:
+ argcount("eq", nargs, 2);
+ v = ((Stack[SP-2] == Stack[SP-1]) ? T : NIL);
+ break;
+ case F_CONS:
+ argcount("cons", nargs, 2);
+ v = mk_cons();
+ car_(v) = Stack[SP-2];
+ cdr_(v) = Stack[SP-1];
+ break;
+ case F_CAR:
+ argcount("car", nargs, 1);
+ v = car(Stack[SP-1]);
+ break;
+ case F_CDR:
+ argcount("cdr", nargs, 1);
+ v = cdr(Stack[SP-1]);
+ break;
+ case F_RPLACA:
+ argcount("rplaca", nargs, 2);
+ car(v=Stack[SP-2]) = Stack[SP-1];
+ break;
+ case F_RPLACD:
+ argcount("rplacd", nargs, 2);
+ cdr(v=Stack[SP-2]) = Stack[SP-1];
+ break;
+ case F_ATOM:
+ argcount("atom", nargs, 1);
+ v = ((!iscons(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_CONSP:
+ argcount("consp", nargs, 1);
+ v = (iscons(Stack[SP-1]) ? T : NIL);
+ break;
+ case F_SYMBOLP:
+ argcount("symbolp", nargs, 1);
+ v = ((issymbol(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_NUMBERP:
+ argcount("numberp", nargs, 1);
+ v = ((isnumber(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_ADD:
+ s = 0;
+ for (i=saveSP+1; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "+");
+ s += n;
+ }
+ v = number(s);
+ break;
+ case F_SUB:
+ if (nargs < 1)
+ lerror("-: error: too few arguments\n");
+ i = saveSP+1;
+ s = (nargs==1) ? 0 : tonumber(Stack[i++], "-");
+ for (; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "-");
+ s -= n;
+ }
+ v = number(s);
+ break;
+ case F_MUL:
+ s = 1;
+ for (i=saveSP+1; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "*");
+ s *= n;
+ }
+ v = number(s);
+ break;
+ case F_DIV:
+ if (nargs < 1)
+ lerror("/: error: too few arguments\n");
+ i = saveSP+1;
+ s = (nargs==1) ? 1 : tonumber(Stack[i++], "/");
+ for (; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "/");
+ if (n == 0)
+ lerror("/: error: division by zero\n");
+ s /= n;
+ }
+ v = number(s);
+ break;
+ case F_LT:
+ argcount("<", nargs, 2);
+ if (tonumber(Stack[SP-2],"<") < tonumber(Stack[SP-1],"<"))
+ v = T;
+ else
+ v = NIL;
+ break;
+ case F_NOT:
+ argcount("not", nargs, 1);
+ v = ((Stack[SP-1] == NIL) ? T : NIL);
+ break;
+ case F_EVAL:
+ argcount("eval", nargs, 1);
+ v = eval(Stack[SP-1], &NIL);
+ break;
+ case F_PRINT:
+ for (i=saveSP+1; i < (int)SP; i++)
+ print(stdout, v=Stack[i], 0);
+ fprintf(stdout, "\n");
+ break;
+ case F_PRINC:
+ for (i=saveSP+1; i < (int)SP; i++)
+ print(stdout, v=Stack[i], 1);
+ break;
+ case F_READ:
+ argcount("read", nargs, 0);
+ v = read_sexpr(stdin);
+ break;
+ case F_LOAD:
+ argcount("load", nargs, 1);
+ v = load_file(tosymbol(Stack[SP-1], "load")->name);
+ break;
+ case F_EXIT:
+ exit(0);
+ break;
+ case F_ERROR:
+ for (i=saveSP+1; i < (int)SP; i++)
+ print(stderr, Stack[i], 1);
+ lerror("\n");
+ break;
+ case F_PROG1:
+ // return first arg
+ if (nargs < 1)
+ lerror("prog1: error: too few arguments\n");
+ v = Stack[saveSP+1];
+ break;
+ case F_APPLY:
+ // unpack a list onto the stack
+ argcount("apply", nargs, 2);
+ v = Stack[saveSP] = Stack[SP-1]; // second arg is new arglist
+ f = Stack[SP-2]; // first arg is new function
+ POPN(2); // pop apply's args
+ if (isbuiltin(f)) {
+ if (isspecial(f))
+ lerror("apply: error: cannot apply special operator "
+ "%s\n", builtin_names[intval(f)]);
+ while (iscons(v)) {
+ PUSH(car_(v));
+ v = cdr_(v);
+ }
+ goto apply_builtin;
+ }
+ noeval = 1;
+ goto apply_lambda;
+ }
+ SP = saveSP;
+ return v;
+ }
+ else {
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ apply_lambda:
+ if (iscons(f)) {
+ headsym = car_(f);
+ if (headsym == LABEL) {
+ // (label name (lambda ...)) behaves the same as the lambda
+ // alone, except with name bound to the whole label expression
+ labl = f;
+ f = car(cdr(cdr_(labl)));
+ headsym = car(f);
+ }
+ // apply lambda or macro expression
+ PUSH(cdr(cdr(cdr_(f))));
+ lenv = &Stack[SP-1];
+ PUSH(car_(cdr_(f)));
+ argsyms = &Stack[SP-1];
+ PUSH(car_(cdr_(cdr_(f))));
+ body = &Stack[SP-1];
+ if (labl) {
+ // add label binding to environment
+ PUSH(labl);
+ PUSH(car_(cdr_(labl)));
+ *lenv = cons_(cons(&Stack[SP-1], &Stack[SP-2]), lenv);
+ POPN(3);
+ v = Stack[saveSP]; // refetch arglist
+ }
+ if (headsym == MACRO)
+ noeval = 1;
+ else if (headsym != LAMBDA)
+ lerror("apply: error: head must be lambda, macro, or label\n");
+ // build a calling environment for the lambda
+ // the environment is the argument binds on top of the captured
+ // environment
+ while (iscons(v)) {
+ // bind args
+ if (!iscons(*argsyms)) {
+ if (*argsyms == NIL)
+ lerror("apply: error: too many arguments\n");
+ break;
+ }
+ asym = car_(*argsyms);
+ if (!issymbol(asym))
+ lerror("apply: error: formal argument not a symbol\n");
+ v = car_(v);
+ if (!noeval) v = eval(v, penv);
+ PUSH(v);
+ *lenv = cons_(cons(&asym, &Stack[SP-1]), lenv);
+ POPN(2);
+ *argsyms = cdr_(*argsyms);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ if (*argsyms != NIL) {
+ if (issymbol(*argsyms)) {
+ if (noeval) {
+ *lenv = cons_(cons(argsyms, &Stack[saveSP]), lenv);
+ }
+ else {
+ PUSH(NIL);
+ PUSH(NIL);
+ rest = &Stack[SP-1];
+ // build list of rest arguments
+ // we have to build it forwards, which is tricky
+ while (iscons(v)) {
+ v = eval(car_(v), penv);
+ PUSH(v);
+ v = cons_(&Stack[SP-1], &NIL);
+ POP();
+ if (iscons(*rest))
+ cdr_(*rest) = v;
+ else
+ Stack[SP-2] = v;
+ *rest = v;
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ *lenv = cons_(cons(argsyms, &Stack[SP-2]), lenv);
+ }
+ }
+ else if (iscons(*argsyms)) {
+ lerror("apply: error: too few arguments\n");
+ }
+ }
+ SP = saveSP; // free temporary stack space
+ PUSH(*lenv); // preserve environment on stack
+ lenv = &Stack[SP-1];
+ v = eval(*body, lenv);
+ POP();
+ // macro: evaluate expansion in the calling environment
+ if (headsym == MACRO)
+ return eval(v, penv);
+ return v;
+ }
+ type_error("apply", "function", f);
+ return NIL;
+}
--- /dev/null
+++ b/femtolisp/tiny/evalt
@@ -1,0 +1,443 @@
+value_t eval_sexpr(value_t e, value_t *penv)
+{
+ value_t f, v, bind, headsym, asym, labl=0, *pv, *argsyms, *body, *lenv;
+ value_t *rest;
+ cons_t *c;
+ symbol_t *sym;
+ u_int32_t saveSP;
+ int i, nargs, noeval=0;
+ number_t s, n;
+
+ eval_top:
+ if (issymbol(e)) {
+ sym = (symbol_t*)ptr(e);
+ if (sym->constant != UNBOUND) return sym->constant;
+ v = *penv;
+ while (iscons(v)) {
+ bind = car_(v);
+ if (iscons(bind) && car_(bind) == e)
+ return cdr_(bind);
+ v = cdr_(v);
+ }
+ if ((v = sym->binding) == UNBOUND)
+ lerror("eval: error: variable %s has no value\n", sym->name);
+ return v;
+ }
+ if ((unsigned)(char*)&nargs < (unsigned)stack_bottom || SP>=(N_STACK-100))
+ lerror("eval: error: stack overflow\n");
+ saveSP = SP;
+ PUSH(e);
+ PUSH(*penv);
+ f = eval(car_(e), penv);
+ *penv = Stack[saveSP+1];
+ if (isbuiltin(f)) {
+ // handle builtin function
+ if (!isspecial(f)) {
+ // evaluate argument list, placing arguments on stack
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ while (iscons(v)) {
+ v = eval(car_(v), penv);
+ *penv = Stack[saveSP+1];
+ PUSH(v);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ }
+ apply_builtin:
+ nargs = SP - saveSP - 2;
+ switch (intval(f)) {
+ // special forms
+ case F_QUOTE:
+ v = cdr_(Stack[saveSP]);
+ if (!iscons(v))
+ lerror("quote: error: expected argument\n");
+ v = car_(v);
+ break;
+ case F_MACRO:
+ case F_LAMBDA:
+ v = Stack[saveSP];
+ if (*penv != NIL) {
+ // build a closure (lambda args body . env)
+ v = cdr_(v);
+ PUSH(car(v));
+ argsyms = &Stack[SP-1];
+ PUSH(car(cdr_(v)));
+ body = &Stack[SP-1];
+ v = cons_(intval(f)==F_LAMBDA ? &LAMBDA : &MACRO,
+ cons(argsyms, cons(body, penv)));
+ }
+ break;
+ case F_LABEL:
+ v = Stack[saveSP];
+ if (*penv != NIL) {
+ v = cdr_(v);
+ PUSH(car(v)); // name
+ pv = &Stack[SP-1];
+ PUSH(car(cdr_(v))); // function
+ body = &Stack[SP-1];
+ *body = eval(*body, penv); // evaluate lambda
+ v = cons_(&LABEL, cons(pv, cons(body, &NIL)));
+ }
+ break;
+ case F_IF:
+ v = car(cdr_(Stack[saveSP]));
+ if (eval(v, penv) != NIL)
+ v = car(cdr_(cdr_(Stack[saveSP])));
+ else
+ v = car(cdr(cdr_(cdr_(Stack[saveSP]))));
+ tail_eval(v, Stack[saveSP+1]);
+ break;
+ case F_COND:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ while (iscons(*pv)) {
+ c = tocons(car_(*pv), "cond");
+ v = eval(c->car, penv);
+ *penv = Stack[saveSP+1];
+ if (v != NIL) {
+ *pv = cdr_(car_(*pv));
+ // evaluate body forms
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ v = eval(car_(*pv), penv);
+ *penv = Stack[saveSP+1];
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv), *penv);
+ }
+ break;
+ }
+ *pv = cdr_(*pv);
+ }
+ break;
+ case F_AND:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = T;
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ if ((v=eval(car_(*pv), penv)) == NIL) {
+ SP = saveSP; return NIL;
+ }
+ *penv = Stack[saveSP+1];
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv), *penv);
+ }
+ break;
+ case F_OR:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ if ((v=eval(car_(*pv), penv)) != NIL) {
+ SP = saveSP; return v;
+ }
+ *penv = Stack[saveSP+1];
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv), *penv);
+ }
+ break;
+ case F_WHILE:
+ PUSH(car(cdr(cdr_(Stack[saveSP]))));
+ body = &Stack[SP-1];
+ Stack[saveSP] = car_(cdr_(Stack[saveSP]));
+ value_t *cond = &Stack[saveSP];
+ PUSH(NIL); pv = &Stack[SP-1];
+ while (eval(*cond, penv) != NIL) {
+ *penv = Stack[saveSP+1];
+ *pv = eval(*body, penv);
+ *penv = Stack[saveSP+1];
+ }
+ v = *pv;
+ break;
+ case F_PROGN:
+ // return last arg
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ v = eval(car_(*pv), penv);
+ *penv = Stack[saveSP+1];
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv), *penv);
+ }
+ break;
+
+ // ordinary functions
+ case F_SET:
+ argcount("set", nargs, 2);
+ e = Stack[SP-2];
+ v = *penv;
+ while (iscons(v)) {
+ bind = car_(v);
+ if (iscons(bind) && car_(bind) == e) {
+ cdr_(bind) = (v=Stack[SP-1]);
+ SP=saveSP; return v;
+ }
+ v = cdr_(v);
+ }
+ tosymbol(e, "set")->binding = (v=Stack[SP-1]);
+ break;
+ case F_BOUNDP:
+ argcount("boundp", nargs, 1);
+ if (tosymbol(Stack[SP-1], "boundp")->binding == UNBOUND)
+ v = NIL;
+ else
+ v = T;
+ break;
+ case F_EQ:
+ argcount("eq", nargs, 2);
+ v = ((Stack[SP-2] == Stack[SP-1]) ? T : NIL);
+ break;
+ case F_CONS:
+ argcount("cons", nargs, 2);
+ v = mk_cons();
+ car_(v) = Stack[SP-2];
+ cdr_(v) = Stack[SP-1];
+ break;
+ case F_CAR:
+ argcount("car", nargs, 1);
+ v = car(Stack[SP-1]);
+ break;
+ case F_CDR:
+ argcount("cdr", nargs, 1);
+ v = cdr(Stack[SP-1]);
+ break;
+ case F_RPLACA:
+ argcount("rplaca", nargs, 2);
+ car(v=Stack[SP-2]) = Stack[SP-1];
+ break;
+ case F_RPLACD:
+ argcount("rplacd", nargs, 2);
+ cdr(v=Stack[SP-2]) = Stack[SP-1];
+ break;
+ case F_ATOM:
+ argcount("atom", nargs, 1);
+ v = ((!iscons(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_CONSP:
+ argcount("consp", nargs, 1);
+ v = (iscons(Stack[SP-1]) ? T : NIL);
+ break;
+ case F_SYMBOLP:
+ argcount("symbolp", nargs, 1);
+ v = ((issymbol(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_NUMBERP:
+ argcount("numberp", nargs, 1);
+ v = ((isnumber(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_ADD:
+ s = 0;
+ for (i=saveSP+2; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "+");
+ s += n;
+ }
+ v = number(s);
+ break;
+ case F_SUB:
+ if (nargs < 1)
+ lerror("-: error: too few arguments\n");
+ i = saveSP+2;
+ s = (nargs==1) ? 0 : tonumber(Stack[i++], "-");
+ for (; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "-");
+ s -= n;
+ }
+ v = number(s);
+ break;
+ case F_MUL:
+ s = 1;
+ for (i=saveSP+2; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "*");
+ s *= n;
+ }
+ v = number(s);
+ break;
+ case F_DIV:
+ if (nargs < 1)
+ lerror("/: error: too few arguments\n");
+ i = saveSP+2;
+ s = (nargs==1) ? 1 : tonumber(Stack[i++], "/");
+ for (; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "/");
+ if (n == 0)
+ lerror("/: error: division by zero\n");
+ s /= n;
+ }
+ v = number(s);
+ break;
+ case F_LT:
+ argcount("<", nargs, 2);
+ if (tonumber(Stack[SP-2],"<") < tonumber(Stack[SP-1],"<"))
+ v = T;
+ else
+ v = NIL;
+ break;
+ case F_NOT:
+ argcount("not", nargs, 1);
+ v = ((Stack[SP-1] == NIL) ? T : NIL);
+ break;
+ case F_EVAL:
+ argcount("eval", nargs, 1);
+ v = Stack[SP-1];
+ tail_eval(v, NIL);
+ break;
+ case F_PRINT:
+ for (i=saveSP+2; i < (int)SP; i++)
+ print(stdout, v=Stack[i], 0);
+ fprintf(stdout, "\n");
+ break;
+ case F_PRINC:
+ for (i=saveSP+2; i < (int)SP; i++)
+ print(stdout, v=Stack[i], 1);
+ break;
+ case F_READ:
+ argcount("read", nargs, 0);
+ v = read_sexpr(stdin);
+ break;
+ case F_LOAD:
+ argcount("load", nargs, 1);
+ v = load_file(tosymbol(Stack[SP-1], "load")->name);
+ break;
+ case F_EXIT:
+ exit(0);
+ break;
+ case F_ERROR:
+ for (i=saveSP+2; i < (int)SP; i++)
+ print(stderr, Stack[i], 1);
+ lerror("\n");
+ break;
+ case F_PROG1:
+ // return first arg
+ if (nargs < 1)
+ lerror("prog1: error: too few arguments\n");
+ v = Stack[saveSP+2];
+ break;
+ case F_APPLY:
+ argcount("apply", nargs, 2);
+ v = Stack[saveSP] = Stack[SP-1]; // second arg is new arglist
+ f = Stack[SP-2]; // first arg is new function
+ POPN(2); // pop apply's args
+ if (isbuiltin(f)) {
+ if (isspecial(f))
+ lerror("apply: error: cannot apply special operator "
+ "%s\n", builtin_names[intval(f)]);
+ // unpack arglist onto the stack
+ while (iscons(v)) {
+ PUSH(car_(v));
+ v = cdr_(v);
+ }
+ goto apply_builtin;
+ }
+ noeval = 1;
+ goto apply_lambda;
+ }
+ SP = saveSP;
+ return v;
+ }
+ else {
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ apply_lambda:
+ if (iscons(f)) {
+ headsym = car_(f);
+ if (headsym == LABEL) {
+ // (label name (lambda ...)) behaves the same as the lambda
+ // alone, except with name bound to the whole label expression
+ labl = f;
+ f = car(cdr(cdr_(labl)));
+ headsym = car(f);
+ }
+ // apply lambda or macro expression
+ PUSH(cdr(cdr(cdr_(f))));
+ lenv = &Stack[SP-1];
+ PUSH(car_(cdr_(f)));
+ argsyms = &Stack[SP-1];
+ PUSH(car_(cdr_(cdr_(f))));
+ body = &Stack[SP-1];
+ if (labl) {
+ // add label binding to environment
+ PUSH(labl);
+ PUSH(car_(cdr_(labl)));
+ *lenv = cons_(cons(&Stack[SP-1], &Stack[SP-2]), lenv);
+ POPN(3);
+ v = Stack[saveSP]; // refetch arglist
+ }
+ if (headsym == MACRO)
+ noeval = 1;
+ else if (headsym != LAMBDA)
+ lerror("apply: error: head must be lambda, macro, or label\n");
+ // build a calling environment for the lambda
+ // the environment is the argument binds on top of the captured
+ // environment
+ while (iscons(v)) {
+ // bind args
+ if (!iscons(*argsyms)) {
+ if (*argsyms == NIL)
+ lerror("apply: error: too many arguments\n");
+ break;
+ }
+ asym = car_(*argsyms);
+ if (!issymbol(asym))
+ lerror("apply: error: formal argument not a symbol\n");
+ v = car_(v);
+ if (!noeval) {
+ v = eval(v, penv);
+ *penv = Stack[saveSP+1];
+ }
+ PUSH(v);
+ *lenv = cons_(cons(&asym, &Stack[SP-1]), lenv);
+ POPN(2);
+ *argsyms = cdr_(*argsyms);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ if (*argsyms != NIL) {
+ if (issymbol(*argsyms)) {
+ if (noeval) {
+ *lenv = cons_(cons(argsyms, &Stack[saveSP]), lenv);
+ }
+ else {
+ PUSH(NIL);
+ PUSH(NIL);
+ rest = &Stack[SP-1];
+ // build list of rest arguments
+ // we have to build it forwards, which is tricky
+ while (iscons(v)) {
+ v = eval(car_(v), penv);
+ *penv = Stack[saveSP+1];
+ PUSH(v);
+ v = cons_(&Stack[SP-1], &NIL);
+ POP();
+ if (iscons(*rest))
+ cdr_(*rest) = v;
+ else
+ Stack[SP-2] = v;
+ *rest = v;
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ *lenv = cons_(cons(argsyms, &Stack[SP-2]), lenv);
+ }
+ }
+ else if (iscons(*argsyms)) {
+ lerror("apply: error: too few arguments\n");
+ }
+ }
+ noeval = 0;
+ // macro: evaluate expansion in the calling environment
+ if (headsym == MACRO) {
+ SP = saveSP;
+ PUSH(*lenv);
+ lenv = &Stack[SP-1];
+ v = eval(*body, lenv);
+ tail_eval(v, *penv);
+ }
+ else {
+ tail_eval(*body, *lenv);
+ }
+ // not reached
+ }
+ type_error("apply", "function", f);
+ return NIL;
+}
--- /dev/null
+++ b/femtolisp/tiny/flutils.c
@@ -1,0 +1,119 @@
+u_int32_t *bitvector_resize(u_int32_t *b, size_t n)
+{
+ u_int32_t *p;
+ size_t sz = ((n+31)>>5) * 4;
+ p = realloc(b, sz);
+ if (p == NULL) return NULL;
+ memset(p, 0, sz);
+ return p;
+}
+
+u_int32_t *mk_bitvector(size_t n)
+{
+ return bitvector_resize(NULL, n);
+}
+
+void bitvector_set(u_int32_t *b, u_int32_t n, u_int32_t c)
+{
+ if (c)
+ b[n>>5] |= (1<<(n&31));
+ else
+ b[n>>5] &= ~(1<<(n&31));
+}
+
+u_int32_t bitvector_get(u_int32_t *b, u_int32_t n)
+{
+ return b[n>>5] & (1<<(n&31));
+}
+
+typedef struct {
+ size_t n, maxsize;
+ unsigned long *items;
+} ltable_t;
+
+void ltable_init(ltable_t *t, size_t n)
+{
+ t->n = 0;
+ t->maxsize = n;
+ t->items = (unsigned long*)malloc(n * sizeof(unsigned long));
+}
+
+void ltable_clear(ltable_t *t)
+{
+ t->n = 0;
+}
+
+void ltable_insert(ltable_t *t, unsigned long item)
+{
+ unsigned long *p;
+
+ if (t->n == t->maxsize) {
+ p = realloc(t->items, (t->maxsize*2)*sizeof(unsigned long));
+ if (p == NULL) return;
+ t->items = p;
+ t->maxsize *= 2;
+ }
+ t->items[t->n++] = item;
+}
+
+#define NOTFOUND ((int)-1)
+
+int ltable_lookup(ltable_t *t, unsigned long item)
+{
+ int i;
+ for(i=0; i < (int)t->n; i++)
+ if (t->items[i] == item)
+ return i;
+ return NOTFOUND;
+}
+
+void ltable_adjoin(ltable_t *t, unsigned long item)
+{
+ if (ltable_lookup(t, item) == NOTFOUND)
+ ltable_insert(t, item);
+}
+
+static const u_int32_t offsetsFromUTF8[6] = {
+ 0x00000000UL, 0x00003080UL, 0x000E2080UL,
+ 0x03C82080UL, 0xFA082080UL, 0x82082080UL
+};
+
+static const char trailingBytesForUTF8[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+int u8_seqlen(const char c)
+{
+ return trailingBytesForUTF8[(unsigned int)(unsigned char)c] + 1;
+}
+
+#define UEOF ((u_int32_t)EOF)
+
+u_int32_t u8_fgetc(FILE *f)
+{
+ int amt=0, sz, c;
+ u_int32_t ch=0;
+
+ c = fgetc(f);
+ if (c == EOF)
+ return UEOF;
+ ch = (u_int32_t)c;
+ amt = sz = u8_seqlen(ch);
+ while (--amt) {
+ ch <<= 6;
+ c = fgetc(f);
+ if (c == EOF)
+ return UEOF;
+ ch += (u_int32_t)c;
+ }
+ ch -= offsetsFromUTF8[sz-1];
+
+ return ch;
+}
binary files /dev/null b/femtolisp/tiny/lisp differ
--- /dev/null
+++ b/femtolisp/tiny/lisp-nontail.c
@@ -1,0 +1,975 @@
+/*
+ femtoLisp
+
+ a minimal interpreter for a minimal lisp dialect
+
+ this lisp dialect uses lexical scope and self-evaluating lambda.
+ it supports 30-bit integers, symbols, conses, and full macros.
+ it is case-sensitive.
+ it features a simple compacting copying garbage collector.
+ it uses a Scheme-style evaluation rule where any expression may appear in
+ head position as long as it evaluates to a function.
+ it uses Scheme-style varargs (dotted formal argument lists)
+ lambdas can have only 1 body expression; use (progn ...) for multiple
+ expressions. this is due to the closure representation
+ (lambda args body . env)
+
+ by Jeff Bezanson
+ Public Domain
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <sys/types.h>
+
+typedef u_int32_t value_t;
+typedef int32_t number_t;
+
+typedef struct {
+ value_t car;
+ value_t cdr;
+} cons_t;
+
+typedef struct _symbol_t {
+ value_t binding; // global value binding
+ value_t constant; // constant binding (used only for builtins)
+ struct _symbol_t *left;
+ struct _symbol_t *right;
+ char name[1];
+} symbol_t;
+
+#define TAG_NUM 0x0
+#define TAG_BUILTIN 0x1
+#define TAG_SYM 0x2
+#define TAG_CONS 0x3
+#define UNBOUND ((value_t)TAG_SYM) // an invalid symbol pointer
+#define tag(x) ((x)&0x3)
+#define ptr(x) ((void*)((x)&(~(value_t)0x3)))
+#define tagptr(p,t) (((value_t)(p)) | (t))
+#define number(x) ((value_t)((x)<<2))
+#define numval(x) (((number_t)(x))>>2)
+#define intval(x) (((int)(x))>>2)
+#define builtin(n) tagptr((((int)n)<<2), TAG_BUILTIN)
+#define iscons(x) (tag(x) == TAG_CONS)
+#define issymbol(x) (tag(x) == TAG_SYM)
+#define isnumber(x) (tag(x) == TAG_NUM)
+#define isbuiltin(x) (tag(x) == TAG_BUILTIN)
+// functions ending in _ are unsafe, faster versions
+#define car_(v) (((cons_t*)ptr(v))->car)
+#define cdr_(v) (((cons_t*)ptr(v))->cdr)
+#define car(v) (tocons((v),"car")->car)
+#define cdr(v) (tocons((v),"cdr")->cdr)
+#define set(s, v) (((symbol_t*)ptr(s))->binding = (v))
+#define setc(s, v) (((symbol_t*)ptr(s))->constant = (v))
+
+enum {
+ // special forms
+ F_QUOTE=0, F_COND, F_IF, F_AND, F_OR, F_WHILE, F_LAMBDA, F_MACRO, F_LABEL,
+ F_PROGN,
+ // functions
+ F_EQ, F_ATOM, F_CONS, F_CAR, F_CDR, F_READ, F_EVAL, F_PRINT, F_SET, F_NOT,
+ F_LOAD, F_SYMBOLP, F_NUMBERP, F_ADD, F_SUB, F_MUL, F_DIV, F_LT, F_PROG1,
+ F_APPLY, F_RPLACA, F_RPLACD, F_BOUNDP, N_BUILTINS
+};
+#define isspecial(v) (intval(v) <= (int)F_PROGN)
+
+static char *builtin_names[] =
+ { "quote", "cond", "if", "and", "or", "while", "lambda", "macro", "label",
+ "progn", "eq", "atom", "cons", "car", "cdr", "read", "eval", "print",
+ "set", "not", "load", "symbolp", "numberp", "+", "-", "*", "/", "<",
+ "prog1", "apply", "rplaca", "rplacd", "boundp" };
+
+static char *stack_bottom;
+#define PROCESS_STACK_SIZE (2*1024*1024)
+#define N_STACK 49152
+static value_t Stack[N_STACK];
+static u_int32_t SP = 0;
+#define PUSH(v) (Stack[SP++] = (v))
+#define POP() (Stack[--SP])
+#define POPN(n) (SP-=(n))
+
+value_t NIL, T, LAMBDA, MACRO, LABEL, QUOTE;
+
+value_t read_sexpr(FILE *f);
+void print(FILE *f, value_t v);
+value_t eval_sexpr(value_t e, value_t *penv);
+value_t load_file(char *fname);
+
+// error utilities ------------------------------------------------------------
+
+jmp_buf toplevel;
+
+void lerror(char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ longjmp(toplevel, 1);
+}
+
+void type_error(char *fname, char *expected, value_t got)
+{
+ fprintf(stderr, "%s: error: expected %s, got ", fname, expected);
+ print(stderr, got); lerror("\n");
+}
+
+// safe cast operators --------------------------------------------------------
+
+#define SAFECAST_OP(type,ctype,cnvt) \
+ctype to##type(value_t v, char *fname) \
+{ \
+ if (is##type(v)) \
+ return (ctype)cnvt(v); \
+ type_error(fname, #type, v); \
+ return (ctype)0; \
+}
+SAFECAST_OP(cons, cons_t*, ptr)
+SAFECAST_OP(symbol,symbol_t*,ptr)
+SAFECAST_OP(number,number_t, numval)
+
+// symbol table ---------------------------------------------------------------
+
+static symbol_t *symtab = NULL;
+
+static symbol_t *mk_symbol(char *str)
+{
+ symbol_t *sym;
+
+ sym = (symbol_t*)malloc(sizeof(symbol_t) + strlen(str));
+ sym->left = sym->right = NULL;
+ sym->constant = sym->binding = UNBOUND;
+ strcpy(&sym->name[0], str);
+ return sym;
+}
+
+static symbol_t **symtab_lookup(symbol_t **ptree, char *str)
+{
+ int x;
+
+ while(*ptree != NULL) {
+ x = strcmp(str, (*ptree)->name);
+ if (x == 0)
+ return ptree;
+ if (x < 0)
+ ptree = &(*ptree)->left;
+ else
+ ptree = &(*ptree)->right;
+ }
+ return ptree;
+}
+
+value_t symbol(char *str)
+{
+ symbol_t **pnode;
+
+ pnode = symtab_lookup(&symtab, str);
+ if (*pnode == NULL)
+ *pnode = mk_symbol(str);
+ return tagptr(*pnode, TAG_SYM);
+}
+
+// initialization -------------------------------------------------------------
+
+static unsigned char *fromspace;
+static unsigned char *tospace;
+static unsigned char *curheap;
+static unsigned char *lim;
+static u_int32_t heapsize = 64*1024;//bytes
+
+void lisp_init(void)
+{
+ int i;
+
+ fromspace = malloc(heapsize);
+ tospace = malloc(heapsize);
+ curheap = fromspace;
+ lim = curheap+heapsize-sizeof(cons_t);
+
+ NIL = symbol("nil"); setc(NIL, NIL);
+ T = symbol("t"); setc(T, T);
+ LAMBDA = symbol("lambda");
+ MACRO = symbol("macro");
+ LABEL = symbol("label");
+ QUOTE = symbol("quote");
+ for (i=0; i < (int)N_BUILTINS; i++)
+ setc(symbol(builtin_names[i]), builtin(i));
+ setc(symbol("princ"), builtin(F_PRINT));
+}
+
+// conses ---------------------------------------------------------------------
+
+void gc(void);
+
+static value_t mk_cons(void)
+{
+ cons_t *c;
+
+ if (curheap > lim)
+ gc();
+ c = (cons_t*)curheap;
+ curheap += sizeof(cons_t);
+ return tagptr(c, TAG_CONS);
+}
+
+static value_t cons_(value_t *pcar, value_t *pcdr)
+{
+ value_t c = mk_cons();
+ car_(c) = *pcar; cdr_(c) = *pcdr;
+ return c;
+}
+
+value_t *cons(value_t *pcar, value_t *pcdr)
+{
+ value_t c = mk_cons();
+ car_(c) = *pcar; cdr_(c) = *pcdr;
+ PUSH(c);
+ return &Stack[SP-1];
+}
+
+// collector ------------------------------------------------------------------
+
+static value_t relocate(value_t v)
+{
+ value_t a, d, nc;
+
+ if (!iscons(v))
+ return v;
+ if (car_(v) == UNBOUND)
+ return cdr_(v);
+ nc = mk_cons();
+ a = car_(v); d = cdr_(v);
+ car_(v) = UNBOUND; cdr_(v) = nc;
+ car_(nc) = relocate(a);
+ cdr_(nc) = relocate(d);
+ return nc;
+}
+
+static void trace_globals(symbol_t *root)
+{
+ while (root != NULL) {
+ root->binding = relocate(root->binding);
+ trace_globals(root->left);
+ root = root->right;
+ }
+}
+
+void gc(void)
+{
+ static int grew = 0;
+ unsigned char *temp;
+ u_int32_t i;
+
+ curheap = tospace;
+ lim = curheap+heapsize-sizeof(cons_t);
+
+ for (i=0; i < SP; i++)
+ Stack[i] = relocate(Stack[i]);
+ trace_globals(symtab);
+#ifdef VERBOSEGC
+ printf("gc found %d/%d live conses\n", (curheap-tospace)/8, heapsize/8);
+#endif
+ temp = tospace;
+ tospace = fromspace;
+ fromspace = temp;
+
+ // if we're using > 80% of the space, resize tospace so we have
+ // more space to fill next time. if we grew tospace last time,
+ // grow the other half of the heap this time to catch up.
+ if (grew || ((lim-curheap) < (int)(heapsize/5))) {
+ temp = realloc(tospace, grew ? heapsize : heapsize*2);
+ if (temp == NULL)
+ lerror("out of memory\n");
+ tospace = temp;
+ if (!grew)
+ heapsize*=2;
+ grew = !grew;
+ }
+ if (curheap > lim) // all data was live
+ gc();
+}
+
+// read -----------------------------------------------------------------------
+
+enum {
+ TOK_NONE, TOK_OPEN, TOK_CLOSE, TOK_DOT, TOK_QUOTE, TOK_SYM, TOK_NUM
+};
+
+static int symchar(char c)
+{
+ static char *special = "()';\\|";
+ return (!isspace(c) && !strchr(special, c));
+}
+
+static u_int32_t toktype = TOK_NONE;
+static value_t tokval;
+static char buf[256];
+
+static char nextchar(FILE *f)
+{
+ char c;
+ int ch;
+
+ do {
+ ch = fgetc(f);
+ if (ch == EOF)
+ return 0;
+ c = (char)ch;
+ if (c == ';') {
+ // single-line comment
+ do {
+ ch = fgetc(f);
+ if (ch == EOF)
+ return 0;
+ } while ((char)ch != '\n');
+ c = (char)ch;
+ }
+ } while (isspace(c));
+ return c;
+}
+
+static void take(void)
+{
+ toktype = TOK_NONE;
+}
+
+static void accumchar(char c, int *pi)
+{
+ buf[(*pi)++] = c;
+ if (*pi >= (int)(sizeof(buf)-1))
+ lerror("read: error: token too long\n");
+}
+
+static int read_token(FILE *f, char c)
+{
+ int i=0, ch, escaped=0;
+
+ ungetc(c, f);
+ while (1) {
+ ch = fgetc(f);
+ if (ch == EOF)
+ goto terminate;
+ c = (char)ch;
+ if (c == '|') {
+ escaped = !escaped;
+ }
+ else if (c == '\\') {
+ ch = fgetc(f);
+ if (ch == EOF)
+ goto terminate;
+ accumchar((char)ch, &i);
+ }
+ else if (!escaped && !symchar(c)) {
+ break;
+ }
+ else {
+ accumchar(c, &i);
+ }
+ }
+ ungetc(c, f);
+ terminate:
+ buf[i++] = '\0';
+ return i;
+}
+
+static u_int32_t peek(FILE *f)
+{
+ char c, *end;
+ number_t x;
+
+ if (toktype != TOK_NONE)
+ return toktype;
+ c = nextchar(f);
+ if (feof(f)) return TOK_NONE;
+ if (c == '(') {
+ toktype = TOK_OPEN;
+ }
+ else if (c == ')') {
+ toktype = TOK_CLOSE;
+ }
+ else if (c == '\'') {
+ toktype = TOK_QUOTE;
+ }
+ else if (isdigit(c) || c=='-') {
+ read_token(f, c);
+ if (buf[0] == '-' && !isdigit(buf[1])) {
+ toktype = TOK_SYM;
+ tokval = symbol(buf);
+ }
+ else {
+ x = strtol(buf, &end, 10);
+ if (*end != '\0')
+ lerror("read: error: invalid integer constant\n");
+ toktype = TOK_NUM;
+ tokval = number(x);
+ }
+ }
+ else {
+ read_token(f, c);
+ if (!strcmp(buf, ".")) {
+ toktype = TOK_DOT;
+ }
+ else {
+ toktype = TOK_SYM;
+ tokval = symbol(buf);
+ }
+ }
+ return toktype;
+}
+
+// build a list of conses. this is complicated by the fact that all conses
+// can move whenever a new cons is allocated. we have to refer to every cons
+// through a handle to a relocatable pointer (i.e. a pointer on the stack).
+static void read_list(FILE *f, value_t *pval)
+{
+ value_t c, *pc;
+ u_int32_t t;
+
+ PUSH(NIL);
+ pc = &Stack[SP-1]; // to keep track of current cons cell
+ t = peek(f);
+ while (t != TOK_CLOSE) {
+ if (feof(f))
+ lerror("read: error: unexpected end of input\n");
+ c = mk_cons(); car_(c) = cdr_(c) = NIL;
+ if (iscons(*pc))
+ cdr_(*pc) = c;
+ else
+ *pval = c;
+ *pc = c;
+ c = read_sexpr(f); // must be on separate lines due to undefined
+ car_(*pc) = c; // evaluation order
+
+ t = peek(f);
+ if (t == TOK_DOT) {
+ take();
+ c = read_sexpr(f);
+ cdr_(*pc) = c;
+ t = peek(f);
+ if (feof(f))
+ lerror("read: error: unexpected end of input\n");
+ if (t != TOK_CLOSE)
+ lerror("read: error: expected ')'\n");
+ }
+ }
+ take();
+ POP();
+}
+
+value_t read_sexpr(FILE *f)
+{
+ value_t v;
+
+ switch (peek(f)) {
+ case TOK_CLOSE:
+ take();
+ lerror("read: error: unexpected ')'\n");
+ case TOK_DOT:
+ take();
+ lerror("read: error: unexpected '.'\n");
+ case TOK_SYM:
+ case TOK_NUM:
+ take();
+ return tokval;
+ case TOK_QUOTE:
+ take();
+ v = read_sexpr(f);
+ PUSH(v);
+ v = cons_("E, cons(&Stack[SP-1], &NIL));
+ POPN(2);
+ return v;
+ case TOK_OPEN:
+ take();
+ PUSH(NIL);
+ read_list(f, &Stack[SP-1]);
+ return POP();
+ }
+ return NIL;
+}
+
+// print ----------------------------------------------------------------------
+
+void print(FILE *f, value_t v)
+{
+ value_t cd;
+
+ switch (tag(v)) {
+ case TAG_NUM: fprintf(f, "%d", numval(v)); break;
+ case TAG_SYM: fprintf(f, "%s", ((symbol_t*)ptr(v))->name); break;
+ case TAG_BUILTIN: fprintf(f, "#<builtin %s>",
+ builtin_names[intval(v)]); break;
+ case TAG_CONS:
+ fprintf(f, "(");
+ while (1) {
+ print(f, car_(v));
+ cd = cdr_(v);
+ if (!iscons(cd)) {
+ if (cd != NIL) {
+ fprintf(f, " . ");
+ print(f, cd);
+ }
+ fprintf(f, ")");
+ break;
+ }
+ fprintf(f, " ");
+ v = cd;
+ }
+ break;
+ }
+}
+
+// eval -----------------------------------------------------------------------
+
+static inline void argcount(char *fname, int nargs, int c)
+{
+ if (nargs != c)
+ lerror("%s: error: too %s arguments\n", fname, nargs<c ? "few":"many");
+}
+
+#define eval(e, env) ((tag(e)<0x2) ? (e) : eval_sexpr((e),env))
+
+value_t eval_sexpr(value_t e, value_t *penv)
+{
+ value_t f, v, bind, headsym, asym, labl=0, *pv, *argsyms, *body, *lenv;
+ value_t *rest;
+ cons_t *c;
+ symbol_t *sym;
+ u_int32_t saveSP;
+ int i, nargs, noeval=0;
+ number_t s, n;
+
+ if (issymbol(e)) {
+ sym = (symbol_t*)ptr(e);
+ if (sym->constant != UNBOUND) return sym->constant;
+ v = *penv;
+ while (iscons(v)) {
+ bind = car_(v);
+ if (iscons(bind) && car_(bind) == e)
+ return cdr_(bind);
+ v = cdr_(v);
+ }
+ if ((v = sym->binding) == UNBOUND)
+ lerror("eval: error: variable %s has no value\n", sym->name);
+ return v;
+ }
+ if ((unsigned)(char*)&nargs < (unsigned)stack_bottom || SP>=(N_STACK-100))
+ lerror("eval: error: stack overflow\n");
+ saveSP = SP;
+ PUSH(e);
+ f = eval(car_(e), penv);
+ if (isbuiltin(f)) {
+ // handle builtin function
+ if (!isspecial(f)) {
+ // evaluate argument list, placing arguments on stack
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ while (iscons(v)) {
+ v = eval(car_(v), penv);
+ PUSH(v);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ }
+ apply_builtin:
+ nargs = SP - saveSP - 1;
+ switch (intval(f)) {
+ // special forms
+ case F_QUOTE:
+ v = cdr_(Stack[saveSP]);
+ if (!iscons(v))
+ lerror("quote: error: expected argument\n");
+ v = car_(v);
+ break;
+ case F_MACRO:
+ case F_LAMBDA:
+ v = Stack[saveSP];
+ if (*penv != NIL) {
+ // build a closure (lambda args body . env)
+ v = cdr_(v);
+ PUSH(car(v));
+ argsyms = &Stack[SP-1];
+ PUSH(car(cdr_(v)));
+ body = &Stack[SP-1];
+ v = cons_(intval(f)==F_LAMBDA ? &LAMBDA : &MACRO,
+ cons(argsyms, cons(body, penv)));
+ }
+ break;
+ case F_LABEL:
+ v = Stack[saveSP];
+ if (*penv != NIL) {
+ v = cdr_(v);
+ PUSH(car(v)); // name
+ pv = &Stack[SP-1];
+ PUSH(car(cdr_(v))); // function
+ body = &Stack[SP-1];
+ *body = eval(*body, penv); // evaluate lambda
+ v = cons_(&LABEL, cons(pv, cons(body, &NIL)));
+ }
+ break;
+ case F_IF:
+ v = car(cdr_(Stack[saveSP]));
+ if (eval(v, penv) != NIL)
+ v = car(cdr_(cdr_(Stack[saveSP])));
+ else
+ v = car(cdr(cdr_(cdr_(Stack[saveSP]))));
+ v = eval(v, penv);
+ break;
+ case F_COND:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ while (iscons(*pv)) {
+ c = tocons(car_(*pv), "cond");
+ if ((v=eval(c->car, penv)) != NIL) {
+ *pv = cdr_(car_(*pv));
+ // evaluate body forms
+ while (iscons(*pv)) {
+ v = eval(car_(*pv), penv);
+ *pv = cdr_(*pv);
+ }
+ break;
+ }
+ *pv = cdr_(*pv);
+ }
+ break;
+ case F_AND:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = T;
+ while (iscons(*pv)) {
+ if ((v=eval(car_(*pv), penv)) == NIL)
+ break;
+ *pv = cdr_(*pv);
+ }
+ break;
+ case F_OR:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ while (iscons(*pv)) {
+ if ((v=eval(car_(*pv), penv)) != NIL)
+ break;
+ *pv = cdr_(*pv);
+ }
+ break;
+ case F_WHILE:
+ PUSH(car(cdr(cdr_(Stack[saveSP]))));
+ body = &Stack[SP-1];
+ Stack[saveSP] = car_(cdr_(Stack[saveSP]));
+ value_t *cond = &Stack[saveSP];
+ PUSH(NIL); pv = &Stack[SP-1];
+ while (eval(*cond, penv) != NIL)
+ *pv = eval(*body, penv);
+ v = *pv;
+ break;
+ case F_PROGN:
+ // return last arg
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ while (iscons(*pv)) {
+ v = eval(car_(*pv), penv);
+ *pv = cdr_(*pv);
+ }
+ break;
+
+ // ordinary functions
+ case F_SET:
+ argcount("set", nargs, 2);
+ e = Stack[SP-2];
+ v = *penv;
+ while (iscons(v)) {
+ bind = car_(v);
+ if (iscons(bind) && car_(bind) == e) {
+ cdr_(bind) = (v=Stack[SP-1]);
+ SP=saveSP; return v;
+ }
+ v = cdr_(v);
+ }
+ tosymbol(e, "set")->binding = (v=Stack[SP-1]);
+ break;
+ case F_BOUNDP:
+ argcount("boundp", nargs, 1);
+ if (tosymbol(Stack[SP-1], "boundp")->binding == UNBOUND)
+ v = NIL;
+ else
+ v = T;
+ break;
+ case F_EQ:
+ argcount("eq", nargs, 2);
+ v = ((Stack[SP-2] == Stack[SP-1]) ? T : NIL);
+ break;
+ case F_CONS:
+ argcount("cons", nargs, 2);
+ v = mk_cons();
+ car_(v) = Stack[SP-2];
+ cdr_(v) = Stack[SP-1];
+ break;
+ case F_CAR:
+ argcount("car", nargs, 1);
+ v = car(Stack[SP-1]);
+ break;
+ case F_CDR:
+ argcount("cdr", nargs, 1);
+ v = cdr(Stack[SP-1]);
+ break;
+ case F_RPLACA:
+ argcount("rplaca", nargs, 2);
+ car(v=Stack[SP-2]) = Stack[SP-1];
+ break;
+ case F_RPLACD:
+ argcount("rplacd", nargs, 2);
+ cdr(v=Stack[SP-2]) = Stack[SP-1];
+ break;
+ case F_ATOM:
+ argcount("atom", nargs, 1);
+ v = ((!iscons(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_SYMBOLP:
+ argcount("symbolp", nargs, 1);
+ v = ((issymbol(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_NUMBERP:
+ argcount("numberp", nargs, 1);
+ v = ((isnumber(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_ADD:
+ s = 0;
+ for (i=saveSP+1; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "+");
+ s += n;
+ }
+ v = number(s);
+ break;
+ case F_SUB:
+ if (nargs < 1)
+ lerror("-: error: too few arguments\n");
+ i = saveSP+1;
+ s = (nargs==1) ? 0 : tonumber(Stack[i++], "-");
+ for (; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "-");
+ s -= n;
+ }
+ v = number(s);
+ break;
+ case F_MUL:
+ s = 1;
+ for (i=saveSP+1; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "*");
+ s *= n;
+ }
+ v = number(s);
+ break;
+ case F_DIV:
+ if (nargs < 1)
+ lerror("/: error: too few arguments\n");
+ i = saveSP+1;
+ s = (nargs==1) ? 1 : tonumber(Stack[i++], "/");
+ for (; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "/");
+ if (n == 0)
+ lerror("/: error: division by zero\n");
+ s /= n;
+ }
+ v = number(s);
+ break;
+ case F_LT:
+ argcount("<", nargs, 2);
+ if (tonumber(Stack[SP-2],"<") < tonumber(Stack[SP-1],"<"))
+ v = T;
+ else
+ v = NIL;
+ break;
+ case F_NOT:
+ argcount("not", nargs, 1);
+ v = ((Stack[SP-1] == NIL) ? T : NIL);
+ break;
+ case F_EVAL:
+ argcount("eval", nargs, 1);
+ v = eval(Stack[SP-1], &NIL);
+ break;
+ case F_PRINT:
+ for (i=saveSP+1; i < (int)SP; i++)
+ print(stdout, v=Stack[i]);
+ break;
+ case F_READ:
+ argcount("read", nargs, 0);
+ v = read_sexpr(stdin);
+ break;
+ case F_LOAD:
+ argcount("load", nargs, 1);
+ v = load_file(tosymbol(Stack[SP-1], "load")->name);
+ break;
+ case F_PROG1:
+ // return first arg
+ if (nargs < 1)
+ lerror("prog1: error: too few arguments\n");
+ v = Stack[saveSP+1];
+ break;
+ case F_APPLY:
+ // unpack a list onto the stack
+ argcount("apply", nargs, 2);
+ v = Stack[saveSP] = Stack[SP-1]; // second arg is new arglist
+ f = Stack[SP-2]; // first arg is new function
+ POPN(2); // pop apply's args
+ if (isbuiltin(f)) {
+ if (isspecial(f))
+ lerror("apply: error: cannot apply special operator "
+ "%s\n", builtin_names[intval(f)]);
+ while (iscons(v)) {
+ PUSH(car_(v));
+ v = cdr_(v);
+ }
+ goto apply_builtin;
+ }
+ noeval = 1;
+ goto apply_lambda;
+ }
+ SP = saveSP;
+ return v;
+ }
+ else {
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ apply_lambda:
+ if (iscons(f)) {
+ headsym = car_(f);
+ if (headsym == LABEL) {
+ // (label name (lambda ...)) behaves the same as the lambda
+ // alone, except with name bound to the whole label expression
+ labl = f;
+ f = car(cdr(cdr_(labl)));
+ headsym = car(f);
+ }
+ // apply lambda or macro expression
+ PUSH(cdr(cdr(cdr_(f))));
+ lenv = &Stack[SP-1];
+ PUSH(car_(cdr_(f)));
+ argsyms = &Stack[SP-1];
+ PUSH(car_(cdr_(cdr_(f))));
+ body = &Stack[SP-1];
+ if (labl) {
+ // add label binding to environment
+ PUSH(labl);
+ PUSH(car_(cdr_(labl)));
+ *lenv = cons_(cons(&Stack[SP-1], &Stack[SP-2]), lenv);
+ POPN(3);
+ v = Stack[saveSP]; // refetch arglist
+ }
+ if (headsym == MACRO)
+ noeval = 1;
+ else if (headsym != LAMBDA)
+ lerror("apply: error: head must be lambda, macro, or label\n");
+ // build a calling environment for the lambda
+ // the environment is the argument binds on top of the captured
+ // environment
+ while (iscons(v)) {
+ // bind args
+ if (!iscons(*argsyms)) {
+ if (*argsyms == NIL)
+ lerror("apply: error: too many arguments\n");
+ break;
+ }
+ asym = car_(*argsyms);
+ if (!issymbol(asym))
+ lerror("apply: error: formal argument not a symbol\n");
+ v = car_(v);
+ if (!noeval) v = eval(v, penv);
+ PUSH(v);
+ *lenv = cons_(cons(&asym, &Stack[SP-1]), lenv);
+ POPN(2);
+ *argsyms = cdr_(*argsyms);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ if (*argsyms != NIL) {
+ if (issymbol(*argsyms)) {
+ if (noeval) {
+ *lenv = cons_(cons(argsyms, &Stack[saveSP]), lenv);
+ }
+ else {
+ PUSH(NIL);
+ PUSH(NIL);
+ rest = &Stack[SP-1];
+ // build list of rest arguments
+ // we have to build it forwards, which is tricky
+ while (iscons(v)) {
+ v = eval(car_(v), penv);
+ PUSH(v);
+ v = cons_(&Stack[SP-1], &NIL);
+ POP();
+ if (iscons(*rest))
+ cdr_(*rest) = v;
+ else
+ Stack[SP-2] = v;
+ *rest = v;
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ *lenv = cons_(cons(argsyms, &Stack[SP-2]), lenv);
+ }
+ }
+ else if (iscons(*argsyms)) {
+ lerror("apply: error: too few arguments\n");
+ }
+ }
+ SP = saveSP; // free temporary stack space
+ PUSH(*lenv); // preserve environment on stack
+ lenv = &Stack[SP-1];
+ v = eval(*body, lenv);
+ POP();
+ // macro: evaluate expansion in the calling environment
+ if (headsym == MACRO)
+ return eval(v, penv);
+ return v;
+ }
+ type_error("apply", "function", f);
+ return NIL;
+}
+
+// repl -----------------------------------------------------------------------
+
+static char *infile = NULL;
+
+value_t load_file(char *fname)
+{
+ value_t e, v=NIL;
+ char *lastfile = infile;
+ FILE *f = fopen(fname, "r");
+ infile = fname;
+ if (f == NULL) lerror("file not found\n");
+ while (1) {
+ e = read_sexpr(f);
+ if (feof(f)) break;
+ v = eval(e, &NIL);
+ }
+ infile = lastfile;
+ fclose(f);
+ return v;
+}
+
+int main(int argc, char* argv[])
+{
+ value_t v;
+
+ stack_bottom = ((char*)&v) - PROCESS_STACK_SIZE;
+ lisp_init();
+ if (setjmp(toplevel)) {
+ SP = 0;
+ fprintf(stderr, "\n");
+ if (infile) {
+ fprintf(stderr, "error loading file \"%s\"\n", infile);
+ infile = NULL;
+ }
+ goto repl;
+ }
+ load_file("system.lsp");
+ if (argc > 1) { load_file(argv[1]); return 0; }
+ printf("Welcome to femtoLisp ----------------------------------------------------------\n");
+ repl:
+ while (1) {
+ printf("> ");
+ v = read_sexpr(stdin);
+ if (feof(stdin)) break;
+ print(stdout, v=eval(v, &NIL));
+ set(symbol("that"), v);
+ printf("\n\n");
+ }
+ return 0;
+}
--- /dev/null
+++ b/femtolisp/tiny/lisp.c
@@ -1,0 +1,1029 @@
+/*
+ femtoLisp
+
+ a minimal interpreter for a minimal lisp dialect
+
+ this lisp dialect uses lexical scope and self-evaluating lambda.
+ it supports 30-bit integers, symbols, conses, and full macros.
+ it is case-sensitive.
+ it features a simple compacting copying garbage collector.
+ it uses a Scheme-style evaluation rule where any expression may appear in
+ head position as long as it evaluates to a function.
+ it uses Scheme-style varargs (dotted formal argument lists)
+ lambdas can have only 1 body expression; use (progn ...) for multiple
+ expressions. this is due to the closure representation
+ (lambda args body . env)
+
+ by Jeff Bezanson
+ Public Domain
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <sys/types.h>
+
+typedef u_int32_t value_t;
+typedef int32_t number_t;
+
+typedef struct {
+ value_t car;
+ value_t cdr;
+} cons_t;
+
+typedef struct _symbol_t {
+ value_t binding; // global value binding
+ value_t constant; // constant binding (used only for builtins)
+ struct _symbol_t *left;
+ struct _symbol_t *right;
+ char name[1];
+} symbol_t;
+
+#define TAG_NUM 0x0
+#define TAG_BUILTIN 0x1
+#define TAG_SYM 0x2
+#define TAG_CONS 0x3
+#define UNBOUND ((value_t)TAG_SYM) // an invalid symbol pointer
+#define tag(x) ((x)&0x3)
+#define ptr(x) ((void*)((x)&(~(value_t)0x3)))
+#define tagptr(p,t) (((value_t)(p)) | (t))
+#define number(x) ((value_t)((x)<<2))
+#define numval(x) (((number_t)(x))>>2)
+#define intval(x) (((int)(x))>>2)
+#define builtin(n) tagptr((((int)n)<<2), TAG_BUILTIN)
+#define iscons(x) (tag(x) == TAG_CONS)
+#define issymbol(x) (tag(x) == TAG_SYM)
+#define isnumber(x) (tag(x) == TAG_NUM)
+#define isbuiltin(x) (tag(x) == TAG_BUILTIN)
+// functions ending in _ are unsafe, faster versions
+#define car_(v) (((cons_t*)ptr(v))->car)
+#define cdr_(v) (((cons_t*)ptr(v))->cdr)
+#define car(v) (tocons((v),"car")->car)
+#define cdr(v) (tocons((v),"cdr")->cdr)
+#define set(s, v) (((symbol_t*)ptr(s))->binding = (v))
+#define setc(s, v) (((symbol_t*)ptr(s))->constant = (v))
+
+enum {
+ // special forms
+ F_QUOTE=0, F_COND, F_IF, F_AND, F_OR, F_WHILE, F_LAMBDA, F_MACRO, F_LABEL,
+ F_PROGN,
+ // functions
+ F_EQ, F_ATOM, F_CONS, F_CAR, F_CDR, F_READ, F_EVAL, F_PRINT, F_SET, F_NOT,
+ F_LOAD, F_SYMBOLP, F_NUMBERP, F_ADD, F_SUB, F_MUL, F_DIV, F_LT, F_PROG1,
+ F_APPLY, F_RPLACA, F_RPLACD, F_BOUNDP, N_BUILTINS
+};
+#define isspecial(v) (intval(v) <= (int)F_PROGN)
+
+static char *builtin_names[] =
+ { "quote", "cond", "if", "and", "or", "while", "lambda", "macro", "label",
+ "progn", "eq", "atom", "cons", "car", "cdr", "read", "eval", "print",
+ "set", "not", "load", "symbolp", "numberp", "+", "-", "*", "/", "<",
+ "prog1", "apply", "rplaca", "rplacd", "boundp" };
+
+static char *stack_bottom;
+#define PROCESS_STACK_SIZE (2*1024*1024)
+#define N_STACK 49152
+static value_t Stack[N_STACK];
+static u_int32_t SP = 0;
+#define PUSH(v) (Stack[SP++] = (v))
+#define POP() (Stack[--SP])
+#define POPN(n) (SP-=(n))
+
+value_t NIL, T, LAMBDA, MACRO, LABEL, QUOTE;
+
+value_t read_sexpr(FILE *f);
+void print(FILE *f, value_t v);
+value_t eval_sexpr(value_t e, value_t *penv);
+value_t load_file(char *fname);
+
+// error utilities ------------------------------------------------------------
+
+jmp_buf toplevel;
+
+void lerror(char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ longjmp(toplevel, 1);
+}
+
+void type_error(char *fname, char *expected, value_t got)
+{
+ fprintf(stderr, "%s: error: expected %s, got ", fname, expected);
+ print(stderr, got); lerror("\n");
+}
+
+// safe cast operators --------------------------------------------------------
+
+#define SAFECAST_OP(type,ctype,cnvt) \
+ctype to##type(value_t v, char *fname) \
+{ \
+ if (is##type(v)) \
+ return (ctype)cnvt(v); \
+ type_error(fname, #type, v); \
+ return (ctype)0; \
+}
+SAFECAST_OP(cons, cons_t*, ptr)
+SAFECAST_OP(symbol,symbol_t*,ptr)
+SAFECAST_OP(number,number_t, numval)
+
+// symbol table ---------------------------------------------------------------
+
+static symbol_t *symtab = NULL;
+
+static symbol_t *mk_symbol(char *str)
+{
+ symbol_t *sym;
+
+ sym = (symbol_t*)malloc(sizeof(symbol_t) + strlen(str));
+ sym->left = sym->right = NULL;
+ sym->constant = sym->binding = UNBOUND;
+ strcpy(&sym->name[0], str);
+ return sym;
+}
+
+static symbol_t **symtab_lookup(symbol_t **ptree, char *str)
+{
+ int x;
+
+ while(*ptree != NULL) {
+ x = strcmp(str, (*ptree)->name);
+ if (x == 0)
+ return ptree;
+ if (x < 0)
+ ptree = &(*ptree)->left;
+ else
+ ptree = &(*ptree)->right;
+ }
+ return ptree;
+}
+
+value_t symbol(char *str)
+{
+ symbol_t **pnode;
+
+ pnode = symtab_lookup(&symtab, str);
+ if (*pnode == NULL)
+ *pnode = mk_symbol(str);
+ return tagptr(*pnode, TAG_SYM);
+}
+
+// initialization -------------------------------------------------------------
+
+static unsigned char *fromspace;
+static unsigned char *tospace;
+static unsigned char *curheap;
+static unsigned char *lim;
+static u_int32_t heapsize = 64*1024;//bytes
+
+void lisp_init(void)
+{
+ int i;
+
+ fromspace = malloc(heapsize);
+ tospace = malloc(heapsize);
+ curheap = fromspace;
+ lim = curheap+heapsize-sizeof(cons_t);
+
+ NIL = symbol("nil"); setc(NIL, NIL);
+ T = symbol("t"); setc(T, T);
+ LAMBDA = symbol("lambda");
+ MACRO = symbol("macro");
+ LABEL = symbol("label");
+ QUOTE = symbol("quote");
+ for (i=0; i < (int)N_BUILTINS; i++)
+ setc(symbol(builtin_names[i]), builtin(i));
+ setc(symbol("princ"), builtin(F_PRINT));
+}
+
+// conses ---------------------------------------------------------------------
+
+void gc(void);
+
+static value_t mk_cons(void)
+{
+ cons_t *c;
+
+ if (curheap > lim)
+ gc();
+ c = (cons_t*)curheap;
+ curheap += sizeof(cons_t);
+ return tagptr(c, TAG_CONS);
+}
+
+static value_t cons_(value_t *pcar, value_t *pcdr)
+{
+ value_t c = mk_cons();
+ car_(c) = *pcar; cdr_(c) = *pcdr;
+ return c;
+}
+
+value_t *cons(value_t *pcar, value_t *pcdr)
+{
+ value_t c = mk_cons();
+ car_(c) = *pcar; cdr_(c) = *pcdr;
+ PUSH(c);
+ return &Stack[SP-1];
+}
+
+// collector ------------------------------------------------------------------
+
+static value_t relocate(value_t v)
+{
+ value_t a, d, nc;
+
+ if (!iscons(v))
+ return v;
+ if (car_(v) == UNBOUND)
+ return cdr_(v);
+ nc = mk_cons();
+ a = car_(v); d = cdr_(v);
+ car_(v) = UNBOUND; cdr_(v) = nc;
+ car_(nc) = relocate(a);
+ cdr_(nc) = relocate(d);
+ return nc;
+}
+
+static void trace_globals(symbol_t *root)
+{
+ while (root != NULL) {
+ root->binding = relocate(root->binding);
+ trace_globals(root->left);
+ root = root->right;
+ }
+}
+
+void gc(void)
+{
+ static int grew = 0;
+ unsigned char *temp;
+ u_int32_t i;
+
+ curheap = tospace;
+ lim = curheap+heapsize-sizeof(cons_t);
+
+ for (i=0; i < SP; i++)
+ Stack[i] = relocate(Stack[i]);
+ trace_globals(symtab);
+#ifdef VERBOSEGC
+ printf("gc found %d/%d live conses\n", (curheap-tospace)/8, heapsize/8);
+#endif
+ temp = tospace;
+ tospace = fromspace;
+ fromspace = temp;
+
+ // if we're using > 80% of the space, resize tospace so we have
+ // more space to fill next time. if we grew tospace last time,
+ // grow the other half of the heap this time to catch up.
+ if (grew || ((lim-curheap) < (int)(heapsize/5))) {
+ temp = realloc(tospace, grew ? heapsize : heapsize*2);
+ if (temp == NULL)
+ lerror("out of memory\n");
+ tospace = temp;
+ if (!grew)
+ heapsize*=2;
+ grew = !grew;
+ }
+ if (curheap > lim) // all data was live
+ gc();
+}
+
+// read -----------------------------------------------------------------------
+
+enum {
+ TOK_NONE, TOK_OPEN, TOK_CLOSE, TOK_DOT, TOK_QUOTE, TOK_SYM, TOK_NUM
+};
+
+static int symchar(char c)
+{
+ static char *special = "()';\\|";
+ return (!isspace(c) && !strchr(special, c));
+}
+
+static u_int32_t toktype = TOK_NONE;
+static value_t tokval;
+static char buf[256];
+
+static char nextchar(FILE *f)
+{
+ char c;
+ int ch;
+
+ do {
+ ch = fgetc(f);
+ if (ch == EOF)
+ return 0;
+ c = (char)ch;
+ if (c == ';') {
+ // single-line comment
+ do {
+ ch = fgetc(f);
+ if (ch == EOF)
+ return 0;
+ } while ((char)ch != '\n');
+ c = (char)ch;
+ }
+ } while (isspace(c));
+ return c;
+}
+
+static void take(void)
+{
+ toktype = TOK_NONE;
+}
+
+static void accumchar(char c, int *pi)
+{
+ buf[(*pi)++] = c;
+ if (*pi >= (int)(sizeof(buf)-1))
+ lerror("read: error: token too long\n");
+}
+
+// return: 1 for dot token, 0 for symbol
+static int read_token(FILE *f, char c)
+{
+ int i=0, ch, escaped=0, dot=(c=='.'), totread=0;
+
+ ungetc(c, f);
+ while (1) {
+ ch = fgetc(f); totread++;
+ if (ch == EOF)
+ goto terminate;
+ c = (char)ch;
+ if (c == '|') {
+ escaped = !escaped;
+ }
+ else if (c == '\\') {
+ ch = fgetc(f);
+ if (ch == EOF)
+ goto terminate;
+ accumchar((char)ch, &i);
+ }
+ else if (!escaped && !symchar(c)) {
+ break;
+ }
+ else {
+ accumchar(c, &i);
+ }
+ }
+ ungetc(c, f);
+ terminate:
+ buf[i++] = '\0';
+ return (dot && (totread==2));
+}
+
+static u_int32_t peek(FILE *f)
+{
+ char c, *end;
+ number_t x;
+
+ if (toktype != TOK_NONE)
+ return toktype;
+ c = nextchar(f);
+ if (feof(f)) return TOK_NONE;
+ if (c == '(') {
+ toktype = TOK_OPEN;
+ }
+ else if (c == ')') {
+ toktype = TOK_CLOSE;
+ }
+ else if (c == '\'') {
+ toktype = TOK_QUOTE;
+ }
+ else if (isdigit(c) || c=='-' || c=='+') {
+ read_token(f, c);
+ x = strtol(buf, &end, 0);
+ if (*end != '\0') {
+ toktype = TOK_SYM;
+ tokval = symbol(buf);
+ }
+ else {
+ toktype = TOK_NUM;
+ tokval = number(x);
+ }
+ }
+ else {
+ if (read_token(f, c)) {
+ toktype = TOK_DOT;
+ }
+ else {
+ toktype = TOK_SYM;
+ tokval = symbol(buf);
+ }
+ }
+ return toktype;
+}
+
+// build a list of conses. this is complicated by the fact that all conses
+// can move whenever a new cons is allocated. we have to refer to every cons
+// through a handle to a relocatable pointer (i.e. a pointer on the stack).
+static void read_list(FILE *f, value_t *pval)
+{
+ value_t c, *pc;
+ u_int32_t t;
+
+ PUSH(NIL);
+ pc = &Stack[SP-1]; // to keep track of current cons cell
+ t = peek(f);
+ while (t != TOK_CLOSE) {
+ if (feof(f))
+ lerror("read: error: unexpected end of input\n");
+ c = mk_cons(); car_(c) = cdr_(c) = NIL;
+ if (iscons(*pc))
+ cdr_(*pc) = c;
+ else
+ *pval = c;
+ *pc = c;
+ c = read_sexpr(f); // must be on separate lines due to undefined
+ car_(*pc) = c; // evaluation order
+
+ t = peek(f);
+ if (t == TOK_DOT) {
+ take();
+ c = read_sexpr(f);
+ cdr_(*pc) = c;
+ t = peek(f);
+ if (feof(f))
+ lerror("read: error: unexpected end of input\n");
+ if (t != TOK_CLOSE)
+ lerror("read: error: expected ')'\n");
+ }
+ }
+ take();
+ POP();
+}
+
+value_t read_sexpr(FILE *f)
+{
+ value_t v;
+
+ switch (peek(f)) {
+ case TOK_CLOSE:
+ take();
+ lerror("read: error: unexpected ')'\n");
+ case TOK_DOT:
+ take();
+ lerror("read: error: unexpected '.'\n");
+ case TOK_SYM:
+ case TOK_NUM:
+ take();
+ return tokval;
+ case TOK_QUOTE:
+ take();
+ v = read_sexpr(f);
+ PUSH(v);
+ v = cons_("E, cons(&Stack[SP-1], &NIL));
+ POPN(2);
+ return v;
+ case TOK_OPEN:
+ take();
+ PUSH(NIL);
+ read_list(f, &Stack[SP-1]);
+ return POP();
+ }
+ return NIL;
+}
+
+// print ----------------------------------------------------------------------
+
+void print(FILE *f, value_t v)
+{
+ value_t cd;
+
+ switch (tag(v)) {
+ case TAG_NUM: fprintf(f, "%d", numval(v)); break;
+ case TAG_SYM: fprintf(f, "%s", ((symbol_t*)ptr(v))->name); break;
+ case TAG_BUILTIN: fprintf(f, "#<builtin %s>",
+ builtin_names[intval(v)]); break;
+ case TAG_CONS:
+ fprintf(f, "(");
+ while (1) {
+ print(f, car_(v));
+ cd = cdr_(v);
+ if (!iscons(cd)) {
+ if (cd != NIL) {
+ fprintf(f, " . ");
+ print(f, cd);
+ }
+ fprintf(f, ")");
+ break;
+ }
+ fprintf(f, " ");
+ v = cd;
+ }
+ break;
+ }
+}
+
+// eval -----------------------------------------------------------------------
+
+static inline void argcount(char *fname, int nargs, int c)
+{
+ if (nargs != c)
+ lerror("%s: error: too %s arguments\n", fname, nargs<c ? "few":"many");
+}
+
+#define eval(e, env) ((tag(e)<0x2) ? (e) : eval_sexpr((e),env))
+#define tail_eval(xpr, env) do { SP = saveSP; \
+ if (tag(xpr)<0x2) { return (xpr); } \
+ else { e=(xpr); *penv=(env); goto eval_top; } } while (0)
+
+value_t eval_sexpr(value_t e, value_t *penv)
+{
+ value_t f, v, bind, headsym, asym, labl=0, *pv, *argsyms, *body, *lenv;
+ value_t *rest;
+ cons_t *c;
+ symbol_t *sym;
+ u_int32_t saveSP;
+ int i, nargs, noeval=0;
+ number_t s, n;
+
+ eval_top:
+ if (issymbol(e)) {
+ sym = (symbol_t*)ptr(e);
+ if (sym->constant != UNBOUND) return sym->constant;
+ v = *penv;
+ while (iscons(v)) {
+ bind = car_(v);
+ if (iscons(bind) && car_(bind) == e)
+ return cdr_(bind);
+ v = cdr_(v);
+ }
+ if ((v = sym->binding) == UNBOUND)
+ lerror("eval: error: variable %s has no value\n", sym->name);
+ return v;
+ }
+ if ((unsigned)(char*)&nargs < (unsigned)stack_bottom || SP>=(N_STACK-100))
+ lerror("eval: error: stack overflow\n");
+ saveSP = SP;
+ PUSH(e);
+ PUSH(*penv);
+ f = eval(car_(e), penv);
+ *penv = Stack[saveSP+1];
+ if (isbuiltin(f)) {
+ // handle builtin function
+ if (!isspecial(f)) {
+ // evaluate argument list, placing arguments on stack
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ while (iscons(v)) {
+ v = eval(car_(v), penv);
+ *penv = Stack[saveSP+1];
+ PUSH(v);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ }
+ apply_builtin:
+ nargs = SP - saveSP - 2;
+ switch (intval(f)) {
+ // special forms
+ case F_QUOTE:
+ v = cdr_(Stack[saveSP]);
+ if (!iscons(v))
+ lerror("quote: error: expected argument\n");
+ v = car_(v);
+ break;
+ case F_MACRO:
+ case F_LAMBDA:
+ v = Stack[saveSP];
+ if (*penv != NIL) {
+ // build a closure (lambda args body . env)
+ v = cdr_(v);
+ PUSH(car(v));
+ argsyms = &Stack[SP-1];
+ PUSH(car(cdr_(v)));
+ body = &Stack[SP-1];
+ v = cons_(intval(f)==F_LAMBDA ? &LAMBDA : &MACRO,
+ cons(argsyms, cons(body, penv)));
+ }
+ break;
+ case F_LABEL:
+ v = Stack[saveSP];
+ if (*penv != NIL) {
+ v = cdr_(v);
+ PUSH(car(v)); // name
+ pv = &Stack[SP-1];
+ PUSH(car(cdr_(v))); // function
+ body = &Stack[SP-1];
+ *body = eval(*body, penv); // evaluate lambda
+ v = cons_(&LABEL, cons(pv, cons(body, &NIL)));
+ }
+ break;
+ case F_IF:
+ v = car(cdr_(Stack[saveSP]));
+ if (eval(v, penv) != NIL)
+ v = car(cdr_(cdr_(Stack[saveSP])));
+ else
+ v = car(cdr(cdr_(cdr_(Stack[saveSP]))));
+ tail_eval(v, Stack[saveSP+1]);
+ break;
+ case F_COND:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ while (iscons(*pv)) {
+ c = tocons(car_(*pv), "cond");
+ v = eval(c->car, penv);
+ *penv = Stack[saveSP+1];
+ if (v != NIL) {
+ *pv = cdr_(car_(*pv));
+ // evaluate body forms
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ v = eval(car_(*pv), penv);
+ *penv = Stack[saveSP+1];
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv), *penv);
+ }
+ break;
+ }
+ *pv = cdr_(*pv);
+ }
+ break;
+ case F_AND:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = T;
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ if ((v=eval(car_(*pv), penv)) == NIL) {
+ SP = saveSP; return NIL;
+ }
+ *penv = Stack[saveSP+1];
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv), *penv);
+ }
+ break;
+ case F_OR:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ if ((v=eval(car_(*pv), penv)) != NIL) {
+ SP = saveSP; return v;
+ }
+ *penv = Stack[saveSP+1];
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv), *penv);
+ }
+ break;
+ case F_WHILE:
+ PUSH(cdr(cdr_(Stack[saveSP])));
+ body = &Stack[SP-1];
+ PUSH(*body);
+ Stack[saveSP] = car_(cdr_(Stack[saveSP]));
+ value_t *cond = &Stack[saveSP];
+ PUSH(NIL);
+ pv = &Stack[SP-1];
+ while (eval(*cond, penv) != NIL) {
+ *penv = Stack[saveSP+1];
+ *body = Stack[SP-2];
+ while (iscons(*body)) {
+ *pv = eval(car_(*body), penv);
+ *penv = Stack[saveSP+1];
+ *body = cdr_(*body);
+ }
+ }
+ v = *pv;
+ break;
+ case F_PROGN:
+ // return last arg
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ v = eval(car_(*pv), penv);
+ *penv = Stack[saveSP+1];
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv), *penv);
+ }
+ break;
+
+ // ordinary functions
+ case F_SET:
+ argcount("set", nargs, 2);
+ e = Stack[SP-2];
+ v = *penv;
+ while (iscons(v)) {
+ bind = car_(v);
+ if (iscons(bind) && car_(bind) == e) {
+ cdr_(bind) = (v=Stack[SP-1]);
+ SP=saveSP; return v;
+ }
+ v = cdr_(v);
+ }
+ tosymbol(e, "set")->binding = (v=Stack[SP-1]);
+ break;
+ case F_BOUNDP:
+ argcount("boundp", nargs, 1);
+ sym = tosymbol(Stack[SP-1], "boundp");
+ if (sym->binding == UNBOUND && sym->constant == UNBOUND)
+ v = NIL;
+ else
+ v = T;
+ break;
+ case F_EQ:
+ argcount("eq", nargs, 2);
+ v = ((Stack[SP-2] == Stack[SP-1]) ? T : NIL);
+ break;
+ case F_CONS:
+ argcount("cons", nargs, 2);
+ v = mk_cons();
+ car_(v) = Stack[SP-2];
+ cdr_(v) = Stack[SP-1];
+ break;
+ case F_CAR:
+ argcount("car", nargs, 1);
+ v = car(Stack[SP-1]);
+ break;
+ case F_CDR:
+ argcount("cdr", nargs, 1);
+ v = cdr(Stack[SP-1]);
+ break;
+ case F_RPLACA:
+ argcount("rplaca", nargs, 2);
+ car(v=Stack[SP-2]) = Stack[SP-1];
+ break;
+ case F_RPLACD:
+ argcount("rplacd", nargs, 2);
+ cdr(v=Stack[SP-2]) = Stack[SP-1];
+ break;
+ case F_ATOM:
+ argcount("atom", nargs, 1);
+ v = ((!iscons(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_SYMBOLP:
+ argcount("symbolp", nargs, 1);
+ v = ((issymbol(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_NUMBERP:
+ argcount("numberp", nargs, 1);
+ v = ((isnumber(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_ADD:
+ s = 0;
+ for (i=saveSP+2; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "+");
+ s += n;
+ }
+ v = number(s);
+ break;
+ case F_SUB:
+ if (nargs < 1)
+ lerror("-: error: too few arguments\n");
+ i = saveSP+2;
+ s = (nargs==1) ? 0 : tonumber(Stack[i++], "-");
+ for (; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "-");
+ s -= n;
+ }
+ v = number(s);
+ break;
+ case F_MUL:
+ s = 1;
+ for (i=saveSP+2; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "*");
+ s *= n;
+ }
+ v = number(s);
+ break;
+ case F_DIV:
+ if (nargs < 1)
+ lerror("/: error: too few arguments\n");
+ i = saveSP+2;
+ s = (nargs==1) ? 1 : tonumber(Stack[i++], "/");
+ for (; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "/");
+ if (n == 0)
+ lerror("/: error: division by zero\n");
+ s /= n;
+ }
+ v = number(s);
+ break;
+ case F_LT:
+ argcount("<", nargs, 2);
+ if (tonumber(Stack[SP-2],"<") < tonumber(Stack[SP-1],"<"))
+ v = T;
+ else
+ v = NIL;
+ break;
+ case F_NOT:
+ argcount("not", nargs, 1);
+ v = ((Stack[SP-1] == NIL) ? T : NIL);
+ break;
+ case F_EVAL:
+ argcount("eval", nargs, 1);
+ v = Stack[SP-1];
+ tail_eval(v, NIL);
+ break;
+ case F_PRINT:
+ for (i=saveSP+2; i < (int)SP; i++)
+ print(stdout, v=Stack[i]);
+ break;
+ case F_READ:
+ argcount("read", nargs, 0);
+ v = read_sexpr(stdin);
+ break;
+ case F_LOAD:
+ argcount("load", nargs, 1);
+ v = load_file(tosymbol(Stack[SP-1], "load")->name);
+ break;
+ case F_PROG1:
+ // return first arg
+ if (nargs < 1)
+ lerror("prog1: error: too few arguments\n");
+ v = Stack[saveSP+2];
+ break;
+ case F_APPLY:
+ argcount("apply", nargs, 2);
+ v = Stack[saveSP] = Stack[SP-1]; // second arg is new arglist
+ f = Stack[SP-2]; // first arg is new function
+ POPN(2); // pop apply's args
+ if (isbuiltin(f)) {
+ if (isspecial(f))
+ lerror("apply: error: cannot apply special operator "
+ "%s\n", builtin_names[intval(f)]);
+ // unpack arglist onto the stack
+ while (iscons(v)) {
+ PUSH(car_(v));
+ v = cdr_(v);
+ }
+ goto apply_builtin;
+ }
+ noeval = 1;
+ goto apply_lambda;
+ }
+ SP = saveSP;
+ return v;
+ }
+ else {
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ apply_lambda:
+ if (iscons(f)) {
+ headsym = car_(f);
+ if (headsym == LABEL) {
+ // (label name (lambda ...)) behaves the same as the lambda
+ // alone, except with name bound to the whole label expression
+ labl = f;
+ f = car(cdr(cdr_(labl)));
+ headsym = car(f);
+ }
+ // apply lambda or macro expression
+ PUSH(cdr(cdr(cdr_(f))));
+ lenv = &Stack[SP-1];
+ PUSH(car_(cdr_(f)));
+ argsyms = &Stack[SP-1];
+ PUSH(car_(cdr_(cdr_(f))));
+ body = &Stack[SP-1];
+ if (labl) {
+ // add label binding to environment
+ PUSH(labl);
+ PUSH(car_(cdr_(labl)));
+ *lenv = cons_(cons(&Stack[SP-1], &Stack[SP-2]), lenv);
+ POPN(3);
+ v = Stack[saveSP]; // refetch arglist
+ }
+ if (headsym == MACRO)
+ noeval = 1;
+ else if (headsym != LAMBDA)
+ lerror("apply: error: head must be lambda, macro, or label\n");
+ // build a calling environment for the lambda
+ // the environment is the argument binds on top of the captured
+ // environment
+ while (iscons(v)) {
+ // bind args
+ if (!iscons(*argsyms)) {
+ if (*argsyms == NIL)
+ lerror("apply: error: too many arguments\n");
+ break;
+ }
+ asym = car_(*argsyms);
+ if (!issymbol(asym))
+ lerror("apply: error: formal argument not a symbol\n");
+ v = car_(v);
+ if (!noeval) {
+ v = eval(v, penv);
+ *penv = Stack[saveSP+1];
+ }
+ PUSH(v);
+ *lenv = cons_(cons(&asym, &Stack[SP-1]), lenv);
+ POPN(2);
+ *argsyms = cdr_(*argsyms);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ if (*argsyms != NIL) {
+ if (issymbol(*argsyms)) {
+ if (noeval) {
+ *lenv = cons_(cons(argsyms, &Stack[saveSP]), lenv);
+ }
+ else {
+ PUSH(NIL);
+ PUSH(NIL);
+ rest = &Stack[SP-1];
+ // build list of rest arguments
+ // we have to build it forwards, which is tricky
+ while (iscons(v)) {
+ v = eval(car_(v), penv);
+ *penv = Stack[saveSP+1];
+ PUSH(v);
+ v = cons_(&Stack[SP-1], &NIL);
+ POP();
+ if (iscons(*rest))
+ cdr_(*rest) = v;
+ else
+ Stack[SP-2] = v;
+ *rest = v;
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ *lenv = cons_(cons(argsyms, &Stack[SP-2]), lenv);
+ }
+ }
+ else if (iscons(*argsyms)) {
+ lerror("apply: error: too few arguments\n");
+ }
+ }
+ noeval = 0;
+ // macro: evaluate expansion in the calling environment
+ if (headsym == MACRO) {
+ SP = saveSP;
+ PUSH(*lenv);
+ lenv = &Stack[SP-1];
+ v = eval(*body, lenv);
+ tail_eval(v, *penv);
+ }
+ else {
+ tail_eval(*body, *lenv);
+ }
+ // not reached
+ }
+ type_error("apply", "function", f);
+ return NIL;
+}
+
+// repl -----------------------------------------------------------------------
+
+static char *infile = NULL;
+
+value_t toplevel_eval(value_t expr)
+{
+ value_t v;
+ u_int32_t saveSP = SP;
+ PUSH(NIL);
+ v = eval(expr, &Stack[SP-1]);
+ SP = saveSP;
+ return v;
+}
+
+value_t load_file(char *fname)
+{
+ value_t e, v=NIL;
+ char *lastfile = infile;
+ FILE *f = fopen(fname, "r");
+ infile = fname;
+ if (f == NULL) lerror("file not found\n");
+ while (1) {
+ e = read_sexpr(f);
+ if (feof(f)) break;
+ v = toplevel_eval(e);
+ }
+ infile = lastfile;
+ fclose(f);
+ return v;
+}
+
+int main(int argc, char* argv[])
+{
+ value_t v;
+
+ stack_bottom = ((char*)&v) - PROCESS_STACK_SIZE;
+ lisp_init();
+ if (setjmp(toplevel)) {
+ SP = 0;
+ fprintf(stderr, "\n");
+ if (infile) {
+ fprintf(stderr, "error loading file \"%s\"\n", infile);
+ infile = NULL;
+ }
+ goto repl;
+ }
+ load_file("system.lsp");
+ if (argc > 1) { load_file(argv[1]); return 0; }
+ printf("Welcome to femtoLisp ----------------------------------------------------------\n");
+ repl:
+ while (1) {
+ printf("> ");
+ v = read_sexpr(stdin);
+ if (feof(stdin)) break;
+ print(stdout, v=toplevel_eval(v));
+ set(symbol("that"), v);
+ printf("\n\n");
+ }
+ return 0;
+}
binary files /dev/null b/femtolisp/tiny/lisp2 differ
--- /dev/null
+++ b/femtolisp/tiny/lisp2.c
@@ -1,0 +1,1434 @@
+/*
+ femtoLisp
+
+ a minimal interpreter for a minimal lisp dialect
+
+ this lisp dialect uses lexical scope and self-evaluating lambda.
+ it supports 30-bit integers, symbols, conses, and full macros.
+ it is case-sensitive.
+ it features a simple compacting copying garbage collector.
+ it uses a Scheme-style evaluation rule where any expression may appear in
+ head position as long as it evaluates to a function.
+ it uses Scheme-style varargs (dotted formal argument lists)
+ lambdas can have only 1 body expression; use (progn ...) for multiple
+ expressions. this is due to the closure representation
+ (lambda args body . env)
+
+ This is a fork of femtoLisp with advanced reading and printing facilities:
+ * circular structure can be printed and read
+ * #. read macro for eval-when-read and correctly printing builtins
+ * read macros for backquote
+ * symbol character-escaping printer
+
+ * new print algorithm
+ 1. traverse & tag all conses to be printed. when you encounter a cons
+ that is already tagged, add it to a table to give it a #n# index
+ 2. untag a cons when printing it. if cons is in the table, print
+ "#n=" before it in the car, " . #n=" in the cdr. if cons is in the
+ table but already untagged, print #n# in car or " . #n#" in the cdr.
+ * read macros for #n# and #n= using the same kind of table
+ * also need a table of read labels to translate from input indexes to
+ normalized indexes (0 for first label, 1 for next, etc.)
+ * read macro #. for eval-when-read. use for printing builtins, e.g. "#.eq"
+
+ The value of this extra complexity, and what makes this fork worthy of
+ the femtoLisp brand, is that the interpreter is fully "closed" in the
+ sense that all representable values can be read and printed.
+
+ by Jeff Bezanson
+ Public Domain
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <sys/types.h>
+
+typedef u_int32_t value_t;
+typedef int32_t number_t;
+
+typedef struct {
+ value_t car;
+ value_t cdr;
+} cons_t;
+
+typedef struct _symbol_t {
+ value_t binding; // global value binding
+ value_t constant; // constant binding (used only for builtins)
+ struct _symbol_t *left;
+ struct _symbol_t *right;
+ char name[1];
+} symbol_t;
+
+#define TAG_NUM 0x0
+#define TAG_BUILTIN 0x1
+#define TAG_SYM 0x2
+#define TAG_CONS 0x3
+#define UNBOUND ((value_t)TAG_SYM) // an invalid symbol pointer
+#define tag(x) ((x)&0x3)
+#define ptr(x) ((void*)((x)&(~(value_t)0x3)))
+#define tagptr(p,t) (((value_t)(p)) | (t))
+#define number(x) ((value_t)((x)<<2))
+#define numval(x) (((number_t)(x))>>2)
+#define intval(x) (((int)(x))>>2)
+#define builtin(n) tagptr((((int)n)<<2), TAG_BUILTIN)
+#define iscons(x) (tag(x) == TAG_CONS)
+#define issymbol(x) (tag(x) == TAG_SYM)
+#define isnumber(x) (tag(x) == TAG_NUM)
+#define isbuiltin(x) (tag(x) == TAG_BUILTIN)
+// functions ending in _ are unsafe, faster versions
+#define car_(v) (((cons_t*)ptr(v))->car)
+#define cdr_(v) (((cons_t*)ptr(v))->cdr)
+#define car(v) (tocons((v),"car")->car)
+#define cdr(v) (tocons((v),"cdr")->cdr)
+#define set(s, v) (((symbol_t*)ptr(s))->binding = (v))
+#define setc(s, v) (((symbol_t*)ptr(s))->constant = (v))
+
+enum {
+ // special forms
+ F_QUOTE=0, F_COND, F_IF, F_AND, F_OR, F_WHILE, F_LAMBDA, F_MACRO, F_LABEL,
+ F_PROGN,
+ // functions
+ F_EQ, F_ATOM, F_CONS, F_CAR, F_CDR, F_READ, F_EVAL, F_PRINT, F_SET, F_NOT,
+ F_LOAD, F_SYMBOLP, F_NUMBERP, F_ADD, F_SUB, F_MUL, F_DIV, F_LT, F_PROG1,
+ F_APPLY, F_RPLACA, F_RPLACD, F_BOUNDP, F_ERROR, F_EXIT, F_PRINC, F_CONSP,
+ F_ASSOC, N_BUILTINS
+};
+#define isspecial(v) (intval(v) <= (number_t)F_PROGN)
+
+static char *builtin_names[] =
+ { "quote", "cond", "if", "and", "or", "while", "lambda", "macro", "label",
+ "progn",
+ "eq", "atom", "cons", "car", "cdr", "read", "eval", "print",
+ "set", "not", "load", "symbolp", "numberp", "+", "-", "*", "/", "<",
+ "prog1", "apply", "rplaca", "rplacd", "boundp", "error", "exit", "princ",
+ "consp", "assoc" };
+
+static char *stack_bottom;
+#define PROCESS_STACK_SIZE (2*1024*1024)
+#define N_STACK 98304
+static value_t Stack[N_STACK];
+static u_int32_t SP = 0;
+#define PUSH(v) (Stack[SP++] = (v))
+#define POP() (Stack[--SP])
+#define POPN(n) (SP-=(n))
+
+value_t NIL, T, LAMBDA, MACRO, LABEL, QUOTE;
+value_t BACKQUOTE, COMMA, COMMAAT, COMMADOT;
+
+value_t read_sexpr(FILE *f);
+void print(FILE *f, value_t v, int princ);
+value_t eval_sexpr(value_t e, value_t *penv, int tail, u_int32_t envend);
+value_t load_file(char *fname);
+value_t toplevel_eval(value_t expr);
+
+#include "flutils.c"
+
+typedef struct _readstate_t {
+ ltable_t labels;
+ ltable_t exprs;
+ struct _readstate_t *prev;
+} readstate_t;
+static readstate_t *readstate = NULL;
+
+// error utilities ------------------------------------------------------------
+
+jmp_buf toplevel;
+
+void lerror(char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+ while (readstate) {
+ free(readstate->labels.items);
+ free(readstate->exprs.items);
+ readstate = readstate->prev;
+ }
+
+ vfprintf(stderr, format, args);
+ va_end(args);
+ longjmp(toplevel, 1);
+}
+
+void type_error(char *fname, char *expected, value_t got)
+{
+ fprintf(stderr, "%s: error: expected %s, got ", fname, expected);
+ print(stderr, got, 0); lerror("\n");
+}
+
+// safe cast operators --------------------------------------------------------
+
+#define SAFECAST_OP(type,ctype,cnvt) \
+ctype to##type(value_t v, char *fname) \
+{ \
+ if (is##type(v)) \
+ return (ctype)cnvt(v); \
+ type_error(fname, #type, v); \
+ return (ctype)0; \
+}
+SAFECAST_OP(cons, cons_t*, ptr)
+SAFECAST_OP(symbol,symbol_t*,ptr)
+SAFECAST_OP(number,number_t, numval)
+
+// symbol table ---------------------------------------------------------------
+
+static symbol_t *symtab = NULL;
+
+static symbol_t *mk_symbol(char *str)
+{
+ symbol_t *sym;
+
+ sym = (symbol_t*)malloc(sizeof(symbol_t) + strlen(str));
+ sym->left = sym->right = NULL;
+ sym->constant = sym->binding = UNBOUND;
+ strcpy(&sym->name[0], str);
+ return sym;
+}
+
+static symbol_t **symtab_lookup(symbol_t **ptree, char *str)
+{
+ int x;
+
+ while(*ptree != NULL) {
+ x = strcmp(str, (*ptree)->name);
+ if (x == 0)
+ return ptree;
+ if (x < 0)
+ ptree = &(*ptree)->left;
+ else
+ ptree = &(*ptree)->right;
+ }
+ return ptree;
+}
+
+value_t symbol(char *str)
+{
+ symbol_t **pnode;
+
+ pnode = symtab_lookup(&symtab, str);
+ if (*pnode == NULL)
+ *pnode = mk_symbol(str);
+ return tagptr(*pnode, TAG_SYM);
+}
+
+// initialization -------------------------------------------------------------
+
+static unsigned char *fromspace;
+static unsigned char *tospace;
+static unsigned char *curheap;
+static unsigned char *lim;
+static u_int32_t heapsize = 128*1024;//bytes
+static u_int32_t *consflags;
+static ltable_t printconses;
+
+void lisp_init(void)
+{
+ int i;
+
+ fromspace = malloc(heapsize);
+ tospace = malloc(heapsize);
+ curheap = fromspace;
+ lim = curheap+heapsize-sizeof(cons_t);
+ consflags = mk_bitvector(heapsize/sizeof(cons_t));
+
+ ltable_init(&printconses, 32);
+
+ NIL = symbol("nil"); setc(NIL, NIL);
+ T = symbol("t"); setc(T, T);
+ LAMBDA = symbol("lambda");
+ MACRO = symbol("macro");
+ LABEL = symbol("label");
+ QUOTE = symbol("quote");
+ BACKQUOTE = symbol("backquote");
+ COMMA = symbol("*comma*");
+ COMMAAT = symbol("*comma-at*");
+ COMMADOT = symbol("*comma-dot*");
+ for (i=0; i < (int)N_BUILTINS; i++)
+ setc(symbol(builtin_names[i]), builtin(i));
+}
+
+// conses ---------------------------------------------------------------------
+
+void gc(int mustgrow);
+
+static value_t mk_cons(void)
+{
+ cons_t *c;
+
+ if (curheap > lim)
+ gc(0);
+ c = (cons_t*)curheap;
+ curheap += sizeof(cons_t);
+ return tagptr(c, TAG_CONS);
+}
+
+// allocate n consecutive conses
+static value_t cons_reserve(int n)
+{
+ cons_t *first;
+
+ n--;
+ if ((cons_t*)curheap > ((cons_t*)lim)-n) {
+ gc(0);
+ while ((cons_t*)curheap > ((cons_t*)lim)-n) {
+ gc(1);
+ }
+ }
+ first = (cons_t*)curheap;
+ curheap += ((n+1)*sizeof(cons_t));
+ return tagptr(first, TAG_CONS);
+}
+
+#define cons_index(c) (((cons_t*)ptr(c))-((cons_t*)fromspace))
+#define ismarked(c) bitvector_get(consflags, cons_index(c))
+#define mark_cons(c) bitvector_set(consflags, cons_index(c), 1)
+#define unmark_cons(c) bitvector_set(consflags, cons_index(c), 0)
+
+// collector ------------------------------------------------------------------
+
+static value_t relocate(value_t v)
+{
+ value_t a, d, nc, first, *pcdr;
+
+ if (!iscons(v))
+ return v;
+ // iterative implementation allows arbitrarily long cons chains
+ pcdr = &first;
+ do {
+ if ((a=car_(v)) == UNBOUND) {
+ *pcdr = cdr_(v);
+ return first;
+ }
+ *pcdr = nc = mk_cons();
+ d = cdr_(v);
+ car_(v) = UNBOUND; cdr_(v) = nc;
+ car_(nc) = relocate(a);
+ pcdr = &cdr_(nc);
+ v = d;
+ } while (iscons(v));
+ *pcdr = d;
+
+ return first;
+}
+
+static void trace_globals(symbol_t *root)
+{
+ while (root != NULL) {
+ root->binding = relocate(root->binding);
+ trace_globals(root->left);
+ root = root->right;
+ }
+}
+
+void gc(int mustgrow)
+{
+ static int grew = 0;
+ void *temp;
+ u_int32_t i;
+ readstate_t *rs;
+
+ curheap = tospace;
+ lim = curheap+heapsize-sizeof(cons_t);
+
+ for (i=0; i < SP; i++)
+ Stack[i] = relocate(Stack[i]);
+ trace_globals(symtab);
+ rs = readstate;
+ while (rs) {
+ for(i=0; i < rs->exprs.n; i++)
+ rs->exprs.items[i] = relocate(rs->exprs.items[i]);
+ rs = rs->prev;
+ }
+#ifdef VERBOSEGC
+ printf("gc found %d/%d live conses\n",
+ (curheap-tospace)/sizeof(cons_t), heapsize/sizeof(cons_t));
+#endif
+ temp = tospace;
+ tospace = fromspace;
+ fromspace = temp;
+
+ // if we're using > 80% of the space, resize tospace so we have
+ // more space to fill next time. if we grew tospace last time,
+ // grow the other half of the heap this time to catch up.
+ if (grew || ((lim-curheap) < (int)(heapsize/5)) || mustgrow) {
+ temp = realloc(tospace, grew ? heapsize : heapsize*2);
+ if (temp == NULL)
+ lerror("out of memory\n");
+ tospace = temp;
+ if (!grew) {
+ heapsize*=2;
+ }
+ else {
+ temp = bitvector_resize(consflags, heapsize/sizeof(cons_t));
+ if (temp == NULL)
+ lerror("out of memory\n");
+ consflags = (u_int32_t*)temp;
+ }
+ grew = !grew;
+ }
+ if (curheap > lim) // all data was live
+ gc(0);
+}
+
+// read -----------------------------------------------------------------------
+
+enum {
+ TOK_NONE, TOK_OPEN, TOK_CLOSE, TOK_DOT, TOK_QUOTE, TOK_SYM, TOK_NUM,
+ TOK_BQ, TOK_COMMA, TOK_COMMAAT, TOK_COMMADOT,
+ TOK_SHARPDOT, TOK_LABEL, TOK_BACKREF, TOK_SHARPQUOTE
+};
+
+// defines which characters are ordinary symbol characters.
+// the only exception is '.', which is an ordinary symbol character
+// unless it is the only character in the symbol.
+static int symchar(char c)
+{
+ static char *special = "()';`,\\|";
+ return (!isspace(c) && !strchr(special, c));
+}
+
+static u_int32_t toktype = TOK_NONE;
+static value_t tokval;
+static char buf[256];
+
+static char nextchar(FILE *f)
+{
+ int ch;
+ char c;
+
+ do {
+ ch = fgetc(f);
+ if (ch == EOF)
+ return 0;
+ c = (char)ch;
+ if (c == ';') {
+ // single-line comment
+ do {
+ ch = fgetc(f);
+ if (ch == EOF)
+ return 0;
+ } while ((char)ch != '\n');
+ c = (char)ch;
+ }
+ } while (isspace(c));
+ return c;
+}
+
+static void take(void)
+{
+ toktype = TOK_NONE;
+}
+
+static void accumchar(char c, int *pi)
+{
+ buf[(*pi)++] = c;
+ if (*pi >= (int)(sizeof(buf)-1))
+ lerror("read: error: token too long\n");
+}
+
+// return: 1 for dot token, 0 for symbol
+static int read_token(FILE *f, char c, int digits)
+{
+ int i=0, ch, escaped=0, dot=(c=='.'), totread=0;
+
+ ungetc(c, f);
+ while (1) {
+ ch = fgetc(f); totread++;
+ if (ch == EOF)
+ goto terminate;
+ c = (char)ch;
+ if (c == '|') {
+ escaped = !escaped;
+ }
+ else if (c == '\\') {
+ ch = fgetc(f);
+ if (ch == EOF)
+ goto terminate;
+ accumchar((char)ch, &i);
+ }
+ else if (!escaped && !(symchar(c) && (!digits || isdigit(c)))) {
+ break;
+ }
+ else {
+ accumchar(c, &i);
+ }
+ }
+ ungetc(c, f);
+ terminate:
+ buf[i++] = '\0';
+ return (dot && (totread==2));
+}
+
+static u_int32_t peek(FILE *f)
+{
+ char c, *end;
+ number_t x;
+ int ch;
+
+ if (toktype != TOK_NONE)
+ return toktype;
+ c = nextchar(f);
+ if (feof(f)) return TOK_NONE;
+ if (c == '(') {
+ toktype = TOK_OPEN;
+ }
+ else if (c == ')') {
+ toktype = TOK_CLOSE;
+ }
+ else if (c == '\'') {
+ toktype = TOK_QUOTE;
+ }
+ else if (c == '`') {
+ toktype = TOK_BQ;
+ }
+ else if (c == '#') {
+ ch = fgetc(f);
+ if (ch == EOF)
+ lerror("read: error: invalid read macro\n");
+ if ((char)ch == '.') {
+ toktype = TOK_SHARPDOT;
+ }
+ else if ((char)ch == '\'') {
+ toktype = TOK_SHARPQUOTE;
+ }
+ else if ((char)ch == '\\') {
+ u_int32_t cval = u8_fgetc(f);
+ toktype = TOK_NUM;
+ tokval = number(cval);
+ }
+ else if (isdigit((char)ch)) {
+ read_token(f, (char)ch, 1);
+ c = (char)fgetc(f);
+ if (c == '#')
+ toktype = TOK_BACKREF;
+ else if (c == '=')
+ toktype = TOK_LABEL;
+ else
+ lerror("read: error: invalid label\n");
+ x = strtol(buf, &end, 10);
+ tokval = number(x);
+ }
+ else {
+ lerror("read: error: unknown read macro\n");
+ }
+ }
+ else if (c == ',') {
+ toktype = TOK_COMMA;
+ ch = fgetc(f);
+ if (ch == EOF)
+ return toktype;
+ if ((char)ch == '@')
+ toktype = TOK_COMMAAT;
+ else if ((char)ch == '.')
+ toktype = TOK_COMMADOT;
+ else
+ ungetc((char)ch, f);
+ }
+ else if (isdigit(c) || c=='-' || c=='+') {
+ read_token(f, c, 0);
+ x = strtol(buf, &end, 0);
+ if (*end != '\0') {
+ toktype = TOK_SYM;
+ tokval = symbol(buf);
+ }
+ else {
+ toktype = TOK_NUM;
+ tokval = number(x);
+ }
+ }
+ else {
+ if (read_token(f, c, 0)) {
+ toktype = TOK_DOT;
+ }
+ else {
+ toktype = TOK_SYM;
+ tokval = symbol(buf);
+ }
+ }
+ return toktype;
+}
+
+static value_t do_read_sexpr(FILE *f, int fixup);
+
+// build a list of conses. this is complicated by the fact that all conses
+// can move whenever a new cons is allocated. we have to refer to every cons
+// through a handle to a relocatable pointer (i.e. a pointer on the stack).
+static void read_list(FILE *f, value_t *pval, int fixup)
+{
+ value_t c, *pc;
+ u_int32_t t;
+
+ PUSH(NIL);
+ pc = &Stack[SP-1]; // to keep track of current cons cell
+ t = peek(f);
+ while (t != TOK_CLOSE) {
+ if (feof(f))
+ lerror("read: error: unexpected end of input\n");
+ c = mk_cons(); car_(c) = cdr_(c) = NIL;
+ if (iscons(*pc)) {
+ cdr_(*pc) = c;
+ }
+ else {
+ *pval = c;
+ if (fixup != -1)
+ readstate->exprs.items[fixup] = c;
+ }
+ *pc = c;
+ c = do_read_sexpr(f,-1); // must be on separate lines due to undefined
+ car_(*pc) = c; // evaluation order
+
+ t = peek(f);
+ if (t == TOK_DOT) {
+ take();
+ c = do_read_sexpr(f,-1);
+ cdr_(*pc) = c;
+ t = peek(f);
+ if (feof(f))
+ lerror("read: error: unexpected end of input\n");
+ if (t != TOK_CLOSE)
+ lerror("read: error: expected ')'\n");
+ }
+ }
+ take();
+ POP();
+}
+
+// fixup is the index of the label we'd like to fix up with this read
+static value_t do_read_sexpr(FILE *f, int fixup)
+{
+ value_t v, *head;
+ u_int32_t t, l;
+ int i;
+
+ t = peek(f);
+ take();
+ switch (t) {
+ case TOK_CLOSE:
+ lerror("read: error: unexpected ')'\n");
+ case TOK_DOT:
+ lerror("read: error: unexpected '.'\n");
+ case TOK_SYM:
+ case TOK_NUM:
+ return tokval;
+ case TOK_COMMA:
+ head = &COMMA; goto listwith;
+ case TOK_COMMAAT:
+ head = &COMMAAT; goto listwith;
+ case TOK_COMMADOT:
+ head = &COMMADOT; goto listwith;
+ case TOK_BQ:
+ head = &BACKQUOTE; goto listwith;
+ case TOK_QUOTE:
+ head = "E;
+ listwith:
+ v = cons_reserve(2);
+ car_(v) = *head;
+ cdr_(v) = tagptr(((cons_t*)ptr(v))+1, TAG_CONS);
+ car_(cdr_(v)) = cdr_(cdr_(v)) = NIL;
+ PUSH(v);
+ if (fixup != -1)
+ readstate->exprs.items[fixup] = v;
+ v = do_read_sexpr(f,-1);
+ car_(cdr_(Stack[SP-1])) = v;
+ return POP();
+ case TOK_SHARPQUOTE:
+ // femtoLisp doesn't need symbol-function, so #' does nothing
+ return do_read_sexpr(f, fixup);
+ case TOK_OPEN:
+ PUSH(NIL);
+ read_list(f, &Stack[SP-1], fixup);
+ return POP();
+ case TOK_SHARPDOT:
+ // eval-when-read
+ // evaluated expressions can refer to existing backreferences, but they
+ // cannot see pending labels. in other words:
+ // (... #2=#.#0# ... ) OK
+ // (... #2=#.(#2#) ... ) DO NOT WANT
+ v = do_read_sexpr(f,-1);
+ return toplevel_eval(v);
+ case TOK_LABEL:
+ // create backreference label
+ l = numval(tokval);
+ if (ltable_lookup(&readstate->labels, l) != NOTFOUND)
+ lerror("read: error: label %d redefined\n", l);
+ ltable_insert(&readstate->labels, l);
+ i = readstate->exprs.n;
+ ltable_insert(&readstate->exprs, UNBOUND);
+ v = do_read_sexpr(f,i);
+ readstate->exprs.items[i] = v;
+ return v;
+ case TOK_BACKREF:
+ // look up backreference
+ l = numval(tokval);
+ i = ltable_lookup(&readstate->labels, l);
+ if (i == NOTFOUND || i >= (int)readstate->exprs.n ||
+ readstate->exprs.items[i] == UNBOUND)
+ lerror("read: error: undefined label %d\n", l);
+ return readstate->exprs.items[i];
+ }
+ return NIL;
+}
+
+value_t read_sexpr(FILE *f)
+{
+ value_t v;
+ readstate_t state;
+ state.prev = readstate;
+ ltable_init(&state.labels, 16);
+ ltable_init(&state.exprs, 16);
+ readstate = &state;
+
+ v = do_read_sexpr(f, -1);
+
+ readstate = state.prev;
+ free(state.labels.items);
+ free(state.exprs.items);
+ return v;
+}
+
+// print ----------------------------------------------------------------------
+
+static void print_traverse(value_t v)
+{
+ while (iscons(v)) {
+ if (ismarked(v)) {
+ ltable_adjoin(&printconses, v);
+ return;
+ }
+ mark_cons(v);
+ print_traverse(car_(v));
+ v = cdr_(v);
+ }
+}
+
+static void print_symbol(FILE *f, char *name)
+{
+ int i, escape=0, charescape=0;
+
+ if (name[0] == '\0') {
+ fprintf(f, "||");
+ return;
+ }
+ if (name[0] == '.' && name[1] == '\0') {
+ fprintf(f, "|.|");
+ return;
+ }
+ if (name[0] == '#')
+ escape = 1;
+ i=0;
+ while (name[i]) {
+ if (!symchar(name[i])) {
+ escape = 1;
+ if (name[i]=='|' || name[i]=='\\') {
+ charescape = 1;
+ break;
+ }
+ }
+ i++;
+ }
+ if (escape) {
+ if (charescape) {
+ fprintf(f, "|");
+ i=0;
+ while (name[i]) {
+ if (name[i]=='|' || name[i]=='\\')
+ fprintf(f, "\\%c", name[i]);
+ else
+ fprintf(f, "%c", name[i]);
+ i++;
+ }
+ fprintf(f, "|");
+ }
+ else {
+ fprintf(f, "|%s|", name);
+ }
+ }
+ else {
+ fprintf(f, "%s", name);
+ }
+}
+
+static void do_print(FILE *f, value_t v, int princ)
+{
+ value_t cd;
+ int label;
+ char *name;
+
+ switch (tag(v)) {
+ case TAG_NUM: fprintf(f, "%d", numval(v)); break;
+ case TAG_SYM:
+ name = ((symbol_t*)ptr(v))->name;
+ if (princ)
+ fprintf(f, "%s", name);
+ else
+ print_symbol(f, name);
+ break;
+ case TAG_BUILTIN: fprintf(f, "#.%s", builtin_names[intval(v)]); break;
+ case TAG_CONS:
+ if ((label=ltable_lookup(&printconses,v)) != NOTFOUND) {
+ if (!ismarked(v)) {
+ fprintf(f, "#%d#", label);
+ return;
+ }
+ fprintf(f, "#%d=", label);
+ }
+ fprintf(f, "(");
+ while (1) {
+ unmark_cons(v);
+ do_print(f, car_(v), princ);
+ cd = cdr_(v);
+ if (!iscons(cd)) {
+ if (cd != NIL) {
+ fprintf(f, " . ");
+ do_print(f, cd, princ);
+ }
+ fprintf(f, ")");
+ break;
+ }
+ else {
+ if ((label=ltable_lookup(&printconses,cd)) != NOTFOUND) {
+ fprintf(f, " . ");
+ do_print(f, cd, princ);
+ fprintf(f, ")");
+ break;
+ }
+ }
+ fprintf(f, " ");
+ v = cd;
+ }
+ break;
+ }
+}
+
+void print(FILE *f, value_t v, int princ)
+{
+ ltable_clear(&printconses);
+ print_traverse(v);
+ do_print(f, v, princ);
+}
+
+// eval -----------------------------------------------------------------------
+
+static inline void argcount(char *fname, int nargs, int c)
+{
+ if (nargs != c)
+ lerror("%s: error: too %s arguments\n", fname, nargs<c ? "few":"many");
+}
+
+// return a cons element of v whose car is item
+static value_t assoc(value_t item, value_t v)
+{
+ value_t bind;
+
+ while (iscons(v)) {
+ bind = car_(v);
+ if (iscons(bind) && car_(bind) == item)
+ return bind;
+ v = cdr_(v);
+ }
+ return NIL;
+}
+
+#define eval(e) ((tag(e)<0x2) ? (e) : eval_sexpr((e),penv,0,envend))
+#define topeval(e, env) ((tag(e)<0x2) ? (e) : eval_sexpr((e),env,1,SP))
+#define tail_eval(xpr) do { SP = saveSP; \
+ if (tag(xpr)<0x2) { return (xpr); } \
+ else { e=(xpr); goto eval_top; } } while (0)
+
+/* stack setup on entry:
+ n n+1 ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+
+ | SYM | VAL | SYM | VAL | CLO | | | |
+ +-----+-----+-----+-----+-----+-----+-----+-----+
+ ^ ^ ^
+ | | |
+ penv envend SP (who knows where)
+
+ sym is an argument name and val is its binding. CLO is a closed-up
+ environment list (which can be empty, i.e. NIL).
+ CLO is always there, but there might be zero SYM/VAL pairs.
+
+ if tail==1, you are allowed (indeed encouraged) to overwrite this
+ environment, otherwise you have to put any new environment on the top
+ of the stack.
+*/
+value_t eval_sexpr(value_t e, value_t *penv, int tail, u_int32_t envend)
+{
+ value_t f, v, headsym, asym, *pv, *argsyms, *body, *lenv, *argenv;
+ cons_t *c;
+ symbol_t *sym;
+ u_int32_t saveSP;
+ int i, nargs, noeval=0;
+ number_t s, n;
+
+ eval_top:
+ if (issymbol(e)) {
+ sym = (symbol_t*)ptr(e);
+ if (sym->constant != UNBOUND) return sym->constant;
+ while (issymbol(*penv)) { // 1. try lookup in argument env
+ if (*penv == NIL)
+ goto get_global;
+ if (*penv == e)
+ return penv[1];
+ penv+=2;
+ }
+ if ((v=assoc(e,*penv)) != NIL) // 2. closure env
+ return cdr_(v);
+ get_global:
+ if ((v = sym->binding) == UNBOUND) // 3. global env
+ lerror("eval: error: variable %s has no value\n", sym->name);
+ return v;
+ }
+ if ((unsigned)(char*)&nargs < (unsigned)stack_bottom || SP>=(N_STACK-100))
+ lerror("eval: error: stack overflow\n");
+ saveSP = SP;
+ PUSH(e);
+ v = car_(e);
+ if (tag(v)<0x2) f = v;
+ else if (issymbol(v) && (f=((symbol_t*)ptr(v))->constant)!=UNBOUND) ;
+ else f = eval_sexpr(v, penv, 0, envend);
+ if (isbuiltin(f)) {
+ // handle builtin function
+ if (!isspecial(f)) {
+ // evaluate argument list, placing arguments on stack
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ while (iscons(v)) {
+ v = eval(car_(v));
+ PUSH(v);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ }
+ apply_builtin:
+ nargs = SP - saveSP - 1;
+ switch (intval(f)) {
+ // special forms
+ case F_QUOTE:
+ v = cdr_(Stack[saveSP]);
+ if (!iscons(v)) lerror("quote: error: expected argument\n");
+ v = car_(v);
+ break;
+ case F_MACRO:
+ case F_LAMBDA:
+ // build a closure (lambda args body . env)
+ if (issymbol(*penv) && *penv != NIL) {
+ // cons up and save temporary environment
+ PUSH(Stack[envend-1]); // passed-in CLOENV
+ // find out how many new conses we need
+ nargs = ((int)(&Stack[envend] - penv - 1))>>1;
+ if (nargs) {
+ lenv = penv;
+ Stack[SP-1] = cons_reserve(nargs*2);
+ c = (cons_t*)ptr(Stack[SP-1]);
+ while (1) {
+ c->car = tagptr(c+1, TAG_CONS);
+ (c+1)->car = penv[0];
+ (c+1)->cdr = penv[1];
+ nargs--;
+ if (nargs==0) break;
+ penv+=2;
+ c->cdr = tagptr(c+2, TAG_CONS);
+ c += 2;
+ }
+ // final cdr points to existing cloenv
+ c->cdr = Stack[envend-1];
+ // environment representation changed; install
+ // the new representation so everybody can see it
+ *lenv = Stack[SP-1];
+ }
+ }
+ else {
+ PUSH(*penv); // env has already been captured; share
+ }
+ v = cdr_(Stack[saveSP]);
+ PUSH(car(v));
+ PUSH(car(cdr_(v)));
+ c = (cons_t*)ptr(v=cons_reserve(3));
+ c->car = (intval(f)==F_LAMBDA ? LAMBDA : MACRO);
+ c->cdr = tagptr(c+1, TAG_CONS); c++;
+ c->car = Stack[SP-2]; //argsyms
+ c->cdr = tagptr(c+1, TAG_CONS); c++;
+ c->car = Stack[SP-1]; //body
+ c->cdr = Stack[SP-3]; //env
+ break;
+ case F_LABEL:
+ // the syntax of label is (label name (lambda args body))
+ // nothing else is guaranteed to work
+ v = cdr_(Stack[saveSP]);
+ PUSH(car(v));
+ PUSH(car(cdr_(v)));
+ body = &Stack[SP-1];
+ *body = eval(*body); // evaluate lambda
+ c = (cons_t*)ptr(cons_reserve(2));
+ c->car = Stack[SP-2]; // name
+ c->cdr = v = *body; c++;
+ c->car = tagptr(c-1, TAG_CONS);
+ f = cdr(cdr(v));
+ c->cdr = cdr(f);
+ // add (name . fn) to front of function's environment
+ cdr_(f) = tagptr(c, TAG_CONS);
+ break;
+ case F_IF:
+ v = car(cdr_(Stack[saveSP]));
+ if (eval(v) != NIL)
+ v = car(cdr_(cdr_(Stack[saveSP])));
+ else
+ v = car(cdr(cdr_(cdr_(Stack[saveSP]))));
+ tail_eval(v);
+ break;
+ case F_COND:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ while (iscons(*pv)) {
+ c = tocons(car_(*pv), "cond");
+ v = eval(c->car);
+ if (v != NIL) {
+ *pv = cdr_(car_(*pv));
+ // evaluate body forms
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ v = eval(car_(*pv));
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv));
+ }
+ break;
+ }
+ *pv = cdr_(*pv);
+ }
+ break;
+ case F_AND:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = T;
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ if ((v=eval(car_(*pv))) == NIL) {
+ SP = saveSP; return NIL;
+ }
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv));
+ }
+ break;
+ case F_OR:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ if ((v=eval(car_(*pv))) != NIL) {
+ SP = saveSP; return v;
+ }
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv));
+ }
+ break;
+ case F_WHILE:
+ PUSH(cdr(cdr_(Stack[saveSP])));
+ body = &Stack[SP-1];
+ PUSH(*body);
+ Stack[saveSP] = car_(cdr_(Stack[saveSP]));
+ value_t *cond = &Stack[saveSP];
+ PUSH(NIL);
+ pv = &Stack[SP-1];
+ while (eval(*cond) != NIL) {
+ *body = Stack[SP-2];
+ while (iscons(*body)) {
+ *pv = eval(car_(*body));
+ *body = cdr_(*body);
+ }
+ }
+ v = *pv;
+ break;
+ case F_PROGN:
+ // return last arg
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ v = eval(car_(*pv));
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv));
+ }
+ break;
+
+ // ordinary functions
+ case F_SET:
+ argcount("set", nargs, 2);
+ e = Stack[SP-2];
+ while (issymbol(*penv)) {
+ if (*penv == NIL)
+ goto set_global;
+ if (*penv == e) {
+ penv[1] = Stack[SP-1];
+ SP=saveSP; return penv[1];
+ }
+ penv+=2;
+ }
+ if ((v=assoc(e,*penv)) != NIL) {
+ cdr_(v) = (e=Stack[SP-1]);
+ SP=saveSP; return e;
+ }
+ set_global:
+ tosymbol(e, "set")->binding = (v=Stack[SP-1]);
+ break;
+ case F_BOUNDP:
+ argcount("boundp", nargs, 1);
+ sym = tosymbol(Stack[SP-1], "boundp");
+ if (sym->binding == UNBOUND && sym->constant == UNBOUND)
+ v = NIL;
+ else
+ v = T;
+ break;
+ case F_EQ:
+ argcount("eq", nargs, 2);
+ v = ((Stack[SP-2] == Stack[SP-1]) ? T : NIL);
+ break;
+ case F_CONS:
+ argcount("cons", nargs, 2);
+ v = mk_cons();
+ car_(v) = Stack[SP-2];
+ cdr_(v) = Stack[SP-1];
+ break;
+ case F_CAR:
+ argcount("car", nargs, 1);
+ v = car(Stack[SP-1]);
+ break;
+ case F_CDR:
+ argcount("cdr", nargs, 1);
+ v = cdr(Stack[SP-1]);
+ break;
+ case F_RPLACA:
+ argcount("rplaca", nargs, 2);
+ car(v=Stack[SP-2]) = Stack[SP-1];
+ break;
+ case F_RPLACD:
+ argcount("rplacd", nargs, 2);
+ cdr(v=Stack[SP-2]) = Stack[SP-1];
+ break;
+ case F_ATOM:
+ argcount("atom", nargs, 1);
+ v = ((!iscons(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_CONSP:
+ argcount("consp", nargs, 1);
+ v = (iscons(Stack[SP-1]) ? T : NIL);
+ break;
+ case F_SYMBOLP:
+ argcount("symbolp", nargs, 1);
+ v = ((issymbol(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_NUMBERP:
+ argcount("numberp", nargs, 1);
+ v = ((isnumber(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_ADD:
+ s = 0;
+ for (i=saveSP+1; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "+");
+ s += n;
+ }
+ v = number(s);
+ break;
+ case F_SUB:
+ if (nargs < 1) lerror("-: error: too few arguments\n");
+ i = saveSP+1;
+ s = (nargs==1) ? 0 : tonumber(Stack[i++], "-");
+ for (; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "-");
+ s -= n;
+ }
+ v = number(s);
+ break;
+ case F_MUL:
+ s = 1;
+ for (i=saveSP+1; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "*");
+ s *= n;
+ }
+ v = number(s);
+ break;
+ case F_DIV:
+ if (nargs < 1) lerror("/: error: too few arguments\n");
+ i = saveSP+1;
+ s = (nargs==1) ? 1 : tonumber(Stack[i++], "/");
+ for (; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "/");
+ if (n == 0) lerror("/: error: division by zero\n");
+ s /= n;
+ }
+ v = number(s);
+ break;
+ case F_LT:
+ argcount("<", nargs, 2);
+ // this implements generic comparison for all atoms
+ // strange comparisons (for example with builtins) are resolved
+ // arbitrarily but consistently.
+ // ordering: number < builtin < symbol < cons
+ if (tag(Stack[SP-2]) != tag(Stack[SP-1])) {
+ v = (tag(Stack[SP-2]) < tag(Stack[SP-1]) ? T : NIL);
+ }
+ else {
+ switch (tag(Stack[SP-2])) {
+ case TAG_NUM:
+ v = (numval(Stack[SP-2]) < numval(Stack[SP-1])) ? T : NIL;
+ break;
+ case TAG_SYM:
+ v = (strcmp(((symbol_t*)ptr(Stack[SP-2]))->name,
+ ((symbol_t*)ptr(Stack[SP-1]))->name) < 0) ?
+ T : NIL;
+ break;
+ case TAG_BUILTIN:
+ v = (intval(Stack[SP-2]) < intval(Stack[SP-1])) ? T : NIL;
+ break;
+ case TAG_CONS:
+ lerror("<: error: expected atom\n");
+ }
+ }
+ break;
+ case F_NOT:
+ argcount("not", nargs, 1);
+ v = ((Stack[SP-1] == NIL) ? T : NIL);
+ break;
+ case F_EVAL:
+ argcount("eval", nargs, 1);
+ v = Stack[SP-1];
+ if (tag(v)<0x2) { SP=saveSP; return v; }
+ if (tail) {
+ *penv = NIL;
+ envend = SP = (u_int32_t)(penv-&Stack[0]) + 1;
+ e=v; goto eval_top;
+ }
+ else {
+ PUSH(NIL);
+ v = eval_sexpr(v, &Stack[SP-1], 1, SP);
+ }
+ break;
+ case F_PRINT:
+ for (i=saveSP+1; i < (int)SP; i++)
+ print(stdout, v=Stack[i], 0);
+ fprintf(stdout, "\n");
+ break;
+ case F_PRINC:
+ for (i=saveSP+1; i < (int)SP; i++)
+ print(stdout, v=Stack[i], 1);
+ break;
+ case F_READ:
+ argcount("read", nargs, 0);
+ v = read_sexpr(stdin);
+ break;
+ case F_LOAD:
+ argcount("load", nargs, 1);
+ v = load_file(tosymbol(Stack[SP-1], "load")->name);
+ break;
+ case F_EXIT:
+ exit(0);
+ break;
+ case F_ERROR:
+ for (i=saveSP+1; i < (int)SP; i++)
+ print(stderr, Stack[i], 1);
+ lerror("\n");
+ break;
+ case F_PROG1:
+ // return first arg
+ if (nargs < 1) lerror("prog1: error: too few arguments\n");
+ v = Stack[saveSP+1];
+ break;
+ case F_ASSOC:
+ argcount("assoc", nargs, 2);
+ v = assoc(Stack[SP-2], Stack[SP-1]);
+ break;
+ case F_APPLY:
+ argcount("apply", nargs, 2);
+ v = Stack[saveSP] = Stack[SP-1]; // second arg is new arglist
+ f = Stack[SP-2]; // first arg is new function
+ POPN(2); // pop apply's args
+ if (isbuiltin(f)) {
+ if (isspecial(f))
+ lerror("apply: error: cannot apply special operator "
+ "%s\n", builtin_names[intval(f)]);
+ // unpack arglist onto the stack
+ while (iscons(v)) {
+ PUSH(car_(v));
+ v = cdr_(v);
+ }
+ goto apply_builtin;
+ }
+ noeval = 1;
+ goto apply_lambda;
+ }
+ SP = saveSP;
+ return v;
+ }
+ else {
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ apply_lambda:
+ if (iscons(f)) {
+ headsym = car_(f);
+ // apply lambda or macro expression
+ PUSH(cdr(cdr_(f)));
+ PUSH(car_(cdr_(f)));
+ argsyms = &Stack[SP-1];
+ argenv = &Stack[SP]; // argument environment starts now
+ if (headsym == MACRO)
+ noeval = 1;
+ //else if (headsym != LAMBDA)
+ // lerror("apply: error: head must be lambda, macro, or label\n");
+ // build a calling environment for the lambda
+ // the environment is the argument binds on top of the captured
+ // environment
+ while (iscons(v)) {
+ // bind args
+ if (!iscons(*argsyms)) {
+ if (*argsyms == NIL)
+ lerror("apply: error: too many arguments\n");
+ break;
+ }
+ asym = car_(*argsyms);
+ if (asym==NIL || iscons(asym))
+ lerror("apply: error: invalid formal argument\n");
+ v = car_(v);
+ if (!noeval) {
+ v = eval(v);
+ }
+ PUSH(asym);
+ PUSH(v);
+ *argsyms = cdr_(*argsyms);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ if (*argsyms != NIL) {
+ if (issymbol(*argsyms)) {
+ PUSH(*argsyms);
+ if (noeval) {
+ PUSH(Stack[saveSP]);
+ }
+ else {
+ // this version uses collective allocation. about 7-10%
+ // faster for lists with > 2 elements, but uses more
+ // stack space
+ PUSH(NIL);
+ i = SP;
+ while (iscons(Stack[saveSP])) {
+ PUSH(eval(car_(Stack[saveSP])));
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ nargs = SP-i;
+ if (nargs) {
+ Stack[i-1] = cons_reserve(nargs);
+ c = (cons_t*)ptr(Stack[i-1]);
+ for(; i < (int)SP; i++) {
+ c->car = Stack[i];
+ c->cdr = tagptr(c+1, TAG_CONS);
+ c++;
+ }
+ (c-1)->cdr = NIL;
+ POPN(nargs);
+ }
+ }
+ }
+ else if (iscons(*argsyms)) {
+ lerror("apply: error: too few arguments\n");
+ }
+ }
+ noeval = 0;
+ lenv = &Stack[saveSP+1];
+ PUSH(cdr(*lenv)); // add cloenv to new environment
+ e = car_(Stack[saveSP+1]);
+ // macro: evaluate expansion in the calling environment
+ if (headsym == MACRO) {
+ if (tag(e)<0x2) ;
+ else e = eval_sexpr(e, argenv, 1, SP);
+ SP = saveSP;
+ if (tag(e)<0x2) return(e);
+ goto eval_top;
+ }
+ else {
+ if (tag(e)<0x2) { SP=saveSP; return(e); }
+ if (tail) {
+ // ok to overwrite environment
+ nargs = (int)(&Stack[SP] - argenv);
+ for(i=0; i < nargs; i++)
+ penv[i] = argenv[i];
+ envend = SP = (u_int32_t)((penv+nargs) - &Stack[0]);
+ goto eval_top;
+ }
+ else {
+ v = eval_sexpr(e, argenv, 1, SP);
+ SP = saveSP;
+ return v;
+ }
+ }
+ // not reached
+ }
+ type_error("apply", "function", f);
+ return NIL;
+}
+
+// repl -----------------------------------------------------------------------
+
+static char *infile = NULL;
+
+value_t toplevel_eval(value_t expr)
+{
+ value_t v;
+ u_int32_t saveSP = SP;
+ PUSH(NIL);
+ v = topeval(expr, &Stack[SP-1]);
+ SP = saveSP;
+ return v;
+}
+
+value_t load_file(char *fname)
+{
+ value_t e, v=NIL;
+ char *lastfile = infile;
+ FILE *f = fopen(fname, "r");
+ infile = fname;
+ if (f == NULL) lerror("file not found\n");
+ while (1) {
+ e = read_sexpr(f);
+ if (feof(f)) break;
+ v = toplevel_eval(e);
+ }
+ infile = lastfile;
+ fclose(f);
+ return v;
+}
+
+int main(int argc, char* argv[])
+{
+ value_t v;
+
+ stack_bottom = ((char*)&v) - PROCESS_STACK_SIZE;
+ lisp_init();
+ if (setjmp(toplevel)) {
+ SP = 0;
+ fprintf(stderr, "\n");
+ if (infile) {
+ fprintf(stderr, "error loading file \"%s\"\n", infile);
+ infile = NULL;
+ }
+ goto repl;
+ }
+ load_file("system.lsp");
+ if (argc > 1) { load_file(argv[1]); return 0; }
+ printf("; _ \n");
+ printf("; |_ _ _ |_ _ | . _ _ 2\n");
+ printf("; | (-||||_(_)|__|_)|_)\n");
+ printf(";-------------------|----------------------------------------------------------\n\n");
+ repl:
+ while (1) {
+ printf("> ");
+ v = read_sexpr(stdin);
+ if (feof(stdin)) break;
+ print(stdout, v=toplevel_eval(v), 0);
+ set(symbol("that"), v);
+ printf("\n\n");
+ }
+ return 0;
+}
--- /dev/null
+++ b/femtolisp/tiny/lisp2.c.bak
@@ -1,0 +1,1448 @@
+/*
+ femtoLisp
+
+ a minimal interpreter for a minimal lisp dialect
+
+ this lisp dialect uses lexical scope and self-evaluating lambda.
+ it supports 30-bit integers, symbols, conses, and full macros.
+ it is case-sensitive.
+ it features a simple compacting copying garbage collector.
+ it uses a Scheme-style evaluation rule where any expression may appear in
+ head position as long as it evaluates to a function.
+ it uses Scheme-style varargs (dotted formal argument lists)
+ lambdas can have only 1 body expression; use (progn ...) for multiple
+ expressions. this is due to the closure representation
+ (lambda args body . env)
+
+ This is a fork of femtoLisp with advanced reading and printing facilities:
+ * circular structure can be printed and read
+ * #. read macro for eval-when-read and correctly printing builtins
+ * read macros for backquote
+ * symbol character-escaping printer
+
+ * new print algorithm
+ 1. traverse & tag all conses to be printed. when you encounter a cons
+ that is already tagged, add it to a table to give it a #n# index
+ 2. untag a cons when printing it. if cons is in the table, print
+ "#n=" before it in the car, " . #n=" in the cdr. if cons is in the
+ table but already untagged, print #n# in car or " . #n#" in the cdr.
+ * read macros for #n# and #n= using the same kind of table
+ * also need a table of read labels to translate from input indexes to
+ normalized indexes (0 for first label, 1 for next, etc.)
+ * read macro #. for eval-when-read. use for printing builtins, e.g. "#.eq"
+
+ The value of this extra complexity, and what makes this fork worthy of
+ the femtoLisp brand, is that the interpreter is fully "closed" in the
+ sense that all representable values can be read and printed.
+
+ by Jeff Bezanson
+ Public Domain
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <sys/types.h>
+
+typedef u_int32_t value_t;
+typedef int32_t number_t;
+
+typedef struct {
+ value_t car;
+ value_t cdr;
+} cons_t;
+
+typedef struct _symbol_t {
+ value_t binding; // global value binding
+ value_t constant; // constant binding (used only for builtins)
+ struct _symbol_t *left;
+ struct _symbol_t *right;
+ char name[1];
+} symbol_t;
+
+#define TAG_NUM 0x0
+#define TAG_BUILTIN 0x1
+#define TAG_SYM 0x2
+#define TAG_CONS 0x3
+#define UNBOUND ((value_t)TAG_SYM) // an invalid symbol pointer
+#define tag(x) ((x)&0x3)
+#define ptr(x) ((void*)((x)&(~(value_t)0x3)))
+#define tagptr(p,t) (((value_t)(p)) | (t))
+#define number(x) ((value_t)((x)<<2))
+#define numval(x) (((number_t)(x))>>2)
+#define intval(x) (((int)(x))>>2)
+#define builtin(n) tagptr((((int)n)<<2), TAG_BUILTIN)
+#define iscons(x) (tag(x) == TAG_CONS)
+#define issymbol(x) (tag(x) == TAG_SYM)
+#define isnumber(x) (tag(x) == TAG_NUM)
+#define isbuiltin(x) (tag(x) == TAG_BUILTIN)
+// functions ending in _ are unsafe, faster versions
+#define car_(v) (((cons_t*)ptr(v))->car)
+#define cdr_(v) (((cons_t*)ptr(v))->cdr)
+#define car(v) (tocons((v),"car")->car)
+#define cdr(v) (tocons((v),"cdr")->cdr)
+#define set(s, v) (((symbol_t*)ptr(s))->binding = (v))
+#define setc(s, v) (((symbol_t*)ptr(s))->constant = (v))
+
+enum {
+ // special forms
+ F_QUOTE=0, F_COND, F_IF, F_AND, F_OR, F_WHILE, F_LAMBDA, F_MACRO, F_LABEL,
+ F_PROGN,
+ // functions
+ F_EQ, F_ATOM, F_CONS, F_CAR, F_CDR, F_READ, F_EVAL, F_PRINT, F_SET, F_NOT,
+ F_LOAD, F_SYMBOLP, F_NUMBERP, F_ADD, F_SUB, F_MUL, F_DIV, F_LT, F_PROG1,
+ F_APPLY, F_RPLACA, F_RPLACD, F_BOUNDP, F_ERROR, F_EXIT, F_PRINC, F_CONSP,
+ F_ASSOC, N_BUILTINS
+};
+#define isspecial(v) (intval(v) <= (number_t)F_PROGN)
+
+static char *builtin_names[] =
+ { "quote", "cond", "if", "and", "or", "while", "lambda", "macro", "label",
+ "progn",
+ "eq", "atom", "cons", "car", "cdr", "read", "eval", "print",
+ "set", "not", "load", "symbolp", "numberp", "+", "-", "*", "/", "<",
+ "prog1", "apply", "rplaca", "rplacd", "boundp", "error", "exit", "princ",
+ "consp", "assoc" };
+
+static char *stack_bottom;
+#define PROCESS_STACK_SIZE (2*1024*1024)
+#define N_STACK 98304
+static value_t Stack[N_STACK];
+static u_int32_t SP = 0;
+#define PUSH(v) (Stack[SP++] = (v))
+#define POP() (Stack[--SP])
+#define POPN(n) (SP-=(n))
+
+value_t NIL, T, LAMBDA, MACRO, LABEL, QUOTE;
+value_t BACKQUOTE, COMMA, COMMAAT, COMMADOT;
+
+value_t read_sexpr(FILE *f);
+void print(FILE *f, value_t v, int princ);
+value_t eval_sexpr(value_t e, value_t *penv, int tail, u_int32_t envend);
+value_t load_file(char *fname);
+value_t toplevel_eval(value_t expr);
+
+#include "flutils.c"
+
+typedef struct _readstate_t {
+ ltable_t labels;
+ ltable_t exprs;
+ struct _readstate_t *prev;
+} readstate_t;
+static readstate_t *readstate = NULL;
+
+// error utilities ------------------------------------------------------------
+
+jmp_buf toplevel;
+
+void lerror(char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+ while (readstate) {
+ free(readstate->labels.items);
+ free(readstate->exprs.items);
+ readstate = readstate->prev;
+ }
+
+ vfprintf(stderr, format, args);
+ va_end(args);
+ longjmp(toplevel, 1);
+}
+
+void type_error(char *fname, char *expected, value_t got)
+{
+ fprintf(stderr, "%s: error: expected %s, got ", fname, expected);
+ print(stderr, got, 0); lerror("\n");
+}
+
+// safe cast operators --------------------------------------------------------
+
+#define SAFECAST_OP(type,ctype,cnvt) \
+ctype to##type(value_t v, char *fname) \
+{ \
+ if (is##type(v)) \
+ return (ctype)cnvt(v); \
+ type_error(fname, #type, v); \
+ return (ctype)0; \
+}
+SAFECAST_OP(cons, cons_t*, ptr)
+SAFECAST_OP(symbol,symbol_t*,ptr)
+SAFECAST_OP(number,number_t, numval)
+
+// symbol table ---------------------------------------------------------------
+
+static symbol_t *symtab = NULL;
+
+static symbol_t *mk_symbol(char *str)
+{
+ symbol_t *sym;
+
+ sym = (symbol_t*)malloc(sizeof(symbol_t) + strlen(str));
+ sym->left = sym->right = NULL;
+ sym->constant = sym->binding = UNBOUND;
+ strcpy(&sym->name[0], str);
+ return sym;
+}
+
+static symbol_t **symtab_lookup(symbol_t **ptree, char *str)
+{
+ int x;
+
+ while(*ptree != NULL) {
+ x = strcmp(str, (*ptree)->name);
+ if (x == 0)
+ return ptree;
+ if (x < 0)
+ ptree = &(*ptree)->left;
+ else
+ ptree = &(*ptree)->right;
+ }
+ return ptree;
+}
+
+value_t symbol(char *str)
+{
+ symbol_t **pnode;
+
+ pnode = symtab_lookup(&symtab, str);
+ if (*pnode == NULL)
+ *pnode = mk_symbol(str);
+ return tagptr(*pnode, TAG_SYM);
+}
+
+// initialization -------------------------------------------------------------
+
+static unsigned char *fromspace;
+static unsigned char *tospace;
+static unsigned char *curheap;
+static unsigned char *lim;
+static u_int32_t heapsize = 128*1024;//bytes
+static u_int32_t *consflags;
+static ltable_t printconses;
+
+void lisp_init(void)
+{
+ int i;
+
+ fromspace = malloc(heapsize);
+ tospace = malloc(heapsize);
+ curheap = fromspace;
+ lim = curheap+heapsize-sizeof(cons_t);
+ consflags = mk_bitvector(heapsize/sizeof(cons_t));
+
+ ltable_init(&printconses, 32);
+
+ NIL = symbol("nil"); setc(NIL, NIL);
+ T = symbol("t"); setc(T, T);
+ LAMBDA = symbol("lambda");
+ MACRO = symbol("macro");
+ LABEL = symbol("label");
+ QUOTE = symbol("quote");
+ BACKQUOTE = symbol("backquote");
+ COMMA = symbol("*comma*");
+ COMMAAT = symbol("*comma-at*");
+ COMMADOT = symbol("*comma-dot*");
+ for (i=0; i < (int)N_BUILTINS; i++)
+ setc(symbol(builtin_names[i]), builtin(i));
+}
+
+// conses ---------------------------------------------------------------------
+
+void gc(int mustgrow);
+
+static value_t mk_cons(void)
+{
+ cons_t *c;
+
+ if (curheap > lim)
+ gc(0);
+ c = (cons_t*)curheap;
+ curheap += sizeof(cons_t);
+ return tagptr(c, TAG_CONS);
+}
+
+// allocate and link n consecutive conses
+// warning: only cdrs are initialized
+static value_t cons_reserve(int n)
+{
+ cons_t *c, *first;
+
+ n--;
+ if ((cons_t*)curheap > ((cons_t*)lim)-n) {
+ gc(0);
+ while ((cons_t*)curheap > ((cons_t*)lim)-n) {
+ gc(1);
+ }
+ }
+ c = first = (cons_t*)curheap;
+ for(; n > 0; n--) {
+ c->cdr = tagptr(c+1, TAG_CONS);
+ c++;
+ }
+ c->cdr = NIL;
+ curheap = (unsigned char*)(c+1);
+ return tagptr(first, TAG_CONS);
+}
+
+value_t *cons(value_t *pcar, value_t *pcdr)
+{
+ value_t c = mk_cons();
+ car_(c) = *pcar; cdr_(c) = *pcdr;
+ PUSH(c);
+ return &Stack[SP-1];
+}
+
+#define cons_index(c) (((cons_t*)ptr(c))-((cons_t*)fromspace))
+#define ismarked(c) bitvector_get(consflags, cons_index(c))
+#define mark_cons(c) bitvector_set(consflags, cons_index(c), 1)
+#define unmark_cons(c) bitvector_set(consflags, cons_index(c), 0)
+
+// collector ------------------------------------------------------------------
+
+static value_t relocate(value_t v)
+{
+ value_t a, d, nc, first, *pcdr;
+
+ if (!iscons(v))
+ return v;
+ // iterative implementation allows arbitrarily long cons chains
+ pcdr = &first;
+ do {
+ if ((a=car_(v)) == UNBOUND) {
+ *pcdr = cdr_(v);
+ return first;
+ }
+ *pcdr = nc = mk_cons();
+ d = cdr_(v);
+ car_(v) = UNBOUND; cdr_(v) = nc;
+ car_(nc) = relocate(a);
+ pcdr = &cdr_(nc);
+ v = d;
+ } while (iscons(v));
+ *pcdr = d;
+
+ return first;
+}
+
+static void trace_globals(symbol_t *root)
+{
+ while (root != NULL) {
+ root->binding = relocate(root->binding);
+ trace_globals(root->left);
+ root = root->right;
+ }
+}
+
+void gc(int mustgrow)
+{
+ static int grew = 0;
+ unsigned char *temp;
+ u_int32_t i;
+ readstate_t *rs;
+
+ curheap = tospace;
+ lim = curheap+heapsize-sizeof(cons_t);
+
+ for (i=0; i < SP; i++)
+ Stack[i] = relocate(Stack[i]);
+ trace_globals(symtab);
+ rs = readstate;
+ while (rs) {
+ for(i=0; i < rs->exprs.n; i++)
+ rs->exprs.items[i] = relocate(rs->exprs.items[i]);
+ rs = rs->prev;
+ }
+#ifdef VERBOSEGC
+ printf("gc found %d/%d live conses\n",
+ (curheap-tospace)/sizeof(cons_t), heapsize/sizeof(cons_t));
+#endif
+ temp = tospace;
+ tospace = fromspace;
+ fromspace = temp;
+
+ // if we're using > 80% of the space, resize tospace so we have
+ // more space to fill next time. if we grew tospace last time,
+ // grow the other half of the heap this time to catch up.
+ if (grew || ((lim-curheap) < (int)(heapsize/5)) || mustgrow) {
+ temp = realloc(tospace, grew ? heapsize : heapsize*2);
+ if (temp == NULL)
+ lerror("out of memory\n");
+ tospace = temp;
+ if (!grew) {
+ heapsize*=2;
+ }
+ else {
+ temp = (char*)bitvector_resize(consflags, heapsize/sizeof(cons_t));
+ if (temp == NULL)
+ lerror("out of memory\n");
+ consflags = (u_int32_t*)temp;
+ }
+ grew = !grew;
+ }
+ if (curheap > lim) // all data was live
+ gc(0);
+}
+
+// read -----------------------------------------------------------------------
+
+enum {
+ TOK_NONE, TOK_OPEN, TOK_CLOSE, TOK_DOT, TOK_QUOTE, TOK_SYM, TOK_NUM,
+ TOK_BQ, TOK_COMMA, TOK_COMMAAT, TOK_COMMADOT,
+ TOK_SHARPDOT, TOK_LABEL, TOK_BACKREF, TOK_SHARPQUOTE
+};
+
+static int symchar(char c)
+{
+ static char *special = "()';`,\\|";
+ return (!isspace(c) && !strchr(special, c));
+}
+
+static u_int32_t toktype = TOK_NONE;
+static value_t tokval;
+static char buf[256];
+
+static char nextchar(FILE *f)
+{
+ char c;
+ int ch;
+
+ do {
+ ch = fgetc(f);
+ if (ch == EOF)
+ return 0;
+ c = (char)ch;
+ if (c == ';') {
+ // single-line comment
+ do {
+ ch = fgetc(f);
+ if (ch == EOF)
+ return 0;
+ } while ((char)ch != '\n');
+ c = (char)ch;
+ }
+ } while (isspace(c));
+ return c;
+}
+
+static void take(void)
+{
+ toktype = TOK_NONE;
+}
+
+static void accumchar(char c, int *pi)
+{
+ buf[(*pi)++] = c;
+ if (*pi >= (int)(sizeof(buf)-1))
+ lerror("read: error: token too long\n");
+}
+
+// return: 1 for dot token, 0 for symbol
+static int read_token(FILE *f, char c, int digits)
+{
+ int i=0, ch, escaped=0, dot=(c=='.'), totread=0;
+
+ ungetc(c, f);
+ while (1) {
+ ch = fgetc(f); totread++;
+ if (ch == EOF)
+ goto terminate;
+ c = (char)ch;
+ if (c == '|') {
+ escaped = !escaped;
+ }
+ else if (c == '\\') {
+ ch = fgetc(f);
+ if (ch == EOF)
+ goto terminate;
+ accumchar((char)ch, &i);
+ }
+ else if (!escaped && !(symchar(c) && (!digits || isdigit(c)))) {
+ break;
+ }
+ else {
+ accumchar(c, &i);
+ }
+ }
+ ungetc(c, f);
+ terminate:
+ buf[i++] = '\0';
+ return (dot && (totread==2));
+}
+
+static u_int32_t peek(FILE *f)
+{
+ char c, *end;
+ number_t x;
+ int ch;
+
+ if (toktype != TOK_NONE)
+ return toktype;
+ c = nextchar(f);
+ if (feof(f)) return TOK_NONE;
+ if (c == '(') {
+ toktype = TOK_OPEN;
+ }
+ else if (c == ')') {
+ toktype = TOK_CLOSE;
+ }
+ else if (c == '\'') {
+ toktype = TOK_QUOTE;
+ }
+ else if (c == '`') {
+ toktype = TOK_BQ;
+ }
+ else if (c == '#') {
+ ch = fgetc(f);
+ if (ch == EOF)
+ lerror("read: error: invalid read macro\n");
+ if ((char)ch == '.') {
+ toktype = TOK_SHARPDOT;
+ }
+ else if ((char)ch == '\'') {
+ toktype = TOK_SHARPQUOTE;
+ }
+ else if (isdigit((char)ch)) {
+ read_token(f, (char)ch, 1);
+ c = fgetc(f);
+ if (c == '#')
+ toktype = TOK_BACKREF;
+ else if (c == '=')
+ toktype = TOK_LABEL;
+ else
+ lerror("read: error: invalid label\n");
+ x = strtol(buf, &end, 10);
+ tokval = number(x);
+ }
+ else {
+ lerror("read: error: unknown read macro\n");
+ }
+ }
+ else if (c == ',') {
+ toktype = TOK_COMMA;
+ ch = fgetc(f);
+ if (ch == EOF)
+ return toktype;
+ if ((char)ch == '@')
+ toktype = TOK_COMMAAT;
+ else if ((char)ch == '.')
+ toktype = TOK_COMMADOT;
+ else
+ ungetc((char)ch, f);
+ }
+ else if (isdigit(c) || c=='-') {
+ read_token(f, c, 0);
+ if (buf[0] == '-' && !isdigit(buf[1])) {
+ toktype = TOK_SYM;
+ tokval = symbol(buf);
+ }
+ else {
+ x = strtol(buf, &end, 10);
+ if (*end != '\0')
+ lerror("read: error: invalid integer constant\n");
+ toktype = TOK_NUM;
+ tokval = number(x);
+ }
+ }
+ else {
+ if (read_token(f, c, 0)) {
+ toktype = TOK_DOT;
+ }
+ else {
+ toktype = TOK_SYM;
+ tokval = symbol(buf);
+ }
+ }
+ return toktype;
+}
+
+static value_t do_read_sexpr(FILE *f, int fixup);
+
+// build a list of conses. this is complicated by the fact that all conses
+// can move whenever a new cons is allocated. we have to refer to every cons
+// through a handle to a relocatable pointer (i.e. a pointer on the stack).
+static void read_list(FILE *f, value_t *pval, int fixup)
+{
+ value_t c, *pc;
+ u_int32_t t;
+
+ PUSH(NIL);
+ pc = &Stack[SP-1]; // to keep track of current cons cell
+ t = peek(f);
+ while (t != TOK_CLOSE) {
+ if (feof(f))
+ lerror("read: error: unexpected end of input\n");
+ c = mk_cons(); car_(c) = cdr_(c) = NIL;
+ if (iscons(*pc)) {
+ cdr_(*pc) = c;
+ }
+ else {
+ *pval = c;
+ if (fixup != -1)
+ readstate->exprs.items[fixup] = c;
+ }
+ *pc = c;
+ c = do_read_sexpr(f,-1); // must be on separate lines due to undefined
+ car_(*pc) = c; // evaluation order
+
+ t = peek(f);
+ if (t == TOK_DOT) {
+ take();
+ c = do_read_sexpr(f,-1);
+ cdr_(*pc) = c;
+ t = peek(f);
+ if (feof(f))
+ lerror("read: error: unexpected end of input\n");
+ if (t != TOK_CLOSE)
+ lerror("read: error: expected ')'\n");
+ }
+ }
+ take();
+ POP();
+}
+
+// fixup is the index of the label we'd like to fix up with this read
+static value_t do_read_sexpr(FILE *f, int fixup)
+{
+ value_t v, *head;
+ u_int32_t t, l;
+ int i;
+
+ t = peek(f);
+ take();
+ switch (t) {
+ case TOK_CLOSE:
+ lerror("read: error: unexpected ')'\n");
+ case TOK_DOT:
+ lerror("read: error: unexpected '.'\n");
+ case TOK_SYM:
+ case TOK_NUM:
+ return tokval;
+ case TOK_COMMA:
+ head = &COMMA; goto listwith;
+ case TOK_COMMAAT:
+ head = &COMMAAT; goto listwith;
+ case TOK_COMMADOT:
+ head = &COMMADOT; goto listwith;
+ case TOK_BQ:
+ head = &BACKQUOTE; goto listwith;
+ case TOK_QUOTE:
+ head = "E;
+ listwith:
+ cons(head, cons(&NIL, &NIL));
+ if (fixup != -1)
+ readstate->exprs.items[fixup] = Stack[SP-1];
+ v = do_read_sexpr(f,-1);
+ car_(Stack[SP-2]) = v;
+ v = Stack[SP-1];
+ POPN(2);
+ return v;
+ case TOK_SHARPQUOTE:
+ // femtoLisp doesn't need symbol-function, so #' does nothing
+ return do_read_sexpr(f, fixup);
+ case TOK_OPEN:
+ PUSH(NIL);
+ read_list(f, &Stack[SP-1], fixup);
+ return POP();
+ case TOK_SHARPDOT:
+ // eval-when-read
+ // evaluated expressions can refer to existing backreferences, but they
+ // cannot see pending labels. in other words:
+ // (... #2=#.#0# ... ) OK
+ // (... #2=#.(#2#) ... ) DO NOT WANT
+ v = do_read_sexpr(f,-1);
+ return toplevel_eval(v);
+ case TOK_LABEL:
+ // create backreference label
+ l = numval(tokval);
+ if (ltable_lookup(&readstate->labels, l) != NOTFOUND)
+ lerror("read: error: label %d redefined\n", l);
+ ltable_insert(&readstate->labels, l);
+ i = readstate->exprs.n;
+ ltable_insert(&readstate->exprs, UNBOUND);
+ v = do_read_sexpr(f,i);
+ readstate->exprs.items[i] = v;
+ return v;
+ case TOK_BACKREF:
+ // look up backreference
+ l = numval(tokval);
+ i = ltable_lookup(&readstate->labels, l);
+ if (i == NOTFOUND || i >= (int)readstate->exprs.n ||
+ readstate->exprs.items[i] == UNBOUND)
+ lerror("read: error: undefined label %d\n", l);
+ return readstate->exprs.items[i];
+ }
+ return NIL;
+}
+
+value_t read_sexpr(FILE *f)
+{
+ value_t v;
+ readstate_t state;
+ state.prev = readstate;
+ ltable_init(&state.labels, 16);
+ ltable_init(&state.exprs, 16);
+ readstate = &state;
+
+ v = do_read_sexpr(f, -1);
+
+ readstate = state.prev;
+ free(state.labels.items);
+ free(state.exprs.items);
+ return v;
+}
+
+// print ----------------------------------------------------------------------
+
+static void print_traverse(value_t v)
+{
+ while (iscons(v)) {
+ if (ismarked(v)) {
+ ltable_adjoin(&printconses, v);
+ return;
+ }
+ mark_cons(v);
+ print_traverse(car_(v));
+ v = cdr_(v);
+ }
+}
+
+static void print_symbol(FILE *f, char *name)
+{
+ int i, escape=0, charescape=0;
+
+ if (name[0] == '\0') {
+ fprintf(f, "||");
+ return;
+ }
+ if (name[0] == '.' && name[1] == '\0') {
+ fprintf(f, "|.|");
+ return;
+ }
+ if (name[0] == '#')
+ escape = 1;
+ i=0;
+ while (name[i]) {
+ if (!symchar(name[i])) {
+ escape = 1;
+ if (name[i]=='|' || name[i]=='\\') {
+ charescape = 1;
+ break;
+ }
+ }
+ i++;
+ }
+ if (escape) {
+ if (charescape) {
+ fprintf(f, "|");
+ i=0;
+ while (name[i]) {
+ if (name[i]=='|' || name[i]=='\\')
+ fprintf(f, "\\%c", name[i]);
+ else
+ fprintf(f, "%c", name[i]);
+ i++;
+ }
+ fprintf(f, "|");
+ }
+ else {
+ fprintf(f, "|%s|", name);
+ }
+ }
+ else {
+ fprintf(f, "%s", name);
+ }
+}
+
+static void do_print(FILE *f, value_t v, int princ)
+{
+ value_t cd;
+ int label;
+ char *name;
+
+ switch (tag(v)) {
+ case TAG_NUM: fprintf(f, "%d", numval(v)); break;
+ case TAG_SYM:
+ name = ((symbol_t*)ptr(v))->name;
+ if (princ)
+ fprintf(f, "%s", name);
+ else
+ print_symbol(f, name);
+ break;
+ case TAG_BUILTIN: fprintf(f, "#.%s", builtin_names[intval(v)]); break;
+ case TAG_CONS:
+ if ((label=ltable_lookup(&printconses,v)) != NOTFOUND) {
+ if (!ismarked(v)) {
+ fprintf(f, "#%d#", label);
+ return;
+ }
+ fprintf(f, "#%d=", label);
+ }
+ fprintf(f, "(");
+ while (1) {
+ unmark_cons(v);
+ do_print(f, car_(v), princ);
+ cd = cdr_(v);
+ if (!iscons(cd)) {
+ if (cd != NIL) {
+ fprintf(f, " . ");
+ do_print(f, cd, princ);
+ }
+ fprintf(f, ")");
+ break;
+ }
+ else {
+ if ((label=ltable_lookup(&printconses,cd)) != NOTFOUND) {
+ fprintf(f, " . ");
+ do_print(f, cd, princ);
+ fprintf(f, ")");
+ break;
+ }
+ }
+ fprintf(f, " ");
+ v = cd;
+ }
+ break;
+ }
+}
+
+void print(FILE *f, value_t v, int princ)
+{
+ ltable_clear(&printconses);
+ print_traverse(v);
+ do_print(f, v, princ);
+}
+
+// eval -----------------------------------------------------------------------
+
+static inline void argcount(char *fname, int nargs, int c)
+{
+ if (nargs != c)
+ lerror("%s: error: too %s arguments\n", fname, nargs<c ? "few":"many");
+}
+
+// return a cons element of v whose car is item
+static value_t assoc(value_t item, value_t v)
+{
+ value_t bind;
+
+ while (iscons(v)) {
+ bind = car_(v);
+ if (iscons(bind) && car_(bind) == item)
+ return bind;
+ v = cdr_(v);
+ }
+ return NIL;
+}
+
+#define eval(e) ((tag(e)<0x2) ? (e) : eval_sexpr((e),penv,0,envend))
+#define topeval(e, env) ((tag(e)<0x2) ? (e) : eval_sexpr((e),env,1,SP))
+#define tail_eval(xpr) do { SP = saveSP; \
+ if (tag(xpr)<0x2) { return (xpr); } \
+ else { e=(xpr); goto eval_top; } } while (0)
+
+/* stack setup on entry:
+ n n+1 ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+
+ | SYM | VAL | SYM | VAL | CLO | | | |
+ +-----+-----+-----+-----+-----+-----+-----+-----+
+ ^ ^ ^
+ | | |
+ penv envend SP (who knows where)
+
+ sym is an argument name and val is its binding. CLO is a closed-up
+ environment list (which can be empty, i.e. NIL).
+ CLO is always there, but there might be zero SYM/VAL pairs.
+
+ if tail==1, you are allowed (indeed encouraged) to overwrite this
+ environment, otherwise you have to put any new environment on the top
+ of the stack.
+*/
+value_t eval_sexpr(value_t e, value_t *penv, int tail, u_int32_t envend)
+{
+ value_t f, v, headsym, asym, labl, *pv, *argsyms, *body, *lenv, *argenv;
+ cons_t *c;
+ symbol_t *sym;
+ u_int32_t saveSP;
+ int i, nargs, noeval=0;
+ number_t s, n;
+
+ eval_top:
+ if (issymbol(e)) {
+ sym = (symbol_t*)ptr(e);
+ if (sym->constant != UNBOUND) return sym->constant;
+ while (issymbol(*penv)) { // 1. try lookup in argument env
+ if (*penv == NIL)
+ goto get_global;
+ if (*penv == e)
+ return penv[1];
+ penv+=2;
+ }
+ if ((v=assoc(e,*penv)) != NIL) // 2. closure env
+ return cdr_(v);
+ get_global:
+ if ((v = sym->binding) == UNBOUND) // 3. global env
+ lerror("eval: error: variable %s has no value\n", sym->name);
+ return v;
+ }
+ if ((unsigned)(char*)&nargs < (unsigned)stack_bottom || SP>=(N_STACK-100))
+ lerror("eval: error: stack overflow\n");
+ saveSP = SP;
+ PUSH(e);
+ v = car_(e);
+ if (tag(v)<0x2) f = v;
+ else if (issymbol(v) && (f=((symbol_t*)ptr(v))->constant)!=UNBOUND) ;
+ else f = eval_sexpr(v, penv, 0, envend);
+ if (isbuiltin(f)) {
+ // handle builtin function
+ if (!isspecial(f)) {
+ // evaluate argument list, placing arguments on stack
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ while (iscons(v)) {
+ v = eval(car_(v));
+ PUSH(v);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ }
+ apply_builtin:
+ nargs = SP - saveSP - 1;
+ switch (intval(f)) {
+ // special forms
+ case F_QUOTE:
+ v = cdr_(Stack[saveSP]);
+ if (!iscons(v)) lerror("quote: error: expected argument\n");
+ v = car_(v);
+ break;
+ case F_MACRO:
+ case F_LAMBDA:
+ if (*penv != NIL) {
+ // build a closure (lambda args body . env)
+ if (issymbol(*penv)) {
+ // cons up and save temporary environment
+ PUSH(Stack[envend-1]); // passed-in CLOENV
+ // find out how many new conses we need
+ nargs = ((int)(&Stack[envend] - penv - 1))>>1;
+ if (nargs) {
+ lenv = penv;
+ v = Stack[SP-1] = cons_reserve(nargs*2);
+ while (1) {
+ e = cdr_(cdr_(v));
+ car_(v) = cdr_(v);
+ car_(cdr_(v)) = penv[0];
+ cdr_(cdr_(v)) = penv[1];
+ nargs--;
+ if (nargs==0) break;
+ penv+=2;
+ cdr_(v) = e;
+ v = e;
+ }
+ // final cdr points to existing cloenv
+ cdr_(v) = Stack[envend-1];
+ // environment representation changed; install
+ // the new representation so everybody can see it
+ *lenv = Stack[SP-1];
+ }
+ }
+ else {
+ PUSH(*penv); // env has already been captured; recapture
+ }
+ v = cdr_(Stack[saveSP]);
+ PUSH(car(v));
+ PUSH(car(cdr_(v)));
+ v = cons_reserve(3);
+ car_(v) = (intval(f)==F_LAMBDA ? LAMBDA : MACRO); f = cdr_(v);
+ car_(f) = Stack[SP-2]; f = cdr_(f); //argsyms
+ car_(f) = Stack[SP-1]; //body
+ cdr_(f) = Stack[SP-3]; //env
+ }
+ else {
+ v = Stack[saveSP];
+ }
+ break;
+ case F_LABEL:
+ v = Stack[saveSP];
+ if (*penv != NIL) {
+ v = cdr_(v);
+ PUSH(car(v));
+ PUSH(car(cdr_(v)));
+ body = &Stack[SP-1];
+ *body = eval(*body); // evaluate lambda
+ v = f = cons_reserve(3);
+ car_(f) = LABEL; f = cdr_(f);
+ car_(f) = Stack[SP-2]; f = cdr_(f); // name
+ car_(f) = *body; // lambda expr
+ }
+ break;
+ case F_IF:
+ v = car(cdr_(Stack[saveSP]));
+ if (eval(v) != NIL)
+ v = car(cdr_(cdr_(Stack[saveSP])));
+ else
+ v = car(cdr(cdr_(cdr_(Stack[saveSP]))));
+ tail_eval(v);
+ break;
+ case F_COND:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ while (iscons(*pv)) {
+ c = tocons(car_(*pv), "cond");
+ v = eval(c->car);
+ if (v != NIL) {
+ *pv = cdr_(car_(*pv));
+ // evaluate body forms
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ v = eval(car_(*pv));
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv));
+ }
+ break;
+ }
+ *pv = cdr_(*pv);
+ }
+ break;
+ case F_AND:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = T;
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ if ((v=eval(car_(*pv))) == NIL) {
+ SP = saveSP; return NIL;
+ }
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv));
+ }
+ break;
+ case F_OR:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ if ((v=eval(car_(*pv))) != NIL) {
+ SP = saveSP; return v;
+ }
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv));
+ }
+ break;
+ case F_WHILE:
+ PUSH(cdr(cdr_(Stack[saveSP])));
+ body = &Stack[SP-1];
+ PUSH(*body);
+ Stack[saveSP] = car_(cdr_(Stack[saveSP]));
+ value_t *cond = &Stack[saveSP];
+ PUSH(NIL);
+ pv = &Stack[SP-1];
+ while (eval(*cond) != NIL) {
+ *body = Stack[SP-2];
+ while (iscons(*body)) {
+ *pv = eval(car_(*body));
+ *body = cdr_(*body);
+ }
+ }
+ v = *pv;
+ break;
+ case F_PROGN:
+ // return last arg
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ v = eval(car_(*pv));
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv));
+ }
+ break;
+
+ // ordinary functions
+ case F_SET:
+ argcount("set", nargs, 2);
+ e = Stack[SP-2];
+ while (issymbol(*penv)) {
+ if (*penv == NIL)
+ goto set_global;
+ if (*penv == e) {
+ penv[1] = Stack[SP-1];
+ SP=saveSP; return penv[1];
+ }
+ penv+=2;
+ }
+ if ((v=assoc(e,*penv)) != NIL) {
+ cdr_(v) = (e=Stack[SP-1]);
+ SP=saveSP; return e;
+ }
+ set_global:
+ tosymbol(e, "set")->binding = (v=Stack[SP-1]);
+ break;
+ case F_BOUNDP:
+ argcount("boundp", nargs, 1);
+ sym = tosymbol(Stack[SP-1], "boundp");
+ if (sym->binding == UNBOUND && sym->constant == UNBOUND)
+ v = NIL;
+ else
+ v = T;
+ break;
+ case F_EQ:
+ argcount("eq", nargs, 2);
+ v = ((Stack[SP-2] == Stack[SP-1]) ? T : NIL);
+ break;
+ case F_CONS:
+ argcount("cons", nargs, 2);
+ v = mk_cons();
+ car_(v) = Stack[SP-2];
+ cdr_(v) = Stack[SP-1];
+ break;
+ case F_CAR:
+ argcount("car", nargs, 1);
+ v = car(Stack[SP-1]);
+ break;
+ case F_CDR:
+ argcount("cdr", nargs, 1);
+ v = cdr(Stack[SP-1]);
+ break;
+ case F_RPLACA:
+ argcount("rplaca", nargs, 2);
+ car(v=Stack[SP-2]) = Stack[SP-1];
+ break;
+ case F_RPLACD:
+ argcount("rplacd", nargs, 2);
+ cdr(v=Stack[SP-2]) = Stack[SP-1];
+ break;
+ case F_ATOM:
+ argcount("atom", nargs, 1);
+ v = ((!iscons(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_CONSP:
+ argcount("consp", nargs, 1);
+ v = (iscons(Stack[SP-1]) ? T : NIL);
+ break;
+ case F_SYMBOLP:
+ argcount("symbolp", nargs, 1);
+ v = ((issymbol(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_NUMBERP:
+ argcount("numberp", nargs, 1);
+ v = ((isnumber(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_ADD:
+ s = 0;
+ for (i=saveSP+1; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "+");
+ s += n;
+ }
+ v = number(s);
+ break;
+ case F_SUB:
+ if (nargs < 1) lerror("-: error: too few arguments\n");
+ i = saveSP+1;
+ s = (nargs==1) ? 0 : tonumber(Stack[i++], "-");
+ for (; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "-");
+ s -= n;
+ }
+ v = number(s);
+ break;
+ case F_MUL:
+ s = 1;
+ for (i=saveSP+1; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "*");
+ s *= n;
+ }
+ v = number(s);
+ break;
+ case F_DIV:
+ if (nargs < 1) lerror("/: error: too few arguments\n");
+ i = saveSP+1;
+ s = (nargs==1) ? 1 : tonumber(Stack[i++], "/");
+ for (; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "/");
+ if (n == 0) lerror("/: error: division by zero\n");
+ s /= n;
+ }
+ v = number(s);
+ break;
+ case F_LT:
+ argcount("<", nargs, 2);
+ // this implements generic comparison for all atoms
+ // strange comparisons (for example with builtins) are resolved
+ // arbitrarily but consistently.
+ // ordering: number < builtin < symbol < cons
+ if (tag(Stack[SP-2]) != tag(Stack[SP-1])) {
+ v = (tag(Stack[SP-2]) < tag(Stack[SP-1]) ? T : NIL);
+ }
+ else {
+ switch (tag(Stack[SP-2])) {
+ case TAG_NUM:
+ v = (numval(Stack[SP-2]) < numval(Stack[SP-1])) ? T : NIL;
+ break;
+ case TAG_SYM:
+ v = (strcmp(((symbol_t*)ptr(Stack[SP-2]))->name,
+ ((symbol_t*)ptr(Stack[SP-1]))->name) < 0) ?
+ T : NIL;
+ break;
+ case TAG_BUILTIN:
+ v = (intval(Stack[SP-2]) < intval(Stack[SP-1])) ? T : NIL;
+ break;
+ case TAG_CONS:
+ lerror("<: error: expected atom\n");
+ }
+ }
+ break;
+ case F_NOT:
+ argcount("not", nargs, 1);
+ v = ((Stack[SP-1] == NIL) ? T : NIL);
+ break;
+ case F_EVAL:
+ argcount("eval", nargs, 1);
+ v = Stack[SP-1];
+ if (tag(v)<0x2) { SP=saveSP; return v; }
+ if (tail) {
+ *penv = NIL;
+ envend = SP = (u_int32_t)(penv-&Stack[0]) + 1;
+ e=v; goto eval_top;
+ }
+ else {
+ PUSH(NIL);
+ v = eval_sexpr(v, &Stack[SP-1], 1, SP);
+ }
+ break;
+ case F_PRINT:
+ for (i=saveSP+1; i < (int)SP; i++)
+ print(stdout, v=Stack[i], 0);
+ fprintf(stdout, "\n");
+ break;
+ case F_PRINC:
+ for (i=saveSP+1; i < (int)SP; i++)
+ print(stdout, v=Stack[i], 1);
+ break;
+ case F_READ:
+ argcount("read", nargs, 0);
+ v = read_sexpr(stdin);
+ break;
+ case F_LOAD:
+ argcount("load", nargs, 1);
+ v = load_file(tosymbol(Stack[SP-1], "load")->name);
+ break;
+ case F_EXIT:
+ exit(0);
+ break;
+ case F_ERROR:
+ for (i=saveSP+1; i < (int)SP; i++)
+ print(stderr, Stack[i], 1);
+ lerror("\n");
+ break;
+ case F_PROG1:
+ // return first arg
+ if (nargs < 1) lerror("prog1: error: too few arguments\n");
+ v = Stack[saveSP+1];
+ break;
+ case F_ASSOC:
+ argcount("assoc", nargs, 2);
+ v = assoc(Stack[SP-2], Stack[SP-1]);
+ break;
+ case F_APPLY:
+ argcount("apply", nargs, 2);
+ v = Stack[saveSP] = Stack[SP-1]; // second arg is new arglist
+ f = Stack[SP-2]; // first arg is new function
+ POPN(2); // pop apply's args
+ if (isbuiltin(f)) {
+ if (isspecial(f))
+ lerror("apply: error: cannot apply special operator "
+ "%s\n", builtin_names[intval(f)]);
+ // unpack arglist onto the stack
+ while (iscons(v)) {
+ PUSH(car_(v));
+ v = cdr_(v);
+ }
+ goto apply_builtin;
+ }
+ noeval = 1;
+ goto apply_lambda;
+ }
+ SP = saveSP;
+ return v;
+ }
+ else {
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ apply_lambda:
+ if (iscons(f)) {
+ headsym = car_(f);
+ if (headsym == LABEL) {
+ // (label name (lambda ...)) behaves the same as the lambda
+ // alone, except with name bound to the whole label expression
+ labl = f;
+ f = car(cdr(cdr_(labl)));
+ headsym = car(f);
+ } else labl=0;
+ // apply lambda or macro expression
+ PUSH(cdr(cdr_(f)));
+ PUSH(car_(cdr_(f)));
+ argsyms = &Stack[SP-1];
+ argenv = &Stack[SP]; // argument environment starts now
+ if (labl) {
+ // add label binding to environment
+ PUSH(car_(cdr_(labl)));
+ PUSH(labl);
+ }
+ if (headsym == MACRO)
+ noeval = 1;
+ //else if (headsym != LAMBDA)
+ // lerror("apply: error: head must be lambda, macro, or label\n");
+ // build a calling environment for the lambda
+ // the environment is the argument binds on top of the captured
+ // environment
+ while (iscons(v)) {
+ // bind args
+ if (!iscons(*argsyms)) {
+ if (*argsyms == NIL)
+ lerror("apply: error: too many arguments\n");
+ break;
+ }
+ asym = car_(*argsyms);
+ if (asym==NIL || iscons(asym))
+ lerror("apply: error: invalid formal argument\n");
+ v = car_(v);
+ if (!noeval) {
+ v = eval(v);
+ }
+ PUSH(asym);
+ PUSH(v);
+ *argsyms = cdr_(*argsyms);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ if (*argsyms != NIL) {
+ if (issymbol(*argsyms)) {
+ PUSH(*argsyms);
+ if (noeval) {
+ PUSH(Stack[saveSP]);
+ }
+ else {
+ // this version uses collective allocation. about 7-10%
+ // faster for lists with > 2 elements, but uses more
+ // stack space
+ PUSH(NIL);
+ i = SP;
+ while (iscons(Stack[saveSP])) {
+ PUSH(eval(car_(Stack[saveSP])));
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ nargs = SP-i;
+ if (nargs) {
+ Stack[i-1] = v = cons_reserve(nargs);
+ for(; i < (int)SP; i++) {
+ car_(v) = Stack[i];
+ v = cdr_(v);
+ }
+ POPN(nargs);
+ }
+ }
+ }
+ else if (iscons(*argsyms)) {
+ lerror("apply: error: too few arguments\n");
+ }
+ }
+ noeval = 0;
+ lenv = &Stack[saveSP+1];
+ PUSH(cdr(*lenv)); // add cloenv to new environment
+ e = car_(Stack[saveSP+1]);
+ // macro: evaluate expansion in the calling environment
+ if (headsym == MACRO) {
+ if (tag(e)<0x2) ;
+ else e = eval_sexpr(e, argenv, 1, SP);
+ SP = saveSP;
+ if (tag(e)<0x2) return(e);
+ goto eval_top;
+ }
+ else {
+ if (tag(e)<0x2) { SP=saveSP; return(e); }
+ if (tail) {
+ // ok to overwrite environment
+ nargs = (int)(&Stack[SP] - argenv);
+ for(i=0; i < nargs; i++)
+ penv[i] = argenv[i];
+ envend = SP = (u_int32_t)((penv+nargs) - &Stack[0]);
+ goto eval_top;
+ }
+ else {
+ v = eval_sexpr(e, argenv, 1, SP);
+ SP = saveSP;
+ return v;
+ }
+ }
+ // not reached
+ }
+ type_error("apply", "function", f);
+ return NIL;
+}
+
+// repl -----------------------------------------------------------------------
+
+static char *infile = NULL;
+
+value_t toplevel_eval(value_t expr)
+{
+ value_t v;
+ PUSH(NIL);
+ v = topeval(expr, &Stack[SP-1]);
+ POP();
+ return v;
+}
+
+value_t load_file(char *fname)
+{
+ value_t e, v=NIL;
+ char *lastfile = infile;
+ FILE *f = fopen(fname, "r");
+ infile = fname;
+ if (f == NULL) lerror("file not found\n");
+ while (1) {
+ e = read_sexpr(f);
+ if (feof(f)) break;
+ v = toplevel_eval(e);
+ }
+ infile = lastfile;
+ fclose(f);
+ return v;
+}
+
+int main(int argc, char* argv[])
+{
+ value_t v;
+
+ stack_bottom = ((char*)&v) - PROCESS_STACK_SIZE;
+ lisp_init();
+ if (setjmp(toplevel)) {
+ SP = 0;
+ fprintf(stderr, "\n");
+ if (infile) {
+ fprintf(stderr, "error loading file \"%s\"\n", infile);
+ infile = NULL;
+ }
+ goto repl;
+ }
+ load_file("system.lsp");
+ if (argc > 1) { load_file(argv[1]); return 0; }
+ printf("; _ \n");
+ printf("; |_ _ _ |_ _ | . _ _ 2\n");
+ printf("; | (-||||_(_)|__|_)|_)\n");
+ printf(";-------------------|----------------------------------------------------------\n\n");
+ repl:
+ while (1) {
+ printf("> ");
+ v = read_sexpr(stdin);
+ if (feof(stdin)) break;
+ print(stdout, v=toplevel_eval(v), 0);
+ set(symbol("that"), v);
+ printf("\n\n");
+ }
+ return 0;
+}
binary files /dev/null b/femtolisp/tiny/lispf differ
--- /dev/null
+++ b/femtolisp/tiny/lispf.c
@@ -1,0 +1,1043 @@
+/*
+ femtoLisp
+
+ a minimal interpreter for a minimal lisp dialect
+
+ this lisp dialect uses lexical scope and self-evaluating lambda.
+ it supports 30-bit integers, symbols, conses, and full macros.
+ it is case-sensitive.
+ it features a simple compacting copying garbage collector.
+ it uses a Scheme-style evaluation rule where any expression may appear in
+ head position as long as it evaluates to a function.
+ it uses Scheme-style varargs (dotted formal argument lists)
+ lambdas can have only 1 body expression; use (progn ...) for multiple
+ expressions. this is due to the closure representation
+ (lambda args body . env)
+
+ lispf is a fork that provides an #ifdef FLOAT option to use single-precision
+ floating point numbers instead of integers, albeit with even less precision
+ than usual---only 21 significant mantissa bits!
+
+ it is now also being used to test a tail-recursive evaluator.
+
+ by Jeff Bezanson
+ Public Domain
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <sys/types.h>
+
+typedef u_int32_t value_t;
+#ifdef FLOAT
+typedef float number_t;
+#else
+typedef int32_t number_t;
+#endif
+
+typedef struct {
+ value_t car;
+ value_t cdr;
+} cons_t;
+
+typedef struct _symbol_t {
+ value_t binding; // global value binding
+ value_t constant; // constant binding (used only for builtins)
+ struct _symbol_t *left;
+ struct _symbol_t *right;
+ char name[1];
+} symbol_t;
+
+#define TAG_NUM 0x0
+#define TAG_BUILTIN 0x1
+#define TAG_SYM 0x2
+#define TAG_CONS 0x3
+#define UNBOUND ((value_t)TAG_SYM) // an invalid symbol pointer
+#define tag(x) ((x)&0x3)
+#define ptr(x) ((void*)((x)&(~(value_t)0x3)))
+#define tagptr(p,t) (((value_t)(p)) | (t))
+#ifdef FLOAT
+#define number(x) ((*(value_t*)&(x))&~0x3)
+#define numval(x) (*(number_t*)&(x))
+#define NUM_FORMAT "%f"
+extern float strtof(const char *nptr, char **endptr);
+#define strtonum(s, e) strtof(s, e)
+#else
+#define number(x) ((value_t)((x)<<2))
+#define numval(x) (((number_t)(x))>>2)
+#define NUM_FORMAT "%d"
+#define strtonum(s, e) strtol(s, e, 10)
+#endif
+#define intval(x) (((int)(x))>>2)
+#define builtin(n) tagptr((((int)n)<<2), TAG_BUILTIN)
+#define iscons(x) (tag(x) == TAG_CONS)
+#define issymbol(x) (tag(x) == TAG_SYM)
+#define isnumber(x) (tag(x) == TAG_NUM)
+#define isbuiltin(x) (tag(x) == TAG_BUILTIN)
+// functions ending in _ are unsafe, faster versions
+#define car_(v) (((cons_t*)ptr(v))->car)
+#define cdr_(v) (((cons_t*)ptr(v))->cdr)
+#define car(v) (tocons((v),"car")->car)
+#define cdr(v) (tocons((v),"cdr")->cdr)
+#define set(s, v) (((symbol_t*)ptr(s))->binding = (v))
+#define setc(s, v) (((symbol_t*)ptr(s))->constant = (v))
+
+enum {
+ // special forms
+ F_QUOTE=0, F_COND, F_IF, F_AND, F_OR, F_WHILE, F_LAMBDA, F_MACRO, F_LABEL,
+ F_PROGN,
+ // functions
+ F_EQ, F_ATOM, F_CONS, F_CAR, F_CDR, F_READ, F_EVAL, F_PRINT, F_SET, F_NOT,
+ F_LOAD, F_SYMBOLP, F_NUMBERP, F_ADD, F_SUB, F_MUL, F_DIV, F_LT, F_PROG1,
+ F_APPLY, F_RPLACA, F_RPLACD, F_BOUNDP, N_BUILTINS
+};
+#define isspecial(v) (intval(v) <= (int)F_PROGN)
+
+static char *builtin_names[] =
+ { "quote", "cond", "if", "and", "or", "while", "lambda", "macro", "label",
+ "progn", "eq", "atom", "cons", "car", "cdr", "read", "eval", "print",
+ "set", "not", "load", "symbolp", "numberp", "+", "-", "*", "/", "<",
+ "prog1", "apply", "rplaca", "rplacd", "boundp" };
+
+static char *stack_bottom;
+#define PROCESS_STACK_SIZE (2*1024*1024)
+#define N_STACK 49152
+static value_t Stack[N_STACK];
+static u_int32_t SP = 0;
+#define PUSH(v) (Stack[SP++] = (v))
+#define POP() (Stack[--SP])
+#define POPN(n) (SP-=(n))
+
+value_t NIL, T, LAMBDA, MACRO, LABEL, QUOTE;
+
+value_t read_sexpr(FILE *f);
+void print(FILE *f, value_t v);
+value_t eval_sexpr(value_t e, value_t *penv);
+value_t load_file(char *fname);
+
+// error utilities ------------------------------------------------------------
+
+jmp_buf toplevel;
+
+void lerror(char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ longjmp(toplevel, 1);
+}
+
+void type_error(char *fname, char *expected, value_t got)
+{
+ fprintf(stderr, "%s: error: expected %s, got ", fname, expected);
+ print(stderr, got); lerror("\n");
+}
+
+// safe cast operators --------------------------------------------------------
+
+#define SAFECAST_OP(type,ctype,cnvt) \
+ctype to##type(value_t v, char *fname) \
+{ \
+ if (is##type(v)) \
+ return (ctype)cnvt(v); \
+ type_error(fname, #type, v); \
+ return (ctype)0; \
+}
+SAFECAST_OP(cons, cons_t*, ptr)
+SAFECAST_OP(symbol,symbol_t*,ptr)
+SAFECAST_OP(number,number_t, numval)
+
+// symbol table ---------------------------------------------------------------
+
+static symbol_t *symtab = NULL;
+
+static symbol_t *mk_symbol(char *str)
+{
+ symbol_t *sym;
+
+ sym = (symbol_t*)malloc(sizeof(symbol_t) + strlen(str));
+ sym->left = sym->right = NULL;
+ sym->constant = sym->binding = UNBOUND;
+ strcpy(&sym->name[0], str);
+ return sym;
+}
+
+static symbol_t **symtab_lookup(symbol_t **ptree, char *str)
+{
+ int x;
+
+ while(*ptree != NULL) {
+ x = strcmp(str, (*ptree)->name);
+ if (x == 0)
+ return ptree;
+ if (x < 0)
+ ptree = &(*ptree)->left;
+ else
+ ptree = &(*ptree)->right;
+ }
+ return ptree;
+}
+
+value_t symbol(char *str)
+{
+ symbol_t **pnode;
+
+ pnode = symtab_lookup(&symtab, str);
+ if (*pnode == NULL)
+ *pnode = mk_symbol(str);
+ return tagptr(*pnode, TAG_SYM);
+}
+
+// initialization -------------------------------------------------------------
+
+static unsigned char *fromspace;
+static unsigned char *tospace;
+static unsigned char *curheap;
+static unsigned char *lim;
+static u_int32_t heapsize = 64*1024;//bytes
+
+void lisp_init(void)
+{
+ int i;
+
+ fromspace = malloc(heapsize);
+ tospace = malloc(heapsize);
+ curheap = fromspace;
+ lim = curheap+heapsize-sizeof(cons_t);
+
+ NIL = symbol("nil"); setc(NIL, NIL);
+ T = symbol("t"); setc(T, T);
+ LAMBDA = symbol("lambda");
+ MACRO = symbol("macro");
+ LABEL = symbol("label");
+ QUOTE = symbol("quote");
+ for (i=0; i < (int)N_BUILTINS; i++)
+ setc(symbol(builtin_names[i]), builtin(i));
+ setc(symbol("princ"), builtin(F_PRINT));
+}
+
+// conses ---------------------------------------------------------------------
+
+void gc(void);
+
+static value_t mk_cons(void)
+{
+ cons_t *c;
+
+ if (curheap > lim)
+ gc();
+ c = (cons_t*)curheap;
+ curheap += sizeof(cons_t);
+ return tagptr(c, TAG_CONS);
+}
+
+static value_t cons_(value_t *pcar, value_t *pcdr)
+{
+ value_t c = mk_cons();
+ car_(c) = *pcar; cdr_(c) = *pcdr;
+ return c;
+}
+
+value_t *cons(value_t *pcar, value_t *pcdr)
+{
+ value_t c = mk_cons();
+ car_(c) = *pcar; cdr_(c) = *pcdr;
+ PUSH(c);
+ return &Stack[SP-1];
+}
+
+// collector ------------------------------------------------------------------
+
+static value_t relocate(value_t v)
+{
+ value_t a, d, nc;
+
+ if (!iscons(v))
+ return v;
+ if (car_(v) == UNBOUND)
+ return cdr_(v);
+ nc = mk_cons(); car_(nc) = NIL;
+ a = car_(v); d = cdr_(v);
+ car_(v) = UNBOUND; cdr_(v) = nc;
+ car_(nc) = relocate(a);
+ cdr_(nc) = relocate(d);
+ return nc;
+}
+
+static void trace_globals(symbol_t *root)
+{
+ while (root != NULL) {
+ root->binding = relocate(root->binding);
+ trace_globals(root->left);
+ root = root->right;
+ }
+}
+
+void gc(void)
+{
+ static int grew = 0;
+ unsigned char *temp;
+ u_int32_t i;
+
+ curheap = tospace;
+ lim = curheap+heapsize-sizeof(cons_t);
+
+ for (i=0; i < SP; i++)
+ Stack[i] = relocate(Stack[i]);
+ trace_globals(symtab);
+#ifdef VERBOSEGC
+ printf("gc found %d/%d live conses\n", (curheap-tospace)/8, heapsize/8);
+#endif
+ temp = tospace;
+ tospace = fromspace;
+ fromspace = temp;
+
+ // if we're using > 80% of the space, resize tospace so we have
+ // more space to fill next time. if we grew tospace last time,
+ // grow the other half of the heap this time to catch up.
+ if (grew || ((lim-curheap) < (int)(heapsize/5))) {
+ temp = realloc(tospace, grew ? heapsize : heapsize*2);
+ if (temp == NULL)
+ lerror("out of memory\n");
+ tospace = temp;
+ if (!grew)
+ heapsize*=2;
+ grew = !grew;
+ }
+ if (curheap > lim) // all data was live
+ gc();
+}
+
+// read -----------------------------------------------------------------------
+
+enum {
+ TOK_NONE, TOK_OPEN, TOK_CLOSE, TOK_DOT, TOK_QUOTE, TOK_SYM, TOK_NUM
+};
+
+static int symchar(char c)
+{
+ static char *special = "()';\\|";
+ return (!isspace(c) && !strchr(special, c));
+}
+
+static u_int32_t toktype = TOK_NONE;
+static value_t tokval;
+static char buf[256];
+
+static char nextchar(FILE *f)
+{
+ char c;
+ int ch;
+
+ do {
+ ch = fgetc(f);
+ if (ch == EOF)
+ return 0;
+ c = (char)ch;
+ if (c == ';') {
+ // single-line comment
+ do {
+ ch = fgetc(f);
+ if (ch == EOF)
+ return 0;
+ } while ((char)ch != '\n');
+ c = (char)ch;
+ }
+ } while (isspace(c));
+ return c;
+}
+
+static void take(void)
+{
+ toktype = TOK_NONE;
+}
+
+static void accumchar(char c, int *pi)
+{
+ buf[(*pi)++] = c;
+ if (*pi >= (int)(sizeof(buf)-1))
+ lerror("read: error: token too long\n");
+}
+
+static int read_token(FILE *f, char c)
+{
+ int i=0, ch, escaped=0;
+
+ ungetc(c, f);
+ while (1) {
+ ch = fgetc(f);
+ if (ch == EOF)
+ goto terminate;
+ c = (char)ch;
+ if (c == '|') {
+ escaped = !escaped;
+ }
+ else if (c == '\\') {
+ ch = fgetc(f);
+ if (ch == EOF)
+ goto terminate;
+ accumchar((char)ch, &i);
+ }
+ else if (!escaped && !symchar(c)) {
+ break;
+ }
+ else {
+ accumchar(c, &i);
+ }
+ }
+ ungetc(c, f);
+ terminate:
+ buf[i++] = '\0';
+ return i;
+}
+
+static u_int32_t peek(FILE *f)
+{
+ char c, *end;
+ number_t x;
+
+ if (toktype != TOK_NONE)
+ return toktype;
+ c = nextchar(f);
+ if (feof(f)) return TOK_NONE;
+ if (c == '(') {
+ toktype = TOK_OPEN;
+ }
+ else if (c == ')') {
+ toktype = TOK_CLOSE;
+ }
+ else if (c == '\'') {
+ toktype = TOK_QUOTE;
+ }
+ else if (isdigit(c) || c=='-') {
+ read_token(f, c);
+ if (buf[0] == '-' && !isdigit(buf[1])) {
+ toktype = TOK_SYM;
+ tokval = symbol(buf);
+ }
+ else {
+ x = strtonum(buf, &end);
+ if (*end != '\0')
+ lerror("read: error: invalid constant\n");
+ toktype = TOK_NUM;
+ tokval = number(x);
+ }
+ }
+ else {
+ read_token(f, c);
+ if (!strcmp(buf, ".")) {
+ toktype = TOK_DOT;
+ }
+ else {
+ toktype = TOK_SYM;
+ tokval = symbol(buf);
+ }
+ }
+ return toktype;
+}
+
+// build a list of conses. this is complicated by the fact that all conses
+// can move whenever a new cons is allocated. we have to refer to every cons
+// through a handle to a relocatable pointer (i.e. a pointer on the stack).
+static void read_list(FILE *f, value_t *pval)
+{
+ value_t c, *pc;
+ u_int32_t t;
+
+ PUSH(NIL);
+ pc = &Stack[SP-1]; // to keep track of current cons cell
+ t = peek(f);
+ while (t != TOK_CLOSE) {
+ if (feof(f))
+ lerror("read: error: unexpected end of input\n");
+ c = mk_cons(); car_(c) = cdr_(c) = NIL;
+ if (iscons(*pc))
+ cdr_(*pc) = c;
+ else
+ *pval = c;
+ *pc = c;
+ c = read_sexpr(f); // must be on separate lines due to undefined
+ car_(*pc) = c; // evaluation order
+
+ t = peek(f);
+ if (t == TOK_DOT) {
+ take();
+ c = read_sexpr(f);
+ cdr_(*pc) = c;
+ t = peek(f);
+ if (feof(f))
+ lerror("read: error: unexpected end of input\n");
+ if (t != TOK_CLOSE)
+ lerror("read: error: expected ')'\n");
+ }
+ }
+ take();
+ POP();
+}
+
+value_t read_sexpr(FILE *f)
+{
+ value_t v;
+
+ switch (peek(f)) {
+ case TOK_CLOSE:
+ take();
+ lerror("read: error: unexpected ')'\n");
+ case TOK_DOT:
+ take();
+ lerror("read: error: unexpected '.'\n");
+ case TOK_SYM:
+ case TOK_NUM:
+ take();
+ return tokval;
+ case TOK_QUOTE:
+ take();
+ v = read_sexpr(f);
+ PUSH(v);
+ v = cons_("E, cons(&Stack[SP-1], &NIL));
+ POPN(2);
+ return v;
+ case TOK_OPEN:
+ take();
+ PUSH(NIL);
+ read_list(f, &Stack[SP-1]);
+ return POP();
+ }
+ return NIL;
+}
+
+// print ----------------------------------------------------------------------
+
+void print(FILE *f, value_t v)
+{
+ value_t cd;
+
+ switch (tag(v)) {
+ case TAG_NUM: fprintf(f, NUM_FORMAT, numval(v)); break;
+ case TAG_SYM: fprintf(f, "%s", ((symbol_t*)ptr(v))->name); break;
+ case TAG_BUILTIN: fprintf(f, "#<builtin %s>",
+ builtin_names[intval(v)]); break;
+ case TAG_CONS:
+ fprintf(f, "(");
+ while (1) {
+ print(f, car_(v));
+ cd = cdr_(v);
+ if (!iscons(cd)) {
+ if (cd != NIL) {
+ fprintf(f, " . ");
+ print(f, cd);
+ }
+ fprintf(f, ")");
+ break;
+ }
+ fprintf(f, " ");
+ v = cd;
+ }
+ break;
+ }
+}
+
+// eval -----------------------------------------------------------------------
+
+static inline void argcount(char *fname, int nargs, int c)
+{
+ if (nargs != c)
+ lerror("%s: error: too %s arguments\n", fname, nargs<c ? "few":"many");
+}
+
+#define eval(e, penv) ((tag(e)<0x2) ? (e) : eval_sexpr((e),penv))
+#define tail_eval(xpr, env) do { SP = saveSP; \
+ if (tag(xpr)<0x2) { return (xpr); } \
+ else { e=(xpr); *penv=(env); goto eval_top; } } while (0)
+
+value_t eval_sexpr(value_t e, value_t *penv)
+{
+ value_t f, v, bind, headsym, asym, labl=0, *pv, *argsyms, *body, *lenv;
+ value_t *rest;
+ cons_t *c;
+ symbol_t *sym;
+ u_int32_t saveSP;
+ int i, nargs, noeval=0;
+ number_t s, n;
+
+ eval_top:
+ if (issymbol(e)) {
+ sym = (symbol_t*)ptr(e);
+ if (sym->constant != UNBOUND) return sym->constant;
+ v = *penv;
+ while (iscons(v)) {
+ bind = car_(v);
+ if (iscons(bind) && car_(bind) == e)
+ return cdr_(bind);
+ v = cdr_(v);
+ }
+ if ((v = sym->binding) == UNBOUND)
+ lerror("eval: error: variable %s has no value\n", sym->name);
+ return v;
+ }
+ if ((unsigned)(char*)&nargs < (unsigned)stack_bottom || SP>=(N_STACK-100))
+ lerror("eval: error: stack overflow\n");
+ saveSP = SP;
+ PUSH(e);
+ PUSH(*penv);
+ f = eval(car_(e), penv);
+ *penv = Stack[saveSP+1];
+ if (isbuiltin(f)) {
+ // handle builtin function
+ if (!isspecial(f)) {
+ // evaluate argument list, placing arguments on stack
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ while (iscons(v)) {
+ v = eval(car_(v), penv);
+ *penv = Stack[saveSP+1];
+ PUSH(v);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ }
+ apply_builtin:
+ nargs = SP - saveSP - 2;
+ switch (intval(f)) {
+ // special forms
+ case F_QUOTE:
+ v = cdr_(Stack[saveSP]);
+ if (!iscons(v))
+ lerror("quote: error: expected argument\n");
+ v = car_(v);
+ break;
+ case F_MACRO:
+ case F_LAMBDA:
+ v = Stack[saveSP];
+ if (*penv != NIL) {
+ // build a closure (lambda args body . env)
+ v = cdr_(v);
+ PUSH(car(v));
+ argsyms = &Stack[SP-1];
+ PUSH(car(cdr_(v)));
+ body = &Stack[SP-1];
+ v = cons_(intval(f)==F_LAMBDA ? &LAMBDA : &MACRO,
+ cons(argsyms, cons(body, penv)));
+ }
+ break;
+ case F_LABEL:
+ v = Stack[saveSP];
+ if (*penv != NIL) {
+ v = cdr_(v);
+ PUSH(car(v)); // name
+ pv = &Stack[SP-1];
+ PUSH(car(cdr_(v))); // function
+ body = &Stack[SP-1];
+ *body = eval(*body, penv); // evaluate lambda
+ v = cons_(&LABEL, cons(pv, cons(body, &NIL)));
+ }
+ break;
+ case F_IF:
+ v = car(cdr_(Stack[saveSP]));
+ if (eval(v, penv) != NIL)
+ v = car(cdr_(cdr_(Stack[saveSP])));
+ else
+ v = car(cdr(cdr_(cdr_(Stack[saveSP]))));
+ tail_eval(v, Stack[saveSP+1]);
+ break;
+ case F_COND:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ while (iscons(*pv)) {
+ c = tocons(car_(*pv), "cond");
+ v = eval(c->car, penv);
+ *penv = Stack[saveSP+1];
+ if (v != NIL) {
+ *pv = cdr_(car_(*pv));
+ // evaluate body forms
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ v = eval(car_(*pv), penv);
+ *penv = Stack[saveSP+1];
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv), *penv);
+ }
+ break;
+ }
+ *pv = cdr_(*pv);
+ }
+ break;
+ case F_AND:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = T;
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ if ((v=eval(car_(*pv), penv)) == NIL) {
+ SP = saveSP; return NIL;
+ }
+ *penv = Stack[saveSP+1];
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv), *penv);
+ }
+ break;
+ case F_OR:
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ if ((v=eval(car_(*pv), penv)) != NIL) {
+ SP = saveSP; return v;
+ }
+ *penv = Stack[saveSP+1];
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv), *penv);
+ }
+ break;
+ case F_WHILE:
+ PUSH(car(cdr(cdr_(Stack[saveSP]))));
+ body = &Stack[SP-1];
+ Stack[saveSP] = car_(cdr_(Stack[saveSP]));
+ value_t *cond = &Stack[saveSP];
+ PUSH(NIL); pv = &Stack[SP-1];
+ while (eval(*cond, penv) != NIL) {
+ *penv = Stack[saveSP+1];
+ *pv = eval(*body, penv);
+ *penv = Stack[saveSP+1];
+ }
+ v = *pv;
+ break;
+ case F_PROGN:
+ // return last arg
+ Stack[saveSP] = cdr_(Stack[saveSP]);
+ pv = &Stack[saveSP]; v = NIL;
+ if (iscons(*pv)) {
+ while (iscons(cdr_(*pv))) {
+ v = eval(car_(*pv), penv);
+ *penv = Stack[saveSP+1];
+ *pv = cdr_(*pv);
+ }
+ tail_eval(car_(*pv), *penv);
+ }
+ break;
+
+ // ordinary functions
+ case F_SET:
+ argcount("set", nargs, 2);
+ e = Stack[SP-2];
+ v = *penv;
+ while (iscons(v)) {
+ bind = car_(v);
+ if (iscons(bind) && car_(bind) == e) {
+ cdr_(bind) = (v=Stack[SP-1]);
+ SP=saveSP; return v;
+ }
+ v = cdr_(v);
+ }
+ tosymbol(e, "set")->binding = (v=Stack[SP-1]);
+ break;
+ case F_BOUNDP:
+ argcount("boundp", nargs, 1);
+ if (tosymbol(Stack[SP-1], "boundp")->binding == UNBOUND)
+ v = NIL;
+ else
+ v = T;
+ break;
+ case F_EQ:
+ argcount("eq", nargs, 2);
+ v = ((Stack[SP-2] == Stack[SP-1]) ? T : NIL);
+ break;
+ case F_CONS:
+ argcount("cons", nargs, 2);
+ v = mk_cons();
+ car_(v) = Stack[SP-2];
+ cdr_(v) = Stack[SP-1];
+ break;
+ case F_CAR:
+ argcount("car", nargs, 1);
+ v = car(Stack[SP-1]);
+ break;
+ case F_CDR:
+ argcount("cdr", nargs, 1);
+ v = cdr(Stack[SP-1]);
+ break;
+ case F_RPLACA:
+ argcount("rplaca", nargs, 2);
+ car(v=Stack[SP-2]) = Stack[SP-1];
+ break;
+ case F_RPLACD:
+ argcount("rplacd", nargs, 2);
+ cdr(v=Stack[SP-2]) = Stack[SP-1];
+ break;
+ case F_ATOM:
+ argcount("atom", nargs, 1);
+ v = ((!iscons(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_SYMBOLP:
+ argcount("symbolp", nargs, 1);
+ v = ((issymbol(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_NUMBERP:
+ argcount("numberp", nargs, 1);
+ v = ((isnumber(Stack[SP-1])) ? T : NIL);
+ break;
+ case F_ADD:
+ s = 0;
+ for (i=saveSP+2; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "+");
+ s += n;
+ }
+ v = number(s);
+ break;
+ case F_SUB:
+ if (nargs < 1)
+ lerror("-: error: too few arguments\n");
+ i = saveSP+2;
+ s = (nargs==1) ? 0 : tonumber(Stack[i++], "-");
+ for (; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "-");
+ s -= n;
+ }
+ v = number(s);
+ break;
+ case F_MUL:
+ s = 1;
+ for (i=saveSP+2; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "*");
+ s *= n;
+ }
+ v = number(s);
+ break;
+ case F_DIV:
+ if (nargs < 1)
+ lerror("/: error: too few arguments\n");
+ i = saveSP+2;
+ s = (nargs==1) ? 1 : tonumber(Stack[i++], "/");
+ for (; i < (int)SP; i++) {
+ n = tonumber(Stack[i], "/");
+ if (n == 0)
+ lerror("/: error: division by zero\n");
+ s /= n;
+ }
+ v = number(s);
+ break;
+ case F_LT:
+ argcount("<", nargs, 2);
+ if (tonumber(Stack[SP-2],"<") < tonumber(Stack[SP-1],"<"))
+ v = T;
+ else
+ v = NIL;
+ break;
+ case F_NOT:
+ argcount("not", nargs, 1);
+ v = ((Stack[SP-1] == NIL) ? T : NIL);
+ break;
+ case F_EVAL:
+ argcount("eval", nargs, 1);
+ v = Stack[SP-1];
+ tail_eval(v, NIL);
+ break;
+ case F_PRINT:
+ for (i=saveSP+2; i < (int)SP; i++)
+ print(stdout, v=Stack[i]);
+ break;
+ case F_READ:
+ argcount("read", nargs, 0);
+ v = read_sexpr(stdin);
+ break;
+ case F_LOAD:
+ argcount("load", nargs, 1);
+ v = load_file(tosymbol(Stack[SP-1], "load")->name);
+ break;
+ case F_PROG1:
+ // return first arg
+ if (nargs < 1)
+ lerror("prog1: error: too few arguments\n");
+ v = Stack[saveSP+2];
+ break;
+ case F_APPLY:
+ argcount("apply", nargs, 2);
+ v = Stack[saveSP] = Stack[SP-1]; // second arg is new arglist
+ f = Stack[SP-2]; // first arg is new function
+ POPN(2); // pop apply's args
+ if (isbuiltin(f)) {
+ if (isspecial(f))
+ lerror("apply: error: cannot apply special operator "
+ "%s\n", builtin_names[intval(f)]);
+ // unpack arglist onto the stack
+ while (iscons(v)) {
+ PUSH(car_(v));
+ v = cdr_(v);
+ }
+ goto apply_builtin;
+ }
+ noeval = 1;
+ goto apply_lambda;
+ }
+ SP = saveSP;
+ return v;
+ }
+ else {
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ apply_lambda:
+ if (iscons(f)) {
+ headsym = car_(f);
+ if (headsym == LABEL) {
+ // (label name (lambda ...)) behaves the same as the lambda
+ // alone, except with name bound to the whole label expression
+ labl = f;
+ f = car(cdr(cdr_(labl)));
+ headsym = car(f);
+ }
+ // apply lambda or macro expression
+ PUSH(cdr(cdr(cdr_(f))));
+ lenv = &Stack[SP-1];
+ PUSH(car_(cdr_(f)));
+ argsyms = &Stack[SP-1];
+ PUSH(car_(cdr_(cdr_(f))));
+ body = &Stack[SP-1];
+ if (labl) {
+ // add label binding to environment
+ PUSH(labl);
+ PUSH(car_(cdr_(labl)));
+ *lenv = cons_(cons(&Stack[SP-1], &Stack[SP-2]), lenv);
+ POPN(3);
+ v = Stack[saveSP]; // refetch arglist
+ }
+ if (headsym == MACRO)
+ noeval = 1;
+ else if (headsym != LAMBDA)
+ lerror("apply: error: head must be lambda, macro, or label\n");
+ // build a calling environment for the lambda
+ // the environment is the argument binds on top of the captured
+ // environment
+ while (iscons(v)) {
+ // bind args
+ if (!iscons(*argsyms)) {
+ if (*argsyms == NIL)
+ lerror("apply: error: too many arguments\n");
+ break;
+ }
+ asym = car_(*argsyms);
+ if (!issymbol(asym))
+ lerror("apply: error: formal argument not a symbol\n");
+ v = car_(v);
+ if (!noeval) {
+ v = eval(v, penv);
+ *penv = Stack[saveSP+1];
+ }
+ PUSH(v);
+ *lenv = cons_(cons(&asym, &Stack[SP-1]), lenv);
+ POPN(2);
+ *argsyms = cdr_(*argsyms);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ if (*argsyms != NIL) {
+ if (issymbol(*argsyms)) {
+ if (noeval) {
+ *lenv = cons_(cons(argsyms, &Stack[saveSP]), lenv);
+ }
+ else {
+ PUSH(NIL);
+ PUSH(NIL);
+ rest = &Stack[SP-1];
+ // build list of rest arguments
+ // we have to build it forwards, which is tricky
+ while (iscons(v)) {
+ v = eval(car_(v), penv);
+ *penv = Stack[saveSP+1];
+ PUSH(v);
+ v = cons_(&Stack[SP-1], &NIL);
+ POP();
+ if (iscons(*rest))
+ cdr_(*rest) = v;
+ else
+ Stack[SP-2] = v;
+ *rest = v;
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ *lenv = cons_(cons(argsyms, &Stack[SP-2]), lenv);
+ }
+ }
+ else if (iscons(*argsyms)) {
+ lerror("apply: error: too few arguments\n");
+ }
+ }
+ noeval = 0;
+ // macro: evaluate expansion in the calling environment
+ if (headsym == MACRO) {
+ SP = saveSP;
+ PUSH(*lenv);
+ lenv = &Stack[SP-1];
+ v = eval(*body, lenv);
+ tail_eval(v, *penv);
+ }
+ else {
+ tail_eval(*body, *lenv);
+ }
+ // not reached
+ }
+ type_error("apply", "function", f);
+ return NIL;
+}
+
+// repl -----------------------------------------------------------------------
+
+static char *infile = NULL;
+
+value_t toplevel_eval(value_t expr)
+{
+ value_t v;
+ PUSH(NIL);
+ v = eval(expr, &Stack[SP-1]);
+ POP();
+ return v;
+}
+
+value_t load_file(char *fname)
+{
+ value_t e, v=NIL;
+ char *lastfile = infile;
+ FILE *f = fopen(fname, "r");
+ infile = fname;
+ if (f == NULL) lerror("file not found\n");
+ while (1) {
+ e = read_sexpr(f);
+ if (feof(f)) break;
+ v = toplevel_eval(e);
+ }
+ infile = lastfile;
+ fclose(f);
+ return v;
+}
+
+int main(int argc, char* argv[])
+{
+ value_t v;
+
+ stack_bottom = ((char*)&v) - PROCESS_STACK_SIZE;
+ lisp_init();
+ if (setjmp(toplevel)) {
+ SP = 0;
+ fprintf(stderr, "\n");
+ if (infile) {
+ fprintf(stderr, "error loading file \"%s\"\n", infile);
+ infile = NULL;
+ }
+ goto repl;
+ }
+ load_file("system.lsp");
+ if (argc > 1) { load_file(argv[1]); return 0; }
+ printf("Welcome to femtoLisp ----------------------------------------------------------\n");
+ repl:
+ while (1) {
+ printf("> ");
+ v = read_sexpr(stdin);
+ if (feof(stdin)) break;
+ print(stdout, v=toplevel_eval(v));
+ set(symbol("that"), v);
+ printf("\n\n");
+ }
+ return 0;
+}
--- /dev/null
+++ b/femtolisp/tiny/scrap.c
@@ -1,0 +1,107 @@
+// code to relocate cons chains iteratively
+ pcdr = &cdr_(nc);
+ while (iscons(d)) {
+ if (car_(d) == FWD) {
+ *pcdr = cdr_(d);
+ return first;
+ }
+ *pcdr = nc = mk_cons();
+ a = car_(d); v = cdr_(d);
+ car_(d) = FWD; cdr_(d) = nc;
+ car_(nc) = relocate(a);
+ pcdr = &cdr_(nc);
+ d = v;
+ }
+ *pcdr = d;
+
+/*
+ f = *rest;
+ *rest = NIL;
+ while (iscons(f)) { // nreverse!
+ v = cdr_(f);
+ cdr_(f) = *rest;
+ *rest = f;
+ f = v;
+ }*/
+
+int favailable(FILE *f)
+{
+ fd_set set;
+ struct timeval tv = {0, 0};
+ int fd = fileno(f);
+
+ FD_ZERO(&set);
+ FD_SET(fd, &set);
+ return (select(fd+1, &set, NULL, NULL, &tv)!=0);
+}
+
+static void print_env(value_t *penv)
+{
+ printf("<[ ");
+ while (issymbol(*penv) && *penv!=NIL) {
+ print(stdout, *penv, 0);
+ printf(" ");
+ penv++;
+ print(stdout, *penv, 0);
+ printf(" ");
+ penv++;
+ }
+ printf("] ");
+ print(stdout, *penv, 0);
+ printf(">\n");
+}
+
+#else
+ PUSH(NIL);
+ PUSH(NIL);
+ value_t *rest = &Stack[SP-1];
+ // build list of rest arguments
+ // we have to build it forwards, which is tricky
+ while (iscons(v)) {
+ v = eval(car_(v));
+ PUSH(v);
+ v = cons_(&Stack[SP-1], &NIL);
+ POP();
+ if (iscons(*rest))
+ cdr_(*rest) = v;
+ else
+ Stack[SP-2] = v;
+ *rest = v;
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ POP();
+#endif
+ // this version uses collective allocation. about 7-10%
+ // faster for lists with > 2 elements, but uses more
+ // stack space
+ i = SP;
+ while (iscons(v)) {
+ v = eval(car_(v));
+ PUSH(v);
+ v = Stack[saveSP] = cdr_(Stack[saveSP]);
+ }
+ if ((int)SP==i) {
+ PUSH(NIL);
+ }
+ else {
+ e = v = cons_reserve(nargs=(SP-i));
+ for(; i < (int)SP; i++) {
+ car_(v) = Stack[i];
+ v = cdr_(v);
+ }
+ POPN(nargs);
+ PUSH(e);
+ }
+
+value_t list_to_vector(value_t l)
+{
+ value_t v;
+ size_t n = llength(l), i=0;
+ v = alloc_vector(n, 0);
+ while (iscons(l)) {
+ vector_elt(v,i) = car_(l);
+ i++;
+ l = cdr_(l);
+ }
+ return v;
+}
--- /dev/null
+++ b/femtolisp/tiny/system.lsp
@@ -1,0 +1,426 @@
+; femtoLisp standard library
+; by Jeff Bezanson
+; Public Domain
+
+(set 'list (lambda args args))
+
+(set 'setq (macro (name val)
+ (list set (list quote name) val)))
+
+(setq sp '| |)
+(setq nl '|
+|)
+
+; 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.
+(setq f-body (lambda (e)
+ (cond ((atom e) e)
+ ((eq (cdr e) ()) (car e))
+ (t (cons progn e)))))
+
+(setq defmacro
+ (macro (name args . body)
+ (list 'setq name (list 'macro args (f-body body)))))
+
+; support both CL defun and Scheme-style define
+(defmacro defun (name args . body)
+ (list 'setq name (list 'lambda args (f-body body))))
+
+(defmacro define (name . body)
+ (if (symbolp name)
+ (list 'setq name (car body))
+ (cons 'defun (cons (car name) (cons (cdr name) body)))))
+
+(defun identity (x) x)
+(setq null not)
+(defun consp (x) (not (atom x)))
+
+(defun map (f lst)
+ (if (atom lst) lst
+ (cons (f (car lst)) (map f (cdr lst)))))
+
+(defmacro let (binds . body)
+ (cons (list 'lambda (map car binds) (f-body body))
+ (map cadr binds)))
+
+(defun nconc lsts
+ (cond ((null lsts) ())
+ ((null (cdr lsts)) (car lsts))
+ (t ((lambda (l d) (if (null l) d
+ (prog1 l
+ (while (consp (cdr l)) (set 'l (cdr l)))
+ (rplacd l d))))
+ (car lsts) (apply nconc (cdr lsts))))))
+
+(defun 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))))))
+
+(defun member (item lst)
+ (cond ((atom lst) ())
+ ((eq (car lst) item) lst)
+ (t (member item (cdr lst)))))
+
+(defun macrop (e) (and (consp e) (eq (car e) 'macro) e))
+(defun macrocallp (e) (and (symbolp (car e))
+ (boundp (car e))
+ (macrop (eval (car e)))))
+(defun macroapply (m args) (apply (cons 'lambda (cdr m)) args))
+
+(defun macroexpand-1 (e)
+ (if (atom e) e
+ (let ((f (macrocallp e)))
+ (if f (macroapply f (cdr e))
+ e))))
+
+; convert to proper list, i.e. remove "dots", and append
+(defun append.2 (l tail)
+ (cond ((null l) tail)
+ ((atom l) (cons l tail))
+ (t (cons (car l) (append.2 (cdr l) tail)))))
+
+(defun macroexpand (e)
+ ((label mexpand
+ (lambda (e env f)
+ (progn
+ (while (and (consp e)
+ (not (member (car e) env))
+ (set 'f (macrocallp e)))
+ (set 'e (macroapply f (cdr e))))
+ (if (and (consp e)
+ (not (or (eq (car e) 'quote)
+ (eq (car e) quote))))
+ (let ((newenv
+ (if (and (or (eq (car e) 'lambda) (eq (car e) 'macro))
+ (consp (cdr e)))
+ (append.2 (cadr e) env)
+ env)))
+ (map (lambda (x) (mexpand x newenv nil)) e))
+ e))))
+ e nil nil))
+
+; uncomment this to macroexpand functions at definition time.
+; makes typical code ~25% faster, but only works for defun expressions
+; at the top level.
+;(defmacro defun (name args . body)
+; (list 'setq name (list 'lambda args (macroexpand (f-body body)))))
+
+; same thing for macros. enabled by default because macros are usually
+; defined at the top level.
+(defmacro defmacro (name args . body)
+ (list 'setq name (list 'macro args (macroexpand (f-body body)))))
+
+(setq = eq)
+(setq eql eq)
+(define (/= a b) (not (eq a b)))
+(define != /=)
+(define (> a b) (< b a))
+(define (<= a b) (not (< b a)))
+(define (>= a b) (not (< a b)))
+(define (mod x y) (- x (* (/ x y) y)))
+(define (abs x) (if (< x 0) (- x) x))
+(define (truncate x) x)
+(setq K prog1) ; K combinator ;)
+(define (funcall f . args) (apply f args))
+(define (symbol-function sym) (eval sym))
+(define (symbol-value sym) (eval sym))
+
+(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 (equal a b)
+ (if (and (consp a) (consp b))
+ (and (equal (car a) (car b))
+ (equal (cdr a) (cdr b)))
+ (eq a b)))
+
+; compare imposes an ordering on all values. yields -1 for a<b,
+; 0 for a==b, and 1 for a>b. lists are compared up to the first
+; point of difference.
+(defun compare (a b)
+ (cond ((eq a b) 0)
+ ((or (atom a) (atom b)) (if (< a b) -1 1))
+ (t (let ((c (compare (car a) (car b))))
+ (if (not (eq c 0))
+ c
+ (compare (cdr a) (cdr b)))))))
+
+(defun every (pred lst)
+ (or (atom lst)
+ (and (pred (car lst))
+ (every pred (cdr lst)))))
+
+(defun any (pred lst)
+ (and (consp lst)
+ (or (pred (car lst))
+ (any pred (cdr lst)))))
+
+(defun listp (a) (or (eq a ()) (consp a)))
+
+(defun length (l)
+ (if (null l) 0
+ (+ 1 (length (cdr l)))))
+
+(defun nthcdr (n lst)
+ (if (<= n 0) lst
+ (nthcdr (- n 1) (cdr lst))))
+
+(defun list-ref (lst n)
+ (car (nthcdr n lst)))
+
+(defun list* l
+ (if (atom (cdr l))
+ (car l)
+ (cons (car l) (apply list* (cdr l)))))
+
+(defun nlist* l
+ (if (atom (cdr l))
+ (car l)
+ (rplacd l (apply nlist* (cdr l)))))
+
+(defun lastcdr (l)
+ (if (atom l) l
+ (lastcdr (cdr l))))
+
+(defun last (l)
+ (cond ((atom l) l)
+ ((atom (cdr l)) l)
+ (t (last (cdr l)))))
+
+(defun map! (f lst)
+ (prog1 lst
+ (while (consp lst)
+ (rplaca lst (f (car lst)))
+ (set 'lst (cdr lst)))))
+
+(defun mapcar (f . lsts)
+ ((label mapcar-
+ (lambda (lsts)
+ (cond ((null lsts) (f))
+ ((atom (car lsts)) (car lsts))
+ (t (cons (apply f (map car lsts))
+ (mapcar- (map cdr lsts)))))))
+ lsts))
+
+(defun transpose (M) (apply mapcar (cons list M)))
+
+(defun filter (pred lst)
+ (cond ((null lst) ())
+ ((not (pred (car lst))) (filter pred (cdr lst)))
+ (t (cons (car lst) (filter pred (cdr lst))))))
+
+(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 lst) (foldl cons nil lst))
+
+(define (reduce0 f zero lst)
+ (if (null lst) zero
+ (reduce0 f (f zero (car lst)) (cdr lst))))
+
+(defun reduce (f lst)
+ (reduce0 f (car lst) (cdr lst)))
+
+(define (copy-list l) (map identity l))
+(define (copy-tree l)
+ (if (atom l) l
+ (cons (copy-tree (car l))
+ (copy-tree (cdr l)))))
+
+(define (assoc item lst)
+ (cond ((atom lst) ())
+ ((eq (caar lst) item) (car lst))
+ (t (assoc item (cdr lst)))))
+
+(define (nreverse l)
+ (let ((prev nil))
+ (while (consp l)
+ (set 'l (prog1 (cdr l)
+ (rplacd l (prog1 prev
+ (set 'prev l))))))
+ prev))
+
+(defmacro let* (binds . body)
+ (cons (list 'lambda (map car binds)
+ (cons progn
+ (nconc (map (lambda (b) (cons 'setq b)) binds)
+ body)))
+ (map (lambda (x) nil) binds)))
+
+(defmacro labels (binds . body)
+ (cons (list 'lambda (map car binds)
+ (cons progn
+ (nconc (map (lambda (b)
+ (list 'setq (car b) (cons 'lambda (cdr b))))
+ binds)
+ body)))
+ (map (lambda (x) nil) binds)))
+
+(defmacro when (c . body) (list if c (f-body body) nil))
+(defmacro unless (c . body) (list if c nil (f-body body)))
+
+(defmacro dotimes (var . body)
+ (let ((v (car var))
+ (cnt (cadr var)))
+ (list 'let (list (list v 0))
+ (list while (list < v cnt)
+ (list prog1 (f-body body) (list 'setq v (list + v 1)))))))
+
+(defun map-int (f n)
+ (let ((acc nil))
+ (dotimes (i n)
+ (setq acc (cons (f i) acc)))
+ (nreverse acc)))
+
+; property lists
+(setq *plists* nil)
+
+(defun symbol-plist (sym)
+ (cdr (or (assoc sym *plists*) '(()))))
+
+(defun set-symbol-plist (sym lst)
+ (let ((p (assoc sym *plists*)))
+ (if (null p) ; sym has no plist yet
+ (setq *plists* (cons (cons sym lst) *plists*))
+ (rplacd p lst))))
+
+(defun get (sym prop)
+ (let ((pl (symbol-plist sym)))
+ (if pl
+ (let ((pr (member prop pl)))
+ (if pr (cadr pr) nil))
+ nil)))
+
+(defun put (sym prop val)
+ (let ((p (assoc sym *plists*)))
+ (if (null p) ; sym has no plist yet
+ (setq *plists* (cons (list sym prop val) *plists*))
+ (let ((pr (member prop p)))
+ (if (null pr) ; sym doesn't have this property yet
+ (rplacd p (cons prop (cons val (cdr p))))
+ (rplaca (cdr pr) val)))))
+ val)
+
+; setf
+; expands (setf (place x ...) v) to (mutator (f x ...) v)
+; (mutator (identity x ...) v) is interpreted as (mutator x ... v)
+(setq *setf-place-list*
+ ; place mutator f
+ '((car rplaca identity)
+ (cdr rplacd identity)
+ (caar rplaca car)
+ (cadr rplaca cdr)
+ (cdar rplacd car)
+ (cddr rplacd cdr)
+ (caaar rplaca caar)
+ (caadr rplaca cadr)
+ (cadar rplaca cdar)
+ (caddr rplaca cddr)
+ (cdaar rplacd caar)
+ (cdadr rplacd cadr)
+ (cddar rplacd cdar)
+ (cdddr rplacd cddr)
+ (get put identity)
+ (aref aset identity)
+ (symbol-function set identity)
+ (symbol-value set identity)
+ (symbol-plist set-symbol-plist identity)))
+
+(defun setf-place-mutator (place val)
+ (if (symbolp place)
+ (list 'setq place val)
+ (let ((mutator (assoc (car place) *setf-place-list*)))
+ (if (null mutator)
+ (error '|setf: error: unknown place | (car place))
+ (if (eq (caddr mutator) 'identity)
+ (cons (cadr mutator) (append (cdr place) (list val)))
+ (list (cadr mutator)
+ (cons (caddr mutator) (cdr place))
+ val))))))
+
+(defmacro setf args
+ (f-body
+ ((label setf-
+ (lambda (args)
+ (if (null args)
+ nil
+ (cons (setf-place-mutator (car args) (cadr args))
+ (setf- (cddr args))))))
+ args)))
+
+(defun revappend (l1 l2) (nconc (reverse l1) l2))
+(defun nreconc (l1 l2) (nconc (nreverse l1) l2))
+
+(defun builtinp (x)
+ (and (atom x)
+ (not (symbolp x))
+ (not (numberp x))))
+
+(defun self-evaluating-p (x)
+ (or (eq x nil)
+ (eq x t)
+ (and (atom x)
+ (not (symbolp x)))))
+
+; backquote
+(defmacro backquote (x) (bq-process x))
+
+(defun splice-form-p (x)
+ (or (and (consp x) (or (eq (car x) '*comma-at*)
+ (eq (car x) '*comma-dot*)))
+ (eq x '*comma*)))
+
+(defun bq-process (x)
+ (cond ((self-evaluating-p x) x)
+ ((atom x) (list quote x))
+ ((eq (car x) 'backquote) (bq-process (bq-process (cadr x))))
+ ((eq (car x) '*comma*) (cadr x))
+ ((not (any splice-form-p x))
+ (let ((lc (lastcdr x))
+ (forms (map bq-bracket1 x)))
+ (if (null lc)
+ (cons 'list forms)
+ (nconc (cons 'nlist* forms) (list (bq-process lc))))))
+ (t (let ((p x) (q '()))
+ (while (and (consp p)
+ (not (eq (car p) '*comma*)))
+ (setq q (cons (bq-bracket (car p)) q))
+ (setq p (cdr p)))
+ (cons 'nconc
+ (cond ((consp p) (nreconc q (list (cadr p))))
+ ((null p) (nreverse q))
+ (t (nreconc q (list (bq-process p))))))))))
+
+(defun bq-bracket (x)
+ (cond ((atom x) (list cons (bq-process x) nil))
+ ((eq (car x) '*comma*) (list cons (cadr x) nil))
+ ((eq (car x) '*comma-at*) (list 'copy-list (cadr x)))
+ ((eq (car x) '*comma-dot*) (cadr x))
+ (t (list cons (bq-process x) nil))))
+
+; bracket without splicing
+(defun bq-bracket1 (x)
+ (if (and (consp x) (eq (car x) '*comma*))
+ (cadr x)
+ (bq-process x)))
--- /dev/null
+++ b/femtolisp/todo
@@ -1,0 +1,840 @@
+* setf
+* plists
+* backquote
+* symbol< (make < generic), generic compare function
+? (cdr nil) should be nil
+* multiple-argument mapcar
+? multi-argument apply. for builtins, just push them. for lambdas, must
+ cons together the evaluated arguments.
+? option *print-shared*. if nil, it still handles circular references
+ but does not specially print non-circular shared structure
+? option *print-circle*
+* read support for #' for compatibility
+* #\c read character as code (including UTF-8 support!)
+* #| |# block comments
+- here-data for binary serialization. proposed syntax:
+ #>size:data, e.g. #>6:000000
+* use syntax environment concept for user-defined macros to plug
+ that hole in the semantics
+* make more builtins generic. if typecheck fails, call out to the
+ generic version to try supporting more types.
+ compare/equal
+ +-*/< for all numeric types
+ length for all sequences
+ ? aref/aset for all sequences (vector, list, c-array)
+ ? copy
+* fixnump, all numeric types should pass numberp
+- make sure all uses of symbols don't assume symbols are unmovable without
+ checking ismanaged()
+* eliminate compiler warnings
+* fix printing nan and inf
+- move to "2.5-bit" type tags
+? builtin abs()
+- try adding optional arguments, (lambda (x (opt 0)) ...), see if performance
+ is acceptable
+* (syntax-environment) to return it as an assoc list
+* (environment) for variables, constantp
+* prettier printing
+
+* readable gensyms and #:
+ . #:n reads similar to #n=#.(gensym) the first time, and #n# after
+* circular equal
+* integer/truncate function
+? car-circularp, cdr-circularp, circularp
+- hashtable. plan as equal-hash, over three stages:
+ 1. first support symbol and fixnum keys, use ptrhash. only values get
+ relocated on GC.
+ 2. create a version of ptrhash that uses equal() and hash(). if a key is
+ inserted requiring this, switch vtable pointer to use these functions.
+ both keys and values get relocated on GC.
+ 3. write hash() for pairs and vectors. now everything works.
+- expose eq-hashtable to user
+- other backquote optimizations:
+ * (nconc x) => x for any x
+ . (copy-list (list|append|nconc ...)) => (list|append|nconc ...)
+ * (apply vector (list ...)) => (vector ...)
+ . (nconc (cons x nil) y) => (cons x y)
+* let form without initializers (let (a b) ...), defaults to nil
+* print (quote a) as 'a, same for ` etc.
+
+- template keyword arguments. you write
+(template (:test eq) (:key caar)
+ (defun assoc (item lst)
+ (cond ((atom lst) ())
+ ((:test (:key lst) item) (car lst))
+ (t (assoc item (cdr lst))))))
+
+This writes assoc as a macro that produces a call to a pre-specialized
+version of the function. For example
+ (assoc x l :test equal)
+first tries to look up the variant '(equal caar) in the dictionary for assoc.
+If it doesn't exist it gets generated and stored. The result is a lambda
+expression.
+The macro returns ((lambda (item lst) <code for assoc>) x l).
+We might have to require different syntax for template invocations inside
+template definitions, such as
+ ((t-instance assoc eq :key) item lst)
+which passes along the same key but always uses eq.
+Alternatively, we could use the keysyms without colons to name the values
+of the template arguments, so the keysyms are always used as markers and
+never appear to have values:
+(template (:test eq) (:key caar)
+ (defun assoc? (item lst)
+ (cond ((atom lst) ())
+ ((test (key lst) item) ...
+ ...
+ (assoc x y :test test :key key)
+This would be even easier if the keyword syntax were something like
+ (: test eq)
+
+
+possible optimizations:
+* delay environment creation. represent environment on the stack as
+ alternating symbols/values, or if cons instead of symbol then traverse
+ as assoc list. only explicitly cons the whole thing when making a closure
+* cons_reserve(n) interface, guarantees n conses available without gc.
+ it could even link them together for you more efficiently
+* assoc builtin
+* special check for constant symbol when evaluating head since that's likely
+* remove the loop from cons_reserve. move all initialization to the loops
+ that follow calls to cons_reserve.
+- case of lambda expression in head (as produced by let), can just modify
+ env in-place in tail position
+* represent lambda environment as a vector (in lispv)
+x setq builtin (didn't help)
+(- list builtin, to use cons_reserve)
+(- let builtin, to further avoid env consing)
+unconventional interpreter builtins that can be used as a compilation
+target without moving away from s-expressions:
+- (*global* . a) ; special form, don't look in local env first
+- (*local* . 2) ; direct stackframe access
+for internal use:
+- a special version of apply that takes arguments on the stack, to avoid
+ consing when implementing "call-with" style primitives like trycatch,
+ hashtable-foreach, or the fl_apply API
+
+
+bugs:
+* with the fully recursive (simpler) relocate(), the size of cons chains
+ is limited by the process stack size. with the iterative version we can
+ have unlimited cdr-deep structures.
+* in #n='e, the case that makes the cons for 'e needs to use label fixup
+* symbol token |.| does not work
+* ltable realloc not multiplying by sizeof(unsigned long)
+* not relocating final cdr in iterative version if it is a vector
+- (setf (car x) y) doesn't return y
+* reader needs to check errno in isnumtok
+* prettyprint size measuring is not utf-8 correct
+
+
+femtoLisp3...with symbolic C interface
+
+c values are builtins with value > N_BUILTINS
+((u_int32_t*)cvalue)[0] & 0x3 must always be 2 to distinguish from vectors
+
+typedef struct _cvtable_t {
+ void (*relocate)(struct _cvalue_t *);
+ void (*free)(struct _cvalue_t *);
+ void (*print)(struct _cvalue_t *, FILE *);
+} cvtable_t;
+
+; remember: variable-length data preferred over variable-length arglists
+
+c type representations:
+symbols void, [u]int[8,16,32,64], float, double, [u]char, [u]short,
+[u]int, [u]long, lispvalue
+(c-function ret-type (argtype ...))
+(array type N)
+(struct ((name type) (name type) ...))
+(union ((name type) (name type) ...))
+(enum (name1 name2 ...))
+(pointer type)
+
+constructors:
+([u]int[8,16] n)
+([u]int32 hi lo)
+([u]int64 b3 b2 b1 b0)
+(float hi lo) or (float "3.14")
+(double b3 b2 b1 b0) or (double "3.14")
+(array ctype (val ...))
+(struct ((name type) ...) (val ...))
+(pointer cvalue) ; constructs pointer to the given value
+(pointer ctype ptr) ; copies/casts a pointer to a different type
+so (pointer 'int8 #int32(0)) doesn't make sense, but
+ (pointer 'int8 (pointer #int32(0))) does.
+(c-function ret-type (argtype ...) ld-symbol-name)
+
+? struct/enum tag:
+ (struct 'tag <initializer>) or (pointer (struct tag))
+ where tag is a global var with a value ((name type) ...)
+
+
+representing c data from lisp is the tricky part to make really elegant and
+efficient. the most elegant but too inefficient option is not to have opaque
+C values at all and always marshal to/from native lisp values like #int16[10].
+the next option is to have opaque values "sometimes", for example returning
+them from C functions but printing them using their lisp representations.
+the next option is to relax the idea that C values of a certain type have a
+specific lisp structure, and use a coercion system that "tries" to translate
+a lisp value to a specified C type. for example [0 1 2], (0 1 2),
+#string[0 1 2], etc. might all be accepted by a C function taking int8_t*.
+you could say (c-coerce <lispvalue> <typedesc>) and get a cvalue back or
+an error if the conversion fails.
+
+the final option is to have cvalues be the only officially-sanctioned
+representation of c data, and make them via constructors, like
+(int32 hi lo) returns an int32 cvalue
+(struct '((name type) (name type) ...) a b ...) makes a struct
+there is a constructor function for each primitive C type.
+you can print these by brute force as e.g. #.(int32 hi lo)
+then all checking just looks like functions checking their arguments
+
+this option seems almost ideal. what's wrong with it?
+. to construct cvalues from lisp you have to build code instead of data
+. it seems like it should take more explicit advantage of tagged vectors
+. should you accept multiple forms? for example
+ (array 'int8 0 1 2) or (array 'int8 [0 1 2])
+ if you're going to be that permissive, why not allow [0 1 2] to be passed
+ directly to a function that expects int8_t* and do the conversion
+ implicitly?
+ . even if these c-primitive-constructor functions exist, you can still
+ write things like c-coerce (in lisp, even) and hack in implicit
+ conversion attempts when something other than a cvalue is passed.
+. the printing code is annoying, because it's not enough to print readably,
+ you have to print evaluably.
+ . solution: constructor notation, #int32(hi lo)
+
+in any case, "opaque" cvalues will not really be opaque because we want to
+know their types and be able to take them apart on the byte level from lisp.
+C code can get references to lisp values and manipulate them using lisp
+operations like car, so to be fair it should work vice-versa; give
+c references to lisp code and let it use c operations like * on them.
+you can write lisp in c and c in lisp, though of course you don't usually
+want to. however, c written in lisp can be generated by a macro, printed,
+and fed to TCC for compilation.
+
+
+for a struct the names and types are parameters of the type, not the
+constructor, so it seems more correct to do
+
+((struct (name type) (name type) ...) (val val ...))
+
+where struct returns a constructor. but this isn't practical because it
+can't be printed in constructor notation and the type is a lambda rather
+than a more sensible expression.
+
+
+notice constructor calls and type representations are "similar". they
+should be related formally:
+
+(define (new type)
+ (if (symbolp type) (apply (eval type) ())
+ (apply (eval (car type)) (cdr type))))
+
+for aggregate types, you can keep a variable referring to the relevant
+piece:
+
+(setq point '((x int) (y int)))
+(struct point [2 3]) ; looks like c declaration 'struct point x;'
+
+a type is a function, so something similar to typedef is achieved by:
+
+(define (point_t vals) (struct point vals))
+
+design points:
+. type constructors will all be able to take 1 or 0 arguments, so i could say
+ (new (typeof val)) ; construct similar
+ (define (new type)
+ (if (symbolp type) (apply (eval type) ())
+ (apply (eval (car type)) (cdr type))))
+. values can be marked as autorelease (1) if user says so, (2) if we can
+ prove that it's ok (e.g. we only allocated the value using malloc because
+ it is too large to move on every GC).
+ in the future you should be able to specify an arbitrary finalization
+ function, not just free().
+. when calling a C function, a value of type_t can be passed to something
+ expecting a type_t* by taking the address of the representation. BUT
+ this is dangerous if the C function might save a reference.
+ a type_t* can be passed as a type_t by copying the representation.
+. you can use (pointer v) to switch v to "malloc'd representation", in
+ which case the value is no longer autoreleased, but you can do whatever
+ you want with the pointer. (other option is to COPY v when making a
+ pointer to it, but this still doesn't prevent C from holding a reference
+ too long)
+
+
+add a cfunction binding to symbols. you register in C simply by setting
+this binding to a function pointer, then
+
+(defun open (path flags)
+ ; could insert type checks here
+ (ccall 'int32 'open path flags))
+
+(setq fd (open "path" 0))
+
+using libdl you could even omit the registration step and extra binding
+
+this is possible:
+(defun malloc (size)
+ (ccall `(array int8 ,size) 'malloc size))
+ ;ret type ;f name ; . args
+
+
+vtable:
+we'd like to be able to define new lisp "types", like vectors
+and hash tables, using this. there needs to be a standard value interface
+you can implement in C and attach a vtable to some c values.
+interface: relocate, finalize, print(, copy)
+
+implementation plan:
+- write cvalue constructors
+- if a head evaluates to a cvalue, call the pointer directly with the arg array
+ . this is the "guest function" interface, a C function written specifically
+ to the femtolisp API. its type must be
+ '(c-function lispvalue ((pointer lispvalue) uint32))
+ which corresponds to
+ value_t func(value_t *args, u_int32_t nargs);
+ . this interface is useful for writing additional builtins, types,
+ interpreter extensions, etc. more efficient.
+ . one of these functions could also be called with
+ (defun func args
+ (ccall 'func 'lispvalue (array 'lispvalue args) (length args)))
+ - these functions are effectively builtins and should have names so they
+ can be printed as such.
+ . have a registration function
+ void guest_function(value_t (*f)(value_t*,u_int32_t), const char *name);
+ so at least the function type can be checked from C
+ . set a flags bit for functions registered this way so we can identify
+ them quickly
+
+- ccall lisp builtin, (ccall rettype name . args). if name has no cfunc
+ binding, looks it up lazily with dlsym and stores the result.
+ this is a guest function that handles type checking, translation, and
+ invocation of foreign c functions.
+
+- you could register builtins from lisp like this:
+ (defun dlopen (name flags) (ccall '(pointer void) 'dlopen name flags))
+ (defun dlsym (handle name type) (ccall type 'dlsym handle name))
+ (define lisp-process (dlopen nil 0))
+ (define vector-sym
+ (dlsym lisp-process 'int_vector
+ '(function lispvalue (pointer lispvalue) uint32)))
+ (ccall 'void 'guest_function vector-sym 'vector)
+
+- write c extensions cref, cset, typeof, sizeof, cvaluep
+* read, print, vectorp methods for vectors
+- quoted string "" reading, produces #(c c c c ...)
+* get rid of primitive builtins read,print,princ,load,exit,
+ implement using ccall
+
+
+other possible design:
+- just add two builtins, call and ccall.
+ (call 'name arg arg arg) lisp guest function interface
+ we can say e.g.
+ (defmacro vector args `(call 'vector ,.args))
+- basically the question is whether to introduce a new kind of callable
+ object or to do everything through the existing builtin mechanism
+ . macros cannot be applied, so without a new kind of callable 'vector'
+ would have to be a lisp function, entailing argument consing...
+ (defun builtin (name)
+ (guest-function name
+ (dlsym lisp-process name '(function value (pointer value) uint32))))
+ then you can print a guest function as e.g.
+ #.(builtin 'vector)
+
+#name(x y z) reads as a tagged vector
+#(x y z) is the same as #vector(x y z)
+should be internally the same as well, so non-taggedness does not formally
+exist.
+
+
+then we can write the vector clause in backquote as e.g.
+
+(if (vectorp x)
+ (let ((body (bq-process (vector-to-list x))))
+ (if (eq (tag x) 'vector)
+ (list 'list-to-vector body)
+ (list 'apply 'tagged-vector
+ (list cons (list quote (tag x)) body))))
+ (list quote x))
+
+
+setup plan:
+- create source directory and svn repository, move llt sources into it
+* write femtolisp.h, definitions for extensions to #include
+- add fl_ prefix to all exported functions
+- port read and print to jclib's iostreams
+* get rid of flutils; use ptrhash instead
+* builtinp needs to be a builtin ;) to distinguish lisp builtins from cvalues
+* allocation and gc for cvalues
+- interface functions fl_list(...), fl_apply
+ e.g. fl_apply(fl_eval(fl_symbol("+")), fl_list(fl_number(2),fl_number(3)))
+ and fl_symval("+"), fl_cons, etc.
+
+-----------------------------------------------------------------------------
+
+vector todo:
+* compare for vectors
+- (aref v i j k) does (reduce aref v '(i j k)); therefore (aref v) => v
+- (aref v ... [1 2 3] ...) vectorized indexing
+- make (setf (aref v i j k) x) expand to (aset (aref v i j) k x)
+these should be done using the ccall interface:
+- concatenate
+- copy-vec
+- (range i j step) to make integer ranges
+- (rref v start stop), plus make it settable! (rset v start stop rhs)
+lower priority:
+- find (strstr)
+
+functions to be generic over vec/list:
+* compare, equal, length
+
+constructor notation:
+
+#func(a b c) does (apply func '(a b c))
+
+-----------------------------------------------------------------------------
+
+how we will allocate cvalues
+
+a vector's size will be a lisp-value number. we will set bit 0x2 to indicate
+a resize request, and bit 0x1 to indicate that it's actually a cvalue.
+
+every cvalue will have the following fields, followed by some number of
+words according to how much space is needed:
+
+ value_t size; // | 0x2
+ cvtable_t *vtable;
+ struct {
+#ifdef BITS64
+ unsigned pad:32;
+#endif
+ unsigned whatever:27;
+ unsigned mark:1;
+ unsigned hasparent:1;
+ unsigned islispfunction:1;
+ unsigned autorelease:1;
+ unsigned inlined:1;
+ } flags;
+ value_t type;
+ size_t len; // length of *data in bytes
+ //void *data; // present if !inlined
+ //value_t parent; // present if hasparent
+
+size/vtable have the same meaning as vector size/elt[0] for relocation
+obviously we only relocate parent and type. if vtable->relocate is present,
+we call it at the end of the relocate process, and it must touch every
+lisp value reachable from it.
+
+when a cvalue is created with a finalizer, its address is added to a special
+list. before GC, everything in that list has its mark bit set. when
+we relocate a cvalue, clear the bit. then go through the list to call
+finalizers on dead values. this is O(n+m) where n is amt of live data and m
+is # of values needing finalization. we expect m << heapsize.
+
+-----------------------------------------------------------------------------
+
+Goal: bootstrap a lisp system where we can do "anything" purely in lisp
+starting with the minimal builtins needed for successive levels of
+completeness:
+
+1. Turing completeness
+quote, if, lambda, eq, atom, cons, car, cdr
+
+2. Naming
+set
+
+3. Control flow
+progn, prog1, apply, eval
+call/cc needed for true completeness, but we'll have attempt, raise
+
+4. Predicate completeness
+symbolp, numberp, builtinp
+
+5. Syntax
+macro
+
+6. I/O completeness
+read, print
+
+7. Mutable state
+rplaca, rplacd
+
+8. Arithmetic completeness
++, -, *, /, <
+
+9. The missing data structure(s): vector
+alloc, aref, aset, vectorp, length
+
+10. Real-world completeness (escape hatch)
+ccall
+
+---
+11. Misc unnecessary
+while, label, cond, and, or, not, boundp, vector
+
+-----------------------------------------------------------------------------
+
+exception todo:
+
+* silence 'in file' errors when user frame active
+* add more useful data to builtin exception types:
+ (UnboundError x)
+ (BoundsError vec index)
+ (TypeError fname expected got)
+ (Error v1 v2 v3 ...)
+* attempt/raise, rewrite (error) in lisp
+* more intelligent exception printers in toplevel handler
+
+-----------------------------------------------------------------------------
+
+lisp variant ideas
+
+- get rid of separate predicates and give every value the same structure
+ ala mathematica
+ . (tag 'a) => symbol
+ (tag '(a b)) => a
+ (tag 'symbol 'a) => a
+ (tag 'blah 3) => (blah 3)
+- have only vectors, not cons cells (sort of like julia)
+ . could have a separate tag field as above
+
+- easiest way to add vectors:
+ . allocate in same heap with conses, have a tag, size, then elements
+ (each elt must be touched on GC for relocation anyway, so might as well
+ copy collect it)
+ . tag pointers as builtins, we identify them as builtins with big values
+ . write (vector) in C, use it from read and eval
+
+8889314663 comcast net #
+
+-----------------------------------------------------------------------------
+
+cvalues reserves the following global symbols:
+
+int8, uint8, int16, uint16, int32, uint32, int64, uint64
+char, uchar, short, ushort, int, uint, long, ulong
+float, double
+struct, array, enum, union, function, void, pointer, lispvalue
+
+it defines (but doesn't reserve) the following:
+
+typeof, sizeof, autorelease, guestfunction, ccall
+
+
+user-defined types and typedefs:
+
+the rule is that a type should be viewed as a self-evaluating constant
+like a number. if i define a complex_t type of two doubles, then
+'complex_t is not a type any more than the symbol 'x could be added to
+something just because it happened to have the value 2.
+
+; typedefs from lisp
+(define wchar_t 'uint32)
+(define complex_t '(struct ((re double) (im double))))
+
+; use them
+(new complex_t)
+(new `(array ,complex_t 10))
+(array complex_t 10)
+
+BUT
+
+(array 'int32 10)
+
+because the primitive types *are* symbols. the fact that they have values is
+just a convenient coincidence that lets you do e.g. (int32 0)
+
+
+; size-annotate a pointer
+(setq p (ccall #c-function((pointer void) (ulong) malloc) n)
+(setq a (deref p `(array int8 ,n)))
+
+cvalues todo:
+
+- use uint32_t instead of wchar_t in C code
+- make sure empty arrays and 0-byte types really work
+* allow int constructors to accept other int cvalues
+* array constructor should accept any cvalue of the right size
+* make sure cvalues participate well in circular printing
+- lispvalue type
+ . keep track of whether a cvalue leads to any lispvalues, so they can
+ be automatically relocated (?)
+* float, double
+- struct, union
+- pointer type, function type
+- finalizers and lifetime dependency tracking
+- functions autorelease, guestfunction
+- cref/cset/byteref/byteset
+* wchar type, wide character strings as (array wchar)
+* printing and reading strings
+- ccall
+- anonymous unions
+* fix princ for cvalues
+
+- string constructor/concatenator:
+(string 'sym #char(65) #wchar(945) "blah" 23)
+ ; gives "symA\u03B1blah23"
+"ccc" reads to (array char)
+
+low-level functions:
+; these are type/bounds-checked accesses
+- (cref|ccopy cvalue key) ; key is field name or index
+- (cset cvalue key cvalue) ; key is field name, index, or struct offset
+- (get-[u]int[8,16,32,64] cvalue addr)
+ ; n is a lisp number or cvalue of size <= 8
+- (set-[u]int[8,16,32,64] cvalue addr n)
+- (c-struct-offset type field)
+- (c2lisp cvalue) ; convert to sexpr form
+- (autorelease cvalue) ; mark cvalue as free-on-gc
+* (typeof cvalue)
+* (sizeof cvalue|type)
+- (deref pointer[, type]) ; convert an unknown pointer to a safe cvalue
+- (ccopy cv)
+
+; (sizeof '(pointer type)) == sizeof(void*)
+; (sizeof '(array type N)) == N * sizeof(type)
+
+
+things you can do with cvalues:
+
+. call native C functions from lisp code without wrappers
+. wrap C functions in pure lisp, automatically inheriting some degree
+ of type safety
+. use lisp functions as callbacks from C code
+. use the lisp garbage collector to reclaim malloc'd storage
+. annotate C pointers with size information for bounds checking
+. attach symbolic type information to a C data structure, allowing it to
+ inherit lisp services such as printing a readable representation
+. add datatypes like strings to lisp
+. use more efficient represenations for your lisp programs' data
+
+
+family of cvalue representations.
+relevant attributes:
+ . large -- needs full size_t to represent size
+ . inline -- allocated along with metadata
+ . prim -- no stored type; uses primtype bits in flags
+ . hasdeps -- depends on other values to stay alive
+
+these attributes have the following dependencies:
+ . large -> !inline
+ . prim -> !hasdeps && !large
+
+so we have the following possibilities:
+
+large inline prim hasdeps rep#
+ 0 0 0 0 0
+ 0 0 0 1 1
+
+ 0 0 1 0 2
+ 0 1 0 0 3
+ 0 1 0 1 4
+ 0 1 1 0 5
+
+ 1 0 0 0 6
+ 1 0 0 1 7
+
+we need to be able to un-inline data, so we need:
+change 3 -> 0 (easy; write pointer over data)
+change 4 -> 1
+change 5 -> 2 (also easy)
+
+
+rep#0&1: (!large && !inline && !prim)
+typedef struct {
+ cvflags_t flags;
+ value_t type;
+ value_t deps;
+ void *data; /* points to malloc'd buffer */
+} cvalue_t;
+
+rep#3&4: (!large && inline && !prim)
+typedef struct {
+ cvflags_t flags;
+ value_t type;
+ value_t deps;
+ /* data goes here inlined */
+} cvalue_t;
+
+
+rep#2: (prim && !inline)
+typedef struct {
+ cvflags_t flags;
+ void *data; /* points to (tiny!) malloc'd buffer */
+} cvalue_t;
+
+rep#5: (prim && inline)
+typedef struct {
+ cvflags_t flags;
+ /* data goes here inlined */
+} cvalue_t;
+
+
+rep#6&7: (large)
+typedef struct {
+ cvflags_t flags;
+ value_t type;
+ value_t deps;
+ void *data; /* points to malloc'd buffer */
+ size_t len;
+} cvalue_t;
+
+-----------------------------------------------------------------------------
+
+times for lispv:
+
+color 2.286s
+sort 0.181s
+fib34 5.205s
+mexpa 0.329s
+
+-----------------------------------------------------------------------------
+
+finalization algorithm that allows finalizers written in lisp:
+
+right after GC, go through finalization list (a weak list) and find objects
+that didn't move. relocate them (bring them back to life) and push them
+all onto the stack. remove all from finalization list.
+
+call finalizer for each value.
+
+optional: after calling a finalizer, make sure the object didn't get put
+back on the finalization list, remove if it did.
+if you don't do this, you can make an unkillable object by registering a
+finalizer that re-registers itself. this could be considered a feature though.
+
+pop dead values off stack.
+
+
+-----------------------------------------------------------------------------
+
+femtolisp semantics
+
+eval* is an internal procedure of 2 arguments, expr and env, invoked
+implicitly on input.
+The user-visible procedure eval performs eval* e Env ()
+
+eval* Symbol s E => lookup* s E
+eval* Atom a E => a
+... special forms ... quote arg, if a b c, other symbols from syntax env.
+eval* Cons f args E =>
+
+First the head expression, f, is evaluated, yielding f-.
+Then control is passed to #.apply f- args
+ #.apply is the user-visible apply procedure.
+ (here we imagine there is a user-invisible environment where f- is
+ bound to the value of the car and args is bound to the cdr of the input)
+
+
+Now (apply b lst) where b is a procedure (i.e. satisfies functionp) is
+identical to
+(eval (map (lambda (e) `',e) (cons b lst)))
+
+-----------------------------------------------------------------------------
+
+design of new toplevel
+
+system.lsp is compiled into the executable, and contains definitions of
+(load) and (repl).
+
+start with load bound to bootstrap_load (in C)
+on startup we call load on system, then call it again afterwards
+
+(load) reads and evaluates every form, keeping track of defined functions
+and macros (at the top level), and grabs a (main ...) form if it sees
+one. it applies optimizations to every definition, then invokes main.
+
+an error E during load should rethrow `(load-error ,filename ,E)
+such exceptions can be printed recursively
+
+lerror() should make a lisp string S from the result of sprintf, then
+raise `(,e ,S). first argument e should be a symbol.
+
+-----------------------------------------------------------------------------
+
+String API
+
+*string - append/construct
+ string.inc - (string.inc s i [nchars])
+ string.dec
+ string.char - char at byte offset
+ string.count - # of chars between 2 byte offsets
+*string.sub - substring between 2 byte offsets, or nil for beginning/end
+*string.split - (string.split s sep-chars)
+ string.trim - (string.trim s chars-at-start chars-at-end)
+*string.reverse
+ string.find - (string.find s str|char), or nil if not found
+ string.map - (string.map f s)
+*string.encode - to utf8
+*string.decode - from utf8 to UCS
+ string.width - # columns
+
+
+IOStream API
+
+ read
+ print, sprint
+ princ, sprinc
+ stream - (stream cvalue-as-bytestream)
+ file
+ fifo
+ socket
+ stream.eof
+ stream.write - (stream.write cvalue)
+ stream.read - (stream.read ctype)
+ stream.copy - (stream.copy to from [nbytes])
+ stream.copyuntil - (stream.copy to from byte)
+ stream.flush
+ stream.pos
+ stream.seek
+ stream.trunc
+ stream.getc - get utf8 character(s)
+
+
+ path.combine
+ path.parts
+ path.absolute
+ path.simplify
+ path.tempdir
+ path.tempname
+ path.homedir
+*path.cwd
+
+
+*time.now
+ time.parts
+ time.fromparts
+*time.string
+ time.fromstring
+
+
+*os.name
+*os.getenv
+*os.setenv
+ os.execv
+
+
+*rand
+*rand.uint32
+*rand.uint64
+*rand.double
+
+-----------------------------------------------------------------------------
+
+prettyprint notes
+
+* if head of list causes VPOS to increase and HPOS is a bit large, then
+switch to miser mode, otherwise default is ok, for example:
+
+> '((lambda (x y) (if (< x y) x y)) (a b c) (d e f) 2 3 (r t y))
+((lambda (x y)
+ (if (< x y) x y)) (a b c)
+ (d e f) 2 3
+ (r t y))
+
+* (if a b c) should always put newlines before b and c
+
+* write try_predict_len that gives a length for easy cases like
+ symbols, else -1. use it to avoid wrapping symbols around lines
--- /dev/null
+++ b/femtolisp/todo-scrap
@@ -1,0 +1,41 @@
+- readable gensyms. have uninterned symbols, but have all same-named
+ gensyms read to the same (eq) symbol within an expression.
+- fat pointers, i.e. 64 bits on 32-bit platforms. we could have full 32-bit
+ integers too. the mind boggles at the possibilities.
+ (it would be great if everybody decided that pointer types should forever
+ be wider than address spaces, with some bits reserved for application use)
+- any way at all to provide O(1) computed lookups (i.e. indexing).
+ CL uses vectors for this. once you have it, it's sufficient to get
+ efficient hash tables and everything else.
+ - could be done just by generalizing cons cells to have more than
+ car, cdr: c2r, c3r, etc. maybe (1 . 2 . 3 . 4 . ...)
+ all you need is a tag+size on the front of the object so the collector
+ knows how to deal with it.
+ (car x) == (ref x 0), etc.
+ (rplaca x v) == (rplac x 0 v), etc.
+ (size (cons 1 2)) == 2, etc.
+ - one possibility: if we see a cons whose CAR is tagptr(0x10,TAG_SYM),
+ then the CDR is the size and the following words are the elements.
+ . this approach is especially good if vectors are separate types from
+ conses
+ - another: add u_int32_t size to cons_t, making them all 50% bigger.
+ access is simpler and more uniform, without fully doubling the size like
+ we'd get with fat pointers.
+
+Notice that the size is one byte more than the number of characters in
+the string. This is because femtoLisp adds a NUL terminator to make its
+strings compatible with C. No effort is made to hide this fact.
+But since femtoLisp tracks the sizes of cvalues, it doesn't need the
+terminator itself. Therefore it treats zero bytes specially as rarely
+as possible. In particular, zeros are only special in values whose type
+is exactly <tt>(array char)</tt>, and are only interpreted in the
+following cases:
+<ul>
+<li>When printing strings, a final NUL is never printed. NULs in the
+middle of a string are printed though.
+<li>String constructors NUL-terminate their output.
+<li>Explicit string functions (like <tt>strlen</tt>) treat NULs the same
+way equivalent C functions would.
+</ul>
+Arrays of uchar, int8, etc. are treated as raw data and zero bytes are
+never special.
--- /dev/null
+++ b/femtolisp/torus.lsp
@@ -1,0 +1,46 @@
+(defun maplist (f l)
+ (if (null l) ()
+ (cons (f l) (maplist f (cdr l)))))
+
+; produce a beautiful, toroidal cons structure
+; make m copies of a CDR-circular list of length n, and connect corresponding
+; conses in CAR-circular loops
+; replace maplist 'identity' with 'copy-tree' for rapdily exploding memory use
+(defun torus (m n)
+ (let* ((l (map-int identity n))
+ (g l)
+ (prev g))
+ (dotimes (i (- m 1))
+ (setq prev g)
+ (setq g (maplist identity g))
+ (rplacd (last prev) prev))
+ (rplacd (last g) g)
+ (let ((a l)
+ (b g))
+ (dotimes (i n)
+ (rplaca a b)
+ (setq a (cdr a))
+ (setq b (cdr b))))
+ l))
+
+(defun cyl (m n)
+ (let* ((l (map-int identity n))
+ (g l))
+ (dotimes (i (- m 1))
+ (setq g (maplist identity g)))
+ (let ((a l)
+ (b g))
+ (dotimes (i n)
+ (rplaca a b)
+ (setq a (cdr a))
+ (setq b (cdr b))))
+ l))
+
+(time (progn (print (torus 100 100)) nil))
+; with ltable
+; printing time: 0.415sec
+; reading time: 0.165sec
+
+; with ptrhash
+; printing time: 0.081sec
+; reading time: 0.0264sec
--- /dev/null
+++ b/femtolisp/unittest.lsp
@@ -1,0 +1,77 @@
+(define (every-int n)
+ (list (fixnum n) (int8 n) (uint8 n) (int16 n) (uint16 n) (int32 n) (uint32 n)
+ (int64 n) (uint64 n)))
+
+(define (every-sint n)
+ (list (fixnum n) (int8 n) (int16 n) (int32 n) (int64 n)))
+
+(define (each f l)
+ (if (atom l) ()
+ (progn (f (car l))
+ (each f (cdr l)))))
+
+(define (each^2 f l m)
+ (each (lambda (o) (each (lambda (p) (f o p)) m)) l))
+
+(define (test-lt a b)
+ (each^2 (lambda (neg pos)
+ (progn
+ (eval `(assert (= -1 (compare ,neg ,pos))))
+ (eval `(assert (= 1 (compare ,pos ,neg))))))
+ a
+ b))
+
+(define (test-eq a b)
+ (each^2 (lambda (a b)
+ (progn
+ (eval `(assert (= 0 (compare ,a ,b))))))
+ a
+ b))
+
+(test-lt (every-sint -1) (every-int 1))
+(test-lt (every-int 0) (every-int 1))
+(test-eq (every-int 88) (every-int 88))
+(test-eq (every-sint -88) (every-sint -88))
+
+(define (test-square a)
+ (each (lambda (i) (eval `(assert (>= (* ,i ,i) 0))))
+ a))
+
+(test-square (every-sint -67))
+(test-square (every-int 3))
+(test-square (every-int 0x80000000))
+(test-square (every-sint 0x80000000))
+(test-square (every-sint -0x80000000))
+
+(assert (= (* 128 0x02000001) 0x100000080))
+
+(assert (= (/ 1) 1))
+(assert (= (/ -1) -1))
+(assert (= (/ 2) 0))
+(assert (= (/ 2.0) 0.5))
+
+; tricky cases involving INT_MIN
+(assert (< (- #uint32(0x80000000)) 0))
+(assert (> (- #int32(0x80000000)) 0))
+(assert (< (- #uint64(0x8000000000000000)) 0))
+(assert (> (- #int64(0x8000000000000000)) 0))
+
+(assert (not (equal #int64(0x8000000000000000) #uint64(0x8000000000000000))))
+(assert (equal (+ #int64(0x4000000000000000) #int64(0x4000000000000000))
+ #uint64(0x8000000000000000)))
+(assert (equal (* 2 #int64(0x4000000000000000))
+ #uint64(0x8000000000000000)))
+
+; ok, a couple end-to-end tests as well
+(define (fib n) (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
+(assert (equal (fib 20) 6765))
+
+(load "color.lsp")
+(assert (equal (color-pairs (generate-5x5-pairs) '(a b c d e))
+ '((23 . a) (9 . a) (22 . b) (17 . d) (14 . d) (8 . b) (21 . e)
+ (19 . b) (16 . c) (13 . c) (11 . b) (7 . e) (24 . c) (20 . d)
+ (18 . e) (15 . a) (12 . a) (10 . e) (6 . d) (5 . c) (4 . e)
+ (3 . d) (2 . c) (0 . b) (1 . a))))
+
+(princ "all tests pass\n")
+T
--- /dev/null
+++ b/femtolisp/wt.lsp
@@ -1,0 +1,8 @@
+(setq i 0)
+(defmacro while- (test . forms)
+ `((label -loop- (lambda ()
+ (if ,test
+ (progn ,@forms
+ (-loop-))
+ nil)))))
+(while (< i 10000000) (set 'i (+ i 1)))