您的位置:首页 > 其它

dsl领域特定语言

2013-07-24 11:54 459 查看
Xtext 2.0 发布,领域专用语言开源框架



用于开发领域专用语言的开源框架Eclipse Xtext最新版本2.0,作为Eclipse 3.7(代号为Indigo)的一部分,已于近日正式发布。

该版本的新特性:

重构框架及支持Rich Hover信息;
可以嵌入在任何一种DSL中的新表达式语言,以及允许用户在自己的语言中编写计算逻辑。通过Xtext 2.0,你不仅可以复用完整的语法定义表达式,也可以复用和扩展所有必需的基础架构,包括编译器、输入系统、解释器,当然也包括纯净的Eclipse集成,并可以把行为描述放在DSL脚本中合适的地方。
包含一个静态键入模板语言Xtend,该语言简化了代码生成器的开发和维护,它已经集成在Eclipse工具中。
进一步提高了框架整体的品质和性能。
支持重命名重构,这一功能甚至支持跨语言使用,包括Java在内。

官网信息:http://www.eclipse.org/Xtext/

下载地址:http://www.eclipse.org/Xtext/download

====================

ruby dsl

http://yonkeltron.com/blog/2010/05/13/creating-a-ruby-dsl/

Creating a Ruby DSL

May 13th, 2010

Tons of people in the Ruby community go on and on aboutdomain-specific languages (abbreviated DSL)and how wonderful they are. In most cases, I agree with them. I began to wonder how I could
go about leveraging Ruby’s awesomely-flexible syntax to create my own DSL. To illustrate my quest, I have written this article. It assumes you know Ruby. The example details are completely fictitious and just contrived enough to be interesting. I promise,
the code will be the main focus and I will link/point to other resources which might be helpful to budding DSL designers like myself.
Problem The Dream Castle Architectural Firm (D-CAF, among friends) wants a tool which it can use for very high-level prototyping of custom homes. They need it to be understandable by both computers and humans. It only needs to keep track of
the following things:- Houses - Each house has a name- Floors - Each house has multiple floors. Each floor has a

number


Rooms - Each floor has multiple rooms and each room has a type

The only other requirement is that the DSL be translatable to plain English so that they can show it to customers. No problem.Solution The DSL for the high-level specification of custom houses will look like the following:

CustomHouse.build :home do
floor(1) {
room :den
room :kitchen
}

floor(2) {
room :bedroom
room :bathroom
}
end

That’s it. You specify that a house should be built, that it has a name, some floors and each floor has some rooms. Simple, easy and 100% pure Ruby. Notice the cunning usage of both do/end and bracket notation for defining blocks. The outer block passed
to
CustomHouse#build
uses do/end syntax while the blocks passed to
House#floor
use the bracket syntax. These could easily be reversed (or combined or whatever) but it makes it look pretty and helps to visually differentiate things so
that you (the developer) and the user (the architect) can things more clearly. Plus, when you print the output (calling
to_s
on the instance of
House
which gets returned), you get the following wonderful text:

House named home has 2 floors.
Floor 1 has 2 rooms (den, kitchen)
Floor 2 has 2 rooms (bedroom, bathroom)

It’s a small house, don’t be a wiseacre. So, how can such a DSL be built?
Implementation
Let’s write the implementation for this together. First, we’re going to start of with a module named
CustomHouse
. In Ruby, modules are just classes so we’ll define a class method called
build
which will behave like a factory method.

module CustomHouse
def self.build(name, &block)
house = House.new(name)
house.instance_eval(&block)
return house
end
end

As you can see, the method takes two params, a name and a block. The first thing that it does is create an instance of the House class (which we have not yet defined) passing in the name parameter. Second it calls
instance_evalon the house passing in the block. This ventures into the territory of
metaprogrammingwhich is great fun but beyond the scope of this document, though I am sure others have used it for DSL construction (coincidentally, if you are interested in Ruby metaprogramming, buythis
book). Suffice it to say that it executes the supplied block in the context of the House instance. Finally, the
CustomHouse#build
method returns the instance of
House
. (There are those who believe that *any* type of eval is fundamentally evil. I have been taught this many times and I try to avoid using eval on an actualy string whenever possible.
Still, someone with a better understanding of Ruby internals might be able to better explain if this in any better.) Next, let’s define what the code for the
House
class looks like. As a note, it will be defined in the
CustomHouse
module, technically making the fully-namespaced name of the class
CustomHouse::House
.

class House
attr_accessor :name, :floors

def initialize(name = '')
@name = name.to_s
@floors = []
end

def floor(number, &block)
fl = Floor.new(number)
fl.instance_eval(&block)
@floors < < fl
end

def to_s
str = "House named #{@name} has #{@floors.length} floors.\n"
@floors.each do |f|
str << f.to_s
end
str
end
end

The above code should appear relatively simple. The House class has only a few methods. First, it has a constructor function which takes a name and a block, setting the name as an instance variable and another instance variable,
@floors
, which is just an empty array. Next, it has a method called
floor
which takes a number and a block. The guts of this method should look familiar to you because it mimics almost exactly the
build
factory method defined on
CustomHouse
. Finally, it has a
to_s
method because of the requirement that the DSL be translatable to plain English for clients to check out. If we can just dwell for a moment on the
floor
method, notice that, it too, takes a block and uses
instance_eval
.
It then adds the newly-constructed instance of
Floor
to the array in
@floor
. Let’s look at the
Floor
class now.

