創建可用的 JVM 語言:概述

已發表: 2022-03-11

創建一種語言有幾個可能的原因,其中一些不是很明顯。 我想將它們與一種方法一起介紹,以便為 Java 虛擬機 (JVM) 開發一種盡可能多地重用現有工具的語言。 通過這種方式,我們將減少開發工作並提供用戶熟悉的工具鏈,從而更容易採用我們的新編程語言。

創建可用的 JVM 語言:概述

在本系列的第一篇文章中,我將概述為 JVM 創建我們自己的編程語言所涉及的策略和各種工具。 在以後的文章中,我們將深入探討實現細節。

為什麼要創建你的 JVM 語言?

已經有無數種編程語言。 那麼為什麼要費心創造一個新的呢? 對此有很多可能的答案。

首先,有許多不同種類的語言:您想創建通用編程語言 (GPL) 還是特定領域的語言? 第一種包括像 Java 或 Scala 這樣的語言:旨在為大量問題編寫足夠體面的解決方案的語言。 相反,領域特定語言 (DSL) 專注於很好地解決一組特定的問題。 想想 HTML 或 Latex:您可以在屏幕上繪圖或用 Java 生成文檔,但這會很麻煩,而使用這些 DSL,您可以非常輕鬆地創建文檔,但它們僅限於特定領域。

因此,也許有一組您經常工作的問題,並且為這些問題創建 DSL 可能是有意義的。 一種語言,可以讓你在一遍又一遍地解決相同類型的問題時非常高效。

相反,也許您想創建一個 GPL,因為您有一些新想法,例如將關係表示為一等公民或表示上下文。

最後,您可能想要創建一種新語言,因為它很有趣、很酷,而且您將在此過程中學到很多東西。

事實是,如果您以 JVM 為目標,您可以輕鬆獲得可用的語言,這是因為:

  • 您只需要生成字節碼,您的代碼將在所有有 JVM 的平台上可用
  • 您將能夠利用 JVM 現有的所有庫和框架

因此,在 JVM 上開發語言的成本大大降低,在 JVM 之外不經濟的場景中創建新語言可能是有意義的。

你需要什麼使它可用?

您絕對需要一些工具來使用您的語言——解析器和編譯器(或解釋器)就在這些工具中。 然而,這還不夠。 為了使您的語言在實踐中真正可用,您需要提供工具鏈的許多其他組件,可能與現有工具集成。

理想情況下,您希望能夠:

  • 管理對從其他語言編譯為 JVM 的代碼的引用
  • 使用語法高亮、錯誤識別和自動完成功能在您喜歡的 IDE 中編輯源文件
  • 您希望能夠使用您最喜歡的構建系統編譯文件:maven、gradle 或其他
  • 您希望能夠編寫測試並將其作為持續集成解決方案的一部分運行

如果你能做到這一點,採用你的語言會容易得多。

那麼我們怎樣才能做到這一點呢? 在這篇文章的其餘部分,我們將研究實現這一目標所需的不同部分。

解析和編譯

在程序中轉換源文件需要做的第一件事是解析它們,獲得代碼中包含的信息的抽象語法樹 (AST) 表示。 此時您需要驗證代碼:是否存在語法錯誤? 語義錯誤? 您需要找到所有這些並將它們報告給用戶。 如果一切順利,您仍然需要解析符號。 例如,“List”是指java.util.List還是java.awt.List ? 當你調用一個重載的方法時,你調用的是哪一個? 最後,您需要為您的程序生成字節碼。

因此,從源代碼到編譯的字節碼,主要分為三個階段:

  1. 構建 AST
  2. 分析和轉換 AST
  3. 從 AST 生成字節碼

讓我們詳細了解這些階段。

構建 AST :解析是一種已解決的問題。 有很多框架,但我建議你使用 ANTLR。 它是眾所周知的,維護良好,並且它具有一些使指定語法變得更容易的功能(它處理較少的遞歸規則 - 你不需要理解它,但要感謝它!)。

分析和轉換 AST :編寫類型系統、驗證和符號解析可能具有挑戰性並且需要大量工作。 僅此主題就需要單獨的帖子。 現在考慮這是您的編譯器的一部分,您將花費大部分精力。

從 AST 生成字節碼:最後一個階段實際上並不難。 您應該在前一階段解析符號並準備地形,以便基本上可以將轉換後的 AST 的單個節點轉換為一個或幾個字節碼指令。 控制結構可能需要一些額外的工作,因為您將在一系列條件和無條件跳轉中轉換您的 for 循環、開關、if 等(是的,在您漂亮的語言下面仍然會有一堆 goto)。 您需要了解 JVM 內部是如何工作的,但實際實現並不難。

與其他語言集成

當你的語言統治世界時,所有代碼都將專門使用它編寫。 但是,作為中間步驟,您的語言可能會與其他 JVM 語言一起使用。 也許有人會開始在一個更大的項目中用您的語言編寫幾個類或一個小模塊。 期望能夠混合多種 JVM 語言是合理的。 那麼,它如何影響您的語言工具呢?

您需要考慮兩種不同的情況:

  • 您的語言和其他語言存在於單獨編譯的模塊中
  • 您的語言和其他語言位於相同的模塊中並一起編譯

