{"id":15010273,"url":"https://github.com/clojure/jvm.tools.analyzer","last_synced_at":"2025-08-01T17:10:32.215Z","repository":{"id":7399371,"uuid":"8729486","full_name":"clojure/jvm.tools.analyzer","owner":"clojure","description":null,"archived":false,"fork":false,"pushed_at":"2025-05-30T20:07:31.000Z","size":277,"stargazers_count":53,"open_issues_count":1,"forks_count":5,"subscribers_count":30,"default_branch":"master","last_synced_at":"2025-07-05T12:45:33.917Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/clojure.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2013-03-12T14:20:34.000Z","updated_at":"2025-05-30T20:07:34.000Z","dependencies_parsed_at":"2025-07-05T12:37:20.179Z","dependency_job_id":"6489b8bc-1aa9-44a2-9d2b-a110014f47c3","html_url":"https://github.com/clojure/jvm.tools.analyzer","commit_stats":null,"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/clojure/jvm.tools.analyzer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clojure%2Fjvm.tools.analyzer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clojure%2Fjvm.tools.analyzer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clojure%2Fjvm.tools.analyzer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clojure%2Fjvm.tools.analyzer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/clojure","download_url":"https://codeload.github.com/clojure/jvm.tools.analyzer/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clojure%2Fjvm.tools.analyzer/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268265831,"owners_count":24222526,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-01T02:00:08.611Z","response_time":67,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-09-24T19:33:18.566Z","updated_at":"2025-08-01T17:10:32.177Z","avatar_url":"https://github.com/clojure.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# jvm.tools.analyzer: An Interface to Clojure's Analyzer\n\nClojure's analysis compilation phase holds rich information about Clojure forms, like type/reflection information.\n\njvm.tools.analyzer provides an interface to this phase, callable a la carte. The output is similar to ClojureScript's analyzer.\n\nSupports Clojure 1.4.0 or later.\n\n# Releases and Dependency Information\n\nLatest stable release is 0.6.1.\n\n[CLI/`deps.edn`](https://clojure.org/reference/deps_and_cli) dependency information:\n```clojure\norg.clojure/jvm.tools.analyzer {:mvn/version \"0.6.1\"}\n```\n\nLeiningen dependency information:\n\n```clojure\n[org.clojure/jvm.tools.analyzer \"0.6.1\"]\n\n; for very recent releases\n:repositories {\"sonatype-oss-public\" \"https://oss.sonatype.org/content/groups/public/\"}\n```\n\nMaven dependency information:\n\n```XML\n\u003cdependency\u003e\n  \u003cgroupId\u003eorg.clojure\u003c/groupId\u003e\n  \u003cartifactId\u003ejvm.tools.analyzer\u003c/artifactId\u003e\n  \u003cversion\u003e0.6.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n# Differences from tools.analyzer.jvm\n\nThe tools.analyzer.* libraries are Clojure compilers/analyzers written in Clojure, with tools.analyzer.jvm\ntargetting the JVM.\n\nThis library jvm.tools.analyzer is a set of tools for manipulating the output of Compiler.java, the official\nClojure compiler written in Java as of Clojure 1.5.\n\n# Caveats (of provided Clojure AST analysis)\n\n## Implicit Evalutation\n\nEvery AST node is `eval`ed after it is processed by analysis. This is to recognise\nscope introduced by `require` and `refer`, and other global side effects.\nIt follows that analysing a `def` will result in it being redefined as if it were\nevaluated.\n\n## Future deprecation\n\nThis library will be deprecated if and when a sufficient Clojure-in-Clojure compiler\nis implemented. For now, `jvm.tools.analyzer` is probably your best bet for libraries\nyou want to build *today*.\n\n## Fragile Implementation\n\nThe implementation consists of reflective calls to the Clojure JVM Compiler to extract\nAST data. It should work with Clojure 1.4.0 or later, but there may be subtle\nchanges in c.l.Compiler.java which we don't account for. It is optimised to support the latest\nversions of Clojure (1.5.1, as of 8 April 2013).\n\n## Non-standard AST\n\nThe shape of the AST map is exactly based on the Compiler's internal representation. No effort\nhas been made to conform to a ClojureScript-like AST. In practice, the main differences are:\n\n- local bindings are wrapped in a `:local-binding-expr` node\n- there are several AST nodes for constants (eg. `:constant`, `:nil`, `:empty-expr`)\n- several interop nodes (no :dot)\n- some ops/fields have different names\n\nI highly recommend browsing the implementation of `clojure.jvm.tools.analyzer` to check the current\nstate of the AST. It should be familiar if you have experience with the ClojureScript analyzer.\n\n# Usage (Clojure)\n\n## Generating AST from syntax\n\nNote: Column numbers are only supported with Clojure 1.5.0 or later.\n\n```clojure\n\nclojure.jvm.tools.analyzer=\u003e (ast [1])\n{:op :constant, :env {:locals {}, :ns {:name clojure.jvm.tools.analyzer}}, :val [1]}\n\nclojure.jvm.tools.analyzer=\u003e (-\u003e (ast (if true 1 2)) clojure.pprint/pprint)\n{:op :if,\n :env\n {:column 10,\n  :line 4,\n  :locals {},\n  :ns {:name clojure.jvm.tools.analyzer}},\n :test\n {:op :boolean,\n  :env {:locals {}, :ns {:name clojure.jvm.tools.analyzer}},\n  :val true},\n :then\n {:op :number,\n  :env {:locals {}, :ns {:name clojure.jvm.tools.analyzer}},\n  :val 1},\n :else\n {:op :number,\n  :env {:locals {}, :ns {:name clojure.jvm.tools.analyzer}},\n  :val 2}}\nnil\n\nclojure.jvm.tools.analyzer=\u003e (-\u003e (ast (fn [x] (+ x 1))) clojure.pprint/pprint)\n{:op :fn-expr,\n :env {:line 5, :locals {}, :ns {:name clojure.jvm.tools.analyzer}},\n :methods\n ({:op :fn-method,\n   :env {:locals {}, :ns {:name clojure.jvm.tools.analyzer}},\n   :body\n   {:op :do,\n    :env\n    {:source \"REPL\",\n     :column 18,\n     :line 5,\n     :locals {},\n     :ns {:name clojure.jvm.tools.analyzer}},\n    :exprs\n    ({:op :static-method,\n      :env\n      {:source \"REPL\",\n       :column 18,\n       :line 5,\n       :locals {},\n       :ns {:name clojure.jvm.tools.analyzer}},\n      :class clojure.lang.Numbers,\n      :method-name \"add\",\n      :method\n      {:name add,\n       :return-type java.lang.Number,\n       :declaring-class clojure.lang.Numbers,\n       :parameter-types [java.lang.Object long],\n       :exception-types [],\n       :flags #{:static :public}},\n      :args\n      ({:op :local-binding-expr,\n        :env {:locals {}, :ns {:name clojure.jvm.tools.analyzer}},\n        :local-binding\n        {:op :local-binding,\n         :env {:locals {}, :ns {:name clojure.jvm.tools.analyzer}},\n         :sym x,\n         :tag nil,\n         :init nil},\n        :tag nil}\n       {:op :number,\n        :env {:locals {}, :ns {:name clojure.jvm.tools.analyzer}},\n        :val 1}),\n      :tag nil})},\n   :required-params\n   ({:op :local-binding,\n     :env {:locals {}, :ns {:name clojure.jvm.tools.analyzer}},\n     :sym x,\n     :tag nil,\n     :init nil}),\n   :rest-param nil}),\n :variadic-method nil,\n :tag nil}\nnil\n```\n\n## Syntax from AST\n\n\n```clojure\nclojure.jvm.tools.analyzer=\u003e (require '[clojure.jvm.tools.analyzer.emit-form :as e])\nnil\nclojure.jvm.tools.analyzer=\u003e (-\u003e (ast 1) e/emit-form)\n1\nclojure.jvm.tools.analyzer=\u003e (-\u003e (ast [(+ 1 2)]) e/emit-form)\n[(clojure.lang.Numbers/add 1 2)]\n```\n\n# Macroexpander\n\nUse `clojure.jvm.tools.analyzer/macroexpand` as a substitute\nfor `macroexpand` for fully macroexpanding forms.\n\n`clojure.jvm.tools.analyzer.hygienic/macroexpand` returns a hygienic form.\n\n# Known Issues\n\n## Evaluating forms\n\nCurrently the analyzer evaluates each form after it is analyzed.\n\n## Incorrect handling of Var mappings within the same form\n\n`analyze` is a thin wrapper over `clojure.lang.Compiler`, so to get our\nhands on analysis results some compromises are made.\n\nThe following form normally evaluates to the Var `clojure.set/intersection`, but\nanalyses to `clojure.core/require`.\n\n\n```clojure\n;normal evaluation\n(eval\n '(do \n    (require '[clojure.set])\n    (refer 'clojure.set \n           :only '[intersection] \n           :rename '{intersection require})\n    require))\n;=\u003e #'clojure.set/intersection\n\n;analysis result\n(-\u003e (ast \n      (do (require '[clojure.set])\n        (refer 'clojure.set \n               :only '[intersection] \n               :rename '{intersection require})\n        require))\n  :exprs last :var)\n;=\u003e #'clojure.core/require\n```\n\n# Usage (Clojurescript)\n\nAll vars are identical to the Clojure implementation, where relevant,\nexcept the namespace prefix is `cljs.jvm.tools.analyzer` instead of\n`clojure.jvm.tools.analyzer`.\n\nSome examples:\n\nNormal AST generation:\n\n```clojure\n(cljs.jvm.tools.analyzer/ast 1)\n;=\u003e {:op :constant, :env {:ns {:defs {a {:column 18, :line 2, :file nil, :name cljs.user/a}}, :name cljs.user}, :context :statement, :locals {}}, :form 1}\n```\n\nHygienic transformation:\n\n```clojure\n(cljs.jvm.tools.analyzer.hygienic/macroexpand\n  '(let [a 1 a a b a a a] a))\n;=\u003e (let* [a 1 a11306 a b a11306 a11307 a11306] (do a11307))\n```\n\n# Developer Information\n\n- [Github Project](https://github.com/clojure/jvm.tools.analyzer)\n- [Bug Tracker](http://dev.clojure.org/jira/browse/JVMTA)\n- [Continuous Integration](https://github.com/clojure/jvm.tools.analyzer/actions/workflows/test.yml)\n\n# Todo\n\n- analyze a leiningen `project.clj` file\n- analyze `clojure.core`\n- use :locals if necessary\n\n# Examples\n\nSee `clojure.jvm.tools.analyzer.examples.*` namespaces.\n\n# Contributors\n\n- Jonas Enlund (jonase)\n- Nicola Mometto (Bronsa)\n- Chris Gray (chrismgray)\n\n## License\n\nCopyright © Ambrose Bonnaire-Sergeant, Rich Hickey \u0026 contributors.\n\nLicensed under the EPL (see the file epl.html).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclojure%2Fjvm.tools.analyzer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclojure%2Fjvm.tools.analyzer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclojure%2Fjvm.tools.analyzer/lists"}