代码之家  ›  专栏  ›  技术社区  ›  Adam Schmideg

语法感知子串替换

  •  6
  • Adam Schmideg  · 技术社区  · 14 年前

    我有一个包含有效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 ,但我不知道怎么做。

    4 回复  |  直到 14 年前
        1
  •  2
  •   Michał Marczyk    14 年前

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

    (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"
    

    如果没关系的话我会很高兴的,但我想我得试着做点什么。。。 叹息

        2
  •  4
  •   Alex Miller    14 年前

    或者另一种选择是 ANTLR parse the Clojure 将代码转换为AST,然后转换AST,并导出回字符串。

        3
  •  2
  •   G__    14 年前

    您可以结合使用(读取字符串)和一些字符串操作来实现这一点:

    (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]]"
    

    这个示例没有处理换行符,但是我认为您可以用相同的方式处理这些换行符。

        4
  •  1
  •   Alex Miller    14 年前

    Clojure parser (使用fnparse用Clojure编写)。你也许可以用它把你从一个字符串变成另一个形式,然后操纵它,然后把它放回一个字符串?