Make all builtins working with deref

Fixes #730
This commit is contained in:
Anton Medvedev
2025-03-08 17:01:31 +01:00
parent 6c8437454d
commit 3fc6c1f535
5 changed files with 74 additions and 42 deletions
+22 -19
View File
@@ -164,7 +164,7 @@ var Builtins = []*Function{
if len(args) != 1 {
return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
}
switch kind(deref.Type(args[0])) {
switch kind(args[0]) {
case reflect.Interface:
return integerType, nil
case reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
@@ -642,18 +642,21 @@ var Builtins = []*Function{
if len(args) != 2 {
return nil, fmt.Errorf("invalid number of arguments (expected 2, got %d)", len(args))
}
v := reflect.ValueOf(args[0])
v := deref.ValueOf(args[0])
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
return nil, fmt.Errorf("cannot take from %s", v.Kind())
}
n := reflect.ValueOf(args[1])
n := deref.ValueOf(args[1])
if !n.CanInt() {
return nil, fmt.Errorf("cannot take %s elements", n.Kind())
}
to := 0
if n.Int() > int64(v.Len()) {
return args[0], nil
to = v.Len()
} else {
to = int(n.Int())
}
return v.Slice(0, int(n.Int())).Interface(), nil
return v.Slice(0, to).Interface(), nil
},
Validate: func(args []reflect.Type) (reflect.Type, error) {
if len(args) != 2 {
@@ -678,7 +681,7 @@ var Builtins = []*Function{
if len(args) != 1 {
return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
}
v := reflect.ValueOf(args[0])
v := deref.ValueOf(args[0])
if v.Kind() != reflect.Map {
return nil, fmt.Errorf("cannot get keys from %s", v.Kind())
}
@@ -708,7 +711,7 @@ var Builtins = []*Function{
if len(args) != 1 {
return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
}
v := reflect.ValueOf(args[0])
v := deref.ValueOf(args[0])
if v.Kind() != reflect.Map {
return nil, fmt.Errorf("cannot get values from %s", v.Kind())
}
@@ -738,7 +741,7 @@ var Builtins = []*Function{
if len(args) != 1 {
return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
}
v := reflect.ValueOf(args[0])
v := deref.ValueOf(args[0])
if v.Kind() != reflect.Map {
return nil, fmt.Errorf("cannot transform %s to pairs", v.Kind())
}
@@ -766,7 +769,7 @@ var Builtins = []*Function{
if len(args) != 1 {
return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
}
v := reflect.ValueOf(args[0])
v := deref.ValueOf(args[0])
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
return nil, fmt.Errorf("cannot transform %s from pairs", v)
}
@@ -798,14 +801,14 @@ var Builtins = []*Function{
},
{
Name: "reverse",
Func: func(args ...any) (any, error) {
Safe: func(args ...any) (any, uint, error) {
if len(args) != 1 {
return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
return nil, 0, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
}
v := reflect.ValueOf(args[0])
v := deref.ValueOf(args[0])
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
return nil, fmt.Errorf("cannot reverse %s", v.Kind())
return nil, 0, fmt.Errorf("cannot reverse %s", v.Kind())
}
size := v.Len()
@@ -815,7 +818,7 @@ var Builtins = []*Function{
arr[i] = v.Index(size - i - 1).Interface()
}
return arr, nil
return arr, uint(size), nil
},
Validate: func(args []reflect.Type) (reflect.Type, error) {
@@ -838,7 +841,7 @@ var Builtins = []*Function{
return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
}
v := reflect.ValueOf(deref.Deref(args[0]))
v := deref.ValueOf(deref.Deref(args[0]))
if v.Kind() != reflect.Array && v.Kind() != reflect.Slice {
return nil, fmt.Errorf("cannot uniq %s", v.Kind())
}
@@ -892,7 +895,7 @@ var Builtins = []*Function{
var arr []any
for _, arg := range args {
v := reflect.ValueOf(deref.Deref(arg))
v := deref.ValueOf(deref.Deref(arg))
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
return nil, 0, fmt.Errorf("cannot concat %s", v.Kind())
@@ -914,7 +917,7 @@ var Builtins = []*Function{
}
for _, arg := range args {
switch kind(deref.Type(arg)) {
switch kind(arg) {
case reflect.Interface, reflect.Slice, reflect.Array:
default:
return anyType, fmt.Errorf("cannot concat %s", arg)
@@ -931,7 +934,7 @@ var Builtins = []*Function{
if len(args) != 1 {
return nil, 0, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
}
v := reflect.ValueOf(deref.Deref(args[0]))
v := deref.ValueOf(deref.Deref(args[0]))
if v.Kind() != reflect.Array && v.Kind() != reflect.Slice {
return nil, size, fmt.Errorf("cannot flatten %s", v.Kind())
}
@@ -945,7 +948,7 @@ var Builtins = []*Function{
}
for _, arg := range args {
switch kind(deref.Type(arg)) {
switch kind(arg) {
case reflect.Interface, reflect.Slice, reflect.Array:
default:
return anyType, fmt.Errorf("cannot flatten %s", arg)
+38 -7
View File
@@ -639,15 +639,46 @@ func Test_int_unwraps_underlying_value(t *testing.T) {
assert.Equal(t, true, out)
}
func TestBuiltin_int_with_deref(t *testing.T) {
func TestBuiltin_with_deref(t *testing.T) {
x := 42
arr := []any{1, 2, 3}
m := map[string]any{"a": 1, "b": 2}
env := map[string]any{
"x": &x,
"x": &x,
"arr": &arr,
"m": &m,
}
program, err := expr.Compile(`int(x)`, expr.Env(env))
require.NoError(t, err)
out, err := expr.Run(program, env)
require.NoError(t, err)
assert.Equal(t, 42, out)
tests := []struct {
input string
want any
}{
{`int(x)`, 42},
{`float(x)`, 42.0},
{`abs(x)`, 42},
{`first(arr)`, 1},
{`last(arr)`, 3},
{`take(arr, 1)`, []any{1}},
{`take(arr, x)`, []any{1, 2, 3}},
{`'a' in keys(m)`, true},
{`1 in values(m)`, true},
{`len(arr)`, 3},
{`type(arr)`, "array"},
{`type(m)`, "map"},
{`reverse(arr)`, []any{3, 2, 1}},
{`uniq(arr)`, []any{1, 2, 3}},
{`concat(arr, arr)`, []any{1, 2, 3, 1, 2, 3}},
{`flatten([arr, [arr]])`, []any{1, 2, 3, 1, 2, 3}},
}
for _, test := range tests {
t.Run(test.input, func(t *testing.T) {
program, err := expr.Compile(test.input, expr.Env(env))
require.NoError(t, err)
out, err := expr.Run(program, env)
require.NoError(t, err)
assert.Equal(t, test.want, out)
})
}
}
+7 -16
View File
@@ -11,7 +11,7 @@ import (
)
func Len(x any) any {
v := reflect.ValueOf(x)
v := deref.ValueOf(x)
switch v.Kind() {
case reflect.Array, reflect.Slice, reflect.Map:
return v.Len()
@@ -26,16 +26,7 @@ func Type(arg any) any {
if arg == nil {
return "nil"
}
v := reflect.ValueOf(arg)
for {
if v.Kind() == reflect.Ptr {
v = v.Elem()
} else if v.Kind() == reflect.Interface {
v = v.Elem()
} else {
break
}
}
v := deref.ValueOf(arg)
if v.Type().Name() != "" && v.Type().PkgPath() != "" {
return fmt.Sprintf("%s.%s", v.Type().PkgPath(), v.Type().Name())
}
@@ -66,7 +57,7 @@ func Type(arg any) any {
}
func Abs(x any) any {
switch x := x.(type) {
switch x := deref.Deref(x).(type) {
case float32:
if x < 0 {
return -x
@@ -221,7 +212,7 @@ func Int(x any) any {
}
func Float(x any) any {
switch x := x.(type) {
switch x := deref.Deref(x).(type) {
case float32:
return float64(x)
case float64:
@@ -264,7 +255,7 @@ func String(arg any) any {
func minMax(name string, fn func(any, any) bool, args ...any) (any, error) {
var val any
for _, arg := range args {
rv := reflect.ValueOf(deref.Deref(arg))
rv := deref.ValueOf(arg)
switch rv.Kind() {
case reflect.Array, reflect.Slice:
size := rv.Len()
@@ -307,7 +298,7 @@ func mean(args ...any) (int, float64, error) {
var count int
for _, arg := range args {
rv := reflect.ValueOf(deref.Deref(arg))
rv := deref.ValueOf(arg)
switch rv.Kind() {
case reflect.Array, reflect.Slice:
size := rv.Len()
@@ -339,7 +330,7 @@ func median(args ...any) ([]float64, error) {
var values []float64
for _, arg := range args {
rv := reflect.ValueOf(deref.Deref(arg))
rv := deref.ValueOf(arg)
switch rv.Kind() {
case reflect.Array, reflect.Slice:
size := rv.Len()
+3
View File
@@ -4,6 +4,8 @@ import (
"fmt"
"reflect"
"time"
"github.com/expr-lang/expr/internal/deref"
)
var (
@@ -20,6 +22,7 @@ func kind(t reflect.Type) reflect.Kind {
if t == nil {
return reflect.Invalid
}
t = deref.Type(t)
return t.Kind()
}
+4
View File
@@ -45,3 +45,7 @@ func Value(v reflect.Value) reflect.Value {
}
return v
}
func ValueOf(v any) reflect.Value {
return Value(reflect.ValueOf(v))
}