Go设计模式之解释器模式

162 阅读4分钟

什么是解释器模式?

在软件开发过程中,我们经常需要解析和执行一些固定格式的代码和规则,例如解析一段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
}

执行截图

image.png