本文共 3371 字,大约阅读时间需要 11 分钟。
本博文通过一个简单的例子介绍如何使用Xtext 编写一个简单DSL 的语法。
建立一个DSL语言
假设我们要建立一个DSL 语言来描述一个会议的信息:最终的描述文本是这样的:
datatype Stringdatatype Boolentity Session {title: StringisTutorial : Bool}entity Conference {name : Stringattendees : Person*speakers : Speaker*}entity Person {name : String}entity Speaker extends Person {sessions : Session*}
下面我们来看看如何使用Xtext来设计这个DSL的语法 。
语法的开头两行
第一行是语法的描述
grammar org.eclipse.xtext.example.Domainmodelwith org.eclipse.xtext.common.Terminals
其中:
org.eclipse.xtext.example.Domainmodel 是语法的名称、
org.eclipse.xtext.common.Terminals 是语法的状态。表示该语法重用和覆盖了特定的语法。org.eclipse.xtext.common.Terminals是Xtext 的库语法。它预定义了大多数公用Terminal 规则,比如ID,STRGING 和INT 等等。
下一个语句
generate domainmodel "http://www.eclipse.org/xtext/example/Domainmodel"
指定从该语法导出的EMF Ecore 包。(Ecore package)
常用的符号
(no operator) | exactly one |
? | zero or one |
* | zero or more |
+ | one or more |
例如:
DomainModel :Entity*;
一个Xtext 语法不仅描述语法规则,而且描述AST(_Abstract Syntax Tree 抽象语法树)的结构。通常,每个语法建立树上的一个对象。元素的类型可以在名称的后面使用returns 关键字。
DomainModel returns DomainModel: ...
也可以写成:
DomainModel : ...
为了连接多个不同的对象在一起,可以写成:
DomainModel :(elements+=Entity)*;
赋值符号
feature=... | 相当于 setFeature(...) |
list+=... | 相当于 getList().add(...) |
condition?=... | 相当于 setCondition(true) |
例如:规则Entity 可以定义多个Feature
Entity :'entity' name=ID '{'(features+=Feature)*'}';
下面再增加Extends 的语法描述:
Entity :'entity' name=ID ('extends' superType=[Entity])? '{'(features+=Feature)*'}';
比较特别的是右边的('extends' superType=[Entity])? 短句。这是一个交叉引用在别的地方说明的Entity。所以在这里并不是指向某个语法规则,而是指向一个EClass(你认为是一个实例就好了)。
数据类型的定义
在我们的例子中,不仅定义了Entity ,而且定义了两种数据类型
DomainModel :(elements+=Type)*;Type:DataType | Entity;DataType:'datatype' name=ID;
下一步我们继续要定义Feature 的语法。
Feature:name=ID ':' type=TypeRef;
定义TypeRef
TypeRef :referenced=[Type] (multi?='*')?;
terminal 规则
从计算机编译技术中我们知道,编译分成词法分析和语法分析两部分,词法分析主要是提取每一个单词,在编译技术中叫做tockens 。在Xtext 中,词法分析的规则称为Terminal rule。例如
terminal ID: '^'?('a'..'z'|'A'..'Z'|'_')('a'..'z'|'A'..'Z'|'_'|'0'..'9')*; terminal INT returns ecore::EInt: ('0'..'9')+;
完整的语法
于是,我们完成了完整的语法描述:
grammar org.eclipse.xtext.example.Domainmodelwith org.eclipse.xtext.common.Terminalsgenerate domainmodel "http://www.eclipse.org/xtext/example/Domainmodel"DomainModel :(elements+=Type)*;Type:DataType | Entity;DataType:'datatype' name=ID;Entity:'entity' name=ID ('extends' superType=[Entity])? '{'(features+=Feature)*'}';Feature:name=ID ':' type=TypeRef;TypeRef:referenced=[Type] (multi?='*')?;
另一个例子:
语言格式
var int avar int bvar int ccalc int x = acalc int y = a + ccalc int z = a * a + b
Xtext 描述的语法
grammar expr.ExprDemo with org.eclipse.xtext.common.Terminalsgenerate exprDemo "http://www.ExprDemo.expr"import "http://www.eclipse.org/emf/2002/Ecore" as ecoreModel: elements+=Element*;Element: VarDecl | Formula;VarDecl returns Symbol: {VarDecl} "var" type=Type name=ID ";";Type: IntType | BoolType | FloatType;IntType: {IntType} "int";BoolType: {BoolType} "bool";FloatType: {FloatType} "float";Formula: "calc" type=Type name=ID "=" expr=Expr ";";Expr: Addition;Addition returns Expression: Multiplication ({Plus.left=current}"+" right=Multiplication)*;Multiplication returns Expression: Atomic ( {Multi.left=current} "*" right=Atomic)*;Atomic returns Expression: {SymbolRef} symbol=[Symbol|QID] | {NumberLiteral} value=NUMBER;terminal NUMBER returns ecore::EBigDecimal: ('0'..'9')* ('.' ('0'..'9')+)?;QID: ID ("." ID)*;
小结
语言的形式化描述是比较令人晦涩的。好像也没有详细描述Xtext 规则的文章。只能从实例中慢慢摸索。
转载地址:http://cazxz.baihongyu.com/