Синтаксическая замена подстроки - PullRequest
6 голосов
/ 13 августа 2010

У меня есть строка, содержащая действительную форму 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 ]

4 голосов
/ 13 августа 2010

Или можно использовать ANTLR - , анализировать код Clojure в AST, затем преобразовывать AST и экспортировать обратно в строку.

2 голосов
/ 13 августа 2010

Я думаю, что это должно работать, быть полностью общим и не требовать собственного читателя / парсера:

(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 голосов
/ 13 августа 2010

Вы можете сделать это с помощью комбинации (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]]"

Обратите внимание, что нам требуется зарезервированный символ-заполнитель (в данном случае _), который вы бы не сталихочу в ваших ключевых словах.Хитрость заключается в том, чтобы избавиться от них, когда читатель хрустит векторной строкой, а затем поместить их обратно.

В этом примере не рассматриваются новые строки, но я думаю, что вы могли бы справиться с ними втак же.

1 голос
/ 13 августа 2010

Я предполагаю, что вы не хотите читать в форме и оценивать ее?У fnparse есть синтаксический анализатор Clojure (написанный на Clojure с использованием fnparse).Вы могли бы использовать это, чтобы вывести вас из строки в форму, затем манипулировать, а затем вернуть обратно в строку?

...