Charj —— 代码的代码化语言

去年,和公司的大佬讨论了一系列关于代码的代码化,还记录了一些笔记。在那以后,我开始了各类尝试:如何将代码转变化代码。原先有一些思路,然后过了一年以后,慢慢地练习,又有了一些新的收获。html

咱们想要作的事情是:把任意的 A 语言转换为任意的 B 语言(PS:这里的任意 A 和任意 B 语言都是主流语言)。如此一来,咱们即可以:前端

  1. 快速重写任何的系统。
  2. 与编程语言无关的领域建模。
  3. 产生一个更强大的 DSL。
  4. 建立新的语言。

引子 0:统一语言模型

统一语言模型,即对不一样的比编程语言进行抽象,使用同一套数据结构描述编程语言。java

在我使用了 Golang + Antlr 实现了 Coca 以后,我意识到这是一条可行的方案。可是,因为 Coca 的架构和用途所限,外加之 Antlr 对于 Java 的支持远比 Go 要好,我并无继续在 Coca 上实施这个方案。git

因而乎,我开始了第二个尝试,使用 Kotlin + Antlr 来实现对不一样语言的模型统一,也就是个人另一个开源项目 Chapi。可是呢,随着不断的尝试,我发现了其中的难度和工做量比较大:github

  1. 编写不一样语言的语法解析。社区上已经有大量的成熟的轮子,其中最出名的就是 Antlr 相关的语法解析。官方维护的代码仓库(grammars-v4)包含了大量的 Antlr 语法解析案例,能够找到市面上一些主流的和非主流的实现。
  2. 设计统一语言模型。即设计出一套能兼容不一样语言的语言模式。固然了,这是一个持续完善的过程,会随着更多语言的加入,变得更加完整和复杂。
  3. 解析不一样语言。即根据不一样语言的语法特性,转换为上述的模型。

从难度上来讲,咱们能够看出技术难度主要是在步骤 1 和步骤 2。而步骤 3 呢,则是一个很是繁琐、工做量巨大的体力活。咱们还须要熟悉不一样的编程语言,并一一解析对应的字段,才能转换每个语言。正则表达式

所以,我尝试创建起了 Chapi 的社区,而后手把手带领一群人干活。尽管,对于不一样的语言我已经创建起了统一的编写模式:TDD + Tasking。彷佛,不少人对于 AST 有点担忧,所以参与的人很是少。因此,对于其它语言的支持就不了了之。算法

相关资源:编程

引子 1:语法高亮的背后

与此同时,哪怕有足够的人,Antlr 并不是一个完美的答案。在编写不一样语言的支持时,我依旧遇到一系列的 Antlr 语法不支持的问题。如 JavaScript 的 Import,Java 的一些 Lambda 问题……。换句话来讲,Antlr 官方只是维护这么一个库,真实的效果就不得而知了。后端

因而,我就回到了一条老路上,使用正则——固然不会本身写了。在那篇《编程语言的 IDE 支持》中,我提到了基于正则表达式来实现语法分析,其中介绍了两个编辑器的实现方式:api

因此,咱们选择了 VSCode 做为了语法解析背后的语言。在这种模式之下:

  1. 咱们有一个成熟稳定的语言解析工具,而且也有一个巨大的团队在维护它们。
  2. 它的社区是很是庞大的,通过大量的反复提高。

所以,我和个人同事从几个前开始编写:github.com/phodal/scie… —— 一个基于 TextMate 语法高亮的库。

引子 2:代码生成与 JavaPoet

在咱们粗糙地完成了 Scie 以后,我开始思考着下一步:如何从 A 语言转换为 B 语言的时候,我从 JavaPoet 获取到了一些灵感。JavaPoet 是一个用来生成 .java 源文件的 Java API。以下是一个简单的 JavaPoet 代码示例:

TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
    .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
    .addMethod(main)
    .build();
复制代码

也就是说,咱们能够写一个 API,以将某语言转换为 B 语言的源码。而要实现任意语言的转换,那么咱们就须要实现一个 DSL:用于描述不一样语言与统一模型的差别。后来,我意识到我还须要另一个 DSL,用于转换统一模型到不一样的语言

引子 3:中间表示的演变

编译器的核心数据结构是被编译程序的中间形式。 —— 《编译器设计》

理论上,经过上述的两种方式,咱们就能够直接生成不一样领域的模型。可是呢,为了调试方便,能够建立一个中间语言来做为它们的承载物,可让咱们实现更有意思的事情,去统一进行编译器优化——固然,我是瞎说的。

随后项目的缘由,我研究了一小段时间的 Proguard + D8 和 Android R8 的实现上。它们两作的事情是类似的,将 .class 字节码,编译优化,再转换成 Android 手机上的 dex。固然了,转换为 Aot 就是一个更有意思的话题了(虽然我也不熟悉)。可是呢,这期间涉及到了一系列的中间状态:java -> .class -> .dex -> odex -> .oat。即从 Java 代码到 JVM 虚拟机字节码 -> Dalvik 虚拟机字节码 -> 优化事后的 Dalvik 字节码 -> ART 机器码。

