关于 Cirru Editor 存储格式

Cirru 是一个使用语法树编辑器来编写代码, 以此绕开语法限制的方案.
目前成熟的编辑器方案有 Stack EditorCumulo Editor,
其中 Cumulo Editor 是我目前开发当中正在持续维护的, 用于开发 ClojureScript.

Cirru Editor 首先会用 Vector 和 String 的递归结构来表示代码.
更进一步, 基于 Clojure 的 Namespace 设计, 对文件的组织方式进行了拆分,
比如下面是 Cirru Editor 存储格式的例子, 每个文件被拆分成 ns defs proc 三部分:

{"app.comp.container" {:ns ["ns"
                            "app.comp.container"
                            [":require"
                             ["[]" "respo.comp.space" ":refer" ["[]" "=<"]]]],
                       :defs {"comp-container" ["defcomp"
                                                "comp-container"
                                                ["store"]],
                              "on-click" ["defn" "on-click" ["e" "dispatch!"] ["dispatch!" ":inc" "nil"]]},
                       :procs []},
 "app.main" {:ns ["ns"
                  "app.main"
                  [":require"
                   ["[]" "app.schema" ":as" "schema"]]],
             :defs {"dispatch!" ["defn"
                                 "dispatch!"
                                 ["op" "op-data"]],
                    "*store" ["defonce" "*store" ["atom" "schema/store"]],
                    "ssr?" ["def" "ssr?" ["some?" ["js/document.querySelector" "|meta.respo-ssr"]]],
                    "main!" ["defn"
                             "main!"
                             []
                             ["println" "|App started."]],
                    "mount-target" ["def" "mount-target" [".querySelector" "js/document" "|.app"]]},
             :procs [["set!" [".-onload" "js/window"] "main!"]]},
 "app.updater.core" {:ns ["ns" "app.updater.core"],
                     :defs {"updater" ["defn"
                                       "updater"
                                       ["store" "op" "op-data"]
                                       ["case" "op" [":inc" ["update" "store" ":data" "inc"]] "store"]]},
                     :procs []},
 "app.schema" {:ns ["ns" "app.schema"],
               :defs {"store" ["def" "store" ["{}" [":states" ["{}"]] [":data" "0"]]]},
               :procs []}}

完整的例子, 比如 Stack Editor 使用的存储格式, 在 namespace 稍微有区别:
https://github.com/mvc-works/...
而 Cumulo Editor 使用的格式存储了更多的信息(因而没有做格式化):
https://github.com/mvc-works/...

总体上使用的格式还是统一的, 依据以下规则:

  • 文件结构按照 namespace 划分, 用 Map 存储
  • 每个文件当中按照 ns defs proc 拆分, 用 Map 存储
    • ns 存储命名空间的定义
    • defs 存储变量的定义, 用 Map 存储
    • proc 存储脚本代码序列, 大多数为空的 Vector
  • 代码对应 S-Expression, 语法强行拆减

这个格式是为了编辑器操作的便利而设计, 并不是最终代码,
因而也就需要编译出代码, 从而可以被其他编译器或解释器解析.
Cirru 格式的编译器需要做的事情有:

  • 对 namespace 进行转换生成文件
  • 单个文件内对 ns defs proc 进行组合拼接
    • 其中 defs 存在依赖关系, 需要进行简单依赖分析和排序
  • 解释 S-Expression 生成目标语言源码

Cirru 目前对应的是动态类型的脚本语言, 比如 Clojure.
由于使用了语法树作为存储格式, 一定程度上会给代码分析带来方便,
但也由于动态语言缺少类型提示, 实际上可供分析的信息有限,
从而难以提供模仿 VS Code 提供的代码提示功能和重构功能.

这个格式的好处是整个项目容易被放进 Cirru 的网页编辑器当中.
然后借助 Cirru Editor 的实现, 提供布局管理和实时同步.

阅读 970

推荐阅读
题叶
用户专栏

ClojureScript 爱好者.

496 人关注
251 篇文章
专栏主页