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.
99 lines
3.9 KiB
Text
99 lines
3.9 KiB
Text
;;; portal-http-client.lsp — fetch an S-expression portal over HTTP,
|
|
;;; evaluate each form to materialize the bindings locally.
|
|
;;;
|
|
;;; This is the concrete demo of "portal over HTTP" from the whitepaper:
|
|
;;; one machine serves its state, another pulls it down and resumes.
|
|
;;; Because the portal format is Scheme source, the client is a few
|
|
;;; lines of string-munging plus (read-from-string) + (eval).
|
|
;;;
|
|
;;; Usage (server must be running first — see portal-http-server.lsp):
|
|
;;; python3 lumbda.py --fast examples/portal-http-client.lsp
|
|
;;; ./c/lumbda examples/portal-http-client.lsp
|
|
;;; ./asm/lumbda < examples/portal-http-client.lsp
|
|
|
|
(define *host* "127.0.0.1")
|
|
(define *port* 9085)
|
|
(define *request* "GET /portal HTTP/1.0\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n")
|
|
|
|
;;; ── Strip HTTP headers: body starts after the first "\r\n\r\n". ──
|
|
;;; Portable implementation — walks bytes, tracks a 4-state matcher.
|
|
|
|
(define (find-crlfcrlf s)
|
|
(let ((len (string-length s)))
|
|
(let loop ((i 0))
|
|
(if (> (+ i 4) len)
|
|
-1
|
|
(if (and (= (char->integer (string-ref s i)) 13)
|
|
(= (char->integer (string-ref s (+ i 1))) 10)
|
|
(= (char->integer (string-ref s (+ i 2))) 13)
|
|
(= (char->integer (string-ref s (+ i 3))) 10))
|
|
(+ i 4)
|
|
(loop (+ i 1)))))))
|
|
|
|
(define (body-of resp)
|
|
(let ((start (find-crlfcrlf resp)))
|
|
(if (< start 0)
|
|
""
|
|
(substring resp start (string-length resp)))))
|
|
|
|
;;; ── Fetch and evaluate ──────────────────────────────────────
|
|
;;;
|
|
;;; Read up to 64 KB of response in one tcp-recv. For a small
|
|
;;; portal body this is always one packet on localhost.
|
|
;;;
|
|
;;; Then split the body by line, read+eval each non-empty,
|
|
;;; non-comment line. Top-level define forms land in the global env.
|
|
|
|
(define (first-n-chars s n)
|
|
(if (> (string-length s) n) (substring s 0 n) s))
|
|
|
|
(define (fetch-portal)
|
|
(let ((sock (tcp-connect *host* *port*)))
|
|
(if sock
|
|
(begin
|
|
(tcp-send sock *request*)
|
|
(let ((resp (tcp-recv sock 65536)))
|
|
(tcp-close sock)
|
|
(body-of resp)))
|
|
"")))
|
|
|
|
(define (line-at s start)
|
|
;; Extract line starting at index `start` (exclusive of \n).
|
|
;; Returns the substring up to the next \n (or end).
|
|
(let ((len (string-length s)))
|
|
(let loop ((i start))
|
|
(cond
|
|
((= i len) (substring s start len))
|
|
((= (char->integer (string-ref s i)) 10) (substring s start i))
|
|
(else (loop (+ i 1)))))))
|
|
|
|
(define (eval-all-lines s)
|
|
(let ((len (string-length s)))
|
|
(let loop ((i 0) (cnt 0))
|
|
(if (>= i len) cnt
|
|
(let ((line (line-at s i)))
|
|
(let ((next (+ i (string-length line) 1)))
|
|
(cond
|
|
((= (string-length line) 0)
|
|
(loop next cnt))
|
|
((= (char->integer (string-ref line 0)) 59) ; ; = comment
|
|
(loop next cnt))
|
|
(else
|
|
(eval (read-from-string line))
|
|
(loop next (+ cnt 1))))))))))
|
|
|
|
;;; ── Go ──────────────────────────────────────────────────────
|
|
|
|
(define portal-body (fetch-portal))
|
|
(display "fetched ") (display (string-length portal-body)) (display " bytes") (newline)
|
|
(display "first line: ") (display (line-at portal-body 0)) (newline)
|
|
|
|
(define evaluated (eval-all-lines portal-body))
|
|
(display "evaluated ") (display evaluated) (display " forms") (newline)
|
|
|
|
;; Now the remote bindings are live locally. Use them:
|
|
(display "counter = ") (display counter) (newline)
|
|
(display "my-int = ") (display my-int) (newline)
|
|
(display "my-fib = ") (display my-fib) (newline)
|
|
(display "my-list has ") (display (length my-list)) (display " items") (newline)
|
|
(display "my-str = ") (display my-str) (newline)
|