mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
451 lines
12 KiB
Go
451 lines
12 KiB
Go
package builtin
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/antonmedv/expr/vm/runtime"
|
|
)
|
|
|
|
type Function struct {
|
|
Name string
|
|
Func func(args ...interface{}) (interface{}, error)
|
|
Builtin1 func(arg interface{}) interface{}
|
|
Types []reflect.Type
|
|
Validate func(args []reflect.Type) (reflect.Type, error)
|
|
}
|
|
|
|
var (
|
|
Index map[string]int
|
|
Names []string
|
|
)
|
|
|
|
func init() {
|
|
Index = make(map[string]int)
|
|
Names = make([]string, len(Functions))
|
|
for i, fn := range Functions {
|
|
Index[fn.Name] = i
|
|
Names[i] = fn.Name
|
|
}
|
|
}
|
|
|
|
var Functions = []*Function{
|
|
{
|
|
Name: "len",
|
|
Builtin1: Len,
|
|
Validate: func(args []reflect.Type) (reflect.Type, error) {
|
|
if len(args) != 1 {
|
|
return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
|
|
}
|
|
switch kind(args[0]) {
|
|
case reflect.Array, reflect.Map, reflect.Slice, reflect.String, reflect.Interface:
|
|
return integerType, nil
|
|
}
|
|
return anyType, fmt.Errorf("invalid argument for len (type %s)", args[0])
|
|
},
|
|
},
|
|
{
|
|
Name: "abs",
|
|
Builtin1: Abs,
|
|
Validate: func(args []reflect.Type) (reflect.Type, error) {
|
|
if len(args) != 1 {
|
|
return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
|
|
}
|
|
switch kind(args[0]) {
|
|
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, reflect.Interface:
|
|
return args[0], nil
|
|
}
|
|
return anyType, fmt.Errorf("invalid argument for abs (type %s)", args[0])
|
|
},
|
|
},
|
|
{
|
|
Name: "int",
|
|
Builtin1: Int,
|
|
Validate: func(args []reflect.Type) (reflect.Type, error) {
|
|
if len(args) != 1 {
|
|
return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
|
|
}
|
|
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:
|
|
return integerType, nil
|
|
case reflect.String:
|
|
return integerType, nil
|
|
}
|
|
return anyType, fmt.Errorf("invalid argument for int (type %s)", args[0])
|
|
},
|
|
},
|
|
{
|
|
Name: "float",
|
|
Builtin1: Float,
|
|
Validate: func(args []reflect.Type) (reflect.Type, error) {
|
|
if len(args) != 1 {
|
|
return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
|
|
}
|
|
switch kind(args[0]) {
|
|
case reflect.Interface:
|
|
return floatType, 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:
|
|
return floatType, nil
|
|
case reflect.String:
|
|
return floatType, nil
|
|
}
|
|
return anyType, fmt.Errorf("invalid argument for float (type %s)", args[0])
|
|
},
|
|
},
|
|
{
|
|
Name: "string",
|
|
Builtin1: String,
|
|
Types: types(new(func(any interface{}) string)),
|
|
},
|
|
{
|
|
Name: "trim",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
if len(args) == 1 {
|
|
return strings.TrimSpace(args[0].(string)), nil
|
|
} else if len(args) == 2 {
|
|
return strings.Trim(args[0].(string), args[1].(string)), nil
|
|
} else {
|
|
return nil, fmt.Errorf("invalid number of arguments for trim (expected 1 or 2, got %d)", len(args))
|
|
}
|
|
},
|
|
Types: types(
|
|
strings.TrimSpace,
|
|
strings.Trim,
|
|
),
|
|
},
|
|
{
|
|
Name: "trimPrefix",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
return strings.TrimPrefix(args[0].(string), args[1].(string)), nil
|
|
},
|
|
Types: types(strings.TrimPrefix),
|
|
},
|
|
{
|
|
Name: "trimSuffix",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
return strings.TrimSuffix(args[0].(string), args[1].(string)), nil
|
|
},
|
|
Types: types(strings.TrimSuffix),
|
|
},
|
|
{
|
|
Name: "upper",
|
|
Builtin1: func(arg interface{}) interface{} {
|
|
return strings.ToUpper(arg.(string))
|
|
},
|
|
Types: types(strings.ToUpper),
|
|
},
|
|
{
|
|
Name: "lower",
|
|
Builtin1: func(arg interface{}) interface{} {
|
|
return strings.ToLower(arg.(string))
|
|
},
|
|
Types: types(strings.ToLower),
|
|
},
|
|
{
|
|
Name: "split",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
return strings.Split(args[0].(string), args[1].(string)), nil
|
|
},
|
|
Types: types(strings.Split),
|
|
},
|
|
{
|
|
Name: "splitN",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
return strings.SplitN(args[0].(string), args[1].(string), runtime.ToInt(args[2])), nil
|
|
},
|
|
Types: types(strings.SplitN),
|
|
},
|
|
{
|
|
Name: "splitAfter",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
return strings.SplitAfter(args[0].(string), args[1].(string)), nil
|
|
},
|
|
Types: types(strings.SplitAfter),
|
|
},
|
|
{
|
|
Name: "splitAfterN",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
return strings.SplitAfterN(args[0].(string), args[1].(string), runtime.ToInt(args[2])), nil
|
|
},
|
|
Types: types(strings.SplitAfterN),
|
|
},
|
|
{
|
|
Name: "replace",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
if len(args) == 4 {
|
|
return strings.Replace(args[0].(string), args[1].(string), args[2].(string), runtime.ToInt(args[3])), nil
|
|
} else if len(args) == 3 {
|
|
return strings.ReplaceAll(args[0].(string), args[1].(string), args[2].(string)), nil
|
|
} else {
|
|
return nil, fmt.Errorf("invalid number of arguments for replace (expected 3 or 4, got %d)", len(args))
|
|
}
|
|
},
|
|
Types: types(
|
|
strings.Replace,
|
|
strings.ReplaceAll,
|
|
),
|
|
},
|
|
{
|
|
Name: "repeat",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
n := runtime.ToInt(args[1])
|
|
if n > 1e6 {
|
|
panic("memory budget exceeded")
|
|
}
|
|
return strings.Repeat(args[0].(string), n), nil
|
|
},
|
|
Types: types(strings.Repeat),
|
|
},
|
|
{
|
|
Name: "join",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
glue := ""
|
|
if len(args) == 2 {
|
|
glue = args[1].(string)
|
|
}
|
|
switch args[0].(type) {
|
|
case []string:
|
|
return strings.Join(args[0].([]string), glue), nil
|
|
case []interface{}:
|
|
var s []string
|
|
for _, arg := range args[0].([]interface{}) {
|
|
s = append(s, arg.(string))
|
|
}
|
|
return strings.Join(s, glue), nil
|
|
}
|
|
return nil, fmt.Errorf("invalid argument for join (type %s)", reflect.TypeOf(args[0]))
|
|
},
|
|
Types: types(
|
|
strings.Join,
|
|
new(func([]interface{}, string) string),
|
|
new(func([]string) string),
|
|
new(func([]interface{}) string),
|
|
),
|
|
},
|
|
{
|
|
Name: "indexOf",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
return strings.Index(args[0].(string), args[1].(string)), nil
|
|
},
|
|
Types: types(strings.Index),
|
|
},
|
|
{
|
|
Name: "lastIndexOf",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
return strings.LastIndex(args[0].(string), args[1].(string)), nil
|
|
},
|
|
Types: types(strings.LastIndex),
|
|
},
|
|
{
|
|
Name: "hasPrefix",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
return strings.HasPrefix(args[0].(string), args[1].(string)), nil
|
|
},
|
|
Types: types(strings.HasPrefix),
|
|
},
|
|
{
|
|
Name: "hasSuffix",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
return strings.HasSuffix(args[0].(string), args[1].(string)), nil
|
|
},
|
|
Types: types(strings.HasSuffix),
|
|
},
|
|
{
|
|
Name: "max",
|
|
Func: Max,
|
|
Validate: func(args []reflect.Type) (reflect.Type, error) {
|
|
if len(args) == 0 {
|
|
return anyType, fmt.Errorf("not enough arguments to call max")
|
|
}
|
|
for _, arg := range args {
|
|
switch kind(arg) {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
|
|
default:
|
|
return anyType, fmt.Errorf("invalid argument for max (type %s)", arg)
|
|
}
|
|
}
|
|
return args[0], nil
|
|
},
|
|
},
|
|
{
|
|
Name: "min",
|
|
Func: Min,
|
|
Validate: func(args []reflect.Type) (reflect.Type, error) {
|
|
if len(args) == 0 {
|
|
return anyType, fmt.Errorf("not enough arguments to call min")
|
|
}
|
|
for _, arg := range args {
|
|
switch kind(arg) {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
|
|
default:
|
|
return anyType, fmt.Errorf("invalid argument for min (type %s)", arg)
|
|
}
|
|
}
|
|
return args[0], nil
|
|
},
|
|
},
|
|
{
|
|
Name: "toJSON",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
b, err := json.MarshalIndent(args[0], "", " ")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return string(b), nil
|
|
},
|
|
Types: types(new(func(interface{}) string)),
|
|
},
|
|
{
|
|
Name: "fromJSON",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
var v interface{}
|
|
err := json.Unmarshal([]byte(args[0].(string)), &v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return v, nil
|
|
},
|
|
Types: types(new(func(string) interface{})),
|
|
},
|
|
{
|
|
Name: "toBase64",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
return base64.StdEncoding.EncodeToString([]byte(args[0].(string))), nil
|
|
},
|
|
Types: types(new(func(string) string)),
|
|
},
|
|
{
|
|
Name: "fromBase64",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
b, err := base64.StdEncoding.DecodeString(args[0].(string))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return string(b), nil
|
|
},
|
|
Types: types(new(func(string) string)),
|
|
},
|
|
{
|
|
Name: "now",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
return time.Now(), nil
|
|
},
|
|
Types: types(new(func() time.Time)),
|
|
},
|
|
{
|
|
Name: "duration",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
return time.ParseDuration(args[0].(string))
|
|
},
|
|
Types: types(time.ParseDuration),
|
|
},
|
|
{
|
|
Name: "date",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
date := args[0].(string)
|
|
if len(args) == 2 {
|
|
layout := args[1].(string)
|
|
return time.Parse(layout, date)
|
|
}
|
|
if len(args) == 3 {
|
|
layout := args[1].(string)
|
|
timeZone := args[2].(string)
|
|
tz, err := time.LoadLocation(timeZone)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
t, err := time.ParseInLocation(layout, date, tz)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return t, nil
|
|
}
|
|
|
|
layouts := []string{
|
|
"2006-01-02",
|
|
"15:04:05",
|
|
"2006-01-02 15:04:05",
|
|
time.RFC3339,
|
|
time.RFC822,
|
|
time.RFC850,
|
|
time.RFC1123,
|
|
}
|
|
for _, layout := range layouts {
|
|
t, err := time.Parse(layout, date)
|
|
if err == nil {
|
|
return t, nil
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("invalid date %s", date)
|
|
},
|
|
Types: types(
|
|
new(func(string) time.Time),
|
|
new(func(string, string) time.Time),
|
|
new(func(string, string, string) time.Time),
|
|
),
|
|
},
|
|
{
|
|
Name: "first",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
return
|
|
}
|
|
}()
|
|
return runtime.Fetch(args[0], 0), nil
|
|
},
|
|
Validate: func(args []reflect.Type) (reflect.Type, error) {
|
|
if len(args) != 1 {
|
|
return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
|
|
}
|
|
switch kind(args[0]) {
|
|
case reflect.Interface:
|
|
return anyType, nil
|
|
case reflect.Slice, reflect.Array:
|
|
return args[0].Elem(), nil
|
|
}
|
|
return anyType, fmt.Errorf("cannot get first element from %s", args[0])
|
|
},
|
|
},
|
|
{
|
|
Name: "last",
|
|
Func: func(args ...interface{}) (interface{}, error) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
return
|
|
}
|
|
}()
|
|
return runtime.Fetch(args[0], -1), nil
|
|
},
|
|
Validate: func(args []reflect.Type) (reflect.Type, error) {
|
|
if len(args) != 1 {
|
|
return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
|
|
}
|
|
switch kind(args[0]) {
|
|
case reflect.Interface:
|
|
return anyType, nil
|
|
case reflect.Slice, reflect.Array:
|
|
return args[0].Elem(), nil
|
|
}
|
|
return anyType, fmt.Errorf("cannot get last element from %s", args[0])
|
|
},
|
|
},
|
|
{
|
|
Name: "get",
|
|
Func: func(args ...interface{}) (out interface{}, err error) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
return
|
|
}
|
|
}()
|
|
return runtime.Fetch(args[0], args[1]), nil
|
|
},
|
|
},
|
|
}
|