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 编写。
假如咱们要实现以下文法:
sum ::= number + numbernumber ::= 1 | 2
表白一个最简略的加法文法,其中加法表达式 sum 和 number 都是非终结符,而 +、1、2 是终结符。这个例子只能做到 1 与 2 的加法,通过这个简略例子,理解一下解释器模式的精华吧:
// 形象表达式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") // trueadd.interpreter("1 + 2") // trueadd.interpreter("1 + 3") // falseadd.interpreter("2 - 1") // false
遇到非终结符则持续调用,只有终结符能力直接判断,原理很简略。
弊病
下面的例子是比拟低效场景,因为当语法简单后,类的数目会显著增多,难以保护,此时须要用一个通用语法解析器,理解更多能够看笔者之前的文章:精读《手写 SQL 编译器 - 语法分析》 系列。
总结
解释器是一种思维,将简单语法解析形象为一个个独立的终结符与非终结符各自判断,只有每个文法本人的判断做好了,剩下的工作就是组装文法。
这种将单个逻辑判断与文法组装解耦的做法,能够使逻辑判断与文法组装独立变换,使简单语法解析转化为一个个具体的简略问题。
探讨地址是:精读《设计模式 - Interpreter 解释器模式》· Issue #296 · dt-fe/weekly
如果你想参加探讨,请 点击这里,每周都有新的主题,周末或周一公布。前端精读 - 帮你筛选靠谱的内容。
关注 前端精读微信公众号
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
版权申明:自在转载-非商用-非衍生-放弃署名(创意共享 3.0 许可证)