(defn parse-def
[[_ sym & expr :as form] {:keys [ns] :as env}]
(when (not (symbol? sym))
(throw (ex-info (str "First argument to def must be a symbol, had: " (class sym))
(merge {:form form}
(-source-info form env)))))
(when (and (namespace sym)
(not= *ns* (the-ns (symbol (namespace sym)))))
(throw (ex-info "Cannot def namespace qualified symbol"
(merge {:form form
:sym sym}
(-source-info form env)))))
(let [pfn (fn
([])
([init]
{:init init})
([doc init]
{:pre [(string? doc)]}
{:init init :doc doc}))
args (apply pfn expr)
doc (or (:doc args) (-> sym meta :doc))
arglists (when-let [arglists (:arglists (meta sym))]
(second arglists)) ;; drop quote
sym (with-meta (symbol (name sym))
(merge (meta sym)
(when arglists
{:arglists arglists})
(when doc
{:doc doc})
(-source-info form env)))
var (create-var sym env) ;; interned var will have quoted arglists, replaced on evaluation
_ (env/deref-env) ;; make sure *env* is bound
_ (swap! env/*env* assoc-in [:namespaces ns :mappings sym] var)
meta (merge (meta sym)
(when arglists
{:arglists (list 'quote arglists)}))
meta-expr (when meta (analyze-form meta (ctx env :ctx/expr))) ;; meta on def sym will be evaluated
args (when-let [[_ init] (find args :init)]
(assoc args :init (analyze-form init (ctx env :ctx/expr))))
init? (:init args)
children (into (into [] (when meta [:meta]))
(when init? [:init]))]
(merge {:op :def
:env env
:form form
:name sym
:var var}
(when meta
{:meta meta-expr})
args
(when-not (empty? children)
{:children children}))))