什么是解释器模式?
在软件开发过程中,我们经常需要解析和执行一些固定格式的代码和规则,例如解析一段JSON字符串,解释一系列自定义的命令等,此时可以使用解释器模式。
解释器模式是一种行为型设计模式,它定义了一种语言,用来解释执行特定的业务规则,将复杂问题转化为一种语言、文法简单的规则表达式,并且可以灵活的扩展解释器,实现新的语法规则。
解释器模式的组成部分
解释器模式由以下四部分组成
- 抽象表达式(Abstract Expression):定义了抽象的解释器接口,用来解释语言中的元素,可以是终止符,也可以是非终止符。
- 终止符表达式(Terminal Expression):实现了抽象表达式中的解释器接口,用来存储语言中的终止符,它们不需要再次解释,通常会返回具体的结果。
- 非终止符表达式(Nonterminal Expression):也实现了抽象表达式中的解释器接口,用来存储语言中的非终止符。
- 上下文(Context):存储解释器解释的信息,并调用解释器进行解释。
解释器模式的思想
解释器模式的思想是将语法规则转换为解释器对象,用解释器对象来解释语法规则。
抽象表达式的主要作用是定义解释器的接口,用来解释语言中的元素,可以是终止符,也可以是非终止符。在具体的解释器中,需要根据元素类型进行相应的操作。
终止符表达式实现了抽象表达式中的解释器接口,用来存储语言中的终止符,它们不需要再次解释,一般会返回具体的结果。
非终止符表达式也实现了抽象表达式中的解释器接口,用来存储语言中的非终止符。非终止符表达式需要使用其他表达式进行组合,来完成复杂的解释任务。
上下文用来存储解释器解释的信息,并调用解释器进行解释。上下文会把需要解释的信息传递给解释器,解释器解释完之后,将结果返回给上下文。
解释器模式的应用场景
解释器模式通常用于编写编译器、正则表达式、查询语言等语法解析器。
在具体应用中,可以根据需求来实现各种具体的解释器,如JSON解析器、XML解析器、命令解析器等。
表达式求值
package main
import (
"fmt"
"strconv"
)
// Expression 表示表达式接口
type Expression interface {
Evaluate() float64
}
// NumberExpression 表示数字表达式
type NumberExpression struct {
value float64
}
func (ne *NumberExpression) Evaluate() float64 {
return ne.value
}
// BinaryExpression 表示二元表达式
type BinaryExpression struct {
left Expression
right Expression
operator string
}
// Evaluate 计算表达式
func (be *BinaryExpression) Evaluate() float64 {
leftValue := be.left.Evaluate()
rightValue := be.right.Evaluate()
switch be.operator {
case "+":
return leftValue + rightValue
case "-":
return leftValue - rightValue
case "*":
return leftValue * rightValue
case "/":
return leftValue / rightValue
default:
panic(fmt.Sprintf("Unknown operator: %s", be.operator))
}
}
// Parser 表示解析器
type Parser struct {
tokens []string
index int
}
// Parse 解析表达式
func (p *Parser) Parse() Expression {
return p.parseExpression(0)
}
// parseExpression 解析表达式
func (p *Parser) parseExpression(precedence int) Expression {
left := p.parsePrimary()
for {
operator := p.peek()
// 如果下一个 token 不是操作符,或者下一个操作符的优先级小于当前优先级,就停止解析
if operator == "" || !isOperator(operator) || getPrecedence(operator) < precedence {
break
}
p.consume()
// 解析右侧表达式
// q: 为什么+1?
// a: 因为如果当前操作符的优先级大于下一个操作符的优先级,就需要先计算当前操作符的表达式
right := p.parseExpression(getPrecedence(operator) + 1)
left = &BinaryExpression{
left: left,
right: right,
operator: operator,
}
}
return left
}
// 是空格
func isSpace(c string) bool {
return c == " "
}
// parsePrimary 解析原子表达式
func (p *Parser) parsePrimary() Expression {
token := p.consume()
if isSpace(token) {
return p.parsePrimary()
}
if isNumber(token) {
value, err := strconv.ParseFloat(token, 64)
if err != nil {
panic(err)
}
return &NumberExpression{value: value}
}
if token == "(" {
expr := p.parseExpression(0)
p.consumeExpect(")")
return expr
}
panic(fmt.Sprintf("Unknown token: %s", token))
}
// peek 查看下一个 token
func (p *Parser) peek() string {
if p.index >= len(p.tokens) {
return ""
}
operator := p.tokens[p.index]
// 判断空格
if isSpace(operator) {
p.index++
return p.peek()
}
return p.tokens[p.index]
}
// consume 消费下一个 token
func (p *Parser) consume() string {
if p.index >= len(p.tokens) {
return ""
}
token := p.tokens[p.index]
p.index++
return token
}
// consumeExpect 消费下一个 token,并且期望它是 expected
func (p *Parser) consumeExpect(expected string) {
actual := p.consume()
if actual != expected {
panic(fmt.Sprintf("Expected token: %s, but got %s", expected, actual))
}
}
// isNumber 判断 token 是否是数字
func isNumber(token string) bool {
_, err := strconv.ParseFloat(token, 64)
return err == nil
}
// isOperator 判断 token 是否是操作符
func isOperator(token string) bool {
return token == "+" || token == "-" || token == "*" || token == "/"
}
// getPrecedence 获取操作符的优先级
func getPrecedence(operator string) int {
if operator == "+" || operator == "-" {
return 1
}
if operator == "*" || operator == "/" {
return 2
}
return 0
}
// 字符串转[]string
func stringToSlice(str string) []string {
var slice []string
for _, v := range str {
slice = append(slice, string(v))
}
return slice
}
func main() {
// 表达式
exprStr := "(1 + 2) * 3 / 4 - 5"
tokens := stringToSlice(exprStr)
parser := Parser{tokens: tokens}
expr := parser.Parse()
result := expr.Evaluate()
fmt.Println(fmt.Sprintf("%s = %f", exprStr, result)) // 输出 (1 + 2) * 3 / 4 - 5 = -2.750000
// 表达式
exprStr = "3 + 2 - 5 * 0"
tokens = stringToSlice(exprStr)
parser = Parser{tokens: tokens}
expr = parser.Parse()
result = expr.Evaluate()
fmt.Println(fmt.Sprintf("%s = %f", exprStr, result)) // 输出 3 + 2 - 5 * 0 = 5.000000
}