golang任意Go表达式动态求值插件库goval的使用

golang任意Go表达式动态求值插件库goval的使用

goval是一个允许程序评估任意算术/字符串/逻辑表达式的Go库,支持访问变量和调用自定义函数。该项目已经稳定并用于生产系统,采用MIT许可证。

安装和使用

安装

go get -u github.com/maja42/goval

基本示例

package main

import (
	"fmt"
	"github.com/maja42/goval"
)

func main() {
	// 创建评估器
	eval := goval.NewEvaluator()
	
	// 评估简单表达式
	result, err := eval.Evaluate(`42 > 21`, nil, nil) // 返回 <true, nil>
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	fmt.Println("Result:", result)
}

变量访问示例

package main

import (
	"fmt"
	"github.com/maja42/goval"
)

func main() {
	eval := goval.NewEvaluator()
	
	// 定义变量
	variables := map[string]interface{}{
		"uploaded": 146,
		"total":    400,
	}
	
	// 使用变量评估表达式
	result, err := eval.Evaluate(`uploaded * 100 / total`, variables, nil) // 返回 <36, nil>
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	fmt.Println("Result:", result)
}

自定义函数示例

package main

import (
	"fmt"
	"github.com/maja42/goval"
	"runtime"
)

func main() {
	eval := goval.NewEvaluator()
	
	// 定义变量
	variables := map[string]interface{}{
		"os":   runtime.GOOS,
		"arch": runtime.GOARCH,
	}
	
	// 定义自定义函数
	functions := make(map[string]goval.ExpressionFunction)
	functions["strlen"] = func(args ...interface{}) (interface{}, error) {
		str := args[0].(string)
		return len(str), nil
	}
	
	// 使用自定义函数评估表达式
	result, err := eval.Evaluate(`strlen(arch[:2]) + strlen("text")`, variables, functions) // 返回 <6, nil>
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	fmt.Println("Result:", result)
}

正则表达式匹配示例

package main

import (
	"fmt"
	"github.com/maja42/goval"
	"regexp"
)

func main() {
	eval := goval.NewEvaluator()
	
	// 定义正则匹配函数
	functions := make(map[string]goval.ExpressionFunction)
	functions["matches"] = func(args ...interface{}) (interface{}, error) {
		str := args[0].(string)
		exp := args[1].(string)
		reg := regexp.MustCompile(exp)
		return reg.MatchString(str), nil
	}
	
	// 使用正则函数评估表达式
	result1, err := eval.Evaluate(`matches("text", "[a-z]+")`, nil, functions)  // 返回 <true, nil>
	result2, err := eval.Evaluate(`matches("1234", "[a-z]+")`, nil, functions) // 返回 <false, nil>
	
	fmt.Println("Result1:", result1)
	fmt.Println("Result2:", result2)
}

支持的类型

goval完全支持以下类型:

  • nil
  • bool
  • int
  • float64
  • string
  • []interface{} (数组)
  • map[string]interface{} (对象)

在表达式中,intfloat64都有number类型并且可以透明地相互转换。

运算符

goval支持多种运算符,包括算术、逻辑、位运算等。运算符优先级严格遵循C/C++规则。

算术运算符示例

3 + 4               // 7
2 + 2 * 3           // 8
2 * 3 + 2.5         // 8.5
12 - 7 - 5          // 0
24 / 10             // 2
24.0 / 10           // 2.4
2 ** 4              // 16 (幂运算)
10 % 3              // 1

逻辑运算符示例

true && true        // true
false || false      // false
!true               // false
42 > 21             // true
"a" == "a"          // true

三元运算符示例

true ? 1 : 2       // 1
false ? 1 : 2      // 2

替代库

如果你正在寻找通用的评估库,也可以考虑Knetic/govaluate。goval与其主要区别包括:

  • 更直观的语法
  • 无中间AST - 解析和评估一步完成
  • 更好的类型支持
  • 更多运算符
  • 十六进制字面量
  • 数组和对象字面量
  • 有用的错误信息
  • 高度优化的解析器代码
  • 高测试覆盖率

goval是一个功能强大且易于使用的表达式评估库,特别适合需要动态评估表达式的场景。


