mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-08 14:37:55 +08:00
* fix: make condition eval compatible with go 1.18 * fix: update circle-ci image to yunion/centos-build:go-1.18.3-0 * update go,mod go version to 1.18 * update vendor Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
749 lines
18 KiB
Go
749 lines
18 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package conditionparser
|
|
|
|
import (
|
|
"fmt"
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/pkg/errors"
|
|
"yunion.io/x/pkg/utils"
|
|
)
|
|
|
|
var (
|
|
ErrInvalidOp = errors.Error("invalid operation")
|
|
ErrFieldNotFound = errors.Error("field not found")
|
|
ErrOutOfIndex = errors.Error("out of index")
|
|
ErrFuncArgument = errors.Error("invalid function arguments")
|
|
ErrMethodNotFound = errors.Error("method not found")
|
|
)
|
|
|
|
func IsValid(exprStr string) bool {
|
|
_, err := parser.ParseExpr(exprStr)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func EvalString(exprStr string, input interface{}) (string, error) {
|
|
if len(exprStr) == 0 {
|
|
return "", nil
|
|
}
|
|
expr, err := parser.ParseExpr(exprStr)
|
|
if err != nil {
|
|
return "", errors.Wrapf(err, "parse expr %s error", exprStr)
|
|
}
|
|
result, err := eval(expr, input)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "eval")
|
|
}
|
|
switch strVal := result.(type) {
|
|
case string:
|
|
return strVal, nil
|
|
case *jsonutils.JSONString:
|
|
return strVal.GetString()
|
|
default:
|
|
return fmt.Sprintf("%s", strVal), nil
|
|
}
|
|
}
|
|
|
|
func EvalBool(exprStr string, input interface{}) (bool, error) {
|
|
if len(exprStr) == 0 {
|
|
return true, nil
|
|
}
|
|
expr, err := parser.ParseExpr(exprStr)
|
|
if err != nil {
|
|
return false, errors.Wrapf(err, "parse expr %s", exprStr)
|
|
}
|
|
result, err := eval(expr, input)
|
|
if err != nil {
|
|
return false, errors.Wrap(err, "eval")
|
|
}
|
|
switch bVal := result.(type) {
|
|
case bool:
|
|
return bVal, nil
|
|
case *jsonutils.JSONBool:
|
|
return bVal.Bool()
|
|
case []interface{}, *jsonutils.JSONArray:
|
|
arrX := getArray(result)
|
|
for i := 0; i < len(arrX); i += 1 {
|
|
val := getBool(arrX[i])
|
|
if val {
|
|
return true, nil
|
|
}
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func eval(expr ast.Expr, input interface{}) (interface{}, error) {
|
|
switch expr.(type) {
|
|
case *ast.BinaryExpr:
|
|
v, err := evalBinary(expr.(*ast.BinaryExpr), input)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "evalBinary")
|
|
}
|
|
return v, nil
|
|
case *ast.UnaryExpr:
|
|
v, err := evalUnary(expr.(*ast.UnaryExpr), input)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "evalUnary")
|
|
}
|
|
return v, nil
|
|
case *ast.SelectorExpr:
|
|
v, err := evalSelector(expr.(*ast.SelectorExpr), input)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "evalSelector")
|
|
}
|
|
return v, nil
|
|
case *ast.Ident:
|
|
v, err := evalIdent(expr.(*ast.Ident), input)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "evalIdent")
|
|
}
|
|
return v, nil
|
|
case *ast.ParenExpr:
|
|
v, err := evalParen(expr.(*ast.ParenExpr), input)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "evalParen")
|
|
}
|
|
return v, nil
|
|
case *ast.CallExpr:
|
|
v, err := evalCall(expr.(*ast.CallExpr), input)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "evalCall")
|
|
}
|
|
return v, nil
|
|
case *ast.IndexExpr:
|
|
v, err := evalIndex(expr.(*ast.IndexExpr), input)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "evalIndex")
|
|
}
|
|
return v, nil
|
|
case *ast.BasicLit:
|
|
v, err := evalBasicLit(expr.(*ast.BasicLit), input)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "evalBasicLit")
|
|
}
|
|
return v, nil
|
|
default:
|
|
return nil, errors.Wrapf(ErrInvalidOp, "unsupported expr type %s", reflect.TypeOf(expr))
|
|
}
|
|
}
|
|
|
|
func evalBasicLit(expr *ast.BasicLit, input interface{}) (interface{}, error) {
|
|
switch expr.Kind {
|
|
case token.IDENT:
|
|
switch input.(type) {
|
|
case *jsonutils.JSONDict:
|
|
jsonX := input.(*jsonutils.JSONDict)
|
|
return jsonX.Get(expr.Value)
|
|
default:
|
|
return nil, errors.Wrapf(ErrInvalidOp, "evalBasicLit unsupported type %s", reflect.TypeOf(input))
|
|
}
|
|
case token.INT:
|
|
return strconv.Atoi(expr.Value)
|
|
case token.FLOAT:
|
|
return strconv.ParseFloat(expr.Value, 64)
|
|
case token.CHAR:
|
|
return expr.Value[1 : len(expr.Value)-1][0], nil
|
|
case token.STRING:
|
|
return expr.Value[1 : len(expr.Value)-1], nil
|
|
default:
|
|
return nil, errors.Wrapf(ErrInvalidOp, "evalBasicLit unsupported kind %s", expr.Kind)
|
|
}
|
|
}
|
|
|
|
func evalIndex(expr *ast.IndexExpr, input interface{}) (interface{}, error) {
|
|
X, err := eval(expr.X, input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
indexV, err := eval(expr.Index, input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch X.(type) {
|
|
case []interface{}, *jsonutils.JSONArray:
|
|
arrX := getArray(X)
|
|
indexI := getInt(indexV)
|
|
if indexI >= 0 && indexI < int64(len(arrX)) {
|
|
return arrX[indexI], nil
|
|
} else {
|
|
return nil, ErrOutOfIndex
|
|
}
|
|
case *jsonutils.JSONDict:
|
|
jsonX := X.(*jsonutils.JSONDict)
|
|
field := getString(indexV)
|
|
return getJSONProperty(jsonX, field)
|
|
case string:
|
|
strX := X.(string)
|
|
indexI := getInt(indexV)
|
|
if indexI >= 0 && indexI < int64(len(strX)) {
|
|
return strX[indexI], nil
|
|
} else {
|
|
return nil, ErrOutOfIndex
|
|
}
|
|
default:
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
func args2Strings(args []interface{}) []string {
|
|
strs := make([]string, len(args))
|
|
for i := 0; i < len(args); i += 1 {
|
|
strs[i] = getString(args[i])
|
|
}
|
|
return strs
|
|
}
|
|
|
|
func args2Ints(args []interface{}) []int {
|
|
ints := make([]int, len(args))
|
|
for i := 0; i < len(args); i += 1 {
|
|
ints[i] = int(getInt(args[i]))
|
|
}
|
|
return ints
|
|
}
|
|
|
|
func evalCallInternal(funcV interface{}, args []interface{}) (interface{}, error) {
|
|
switch funcV.(type) {
|
|
case []interface{}, *jsonutils.JSONArray:
|
|
arrFuncV := getArray(funcV)
|
|
ret := make([]interface{}, len(arrFuncV))
|
|
for i := 0; i < len(arrFuncV); i += 1 {
|
|
reti, err := evalCallInternal(arrFuncV[i], args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ret[i] = reti
|
|
}
|
|
return ret, nil
|
|
case *funcCaller:
|
|
caller := funcV.(*funcCaller)
|
|
switch caller.caller.(type) {
|
|
case string:
|
|
strX := caller.caller.(string)
|
|
switch caller.method {
|
|
case "startswith":
|
|
strArgs := args2Strings(args)
|
|
if len(strArgs) != 1 {
|
|
return nil, ErrFuncArgument
|
|
}
|
|
return strings.HasPrefix(strX, strArgs[0]), nil
|
|
case "endswith":
|
|
strArgs := args2Strings(args)
|
|
if len(strArgs) != 1 {
|
|
return nil, ErrFuncArgument
|
|
}
|
|
return strings.HasSuffix(strX, strArgs[0]), nil
|
|
case "contains":
|
|
strArgs := args2Strings(args)
|
|
if len(strArgs) != 1 {
|
|
return nil, ErrFuncArgument
|
|
}
|
|
return strings.Contains(strX, strArgs[0]), nil
|
|
case "in":
|
|
var strArgs []string
|
|
if len(args) == 0 {
|
|
return nil, ErrFuncArgument
|
|
} else if len(args) == 1 {
|
|
switch args[0].(type) {
|
|
case string, *jsonutils.JSONString:
|
|
strArgs = []string{args[0].(string)}
|
|
case []interface{}, *jsonutils.JSONArray:
|
|
strArgs = args2Strings(getArray(args[0]))
|
|
default:
|
|
return nil, ErrFuncArgument
|
|
}
|
|
} else {
|
|
strArgs = args2Strings(args)
|
|
}
|
|
return utils.IsInStringArray(strX, strArgs), nil
|
|
case "len":
|
|
if len(args) > 0 {
|
|
return nil, ErrFuncArgument
|
|
}
|
|
return len(strX), nil
|
|
case "substr":
|
|
intArgs := args2Ints(args)
|
|
var o1, o2 int
|
|
if len(intArgs) == 1 {
|
|
o1 = 0
|
|
o2 = intArgs[0]
|
|
} else if len(intArgs) == 2 {
|
|
o1 = intArgs[0]
|
|
o2 = intArgs[1]
|
|
} else {
|
|
return nil, ErrFuncArgument
|
|
}
|
|
if o1 < 0 {
|
|
o1 = len(strX) + o1
|
|
}
|
|
if o2 < 0 {
|
|
o2 = len(strX) + o2
|
|
}
|
|
if o1 < 0 || o1 >= len(strX) {
|
|
return nil, ErrFuncArgument
|
|
}
|
|
if o2 <= o1 || o2 > len(strX) {
|
|
return nil, ErrFuncArgument
|
|
}
|
|
return strX[o1:o2], nil
|
|
default:
|
|
return nil, ErrInvalidOp
|
|
}
|
|
case *jsonutils.JSONDict:
|
|
jsonX := caller.caller.(*jsonutils.JSONDict)
|
|
switch caller.method {
|
|
case "contains":
|
|
strArgs := args2Strings(args)
|
|
return jsonX.Contains(strArgs...), nil
|
|
case "len":
|
|
if len(args) > 0 {
|
|
return nil, ErrFuncArgument
|
|
}
|
|
return jsonX.Size(), nil
|
|
case "keys":
|
|
if len(args) > 0 {
|
|
return nil, ErrFuncArgument
|
|
}
|
|
return jsonutils.NewStringArray(jsonX.SortedKeys()), nil
|
|
default:
|
|
return nil, ErrMethodNotFound
|
|
}
|
|
case []interface{}, *jsonutils.JSONArray:
|
|
array := getArray(caller.caller)
|
|
switch caller.method {
|
|
case "len":
|
|
if len(args) > 0 {
|
|
return nil, ErrFuncArgument
|
|
}
|
|
return len(array), nil
|
|
case "contains":
|
|
if len(args) < 1 {
|
|
return nil, ErrFuncArgument
|
|
}
|
|
for j := 0; j < len(args); j += 1 {
|
|
find := false
|
|
for i := 0; i < len(array); i += 1 {
|
|
findInf, err := evalBinaryInternal(array[i], args[j], token.EQL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch findInf.(type) {
|
|
case bool:
|
|
if findInf.(bool) {
|
|
find = true
|
|
}
|
|
default:
|
|
return nil, ErrInvalidOp
|
|
}
|
|
}
|
|
if !find {
|
|
return false, nil
|
|
}
|
|
}
|
|
return true, nil
|
|
default:
|
|
return nil, ErrMethodNotFound
|
|
}
|
|
default:
|
|
return nil, ErrInvalidOp
|
|
}
|
|
default:
|
|
return nil, ErrInvalidOp
|
|
}
|
|
}
|
|
|
|
func evalCall(expr *ast.CallExpr, input interface{}) (interface{}, error) {
|
|
funcV, err := eval(expr.Fun, input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
args := make([]interface{}, len(expr.Args))
|
|
for i := 0; i < len(expr.Args); i += 1 {
|
|
args[i], err = eval(expr.Args[i], input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return evalCallInternal(funcV, args)
|
|
}
|
|
|
|
func evalParen(expr *ast.ParenExpr, input interface{}) (interface{}, error) {
|
|
return eval(expr.X, input)
|
|
}
|
|
|
|
func getJSONProperty(json *jsonutils.JSONDict, identStr string) (jsonutils.JSONObject, error) {
|
|
if json.Contains(identStr) {
|
|
return json.Get(identStr)
|
|
} else {
|
|
identArray := jsonutils.GetArrayOfPrefix(json, identStr)
|
|
if len(identArray) > 0 {
|
|
return jsonutils.NewArray(identArray...), nil
|
|
} else {
|
|
return nil, ErrFieldNotFound
|
|
}
|
|
}
|
|
}
|
|
|
|
func evalIdent(expr *ast.Ident, input interface{}) (interface{}, error) {
|
|
if input == nil {
|
|
return expr.Name, nil
|
|
} else {
|
|
switch input.(type) {
|
|
case *jsonutils.JSONDict:
|
|
json := input.(*jsonutils.JSONDict)
|
|
return getJSONProperty(json, expr.Name)
|
|
default:
|
|
return nil, errors.Wrapf(ErrInvalidOp, "evalIdent unsupported type %s", reflect.TypeOf(input))
|
|
}
|
|
}
|
|
}
|
|
|
|
type funcCaller struct {
|
|
caller interface{}
|
|
method string
|
|
}
|
|
|
|
func getArray(X interface{}) []interface{} {
|
|
switch X.(type) {
|
|
case *jsonutils.JSONArray:
|
|
arr := X.(*jsonutils.JSONArray)
|
|
ret := make([]interface{}, arr.Size())
|
|
for i := 0; i < arr.Size(); i += 1 {
|
|
ret[i], _ = arr.GetAt(i)
|
|
}
|
|
return ret
|
|
default:
|
|
return X.([]interface{})
|
|
}
|
|
}
|
|
|
|
func evalSelectorInternal(X interface{}, identStr string) (interface{}, error) {
|
|
switch X.(type) {
|
|
case *jsonutils.JSONDict:
|
|
json := X.(*jsonutils.JSONDict)
|
|
ret, err := getJSONProperty(json, identStr)
|
|
if err == ErrFieldNotFound {
|
|
return &funcCaller{caller: X, method: identStr}, nil
|
|
} else {
|
|
return ret, errors.Wrapf(err, "getJSONProperty %s", identStr)
|
|
}
|
|
case []interface{}, *jsonutils.JSONArray:
|
|
if identStr == "len" || identStr == "contains" {
|
|
return &funcCaller{caller: X, method: identStr}, nil
|
|
}
|
|
arrX := getArray(X)
|
|
ret := make([]interface{}, len(arrX))
|
|
for i := 0; i < len(arrX); i += 1 {
|
|
reti, err := evalSelectorInternal(arrX[i], identStr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ret[i] = reti
|
|
}
|
|
return ret, nil
|
|
case string, *jsonutils.JSONString:
|
|
return &funcCaller{caller: getString(X), method: identStr}, nil
|
|
default:
|
|
return nil, errors.Wrapf(ErrInvalidOp, "unsupported type %s", reflect.TypeOf(X))
|
|
}
|
|
}
|
|
|
|
func evalSelector(expr *ast.SelectorExpr, input interface{}) (interface{}, error) {
|
|
X, err := eval(expr.X, input)
|
|
if err != nil {
|
|
return nil, errors.Wrap(ErrInvalidOp, "eval selector X")
|
|
}
|
|
ident, err := evalIdent(expr.Sel, nil)
|
|
if err != nil {
|
|
return nil, errors.Wrap(ErrInvalidOp, "eval selector Sel")
|
|
}
|
|
identStr := ident.(string)
|
|
|
|
return evalSelectorInternal(X, identStr)
|
|
}
|
|
|
|
func evalUnaryInternal(X interface{}, op token.Token) (interface{}, error) {
|
|
switch X.(type) {
|
|
case []interface{}, *jsonutils.JSONArray:
|
|
arrX := getArray(X)
|
|
ret := make([]interface{}, len(arrX))
|
|
for i := 0; i < len(arrX); i += 1 {
|
|
reti, err := evalUnaryInternal(arrX[i], op)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ret[i] = reti
|
|
}
|
|
return ret, nil
|
|
case bool, *jsonutils.JSONBool:
|
|
boolX := getBool(X)
|
|
switch op {
|
|
case token.NOT:
|
|
return !boolX, nil
|
|
default:
|
|
return nil, ErrInvalidOp
|
|
}
|
|
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, *jsonutils.JSONInt:
|
|
intX := getInt(X)
|
|
switch op {
|
|
case token.SUB:
|
|
return -intX, nil
|
|
default:
|
|
return nil, ErrInvalidOp
|
|
}
|
|
case float32, float64, *jsonutils.JSONFloat:
|
|
floatX := getFloat(X)
|
|
switch op {
|
|
case token.SUB:
|
|
return -floatX, nil
|
|
default:
|
|
return nil, ErrInvalidOp
|
|
}
|
|
default:
|
|
return nil, ErrInvalidOp
|
|
}
|
|
}
|
|
|
|
func evalUnary(expr *ast.UnaryExpr, input interface{}) (interface{}, error) {
|
|
X, err := eval(expr.X, input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
op := expr.Op
|
|
return evalUnaryInternal(X, op)
|
|
}
|
|
|
|
func getString(val interface{}) string {
|
|
switch val.(type) {
|
|
case *jsonutils.JSONString:
|
|
jsonVal, _ := val.(*jsonutils.JSONString).GetString()
|
|
return jsonVal
|
|
default:
|
|
return val.(string)
|
|
}
|
|
}
|
|
|
|
func getBool(val interface{}) bool {
|
|
switch val.(type) {
|
|
case *jsonutils.JSONBool:
|
|
jsonVal, _ := val.(*jsonutils.JSONBool).Bool()
|
|
return jsonVal
|
|
default:
|
|
return val.(bool)
|
|
}
|
|
}
|
|
|
|
func getInt(val interface{}) int64 {
|
|
switch val.(type) {
|
|
case *jsonutils.JSONInt:
|
|
jsonVal, _ := val.(*jsonutils.JSONInt).Int()
|
|
return jsonVal
|
|
default:
|
|
return reflect.ValueOf(val).Int()
|
|
}
|
|
}
|
|
|
|
func getFloat(val interface{}) float64 {
|
|
switch val.(type) {
|
|
case *jsonutils.JSONFloat:
|
|
jsonVal, _ := val.(*jsonutils.JSONFloat).Float()
|
|
return jsonVal
|
|
default:
|
|
return reflect.ValueOf(val).Float()
|
|
}
|
|
}
|
|
|
|
func evalBinaryInternal(X, Y interface{}, op token.Token) (interface{}, error) {
|
|
switch X.(type) {
|
|
case []interface{}, *jsonutils.JSONArray:
|
|
arrX := getArray(X)
|
|
ret := make([]interface{}, len(arrX))
|
|
for i := 0; i < len(arrX); i += 1 {
|
|
reti, err := evalBinaryInternal(arrX[i], Y, op)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "X.evalBinaryInternal at [%d]", i)
|
|
}
|
|
ret[i] = reti
|
|
}
|
|
return ret, nil
|
|
}
|
|
switch Y.(type) {
|
|
case []interface{}, *jsonutils.JSONArray:
|
|
arrY := getArray(Y)
|
|
ret := make([]interface{}, len(arrY))
|
|
for i := 0; i < len(arrY); i += 1 {
|
|
reti, err := evalBinaryInternal(X, arrY[i], op)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "Y.evalBinaryInternal at [%d]", i)
|
|
}
|
|
ret[i] = reti
|
|
}
|
|
return ret, nil
|
|
}
|
|
switch X.(type) {
|
|
case bool, *jsonutils.JSONBool:
|
|
switch Y.(type) {
|
|
case bool, *jsonutils.JSONBool:
|
|
boolX := getBool(X)
|
|
boolY := getBool(Y)
|
|
switch op {
|
|
case token.LAND:
|
|
return boolX && boolY, nil
|
|
case token.LOR:
|
|
return boolX || boolY, nil
|
|
default:
|
|
return nil, errors.Wrapf(ErrInvalidOp, "bool op %v", op)
|
|
}
|
|
default:
|
|
return nil, errors.Wrap(ErrInvalidOp, "mismatch bool type")
|
|
}
|
|
case string, *jsonutils.JSONString:
|
|
switch Y.(type) {
|
|
case string, *jsonutils.JSONString:
|
|
strX := getString(X)
|
|
strY := getString(Y)
|
|
switch op {
|
|
case token.ADD:
|
|
return strX + strY, nil
|
|
case token.EQL:
|
|
return strX == strY, nil
|
|
case token.NEQ:
|
|
return strX != strY, nil
|
|
default:
|
|
return nil, errors.Wrapf(ErrInvalidOp, "string op %v", op)
|
|
}
|
|
default:
|
|
return nil, errors.Wrap(ErrInvalidOp, "mismatch string type")
|
|
}
|
|
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, *jsonutils.JSONInt:
|
|
intX := getInt(X)
|
|
switch Y.(type) {
|
|
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, *jsonutils.JSONInt:
|
|
intY := getInt(Y)
|
|
return evalIntegerOp(intX, intY, op)
|
|
case float32, float64, jsonutils.JSONFloat:
|
|
floatX := float64(intX)
|
|
floatY := getFloat(Y)
|
|
return evalFloatOp(floatX, floatY, op)
|
|
default:
|
|
return nil, errors.Wrap(ErrInvalidOp, "mismatch type int")
|
|
}
|
|
case float32, float64, *jsonutils.JSONFloat:
|
|
floatX := getFloat(X)
|
|
switch Y.(type) {
|
|
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, *jsonutils.JSONInt:
|
|
floatY := float64(getInt(Y))
|
|
return evalFloatOp(floatX, floatY, op)
|
|
case float32, float64, *jsonutils.JSONFloat:
|
|
floatY := getFloat(Y)
|
|
return evalFloatOp(floatX, floatY, op)
|
|
default:
|
|
return nil, errors.Wrap(ErrInvalidOp, "mismatch type float")
|
|
}
|
|
default:
|
|
return nil, errors.Wrapf(ErrInvalidOp, "unsupported op type %v", reflect.TypeOf(X))
|
|
}
|
|
}
|
|
|
|
func evalBinary(bExpr *ast.BinaryExpr, input interface{}) (interface{}, error) {
|
|
X, err := eval(bExpr.X, input)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "eval binary left")
|
|
}
|
|
Y, err := eval(bExpr.Y, input)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "eval binary right")
|
|
}
|
|
return evalBinaryInternal(X, Y, bExpr.Op)
|
|
}
|
|
|
|
func evalIntegerOp(X, Y int64, op token.Token) (interface{}, error) {
|
|
switch op {
|
|
case token.ADD:
|
|
return X + Y, nil
|
|
case token.SUB:
|
|
return X - Y, nil
|
|
case token.MUL:
|
|
return X * Y, nil
|
|
case token.QUO:
|
|
return X / Y, nil
|
|
case token.REM:
|
|
return X % Y, nil
|
|
case token.AND:
|
|
return X & Y, nil
|
|
case token.OR:
|
|
return X | Y, nil
|
|
case token.XOR:
|
|
return X ^ Y, nil
|
|
case token.SHL:
|
|
return X << uint64(Y), nil
|
|
case token.SHR:
|
|
return X >> uint64(Y), nil
|
|
case token.AND_NOT:
|
|
return X &^ Y, nil
|
|
case token.EQL:
|
|
return X == Y, nil
|
|
case token.LSS:
|
|
return X < Y, nil
|
|
case token.GTR:
|
|
return X > Y, nil
|
|
case token.NEQ:
|
|
return X != Y, nil
|
|
case token.LEQ:
|
|
return X <= Y, nil
|
|
case token.GEQ:
|
|
return X >= Y, nil
|
|
default:
|
|
return nil, ErrInvalidOp
|
|
}
|
|
}
|
|
|
|
func evalFloatOp(X, Y float64, op token.Token) (interface{}, error) {
|
|
switch op {
|
|
case token.ADD:
|
|
return X + Y, nil
|
|
case token.SUB:
|
|
return X - Y, nil
|
|
case token.MUL:
|
|
return X * Y, nil
|
|
case token.QUO:
|
|
return X / Y, nil
|
|
case token.EQL:
|
|
return X == Y, nil
|
|
case token.LSS:
|
|
return X < Y, nil
|
|
case token.GTR:
|
|
return X > Y, nil
|
|
case token.NEQ:
|
|
return X != Y, nil
|
|
case token.LEQ:
|
|
return X <= Y, nil
|
|
case token.GEQ:
|
|
return X >= Y, nil
|
|
default:
|
|
return nil, ErrInvalidOp
|
|
}
|
|
}
|