百度广告
Ward Cunningham 曾经说过,干净的代码清晰地表达了代码编写者所想要表达的东西,而优美的代码则更进一步,优美的代码看起来就像是专门为了要解决的问题而存在的。在本文中,我们将展示一个组合式解析器的设计、实现过程,最终的代码是优美的,极具扩展性,就像是为了解析特定的语法而存在的。我们还会选取 H.248 协议中的一个例子,用上述的组合式解析器实现其语法解析器。读者在这个过程中不仅能体会到代码的美感,还可以学习到函数式编程以及构建 DSL 的一些知识。
DSL 设计基础
我们在用编程语言(比如:Java 语言)实现某项功能时,其实就是在指挥计算机去完成这项功能。但是,语言能够带给我们的并不仅仅局限于此。更重要的是,语言提供了一种用于组织我们的思维,表达计算过程的框架。这个框架的核心就在于,如何能够把简单的概念组合成更为复杂的概念,同时又保持其对于组合方法的封闭,也就是说,组合起来的复杂概念对于组合手段来说和简单的概念别无二致。引用"Structure and Interpretation of Computer Programs"一书中的话来讲,任何一个强大的语言都是通过如下三种机制来达成这个目标的:
原子:语言中最简单、最基本的实体;
抽象手段:命名复杂实体的方法,命名后的复杂实体可以和原子一样通过组合手段组合成为更复杂的实体。
我们曾经在"基于 Java 的界面布局 DSL 的设计与实现"一文中,构建了一种用于界面布局的 DSL,其中呈现了上述思想。在本文中,我们将以解析器的构造为例,来讲述如何构建一种用于字符串解析的 DSL,这种 DSL 具有强大的扩展能力,读者可以根据自己的需要定义出自己的组合手段。此外,从本文中读者还可以领略到 函数编程的优雅之处。
解析器原子
什么是解析器?最简单的解析器是什么?大家通常都会认为解析器就是判断输入的字符串是否满足给定的语法规则,在需要时还可以提取出相应的语法单位实例。从概念上来讲,这种理解没什么问题。不过要想定义出用于解析的 DSL,那么就需要更为精确的定义,也就是说我们要定义出解析器这个概念的确切类型。在 Java 中,我们用 interface 来定义解析器类型,如下:
{
}
class Result
private String recognized;
private boolean succeeded;
boolean succeeded) {
this.remaining = remaining;
}
return succeeded;
public String get_recognized() {
}
return remaining;
public static Result succeed(String recognized,
return new Result(recognized, remaining, true);
public static Result fail() {
} }
class Zero implements Parser
public Result parse(String target) {
}
Zero 解析器一定会解析成功,不做任何语法单位识别并直接返回目标字符串。下面我们来定义另外一个很简单的解析器 Item,只要目标字符串不为空,Item 就会把目标字符串的第一个字符作为其识别结果,并返回成功,如果目标字符串为空,就返回失败,Item 的定义如下:
{
if(target.length() > 0) {
target.substring(1));
return Result.fail();
}
点击加载更多评论>>