【问题标题】:Reading another Clojure program as a list of S-Expressions读取另一个 Clojure 程序作为 S-Expressions 列表
【发布时间】:2016-10-12 23:28:36
【问题描述】:

假设我在磁盘上有一个非常简单的.clj 文件,其内容如下:

(def a 2)
(def b 3)
(defn add-two [x y] (+ x y))
(println (add-two a b))

从单独程序的上下文来看,我想将上述程序作为 S-Expressions 的列表阅读,'((def a 2) (def b 3) ... (add-two a b)))

我想这样做的一种方法涉及 1. 在 (io/file file-name.clj) 上使用 slurp 来生成包含文件内容的字符串,2. 将该字符串传递给 Clojure 代码的解析器,以及 3. 注入生成的序列由解析器发送到一个列表(即(into '() parsed-code))。

但是,这种方法似乎有点笨拙且容易出错。 有人知道将 Clojure 文件作为 S-Expressions 列表读取的更优雅和/或惯用的方法吗?

更新:根据 cmets 部分的反馈,我决定尝试使用 aphyr 的 clj-antlr 在实际源文件中提到的方法,如下所示:

=> (def file-as-string (slurp (clojure.java.io/file "src/tcl/core.clj")))
=> tcl.core=> (pprint (antlr/parser "src/grammars/Clojure.g4" file-as-string))
{:parser
 {:local
  #object[java.lang.ThreadLocal 0x5bfcab6 "java.lang.ThreadLocal@5bfcab6"],
  :grammar
  #object[org.antlr.v4.tool.Grammar 0x5b8cfcb9 "org.antlr.v4.tool.Grammar@5b8cfcb9"]},
 :opts
 "(ns tcl.core\n  (:gen-class)\n  (:require [clj-antlr.core :as antlr]))\n\n(def foo 42)\n\n(defn parse-program\n  \"uses antlr grammar to \"\n  [program]\n  ((antlr/parser \"src/grammars/Clojure.g4\") program))\n\n\n(defn -main\n  \"I don't do a whole lot ... yet.\"\n  [& args]\n  (println \"tlc is tcl\"))\n"}
nil

有谁知道如何将此输出转换为最初预期的 S-Expressions 列表?也就是说,如何从使用 clj 解析的结果中压缩有效的 Clojure 代码/数据-antlr?

【问题讨论】:

  • 请检查stackoverflow.com/a/24576026/597473 是否还没有回答您的问题。
  • @PiotrekBzdyl 感谢您指出之前的问题。我自己永远也不会发现。当我有时间时,我会根据线索提交一个答案,但你可以自己做。由于之前的问题似乎有点悬而未决,所以我将暂时留下我的问题。
  • 这种方法对我来说似乎很简单。为什么你认为它“笨拙且容易出错”?
  • @OlegTheCat 我想在任意代码上信任来自核心外部的解析器似乎是乐观的。如果您不这么认为,一旦我考虑了桌面上的选项,我将很乐意分享我的代码。另外,如果你知道的话,请分享一个值得信赖的解析器。
  • @PiotrekBzdyl 上一篇文章中接受的答案似乎没有按预期工作:例如,在 (slurp "sample.clj 的结果上调用定义的函数 read-string-safely ") 似乎只返回代码的第一行,(def a 2)。

标签: clojure


【解决方案1】:
(import '[java.io PushbackReader])
(require '[clojure.java.io :as io])
(require '[clojure.edn :as edn])

;; adapted from: http://stackoverflow.com/a/24922859/6264
(defn read-forms [file]
  (let [rdr (-> file io/file io/reader PushbackReader.)
        sentinel (Object.)]
    (loop [forms []]
      (let [form (edn/read {:eof sentinel} rdr)]
        (if (= sentinel form)
          forms
          (recur (conj forms form)))))))

(comment
  (spit "/tmp/example.clj"
        "(def a 2)
(def b 3)
(defn add-two [x y] (+ x y))
(println (add-two a b))")

  (read-forms "/tmp/example.clj")
  ;;=> [(def a 2) (def b 3) (defn add-two [x y] (+ x y)) (println (add-two a b))]
)

【讨论】:

    【解决方案2】:

    你需要这样的东西吗?

    (let [exprs (slurp "to_read.clj")]
      ;; adding braces to form a proper list
      (-> (str "("  (str  exprs")"))
      ;; read-string is potentially harmful, since it evals the string
      ;; there exist non-evaluating readers for clojure but I don't know
      ;; which one are good 
          (read-string)
          (prn)))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-05
      • 1970-01-01
      • 1970-01-01
      • 2022-11-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多