Home

Awesome

Math-Engine

Actions Go Report Card godoc.org Goproxy.cn

使用 Go 实现的数学表达式解析计算引擎库,它小巧,无任何依赖,具有扩展性(比如可以注册自己的函数到引擎中),比较完整的完成了数学表达式解析执行,包括词法分析、语法分析、构建AST、运行。

go get -u github.com/dengsgo/math-engine

能够处理的表达式样例:

Demo

asciicast

Method Support

symbolexplanatione.g.
+加,plus1+2 = 3
-减,sub8-3.5 = 4.5
*乘,multiply2*3 = 6
/除,division5/2 = 2.5
%取余,remainder5%2 = 1
^整数次方,integer power2^3 = 8, 3^2 = 9
e科学计数法,E-notation1.2e3 = 1.2e+3 = 1200,1.2e-2 = 0.012
()括号,brackets(2+3)*4 = 20
_数字分隔符,number separator123_456_789 = 123456789
piπpi = 3.141592653589793
sin(x)正弦函数,sinesin(pi/2) = 1
cos(x)余弦函数,cosinecos(0) = 1
tan(x)正切函数,tangenttan(pi/4) = 1
cot(x)余切函数,cotangentcot(pi/4) = 1
sec(x)正割函数,secantsec(0) = 1
csc(x)余割函数,cosecantcsc(pi/2) = 1
abs(x)绝对值,absolute valueabs(-6) = 6
ceil(x)向上取整ceil(4.2) = 5
floor(x)向下取整floor(4.8) = 4
round(x)四舍五入取整round(4.4) = 4, round(4.5) = 5
sqrt(x)平方根,square rootsqrt(4) = abs(sqrt(4)) = 2
cbrt(x)立方根,cube rootcbrt(27) = 3
max(x, ...)参数中的较大值max(1)=1,max(2,3)=3,max(4,8,6,8,10)=10
min(x, ...)参数中的较小值min(1)=1,min(2,3)=2,max(4,8,6,8,10)=4
noerr(x)计算 x 出错时返回 0noerr(1 / 1) = 1, noerr( 1/ 0 ) = 0
double(x)返回 x 的双倍值,这是一个自定义的函数示例,你可以注册任意的自定义函数到引擎中double(6) = 12

Usage

你可以直接引用该库嵌入到自己的程序中:

go get -u github.com/dengsgo/math-engine

在代码中引入:

import "github.com/dengsgo/math-engine/engine"

e.g. 1 常规用法: 直接调用解析执行函数 :

import "github.com/dengsgo/math-engine/engine"

func main() {
  s := "1 + 2 * 6 / 4 + (456 - 8 * 9.2) - (2 + 4 ^ 5)"
  // call top level function
  r, err := engine.ParseAndExec(s)
  if err != nil {
    fmt.Println(err)
  }
  fmt.Printf("%s = %v", s, r)
}

e.g. 2 高级用法: 依次调用函数,手动执行 :

import "github.com/dengsgo/math-engine/engine"

func main() {
	s := "1 + 2 * 6 / 4 + (456 - 8 * 9.2) - (2 + 4 ^ 5)"
	exec(s)
}

// call engine
// one by one
func exec(exp string) {
	// input text -> []token
	toks, err := engine.Parse(exp)
	if err != nil {
		fmt.Println("ERROR: " + err.Error())
		return
	}
	// []token -> AST Tree
	ast := engine.NewAST(toks, exp)
	if ast.Err != nil {
		fmt.Println("ERROR: " + ast.Err.Error())
		return
	}
	// AST builder
	ar := ast.ParseExpression()
	if ast.Err != nil {
		fmt.Println("ERROR: " + ast.Err.Error())
		return
	}
	fmt.Printf("ExprAST: %+v\n", ar)
	// AST traversal -> result
	r := engine.ExprASTResult(ar)
	fmt.Println("progressing ...\t", r)
	fmt.Printf("%s = %v\n", exp, r)
}

编译运行,应该可以看到如下输出:

ExprAST: {Op:- Lhs:{Op:+ Lhs:{Op:+ Lhs:{Val:1} Rhs:{Op:/ Lhs:{Op:* Lhs:{Val:2} Rhs:{Val:6}} Rhs:{Val:4}}} Rhs:{Op:- Lhs:{Val:456} Rhs:{Op:* Lhs:{Val:8} Rhs:{Val:9.2}}}} Rhs:{Op:+ Lhs:{Val:2} Rhs:{Op:^ Lhs:{Val:4} Rhs:{Val:5}}}}
progressing ...  -639.6
1+2*6/4+(456-8*9.2)-(2+4^5) = -639.6

TrigonometricMode

三角函数的参数类型默认为弧度RadianMode,e.g. sin(pi/2) = 1.

你可以通过设置 TrigonometricMode 调整参数类型,可选 弧度RadianMode、角度AngleMode,e.g. :

import "github.com/dengsgo/math-engine/engine"

func main() {
  s := "1 + sin(90)"
  engine.TrigonometricMode = engine.AngleMode
  engine.ParseAndExec(s) // will return 2, nil
  s = "1 + sin(pi/2)"
  engine.TrigonometricMode = engine.RadianMode
  engine.ParseAndExec(s) // will return 2, nil
}

Register Function

math-engine 提供了自定义函数注册到引擎的能力。你可以把常用的函数注册到引擎中,然后就能像内置函数一样在输入的数学表达式中使用。

e.g

  // RegFunction is Top level function
  // the same function name only needs to be registered once.
  // double is register function name.
  // 1 is a number of parameter signatures. should be -1, 0, or a positive integer
  // func(expr ...engine.ExprAST) float64 is your function.
  engine.RegFunction("double", 1, func(expr ...engine.ExprAST) float64 {
    // when argc > 0,you can use the index value directly according to the number of parameters
    // without worrying about crossing the boundary.
    // use ExprASTResult to get the result of the ExprAST structure.
    return engine.ExprASTResult(expr[0]) * 2
  })

然后你就可以在输入的表达式中使用这个函数 double:

//exp := "double(6) + 2"
r, err := engine.ParseAndExec("double(6) + 2")
if err != nil {
  panic(err)
}
fmt.Printf("double(6) + 2 = %f\n", r) // will print : double(6) + 2 = 14.000000

注意事项:

Compile

go version 1.12

# Compile Demo
go test
go build
./math-engine

也可以直接下载已编译好的二进制文件,直接运行:

Github Releases

实现细节

请阅读我的博客文章:用 Go 实现一个完整的数学表达式计算引擎

TODO

已实现

input /> 123+89-0.0.9
ERROR: strconv.ParseFloat: parsing "0.0.9": invalid syntax
want '(' or '0-9' but get '0.0.9'
------------
123+89-0.0.9
       ^
------------