func walkUnary(ctx expr.EvalContext, node *expr.UnaryNode) (value.Value, bool) { a, ok := Eval(ctx, node.Arg) if !ok { if node.Operator.T == lex.TokenExists { return value.NewBoolValue(false), true } u.Debugf("urnary could not evaluate %#v", node) return a, false } switch node.Operator.T { case lex.TokenNegate: switch argVal := a.(type) { case value.BoolValue: //u.Infof("found urnary bool: res=%v expr=%v", !argVal.v, node.StringAST()) return value.NewBoolValue(!argVal.Val()), true default: //u.Errorf("urnary type not implementedUnknonwn node type: %T", argVal) panic(ErrUnknownNodeType) } case lex.TokenMinus: if an, aok := a.(value.NumericValue); aok { return value.NewNumberValue(-an.Float()), true } case lex.TokenExists: return value.NewBoolValue(true), true default: u.Warnf("urnary not implemented for type %s %#v", node.Operator.T.String(), node) } return value.NewNilValue(), false }
// 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 }
func TestSqlDelete(t *testing.T) { db := datasource.NewContextSimple() user1 := map[string]value.Value{ "user_id": value.NewIntValue(5), "item_count": value.NewStringValue("5"), "bval": value.NewBoolValue(true), "bvalf": value.NewBoolValue(false), "reg_date": value.NewStringValue("2014/11/01"), "name": value.NewStringValue("bob")} db.Insert(user1) user2 := map[string]value.Value{ "user_id": value.NewIntValue(6), "item_count": value.NewStringValue("5"), "reg_date": value.NewStringValue("2012/11/01"), "name": value.NewStringValue("allison")} db.Insert(user2) assert.Tf(t, len(db.Rows) == 2, "has 2 users") verifySqlDelete(t, ` DELETE FROM mytable WHERE yy(reg_date) == 14 `, db) assert.Tf(t, len(db.Rows) == 1, "must have 1 rows: %v %v", len(db.Rows), db.Rows) assert.Tf(t, db.Rows[0]["name"].ToString() == "allison", "%v", db.Rows) }
// TriNode evaluator // // A BETWEEN B AND C // func walkTri(ctx expr.EvalContext, node *expr.TriNode) (value.Value, bool) { a, aok := Eval(ctx, node.Args[0]) b, bok := Eval(ctx, node.Args[1]) c, c*k := Eval(ctx, node.Args[2]) //u.Infof("tri: %T:%v %v %T:%v %T:%v", a, a, node.Operator, b, b, c, c) if !aok { return value.BoolValueFalse, false } if !bok || !c*k { u.Debugf("Could not evaluate args, %#v", node.String()) return value.BoolValueFalse, false } if a == nil || b == nil || c == nil { return value.BoolValueFalse, false } switch node.Operator.T { case lex.TokenBetween: switch a.Type() { case value.IntType: //u.Infof("found tri: %v %v %v expr=%v", a, b, c, node.StringAST()) if aiv, ok := a.(value.IntValue); ok { if biv, ok := b.(value.IntValue); ok { if civ, ok := c.(value.IntValue); ok { if aiv.Int() > biv.Int() && aiv.Int() < civ.Int() { return value.NewBoolValue(true), true } else { return value.NewBoolValue(false), true } } } } return value.BoolValueFalse, false case value.NumberType: //u.Infof("found tri: %v %v %v expr=%v", a, b, c, node.StringAST()) if afv, ok := a.(value.NumberValue); ok { if bfv, ok := b.(value.NumberValue); ok { if cfv, ok := c.(value.NumberValue); ok { if afv.Float() > bfv.Float() && afv.Float() < cfv.Float() { return value.NewBoolValue(true), false } else { return value.NewBoolValue(false), true } } } } return value.BoolValueFalse, false default: u.Warnf("between not implemented for type %s %#v", a.Type().String(), node) } default: u.Warnf("tri node walk not implemented: %#v", node) } return value.NewNilValue(), false }
// Any: Answers True/False if any of the arguments evaluate to truish (javascripty) // type definintion of true // // int > 0 = true // string != "" = true // // // any(item,item2) => true, true // any(not_field) => false, true // func AnyFunc(ctx expr.EvalContext, vals ...value.Value) (value.BoolValue, bool) { for _, v := range vals { if v.Err() || v.Nil() { // continue } else if !value.IsNilIsh(v.Rv()) { return value.NewBoolValue(true), true } } return value.NewBoolValue(false), 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 }
// Not: urnary negation function // // eq(item,5) func NotFunc(ctx expr.EvalContext, item value.Value) (value.BoolValue, bool) { boolVal, ok := value.ToBool(item.Rv()) if ok { return value.NewBoolValue(!boolVal), true } return value.BoolValueFalse, false }
// Equal function? returns true if items are equal // // eq(item,5) func Eq(ctx expr.EvalContext, itemA, itemB value.Value) (value.BoolValue, bool) { //return BoolValue(itemA == itemB) //rvb := value.CoerceTo(itemA.Rv(), itemB.Rv()) //u.Infof("Eq(): a:%T b:%T %v=%v?", itemA, itemB, itemA.Value(), rvb) //u.Infof("Eq()2: %T %T", itemA.Rv(), rvb) return value.NewBoolValue(reflect.DeepEqual(itemA.Value(), itemB.Value())), true }
// <= Less Than or Equal // Must be able to convert items to Floats or else not ok // func LeFunc(ctx expr.EvalContext, lv, rv value.Value) (value.BoolValue, bool) { left, _ := value.ToFloat64(lv.Rv()) right, _ := value.ToFloat64(rv.Rv()) if math.IsNaN(left) || math.IsNaN(right) { return value.BoolValueFalse, false } return value.NewBoolValue(left <= right), true }
// < Less Than // Must be able to convert items to Floats or else not ok // func LtFunc(ctx expr.EvalContext, lv, rv value.Value) (value.BoolValue, bool) { left := value.ToFloat64(lv.Rv()) right := value.ToFloat64(rv.Rv()) if left == math.NaN() || right == math.NaN() { return value.BoolValueFalse, false } return value.NewBoolValue(left < right), 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.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 }
// 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 }
func walkIdentity(ctx expr.EvalContext, node *expr.IdentityNode) (value.Value, bool) { if node.IsBooleanIdentity() { //u.Debugf("walkIdentity() boolean: node=%T %v Bool:%v", node, node, node.Bool()) return value.NewBoolValue(node.Bool()), true } if ctx == nil { return value.NewStringValue(node.Text), true } return ctx.Get(node.Text) }
// All: Answers True/False if all of the arguments evaluate to truish (javascripty) // type definintion of true // // int > 0 = true // string != "" = true // boolean natively supported true/false // // // all("hello",2, true) => true // all("hello",0,true) => false // all("",2, true) => false // func AllFunc(ctx expr.EvalContext, vals ...value.Value) (value.BoolValue, bool) { for _, v := range vals { if v.Err() || v.Nil() { return value.NewBoolValue(false), true } else if value.IsNilIsh(v.Rv()) { return value.NewBoolValue(false), true } if nv, ok := v.(value.NumericValue); ok { if iv := nv.Int(); iv < 0 { return value.NewBoolValue(false), true } continue } switch vt := v.(type) { case value.TimeValue: if vt.Val().IsZero() { return value.NewBoolValue(false), true } case value.BoolValue: if vt.Val() == false { return value.NewBoolValue(false), true } } } return value.NewBoolValue(true), true }
func walkUnary(ctx expr.EvalContext, node *expr.UnaryNode) (value.Value, bool) { a, ok := Eval(ctx, node.Arg) if !ok { switch node.Operator.T { case lex.TokenExists: return value.NewBoolValue(false), true case lex.TokenNegate: return value.NewBoolValue(true), true } u.Debugf("unary could not evaluate for[ %s ] and %#v", node.String(), node) return a, false } switch node.Operator.T { case lex.TokenNegate: switch argVal := a.(type) { case value.BoolValue: //u.Debugf("found unary bool: res=%v expr=%v", !argVal.Val(), node) return value.NewBoolValue(!argVal.Val()), true case nil, value.NilValue: return value.NewBoolValue(false), false default: u.LogThrottle(u.WARN, 5, "unary type not implemented. Unknonwn node type: %T:%v node=%s", argVal, argVal, node.String()) return value.NewNilValue(), false } case lex.TokenMinus: if an, aok := a.(value.NumericValue); aok { return value.NewNumberValue(-an.Float()), true } case lex.TokenExists: switch a.(type) { case nil, value.NilValue: return value.NewBoolValue(false), true } if a.Nil() { return value.NewBoolValue(false), true } return value.NewBoolValue(true), true default: u.Warnf("urnary not implemented for type %s %#v", node.Operator.T.String(), node) } return value.NewNilValue(), false }
func (m *Sqlbridge) parseUpdateList() (map[string]*ValueColumn, error) { cols := make(map[string]*ValueColumn) lastColName := "" for { //u.Debugf("col:%v cur:%v", lastColName, m.Cur().String()) switch m.Cur().T { case lex.TokenWhere, lex.TokenLimit, lex.TokenEOS, lex.TokenEOF: return cols, nil case lex.TokenValue: cols[lastColName] = &ValueColumn{Value: value.NewStringValue(m.Cur().V)} case lex.TokenInteger: iv, _ := strconv.ParseInt(m.Cur().V, 10, 64) cols[lastColName] = &ValueColumn{Value: value.NewIntValue(iv)} case lex.TokenComma, lex.TokenEqual: // don't need to do anything case lex.TokenIdentity: // TODO: this is a bug in lexer lv := m.Cur().V if bv, err := strconv.ParseBool(lv); err == nil { cols[lastColName] = &ValueColumn{Value: value.NewBoolValue(bv)} } else { lastColName = m.Cur().V } case lex.TokenUdfExpr: tree := expr.NewTreeFuncs(m.SqlTokenPager, m.funcs) if err := m.parseNode(tree); err != nil { u.Errorf("could not parse: %v", err) return nil, err } cols[lastColName] = &ValueColumn{Expr: tree.Root} default: u.Warnf("don't know how to handle ? %v", m.Cur()) return nil, fmt.Errorf("expected column but got: %v", m.Cur().String()) } m.Next() } panic("unreachable") }
func (m *Sqlbridge) parseValueList() ([][]*ValueColumn, error) { if m.Cur().T != lex.TokenLeftParenthesis { return nil, fmt.Errorf("Expecting opening paren ( but got %v", m.Cur()) } var row []*ValueColumn values := make([][]*ValueColumn, 0) for { //u.Debug(m.Cur().String()) switch m.Cur().T { case lex.TokenLeftParenthesis: // start of row if len(row) > 0 { values = append(values, row) } row = make([]*ValueColumn, 0) case lex.TokenRightParenthesis: values = append(values, row) case lex.TokenFrom, lex.TokenInto, lex.TokenLimit, lex.TokenEOS, lex.TokenEOF: if len(row) > 0 { values = append(values, row) } return values, nil case lex.TokenValue: row = append(row, &ValueColumn{Value: value.NewStringValue(m.Cur().V)}) case lex.TokenInteger: iv, err := strconv.ParseInt(m.Cur().V, 10, 64) if err != nil { return nil, err } row = append(row, &ValueColumn{Value: value.NewIntValue(iv)}) case lex.TokenFloat: fv, err := strconv.ParseFloat(m.Cur().V, 64) if err != nil { return nil, err } row = append(row, &ValueColumn{Value: value.NewNumberValue(fv)}) case lex.TokenBool: bv, err := strconv.ParseBool(m.Cur().V) if err != nil { return nil, err } row = append(row, &ValueColumn{Value: value.NewBoolValue(bv)}) case lex.TokenIdentity: // TODO: this is a bug in lexer lv := m.Cur().V if bv, err := strconv.ParseBool(lv); err == nil { row = append(row, &ValueColumn{Value: value.NewBoolValue(bv)}) } else { // error? u.Warnf("Could not figure out how to use: %v", m.Cur()) } case lex.TokenLeftBracket: // an array of values? m.Next() // Consume the [ arrayVal, err := expr.ValueArray(m.SqlTokenPager) if err != nil { return nil, err } //n := NewValueNode(arrayVal) row = append(row, &ValueColumn{Value: arrayVal}) u.Infof("what is token? %v peek:%v", m.Cur(), m.Peek()) //t.Next() case lex.TokenComma: // don't need to do anything case lex.TokenUdfExpr: tree := expr.NewTreeFuncs(m.SqlTokenPager, m.funcs) if err := m.parseNode(tree); err != nil { u.Errorf("could not parse: %v", err) return nil, err } //col.Expr = tree.Root row = append(row, &ValueColumn{Expr: tree.Root}) default: u.Warnf("don't know how to handle ? %v", m.Cur()) return nil, fmt.Errorf("expected column but got: %v", m.Cur().String()) } m.Next() } panic("unreachable") }
{`match("score_")`, value.NewMapValue(map[string]interface{}{"amount": "22"})}, {`match("score_","tag_")`, value.NewMapValue(map[string]interface{}{"amount": "22", "name": "bob"})}, {`match("nonfield_")`, value.ErrValue}, {`filter(match("score_","tag_"),"nam*")`, value.NewMapValue(map[string]interface{}{"amount": "22"})}, {`filter(match("score_","tag_"),"name")`, value.NewMapValue(map[string]interface{}{"amount": "22"})}, {`filter(split("apples,oranges",","),"ora*")`, value.NewStringsValue([]string{"apples"})}, {`filter(split("apples,oranges",","), ["ora*","notmatch","stuff"] )`, value.NewStringsValue([]string{"apples"})}, {`email("*****@*****.**")`, value.NewStringValue("*****@*****.**")}, {`email("Bob <bob>")`, value.ErrValue}, {`email("Bob <*****@*****.**>")`, value.NewStringValue("*****@*****.**")}, {`oneof(not_a_field, email("Bob <*****@*****.**>"))`, value.NewStringValue("*****@*****.**")}, {`oneof(email, email(not_a_field))`, value.NewStringValue("*****@*****.**")}, {`oneof(email, email(not_a_field)) NOT IN ("a","b",10, 4.5) `, value.NewBoolValue(true)}, {`oneof(email, email(not_a_field)) IN ("*****@*****.**","b",10, 4.5) `, value.NewBoolValue(true)}, {`oneof(email, email(not_a_field)) IN ("b",10, 4.5) `, value.NewBoolValue(false)}, {`emailname("Bob<*****@*****.**>")`, value.NewStringValue("Bob")}, {`emaildomain("Bob<*****@*****.**>")`, value.NewStringValue("gmail.com")}, {`map(event, 22)`, value.NewMapValue(map[string]interface{}{"hello": 22})}, {`map(event, toint(score_amount))`, value.NewMapValue(map[string]interface{}{"hello": 22})}, {`host("https://www.Google.com/search?q=golang")`, value.NewStringValue("www.google.com")}, {`host("www.Google.com/?q=golang")`, value.NewStringValue("www.google.com")}, //{`host("notvalid")`, value.NewStringValue("notvalid")}, {`hosts("www.Google.com/?q=golang", "www.golang.org/")`, value.NewStringsValue([]string{"www.google.com", "www.golang.org"})},
} expr.FuncAdd("eq", Eq) expr.FuncAdd("toint", ToInt) expr.FuncAdd("yy", Yy) } var ( // This is the message context which will be added to all tests below // and be available to the VM runtime for evaluation by using // key's such as "int5" or "user_id" msgContext = datasource.NewContextSimpleData(map[string]value.Value{ "int5": value.NewIntValue(5), "str5": value.NewStringValue("5"), "bvalt": value.NewBoolValue(true), "bvalf": value.NewBoolValue(false), "user_id": value.NewStringValue("abc"), }) // list of tests vmTests = []vmTest{ // Between: Tri Node Tests vmt("tri between ints", `10 BETWEEN 1 AND 50`, true, noError), vmt("tri between ints false", `10 BETWEEN 20 AND 50`, false, noError), vmtall("tri between ints false", `10 BETWEEN 20 AND true`, nil, parseOk, evalError), // In: Multi Arg Tests vmtall("multi-arg: In (x,y,z) ", `10 IN ("a","b",10, 4.5)`, true, parseOk, evalError), vmtall("multi-arg: In (x,y,z) ", `10 IN ("a","b",20, 4.5)`, false, parseOk, evalError), vmtall("multi-arg: In (x,y,z) ", `"a" IN ("a","b",10, 4.5)`, true, parseOk, evalError),
{`all("Linux",true,not_a_realfield)`, value.BoolValueFalse}, {`all("Linux",false)`, value.BoolValueFalse}, {`all("Linux","")`, value.BoolValueFalse}, {`all("Linux",notreal)`, value.BoolValueFalse}, {`match("score_")`, value.NewMapValue(map[string]interface{}{"amount": "22"})}, {`match("score_","tag_")`, value.NewMapValue(map[string]interface{}{"amount": "22", "name": "bob"})}, {`match("nonfield_")`, value.ErrValue}, {`email("*****@*****.**")`, value.NewStringValue("*****@*****.**")}, {`email("Bob <bob>")`, value.ErrValue}, {`email("Bob <*****@*****.**>")`, value.NewStringValue("*****@*****.**")}, {`oneof(not_a_field, email("Bob <*****@*****.**>"))`, value.NewStringValue("*****@*****.**")}, {`oneof(email, email(not_a_field))`, value.NewStringValue("*****@*****.**")}, {`oneof(email, email(not_a_field)) NOT IN ("a","b",10, 4.5) `, value.NewBoolValue(true)}, {`oneof(email, email(not_a_field)) IN ("*****@*****.**","b",10, 4.5) `, value.NewBoolValue(true)}, {`oneof(email, email(not_a_field)) IN ("b",10, 4.5) `, value.NewBoolValue(false)}, {`emailname("Bob<*****@*****.**>")`, value.NewStringValue("Bob")}, {`emaildomain("Bob<*****@*****.**>")`, value.NewStringValue("gmail.com")}, {`map(event, 22)`, value.NewMapValue(map[string]interface{}{"hello": 22})}, {`map(event, toint(score_amount))`, value.NewMapValue(map[string]interface{}{"hello": 22})}, {`host("https://www.Google.com/search?q=golang")`, value.NewStringValue("www.google.com")}, {`host("www.Google.com/?q=golang")`, value.NewStringValue("www.google.com")}, //{`host("notvalid")`, value.NewStringValue("notvalid")}, {`urldecode("hello+world")`, value.NewStringValue("hello world")},
// Equal function? returns true if items are equal // // eq(item,5) func Eq(e *State, itemA, itemB value.Value) (value.BoolValue, bool) { //return BoolValue(itemA == itemB) rvb := value.CoerceTo(itemA.Rv(), itemB.Rv()) //u.Infof("Eq(): a:%T b:%T %v=%v?", itemA, itemB, itemA.Value(), rvb) return value.NewBoolValue(reflect.DeepEqual(itemA.Rv(), rvb)), true }
func walkBinary(ctx expr.EvalContext, node *expr.BinaryNode) value.Value { ar, aok := Eval(ctx, node.Args[0]) br, bok := Eval(ctx, node.Args[1]) if !aok || !bok { u.Warnf("not ok: %v l:%v r:%v %T %T", node, ar, br, ar, br) return nil } //u.Debugf("node.Args: %#v", node.Args) //u.Debugf("walkBinary: %v l:%v r:%v %T %T", node, ar, br, ar, br) 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 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 default: u.Errorf("unknown type: %T %v", bt, bt) panic(ErrUnknownOp) } case value.NumberValue: switch bt := br.(type) { case value.IntValue: n := operateNumbers(node.Operator, at, bt.NumberValue()) return n case value.NumberValue: n := operateNumbers(node.Operator, at, bt) return n default: u.Errorf("unknown type: %T %v", bt, bt) panic(ErrUnknownOp) } 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: return value.NewBoolValue(atv && btv) case lex.TokenLogicOr: return value.NewBoolValue(atv || btv) case lex.TokenEqualEqual, lex.TokenEqual: return value.NewBoolValue(atv == btv) case lex.TokenNE: return value.NewBoolValue(atv != btv) default: u.Infof("bool binary?: %v %v", at, bt) panic(ErrUnknownOp) } default: u.Errorf("at?%T %v coerce?%v bt? %T %v", at, at.Value(), at.CanCoerce(stringRv), bt, bt.Value()) panic(ErrUnknownOp) } case value.StringValue: switch bt := br.(type) { case value.StringValue: // Nice, both strings return operateStrings(node.Operator, at, bt) 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()) case lex.TokenNE: return value.NewBoolValue(value.BoolStringVal(at.Val()) != bt.Val()) } } 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 case value.IntValue: n := operateNumbers(node.Operator, at.NumberValue(), bt.NumberValue()) return n case value.NumberValue: n := operateNumbers(node.Operator, at.NumberValue(), bt) return n default: u.Errorf("at?%T %v coerce?%v bt? %T %v", at, at.Value(), at.CanCoerce(stringRv), bt, bt.Value()) panic(ErrUnknownOp) } } else { u.Errorf("at?%T %v coerce?%v bt? %T %v", at, at.Value(), at.CanCoerce(stringRv), br, br) } } // case nil: // // TODO, remove this case? is this valid? used? // switch bt := br.(type) { // case StringValue: // n := operateNumbers(node.Operator, NumberNaNValue, bt.NumberValue()) // return n // case IntValue: // n := operateNumbers(node.Operator, NumberNaNValue, bt.NumberValue()) // return n // case NumberValue: // n := operateNumbers(node.Operator, NumberNaNValue, bt) // return n // case nil: // u.Errorf("a && b nil? at?%v %v %v", at, bt, node.Operator) // default: // u.Errorf("nil at?%v %T %v", at, bt, node.Operator) // panic(ErrUnknownOp) // } // default: u.Errorf("Unknown op? %T %T %v", ar, at, ar) panic(ErrUnknownOp) } return nil }
// 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 }
func walkFunc(ctx expr.EvalContext, node *expr.FuncNode) (value.Value, bool) { //u.Debugf("walkFunc node: %v", node.StringAST()) // we create a set of arguments to pass to the function, first arg // is this Context var ok bool funcArgs := make([]reflect.Value, 0) if ctx != nil { funcArgs = append(funcArgs, reflect.ValueOf(ctx)) } else { var nilArg expr.EvalContext funcArgs = append(funcArgs, reflect.ValueOf(&nilArg).Elem()) } for _, a := range node.Args { //u.Debugf("arg %v %T %v", a, a, a) var v interface{} switch t := a.(type) { case *expr.StringNode: // String Literal v = value.NewStringValue(t.Text) case *expr.IdentityNode: // Identity node = lookup in context if t.IsBooleanIdentity() { v = value.NewBoolValue(t.Bool()) } else { v, ok = ctx.Get(t.Text) //u.Infof("%#v", ctx.Row()) //u.Debugf("get '%s'? %T %v %v", t.String(), v, v, ok) if !ok { // nil arguments are valid v = value.NewNilValue() } } case *expr.NumberNode: v, ok = numberNodeToValue(t) case *expr.FuncNode: //u.Debugf("descending to %v()", t.Name) v, ok = walkFunc(ctx, t) if !ok { //return value.NewNilValue(), false // nil arguments are valid v = value.NewNilValue() } //u.Debugf("result of %v() = %v, %T", t.Name, v, v) case *expr.UnaryNode: v, ok = walkUnary(ctx, t) if !ok { // nil arguments are valid ?? v = value.NewNilValue() } case *expr.BinaryNode: v, ok = walkBinary(ctx, t) case *expr.ValueNode: v = t.Value default: panic(fmt.Errorf("expr: unknown func arg type")) } if v == nil { //u.Warnf("Nil vals? %v %T arg:%T", v, v, a) // What do we do with Nil Values? switch a.(type) { case *expr.StringNode: // String Literal u.Warnf("NOT IMPLEMENTED T:%T v:%v", a, a) case *expr.IdentityNode: // Identity node = lookup in context v = value.NewStringValue("") default: u.Warnf("un-handled type: %v %T", v, v) } funcArgs = append(funcArgs, reflect.ValueOf(v)) } else { //u.Debugf(`found func arg: "%v" %T arg:%T`, v, v, a) funcArgs = append(funcArgs, reflect.ValueOf(v)) } } // Get the result of calling our Function (Value,bool) //u.Debugf("Calling func:%v(%v) %v", node.F.Name, funcArgs, node.F.F) fnRet := node.F.F.Call(funcArgs) //u.Debugf("fnRet: %v ok?%v", fnRet, fnRet[1].Bool()) // check if has an error response? if len(fnRet) > 1 && !fnRet[1].Bool() { // What do we do if not ok? return value.EmptyStringValue, false } //u.Debugf("response %v %v %T", node.F.Name, fnRet[0].Interface(), fnRet[0].Interface()) return fnRet[0].Interface().(value.Value), true }
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]) if !aok || !bok { // If !aok, but token is a Negate? u.Debugf("walkBinary not ok: op=%s %v l:%v r:%v %T %T", node.Operator, node, ar, br, ar, br) //u.Debugf("node: %s --- %s", node.Args[0], node.Args[1]) // if ar != nil && br != nil { // u.Debugf("not ok: %v l:%v r:%v", node, ar.ToString(), br.ToString()) // } return nil, false } // if ar == nil { // u.Warnf("Wat? %q node0: %#v", node.Args[0], node.Args[0]) // //return nil, false // } // if br == nil { // u.Warnf("wat2 %q node1: %#v", node.Args[1], node.Args[1]) // //return nil, false // } //u.Debugf("node.Args: %#v", node.Args) //u.Debugf("walkBinary: %v l:%v r:%v %T %T", node, ar, br, ar, br) 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.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 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 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: 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, true } 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()) } 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.Warnf("unsupported op: %v", node.Operator) 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.Warnf("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 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 default: u.Debugf("left side nil binary: %q", node) return nil, true } 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 }