;;; 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 lumbda.py --fast examples/rpc-client.lsp
;;;   ./c/lumbda examples/rpc-client.lsp
;;;   ./asm/lumbda < 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))
