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
:localis:arg, a locally unique ID representing this parameter - :variadic?
- [optional] when
:localis:arg, a boolean representing whether this parameter binds to a variable number of arguments - :tag
- [optional] when
:localis:catch, the type of a caught exception - :init
- [optional] when
:localis:let,:letfnor:loop, an AST node representing the bound value - :children
- [optional] when
:localis:let,:letfnor:loop,[:init]
#catch
- :op
:catch- :env
- environment map
- :form
(catch class local body*)- :class
- the type of exception to catch
- :local
:bindingAST node representing the local name of a caught exception- :body
:doAST node with key:body? truerepresenting 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
:metais 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]
trueif 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-methodAST 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]
:bindingAST node representing the function's local name - :children
- if
:localis 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
:bindingAST 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]
:bindingAST 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
:testevaluates to truthy - :else
- AST node representing the block's return value if
:testevaluates 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
:bindingAST nodes representing locally bound symbols - :body
:doAST node with key:body? truerepresenting the let body- :children
[:bindings :body]
#letfn
- :op
:letfn- :env
- environment map
- :form
(letfn* [binding*] body*)- :bindings
- vector of
:bindingAST nodes representing locally bound functions - :body
:doAST node with key:body? truerepresenting the letfn body- :children
[:bindings :body]
#local
- :op
:local- :assignable?
- whether or not the corresponding
:bindingAST node is mutable - :children
- same as
:childrenfor the corresponding:bindingAST node, but never contains:init - other keys
- same as those of the corresponding
:bindingAST node
#loop
- :op
:loop- :env
- environment map
- :form
(loop* [binding*] body*)- :bindings
- ordered vector of
:bindingAST nodes representing locally bound symbols - :body
:doAST node with key:body? truerepresenting 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
:constAST 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
:doAST node with key:body? truerepresenting the try body- :catches
- ordered vector of
:catchAST nodes representing catch clauses - :finally
- [optional]
:doAST node with key:body? truerepresenting 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]