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

@ -836,7 +836,15 @@
(apply f args)
(if (keyword? f)
(get (first args) f)
(error (string "Cannot call " (type f) " as a function"))))))))
(if (and (table? f) (get f :jolt/deftype))
(let [ifn-fn (find-protocol-method ctx (get f :jolt/deftype) "IFn" "-invoke")]
(if ifn-fn (apply ifn-fn f args)
(if (get f :jolt/protocol-methods)
(let [invoke-fn (get (f :jolt/protocol-methods) :-invoke)]
(if invoke-fn (apply invoke-fn f args)
(error (string "Cannot call " (type f) " as a function"))))
(error (string "Cannot call " (type f) " as a function")))))
(error (string "Cannot call " (type f) " as a function")))))))))
(set eval-form (fn [ctx bindings form]
(cond

View file

@ -282,7 +282,35 @@
(defn read-anon-fn [s pos]
# pos is at #, next char is (
(let [[form new-pos] (read-form s (+ pos 1))]
[(array/insert form 0 (sym "fn*")) new-pos]))
# Collect % arg references and rename them to gensyms
(var arg-map @{})
(defn- replace-pct [f]
(cond
(and (struct? f) (= :symbol (f :jolt/type)))
(let [nm (f :name)]
(if (and (> (length nm) 0) (= "%" (string/slice nm 0 1)))
(let [existing (get arg-map nm)]
(if existing
{:jolt/type :symbol :ns nil :name existing}
(let [gen (gensym)]
(put arg-map nm (string gen))
{:jolt/type :symbol :ns nil :name (string gen)})))
f))
(array? f) (array ;(map replace-pct f))
(tuple? f) (tuple ;(map replace-pct f))
f))
(def replaced (replace-pct form))
(def arg-names @[])
# Sort %1 %2 %3 ..., then %, then %&
(def sorted-keys (sort (keys arg-map)))
(each k sorted-keys
(array/push arg-names {:jolt/type :symbol :ns nil :name (get arg-map k)}))
(def result @[(sym "fn*")])
(if (> (length arg-names) 0)
(array/push result (tuple ;arg-names))
(array/push result (tuple))) # no args
(array/push result replaced)
[result new-pos]))
(defn read-reader-conditional [s pos]
# pos is at #, next char is ? or ?@