letfn*
special form | since v0.0-1236 | Edit |
(defmethod parse 'letfn*
[op env [_ bindings & exprs :as form] name _]
(when-not (and (vector? bindings) (even? (count bindings)))
(throw (error env "bindings must be vector of even number of elements")))
(let [n->fexpr (into {} (map (juxt first second) (partition 2 bindings)))
names (keys n->fexpr)
context (:context env)
;; first pass to collect information for recursive references
[meth-env bes]
(reduce (fn [[{:keys [locals] :as env} bes] n]
(let [ret-tag (-> n meta :tag)
fexpr (no-warn (analyze env (n->fexpr n)))
be (cond->
{:name n
:op :binding
:fn-var true
:line (get-line n env)
:column (get-col n env)
:local :letfn
:shadow (handle-symbol-local n (locals n))
:variadic? (:variadic? fexpr)
:max-fixed-arity (:max-fixed-arity fexpr)
:method-params (map :params (:methods fexpr))}
ret-tag (assoc :ret-tag ret-tag))]
[(assoc-in env [:locals n] be)
(conj bes be)]))
[env []] names)
meth-env (assoc meth-env :context :expr)
;; the real pass
[meth-env bes]
(reduce (fn [[meth-env bes] {:keys [name shadow] :as be}]
(let [env (assoc-in meth-env [:locals name] shadow)
fexpr (analyze env (n->fexpr name))
be' (assoc be
:init fexpr
:variadic? (:variadic? fexpr)
:max-fixed-arity (:max-fixed-arity fexpr)
:method-params (map :params (:methods fexpr))
:children [:init])]
[(assoc-in env [:locals name] be')
(conj bes be')]))
[meth-env []] bes)
expr (-> (analyze (assoc meth-env :context (if (= :expr context) :return context)) `(do ~@exprs))
(assoc :body? true))]
{:env env :op :letfn :bindings bes :body expr :form form
:children [:bindings :body]}))
(defmethod emit* :letfn
[{expr :body :keys [bindings env]}]
(let [context (:context env)]
(when (= :expr context) (emits "(function (){"))
(doseq [{:keys [init] :as binding} bindings]
(emitln "var " (munge binding) " = " init ";"))
(emits expr)
(when (= :expr context) (emits "})()"))))