更多关于golang任意Go表达式动态求值插件库goval的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang任意Go表达式动态求值插件库goval的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用goval库动态求值Go表达式

goval是一个Go语言的动态表达式求值库,它允许你在运行时解析和执行Go语言的表达式。这个库非常有用,当你需要从配置文件、数据库或用户输入中动态执行表达式时。

安装goval

go get github.com/maja42/goval

基本用法

1. 简单表达式求值

package main

import (
	"fmt"
	"github.com/maja42/goval"
)

func main() {
	eval := goval.NewEvaluator()
	
	// 简单数学表达式
	result, err := eval.Evaluate(`1 + 2 * 3`, nil, nil)
	if err != nil {
		panic(err)
	}
	fmt.Println(result) // 输出: 7

	// 布尔表达式
	result, err = eval.Evaluate(`3 > 2 && true`, nil, nil)
	if err != nil {
		panic(err)
	}
	fmt.Println(result) // 输出: true
}

2. 使用变量

func main() {
	eval := goval.NewEvaluator()
	
	// 定义变量
	vars := map[string]interface{}{
		"price":    100.0,
		"quantity": 3,
		"discount": 0.9,
	}
	
	// 使用变量的表达式
	result, err := eval.Evaluate(`price * quantity * discount`, vars, nil)
	if err != nil {
		panic(err)
	}
	fmt.Println(result) // 输出: 270
}

3. 使用函数

func main() {
	eval := goval.NewEvaluator()
	
	// 定义函数
	funcs := map[string]interface{}{
		"min": func(a, b float64) float64 {
			if a < b {
				return a
			}
			return b
		},
		"max": func(a, b float64) float64 {
			if a > b {
				return a
			}
			return b
		},
	}
	
	// 使用函数的表达式
	result, err := eval.Evaluate(`min(10, 5) + max(3, 8)`, nil, funcs)
	if err != nil {
		panic(err)
	}
	fmt.Println(result) // 输出: 13 (5 + 8)
}

4. 复杂示例:条件逻辑

func main() {
	eval := goval.NewEvaluator()
	
	// 定义变量和函数
	vars := map[string]interface{}{
		"age":       25,
		"isStudent": false,
		"income":    50000,
	}
	
	funcs := map[string]interface{}{
		"contains": func(s, substr string) bool {
			return strings.Contains(s, substr)
		},
	}
	
	// 复杂条件表达式
	expr := `(age >= 18 && !isStudent) || 
	         (income > 40000 && contains("high income", "income"))`
	
	result, err := eval.Evaluate(expr, vars, funcs)
	if err != nil {
		panic(err)
	}
	fmt.Println(result) // 输出: true
}

安全注意事项

goval可以执行任意Go代码,因此使用时需要注意安全风险:

  1. 不要直接执行用户输入的表达式,除非你完全信任用户或对输入进行了严格过滤
  2. 可以限制可用的变量和函数,只暴露必要的功能
  3. 考虑使用沙箱环境执行表达式

性能考虑

goval在第一次执行表达式时会进行解析和编译,之后可以缓存表达式以提高性能:

func main() {
	eval := goval.NewEvaluator()
	
	// 预编译表达式
	expr, err := eval.Parse(`a + b * c`)
	if err != nil {
		panic(err)
	}
	
	// 多次执行相同表达式
	vars := map[string]interface{}{"a": 1, "b": 2, "c": 3}
	for i := 0; i < 5; i++ {
		result, err := eval.EvaluateParsed(expr, vars, nil)
		if err != nil {
			panic(err)
		}
		fmt.Println(result) // 每次输出7
		vars["a"] = vars["a"].(int) + 1 // 修改变量
	}
}

限制

goval有一些限制需要注意:

  1. 不支持Go语言的所有特性,主要是表达式求值
  2. 不支持自定义类型的方法调用
  3. 不支持Go的反射功能
  4. 不支持goroutine和channel操作

总结

goval是一个强大的Go表达式求值库,适用于需要动态执行表达式的场景。通过合理使用变量和函数,可以实现复杂的业务逻辑。但在使用时需要注意安全性和性能问题。

对于更复杂的需求,可以考虑结合其他库或使用Go的插件系统,但goval在简单表达式求值场景下是一个非常方便的选择。

回到顶部