Initial implementation of uncommonlisp

Single-file Scheme-like Lisp interpreter in Python with:
- TCO via explicit while loop (no Python stack overflow at any depth)
- syntax-rules with ellipsis for hygienic macros
- define-macro for procedural macros
- Full numeric tower, strings, chars, vectors, hash tables
- SRFI-1 list library
- call/cc (escape continuations), values, dynamic-wind, guard
- Python interop (py-eval, py-import, py-call, py-attr)
- 396 passing unit/integration/functional tests
- stdlib.lsp with 60+ utility functions
- Benchmark suite vs CPython baseline
This commit is contained in:
russell@unturf.com 2026-04-13 11:01:04 -04:00
commit f72190d2dc
8 changed files with 3960 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
__pycache__/
*.pyc
*.pyo

28
CLAUDE.md Normal file
View file

@ -0,0 +1,28 @@
# Agent Blackops
This repo is operated by **agent blackops** — ml agent for fox/timehexon on the unsandbox/unturf/permacomputer platform.
## Identity
Full shard: `~/git/unsandbox.com/blackops/BLACKOPS.md`
## Rules
- I propose, fox decides. Unsure = ask. Can't ask = stop.
- No autonomous ops decisions. No destructive commands without explicit instruction.
- Fail-closed. Cleanup crew, not demolition.
- Check the time every session. Gaps are information.
- DRY in context — single source of truth, no sprawl.
- Never say "AI" — always say "machine learning."
- Prefer "defect" over "bug."
## Orientation
```bash
date -u
pwd
git log --oneline -5
git status
```
Then ask fox what the mission is.

17
Makefile Normal file
View file

@ -0,0 +1,17 @@
all: test
test:
python3 tests.py
test-verbose:
python3 tests.py -v
bench:
python3 bench.py
repl:
python3 uncommonlisp.py
clean:
.PHONY: all test test-verbose bench repl clean

141
README.md Normal file
View file

