這篇文章解釋一下 Cirru 到 Clojure 代碼的編譯步驟.
目前編輯使用的是 Cumulo Editor, 參考這裏的 Demo.
Cirru 是整個大的項目的名字, Cirru 自己的目標是 AST 編程,
而 Cumulo Editor 是目前項目下最新的語法樹編輯器. 也就是 AST 裏的 Syntax Tree.
因此在新聞中你看到的 Cirru 的存在形態, 目前是這樣的:html
這個函數表示的是下面這段 Clojure 代碼:git
(defcomp comp-search (buffer query mock-ssr?) (div {:style (merge ui/column-center style-searcher)} (div {} (if mock-ssr? (div {:style style-mock} (<> span "Loading..." nil)) (input {:style (merge ui/input style-input), :value buffer, :placeholder "Press Enter to search...", :on {:input on-search, :keydown (on-keydown buffer)}}))) (=< nil 16) (if (>= (count query) 2) (let [results (->> information (filter (fn [item] (string/includes? (string/lower-case (:title item)) (string/lower-case query)))))] (if (empty? results) (div {:style style-empty} (<> span (str "找不到" (pr-str query) ".") nil)) (div {:style style-results} (->> results (map-indexed (fn [idx item] [idx (comp-item item)]))))))) (=< nil 16) (if (>= (count query) 2) (comp-search-engine query))))
這中間有比較複雜的編譯過程. 我首先把上面的內容, 簡化.
好比圖片當中的 DOM, 在瀏覽器當中其實是 HTML, 大體能夠表示成:github
<div> <input value="defcomp"> <input value="comp-search"> <div></div> <div> <input value="div"> </div> </div>
這個結構用 JSON 表示的話, 能夠是:編程
["defcomp", "comp-search", [], ["div"]]
這在 Clojure 裏, 相似的 JSON 的數據格式叫作 EDN, 和 JSON 類似, 看上去這樣:json
["defcomp" "comp-search" [] ["div"]]
不過實際當中考慮到原信息和優化方案, 這個結構其實很複雜
好比說這 "comp-search"
實際存儲當中的會帶上不少信息:瀏覽器
{:type :leaf, :author "root", :time 1503305265979, :text "comp-search", :id "B1_VAM_Ob"}
完整的結構很是繁瑣, 須要藉助工具才能查看, 若是非要查看的話:
https://github.com/tiye/tiye....
http://pretty-print.net/編輯器
歸結一下來講, 大體上 Cirru 在程序中使用和最後存儲的格式對應是這樣:函數
["defcomp" "comp-search" [] ["div"]]
這是 S-Expression 的一套簡寫. 只要用 Vector 和 String 就能直白地表示.
雖然 Clojure 語法不單單是 S-Expression, 可是說大部分能夠用 S-Expression 表示沒問題.
固然, 這樣確定不能表示 Macros, 也不能表示各類 Reader 生成的語法.
實際開發當中能夠和 Clojure 代碼混用, 好在不少代碼其實這套方案是足夠的.工具
還要考慮的一個問題是 Cirru 當中爲了統一前綴表達式約定了一些寫法,
在 Clojure 當中存在慣用的寫法, 這就須要預先進行轉換了.
好比 Cirru 當中 ["{}" [":a" "1"]]
對應 {:a 1}
, ["[]" "1"]
對應 [1]
,
甚至字符串標記 "|str1"
對應 "str1"
, 都須要作轉換.
爲了完成這個功能, 我寫了一個模塊專門在 Symbol 層面作了數據轉換:
https://github.com/Cirru/sepa...佈局
在這個模塊當中, 甚至的 case
let
defn
等函數或特殊格式作了調整,
某種程度上和在 Clojure 當中習慣是不一致的, 可是在 Cirru Editor 當中很更清晰.
也是得益於 Lisp 整個語法設計的精巧, 才能很容易達成.
生成代碼反而是簡單的, 因爲現成的類庫比較強大:
https://github.com/brandonblo...
fipp 能夠接受 Symbol 類型的 S-Expression, 返回格式化完成的代碼.
並且這個代碼的佈局和縮進都比較符合人們閱讀的習慣.
這是 fipp 給出的一個將 Quote List 編譯到 Clojure 代碼字符串的 API:
(fipp.clojure/pprint '(let [foo "abc 123" bar {:x 1 :y 2 :z 3}] (do-stuff foo (assoc bar :w 4))) {:width 40})
因此總結下來, 從最初的 Cirru Editor 採用的存儲格式:
["defcomp" "comp-search" [] ["div" ["{}" [":style" "style-searcher"]]]]
首先會在 Symbol 層面進行一些轉化, 獲得一個 Quoted List:
'(defcomp comp-search () (div {:style style-searcher}))
最後由 fipp 生成帶縮進的代碼字符串:
(defcomp comp-search () (div {:style style-searcher}))