class Floor
attr_accessor :number, :rooms

def initialize(number = 0)
@number = number@rooms = []
end

def room(type)
@rooms < < Room.new(type)
end

def to_s
str = "Floor #{@number} has #{@rooms.length} rooms ("
@rooms.each do |r|
str += "#{r.type}, "
end
str.chop!.chop!
str += ")\n"
str
end
end

There shouldn’t be anything confusing about the above code as it doesn’t use any sort of block eval. In fact, the only thing left to look at is the class for
Room
which is even less impressive.

class Room
attr_reader :type

def initialize(type)
@type = type
end
end

That’s it. Seriously, that’s the entire implementation of the DSL. You pass a block to
CustomHouse#build
which gets executed in the context of a new instance of
House
. The block calls the
House#floor
method with a block which in turn gets executed in the context of a new instance of
Floor
. The Floor#room method adds new
Room
instances to the class and that’s basically it. Here’s all the code together with the example:

module CustomHouse

def self.build(name, &block)
house = House.new(name)
house.instance_eval(&block)
return house
end

class House
attr_accessor :name, :floors

def initialize(name = '')
@name = name.to_s
@floors = []
end

def floor(number, &block)
fl = Floor.new(number)
fl.instance_eval(&block)
@floors << fl
end

def to_s
str = "House named #{@name} has #{@floors.length} floors.\n"
@floors.each do |f|
str << f.to_s
end
str
end
end

class Floor
attr_accessor :number, :rooms

def initialize(number = 0)
@number = number@rooms = []
end

def room(type)
@rooms << Room.new(type)
end

def to_s
str = "Floor #{@number} has #{@rooms.length} rooms ("
@rooms.each do |r|
str += "#{r.type}, "
end
str.chop!.chop!
str += ")\n"
str
end
end

class Room attr_reader :type def initialize(type) @type = type end endend

h = CustomHouse.build :home do floor(1) { room :den room :kitchen } floor(2) { room :bedroom room :bathroom } end
puts h

Try running it and see what happens! Then try writing other definitions for custom houses and experience the theoretical joy of the hypothetical architectural firm.DSL construction techniques For clarification and context, I’d like to share
some other, smaller examples which build on this technique and demonstrate one more. Behold, a DSL for feeding Pandas:

Panda.feed {
nom :bamboo
nom :chocolate
}

The implementation of this is both simple and straightforward.

class Panda
def self.feed(&block)
panda = Panda.new
panda.instance_eval(&block)
end

def nom(food)
#whatever
end
end

Since the block is evaluated in the context of the new
Panda
instance, it has access to the
Panda#nom
method. For people deathly afraid of eval, there is this alternative syntax:

Panda.feed do |p|
p.nom :bamboo
p.nom :chocolate
end

Which is implemented with
yield
instead of
instance_eval
like so:

class Panda
def self.feed
yield Panda.new
end

def nom(food)
# whatever
end
end

For a wonderful and inspiring treatment of Ruby DSLs and associated patterns, see themost-excellent talk on the matter given by Jeremy
McAnally at the 2009 Mid West Ruby Conference.Conclusion DSLs are a fantastic tool which can help to simplify complicated and repetitive tasks. Ruby is very good for creating DSLs but it is not the only good tool out there. I advise you
look into the creation of DSLs with Scala and, the best DSL-creation tool there ever was, Lisp Macros. I am interested in improving this tutorial for the benefit of those programmers who wish to learn about DSL construction but don’t know where to start.

Posted by adminMay 13th, 2010

=========================

http://www.infoq.com/cn/news/2007/06/dsl-or-not

说说我个人对DSL(领域特定语言)的理解,

1.很多开发都需要相应的领域知识,比如,银行,保险,电信等等。同时是技术专家和领域专家的人比较少(个人推测),但是单在某个领域是专家的人就不少。所以,作为开发者提供一种让某个领域的专家更易懂的该领域语言(DSL)不失为一种比较好的解决方案。

2.我们将某个领域的语言 翻译成开发人员能够理解的语言(Ruby、Jave等),Ruby、Jave等虚拟机、编译器又将我们写的代码翻译成计算机可以理解的语言(01代码)。从某种角度上说DSL是我们为某个领域所特别开发的语言,作为开发人员就是在做高级语言编译器。

3.Ruby具有快速建立DSL的能力(个人看法)。

模板velocity->翻译.net->机器语言

领域特定语言(domain-specific languages,简称DSL
在定义DSL是什么的问题上,Flowler认为目前经常使用的一些特征,例如“关注于领域”、“有限的表现”和“语言本质”是非常模糊的。因此,唯一能够确定DSL边界的方法是考虑“一门语言的一种特定用法”和“该语言的设计者或使用者的意图”:
如果XSLT的设计者将其设计为XML的转换工具,那么我认为XSLT是一个DSL。如果一个用户使用DSL的目的是该DSL所要达到的目的,那么它一个DSL,但是如果有人以通用的方式来使用一个DSL,那么它(在这种用法下)就不再是一个DSL了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: