https://github.com/clojure/tools.analyzer
An analyzer for Clojure code, written in Clojure and producing AST in EDN
https://github.com/clojure/tools.analyzer
Last synced: about 1 month ago
JSON representation
An analyzer for Clojure code, written in Clojure and producing AST in EDN
- Host: GitHub
- URL: https://github.com/clojure/tools.analyzer
- Owner: clojure
- License: epl-1.0
- Created: 2013-10-04T13:41:24.000Z (over 11 years ago)
- Default Branch: master
- Last Pushed: 2024-07-15T17:55:45.000Z (11 months ago)
- Last Synced: 2025-04-11T19:55:31.868Z (2 months ago)
- Language: Clojure
- Size: 1.58 MB
- Stars: 262
- Watchers: 51
- Forks: 33
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# tools.analyzer
An analyzer for host agnostic Clojure code, written in Clojure and producing AST in EDN.
I gave a talk on tools.analyzer[.jvm] at ClojureX in December 2015. Video [here](https://www.youtube.com/watch?v=oZyt93lmF5s)
Timothy Baldridge gave a talk on tools.analyzer[.jvm] at Clojure/West in
March 2014. Video
[here](https://www.youtube.com/watch?v=KhRQmT22SSg&list=PLZdCLR02grLp__wRg5OTavVj4wefg69hM&index=11).Note that the analyzer in this library should not to be used directly as it lacks any knowledge about host-specific special forms and it should only be considered as a building platform for host-specific analyzers.
Currently the following platform specific analyzers written on top of tools.analyzer exist: [tools.analyzer.jvm](https://github.com/clojure/tools.analyzer.jvm), [tools.analyzer.js](https://github.com/clojure/tools.analyzer.js)* [Example Usage](#example-usage)
* [Quickref](#quickref)
* [Releases and Dependency Information](#releases-and-dependency-information)
* [Changelog](#changelog)
* [API Index](#api-index)
* [Developer Information](#developer-information)
* [License](#license)[Quickref](https://clojure.github.io/tools.analyzer/spec/quickref.html)
========================================## Example Usage
`clojure.tools.analyzer/analyze` will not work out of the box, as it requires a number of entry-points to be set.
Here's what could happen trying to use `clojure.tools.analyzer/analyze` directly:
```clojure
clojure.tools.analyzer> (analyze 'a {})
Attempting to call unbound fn: #'clojure.tools.analyzer/macroexpand-1
[Thrown class java.lang.IllegalStateException]
```At the moment there exist two official analyzers written on top of [tools.analyzer](https://github.com/clojure/tools.analyzer): [tools.analyzer.jvm](https://github.com/clojure/tools.analyzer.jvm) for clojure on the JVM and [tools.analyzer.js](https://github.com/clojure/tools.analyzer.js) for clojurescript.
We will use [tools.analyzer.jvm](https://github.com/clojure/tools.analyzer.jvm) for those examples.Here's a simplified version of how `clojure.tools.analyzer.jvm/analyze` is defined:
```clojure
(require '[clojure.tools.analyzer :as ana])
(require '[clojure.tools.analyzer.env :as env])
(defn analyze [form env]
(binding [ana/macroexpand-1 macroexpand-1
ana/create-var create-var
ana/parse parse
ana/var? var?]
(env/ensure (global-env)
(run-passes (-analyze form env))))))
```Here, `-analyze` is a multimethod that defaults to `ana/analyze` and defines analysis methods for the JVM specific special forms, `global-env` is a function that returns a global environment for the JVM analyzer and `run-passes` is a function that takes an AST and applies a number of passes to it.
The `tools.analyzer.jvm` [README](https://github.com/clojure/tools.analyzer.jvm#example-usage) contains more examples on how the `analyze` function works as well as a reference for all the nodes it can return.
One of the most important features of `tools.analyzer` is the ability to walk generically through the AST nodes, this has been immensely useful to write most of the passes used by the various analyzers.
The `tools.analyzer.ast` namespace provides a number of functions that implement various generic AST walking strategies.The `children` function returns a vector of the children nodes of the current node (the output has been elided and pretty-printed for clarity):
```clojure
clojure.tools.analyzer.jvm> (require '[clojure.tools.analyzer.ast :as ast])
nil
clojure.tools.analyzer.jvm> (ast/children (analyze '(do 1 2 :foo)))
[{:op :const,
:id 0,
:type :number,
:val 1,
:form 1,
...}
{:op :const,
:id 1,
:type :number,
:val 2,
:form 2,
...}
{:op :const,
:id 3,
:type :keyword,
:val :foo,
:form :foo,
...}]
```If we want to access a flattened view of all the nodes of an AST, we can use the `nodes` function:
```clojure
clojure.tools.analyzer.jvm> (ast/nodes (analyze '[1 (+ 1 2)]))
({:op :vector,
:top-level true,
:items
[{:op :const,
:type :number,
:val 1,
...}
{:op :static-call,
:class clojure.lang.Numbers,
:method add,
:form (. clojure.lang.Numbers (add 1 2)),
:args [{:op :const,
:val 1,
...}
{:op :const,
:val 2,
...}],
...}]
:form [1 (+ 1 2)],
...}
{:op :const,
:type :number,
:val 1,
...}
{:op :static-call,
:class clojure.lang.Numbers,
:method add,
:form (. clojure.lang.Numbers (add 1 2)),
:args [{:op :const,
:val 1,
...}
{:op :const,
:val 2,
...}],
...}
..)
```The `update-children` function takes an AST node and a function and replaces the children nodes of the given node with the result of applying the function to each children node.
```clojure
clojure.tools.analyzer.jvm> (ast/update-children (analyze '(do 1 (+ 1 2) :foo))
#(assoc % :visited true))
{:op :do
:statements
[{:op :const,
:val 1,
:visited true,
...}
{:op :static-call,
:class clojure.lang.Numbers,
:method add,
:visited true,
:args [{:op :const
:val 1,
...}
{:op :const,
:val 2,
...}],
...}]
:ret
{:op :const,
:val :foo,
:visited true,
...},
...}
```
If it's desirable to walk all the AST applying a function to all the nodes and the children nodes, one of `walk`, `prewalk` or `postwalk` should be used, read the docstrings of the three functions to understand the differences.
Here's the previous example using `prewalk` instead of `update-children`:
```clojure
clojure.tools.analyzer.jvm> (ast/prewalk (analyze '(do 1 (+ 1 2) :foo))
#(assoc % :visited true))
{:op :do
:visited true,
:statements
[{:op :const,
:val 1,
:visited true,
...}
{:op :static-call,
:class clojure.lang.Numbers,
:method add,
:visited true,
:args [{:op :const
:val 1,
:visited true,
...}
{:op :const,
:val 2,
:visited true,
...}],
...}]
:ret
{:op :const,
:val :foo,
:visited true,
...},
...}
```
As you can see, this time all the nodes have been marked `:visited`.Since version `0.6.0`, passes can be scheduled automatically using `clojure.tools.analyzer.passes/schedule` rather than having to compose them and sort out pass dependencies manually, refer to its docstrings and examples from `tools.analyzer.jvm` for more info.
## SPONSORSHIP
* Cognitect (https://cognitect.com/) has sponsored tools.analyzer development (https://groups.google.com/d/msg/clojure/iaP16MHpX0E/EMtnGmOz-rgJ)
* Ambrose BS (https://twitter.com/ambrosebs) has sponsored tools.analyzer development in his typed clojure campaign (https://www.indiegogo.com/projects/typed-clojure).## YourKit
YourKit has given an open source license for their profiler, greatly simplifying the profiling of tools.analyzer performance.
YourKit is kindly supporting open source projects with its full-featured Java Profiler. YourKit, LLC is the creator of innovative and intelligent tools for profiling Java and .NET applications. Take a look at YourKit's leading software products:
* YourKit Java Profiler and
* YourKit .NET Profiler.Releases and Dependency Information
========================================Latest stable release: 1.2.0
* [All Released Versions](https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22tools.analyzer%22)
* [Development Snapshot Versions](https://clojure.org/releases/downloads#_using_clojure_snapshot_releases)[Leiningen](https://github.com/technomancy/leiningen) dependency information:
```clojure
[org.clojure/tools.analyzer "1.2.0"]
```
[Maven](https://maven.apache.org/) dependency information:```xml
org.clojure
tools.analyzer
1.2.0```
[Changelog](CHANGELOG.md)
========================================API Index
========================================* [API index](https://clojure.github.io/tools.analyzer)
Developer Information
========================================* [GitHub project](https://github.com/clojure/tools.analyzer)
* [Bug Tracker](https://clojure.atlassian.net/browse/TANAL)
* [Continuous Integration](https://github.com/clojure/tools.analyzer/actions/workflows/test.yml)## License
Copyright © Nicola Mometto, Rich Hickey & contributors.
Distributed under the Eclipse Public License, the same as Clojure.