·使用Xtext/Xtend 实现域专用语言DSL(1)

 本博文通过一个简单的例子介绍如何使用Xtext 编写一个简单DSL 的语法。

建立一个DSL语言

假设我们要建立一个DSL 语言来描述一个会议的信息:最终的描述文本是这样的:

datatype String
datatype Bool
entity Session {
title: String
isTutorial : Bool
}
entity Conference {
name : String
attendees : Person*
speakers : Speaker*
}
entity Person {
name : String
}
entity Speaker extends Person {
sessions : Session*
}

下面我们来看看如何使用Xtext来设计这个DSL的语法

语法的开头两行

第一行是语法的描述

grammar org.eclipse.xtext.example.Domainmodel
with 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.Domainmodel
with org.eclipse.xtext.common.Terminals
generate 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 a
var int b
var int c
calc int x = a
calc int y = a + c
calc int z = a * a + b

Xtext 描述的语法

grammar expr.ExprDemo with org.eclipse.xtext.common.Terminals
generate exprDemo "http://www.ExprDemo.expr"

import "http://www.eclipse.org/emf/2002/Ecore" as ecore

Model:
  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 规则的文章。只能从实例中慢慢摸索。

猜你喜欢

转载自blog.csdn.net/yaojiawan/article/details/115887013
今日推荐