lumbda/wasm/dist-repl/python/stdlib.lsp
russell@unturf.com d8ffab5ea6
repl: /repl/ page with encrypted multi-tab sessions
Interactive REPL at lumbda.com/repl with:
  - multi-tab sessions (click + to add, × to close, double-click to rename)
  - per-tab tier selector (python/c/asm/all-three race)
  - persistent transcripts encrypted in localStorage via Web Crypto
    (PBKDF2 + AES-GCM, vault id = SHA-256(password || device-salt) —
    same pattern as unsandbox's vault-encryption-design.md, native
    crypto.subtle API instead of CryptoJS)
  - ephemeral mode (skip vault, transcripts vanish on reload)
  - one worker per (tab × tier) — state persists across evals in a tab
  - reboot tier button (terminate this tab's worker, fresh state next eval)
  - cancel button (kills the running worker in active tab)

Home page now links to both /playground/ and /repl/.

Tier state itself does NOT persist across reloads — the transcript does,
but defines/set!/hash-tables vanish with the worker. Portal save/resume
in WAT (deferred) will let a tier session survive close+reopen.
2026-06-14 12:50:35 -04:00

385 lines
13 KiB
Text

;;; stdlib.lsp — standard library for lumbda
;;; Load with: (load "stdlib.lsp")
;;; Automatically loaded by the interpreter if found next to lumbda.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)))))))))
;;; ─── SRFI-64 lightweight test framework ─────────────────────────────────────
(define *test-pass* 0)
(define *test-fail* 0)
(define *test-group* "")
(define *test-verbose* #f)
(define (test-begin name)
(set! *test-group* name)
(set! *test-pass* 0)
(set! *test-fail* 0)
(display (string-append "--- " name " ---\n")))
(define (test-end)
(display (string-append *test-group* ": "
(number->string *test-pass*) " passed, "
(number->string *test-fail*) " failed\n"))
(= *test-fail* 0))
(define (test-assert msg val)
(if val
(begin (set! *test-pass* (+ *test-pass* 1))
(when *test-verbose* (display (string-append " OK " msg "\n"))))
(begin (set! *test-fail* (+ *test-fail* 1))
(display (string-append " FAIL " msg "\n")))))
(define-macro (test-equal msg expected expr)
(let ((r (gensym)) (e (gensym)))
`(let ((,r ,expr) (,e ,expected))
(if (equal? ,r ,e)
(begin (set! *test-pass* (+ *test-pass* 1))
(when *test-verbose* (display (string-append " OK " ,msg "\n"))))
(begin (set! *test-fail* (+ *test-fail* 1))
(display (string-append " FAIL " ,msg ": got " (write-to-string ,r)
" expected " (write-to-string ,e) "\n")))))))
(define-macro (test-error msg expr)
(let ((ok (gensym)))
`(let ((,ok (guard (e (#t #t)) ,expr #f)))
(if ,ok
(begin (set! *test-pass* (+ *test-pass* 1))
(when *test-verbose* (display (string-append " OK " ,msg " (error)\n"))))
(begin (set! *test-fail* (+ *test-fail* 1))
(display (string-append " FAIL " ,msg " (expected error)\n")))))))