http://blog.csdn.net/aimingoo/archive/2009/09/08/4532496.aspx
(书接上回,继续!)
五、这个DSL框架有什么问题?
=============
有什么问题吗?有一点,并不严重。比如说,我们在Env中声明了一些属性和方法。对于Env这个对象
我们要在calc()方法中访问max/min属性,应该写成“this.max/this.min”,这一则是不方便,另外,在用户的dsl代码中还不得不考虑“当前this是谁”的问题。这是问题之一。
第二个,我们传入了一个evaluator(),相当于脚本执行器,那么我们能不能在dsl()代码中也使用这个执行器呢?也就是说,我们的dsl不单是“domain-specific language”,也可以是一个“domain-script language”的。
第三个问题,我们是不是需要一个类似在JavaScript中的window对象的东西,以便能引用到执行环境的全局。
这三个问题都应该是在DSL()层面解决的。简单说来,第二、三个问题,实质是在初始化环境environment,使之具有某些在dsl代码中能访问到的性质。所以很容易处理:
同理的,用户可以在上面这里对environment加入更多性质,这些都是可以在用户的dsl(…)中访问到的。以上面为例,当用户传入的执行器evaluator是一个具名函数的时候,则该函数名会成为dsl(…)环境中的可用的执行函数(类似于exec, execScript或eval等)。例如:
注意在dsl()访问到的myeval()方法,其实不是用户原始的myeval(),而是上述dsl变量的一个引用。这个,从DSL()函数的实现中可以看到。
接下来,就是上面三个问题中的第一个,亦即是在calc()方法从必须使用this.max/this.min的问题。事实上,这是因为声明calc方法的时候,该函数位于Env变量所在的全局闭包里面。这样,它就默认只能访问到全局的变量、标识符。所以,解决这个问题的方法,仍然和前面一样:改变它的闭包位置——使用Scope()函数。如下:
现在有了一个新的、完善的DSL()。使用方法与前面是一致的。比如:
最后,留意一下当调用DSL()的时候,我们标出了”Env”这个全局变量。注意的是,我们直接使用了这个对象。那么它与使用Unique(Env)有什么不同呢?答案是,直接使用Env时,在dsl(…)中的代码可以直接修改到Env中的成员,而如果使用Unique(Env),则dsl(…)中的代码只会修改到Env的一个副本。这样一来,我们就有机会为不同的dsl语言提供各各独立的环境了——这有点象沙箱。
六、变量泄漏?
========
在JavaScript语言中有一个“根深蒂固”的问题,就是“当在函数内访问一个不存在的变量时,引擎会试图在全局变量环境中打找该变量”。这通常是很多很多烂系统的根源。对于我们上面的dsl语言来说,系统其实只给出了五个标识符:max/calc/show/system/myeval。其中的后面两个,是DSL()函数在“语言引擎层面”提供的,其它的则是Env环境变量提供的。“变量泄漏”带来的直接问题是,对于上面的这个例子,dsl(…)中除了能访问这五个标识符之外,还能访问全局的window/String/Number/Math/RegExp/NaN等等预定义对象和属性。而这,可能根本就不是我们的dsl语言需要的。
这怎么办呢?
由于Unique()得到了Env环境对象的一个副本,而且在dsl(…)中无法通过这个副本来修改原始的Env的成员,也不能delete它。所以如果我们在Env的属性中加入这些“受保护的标识符”,那么dsl(…)就只能访问到Env的这些属性,而不会访问到全局里面的了。下面的代码简单地实现这一效果:
七、evaluator/parser是不是太简单了?
=================
当然。我们在evaluator, parser中基本什么也没有做,当然是相当简单的。如果你要做一个完整的DSL,那么你得花一些工夫来做语法解析,并实现在语法树的基础上的代码执行、运行环境的维护等等。我QoBean的DSL()中,主要是提供了一个运行你的代码的基础语言环境,有点象是——嗯——沙箱。
当然,除了沙箱的基本功能之外。DSL()通过environment来维护给用户代码的一组基本标识符(或称为保留字),并保证用户在不同的environment之间不会相互影响。
除了上述的基本描述之外,我们最后再关注一下evaluator和parser的实现。对于下面的代码:
实际上的效果是dsl()将红色显示部分的函数作为一个一个参数source,传入myparser()和myeval()。parser通过Block()取出这个函数代码的body部分,然后交给myeval()中的eval()函数执行。也就是说,我们在DSL()中调用Weave()的效果就是,将myparser()和myeval()并在一起,变成了:
而dsl()最终执行的就是上面这个匿名函数。更进一步,在environment上也会有一个名为’myeval’的方法,指向这个匿名函数。
但是,首先这里就有一个不小的问题:’source’在这里也是一个标识符。在eval(…)中执行时,代码是可以感知到这个标识符的——而对于dsl(…)中的用户代码,source可能是另外需要的一个标识符,所以这里我们要想办法屏蔽掉对这个变量名的依赖。这其实处理起来很简单:
你应该注意到,我们用arguments[0]就可以简单地绕过一个入口参数名的使用了。这个,很简单,也很实用。
接下来,我们总不能要求用户每次执行dsl(…)时都要传入一个函数吧?我们最终声明的用户的DSL可能是相当怪异的、完全不符合JS的语法的,根本就不能写到一个函数中去,又该怎么办呢?这个问题,显然的——首先的——他该是parser的问题。因此我们也就简单地讲一下扩充myparser()的方法。比如说,我们想实现下面的效果:
- 当dsl(…)传入一个字符串时,让myeval()直接执行该字符串;
- 当dsl(…)传入一个函数,但函数体内是完整的整块注释时,让myeval()执行这个注释块。
例如如下的调用:
=========
现在我们需要进一步完善我们的myparser(),提供一个基本的模式来支持这种设计。简单的方法如下:
当然,由于代码的语法规则改变了,所以myeval()的设计也应该发生相应的变化了。而这些,就应该是DSL语言设计者的工作,而不是QoBean在DSL()框架上要考虑的事情了。
八、终结:DSL,关键不在用什么语言实现,而在于为什么Domain设计什么样的语言
=============
我们用Javascript,只写了不到了10行代码,就实现了一个DSL()的通用框架,但是,我们却没有做出对任何一个真实的Domain有意义的DSL。对于Ruby、Python、Erlang还是Scala,或者更原始的LISP或更新的F#这些基础语言,对他们的选择更多的只是喜好或者出于某些局部的优异与方便的考虑,与我们“设计一个DSL”是没有多大的关系的。一个DSL的设计,在于对领域的、领域相关业务的分析与抽象。在这些分析、抽象的基础上,进行语法设计、语义定义,最终才表现为“怎样的一个语言”。当我们看到这个“表现”的时候,整个DSL的设计都已经结束了——我们接下来只需要构建基本运行库(runtime library),以及其上的应用逻辑就好了。所以,大多数看到某个DSL的人,只是它的实现者和使用者,而不是它的设计者。多数人只是埋头于使用,或者激情于评说,而忘了看看“一个具体DSL的背景”。
例如,难道DOS批处理不是一个DSL吗?10行的JavaScript难道不就是一个完整的DSL framework吗?如果是,那么我们还有必要讨论“什么是DSL”,以及“怎样的DSL开发环境更好”的问题吗?我们是不是看看“我们在什么Domain”,以及“这个Domain如何描述、如何结构化和如何逻辑驱动之”,这些问题是不是才是更关键的?
上面两个示例中都有一个相同的dsl代码片断——这是一种假想的、完全不符合javascript的规范的新语言。示例1是通过一个字符串传给dsl()的,示例2仍然是通过一个函数,但函数体内是从/*..*/的一个注释块。
分享到:
相关推荐
1. **JavaScript在Photoshop中的应用**:JavaScript是一种广泛使用的脚本语言,它在Photoshop中允许用户通过编写脚本来自动化重复任务,创建自定义工作流程,以及扩展Photoshop的功能。这包括调整图像参数、处理图层...
JavaScript在Photoshop中的应用是通过ECMAScript实现的,这是一种基于标准的、广泛使用的脚本语言,它为Photoshop提供了强大的编程接口(API),使得开发者可以深入操控图像处理、图层管理、颜色调整等多个方面。...
首先,JavaScript在Photoshop CC中的核心功能是提供一个脚本接口,允许开发者直接操控Photoshop的对象模型。对象模型包含了所有可用的面板、菜单项、图层、选区等元素,可以通过JavaScript进行访问和操作。例如,你...
3. **Photoshop API**:Adobe提供了丰富的API,允许开发者通过JavaScript访问Photoshop的功能。例如,你可以用JavaScript来创建、修改或保存文档,调整图像属性,处理图层,或者实现批量处理任务。 4. **事件处理**...
04-JavaScript脚本语言基础(二)DOM(L)
JavaScript是一种广泛应用于网页开发的脚本语言,它允许在用户浏览器上动态地更新内容,实现交互性和动画效果。"网页JavaScript脚本语言提取器"是一个工具,专门设计用于从网页中抓取并分析JavaScript代码,这在进行...
基于神经网络的恶意脚本分类-JavaScript&VBScript Neural Classification of Malicious Scripts: A study with JavaScript and VBScript
《Photoshop CS6 JavaScript 脚本参考指南》是一份由Adobe Systems Incorporated出版的专业文档,旨在为使用Adobe Creative Suite 6中的Photoshop软件进行JavaScript脚本编程的用户提供全面的指导和参考。...
JavaScript脚本语言是一种广泛应用于网页和网络应用中的编程语言,主要负责实现客户端的交互性和动态效果。本课程针对JavaScript的基础知识进行深入讲解,包括语法、流程控制语句、函数以及数据验证方法,旨在帮助...
JavaScript是一种广泛使用的脚本语言,主要用于Web前端开发,可以实现动态效果、交互功能等。JavaScript与HTML和CSS一起构成了现代Web开发的基础。 #### 二、基本示例 1. **生成文本** ```html ...
项目14-JavaScript脚本编程.pptx
3ds MAXScript脚本语言作为3ds Max的重要组成部分,为用户提供了强大的编程能力,使其能够在3ds Max环境中实现高度自定义和自动化的工作流程。《3ds MAXScript脚本语言完全学习手册》是一本针对该语言的全面指南,...
JavaScript应用实例-脚本商店界面-增加点击事件.js
3. **词法分析和解析**:实现这样的脚本引擎需要理解编译原理,包括词法分析(将源代码分解成标记)和语法解析(将标记转化为抽象语法树)。 4. **虚拟机或解释器设计**:这个引擎可能有一个自己的虚拟机或解释器来...
JavaScript作为世界上最流行的脚本语言之一,在Web开发领域占据着举足轻重的地位。无论是个人开发者还是大型企业,都离不开JavaScript的支持。本教程旨在帮助初学者快速掌握JavaScript的基础知识,同时也适合有一定...
奔梦向前:学编程其实很简单,html、css、JavaScript、html5、css3、vue、Canvas实现网页特效页面、新手入门学习、了解网页动画的制作、代码实现网页动态画面-表白代码脚本-2020-04-28-1。
"如何在C++程序中嵌入JavaScript脚本语言" 本文主要介绍了如何在C++程序中嵌入JavaScript脚本语言,旨在帮助开发人员更好地理解和实现脚本语言在C++程序中的嵌入。 首先,文章简要介绍了脚本语言的兴起和重要性,...
你应该用什么编程语言? 你的服务器设置正确吗? 如果你的服务器不是UNIX系统呢? 解剖CGI脚本 输出头部 输出数据部 带阐述的脚本 传递其他信息给脚本 创建特殊的脚本输出 以装载另一个文本响应 无响应 ...
3. 基于对象和事件驱动:JavaScript脚本语言自身提供了对象,如Math对象、String对象、Date对象等等,还可以使用浏览器对象,如window对象、document对象等,不需要创建类就直接可以使用或通过创建一个新对象来设置...
如何在C++程序中嵌入JavaScript脚本语言.pdf