??xml version="1.0" encoding="utf-8" standalone="yes"?>国产精品高清在线,成人免费视频视频,欧美日韩一区中文字幕http://www.aygfsteel.com/Vencent/category/12368.html<font color='orange'>本博客仅为收集所用。在此对原作者表C感谢?lt;/font> <div class="custom"> <script type="text/javascript"><!-- google_ad_client = "pub-6256612161572960"; google_ad_width = 728; google_ad_height = 90; google_ad_format = "728x90_as"; google_ad_type = "text"; google_ad_channel =""; google_page_url = document.location; google_color_border = ["FFDDAA","FFDDAA","FFDDAA","FFDDAA"]; google_color_bg = ["FFDDAA","FFDDAA","FFDDAA","FFDDAA"]; google_color_link = ["FFDDAA","0033FF","00008B","99CC33"]; google_color_url = ["999999","0033FF","00008B","FFCC00"]; google_color_text = ["0033FF","AA9999","0033FF","AA9999"]; //--></script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script></div>zh-cnWed, 28 Feb 2007 21:12:43 GMTWed, 28 Feb 2007 21:12:43 GMT60 Web 昄层技术评? http://www.aygfsteel.com/Vencent/articles/58380.htmlVincent ChenVincent ChenSat, 15 Jul 2006 17:46:00 GMThttp://www.aygfsteel.com/Vencent/articles/58380.htmlhttp://www.aygfsteel.com/Vencent/comments/58380.htmlhttp://www.aygfsteel.com/Vencent/articles/58380.html#Feedback0http://www.aygfsteel.com/Vencent/comments/commentRss/58380.htmlhttp://www.aygfsteel.com/Vencent/services/trackbacks/58380.html阅读全文

