lumbda/examples/spiral-client.lsp
russell@unturf.com f7352b51b0 rename: uncommonlisp -> lumbda throughout the repo
Historical internal name "uncommonlisp" retired in favor of the
public name "lumbda" ahead of lumbda.com going live. Scope of
this commit:

Source files renamed:
  uncommonlisp.py                     -> lumbda.py
  asm/uncommonlisp.s                  -> asm/lumbda.s
  c/uncommonlisp.h                    -> c/lumbda.h
  whitepaper/uncommonlisp-whitepaper  -> whitepaper/lumbda-whitepaper (.rst + .pdf)

Binaries renamed (tracked ones; c/ was always gitignored):
  asm/uncommonlisp, asm/uncommonlisp-gc, asm/uncommonlisp.o,
  asm/uncommonlisp-gc.o                -> asm/lumbda(-gc)(.o)
  c/.gitignore                          -> ignores lumbda

Internal string updates (sed pass ordered longest-first):
  asm/uncommonlisp -> asm/lumbda
  c/uncommonlisp   -> c/lumbda
  uncommonlisp.py  -> lumbda.py
  UNCOMMONLISP_BIN -> LUMBDA_BIN (asm/test.sh env var)
  "uncommonlisp> " -> "lumbda> " (asm REPL prompt baked into binary)
  UNCOMMONLISP     -> LUMBDA (macros, comments)
  uncommonlisp     -> lumbda (prose)

Binary portal magic updated:
  "ULPORTAL" -> "LUMBDAB1"   # "Lumbda Binary v1"
Old portal files are not backward-compatible — this is a deliberate
break since it's the rename moment. S-expression portals already
carry their own ";; lumbda-portal v1" header and remain cleanly
versioned.

WHITEPAPER.pdf / WHITEPAPER.rst symlinks repointed to the renamed
files. Makefile's whitepaper target targets lumbda-whitepaper.pdf.

Not changed (intentional, separate phases):
  - Filesystem directory /home/fox/git/uncommonlisp itself
    (fox renames locally and the gitlab repo URL in a follow-up)
  - tests.py hardcoded cwd=/home/fox/git/uncommonlisp
    (matches the current on-disk location; will flip when the
    directory rename ships)
  - Git history (immutable; old commits still say uncommonlisp,
    which is correct — that's what they were)

Verified:
  137 asm no-GC + 137 asm GC + 571 Python + 83 C + 189 shared
  functional tests all pass under the new names.
  bench-gc-http (2000 req): all 4 cells behave as expected
  (cells 1/2 flat, 3 leaks, 4 bounded at 1 chunk).
  Python REPL, C REPL, asm REPL all start cleanly.
2026-04-19 10:20:11 -04:00

113 lines
3.9 KiB
Text

;;; spiral-client.lsp — demonstrate envelope teleport across two nodes.
;;;
;;; Flow:
;;; 1. Seed node A with EML theorems 1..3
;;; 2. Seed node B with EML theorems 3..5 (theorem 3 overlaps)
;;; 3. Pre-spiral: A has 3 hashes, B has 3 hashes, 1 in common
;;; 4. Spiral A→B: B pulls A's envelope and merges
;;; 5. Spiral B→A: A pulls B's envelope and merges
;;; 6. Post-spiral: both nodes hold the union (5 unique hashes each)
;;;
;;; Proves that independent caches converge with one round-trip per
;;; direction. N nodes in a mesh converge after N-1 spiral passes.
;;;
;;; Usage (start both nodes first):
;;; python3 lumbda.py --fast examples/spiral-client.lsp
(define *host* "127.0.0.1")
(define *port-a* 9086)
(define *port-b* 9087)
;;; ─── Canonical serializer ──────────────────────────────────
(define (atom->string v)
(cond
((number? v) (number->string v))
((symbol? v) (symbol->string v))
((null? v) "()")
((pair? v) (string-append "(" (list->str v) ")"))
(else "?")))
(define (list->str lst)
(cond
((null? lst) "")
((null? (cdr lst)) (atom->string (car lst)))
(else (string-append (atom->string (car lst)) " "
(list->str (cdr lst))))))
;;; ─── Wire helpers ──────────────────────────────────────────
(define (request port str)
(let ((sock (tcp-connect *host* port)))
(if sock
(begin
(tcp-send sock str)
(let ((resp (tcp-recv sock 65536)))
(tcp-close sock)
(if resp resp "NO-RESPONSE")))
"NO-CONNECT")))
(define (prove port lhs rhs)
(request port
(string-append "(" (atom->string lhs) " " (atom->string rhs) ")")))
(define (parse-envelope resp)
(let ((form (read-from-string resp)))
(if (and (pair? form) (eqv? (car form) 'envelope) (pair? (cdr form)))
(car (cdr form))
'())))
(define (count-list form n)
(if (pair? form) (count-list (cdr form) (+ n 1)) n))
(define (envelope-of port)
(parse-envelope (request port "(envelope)")))
(define (hashes->string lst acc)
(if (null? lst) acc
(hashes->string (cdr lst)
(string-append acc (number->string (car lst)) " "))))
(define (merge-into port hashes)
(request port
(string-append "(merge (" (hashes->string hashes "") "))")))
;;; ─── Report helpers ────────────────────────────────────────
(define (report-sizes label)
(let ((a (envelope-of *port-a*))
(b (envelope-of *port-b*)))
(display label)
(display " A=") (display (count-list a 0))
(display " B=") (display (count-list b 0)) (newline)))
;;; ─── Run ───────────────────────────────────────────────────
(display "spiral demo across nodes A:") (display *port-a*)
(display " and B:") (display *port-b*) (newline)
(newline)
(display "seeding node A (theorems 1..3)") (newline)
(prove *port-a* '(eml ?x 1) '(exp ?x))
(prove *port-a* '(eml 1 1) '(exp 1))
(prove *port-a* '(eml 1 (eml (eml 1 ?x) 1)) '(ln ?x))
(display "seeding node B (theorems 3..5 — overlap on #3)") (newline)
(prove *port-b* '(eml 1 (eml (eml 1 ?x) 1)) '(ln ?x))
(prove *port-b* '(eml 1 (eml (eml 1 1) 1)) 0)
(prove *port-b* '(eml (ln ?a) (exp ?b)) '(- ?a ?b))
(newline)
(report-sizes "pre-spiral ")
(display "spiral A → B (B pulls A's envelope and merges)") (newline)
(display " B: ") (display (merge-into *port-b* (envelope-of *port-a*)))
(display "spiral B → A (A pulls B's envelope and merges)") (newline)
(display " A: ") (display (merge-into *port-a* (envelope-of *port-b*)))
(newline)
(report-sizes "post-spiral ")
(newline)
(display "done") (newline)