golang高度可定制的表达式语言插件库gval的使用

Golang高度可定制的表达式语言插件库gval的使用

Gval (Go eVALuate) 是一个支持评估任意表达式的库,特别是类似Go语言的表达式。

gopher

基本功能

Gval可以评估带参数的表达式,支持算术、逻辑和字符串操作:

基本示例

package main

import (
	"fmt"
	"github.com/PaesslerAG/gval"
)

func main() {
	// 基本表达式
	value, err := gval.Evaluate("10 > 0", nil)
	fmt.Println(value, err) // true <nil>
	
	// 带参数的表达式
	value, err = gval.Evaluate("foo > 0", map[string]interface{}{"foo": 5})
	fmt.Println(value, err) // true <nil>
	
	// 嵌套参数表达式
	value, err = gval.Evaluate("foo.bar > 0", map[string]interface{}{
		"foo": map[string]interface{}{"bar": 3},
	})
	fmt.Println(value, err) // true <nil>
	
	// 算术表达式
	value, err = gval.Evaluate("(requests_made * requests_succeeded / 100) >= 90", 
		map[string]interface{}{
			"requests_made": 100,
			"requests_succeeded": 95,
		})
	fmt.Println(value, err) // false <nil>
	
	// 字符串表达式
	value, err = gval.Evaluate(`http_response_body == "service is ok"`, 
		map[string]interface{}{"http_response_body": "service is ok"})
	fmt.Println(value, err) // true <nil>
	
	// 浮点数表达式
	value, err = gval.Evaluate("(mem_used / total_mem) * 100", 
		map[string]interface{}{
			"mem_used": 750,
			"total_mem": 1000,
		})
	fmt.Println(value, err) // 75 <nil>
}

自定义功能

Gval可以轻松扩展自定义函数或运算符:

package main

import (
	"fmt"
	"github.com/PaesslerAG/gval"
	"time"
)

func main() {
	// 自定义日期比较函数
	language := gval.Full(
		gval.Function("date", func(args ...interface{}) (interface{}, error) {
			if len(args) != 1 {
				return nil, fmt.Errorf("date expects exactly one string argument")
			}
			s, ok := args[0].(string)
			if !ok {
				return nil, fmt.Errorf("date expects a string argument")
			}
			return time.Parse("2006-01-02", s)
		}),
	)
	
	value, err := language.Evaluate(`date("2014-01-02") > date("2014-01-01")`, nil)
	fmt.Println(value, err) // true <nil>
	
	// 自定义字符串长度函数
	language = gval.Full(
		gval.Function("strlen", func(args ...interface{}) (interface{}, error) {
			if len(args) != 1 {
				return nil, fmt.Errorf("strlen expects exactly one string argument")
			}
			s, ok := args[0].(string)
			if !ok {
				return nil, fmt.Errorf("strlen expects a string argument")
			}
			return len(s), nil
		}),
	)
	
	value, err = language.Evaluate(`strlen("someReallyLongInputString") <= 16`, nil)
	fmt.Println(value, err) // false <nil>
}

性能优化

可以解析gval.Expression一次并多次重用:

package main

import (
	"fmt"
	"github.com/PaesslerAG/gval"
)

func main() {
	// 解析表达式一次
	expression, err := gval.Full().NewEvaluable("(requests_made * requests_succeeded / 100) >= 90")
	if err != nil {
		panic(err)
	}
	
	// 多次使用不同的参数评估
	for i := 0; i < 5; i++ {
		value, err := expression.Eval(map[string]interface{}{
			"requests_made": 100,
			"requests_succeeded": 90 + i,
		})
		fmt.Println(value, err)
	}
	// 输出:
	// true <nil>
	// true <nil>
	// false <nil>
	// false <nil>
	// false <nil>
}

参数访问

变量可以通过字符串字面量访问:

package main

import (
	"fmt"
	"github.com/PaesslerAG/gval"
)

func main() {
	// 点选择器访问嵌套参数
	value, err := gval.Evaluate("foo.bar > 0", map[string]interface{}{
		"foo": map[string]interface{}{"bar": 3},
	})
	fmt.Println(value, err) // true <nil>
	
	// 方括号选择器访问数组和map
	value, err = gval.Evaluate("foo[0] > 0", map[string]interface{}{
		"foo": []int{1, 2, 3},
	})
	fmt.Println(value, err) // true <nil>
	
	value, err = gval.Evaluate(`foo["bar"] > 0`, map[string]interface{}{
		"foo": map[string]int{"bar": 3},
	})
	fmt.Println(value, err) // true <nil>
}

默认语言特性

gval.Full包含以下元素:

  • 修饰符: + - / * & | ^ ** % >> <<
  • 比较器: > >= < <= == != =~ !~
  • 逻辑运算符: || &&
  • 数字常量,作为64位浮点数(12345.678)
  • 字符串常量(双引号: "foobar")
  • 日期函数’Date(x)’,使用RFC3339、ISO8601、ruby日期或unix日期的任何排列
  • 布尔常量: true false
  • 控制评估顺序的括号 ( )
  • Json数组: [1, 2, "foo"]
  • Json对象: {"a":1, "b":2, "c":"foo"}
  • 前缀: ! - ~
  • 三元条件: ? :
  • 空值合并: ??

自定义语言

Gval是完全可定制的,每个常量、函数或运算符都可以单独定义:

package main

import (
	"fmt"
	"github.com/PaesslerAG/gval"
)

