(defn parse-try
[[_ & body :as form] env]
(let [catch? (every-pred seq? #(= (first %) 'catch))
finally? (every-pred seq? #(= (first %) 'finally))
[body tail'] (split-with' (complement (some-fn catch? finally?)) body)
[cblocks tail] (split-with' catch? tail')
[[fblock & fbs :as fblocks] tail] (split-with' finally? tail)]
(when-not (empty? tail)
(throw (ex-info "Only catch or finally clause can follow catch in try expression"
(merge {:expr tail
:form form}
(-source-info form env)))))
(when-not (empty? fbs)
(throw (ex-info "Only one finally clause allowed in try expression"
(merge {:expr fblocks
:form form}
(-source-info form env)))))
(let [env' (assoc env :in-try true)
body (analyze-body body env')
cenv (ctx env' :ctx/expr)
cblocks (mapv #(parse-catch % cenv) cblocks)
fblock (when-not (empty? fblock)
(analyze-body (rest fblock) (ctx env :ctx/statement)))]
(merge {:op :try
:env env
:form form
:body body
:catches cblocks}
(when fblock
{:finally fblock})
{:children (into [:body :catches]
(when fblock [:finally]))}))))