Cirru 是怎樣編譯到 Clojure 的

這篇文章解釋一下 Cirru 到 Clojure 代碼的編譯步驟.
目前編輯使用的是 Cumulo Editor, 參考這裏的 Demo.
Cirru 是整個大的項目的名字, Cirru 自己的目標是 AST 編程,
而 Cumulo Editor 是目前項目下最新的語法樹編輯器. 也就是 AST 裏的 Syntax Tree.
因此在新聞中你看到的 Cirru 的存在形態, 目前是這樣的:html

Cirru Cumulo Editor showcase

這個函數表示的是下面這段 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}))
相關文章
相關標籤/搜索