updates
ober
1f436c4689571bbae8dc0858dde9974ea4160285
--- a/README.md +++ b/README.md @@ -1,605 +1,281 @@ # Jerboa -A Gerbil-syntax, Clojure-compatible Scheme dialect for production -concurrent systems, running on stock [Chez Scheme](https://cisco.github.io/ChezScheme/). +Jerboa is a Scheme dialect built on Chez Scheme. It keeps the parts of +Gerbil that make day-to-day Scheme pleasant, adds a large Clojure-style +data and concurrency layer, and ships a production standard library on +top of native Chez code plus an optional memory-safe Rust backend. -Jerboa implements Gerbil's user-facing language (`def`, `defstruct`, -`match`, hash tables, `:std/*` libraries) **and** a substantial Clojure -compatibility layer (atoms, refs/STM, agents, persistent collections, -core.async, transducers, protocols, multimethods) as Chez Scheme -macros and native libraries. No Gerbil expander, no Gambit -compatibility layer, no patched Chez. The standard Chez compiler -produces the binaries. - -## Why Chez Scheme - -Jerboa picks Chez Scheme as its host because its concurrency model is -the rare combination that real production systems need: - -- **Real OS threads, no GIL.** Chez Scheme's threaded build - (`--threads`) gives you native OS threads with a thread-safe - generational GC. CPU-bound work scales linearly across cores; you - do not pay for parallelism with a global interpreter lock. -- **First-class synchronization primitives.** `make-mutex`, - `make-condition`, `with-mutex`, `condition-wait`, - `condition-broadcast`, and atomic CAS on tc-mutex slots are - exposed in the language. Jerboa's higher-level concurrency - abstractions (`(std atom)`, `(std stm)`, `(std csp)`, - `(std net io)` fibers) are built directly on these. -- **Cheap closures, fast call paths.** Chez compiles to machine code - through the nanopass framework — its closure allocation and - call-through-pointer costs are competitive with what Go achieves - via its runtime. That gives Jerboa room to express tens of - thousands of fibers as plain procedures over a small worker pool - without the cost dominating the workload. -- **Fixnum-tagged arithmetic and unboxed bytevectors.** Hot paths in - protocol handling (HTTP/2, WebSocket framing, base64, SHA) stay - in fixnums and unboxed bytes, so a Scheme implementation of a - protocol can sit within a small constant factor of a hand-written - C version. -- **A stable, fast FFI.** `foreign-procedure` and `foreign-callable` - let Jerboa drop into a Rust shared library (`libjerboa_native`) - for everything that genuinely needs sharp edges — ring for - crypto, rustls for TLS, regex for ReDoS-immune matching, - rusqlite/postgres/duckdb for storage. The Scheme side stays - high-level; the Rust side stays memory-safe. -- **A self-hosting compiler.** When Jerboa needs a primitive that - Chez does not ship (e.g. `bytevector-slice`, `bytevector-append`, - `base64-encode`/`base64-decode`, `sha1-bytevector`, - `sha256-bytevector`), we add it to our Chez fork in pure Scheme - with full backwards compatibility. No patched bootstrap, no - vendored compiler — just additional primitives in `(chezscheme)`. -- **Apache 2.0**, no license entanglements. - -The result: a single language where green threads (fibers), OS -threads, atomic state, software-transactional memory, and CSP -channels all coexist on top of the same scheduler primitives, with -predictable performance and no runtime to fight. - -## Quick Start +All user-facing Jerboa code is written in `.ss` files: ```scheme (import (jerboa prelude)) -(def (main) - (defstruct point (x y)) - (let ([p (make-point 3 4)]) - (displayln (point-x p)) ;; 3 - (displayln (sort < [5 1 3])) ;; (1 3 5) - (displayln (string-join ["a" "b"] ",")) ;; a,b - (displayln (json-object->string [1 2 3])))) ;; [1,2,3] +(def (hello name) + (displayln (str "hello, " name))) -(main) +(hello "world") ``` -Run with: +Run a file with: + ```bash -scheme --libdirs lib --script your-file.ss +scheme --libdirs lib --script hello.ss ``` -## Architecture +Do not write `(library ...)` forms in user code. The `.sls` files in +this repository are implementation internals. -``` -┌──────────────────────────────────────────────┐ -│ User's Gerbil-like code │ -│ (def (main) (displayln (sort < [3 1 2]))) │ -└──────────────┬───────────────────────────────┘ - │ -┌──────────────▼───────────────────────────────┐ -│ Reader: [...] = (...), {...} → (~ ..) │ -│ :std/sort → (std sort), keyword:, heredocs │ -│ Optional Clojure mode (#!cloj) │ -├──────────────────────────────────────────────┤ -│ Core Macros: def, defstruct, match, try │ -│ All expand to standard Chez Scheme │ -├──────────────────────────────────────────────┤ -│ Runtime: hash tables, method dispatch, │ -│ persistent collections, transients │ -├──────────────────────────────────────────────┤ -│ Concurrency: fibers, CSP, STM, atoms, │ -│ agents, futures, work-stealing scheduler │ -├──────────────────────────────────────────────┤ -│ Standard Library: 229 (std *) modules │ -├──────────────────────────────────────────────┤ -│ Native FFI: libjerboa_native.so (Rust) │ -│ ring · flate2 · regex · sqlite · │ -│ postgres · rustls · landlock · ed25519 │ -├──────────────────────────────────────────────┤ -│ Stock Chez Scheme — additive primitives, │ -│ no fork, no patches │ -└──────────────────────────────────────────────┘ -``` +## Quick Start -## What's Landed in the Last Month +The prelude is the default way to write Jerboa. It exports the core +language, common data structures, strings, lists, hashes, result types, +iterators, JSON/CSV, paths, datetime, regex helpers, file I/O, pretty +printing, and compatibility aliases for common Scheme names. -The recent push has expanded Jerboa from "Gerbil syntax on Chez" into -a full production stack. The additions group into seven themes. +```scheme +(import (jerboa prelude)) -### 1. Fiber-aware concurrency stack +(defstruct point (x y)) -A green-thread system layered on Chez OS threads with epoll-integrated -I/O. The eight-phase rollout (`docs/green-wins.md`) is now complete. +(def (describe-point p) + (match p + ((? point? pt) (str "point(" (point-x pt) ", " (point-y pt) ")")) + (_ "not a point"))) -| Module | Provides | -|---|---| -| `(std net io)` | epoll-integrated I/O core for fibers | -| `(std net fiber-httpd)` | Fiber-native HTTP/1.1 server with URL-param routing | -| `(std net fiber-ws)` | Fiber-aware WebSocket server, integrates with httpd | -| `(std workpool)` | Bounded worker pool for blocking syscalls | -| `(std net dns)` | Fiber-friendly resolver with caching | -| `(std net filepool)` | File-descriptor pool offloading slow disk I/O | -| `(std net sendfile)` | Zero-copy `sendfile(2)` paths for static content | -| `(std net connpool)` | Fiber-aware connection pooling for outbound TCP | -| `(std net rate)` | Token bucket, sliding/fixed-window limiters | -| `(std net router)` | HTTP routing with `:param` captures + middleware | -| `(std semaphore)` / `(std net admission)` | Production hardening: admission control + circuit metrics | -| `(std fiber)` | Cancellation, fiber-locals, join, link, select, timeouts, groups | - -Internally, the scheduler now uses **per-worker work-stealing deques** -instead of a shared run-queue, and the completion / wait API was -re-wired through fibers so that ports and channels block fibers -without parking the OS thread. - -### 2. Clojure compatibility layer — `(std clojure)` - -A substantial Clojure-on-Scheme port: sequences, atoms, agents, refs, -multimethods, protocols, transducers, persistent collections, lazy -sequences, core.async, EDN, specter, zippers, and the rest of the -common idiom. - -**Persistent collections** (HAMT- and RRB-backed): - -| Module | Provides | -|---|---| -| `(std pmap)` | Persistent hash-array-mapped trie map + `transient-map` for fast bulk build | -| `(std pvec)` | Persistent RRB vector | -| `(std pset)` | Persistent set on top of `pmap` | -| `(std sorted-set)` | Sorted persistent set | -| `(std sorted-map)` | Sorted persistent map | -| `(std pqueue)` | Persistent FIFO queue (SRFI-134-backed) | +(def (main) + (def p (make-point 3 4)) + (displayln (describe-point p)) + (displayln (sort '(5 1 3) <)) + (displayln (for/collect ([x (in-range 5)]) (* x x))) + (displayln (-> " a,b,c " (string-trim) (string-split #\,))) + (displayln (json-object->string '(1 2 3))) + (displayln (unwrap (->? (ok 10) (+ 5) (* 2))))) -All persistent collections participate in Chez's `equal?` / -`equal-hash` / `display`, integrate with the `for/collect` / `for/fold` -iterator protocol, and are destructurable via `match`. They use -nongenerative RTDs so equality survives separate compilation. +(main) +``` -**Concurrency primitives** (Clojure-style names, Chez-thread-safe): +Important syntax note: square brackets are just parentheses, like +Gerbil and Chez. Use quoted lists such as `'(1 2 3)` when you want a +literal list. -| Module | Provides | -|---|---| -| `(std atom)` | `atom`, `swap!`, `reset!`, `compare-and-set!`, `add-watch!`, `volatile!` family | -| `(std agent)` | Async state cells with bounded send queues | -| `(std stm)` | `ref`, `dosync`, `alter`, `commute`, `ensure` on top of TVars | -| `(std multi)` | `defmulti` / `defmethod` with `:hierarchy` and `derive!` | -| `(std protocol)` | Open-world protocols (`defprotocol`, `extend-type`, `extend!`) | -| `(std meta)` | Metadata wrappers (`with-meta`, `meta`) | -| `(std component)` | Stuart Sierra-style lifecycle (`start`, `stop`, dependency graph) | - -**core.async-style CSP** — `(std csp)`: - -- `(chan n xform)` — transducer-backed channels -- Sliding / dropping / blocking buffers -- `alts!` / `alts!!` non-deterministic select -- `pipe`, `mult`, `mix` (with `'block` / `'drop` / `'timeout` policies) -- `pub` / `sub` topic filtering -- `split` / `chan-classify-by` n-way split -- `async-reduce`, `onto-chan!`, `onto-chan!!` -- `put!` / `take!` non-blocking callback variants -- Timer wheel for scalable timeouts (`JERBOA_CSP_TIMER_WHEEL=1`) -- `(std csp fiber-chan)` integrates channels with fibers - -**Sequence / data manipulation**: - -| Module | Provides | -|---|---| -| `(std clojure)` | Umbrella with `delay`, `future`, `promise`, polymorphic `deref` | -| `(std clojure walk)` | `prewalk`, `postwalk`, `keywordize-keys`, etc. | -| `(std clojure data)` | `diff` | -| `(std clojure zip)` | Tree zippers | -| `(std clojure seq)` | Lazy sequences: `range`, `iterate`, `repeat`, `cycle`, `take-while`, `drop-while` | -| `(std clojure reducers)` | Reducible/transducible adapters | -| `(std specter)` | Composable path navigation for nested data | -| `(std zipper)` | Functional tree zippers | -| `(std text edn)` | EDN reader/writer (#inst, #uuid, tagged literals) | -| `(std misc nested)` | `get-in`, `assoc-in`, `update-in` | -| `(std injest)` | Smart thread-last with transducer fusion | - -The `(std clojure)` module also lands `ex-info`, `condp =>`, -`reduce-kv`, set relational ops (`select`, `project`, `rename`, -`index`, `join`), and full destructuring. - -`(std test check)` is a property-based testing library with -shrinking, in the style of `test.check`. - -### 3. Slang + WebAssembly backend - -Slang is a secure language compiler that targets WebAssembly, with -two execution backends. - -- Full WASM MVP plus post-MVP features: saturating conversions, bulk - memory operations, reference types, tables, tail calls, exception - handling, GC, host imports. -- `(jerboa wasm format)` — binary format primitives (LEB128, IEEE 754). -- `(jerboa wasm codegen)` — Scheme→WASM compiler for an i32 subset - (closures, tail calls, exceptions, variadic lambdas). -- `(jerboa wasm runtime)` — stack-based interpreter for testing. -- **wasmi backend** — Rust embedding with fuel metering and bounded - memory; security-hardened (bounds checks, exception boundary, - import validation, module-size limits). -- **SpiderMonkey backend** via the `mozjs` crate — production JIT for - benchmarks where wasmi is the bottleneck. -- Argon2id key derivation, message HMAC, sandbox limits, taint checks - baked in. -- `wasm-sandbox-instantiate-hosted` provides a Scheme FFI binding for - hosting third-party WASM modules with capability-based imports. - -### 4. Native Rust backend — `libjerboa_native.so` - -A unified Rust shared library replaces the previous C-FFI surface -with memory-safe implementations behind a small extern-"C" facade. - -| Module | Crate(s) | Provides | -|---|---|---| -| `(std crypto native-rust)` | ring | SHA-1/256/384/512, HMAC, AES-256-GCM, PBKDF2, CSPRNG, ed25519 sign/verify | -| `(std crypto secure-mem)` | libc (mmap/mlock) | Guard-paged, mlocked memory outside GC | -| `(std compress native-rust)` | flate2 | `deflate`/`inflate`/`gzip`/`gunzip` with size limits | -| `(std regex-native)` | regex (NFA) | Compile / match / find / replace — ReDoS-immune | -| `(std db sqlite-native)` | rusqlite (bundled) | Open / exec / prepare with parameterized queries | -| `(std db postgresql-native)` | rust-postgres | Connect / exec / query | -| `(std db duckdb-native)` | duckdb-rs | Native DuckDB integration | -| `(std os epoll-native)` | libc | epoll create / ctl / wait | -| `(std os inotify-native)` | libc | inotify init / add_watch / read_events | -| `(std os landlock-native)` | libc | Landlock LSM ABI v1–v7 (FS + network confinement) | -| `(std net tls-rustls)` | rustls | HTTPS / `wss://` with pinned cert verification | -| `(std net request)` | rustls | HTTPS via the Rust TLS path | -| `(std pcap)` | rscap | Live packet capture | - -`panic.rs` wraps every extern "C" entry point in `catch_unwind` so a -Rust panic never crosses the FFI boundary as undefined behavior. - -### 5. Cross-platform ports - -Jerboa now runs on Linux (glibc + musl), FreeBSD, macOS (Intel + Apple -Silicon), and Android (Termux). - -- **FreeBSD**: full Capsicum support in `(std security cage)`, - `__error` errno binding, correct platform constants for O_* flags, - `struct stat`, `sockaddr_in`, signals (SIGTSTP/SIGCHLD/SIGCONT and - six others were hardcoded to Linux values), and integrity check via - `sysctl` instead of `/proc/curproc/file`. -- **macOS**: BPF live capture activation via `BIOCSETF` + `BIOCIMMEDIATE` - (replacing `BIOCSETFNR` which doesn't exist on macOS), portable - libc/libm names, dylib path fallback, AArch64 seccomp. -- **Android**: bionic libc errno symbol support in TCP layer. - -`(std security cage)` provides pledge/unveil-style process -confinement that maps to the strongest mechanism available on each -platform: Landlock + seccomp on Linux, Capsicum on FreeBSD, sandbox-exec -on macOS. - -### 6. Security hardening - -- `(std security)` — `audit`, `auth`, `cage`, `capability`, - `flow`, `import-audit`, `io-intercept`, `landlock`, - `metrics`, `privsep`, `restrict`, `sandbox`, `sanitize`, - `seatbelt`, `seccomp`, `secret`, `taint`, `capsicum`, - `capability-typed`, `errors`. -- mTLS with **pinned cert verifier** (replacing - `WebPkiClientVerifier`); in-memory cert/key APIs. -- Argon2id, TOCTOU-safe path handling, message HMAC. -- Binary-hardening flags wired into the musl static build (PIE, - RELRO, stack-canaries, NX) — see `docs/secure-binary.md`. -- aarch64 seccomp filter. - -### 7. Performance work — phases 4–22 - -A multi-phase optimization push, each with a benchmark gate to detect -regressions. The scaffolding lives in `(std bench)` and runs in CI. - -| Phase | Optimization | +## What Jerboa Includes + +At this snapshot, `lib/std` contains 614 `.ss` modules. The prelude +exports the everyday language; specialized libraries are imported from +`(std ...)`, `(jerboa ...)`, and related module trees. + +| Area | Highlights | |---|---| -| 4 | Memoize regex compilation for literal reuse | -| 5 | Fuse `for` / `for/collect` / `for/fold` over `in-range` / `in-vector` / `in-string` | -| 6 | Single-pass keyword-arg extraction in `def` expansion | -| 7 | Build the prelude aggregator to produce WPO for `std/*` | -| 8 | Bench-suite harness + regression gate | -| 12 | Arity-specialized method dispatch in `(~ obj 'name ...)` | -| 13 | Fuse `in-hash-keys` / `in-hash-values` in `for/collect` | -| 14 | Fuse runs of `(list 'TAG p ...)` clauses in `match` | -| 17 | Method dispatch cost vs direct call (baseline) | -| 18 | Kwarg call overhead (baseline) | -| 22 | `string-append` adjacent-literal fold (Chez-side) | -| — | `defstruct` / `defrecord` / `ok` / `err` seal by default | -| — | `str` expand-time constant folding for literal args | -| — | Persistent-collection nongenerative UIDs | -| — | Iter fusion: `for/or` / `for/and` over known iterator heads | -| — | Match2 fast-path `(: Type)` with fused RTD dispatch | - -### 8. Reader & build infrastructure - -- **Clojure reader compatibility mode** (`#!cloj`) — switch a file or - REPL session into Clojure surface syntax (`#"regex"`, `#{}` sets, - `#_form` discard, etc.). -- **Regex tier 1–3** — raw strings, unified `(re ...)` API across the - pure-Scheme and Rust backends, `rx` macros, full PEG grammar - system in `(std peg)`. -- **Single-file packages** — `jerboa exec` reads a self-contained - `.ss` file with header metadata and runs it without project - scaffolding. -- **jerbuild** improvements — per-project feature gating so a - static-musl build only links the dependencies the project - actually uses. -- **Docker base image** for static musl binary builds; CI workflow - for macOS Rust libraries; jemacs static build TUI deps included. -- **nREPL** — `(std nrepl)` is the canonical source; full CIDER / - Calva middleware plus Jerboa extensions. -- **AI compatibility aliases** — `(jerboa prelude)` exports common - names from Racket / Gambit / Common Lisp that LLMs frequently - hallucinate (`hash-has-key?` → `hash-key?`, - `directory-exists?` → `file-directory?`, `random-integer` → - `random`, `eql?` → `eqv?`, etc.). - -### 9. Documentation push - -`docs/` is now ~90 files. Highlights: - -- `docs/anti-cookbook.md` — common Scheme/Clojure mistakes and how - Jerboa makes them explicit. -- `docs/api-index.md` — comprehensive API reference (~700KB, - generated). -- `docs/clojure-vs-jerboa.md` — feature scorecard with status - markers. -- `docs/clojure-left.md` — gap analysis driving the parity work. -- `docs/green-wins.md` — fiber roadmap (now complete). -- `docs/jerboa-edge.md` — edge-computing roadmap. -- `docs/jerboa-db.md` — database integration tracking. -- `docs/native-rust.md` — Rust backend architecture and migration. -- `docs/regex-rx-peg.md` — unified regex/SRE/PEG reference. - -## What's Included - -### Core Macros (`(jerboa core)`) -- `def`, `def*` — functions with optional / multi-arity / rest args -- `defstruct` — sealed Chez records with auto-generated accessors -- `defclass` — records with single inheritance -- `defmethod`, `defmulti` — method dispatch and multimethods -- `match`, `match2` — pattern matching (lists, predicates, - `and`/`or`/`not`, cons, type patterns, persistent-collection - destructuring, wildcards) -- `try`/`catch`/`finally`, `errdefer`, `defvariant` (Zig-inspired) -- `defrule`/`defrules` — syntax-rules shortcuts -- `while`/`until`, `for` family -- `hash-literal`/`let-hash` -- Threading macros: `chain`, `chain-and`, `->`, `->>`, `as->`, - `some->`, `cond->` - -### Runtime (`(jerboa runtime)`) -- Full Gerbil hash table API -- Method dispatch: `~`, `bind-method!`, `call-method` -- Keywords, ports, displayln, `iota`, `1+`, `1-` -- `(jerboa pkg)`, `(jerboa lock)` — semver, dep resolution, - manifests, lockfile management -- `(jerboa hot)` — hot code reload via mtime polling -- `(jerboa embed)` — sandboxed evaluation environments -- `(jerboa cross)` — cross-compilation config and ABI naming - -### Standard Library — 229 modules under `lib/std/` - -Major areas: - -- **Concurrency**: `(std atom)`, `(std agent)`, `(std stm)`, - `(std csp)`, `(std multi)`, `(std protocol)`, `(std meta)`, - `(std component)`, `(std clojure)`, `(std fiber)`, `(std raft)`, - `(std actor)`, `(std concur hash)`, `(std concur stm)`, - `(std concur structured)`. -- **Networking**: 35+ `(std net *)` modules — `request`, `httpd`, - `fiber-httpd`, `fiber-ws`, `websocket`, `http2`, `dns`, - `sendfile`, `connpool`, `workpool`, `tcp`, `udp`, `tls`, - `tls-rustls`, `ssh`, `s3`, `smtp`, `socks5-server`, `9p`, - `grpc`, `json-rpc`, `router`, `rate`, `uri`. -- **Data**: `(std pmap)`, `(std pvec)`, `(std pset)`, - `(std sorted-set)`, `(std sorted-map)`, `(std pqueue)`, - `(std specter)`, `(std zipper)`, `(std injest)`, - `(std clojure data/walk/zip)`, `(std misc nested)`. -- **Persistence / DB**: `(std db sqlite/postgresql/duckdb/leveldb)`, - native-Rust variants, `(std db dbi)`, `(std db query-compile)`, - `(std db conpool)`. -- **Text**: `(std text json/edn/csv/xml/yaml/base64/hex/utf8)`, - `(std regex-native)`, `(std rx)`, `(std peg)`. -- **OS / runtime**: `(std os env/path/temporaries/signal/fdio)`, - native epoll/inotify/landlock, `(std os capsicum)`, - `(std misc process)`, `(std misc thread)`, `(std misc cpu)`. -- **Crypto**: `(std crypto digest/cipher/hmac/pkey/kdf)`, - `(std crypto native-rust)` (ring), `(std crypto secure-mem)`. -- **Production infra**: `(std log)`, `(std metrics)`, `(std span)`, - `(std health)`, `(std circuit)`, `(std semaphore)`, `(std lint)`, - `(std test)`, `(std test check)`, `(std bench)`. -- **Security**: 21 `(std security *)` modules — pledge/unveil, - Landlock, Capsicum, seccomp, capability-typed I/O, taint, sandbox. - -### FFI (`(jerboa ffi)`) -- `c-lambda` → `foreign-procedure` with automatic type translation -- `define-c-lambda`, `begin-ffi`, `c-declare` -- Full Gambit-to-Chez type mapping -- `native-available?` guards for build-time platform feature gating - -### Reader (`(jerboa reader)`) -- `[...]` = `(...)` (same as Gerbil and Chez) -- `{method obj}` → `(~ obj 'method)` -- `keyword:` → keyword objects -- `:std/sort` → `(std sort)` module paths -- Heredoc strings, datum comments, block comments -- Optional Clojure surface syntax under `#!cloj` - -### Prelude (`(jerboa prelude)`) -One import for everything: -```scheme -(import (jerboa prelude)) -``` +| Core language | `def`, `def*`, `defn`, optional/rest args, typed parameters, `defrule`/`defrules`, `define-values`, sealed records, enums, active patterns, ergonomic casts, method dispatch, `match`/`match2`, contracts, `try`/`catch`/`finally`, `with-resource`, result types, threading macros, iterator macros. | +| Reader syntax | `[...]` as parentheses, `{method obj args}` method dispatch, trailing `name:` keywords, `:std/path` imports, heredoc strings, raw strings and regex reader support, optional Clojure reader mode via `#!cloj`. | +| Prelude data | Hash tables, alists/plists, list utilities, functional combinators, strings, paths, file I/O, JSON, CSV, datetime, formatting, pretty printing, regex helpers, atoms, volatiles, shared state, lightweight SQLite/TCP helpers. | +| Persistent and generic collections | Persistent maps/vectors/sets/queues, sorted maps/sets, HAMT maps, weak collections, ephemerons, immutable values, lazy sequences, generic collection protocols, relation operations, tables, dataframes, datalog, lenses, zippers, Specter-style navigation. | +| Clojure compatibility | Sequences, reducers, transducers, atoms, agents, refs/STM, protocols, multimethods, metadata, futures/promises/deref, EDN, walkers, zippers, nested data helpers, datafy, core.async-style CSP channels. | +| Concurrency | Native OS threads with no GIL, atomics, mutex/condition wrappers, M:N fibers, fiber-aware events/channels, async/await, structured concurrency, work pools, resource pools, barriers, wait groups, actor systems, supervisors, distributed actors, CRDTs, Raft, STM, CSP. | +| Networking and web | HTTP client/server, fiber HTTP server, WebSocket/fiber WebSocket, HTTP/2, DNS, routers, rate limiting, connection pools, sendfile/zero-copy paths, TCP/UDP/TLS/rustls, SSH, S3, SMTP, SOCKS5, gRPC, JSON-RPC, 9P, FastCGI, Rack-style web adapters, event streams. | +| Text and protocols | JSON and JSON Schema, CSV, YAML, XML, HTML/SXML, TOML, INI, EDN, MessagePack, CBOR, Transit, protobuf, base58/base64/hex, UTF-8/16/32, globbing, diffs, templates, regex, native regex, PCRE2, `rx`, PEG parsers. | +| Storage and databases | SQLite, PostgreSQL, DuckDB, LevelDB, DBI layer, query compilation, connection pools, mmap and mmap-btree helpers, content-addressed storage, image/closure persistence, package stores. | +| Native Rust backend | Optional `libjerboa_native` bindings for ring crypto, rustls TLS, ReDoS-resistant regex, flate2 compression, rusqlite/postgres/duckdb, secure memory, epoll, inotify, Landlock, packet capture, and panic-contained FFI entry points. | +| Security and capabilities | Audit logging, auth, cages, capability and capability-typed I/O, import audit, flow/taint tracking, IO interception, Landlock, seccomp, Capsicum, seatbelt, sandboxing, sanitizers, secret handling, privilege separation, safe/pure audit tools. | +| OS and runtime | Environment/path/fd/signal/process modules, errno/fcntl/flock, mmap, temp files, tty, aproc, exec identity, inotify, epoll, kqueue, io_uring, platform detection, supervised services. | +| Typed and effect systems | Typed parser/checker, Rust/LLVMIR wrappers, affine and linear types, refinement, phantom, GADT, HKT, rows, typeclasses, effect typing, deep/scoped/resource effects, contracts and chaperones. | +| Compiler and WASM | Compiler passes, partial evaluation, PGO, specialization, staging/comptime, secure compiler/link modules, WebAssembly format/codegen/runtime/GC/sandbox/WASI modules, Slang docs and secure WASM target work. | +| Tooling and operations | REPL/nREPL, LSP, docs generation, package manager, reproducible builds, SBOM and build verification, watch mode, logging, metrics, tracing/spans, health checks, circuit breakers, profiling, flamegraphs, heap/thread/timetravel debugging, fuzzing, QuickCheck, property tests, benchmarks. | + +## Recent Additions + +The latest standard-library push filled in several areas that the old +README did not cover: + +- **Concurrency and scheduling:** `(std misc fiber)`, `(std misc event)`, + `(std misc custodian)`, `(std misc delimited)`, enhanced + `(std misc pool)`. +- **Data structures:** `(std misc persistent)`, `(std misc lazy-seq)`, + `(std misc weak)`, `(std misc collection)`, `(std misc relation)`. +- **Serialization and protocols:** `(std text msgpack)`, `(std net 9p)`, + `(std misc binary-type)`. +- **Testing and debugging:** `(std test)`, `(std test quickcheck)`, + `(std test check)`, `(std misc profile)`, `(std misc equiv)`, + `(std misc diff)`. +- **Metaprogramming:** `(std misc typeclass)`, `(std misc ck-macros)`, + `(std misc fmt)`, `(std misc chaperone)`, `(std misc advice)`. +- **System utilities:** `(std misc config)`, `(std misc cont-marks)`, + `(std misc terminal)`, `(std misc highlight)`, + `(std misc guardian-pool)`, `(std misc amb)`, + `(std misc memoize)`. + +See [`docs/whats-new.md`](docs/whats-new.md) for the detailed module +notes and examples. ## Examples -### Fiber-based HTTP server +### Persistent HAMT Map + ```scheme (import (jerboa prelude) - (std net fiber-httpd)) + (std misc persistent)) -(define (handler req) - (let-values ([(method path) (request-method+path req)]) - (case method - [(GET) (respond-text 200 "hello\n")] - [else (respond-text 405 "method not allowed\n")]))) +(def users + (hamt-set + (hamt-set hamt-empty 'alice 1) + 'bob 2)) -(fiber-httpd-serve port: 8080 handler: handler workers: 4) +(displayln (hamt-ref users 'alice #f)) +(displayln (hamt-contains? users 'bob)) ``` -### CSP pipeline +### Property-Based Test + ```scheme (import (jerboa prelude) - (std csp) (std csp ops)) - -(let ([in (chan 100)] - [out (chan 100 (map (lambda (x) (* x x))))]) - (pipe in out) - (go (let loop ([i 0]) - (when (< i 1000) (put! in i) (loop (+ i 1))))) - (let consume ([n 0]) - (when (< n 1000) - (displayln (take! out)) (consume (+ n 1))))) + (std test check)) + +(def sort-preserves-length + (for-all ([xs (gen:list (gen:integer))]) + (= (length (sort xs <)) (length xs)))) + +(displayln (check-property 100 sort-preserves-length)) ``` -### STM (Clojure refs) -```scheme -(import (jerboa prelude) (std stm)) +## Why Chez Scheme -(define account-a (ref 100)) -(define account-b (ref 50)) +Jerboa uses Chez because it has the runtime properties a systems +language needs: -(define (transfer! from to amount) - (dosync - (alter from - amount) - (alter to + amount))) +- Real OS threads with no global interpreter lock. +- Thread-safe generational GC and cheap compiled closures. +- Fast fixnum arithmetic and bytevector-heavy protocol code. +- Stable FFI via `foreign-procedure` and `foreign-callable`. +- A mature native-code compiler with whole-program optimization paths. +- Apache 2.0 licensing. -(transfer! account-a account-b 25) -``` +The repository can use stock Chez 10.x. It also vendors an additive +Chez tree under [`vendor/ChezScheme/`](vendor/ChezScheme) for primitives +the standard library can use without changing the user-facing language. + +## Reader Syntax + +Jerboa extends the Chez reader with Gerbil-inspired syntax: -### Persistent map ```scheme -(import (jerboa prelude) (std pmap)) +(let ([x 1] [y 2]) (+ x y)) ;; brackets are parentheses +{area circle} ;; -> (~ circle 'area) +name: ;; keyword object #:name +:std/net/request ;; -> (std net request) + +#<<END +multi-line string +END +``` + +Both import spellings work: -(define m1 (pmap 'a 1 'b 2 'c 3)) -(define m2 (pmap-assoc m1 'd 4)) -(displayln (pmap-ref m2 'b)) ;; 2 -(displayln (pmap-ref m1 'd #f)) ;; #f (m1 unchanged) +```scheme +(import :std/net/request) +(import (std net request)) ``` -## Testing +## Gerbil and Clojure Compatibility + +Most user-level Gerbil-style code works: `def`, `defstruct`, method +dispatch, `match`, `try`, `:std/*` module paths, and square-bracket +binding forms are first-class Jerboa syntax. + +Jerboa is not the Gerbil expander on Chez. These do not carry over +unchanged: + +- Gerbil expander internals such as `:gerbil/expander`. +- Gambit `##` primitives except where Jerboa provides explicit wrappers. +- Gerbil-specific export extensions unless translated. +- Gambit thread/runtime APIs that have no Chez equivalent. + +The Clojure layer is provided as Jerboa libraries rather than by +embedding Clojure. It covers the common data, sequence, state, protocol, +multimethod, STM, and CSP idioms while staying inside Chez Scheme. + +## Build and Test + +Common local targets: ```bash -make test # Core tests (289 tests) -make test-features # Phase 2+3 feature tests (637 tests) +make build # build core Jerboa libraries +make binary # native binary build for macOS/FreeBSD/other local hosts +make test # core test suite +make test-features # feature-phase tests make test-native # Rust native backend tests -make test-wrappers # Legacy chez-* C library wrappers (27 tests) -make test-clojure # Clojure compatibility layer tests -make test-fiber # Fiber + CSP + STM tests -make test-wasm # Slang/WASM backend tests -make test-all # Everything (1500+ tests) +make test-all # broad local test run ``` -Property-based testing is available via `(std test check)`: +On Linux, the release pipeline also uses: -```scheme -(import (std test) (std test check)) - -(check 'sort-is-idempotent - (lambda (xs) (equal? (sort < xs) (sort < (sort < xs)))) - (gen-list (gen-integer))) +```bash +make docker-build ``` -## Requirements - -- [Chez Scheme](https://cisco.github.io/ChezScheme/) 10.x (stock, - unmodified — though we maintain an additive in-tree fork in - this repo at [`vendor/ChezScheme/`](vendor/ChezScheme) with a - handful of pure-Scheme primitives that the standard library - uses). Both work. -- Optional: [Rust toolchain](https://rustup.rs/) for building - `libjerboa_native.so` (crypto, compression, regex, databases, - TLS, OS integration, ed25519). -- Optional (legacy): [chez-*](https://sr.ht/~lisp) libraries for - the older C-FFI shims (still functional, being superseded by the - Rust backend). - -### Supported platforms - -| OS | Architecture | Notes | -|---|---|---| -| Linux | x86_64, aarch64 | glibc + musl static both supported | -| FreeBSD | x86_64 | Capsicum enabled by default | -| macOS | x86_64, aarch64 | Apple Silicon native; sandbox-exec for `(std security cage)` | -| Android | aarch64 | Termux; bionic libc compat | +For MCP tooling data and portable MCP binaries: -## Project Structure - -``` -lib/ - jerboa/ # Reader, core macros, runtime, FFI, prelude - # + pkg, lock, hot, embed, cross, wasm, build - std/ # 229 modules — see "Standard Library" above -jerboa-native-rs/ # Unified Rust shared library - src/ - lib.rs # module declarations, init - crypto.rs # ring digests, hmac, aead, csprng, ed25519 - compress.rs # flate2: deflate, inflate, gzip - regex_native.rs # regex crate - sqlite.rs # rusqlite - postgres_native.rs # rust-postgres - duckdb.rs # duckdb-rs - epoll.rs / inotify_native.rs / landlock.rs - secure_mem.rs # mlock + guard pages + explicit_bzero - tls_rustls.rs # rustls-based TLS - panic.rs # catch_unwind wrapper for FFI -slang/ # Slang language compiler - src/ # IR, lowering, codegen - wasm/ # WASM backend (wasmi + SpiderMonkey) -docs/ # ~90 files — green-wins, clojure-vs-jerboa, native-rust, … -tests/ # Test suite (1500+ tests) -benchmarks/ # Bench harness with regression gate -support/ # Static-build artifacts, Docker base image +```bash +make jmcp +make jmcp-portable ``` -## What Gerbil Code Works +## Requirements and Platforms -Most user-level Gerbil code works unchanged: +- Chez Scheme 10.x. +- Optional Rust toolchain for `libjerboa_native` features. +- Optional legacy `chez-*` C libraries for older wrappers that remain + available while the Rust backend supersedes them. -```scheme -(import :std/sugar :std/sort :std/format) - -(def (run-command cmd env) - (try - (let* ([tokens (tokenize cmd)] - [expanded (expand-aliases tokens env)]) - (match expanded - ([prog . args] (exec-pipeline prog args env)) - (else (displayln "empty command")))) - (catch (e) (displayln "error: " (error-message e))))) -``` +Supported platform work in this repository covers Linux glibc/musl, +FreeBSD, macOS on Intel and Apple Silicon, and Android/Termux. +Security confinement maps to the strongest local mechanism available: +Landlock/seccomp on Linux, Capsicum on FreeBSD, and sandbox-exec style +support on macOS. -## What Won't Work +## Project Structure + +```text +lib/ + jerboa/ core reader, macros, runtime, prelude, package/build glue, + typed wrappers, WASM support + std/ 614 standard-library .ss modules +jerboa-native-rs/ optional Rust shared library backend +mcp/ active Jerboa MCP server +data/ MCP cookbook, API signatures, feature and changelog data +docs/ language, library, build, security, WASM, typed, and ops docs +tests/ Scheme test suites +benchmarks/ benchmark harnesses +tools/ generators, linters, build and audit helpers +vendor/ vendored Chez Scheme tree +``` -1. **Gerbil expander API** (`:gerbil/expander`) — not applicable -2. **Gambit `##` primitives** — provided case-by-case -3. **`(export #t)`** — re-export-everything needs explicit exports -4. **Gerbil-specific `syntax-case` binding semantics** — uses Chez R6RS -5. **Gambit thread API** is wrapped (`(std misc thread)`); some - Gambit-specific primitives (e.g. `thread-yield!` quirks) are not - 1:1 — see `docs/concurrency-extended.md`. +Start with these docs: + +- [`docs/JERBOA-LANG.md`](docs/JERBOA-LANG.md) for the language guide. +- [`docs/quickstart.md`](docs/quickstart.md) and + [`docs/tutorial.md`](docs/tutorial.md) for getting oriented. +- [`docs/api-index.md`](docs/api-index.md) for generated API coverage. +- [`docs/libraries.md`](docs/libraries.md) for standard-library notes. +- [`docs/reader-syntax.md`](docs/reader-syntax.md) and + [`docs/pattern-matching.md`](docs/pattern-matching.md) for syntax. +- [`docs/concurrency.md`](docs/concurrency.md), + [`docs/fiber.md`](docs/fiber.md), [`docs/async.md`](docs/async.md), + [`docs/stm.md`](docs/stm.md), and + [`docs/core-async.md`](docs/core-async.md) for concurrency. +- [`docs/security-reference.md`](docs/security-reference.md) and + [`docs/safety-guide.md`](docs/safety-guide.md) for hardening. +- [`docs/native-rust.md`](docs/native-rust.md), + [`docs/ffi.md`](docs/ffi.md), [`docs/wasm.md`](docs/wasm.md), and + [`docs/slang.md`](docs/slang.md) for native and WASM integration. +- [`docs/build.md`](docs/build.md), [`docs/cross-compile.md`](docs/cross-compile.md), + [`docs/single-binary.md`](docs/single-binary.md), and + [`docs/jpkg-guide.md`](docs/jpkg-guide.md) for builds and packages. ## License Jerboa is licensed under the Apache License 2.0. -Jerboa bundles a vendored copy of **Chez Scheme** (`vendor/ChezScheme/`), also -under the Apache License 2.0 — © 1984–2025 Cisco Systems, Inc. Its NOTICE and -full license text are reproduced at [`LICENSE-CHEZ`](LICENSE-CHEZ); Chez itself -incorporates the Nanopass framework, zlib, and LZ4, credited there. +Jerboa bundles a vendored copy of Chez Scheme +([`vendor/ChezScheme/`](vendor/ChezScheme)), also under the Apache +License 2.0. Its NOTICE and full license text are reproduced at +[`LICENSE-CHEZ`](LICENSE-CHEZ). --- a/data/cookbooks.sexp +++ b/data/cookbooks.sexp @@ -5385,4 +5385,28 @@ "try-or-false and try-or-false* return #f on exceptions. Enable *try-debug* or JERBOA_TRY_DEBUG to render swallowed conditions to current-error-port with display-condition while preserving the #f result.") ("tags" "prelude" "try-or-false" "exception" "debug" "display-condition" "JERBOA_TRY_DEBUG") - ("title" . "Debug swallowed exceptions with try-or-false"))) + ("title" . "Debug swallowed exceptions with try-or-false")) + (("code" + . + "#!chezscheme\n(library (example cache)\n (export cache make-cache cache-lookup)\n (import (chezscheme))\n\n ;; In a library body, put helpers before top-level value initializers\n ;; that call them. Procedure bodies can refer forward, but initializer\n ;; expressions are evaluated while the library is loaded.\n (define (make-cache)\n (make-hashtable equal-hash equal?))\n\n (define cache (make-cache))\n\n (define (cache-lookup key default)\n (hashtable-ref cache key default)))") ("id" . "r6rs-library-define-initializer-order") + ("imports" "chezscheme") + ("notes" + . + "If `(define cache (make-cache))` appears before `(define (make-cache) ...)`, loading the library can fail with `Exception: attempt to reference undefined variable make-cache`. Fix by moving the helper definition before the value initializer, or by inlining the initializer as `(define cache (make-hashtable equal-hash equal?))`. This matters for generated/internal `.sls` libraries as well as handwritten R6RS library files.") + ("tags" "r6rs" "library" "define" "initializer" + "forward-reference" "undefined-variable") + ("title" + . + "Avoid forward references in R6RS library define initializers")) + (("code" + . + "(import (jerboa prelude)\n (std misc persistent))\n\n(def users\n (hamt-set\n (hamt-set hamt-empty 'alice 1)\n 'bob 2))\n\n(displayln (hamt-ref users 'alice #f))\n(displayln (hamt-ref users 'carol 'missing))\n(displayln (hamt-contains? users 'bob))\n(displayln (hamt-size users))") ("id" . "std-misc-persistent-hamt-basic") + ("imports" "(jerboa prelude)" "(std misc persistent)") + ("notes" + . + "`hamt-empty` is an exported empty HAMT value, not a zero-argument constructor. `hamt-set` takes `(hamt-set map key value)`. `hamt-ref` takes `(hamt-ref map key default)`; pass an explicit default for missing keys.") + ("tags" "std/misc/persistent" "hamt" "persistent-map" + "immutable" "hamt-empty" "hamt-ref") + ("title" + . + "Use HAMT persistent maps from (std misc persistent)"))) --- a/data/features.sexp +++ b/data/features.sexp @@ -824,9 +824,12 @@ . "Kernel sandboxes usually provide network on/off, but jsh needs portable host-level network policy for AI tools and package managers.") ("votes" . 0)) - (("description" + (("closed_reason" . - "try-or-false and (guard (e [else #f]) ...) silently return #f on any exception. Catastrophic when debugging - a real bug looks like the function just does not work, with no error. Proposal: a JERBOA_TRY_DEBUG=1 env var (or parameterize ([try-debug #t])) that causes the wrapper to render the condition via display-condition to current-error-port before returning #f. Opt-in keeps existing semantics; only the diagnostic output is added.") + "(jerboa prelude) now exports *try-debug*, try-debug-enabled?, try-debug-log, try-or-false, and try-or-false*. try-or-false keeps the existing #f-on-exception behavior by default, while parameterizing *try-debug* or setting JERBOA_TRY_DEBUG causes swallowed exceptions to be rendered with display-condition to current-error-port before returning #f. tests/test-try-debug.ss covers value return, swallowed exception return, expression macro usage, and debug output capture.") + ("description" + . + "try-or-false and (guard (e [else #f]) ...) silently return #f on any exception. Catastrophic when debugging - a real bug looks like the function just does not work, with no error. Proposal: a JERBOA_TRY_DEBUG=1 env var (or parameterize ([try-debug #t])) that causes the wrapper to render the condition via display-condition to current-error-port before returning #f. Opt-in keeps existing semantics; only the diagnostic output is added.") ("estimated_token_reduction" . "~500-2000 tokens per masked-exception bug") @@ -834,12 +837,11 @@ . "While implementing JSONL audit output for jsh limits.sls, the audit file was silently never created. try-or-false hid an exception. Hand-replacing with with-exception-handler + display-condition revealed car-not-a-pair from the JSON encoder. ~20 minutes of debugging that JERBOA_TRY_DEBUG=1 would have surfaced on the first run.") ("id" . "try-or-false-debug-mode") ("impact" . "medium") - ("implemented_in" . "lib/jerboa/prelude.ss, tests/test-try-debug.ss, Makefile") + ("implemented_in" + . + "lib/jerboa/prelude.ss, tests/test-try-debug.ss, Makefile") ("implemented_tool" . "(jerboa prelude)") ("status" . "implemented") - ("closed_reason" - . - "(jerboa prelude) now exports *try-debug*, try-debug-enabled?, try-debug-log, try-or-false, and try-or-false*. try-or-false keeps the existing #f-on-exception behavior by default, while parameterizing *try-debug* or setting JERBOA_TRY_DEBUG causes swallowed exceptions to be rendered with display-condition to current-error-port before returning #f. tests/test-try-debug.ss covers value return, swallowed exception return, expression macro usage, and debug output capture.") ("tags" "try-or-false" "guard" "exception" "debug" "diagnostic") ("title" @@ -949,9 +951,12 @@ . "Running jmcp with constrained local models that cannot carry the entire MCP tool catalog in their prompt/k-v cache.") ("votes" . 0)) - (("description" + (("closed_reason" . - "jerboa_verify and compile_check failed on jerbuild.ss with an internal string-ref invalid-index exception while dumping the whole file, so verification had to fall back to running the actual jerbuild build and a custom config parser check. The tool should either handle large source strings safely or return a focused diagnostic with file/offset context.") + "Verifier error paths now use bounded actionable diagnostics with trimmed condition text, source-index fallback, and nearby source excerpts. A large malformed file smoke returned a short Source excerpt instead of dumping the full source.") + ("description" + . + "jerboa_verify and compile_check failed on jerbuild.ss with an internal string-ref invalid-index exception while dumping the whole file, so verification had to fall back to running the actual jerbuild build and a custom config parser check. The tool should either handle large source strings safely or return a focused diagnostic with file/offset context.") ("estimated_token_reduction" . "~2,000 tokens per failure by avoiding large crash dumps and fallback shell checks") @@ -959,13 +964,11 @@ . "After adding .jerbuild parsing support, jerboa_verify on /Users/user/mine/jerboa/jerbuild.ss returned `Exception in string-ref: 112360 is not a valid index` and printed a huge escaped file body instead of a useful syntax/compile result.") ("id" . "jerboa-verify-large-file-diagnostic") - ("impact" . "medium") - ("implemented_in" . "mcp/server.ss") - ("implemented_tool" . "jerboa_verify, jerboa_compile_check, jerboa_check_syntax") - ("status" . "implemented") - ("closed_reason" + ("impact" . "medium") ("implemented_in" . "mcp/server.ss") + ("implemented_tool" . - "Verifier error paths now use bounded actionable diagnostics with trimmed condition text, source-index fallback, and nearby source excerpts. A large malformed file smoke returned a short Source excerpt instead of dumping the full source.") + "jerboa_verify, jerboa_compile_check, jerboa_check_syntax") + ("status" . "implemented") ("tags" "verify" "compile-check" "large-file" "diagnostics") ("title" . @@ -1066,7 +1069,9 @@ . "Security reviewing Jerboa source without spending most of the time triaging scanner noise.") ("votes" . 0)) - (("closed_reason" . "") + (("closed_reason" + . + "Large-file verifier failures are now reported through the same bounded actionable diagnostic formatter used by parse/compile errors, with source-index fallback and source excerpts rather than full source dumps.") ("description" . "jerboa_verify can fail internally while formatting or scanning very large Scheme files, e.g. with an Exception in string-ref on a provider module, even when the project build can compile the file. The verifier should stream or bound snippets safely and report a tool error separately from source diagnostics.") @@ -1080,9 +1085,6 @@ ("impact" . "medium") ("implemented_in" . "mcp/server.ss") ("implemented_tool" . "jerboa_verify, jerboa_compile_check") ("status" . "implemented") - ("closed_reason" - . - "Large-file verifier failures are now reported through the same bounded actionable diagnostic formatter used by parse/compile errors, with source-index fallback and source excerpts rather than full source dumps.") ("tags" "verify" "large-files" "diagnostics" "tool-error") ("title" . @@ -1091,9 +1093,12 @@ . "Validating large Jerboa modules after a small edit without falling back to a full make build.") ("votes" . 0)) - (("description" + (("closed_reason" . - "When running jerboa_eval, there is no way to query which Gerbil version the runtime is using. Trying (gerbil-version-string) throws 'unbound identifier'. This makes it impossible to confirm whether v0.19 features are available before testing, or to include version context in error messages.") + "jerboa_eval now wraps expressions with local jerboa-runtime-version, jerboa-version-string, and gerbil-version-string bindings that return the current Jerboa MCP/Chez runtime context. The existing jerboa_version tool remains available for direct version reporting.") + ("description" + . + "When running jerboa_eval, there is no way to query which Gerbil version the runtime is using. Trying (gerbil-version-string) throws 'unbound identifier'. This makes it impossible to confirm whether v0.19 features are available before testing, or to include version context in error messages.") ("estimated_token_reduction" . "~300 tokens per session: eliminates 3-4 failed eval probes plus the bash fallback to determine version") @@ -1101,12 +1106,11 @@ . "Trying to test v0.19 std/iter patterns: (for (x (in-range 5)) body) fails because the runtime is v0.18.1. There was no way to discover this without trial-and-error. A (jerboa-runtime-version) or (##gerbil-version-string) binding in eval would let the recipe-authoring workflow confirm compatibility upfront.") ("id" . "jerboa-version-query") ("impact" . "medium") - ("implemented_in" . "mcp/server.ss, mcp/test/protocol-test.ss") + ("implemented_in" + . + "mcp/server.ss, mcp/test/protocol-test.ss") ("implemented_tool" . "jerboa_eval, jerboa_version") ("status" . "implemented") - ("closed_reason" - . - "jerboa_eval now wraps expressions with local jerboa-runtime-version, jerboa-version-string, and gerbil-version-string bindings that return the current Jerboa MCP/Chez runtime context. The existing jerboa_version tool remains available for direct version reporting.") ("tags" "eval" "version" "runtime" "gerbil" "compatibility") ("title" . @@ -1115,9 +1119,12 @@ . "Discovering which stdlib API version is available before testing new patterns; writing version-conditional recipes; diagnosing why a function is missing.") ("votes" . 0)) - (("description" + (("closed_reason" . - "When testing API patterns for a future Gerbil version (e.g., v0.19 while runtime is v0.18), jerboa_eval throws cryptic errors instead of a clear 'not available in this runtime' message. A gerbil_version parameter on jerboa_eval would let callers declare the minimum required version and get a clean skip/warning instead of a confusing exception.") + "jerboa_eval accepts gerbil_version and jerboa_version guard arguments. gerbil_version returns a clean skip because Jerboa is Chez-based and not a Gerbil runtime; jerboa_version compares against the MCP/runtime version and skips when the requested version is newer.") + ("description" + . + "When testing API patterns for a future Gerbil version (e.g., v0.19 while runtime is v0.18), jerboa_eval throws cryptic errors instead of a clear 'not available in this runtime' message. A gerbil_version parameter on jerboa_eval would let callers declare the minimum required version and get a clean skip/warning instead of a confusing exception.") ("estimated_token_reduction" . "~400 tokens per session for version-crossing recipe work: eliminates confusing error interpretation and the bash version-probe detour") @@ -1126,12 +1133,11 @@ "Calling jerboa_eval to test (for (x (in-range 5)) body) from v0.19 std/iter while v0.18.1 is installed. The macro has different syntax in v0.18, so the error 'invalid syntax (x (in-range 5))' is confusing — it looks like the test code is wrong, not that it needs a newer runtime.") ("id" . "jerboa-eval-imports-version-guard") ("impact" . "medium") - ("implemented_in" . "mcp/server.ss, mcp/test/protocol-test.ss") + ("implemented_in" + . + "mcp/server.ss, mcp/test/protocol-test.ss") ("implemented_tool" . "jerboa_eval") ("status" . "implemented") - ("closed_reason" - .