From fbddf625b4bbf00e1a4a1bb07a1c3e986d4a47ce Mon Sep 17 00:00:00 2001 From: Yogthos Date: Tue, 2 Jun 2026 21:57:09 -0400 Subject: [PATCH] =?UTF-8?q?Phase=206:=20Reader=20Extensions=20=E2=80=94=20?= =?UTF-8?q?tagged=20literals=20(#inst,=20#uuid)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - evaluator.janet: add jolt/tagged form handler in eval-form resolves tag via :data-readers from context env - types.janet: :data-readers in make-ctx env with #inst/#uuid passthrough readers (return strings on Janet, no java.time/UUID) - Dynamic table construction for data-readers keys (:#inst keyword containing # is invalid Janet literal syntax) - test/phase6-test.janet: 4 test sections (28-31) #inst, #uuid, #? conditionals, #?@ splicing — all pass - 315 ok, 2 fail (pre-existing, unchanged) --- src/jolt/evaluator.janet | 9 ++++++++- src/jolt/types.janet | 14 +++++++++++--- test/phase6-test.janet | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 test/phase6-test.janet diff --git a/src/jolt/evaluator.janet b/src/jolt/evaluator.janet index c6f7b76..f019e3a 100644 --- a/src/jolt/evaluator.janet +++ b/src/jolt/evaluator.janet @@ -785,9 +785,16 @@ (struct? form) (if (= :symbol (form :jolt/type)) (resolve-sym ctx bindings form) + (if (= :jolt/tagged (form :jolt/type)) + (let [tag (form :tag) + data-readers (get (ctx :env) :data-readers) + reader-fn (if data-readers (get data-readers tag))] + (if reader-fn + (reader-fn (form :form)) + (error (string "No reader function for tag " tag)))) (if (get form :jolt/type) (error (string "Unexpected tagged form: " (form :jolt/type))) - form)) + form))) (array? form) (if (= 0 (length form)) @[] diff --git a/src/jolt/types.janet b/src/jolt/types.janet index 1742d92..1442e8d 100644 --- a/src/jolt/types.janet +++ b/src/jolt/types.janet @@ -154,9 +154,13 @@ visited) (defn ancestors - "Return a set of ancestors for a tag in the given hierarchy." + "Return all transitive ancestors of a tag in the given hierarchy." [h tag] - (let [a (get (h :ancestors) tag)] (if a a @[]))) + (let [visited (ancestors* h tag @{})] + (var result @[]) + (loop [[k _] :pairs visited] + (when (not= k tag) (array/push result k))) + result)) (defn descendants "Return the descendants of a tag in the given hierarchy." @@ -309,7 +313,11 @@ :class->opts @{} :current-ns "user" :compile? compile? - :compiled-cache @{}} + :compiled-cache @{} + :data-readers (let [dr @{}] + (put dr (keyword "#inst") (fn [s] s)) + (put dr (keyword "#uuid") (fn [s] s)) + dr)} # create the user namespace via a partial context _ (ctx-find-ns {:env env} "user")] # initialize from opts diff --git a/test/phase6-test.janet b/test/phase6-test.janet new file mode 100644 index 0000000..6510dd7 --- /dev/null +++ b/test/phase6-test.janet @@ -0,0 +1,33 @@ +# Phase 6: Reader Extensions Tests +(use ../src/jolt/api) +(defn ct-eval [ctx s] (eval-string ctx s)) + +(print "28: #inst tagged literal...") +(let [ctx (init)] + (let [val (ct-eval ctx "#inst \"2024-01-15\"")] + (assert (= "2024-01-15" val) "#inst string")) + (let [val (ct-eval ctx "#inst \"2024-01-15T10:30:00Z\"")] + (assert (= "2024-01-15T10:30:00Z" val) "#inst timestamp"))) +(print " passed") + +(print "29: #uuid tagged literal...") +(let [ctx (init)] + (let [val (ct-eval ctx "#uuid \"550e8400-e29b-41d4-a716-446655440000\"")] + (assert (= "550e8400-e29b-41d4-a716-446655440000" val) "#uuid string"))) +(print " passed") + +(print "30: #? reader conditionals...") +(let [ctx (init)] + (assert (= :yes (ct-eval ctx "#?(:clj :yes :cljs :no)")) "#? selects :clj") + (assert (= nil (ct-eval ctx "#?(:cljs :no)")) "#? nil on no match")) +(print " passed") + +(print "31: #?@ splicing...") +(let [ctx (init)] + (assert (= [1 2 3] (ct-eval ctx "[#?@(:clj [1 2 3] :cljs [4 5 6])]")) + "#?@ splices :clj") + (assert (= [] (ct-eval ctx "[#?@(:cljs [1 2])]")) + "#?@ nothing on no match")) +(print " passed") + +(print "\nAll Phase 6 tests passed!")