Skip to content


Switch branches/tags

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time
  An Interface to Clojure's Analyzer

Clojure's analysis compilation phase holds rich information about Clojure forms, like type/reflection information. provides an interface to this phase, callable a la carte. The output is similar to ClojureScript's analyzer.

Supports Clojure 1.4.0 or later.

Releases and Dependency Information

Latest stable release is 0.6.1.

CLI/deps.edn dependency information:

org.clojure/ {:mvn/version "0.6.1"}

Leiningen dependency information:

[org.clojure/ "0.6.1"]

; for very recent releases
:repositories {"sonatype-oss-public" ""}

Maven dependency information:


Differences from tools.analyzer.jvm

The tools.analyzer.* libraries are Clojure compilers/analyzers written in Clojure, with tools.analyzer.jvm targetting the JVM.

This library is a set of tools for manipulating the output of, the official Clojure compiler written in Java as of Clojure 1.5.

Caveats (of provided Clojure AST analysis)

Implicit Evalutation

Every AST node is evaled after it is processed by analysis. This is to recognise scope introduced by require and refer, and other global side effects. It follows that analysing a def will result in it being redefined as if it were evaluated.

Future deprecation

This library will be deprecated if and when a sufficient Clojure-in-Clojure compiler is implemented. For now, is probably your best bet for libraries you want to build today.

Fragile Implementation

The implementation consists of reflective calls to the Clojure JVM Compiler to extract AST data. It should work with Clojure 1.4.0 or later, but there may be subtle changes in which we don't account for. It is optimised to support the latest versions of Clojure (1.5.1, as of 8 April 2013).

Non-standard AST

The shape of the AST map is exactly based on the Compiler's internal representation. No effort has been made to conform to a ClojureScript-like AST. In practice, the main differences are:

  • local bindings are wrapped in a :local-binding-expr node
  • there are several AST nodes for constants (eg. :constant, :nil, :empty-expr)
  • several interop nodes (no :dot)
  • some ops/fields have different names

I highly recommend browsing the implementation of to check the current state of the AST. It should be familiar if you have experience with the ClojureScript analyzer.

Usage (Clojure)

Generating AST from syntax

Note: Column numbers are only supported with Clojure 1.5.0 or later.> (ast [1])
{:op :constant, :env {:locals {}, :ns {:name}}, :val [1]}> (-> (ast (if true 1 2)) clojure.pprint/pprint)
{:op :if,
 {:column 10,
  :line 4,
  :locals {},
  :ns {:name}},
 {:op :boolean,
  :env {:locals {}, :ns {:name}},
  :val true},
 {:op :number,
  :env {:locals {}, :ns {:name}},
  :val 1},
 {:op :number,
  :env {:locals {}, :ns {:name}},
  :val 2}}
nil> (-> (ast (fn [x] (+ x 1))) clojure.pprint/pprint)
{:op :fn-expr,
 :env {:line 5, :locals {}, :ns {:name}},
 ({:op :fn-method,
   :env {:locals {}, :ns {:name}},
   {:op :do,
    {:source "REPL",
     :column 18,
     :line 5,
     :locals {},
     :ns {:name}},
    ({:op :static-method,
      {:source "REPL",
       :column 18,
       :line 5,
       :locals {},
       :ns {:name}},
      :class clojure.lang.Numbers,
      :method-name "add",
      {:name add,
       :return-type java.lang.Number,
       :declaring-class clojure.lang.Numbers,
       :parameter-types [java.lang.Object long],
       :exception-types [],
       :flags #{:static :public}},
      ({:op :local-binding-expr,
        :env {:locals {}, :ns {:name}},
        {:op :local-binding,
         :env {:locals {}, :ns {:name}},
         :sym x,
         :tag nil,
         :init nil},
        :tag nil}
       {:op :number,
        :env {:locals {}, :ns {:name}},
        :val 1}),
      :tag nil})},
   ({:op :local-binding,
     :env {:locals {}, :ns {:name}},
     :sym x,
     :tag nil,
     :init nil}),
   :rest-param nil}),
 :variadic-method nil,
 :tag nil}

Syntax from AST> (require '[ :as e])
nil> (-> (ast 1) e/emit-form)
1> (-> (ast [(+ 1 2)]) e/emit-form)
[(clojure.lang.Numbers/add 1 2)]


Use as a substitute for macroexpand for fully macroexpanding forms. returns a hygienic form.

Known Issues

Evaluating forms

Currently the analyzer evaluates each form after it is analyzed.

Incorrect handling of Var mappings within the same form

analyze is a thin wrapper over clojure.lang.Compiler, so to get our hands on analysis results some compromises are made.

The following form normally evaluates to the Var clojure.set/intersection, but analyses to clojure.core/require.

;normal evaluation
    (require '[clojure.set])
    (refer 'clojure.set 
           :only '[intersection] 
           :rename '{intersection require})
;=> #'clojure.set/intersection

;analysis result
(-> (ast 
      (do (require '[clojure.set])
        (refer 'clojure.set 
               :only '[intersection] 
               :rename '{intersection require})
  :exprs last :var)
;=> #'clojure.core/require

Usage (Clojurescript)

All vars are identical to the Clojure implementation, where relevant, except the namespace prefix is instead of

Some examples:

Normal AST generation:

( 1)
;=> {:op :constant, :env {:ns {:defs {a {:column 18, :line 2, :file nil, :name cljs.user/a}}, :name cljs.user}, :context :statement, :locals {}}, :form 1}

Hygienic transformation:

  '(let [a 1 a a b a a a] a))
;=> (let* [a 1 a11306 a b a11306 a11307 a11306] (do a11307))

Developer Information


  • analyze a leiningen project.clj file
  • analyze clojure.core
  • use :locals if necessary


See* namespaces.


  • Jonas Enlund (jonase)
  • Nicola Mometto (Bronsa)
  • Chris Gray (chrismgray)


Copyright © Ambrose Bonnaire-Sergeant, Rich Hickey & contributors.

Licensed under the EPL (see the file epl.html).


No description, website, or topics provided.






No packages published