Clojure AST Quickref
Helpful notes
#Generic AST traversal
The Clojure AST format documented here was designed to facilitate generic traversal by maximally clueless consumers. Tooling that relies on this AST structure doesn't have to know much of anything about the structure of each individual type of node, so it's safe to extend the tree with custom nodes beyond the types documented here if this becomes desirable. It's also safe to annotate nodes of any type with your own key-value pairs, provided the keys are properly namespaced so as to avoid conflicts.
The recommended generic method of AST traversal (provided you're not depending directly on tools.analyzer, which gives you a generic AST-walking API for free) is to examine the :children
key in the node you're currently visiting. If present, a node's :children
key will always point to a vector of other keys in the same node. What's more, each of these other keys will always point to either a single AST node (which you can then visit immediately) or a correctly ordered vector of AST nodes (which you can iterate over, visiting each in turn).
Individually, each AST node is guaranteed to contain at least three key-value pairs: :op
(a keyword denoting the node's type), :env
(an environment map representing the context in which the node should be examined), and :form
(the Clojure data structure from which the node was originally parsed).
Nodes reference
#binding
- :op
:binding
- :env
- environment map
- :form
- a binding symbol
- :name
- same as
:form
- :local
- one of
:arg
,:catch
,:fn
,:let
,:letfn
, or:loop
- :arg-id
- [optional] when
:local
is:arg
, a locally unique ID representing this parameter - :variadic?
- [optional] when
:local
is:arg
, a boolean representing whether this parameter binds to a variable number of arguments - :tag
- [optional] when
:local
is:catch
, the type of a caught exception - :init
- [optional] when
:local
is:let
,:letfn
or:loop
, an AST node representing the bound value - :children
- [optional] when
:local
is:let
,:letfn
or:loop
,[:init]
#catch
- :op
:catch
- :env
- environment map
- :form
(catch class local body*)
- :class
- the type of exception to catch
- :local
:binding
AST node representing the local name of a caught exception- :body
:do
AST node with key:body? true
representing the clause body- :children
[:local :body]
#const
- :op
:const
- :env
- environment map
- :form
- a constant literal or quoted collection literal
- :type
- one of
:nil
,:bool
,:keyword
,:symbol
,:string
,:number
,:type
,:record
,:map
,:vector
,:set
,:seq
,:char
,:regex
,:class
,:var
, or:unknown
- :literal?
true
- :val
- same as
:form
- :meta
- [optional] AST node representing metadata attached to
:form
- :children
- [optional] when
:meta
is present,[:meta]
#def
- :op
:def
- :env
- environment map
- :form
(def name init?)
- :name
- the symbol to define
- :var
- the var object created to hold the defined value
- :meta
- [optional] AST node representing metadata attached to
:name
- :init
- [optional] AST node representing the initial value of the created var
- :doc
- [optional] docstring explaining the purpose of this definition
- :children
- [optional]
[:meta]
or[:init]
or[:meta :init]
#do
- :op
:do
- :env
- environment map
- :form
(do statement* ret)
- :statements
- ordered vector of AST nodes representing all expressions in the body except the last
- :ret
- AST node representing the last expression in the body (the block's return value)
- :body?
- [optional]
true
if this node was synthesized as another node's body - :children
[:statements :ret]
#fn
- :op
:fn
- :env
- environment map
- :form
(fn* name? [arg*] body*)
or(fn* name? method*)
- :variadic?
- whether or not this function has a variadic method
- :max-fixed-arity
- the number of arguments taken by the fixed-arity method that takes the most arguments
- :methods
- vector of
:fn-method
AST nodes - :once
- whether or not this function will only be invoked once (if
true
, permits the compiler to clear closed-over locals in the function body) - :local
- [optional]
:binding
AST node representing the function's local name - :children
- if
:local
is present,[:local :methods]
, otherwise[:methods]
#fn-method
- :op
:fn-method
- :env
- environment map
- :form
([param*] body*)
- :loop-id
- gensym with prefix
loop_
uniquely identifying this method (for recursion) - :variadic?
- whether or not this method takes a variable number of arguments
- :params
- ordered vector of
:binding
AST nodes representing this method's parameters - :fixed-arity
- the number of arguments this method takes, discounting variadic parameters
- :body
- ordered vector of AST nodes representing this method's body
- :local
- [optional]
:binding
AST node representing the parent function's local name - :children
[:params :body]
#host-call
- :op
:host-call
- :env
- environment map
- :form
(. target method arg*)
- :target
- AST node representing the object on which to call the method
- :method
- symbol naming the method called
- :args
- ordered vector of AST nodes representing arguments passed to the method
- :children
[:target :args]
#host-field
- :op
:host-field
- :env
- environment map
- :form
(. target -field)
- :target
- AST node representing the object on which to access the field
- :field
- symbol naming the field accessed
- :children
[:target]
#host-interop
- :op
:host-interop
- :env
- environment map
- :form
(. target m-or-f)
- :target
- AST node representing the target object
- :m-or-f
- symbol naming either a 0-args method or a field
- :children
[:target]
#if
- :op
:if
- :env
- environment map
- :form
(if test then else?)
- :test
- AST node representing the test expression
- :then
- AST node representing the block's return value if
:test
evaluates to truthy - :else
- AST node representing the block's return value if
:test
evaluates to falsey - :children
[:test :then :else]
#invoke
- :op
:invoke
- :env
- environment map
- :form
(f arg*)
- :fn
- AST node representing the invoked function
- :args
- ordered vector of AST nodes representing the arguments passed to the function
- :meta
- [optional] map of metadata attached to
:form
(shouldn't be evaluated) - :children
[:fn :args]
#let
- :op
:let
- :env
- environment map
- :form
(let* [binding*] body*)
- :bindings
- ordered vector of
:binding
AST nodes representing locally bound symbols - :body
:do
AST node with key:body? true
representing the let body- :children
[:bindings :body]
#letfn
- :op
:letfn
- :env
- environment map
- :form
(letfn* [binding*] body*)
- :bindings
- vector of
:binding
AST nodes representing locally bound functions - :body
:do
AST node with key:body? true
representing the letfn body- :children
[:bindings :body]
#local
- :op
:local
- :assignable?
- whether or not the corresponding
:binding
AST node is mutable - :children
- same as
:children
for the corresponding:binding
AST node, but never contains:init
- other keys
- same as those of the corresponding
:binding
AST node
#loop
- :op
:loop
- :env
- environment map
- :form
(loop* [binding*] body*)
- :bindings
- ordered vector of
:binding
AST nodes representing locally bound symbols - :body
:do
AST node with key:body? true
representing the loop body- :loop-id
- gensym with prefix
loop_
uniquely identifying this loop - :children
[:bindings :body]
#map
- :op
:map
- :env
- environment map
- :form
{[key val]*}
- :keys
- ordered vector of AST nodes representing keys in
:form
- :vals
- ordered vector of AST nodes representing vals in
:form
- :children
[:keys :vals]
#maybe-class
- :op
:maybe-class
- :env
- environment map
- :form
- a symbol
- :class
- same as
:form
#maybe-host-form
- :op
:maybe-host-form
- :env
- environment map
- :form
class/field
- :class
- a symbol naming the namespace part of
:form
- :field
- a symbol naming the name part of
:form
#new
- :op
:new
- :env
- environment map
- :form
(new class arg*)
- :class
- the class to instantiate (not an AST node)
- :args
- ordered vector of AST nodes representing arguments passed to the class constructor
- :children
[:args]
#quote
- :op
:quote
- :env
- environment map
- :form
(quote expr)
- :expr
:const
AST node representing the quoted expression- :literal?
true
- :children
[:expr]
#recur
- :op
:recur
- :env
- environment map
- :form
(recur expr*)
- :exprs
- ordered vector of AST nodes representing new bound values for the next loop iteration
- :loop-id
- gensym with prefix
loop_
uniquely identifying the enclosing loop - :children
[:exprs]
#set
- :op
:set
- :env
- environment map
- :form
#{item*}
- :items
- vector of AST nodes representing items in
:form
- :children
[:items]
#set!
- :op
:set!
- :env
- environment map
- :form
(set! target val)
- :target
- AST node representing what to set the value of
- :val
- AST node representing the new value to set
- :children
[:target :val]
#throw
- :op
:throw
- :env
- environment map
- :form
(throw exception)
- :exception
- AST node representing the exception to throw
- :children
[:exception]
#try
- :op
:try
- :env
- environment map
- :form
(try body* catch* finally?)
- :body
:do
AST node with key:body? true
representing the try body- :catches
- ordered vector of
:catch
AST nodes representing catch clauses - :finally
- [optional]
:do
AST node with key:body? true
representing the finally clause body - :children
[:body :catches]
or[:body :catches :finally]
#var
- :op
:var
- :env
- environment map
- :form
- a symbol naming the var
- :var
- the var object itself
- :assignable?
- whether or not the var is dynamic
#vector
- :op
:vector
- :env
- environment map
- :form
[item*]
- :items
- ordered vector of AST nodes representing items in
:form
- :children
[:items]
#with-meta
- :op
:with-meta
- :env
- environment map
- :form
- non-quoted collection literal with attached metadata
- :meta
- AST node representing metadata to evaluate at runtime
- :expr
- AST node representing
:form
(minus metadata) - :children
[:meta :expr]