Phase 12: Protocol System — full implementation with all tests passing
Root cause: fn* multi-arity detection checks (array? (in form 1)) — defprotocol used @[...] for args, triggering multi-arity path that treated the body form as a second arity pair. Fixes applied: - defprotocol: change fn* args from @[...] to [...] (tuple) so single-arity fn* path is used with [this & rest-args] - defprotocol: remove quote wrappers from protocol-dispatch call (protocol-name and method-name passed directly as symbols) - extend-type/extend-protocol: remove quote wrappers from register-method call (symbols passed directly, not via quote) - protocol-dispatch: use (in form ...) directly for proto/method symbols (no eval-form needed after quote removal) - satisfy?: extract name string from protocol value's :name field (which is a symbol struct, not a plain string) - make-reified: remove spurious (apply fn []) call - core-reify: fix (keyword struct) → extract :name string first - core-extend-protocol: handle single method spec vs multi 5 test sections (35-39) covering: defprotocol, extend-type, extend-protocol, satisfies?, reify — all pass 315 ok, 2 fail (pre-existing, unchanged)
This commit is contained in:
parent
d6eb29646d
commit
f7747ed1b4
4 changed files with 118 additions and 55 deletions
|
|
@ -1,32 +1,23 @@
|
|||
{
|
||||
"jolt-persistent-structures": {
|
||||
"jolt-dev": {
|
||||
"created_by": "agent",
|
||||
"use_count": 1,
|
||||
"view_count": 7,
|
||||
"patch_count": 0,
|
||||
"last_used_at": "2026-06-03T03:49:43.520839+00:00",
|
||||
"last_viewed_at": "2026-06-03T04:18:13.482762+00:00",
|
||||
"created_at": "2026-06-03T03:35:04.130959+00:00",
|
||||
"state": "active",
|
||||
"pinned": false
|
||||
},
|
||||
"jolt-gotchas": {
|
||||
"created_by": "agent",
|
||||
"use_count": 0,
|
||||
"view_count": 5,
|
||||
"patch_count": 0,
|
||||
"last_viewed_at": "2026-06-03T04:18:13.476736+00:00",
|
||||
"created_at": "2026-06-03T03:50:41.474730+00:00",
|
||||
"use_count": 35,
|
||||
"view_count": 62,
|
||||
"patch_count": 42,
|
||||
"last_used_at": "2026-06-03T03:49:43.510599+00:00",
|
||||
"last_viewed_at": "2026-06-03T04:20:32.579847+00:00",
|
||||
"last_patched_at": "2026-06-03T03:34:09.007253+00:00",
|
||||
"created_at": "2026-06-01T21:26:06.614465+00:00",
|
||||
"state": "active",
|
||||
"pinned": false
|
||||
},
|
||||
"jolt-compiler": {
|
||||
"created_by": "agent",
|
||||
"use_count": 9,
|
||||
"view_count": 19,
|
||||
"view_count": 25,
|
||||
"patch_count": 9,
|
||||
"last_used_at": "2026-06-03T03:49:43.470444+00:00",
|
||||
"last_viewed_at": "2026-06-03T04:18:13.465606+00:00",
|
||||
"last_viewed_at": "2026-06-03T04:20:32.574065+00:00",
|
||||
"last_patched_at": "2026-06-03T03:03:27.524946+00:00",
|
||||
"created_at": "2026-06-02T17:54:38.690279+00:00",
|
||||
"state": "active",
|
||||
|
|
@ -35,35 +26,44 @@
|
|||
"jpm-build": {
|
||||
"created_by": "agent",
|
||||
"use_count": 0,
|
||||
"view_count": 21,
|
||||
"view_count": 27,
|
||||
"patch_count": 0,
|
||||
"last_viewed_at": "2026-06-03T04:18:13.488874+00:00",
|
||||
"last_viewed_at": "2026-06-03T04:20:32.597461+00:00",
|
||||
"created_at": "2026-06-01T20:56:39.144222+00:00",
|
||||
"state": "active",
|
||||
"pinned": false
|
||||
},
|
||||
"jolt-dev": {
|
||||
"created_by": "agent",
|
||||
"use_count": 35,
|
||||
"view_count": 56,
|
||||
"patch_count": 42,
|
||||
"last_used_at": "2026-06-03T03:49:43.510599+00:00",
|
||||
"last_viewed_at": "2026-06-03T04:18:13.470424+00:00",
|
||||
"last_patched_at": "2026-06-03T03:34:09.007253+00:00",
|
||||
"created_at": "2026-06-01T21:26:06.614465+00:00",
|
||||
"state": "active",
|
||||
"pinned": false
|
||||
},
|
||||
"jolt-bootstrap": {
|
||||
"created_by": "agent",
|
||||
"use_count": 9,
|
||||
"view_count": 30,
|
||||
"view_count": 36,
|
||||
"patch_count": 10,
|
||||
"last_used_at": "2026-06-02T18:45:23.336653+00:00",
|
||||
"last_viewed_at": "2026-06-03T04:18:13.459884+00:00",
|
||||
"last_viewed_at": "2026-06-03T04:20:32.567542+00:00",
|
||||
"last_patched_at": "2026-06-02T03:44:45.935676+00:00",
|
||||
"created_at": "2026-06-01T21:49:51.101718+00:00",
|
||||
"state": "active",
|
||||
"pinned": false
|
||||
},
|
||||
"jolt-gotchas": {
|
||||
"created_by": "agent",
|
||||
"use_count": 0,
|
||||
"view_count": 11,
|
||||
"patch_count": 0,
|
||||
"last_viewed_at": "2026-06-03T04:20:32.585916+00:00",
|
||||
"created_at": "2026-06-03T03:50:41.474730+00:00",
|
||||
"state": "active",
|
||||
"pinned": false
|
||||
},
|
||||
"jolt-persistent-structures": {
|
||||
"created_by": "agent",
|
||||
"use_count": 1,
|
||||
"view_count": 13,
|
||||
"patch_count": 0,
|
||||
"last_used_at": "2026-06-03T03:49:43.520839+00:00",
|
||||
"last_viewed_at": "2026-06-03T04:20:32.591070+00:00",
|
||||
"created_at": "2026-06-03T03:35:04.130959+00:00",
|
||||
"state": "active",
|
||||
"pinned": false
|
||||
}
|
||||
}
|
||||
|
|
@ -1056,11 +1056,11 @@
|
|||
(array/push method-def method-name)
|
||||
(def fn-form @[])
|
||||
(array/push fn-form {:jolt/type :symbol :ns nil :name "fn*"})
|
||||
(array/push fn-form @[{:jolt/type :symbol :ns nil :name "this"} {:jolt/type :symbol :ns nil :name "&"} {:jolt/type :symbol :ns nil :name "rest-args"}])
|
||||
(array/push fn-form [{:jolt/type :symbol :ns nil :name "this"} {:jolt/type :symbol :ns nil :name "&"} {:jolt/type :symbol :ns nil :name "rest-args"}])
|
||||
(array/push fn-form @[
|
||||
{:jolt/type :symbol :ns nil :name "protocol-dispatch"}
|
||||
{:jolt/type :symbol :ns nil :name "quote"} protocol-name
|
||||
{:jolt/type :symbol :ns nil :name "quote"} method-name
|
||||
protocol-name
|
||||
method-name
|
||||
{:jolt/type :symbol :ns nil :name "this"}
|
||||
{:jolt/type :symbol :ns nil :name "rest-args"}])
|
||||
(array/push method-def fn-form)
|
||||
|
|
@ -1076,9 +1076,9 @@
|
|||
(def fn-form @[{:jolt/type :symbol :ns nil :name "fn*"} arg-vec ;body])
|
||||
(array/push result @[
|
||||
{:jolt/type :symbol :ns nil :name "register-method"}
|
||||
{:jolt/type :symbol :ns nil :name "quote"} type-sym
|
||||
{:jolt/type :symbol :ns nil :name "quote"} proto-sym
|
||||
{:jolt/type :symbol :ns nil :name "quote"} method-name
|
||||
type-sym
|
||||
proto-sym
|
||||
method-name
|
||||
fn-form]))
|
||||
result)
|
||||
|
||||
|
|
@ -1087,21 +1087,32 @@
|
|||
(var i 0)
|
||||
(while (< i (length type-impls))
|
||||
(let [type-sym (type-impls i)
|
||||
impls (type-impls (+ i 1))]
|
||||
(var j 0)
|
||||
(while (< j (length impls))
|
||||
(let [method-spec (impls j)]
|
||||
methods (type-impls (+ i 1))]
|
||||
# methods is a single method spec array or an array of method specs
|
||||
# If the first element is a symbol (method name), treat as single spec
|
||||
(if (and (struct? (methods 0)) (= :symbol ((methods 0) :jolt/type)))
|
||||
(let [method-spec methods]
|
||||
(def method-name (method-spec 0))
|
||||
(def arg-vec (method-spec 1))
|
||||
(def body (tuple/slice method-spec 2))
|
||||
(def fn-form @[{:jolt/type :symbol :ns nil :name "fn*"} arg-vec ;body])
|
||||
(array/push result @[
|
||||
{:jolt/type :symbol :ns nil :name "register-method"}
|
||||
{:jolt/type :symbol :ns nil :name "quote"} type-sym
|
||||
{:jolt/type :symbol :ns nil :name "quote"} proto-sym
|
||||
{:jolt/type :symbol :ns nil :name "quote"} method-name
|
||||
type-sym
|
||||
proto-sym
|
||||
method-name
|
||||
fn-form]))
|
||||
(+= j 2)))
|
||||
(each method-spec methods
|
||||
(def method-name (method-spec 0))
|
||||
(def arg-vec (method-spec 1))
|
||||
(def body (tuple/slice method-spec 2))
|
||||
(def fn-form @[{:jolt/type :symbol :ns nil :name "fn*"} arg-vec ;body])
|
||||
(array/push result @[
|
||||
{:jolt/type :symbol :ns nil :name "register-method"}
|
||||
type-sym
|
||||
proto-sym
|
||||
method-name
|
||||
fn-form]))))
|
||||
(+= i 2))
|
||||
result)
|
||||
|
||||
|
|
@ -1116,12 +1127,12 @@
|
|||
(def method-name (method-spec 0))
|
||||
(def arg-vec (method-spec 1))
|
||||
(def body (tuple/slice method-spec 2))
|
||||
(put methods (keyword method-name) @{:fn* true :args arg-vec :body body})
|
||||
(put methods (keyword (if (struct? method-name) (method-name :name) method-name)) @{:fn* true :args arg-vec :body body})
|
||||
(+= i 2)))
|
||||
(array/push result @[
|
||||
{:jolt/type :symbol :ns nil :name "make-reified"}
|
||||
{:jolt/type :symbol :ns nil :name "quote"} proto-sym
|
||||
{:jolt/type :symbol :ns nil :name "quote"} methods])
|
||||
proto-sym
|
||||
methods])
|
||||
result)
|
||||
|
||||
(def core-satisfies? (fn [proto-sym obj] false))
|
||||
|
|
|
|||
|
|
@ -651,8 +651,8 @@
|
|||
(let [fn-value (if (and (table? v) (get v :fn*))
|
||||
(let [args-vec (get v :args)
|
||||
body-forms (get v :body)]
|
||||
(apply (eval-form ctx @{}
|
||||
@[{:jolt/type :symbol :ns nil :name "fn*"} args-vec ;body-forms]) []))
|
||||
(eval-form ctx @{}
|
||||
@[{:jolt/type :symbol :ns nil :name "fn*"} args-vec ;body-forms]))
|
||||
v)]
|
||||
(put (obj :jolt/protocol-methods) k fn-value)))
|
||||
obj)
|
||||
|
|
@ -663,7 +663,9 @@
|
|||
(if (get obj :jolt/protocol-methods)
|
||||
(get obj :jolt/deftype)))]
|
||||
(if type-tag
|
||||
(type-satisfies? ctx type-tag (proto-sym :name))
|
||||
(let [pn (proto-sym :name)
|
||||
pn-str (if (struct? pn) (pn :name) pn)]
|
||||
(type-satisfies? ctx type-tag pn-str))
|
||||
false))
|
||||
"locking" (eval-form ctx bindings (in form 2))
|
||||
"instance?" (let [type-sym (in form 1)
|
||||
|
|
|
|||
50
test/phase12-test.janet
Normal file
50
test/phase12-test.janet
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# Phase 12: Protocol System Tests
|
||||
(use ../src/jolt/api)
|
||||
(defn ct-eval [ctx s] (eval-string ctx s))
|
||||
|
||||
(print "35: defprotocol...")
|
||||
(let [ctx (init)]
|
||||
(ct-eval ctx "(defprotocol Greet (greet [this]))")
|
||||
(let [p (ct-eval ctx "Greet")]
|
||||
(assert (not (nil? p)) "protocol var exists")
|
||||
(assert (= :jolt/protocol (get p :jolt/type)) "protocol type tag")
|
||||
(assert (get (get p :methods) :greet) "protocol has greet method"))
|
||||
(assert (or (function? (ct-eval ctx "greet")) (cfunction? (ct-eval ctx "greet"))) "method fn exists"))
|
||||
(print " passed")
|
||||
|
||||
(print "36: extend-type...")
|
||||
(let [ctx (init)]
|
||||
(ct-eval ctx "(deftype Person [name])")
|
||||
(ct-eval ctx "(defprotocol Namable (get-name [this]))")
|
||||
(ct-eval ctx "(extend-type Person Namable (get-name [this] (.-name this)))")
|
||||
(assert (= "Alice" (ct-eval ctx "(get-name (Person. \"Alice\"))")) "extend-type works"))
|
||||
(print " passed")
|
||||
|
||||
(print "37: extend-protocol...")
|
||||
(let [ctx (init)]
|
||||
(ct-eval ctx "(deftype Dog [breed])")
|
||||
(ct-eval ctx "(deftype Cat [color])")
|
||||
(ct-eval ctx "(defprotocol Animal (speak [this]))")
|
||||
(ct-eval ctx "(extend-protocol Animal
|
||||
Dog (speak [this] (str \"woof from \" (.-breed this)))
|
||||
Cat (speak [this] (str \"meow from \" (.-color this))))")
|
||||
(assert (= "woof from poodle" (ct-eval ctx "(speak (Dog. \"poodle\"))")) "dog speak")
|
||||
(assert (= "meow from black" (ct-eval ctx "(speak (Cat. \"black\"))")) "cat speak"))
|
||||
(print " passed")
|
||||
|
||||
(print "38: satisfies?...")
|
||||
(let [ctx (init)]
|
||||
(ct-eval ctx "(deftype Point [x y])")
|
||||
(ct-eval ctx "(defprotocol Locatable (location [this]))")
|
||||
(ct-eval ctx "(extend-type Point Locatable (location [this] [(.-x this) (.-y this)]))")
|
||||
(assert (= true (ct-eval ctx "(satisfies? Locatable (Point. 3 4))")) "satisfies? true")
|
||||
(assert (= false (ct-eval ctx "(satisfies? Locatable {:x 1})")) "satisfies? false"))
|
||||
(print " passed")
|
||||
|
||||
(print "39: reify...")
|
||||
(let [ctx (init)]
|
||||
(ct-eval ctx "(defprotocol Stringable (to-str [this]))")
|
||||
(assert (= "works" (ct-eval ctx "(to-str (reify Stringable (to-str [this] \"works\")))")) "reify single method"))
|
||||
(print " passed")
|
||||
|
||||
(print "\nAll Phase 12 tests passed!")
|
||||
Loading…
Add table
Add a link
Reference in a new issue