3.3 外部DSL集成模式

怎样在应用程序中集成XML?你会立即回答:“使用XML解析器!”没错!因为XML不同于应用程序的宿主语言,所以需要单独的解析和处理机制。XML应用非常广泛,相应地工具非常多,比如XPath、XQuery等,而且几乎随便一个企业解决方案都会带上各式各样XML解析器。在应用程序中集成XML简直轻而易举。然而很遗憾,我们为应用程序设计的外部DSL没有这样的“全套行头”。集成我们的外部DSL到应用程序更多地依赖于特殊情况下的特殊举措,难以推广成通用的模式。

基于上一段中的说法,你恐怕觉得集成外部DSL会是软件开发中的一场噩梦。是否如此则取决于DSL的复杂度和实现技术。如果外部DSL的解析器是采用ANTLR、YACC等标准工具来开发的,那么集成起来还是很简单的。如果重读一遍2.3.2节,你会发现那里描述的每一种外部DSL实现模式,都为其设计产物留下了显而易见的集成入口。

那我们就再细数一遍2.3.2节中的外部DSL模式,试试找出它们的集成入口。表3-3对于如何集成外部DSL到应用程序作了总结。

表3-3 外部DSL的集成入口

外部DSL模式 集成入口
上下文驱动的字符串操控 字符串经过分词处理被转化为宿主语言代码,这一过程中用到了正则表达式匹配和动态代码解释等技术。转化得来的代码片段就是与应用程序集成的入口
XML转换成可使用的资源 XML解析器就是最自然的集成入口。经过解析,XML被转化成宿主语言中的数据结构,可被应用程序直接使用
非文本表示 非文本表示被转化成AST。以AST为基础,我们可以生成多种形式的具体语法树。只要根据应用本身使用的宿主语言生成一棵该语言的具体语法树,我们就有了集成入口
DSL中内嵌异质代码 DSL处理引擎按内嵌代码所属语言把DSL转化成该语言的适当数据结构,同时将各段内嵌代码作为回调函数插入其中。结果得到内嵌代码中的一组数据结构,由于语言相同,可以直接被核心应用程序使用
基于解析器组合子的DSL设计 在Scala等语言中,解析器组合子被实现为库。以宿主语言写成的组合子就是解析外部DSL的规则。利用一些嵌入的宿主语言代码,规则一边解析,一边填充语义模型的数据结构。当规则约减到AST的最高节点,我们就得到了完整的DSL语义模型

为何外部DSL集成模式讲解得不如内部DSL集成模式那么详细?内部DSL集成只需要倚靠宿主语言,而外部DSL根据其领域常需要种类不确定的、数量又比较多的全套设施。比起用宿主语言设计一套API,语言处理设施的设计没有一定之规。因此,若脱离了具体的环境和要求,我们很难一般性地讨论外部DSL集成模式。第7章和第8章将以具体示例来详细介绍这方面的技术。

enter image description here第7章讨论如何用ANTLR设计DSL,ANTLR是常用的解析器生成器。我们还介绍了利用DSL工作台的一些外部DSL生成工具。另外,针对采用ANTLR和DSL工作台两种方式下产生的外部DSL,我们还介绍了如何将DSL与核心应用程序集成。

第8章详细介绍了如何利用Scala解析器组合子设计外部DSL。

我们已经介绍完毕内部DSL和外部DSL的所有集成模式,这些模式足以应对工作中的大部分情况。全篇讨论都假定核心应用程序是以Java开发的,而准备集成的DSL则是以表现力更强的语言写成的。这个假设符合现实中最常见的情况,所以请务必充分理解本章讨论的这些集成问题。

本章一开篇就在图3-1中展示了DSL驱动应用程序开发中头等重要的问题:集成DSL到核心应用程序。但不时有些开发者忘了提前规划这个问题,直到开发后期才开始考虑。我们即将讨论的下一个问题,也时常在起始阶段被开发者忽略:决定错误及异常处理的策略。这是一件应该优先去做的事情,尤其是DSL的用户基数比较大的时候。