使用Xtext为Eclipse和IntelliJ开发DSL
在這篇文章中,我們將看到如何開發(fā)一種簡單的語言。 我們的目標(biāo)是:
- 語言的解析器
- IntelliJ的編輯器 。 編輯器應(yīng)具有語法突出顯示,驗證和自動完成功能
我們還將免費提供Eclipse和Web編輯器的編輯器 ,但請包含您的興奮之處,本文中不再贅述。
去年,我專注于學(xué)習(xí)新知識(主要是Web和ops知識),但是我仍然最喜歡的一件事就是開發(fā)DSL(領(lǐng)域特定語言)。 我使用的第一個相關(guān)技術(shù)是Xtext :Xtext是一個出色的工具,可讓您定義語言的語法并生成該語言的出色編輯器。 到目前為止,僅針對Eclipse平臺進行了開發(fā):這意味著可以使用Eclipse開發(fā)新語言,然后可以在Eclipse中安裝生成的編輯器。
最近,我使用的Eclipse大大減少了,所以直到現(xiàn)在,我對Xtext的興趣逐漸消失,直到最后,新版本的Xtext(仍處于beta版)瞄準(zhǔn)了IntelliJ。 因此,盡管我們將使用Eclipse開發(fā)語言,然后我們將生成插件以在IntelliJ中使用我們的語言。
我們將要看到的技術(shù)可以用于開發(fā)任何種類的語言,但是我們將把它們應(yīng)用于特定的情況:AST轉(zhuǎn)換。 這篇文章是為Xtext新手準(zhǔn)備的,我現(xiàn)在不做很多詳細(xì)介紹,我只是分享對IntelliJ目標(biāo)的第一印象。 考慮到該功能目前是測試版,因此我們可能會遇到一些困難。
我們正在嘗試解決的問題:調(diào)整ANTLR解析器以獲得出色的AST
我喜歡玩解析器,而ANTLR是出色的解析器生成器。 對于像Java這樣的功能強大的語言,有很多漂亮的語法。 現(xiàn)在,問題在于Java之類的語言語法非常復(fù)雜,并且生成的解析器會生成不易使用的AST。 主要問題是由于如何處理優(yōu)先規(guī)則。 考慮一下Terence Parr和Sam Harwell編寫的Java 8語法 。 讓我們看看如何定義一些表達式:
conditionalExpression: conditionalOrExpression| conditionalOrExpression '?' expression ':' conditionalExpression;conditionalOrExpression: conditionalAndExpression| conditionalOrExpression '||' conditionalAndExpression;conditionalAndExpression: inclusiveOrExpression| conditionalAndExpression '&&' inclusiveOrExpression;inclusiveOrExpression: exclusiveOrExpression| inclusiveOrExpression '|' exclusiveOrExpression;exclusiveOrExpression: andExpression| exclusiveOrExpression '^' andExpression;andExpression: equalityExpression| andExpression '&' equalityExpression;equalityExpression: relationalExpression| equalityExpression '==' relationalExpression| equalityExpression '!=' relationalExpression;relationalExpression: shiftExpression| relationalExpression '<' shiftExpression| relationalExpression '>' shiftExpression| relationalExpression '<=' shiftExpression| relationalExpression '>=' shiftExpression| relationalExpression 'instanceof' referenceType;shiftExpression: additiveExpression| shiftExpression '<' '<' additiveExpression| shiftExpression '>' '>' additiveExpression| shiftExpression '>' '>' '>' additiveExpression;additiveExpression: multiplicativeExpression| additiveExpression '+' multiplicativeExpression| additiveExpression '-' multiplicativeExpression;multiplicativeExpression: unaryExpression| multiplicativeExpression '*' unaryExpression| multiplicativeExpression '/' unaryExpression| multiplicativeExpression '%' unaryExpression;unaryExpression: preIncrementExpression| preDecrementExpression| '+' unaryExpression| '-' unaryExpression| unaryExpressionNotPlusMinus;這只是用于定義表達式的大部分代碼的一部分。 現(xiàn)在考慮您有一個簡單的preIncrementExpression (類似: ++ a )。 在AST中,我們將擁有類型為preIncrementExpression的節(jié)點,該節(jié)點將包含在unaryExpression中。
一元表達式將包含在一個乘法 表達式中,該表達式將包含在一個additiveExpression中 ,依此類推。 該組織對于處理不同類型的運算之間的運算符優(yōu)先級很有必要,因此將1 + 2 * 3解析為1和 2 * 3的和,而不是1 + 2和3的乘法。 問題是,從邏輯的角度來看,乘法和加法是同一級別的表達式:擁有Matryoshka AST節(jié)點沒有意義。 考慮以下代碼:
class A { int a = 1 + 2 * 3; }雖然我們想要這樣的東西:
[CompilationUnitContext][TypeDeclarationContext][ClassDeclarationContext][NormalClassDeclarationContext]classA[ClassBodyContext]{[ClassBodyDeclarationContext][ClassMemberDeclarationContext][FieldDeclarationContext][UnannTypeContext][UnannPrimitiveTypeContext][NumericTypeContext][IntegralTypeContext]int[VariableDeclaratorListContext][VariableDeclaratorContext][VariableDeclaratorIdContext]a=[VariableInitializerContext][ExpressionContext][AssignmentExpressionContext][ConditionalExpressionContext][ConditionalOrExpressionContext][ConditionalAndExpressionContext][InclusiveOrExpressionContext][ExclusiveOrExpressionContext][AndExpressionContext][EqualityExpressionContext][RelationalExpressionContext][ShiftExpressionContext][AdditiveExpressionContext][AdditiveExpressionContext][MultiplicativeExpressionContext][UnaryExpressionContext][UnaryExpressionNotPlusMinusContext][PostfixExpressionContext][PrimaryContext][PrimaryNoNewArray_lfno_primaryContext][LiteralContext]1+[MultiplicativeExpressionContext][MultiplicativeExpressionContext][UnaryExpressionContext][UnaryExpressionNotPlusMinusContext][PostfixExpressionContext][PrimaryContext][PrimaryNoNewArray_lfno_primaryContext][LiteralContext]2*[UnaryExpressionContext][UnaryExpressionNotPlusMinusContext][PostfixExpressionContext][PrimaryContext][PrimaryNoNewArray_lfno_primaryContext][LiteralContext]3;}<EOF>雖然我們想要這樣的東西:
[CompilationUnit][FieldDeclaration][PrimitiveTypeRef][Sum][Multiplication][IntegerLiteral][IntegerLiteral][IntegerLiteral]理想情況下,我們要指定產(chǎn)生Matryoshka風(fēng)格的AST的語法,但在對代碼進行分析時使用更平坦的AST,因此我們將根據(jù)Antlr和“邏輯” AST生成的AST構(gòu)建適配器。 我們打算如何做? 我們將首先開發(fā)一種定義節(jié)點形狀的語言,以使它們出現(xiàn)在邏輯AST中,并且還將定義如何將Antlr節(jié)點( Matryoshka風(fēng)格的節(jié)點)映射到這些邏輯節(jié)點中。 這只是我們要解決的問題:Xtext可用于開發(fā)任何一種語言,這只是作為一種解析器狂,我喜歡使用DSL解決解析器相關(guān)的問題。 這是很元的 。
入門:安裝Eclipse Luna DSL并創(chuàng)建項目
我們將下載一個包含Xtext 2.9 Beta的 Eclipse版本。 在全新的Eclipse中,您可以創(chuàng)建一種新型的項目: Xtext Projects 。
我們只需要定義項目的名稱,然后選擇與我們的新語言相關(guān)聯(lián)的擴展名即可
然后,我們選擇感興趣的平臺(是的,還有Web平臺……我們將在以后進行研究)
創(chuàng)建的項目包含一個示例語法。 我們可以按原樣使用它,我們只需要生成幾個運行MWE2文件的文件即可。
運行此命令后,我們可以僅在IntelliJ或Eclipse中使用我們的新插件。 但是,我們將改為首先更改語法,以在光榮的DSL中轉(zhuǎn)換給定的示例。
我們的DSL示例
我們的語言在IntelliJ IDEA中看起來像這樣(很酷,是嗎?)。
當(dāng)然這只是一個開始,但我們開始為Java解析器定義一些基本節(jié)點類型:
- 表示可能的修飾語的枚舉(警告:這不是完整列表)
- CompilationUnit,其中包含可選的PackageDeclaration和可能的許多TypeDeclaration
- TypeDeclaration是一個抽象節(jié)點,有三種擴展它的具體類型: EnumDeclaration,ClassDeclaration和InterfaceDeclaration (我們?nèi)鄙僮⑨屄暶?#xff09;
我們將需要添加數(shù)十個表達式和語句,但是您應(yīng)該對我們嘗試構(gòu)建的語言有所了解。 還要注意,我們已經(jīng)引用了Antlr語法(在第一行中),但是尚未指定定義的節(jié)點類型如何映射到Antlr節(jié)點類型。 現(xiàn)在的問題是:我們?nèi)绾螛?gòu)建它?
定義語法
我們可以使用簡單的EBNF表示法(帶有一些擴展名)來定義語言的語法。 在您的項目中查找?guī)в衳text擴展名的文件, 并按如下所示進行更改:
grammar me.tomassetti.AstTransformationsDsl with org.eclipse.xtext.common.Terminalsgenerate astTransformationsDsl "http://www.tomassetti.me/AstTransformationsDsl"Model:antlr=AntlrGrammarRef declarations+=Declaration*;AntlrGrammarRef:'adapt' grammarFile=STRING;Declaration: NodeType | NamedEnumDeclaration;NamedEnumDeclaration: 'enum' name=ID '{' values+=EnumNodeTypeFieldValue+ '}'; UnnamedEnumDeclaration: 'enum' '{' values+=EnumNodeTypeFieldValue+ '}';NodeType:'abstract'? 'type' name=ID ('extends' superType=[NodeType])? ('from' antlrNode=ID)? '{' fields+=NodeTypeField*'}'; NodeTypeField:name=ID (many='*='|optional='?='|single='=') value=NodeTypeFieldValue; NodeTypeFieldValue:UnnamedEnumDeclaration | RelationNodeTypeField | AttributeNodeTypeField;EnumNodeTypeFieldValue: name=ID;RelationNodeTypeField: type=[NodeType];AttributeNodeTypeField:{AttributeNodeTypeField}('string'|'int'|'boolean');我們定義的第一個規(guī)則對應(yīng)于AST的根(在本例中為Model )。 我們的模型從對Antlr文件和聲明列表的引用開始。 想法是指定我們的“邏輯”節(jié)點類型的聲明以及應(yīng)如何將“ antlr”節(jié)點類型映射到它們。 因此,我們將定義轉(zhuǎn)換,該轉(zhuǎn)換將引用在AntlrGrammarRef規(guī)則中指定的antlr語法中定義的元素的引用。
我們可以定義Enum或NodeType。 NodeType有一個名稱,可以是抽象的,并且可以擴展另一個NodeType。 請注意, 超類型是對NodeType的引用。 這意味著生成的編輯器將自動能夠為我們提供自動完成功能(列出文件中定義的所有NodeTypes )并進行驗證,從而驗證我們是否引用了現(xiàn)有的NodeType 。
在我們的NodeTypes中,我們可以定義任意多個字段( NodeTypeField )。 每個字段均以名稱開頭,后跟一個運算符:
- * =表示我們可以在此字段中使用0..n值
- ?=表示該字段是可選的(0..1)值
- =表示始終始終存在一個值
NodeTypeField還具有一個值類型,該值類型可以是內(nèi)聯(lián)定義的枚舉( UnnamedEnumDeclaration ),關(guān)系(表示此節(jié)點包含其他節(jié)點)或?qū)傩?#xff08;表示此節(jié)點具有一些基本屬性,如字符串或布爾值)。
很簡單,是嗎?
因此,我們基本上重新運行了MWE2文件,我們準(zhǔn)備好了。
查看實際使用的插件
要查看我們在IntelliJ IDEA中安裝的插件,我們只需要從包含想法插件的目錄(在本例中為me.tomassetti.asttransf.idea )運行g(shù)radle runIdea 。 請注意,您需要使用gradle的最新版本,并且需要定義JAVA_HOME 。 此命令將下載IntelliJ IDEA,安裝我們開發(fā)的插件并啟動它。 在打開的IDE中,您可以創(chuàng)建一個新項目并定義一個新文件。 只需使用我們在創(chuàng)建項目時指定的擴展名(本例中為.anttr ) ? IDEA應(yīng)該使用我們新定義的編輯器。
目前驗證工作正常,但編輯器的反應(yīng)似乎很慢。 自動完成功能反而對我不利。 考慮到這只是一個beta,因此我希望這些問題在Xtext 2.9發(fā)布之前會消失。
下一步
我們才剛剛起步,但是令人驚奇的是,如何在幾分鐘內(nèi)就可以使用其IDEA編輯器創(chuàng)建DSL。
我計劃朝幾個不同的方向工作:
- 我們需要了解如何打包和分發(fā)插件:我們可以使用gradle runIdea嘗試使用它,但我們只想生成一個二進制文件供人們安裝,而無需處理編輯器的源代碼
- 使用來自Maven的任意依賴項:這將變得相當(dāng)復(fù)雜,因為Maven和Eclipse插件(OSGi捆綁包)以自己的方式定義了它們的依賴關(guān)系,因此通常必須將jar打包成捆綁包才能在Eclipse插件中使用。 但是,還有其他選擇,例如Tycho和p2-maven-plugin 。 劇透 :我不希望這太快又容易……
- 我們還不能引用Antlr語法中定義的元素。 現(xiàn)在,這意味著我們應(yīng)該能夠解析Antlr語法并以編程方式創(chuàng)建EMF模型,以便我們可以在DSL中引用它。 它需要了解EMF(并且需要一些時間……)。 我將在將來使用它,這可能需要使用loooong教程。
結(jié)論
盡管我不再喜歡Eclipse(現(xiàn)在我已經(jīng)習(xí)慣了IDEA,但對我來說似乎更好了:更快,更輕便),但是Eclipse Modeling Framework一直是一個非常有趣的軟件,并且能夠與IDEA一起使用非常棒。
一段時間以來,我沒有使用EMF和Xtext,不得不說我看到了一些改進。 我覺得Eclipse不太命令行友好,并且通常很難將其與CI系統(tǒng)集成。 我看到正在為解決這些問題而努力(請參閱Tycho或我們用來使用開發(fā)的編輯器啟動IDEA的gradle作業(yè)),這對我來說似乎非常積極。
我的理念是混合技術(shù),以務(wù)實的方式結(jié)合不同世界的最佳方面,因此,我希望有時間玩這些東西。
翻譯自: https://www.javacodegeeks.com/2015/08/develop-dsls-for-eclipse-and-intellij-using-xtext.html
總結(jié)
以上是生活随笔為你收集整理的使用Xtext为Eclipse和IntelliJ开发DSL的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 献丑了是什么意思 献丑了解释
- 下一篇: 爱豆的英文 爱豆的英文是什么