而咱们再回过到来看,编码语言自己也是一种中间表示,由于机器运行的是靠机器码。即,那句经典的话:代码是写给人看的

引子 4:DSL 的 DSL

对于有的编译器来讲 ,它们可能有惟一的 IR(中间表示,Intermediate representation),也可能会有一系列的 IR。最多见的一些实现,即是咱们看到的那些使用 LLVM 做为后端的语言,它们能够生成中间形式的 LLVM IR。一样的对于咱们想作到的事来讲,咱们能够设计一个相似于 LLVM IR 的高级中间表示,用于承载语言的设计。

因为项目涉及到一丁点的代码优化,因此我还阅读了一下那本《高级编译器设计与实现》,书中引入了 ICAN 这个中间语言。嗯,这就是已经被论证的结果了,再也不须要我去论证它的必要性。因此下一步就是:

自举,在计算机科学中,它是一种用于生成自编译编译器的技术,即便用打算编译的源编程语言编写的编译器。

在业内,人们每每往把自举定义在编译器领域中。可是呢,它能够在更多的领域被应用。例如 Java 的构建工具,Gradle 使用 Gradle 来构建本身 —— 固然与编程语言相比,这事要相对容易一些。

而人的自举就是把本身替换便,让工具作了本身的事,让别人作得了本身的事。因此,咱们就须要 Charj 来作本身所能作的事情。

Charj Lang

终于回到了正题上了,在有了上面的几步以后,咱们就能:

  1. 经过正则表达式,解析、生成不一样语言的语法树。
  2. 编写 Poet API 将上述的语法树,转换为某一特定语言源码。
  3. 设计某一中间语言,用来做为 A 语言转换为 C 语言的载体。
  4. 实现 A 语言到 C 语言,又或者 C 语言到 A 语言的自由转换。

这即是从任意语言转换为任意语言的想法和思路。因而乎,我和个人同事们开始设计一个中间语言:Charj。

固然了,开发一个语言的目的主要是为了锻炼本身的能力,不管是抽象能力,仍是算法能力等等。在这个漫长的人生里, 它将会变得有意义。之后,请叫我 Charj 语言做者。PS:你也能够是 Charj 语言做者。

回过头来看,事实上应该是这样的,我已经尝试造了各式各样的工具,从各种的编辑器到各种的命令行工具。而在学习了 Rust 以后,我研究了 JVM、编辑器底层,也正在逐一尝试建立平常所使用的工具。而在上一年里,由于编写重构工具 Coca,再到随后的转换为统一语言模型的 Chapi。对于编译器前端,我已经有了至关丰富的经验。天然而然的,创造一个语言就成了下一个方向。

为何叫 Charj ?

从本义上来讲,Char 是更适合 Charj 的定义的,可是 Char(仓颉)的商标已经被注册了。退而求其次,我只好叫 Charj,能够引申为中英混合式的:字符(Char)集(Ji),又或者是字符(Char)集(姬)。又或者是『字符 J』 —— 至于 J 是什么意思,我还没想清楚。咱们能够再定义,再取一个新的名字。

Charj 进展

Charj 使用的是 Rust 为主的语言编写的。Rust 的自举已经证实了:Rust 用于开发编程语言是没有问题的。固然了,主要缘由还在于让我 C++,还不如让我写 Haskell。

Charj Lang (设计中)

Charj lang 如今的工做分为两部分:

  1. 完善语法设计
  2. 编译器的流程设计

尽管从理论上来讲,Charj 不必定须要编译 + 可运行,可是为了自举,咱们须要它们。因而,咱们在后端采用了 LLVM,前端使用的是 Rust 里的 LR(1)解析器生成器 lalrpop

GitHub:github.com/charj-lang/…

Charj IDE(开发中)

当前已经有一个简单的语言插件,固然只有基本的高亮和跳转功能。若是你有必定的 IDEA 插件开发经验,也能够来咱们一块儿搞搞。

GitHub:github.com/charj-lang/…

Scie

Scie(Simple Code Identify Engine)是一个基于正则表达式的通用语言转换器。主要开发工做基本已经完成了,可是有几个问题须要解决:

  1. 效率优化
  2. 调用 Oniguruma FFI 时会随机出错。

GitHub:github.com/charj-lang/…

Charj Poet(开发中)

Charj Poet 是一个是用于生成 Charj 代码的 Rust API。计划等语法设计完,再进一步完善。

GitHub:github.com/charj-lang/…

Poet DSL(待定)

两部分:

  1. 即设计一个新的 DSL,来描述不一样语言转换为 Charj Lang 的 DSL。
  2. 即设计一个新的 DSL,来描述 Charj Lang 转换为不一样语言的 DSL。

官网

简陋和粗糙的官网:charj-lang.org/

其它

此时此刻,虽然我翻过几本编译相关的书籍,我也并不是一个编译原理相关的专家。因此,若是你也有兴趣,欢迎来加入咱们。

相关文章
相关标签/搜索