S-expressions over sockets — RPC + remote REPL in portable Scheme
Fuses portal (Scheme-source-as-interchange) with sockets (bytes over the network). Wire protocol: one S-expression per connection. Same server + client .lsp runs byte-identically in Python, C, and asm. New primitives in all three impls: - read-from-string — parse one sexp from a string Asm gets two more: - symbol->string — standard R7RS, was missing - eval — evaluate a Scheme value in the global env (Python + C had it as a special form; asm exposes it as a builtin) examples/rpc-server.lsp (port 9080): - Whitelisted dispatch: ping / add / mul / fib / echo - Never calls eval on client input; safe by construction - Uses heap-snapshot/restore for O(1) memory on asm - ~90 lines, portable examples/rpc-client.lsp: - Sends one request, reads one response, displays both - 45 lines, portable examples/repl-server.lsp (port 9081): - DANGER: full remote eval. Any Scheme form accepted and evaluated in the server's global env. Persistent across connections. - Deliberately does NOT use heap-snapshot — remote (define x ...) lives in the global env above any snapshot point; rewinding would invalidate the new binding. The ulimit -v 512 MB safety net (documented in CLAUDE.md) ensures an escaped process can't crash the machine. - ~70 lines, portable. Demonstrates what "the language IS the interchange format" gets you at the limit: a single socket and a single primitive (eval) carry a full-powered REPL. Verified 3×3 server×client matrix: all 9 combinations green. All 132 asm + 571 py + 189 shared + 83 c tests still pass. One quirk discovered and worked around: in asm, a closure captures its env chain by pointer at define time. Forward-referenced names in mutually-recursive toplevel defines can fail under specific heap-restore patterns — see the leaf-first ordering note in rpc-server.lsp.
This commit is contained in:
parent
fb41b73274
commit
574ddc50d7
8 changed files with 370 additions and 1 deletions
53
examples/rpc-client.lsp
Normal file
53
examples/rpc-client.lsp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
;;; rpc-client.lsp — send one S-expression, read one back.
|
||||
;;;
|
||||
;;; Demonstrates the three-way symmetry: the CLIENT is also portable
|
||||
;;; across Python, C, and asm. Any client impl talks to any server impl
|
||||
;;; because they agree on S-expression framing.
|
||||
;;;
|
||||
;;; Usage:
|
||||
;;; python3 uncommonlisp.py --fast examples/rpc-client.lsp
|
||||
;;; ./c/uncommonlisp examples/rpc-client.lsp
|
||||
;;; ./asm/uncommonlisp < examples/rpc-client.lsp
|
||||
;;;
|
||||
;;; Override *target-port* before loading to hit either rpc-server (9080)
|
||||
;;; or repl-server (9081).
|
||||
|
||||
(define *host* "127.0.0.1")
|
||||
(define *target-port* 9080)
|
||||
|
||||
;; Serialize a form to a string — same pattern as rpc-server's
|
||||
;; response->string. Avoids open-output-string so this runs in asm.
|
||||
(define (form->string v)
|
||||
(cond
|
||||
((number? v) (number->string v))
|
||||
((symbol? v) (symbol->string v))
|
||||
((null? v) "()")
|
||||
((pair? v) (string-append "(" (list->string v) ")"))
|
||||
((string? v) (string-append "\"" v "\""))
|
||||
(else "#<unknown>")))
|
||||
(define (list->string lst)
|
||||
(cond
|
||||
((null? lst) "")
|
||||
((null? (cdr lst)) (form->string (car lst)))
|
||||
(else (string-append (form->string (car lst)) " " (list->string (cdr lst))))))
|
||||
|
||||
(define (rpc request-form)
|
||||
(let ((sock (tcp-connect *host* *target-port*)))
|
||||
(tcp-send sock (form->string request-form))
|
||||
(let ((resp (tcp-recv sock 8192)))
|
||||
(tcp-close sock)
|
||||
(if (and resp (> (string-length resp) 0))
|
||||
(read-from-string resp)
|
||||
'no-response))))
|
||||
|
||||
(define (demo form)
|
||||
(display "→ ") (write form) (newline)
|
||||
(display "← ") (write (rpc form)) (newline)
|
||||
(newline))
|
||||
|
||||
(demo '(ping))
|
||||
(demo '(add 1 2 3 4 5))
|
||||
(demo '(mul 6 7))
|
||||
(demo '(fib 30))
|
||||
(demo '(echo (hello world)))
|
||||
(demo '(nope whatever))
|
||||
Loading…
Add table
Add a link
Reference in a new issue