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]