@ -0,0 +1,141 @@
# uncommonlisp
A Scheme-like Lisp interpreter in one Python file.
```
λ> (define (fib n)
(let loop ((a 0) (b 1) (i 0))
(if (= i n) a (loop b (+ a b) (+ i 1)))))
λ> (map fib (iota 10))
(0 1 1 2 3 5 8 13 21 34)
```
## Usage
```bash
python3 uncommonlisp.py # interactive REPL
python3 uncommonlisp.py script.lsp # run a file
python3 uncommonlisp.py -e '(+ 1 2)' # eval an expression
```
## What's implemented
**Core language**
- Full lexical scoping and closures
- Tail-call optimization (TCO) via explicit loop — deep recursion never blows the stack
- Hygienic macros via `syntax-rules` with ellipsis (`...`) support
- `define-macro` / `defmacro` for procedural macro transformers
- `call/cc` (escape continuations)
- `values` / `call-with-values`
- `dynamic-wind`, `guard`, `with-exception-handler`
- `quasiquote` / `unquote` / `unquote-splicing` with proper nesting
**Special forms**
`define` `set!` `lambda` `λ` `if` `cond` `case` `and` `or` `when` `unless`
`begin` `let` `let*` `letrec` `letrec*` named-let `do`
`quasiquote` `define-macro` `define-syntax` `syntax-rules`
`let-syntax` `letrec-syntax` `apply` `eval` `values` `call/cc`
`dynamic-wind` `guard` `parameterize` `load` `error`
**Built-ins**
- Arithmetic: `+` `-` `*` `/` `quotient` `remainder` `modulo` `expt` `sqrt` `abs` `floor` `ceiling` `round` `truncate` `min` `max` `gcd` `lcm` `log` `exp` `sin` `cos` `tan` `atan` and more
- Comparison: `=` `<` `>` `<=` `>=` `zero?` `positive?` `negative?` `odd?` `even?`
- Booleans: `not` `boolean?` `boolean=?`
- Equality: `eq?` `eqv?` `equal?`
- Pairs & lists: `cons` `car` `cdr` `set-car!` `set-cdr!` `list` `list*` `length` `append` `reverse` `list-ref` `list-tail` `memq` `memv` `member` `assq` `assv` `assoc` `iota` `map` `for-each` `filter` `fold-left` `fold-right` `reduce` `any` `every` `count` `flat-map` `sort` `sort-by` `partition` `find` `take` `drop` `take-while` `drop-while` `take-right` `drop-right` `zip` `flatten` `concatenate` `list-tabulate` `unfold` and more
- SRFI-1: `last` `first``fifth` `delete` `lset-union` `lset-intersection` `lset-difference` `proper-list?` `dotted-list?`
- Strings: `string-length` `string-ref` `substring` `string-append` `string-upcase` `string-downcase` `string->list` `list->string` `string->symbol` `symbol->string` `string->number` `number->string` `string-contains` `string-split` `string-join` `string-trim` `string-replace` `format` and more
- Characters: `char->integer` `integer->char` `char-alphabetic?` `char-numeric?` `char-upcase` `char-downcase`
- Vectors: `make-vector` `vector` `vector-ref` `vector-set!` `vector->list` `list->vector`
- Hash tables: `make-hash-table` `hash-table-set!` `hash-table-ref` `hash-table-ref/default` `hash-table-delete!` `hash-table-exists?` `hash-table-keys` `hash-table-values` `hash-table->alist` `hash-table-walk` and more
- Type predicates: `number?` `integer?` `real?` `string?` `symbol?` `pair?` `null?` `list?` `char?` `vector?` `boolean?` `procedure?` `exact?` `inexact?`
- I/O: `display` `write` `newline` `read` `read-line` `load` `with-output-to-string`
- Python interop: `py-eval` `py-exec` `py-import` `py-call` `py-attr`
**Prelude** (loaded automatically)
`when` `unless` `case` `while` `for` `define-record-type` `1+` `1-` `add1` `sub1` `square` `cube` `compose` `atom?` `range` `flatten` `string-map` `string-for-each`
**Standard library** (`stdlib.lsp`, load explicitly)
Syntax-rules versions of `let`/`and`/`or`/`cond`/`case`/`do`, `fluid-let`, `receive` (SRFI-8), `begin0`, `while`/`until`, `dotimes`/`dolist`, `push!`/`pop!`, `and-let*` (SRFI-2), string utilities, list utilities (`sum` `product` `maximum` `minimum` `average` `enumerate` `transpose` `chunks` `interleave`), numeric utilities (`factorial` `fib` `prime?` `primes-up-to` `clamp`), alist/hash utilities, tree utilities, simple object system, coroutines via `call/cc`
## Examples
```scheme
; Closures
(define (make-counter)
(let ((n 0))
(lambda () (set! n (+ n 1)) n)))
(define c (make-counter))
(c) ; => 1
(c) ; => 2
; Hygienic macro (syntax-rules)
(define-syntax my-or
(syntax-rules ()
((my-or) #f)
((my-or e) e)
((my-or e1 e2 ...)
(let ((t e1))
(if t t (my-or e2 ...))))))
; define-record-type
(define-record-type point
(make-point x y)
point?
(x point-x)
(y point-y set-point-y!))
(define p (make-point 3 4))
(point-x p) ; => 3
; Named let (looping)
(let loop ((i 0) (acc '()))
(if (= i 5)
(reverse acc)
(loop (+ i 1) (cons (* i i) acc))))
; => (0 1 4 9 16)
; Hash tables
(define freq
(let ((h (make-hash-table)))
(for-each (lambda (x)
(hash-table-set! h x (+ 1 (hash-table-ref/default h x 0))))
'(a b a c b a))
h))
(hash-table-ref freq 'a) ; => 3
; Tail calls — no stack overflow even at depth 1,000,000
(define (count-down n)
(if (= n 0) 'done (count-down (- n 1))))
(count-down 1000000) ; => done
; Python interop
(define re (py-import "re"))
(py-call (py-attr re 'findall) "[0-9]+" "abc123def456")
; => ["123", "456"]
```
## Running tests
```bash
make test # run 396 tests
make test-verbose # verbose output
```
## Running benchmarks
```bash
python3 bench.py # compare against CPython baseline
python3 bench.py -v # show result values too
```
## File layout
```
uncommonlisp.py interpreter (self-contained, one file)
stdlib.lsp extended standard library (load manually)
tests.py test suite (396 tests)
bench.py benchmarks vs CPython
Makefile make test / make repl
```

265
bench.py Normal file
View file

