Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

[Squint](https://github.com/squint-cljs/squint): Light-weight ClojureScript dialect

## Unreleased

- Fix [#645](https://github.com/squint-cljs/squint/issues/645): Implement a `binding` macro

## v0.8.142 (2025-03-19)

- Fix [#640](https://github.com/squint-cljs/squint/issues/640): don't emit anonymous function if it is a statement ([@jonasseglare](https://github.com/jonasseglare))
Expand Down
1 change: 1 addition & 0 deletions src/squint/compiler.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
'or macros/core-or
'and macros/core-and
'assert macros/core-assert
'binding macros/core-binding
'simple-benchmark macros/simple-benchmark
'delay macros/delay}
cc/common-macros))
Expand Down
12 changes: 12 additions & 0 deletions src/squint/internal/macros.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,18 @@
(throw (js/Error.
(cljs.core/str "Assert failed: " ~message "\n" ~(pr-str x)))))))

(defn core-binding [_ _ bindings & body]
(let [binding-triplets (mapv cons
(repeatedly gensym)
(partition 2 bindings))]
`(let ~(into [] (mapcat #(take 2 %)) binding-triplets)
(try
~@(map (fn [[_ var expr]] `(set! ~var ~expr)) binding-triplets)
(do ~@body)
(finally
~@(map (fn [[previous var _]] `(set! ~var ~previous))
binding-triplets))))))

(defn coercive-=
[_ _ x y]
(bool-expr (list 'js* "(~{} == ~{})" x y)))
Expand Down
31 changes: 31 additions & 0 deletions test/squint/compiler_test.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -1844,6 +1844,37 @@
(deftest override-core-var-test
(is (= 1 (jsv! "(def count 1) (set! count (inc count)) (defn frequencies [x] (dec x)) (frequencies count)"))))

(deftest binding-test
(is (= 4 (jsv! "(def ^:dynamic a 3) (binding [a 4] a)")))
(is (= 3 (jsv! "(def ^:dynamic a 3) (binding [a 4] a) a")))
(is (eq #js [#js [3 4] #js [100 10] #js [3 4]] (jsv! "(def ^:dynamic a 3) (def ^:dynamic b 4) [[a b] (binding [a 100 b 10] [a b]) [a b]]")))
(is (eq #js [500 119] (jsv! "(def ^:dynamic x 119) (def y (try (binding [x 120] (throw (js/Error \"Failed.\")) x) (catch js/Error _ 500))) [y x]")))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I included ^:dynamic here even if it is not needed to make the code look like Clojure. But maybe I should simply remove it.

(is (nil? (jsv! "(def ^:dynamic x 119) (binding [x 120])")))
(is (= 1120 (jsv! "(def ^:dynamic x 119) (binding [x 120] (+ x 100) (+ x 200) (+ x 1000))")))
(is (eq #js [17 1120 51 119] (jsv! "(def ^:dynamic x 119) (def y 17) [y (binding [x 120] (set! y 51) (+ x 1000)) y x]")))
(is (eq #js [3 4] (jsv! "(def ^:dynamic x 3) (def ^:dynamic y 4) (try (binding [x 100 y (do (throw (js/Error \"Failed.\")) 130)] (+ x y)) (catch js/Error _ nil)) [x y]"))))

(deftest async-binding-test
(async done
(->
(.then (jsv! '(do (def ^:dynamic x 119)

(defn ^:async foo []
(js-await (js/Promise.resolve 10000)))

(defn ^:async bar []
[x
(js-await (binding [x 120]
(+ x (js-await (foo)))))
x])

(bar)))
(fn [v]
(is (eq #js [119 10120 119] v))))
(.catch (fn [err]
(is false (.-message err))))
(.finally #(done)))))

(deftest lazy-seq-test
(is (eq #js [1] (jsv! "(vec (cons 1 (lazy-seq nil)))")))
(is (eq #js [1 2] (jsv! "(vec (cons 1 (lazy-seq (cons 2 nil))))")))
Expand Down