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:
commit
f72190d2dc
8 changed files with 3960 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
28
CLAUDE.md
Normal file
28
CLAUDE.md
Normal 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
17
Makefile
Normal 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
141
README.md
Normal 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
265
bench.py
Normal 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
340
stdlib.lsp
Normal 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)))))))))
|
||||
1523
uncommonlisp.py
Normal file
1523
uncommonlisp.py
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue