2010-08-12 31 views
6

Tôi có một chuỗi chứa biểu mẫu Clojure hợp lệ. Tôi muốn thay thế một phần của nó, giống như với assoc-in, nhưng xử lý toàn bộ chuỗi như mã thông báo.Thay thế chuỗi con nhận biết cú pháp

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

Tôi muốn viết string-assoc-in. Lưu ý rằng đối số đầu tiên và cuối cùng của nó là các chuỗi, và nó giữ ngắt dòng và dấu phẩy. Nó có thể làm được trong Clojure không? Điều gần nhất tôi tìm thấy là read gọi clojure.lang.LispReader, nhưng tôi không biết cách hoạt động.

Tôi muốn sử dụng nó để đọc tệp nguồn Clojure và hiển thị nó với một số sửa đổi, giữ cấu trúc của tệp.

+2

Tôi không thể nghĩ ra cách nào để làm điều này một cách đáng tin cậy mà không cần viết Reader của riêng bạn. –

+0

Âm thanh giống như một defmacro để * me * –

+0

@Paul Nathan: Trên thực tế, một macro Lisp có các thao tác xử lý chuỗi giống nhau theo ý của nó như là một hàm bình thường. "Toàn bộ ngôn ngữ luôn luôn có", như Paul Graham đặt nó. :-) –

Trả lời

2

Tôi nghĩ rằng điều này sẽ làm việc, hoàn toàn nói chung và không yêu cầu đọc riêng của mình/phân tích cú pháp:

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

Ví dụ:

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

Cập nhật: Ouch, bắt một lỗi:

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

Tôi rất thích nó nếu nó không quan trọng, nhưng tôi đoán tôi sẽ hav e để thử và làm điều gì đó về nó ... thở dài

+0

Tôi thích thủ thuật này với chia tách trên khoảng trắng, sau đó xen kẽ lại. Nó chỉ cho tôi một cách để làm điều đó mà không cần phải viết một người đọc. –

+0

Tôi không muốn viết một người đọc. Trớ trêu thay, suy nghĩ về câu trả lời của bạn đã khiến tôi viết một câu trả lời. –

1

Tôi giả sử bạn không muốn thực sự đọc trong biểu mẫu và đánh giá nó? fnparse có Clojure parser (được viết bằng Clojure sử dụng fnparse). Bạn có thể sử dụng nó để giúp bạn từ chuỗi thành dạng, sau đó thao tác, sau đó đặt nó trở lại chuỗi?

2

Bạn có thể làm điều này với một sự kết hợp của (string đọc) và một số chuỗi thao tác:

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

Lưu ý rằng chúng tôi yêu cầu một nhân vật giữ chỗ dành riêng (trong trường hợp này, _) mà bạn sẽ không muốn trong từ khóa của bạn. Bí quyết là để có được những người, trên con đường khi người đọc được crunching trên chuỗi vector, sau đó đưa chúng trở lại.

Mẫu này không giải quyết các dòng mới, nhưng tôi nghĩ rằng bạn có thể xử lý các mẫu đó theo cùng một cách.

+0

Tôi không làm theo - '(cho [s" [: a [: b ,,: c]] "] (chuỗi-assoc-in s [1 0]": mới "))' hoạt động tốt? Tuy nhiên, tôi đồng ý rằng macro là không cần thiết và chức năng cũng sẽ hoạt động tốt (macro là một tạo tác từ các giải pháp của tôi), vì vậy tôi sẽ chỉnh sửa câu trả lời để sử dụng defn. – Greg

+1

@all: Greg đang trả lời nhận xét trong đó tôi đã nhầm lẫn cho rằng những điều trên không có tác dụng. Tôi sẽ thay thế nó bằng một phiên bản sửa đổi - bằng cách đăng bình luận dài hơn một chút và xóa bản gốc - nhưng, trong một sai lầm tuyệt đẹp, tôi đã nhấp xóa * đầu tiên *. Xin lỗi, không phải là cách để đi sau khi bình luận được lên cho một vài phút. * thở dài * @Greg: Bạn nói đúng, dù sao đi nữa, xin lỗi vì sự nhầm lẫn. –

+1

Upvoted này cho tôi một ý tưởng cho giải pháp của tôi, tuy nhiên bây giờ tôi thấy rằng nó thể hiện lỗi tương tự/rất giống mà tôi đã phát hiện ra trong mã của tôi (ví dụ như '(string-assoc-in" [: a [: b ,,: c ,,]] "[1 0]": mới ")' hoặc '[: b ,,]' hoặc '[: b ,,: c]' ...). Không có một trình đọc phân tích cú pháp/đặc biệt cho mục đích này. –

4

Hoặc một tùy chọn khác là sử dụng mã số ANTLR đến parse the Clojure vào AST, sau đó chuyển AST và xuất trở lại chuỗi.

+0

À, đây có thể là cách tiếp cận tốt nhất ... Ngữ pháp của CCW có khả năng được toàn diện và được duy trì tốt (và theo cách đó theo thời gian!). Tuy nhiên, ANTLR-fu của tôi vẫn còn quá yếu đối với tôi để biết cách trích xuất nội dung được đặt trên "kênh ẩn". Tôi nghĩ rằng người lexer thấy điều đó, nhưng người phân tích cú pháp thì không ...? –

+0

Tôi không biết có một tập tin ngữ pháp Clojure cho ANTLR, nhờ con trỏ. Tuy nhiên, tôi thích một giải pháp Clojure thuần túy hơn. –

Các vấn đề liên quan