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}))
相关文章
相关标签/搜索