语法感知子串替换

时间:2010-08-12 21:52:29

标签: string syntax clojure replace tokenize

我有一个包含有效Clojure表单的字符串。我想替换它的一部分,就像使用assoc-in一样,但是将整个字符串作为标记处理。

=> (assoc-in [:a [:b :c]] [1 0] :new)
[:a [:new :c]]
=> (assoc-in [:a 
                [:b,, :c]] [1 0] :new)
[:a [:new :c]]
=> (string-assoc-in "[:a 
                       [:b,, :c]]" [1 0] ":new")
"[:a 
   [:new,, :c]]"

我想写string-assoc-in。请注意,它的第一个和最后一个参数是字符串,它保留换行符和逗号。它在Clojure中可行吗?我发现最接近的是read,它会调用clojure.lang.LispReader,但我不知道它是如何工作的。

我想用它来读取Clojure源文件,并通过一些修改显示它,保留文件的结构。

4 个答案:

答案 0 :(得分:4)

或者另一个选择是将ANTLRparse the Clojure代码用于AST,然后转换AST,然后导出回字符串。

答案 1 :(得分:2)

你可以用(read-string)和一些字符串操作的组合来做到这一点:

(defn string-assoc-in
  [a b c]
  (.replaceAll
    (str
     (assoc-in (read-string (.replaceAll a ",," ",_,")) b (read-string c)))
    " _ " ",, "))

user> (string-assoc-in "[:a [:b,, :c]]" [1 0] ":new")
"[:a [:new,, :c]]"

请注意,我们需要一个您不希望在关键字中使用的保留占位符字符(在本例中为_)。诀窍在于,当读者在矢量字符串上进行处理时,将它们放在一边,然后将它们放回去。

此示例未解决换行符,但我认为您可以采用相同的方式处理这些换行符。

答案 2 :(得分:2)

我认为这应该有用,完全是通用的,不需要自己的读者/解析器:

(defn is-clojure-whitespace? [c]
  (or (Character/isSpace c)
      (= \, c)))

(defn whitespace-split
  "Returns a map of true -> (maximal contiguous substrings of s
  consisting of Clojure whitespace), false -> (as above, non-whitespace),
  :starts-on-whitespace? -> (whether s starts on whitespace)."
  [s]
  (if (empty? s)
    {}
    (assoc (group-by (comp is-clojure-whitespace? first)
                     (map (partial apply str)
                          (partition-by is-clojure-whitespace? s)))
      :starts-on-whitespace?
      (if (is-clojure-whitespace? (first s)) true false))))

(defn string-assoc-in [s coords subst]
  (let [{space-blocks true
         starts-on-whitespace? :starts-on-whitespace?}
        (whitespace-split s)
        s-obj (assoc-in (binding [*read-eval* false] (read-string s))
                        coords
                        (binding [*read-eval* false] (read-string subst)))
        {non-space-blocks false}
        (whitespace-split (pr-str s-obj))]
    (apply str
           (if starts-on-whitespace?
             (interleave space-blocks (concat non-space-blocks [nil]))
             (interleave non-space-blocks (concat space-blocks [nil]))))))

示例:

user> (string-assoc-in "[:a [:b,, :c]]" [1 0] ":new")
"[:a [:new,, :c]]"

更新:哎呀,抓到了一个错误:

user> (string-assoc-in "[:a [:b,, :c\n]]" [1 0] ":new")
"[:a [:new,, :c]]\n"

如果没关系,我会喜欢它,但我想我必须尝试做点什么...... 叹息

答案 3 :(得分:1)

我假设您不想真正阅读表格并对其进行评估? fnparse有一个Clojure parser(使用fnparse用Clojure编写)。您可以使用它来从字符串到表单,然后操作,然后将其放回字符串?