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/

其它

此時此刻,雖然我翻過幾本編譯相關的書籍,我也並不是一個編譯原理相關的專家。因此,若是你也有興趣,歡迎來加入咱們。

相關文章
相關標籤/搜索