Skip to content

Commit 929579f

Browse files
committed
wip
1 parent 05fc0c4 commit 929579f

File tree

2 files changed

+109
-53
lines changed

2 files changed

+109
-53
lines changed

src/sci/impl/async_macro.cljc

Lines changed: 54 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,12 @@
9292
(if (seq acc)
9393
;; Have accumulated expressions before promise - mark the do as promise-producing
9494
(let [then-expr (if rest-body
95-
(promise-then transformed (list 'fn ['_] rest-body))
95+
(promise-then transformed (list 'fn* ['_] rest-body))
9696
transformed)]
9797
(mark-promise (list* 'do (conj acc then-expr))))
9898
;; No accumulated expressions
9999
(if rest-body
100-
(promise-then transformed (list 'fn ['_] rest-body))
100+
(promise-then transformed (list 'fn* ['_] rest-body))
101101
transformed)))
102102
;; No promise, accumulate
103103
(recur rest-exprs (conj acc transformed))))
@@ -150,7 +150,7 @@
150150
transformed-body
151151
(wrap-promise transformed-body))
152152
;; Build the loop function
153-
loop-fn (list 'fn loop-fn-name param-names promised-body)
153+
loop-fn (list 'fn* loop-fn-name param-names promised-body)
154154
;; Immediately invoke with initial values
155155
loop-call (apply list loop-fn init-values)]
156156
;; Wrap in js/Promise.resolve so it's recognized as promise-producing
@@ -175,9 +175,9 @@
175175
(if (seq acc-bindings)
176176
(list 'let* (vec acc-bindings)
177177
(promise-then transformed-init
178-
(list 'fn [binding-name] rest-body)))
178+
(list 'fn* [binding-name] rest-body)))
179179
(promise-then transformed-init
180-
(list 'fn [binding-name] rest-body))))
180+
(list 'fn* [binding-name] rest-body))))
181181
;; No await in this binding - accumulate and continue
182182
(recur (rest pairs)
183183
(conj acc-bindings binding-name transformed-init)
@@ -213,25 +213,25 @@
213213
(wrap-promise transformed-body))
214214
;; Add .catch clauses
215215
with-catch (reduce
216-
(fn [chain catch-clause]
217-
;; (catch Type e handler-body...)
218-
(let [[_ _type binding & handler-body] catch-clause
219-
;; Add catch binding to locals for handler
220-
handler-locals (conj locals binding)
221-
transformed-handler (if (= 1 (count handler-body))
222-
(transform-async-body ctx handler-locals (first handler-body))
223-
(transform-do ctx handler-locals handler-body))]
224-
(promise-catch chain (list 'fn [binding] transformed-handler))))
225-
promise-chain
226-
catch-clauses)
227-
;; Add .finally if present
228-
with-finally (if finally-clause
229-
(let [[_ & finally-body] finally-clause
230-
transformed-finally (if (= 1 (count finally-body))
231-
(transform-async-body ctx locals (first finally-body))
232-
(transform-do ctx locals finally-body))]
233-
(promise-finally with-catch (list 'fn [] transformed-finally)))
234-
with-catch)]
216+
(fn [chain catch-clause]
217+
;; (catch Type e handler-body...)
218+
(let [[_ _type binding & handler-body] catch-clause
219+
;; Add catch binding to locals for handler
220+
handler-locals (conj locals binding)
221+
transformed-handler (if (= 1 (count handler-body))
222+
(transform-async-body ctx handler-locals (first handler-body))
223+
(transform-do ctx handler-locals handler-body))]
224+
(promise-catch chain (list 'fn* [binding] transformed-handler))))
225+
promise-chain
226+
catch-clauses)
227+
;; Add .finally if present
228+
with-finally (if finally-clause
229+
(let [[_ & finally-body] finally-clause
230+
transformed-finally (if (= 1 (count finally-body))
231+
(transform-async-body ctx locals (first finally-body))
232+
(transform-do ctx locals finally-body))]
233+
(promise-finally with-catch (list 'fn* [] transformed-finally)))
234+
with-catch)]
235235
with-finally))
236236

237237
(defn- transform-coll-with-await
@@ -240,23 +240,24 @@
240240
[ctx locals elements rebuild-fn]
241241
(let [transformed (doall (map #(transform-async-body ctx locals %) elements))]
242242
(if (some promise-form? transformed)
243-
;; Chain promises sequentially
244-
(loop [elems-before []
245-
remaining transformed]
246-
(if (seq remaining)
247-
(let [elem (first remaining)]
248-
(if (promise-form? elem)
249-
;; Found promise - chain from here
250-
(let [await-sym (gensym "await__")
251-
rest-elems (rest remaining)
252-
rebuilt (rebuild-fn (concat elems-before [await-sym] rest-elems))
253-
;; Recursively transform in case there are more promises
254-
rest-expr (transform-async-body ctx (conj locals await-sym) rebuilt)]
255-
(promise-then elem (list 'fn [await-sym] rest-expr)))
256-
;; Not a promise, accumulate
257-
(recur (conj elems-before elem) (rest remaining))))
258-
;; Shouldn't reach here
259-
(rebuild-fn transformed)))
243+
;; Chain promises sequentially - don't re-transform already transformed elements
244+
(letfn [(chain-remaining [elems-before remaining]
245+
(if (seq remaining)
246+
(let [elem (first remaining)]
247+
(if (promise-form? elem)
248+
;; Found promise - chain from here
249+
(let [await-sym (gensym "await__")
250+
rest-transformed (rest remaining)
251+
;; Recursively chain the rest WITHOUT re-transforming
252+
rest-expr (chain-remaining (conj elems-before await-sym)
253+
rest-transformed)]
254+
(promise-then elem (list 'fn* [await-sym] rest-expr)))
255+
;; Not a promise, accumulate
256+
(chain-remaining (conj elems-before elem)
257+
(rest remaining))))
258+
;; No more elements - rebuild collection
259+
(rebuild-fn elems-before)))]
260+
(chain-remaining [] transformed))
260261
;; No promises
261262
(rebuild-fn transformed))))
262263

@@ -281,7 +282,7 @@
281282
rebuilt (apply list op (concat args-before [await-sym] rest-args))
282283
;; Recursively transform in case there are more promises
283284
rest-expr (transform-async-body ctx (conj locals await-sym) rebuilt)]
284-
(promise-then arg (list 'fn [await-sym] rest-expr)))
285+
(promise-then arg (list 'fn* [await-sym] rest-expr)))
285286
;; Not a promise, accumulate
286287
(recur (conj args-before arg)
287288
(rest remaining-args))))
@@ -302,7 +303,7 @@
302303
expanded (if (and (seq? body)
303304
(symbol? op)
304305
(not (contains? locals op)) ;; Don't expand if locally bound
305-
(not (#{'await 'let* 'loop* 'do 'fn 'fn* 'if 'quote 'try 'case*
306+
(not (#{'await 'let* 'loop* 'do 'fn* 'if 'quote 'try 'case*
306307
'sci.impl.async-await/promise} op)))
307308
(macroexpand/macroexpand-1 ctx body)
308309
body)]
@@ -355,7 +356,7 @@
355356
(if (promise-form? transformed-test)
356357
(let [test-binding (gensym "case_test__")]
357358
(promise-then transformed-test
358-
(list 'fn [test-binding]
359+
(list 'fn* [test-binding]
359360
(apply list case*-sym test-binding all-transformed))))
360361
rebuilt-case))
361362

@@ -377,27 +378,30 @@
377378
(if (promise-form? transformed-test)
378379
(let [test-binding (gensym "test__")]
379380
(promise-then transformed-test
380-
(list 'fn [test-binding]
381+
(list 'fn* [test-binding]
381382
(if transformed-else
382383
(list 'if test-binding transformed-then transformed-else)
383384
(list 'if test-binding transformed-then)))))
384385
(if transformed-else
385386
(list 'if transformed-test transformed-then transformed-else)
386387
(list 'if transformed-test transformed-then))))
387388

388-
;; fn/fn* - don't recurse into (handled by analyzer)
389-
(fn fn*)
390-
body
389+
;; fn* - don't recurse into (handled by analyzer)
390+
fn* body
391391

392392
;; General expression - transform subforms
393393
(transform-expr-with-await ctx locals body))
394-
;; Not a seq - check for vector or map
394+
;; Not a seq - check for vector, set, or map
395395
(cond
396396
;; Vector literal - transform elements
397397
(vector? body)
398398
(transform-coll-with-await ctx locals body vec)
399399

400-
;; Map literal - transform values (keys and values as pairs)
400+
;; Set literal - transform elements
401+
(set? body)
402+
(transform-coll-with-await ctx locals body set)
403+
404+
;; Map literal - transform keys and values as pairs
401405
(map? body)
402406
(transform-coll-with-await ctx locals
403407
(mapcat identity body) ;; flatten to [k1 v1 k2 v2 ...]

test/sci/async_await_test.cljs

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,52 @@
444444
(is false (str err))))
445445
(p/finally done)))))
446446

447+
(deftest async-fn-vector-literal-await-test
448+
(testing "^:async fn with await inside vector literal"
449+
(async done
450+
(-> (p/let [ctx (sci/init {:classes {'js js/globalThis :allow :all}})
451+
v (sci/eval-string* ctx
452+
"(defn ^:async foo []
453+
[(await (js/Promise.resolve 1))
454+
(await (js/Promise.resolve 2))
455+
(+ 1 (await (js/Promise.resolve 2)))])
456+
(foo)")]
457+
(p/let [result v]
458+
(is (= [1 2 3] result))))
459+
(p/catch (fn [err]
460+
(is false (str err))))
461+
(p/finally done)))))
462+
463+
(deftest async-fn-set-literal-await-test
464+
(testing "^:async fn with await inside set literal"
465+
(async done
466+
(-> (p/let [ctx (sci/init {:classes {'js js/globalThis :allow :all}})
467+
v (sci/eval-string* ctx
468+
"(defn ^:async foo []
469+
#{(await (js/Promise.resolve 1))
470+
(await (js/Promise.resolve 2))})
471+
(foo)")]
472+
(p/let [result v]
473+
(is (= #{1 2} result))))
474+
(p/catch (fn [err]
475+
(is false (str err))))
476+
(p/finally done)))))
477+
478+
(deftest async-fn-map-literal-await-test
479+
(testing "^:async fn with await inside map literal"
480+
(async done
481+
(-> (p/let [ctx (sci/init {:classes {'js js/globalThis :allow :all}})
482+
v (sci/eval-string* ctx
483+
"(defn ^:async foo []
484+
{(await (js/Promise.resolve :a)) (await (js/Promise.resolve 1))
485+
:b (await (js/Promise.resolve 2))})
486+
(foo)")]
487+
(p/let [result v]
488+
(is (= {:a 1 :b 2} result))))
489+
(p/catch (fn [err]
490+
(is false (str err))))
491+
(p/finally done)))))
492+
447493
(deftest async-fn-integration-test
448494
(testing "^:async fn integration test with multiple features combined"
449495
(async done
@@ -508,18 +554,24 @@
508554
:nested-result)))]
509555
;; for comprehension with await
510556
p [(vec (for [x (await (js/Promise.resolve [1 2]))]
511-
(* x 2)))]]
557+
(* x 2)))]
558+
;; set literal with await
559+
q [#{(await (js/Promise.resolve :x))
560+
(await (js/Promise.resolve :y))}]
561+
;; map literal with await in keys and values
562+
r [{(await (js/Promise.resolve :key)) (await (js/Promise.resolve :val))}]]
512563
{:a a :b b :c c :d d :e e :f f :g g :h h :i i :j j :k k
513564
:l l :l-side-effects l-side-effects
514565
:m m :m-side-effects m-side-effects
515-
:n n :o o :p p}))
566+
:n n :o o :p p :q q :r r}))
516567
(test-complex)")]
517568
(p/let [result v]
518569
(is (= {:a [1] :b [7] :c [17] :d [3] :e [42] :f [:matched-x] :g [10] :h [11] :i [101]
519570
:j [:found] :k [3]
520571
:l [false] :l-side-effects [0]
521572
:m [:truthy] :m-side-effects [0]
522-
:n [9] :o [:nested-result] :p [[2 4]]} result))))
573+
:n [9] :o [:nested-result] :p [[2 4]]
574+
:q [#{:x :y}] :r [{:key :val}]} result))))
523575
(p/catch (fn [err]
524576
(is false (str err))))
525577
(p/finally done)))))

0 commit comments

Comments
 (0)