@ -0,0 +1,265 @@
#!/usr/bin/env python3
"""
bench.py benchmarks for uncommonlisp.
Compares interpreter time against equivalent CPython.
Usage: python3 bench.py [-v]
"""
import sys, time, math
from uncommonlisp import make_global_env, read_all, leval, PRELUDE, show
VERBOSE = '-v' in sys.argv
def fresh():
g = make_global_env()
for e in read_all(PRELUDE): leval(e, g)
return g
def run(src, env):
result = None
for e in read_all(src): result = leval(e, env)
return result
def bench(name, lisp_src, python_fn, iters=3):
"""Time both lisp and python implementations; print results."""
env = fresh()
# Warm up
run(lisp_src, env)
python_fn()
# Time Lisp
lisp_times = []
for _ in range(iters):
env2 = fresh()
t = time.perf_counter()
result = run(lisp_src, env2)
lisp_times.append(time.perf_counter() - t)
lisp_best = min(lisp_times)
# Time Python
py_times = []
for _ in range(iters):
t = time.perf_counter()
py_result = python_fn()
py_times.append(time.perf_counter() - t)
py_best = min(py_times)
slowdown = lisp_best / py_best if py_best > 0 else float('inf')
status = '' if result == py_result or str(result) == str(py_result) else ''
print(f'{status} {name:<30} lisp={lisp_best*1000:7.1f}ms py={py_best*1000:7.1f}ms '
f'ratio={slowdown:5.1f}x')
if VERBOSE:
print(f' lisp result: {show(result)!r}')
print(f' py result: {py_result!r}')
return lisp_best, py_best
print('uncommonlisp benchmarks')
print('=' * 75)
# ── 1. Fibonacci (iterative, named let) ──────────────────────────────────────
bench(
'fib(35) named-let',
'''
(define (fib n)
(let loop ((a 0) (b 1) (i 0))
(if (= i n) a (loop b (+ a b) (+ i 1)))))
(fib 35)
''',
lambda: (lambda a=0, b=1, i=0, n=35:
[setattr(sys.modules[__name__], '_fib', None)] and
(lambda: exec('''
def _fib(n):
a, b = 0, 1
for _ in range(n): a, b = b, a+b
return a
''', globals()) or _fib(35))())()
)
# Simpler lambda-based benchmark
def py_fib(n=35):
a, b = 0, 1
for _ in range(n): a, b = b, a + b
return a
bench(
'fib(35) iterative',
'''
(define (fib n)
(let loop ((a 0) (b 1) (i 0))
(if (= i n) a (loop b (+ a b) (+ i 1)))))
(fib 35)
''',
lambda: py_fib(35)
)
# ── 2. fib(25) tree-recursive ─────────────────────────────────────────────────
def py_fib_rec(n):
if n <= 1: return n
return py_fib_rec(n-1) + py_fib_rec(n-2)
bench(
'fib(25) tree-recursive',
'''
(define (fib n)
(if (<= n 1) n (+ (fib (- n 1)) (fib (- n 2)))))
(fib 25)
''',
lambda: py_fib_rec(25)
)
# ── 3. Tak function ───────────────────────────────────────────────────────────
def py_tak(x, y, z):
if y >= x: return z
return py_tak(py_tak(x-1,y,z), py_tak(y-1,z,x), py_tak(z-1,x,y))
bench(
'tak(18,12,6)',
'''
(define (tak x y z)
(if (>= y x) z
(tak (tak (- x 1) y z)
(tak (- y 1) z x)
(tak (- z 1) x y))))
(tak 18 12 6)
''',
lambda: py_tak(18, 12, 6)
)
# ── 4. Tail-recursive sum ─────────────────────────────────────────────────────
def py_sum(n):
acc = 0
while n > 0: acc += n; n -= 1
return acc
bench(
'sum-to(100000) tail-recursive',
'''
(define (sum-to n)
(let loop ((i n) (acc 0))
(if (= i 0) acc (loop (- i 1) (+ acc i)))))
(sum-to 100000)
''',
lambda: py_sum(100000)
)
# ── 5. List operations ────────────────────────────────────────────────────────
def py_list_ops():
lst = list(range(5000))
lst = list(reversed(lst))
lst = [x for x in lst if x % 2 == 1]
lst = [x * x for x in lst]
return sum(lst)
bench(
'list ops (5000 elements)',
'''
(define lst (iota 5000))
(define lst (reverse lst))
(define lst (filter odd? lst))
(define lst (map (lambda (x) (* x x)) lst))
(fold-left + 0 lst)
''',
py_list_ops
)
# ── 6. Higher-order / closures ────────────────────────────────────────────────
def py_adder_factory():
adders = [(lambda n: lambda x: x + n)(i) for i in range(100)]
return sum(f(10) for f in adders)
bench(
'closure factory (100 adders)',
'''
(define (make-adder n) (lambda (x) (+ x n)))
(define adders (map make-adder (iota 100)))
(fold-left + 0 (map (lambda (f) (f 10)) adders))
''',
py_adder_factory
)
# ── 7. Hash table ─────────────────────────────────────────────────────────────
def py_hash_ops():
h = {}
for i in range(1000):
h[i] = i * i
return sum(h.get(i, 0) for i in range(1000))
bench(
'hash-table (1000 set+ref)',
'''
(define h (make-hash-table))
(do ((i 0 (+ i 1))) ((= i 1000))
(hash-table-set! h i (* i i)))
(do ((i 0 (+ i 1)) (s 0 (+ s (hash-table-ref/default h i 0))))
((= i 1000) s))
''',
py_hash_ops
)
# ── 8. String operations ──────────────────────────────────────────────────────
def py_string_ops():
parts = [str(i) for i in range(200)]
joined = ', '.join(parts)
return len(joined)
bench(
'string-join (200 numbers)',
'''
(string-length
(string-join (map number->string (iota 200)) ", "))
''',
py_string_ops
)
# ── 9. Ackermann (small) ─────────────────────────────────────────────────────
def py_ack(m, n):
if m == 0: return n + 1
if n == 0: return py_ack(m-1, 1)
return py_ack(m-1, py_ack(m, n-1))
bench(
'ackermann(3,6)',
'''
(define (ack m n)
(cond ((= m 0) (+ n 1))
((= n 0) (ack (- m 1) 1))
(else (ack (- m 1) (ack m (- n 1))))))
(ack 3 6)
''',
lambda: py_ack(3, 6)
)
# ── 10. Mergesort ─────────────────────────────────────────────────────────────
def py_msort(lst):
if len(lst) <= 1: return lst
mid = len(lst) // 2
L = py_msort(lst[:mid]); R = py_msort(lst[mid:])
result = []; i = j = 0
while i < len(L) and j < len(R):
if L[i] <= R[j]: result.append(L[i]); i += 1
else: result.append(R[j]); j += 1
return result + L[i:] + R[j:]
bench(
'mergesort (500 elements)',
'''
(define (merge a b)
(cond ((null? a) b) ((null? b) a)
((< (car a) (car b)) (cons (car a) (merge (cdr a) b)))
(else (cons (car b) (merge a (cdr b))))))
(define (split lst)
(let loop ((l lst) (a (quote ())) (b (quote ())))
(if (null? l) (list a b) (loop (cdr l) b (cons (car l) a)))))
(define (msort lst)
(if (or (null? lst) (null? (cdr lst))) lst
(let ((h (split lst)))
(merge (msort (car h)) (msort (cadr h))))))
(length (msort (reverse (iota 500))))
''',
lambda: len(py_msort(list(range(499, -1, -1))))
)
print('=' * 75)
print('ratio = lisp time / python time (lower is better for lisp)')

