From 36ca99b64afb5d52fc82fc4ce4a6a0918ad95ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Madeleine=20Sydney=20=C5=9Alaga?= Date: Thu, 17 Sep 2026 10:20:59 -0600 Subject: [PATCH] unifyish --- src/expand.clj | 87 +++++++++++++++++++++++++++----------------- test/expand_test.clj | 12 +++++- 2 files changed, 63 insertions(+), 36 deletions(-) diff --git a/src/expand.clj b/src/expand.clj index 27c93c2..742dad6 100644 --- a/src/expand.clj +++ b/src/expand.clj @@ -16,19 +16,9 @@ [:lexical sym uniq] (and (symbol? sym) (symbol? uniq)) [:global sym] (symbol? sym))) -(defn- parse-pattern-list [pat] - (loop [xs pat - acc {:type :list :leading []}] - (match xs - ([x '... & ys] :seq) (-> acc - (assoc :ellipsis x :trailing (seq ys)) - (update :leading seq)) - ([] :seq) (update acc :leading seq) - ([x & ys] :seq) (recur ys (update acc :leading #(conj % x)))))) - (s/def ::pattern (s/or :var (s/and symbol? - #(not= '... %)) + (comp not #{'... '_})) :lit ::literal :list (s/and seq? (s/cat :leading (s/* ::pattern) @@ -43,16 +33,7 @@ :ellipsis variadic :trailing trailing} ::s/invalid)) - pat))))) - #_(s/conformer - (fn go [pat] - (cond (seq? pat) - (-> (parse-pattern-list pat) - (update :leading #(map go %)) - (update :ellipsis #(some-> % go)) - (update :trailing #(map go %))) - (symbol? pat) pat - :else ::s/invalid)))) + pat)))))) (s/def ::literal (some-fn string? boolean? number? char?)) @@ -110,25 +91,63 @@ (s/def ::prim-let-syntax ::prim-let) (s/def ::prim-syntax-rules - (s/cat :keyword symbol + (s/cat :keyword symbol? :keywords (s/coll-of symbol?) :rules (s/* ::rule))) -(defn parse-pattern [pat] - (cond (seq? pat) - (loop [xs pat - acc {:type :list :leading []}] - (match xs - ([x '... & ys] :seq) (-> acc - (assoc :ellipsis x :trailing (seq ys)) - (update :leading seq)) - ([] :seq) (update acc :leading seq) - ([x & ys] :seq) (recur ys (update acc :leading #(conj % x))))) - (symbol? pat) pat)) - +(defn- merge-unify-with + ([f] {}) + ([f a] a) + ([f a b] (and a b (merge-with f a b))) + ([f a b & xs] (reduce #(merge-unify-with f %1 %2) (cons a (cons b xs))))) +(defn- merge-unify [& xs] + (apply merge-unify-with (fn [_ x] x) xs)) + +(defn traverse [f xs] + (let [xs* (map f xs)] + (if (contains? xs* nil) + nil + xs*))) + +(defn unify [pattern datum] + (unify* (s/conform ::pattern pattern) datum)) + +(defn- unify* [pattern datum] + (match [pattern datum] + [[:var x] _] + {x datum} + + [[:list {:leading leading + :trailing nil + :ellipsis ellipsis}] + (_ :guard seq?)] + (let [[leading-datum ellipsis-datum] (split-at (count leading) datum)] + (merge-unify + (apply merge-unify (map #(unify* %1 %2) + leading + leading-datum)) + (some->> ellipsis-datum + (map #(when-some [r (unify* ellipsis %)] + (->> r + (map (fn [[k v]] [k (list v)])) + (into {})))) + (apply merge-unify-with concat) + (map (fn [[k v]] + [k [:ellipsis v]])) + (into {})))) + + [[:list {:leading leading}] + (_ :guard seq?)] + (and (= (count datum) + (count leading)) + (->> (map #(unify* depth %1 %2) leading datum) + (apply merge-unify))) + + ;; no match + :else nil)) diff --git a/test/expand_test.clj b/test/expand_test.clj index 3835b20..5cd0a18 100644 --- a/test/expand_test.clj +++ b/test/expand_test.clj @@ -28,9 +28,12 @@ '(a b) nil nil '(a b) '(a) 'b nil '(a b ...) '(a) 'b '(c) '(a b ... c) - '(a) 'b '(c d) '(a b ... c d))) + '(a) 'b '(c d) '(a b ... c d)) + (t/are [p] (s/invalid? ::pattern p) + '... '(...) '(... a b c) + '(a ... b ...))) (t/testing "simple data" - (t/is (= 'a (sut/parse-pattern 'a))))) + (t/is (= 'a (sut/conform ::patern 'a))))) (t/deftest expand-test (t/testing "unbound symbol" @@ -81,3 +84,8 @@ '(syntax-rules () ((m) 123))))) + +(t/deftest unify-test + (t/are [result pattern datum] (= result (sut/unify pattern datum)) + {'a 123 'b 456} '(a b) '(123 456) + nil '(a b) 123))