语法感知的子字符串替换

发布于 2024-09-13 19:02:37 字数 558 浏览 9 评论 0原文

我有一个包含有效 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 源文件并进行一些修改后显示它,同时保留文件的结构。

I have a string containing a valid Clojure form. I want to replace a part of it, just like with assoc-in, but processing the whole string as tokens.

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

I want to write string-assoc-in. Note that its first and last arguments are strings, and it keeps the line break and the commas. Is it doable in Clojure? The closest thing I found is read which calls clojure.lang.LispReader, but I don't know how works.

I want to use it to read a Clojure source file and display it with some modifications, keeping the structure of the file.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

人生百味 2024-09-20 19:02:37

或者另一种选择是使用 ANTLR将 Clojure 代码解析为 AST,然后转换 AST,然后导出回字符串。

Or another option would be to use ANTLR to parse the Clojure code into an AST, then transform the AST, and export back out to a string.

给不了的爱 2024-09-20 19:02:37

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

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

如果不重要的话我会喜欢它,但我想我得尝试做点什么......叹气

I think this should work, be entirely general and not require its own reader / parser:

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

Example:

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

Update: Ouch, caught a bug:

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

I'd love it if it didn't matter, but I guess I'll have to try and do something about it... sigh

小镇女孩 2024-09-20 19:02:37

您可以通过 (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]]"

请注意,我们需要一个保留的占位符字符(在本例中为 _),您不希望在关键字中使用它。诀窍是,当读者正在处理向量字符串时,将这些 ,, 移开,然后将它们放回去。

该示例不涉及换行符,但我认为您可以以相同的方式处理这些换行符。

You could do this with a combination of (read-string) and some string manipulation:

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

Note that we require a reserved placeholder character (in this case, _) which you wouldn't want in your keywords. The trick is to get those ,, out of the way when the reader is crunching on the vector string, then put them back.

This sample doesn't address the newlines, but I think you could handle those in the same way.

清浅ˋ旧时光 2024-09-20 19:02:37

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

I'm assuming you don't want to actually read in a form and evaluate it? fnparse has a Clojure parser (written in Clojure using fnparse). You might be able to use that to get you from string to form, then manipulate, then put it back out to a string?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文