Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65de19ddb6 | ||
|
|
9c0f11cc55 |
@@ -0,0 +1,125 @@
|
||||
# Interpreter(解释器模式)
|
||||
|
||||
Interpreter(解释器模式)属于行为型模式。
|
||||
|
||||
**意图:给定一个语言,定义它的文法的一种表示,并定义一个解释器。这个解释器使用该表示来解释语言中的句子。**
|
||||
|
||||
任何一门语言,无论是日常语言还是编程语言都有明确的语法,只要有语法就可以用文法描述,并通过语法解释器将字符串的语言结构化。
|
||||
|
||||
## 举例子
|
||||
|
||||
如果看不懂上面的意图介绍,没有关系,设计模式需要在日常工作里用起来,结合例子可以加深你的理解,下面我准备了三个例子,让你体会什么场景下会用到这种设计模式。
|
||||
|
||||
### SQL 解释器
|
||||
|
||||
SQL 是一种描述语言,所以也适用于解释器模式。不同的 SQL 方言有不同的语法,我们可以根据某种特定的 SQL 方言定制一套适配它的文法表达式,再利用 antlr 解析为一颗语法书。在这个例子中,antlr 就是解释器。
|
||||
|
||||
### 代码编译器
|
||||
|
||||
程序语言也因为其天然是字符串的原因,和 SQL、日常语言都类似,需要一种模式解析后才能工作。不同的语言有不同的文法表示,我们只需要一个类似 antlr 的通用解释器,通过传入不同的文法表示,返回不同的对象结构。
|
||||
|
||||
### 自然语言处理
|
||||
|
||||
自然语言处理也是解释器的一种,首先自然语言处理一般只能处理日常语言的子集,因此先定义好支持的范围,再定义一套分词系统与文法表达式,并将分词后的结果传入灌入了此文法表达式的解释器,这样解释器可以返回结构化数据,根据结构化数据再进行分析与加工。
|
||||
|
||||
## 意图解释
|
||||
|
||||
**意图:给定一个语言,定义它的文法的一种表示,并定义一个解释器。这个解释器使用该表示来解释语言中的句子。**
|
||||
|
||||
对于给定的语言,可以是 SQL、代码或自然语言,“定义它的文法的一种表示” 即文法可以有多种表示,只需定义一种。要注意的是,不同文法执行效率会有差异。
|
||||
|
||||
“并定义一个解释器”,这个解释器就是类似 antlr 的东西,传给它一个文法表达式,就可以解析句子了。即:解释器(语言, 文法) = 抽象语法树。
|
||||
|
||||
我们可以直接把文法定义耦合到解释器里,但这样做会导致语法复杂时,解释器难以维护。比较好的方式是定义一套与解释器解耦的文法表达式,通过预处理器最终生成解释器。
|
||||
|
||||
## 结构图
|
||||
|
||||
<img width=600 src="https://img.alicdn.com/imgextra/i4/O1CN019y6R201yinq7xjJEK_!!6000000006613-2-tps-1530-776.png">
|
||||
|
||||
Context 是其他上下文变量,AbstractExpression 是抽象语法表达式。
|
||||
|
||||
可以看到,TerminalExpression(终结符)与 NonterminalExpression(非终结符) 都继承于 AbstractExpression,终结符指的是没有后续展开的符号,非终结符相反,所以非终结符又指向了 AbstractExpression,如此递归。
|
||||
|
||||
## 代码例子
|
||||
|
||||
下面例子使用 typescript 编写。
|
||||
|
||||
假设我们要实现以下文法:
|
||||
|
||||
```text
|
||||
sum ::= number + number
|
||||
number ::= 1 | 2
|
||||
```
|
||||
|
||||
表达一个最简单的加法文法,其中加法表达式 sum 和 number 都是非终结符,而 +、1、2 是终结符。这个例子只能做到 1 与 2 的加法,通过这个简单例子,了解一下解释器模式的精髓吧:
|
||||
|
||||
```typescript
|
||||
// 抽象表达式
|
||||
class AbstractExpression {
|
||||
interpret (text: string) {}
|
||||
}
|
||||
|
||||
// 终结符表达式
|
||||
class TerminalExpression extends AbstractExpression {
|
||||
constructor(values: string[]) {
|
||||
this.values = values
|
||||
}
|
||||
|
||||
interpret(value: string) {
|
||||
// 值必须是其中之一
|
||||
return this.values.includes(value)
|
||||
}
|
||||
}
|
||||
|
||||
// 非终结符表达式
|
||||
class NonterminalExpression extends AbstractExpression {
|
||||
constructor(left: TerminalExpression, right: TerminalExpression) {
|
||||
this.left = left
|
||||
this.right = right
|
||||
}
|
||||
|
||||
interpret(value: string) {
|
||||
if (value.indexOf("+") === -1) {
|
||||
// 必须包含 + 号
|
||||
return false
|
||||
}
|
||||
|
||||
const splitValue = value.split('+')
|
||||
|
||||
return this.left.interpret(splitValue[0])
|
||||
&& this.right.interpret(splitValue[1])
|
||||
}
|
||||
}
|
||||
|
||||
// 调用
|
||||
const context = new Context()
|
||||
const terminal = new TerminalExpression(["1", "2"])
|
||||
const add = new AddExpression(terminal, terminal)
|
||||
|
||||
add.interpreter("1 + 1") // true
|
||||
add.interpreter("1 + 2") // true
|
||||
add.interpreter("1 + 3") // false
|
||||
add.interpreter("2 - 1") // false
|
||||
```
|
||||
|
||||
遇到非终结符则继续调用,只有终结符才能直接判断,原理很简单。
|
||||
|
||||
## 弊端
|
||||
|
||||
上面的例子是比较低效场景,因为当语法复杂后,类的数目会明显增多,难以维护,此时需要用一个通用语法解析器,了解更多可以看笔者之前的文章:[精读《手写 SQL 编译器 - 语法分析》](https://github.com/dt-fe/weekly/blob/v2/066.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E8%AF%AD%E6%B3%95%E5%88%86%E6%9E%90%E3%80%8B.md) 系列。
|
||||
|
||||
## 总结
|
||||
|
||||
解释器是一种思维,将复杂语法解析抽象为一个个独立的终结符与非终结符各自判断,只要每个文法自己的判断做好了,剩下的工作就是组装文法。
|
||||
|
||||
这种将单个逻辑判断与文法组装解耦的做法,可以使逻辑判断与文法组装独立变换,使复杂语法解析转化为一个个具体的简单问题。
|
||||
|
||||
> 讨论地址是:[精读《设计模式 - Interpreter 解释器模式》· Issue #296 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/296)
|
||||
|
||||
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
|
||||
|
||||
> 关注 **前端精读微信公众号**
|
||||
|
||||
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
|
||||
|
||||
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh))
|
||||
Reference in New Issue
Block a user