在第一種情況下,您的代碼只需要使用用其他語言編寫的編譯代碼。 例如,一些依賴項(如 Guava)或同一項目中的模塊可以單獨編譯。 這種集成需要兩件事:首先,您應該能夠解釋由其他語言生成的類文件,以將符號解析為它們並生成用於調用這些類的字節碼。 第二點與第一點不同:其他模塊可能希望在編譯後重用用您的語言編寫的代碼。 現在,通常這不是問題,因為 Java 可以與大多數類文件進行交互。 但是,您仍然可以設法編寫對 JVM 有效但不能從 Java 調用的類文件(例如,因為您使用在 Java 中無效的標識符)。

第二種情況更複雜:假設您有一個用 Java 代碼定義的 A 類和一個用您的語言編寫的 B 類。 假設這兩個類相互引用(例如 A 可以擴展 B 並且 B 可以接受 A 作為同一方法的參數)。 現在的重點是Java編譯器無法處理您的語言中的代碼,因此您必須為B類提供一個類文件。但是要編譯B類,您需要插入對A類的引用。所以您需要做的是有一種部分 Java 編譯器,它給定一個 Java 源文件能夠解釋它並生成它的模型,您可以使用它來編譯您的 B 類。請注意,這要求您能夠解析 Java 代碼(使用JavaParser 之類的東西)和解決符號。 如果您不知道從哪裡開始,請查看 java-symbol-solver。

工具:Gradle、Maven、測試框架、CI

好消息是,您可以通過為 gradle 或 maven 開發插件,使他們使用以您的語言編寫的模塊對用戶完全透明。 您可以指示構建系統以您的編程語言編譯文件。 用戶將繼續運行 mvn compile 或 gradle assemble 而不會注意到任何差異。

壞消息是編寫 Maven 插件並不容易:文檔很差,難以理解,而且大多過時或完全錯誤。 是的,這聽起來並不令人欣慰。 我還沒有寫過 gradle 插件,但看起來要容易得多。

請注意,您還應該考慮如何使用構建系統運行測試。 對於支持測試,您應該考慮一個非常基本的單元測試框架,並且應該將其與構建系統集成,以便運行 maven 測試以您的語言查找測試,編譯並運行它們,將輸出報告給用戶。

我的建議是查看可用的示例:其中之一是都靈編程語言的 Maven 插件。

一旦你實現了它,每個人都應該能夠輕鬆地編譯用你的語言編寫的源文件,並在 Travis 等持續集成服務中使用它。

IDE 插件

IDE 的插件將是您的用戶最明顯的工具,並且會極大地影響您對語言的感知。 一個好的插件可以通過提供智能自動完成、上下文錯誤和建議的重構來幫助用戶學習語言。

現在,最常見的策略是選擇一個 IDE(通常是 Eclipse 或 IntelliJ IDEA)並為其開發特定的插件。 這可能是您的工具鏈中最複雜的部分。 出現這種情況有幾個原因:首先,您無法合理地重用您為一個 IDE 開發插件而為其他 IDE 所花費的工作。 您的 Eclipse 和 IntelliJ 插件將完全分開。 第二點是IDE插件開發不是很常見的東西,所以文檔不多,社區小。 這意味著您將不得不花費大量時間為自己弄清楚事情。 我個人為 Eclipse 和 IntelliJ IDEA 開發了插件。 我在 Eclipse 論壇上的問題幾個月或幾年都沒有得到解答。 在 IntelliJ 論壇上我運氣更好,有時我會從開發人員那裡得到答案。 然而,插件開發者的用戶群較小,API 非常拜占庭式。 準備受苦。

所有這一切都有一個替代方案,那就是使用 Xtext。 Xtext 是一個為 Eclipse、IntelliJ IDEA 和 web 開發插件的框架。 它是在 Eclipse 上誕生的,並且最近剛剛擴展以支持其他平台,因此在這方面沒有太多經驗,但它可能是一個值得考慮的替代方案。 讓我直截了當地說:開發一個非常好的插件的唯一方法是使用每個 IDE 的本機 API 來開發它。 但是,使用 Xtext,您只需花費很少的精力就可以獲得相當不錯的東西 - 您只需將其提供給您的語言的語法,您就會免費獲得語法錯誤/完成。 儘管如此,您必須實現符號解析和困難的部分,但這是一個非常有趣的起點; 然而,難點在於與平台特定庫的集成以解決 Java 符號,因此這並不能真正解決您的所有問題。

結論

您可能會通過多種方式失去對您的語言表現出興趣的潛在用戶。 採用一種新語言是一項挑戰,因為它需要學習它並適應我們的開發習慣。 通過盡可能減少損耗並利用用戶已知的生態系統,您可以防止用戶在學習並愛上您的語言之前就放棄。

在理想情況下,您的用戶可以克隆一個用您的語言編寫的簡單項目,並使用標準工具(Maven 或 Gradle)構建它,而不會注意到任何差異。 如果他想編輯項目,他可以在它最喜歡的編輯器中打開它,插件將幫助他指出錯誤並提供智能補全。 這是一個與必須弄清楚如何調用編譯器和使用記事本編輯文件大不相同的場景。 圍繞您的語言的生態系統確實可以發揮作用,如今可以通過合理的努力來構建它。

我的建議是在你的語言中發揮創造力,而不是在你的工具中。 通過使用熟悉的標準,減少人們在採用您的語言時必鬚麵對的初始困難。

快樂的語言設計!


進一步閱讀 Toptal 工程博客:

  • 如何從頭開始編寫解釋器