Skip to content

Commit 1792671

Browse files
committed
feat: max-let-bindings var
1 parent 7fbfebe commit 1792671

File tree

2 files changed

+29
-11
lines changed

2 files changed

+29
-11
lines changed

src/uclj/core.clj

+19-8
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
(run! require namespaces-to-require)
3131

32-
(def ^:private ^:dynamic *template-vars* {})
32+
(def ^:dynamic *template-vars* {})
3333
(defn- template* [bindings expr]
3434
(assert (vector? bindings))
3535
(letfn [(unroll [expr] (cond (seq? expr) (unroll-seq expr)
@@ -401,25 +401,36 @@
401401
[arity (->eval-node iden->idx recur-indices (list* 'do bodies))]))]
402402
(make-fn-body fname symbol-used arity->body-node arity->symbols-introduced iden->idx vararg-arity)))
403403

404+
(def ^:const max-let-bindings 32)
405+
404406
(defmethod seq->eval-node 'let* seq-eval-let [iden->idx recur-indices [_ bindings & bodies :as form]]
405407
(cond
406408
;; can merge (let*) forms
407-
(and (= 1 (count bodies)) (seq? (first bodies)) (= 'let* (ffirst bodies)))
409+
(and (= 1 (count bodies))
410+
(< (count bindings) (* 2 max-let-bindings))
411+
(seq? (first bodies)) (= 'let* (ffirst bodies)))
408412
(recur iden->idx recur-indices (list* 'let*
409-
(into bindings (second (first bodies)))
410-
(nnext (first bodies))))
411-
413+
(into bindings (second (first bodies)))
414+
(nnext (first bodies))))
415+
;; it must have at most max-let-bindings binding vars
416+
(> (count bindings) (* 2 max-let-bindings))
417+
(recur iden->idx recur-indices
418+
(list 'let*
419+
(vec (take (* 2 max-let-bindings) bindings))
420+
(list* 'let*
421+
(vec (drop (* 2 max-let-bindings) bindings))
422+
bodies)))
412423
:else
413-
(template [idx-symbols (mapv #(symbol (str 'idx- %)) (range 20))
414-
node-symbols (mapv #(symbol (str 'node- %)) (range 20))]
424+
(template [idx-symbols (mapv #(symbol (str 'idx- %)) (range max-let-bindings))
425+
node-symbols (mapv #(symbol (str 'node- %)) (range max-let-bindings))]
415426
(let [[~@(map vector idx-symbols node-symbols)]
416427
(for [[k v] (partition 2 bindings)]
417428
[(int (iden->idx (::symbol-identity (meta k))))
418429
(->eval-node iden->idx nil v)])
419430
body-node (seq->eval-node iden->idx recur-indices (list* 'do bodies))]
420431
(case (count bindings)
421432
~@(mapcat seq
422-
(for [i (range 20)]
433+
(for [i (range (inc max-let-bindings))]
423434
[(* 2 i)
424435
`(gen-eval-node
425436
(do ~@(for [i (range i)]

test/uclj/core_test.clj

+10-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,14 @@
1919
(is (fn? (evaluator '(let [b :lol] (fn [] (let [_ b] :hi)))))))
2020

2121
(testing "Shadow lexical bindings"
22-
(is (= 4 (evaluator '(let [inc dec] (inc 5)))))))
22+
(is (= 4 (evaluator '(let [inc dec] (inc 5))))))
23+
24+
(testing "let forms with many bindings"
25+
(template [varnames (repeatedly gensym)]
26+
(do ~@(for [i (range 0 100 5)]
27+
`(is (= (reduce + (range ~i))
28+
(evaluator '(let [~@(interleave varnames (range i))]
29+
(+ ~@(take i varnames)))))))))))
2330

2431
(deftest test-eval-try
2532
(testing "try-catch maintains sybol usage across closure"
@@ -145,8 +152,8 @@
145152
(evaluator '(loop [i 2] (case (recur (dec i)) 1 1 2 2 :three)))))))
146153

147154
(testing "used vars in case are correctly encapsulated in closure"
148-
(is (= :ok
149-
(evaluator '(let [a :ok b :w1 c :w2] ((fn [t] (case t, 1 a, 2 b, c)) 1))))))
155+
(is (= :ok
156+
(evaluator '(let [a :ok b :w1 c :w2] ((fn [t] (case t, 1 a, 2 b, c)) 1))))))
150157

151158
(testing "Identity checking because all cases are keywords"
152159
(testing "All keys have different hashes"

0 commit comments

Comments
 (0)