Cirru 是一个使用语法树编辑器来编写代码, 以此绕开语法限制的方案.
目前成熟的编辑器方案有 Stack Editor 和 Cumulo Editor,
其中 Cumulo Editor 是我目前开发当中正在持续维护的, 用于开发 ClojureScript.git
Cirru Editor 首先会用 Vector 和 String 的递归结构来表示代码.
更进一步, 基于 Clojure 的 Namespace 设计, 对文件的组织方式进行了拆分,
好比下面是 Cirru Editor 存储格式的例子, 每一个文件被拆分红 ns
defs
proc
三部分:github
{"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/...mvc
整体上使用的格式仍是统一的, 依据如下规则:app
ns
defs
proc
拆分, 用 Map 存储ns
存储命名空间的定义defs
存储变量的定义, 用 Map 存储proc
存储脚本代码序列, 大多数为空的 Vector这个格式是为了编辑器操做的便利而设计, 并非最终代码,
于是也就须要编译出代码, 从而能够被其余编译器或解释器解析.
Cirru 格式的编译器须要作的事情有:编辑器
ns
defs
proc
进行组合拼接defs
存在依赖关系, 须要进行简单依赖分析和排序Cirru 目前对应的是动态类型的脚本语言, 好比 Clojure.
因为使用了语法树做为存储格式, 必定程度上会给代码分析带来方便,
但也因为动态语言缺乏类型提示, 实际上可供分析的信息有限,
从而难以提供模仿 VS Code 提供的代码提示功能和重构功能.布局
这个格式的好处是整个项目容易被放进 Cirru 的网页编辑器当中.
而后借助 Cirru Editor 的实现, 提供布局管理和实时同步.ui