diff --git a/.beads/interactions.jsonl b/.beads/interactions.jsonl index e136c2b..c9d1362 100644 --- a/.beads/interactions.jsonl +++ b/.beads/interactions.jsonl @@ -15,3 +15,4 @@ {"id":"int-5ddb241f","kind":"field_change","created_at":"2026-06-04T18:57:27.904474Z","actor":"Yogthos","issue_id":"jolt-7dy","extra":{"field":"status","new_value":"deferred","old_value":"open"}} {"id":"int-9a5516da","kind":"field_change","created_at":"2026-06-04T19:41:39.048731Z","actor":"Yogthos","issue_id":"jolt-4zg","extra":{"field":"status","new_value":"closed","old_value":"open"}} {"id":"int-b700eb9e","kind":"field_change","created_at":"2026-06-04T19:48:03.780728Z","actor":"Yogthos","issue_id":"jolt-l8f","extra":{"field":"status","new_value":"closed","old_value":"open"}} +{"id":"int-700a80a8","kind":"field_change","created_at":"2026-06-04T19:56:36.343102Z","actor":"Yogthos","issue_id":"jolt-7dy","extra":{"field":"status","new_value":"closed","old_value":"deferred"}} diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 985165b..af06da4 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -9,7 +9,7 @@ {"_type":"issue","id":"jolt-40n","title":"HIGH: update/update-in/assoc-in fail on map literals (struct)","status":"closed","priority":2,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:09:56Z","created_by":"Yogthos","updated_at":"2026-06-04T18:40:42Z","closed_at":"2026-06-04T18:40:42Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-ytt","title":"HIGH: str semantics — (str nil)='nil' should be '', (str :b)='b' should be ':b'","status":"closed","priority":2,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:09:56Z","created_by":"Yogthos","updated_at":"2026-06-04T18:33:45Z","closed_at":"2026-06-04T18:33:45Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-9sb","title":"HIGH: destructuring gaps (nested seq, :strs, nested map, fn-param)","status":"closed","priority":2,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:09:55Z","created_by":"Yogthos","updated_at":"2026-06-04T18:31:24Z","closed_at":"2026-06-04T18:31:24Z","dependency_count":0,"dependent_count":0,"comment_count":0} -{"_type":"issue","id":"jolt-7dy","title":"MED: compiler path lacks IFn dispatch (collections-as-fn) — only interpreter fixed","status":"deferred","priority":3,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:10:00Z","created_by":"Yogthos","updated_at":"2026-06-04T18:57:28Z","dependency_count":0,"dependent_count":0,"comment_count":0} +{"_type":"issue","id":"jolt-7dy","title":"MED: compiler path lacks IFn dispatch (collections-as-fn) — only interpreter fixed","status":"closed","priority":3,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:10:00Z","created_by":"Yogthos","updated_at":"2026-06-04T19:56:36Z","closed_at":"2026-06-04T19:56:36Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-90c","title":"MED: filter/take-while not lazy — infinite-seq (take N (filter p (range))) loops","status":"closed","priority":3,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:10:00Z","created_by":"Yogthos","updated_at":"2026-06-04T18:56:26Z","closed_at":"2026-06-04T18:56:26Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-7n5","title":"MED: REPL display — def/defn should print #'ns/name; lazy-seq printer realizes only head ((0 \u003cfunction\u003e))","status":"closed","priority":3,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:09:59Z","created_by":"Yogthos","updated_at":"2026-06-04T18:55:11Z","closed_at":"2026-06-04T18:55:11Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-h0n","title":"MED: missing core fns — letfn fnil trampoline doseq cycle partition-all reductions dedupe keep-indexed map-indexed transduce reduce-kv assoc-in peek subvec read-string format","status":"closed","priority":3,"issue_type":"feature","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:09:58Z","created_by":"Yogthos","updated_at":"2026-06-04T18:57:26Z","closed_at":"2026-06-04T18:57:26Z","dependency_count":0,"dependent_count":0,"comment_count":0} diff --git a/src/jolt/compiler.janet b/src/jolt/compiler.janet index 72e75df..b56ca08 100644 --- a/src/jolt/compiler.janet +++ b/src/jolt/compiler.janet @@ -6,6 +6,7 @@ (use ./types) (use ./core) +(use ./phm) (def- core-renames @{"+" "core-+" @@ -504,7 +505,12 @@ {:op :vector :items items}) (struct? form) - {:op :map :form form} + (cond + (= :jolt/set (form :jolt/type)) + {:op :set :items (map |(analyze-form $ bindings ctx) (form :value))} + (= :jolt/char (form :jolt/type)) + {:op :const :val form} + {:op :map :form form}) {:op :const :val form})) @@ -642,6 +648,11 @@ (defn- emit-map-str [form buf] (buffer/push buf (string form))) +(defn- emit-set-str [items buf] + (buffer/push buf "(make-phs") + (each it items (buffer/push buf " ") (emit-ast it buf)) + (buffer/push buf ")")) + (defn- raw-form->janet "Convert a Jolt reader form to a Janet data structure for quoting." [form] @@ -689,6 +700,7 @@ :invoke (emit-invoke-str (ast :fn) (ast :args) buf) :vector (emit-vector-str (ast :items) buf) :map (emit-map-str (ast :form) buf) + :set (emit-set-str (ast :items) buf) :quote (emit-quote-str (ast :expr) buf) (buffer/push buf (string "/* unhandled op: " (ast :op) " */"))))) @@ -779,7 +791,9 @@ (tuple/slice (tuple ;exprs))) (defn- emit-invoke-expr [f-ast args] - (def exprs @[(emit-expr f-ast)]) + # Embed the jolt-call function value directly (like core symbols) so compiled + # invocations dispatch real fns AND IFn collections at runtime. + (def exprs @[jolt-call (emit-expr f-ast)]) (each arg args (array/push exprs (emit-expr arg))) (tuple/slice (tuple ;exprs))) @@ -790,6 +804,9 @@ (defn- emit-map-expr [form] form) +(defn- emit-set-expr [items] + (tuple/slice (tuple make-phs ;(map emit-expr items)))) + (defn- emit-quote-expr [expr] ['quote (raw-form->janet expr)]) @@ -813,6 +830,7 @@ :invoke (emit-invoke-expr (ast :fn) (ast :args)) :vector (emit-vector-expr (ast :items)) :map (emit-map-expr (ast :form)) + :set (emit-set-expr (ast :items)) :quote (emit-quote-expr (ast :expr)) (error (string "Unhandled op: " (ast :op)))))) diff --git a/src/jolt/core.janet b/src/jolt/core.janet index 95e5e0a..85ba457 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -255,6 +255,25 @@ (in m k) default)))))) +# Runtime invoke dispatch for COMPILED code (interpreter uses evaluator's +# jolt-invoke). Handles real functions plus Clojure IFn collections. +(defn jolt-call [f & args] + (cond + (or (function? f) (cfunction? f)) (apply f args) + (keyword? f) (core-get (get args 0) f (get args 1)) + (and (struct? f) (= :symbol (f :jolt/type))) (core-get (get args 0) f (get args 1)) + (phm? f) (phm-get f (get args 0) (get args 1)) + (set? f) (if (phs-contains? f (get args 0)) (get args 0) (get args 1)) + (or (tuple? f) (array? f)) + (let [k (get args 0)] + (if (and (number? k) (= k (math/floor k)) (>= k 0) (< k (length f))) + (in f k) + (error (string "Index " k " out of bounds for vector of length " (length f))))) + (or (struct? f) (and (table? f) (get f :jolt/deftype))) + (let [v (get f (get args 0) :jolt/not-found)] + (if (= v :jolt/not-found) (get args 1) v)) + (error (string "Cannot call " (type f) " as a function")))) + (defn core-get-in [m ks &opt default] (default default nil) (var current m)