Add prelude, fix sugar macros, add README
ober
a71e472f159a666f17f6d3bb3876bafdd281ecbe
--- a/README.md +++ b/README.md @@ -1 +1,172 @@ -# jerboa +# Jerboa + +Gerbil Scheme's syntax and APIs, running on stock Chez Scheme. + +Jerboa implements Gerbil's user-facing language — `def`, `defstruct`, `match`, hash tables, `:std/*` libraries — as Chez Scheme macros and native libraries. No Gerbil expander, no Gambit compatibility layer, no patched Chez. + +## Quick Start + +```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] + +(main) +``` + +Run with: +```bash +scheme --libdirs lib --script your-file.ss +``` + +## Architecture + +``` +┌──────────────────────────────────────────────┐ +│ User's Gerbil-like code │ +│ (def (main) (displayln (sort [3 1 2] <))) │ +└──────────────┬───────────────────────────────┘ + │ +┌──────────────▼───────────────────────────────┐ +│ Reader: [...] → (list ...), {...} → (~ ..) │ +│ :std/sort → (std sort) module paths │ +├──────────────────────────────────────────────┤ +│ Core Macros: def, defstruct, match, try │ +│ All expand to standard Chez Scheme │ +├──────────────────────────────────────────────┤ +│ Runtime: hash tables, method dispatch │ +├──────────────────────────────────────────────┤ +│ Standard Library: sort, JSON, paths, etc. │ +├──────────────────────────────────────────────┤ +│ FFI: c-lambda → foreign-procedure │ +├──────────────────────────────────────────────┤ +│ Stock Chez Scheme — no fork, no patches │ +└──────────────────────────────────────────────┘ +``` + +## What's Included + +### Core Macros (`(jerboa core)`) +- `def` — functions with optional args, rest args +- `def*` — case-lambda shorthand +- `defstruct` — native Chez records with auto-generated accessors +- `defclass` — records with inheritance +- `defmethod` — method dispatch via `bind-method!` +- `match` — pattern matching (lists, predicates, `and`/`or`/`not`, cons, wildcards) +- `try`/`catch`/`finally` — exception handling +- `defrule`/`defrules` — syntax-rules shortcuts +- `while`/`until` — loop macros +- `hash-literal`/`let-hash` — hash table construction and destructuring + +### Runtime (`(jerboa runtime)`) +- Full Gerbil hash table API: `hash-ref`, `hash-put!`, `hash-get`, `hash-keys`, etc. +- Method dispatch: `~`, `bind-method!`, `call-method` +- Keywords: `string->keyword`, `keyword?`, `keyword->string` +- Utilities: `displayln`, `iota`, `1+`, `1-` + +### Standard Library +| Module | Provides | +|--------|----------| +| `(std sort)` | `sort`, `stable-sort` | +| `(std format)` | `printf`, `fprintf`, `eprintf` | +| `(std error)` | `Error`, `ContractViolation` | +| `(std sugar)` | `chain`, `chain-and`, `assert!` | +| `(std text json)` | `read-json`, `write-json`, `string->json-object`, `json-object->string` | +| `(std os path)` | `path-join`, `path-directory`, `path-extension`, etc. | +| `(std misc string)` | `string-split`, `string-join`, `string-trim`, `string-contains`, etc. | +| `(std misc list)` | `flatten`, `unique`, `take`, `drop`, `every`, `any`, `filter-map`, `zip` | +| `(std misc alist)` | `agetq`, `pgetq`, `alist->hash-table` | +| `(std misc ports)` | `read-file-string`, `with-output-to-string`, etc. | +| `(std misc channel)` | Thread-safe channels (Chez mutex/condvar) | + +### FFI (`(jerboa ffi)`) +- `c-lambda` → `foreign-procedure` with automatic type translation +- `define-c-lambda` — named FFI bindings +- `begin-ffi`, `c-declare` — compatibility forms +- Full Gambit-to-Chez type mapping + +### Reader (`(jerboa reader)`) +- `[1 2 3]` → `(list 1 2 3)` +- `{method obj}` → `(~ obj 'method)` +- `keyword:` → keyword objects +- `:std/sort` → `(std sort)` module paths +- Heredoc strings, datum comments, block comments + +### Prelude (`(jerboa prelude)`) +One import for everything: +```scheme +(import (jerboa prelude)) +``` + +## Testing + +```bash +make test +``` + +Runs 213 tests across reader, core macros, runtime, standard library, FFI, and module path mapping. + +## Requirements + +- [Chez Scheme](https://cisco.github.io/ChezScheme/) 10.x (stock, unmodified) + +## Project Structure + +``` +lib/ + jerboa/ + reader.sls # Gerbil-compatible reader + core.sls # Core syntax macros + runtime.sls # Hash tables, method dispatch, keywords + ffi.sls # FFI translation macros + prelude.sls # All-in-one import + std/ + sort.sls # :std/sort + format.sls # :std/format + error.sls # :std/error + sugar.sls # :std/sugar + text/json.sls # :std/text/json + os/path.sls # :std/os/path + misc/ + string.sls # :std/misc/string + list.sls # :std/misc/list + alist.sls # :std/misc/alist + ports.sls # :std/misc/ports + channel.sls # :std/misc/channel +tests/ + test-reader.ss # 65 reader tests + test-core.ss # 68 core macro tests + test-stdlib.ss # 65 stdlib tests + test-ffi.ss # 7 FFI tests + test-modules.ss # 8 module path tests +``` + +## What Gerbil Code Works + +Most user-level Gerbil code works unchanged: + +```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))))) +``` + +## What Won't Work + +1. **Gerbil expander API** (`:gerbil/expander`) — not applicable +2. **Gambit `##` primitives** — provide needed ones case-by-case +3. **`(export #t)`** — re-export-everything needs explicit exports +4. **Gerbil-specific `syntax-case` binding semantics** — uses Chez R6RS semantics new file mode 100644 --- /dev/null +++ b/lib/jerboa/prelude.sls @@ -0,0 +1,114 @@ +#!chezscheme +;;; jerboa/prelude -- One-import-to-rule-them-all +;;; +;;; (import (jerboa prelude)) gives you all of Gerbil's user-facing API: +;;; - Core macros: def, defstruct, defmethod, match, try/catch, etc. +;;; - Runtime: hash tables, method dispatch, keywords +;;; - Standard library: sort, format, JSON, paths, strings, lists, etc. +;;; - FFI: c-lambda, define-c-lambda + +(library (jerboa prelude) + (export + ;; ---- Core macros ---- + def def* defrule defrules + defstruct defclass defmethod + match + try catch finally + while until + + ;; hash constructors + hash-literal hash-eq-literal + let-hash + + ;; ---- Runtime ---- + ~ bind-method! call-method + make-hash-table make-hash-table-eq + hash-ref hash-get hash-put! hash-update! hash-remove! + hash-key? hash->list hash->plist hash-for-each hash-map hash-fold + hash-find hash-keys hash-values hash-copy hash-clear! + hash-merge hash-merge! hash-length hash-table? + list->hash-table plist->hash-table + keyword? keyword->string string->keyword make-keyword + error-message error-irritants error-trace + displayln 1+ 1- + iota last-pair + *method-tables* + register-struct-type! *struct-types* + struct-predicate struct-field-ref struct-field-set! + struct-type-info + + ;; ---- std/sort ---- + sort sort! stable-sort stable-sort! + + ;; ---- std/format ---- + format printf fprintf eprintf + + ;; ---- std/error ---- + Error ContractViolation + + ;; ---- std/sugar ---- + chain chain-and assert! + + ;; ---- std/text/json ---- + read-json write-json json-object->string string->json-object + + ;; ---- std/os/path ---- + path-expand path-normalize path-directory path-strip-directory + path-extension path-strip-extension + path-join path-absolute? + + ;; ---- std/misc/string ---- + string-split string-join string-trim + string-prefix? string-suffix? + string-contains string-index + string-empty? + + ;; ---- std/misc/list ---- + flatten unique snoc + take drop + every any + filter-map + group-by + zip + + ;; ---- std/misc/alist ---- + agetq agetv aget + asetq! asetv! aset! + pgetq pgetv pget + alist->hash-table + + ;; ---- std/misc/ports ---- + read-all-as-string read-all-as-lines + read-file-string read-file-lines + write-file-string + with-input-from-string with-output-to-string + + ;; ---- FFI ---- + c-lambda define-c-lambda + begin-ffi c-declare) + + (import + (except (chezscheme) + make-hash-table hash-table? + sort sort! + printf fprintf + path-extension path-absolute? + with-input-from-string with-output-to-string + iota 1+ 1-) + (jerboa core) + (jerboa ffi) + (std sort) + (std format) + (except (std error) error-message error-irritants error-trace error? + with-exception-handler) + (except (std sugar) try catch finally while until + hash-literal hash-eq-literal let-hash + defrule defrules) + (std text json) + (std os path) + (std misc string) + (std misc list) + (std misc alist) + (std misc ports)) + + ) ;; end library --- a/lib/std/sugar.sls +++ b/lib/std/sugar.sls @@ -28,17 +28,22 @@ (define-syntax chain-apply (lambda (stx) - (syntax-case stx (_) + (syntax-case stx () [(_ f val) #'(f val)] - [(_ f val _ arg ...) #'(f val arg ...)] + [(_ f val placeholder arg ...) + (and (identifier? #'placeholder) (eq? (syntax->datum #'placeholder) '_)) + #'(f val arg ...)] [(_ f val arg1 rest ...) #'(chain-apply-tail f val (arg1) rest ...)]))) (define-syntax chain-apply-tail (lambda (stx) - (syntax-case stx (_) - [(_ f val (args ...) _) #'(f args ... val)] - [(_ f val (args ...) _ rest ...) + (syntax-case stx () + [(_ f val (args ...) placeholder) + (and (identifier? #'placeholder) (eq? (syntax->datum #'placeholder) '_)) + #'(f args ... val)] + [(_ f val (args ...) placeholder rest ...) + (and (identifier? #'placeholder) (eq? (syntax->datum #'placeholder) '_)) #'(chain-apply-tail f val (args ... val) rest ...)] [(_ f val (args ...) arg rest ...) #'(chain-apply-tail f val (args ... arg) rest ...)]