Add predicates to list of builtins

This commit is contained in:
Anton Medvedev
2023-08-22 22:30:07 +02:00
parent ed5f9bfbcb
commit 3fad31af59
4 changed files with 68 additions and 23 deletions
+41 -5
View File
@@ -12,11 +12,12 @@ import (
)
type Function struct {
Name string
Func func(args ...interface{}) (interface{}, error)
Types []reflect.Type
Builtin1 func(arg interface{}) interface{}
Validate func(args []reflect.Type) (reflect.Type, error)
Name string
Func func(args ...interface{}) (interface{}, error)
Types []reflect.Type
Builtin1 func(arg interface{}) interface{}
Validate func(args []reflect.Type) (reflect.Type, error)
Predicate bool
}
var (
@@ -34,6 +35,41 @@ func init() {
}
var Builtins = []*Function{
{
Name: "all",
Predicate: true,
Types: types(new(func([]interface{}, func(interface{}) bool) bool)),
},
{
Name: "none",
Predicate: true,
Types: types(new(func([]interface{}, func(interface{}) bool) bool)),
},
{
Name: "any",
Predicate: true,
Types: types(new(func([]interface{}, func(interface{}) bool) bool)),
},
{
Name: "one",
Predicate: true,
Types: types(new(func([]interface{}, func(interface{}) bool) bool)),
},
{
Name: "filter",
Predicate: true,
Types: types(new(func([]interface{}, func(interface{}) bool) []interface{})),
},
{
Name: "map",
Predicate: true,
Types: types(new(func([]interface{}, func(interface{}) interface{}) []interface{})),
},
{
Name: "count",
Predicate: true,
Types: types(new(func([]interface{}, func(interface{}) bool) int)),
},
{
Name: "len",
Builtin1: Len,
+17 -8
View File
@@ -113,6 +113,9 @@ func TestBuiltin_works_with_any(t *testing.T) {
}
for _, b := range builtin.Builtins {
if b.Predicate {
continue
}
t.Run(b.Name, func(t *testing.T) {
arity := 1
if c, ok := config[b.Name]; ok {
@@ -249,12 +252,15 @@ func TestBuiltin_disallow_builtins_override(t *testing.T) {
func TestBuiltin_DisableBuiltin(t *testing.T) {
t.Run("via env", func(t *testing.T) {
for _, name := range builtin.Names {
t.Run(name, func(t *testing.T) {
for _, b := range builtin.Builtins {
if b.Predicate {
continue // TODO: allow to disable predicates
}
t.Run(b.Name, func(t *testing.T) {
env := map[string]interface{}{
name: func() int { return 42 },
b.Name: func() int { return 42 },
}
program, err := expr.Compile(name+"()", expr.Env(env), expr.DisableBuiltin(name))
program, err := expr.Compile(b.Name+"()", expr.Env(env), expr.DisableBuiltin(b.Name))
require.NoError(t, err)
out, err := expr.Run(program, env)
@@ -264,15 +270,18 @@ func TestBuiltin_DisableBuiltin(t *testing.T) {
}
})
t.Run("via expr.Function", func(t *testing.T) {
for _, name := range builtin.Names {
t.Run(name, func(t *testing.T) {
fn := expr.Function(name,
for _, b := range builtin.Builtins {
if b.Predicate {
continue // TODO: allow to disable predicates
}
t.Run(b.Name, func(t *testing.T) {
fn := expr.Function(b.Name,
func(params ...interface{}) (interface{}, error) {
return 42, nil
},
new(func() int),
)
program, err := expr.Compile(name+"()", fn, expr.DisableBuiltin(name))
program, err := expr.Compile(b.Name+"()", fn, expr.DisableBuiltin(b.Name))
require.NoError(t, err)
out, err := expr.Run(program, nil)
+2 -2
View File
@@ -635,7 +635,7 @@ func TestCheck_AllowUndefinedVariables(t *testing.T) {
A int
}
tree, err := parser.Parse(`any + fn()`)
tree, err := parser.Parse(`Any + fn()`)
require.NoError(t, err)
config := conf.New(Env{})
@@ -648,7 +648,7 @@ func TestCheck_AllowUndefinedVariables(t *testing.T) {
func TestCheck_AllowUndefinedVariables_DefaultType(t *testing.T) {
env := map[string]bool{}
tree, err := parser.Parse(`any`)
tree, err := parser.Parse(`Any`)
require.NoError(t, err)
config := conf.New(env)
+8 -8
View File
@@ -12,12 +12,12 @@ func TestDeref_binary(t *testing.T) {
i := 1
env := map[string]interface{}{
"i": &i,
"map": map[string]interface{}{
"obj": map[string]interface{}{
"i": &i,
},
}
t.Run("==", func(t *testing.T) {
program, err := expr.Compile(`i == 1 && map.i == 1`, expr.Env(env))
program, err := expr.Compile(`i == 1 && obj.i == 1`, expr.Env(env))
require.NoError(t, err)
out, err := expr.Run(program, env)
@@ -25,7 +25,7 @@ func TestDeref_binary(t *testing.T) {
require.Equal(t, true, out)
})
t.Run("><", func(t *testing.T) {
program, err := expr.Compile(`i > 0 && map.i < 99`, expr.Env(env))
program, err := expr.Compile(`i > 0 && obj.i < 99`, expr.Env(env))
require.NoError(t, err)
out, err := expr.Run(program, env)
@@ -33,7 +33,7 @@ func TestDeref_binary(t *testing.T) {
require.Equal(t, true, out)
})
t.Run("??+", func(t *testing.T) {
program, err := expr.Compile(`(i ?? map.i) + 1`, expr.Env(env))
program, err := expr.Compile(`(i ?? obj.i) + 1`, expr.Env(env))
require.NoError(t, err)
out, err := expr.Run(program, env)
@@ -47,12 +47,12 @@ func TestDeref_unary(t *testing.T) {
ok := true
env := map[string]interface{}{
"i": &i,
"map": map[string]interface{}{
"obj": map[string]interface{}{
"ok": &ok,
},
}
program, err := expr.Compile(`-i < 0 && !!map.ok`, expr.Env(env))
program, err := expr.Compile(`-i < 0 && !!obj.ok`, expr.Env(env))
require.NoError(t, err)
out, err := expr.Run(program, env)
@@ -64,11 +64,11 @@ func TestDeref_eval(t *testing.T) {
i := 1
env := map[string]interface{}{
"i": &i,
"map": map[string]interface{}{
"obj": map[string]interface{}{
"i": &i,
},
}
out, err := expr.Eval(`i == 1 && map.i == 1`, env)
out, err := expr.Eval(`i == 1 && obj.i == 1`, env)
require.NoError(t, err)
require.Equal(t, true, out)
}