一款可以使用json编码的代码执行器,可以用来实现规则引擎
特点:
- 简单
- 灵活
- 高效
goos: darwin goarch: amd64 pkg: github.com/liangdas/lithengine cpu: Intel(R) Core(TM) i5-1038NG7 CPU @ 2.00GHz BenchmarkRiskLogic BenchmarkRiskLogic-8 1768824 611.0 ns/op PASS
#支持数据类型
- nil
- {"nil":true}
- string
- {"string":"string"}
- "string"
- int64
- {"int64":666}
- double
- {"double":6.6}
- 6.6
- bool
- {"bool":true}
- true
- list
- {"list":["string",666]}
- ["string",666]
- hash
- {"hash":{"a":"string","b":666}}
- 函数
- { "in": [。。。] }
- 延迟函数
- 延迟函数可以作为参数传递(不会提前执行)
- { "closure": true, "in": [。。。] }
- 代码块
- 内置支持
- 加,
- 减,
- 乘,
- 除,
- =,
- !=,
- >,<,>=,<=,&&,||,
- not,
- if,
- case,
- int64(浮点数转int64),
- getArgs(获取传参),
- in(包含检查),
- getHash,
- isType(类型判断)
- chain (串行执行多个表达式)
- exec (执行表达式)
- set (设置变量)
- get (读取变量)
- defun (函数定义)
- 支持添加自定义函数
伪代码
10.0+15.0+5.0=30.0
engine := NewBaseEngine()
output, err := engine.ExecParse(context.Background(), []byte(
`{
"=": [
{"+": [10,15,5]},
30
]
}`,
))
语法1:
{"+":[]}
语法2:
{"func":"+",input:[]}
语法1:
{"getArgs":"clientId"}
语法2:
{"getArgs":["clientId"]}
语法3:
{"func":"getArgs",input:["clientId"]}
语法1:
{"+":[1,1]}
语法2:
{"func":"+",input:[1,1]}
chain 会按顺序执行传入的表达式
当遇到表达式返回{"return":[结果]}类型时终止后续表达式执行且使用return的结果作为chain的输出
伪代码
func chain(){
if false {
return "a"
}
if false {
return "b"
}
return: "c"
}
engine := NewBaseEngine()
output, err := engine.ExecParse(context.Background(), []byte(
`{
"chain": [
{
"if":[false,{"return": "a"}]
},
{
"if":[false,{"return": "b"}]
},
{"return": "c"}
]
}`,
))
结果 "c"
设置变量
{
"set":[
"name", //第一个参数是变量名
"value" //第二个参数是变量值
]
}
获取变量
{
"get":[
"name", //第一个参数是变量名
"defaultValue" //第二个参数是当变量不存在时返回的默认值,这是可选入参,不传时当变量不存在时返回 {"nil":true}
]
}
伪代码
func chain(){
a="b"
return a
}
engine := NewBaseEngine()
output, err := engine.ExecParse(context.Background(), []byte(
`{
"chain": [
{"set": ["a","b"]},
{"return": {"get": "a"}}
]
}`,
))
结果 "b"
伪代码
func execFunc(){
a="aa"
}
func chain(){
getArgs("execFunc")()
return a
}
args["execFunc"]=execFunc
chain()
engine := NewBaseEngine()
output, err := engine.ExecParse(context.Background(), []byte(
`{
"args":{"execFunc":{"closure":true,"set":["a","aa"]}},
"exec":
{
"chain":[
{"exec": {"getArgs":"execFunc"}},
{"return":{"get":"a"}}
]
}
}`,
))
结果 "aa"
Defun funcName [inputType] block
eg 定义
{
"defun":[
"notEq", //函数名
[{"name":"a"},{"name":"b"}], //入参名称
{"not":{"eq":[{"get":"a"},{"get":"b"}]}} //函数体
]
}
使用
{"notEq":[3,4]}
伪代码
func notEq(a,b){
return a!=b;
}
engine := NewBaseEngine()
output, err := engine.ExecParse(context.Background(), []byte(
`{
"chain": [
{
"defun":[
"notEq",
[{"name":"a"},{"name":"b"}],
{"not":{"eq":[{"get":"a"},{"get":"b"}]}}
]
},
{
"return":{"notEq":[3,4]}
}
]
}`,
))
assert.Empty(t, err)
assert.Equal(t, output.Bool, true)
engine := NewEngine(rFuncMap, rBlockMap)
engine.RegisterFunc("getMetadata", func(context context.Context, e *Engine, inputs []*pb.Struct) ([]*pb.Struct, error) {
md, ok := FromContext(context)
if !ok {
return nil, errors.New("no args")
}
a, err := e.Exec(context, inputs[0])
if err != nil {
return nil, err
}
if a.StructType != pb.StructType_string {
return nil, errors.New(fmt.Sprintf("%v not be string", a.StructType.String()))
}
str, ok := md.GetExtra(a.String_)
if !ok {
if len(inputs) >= 2 {
return []*pb.Struct{
inputs[1],
}, nil
}
return nil, errors.New("no args interface")
}
return []*pb.Struct{
&pb.Struct{
StructType: pb.StructType_string,
String_: str.(string),
},
}, nil
})
engine.RegisterFunc("removeMetadata", func(context context.Context, e *Engine, inputs []*pb.Struct) ([]*pb.Struct, error) {
md, ok := FromContext(context)
if !ok {
return nil, errors.New("no args")
}
a, err := e.Exec(context, inputs[0])
if err != nil {
return nil, err
}
if a.StructType != pb.StructType_string {
return nil, errors.New(fmt.Sprintf("%v not be string", a.StructType.String()))
}
md.RemoveExtra(a.String_)
return []*pb.Struct{
&pb.Struct{
StructType: pb.StructType_bool,
Bool: true,
},
}, nil
})
md := New(map[string]*pb.Struct{})
md.SetExtra("interface", "this is interface string")
output, err := engine.ExecParse(NewContext(context.Background(), md), []byte(
`{"getMetadata": ["interface"]}`,
))
assert.Empty(t, err)
assert.Equal(t, output.String_, "this is interface string")
更多示例请见 engine_test.go