Implement uint64 logic into eval

Any operation between an int64 and uint64 results in the type becoming a
uint64. This is because the most common will usually be an unsigned
cursor being modified by an int literal. Even in cases where we were to
add an unsigned literal to an integer iterator, that would just result
in it being recast back and overflow.
pull/8884/head
Jonathan A. Sternberg 2017-09-19 12:41:10 -05:00
parent 963cbfd275
commit f0fb0174b6
2 changed files with 228 additions and 45 deletions

View File

@ -3820,6 +3820,8 @@ func Eval(expr Expr, m map[string]interface{}) interface{} {
return expr.Val
case *NumberLiteral:
return expr.Val
case *UnsignedLiteral:
return expr.Val
case *ParenExpr:
return Eval(expr.Expr, m)
case *RegexLiteral:
@ -3870,12 +3872,14 @@ func evalBinaryExpr(expr *BinaryExpr, m map[string]interface{}) interface{} {
return ok && (lhs != rhs)
}
case float64:
// Try the rhs as a float64 or int64
// Try the rhs as a float64, int64, or uint64
rhsf, ok := rhs.(float64)
if !ok {
var rhsi int64
if rhsi, ok = rhs.(int64); ok {
rhsf = float64(rhsi)
switch val := rhs.(type) {
case int64:
rhsf, ok = float64(val), true
case uint64:
rhsf, ok = float64(val), true
}
}
@ -3923,10 +3927,9 @@ func evalBinaryExpr(expr *BinaryExpr, m map[string]interface{}) interface{} {
}
case int64:
// Try as a float64 to see if a float cast is required.
rhsf, ok := rhs.(float64)
if ok {
switch rhs := rhs.(type) {
case float64:
lhs := float64(lhs)
rhs := rhsf
switch expr.Op {
case EQ:
return lhs == rhs
@ -3954,64 +3957,209 @@ func evalBinaryExpr(expr *BinaryExpr, m map[string]interface{}) interface{} {
case MOD:
return math.Mod(lhs, rhs)
}
} else {
rhs, ok := rhs.(int64)
case int64:
switch expr.Op {
case EQ:
return ok && (lhs == rhs)
return lhs == rhs
case NEQ:
return ok && (lhs != rhs)
return lhs != rhs
case LT:
return ok && (lhs < rhs)
return lhs < rhs
case LTE:
return ok && (lhs <= rhs)
return lhs <= rhs
case GT:
return ok && (lhs > rhs)
return lhs > rhs
case GTE:
return ok && (lhs >= rhs)
return lhs >= rhs
case ADD:
if !ok {
return nil
}
return lhs + rhs
case SUB:
if !ok {
return nil
}
return lhs - rhs
case MUL:
if !ok {
return nil
}
return lhs * rhs
case DIV:
if !ok {
return nil
} else if rhs == 0 {
if rhs == 0 {
return float64(0)
}
return lhs / rhs
case MOD:
if !ok {
return nil
} else if rhs == 0 {
if rhs == 0 {
return int64(0)
}
return lhs % rhs
case BITWISE_AND:
if !ok {
return nil
}
return lhs & rhs
case BITWISE_OR:
if !ok {
return nil
}
return lhs | rhs
case BITWISE_XOR:
if !ok {
return nil
return lhs ^ rhs
}
case uint64:
switch expr.Op {
case EQ:
return uint64(lhs) == rhs
case NEQ:
return uint64(lhs) != rhs
case LT:
if lhs < 0 {
return true
}
return uint64(lhs) < rhs
case LTE:
if lhs < 0 {
return true
}
return uint64(lhs) <= rhs
case GT:
if lhs < 0 {
return false
}
return uint64(lhs) > rhs
case GTE:
if lhs < 0 {
return false
}
return uint64(lhs) >= rhs
case ADD:
return uint64(lhs) + rhs
case SUB:
return uint64(lhs) - rhs
case MUL:
return uint64(lhs) * rhs
case DIV:
if rhs == 0 {
return uint64(0)
}
return uint64(lhs) / rhs
case MOD:
if rhs == 0 {
return uint64(0)
}
return uint64(lhs) % rhs
case BITWISE_AND:
return uint64(lhs) & rhs
case BITWISE_OR:
return uint64(lhs) | rhs
case BITWISE_XOR:
return uint64(lhs) ^ rhs
}
}
case uint64:
// Try as a float64 to see if a float cast is required.
switch rhs := rhs.(type) {
case float64:
lhs := float64(lhs)
switch expr.Op {
case EQ:
return lhs == rhs
case NEQ:
return lhs != rhs
case LT:
return lhs < rhs
case LTE:
return lhs <= rhs
case GT:
return lhs > rhs
case GTE:
return lhs >= rhs
case ADD:
return lhs + rhs
case SUB:
return lhs - rhs
case MUL:
return lhs * rhs
case DIV:
if rhs == 0 {
return float64(0)
}
return lhs / rhs
case MOD:
return math.Mod(lhs, rhs)
}
case int64:
switch expr.Op {
case EQ:
return lhs == uint64(rhs)
case NEQ:
return lhs != uint64(rhs)
case LT:
if rhs < 0 {
return false
}
return lhs < uint64(rhs)
case LTE:
if rhs < 0 {
return false
}
return lhs <= uint64(rhs)
case GT:
if rhs < 0 {
return true
}
return lhs > uint64(rhs)
case GTE:
if rhs < 0 {
return true
}
return lhs >= uint64(rhs)
case ADD:
return lhs + uint64(rhs)
case SUB:
return lhs - uint64(rhs)
case MUL:
return lhs * uint64(rhs)
case DIV:
if rhs == 0 {
return uint64(0)
}
return lhs / uint64(rhs)
case MOD:
if rhs == 0 {
return uint64(0)
}
return lhs % uint64(rhs)
case BITWISE_AND:
return lhs & uint64(rhs)
case BITWISE_OR:
return lhs | uint64(rhs)
case BITWISE_XOR:
return lhs ^ uint64(rhs)
}
case uint64:
switch expr.Op {
case EQ:
return lhs == rhs
case NEQ:
return lhs != rhs
case LT:
return lhs < rhs
case LTE:
return lhs <= rhs
case GT:
return lhs > rhs
case GTE:
return lhs >= rhs
case ADD:
return lhs + rhs
case SUB:
return lhs - rhs
case MUL:
return lhs * rhs
case DIV:
if rhs == 0 {
return uint64(0)
}
return lhs / rhs
case MOD:
if rhs == 0 {
return uint64(0)
}
return lhs % rhs
case BITWISE_AND:
return lhs & rhs
case BITWISE_OR:
return lhs | rhs
case BITWISE_XOR:
return lhs ^ rhs
}
}
@ -4020,29 +4168,36 @@ func evalBinaryExpr(expr *BinaryExpr, m map[string]interface{}) interface{} {
case EQ:
rhs, ok := rhs.(string)
if !ok {
return nil
return false
}
return lhs == rhs
case NEQ:
rhs, ok := rhs.(string)
if !ok {
return nil
return false
}
return lhs != rhs
case EQREGEX:
rhs, ok := rhs.(*regexp.Regexp)
if !ok {
return nil
return false
}
return rhs.MatchString(lhs)
case NEQREGEX:
rhs, ok := rhs.(*regexp.Regexp)
if !ok {
return nil
return false
}
return !rhs.MatchString(lhs)
}
}
// The types were not comparable. If our operation was an equality operation,
// return false instead of true.
switch expr.Op {
case EQ, NEQ, LT, LTE, GT, GTE:
return false
}
return nil
}

View File

@ -995,6 +995,34 @@ func TestEval(t *testing.T) {
{in: `0 = 'test'`, out: false},
{in: `1.0 = 1`, out: true},
{in: `1.2 = 1`, out: false},
{in: `-1 = 9223372036854775808`, out: false},
{in: `-1 != 9223372036854775808`, out: true},
{in: `-1 < 9223372036854775808`, out: true},
{in: `-1 <= 9223372036854775808`, out: true},
{in: `-1 > 9223372036854775808`, out: false},
{in: `-1 >= 9223372036854775808`, out: false},
{in: `9223372036854775808 = -1`, out: false},
{in: `9223372036854775808 != -1`, out: true},
{in: `9223372036854775808 < -1`, out: false},
{in: `9223372036854775808 <= -1`, out: false},
{in: `9223372036854775808 > -1`, out: true},
{in: `9223372036854775808 >= -1`, out: true},
{in: `9223372036854775808 = 9223372036854775808`, out: true},
{in: `9223372036854775808 != 9223372036854775808`, out: false},
{in: `9223372036854775808 < 9223372036854775808`, out: false},
{in: `9223372036854775808 <= 9223372036854775808`, out: true},
{in: `9223372036854775808 > 9223372036854775808`, out: false},
{in: `9223372036854775808 >= 9223372036854775808`, out: true},
{in: `9223372036854775809 = 9223372036854775808`, out: false},
{in: `9223372036854775809 != 9223372036854775808`, out: true},
{in: `9223372036854775809 < 9223372036854775808`, out: false},
{in: `9223372036854775809 <= 9223372036854775808`, out: false},
{in: `9223372036854775809 > 9223372036854775808`, out: true},
{in: `9223372036854775809 >= 9223372036854775808`, out: true},
{in: `9223372036854775808 / 0`, out: uint64(0)},
{in: `9223372036854775808 + 1`, out: uint64(9223372036854775809)},
{in: `9223372036854775808 - 1`, out: uint64(9223372036854775807)},
{in: `9223372036854775809 - 9223372036854775808`, out: uint64(1)},
// Boolean literals.
{in: `true AND false`, out: false},
@ -1004,7 +1032,7 @@ func TestEval(t *testing.T) {
// String literals.
{in: `'foo' = 'bar'`, out: false},
{in: `'foo' = 'foo'`, out: true},
{in: `'' = 4`, out: nil},
{in: `'' = 4`, out: false},
// Regex literals.
{in: `'foo' =~ /f.*/`, out: true},
@ -1015,8 +1043,8 @@ func TestEval(t *testing.T) {
// Variable references.
{in: `foo`, out: "bar", data: map[string]interface{}{"foo": "bar"}},
{in: `foo = 'bar'`, out: true, data: map[string]interface{}{"foo": "bar"}},
{in: `foo = 'bar'`, out: nil, data: map[string]interface{}{"foo": nil}},
{in: `'bar' = foo`, out: nil, data: map[string]interface{}{"foo": nil}},
{in: `foo = 'bar'`, out: false, data: map[string]interface{}{"foo": nil}},
{in: `'bar' = foo`, out: false, data: map[string]interface{}{"foo": nil}},
{in: `foo <> 'bar'`, out: true, data: map[string]interface{}{"foo": "xxx"}},
{in: `foo =~ /b.*/`, out: true, data: map[string]interface{}{"foo": "bar"}},
{in: `foo !~ /b.*/`, out: false, data: map[string]interface{}{"foo": "bar"}},