// MultiNode evaluator // // A IN (b,c,d) // func walkMulti(ctx expr.EvalContext, node *expr.MultiArgNode) (value.Value, bool) { a, aok := Eval(ctx, node.Args[0]) //u.Debugf("multi: %T:%v %v", a, a, node.Operator) if !aok || a == nil || a.Type() == value.NilType { // this is expected, most likely to missing data to operate on //u.Debugf("Could not evaluate args, %#v", node.Args[0]) return value.BoolValueFalse, false } if node.Operator.T != lex.TokenIN { u.Warnf("walk multiarg not implemented for node type %#v", node) return value.NilValueVal, false } // Support `"literal" IN identity` if len(node.Args) == 2 && node.Args[1].NodeType() == expr.IdentityNodeType { ident := node.Args[1].(*expr.IdentityNode) mval, ok := walkIdentity(ctx, ident) if !ok { // Failed to lookup ident return value.BoolValueFalse, true } sval, ok := mval.(value.Slice) if !ok { u.Debugf("expected slice but received %T", mval) return value.BoolValueFalse, false } for _, val := range sval.SliceValue() { match, err := value.Equal(val, a) if err != nil { // Couldn't compare values u.Debugf("IN: couldn't compare %s and %s", val, a) continue } if match { return value.BoolValueTrue, true } } // No match, return false return value.BoolValueFalse, true } for i := 1; i < len(node.Args); i++ { v, ok := Eval(ctx, node.Args[i]) if ok && v != nil { //u.Debugf("in? %v %v", a, v) if eq, err := value.Equal(a, v); eq && err == nil { return value.NewBoolValue(true), true } } else { //u.Debugf("could not evaluate arg: %v", node.Args[i]) } } return value.BoolValueFalse, true }
// Not Equal function? returns true if items are equal // // ne(item,5) func Ne(ctx expr.EvalContext, itemA, itemB value.Value) (value.BoolValue, bool) { eq, err := value.Equal(itemA, itemB) if err == nil { return value.NewBoolValue(!eq), true } return value.BoolValueFalse, true }
// MultiNode evaluator // // A IN (b,c,d) // func walkMulti(ctx expr.EvalContext, node *expr.MultiArgNode) (value.Value, bool) { a, aok := Eval(ctx, node.Args[0]) //u.Infof("multi: %T:%v %v", a, a, node.Operator) if !aok { u.Infof("Could not evaluate args, %#v", node.Args[0]) return value.BoolValueFalse, false } switch node.Operator.T { case lex.TokenIN: for i := 1; i < len(node.Args); i++ { v, ok := Eval(ctx, node.Args[i]) if ok { //u.Debugf("in? %v %v", a, v) if eq, err := value.Equal(a, v); eq && err == nil { return value.NewBoolValue(true), true } } else { u.Warnf("could not evaluate arg: %v", node.Args[i]) } } return value.NewBoolValue(false), true default: u.Warnf("tri node walk not implemented: %#v", node) } return value.NewNilValue(), false }
// Equal function? returns true if items are equal // // eq(item,5) // func Eq(ctx expr.EvalContext, itemA, itemB value.Value) (value.BoolValue, bool) { eq, err := value.Equal(itemA, itemB) //u.Infof("EQ: %v %v ==? %v", itemA, itemB, eq) if err == nil { return value.NewBoolValue(eq), true } return value.BoolValueFalse, false }
// Binary operands: =, ==, !=, OR, AND, >, <, >=, <=, LIKE, contains // // x == y, x = y // x != y // x OR y // x > y // x < = // func walkBinary(ctx expr.EvalContext, node *expr.BinaryNode) (value.Value, bool) { ar, aok := Eval(ctx, node.Args[0]) br, bok := Eval(ctx, node.Args[1]) //u.Debugf("walkBinary: aok?%v ar:%v %T node=%s", aok, ar, ar, node.Args[0]) //u.Debugf("walkBinary: bok?%v br:%v %T node=%s", bok, br, br, node.Args[1]) //u.Debugf("walkBinary: l:%v r:%v %T %T node=%s", ar, br, ar, br, node) // If we could not evaluate either we can shortcut if !aok && !bok { switch node.Operator.T { case lex.TokenLogicOr, lex.TokenOr: return value.NewBoolValue(false), true case lex.TokenEqualEqual, lex.TokenEqual: // We don't alllow nil == nil here bc we have a NilValue type // that we would use for that return value.NewBoolValue(false), true case lex.TokenNE: return value.NewBoolValue(false), true case lex.TokenGT, lex.TokenGE, lex.TokenLT, lex.TokenLE, lex.TokenLike: return value.NewBoolValue(false), true } //u.Debugf("walkBinary not ok: op=%s %v l:%v r:%v %T %T", node.Operator, node, ar, br, ar, br) return nil, false } // Else if we can only evaluate one, we can short circuit as well if !aok || !bok { switch node.Operator.T { case lex.TokenAnd, lex.TokenLogicAnd: return value.NewBoolValue(false), true case lex.TokenEqualEqual, lex.TokenEqual: return value.NewBoolValue(false), true case lex.TokenNE: // they are technically not equal? return value.NewBoolValue(true), true case lex.TokenGT, lex.TokenGE, lex.TokenLT, lex.TokenLE, lex.TokenLike: return value.NewBoolValue(false), true } //u.Debugf("walkBinary not ok: op=%s %v l:%v r:%v %T %T", node.Operator, node, ar, br, ar, br) // need to fall through to below } switch at := ar.(type) { case value.IntValue: switch bt := br.(type) { case value.IntValue: //u.Debugf("doing operate ints %v %v %v", at, node.Operator.V, bt) n := operateInts(node.Operator, at, bt) return n, true case value.StringValue: bi, err := strconv.ParseInt(bt.Val(), 10, 64) if err == nil { n, err := operateIntVals(node.Operator, at.Val(), bi) if err != nil { return nil, false } return n, true } case value.NumberValue: //u.Debugf("doing operate ints/numbers %v %v %v", at, node.Operator.V, bt) n := operateNumbers(node.Operator, at.NumberValue(), bt) return n, true case value.SliceValue: switch node.Operator.T { case lex.TokenIN: for _, val := range bt.Val() { switch valt := val.(type) { case value.StringValue: if at.Val() == valt.IntValue().Val() { return value.BoolValueTrue, true } case value.IntValue: if at.Val() == valt.Val() { return value.BoolValueTrue, true } case value.NumberValue: if at.Val() == valt.Int() { return value.BoolValueTrue, true } default: u.Debugf("Could not coerce to number: T:%T v:%v", val, val) } } return value.NewBoolValue(false), true default: u.Debugf("unsupported op for SliceValue op:%v rhT:%T", node.Operator, br) return nil, false } case nil, value.NilValue: return nil, false default: u.Errorf("unknown type: %T %v", bt, bt) } case value.NumberValue: switch bt := br.(type) { case value.IntValue: n := operateNumbers(node.Operator, at, bt.NumberValue()) return n, true case value.NumberValue: n := operateNumbers(node.Operator, at, bt) return n, true case value.SliceValue: for _, val := range bt.Val() { switch valt := val.(type) { case value.StringValue: if at.Val() == valt.NumberValue().Val() { return value.BoolValueTrue, true } case value.IntValue: if at.Val() == valt.NumberValue().Val() { return value.BoolValueTrue, true } case value.NumberValue: if at.Val() == valt.Val() { return value.BoolValueTrue, true } default: u.Debugf("Could not coerce to number: T:%T v:%v", val, val) } } return value.BoolValueFalse, true //case value.StringValue: case nil, value.NilValue: return nil, false default: u.Errorf("unknown type: %T %v", bt, bt) } case value.BoolValue: switch bt := br.(type) { case value.BoolValue: atv, btv := at.Value().(bool), bt.Value().(bool) switch node.Operator.T { case lex.TokenLogicAnd, lex.TokenAnd: return value.NewBoolValue(atv && btv), true case lex.TokenLogicOr, lex.TokenOr: return value.NewBoolValue(atv || btv), true case lex.TokenEqualEqual, lex.TokenEqual: return value.NewBoolValue(atv == btv), true case lex.TokenNE: return value.NewBoolValue(atv != btv), true default: u.Warnf("bool binary?: %#v %v %v", node, at, bt) } case nil, value.NilValue: switch node.Operator.T { case lex.TokenLogicAnd: return value.NewBoolValue(false), true case lex.TokenLogicOr, lex.TokenOr: return at, true case lex.TokenEqualEqual, lex.TokenEqual: return value.NewBoolValue(false), true case lex.TokenNE: return value.NewBoolValue(true), true // case lex.TokenGE, lex.TokenGT, lex.TokenLE, lex.TokenLT: // return value.NewBoolValue(false), true default: u.Warnf("right side nil binary: %q", node) return nil, false } default: //u.Warnf("br: %#v", br) //u.Errorf("at?%T %v coerce?%v bt? %T %v", at, at.Value(), at.CanCoerce(stringRv), bt, bt.Value()) return nil, false } case value.StringValue: switch bt := br.(type) { case value.StringValue: // Nice, both strings return operateStrings(node.Operator, at, bt), true case nil, value.NilValue: switch node.Operator.T { case lex.TokenEqualEqual, lex.TokenEqual: if at.Nil() { return value.NewBoolValue(true), true } return value.NewBoolValue(false), true case lex.TokenNE: if at.Nil() { return value.NewBoolValue(false), true } return value.NewBoolValue(true), true default: u.Debugf("unsupported op: %v", node.Operator) return nil, false } case value.SliceValue: switch node.Operator.T { case lex.TokenIN: for _, val := range bt.Val() { if at.Val() == val.ToString() { return value.NewBoolValue(true), true } } return value.NewBoolValue(false), true default: u.Debugf("unsupported op for SliceValue op:%v rhT:%T", node.Operator, br) return nil, false } case value.StringsValue: switch node.Operator.T { case lex.TokenIN: for _, val := range bt.Val() { if at.Val() == val { return value.NewBoolValue(true), true } } return value.NewBoolValue(false), true default: u.Debugf("unsupported op for Strings op:%v rhT:%T", node.Operator, br) return nil, false } case value.MapIntValue: switch node.Operator.T { case lex.TokenIN: for key, _ := range bt.Val() { if at.Val() == key { return value.NewBoolValue(true), true } } return value.NewBoolValue(false), true default: u.Debugf("unsupported op for MapInt op:%v rhT:%T", node.Operator, br) return nil, false } case value.BoolValue: if value.IsBool(at.Val()) { //u.Warnf("bool eval: %v %v %v :: %v", value.BoolStringVal(at.Val()), node.Operator.T.String(), bt.Val(), value.NewBoolValue(value.BoolStringVal(at.Val()) == bt.Val())) switch node.Operator.T { case lex.TokenEqualEqual, lex.TokenEqual: return value.NewBoolValue(value.BoolStringVal(at.Val()) == bt.Val()), true case lex.TokenNE: return value.NewBoolValue(value.BoolStringVal(at.Val()) != bt.Val()), true default: u.Debugf("unsupported op: %v", node.Operator) return nil, false } } else { // Should we evaluate strings that are non-nil to be = true? u.Debugf("not handled: boolean %v %T=%v expr: %s", node.Operator, at.Value(), at.Val(), node.String()) return nil, false } default: // TODO: this doesn't make sense, we should be able to operate on other types if at.CanCoerce(int64Rv) { switch bt := br.(type) { case value.StringValue: n := operateNumbers(node.Operator, at.NumberValue(), bt.NumberValue()) return n, true case value.IntValue: n := operateNumbers(node.Operator, at.NumberValue(), bt.NumberValue()) return n, true case value.NumberValue: n := operateNumbers(node.Operator, at.NumberValue(), bt) return n, true default: u.Errorf("at?%T %v coerce?%v bt? %T %v", at, at.Value(), at.CanCoerce(stringRv), bt, bt.Value()) } } else { u.Errorf("at?%T %v coerce?%v bt? %T %v", at, at.Value(), at.CanCoerce(stringRv), br, br) } } case value.SliceValue: switch node.Operator.T { case lex.TokenContains: switch bval := br.(type) { case nil, value.NilValue: return nil, false case value.StringValue: // [x,y,z] contains str for _, val := range at.Val() { if strings.Contains(val.ToString(), bval.Val()) { return value.BoolValueTrue, true } } return value.BoolValueFalse, true case value.IntValue: // [] contains int for _, val := range at.Val() { //u.Infof("int contains? %v %v", val.Value(), br.Value()) if eq, _ := value.Equal(val, br); eq { return value.BoolValueTrue, true } } return value.BoolValueFalse, true } case lex.TokenLike: switch bv := br.(type) { case value.StringValue: // [x,y,z] LIKE str for _, val := range at.Val() { if boolVal, ok := LikeCompare(val.ToString(), bv.Val()); ok && boolVal.Val() == true { return boolVal, true } } return value.BoolValueFalse, true } case lex.TokenIntersects: switch bt := br.(type) { case nil, value.NilValue: return nil, false case value.SliceValue: for _, aval := range at.Val() { for _, bval := range bt.Val() { if eq, _ := value.Equal(aval, bval); eq { return value.BoolValueTrue, true } } } return value.BoolValueFalse, true case value.StringsValue: for _, aval := range at.Val() { for _, bstr := range bt.Val() { if aval.ToString() == bstr { return value.BoolValueTrue, true } } } return value.BoolValueFalse, true } } return nil, false case value.StringsValue: switch node.Operator.T { case lex.TokenContains: switch bv := br.(type) { case value.StringValue: // [x,y,z] contains str for _, val := range at.Val() { //u.Infof("str contains? %v %v", val, bv.Val()) if strings.Contains(val, bv.Val()) { return value.BoolValueTrue, true } } return value.BoolValueFalse, true } case lex.TokenLike: switch bv := br.(type) { case value.StringValue: // [x,y,z] LIKE str for _, val := range at.Val() { boolVal, ok := LikeCompare(val, bv.Val()) //u.Debugf("%s like %s ?? ok?%v result=%v", val, bv.Val(), ok, boolVal) if ok && boolVal.Val() == true { return boolVal, true } } return value.BoolValueFalse, true } case lex.TokenIntersects: switch bt := br.(type) { case nil, value.NilValue: return nil, false case value.SliceValue: for _, astr := range at.Val() { for _, bval := range bt.Val() { if astr == bval.ToString() { return value.BoolValueTrue, true } } } return value.BoolValueFalse, true case value.StringsValue: for _, astr := range at.Val() { for _, bstr := range bt.Val() { if astr == bstr { return value.BoolValueTrue, true } } } return value.BoolValueFalse, true } } return nil, false case value.TimeValue: rht := time.Time{} lht := at.Val() var err error switch bv := br.(type) { case value.TimeValue: rht = bv.Val() case value.StringValue: te := bv.Val() if len(te) > 3 && strings.ToLower(te[:3]) == "now" { // Is date math rht, err = datemath.Eval(te[3:]) } else { rht, err = dateparse.ParseAny(te) } if err != nil { u.Warnf("error? %s err=%v", te, err) return value.BoolValueFalse, false } case value.IntValue: // really? we are going to try ints? rht, err = dateparse.ParseAny(bv.ToString()) if err != nil { return value.BoolValueFalse, false } if rht.Year() < 1800 || rht.Year() > 2300 { return value.BoolValueFalse, false } default: //u.Warnf("un-handled? %#v", bv) } // if rht.IsZero() { // return nil, false // } switch node.Operator.T { case lex.TokenEqual, lex.TokenEqualEqual: if lht.Unix() == rht.Unix() { return value.BoolValueTrue, true } return value.BoolValueFalse, true case lex.TokenGT: // lhexpr > rhexpr if lht.Unix() > rht.Unix() { return value.BoolValueTrue, true } return value.BoolValueFalse, true case lex.TokenGE: // lhexpr >= rhexpr if lht.Unix() >= rht.Unix() { return value.BoolValueTrue, true } return value.BoolValueFalse, true case lex.TokenLT: // lhexpr < rhexpr if lht.Unix() < rht.Unix() { return value.BoolValueTrue, true } return value.BoolValueFalse, true case lex.TokenLE: // lhexpr <= rhexpr if lht.Unix() <= rht.Unix() { return value.BoolValueTrue, true } return value.BoolValueFalse, true default: u.Warnf("unhandled date op %v", node.Operator) } return nil, false case nil, value.NilValue: switch node.Operator.T { case lex.TokenLogicAnd: return value.NewBoolValue(false), true case lex.TokenLogicOr, lex.TokenOr: switch bt := br.(type) { case value.BoolValue: return bt, true default: return value.NewBoolValue(false), true } case lex.TokenEqualEqual, lex.TokenEqual: // does nil==nil = true ?? switch br.(type) { case nil, value.NilValue: return value.NewBoolValue(true), true default: return value.NewBoolValue(false), true } case lex.TokenNE: return value.NewBoolValue(true), true // case lex.TokenGE, lex.TokenGT, lex.TokenLE, lex.TokenLT: // return value.NewBoolValue(false), true case lex.TokenContains, lex.TokenLike, lex.TokenIN: return value.NewBoolValue(false), true default: //u.Debugf("left side nil binary: %q", node) return nil, false } default: u.Debugf("Unknown op? %T %T %v", ar, at, ar) return value.NewErrorValue(fmt.Sprintf("unsupported left side value: %T in %s", at, node)), false } return value.NewErrorValue(fmt.Sprintf("unsupported binary expression: %s", node)), false }