ref: c2026ba77cad42c4cb4b277dd80061c53d79361c
parent: c38c47d264f11a1a1f1a2e6a2d23d9eb755f0127
author: JeffBezanson <[email protected]>
date: Mon May 18 22:54:56 EDT 2009
adding gc handles, making evaluator stack static this provides a better interface and could only help performance starting to add some useful library code
--- a/femtolisp/builtins.c
+++ b/femtolisp/builtins.c
@@ -173,9 +173,11 @@
{
(void)args;
argcount("environment", nargs, 0);
- PUSH(NIL);
- global_env_list(symtab, &Stack[SP-1]);
- return POP();
+ value_t lst = NIL;
+ fl_gc_handle(&lst);
+ global_env_list(symtab, &lst);
+ fl_free_gc_handles(1);
+ return lst;
}
extern value_t QUOTE;
--- a/femtolisp/flisp.c
+++ b/femtolisp/flisp.c
@@ -84,10 +84,16 @@
ANYARGS, 2, 3 };
#define N_STACK 262144
-value_t StaticStack[N_STACK];
-value_t *Stack = StaticStack;
-uint32_t SP = 0;
+static value_t Stack[N_STACK];
+static uint32_t SP = 0;
+#define PUSH(v) (Stack[SP++] = (v))
+#define POP() (Stack[--SP])
+#define POPN(n) (SP-=(n))
+#define N_GC_HANDLES 1024
+static value_t *GCHandleStack[N_GC_HANDLES];
+static uint32_t N_GCHND = 0;
+
value_t NIL, FL_T, FL_F, LAMBDA, QUOTE, IF, TRYCATCH;
value_t BACKQUOTE, COMMA, COMMAAT, COMMADOT, FUNCTION;
value_t IOError, ParseError, TypeError, ArgError, UnboundError, MemoryError;
@@ -371,6 +377,19 @@
// collector ------------------------------------------------------------------
+void fl_gc_handle(value_t *pv)
+{
+ if (N_GCHND >= N_GC_HANDLES)
+ lerror(MemoryError, "out of gc handles");
+ GCHandleStack[N_GCHND++] = pv;
+}
+
+void fl_free_gc_handles(int n)
+{
+ assert(N_GCHND >= n);
+ N_GCHND -= n;
+}
+
static value_t relocate(value_t v)
{
value_t a, d, nc, first, *pcdr;
@@ -493,6 +512,8 @@
for (i=0; i < SP; i++)
Stack[i] = relocate(Stack[i]);
+ for (i=0; i < N_GCHND; i++)
+ *GCHandleStack[i] = relocate(*GCHandleStack[i]);
trace_globals(symtab);
relocate_typetable();
rs = readstate;
--- a/femtolisp/flisp.h
+++ b/femtolisp/flisp.h
@@ -96,11 +96,8 @@
#define isclosure(x) isfunction(x)
#define iscbuiltin(x) (iscvalue(x) && (cv_class((cvalue_t*)ptr(x))==builtintype))
-extern value_t *Stack;
-extern uint32_t SP;
-#define PUSH(v) (Stack[SP++] = (v))
-#define POP() (Stack[--SP])
-#define POPN(n) (SP-=(n))
+void fl_gc_handle(value_t *pv);
+void fl_free_gc_handles(int n);
// maximum number of explicit arguments. the 128th arg is a list of rest args.
// the largest value nargs can have is MAX_ARGS+1
--- a/femtolisp/iostream.c
+++ b/femtolisp/iostream.c
@@ -91,15 +91,18 @@
value_t fl_read(value_t *args, u_int32_t nargs)
{
+ value_t arg;
if (nargs > 1) {
argcount("read", nargs, 1);
}
else if (nargs == 0) {
- PUSH(symbol_value(instrsym));
- args = &Stack[SP-1];
+ arg = symbol_value(instrsym);
}
- (void)toiostream(args[0], "read");
- return read_sexpr(args[0]);
+ else {
+ arg = args[0];
+ }
+ (void)toiostream(arg, "read");
+ return read_sexpr(arg);
}
value_t fl_iogetc(value_t *args, u_int32_t nargs)
--- /dev/null
+++ b/femtolisp/lib/lazy.scm
@@ -1,0 +1,47 @@
+; SRFI 45: Primitives for Expressing Iterative Lazy Algorithms
+; by André van Tonder
+;=========================================================================
+; Boxes
+
+(define (box x) (list x))
+(define unbox car)
+(define set-box! set-car!)
+
+;=========================================================================
+; Primitives for lazy evaluation:
+
+(define (eager x)
+ (box (cons 'eager x)))
+
+#|
+(define-syntax lazy
+ (syntax-rules ()
+ ((lazy exp)
+ (box (cons 'lazy (lambda () exp))))))
+
+(define-syntax delay
+ (syntax-rules ()
+ ((delay exp) (lazy (eager exp)))))
+|#
+
+(define-macro (lazy exp)
+ `(box (cons 'lazy (lambda () ,exp))))
+
+(define-macro (delay exp)
+ `(lazy (eager ,exp)))
+
+(define (force promise)
+ (let ((content (unbox promise)))
+ (case (car content)
+ ((eager) (cdr content))
+ ((lazy) (let* ((promise* ((cdr content)))
+ (content (unbox promise))) ; *
+ (if (not (eqv? (car content) 'eager)) ; *
+ (begin (set-car! content (car (unbox promise*)))
+ (set-cdr! content (cdr (unbox promise*)))
+ (set-box! promise* content)))
+ (force promise))))))
+
+; (*) These two lines re-fetch and check the original promise in case
+; the first line of the let* caused it to be forced. For an example
+; where this happens, see reentrancy test 3 below.
--- /dev/null
+++ b/femtolisp/lib/sort.scm
@@ -1,0 +1,193 @@
+;;; "sort.scm" Defines: sorted?, merge, merge!, sort, sort!
+;;; Author : Richard A. O'Keefe (based on Prolog code by D.H.D.Warren)
+;;;
+;;; This code is in the public domain.
+
+;;; Updated: 11 June 1991
+;;; Modified for scheme library: Aubrey Jaffer 19 Sept. 1991
+;;; Updated: 19 June 1995
+;;; (sort, sort!, sorted?): Generalized to strings by jaffer: 2003-09-09
+;;; (sort, sort!, sorted?): Generalized to arrays by jaffer: 2003-10-04
+;;; jaffer: 2006-10-08:
+;;; (sort, sort!, sorted?, merge, merge!): Added optional KEY argument.
+;;; jaffer: 2006-11-05:
+;;; (sorted?, merge, merge!, sort, sort!): Call KEY arg at most once
+;;; per element.
+
+;(require 'array)
+
+;;; (sorted? sequence less?)
+;;; is true when sequence is a list (x0 x1 ... xm) or a vector #(x0 ... xm)
+;;; such that for all 1 <= i <= m,
+;;; (not (less? (list-ref list i) (list-ref list (- i 1)))).
+;@
+(define (sorted? seq less? . opt-key)
+ (define key (if (null? opt-key) identity (car opt-key)))
+ (cond ((null? seq) #t)
+ ((array? seq)
+ (let ((dimax (+ -1 (car (array-dimensions seq)))))
+ (or (<= dimax 1)
+ (let loop ((idx (+ -1 dimax))
+ (last (key (array-ref seq dimax))))
+ (or (negative? idx)
+ (let ((nxt (key (array-ref seq idx))))
+ (and (less? nxt last)
+ (loop (+ -1 idx) nxt))))))))
+ ((null? (cdr seq)) #t)
+ (else
+ (let loop ((last (key (car seq)))
+ (next (cdr seq)))
+ (or (null? next)
+ (let ((nxt (key (car next))))
+ (and (not (less? nxt last))
+ (loop nxt (cdr next)))))))))
+
+;;; (merge a b less?)
+;;; takes two lists a and b such that (sorted? a less?) and (sorted? b less?)
+;;; and returns a new list in which the elements of a and b have been stably
+;;; interleaved so that (sorted? (merge a b less?) less?).
+;;; Note: this does _not_ accept arrays. See below.
+;@
+(define (merge a b less? . opt-key)
+ (define key (if (null? opt-key) identity (car opt-key)))
+ (cond ((null? a) b)
+ ((null? b) a)
+ (else
+ (let loop ((x (car a)) (kx (key (car a))) (a (cdr a))
+ (y (car b)) (ky (key (car b))) (b (cdr b)))
+ ;; The loop handles the merging of non-empty lists. It has
+ ;; been written this way to save testing and car/cdring.
+ (if (less? ky kx)
+ (if (null? b)
+ (cons y (cons x a))
+ (cons y (loop x kx a (car b) (key (car b)) (cdr b))))
+ ;; x <= y
+ (if (null? a)
+ (cons x (cons y b))
+ (cons x (loop (car a) (key (car a)) (cdr a) y ky b))))))))
+
+(define (sort:merge! a b less? key)
+ (define (loop r a kcara b kcarb)
+ (cond ((less? kcarb kcara)
+ (set-cdr! r b)
+ (if (null? (cdr b))
+ (set-cdr! b a)
+ (loop b a kcara (cdr b) (key (cadr b)))))
+ (else ; (car a) <= (car b)
+ (set-cdr! r a)
+ (if (null? (cdr a))
+ (set-cdr! a b)
+ (loop a (cdr a) (key (cadr a)) b kcarb)))))
+ (cond ((null? a) b)
+ ((null? b) a)
+ (else
+ (let ((kcara (key (car a)))
+ (kcarb (key (car b))))
+ (cond
+ ((less? kcarb kcara)
+ (if (null? (cdr b))
+ (set-cdr! b a)
+ (loop b a kcara (cdr b) (key (cadr b))))
+ b)
+ (else ; (car a) <= (car b)
+ (if (null? (cdr a))
+ (set-cdr! a b)
+ (loop a (cdr a) (key (cadr a)) b kcarb))
+ a))))))
+
+;;; takes two sorted lists a and b and smashes their cdr fields to form a
+;;; single sorted list including the elements of both.
+;;; Note: this does _not_ accept arrays.
+;@
+(define (merge! a b less? . opt-key)
+ (sort:merge! a b less? (if (null? opt-key) identity (car opt-key))))
+
+(define (sort:sort-list! seq less? key)
+ (define keyer (if key car identity))
+ (define (step n)
+ (cond ((> n 2) (let* ((j (quotient n 2))
+ (a (step j))
+ (k (- n j))
+ (b (step k)))
+ (sort:merge! a b less? keyer)))
+ ((= n 2) (let ((x (car seq))
+ (y (cadr seq))
+ (p seq))
+ (set! seq (cddr seq))
+ (cond ((less? (keyer y) (keyer x))
+ (set-car! p y)
+ (set-car! (cdr p) x)))
+ (set-cdr! (cdr p) '())
+ p))
+ ((= n 1) (let ((p seq))
+ (set! seq (cdr seq))
+ (set-cdr! p '())
+ p))
+ (else '())))
+ (define (key-wrap! lst)
+ (cond ((null? lst))
+ (else (set-car! lst (cons (key (car lst)) (car lst)))
+ (key-wrap! (cdr lst)))))
+ (define (key-unwrap! lst)
+ (cond ((null? lst))
+ (else (set-car! lst (cdar lst))
+ (key-unwrap! (cdr lst)))))
+ (cond (key
+ (key-wrap! seq)
+ (set! seq (step (length seq)))
+ (key-unwrap! seq)
+ seq)
+ (else
+ (step (length seq)))))
+
+(define (rank-1-array->list array)
+ (define dimensions (array-dimensions array))
+ (do ((idx (+ -1 (car dimensions)) (+ -1 idx))
+ (lst '() (cons (array-ref array idx) lst)))
+ ((< idx 0) lst)))
+
+;;; (sort! sequence less?)
+;;; sorts the list, array, or string sequence destructively. It uses
+;;; a version of merge-sort invented, to the best of my knowledge, by
+;;; David H. D. Warren, and first used in the DEC-10 Prolog system.
+;;; R. A. O'Keefe adapted it to work destructively in Scheme.
+;;; A. Jaffer modified to always return the original list.
+;@
+(define (sort! seq less? . opt-key)
+ (define key (if (null? opt-key) #f (car opt-key)))
+ (cond ((array? seq)
+ (let ((dims (array-dimensions seq)))
+ (do ((sorted (sort:sort-list! (rank-1-array->list seq) less? key)
+ (cdr sorted))
+ (i 0 (+ i 1)))
+ ((null? sorted) seq)
+ (array-set! seq (car sorted) i))))
+ (else ; otherwise, assume it is a list
+ (let ((ret (sort:sort-list! seq less? key)))
+ (if (not (eq? ret seq))
+ (do ((crt ret (cdr crt)))
+ ((eq? (cdr crt) seq)
+ (set-cdr! crt ret)
+ (let ((scar (car seq)) (scdr (cdr seq)))
+ (set-car! seq (car ret)) (set-cdr! seq (cdr ret))
+ (set-car! ret scar) (set-cdr! ret scdr)))))
+ seq))))
+
+;;; (sort sequence less?)
+;;; sorts a array, string, or list non-destructively. It does this
+;;; by sorting a copy of the sequence. My understanding is that the
+;;; Standard says that the result of append is always "newly
+;;; allocated" except for sharing structure with "the last argument",
+;;; so (append x '()) ought to be a standard way of copying a list x.
+;@
+(define (sort seq less? . opt-key)
+ (define key (if (null? opt-key) #f (car opt-key)))
+ (cond ((array? seq)
+ (let ((dims (array-dimensions seq)))
+ (define newra (apply make-array seq dims))
+ (do ((sorted (sort:sort-list! (rank-1-array->list seq) less? key)
+ (cdr sorted))
+ (i 0 (+ i 1)))
+ ((null? sorted) newra)
+ (array-set! newra (car sorted) i))))
+ (else (sort:sort-list! (append seq '()) less? key))))
--- a/femtolisp/string.c
+++ b/femtolisp/string.c
@@ -80,10 +80,11 @@
{
int term=0;
if (nargs == 2) {
- term = (POP() != FL_F);
- nargs--;
+ term = (args[1] != FL_F);
}
- argcount("string.decode", nargs, 1);
+ else {
+ argcount("string.decode", nargs, 1);
+ }
if (!isstring(args[0]))
type_error("string.decode", "string", args[0]);
cvalue_t *cv = (cvalue_t*)ptr(args[0]);
@@ -119,9 +120,9 @@
}
set(printreadablysym, oldpr);
set(printprettysym, oldpp);
- PUSH(buf);
- value_t outp = stream_to_string(&Stack[SP-1]);
- (void)POP();
+ fl_gc_handle(&buf);
+ value_t outp = stream_to_string(&buf);
+ fl_free_gc_handles(1);
return outp;
}
@@ -132,10 +133,12 @@
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;
+ value_t first=NIL, c=NIL, last;
size_t junk;
+ fl_gc_handle(&first);
+ fl_gc_handle(&last);
+
do {
// find and allocate next token
tokstart = tokend = i;
@@ -143,7 +146,7 @@
!u8_memchr(delim, u8_nextmemchar(s, &i), dlen, &junk))
tokend = i;
ssz = tokend - tokstart;
- PUSH(c); // save previous cons cell
+ last = c; // save previous cons cell
c = fl_cons(cvalue_string(ssz), NIL);
// we've done allocation; reload movable pointers
@@ -153,19 +156,17 @@
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;
- }
+ if (last == NIL)
+ first = c; // first time, save first cons
+ else
+ ((cons_t*)ptr(last))->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();
+ fl_free_gc_handles(2);
+ return first;
}
value_t fl_string_sub(value_t *args, u_int32_t nargs)