Files
kubevpn/vendor/github.com/expr-lang/expr/vm/runtime/runtime.go
2025-04-19 10:06:56 +08:00

395 lines
7.4 KiB
Go

package runtime
//go:generate sh -c "go run ./helpers > ./helpers[generated].go"
import (
"fmt"
"math"
"reflect"
"github.com/expr-lang/expr/internal/deref"
)
func Fetch(from, i any) any {
v := reflect.ValueOf(from)
if v.Kind() == reflect.Invalid {
panic(fmt.Sprintf("cannot fetch %v from %T", i, from))
}
// Methods can be defined on any type.
if v.NumMethod() > 0 {
if methodName, ok := i.(string); ok {
method := v.MethodByName(methodName)
if method.IsValid() {
return method.Interface()
}
}
}
// Structs, maps, and slices can be access through a pointer or through
// a value, when they are accessed through a pointer we don't want to
// copy them to a value.
// De-reference everything if necessary (interface and pointers)
v = deref.Value(v)
switch v.Kind() {
case reflect.Array, reflect.Slice, reflect.String:
index := ToInt(i)
l := v.Len()
if index < 0 {
index = l + index
}
if index < 0 || index >= l {
panic(fmt.Sprintf("index out of range: %v (array length is %v)", index, l))
}
value := v.Index(index)
if value.IsValid() {
return value.Interface()
}
case reflect.Map:
var value reflect.Value
if i == nil {
value = v.MapIndex(reflect.Zero(v.Type().Key()))
} else {
value = v.MapIndex(reflect.ValueOf(i))
}
if value.IsValid() {
return value.Interface()
} else {
elem := reflect.TypeOf(from).Elem()
return reflect.Zero(elem).Interface()
}
case reflect.Struct:
fieldName := i.(string)
value := v.FieldByNameFunc(func(name string) bool {
field, _ := v.Type().FieldByName(name)
if field.Tag.Get("expr") == fieldName {
return true
}
return name == fieldName
})
if value.IsValid() {
return value.Interface()
}
}
panic(fmt.Sprintf("cannot fetch %v from %T", i, from))
}
type Field struct {
Index []int
Path []string
}
func FetchField(from any, field *Field) any {
v := reflect.ValueOf(from)
if v.Kind() != reflect.Invalid {
v = reflect.Indirect(v)
// We can use v.FieldByIndex here, but it will panic if the field
// is not exists. And we need to recover() to generate a more
// user-friendly error message.
// Also, our fieldByIndex() function is slightly faster than the
// v.FieldByIndex() function as we don't need to verify what a field
// is a struct as we already did it on compilation step.
value := fieldByIndex(v, field)
if value.IsValid() {
return value.Interface()
}
}
panic(fmt.Sprintf("cannot get %v from %T", field.Path[0], from))
}
func fieldByIndex(v reflect.Value, field *Field) reflect.Value {
if len(field.Index) == 1 {
return v.Field(field.Index[0])
}
for i, x := range field.Index {
if i > 0 {
if v.Kind() == reflect.Ptr {
if v.IsNil() {
panic(fmt.Sprintf("cannot get %v from %v", field.Path[i], field.Path[i-1]))
}
v = v.Elem()
}
}
v = v.Field(x)
}
return v
}
type Method struct {
Index int
Name string
}
func FetchMethod(from any, method *Method) any {
v := reflect.ValueOf(from)
kind := v.Kind()
if kind != reflect.Invalid {
// Methods can be defined on any type, no need to dereference.
method := v.Method(method.Index)
if method.IsValid() {
return method.Interface()
}
}
panic(fmt.Sprintf("cannot fetch %v from %T", method.Name, from))
}
func Slice(array, from, to any) any {
v := reflect.ValueOf(array)
switch v.Kind() {
case reflect.Array, reflect.Slice, reflect.String:
length := v.Len()
a, b := ToInt(from), ToInt(to)
if a < 0 {
a = length + a
}
if a < 0 {
a = 0
}
if b < 0 {
b = length + b
}
if b < 0 {
b = 0
}
if b > length {
b = length
}
if a > b {
a = b
}
value := v.Slice(a, b)
if value.IsValid() {
return value.Interface()
}
case reflect.Ptr:
value := v.Elem()
if value.IsValid() {
return Slice(value.Interface(), from, to)
}
}
panic(fmt.Sprintf("cannot slice %v", from))
}
func In(needle any, array any) bool {
if array == nil {
return false
}
v := reflect.ValueOf(array)
switch v.Kind() {
case reflect.Array, reflect.Slice:
for i := 0; i < v.Len(); i++ {
value := v.Index(i)
if value.IsValid() {
if Equal(value.Interface(), needle) {
return true
}
}
}
return false
case reflect.Map:
var value reflect.Value
if needle == nil {
value = v.MapIndex(reflect.Zero(v.Type().Key()))
} else {
value = v.MapIndex(reflect.ValueOf(needle))
}
if value.IsValid() {
return true
}
return false
case reflect.Struct:
n := reflect.ValueOf(needle)
if !n.IsValid() || n.Kind() != reflect.String {
panic(fmt.Sprintf("cannot use %T as field name of %T", needle, array))
}
value := v.FieldByName(n.String())
if value.IsValid() {
return true
}
return false
case reflect.Ptr:
value := v.Elem()
if value.IsValid() {
return In(needle, value.Interface())
}
return false
}
panic(fmt.Sprintf(`operator "in" not defined on %T`, array))
}
func Len(a any) int {
v := reflect.ValueOf(a)
switch v.Kind() {
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
return v.Len()
default:
panic(fmt.Sprintf("invalid argument for len (type %T)", a))
}
}
func Negate(i any) any {
switch v := i.(type) {
case float32:
return -v
case float64:
return -v
case int:
return -v
case int8:
return -v
case int16:
return -v
case int32:
return -v
case int64:
return -v
case uint:
return -v
case uint8:
return -v
case uint16:
return -v
case uint32:
return -v
case uint64:
return -v
default:
panic(fmt.Sprintf("invalid operation: - %T", v))
}
}
func Exponent(a, b any) float64 {
return math.Pow(ToFloat64(a), ToFloat64(b))
}
func MakeRange(min, max int) []int {
size := max - min + 1
if size <= 0 {
return []int{}
}
rng := make([]int, size)
for i := range rng {
rng[i] = min + i
}
return rng
}
func ToInt(a any) int {
switch x := a.(type) {
case float32:
return int(x)
case float64:
return int(x)
case int:
return x
case int8:
return int(x)
case int16:
return int(x)
case int32:
return int(x)
case int64:
return int(x)
case uint:
return int(x)
case uint8:
return int(x)
case uint16:
return int(x)
case uint32:
return int(x)
case uint64:
return int(x)
default:
panic(fmt.Sprintf("invalid operation: int(%T)", x))
}
}
func ToInt64(a any) int64 {
switch x := a.(type) {
case float32:
return int64(x)
case float64:
return int64(x)
case int:
return int64(x)
case int8:
return int64(x)
case int16:
return int64(x)
case int32:
return int64(x)
case int64:
return x
case uint:
return int64(x)
case uint8:
return int64(x)
case uint16:
return int64(x)
case uint32:
return int64(x)
case uint64:
return int64(x)
default:
panic(fmt.Sprintf("invalid operation: int64(%T)", x))
}
}
func ToFloat64(a any) float64 {
switch x := a.(type) {
case float32:
return float64(x)
case float64:
return x
case int:
return float64(x)
case int8:
return float64(x)
case int16:
return float64(x)
case int32:
return float64(x)
case int64:
return float64(x)
case uint:
return float64(x)
case uint8:
return float64(x)
case uint16:
return float64(x)
case uint32:
return float64(x)
case uint64:
return float64(x)
default:
panic(fmt.Sprintf("invalid operation: float(%T)", x))
}
}
func IsNil(v any) bool {
if v == nil {
return true
}
r := reflect.ValueOf(v)
switch r.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
return r.IsNil()
default:
return false
}
}