Math-Engine
使用 Go 实现的数学表达式解析计算引擎库,它小巧,无任何依赖,具有扩展性(比如可以注册自己的函数到引擎中),比较完整的完成了数学表达式解析执行,包括词法分析、语法分析、构建AST、运行。
go get -u github.com/dengsgo/math-engine
能够处理的表达式样例:
1+127-21+(3-4)*6/2.5(88+(1+8)*6)/2+99123_345_456 * 1.5 - 2 ^ 4-4 * 6 + 2e2 - 1.6e-3sin(pi/2)+cos(45-45*1)+tan(pi/4)99+abs(-1)-ceil(88.8)+floor(88.8)max(min(2^3, 3^2), 10*1.5-7)double(6) + 3double
Demo
Method Support
+-*/%^e()_pisin(x)cos(x)tan(x)cot(x)sec(x)csc(x)abs(x)ceil(x)floor(x)round(x)sqrt(x)cbrt(x)max(x, ...)min(x, ...)noerr(x)double(x)
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
RadianModesin(pi/2) = 1
TrigonometricModeRadianModeAngleMode
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
注意事项:
- 注册的函数名只能是英文字母和数字,且必须英文字母开头(区分大小写);
- 每一个函数名只能且只需注册一次;
- 注册的函数逻辑中如果有 panic ,需要程序自己捕获处理;
- argc=-1,即该函数的参数是可变的,expr 的长度需要开发者自行逻辑判断处理;
Compile
go version 1.12
# Compile Demo go test go build ./math-engine
也可以直接下载已编译好的二进制文件,直接运行:
实现细节
TODO
已实现
+-*/%^1.2e71.2e-7()1+2*6/4+(456-8*9.2)-(2+4^5)*2e3+1.2e-2123_456_789sin, cos, tan, cot, sec, cscabs, ceil, floor, sqrt, cbrt, max, min, noerr
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
^
------------