Add a general Janet interop bridge and an nREPL implemented on top of it. Interop bridge (evaluator): - A qualified symbol whose namespace is `janet` or `janet.<module>` resolves against Janet's environment: `janet/<name>` -> root binding (janet/slurp), `janet.<module>/<name>` -> module binding (janet.net/server, janet.os/clock). The explicit `janet` segment marks every crossing into host code (where Clojure semantics, e.g. collection representation, no longer hold). This makes the whole Janet stdlib — networking included — reachable from Clojure. jolt.nrepl (Clojure, src/jolt/jolt/nrepl.clj): - bencode codec (encode + streaming decode), ported from nrepl.bencode. - server: accept loop via janet.net/accept + janet.ev/call (Janet's built-in handler arity-checks Jolt closures, so we drive accept ourselves); ops clone/ describe/eval/load-file/close/ls-sessions/interrupt/eldoc following babashka.nrepl response shapes. eval captures *out* by rebinding Janet's :out, reports ns, streams out, and isolates the eval namespace (current-ns is global ctx state) restoring it afterward. Vars are rendered as #'ns/name (pr-str loops on a var's cyclic ns refs). - client: connect / request / client-eval / client-clone / client-close. CLI: `jolt nrepl [port]` starts the server and writes .nrepl-port; the Clojure source is embedded at build time so the binary is self-contained from any cwd. Tests: test/spec/nrepl-spec.janet (bencode), test/integration/nrepl-test.janet (server+client over a real TCP/bencode wire, server in a subprocess).
27 lines
1.3 KiB
Text
27 lines
1.3 KiB
Text
# Specification: jolt.nrepl bencode codec (pure, no networking).
|
|
# The server/client wire behavior is covered by test/integration/nrepl-test.janet.
|
|
(use ../support/harness)
|
|
|
|
(defn- b [body]
|
|
(string "(do (require '[jolt.nrepl :as nr]) " body ")"))
|
|
(defn- rt [body]
|
|
# round-trip a value through encode -> decode
|
|
(b (string "(nr/decode (nr/reader nil (nr/encode " body ")))")))
|
|
|
|
(defspec "jolt.nrepl / bencode round-trip"
|
|
["integer" "42" (rt "42")]
|
|
["negative" "-7" (rt "-7")]
|
|
["string" "\"hello\"" (rt "\"hello\"")]
|
|
["empty string" "\"\"" (rt "\"\"")]
|
|
["list" "[\"a\" 1 \"b\"]" (rt "[\"a\" 1 \"b\"]")]
|
|
["nested list" "[1 [2 3]]" (rt "[1 [2 3]]")]
|
|
["dict" "{\"op\" \"eval\" \"id\" \"7\"}" (rt "{\"op\" \"eval\" \"id\" \"7\"}")]
|
|
["dict with list" "{\"status\" [\"done\"]}" (rt "{\"status\" [\"done\"]}")]
|
|
["nested dict" "{\"a\" {\"b\" 1}}" (rt "{\"a\" {\"b\" 1}}")])
|
|
|
|
(defspec "jolt.nrepl / bencode encode shape"
|
|
["int" "\"i42e\"" (b "(nr/encode 42)")]
|
|
["string" "\"5:hello\"" (b "(nr/encode \"hello\")")]
|
|
["list" "\"li1ei2ee\"" (b "(nr/encode [1 2])")]
|
|
# dict keys are sorted lexicographically
|
|
["dict sorted keys" "\"d1:ai1e1:bi2ee\"" (b "(nr/encode {\"b\" 2 \"a\" 1})")])
|