嵌入式輔助工具

2007 年 3 月 26 日

最近幾週,我一直在玩和研究編譯器編譯器工具。這些工具的共同特點是它們有一個語法檔案,其核心是對語言語法的產生規則的描述。除了描述語法之外,檔案還向解析器提供有關如何處理語言的資訊,因為它識別語言元素。在大多數編譯器編譯器工具中,這些指令表示為語法中的動作 - 這些動作通常編碼為高級語言中的程式碼片段。

例如,在我的 HelloAntlr 範例中,您會看到嵌入式 Java 片段,用於從原始檔案建立和填入組態。(嵌入式 Java 並不是唯一的方法,樹狀瀏覽是另一種方法。)

這種將通用語言 (GPL) 嵌入另一個特定領域語言 (DSL) 的方法非常常見。這裡的大多數讀者在使用 Velocity、JSP、ERB 等範本系統建立 HTML 頁面時都會遇到它。同樣地,我們有不同的表示(HTML),我們可以在其中嵌入 GPL 片段以提供動態資料和更複雜的處理。

當我在這樣的環境中工作時,我喜歡將範本中的 Java(或我正在使用的任何 GPL)數量減到最少。一種常見的技術是使用 Java 建立一個單獨的輔助類別,並確保範本中所有嵌入式 Java 所做的只是對此輔助類別進行簡單的方法呼叫。

我喜歡這樣做的主要原因是因為我相信如果您在 DSL 中嵌入大量的 GPL,您最終會模糊 DSL 的流程。使用範本語言處理 HTML 的重點是專注於 HTML,因此您放入其中的每個 Java 位元都會造成阻礙。對於動作中有很多程式碼的語法檔案來說尤其如此,這使得難以理解產生式。

使用嵌入式輔助工具的另一個好處是,它讓工具更容易執行其工作。無論是語法突顯,還是 PostIntelliJ IDE 的全部功能,這些工具通常無法很好地處理混合語言檔案。例如,AntlrWorks 將突顯並提供 Antlr 的語法完成,但嵌入式 Java 只是純文字。

當使用像這樣的輔助程式時,我的正常風格是在主機 (DSL) 檔案中包含早期程式碼來設定輔助程式。這通常包括在主機中宣告一個欄位,並在其中建構一個新的輔助程式,或讓呼叫者傳入一個輔助程式。(我承認我很樂意在我的 Antlr 語法中使用一個公開欄位來執行此操作。)之後,主機中的所有內嵌 Java 都是對輔助程式的簡單呼叫。我從主機檔案的角度命名這些呼叫,以指出從輔助程式中需要什麼。

輔助程式和主機檔案非常緊密地結合在一起,通常在它們之間有一個雙向連結,而且有很多來回。輔助程式知道關於主機的所有種類的骯髒細節 - 我很樂意讓 HTML 輔助程式吐出 HTML,而語法輔助程式會在剖析樹中四處查看。

我通常將類別上的「輔助程式」一詞視為一個警示,因為它通常表示一個思考不周全的抽象。在這裡我很樂意使用這個詞,因為輔助程式實際上只存在於支援主機檔案中。