xtext_使用Xtext为Eclipse和IntelliJ开发DSL
xtext
在這篇文章中,我們將看到如何開發一種簡單的語言。 我們的目標是:
- 語言的解析器
- IntelliJ的編輯器 。 編輯器應具有語法突出顯示,驗證和自動完成功能
我們還將免費提供Eclipse和Web編輯器的編輯器 ,但請包含您的興奮之處,本文中不再贅述。
去年,我專注于學習新知識(主要是Web和ops知識),但我仍然最喜歡的一件事就是開發DSL(領域特定語言)。 我使用的第一個相關技術是Xtext :Xtext是一種出色的工具,可讓您定義語言的語法并生成該語言的出色編輯器。 到目前為止,僅針對Eclipse平臺進行了開發:這意味著可以使用Eclipse開發新語言,然后可以在Eclipse中安裝生成的編輯器。
最近,我使用的Eclipse少了很多,所以直到現在,我對Xtext的興趣逐漸消失,直到最后,新版本的Xtext(仍處于beta版)瞄準了IntelliJ。 因此,當我們使用Eclipse開發語言時,我們將生成插件以在IntelliJ中使用我們的語言。
我們將要看到的技術可以用于開發任何種類的語言,但是我們將把它們應用于特定的情況:AST轉換。 這篇文章是為Xtext新手準備的,我現在不做任何詳細介紹,我只是分享對IntelliJ目標的第一印象。 考慮到該功能目前是測試版,因此我們可以預期會有一些粗糙的邊緣。
我們正在嘗試解決的問題:調整ANTLR解析器以獲取出色的AST
我喜歡玩解析器,而ANTLR是出色的解析器生成器。 對于像Java這樣的功能強大的語言,有很多漂亮的語法。 現在,問題是Java之類的語言的語法非常復雜,并且生成的解析器會生成不易于使用的AST。 主要問題是由于如何處理優先級規則。 考慮一下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;這只是用于定義表達式的大部分代碼的一部分。 現在考慮您有一個簡單的preIncrementExpression (類似: ++ a )。 在AST中,我們將擁有類型為preIncrementExpression的節點,該節點將包含在unaryExpression中。
一元表達式將包含在一個乘法 表達式中,該表達式將包含在一個additiveExpression中 ,依此類推。 該組織對于處理不同類型的運算之間的運算符優先級很有必要,因此將1 + 2 * 3解析為1和 2 * 3的總和,而不是1 + 2和3的乘法。 問題是,從邏輯的角度來看,乘法和加法是同一級別的表達式:擁有Matryoshka AST節點沒有意義。 考慮以下代碼:
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]理想情況下,我們希望指定產生Matryoshka風格的AST的語法,但在對代碼進行分析時使用更平坦的AST,因此我們將根據Antlr和“邏輯” AST產生的AST構建適配器。 我們打算如何做? 我們將首先開發一種定義節點形狀的語言,以使它們出現在邏輯AST中,并且還將定義如何將Antlr節點( Matryoshka風格的節點)映射到這些邏輯節點中。 這只是我們要解決的問題:Xtext可用于開發任何一種語言,只是作為解析器狂人,我喜歡使用DSL解決與解析器相關的問題。 這是很元的 。
入門:安裝Eclipse Luna DSL并創建項目
我們將下載一個包含Xtext 2.9 Beta的 Eclipse版本。 在全新的Eclipse中,您可以創建一種新型的項目: Xtext Projects 。
我們只需要定義項目的名稱,然后選擇與我們的新語言相關聯的擴展名即可
然后,我們選擇我們感興趣的平臺(是的,還有Web平臺……我們將在將來進行研究)
創建的項目包含一個示例語法。 我們可以按原樣使用它,我們只需要生成幾個運行MWE2文件的文件即可。
運行此命令后,我們可以僅在IntelliJ或Eclipse中使用我們的新插件。 但是,我們將改為首先更改語法,以在光榮的DSL中轉換給定的示例。
我們的DSL示例
我們的語言在IntelliJ IDEA中看起來像這樣(很酷,是嗎?)。
當然這只是一個開始,但是我們開始為Java解析器定義一些基本節點類型:
- 表示可能的修飾語的枚舉(警告:這不是完整列表)
- CompilationUnit,其中包含可選的PackageDeclaration和可能的許多TypeDeclaration
- TypeDeclaration是一個抽象節點,有三種擴展它的具體類型: EnumDeclaration,ClassDeclaration和InterfaceDeclaration (我們缺少注釋聲明)
我們將需要添加數十個表達式和語句,但是您應該對我們嘗試構建的語言有所了解。 還要注意,我們已經引用了Antlr語法(在第一行中),但尚未指定定義的節點類型如何映射到Antlr節點類型。 現在的問題是:我們如何構建它?
定義語法
我們可以使用簡單的EBNF表示法(帶有一些擴展名)來定義語言的語法。 在您的項目中查找帶有xtext擴展名的文件, 并按如下所示進行更改:
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');我們定義的第一個規則對應于AST的根(在本例中為Model )。 我們的模型始于對Antlr文件的引用和聲明列表。 想法是指定我們的“邏輯”節點類型的聲明以及應如何將“ antlr”節點類型映射到它們。 因此,我們將定義轉換,該轉換將引用在AntlrGrammarRef規則中指定的antlr語法中定義的元素的引用。
我們可以定義Enum或NodeType。 NodeType有一個名稱,可以是抽象的,并且可以擴展另一個NodeType。 請注意, 超類型是對NodeType的引用。 這意味著生成的編輯器將自動能夠為我們提供自動完成功能(列出文件中定義的所有NodeType )并進行驗證,從而驗證我們是否引用了現有的NodeType 。
在我們的NodeTypes中,我們可以定義任意多個字段( NodeTypeField )。 每個字段均以名稱開頭,后跟一個運算符:
- * =表示我們可以在此字段中使用0..n值
- ?=表示該字段是可選的(0..1)值
- =表示始終存在一個值
NodeTypeField還具有一個值類型,該值類型可以是內聯定義的枚舉( UnnamedEnumDeclaration ),關系(表示此節點包含其他節點)或屬性(表示此節點具有一些基本屬性,如字符串或布爾值)。
很簡單,是嗎?
因此,我們基本上重新運行了MWE2文件,我們準備好了。
查看實際使用的插件
要查看我們在IntelliJ IDEA中安裝的插件,我們只需要從包含想法插件的目錄(在我們的示例中為me.tomassetti.asttransf.idea )運行gradle runIdea 。 請注意,您需要使用gradle的最新版本,并且需要定義JAVA_HOME 。 此命令將下載IntelliJ IDEA,安裝我們開發的插件并啟動它。 在打開的IDE中,您可以創建一個新項目并定義一個新文件。 只需使用我們在創建項目時指定的擴展名(本例中為.anttr ) ? IDEA應該使用我們新定義的編輯器。
目前驗證工作正常,但編輯器的React似乎很慢。 自動完成功能反而對我不利。 考慮到這只是一個beta,因此我希望這些問題在Xtext 2.9發布之前會消失。
下一步
我們才剛剛起步,但是令人驚奇的是,如何在幾分鐘內就可以使用其IDEA編輯器創建DSL。
我計劃朝幾個不同的方向工作:
- 我們需要了解如何打包和分發插件:我們可以使用gradle runIdea嘗試它,但我們只想生成一個二進制文件供人們安裝,而無需處理編輯器的源代碼
- 使用來自Maven的任意依賴項:這將變得相當復雜,因為Maven和Eclipse插件(OSGi捆綁包)以自己的方式定義了它們的依賴關系,因此通常必須將jar打包成捆綁包才能在Eclipse插件中使用。 但是,還有其他選擇,例如Tycho和p2-maven-plugin 。 劇透 :我不希望這太快又容易……
- 我們還不能引用Antlr語法中定義的元素。 現在,這意味著我們應該能夠解析Antlr語法并以編程方式創建EMF模型,以便我們可以在DSL中引用它。 它需要了解EMF(并且需要一些時間……)。 我將在將來使用它,這可能需要使用loooong教程。
結論
雖然我不再喜歡Eclipse(現在我已經習慣了IDEA,但對我來說似乎好得多了:更快,更輕便),但是Eclipse Modeling Framework一直是一個非常有趣的軟件,并且能夠與IDEA一起使用非常棒。
一段時間以來,我沒有使用EMF和Xtext,不得不說我看到了一些改進。 我感到Eclipse并不是非常命令行友好的,并且通常很難將其與CI系統集成。 我看到正在努力解決這些問題(請參閱Tycho或使用我們開發的編輯器來啟動IDEA的gradle作業),這對我來說似乎非常積極。
我的理念是混合技術,以務實的方式結合不同世界的最佳方面,因此,我希望找到時間玩這些東西。
翻譯自: https://www.javacodegeeks.com/2015/08/develop-dsls-for-eclipse-and-intellij-using-xtext.html
xtext
總結
以上是生活随笔為你收集整理的xtext_使用Xtext为Eclipse和IntelliJ开发DSL的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 万代《刀剑神域:异绊集结》试玩 9 月
- 下一篇: ARPG 游戏《幽灵之国》明年登陆 PC