mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
Add eval
This commit is contained in:
@@ -0,0 +1,344 @@
|
||||
package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type evaluator interface {
|
||||
eval(env interface{}) (interface{}, error)
|
||||
}
|
||||
|
||||
// Eval parses and evaluates given input.
|
||||
func Eval(input string, env interface{}) (node interface{}, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("%v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
node, err = Parse(input)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Run(node, env)
|
||||
}
|
||||
|
||||
// Run evaluates given ast.
|
||||
func Run(node Node, env interface{}) (interface{}, error) {
|
||||
if e, ok := node.(evaluator); ok {
|
||||
return e.eval(env)
|
||||
}
|
||||
return nil, fmt.Errorf("implement evaluator for %T", node)
|
||||
}
|
||||
|
||||
// eval functions
|
||||
|
||||
func (n nilNode) eval(env interface{}) (interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (n identifierNode) eval(env interface{}) (interface{}, error) {
|
||||
return n.value, nil
|
||||
}
|
||||
|
||||
func (n numberNode) eval(env interface{}) (interface{}, error) {
|
||||
return n.value, nil
|
||||
}
|
||||
|
||||
func (n boolNode) eval(env interface{}) (interface{}, error) {
|
||||
return n.value, nil
|
||||
}
|
||||
|
||||
func (n textNode) eval(env interface{}) (interface{}, error) {
|
||||
return n.value, nil
|
||||
}
|
||||
|
||||
func (n nameNode) eval(env interface{}) (interface{}, error) {
|
||||
return extract(env, n.name)
|
||||
}
|
||||
|
||||
func (n unaryNode) eval(env interface{}) (interface{}, error) {
|
||||
val, err := Run(n.node, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch n.operator {
|
||||
case "not", "!":
|
||||
if !isBool(val) {
|
||||
return nil, fmt.Errorf("negation of non-bool type %T", val)
|
||||
}
|
||||
return !toBool(val), nil
|
||||
}
|
||||
|
||||
v, err := cast(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch n.operator {
|
||||
case "-":
|
||||
return -v, nil
|
||||
case "+":
|
||||
return +v, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("implement unary %q operator", n.operator)
|
||||
}
|
||||
|
||||
func (n binaryNode) eval(env interface{}) (interface{}, error) {
|
||||
left, err := Run(n.left, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch n.operator {
|
||||
case "or", "||":
|
||||
if !isBool(left) {
|
||||
return nil, fmt.Errorf("non-bool value in cond (%T)", left)
|
||||
}
|
||||
|
||||
if toBool(left) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
right, err := Run(n.right, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isBool(right) {
|
||||
return nil, fmt.Errorf("non-bool value in cond (%T)", right)
|
||||
}
|
||||
|
||||
return toBool(right), nil
|
||||
|
||||
case "and", "&&":
|
||||
if !isBool(left) {
|
||||
return nil, fmt.Errorf("non-bool value in cond (%T)", left)
|
||||
}
|
||||
|
||||
if toBool(left) {
|
||||
right, err := Run(n.right, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isBool(right) {
|
||||
return nil, fmt.Errorf("non-bool value in cond (%T)", right)
|
||||
}
|
||||
return toBool(right), nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
right, err := Run(n.right, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch n.operator {
|
||||
case "==":
|
||||
return equal(left, right), nil
|
||||
|
||||
case "!=":
|
||||
return !equal(left, right), nil
|
||||
|
||||
case "in":
|
||||
ok, err := contains(left, right)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ok, nil
|
||||
|
||||
case "not in":
|
||||
ok, err := contains(left, right)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return !ok, nil
|
||||
|
||||
case "matches":
|
||||
if isText(left) && isText(right) {
|
||||
matched, err := regexp.MatchString(toText(right), toText(left))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return matched, nil
|
||||
}
|
||||
return nil, fmt.Errorf("operator matches not defined on (%T, %T)", left, right)
|
||||
|
||||
case "~":
|
||||
if isText(left) && isText(right) {
|
||||
return toText(left) + toText(right), nil
|
||||
}
|
||||
return nil, fmt.Errorf("operator ~ not defined on (%T, %T)", left, right)
|
||||
}
|
||||
|
||||
// Next goes operators on numbers
|
||||
|
||||
l, err := cast(left)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, err := cast(right)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch n.operator {
|
||||
case "|":
|
||||
return int(l) | int(r), nil
|
||||
|
||||
case "^":
|
||||
return int(l) ^ int(r), nil
|
||||
|
||||
case "&":
|
||||
return int(l) & int(r), nil
|
||||
|
||||
case "<":
|
||||
return l < r, nil
|
||||
|
||||
case ">":
|
||||
return l > r, nil
|
||||
|
||||
case ">=":
|
||||
return l >= r, nil
|
||||
|
||||
case "<=":
|
||||
return l <= r, nil
|
||||
|
||||
case "+":
|
||||
return l + r, nil
|
||||
|
||||
case "-":
|
||||
return l - r, nil
|
||||
|
||||
case "*":
|
||||
return l * r, nil
|
||||
|
||||
case "/":
|
||||
div := r
|
||||
if div == 0 {
|
||||
return nil, fmt.Errorf("division by zero")
|
||||
}
|
||||
return l / div, nil
|
||||
|
||||
case "%":
|
||||
numerator := int64(l)
|
||||
denominator := int64(r)
|
||||
if denominator == 0 {
|
||||
return nil, fmt.Errorf("division by zero")
|
||||
}
|
||||
return float64(numerator % denominator), nil
|
||||
|
||||
case "**":
|
||||
return math.Pow(l, r), nil
|
||||
|
||||
case "..":
|
||||
return makeRange(int64(l), int64(r)), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("implement %q operator", n.operator)
|
||||
}
|
||||
|
||||
func makeRange(min, max int64) []float64 {
|
||||
a := make([]float64, max-min+1)
|
||||
for i := range a {
|
||||
a[i] = float64(min + int64(i))
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func (n propertyNode) eval(env interface{}) (interface{}, error) {
|
||||
v, err := Run(n.node, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p, err := Run(n.property, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return extract(v, p)
|
||||
}
|
||||
|
||||
func (n methodNode) eval(env interface{}) (interface{}, error) {
|
||||
v, err := Run(n.node, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
method, err := extract(v, n.property.value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return call(n.property.value, method, n.arguments, env)
|
||||
}
|
||||
|
||||
func (n functionNode) eval(env interface{}) (interface{}, error) {
|
||||
fn, err := extract(env, n.name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return call(n.name, fn, n.arguments, env)
|
||||
}
|
||||
|
||||
func (n conditionalNode) eval(env interface{}) (interface{}, error) {
|
||||
cond, err := Run(n.cond, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If
|
||||
if !isBool(cond) {
|
||||
return nil, fmt.Errorf("non-bool value used in cond (%T)", cond)
|
||||
}
|
||||
// Then
|
||||
if toBool(cond) {
|
||||
a, err := Run(n.exp1, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
// Else
|
||||
b, err := Run(n.exp2, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
|
||||
}
|
||||
|
||||
func (n arrayNode) eval(env interface{}) (interface{}, error) {
|
||||
array := make([]interface{}, 0)
|
||||
for _, node := range n.nodes {
|
||||
val, err := Run(node, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
array = append(array, val)
|
||||
}
|
||||
return array, nil
|
||||
}
|
||||
|
||||
func (n mapNode) eval(env interface{}) (interface{}, error) {
|
||||
m := make(map[interface{}]interface{})
|
||||
for _, pair := range n.pairs {
|
||||
key, err := Run(pair.key, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value, err := Run(pair.value, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m[key] = value
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
+343
@@ -0,0 +1,343 @@
|
||||
package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type evalTest struct {
|
||||
input string
|
||||
env interface{}
|
||||
expected interface{}
|
||||
}
|
||||
|
||||
var evalTests = []evalTest{
|
||||
{
|
||||
"foo",
|
||||
map[string]int{"foo": 33},
|
||||
33,
|
||||
},
|
||||
{
|
||||
"foo == bar",
|
||||
map[string]interface{}{"foo": 1, "bar": 1},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"foo || (bar && !false && true)",
|
||||
map[string]interface{}{"foo": false, "bar": true},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"foo && bar",
|
||||
map[string]interface{}{"foo": false, "bar": true},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"!foo && bar",
|
||||
map[string]interface{}{"foo": false, "bar": true},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"2+2==4",
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"2+3",
|
||||
nil,
|
||||
float64(5),
|
||||
},
|
||||
{
|
||||
"5-2",
|
||||
nil,
|
||||
float64(3),
|
||||
},
|
||||
{
|
||||
"2*3",
|
||||
nil,
|
||||
float64(6),
|
||||
},
|
||||
{
|
||||
"6/2",
|
||||
nil,
|
||||
float64(3),
|
||||
},
|
||||
{
|
||||
"8%3",
|
||||
nil,
|
||||
float64(2),
|
||||
},
|
||||
{
|
||||
"2**4",
|
||||
nil,
|
||||
float64(16),
|
||||
},
|
||||
{
|
||||
"2**4",
|
||||
nil,
|
||||
float64(16),
|
||||
},
|
||||
{
|
||||
"-(2-5)**3-2/(+4-3)+-2",
|
||||
nil,
|
||||
float64(23),
|
||||
},
|
||||
{
|
||||
`"hello" ~ ' ' ~ "world"`,
|
||||
nil,
|
||||
"hello world",
|
||||
},
|
||||
{
|
||||
"+0 == -0",
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"1 < 2 and 3 > 2",
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"!(1 != 1) && 2 >= 2 && 3 <= 3",
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"[1, 02, 1e3, 1.2e-4]",
|
||||
nil,
|
||||
[]interface{}{float64(1), float64(2), float64(1000), float64(0.00012)},
|
||||
},
|
||||
{
|
||||
"1..5",
|
||||
nil,
|
||||
[]float64{1, 2, 3, 4, 5},
|
||||
},
|
||||
{
|
||||
"{foo: 1}",
|
||||
nil,
|
||||
map[interface{}]interface{}{"foo": float64(1)},
|
||||
},
|
||||
{
|
||||
`{foo: "bar"}.foo`,
|
||||
nil,
|
||||
"bar",
|
||||
},
|
||||
{
|
||||
`Foo.Bar`,
|
||||
struct{ Foo struct{ Bar bool } }{Foo: struct{ Bar bool }{Bar: true}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"foo[2]",
|
||||
map[string]interface{}{"foo": []rune{'a', 'b', 'c'}},
|
||||
'c',
|
||||
},
|
||||
{
|
||||
"[1, 2, 3][2/2]",
|
||||
nil,
|
||||
float64(2),
|
||||
},
|
||||
{
|
||||
`[true][A]`,
|
||||
struct{ A int }{0},
|
||||
true,
|
||||
},
|
||||
{
|
||||
`A-1`,
|
||||
struct{ A int }{1},
|
||||
float64(0),
|
||||
},
|
||||
{
|
||||
`A == 0`,
|
||||
struct{ A uint8 }{0},
|
||||
true,
|
||||
},
|
||||
{
|
||||
`A == B`,
|
||||
struct {
|
||||
A uint8
|
||||
B float64
|
||||
}{1, 1},
|
||||
true,
|
||||
},
|
||||
{
|
||||
`5 in 0..9`,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
`"1" in ["1", "2"]`,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
`"0" not in ["1", "2"]`,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
`A == nil`,
|
||||
struct{ A interface{} }{nil},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"foo['bar'].baz",
|
||||
map[string]interface{}{"foo": map[string]interface{}{"bar": map[string]interface{}{"baz": true}}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
`60 & 13`,
|
||||
nil,
|
||||
12,
|
||||
},
|
||||
{
|
||||
`60 ^ 13`,
|
||||
nil,
|
||||
49,
|
||||
},
|
||||
{
|
||||
`60 | 13`,
|
||||
nil,
|
||||
61,
|
||||
},
|
||||
{
|
||||
`"seafood" matches "foo.*"`,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
`not ("seafood" matches "[0-9]+") ? "a" : "b"`,
|
||||
nil,
|
||||
"a",
|
||||
},
|
||||
{
|
||||
`foo.bar("world")`,
|
||||
map[string]interface{}{"foo": map[string]interface{}{"bar": func(in string) string { return "hello " + in }}},
|
||||
"hello world",
|
||||
},
|
||||
{
|
||||
`foo.bar()`,
|
||||
map[string]interface{}{"foo": map[string]interface{}{"bar": func() {}}},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`foo("world")`,
|
||||
map[string]interface{}{"foo": func(in string) string { return "hello " + in }},
|
||||
"hello world",
|
||||
},
|
||||
}
|
||||
|
||||
type evalErrorTest struct {
|
||||
input string
|
||||
env interface{}
|
||||
err string
|
||||
}
|
||||
|
||||
var evalErrorTests = []evalErrorTest{
|
||||
{
|
||||
"bar",
|
||||
map[string]int{"foo": 1},
|
||||
`can't get "bar"`,
|
||||
},
|
||||
{
|
||||
`"foo" ~ nil`,
|
||||
nil,
|
||||
`operator ~ not defined on (string, <nil>)`,
|
||||
},
|
||||
{
|
||||
"foo['bar'].baz",
|
||||
map[string]interface{}{"foo": nil},
|
||||
`can't get "bar" from <nil>`,
|
||||
},
|
||||
{
|
||||
`"seafood" matches "a(b"`,
|
||||
nil,
|
||||
`error parsing regexp:`,
|
||||
},
|
||||
{
|
||||
`0 ? 1 : 2`,
|
||||
nil,
|
||||
`non-bool value used in cond`,
|
||||
},
|
||||
{
|
||||
`foo()`,
|
||||
map[string]interface{}{"foo": func() (int, int) { return 0, 1 }},
|
||||
`func "foo" must return only one value`,
|
||||
},
|
||||
{
|
||||
`foo()`,
|
||||
map[string]interface{}{"foo": nil},
|
||||
`reflect: call of reflect.Value.Call on zero Value`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestEval(t *testing.T) {
|
||||
for _, test := range evalTests {
|
||||
actual, err := Eval(test.input, test.env)
|
||||
if err != nil {
|
||||
t.Errorf("%s:\n%v", test.input, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(actual, test.expected) {
|
||||
t.Errorf("%s:\ngot\n\t%#v\nexpected\n\t%#v", test.input, actual, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvalError(t *testing.T) {
|
||||
for _, test := range evalErrorTests {
|
||||
_, err := Eval(test.input, test.env)
|
||||
if err == nil {
|
||||
err = fmt.Errorf("<nil>")
|
||||
}
|
||||
if !strings.HasPrefix(err.Error(), test.err) {
|
||||
t.Errorf("%s:\ngot\n\t%+v\nexpected\n\t%v", test.input, err.Error(), test.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvalComplex(t *testing.T) {
|
||||
type cookie struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
type user struct {
|
||||
UserAgent string
|
||||
Cookies []cookie
|
||||
}
|
||||
type request struct {
|
||||
User user
|
||||
}
|
||||
|
||||
p := map[string]interface{}{
|
||||
"Request": request{user{
|
||||
Cookies: []cookie{{"origin", "www"}},
|
||||
UserAgent: "Mozilla 1",
|
||||
}},
|
||||
"Values": func(xs []cookie) []string {
|
||||
vs := make([]string, 0)
|
||||
for _, x := range xs {
|
||||
vs = append(vs, x.Value)
|
||||
}
|
||||
return vs
|
||||
},
|
||||
}
|
||||
|
||||
input := `Request.User.UserAgent matches "Mozilla" && "www" in Values(Request.User.Cookies)`
|
||||
node, err := Parse(input, Names("Request"), Funcs("Values"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
actual, err := Run(node, p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := true
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("TestEvalComplex:\ngot\n\t%#v\nexpected:\n\t%#v", actual, expected)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func isBool(val interface{}) bool {
|
||||
return val != nil && reflect.TypeOf(val).Kind() == reflect.Bool
|
||||
}
|
||||
|
||||
func toBool(val interface{}) bool {
|
||||
return reflect.ValueOf(val).Bool()
|
||||
}
|
||||
|
||||
func isText(val interface{}) bool {
|
||||
return val != nil && reflect.TypeOf(val).Kind() == reflect.String
|
||||
}
|
||||
|
||||
func toText(val interface{}) string {
|
||||
return reflect.ValueOf(val).String()
|
||||
}
|
||||
|
||||
func equal(left, right interface{}) bool {
|
||||
if isNumber(left) && canBeNumber(right) {
|
||||
right, _ := cast(right)
|
||||
return left == right
|
||||
} else if canBeNumber(left) && isNumber(right) {
|
||||
left, _ := cast(left)
|
||||
return left == right
|
||||
} else {
|
||||
return reflect.DeepEqual(left, right)
|
||||
}
|
||||
}
|
||||
|
||||
func isNumber(val interface{}) bool {
|
||||
return val != nil && reflect.TypeOf(val).Kind() == reflect.Float64
|
||||
}
|
||||
|
||||
func cast(v interface{}) (float64, error) {
|
||||
if v != nil {
|
||||
switch reflect.TypeOf(v).Kind() {
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.(float64), nil
|
||||
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return float64(reflect.ValueOf(v).Int()), nil
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return float64(reflect.ValueOf(v).Uint()), nil // TODO: Check if uint64 fits into float64.
|
||||
}
|
||||
}
|
||||
return 0, fmt.Errorf("can't cast %T to float64", v)
|
||||
}
|
||||
|
||||
func canBeNumber(v interface{}) bool {
|
||||
if v != nil {
|
||||
switch reflect.TypeOf(v).Kind() {
|
||||
case reflect.Float32, reflect.Float64:
|
||||
fallthrough
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
fallthrough
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func extract(from interface{}, it interface{}) (interface{}, error) {
|
||||
if from != nil {
|
||||
switch reflect.TypeOf(from).Kind() {
|
||||
case reflect.Array, reflect.Slice, reflect.String:
|
||||
i, err := cast(it)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value := reflect.ValueOf(from).Index(int(i))
|
||||
if value.IsValid() && value.CanInterface() {
|
||||
return value.Interface(), nil
|
||||
}
|
||||
case reflect.Map:
|
||||
value := reflect.ValueOf(from).MapIndex(reflect.ValueOf(it))
|
||||
if value.IsValid() && value.CanInterface() {
|
||||
return value.Interface(), nil
|
||||
}
|
||||
case reflect.Struct:
|
||||
value := reflect.ValueOf(from).FieldByName(reflect.ValueOf(it).String())
|
||||
if value.IsValid() && value.CanInterface() {
|
||||
return value.Interface(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("can't get %q from %T", it, from)
|
||||
}
|
||||
|
||||
func contains(needle interface{}, array interface{}) (bool, error) {
|
||||
if array != nil {
|
||||
value := reflect.ValueOf(array)
|
||||
switch reflect.TypeOf(array).Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
value := value.Index(i)
|
||||
if value.IsValid() && value.CanInterface() {
|
||||
if equal(value.Interface(), needle) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
return false, fmt.Errorf("operator in not defined on %T", array)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func call(name string, fn interface{}, arguments []Node, env interface{}) (interface{}, error) {
|
||||
in := make([]reflect.Value, 0)
|
||||
for _, arg := range arguments {
|
||||
a, err := Run(arg, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
in = append(in, reflect.ValueOf(a))
|
||||
}
|
||||
|
||||
out := reflect.ValueOf(fn).Call(in)
|
||||
if len(out) == 0 {
|
||||
return nil, nil
|
||||
} else if len(out) > 1 {
|
||||
return nil, fmt.Errorf("func %q must return only one value", name)
|
||||
}
|
||||
|
||||
if out[0].IsValid() && out[0].CanInterface() {
|
||||
return out[0].Interface(), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
Reference in New Issue
Block a user