func main() {
	// 创建自定义语言
	language := gval.NewLanguage(
		gval.Arithmetic(), // 包含算术运算符
		gval.Text(),       // 包含文本操作
		gval.PropositionalLogic(), // 包含逻辑运算符
		gval.Function("hello", func(args ...interface{}) (interface{}, error) {
			return "Hello World!", nil
		}),
	)
	
	value, err := language.Evaluate("hello() + ' How are you?'", nil)
	fmt.Println(value, err) // Hello World! How are you? <nil>
}

自定义选择器

如果你想为选择器提供自定义逻辑,可以在你的结构体上实现SelectGVal(ctx context.Context, k string) (interface{}, error)方法:

package main

import (
	"context"
	"fmt"
	"github.com/PaesslerAG/gval"
)

type customSelector struct {
	values map[string]interface{}
}

func (c customSelector) SelectGVal(ctx context.Context, k string) (interface{}, error) {
	v, ok := c.values[k]
	if !ok {
		return nil, fmt.Errorf("unknown field %s", k)
	}
	return v, nil
}

func main() {
	selector := customSelector{
		values: map[string]interface{}{
			"foo": 42,
			"bar": "baz",
		},
	}
	
	value, err := gval.Evaluate("foo + bar", selector)
	fmt.Println(value, err) // 42baz <nil>
}

性能

该库旨在快速运行,但对于大多数应用程序来说已经足够快了。如果性能是一个问题,请确保只创建一次表达式语言,包含所有函数、常量和运算符。


更多关于golang高度可定制的表达式语言插件库gval的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang高度可定制的表达式语言插件库gval的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Gval - Go 高度可定制的表达式语言插件库

Gval 是一个强大的 Go 语言表达式求值库,它提供了高度可定制的表达式解析和求值功能。下面我将详细介绍 Gval 的主要特性和使用方法。

Gval 核心特性

  1. 简单易用:提供简洁的 API 接口
  2. 高度可扩展:支持自定义函数、运算符和变量
  3. 类型安全:支持类型检查和转换
  4. 性能优异:表达式会被预编译以提高执行效率

基本使用示例

package main

import (
	"fmt"
	"github.com/PaesslerAG/gval"
)

func main() {
	// 最简单的表达式求值
	value, err := gval.Evaluate("1 + 2", nil)
	if err != nil {
		panic(err)
	}
	fmt.Println(value) // 输出: 3

	// 带变量的表达式
	value, err = gval.Evaluate("a + b", map[string]interface{}{
		"a": 10,
		"b": 20,
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(value) // 输出: 30
}

自定义函数

func main() {
	// 创建自定义函数
	eval := gval.Full(
		gval.Function("pow", func(args ...interface{}) (interface{}, error) {
			if len(args) != 2 {
				return nil, fmt.Errorf("pow expects exactly 2 arguments")
			}
			x, ok := args[0].(float64)
			if !ok {
				return nil, fmt.Errorf("first argument must be a number")
			}
			y, ok := args[1].(float64)
			if !ok {
				return nil, fmt.Errorf("second argument must be a number")
			}
			return math.Pow(x, y), nil
		}),
	)

	value, err := eval("pow(2, 3)", nil)
	if err != nil {
		panic(err)
	}
	fmt.Println(value) // 输出: 8
}

自定义运算符

func main() {
	// 创建自定义运算符
	eval := gval.Full(
		gval.InfixOperator("**", func(a, b interface{}) (interface{}, error) {
			x, ok := a.(float64)
			if !ok {
				return nil, fmt.Errorf("first operand must be a number")
			}
			y, ok := b.(float64)
			if !ok {
				return nil, fmt.Errorf("second operand must be a number")
			}
			return math.Pow(x, y), nil
		}),
	)

	value, err := eval("2 ** 3", nil)
	if err != nil {
		panic(err)
	}
	fmt.Println(value) // 输出: 8
}

复杂表达式示例

func main() {
	// 复杂表达式示例
	eval := gval.Full()

	params := map[string]interface{}{
		"user": map[string]interface{}{
			"name": "Alice",
			"age":  30,
			"roles": []string{"admin", "editor"},
		},
		"settings": map[string]interface{}{
			"max_login_attempts": 5,
			"block_duration":     "30m",
		},
	}

	expression := `
		user.age >= 18 && 
		contains(user.roles, "admin") && 
		settings.max_login_attempts > 3
	`

	value, err := eval(expression, params)
	if err != nil {
		panic(err)
	}
	fmt.Println(value) // 输出: true
}

性能优化 - 预编译表达式

func main() {
	// 预编译表达式以提高性能
	expression := "a * b + c / d"
	
	// 编译阶段
	evaluable, err := gval.Full().NewEvaluable(expression)
	if err != nil {
		panic(err)
	}
	
	// 执行阶段 (可重复使用)
	for i := 0; i < 5; i++ {
		value, err := evaluable(map[string]interface{}{
			"a": 10 + i,
			"b": 20 + i,
			"c": 30 + i,
			"d": 2,
		})
		if err != nil {
			panic(err)
		}
		fmt.Println(value)
	}
}

实际应用场景

  1. 规则引擎:业务规则配置化
  2. 动态配置:根据条件动态计算配置值
  3. 权限控制:复杂的权限判断逻辑
  4. 数据过滤:动态数据过滤条件

总结

Gval 是一个功能强大且灵活的 Go 表达式求值库,通过其丰富的扩展接口,可以轻松实现各种复杂的表达式逻辑。它的主要优势在于:

  • 简洁直观的 API 设计
  • 强大的扩展能力(函数、运算符等)
  • 良好的性能(支持预编译)
  • 丰富的内置功能(类型转换、集合操作等)

对于需要动态表达式求值的 Go 项目,Gval 是一个非常值得考虑的选择。

回到顶部