| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748 |
- // 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
- }
- }
|