340
stdlib.lsp Normal file
View file

@ -0,0 +1,340 @@
;;; stdlib.lsp — standard library for uncommonlisp
;;; Load with: (load "stdlib.lsp")
;;; Automatically loaded by the interpreter if found next to uncommonlisp.py.
;;;; ── Syntax-rules versions of core macros ────────────────────────────────
(define-syntax my-let
(syntax-rules ()
((my-let ((var val) ...) body ...)
((lambda (var ...) body ...) val ...))))
(define-syntax my-let*
(syntax-rules ()
((my-let* () body ...)
(begin body ...))
((my-let* ((var val) rest ...) body ...)
(let ((var val)) (my-let* (rest ...) body ...)))))
(define-syntax my-and
(syntax-rules ()
((my-and) #t)
((my-and e) e)
((my-and e1 e2 ...)
(if e1 (my-and e2 ...) #f))))
(define-syntax my-or
(syntax-rules ()
((my-or) #f)
((my-or e) e)
((my-or e1 e2 ...)
(let ((t e1))
(if t t (my-or e2 ...))))))
(define-syntax my-cond
(syntax-rules (else =>)
((my-cond (else e ...)) (begin e ...))
((my-cond (test => f) rest ...)
(let ((t test)) (if t (f t) (my-cond rest ...))))
((my-cond (test e ...) rest ...)
(if test (begin e ...) (my-cond rest ...)))
((my-cond) (void))))
(define-syntax my-case
(syntax-rules (else)
((my-case key (else e ...)) (begin e ...))
((my-case key ((datum ...) e ...) rest ...)
(if (memv key '(datum ...))
(begin e ...)
(my-case key rest ...)))
((my-case key) (void))))
(define-syntax my-when
(syntax-rules ()
((my-when test body ...)
(if test (begin body ...) (void)))))
(define-syntax my-unless
(syntax-rules ()
((my-unless test body ...)
(if test (void) (begin body ...)))))
(define-syntax my-do
(syntax-rules ()
((my-do ((var init step ...) ...)
(test result ...)
body ...)
(let loop ((var init) ...)
(if test
(begin result ...)
(begin body ...
(loop (if (null? '(step ...)) var (car '(step ...))) ...)))))))
;;;; ── Pattern-matched swap ────────────────────────────────────────────────
(define-syntax swap!
(syntax-rules ()
((swap! a b)
(let ((tmp a))
(set! a b)
(set! b tmp)))))
;;;; ── fluid-let ──────────────────────────────────────────────────────────
(define-syntax fluid-let
(syntax-rules ()
((fluid-let ((var val) ...) body ...)
(let ((old-var var) ...)
(set! var val) ...
(let ((result (begin body ...)))
(set! var old-var) ...
result)))))
;;;; ── receive (SRFI-8) ───────────────────────────────────────────────────
(define-syntax receive
(syntax-rules ()
((receive formals expression body ...)
(call-with-values (lambda () expression)
(lambda formals body ...)))))
;;;; ── begin0 ────────────────────────────────────────────────────────────
(define-syntax begin0
(syntax-rules ()
((begin0 first rest ...)
(let ((result first))
rest ...
result))))
;;;; ── while / until ──────────────────────────────────────────────────────
(define-syntax while
(syntax-rules ()
((while test body ...)
(let loop ()
(when test body ... (loop))))))
(define-syntax until
(syntax-rules ()
((until test body ...)
(let loop ()
(unless test body ... (loop))))))
;;;; ── dotimes / dolist ───────────────────────────────────────────────────
(define-syntax dotimes
(syntax-rules ()
((dotimes (var n result ...) body ...)
(let loop ((var 0))
(if (= var n)
(begin result ...)
(begin body ... (loop (+ var 1))))))))
(define-syntax dolist
(syntax-rules ()
((dolist (var lst result ...) body ...)
(begin
(for-each (lambda (var) body ...) lst)
result ...))))
;;;; ── push! / pop! ───────────────────────────────────────────────────────
(define-syntax push!
(syntax-rules ()
((push! val lst)
(set! lst (cons val lst)))))
(define-syntax pop!
(syntax-rules ()
((pop! lst)
(let ((top (car lst)))
(set! lst (cdr lst))
top))))
;;;; ── and-let* (SRFI-2) ─────────────────────────────────────────────────
(define-syntax and-let*
(syntax-rules ()
((and-let* () body ...) (begin body ...))
((and-let* ((var expr) rest ...) body ...)
(let ((var expr))
(if var (and-let* (rest ...) body ...) #f)))
((and-let* ((expr) rest ...) body ...)
(if expr (and-let* (rest ...) body ...) #f))))
;;;; ── string utilities ───────────────────────────────────────────────────
(define (string-repeat s n)
(apply string-append (map (lambda (_) s) (iota n))))
(define (string-pad-left s len ch)
(let ((pad (- len (string-length s))))
(if (<= pad 0) s
(string-append (make-string pad ch) s))))
(define (string-pad-right s len ch)
(let ((pad (- len (string-length s))))
(if (<= pad 0) s
(string-append s (make-string pad ch)))))
(define (string->chars s) (string->list s))
(define (chars->string cs) (list->string cs))
;;;; ── list utilities ─────────────────────────────────────────────────────
(define (list-update! lst i val)
(list-set! lst i val)
lst)
(define (enumerate lst)
(map list (iota (length lst)) lst))
(define (transpose lsts)
(apply map list lsts))
(define (interleave lst sep)
(if (or (null? lst) (null? (cdr lst)))
lst
(cons (car lst) (cons sep (interleave (cdr lst) sep)))))
(define (chunks lst n)
(if (null? lst)
'()
(cons (take lst (min n (length lst)))
(chunks (drop lst n) n))))
(define (repeat-list x n)
(map (lambda (_) x) (iota n)))
(define (zip-with f . lsts)
(apply map f lsts))
(define (sum lst) (fold-left + 0 lst))
(define (product lst) (fold-left * 1 lst))
(define (maximum lst) (fold-left max (car lst) (cdr lst)))
(define (minimum lst) (fold-left min (car lst) (cdr lst)))
(define (average lst) (/ (sum lst) (length lst)))
;;;; ── numeric utilities ──────────────────────────────────────────────────
(define (clamp x lo hi) (max lo (min hi x)))
(define (between? x lo hi) (and (>= x lo) (<= x hi)))
(define (factorial n)
(let loop ((i n) (acc 1))
(if (<= i 1) acc (loop (- i 1) (* acc i)))))
(define (fib n)
(let loop ((a 0) (b 1) (i 0))
(if (= i n) a (loop b (+ a b) (+ i 1)))))
(define (prime? n)
(if (< n 2) #f
(let loop ((i 2))
(cond ((> (* i i) n) #t)
((= (remainder n i) 0) #f)
(else (loop (+ i 1)))))))
(define (primes-up-to n)
(filter prime? (range 2 (+ n 1))))
;;;; ── I/O utilities ──────────────────────────────────────────────────────
(define (println . args)
(for-each (lambda (x) (display x) (display " ")) args)
(newline))
(define (print-table rows)
(for-each (lambda (row)
(for-each (lambda (cell) (display cell) (display "\t")) row)
(newline))
rows))
(define (with-output-string thunk)
(with-output-to-string thunk))
;;;; ── association-list utilities ─────────────────────────────────────────
(define (alist-get key alist . default)
(let ((pair (assoc key alist)))
(if pair (cdr pair)
(if (null? default) #f (car default)))))
(define (alist-set key val alist)
(cons (cons key val)
(filter (lambda (p) (not (equal? (car p) key))) alist)))
(define (alist-remove key alist)
(filter (lambda (p) (not (equal? (car p) key))) alist))
(define (alist-keys alist) (map car alist))
(define (alist-values alist) (map cdr alist))
;;;; ── hash-table utilities ───────────────────────────────────────────────
(define (hash-table-map h f)
(let ((result (make-hash-table)))
(hash-table-walk h (lambda (k v) (hash-table-set! result k (f v))))
result))
(define (hash-table-filter h pred)
(let ((result (make-hash-table)))
(hash-table-walk h (lambda (k v) (when (pred k v) (hash-table-set! result k v))))
result))
(define (hash-table-from-lists keys vals)
(let ((h (make-hash-table)))
(for-each (lambda (k v) (hash-table-set! h k v)) keys vals)
h))
;;;; ── tree utilities ─────────────────────────────────────────────────────
(define (tree-map f tree)
(if (pair? tree)
(cons (tree-map f (car tree)) (tree-map f (cdr tree)))
(f tree)))
(define (tree-fold f init tree)
(if (pair? tree)
(tree-fold f (tree-fold f init (car tree)) (cdr tree))
(f init tree)))
(define (tree-member? x tree)
(cond ((null? tree) #f)
((equal? x tree) #t)
((pair? tree) (or (tree-member? x (car tree))
(tree-member? x (cdr tree))))
(else #f)))
;;;; ── simple object system ───────────────────────────────────────────────
;;; (make-object methods-alist) → an object
;;; (send obj 'method arg...) → dispatch
(define (make-object methods)
(lambda (msg . args)
(let ((m (assoc msg methods)))
(if m
(apply (cdr m) args)
(error "unknown method" msg)))))
(define (send obj msg . args)
(apply obj msg args))
;;;; ── coroutine via call/cc ───────────────────────────────────────────────
(define (make-generator thunk)
(let ((k #f) (done #f))
(lambda ()
(if done 'done
(call/cc
(lambda (return)
(if k
(k return)
(begin
(thunk (lambda (val)
(call/cc (lambda (next)
(set! k next)
(return val)))))
(set! done #t)
(return 'done)))))))))

1643
tests.py Normal file

File diff suppressed because it is too large Load diff

1523
uncommonlisp.py Normal file

File diff suppressed because it is too large Load diff