Vincent Chen 2006-07-16 01:46 发表评论
]]>
用Ruby 创徏领域特定语言QDSLQ?/title><link>http://www.aygfsteel.com/Vencent/articles/54086.html</link><dc:creator>Vincent Chen</dc:creator><author>Vincent Chen</author><pubDate>Tue, 20 Jun 2006 15:43:00 GMT</pubDate><guid>http://www.aygfsteel.com/Vencent/articles/54086.html</guid><wfw:comment>http://www.aygfsteel.com/Vencent/comments/54086.html</wfw:comment><comments>http://www.aygfsteel.com/Vencent/articles/54086.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/Vencent/comments/commentRss/54086.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/Vencent/services/trackbacks/54086.html</trackback:ping><description><![CDATA[ by Jim Freeze<br /><p>英文原文见:http://www.artima.com/rubycs/articles/ruby_as_dsl.html<br /><br /></p><h3>摘要Q?/h3>? 的说来,有两U创?DSL 的方法?一U方法是从零开始发明一个新的语法,qؓ之构造一个编译器或解释器? 另一U方法是裁剪一个现存的通用目的语言Q?增加或修Ҏ法(methodsQ? 操作W(operatorsQ?以及~省的行为? 本文讲述使用W二U方法来?Ruby 之上创徏 DSL.<br /><br /><br />一?DSL, 是一个针对相对窄的应用范围设计的~程或描q语a。相对于通用目的语言Q?它被设计来处理特定的计算dQ?DSL 仅适用于特定领域?你可以用两种方式创徏一?DSL.<br /><br /><ul><li>从零开始发明一个新的语法,qؓ之构造一个编译器或解释器?/li><li>裁剪一个现存的通用目的语言Q?增加或修Ҏ法(methodQ? 操作W(operatorQ?以及~省的行为?/li></ul><br />W? 二种Ҏ的优势是你节省了旉Q?因ؓ你不需要生成和调试一个新的语aQ?q样你有更多的时间用于解xl用户面临的问题。缺Ҏ DSL 会受限于其下的通用目的语言的语法和能力?更进一步来_Z另一个语a来构造DSL 常常意味着最l用户可以获得这个基语言所有的威力Q这可能是优点也可能是缺点, 它取决于特定的应用环境? q篇文章讲述怎么使用W二U方法来在Ruby之上创徏 DSL.<br /><br /><h3><span style="font-weight: bold;">描述堆叠模型Qstackup modelsQ?/span></h3>? 是一个互q模型(interconnect modelingQ?工程师,在工作中Q? 我们需要一U方式来描述半导体晶片上的电路的垂直几何l构Qvertical geometric profileQ。这些描q被保存在一个堆叠模型(stackup modelQ? 文g中。(我们造了一个词stackupQ因为金属连U是在装配过E(fabrication processQ中通过层层堆叠形成的)。现在的问题是,每一个供货商有他们自q描述堆叠模型QstackupQ?的格式, 而我们想要一个公q格式使得我们可以在不同的格式间进行{换?换句话说Q?我们需要定义一个公q堆叠模型QstackupQ?DSL, q且写一个程序用来从我们格式转换Z同供货商的特定的格式?br /><br />供货商没有用一个复杂的DSL 语言Q他们的语言仅仅包含静态的数据元素于一个基本^坦的文本数据库中。他们的文g格式不允许有参数化类型(parameterized typeQ? 变量Q?帔RQ?以及{式QequationQ?只有静态数据。更q一步来_格式非常单。它是基于行的,或者基于只有一个层ơ的块结构?br /><br />? 们开始描q我们的堆叠模型格式Ӟ要求q不高,因ؓ我们仅仅需要达C货商实现的程度就行。但我们很快扩展了我们的格式。ؓ什么我们可以做到这一点, 而我们的供货商却做不到。我怿q是因ؓ我们使用?Ruby, 而不象我们的供货商用 C 从零开始。我怿也可以用其他的语aQ? 但是我不认ؓ最l的产品可以同样。通用语言的选择是关键的一步?br /><br />我也怿供货商的开发速度因ؓ使用 C 而受到阻, 因ؓ他们要保持他们的堆叠模型的语法尽量简单以便于分析QparseQ? 可能q偶然Q很多供货商在他们的文g格式中用一些共同的单的语法l构? 因ؓq些单的语法l构出现频繁Q?我们先?Ruby 中仿制它们, 然后再{到更复杂的语法结构?<br /><br /><h3>Z行和块别的 DSL l构QconstructsQ?/h3>Z行的l构是一U将一个值或一个范围的D予一个参敎ͼparameterQ的方式?在我们所考察的供货商的文件中Q用了下面的格式?br /><br /><div style="margin-left: 40px;"> 1. parameter = value<br /> 2. parameter value<br /> 3. parameter min_value max_value step_value<br /></div><br />除了2 中没有显式出现??, 格式1? 是等L?格式3 一个范围的Dl了一个参数?br /><br />更ؓ复杂的格式包含一个块l构。有两种格式如下所C?q两U块l构可以使用一个基于行的分析器和一个堆?Q或一个带堆栈的关键字分析?key-letter and word parser)来手动分析?br /><br />begin<br /><div style="margin-left: 40px;"> type = TYPE<br /> name = NAME<br /> param1 = value1<br /> param2 = value2<br /> ...<br /></div>end<br /><br />下面一U块格式使用 C 风格的大括号{}来标识一个块Q?但参?值对用空格来分隔?br /><br />TYPE NAME {param1 = value1 param2 = value2 }<br /><br /><h3>三次是一个咒?/h3>在我们构建我们的堆叠文g的DSLӞ 我们把问题解决了三次?首先Q?我们写了自己的语法分析器Q然后发现那L话有太多的工作要l护。不仅仅是代码,而且q有文档。因为我们的DSL _复杂Q如果没有够文档,没有昄的方法去使用它的所有特性?br /><br />? 着Q在一个短旉内,我们?XML实现?DSL?q样Q我们不需要去写自q语法分析器,因ؓXML 有现成的分析器。但? XML引入太多的噪壎ͼ模糊了文件的实际内容 。我们的工程师发现思维在理解堆叠文件的含义和理解XML之间q行切换很困难。由此我认识刎ͼXML 不适合人来阅读QXML可能不是用来创徏DSL 的好的选择Q?管我们有不用开发语法分析器QparserQ的好处?br /><br />最后,我们用Ruby实现了DSL?因ؓ利用?Ruby 解释器的分析功能Q实现v来是很快的。我们不需要写分析器(是RubyQ的文档Q因为它已经存在。而且Q最l的DSL 非常Ҏ被h理解Q也很简z灵zR?br /><br />好的?让我们用Ruby来创Z?DSLQ该DSL允许我们定义形如‘parameter = value’的语句。请考虑以下假想的DSL 文g?br /><br />% cat params_with_equal.dsl <br />name = fred<br />parameter = .55<br /><br />q不是合法的Ruby代码Q我们需要稍微修改一下语法得Ruby可以接受它。让我们它改ؓQ?br /><br />% cat params_with_equal.dsl<br />name = "fred" <br />parameter = 0.55<br /><br />一旦我们让DSL 遵@Ruby的语法,Ruby ׃ؓ我们做了所有的分析工作Qƈ且提供了一U方式访问分析的l果?现在Q让我们写一?Ruby 代码来读DSL?br /><br />首先Q我们想要将q些参数用某U方式封装v来?一个好的方法是它们放C个类中。我们称q个cMؓ MyDSL?br /><br />% cat mydsl.rb<br />class MyDSL<br /> ...<br />end#class MyDSL<br /><br />从开发者的角度看,我们需要一个简单和直接的方式来分析DSL 文g。就如下面所C:<br /><br />my_dsl = MyDSL.load(filename)<br /><br />接着Q让我们来写cL?load:<br /><br />def self.load(filename)<br /><div style="margin-left: 40px;"> dsl = new<br /> dsl.instance_eval(File.read(filename), filename)<br /> dsl<br /></div>end<br /><br />c? Ҏload 产生一个MyDSL对象Q?q且以DSL 文g的内容ؓ参数调用该对象的instance_eval。Instance_eval的第二个参数是可选的Q?它得Ruby 在出现语法分析错误时可以报告文g名?一个可选的W三个参敎ͼ没有使用Q可以Ruby 在出现分析错误时能提供错误开始的行号<br />q个代码能工作吗Q?让我们看看发生了什么?<br /><br /><div style="margin-left: 40px;">% cat dsl-loader.rb<br />require 'mydsl'<br /><br />my_dsl = MyDSL.load(ARGV.shift) # put the DSL filename on the command line<br />p my_dsl<br />p my_dsl.instance_variables<br />% ruby dsl-loader.rb params_with_equal.dsl<br />#<mydsl:0x89cd8><br /><mydsl:0x89cd8>[]</mydsl:0x89cd8><br /><mydsl:0x89cd8></mydsl:0x89cd8></mydsl:0x89cd8></div><mydsl:0x89cd8><br />? 生了什? name 和parameter到那里去了? q是因ؓname和parameter在等L左侧QRuby 认ؓ他们是局部变量。我们可以告诉Ruby它们是实例变量。有两种方式Q一U是使用 self.name = “fred? self.parameter = 0.55 Q?另一U是使用@W号?br /><br />@name = "fred" <br />@parameter = 0.55<br /><br />但是Ҏ来说Q这样很丑陋。写成下面的形式也是一栗?br /><br />$name = "fred" <br />$parameter = 0.55<br /><br />q? 有一个办法让Ruby 知道q些ҎQmethodQ执行的上下文, 那就是利用块(block)?yield selfQMyDsl的对象实例) 来显式的声明作用域?Z做到q一点,我们加一个顶层方法来开始我们的DSLQ?q且实际内Ҏq所附的?block)中?修改q的 DSL 看v来是q样Q?br /><br />% cat params_with_equal2.dsl<br />define_parameters do |p|<br /></mydsl:0x89cd8><div style="margin-left: 40px;"><mydsl:0x89cd8> p.name = "fred" </mydsl:0x89cd8><br /><mydsl:0x89cd8> p.parameter = 0.55</mydsl:0x89cd8><br /><mydsl:0x89cd8></mydsl:0x89cd8></div><mydsl:0x89cd8>end<br /><br />define_parameter 已经被定义ؓ一个实例方法(instance methodQ?br /><br />% cat mydsl2.rb<br />class MyDSL<br /></mydsl:0x89cd8><div style="margin-left: 40px;"><mydsl:0x89cd8> def define_parameters</mydsl:0x89cd8><br /><div style="margin-left: 40px;"><mydsl:0x89cd8> yield self</mydsl:0x89cd8><br /></div><mydsl:0x89cd8> end</mydsl:0x89cd8><br /><mydsl:0x89cd8></mydsl:0x89cd8></div><mydsl:0x89cd8><br /></mydsl:0x89cd8><div style="margin-left: 40px;"><mydsl:0x89cd8> def self.load(filename)</mydsl:0x89cd8><br /><div style="margin-left: 40px;"><mydsl:0x89cd8> dsl = new</mydsl:0x89cd8><br /><mydsl:0x89cd8> dsl.instance_eval(File.read(filename), filename)</mydsl:0x89cd8><br /><mydsl:0x89cd8> dsl</mydsl:0x89cd8><br /></div><mydsl:0x89cd8> end</mydsl:0x89cd8><br /><mydsl:0x89cd8></mydsl:0x89cd8></div><mydsl:0x89cd8>end#class MyDSL<br /><br />修改dsl-loader中的requireQ让它用mydsl2.rb 中的新版本的MyDSL c:<br /><br /></mydsl:0x89cd8><div style="margin-left: 40px;"><mydsl:0x89cd8>% cat dsl-loader.rb</mydsl:0x89cd8><br /><mydsl:0x89cd8>require 'mydsl2'</mydsl:0x89cd8><br /><mydsl:0x89cd8><br /><mydsl:0x89cd8>my_dsl = MyDSL.load(ARGV.shift)</mydsl:0x89cd8><br /><mydsl:0x89cd8>p my_dsl</mydsl:0x89cd8><br /><mydsl:0x89cd8>p my_dsl.instance_variables</mydsl:0x89cd8><br /><mydsl:0x89cd8></mydsl:0x89cd8></mydsl:0x89cd8></div><mydsl:0x89cd8><br />理论上,q可以工作, 让我们测试一下?br /><br />% ruby dsl-loader.rb params_with_equal2.dsl<br />params_with_equal2.dsl:2:in `load': undefined method `name=' for #<mydsl:0x26300> (NoMethodError)<br /><br />噢,我们忘记了ؓname 和parameter 定义讉K函数QaccessorQ?让我们加上它们, 然后看一下完整的E序Q?br /><br />% cat mydsl2.rb<br />class MyDSL<br /></mydsl:0x26300></mydsl:0x89cd8><div style="margin-left: 40px;"><mydsl:0x89cd8><mydsl:0x26300> attr_accessor :name, :parameter</mydsl:0x26300></mydsl:0x89cd8><br /><mydsl:0x89cd8><mydsl:0x26300><br /><mydsl:0x89cd8><mydsl:0x26300> def define_parameters</mydsl:0x26300></mydsl:0x89cd8><br /></mydsl:0x26300></mydsl:0x89cd8><div style="margin-left: 40px;"><mydsl:0x89cd8><mydsl:0x26300> yield self</mydsl:0x26300></mydsl:0x89cd8><br /></div><mydsl:0x89cd8><mydsl:0x26300> end</mydsl:0x26300></mydsl:0x89cd8><br /><mydsl:0x89cd8><mydsl:0x26300><br /><mydsl:0x89cd8><mydsl:0x26300> def self.load(filename)</mydsl:0x26300></mydsl:0x89cd8><br /></mydsl:0x26300></mydsl:0x89cd8><div style="margin-left: 40px;"><mydsl:0x89cd8><mydsl:0x26300> # ... same as before</mydsl:0x26300></mydsl:0x89cd8><br /></div><mydsl:0x89cd8><mydsl:0x26300> end</mydsl:0x26300></mydsl:0x89cd8><br /><mydsl:0x89cd8><mydsl:0x26300></mydsl:0x26300></mydsl:0x89cd8></div><mydsl:0x89cd8><mydsl:0x26300>end<br /><br />现在Q?再测试一遍?br /><br />% ruby dsl-loader.rb params_with_equal2.dsl<br />#<mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"><br />["@name", "@parameter"]<br /><br />? 功! 现在工作了。但是我们在DSL文g中加了额外的两行Q?q有额外?.p , q些都引入了噪声。这L记法QnotationQ更适合于当DSL文g中存在多个层ơ, q且需要显式指定上下文的情c? 在我们的单例子里Q我们应该隐式的定义上下文, 且让Ruby 知道name 和parameter 是方法(methodQ?让我们删??? Q?DSL 文g写成Q?br /><br />% cat params.dsl<br />name "fred" <br />parameter 0.55<br /><br />? 在,我们需要ؓname ?parameter 定义新的讉KҎQaccessorQ。这里的H门是:不带参数的name 是@name的读ҎQreaderQ, 带一个或多个参数的name 是@name的写ҎQsetterQ。(注意Q用这个办法很方便Q即使是DSL文g有多个层ơ而且上下文是昑ּ声明的)?我们下面为name 和parameter 定义讉KҎ, 删除attr_accessor那一行,加入以下代码Q?br /><br />% cat mydsl3.rb<br />class MyDSL<br /></mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><div style="margin-left: 40px;"><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"> def name(*val)</mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><br /><div style="margin-left: 40px;"><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"> if val.empty?</mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><br /><div style="margin-left: 40px;"><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"> @name</mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><br /></div><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"> else</mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><br /><div style="margin-left: 40px;"><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"> @name = val.size == 1 ? val[0] : val</mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><br /></div><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"> end</mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><br /></div><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"> end</mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><br /><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"></mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8></div><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"><br /></mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><div style="margin-left: 40px;"><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"> def parameter(*val)</mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><br /><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"></mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8></div><div style="margin-left: 40px;"><div style="margin-left: 40px;"><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"> if val.empty?</mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><br /><div style="margin-left: 40px;"><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"> @parameter</mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><br /></div><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"> else</mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><br /><div style="margin-left: 40px;"><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"> @parameters = val.size == 1 ? val[0] : val</mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><br /></div><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"> end</mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><br /></div><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"> end</mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><br /><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"></mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8></div><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"><br /></mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><div style="margin-left: 40px;"><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"> def self.load(filename)</mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><br /><div style="margin-left: 40px;"><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"> # ... same as before</mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><br /></div><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"> end</mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><br /></div><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred">end#class MyDSL</mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><br /><div style="margin-left: 40px;"><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"></mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8></div><mydsl:0x89cd8><mydsl:0x26300><mydsl:0x25ec8 @parameter="0.55" ,="" @name="fred"><br />如果 name 或parameter 不带参数Q它们将q回它们的倹{如果带参数Q?br /></mydsl:0x25ec8></mydsl:0x26300></mydsl:0x89cd8><ul><li> 如果带一个参敎ͼ它们会被赋予该参数的?/li><li> 如果带多个参敎ͼ它们会被赋予一个数l,该数l包含所有的参数?/li></ul><br />让我们运行我们的分析器(现在是mydsl3.rbQ来试一下:<br /><br />% ruby dsl-loader.rb params.dsl<br />#<mydsl:0x25edc @name="fred" @parameter="0.55,"><br />["@parameter", "@name"]<br /><br />又成功了。但是显式地定义讉KҎ( accessors) 很烦人。让我们定义一个定制的讉KҎQƈ且让所有的c都可以使用它?我们通过此Ҏ(method)攑ֈ Module class 中来做到q一炏V?br /><br />% cat dslhelper.rb<br />class Module<br /></mydsl:0x25edc><div style="margin-left: 40px;"><mydsl:0x25edc @name="fred" @parameter="0.55,"> def dsl_accessor(*symbols)</mydsl:0x25edc><br /><div style="margin-left: 40px;"><mydsl:0x25edc @name="fred" @parameter="0.55,"> symbols.each { |sym|</mydsl:0x25edc><br /><div style="margin-left: 40px;"><mydsl:0x25edc @name="fred" @parameter="0.55,"> class_eval %{</mydsl:0x25edc><br /><div style="margin-left: 40px;"><mydsl:0x25edc @name="fred" @parameter="0.55,"> def #{sym}(*val)</mydsl:0x25edc><br /><div style="margin-left: 40px;"><mydsl:0x25edc @name="fred" @parameter="0.55,"> if val.empty?</mydsl:0x25edc><br /><div style="margin-left: 40px;"><mydsl:0x25edc @name="fred" @parameter="0.55,"> @#{sym}</mydsl:0x25edc><br /></div><mydsl:0x25edc @name="fred" @parameter="0.55,"> else</mydsl:0x25edc><br /><div style="margin-left: 40px;"><mydsl:0x25edc @name="fred" @parameter="0.55,"> @#{sym} = val.size == 1 ? val[0] : val</mydsl:0x25edc><br /></div><mydsl:0x25edc @name="fred" @parameter="0.55,"> end</mydsl:0x25edc><br /></div><mydsl:0x25edc @name="fred" @parameter="0.55,"> end</mydsl:0x25edc><br /></div><mydsl:0x25edc @name="fred" @parameter="0.55,"> }</mydsl:0x25edc><br /></div><mydsl:0x25edc @name="fred" @parameter="0.55,"> }</mydsl:0x25edc><br /></div><mydsl:0x25edc @name="fred" @parameter="0.55,"> end</mydsl:0x25edc><br /><mydsl:0x25edc @name="fred" @parameter="0.55,"></mydsl:0x25edc></div><mydsl:0x25edc @name="fred" @parameter="0.55,">end<br /><br />上面的代码简单的定义了一?dsl_accessor ҎQ?它是我们的DSL特定的访问方法。现在我们用它取代attr_accessorQ?br /><br />% cat mydsl4.rb<br />require 'dslhelper'<br /><br />class MyDSL<br /></mydsl:0x25edc><div style="margin-left: 40px;"><mydsl:0x25edc @name="fred" @parameter="0.55,"> dsl_accessor :name, :parameter</mydsl:0x25edc><br /><mydsl:0x25edc @name="fred" @parameter="0.55,"><br /><mydsl:0x25edc @name="fred" @parameter="0.55,"> def self.load(filename)</mydsl:0x25edc><br /></mydsl:0x25edc><div style="margin-left: 40px;"><mydsl:0x25edc @name="fred" @parameter="0.55,"> # ... same as before</mydsl:0x25edc><br /></div><mydsl:0x25edc @name="fred" @parameter="0.55,"> end</mydsl:0x25edc><br /><mydsl:0x25edc @name="fred" @parameter="0.55,"></mydsl:0x25edc></div><mydsl:0x25edc @name="fred" @parameter="0.55,">end#class MyDSL<br /><br />再一ơ,我们更新dsl-loader.rb 中的require 语句Q加载mydsl4.rbQ?然后q行loader:<br /><br />% ruby dsl-loader.rb params.dsl <br />#<mydsl:0x25edc @name="fred" @parameter="0.55,"><br />["@parameter", "@name"]<br /><br />一 切都很好。但是如果我不能事先知道参数的名字怎么办? 在实际用中Q参数名应该可以qh生成?别害怕。有Ruby 在, 我们可以使用 method_missing 的威力。给 MyDSL加一个两行的ҎQ?我们可以用dsl_accessor Ҏ需要随时定义新的属?attribute)?也就是说Q如果一个D赋予一个不存在的参敎ͼmethod_missing 会定义一? getter 和一个setter Qƈ且将该D予新生成的参数?br /><br />% cat mydsl5.rb<br />require 'dslhelper'<br /><br />class MyDSL<br /></mydsl:0x25edc></mydsl:0x25edc><div style="margin-left: 40px;"><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"> def method_missing(sym, *args)</mydsl:0x25edc></mydsl:0x25edc><br /><div style="margin-left: 40px;"><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"> self.class.dsl_accessor sym</mydsl:0x25edc></mydsl:0x25edc><br /><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"> send(sym, *args)</mydsl:0x25edc></mydsl:0x25edc><br /></div><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"> end</mydsl:0x25edc></mydsl:0x25edc><br /><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"><br /><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"> def self.load(filename)</mydsl:0x25edc></mydsl:0x25edc><br /></mydsl:0x25edc></mydsl:0x25edc><div style="margin-left: 40px;"><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"> # ... Same as before</mydsl:0x25edc></mydsl:0x25edc><br /></div><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"> end</mydsl:0x25edc></mydsl:0x25edc><br /><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"></mydsl:0x25edc></mydsl:0x25edc></div><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,">end<br /><br />% head -1 dsl-loader.rb<br />require 'mydsl5'<br /><br />% ruby dsl-loader.rb params.dsl<br />#<mydsl:0x25b80 @name="fred" @parameter="0.55,"><br />["@parameter", "@name"]<br /><br />哇!是不是感觉很好? 仅仅写了一点代码,我们有了一个可以读和定义Q意数目参数的分析器。还可以吧。但是如果最l用户不知道RubyQ且使用了与现存的Ruby Ҏ冲突的名字,怎么办? 举例来说Q如果我们的DSL文g包含以下内容Q?br /><br />% cat params_with_keyword.dsl <br />methods %w(one two three)<br />id 12345<br /><br />% ruby dsl-loader.rb params_with_keyword.dsl <br />params_with_keyword.dsl:2:in `id': wrong number of arguments (1 for 0) (ArgumentError)<br /><br />噢, 真不好意思。不q我们可以迅速的解决q个问题?q里要用C个类叫BlankSlate, 它最初是?Jim Weirich构思出来的? 用在q的BlankSlate 和Jim 的有l微的差别,因ؓ我们惌多保留一些功能?我们留下七个方法? 你可以试一试看看那些是l对需要的Q那些是用来辅助我们看MyDSL 的对象实例的内容?br /><br />% cat mydsl6.rb <br />require 'dslhelper'<br /><br />class BlankSlate<br /></mydsl:0x25b80></mydsl:0x25edc></mydsl:0x25edc><div style="margin-left: 40px;"><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25b80 @name="fred" @parameter="0.55,"> instance_methods.each { |m| undef_method(m) unless %w(</mydsl:0x25b80></mydsl:0x25edc></mydsl:0x25edc><br /></div><div style="margin-left: 40px;"><div style="margin-left: 40px;"><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25b80 @name="fred" @parameter="0.55,"> __send__ __id__ send class </mydsl:0x25b80></mydsl:0x25edc></mydsl:0x25edc><br /><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25b80 @name="fred" @parameter="0.55,"> inspect instance_eval instance_variables </mydsl:0x25b80></mydsl:0x25edc></mydsl:0x25edc><br /><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25b80 @name="fred" @parameter="0.55,"> ).include?(m)</mydsl:0x25b80></mydsl:0x25edc></mydsl:0x25edc><br /></div><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25b80 @name="fred" @parameter="0.55,"> }</mydsl:0x25b80></mydsl:0x25edc></mydsl:0x25edc><br /><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25b80 @name="fred" @parameter="0.55,"></mydsl:0x25b80></mydsl:0x25edc></mydsl:0x25edc></div><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25b80 @name="fred" @parameter="0.55,">end#class BlankSlate<br /><br /># MyDSL now inherits from BlankSlate<br />class MyDSL < BlankSlate<br /></mydsl:0x25b80></mydsl:0x25edc></mydsl:0x25edc><div style="margin-left: 40px;"><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25b80 @name="fred" @parameter="0.55,"> # ... nothing new here, move along...</mydsl:0x25b80></mydsl:0x25edc></mydsl:0x25edc><br /><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25b80 @name="fred" @parameter="0.55,"></mydsl:0x25b80></mydsl:0x25edc></mydsl:0x25edc></div><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25edc @name="fred" @parameter="0.55,"><mydsl:0x25b80 @name="fred" @parameter="0.55,">end#class MyDSL<br /><br />现在我们试一下加载包含关键字(keyword)的DSL 文gQ?我们会看C些更合理的东ѝ?br /><br />% head -1 dsl-loader.rb <br />require 'mydsl6'<br /><br />% ruby dsl-loader.rb params_with_keyword.dsl <br />#<mydsl:0x23538 ]="" three="" two="" @methods="[" one="" ,="" @id="12345,"><br />["@id", "@methods"]<br /><br />? 以确信, 我们成功了?q是一个好消息Q? 我们可以L那些没用的方法,l予我们的最l用h自由的用参数名字的权利。但是不怎样Q请注意Q我们终I不能让最l用户完全自q使用参数名。这? 使用通用~程语言创徏DSL的一个缺点, 但我认ؓQ禁止最l用户用’class’作为参数名Q应该不会给我们的品销路带来多大的风险?br /><br /></mydsl:0x23538></mydsl:0x25b80></mydsl:0x25edc></mydsl:0x25edc><h3>更复杂的DSL</h3>? 们现在来实现更复杂的DSL Ҏ?不仅仅操作数据,而且要执行更具体的行为? 惌一下我们厌烦了在每ơ开始一个新的项目的时候,手动的生成一个通用的目录集和文仉? 如果Ruby可以帮我们做q些好了。更q一步,如果我们有一个小的DSL使得我们可以直接修改目目录l构而不用去~写低的代码,岂不更好?br /><br />我们现在开始ؓq个问题定义一个DSL?下面的文件是q个DSL ?.01 版本Q?br /><br />% cat project_template.dsl <br />create_project do<br /><div style="margin-left: 40px;"> dir "bin" do<br /><div style="margin-left: 40px;"> create_from_template :exe, name<br /></div> end<br /><br /> dir "lib" do<br /><div style="margin-left: 40px;"> create_rb_file name<br /></div><div style="margin-left: 40px;"> dir name do<br /></div><div style="margin-left: 80px;"> create_rb_file name<br /></div><div style="margin-left: 40px;"> end<br /></div> end<br /><br /> dir "test" <br /></div><br /><div style="margin-left: 40px;"> touch :CHANGELOG, :README, :TODO<br /></div>end<br /><br />? q个DSL文g里,我们生成了一个项目,在其中加了三个目录和三个文g。在’bin ? 目录中,我们使用?exe’模板生成了一个与目名字同名的可执行文g。在’lib’目录,我们生成了一?rb 文g和一个目录, 都与目名字同名。在q个内部子目录中Q又生成另一个与目名字同名的?rb? 文g。最后,在项目顶U目录下Q生成了一个’test’目录,和三个空文g?br /><br />q个DSL需要的Ҏ(method)是:create_project,dir,create_from_template,create_rb_file, 以及 touch?让我们逐个的看一下这些方法?br /><br />Ҏcreate_project是最外层的壳QwrapperQ?q个Ҏ提供了一个作用域让我们将所有的DSL代码都放在一个块(block)中。(完整的代码列表请看文章的最后)<br /><br /> def create_project()<br /><div style="margin-left: 40px;"> yield<br /></div> end<br /><br />Ҏdir 完成实质性的工作。该Ҏ不仅仅生成目录,而且当前的工作目录保存在实例变?@cwd中?在这里,使用ensure 来保证@cwd 的始l有正确的倹{?br /><br /><br /> def dir(dir_name)<br /><div style="margin-left: 40px;"> old_cwd = @cwd<br /> @cwd = File.join(@cwd, dir_name)<br /><br /> FileUtils.mkdir_p(@cwd)<br /> yield self if block_given?<br /></div> ensure<br /><div style="margin-left: 40px;"> @cwd = old_cwd<br /></div> end<br /><br />Ҏtouch ?create_rb_file 基本是一LQ除了后面一个给文g名加了一个后~’rb’以外?q些Ҏ可以接受一个或多个文g名,q些名字可以是字W串或符PsymbolsQ?br /><br /> def touch(*file_names)<br /><div style="margin-left: 40px;"> file_names.flatten.each { |file| <br /><div style="margin-left: 40px;"> FileUtils.touch(File.join(@cwd, "#{file}")) <br /></div> }<br /></div> end<br /><br />最后,Ҏcreate_from_template 是一个粗略的例子用于说明怎么样可以在一个DSL中实C些实际的功能?L代码的完整列?<br /><br />Zq行q些代码Q我们构Z一个小的测试应用?br /><br /> % cat create_project.rb <br />require 'project_builder'<br /><br />project_name = ARGV.shift<br />proj = ProjectBuilder.load(project_name)<br />puts "== DIR TREE OF PROJECT '#{project_name}' =="<br />puts `find #{project_name}`<br /><br />q行l果是:<br /><br /> % ruby create_project.rb fred<br />== DIR TREE OF PROJECT 'fred' ==<br />fred<br />fred/bin<br />fred/bin/fred<br />fred/CHANGELOG<br />fred/lib<br />fred/lib/fred<br />fred/lib/fred/fred.rb<br />fred/lib/fred.rb<br />fred/README<br />fred/test<br />fred/TODO<br /><br />% cat fred/bin/fred <br />#!/usr/bin/env ruby<br /><br />require 'rubygems'<br />require 'commandline<br />require 'fred'<br /><br />class FredApp < CommandLine::Application<br /><div style="margin-left: 40px;"> def initialize<br /></div><div style="margin-left: 40px;"> end<br /><br /> def main<br /> end<br /></div>end#class FredApp<br /><br /><br />哇!工作得很好?q且没费多少力气?br /><br /><h3>ȝ</h3>我做q的很多目要求一个非常详l的控制描q?在每个项目中Q这常常让我停下来ƈ思考怎么这些详l的配置数据引入到应用(applicationQ中?现在QRuby作ؓ一个DSLQ几乎是最适合的,而且常常可以非常高效和快速的解决问题?br /><br />? 培训Ruby 的时候,我会让整个班U用以下Ҏ来解决问题,我们先用p来描q问题,然后用伪代码Q然后用Ruby。但是,在某些情况下Q伪代码是合法? Ruby 代码?我认为,Ruby的高度可L?Ruby是一个可用做DSL的理惌a?当Ruby 为更多的人所了解Q?用Ruby 写的DSL 成Z个与应用通信的流行的方式<br /><br />目 ProjectBuilder DSL 的代码列表:<br /><br />% cat project_builder.rb <br />require 'fileutils'<br /><br />class ProjectBuilder<br /><div style="margin-left: 40px;"> PROJECT_TEMPLATE_DSL = "project_template.dsl"<br /><br /> attr_reader :name<br /><br /> TEMPLATES = {<br /> :exe =><br /><<-EOT<br /><div style="margin-left: 40px;">#!/usr/bin/env ruby<br /><br />require 'rubygems'<br />require 'commandline<br />require '%name%'<br /><br />class %name.capitalize%App < CommandLine::Application<br /><div style="margin-left: 40px;"> def initialize<br /> end<br /><br /> def main<br /> end<br /></div>end#class %name.capitalize%App<br /></div></div><div style="margin-left: 40px;">EOT<br /></div><div style="margin-left: 40px;"> }<br /></div><br /><div style="margin-left: 40px;"> def initialize(name)<br /><div style="margin-left: 40px;"> @name = name<br /> @top_level_dir = Dir.pwd<br /> @project_dir = File.join(@top_level_dir, @name)<br /> FileUtils.mkdir_p(@project_dir)<br /> @cwd = @project_dir<br /></div> end<br /></div><br /><div style="margin-left: 40px;"> def create_project<br /><div style="margin-left: 40px;"> yield<br /></div> end<br /></div><br /><div style="margin-left: 40px;"> def self.load(project_name, dsl=PROJECT_TEMPLATE_DSL)<br /><div style="margin-left: 40px;"> proj = new(project_name)<br /> proj = proj.instance_eval(File.read(dsl), dsl)<br /> proj<br /></div> end<br /></div><br /><div style="margin-left: 40px;"> def dir(dir_name)<br /><div style="margin-left: 40px;"> old_cwd = @cwd<br /> @cwd = File.join(@cwd, dir_name)<br /> FileUtils.mkdir_p(@cwd)<br /> yield self if block_given?<br /></div> ensure<br /><div style="margin-left: 40px;"> @cwd = old_cwd<br /></div> end<br /></div><br /><div style="margin-left: 40px;"> def touch(*file_names)<br /><div style="margin-left: 40px;"> file_names.flatten.each { |file| <br /> FileUtils.touch(File.join(@cwd, "#{file}")) <br /> }<br /></div> end<br /></div><br /><div style="margin-left: 40px;"> def create_rb_file(file_names)<br /><div style="margin-left: 40px;"> file_names.each { |file| touch(file + ".rb") }<br /></div> end<br /></div><br /><div style="margin-left: 40px;"> def create_from_template(template_id, filename)<br /><div style="margin-left: 40px;"> File.open(File.join(@cwd, filename), "w+") { |f|<br /><div style="margin-left: 40px;"> str = TEMPLATES[template_id]<br /> str.gsub!(/%[^%]+%/) { |m| instance_eval m[1..-2] }<br /></div></div><div style="margin-left: 80px;"> f.puts str<br /></div><div style="margin-left: 40px;"> }<br /></div> end<br /></div>end#class ProjectBuilder<br /><br /># Execute as:<br /># ruby create-project.rb project_name<br /><br /><h3>资源</h3>1QBlankSlate 是一个Ruby class,用于产生没有Ҏ(method-free)的对象实例?参见Q?br />http://onestepback.org/index.cgi/Tech/Ruby/BlankSlate.rdoc<br />2QJimWeirich是BlankSlate 的创,也是很多著名的Ruby 工具和库的创?br />http://onestepback.org<br /><br /><h3>关于作?/h3><br />Jim Freeze ?001q初学习Ruby以来Q一直是 Ruby 的热p?br />Jim ?CommandLine and Stax gems 的作?img src ="http://www.aygfsteel.com/Vencent/aggbug/54086.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/Vencent/" target="_blank">Vincent Chen</a> 2006-06-20 23:43 <a href="http://www.aygfsteel.com/Vencent/articles/54086.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <a href="http://www.aygfsteel.com/" title="狠狠久久亚洲欧美专区_中文字幕亚洲综合久久202_国产精品亚洲第五区在线_日本免费网站视频">狠狠久久亚洲欧美专区_中文字幕亚洲综合久久202_国产精品亚洲第五区在线_日本免费网站视频</a> </div> </footer> վ֩ģ壺 <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ԭ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">е</a>| <a href="http://" target="_blank">Ҷ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ɽ</a>| <a href="http://" target="_blank">Ϊ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ֹ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ʩ</a>| <a href="http://" target="_blank">ƽ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ˮ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ʡ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">®</a>| <a href="http://" target="_blank">ˮ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">˳</a>| <a href="http://" target="_blank">ɽ</a>| <a href="http://" target="_blank"></a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>