Phase 13: Protocol Completion — reify dispatch, #() reader, IFn protocol

- reader.janet: rewrite read-anon-fn to handle % arg references
  % → gensym, %1/%2 → sorted gensyms, replaces all matching % refs
- evaluator.janet: IFn protocol support in default invocation arm
  Before erroring "Cannot call X as a function", checks for:
  1) type-registry IFn/-invoke method (extend-type protocols)
  2) :jolt/protocol-methods :-invoke (reified objects)
- test/phase13-test.janet: 4 test sections (28-31)
  28: reify dispatch — protocol methods on reified objects
  29: #() anon-fn — % and %1/%2 arg handling
  30: extend-type — protocol method dispatch for deftypes
  31: clojure.walk loading — keywordize-keys loads correctly
- All pass: 316 ok, 1 fail (pre-existing, unchanged)
This commit is contained in:
Yogthos 2026-06-03 13:19:56 -04:00
parent 5d7f392666
commit df1e836cda
7 changed files with 130 additions and 44 deletions

View file

@ -1,5 +1,5 @@
Clojure .clj source files loaded via eval-form cannot have docstrings. If a defn form has 5 elements (defn, name, docstring, params, body), the evaluator's defn macro handler gets 4 args instead of 3, breaking with "macro arity mismatch". All .clj files in src/jolt/clojure/ must use 4-element defn forms: (defn name [params] body). Docstrings on defn are a Clojure feature not supported by Jolt's defn macro.
§
PHM internal key leak: Core functions iterating over PHM maps with keys/pairs get internal metadata keys (:jolt/deftype, :cnt, :buckets, :_meta). Always check for set?/phm? first and use type-aware helpers (phm-to-struct, phs-seq, phm-keys, phm-entries) before generic iteration. Hit us in core-merge, core-reduce, core-every?, core-filter which all needed set?/phm? checks added.
§
Janet `and` returns last truthy value, not boolean. `(and table? deftype)` returns the deftype string, not true. Predicate-like functions (core-map?, core-contains?, etc.) that check type tags via `(and ...)` must wrap in `(if ... true false)`. Hit us with core-map? returning the deftype string instead of boolean true for record instances. Also hit type-satisfies? which had to replace `(boolean ...)` (nonexistent) with `(if ... true false)`.
§
Janet `and` returns last truthy value, not boolean. `(and table? deftype)` returns the deftype string, not true. Predicate-like functions (core-map?, core-contains?, etc.) that check type tags via `(and ...)` must wrap in `(if ... true false)`. Examples: core-map? returning deftype string instead of boolean true for record instances. Also applies to type-satisfies? which must use `(if (and ...) true false)` — Janet has no `boolean` function.
§
Clojure .clj source files loaded via eval-form cannot have docstrings. If a defn form has 5 elements (defn, name, docstring, params, body), the evaluator's defn macro handler gets 4 args instead of 3, breaking with "macro arity mismatch". All .clj files in src/jolt/clojure/ must use 4-element defn forms: (defn name [params] body). Docstrings on defn are a Clojure feature not supported by Jolt's defn macro.