From f7747ed1b46b76b387b0c8d051aff39c39685a91 Mon Sep 17 00:00:00 2001 From: Yogthos Date: Wed, 3 Jun 2026 00:33:41 -0400 Subject: [PATCH] =?UTF-8?q?Phase=2012:=20Protocol=20System=20=E2=80=94=20f?= =?UTF-8?q?ull=20implementation=20with=20all=20tests=20passing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .dirge/skills/.usage.json | 70 +++++++++++++++++++-------------------- src/jolt/core.janet | 45 +++++++++++++++---------- src/jolt/evaluator.janet | 8 +++-- test/phase12-test.janet | 50 ++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 55 deletions(-) create mode 100644 test/phase12-test.janet diff --git a/.dirge/skills/.usage.json b/.dirge/skills/.usage.json index b211ce5..cc5123c 100644 --- a/.dirge/skills/.usage.json +++ b/.dirge/skills/.usage.json @@ -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 } } \ No newline at end of file diff --git a/src/jolt/core.janet b/src/jolt/core.janet index 26ebf48..6824b7f 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -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)) diff --git a/src/jolt/evaluator.janet b/src/jolt/evaluator.janet index c51d6b0..9fd8f3b 100644 --- a/src/jolt/evaluator.janet +++ b/src/jolt/evaluator.janet @@ -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) diff --git a/test/phase12-test.janet b/test/phase12-test.janet new file mode 100644 index 0000000..5bf93ee --- /dev/null +++ b/test/phase12-test.janet @@ -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!")