Golang解释器模式

111 阅读1分钟

In my opinion, the Interpreter Design Pattern is simply to create a parser to execute syntax beyond the rules of the language and get results.

We have the following text to process:

1 + 1 = ?
(1 + 1) * 5 / 2 = ?
6.43 * 7 = ?

These are very simple mathematical expressions.

We only use Golang to write a simple program to calculate the result.

But if the mathematical expressions are complex or there are many different mathematical expressions, do we need to write a program for each expression?

So the interpreter design pattern solves this problem. We set a specified syntax input for this expression, then write an interpreter to interpret and calculate the input, and then return the result, that is, only write once, but all different expressions can be run

There is no javascript evel() function in golang. Therefore, we can using goja to use the javascript engine to call the eval() function:

github.com/dop251/goja

We using eval() function to write a Interpreter:

interpreter.go

package designpattern

import (
	"fmt"
	"github.com/dop251/goja"
)

type Context struct {
	expression Expression
}

func NewContext() *Context {
	return &Context{expression: &JavaScriptExpression{}}
}

func (context *Context) Eval(expr string) float64 {
	return context.expression.interpret(expr)
}

type Expression interface {
	interpret(expr string) float64
}

type JavaScriptExpression struct {
}

func (expression *JavaScriptExpression) interpret(expr string) float64 {
	javascript := fmt.Sprintf("eval(%s).toFixed(6)", expr)
	vm := goja.New()
	v, err := vm.RunString(javascript)
	if err != nil {
		panic("Javascript eval expression exception")
	}

	return v.ToFloat()
}

interpreter_test.go

package designpattern_test

import (
	designpattern "ptarmigan-golang-design-pattern/src"
	"testing"
)

func TestInterpreter(t *testing.T) {
	ctx := designpattern.NewContext()
	f1 := ctx.Eval("1 + 1")
	if f1 != 2 {
		t.Error("The result must equals 2")
	}
	f2 := ctx.Eval("12 + 7 * 3")
	if f2 != 33 {
		t.Error("The result must equals 33")
	}
	f3 := ctx.Eval("(1 + 1) * 5 / 2")
	if f3 != 5 {
		t.Error("The result must equals 5")
	}
	f4 := ctx.Eval("13 % 2")
	if f4 != 1 {
		t.Error("The result must equals 1")
	}
	f5 := ctx.Eval("Math.cos(Math.PI / 3) * 2")
	if f5 != 1 {
		t.Error("The result must equals 1")
	}
	f6 := ctx.Eval("6.43 * 7")
	if f6 != 45.01 {
		t.Error("The result must equals 45.01")
	}
}