文档: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表示一组表达式标记,它们合在一起是一个可以被计算为单个值的表达式

  1. type EvaluableExpression struct {
  2. QueryDateFormat string
  3. ChecksTypes bool
  4. }

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

表示可以从表达式中调用的函数。如果由于任何原因,该方法不能准确地生成一个明确的结果,则必须返回一个错误。返回的错误将停止表达式的执行。

  1. type ExpressionFunction func(arguments ...interface{}) (interface{}, error)

type Parameters

当表达式试图使用参数时,可EvaluableExpression使用参数集合来检索参数

  1. type Parameters interface {
  2. Get(name string) (interface{}, error)
  3. }

例子

简单表达式

  1. func main() {
  2. expr, err := govaluate.NewEvaluableExpression("10 > 0")
  3. if err != nil {
  4. log.Fatal("syntax error:", err)
  5. }
  6. result, err := expr.Evaluate(nil)
  7. if err != nil {
  8. log.Fatal("evaluate error:", err)
  9. }
  10. fmt.Println(result) // true
  11. }

带参数表达式

  1. func main() {
  2. expr, _ := govaluate.NewEvaluableExpression("foo > 0")
  3. parameters := make(map[string]interface{})
  4. parameters["foo"] = -1
  5. result, _ := expr.Evaluate(parameters)
  6. fmt.Println(result) // false
  7. expr, _ = govaluate.NewEvaluableExpression("(requests_made * requests_succeeded / 100) >= 90")
  8. parameters = make(map[string]interface{})
  9. parameters["requests_made"] = 100
  10. parameters["requests_succeeded"] = 80
  11. result, _ = expr.Evaluate(parameters)
  12. fmt.Println(result) // false
  13. expr, _ = govaluate.NewEvaluableExpression("(mem_used - total_mem) * 100")
  14. parameters = make(map[string]interface{})
  15. parameters["total_mem"] = 1024
  16. parameters["mem_used"] = 512
  17. result, _ = expr.Evaluate(parameters)
  18. fmt.Println(result) // -51200
  19. }

命名规范

使用govaluate与直接编写 Go 代码不同,在 Go 代码中标识符中不能出现-+$等符号。govaluate可以通过转义使用这些符号。有两种转义方式:

  • 将名称用[]包裹起来,例如[response-time]
  • 使用\将紧接着下一个的字符转义。

    1. func main() {
    2. expr, _ := govaluate.NewEvaluableExpression("[response-time] < 100")
    3. parameters := make(map[string]interface{})
    4. parameters["response-time"] = 80
    5. result, _ := expr.Evaluate(parameters)
    6. fmt.Println(result) // true
    7. expr, _ = govaluate.NewEvaluableExpression("response\\-time < 50")
    8. parameters = make(map[string]interface{})
    9. parameters["response-time"] = 80
    10. result, _ = expr.Evaluate(parameters)
    11. fmt.Println(result) // false
    12. }

    一次”编译”多次运行

    1. func main() {
    2. expr, _ := govaluate.NewEvaluableExpression("a + b")
    3. parameters := make(map[string]interface{})
    4. parameters["a"] = 1
    5. parameters["b"] = 2
    6. result, _ := expr.Evaluate(parameters)
    7. fmt.Println(result) // 3
    8. parameters = make(map[string]interface{})
    9. parameters["a"] = 10
    10. parameters["b"] = 20
    11. result, _ = expr.Evaluate(parameters)
    12. fmt.Println(result) // 30
    13. }

    函数

    如果仅仅能进行常规的算数和逻辑运算,govaluate的功能会大打折扣。govaluate提供了自定义函数的功能。所有自定义函数需要先定义好,存入一个map[string]govaluate.ExpressionFunction变量中,然后调用govaluate.NewEvaluableExpressionWithFunctions()生成表达式,此表达式中就可以使用这些函数了。自定义函数类型为func (args ...interface{}) (interface{}, error),如果函数返回错误,则这个表达式求值返回错误。

    1. func main() {
    2. functions := map[string]govaluate.ExpressionFunction{
    3. "strlen": func(args ...interface{}) (interface{}, error) {
    4. length := len(args[0].(string))
    5. return length, nil
    6. },
    7. }
    8. exprString := "strlen('teststring')"
    9. expr, _ := govaluate.NewEvaluableExpressionWithFunctions(exprString, functions)
    10. result, _ := expr.Evaluate(nil)
    11. fmt.Println(result) // 10
    12. }

支持的操作和类型

govaluate支持的操作和类型与 Go 语言有些不同。一方面govaluate中的类型和操作不如 Go 丰富,另一方面govaluate也对一些操作进行了扩展。
算数、比较和逻辑运算:

  • +``-``/``*``&``|``^``**``%``>>``<<:加减乘除,按位与,按位或,异或,乘方,取模,左移和右移;
  • >``>=``<``<=``==``!=``=~``!~=~为正则匹配,!~为正则不匹配;
  • ||``&&:逻辑或和逻辑与。

常量:

  • 数字常量,govaluate中将数字都作为 64 位浮点数处理;
  • 字符串常量,注意在govaluate中,字符串用单引号'
  • 日期时间常量,格式与字符串相同,govaluate会尝试自动解析字符串是否是日期,只支持 RFC3339、ISO8601等有限的格式;
  • 布尔常量:truefalse

其他:

  • 圆括号可以改变计算优先级;
  • 数组定义在()中,每个元素之间用,分隔,可以支持任意的元素类型,如(1, 2, 'foo')。实际上在govaluate中数组是用[]interface{}来表示的;
  • 三目运算符:? :