【问题标题】:Modify Clojure source code file in clojure在clojure中修改Clojure源代码文件
【发布时间】:2011-08-17 18:08:33
【问题描述】:

我想知道是否可以在不编译的情况下将 Clojure .clj 源文件中包含的代码加载为列表。

如果我可以将 .clj 文件加载为列表,我可以修改该列表并将其漂亮地打印回同一个文件,然后可以再次加载。

(也许这是个坏主意。)有人知道这是否可能吗?

【问题讨论】:

  • 这不是一个坏主意。它可用于代码分析和重构等。

标签: file-io clojure


【解决方案1】:

这不是一个坏主意,它是 lisp 的主要属性之一,代码就是数据。 您可以使用读取字符串将 clj 文件作为列表读取并修改并写回。

(ns tmp (:require [clojure.zip :as zip]) (:use clojure.contrib.pprint)) (def some-var true) ;;stolen from http://nakkaya.com/2011/06/29/ferret-an-experimental-clojure-compiler/ (defn morph-form [tree pred f] (loop [loc (zip/seq-zip tree)] (if (zip/end? loc) (zip/root loc) (recur (zip/next (if (pred (zip/node loc)) (zip/replace loc (f (zip/node loc))) loc)))))) (let [morphed (morph-form (read-string (str \( (slurp "test.clj")\))) #(or (= 'true %) (= 'false %)) (fn [v] (if (= 'true v) 'false 'true)))] (spit "test.clj" (with-out-str (doseq [f morphed] (pprint f)))))

这会读取自身并切换布尔值并将其写回。

【讨论】:

  • 在 slurped 文本周围添加括号不是一个好主意。重复使用read 直到文件被使用,这样更干净、更健壮。
  • 酷!我要试试这个!谢谢!
【解决方案2】:

一个稍微简单的例子:

user=> (def a '(println (+ 1 1))) ; "'" escapes the form to prevent immediate evaluation
#'user/a
user=> (spit "test.code" a) ; write it to a file
nil

user=> (def from-file (read-string (slurp "test.code"))) ; read it from a file
#'user/from-file
user=> (def modified (clojure.walk/postwalk-replace {1 2} from-file)) ; modify the code
#'user/modified
user=> (spit "new.code" modified) ; write it back
nil
user=> (load-string (slurp "new.code")) ; check it worked!
4
nil

slurp 给你一个字符串,read-string 给你一个未评估的表格,load-string 给你评估表格的结果。

【讨论】:

    【解决方案3】:

    有一个名为 rewrite-clj 的库可以处理这类事情。

    https://github.com/xsc/rewrite-clj

    【讨论】:

      【解决方案4】:

      没有任何库,你想做的是

      (with-open [reader (java.io.PushbackReader. 
                           (clojure.java.io/reader "src/my/file/ns_path.clj"))]
        (loop [forms []]
          (try 
            (recur (conj forms (read reader)))
            (catch Exception ex
              (println (.getMessage ex))
              forms))))
      
      

      但是 Clojure 不允许 recur 跨越 try,所以你可以这样做

      (with-open [reader (java.io.PushbackReader. 
                           (clojure.java.io/reader "src/my/file/ns_path.clj"))]
        (loop [[ forms done? ] 
               [ []    false ]]
          (if done?
            forms
            (recur (try
                     [(conj forms (read reader)) false]
                     (catch Exception ex
                       (println (.getMessage ex))
                       [forms true]))))))
      

      一旦出现 EOF(文件结尾)错误,读取完成后,您需要自己传播 done? 信号。我尝试对 loop 绑定进行布局,以便它们从上到下清晰匹配。

      这将返回文件中所有表单的向量。从那里,您可以在表单上执行任何您想要的转换作为数据,然后再次将它们吐出/打印/写回文件。


      关于read的必要法律免责声明

      不要在您没有编写或检查自己的代码/数据上运行clojure.core/read。有关可能出错的示例,请参阅this ClojureDocs comment

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多