diff --git a/src/jolt/clojure/set.clj b/src/jolt/clojure/set.clj index 82b5d8a..f480754 100644 --- a/src/jolt/clojure/set.clj +++ b/src/jolt/clojure/set.clj @@ -40,6 +40,33 @@ [xrel ks] (reduce (fn [m x] (let [ik (select-keys x ks)] (assoc m ik (conj (get m ik #{}) x)))) {} xrel)) +(defn join + "When passed 2 rels, returns the rel corresponding to the natural join. + When passed an additional keymap, joins on the corresponding keys." + ([xrel yrel] + (if (and (seq xrel) (seq yrel)) + (let [ks (intersection (set (keys (first xrel))) (set (keys (first yrel)))) + [r s] (if (<= (count xrel) (count yrel)) [xrel yrel] [yrel xrel]) + idx (index r ks)] + (reduce (fn [ret x] + (let [found (idx (select-keys x ks))] + (if found + (reduce (fn [acc y] (conj acc (merge y x))) ret found) + ret))) + #{} s)) + #{})) + ([xrel yrel km] + (let [[r s k] (if (<= (count xrel) (count yrel)) + [xrel yrel (map-invert km)] + [yrel xrel km]) + idx (index r (vals k))] + (reduce (fn [ret x] + (let [found (idx (rename-keys (select-keys x (keys k)) k))] + (if found + (reduce (fn [acc y] (conj acc (merge y x))) ret found) + ret))) + #{} s)))) + (defn subset? [set1 set2] (and (<= (count set1) (count set2)) diff --git a/src/jolt/core.janet b/src/jolt/core.janet index 6694839..843f952 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -494,7 +494,7 @@ (defn core-select-keys [m ks] (var result @{}) - (each k ks + (each k (realize-for-iteration ks) (let [v (core-get m k)] (if (not (nil? v)) (put result k v)))) (if (struct? m) (table/to-struct result) result)) @@ -2237,15 +2237,21 @@ (put prefs dispatch-val-a dispatch-val-b) mm-var)) (defn core-with-meta [obj meta] - (var new-obj @{}) - (each k (keys obj) - (put new-obj k (get obj k))) - # table/setproto requires a table, convert struct meta to table - (var meta-tab @{}) - (each k (keys meta) (put meta-tab k (get meta k))) - (table/setproto new-obj meta-tab) - (put new-obj :jolt/meta meta) - new-obj) + # Functions and scalars can't carry metadata in Jolt's model — return as-is + # rather than crashing (Clojure attaches meta only to IObj values). + (if (or (function? obj) (cfunction? obj) (number? obj) (boolean? obj) + (nil? obj) (string? obj) (keyword? obj) (buffer? obj)) + obj + (do + (var new-obj @{}) + (each k (keys obj) + (put new-obj k (get obj k))) + # table/setproto requires a table, convert struct meta to table + (var meta-tab @{}) + (each k (keys meta) (put meta-tab k (get meta k))) + (table/setproto new-obj meta-tab) + (put new-obj :jolt/meta meta) + new-obj))) (defn core-var-dynamic? [v] (var-dynamic? v)) diff --git a/test/test-load-sci.janet b/test/test-load-sci.janet index bdecc20..35080e8 100644 --- a/test/test-load-sci.janet +++ b/test/test-load-sci.janet @@ -42,8 +42,9 @@ (flush) (if (try (do (eval-form ctx @{} form) true) - ([err] + ([err fib] (printf " FAIL: %q\n" err) + (when (os/getenv "SCI_TRACE") (debug/stacktrace fib "")) (array/push failures {:form-number count :error (string err) :form (string form)}) false)) (do @@ -104,3 +105,8 @@ (each f all-failures (printf "[%s:%d] %s\n" (f :file) (f :form-number) (f :error)) (printf " form: %s\n" (f :form)))) + +# Regression guard: every form in the loaded SCI modules must evaluate cleanly. +(assert (= 0 total-fail) + (string total-fail " SCI form(s) failed to load (see FAILURES above)")) +(print "\nAll SCI bootstrap forms loaded successfully.")