This commit is contained in:
msyds committed 2026-09-20 02:35:55 -06:00
1 parent 2f6160f178
commit 35bbd7ffef
2 files changed
+73 -35

No files matched your search

+53 -29
View File
@@ -3,7 +3,9 @@
[clojure.core.match :refer [match]]
[spec-dict.main :refer [dict]]
[clojure.set :as set]
[clojure.pprint :as pprint]))
[clojure.pprint :as pprint]
[clojure.string :as str]
[clojure.walk]))
(defmacro todo
([] `(throw (ex-info "todo" {})))
@@ -99,7 +101,25 @@
(s/def ::body
(s/+ any?))
(defrecord Variable [symbol scopes])
(declare destructure-variable)
(defprotocol IVariable
(variable-symbol [this])
(variable-scopes [this]))
(deftype Variable [symbol scopes]
Object
(equals [x y]
(tap> y)
(if-some [[y-symbol y-scopes] (destructure-variable y)]
(do (tap> [symbol y-symbol])
(tap> [scopes y-scopes])
(and (.equals symbol y-symbol)
(.equals scopes y-scopes)))
false))
IVariable
(variable-symbol [this] symbol)
(variable-scopes [this] scopes))
(def ^:dynamic *print-variable-scopes* true)
@@ -107,14 +127,14 @@
(defmethod print-method Variable [this writer]
(binding [*out* writer]
(if *print-variable-scopes*
(pr [(:symbol this) (:scopes this)])
(pr (:symbol this)))))
(pr [(variable-symbol this) (variable-scopes this)])
(pr (variable-symbol this)))))
(defmethod clojure.pprint/simple-dispatch expand.Variable [x]
(clojure.pprint/simple-dispatch
(if *print-variable-scopes*
[(:symbol x) (:scopes x)]
(:symbol x))))
[(variable-symbol x) (variable-scopes x)]
(variable-symbol x))))
(s/def ::variable
(s/conformer
@@ -122,16 +142,16 @@
(match x
(_ :guard symbol?) (->Variable x nil)
(_ :guard #(instance? Variable %))
(if (symbol? (:symbol x))
(let [r (s/conform ::scope-set (:scopes x))]
(if (symbol? (variable-symbol x))
(let [r (s/conform ::scope-set (variable-scopes x))]
(if (s/invalid? r)
::s/invalid
(->Variable (:symbol x) (:scopes x))))
(->Variable (variable-symbol x) (variable-scopes x))))
::s/invalid)
[sym scopes] (let [r (s/conform (s/nilable ::scope-set) scopes)]
(if (or (not (symbol? sym)) (s/invalid? r))
::s/invalid
(->Variable sym r)))
(if (and (symbol? sym) (not (s/invalid? r)))
(->Variable sym r)
::s/invalid))
:else ::s/invalid))))
(defn variable? [x]
@@ -145,12 +165,14 @@
(defn destructure-variable [v]
(let [r (s/conform ::variable v)]
(when-not (s/invalid? r)
[(:symbol r) (:scopes r)])))
[(variable-symbol r) (variable-scopes r)])))
#_
(defn variable-scopes [v]
(when-some [[_ scopes] (destructure-variable v)]
scopes))
#_
(defn variable-symbol [v]
(when-some [[s _] (destructure-variable v)]
s))
@@ -384,7 +406,7 @@
[:var] symtable-ref)))]
(concat (list 'let
(for [[k v] bindings]
(list (emit-variable (offer-scopes k scopes*)) v)))
(list (offer-scopes k scopes*) v)))
(for [e body]
(expand e env* symtable-ref scopes*)))))
@@ -469,25 +491,27 @@
(defn beautify
(defn regensym
"Renumber the gensyms created by `new-scope` found in datum,
starting from zero."
[datum]
(let [c (atom 0)]
(walk/postwalk (fn [x]
(if (and (symbol? x)
(clojure.string/starts-with?
(name x)
"scope__"))
(symbol (str "s" (first (swap-vals! c inc))))
x))
datum)))
(let [c (atom {})]
(clojure.walk/postwalk
(fn [x]
(if (and (symbol? x) (str/starts-with? (name x) "scope__"))
(let [syms (swap! c #(if (contains? % x)
%
(assoc % x (symbol (str "s" (count %))))))]
(get syms x))
x))
datum)))
(defn unscope
"Remove all scopes from the variables found in datum."
[datum]
(walk/postwalk (fn [x]
(if (instance? Variable x)
(variable-symbol x)
x))
datum))
(clojure.walk/postwalk
(fn [x]
(if (instance? Variable x)
(variable-symbol x)
x))
datum))
+20 -6
View File
@@ -46,6 +46,9 @@
(defn- us-expand [datum]
(sut/unscope (sut/expand datum)))
(defn- rg-expand [datum]
(sut/regensym (sut/expand datum)))
(t/deftest expand-test
(t/testing "unbound symbol"
(t/is (thrown? Exception (sut/expand 'x))))
@@ -53,11 +56,11 @@
(t/is (= '(lambda (x) x)
(us-expand '(λ (x) x)))))
#_(t/testing "syntax-rules"
(t/is (= '(lambda (x) x)
(sut/expand
'(syntax-rules
()
((_)))))))
(t/is (= '(lambda (x) x)
(sut/expand
'(syntax-rules
()
((_)))))))
(t/testing "let"
(t/is (= '(let ((x 123)
(y 456))
@@ -68,7 +71,18 @@
(+ x y))))))
(t/testing "application"
(t/is (= '(lambda (f x) (f x))
(us-expand '(lambda (f x) (f x)))))))
(us-expand '(lambda (f x) (f x))))))
(t/testing "macros"
(t/testing "when"
(t/is (= '(let ((x 123)) (if x 456))
(us-expand '(let ((x 123))
(when x 456)))))
(t/is (= '(let (([body #{s0}] 123))
(if [body #{s0}]
456))
(rg-expand '(let ((body 123))
(when body 456))))
"`when` is hygienic"))))
(t/deftest prim-lambda-spec
(t/is (s/valid? ::sut/prim-lambda '(lambda (x) x)))