如何在Clojure表达式中找到所有自由变量?

Ben*_*itz 5 macros scope clojure

如何在Clojure表达式中找到所有自由变量?

通过自由变量,我指的是在本地环境中未定义的所有符号(包括表达式中定义的所有本地环境),未在全局环境中定义(当前命名空间中的所有符号,包括从其他命名空间导入的所有符号) ,而不是Clojure原语.

例如,此表达式中有一个自由变量:

(fn [ub]
  (* (rand-int ub) scaling-factor))
Run Code Online (Sandbox Code Playgroud)

那就是scaling-factor.fn,*rand-int,都在全球环境中定义.ub在它发生的范围内定义,因此它也是一个绑定变量(即不是自由的).

我想我自己也可以写这个 - 它看起来并不太难 - 但我希望有一些标准的方法来做我应该使用的,或者是一种标准的方式来访问Clojure编译器来做到这一点(因为Clojure)编译器肯定也必须这样做).天真实现的一个潜在缺陷是表达式中的所有宏必须完全展开,因为宏可以引入新的自由变量.

Ole*_*Cat 7

您可以tools.analyzer.jvm通过传递特定回调来分析表单,以处理无法解析的符号.像这样的东西:

(require '[clojure.tools.analyzer.jvm :as ana.jvm])

(def free-variables (atom #{}))

(defn save-and-replace-with-nil [_ s _]
  (swap! free-variables conj s)

  ;; replacing unresolved symbol with `nil`
  ;; in order to keep AST valid
  {:op :const
   :env {}
   :type :nil
   :literal? true
   :val nil
   :form nil
   :top-level true
   :o-tag nil
   :tag nil})

(ana.jvm/analyze
 '(fn [ub]
    (* (rand-int ub) scaling-factor))
 (ana.jvm/empty-env)
 {:passes-opts
  (assoc ana.jvm/default-passes-opts
         :validate/unresolvable-symbol-handler save-and-replace-with-nil)})

(println @free-variables) ;; => #{scaling-factor}
Run Code Online (Sandbox Code Playgroud)

它还将正确处理宏展开:

(defmacro produce-free-var []
  `(do unresolved))

(ana.jvm/analyze
 '(let [x :foo] (produce-free-var))
 (ana.jvm/empty-env)
 {:passes-opts
  (assoc ana.jvm/default-passes-opts
         :validate/unresolvable-symbol-handler save-and-replace-with-nil)})

(println @free-variables) ;; => #{scaling-factor unresolved}
Run Code Online (Sandbox Code Playgroud)