文档:https://godoc.org/github.com/Knetic/govaluate
参考:https://zhuanlan.zhihu.com/p/122561534
ps:casbin用到该库
govaluate与 JavaScript 中的eval功能类似,用于计算任意表达式的值。此类函数在JavaScript/Python 等动态语言中比较常见。govaluate让 Go 这个编译型语言也有了这个能力!
type EvaluableExpression
EvaluableExpression表示一组表达式标记,它们合在一起是一个可以被计算为单个值的表达式
type EvaluableExpression struct {
QueryDateFormat string
ChecksTypes bool
}
func NewEvaluableExpression(expression string) (*EvaluableExpression, error)
- 从给定的[表达式]字符串中解析一个新的EvaluableExpression
- 如果给定表达式具有无效语法,则返回错误
func NewEvaluableExpressionWithFunctions(expression string, functions map[string]ExpressionFunction) (*EvaluableExpression, error)
- 类似于[NewEvaluableExpression],除了允许使用用户定义的函数。传入该函数的函数将对表达式可用
func (this EvaluableExpression) Eval(parameters Parameters) (interface{}, error)
- 使用给定的[参数]运行整个表达式
func (this EvaluableExpression) Evaluate(parameters map[string]interface{}) (interface{}, error)
- 与“Eval”相同,但会自动将参数映射包装到“govalute”中
type ExpressionFunction
表示可以从表达式中调用的函数。如果由于任何原因,该方法不能准确地生成一个明确的结果,则必须返回一个错误。返回的错误将停止表达式的执行。
type ExpressionFunction func(arguments ...interface{}) (interface{}, error)
type Parameters
当表达式试图使用参数时,可EvaluableExpression使用参数集合来检索参数
type Parameters interface {
Get(name string) (interface{}, error)
}
例子
简单表达式
func main() {
expr, err := govaluate.NewEvaluableExpression("10 > 0")
if err != nil {
log.Fatal("syntax error:", err)
}
result, err := expr.Evaluate(nil)
if err != nil {
log.Fatal("evaluate error:", err)
}
fmt.Println(result) // true
}
带参数表达式
func main() {
expr, _ := govaluate.NewEvaluableExpression("foo > 0")
parameters := make(map[string]interface{})
parameters["foo"] = -1
result, _ := expr.Evaluate(parameters)
fmt.Println(result) // false
expr, _ = govaluate.NewEvaluableExpression("(requests_made * requests_succeeded / 100) >= 90")
parameters = make(map[string]interface{})
parameters["requests_made"] = 100
parameters["requests_succeeded"] = 80
result, _ = expr.Evaluate(parameters)
fmt.Println(result) // false
expr, _ = govaluate.NewEvaluableExpression("(mem_used - total_mem) * 100")
parameters = make(map[string]interface{})
parameters["total_mem"] = 1024
parameters["mem_used"] = 512
result, _ = expr.Evaluate(parameters)
fmt.Println(result) // -51200
}
命名规范
使用govaluate
与直接编写 Go 代码不同,在 Go 代码中标识符中不能出现-
、+
、$
等符号。govaluate
可以通过转义使用这些符号。有两种转义方式:
- 将名称用
[
和]
包裹起来,例如[response-time]
; 使用
\
将紧接着下一个的字符转义。func main() {
expr, _ := govaluate.NewEvaluableExpression("[response-time] < 100")
parameters := make(map[string]interface{})
parameters["response-time"] = 80
result, _ := expr.Evaluate(parameters)
fmt.Println(result) // true
expr, _ = govaluate.NewEvaluableExpression("response\\-time < 50")
parameters = make(map[string]interface{})
parameters["response-time"] = 80
result, _ = expr.Evaluate(parameters)
fmt.Println(result) // false
}
一次”编译”多次运行
func main() {
expr, _ := govaluate.NewEvaluableExpression("a + b")
parameters := make(map[string]interface{})
parameters["a"] = 1
parameters["b"] = 2
result, _ := expr.Evaluate(parameters)
fmt.Println(result) // 3
parameters = make(map[string]interface{})
parameters["a"] = 10
parameters["b"] = 20
result, _ = expr.Evaluate(parameters)
fmt.Println(result) // 30
}
函数
如果仅仅能进行常规的算数和逻辑运算,
govaluate
的功能会大打折扣。govaluate
提供了自定义函数的功能。所有自定义函数需要先定义好,存入一个map[string]govaluate.ExpressionFunction
变量中,然后调用govaluate.NewEvaluableExpressionWithFunctions()
生成表达式,此表达式中就可以使用这些函数了。自定义函数类型为func (args ...interface{}) (interface{}, error)
,如果函数返回错误,则这个表达式求值返回错误。func main() {
functions := map[string]govaluate.ExpressionFunction{
"strlen": func(args ...interface{}) (interface{}, error) {
length := len(args[0].(string))
return length, nil
},
}
exprString := "strlen('teststring')"
expr, _ := govaluate.NewEvaluableExpressionWithFunctions(exprString, functions)
result, _ := expr.Evaluate(nil)
fmt.Println(result) // 10
}
支持的操作和类型
govaluate
支持的操作和类型与 Go 语言有些不同。一方面govaluate
中的类型和操作不如 Go 丰富,另一方面govaluate
也对一些操作进行了扩展。
算数、比较和逻辑运算:
+``-``/``*``&``|``^``**``%``>>``<<
:加减乘除,按位与,按位或,异或,乘方,取模,左移和右移;>``>=``<``<=``==``!=``=~``!~
:=~
为正则匹配,!~
为正则不匹配;||``&&
:逻辑或和逻辑与。
常量:
- 数字常量,
govaluate
中将数字都作为 64 位浮点数处理; - 字符串常量,注意在
govaluate
中,字符串用单引号'
; - 日期时间常量,格式与字符串相同,
govaluate
会尝试自动解析字符串是否是日期,只支持 RFC3339、ISO8601等有限的格式; - 布尔常量:
true
、false
。
其他:
- 圆括号可以改变计算优先级;
- 数组定义在
()
中,每个元素之间用,
分隔,可以支持任意的元素类型,如(1, 2, 'foo')
。实际上在govaluate
中数组是用[]interface{}
来表示的; - 三目运算符:
? :
。