首先,您永远不应该直接从不受信任的数据源中读取 clojure 代码。您应该改用 EDN 或其他序列化格式。
话虽如此,自 Clojure 1.5 以来,有一种无需评估即可读取字符串的安全方法。在使用 read-string 之前,您应该将read-eval var 绑定为 false。在 Clojure 1.4 和更早的版本中,这可能会导致调用 java 构造函数引起的副作用。这些问题已经得到解决。
这是一些示例代码:
(defn read-string-safely [s]
(binding [*read-eval* false]
(read-string s)))
(read-string-safely "#=(eval (def x 3))")
=> RuntimeException EvalReader not allowed when *read-eval* is false. clojure.lang.Util.runtimeException (Util.java:219)
(read-string-safely "(def x 3)")
=> (def x 3)
(read-string-safely "#java.io.FileWriter[\"precious-file.txt\"]")
=> RuntimeException Record construction syntax can only be used when *read-eval* == true clojure.lang.Util.runtimeException (Util.java:219)
关于阅读器宏
调度宏 (#) 和标记文字在读取时被调用。Clojure 数据中没有它们的表示,因为到那时这些构造都已被处理。据我所知,没有内置方法可以生成 Clojure 代码的语法树。
您将不得不使用外部解析器来保留该信息。您可以使用自己的自定义解析器,也可以使用 Instaparse 和 ANTLR 等解析器生成器。可能很难找到其中任何一个库的完整 Clojure 语法,但您可以扩展其中一个 EDN 语法以包含额外的 Clojure 形式。一个快速的谷歌揭示了 Clojure 语法的 ANTLR 语法,如果需要,您可以更改它以支持缺少的结构。
还有Sjacket是为 Clojure 工具制作的库,需要保留有关源代码本身的信息。这似乎很适合您尝试做的事情,但我个人对此没有任何经验。从测试来看,它的解析器确实支持阅读器宏。