diff options
Diffstat (limited to 'core/vm/sqlvm')
-rw-r--r-- | core/vm/sqlvm/cmd/gen-op-test/main.go | 21 | ||||
-rw-r--r-- | core/vm/sqlvm/common/context.go | 7 | ||||
-rw-r--r-- | core/vm/sqlvm/common/decimal/decimal.go | 5 | ||||
-rw-r--r-- | core/vm/sqlvm/errors/errors.go | 8 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/instructions.go | 1526 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/instructions_op_test.go | 3110 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/instructions_test.go | 61 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/instructions_tmpl.go | 82 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/instructions_tmpl_data.go | 2934 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/jumptable.go | 26 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/opcodes.go | 95 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/runtime.go | 25 |
12 files changed, 7784 insertions, 116 deletions
diff --git a/core/vm/sqlvm/cmd/gen-op-test/main.go b/core/vm/sqlvm/cmd/gen-op-test/main.go new file mode 100644 index 000000000..60387dddf --- /dev/null +++ b/core/vm/sqlvm/cmd/gen-op-test/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "flag" + + "github.com/dexon-foundation/dexon/core/vm/sqlvm/runtime" +) + +func main() { + var output string + flag.StringVar( + &output, "o", "./runtime/instructions_op_test.go", + "the output path of generated testcases", + ) + flag.Parse() + + err := runtime.RenderOpTest(output) + if err != nil { + panic(err) + } +} diff --git a/core/vm/sqlvm/common/context.go b/core/vm/sqlvm/common/context.go index 1985473fa..56ad89ded 100644 --- a/core/vm/sqlvm/common/context.go +++ b/core/vm/sqlvm/common/context.go @@ -2,10 +2,17 @@ package common import "github.com/dexon-foundation/dexon/core/vm" +// Option is collection of SQL options. +type Option struct { + SafeMath bool +} + // Context holds SQL VM required params. type Context struct { vm.Context Storage Storage Contract *vm.Contract + + Opt Option } diff --git a/core/vm/sqlvm/common/decimal/decimal.go b/core/vm/sqlvm/common/decimal/decimal.go index 445256cf5..2b9993b02 100644 --- a/core/vm/sqlvm/common/decimal/decimal.go +++ b/core/vm/sqlvm/common/decimal/decimal.go @@ -6,4 +6,9 @@ import "github.com/dexon-foundation/decimal" var ( False = decimal.New(0, 0) True = decimal.New(1, 0) + + Int64Max = decimal.New(1, 63).Sub(decimal.One) + Int64Min = decimal.New(1, 63).Neg() + + UInt16Max = decimal.New(1, 16).Sub(decimal.One) ) diff --git a/core/vm/sqlvm/errors/errors.go b/core/vm/sqlvm/errors/errors.go index ad00e5bb0..9a22bf453 100644 --- a/core/vm/sqlvm/errors/errors.go +++ b/core/vm/sqlvm/errors/errors.go @@ -116,6 +116,7 @@ const ( // Planner Error ErrorCodePlanner // Runtime Error + ErrorCodeInvalidOperandNum ErrorCodeInvalidDataType ErrorCodeOverflow ErrorCodeUnderflow @@ -123,6 +124,9 @@ const ( ErrorCodeInvalidCastType ErrorCodeDividedByZero ErrorCodeNegDecimalToUint64 + ErrorCodeDataLengthNotMatch + ErrorCodeMultipleEscapeByte + ErrorCodePendingEscapeByte ) var errorCodeMap = [...]string{ @@ -149,6 +153,7 @@ var errorCodeMap = [...]string{ // Planner Error ErrorCodePlanner: "planner failure", // TODO: fix the message. // Runtime Error + ErrorCodeInvalidOperandNum: "invalid operand number", ErrorCodeInvalidDataType: "invalid data type", ErrorCodeOverflow: "overflow", ErrorCodeUnderflow: "underflow", @@ -156,6 +161,9 @@ var errorCodeMap = [...]string{ ErrorCodeInvalidCastType: "invalid cast type", ErrorCodeDividedByZero: "divide by zero", ErrorCodeNegDecimalToUint64: "negative deciaml to uint64", + ErrorCodeDataLengthNotMatch: "data length not match", + ErrorCodeMultipleEscapeByte: "multiple escape byte", + ErrorCodePendingEscapeByte: "pending escape byte", } func (c ErrorCode) Error() string { diff --git a/core/vm/sqlvm/runtime/instructions.go b/core/vm/sqlvm/runtime/instructions.go index d1bfa8a17..bb0e8a425 100644 --- a/core/vm/sqlvm/runtime/instructions.go +++ b/core/vm/sqlvm/runtime/instructions.go @@ -1,7 +1,11 @@ package runtime import ( + "bytes" + "errors" "fmt" + "regexp" + "sort" "strings" "github.com/dexon-foundation/decimal" @@ -9,21 +13,31 @@ import ( dexCommon "github.com/dexon-foundation/dexon/common" "github.com/dexon-foundation/dexon/core/vm/sqlvm/ast" "github.com/dexon-foundation/dexon/core/vm/sqlvm/common" - "github.com/dexon-foundation/dexon/core/vm/sqlvm/errors" + dec "github.com/dexon-foundation/dexon/core/vm/sqlvm/common/decimal" + se "github.com/dexon-foundation/dexon/core/vm/sqlvm/errors" ) -var tupleJoin = "|" +var ( + byteLikeP = byte('%') + byteLikeU = byte('_') + byteDot = byte('.') + bytesLikeReg = []byte{'.', '*', '?'} + bytesStart = []byte{'^'} + bytesEnd = []byte{'$'} + + tupleJoin = "|" +) // OpFunction type // data could be fields Fields, pattern []byte, order Orders -type OpFunction func(ctx *common.Context, ops []*Operand, registers []*Operand, output int) error +type OpFunction func(ctx *common.Context, ops, registers []*Operand, output uint) error // Instruction represents single instruction with essential information // collection. type Instruction struct { Op OpCode Input []*Operand - Output int + Output uint Position uint32 // ast tree position } @@ -46,7 +60,7 @@ func (t Tuple) String() string { for i := 0; i < len(t); i++ { rawStr = append(rawStr, t[i].String()) } - return strings.Join(rawStr, tupleJoin) + return fmt.Sprintf("\n%v\n", strings.Join(rawStr, tupleJoin)) } // Operand would be array-based value associated with meta to describe type of @@ -58,9 +72,9 @@ type Operand struct { RegisterIndex uint } -func (o *Operand) toUint64() (result []uint64, err error) { - result = make([]uint64, len(o.Data)) - for i, tuple := range o.Data { +func (op *Operand) toUint64() (result []uint64, err error) { + result = make([]uint64, len(op.Data)) + for i, tuple := range op.Data { result[i], err = ast.DecimalToUint64(tuple[0].Value) if err != nil { return @@ -69,9 +83,9 @@ func (o *Operand) toUint64() (result []uint64, err error) { return } -func (o *Operand) toUint8() ([]uint8, error) { - result := make([]uint8, len(o.Data)) - for i, tuple := range o.Data { +func (op *Operand) toUint8() ([]uint8, error) { + result := make([]uint8, len(op.Data)) + for i, tuple := range op.Data { u, err := ast.DecimalToUint64(tuple[0].Value) if err != nil { return nil, err @@ -81,10 +95,10 @@ func (o *Operand) toUint8() ([]uint8, error) { return result, nil } -func opLoad(ctx *common.Context, input []*Operand, registers []*Operand, output int) error { +func opLoad(ctx *common.Context, input []*Operand, registers []*Operand, output uint) error { tableIdx := input[0].Data[0][0].Value.IntPart() if tableIdx >= int64(len(ctx.Storage.Schema)) { - return errors.ErrorCodeIndexOutOfRange + return se.ErrorCodeIndexOutOfRange } table := ctx.Storage.Schema[tableIdx] @@ -164,3 +178,1489 @@ func decode(ctx *common.Context, dt ast.DataType, slot dexCommon.Hash, bytes []b } return rVal, nil } + +func (op *Operand) clone(metaOnly bool) (op2 *Operand) { + op2 = &Operand{ + Meta: op.cloneMeta(), + // skip RegisterIndex since is only used when loading + // skip IsImmediate flag since is only set by codegen + } + + if metaOnly { + return + } + + op2.Data = make([]Tuple, len(op.Data)) + for i := 0; i < len(op.Data); i++ { + op2.Data[i] = append(Tuple{}, op.Data[i]...) + } + return +} + +func (op *Operand) cloneMeta() (meta []ast.DataType) { + meta = make([]ast.DataType, len(op.Meta)) + copy(meta, op.Meta) + return +} + +// Equal compares underlying data level-by-level. +func (op *Operand) Equal(op2 *Operand) (equal bool) { + if op2 == nil { + return + } + + equal = op.IsImmediate == op2.IsImmediate + if !equal { + return + } + + equal = metaAllEq(op, op2) + if !equal { + return + } + + equal = len(op.Data) == len(op2.Data) + if !equal { + return + } + + for i := 0; i < len(op.Data); i++ { + equal = op.Data[i].Equal(op2.Data[i], op.Meta) + if !equal { + return + } + } + return +} + +// Equal compares tuple values one by one. +func (t Tuple) Equal(t2 Tuple, meta []ast.DataType) (equal bool) { + equal = len(t) == len(t2) + if !equal { + return + } + + for i := 0; i < len(t); i++ { + equal = t[i].Equal(t2[i], meta[i]) + if !equal { + return + } + } + return +} + +// Equal compares raw by type. +func (r *Raw) Equal(r2 *Raw, dType ast.DataType) (equal bool) { + major, _ := ast.DecomposeDataType(dType) + switch major { + case ast.DataTypeMajorDynamicBytes, + ast.DataTypeMajorFixedBytes, + ast.DataTypeMajorAddress: + equal = bytes.Equal(r.Bytes, r2.Bytes) + default: + equal = r.Value.Cmp(r2.Value) == 0 + } + return +} + +var ( + rawFalse = &Raw{Value: dec.False} + rawTrue = &Raw{Value: dec.True} +) + +func metaAllEq(op1, op2 *Operand) bool { + if len(op1.Meta) != len(op2.Meta) { + return false + } + + for i := 0; i < len(op1.Meta); i++ { + if op1.Meta[i] != op2.Meta[i] { + return false + } + } + return true +} + +func metaAll(op *Operand, fn func(ast.DataType) bool) bool { + for i := 0; i < len(op.Meta); i++ { + if !fn(op.Meta[i]) { + return false + } + } + return true +} + +func metaBool(dType ast.DataType) bool { + dMajor, _ := ast.DecomposeDataType(dType) + return dMajor == ast.DataTypeMajorBool +} + +func metaAllBool(op *Operand) bool { return metaAll(op, metaBool) } + +func metaArith(dType ast.DataType) bool { + major, _ := ast.DecomposeDataType(dType) + if major == ast.DataTypeMajorInt || + major == ast.DataTypeMajorUint { + return true + } + return false +} + +func metaAllArith(op *Operand) bool { return metaAll(op, metaArith) } + +func findMaxDataLength(ops []*Operand) (l int, err error) { + l = -1 + + for i := 0; i < len(ops); i++ { + if ops[i].IsImmediate { + continue + } + + if l == -1 { + l = len(ops[i].Data) + } + + if len(ops[i].Data) != l { + err = se.ErrorCodeDataLengthNotMatch + return + } + } + + if l == -1 { + l = 1 + } + return +} + +func bool2Raw(b bool) (r *Raw) { + if b { + r = rawTrue + } else { + r = rawFalse + } + return +} + +func value2ColIdx(v decimal.Decimal) (idx uint16) { + if v.GreaterThan(dec.UInt16Max) { + panic(errors.New("field index greater than uint16 max")) + } else if v.LessThan(decimal.Zero) { + panic(errors.New("field index less than 0")) + } + + idx = uint16(v.IntPart()) + return +} + +func flowCheck(ctx *common.Context, v decimal.Decimal, dType ast.DataType) (err error) { + if !ctx.Opt.SafeMath { + return + } + + min, max, ok := dType.GetMinMax() + if !ok { + err = se.ErrorCodeInvalidDataType + return + } + + if v.Cmp(max) > 0 { + err = se.ErrorCodeOverflow + } else if v.Cmp(min) < 0 { + err = se.ErrorCodeUnderflow + } + return +} + +func opAdd(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) || !metaAllArith(op1) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + + raw, iErr := op1.Data[j].add(ctx, op2.Data[k], op1.Meta) + if iErr != nil { + err = iErr + return + } + data[i] = raw + } + + registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data} + return +} + +func (t Tuple) add(ctx *common.Context, t2 Tuple, meta []ast.DataType) (t3 Tuple, err error) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + raw := t[i].add(t2[i]) + err = flowCheck(ctx, raw.Value, meta[i]) + if err != nil { + return + } + t3[i] = raw + } + return +} + +func (r *Raw) add(r2 *Raw) (r3 *Raw) { + r3 = &Raw{Value: r.Value.Add(r2.Value)} + return +} + +func opMul(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) || !metaAllArith(op1) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + + raw, iErr := op1.Data[j].mul(ctx, op2.Data[k], op1.Meta) + if iErr != nil { + err = iErr + return + } + data[i] = raw + } + + registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data} + return +} + +func (t Tuple) mul(ctx *common.Context, t2 Tuple, meta []ast.DataType) (t3 Tuple, err error) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + raw := t[i].mul(t2[i]) + err = flowCheck(ctx, raw.Value, meta[i]) + if err != nil { + return + } + + t3[i] = raw + } + return +} + +func (r *Raw) mul(r2 *Raw) (r3 *Raw) { + r3 = &Raw{Value: r.Value.Mul(r2.Value)} + return +} + +func opSub(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) || !metaAllArith(op1) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + + raw, iErr := op1.Data[j].sub(ctx, op2.Data[k], op1.Meta) + if iErr != nil { + err = iErr + return + } + data[i] = raw + } + + registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data} + return +} + +func (t Tuple) sub(ctx *common.Context, t2 Tuple, meta []ast.DataType) (t3 Tuple, err error) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + raw := t[i].sub(t2[i]) + err = flowCheck(ctx, raw.Value, meta[i]) + if err != nil { + return + } + + t3[i] = raw + } + return +} + +func (r *Raw) sub(r2 *Raw) (r3 *Raw) { + r3 = &Raw{Value: r.Value.Sub(r2.Value)} + return +} + +func opDiv(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) || !metaAllArith(op1) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + raw, iErr := op1.Data[j].div(ctx, op2.Data[k], op1.Meta) + if iErr != nil { + err = iErr + return + } + data[i] = raw + } + + registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data} + return +} + +func (t Tuple) div(ctx *common.Context, t2 Tuple, meta []ast.DataType) (t3 Tuple, err error) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + raw, iErr := t[i].div(t2[i]) + if iErr != nil { + err = iErr + return + } + + iErr = flowCheck(ctx, raw.Value, meta[i]) + if iErr != nil { + err = iErr + return + } + + t3[i] = raw + } + return +} + +func (r *Raw) div(r2 *Raw) (r3 *Raw, err error) { + if r2.Value.IsZero() { + err = se.ErrorCodeDividedByZero + return + } + + q, _ := r.Value.QuoRem(r2.Value, 0) + + r3 = &Raw{Value: q} + return +} + +func opMod(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) || !metaAllArith(op1) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + raw, iErr := op1.Data[j].mod(ctx, op2.Data[k], op1.Meta) + if iErr != nil { + err = iErr + return + } + data[i] = raw + } + + registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data} + return +} + +func (t Tuple) mod(ctx *common.Context, t2 Tuple, meta []ast.DataType) (t3 Tuple, err error) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + raw, iErr := t[i].mod(t2[i]) + if iErr != nil { + err = iErr + return + + } + t3[i] = raw + } + return +} + +func (r *Raw) mod(r2 *Raw) (r3 *Raw, err error) { + if r2.Value.IsZero() { + err = se.ErrorCodeDividedByZero + return + } + + _, qr := r.Value.QuoRem(r2.Value, 0) + + r3 = &Raw{Value: qr} + return +} + +func opLt(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + data[i] = op1.Data[j].lt(op2.Data[k]) + } + + boolType := ast.ComposeDataType(ast.DataTypeMajorBool, 0) + meta := make([]ast.DataType, len(op1.Meta)) + for i := 0; i < len(op1.Meta); i++ { + meta[i] = boolType + } + + registers[output] = &Operand{Meta: meta, Data: data} + return +} + +func (t Tuple) lt(t2 Tuple) (t3 Tuple) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + t3[i] = bool2Raw(t[i].lt(t2[i])) + } + return +} + +func (r *Raw) lt(r2 *Raw) (lt bool) { + if r.Bytes == nil { + lt = r.Value.Cmp(r2.Value) < 0 + } else { + lt = bytes.Compare(r.Bytes, r2.Bytes) < 0 + } + return +} + +func opGt(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + data[i] = op1.Data[j].gt(op2.Data[k]) + } + + boolType := ast.ComposeDataType(ast.DataTypeMajorBool, 0) + meta := make([]ast.DataType, len(op1.Meta)) + for i := 0; i < len(op1.Meta); i++ { + meta[i] = boolType + } + + registers[output] = &Operand{Meta: meta, Data: data} + return +} + +func (t Tuple) gt(t2 Tuple) (t3 Tuple) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + t3[i] = bool2Raw(t[i].gt(t2[i])) + } + return +} + +func (r *Raw) gt(r2 *Raw) (gt bool) { + if r.Bytes == nil { + gt = r.Value.Cmp(r2.Value) > 0 + } else { + gt = bytes.Compare(r.Bytes, r2.Bytes) > 0 + } + return +} + +func opEq(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + data[i] = op1.Data[j].eq(op2.Data[k], op1.Meta) + } + + boolType := ast.ComposeDataType(ast.DataTypeMajorBool, 0) + meta := make([]ast.DataType, len(op1.Meta)) + for i := 0; i < len(op1.Meta); i++ { + meta[i] = boolType + } + + registers[output] = &Operand{Meta: meta, Data: data} + return +} + +func (t Tuple) eq(t2 Tuple, meta []ast.DataType) (t3 Tuple) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + t3[i] = bool2Raw(t[i].Equal(t2[i], meta[i])) + } + return +} + +func opAnd(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) { + err = se.ErrorCodeInvalidDataType + return + } + + if !metaAllBool(op1) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + data[i] = op1.Data[j].and(op2.Data[k]) + } + + boolType := ast.ComposeDataType(ast.DataTypeMajorBool, 0) + meta := make([]ast.DataType, l) + for i := 0; i < l; i++ { + meta[i] = boolType + } + + registers[output] = &Operand{Meta: meta, Data: data} + return +} + +func (t Tuple) and(t2 Tuple) (t3 Tuple) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + t3[i] = t[i].and(t2[i]) + } + return +} + +func (r *Raw) and(r2 *Raw) (r3 *Raw) { + r3 = bool2Raw(r.Value.Equal(dec.True) && r2.Value.Equal(dec.True)) + return +} + +func opOr(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) { + err = se.ErrorCodeInvalidDataType + return + } + + if !metaAllBool(op1) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + data[i] = op1.Data[j].or(op2.Data[k]) + } + + boolType := ast.ComposeDataType(ast.DataTypeMajorBool, 0) + meta := make([]ast.DataType, l) + for i := 0; i < l; i++ { + meta[i] = boolType + } + + registers[output] = &Operand{Meta: meta, Data: data} + return +} + +func (t Tuple) or(t2 Tuple) (t3 Tuple) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + t3[i] = t[i].or(t2[i]) + } + return +} + +func (r *Raw) or(r2 *Raw) (r3 *Raw) { + r3 = bool2Raw(r.Value.Equal(dec.True) || r2.Value.Equal(dec.True)) + return +} + +func opNot(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 1 { + err = se.ErrorCodeInvalidOperandNum + return + } + op := ops[0] + + if !metaAllBool(op) { + err = se.ErrorCodeInvalidDataType + return + } + + data := make([]Tuple, len(op.Data)) + for i := 0; i < len(op.Data); i++ { + data[i] = op.Data[i].not() + } + + registers[output] = &Operand{Meta: op.cloneMeta(), Data: data} + return +} + +func (t Tuple) not() (t2 Tuple) { + t2 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + t2[i] = t[i].not() + } + return +} + +func (r *Raw) not() (r2 *Raw) { + r2 = bool2Raw(r.Value.IsZero()) + return +} + +func opUnion(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) { + err = se.ErrorCodeInvalidDataType + return + } + + if len(op1.Data) > len(op2.Data) { + op1, op2 = op2, op1 + } + + tmpMap := make(map[string]struct{}) + for i := 0; i < len(op1.Data); i++ { + tmpMap[op1.Data[i].String()] = struct{}{} + } + + op3 := op1.clone(false) + + for i := 0; i < len(op2.Data); i++ { + if _, ok := tmpMap[op2.Data[i].String()]; !ok { + op3.Data = append(op3.Data, append(Tuple{}, op2.Data[i]...)) + } + } + + orders := make([]sortOption, len(op3.Meta)) + for i := 0; i < len(orders); i++ { + orders[i] = sortOption{Asc: true, Field: uint(i)} + } + + sort.SliceStable( + op3.Data, + func(i, j int) bool { return op3.Data[i].less(op3.Data[j], orders) }, + ) + + registers[output] = op3 + return +} + +func opIntxn(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) { + err = se.ErrorCodeInvalidDataType + return + } + + if len(op1.Data) > len(op2.Data) { + op1, op2 = op2, op1 + } + + tmpMap := make(map[string]struct{}) + for i := 0; i < len(op1.Data); i++ { + tmpMap[op1.Data[i].String()] = struct{}{} + } + + op3 := &Operand{Meta: op1.cloneMeta(), Data: []Tuple{}} + + for i := 0; i < len(op2.Data); i++ { + if _, ok := tmpMap[op2.Data[i].String()]; ok { + op3.Data = append(op3.Data, append(Tuple{}, op2.Data[i]...)) + } + } + + orders := make([]sortOption, len(op3.Meta)) + for i := 0; i < len(orders); i++ { + orders[i] = sortOption{Asc: true, Field: uint(i)} + } + + sort.SliceStable( + op3.Data, + func(i, j int) bool { return op3.Data[i].less(op3.Data[j], orders) }, + ) + + registers[output] = op3 + return +} + +func opLike(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 && len(ops) != 3 { + err = se.ErrorCodeInvalidOperandNum + return + } + op, pattern := ops[0], ops[1] + + var escape *Operand + if len(ops) > 2 { + escape = ops[2] + } + + var cReg *regexp.Regexp + + matchWithI := pattern.IsImmediate && (escape == nil || escape.IsImmediate) + if matchWithI { + var escapeBytes []byte + if escape != nil && len(escape.Data) > 0 { + escapeBytes = escape.Data[0][0].Bytes + } + + if len(escapeBytes) > 1 { + err = se.ErrorCodeMultipleEscapeByte + return + } + + cReg, err = like2regexp(pattern.Data[0][0].Bytes, escapeBytes) + if err != nil { + return + } + + } + + data := make([]Tuple, len(op.Data)) + if matchWithI { + for i := 0; i < len(op.Data); i++ { + raw, iErr := op.Data[i].like(cReg) + if iErr != nil { + err = iErr + return + } + data[i] = raw + } + } else { + var ( + pat []byte + esc []byte + ) + + for i := 0; i < len(op.Data); i++ { + if pattern.IsImmediate { + pat = pattern.Data[0][0].Bytes + } else { + pat = pattern.Data[i][0].Bytes + } + + if escape != nil { + if escape.IsImmediate { + esc = escape.Data[0][0].Bytes + } else { + esc = escape.Data[i][0].Bytes + } + } else { + esc = []byte{} + } + + if len(esc) > 1 { + err = se.ErrorCodeMultipleEscapeByte + return + } + + reg, iErr := like2regexp(pat, esc) + if iErr != nil { + err = iErr + return + } + + raw, iErr := op.Data[i].like(reg) + if iErr != nil { + err = iErr + return + } + + data[i] = raw + } + } + + boolType := ast.ComposeDataType(ast.DataTypeMajorBool, 0) + meta := make([]ast.DataType, len(op.Meta)) + for i := 0; i < len(meta); i++ { + meta[i] = boolType + } + + registers[output] = &Operand{Meta: meta, Data: data} + return +} + +// check parser/parser.go comment for string encoding +func encB(b []byte) []byte { + encBuf := bytes.Buffer{} + for _, c := range b { + encBuf.WriteRune(rune(c)) + } + return encBuf.Bytes() +} + +func writeC2Buf(buf *bytes.Buffer, c byte) { + if c < 0x80 { + // quote for valid ascii + buf.WriteString(regexp.QuoteMeta(string(c))) + } else { + buf.WriteRune(rune(c)) + } +} + +func like2regexp(pattern []byte, escape []byte) (reg *regexp.Regexp, err error) { + var ( + buf = &bytes.Buffer{} + escMode = len(escape) > 0 + isEsc = false + c byte + ) + + for i := 0; i < len(pattern); i++ { + c = pattern[i] + + if escMode && !isEsc && c == escape[0] { + isEsc = true + continue + } + + if escMode && isEsc { + isEsc = false + writeC2Buf(buf, c) + continue + } + + switch c { + case byteLikeP: + buf.Write(bytesLikeReg) + case byteLikeU: + buf.WriteByte(byteDot) + default: + writeC2Buf(buf, c) + } + } + + if isEsc { + err = se.ErrorCodePendingEscapeByte + return + } + + rPattern := buf.Bytes() + + if !bytes.HasPrefix(rPattern, bytesLikeReg) { + rPattern = append(bytesStart, rPattern...) + } + + if !bytes.HasSuffix(rPattern, bytesLikeReg) { + rPattern = append(rPattern, bytesEnd...) + } + + reg, err = regexp.Compile(string(rPattern)) + return +} + +func (t Tuple) like(reg *regexp.Regexp) (t2 Tuple, err error) { + t2 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + t2[i] = t[i].like(reg) + } + return +} + +func (r *Raw) like(reg *regexp.Regexp) (r2 *Raw) { + r2 = bool2Raw(reg.Match(encB(r.Bytes))) + return +} + +func opZip(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + op3 := &Operand{Meta: make([]ast.DataType, 0), Data: make([]Tuple, l)} + + for i := 0; i < len(ops); i++ { + op3.Meta = append(op3.Meta, ops[i].Meta...) + } + + for i := 0; i < l; i++ { + if ops[0].IsImmediate { + op3.Data[i] = append(Tuple{}, ops[0].Data[0]...) + } else { + op3.Data[i] = append(Tuple{}, ops[0].Data[i]...) + } + + for j := 1; j < len(ops); j++ { + if ops[j].IsImmediate { + op3.Data[i] = append(op3.Data[i], ops[j].Data[0]...) + } else { + op3.Data[i] = append(op3.Data[i], ops[j].Data[i]...) + } + } + } + + registers[output] = op3 + return +} + +func opField(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op, fields := ops[0], ops[1].Data[0] + fLen := len(fields) + + var fieldIdx uint16 + meta, fieldIdxs := make([]ast.DataType, fLen), make([]uint16, fLen) + for i := 0; i < fLen; i++ { + fieldIdx = value2ColIdx(fields[i].Value) + if len(op.Meta) <= int(fieldIdx) { + err = se.ErrorCodeIndexOutOfRange + return + } + meta[i], fieldIdxs[i] = op.Meta[fieldIdx], fieldIdx + } + + data := make([]Tuple, len(op.Data)) + for i := 0; i < len(op.Data); i++ { + tuple := make(Tuple, fLen) + for j := 0; j < fLen; j++ { + tuple[j] = op.Data[i][fieldIdxs[j]] + } + data[i] = tuple + } + + registers[output] = &Operand{Meta: meta, Data: data} + return +} + +// in-place Op +func opPrune(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op, fields := ops[0], ops[1].Data[0] + fLen := len(fields) + + var fieldIdx uint16 + fieldIdxs := make([]int, fLen) + for i := 0; i < fLen; i++ { + fieldIdx = value2ColIdx(fields[i].Value) + if len(op.Meta) <= int(fieldIdx) { + err = se.ErrorCodeIndexOutOfRange + return + } + fieldIdxs[i] = int(fieldIdx) + } + + op.Meta = pruneMeta(op.Meta, fieldIdxs) + + for i := 0; i < len(op.Data); i++ { + op.Data[i] = pruneTuple(op.Data[i], fieldIdxs) + } + return +} + +func pruneMeta(meta []ast.DataType, idxs []int) (meta2 []ast.DataType) { + for c, idx := range idxs { + end := len(meta) - 1 + if c+1 < len(idxs) { + end = idxs[c+1] + } + + for i := idx; i < end; i++ { + meta[i-c] = meta[i+1] + } + } + meta2 = meta[:len(meta)-len(idxs)] + return +} + +func pruneTuple(t Tuple, idxs []int) (t2 Tuple) { + for c, idx := range idxs { + end := len(t) - 1 + if c+1 < len(idxs) { + end = idxs[c+1] + } + + for i := idx; i < end; i++ { + t[i-c] = t[i+1] + } + } + t2 = t[:len(t)-len(idxs)] + return +} + +// in-place Op +func opSort(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op := ops[0] + + // orders (ascending bool, field) + orders := make([]sortOption, len(ops[1].Data)) + for i, o := range ops[1].Data { + orders[i] = sortOption{ + Asc: o[0].cmp(rawTrue) == 0, + Field: uint(value2ColIdx(o[1].Value)), + } + } + + sort.SliceStable( + op.Data, + func(i, j int) bool { return op.Data[i].less(op.Data[j], orders) }, + ) + return +} + +type sortOption struct { + Asc bool + Field uint +} + +func (t Tuple) less(t2 Tuple, orders []sortOption) bool { + var r int + + for _, o := range orders { + r = 0 + + if o.Asc { + r = t[o.Field].cmp(t2[o.Field]) + } else { + r = t2[o.Field].cmp(t[o.Field]) + } + + switch r { + case -1: + return true + case 1: + return false + } + } + return false +} + +func (r *Raw) cmp(r2 *Raw) (v int) { + if r.Bytes != nil { + v = bytes.Compare(r.Bytes, r2.Bytes) + } else { + v = r.Value.Cmp(r2.Value) + } + return +} + +func opFilter(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op, filters := ops[0], ops[1] + + op2 := &Operand{Meta: op.cloneMeta(), Data: make([]Tuple, 0)} + + for i := 0; i < len(filters.Data); i++ { + if filters.Data[i][0].cmp(rawTrue) == 0 { + op2.Data = append(op2.Data, append(Tuple{}, op.Data[i]...)) + } + } + + registers[output] = op2 + return +} + +// Type check will ensure all cast is valid +func opCast(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op := ops[0] + dTypes := ops[1].Meta + + if len(dTypes) != len(op.Meta) { + err = se.ErrorCodeInvalidDataType + return + } + + op2 := &Operand{Meta: ops[1].cloneMeta(), Data: make([]Tuple, len(op.Data))} + for i := 0; i < len(op.Data); i++ { + op2.Data[i] = append(Tuple{}, op.Data[i]...) + + for j, dType := range dTypes { + if op.Meta[j] == dType { + continue + } + + err = op2.Data[i][j].cast(ctx, op.Meta[j], dType) + if err != nil { + return + } + } + } + + registers[output] = op2 + return +} + +func (r *Raw) cast(ctx *common.Context, origin, target ast.DataType) (err error) { + oMajor, _ := ast.DecomposeDataType(origin) + + // conversion table + switch oMajor { + case ast.DataTypeMajorInt, ast.DataTypeMajorUint: + err = r.castInt(ctx, origin, target) + case ast.DataTypeMajorFixedBytes: + err = r.castFixedBytes(ctx, origin, target) + case ast.DataTypeMajorAddress: + err = r.castAddress(ctx, origin, target) + case ast.DataTypeMajorBool: + err = r.castBool(origin, target) + case ast.DataTypeMajorDynamicBytes: + err = r.castDynBytes(origin, target) + default: + err = se.ErrorCodeInvalidCastType + } + return +} + +func (r *Raw) castValue( + ctx *common.Context, + origin, target ast.DataType, + l int, signed, rPadding bool) (err error) { + oBytes, err := ast.DecimalEncode(origin, r.Value) + if err != nil { + return + } + + bytes2 := r.shiftBytes(oBytes, l, signed, rPadding) + + r.Value, err = ast.DecimalDecode(target, bytes2) + if err != nil { + return + } + + err = flowCheck(ctx, r.Value, target) + return +} + +func (r *Raw) castInt(ctx *common.Context, origin, target ast.DataType) (err error) { + oMajor, oMinor := ast.DecomposeDataType(origin) + tMajor, tMinor := ast.DecomposeDataType(target) + signed := oMajor == ast.DataTypeMajorInt + + switch tMajor { + case ast.DataTypeMajorInt, ast.DataTypeMajorUint: + err = r.castValue(ctx, origin, target, int(tMinor)+1, signed, false) + case ast.DataTypeMajorAddress: + r.Bytes, err = ast.DecimalEncode(origin, r.Value) + if err != nil { + return + } + + if len(r.Bytes) > dexCommon.AddressLength { + if r.Bytes[0]&0x80 != 0 { + err = se.ErrorCodeUnderflow + } else { + err = se.ErrorCodeOverflow + } + return + } + + r.Bytes = r.shiftBytes(r.Bytes, dexCommon.AddressLength, signed, false) + r.Value = decimal.Zero + case ast.DataTypeMajorFixedBytes: + if tMinor != oMinor { + err = se.ErrorCodeInvalidCastType + return + } + r.Bytes, err = ast.DecimalEncode(origin, r.Value) + if err != nil { + return + } + r.Value = decimal.Zero + case ast.DataTypeMajorBool: + r.Value = bool2Raw(!r.Value.IsZero()).Value + default: + err = se.ErrorCodeInvalidCastType + } + return +} + +func (r *Raw) castFixedBytes(ctx *common.Context, origin, target ast.DataType) (err error) { + _, oMinor := ast.DecomposeDataType(origin) + tMajor, tMinor := ast.DecomposeDataType(target) + switch tMajor { + case ast.DataTypeMajorDynamicBytes: + case ast.DataTypeMajorInt, ast.DataTypeMajorUint: + if tMinor != oMinor { + err = se.ErrorCodeInvalidCastType + return + } + r.Value, err = ast.DecimalDecode(target, r.Bytes) + if err != nil { + return + } + r.Bytes = nil + case ast.DataTypeMajorFixedBytes: + r.Bytes = r.shiftBytes(r.Bytes, int(tMinor)+1, false, true) + case ast.DataTypeMajorAddress: + if oMinor != (dexCommon.AddressLength - 1) { + err = se.ErrorCodeInvalidCastType + return + } + default: + err = se.ErrorCodeInvalidCastType + } + return +} + +func (r *Raw) castAddress(ctx *common.Context, origin, target ast.DataType) (err error) { + tMajor, tMinor := ast.DecomposeDataType(target) + + switch tMajor { + case ast.DataTypeMajorAddress: + case ast.DataTypeMajorInt, ast.DataTypeMajorUint: + r.Value, err = ast.DecimalDecode( + target, + r.shiftBytes(r.Bytes, int(tMinor)+1, false, false), + ) + if err != nil { + return + } + err = flowCheck(ctx, r.Value, target) + if err != nil { + return + } + r.Bytes = nil + case ast.DataTypeMajorFixedBytes: + if tMinor != (dexCommon.AddressLength - 1) { + err = se.ErrorCodeInvalidCastType + return + } + default: + err = se.ErrorCodeInvalidCastType + } + return +} + +func (r *Raw) castBool(origin, target ast.DataType) (err error) { + tMajor, _ := ast.DecomposeDataType(target) + switch tMajor { + case ast.DataTypeMajorBool, ast.DataTypeMajorInt, ast.DataTypeMajorUint: + default: + err = se.ErrorCodeInvalidCastType + } + return +} + +func (r *Raw) castDynBytes(origin, target ast.DataType) (err error) { + tMajor, tMinor := ast.DecomposeDataType(target) + switch tMajor { + case ast.DataTypeMajorDynamicBytes: + case ast.DataTypeMajorFixedBytes: + r.Bytes = r.shiftBytes(r.Bytes, int(tMinor)+1, false, true) + default: + err = se.ErrorCodeInvalidCastType + } + return +} + +func (r *Raw) shiftBytes(src []byte, l int, signed, rPadding bool) (tgr []byte) { + if len(src) >= l { + if rPadding { + tgr = src[:l] + } else { + tgr = src[len(src)-l:] + } + return + } + + tgr = make([]byte, l) + + if rPadding { + copy(tgr, src) + return + } + + copy(tgr[l-len(src):], src) + + if signed && src[0]&0x80 != 0 { + for i := 0; i < l-len(src); i++ { + tgr[i] = 0xff + } + } + return +} diff --git a/core/vm/sqlvm/runtime/instructions_op_test.go b/core/vm/sqlvm/runtime/instructions_op_test.go new file mode 100644 index 000000000..f18fb0181 --- /dev/null +++ b/core/vm/sqlvm/runtime/instructions_op_test.go @@ -0,0 +1,3110 @@ +// Code generated - DO NOT EDIT. + +package runtime + +import ( + "github.com/dexon-foundation/decimal" + + "github.com/dexon-foundation/dexon/core/vm/sqlvm/ast" + "github.com/dexon-foundation/dexon/core/vm/sqlvm/errors" +) + +func (s *instructionSuite) TestOpAdd() { + testcases := []opTestcase{ + { + "None Immediate", + Instruction{ + Op: ADD, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(2)}}, + {&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-2)}}, + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(10)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(2)}}, + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(2)}}, + {&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(3)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(4)}}, + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}, + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(20)}}, + {&Raw{Value: decimal.NewFromFloat(-20)}, &Raw{Value: decimal.NewFromFloat(13)}}, + }, + ), + nil, + }, + { + "Immediate", + Instruction{ + Op: ADD, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + {&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(11)}, &Raw{Value: decimal.NewFromFloat(8)}, &Raw{Value: decimal.NewFromFloat(0)}}, + {&Raw{Value: decimal.NewFromFloat(-9)}, &Raw{Value: decimal.NewFromFloat(-12)}, &Raw{Value: decimal.NewFromFloat(-20)}}, + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + }, + ), + nil, + }, + { + "Immediate 2", + Instruction{ + Op: ADD, + Input: []*Operand{ + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + {&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(11)}, &Raw{Value: decimal.NewFromFloat(8)}, &Raw{Value: decimal.NewFromFloat(0)}}, + {&Raw{Value: decimal.NewFromFloat(-9)}, &Raw{Value: decimal.NewFromFloat(-12)}, &Raw{Value: decimal.NewFromFloat(-20)}}, + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + }, + ), + nil, + }, + { + "Overflow - Immediate", + Instruction{ + Op: ADD, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(127)}}, + {&Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(1)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeOverflow, + }, + { + "Overflow None Immediate", + Instruction{ + Op: ADD, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(126)}}, + {&Raw{Value: decimal.NewFromFloat(126)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(2)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeOverflow, + }, + { + "Underflow - Immediate", + Instruction{ + Op: ADD, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(-128)}}, + {&Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(-1)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(-1)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeUnderflow, + }, + { + "Underflow None Immediate", + Instruction{ + Op: ADD, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(-127)}}, + {&Raw{Value: decimal.NewFromFloat(-127)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(-2)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeUnderflow, + }, + } + + s.run(testcases, opAdd) +} + +func (s *instructionSuite) TestOpSub() { + testcases := []opTestcase{ + { + "None Immediate", + Instruction{ + Op: SUB, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(2)}}, + {&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-2)}}, + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(10)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(2)}}, + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(2)}}, + {&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(3)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}, + {&Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(-4)}}, + {&Raw{Value: decimal.NewFromFloat(20)}, &Raw{Value: decimal.NewFromFloat(0)}}, + {&Raw{Value: decimal.NewFromFloat(-20)}, &Raw{Value: decimal.NewFromFloat(7)}}, + }, + ), + nil, + }, + { + "Immediate", + Instruction{ + Op: SUB, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + {&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(9)}, &Raw{Value: decimal.NewFromFloat(12)}, &Raw{Value: decimal.NewFromFloat(20)}}, + {&Raw{Value: decimal.NewFromFloat(-11)}, &Raw{Value: decimal.NewFromFloat(-8)}, &Raw{Value: decimal.NewFromFloat(0)}}, + {&Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(4)}, &Raw{Value: decimal.NewFromFloat(10)}}, + }, + ), + nil, + }, + { + "Immediate 2", + Instruction{ + Op: SUB, + Input: []*Operand{ + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + {&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(-9)}, &Raw{Value: decimal.NewFromFloat(-12)}, &Raw{Value: decimal.NewFromFloat(-20)}}, + {&Raw{Value: decimal.NewFromFloat(11)}, &Raw{Value: decimal.NewFromFloat(8)}, &Raw{Value: decimal.NewFromFloat(0)}}, + {&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-4)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + }, + ), + nil, + }, + { + "Overflow - Immediate", + Instruction{ + Op: SUB, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(127)}}, + {&Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(1)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(-1)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeOverflow, + }, + { + "Overflow None Immediate", + Instruction{ + Op: SUB, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(126)}}, + {&Raw{Value: decimal.NewFromFloat(126)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(-2)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeOverflow, + }, + { + "Underflow - Immediate", + Instruction{ + Op: SUB, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(-128)}}, + {&Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(-1)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeUnderflow, + }, + { + "Underflow None Immediate", + Instruction{ + Op: SUB, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(-127)}}, + {&Raw{Value: decimal.NewFromFloat(-127)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(2)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeUnderflow, + }, + } + + s.run(testcases, opSub) +} + +func (s *instructionSuite) TestOpMul() { + testcases := []opTestcase{ + { + "None Immediate", + Instruction{ + Op: MUL, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(4)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(-4)}, &Raw{Value: decimal.NewFromFloat(-100)}}, + {&Raw{Value: decimal.NewFromFloat(100)}, &Raw{Value: decimal.NewFromFloat(100)}}, + }, + ), + nil, + }, + { + "Immediate", + Instruction{ + Op: MUL, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(-20)}, &Raw{Value: decimal.NewFromFloat(0)}}, + {&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(20)}, &Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + nil, + }, + { + "Immediate - 2", + Instruction{ + Op: MUL, + Input: []*Operand{ + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(-20)}, &Raw{Value: decimal.NewFromFloat(0)}}, + {&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(20)}, &Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + nil, + }, + { + "Overflow - Immediate", + Instruction{ + Op: MUL, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(127)}}, + {&Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(1)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(2)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeOverflow, + }, + { + "Overflow None Immediate", + Instruction{ + Op: MUL, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(126)}}, + {&Raw{Value: decimal.NewFromFloat(126)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(2)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeOverflow, + }, + { + "Underflow - Immediate", + Instruction{ + Op: MUL, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(-128)}}, + {&Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(-1)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(2)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeUnderflow, + }, + { + "Underflow None Immediate", + Instruction{ + Op: MUL, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(-127)}}, + {&Raw{Value: decimal.NewFromFloat(-127)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(2)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeUnderflow, + }, + } + + s.run(testcases, opMul) +} + +func (s *instructionSuite) TestOpDiv() { + testcases := []opTestcase{ + { + "None Immediate", + Instruction{ + Op: DIV, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}}, + }, + ), + nil, + }, + { + "Immediate", + Instruction{ + Op: DIV, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}, + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + {&Raw{Value: decimal.NewFromFloat(13)}, &Raw{Value: decimal.NewFromFloat(13)}}, + {&Raw{Value: decimal.NewFromFloat(-13)}, &Raw{Value: decimal.NewFromFloat(-13)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-2)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}, + {&Raw{Value: decimal.NewFromFloat(5)}, &Raw{Value: decimal.NewFromFloat(-5)}}, + {&Raw{Value: decimal.NewFromFloat(-5)}, &Raw{Value: decimal.NewFromFloat(5)}}, + {&Raw{Value: decimal.NewFromFloat(6)}, &Raw{Value: decimal.NewFromFloat(-6)}}, + {&Raw{Value: decimal.NewFromFloat(-6)}, &Raw{Value: decimal.NewFromFloat(6)}}, + }, + ), + nil, + }, + { + "Immediate 2", + Instruction{ + Op: DIV, + Input: []*Operand{ + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(50)}, &Raw{Value: decimal.NewFromFloat(-50)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + {&Raw{Value: decimal.NewFromFloat(9)}, &Raw{Value: decimal.NewFromFloat(9)}}, + {&Raw{Value: decimal.NewFromFloat(-9)}, &Raw{Value: decimal.NewFromFloat(-9)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(5)}, &Raw{Value: decimal.NewFromFloat(-5)}}, + {&Raw{Value: decimal.NewFromFloat(-5)}, &Raw{Value: decimal.NewFromFloat(5)}}, + {&Raw{Value: decimal.NewFromFloat(5)}, &Raw{Value: decimal.NewFromFloat(-5)}}, + {&Raw{Value: decimal.NewFromFloat(-5)}, &Raw{Value: decimal.NewFromFloat(5)}}, + }, + ), + nil, + }, + { + "DivideByZero Immediate", + Instruction{ + Op: DIV, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(10)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeDividedByZero, + }, + { + "DivideByZero None Immediate", + Instruction{ + Op: DIV, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(10)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeDividedByZero, + }, + { + "Overflow - Immediate", + Instruction{ + Op: DIV, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(-128)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(-1)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeOverflow, + }, + { + "Overflow None Immediate", + Instruction{ + Op: DIV, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(-128)}}, + {&Raw{Value: decimal.NewFromFloat(-128)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(-2)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeOverflow, + }, + } + + s.run(testcases, opDiv) +} + +func (s *instructionSuite) TestOpMod() { + testcases := []opTestcase{ + { + "None Immediate", + Instruction{ + Op: MOD, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-2)}}, + {&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-2)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(3)}, &Raw{Value: decimal.NewFromFloat(3)}}, + {&Raw{Value: decimal.NewFromFloat(-3)}, &Raw{Value: decimal.NewFromFloat(-3)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}, + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}, + {&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-2)}}, + {&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-2)}}, + }, + ), + nil, + }, + { + "Immediate", + Instruction{ + Op: MOD, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}, + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + {&Raw{Value: decimal.NewFromFloat(13)}, &Raw{Value: decimal.NewFromFloat(13)}}, + {&Raw{Value: decimal.NewFromFloat(-13)}, &Raw{Value: decimal.NewFromFloat(-13)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(3)}, &Raw{Value: decimal.NewFromFloat(-3)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}, + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + }, + ), + nil, + }, + { + "Immediate - 2", + Instruction{ + Op: MOD, + Input: []*Operand{ + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(31)}, &Raw{Value: decimal.NewFromFloat(-31)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}, + {&Raw{Value: decimal.NewFromFloat(13)}, &Raw{Value: decimal.NewFromFloat(13)}}, + {&Raw{Value: decimal.NewFromFloat(-13)}, &Raw{Value: decimal.NewFromFloat(-13)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(5)}, &Raw{Value: decimal.NewFromFloat(-5)}}, + {&Raw{Value: decimal.NewFromFloat(5)}, &Raw{Value: decimal.NewFromFloat(-5)}}, + }, + ), + nil, + }, + { + "ModideByZero Immediate", + Instruction{ + Op: MOD, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(10)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeDividedByZero, + }, + { + "ModideByZero None Immediate", + Instruction{ + Op: MOD, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(10)}}, + {&Raw{Value: decimal.NewFromFloat(10)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeDividedByZero, + }, + } + + s.run(testcases, opMod) +} + +func (s *instructionSuite) TestOpLt() { + testcases := []opTestcase{ + { + "None Immediate", + Instruction{ + Op: LT, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}, + {&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawFalse, rawTrue, rawTrue}, + {rawFalse, rawFalse, rawTrue}, + {rawFalse, rawFalse, rawFalse}, + }, + ), + nil, + }, + { + "Immediate", + Instruction{ + Op: LT, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawFalse, rawFalse, rawTrue}, + }, + ), + nil, + }, + { + "Immediate - 2", + Instruction{ + Op: LT, + Input: []*Operand{ + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawFalse, rawTrue, rawFalse}, + }, + ), + nil, + }, + } + + s.run(testcases, opLt) +} + +func (s *instructionSuite) TestOpGt() { + testcases := []opTestcase{ + { + "None Immediate", + Instruction{ + Op: GT, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}, + {&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawFalse, rawFalse, rawFalse}, + {rawTrue, rawFalse, rawFalse}, + {rawTrue, rawTrue, rawFalse}, + }, + ), + nil, + }, + { + "Immediate", + Instruction{ + Op: GT, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawFalse, rawTrue, rawFalse}, + }, + ), + nil, + }, + { + "Immediate - 2", + Instruction{ + Op: GT, + Input: []*Operand{ + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawFalse, rawFalse, rawTrue}, + }, + ), + nil, + }, + } + + s.run(testcases, opGt) +} + +func (s *instructionSuite) TestOpEq() { + testcases := []opTestcase{ + { + "None Immediate", + Instruction{ + Op: EQ, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawTrue, rawTrue}, + {rawTrue, rawFalse, rawFalse}, + }, + ), + nil, + }, + { + "Immediate", + Instruction{ + Op: EQ, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}, + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-1)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawTrue, rawTrue}, + {rawTrue, rawFalse, rawFalse}, + }, + ), + nil, + }, + } + + s.run(testcases, opEq) +} + +func (s *instructionSuite) TestOpAnd() { + testcases := []opTestcase{ + { + "None Immediate", + Instruction{ + Op: AND, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawFalse}, + {rawFalse, rawTrue}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawTrue}, + {rawFalse, rawFalse}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawFalse}, + {rawFalse, rawFalse}, + }, + ), + nil, + }, + { + "Immediate", + Instruction{ + Op: AND, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawFalse}, + {rawFalse, rawTrue}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawTrue}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawFalse}, + {rawFalse, rawTrue}, + }, + ), + nil, + }, + { + "Immediate - 2", + Instruction{ + Op: AND, + Input: []*Operand{ + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawTrue}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawFalse}, + {rawFalse, rawTrue}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawFalse}, + {rawFalse, rawTrue}, + }, + ), + nil, + }, + { + "Invalid Data Type", + Instruction{ + Op: AND, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeInvalidDataType, + }, + } + + s.run(testcases, opAnd) +} + +func (s *instructionSuite) TestOpOr() { + testcases := []opTestcase{ + { + "None Immediate", + Instruction{ + Op: OR, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawFalse}, + {rawFalse, rawTrue}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawTrue}, + {rawFalse, rawFalse}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawTrue}, + {rawFalse, rawTrue}, + }, + ), + nil, + }, + { + "Immediate", + Instruction{ + Op: OR, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawFalse}, + {rawFalse, rawTrue}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawTrue}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawTrue}, + {rawTrue, rawTrue}, + }, + ), + nil, + }, + { + "Immediate - 2", + Instruction{ + Op: OR, + Input: []*Operand{ + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawTrue}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawFalse}, + {rawFalse, rawTrue}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawTrue}, + {rawTrue, rawTrue}, + }, + ), + nil, + }, + { + "Invalid Data Type", + Instruction{ + Op: OR, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeInvalidDataType, + }, + } + + s.run(testcases, opOr) +} + +func (s *instructionSuite) TestOpNot() { + testcases := []opTestcase{ + { + "None Immediate", + Instruction{ + Op: NOT, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawFalse}, + {rawFalse, rawTrue}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawFalse, rawTrue}, + {rawTrue, rawFalse}, + }, + ), + nil, + }, + { + "Errors Invalid Data Type", + Instruction{ + Op: NOT, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeInvalidDataType, + }, + } + + s.run(testcases, opNot) +} + +func (s *instructionSuite) TestOpUnion() { + testcases := []opTestcase{ + { + "None Immediate", + Instruction{ + Op: UNION, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawFalse}, + {rawFalse, rawTrue}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawTrue}, + {rawFalse, rawFalse}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawFalse, rawFalse}, + {rawFalse, rawTrue}, + {rawTrue, rawFalse}, + {rawTrue, rawTrue}, + }, + ), + nil, + }, + } + + s.run(testcases, opUnion) +} + +func (s *instructionSuite) TestOpIntxn() { + testcases := []opTestcase{ + { + "None Immediate", + Instruction{ + Op: INTXN, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawFalse}, + {rawFalse, rawTrue}, + {rawTrue, rawTrue}, + {rawFalse, rawFalse}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawTrue}, + {rawFalse, rawFalse}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawFalse, rawFalse}, + {rawTrue, rawTrue}, + }, + ), + nil, + }, + } + + s.run(testcases, opIntxn) +} + +func (s *instructionSuite) TestOpLike() { + testcases := []opTestcase{ + { + "Like %\\%b% escape \\", + Instruction{ + Op: LIKE, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(7, 0), ast.ComposeDataType(7, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("a%bcdefg")}, &Raw{Bytes: []byte("gfedcba")}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(7, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("%\\%b%")}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(7, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("\\")}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawFalse}, + }, + ), + nil, + }, + { + "Like t1 escape t2", + Instruction{ + Op: LIKE, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(7, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("a%bcdefg")}}, + {&Raw{Bytes: []byte("gfedcba")}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(7, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("%\\%b%")}}, + {&Raw{Bytes: []byte("_fed%")}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(7, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("\\")}}, + {&Raw{Bytes: []byte("")}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue}, + {rawTrue}, + }, + ), + nil, + }, + { + "Like with valid and invalid UTF8", + Instruction{ + Op: LIKE, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(7, 0), ast.ComposeDataType(7, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte{226, 40, 161, 228, 189, 160, 229, 165, 189}}, &Raw{Bytes: []byte("gfedcba")}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(7, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte{37, 228, 189, 160, 37}}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawFalse}, + }, + ), + nil, + }, + } + + s.run(testcases, opLike) +} + +func (s *instructionSuite) TestOpZip() { + testcases := []opTestcase{ + { + "Zip two array", + Instruction{ + Op: ZIP, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(7, 0), ast.ComposeDataType(7, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("abcdefg-1")}, &Raw{Bytes: []byte("gfedcba-1")}}, + {&Raw{Bytes: []byte("abcdefg-2")}, &Raw{Bytes: []byte("gfedcba-2")}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, rawTrue}, + {&Raw{Value: decimal.NewFromFloat(2)}, rawFalse}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(7, 0), ast.ComposeDataType(7, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("abcdefg-1")}, &Raw{Bytes: []byte("gfedcba-1")}, &Raw{Value: decimal.NewFromFloat(1)}, rawTrue}, + {&Raw{Bytes: []byte("abcdefg-2")}, &Raw{Bytes: []byte("gfedcba-2")}, &Raw{Value: decimal.NewFromFloat(2)}, rawFalse}, + }, + ), + nil, + }, + { + "Zip immediate", + Instruction{ + Op: ZIP, + Input: []*Operand{ + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(7, 0), ast.ComposeDataType(7, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("abcdefg-1")}, &Raw{Bytes: []byte("gfedcba-1")}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, rawTrue}, + {&Raw{Value: decimal.NewFromFloat(2)}, rawFalse}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(7, 0), ast.ComposeDataType(7, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("abcdefg-1")}, &Raw{Bytes: []byte("gfedcba-1")}, &Raw{Value: decimal.NewFromFloat(1)}, rawTrue}, + {&Raw{Bytes: []byte("abcdefg-1")}, &Raw{Bytes: []byte("gfedcba-1")}, &Raw{Value: decimal.NewFromFloat(2)}, rawFalse}, + }, + ), + nil, + }, + } + + s.run(testcases, opZip) +} + +func (s *instructionSuite) TestOpField() { + testcases := []opTestcase{ + { + "Retrieve 2nd,3rd column", + Instruction{ + Op: FIELD, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(7, 0), ast.ComposeDataType(7, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("abcdefg-1")}, &Raw{Bytes: []byte("gfedcba-1")}, &Raw{Value: decimal.NewFromFloat(1)}, rawTrue}, + {&Raw{Bytes: []byte("abcdefg-2")}, &Raw{Bytes: []byte("gfedcba-2")}, &Raw{Value: decimal.NewFromFloat(2)}, rawFalse}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(2)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(7, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("gfedcba-1")}, &Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Bytes: []byte("gfedcba-2")}, &Raw{Value: decimal.NewFromFloat(2)}}, + }, + ), + nil, + }, + } + + s.run(testcases, opField) +} + +func (s *instructionSuite) TestOpPrune() { + testcases := []opTestcase{ + { + "Prune 2nd,4th,5th column", + Instruction{ + Op: PRUNE, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(7, 0), ast.ComposeDataType(7, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("abcdefg-1")}, &Raw{Bytes: []byte("gfedcba-1")}, &Raw{Value: decimal.NewFromFloat(1)}, rawFalse, rawTrue}, + {&Raw{Bytes: []byte("abcdefg-2")}, &Raw{Bytes: []byte("gfedcba-2")}, &Raw{Value: decimal.NewFromFloat(2)}, rawTrue, rawFalse}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(3)}, &Raw{Value: decimal.NewFromFloat(4)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(7, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("abcdefg-1")}, &Raw{Value: decimal.NewFromFloat(1)}}, + {&Raw{Bytes: []byte("abcdefg-2")}, &Raw{Value: decimal.NewFromFloat(2)}}, + }, + ), + nil, + }, + } + + s.run(testcases, opPrune) +} + +func (s *instructionSuite) TestOpFilter() { + testcases := []opTestcase{ + { + "Filter first 2 rows", + Instruction{ + Op: FILTER, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawFalse}, + {rawFalse, rawTrue}, + {rawTrue, rawTrue}, + {rawFalse, rawFalse}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue}, + {rawTrue}, + {rawFalse}, + {rawFalse}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawFalse}, + {rawFalse, rawTrue}, + }, + ), + nil, + }, + } + + s.run(testcases, opFilter) +} + +func (s *instructionSuite) TestOpCast() { + testcases := []opTestcase{ + { + "None Immediate - int", + Instruction{ + Op: CAST, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 1), ast.ComposeDataType(4, 1), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(127)}, &Raw{Value: decimal.NewFromFloat(127)}}, + {&Raw{Value: decimal.NewFromFloat(-128)}, &Raw{Value: decimal.NewFromFloat(-128)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 2), + }, + []Tuple{}, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 0), ast.ComposeDataType(4, 2), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(127)}, &Raw{Value: decimal.NewFromFloat(127)}}, + {&Raw{Value: decimal.NewFromFloat(-128)}, &Raw{Value: decimal.NewFromFloat(-128)}}, + }, + ), + nil, + }, + { + "None Immediate - int2", + Instruction{ + Op: CAST, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 1), ast.ComposeDataType(4, 1), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(32767)}, &Raw{Value: decimal.NewFromFloat(-32768)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(5, 1), ast.ComposeDataType(5, 1), + }, + []Tuple{}, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(5, 1), ast.ComposeDataType(5, 1), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(32767)}, &Raw{Value: decimal.NewFromFloat(32768)}}, + }, + ), + nil, + }, + { + "None Immediate - int3", + Instruction{ + Op: CAST, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 1), ast.ComposeDataType(4, 1), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(32767)}, &Raw{Value: decimal.NewFromFloat(-32768)}}, + {&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{}, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawTrue}, + {rawFalse, rawFalse}, + }, + ), + nil, + }, + { + "None Immediate - int4", + Instruction{ + Op: CAST, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 1), ast.ComposeDataType(4, 1), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(32767)}, &Raw{Value: decimal.NewFromFloat(-32768)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(6, 1), ast.ComposeDataType(3, 0), + }, + []Tuple{}, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(6, 1), ast.ComposeDataType(3, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte{0x7f, 0xff}}, &Raw{Bytes: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0x80, 0x00}}}, + }, + ), + nil, + }, + { + "None Immediate - uint", + Instruction{ + Op: CAST, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(5, 1), ast.ComposeDataType(5, 1), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(128)}, &Raw{Value: decimal.NewFromFloat(128)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(5, 0), ast.ComposeDataType(5, 2), + }, + []Tuple{}, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(5, 0), ast.ComposeDataType(5, 2), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(128)}, &Raw{Value: decimal.NewFromFloat(128)}}, + }, + ), + nil, + }, + { + "None Immediate - uint2", + Instruction{ + Op: CAST, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(5, 1), ast.ComposeDataType(5, 1), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(32767)}, &Raw{Value: decimal.NewFromFloat(32768)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 1), ast.ComposeDataType(6, 1), + }, + []Tuple{}, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 1), ast.ComposeDataType(6, 1), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(32767)}, &Raw{Bytes: []byte{0x80, 0x00}}}, + }, + ), + nil, + }, + { + "None Immediate - uint3", + Instruction{ + Op: CAST, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(5, 1), ast.ComposeDataType(5, 1), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(32767)}, &Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{}, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue, rawFalse}, + }, + ), + nil, + }, + { + "None Immediate - uint4", + Instruction{ + Op: CAST, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(5, 1), ast.ComposeDataType(5, 1), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(32767)}, &Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(6, 1), ast.ComposeDataType(6, 1), + }, + []Tuple{}, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(6, 1), ast.ComposeDataType(6, 1), + }, + []Tuple{ + {&Raw{Bytes: []byte{0x7f, 0xff}}, &Raw{Bytes: []byte{0x00, 0x00}}}, + }, + ), + nil, + }, + { + "None Immediate - uint5", + Instruction{ + Op: CAST, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(5, 1), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(32767)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(3, 1), + }, + []Tuple{}, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(3, 1), + }, + []Tuple{ + {&Raw{Bytes: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x7f, 0xff}}}, + }, + ), + nil, + }, + { + "None Immediate - bytes", + Instruction{ + Op: CAST, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(6, 1), ast.ComposeDataType(6, 1), + }, + []Tuple{ + {&Raw{Bytes: []byte{0xff, 0xff}}, &Raw{Bytes: []byte{0xff, 0xff}}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(6, 0), ast.ComposeDataType(6, 2), + }, + []Tuple{}, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(6, 0), ast.ComposeDataType(6, 2), + }, + []Tuple{ + {&Raw{Bytes: []byte{0xff}}, &Raw{Bytes: []byte{0xff, 0xff, 0x00}}}, + }, + ), + nil, + }, + { + "None Immediate - bytes2", + Instruction{ + Op: CAST, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(6, 1), ast.ComposeDataType(6, 1), + }, + []Tuple{ + {&Raw{Bytes: []byte{0x7f, 0xff}}, &Raw{Bytes: []byte{0x80, 0x00}}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(4, 1), ast.ComposeDataType(5, 1), + }, + []Tuple{}, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 1), ast.ComposeDataType(5, 1), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(32767)}, &Raw{Value: decimal.NewFromFloat(32768)}}, + }, + ), + nil, + }, + { + "None Immediate - bytes3", + Instruction{ + Op: CAST, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(6, 1), + }, + []Tuple{ + {&Raw{Bytes: []byte{0x7f, 0xff}}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(7, 1), + }, + []Tuple{}, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(7, 1), + }, + []Tuple{ + {&Raw{Bytes: []byte{0x7f, 0xff}}}, + }, + ), + nil, + }, + { + "Same type", + Instruction{ + Op: CAST, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(2, 0), + }, + []Tuple{}, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(2, 0), + }, + []Tuple{ + {rawTrue}, + }, + ), + nil, + }, + { + "Error Invalid Type", + Instruction{ + Op: CAST, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(4, 2), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(-32768)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(7, 0), + }, + []Tuple{}, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeInvalidCastType, + }, + } + + s.run(testcases, opCast) +} + +func (s *instructionSuite) TestOpSort() { + testcases := []opTestcase{ + { + "Multi-column sorting", + Instruction{ + Op: SORT, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(7, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("c")}, &Raw{Value: decimal.NewFromFloat(1)}, rawTrue}, + {&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(2)}, rawTrue}, + {&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(3)}, rawTrue}, + {&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(1)}, rawFalse}, + {&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(2)}, rawFalse}, + {&Raw{Bytes: []byte("c")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}, + {&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}, + {&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {rawFalse, &Raw{Value: decimal.NewFromFloat(1)}}, + {rawTrue, &Raw{Value: decimal.NewFromFloat(2)}}, + {rawFalse, &Raw{Value: decimal.NewFromFloat(0)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(7, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("c")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}, + {&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}, + {&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}, + {&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(3)}, rawTrue}, + {&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(2)}, rawFalse}, + {&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(2)}, rawTrue}, + {&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(1)}, rawFalse}, + {&Raw{Bytes: []byte("c")}, &Raw{Value: decimal.NewFromFloat(1)}, rawTrue}, + }, + ), + nil, + }, + { + "Multi-column sorting - 2", + Instruction{ + Op: SORT, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(7, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("c")}, &Raw{Value: decimal.NewFromFloat(1)}, rawTrue}, + {&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(2)}, rawTrue}, + {&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(3)}, rawTrue}, + {&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(1)}, rawFalse}, + {&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(2)}, rawFalse}, + {&Raw{Bytes: []byte("c")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}, + {&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}, + {&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(2, 0), ast.ComposeDataType(4, 0), + }, + []Tuple{ + {rawTrue, &Raw{Value: decimal.NewFromFloat(0)}}, + {rawTrue, &Raw{Value: decimal.NewFromFloat(1)}}, + {rawFalse, &Raw{Value: decimal.NewFromFloat(2)}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(7, 0), ast.ComposeDataType(4, 0), ast.ComposeDataType(2, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(1)}, rawFalse}, + {&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(3)}, rawTrue}, + {&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}, + {&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(2)}, rawTrue}, + {&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(2)}, rawFalse}, + {&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}, + {&Raw{Bytes: []byte("c")}, &Raw{Value: decimal.NewFromFloat(1)}, rawTrue}, + {&Raw{Bytes: []byte("c")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}, + }, + ), + nil, + }, + } + + s.run(testcases, opSort) +} diff --git a/core/vm/sqlvm/runtime/instructions_test.go b/core/vm/sqlvm/runtime/instructions_test.go index 7c6bb2c5b..b679c366f 100644 --- a/core/vm/sqlvm/runtime/instructions_test.go +++ b/core/vm/sqlvm/runtime/instructions_test.go @@ -191,7 +191,7 @@ type decodeTestCase struct { type opLoadTestCase struct { title string - outputIdx int + outputIdx uint expectedOutput *Operand expectedErr error ids []uint64 @@ -410,3 +410,62 @@ func (s *opLoadSuite) TestOpLoad() { func TestOpLoad(t *testing.T) { suite.Run(t, new(opLoadSuite)) } + +func TestInstructions(t *testing.T) { + suite.Run(t, new(instructionSuite)) +} + +func makeOperand(im bool, meta []ast.DataType, pTuple []Tuple) (op *Operand) { + op = &Operand{IsImmediate: im, Meta: meta, Data: pTuple} + return +} + +func loadRegister(input, registers []*Operand) { + for i, operand := range input { + if operand != nil && !operand.IsImmediate { + input[i] = registers[operand.RegisterIndex] + } + } +} + +type opTestcase struct { + Name string + In Instruction + Output *Operand + Err error +} + +type instructionSuite struct { + suite.Suite +} + +func (s *instructionSuite) run(testcases []opTestcase, opfunc OpFunction) { + for idx, c := range testcases { + registers := make([]*Operand, len(c.In.Input)) + + for i, j := 0, 0; i < len(c.In.Input); i++ { + if !c.In.Input[i].IsImmediate { + registers[j] = c.In.Input[i] + j++ + } + } + err := opfunc( + &common.Context{Opt: common.Option{SafeMath: true}}, + c.In.Input, registers, c.In.Output) + s.Require().Equal( + c.Err, err, + "idx: %v, op: %v, case: %v\nerror not equal: %v != %v", + idx, c.In.Op, c.Name, c.Err, err, + ) + if c.Err != nil { + continue + } + + result := registers[0] + s.Require().True( + c.Output.Equal(result), + "idx: %v, op: %v, case: %v\noutput not equal.\nExpect: %v\nResult: %v\n", + idx, c.In.Op, c.Name, c.Output, result, + ) + } +} diff --git a/core/vm/sqlvm/runtime/instructions_tmpl.go b/core/vm/sqlvm/runtime/instructions_tmpl.go new file mode 100644 index 000000000..22c6bef88 --- /dev/null +++ b/core/vm/sqlvm/runtime/instructions_tmpl.go @@ -0,0 +1,82 @@ +package runtime + +import "github.com/dexon-foundation/dexon/core/vm/sqlvm/ast" + +type tmplData struct { + BinOpCollections []tmplTestCollection +} + +type tmplTestCollection struct { + TestName string + Cases []tmplTestCase + OpFunc string +} + +type tmplTestCase struct { + Name string + OpCode string + Inputs []tmplOp + Output tmplOp + Error string +} + +type tmplOp struct { + Im bool + Metas []tmplOpMeta + Data []string +} + +type tmplOpMeta struct { + Major ast.DataTypeMajor + Minor ast.DataTypeMinor +} + +const binOpTmplStr = ` +// Code generated - DO NOT EDIT. + +package runtime + +import ( + "github.com/dexon-foundation/decimal" + + "github.com/dexon-foundation/dexon/core/vm/sqlvm/ast" + "github.com/dexon-foundation/dexon/core/vm/sqlvm/errors" +) + +{{range .BinOpCollections}} +func (s *instructionSuite) Test{{.TestName}}() { + testcases := []opTestcase{ {{range .Cases}} + { + "{{.Name}}", + Instruction{ + Op: {{.OpCode}}, + Input: []*Operand{ {{range .Inputs}} + makeOperand( + {{.Im}}, + []ast.DataType{ + {{range .Metas}}ast.ComposeDataType({{.Major}}, {{.Minor}}),{{end}} + }, + []Tuple{ {{range .Data}} + {{.}},{{end}} + }, + ),{{end}} + }, + Output: 0, + }, + makeOperand( + {{.Output.Im}}, + []ast.DataType{ + {{range .Output.Metas}}ast.ComposeDataType({{.Major}}, {{.Minor}}),{{end}} + }, + []Tuple{ {{range .Output.Data}} + {{.}},{{end}} + }, + ), + {{.Error}}, + },{{end}} + } + + s.run(testcases, {{.OpFunc}}) +} +{{end}} +` diff --git a/core/vm/sqlvm/runtime/instructions_tmpl_data.go b/core/vm/sqlvm/runtime/instructions_tmpl_data.go new file mode 100644 index 000000000..345d200ec --- /dev/null +++ b/core/vm/sqlvm/runtime/instructions_tmpl_data.go @@ -0,0 +1,2934 @@ +package runtime + +import ( + "bytes" + "go/format" + "os" + "text/template" + + "github.com/dexon-foundation/dexon/core/vm/sqlvm/ast" +) + +// RenderOpTest render op test to test file. +func RenderOpTest(output string) (err error) { + binOpT, err := template.New("binOp").Parse(binOpTmplStr) + if err != nil { + return + } + + b := new(bytes.Buffer) + + err = binOpT.Execute(b, testData) + if err != nil { + return + } + + src, err := format.Source(b.Bytes()) + if err != nil { + return + } + + f, err := os.Create(output) + if err != nil { + return + } + defer f.Close() + + _, err = f.Write(src) + return +} + +var testData = tmplData{ + BinOpCollections: []tmplTestCollection{ + { + TestName: "OpAdd", OpFunc: "opAdd", + Cases: []tmplTestCase{ + { + Name: "None Immediate", + Error: "nil", OpCode: "ADD", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(2)}}", + "{&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-2)}}", + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(10)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(2)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(2)}}", + "{&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(3)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(4)}}", + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}", + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(20)}}", + "{&Raw{Value: decimal.NewFromFloat(-20)}, &Raw{Value: decimal.NewFromFloat(13)}}", + }, + }, + }, + { + Name: "Immediate", + Error: "nil", OpCode: "ADD", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + "{&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(11)}, &Raw{Value: decimal.NewFromFloat(8)}, &Raw{Value: decimal.NewFromFloat(0)}}", + "{&Raw{Value: decimal.NewFromFloat(-9)}, &Raw{Value: decimal.NewFromFloat(-12)}, &Raw{Value: decimal.NewFromFloat(-20)}}", + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + }, + }, + }, + { + Name: "Immediate 2", + Error: "nil", OpCode: "ADD", + Inputs: []tmplOp{ + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + "{&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(11)}, &Raw{Value: decimal.NewFromFloat(8)}, &Raw{Value: decimal.NewFromFloat(0)}}", + "{&Raw{Value: decimal.NewFromFloat(-9)}, &Raw{Value: decimal.NewFromFloat(-12)}, &Raw{Value: decimal.NewFromFloat(-20)}}", + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + }, + }, + }, + { + Name: "Overflow - Immediate", + Error: "errors.ErrorCodeOverflow", OpCode: "ADD", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(127)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}}", + }, + }, + }, + Output: tmplOp{}, + }, + { + Name: "Overflow None Immediate", + Error: "errors.ErrorCodeOverflow", OpCode: "ADD", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(126)}}", + "{&Raw{Value: decimal.NewFromFloat(126)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(2)}}", + }, + }, + }, + Output: tmplOp{}, + }, + { + Name: "Underflow - Immediate", + Error: "errors.ErrorCodeUnderflow", OpCode: "ADD", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(-128)}}", + "{&Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(-1)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(-1)}}", + }, + }, + }, + Output: tmplOp{}, + }, + { + Name: "Underflow None Immediate", + Error: "errors.ErrorCodeUnderflow", OpCode: "ADD", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(-127)}}", + "{&Raw{Value: decimal.NewFromFloat(-127)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(-2)}}", + }, + }, + }, + Output: tmplOp{}, + }, + }, + }, + // -- end of ADD + { + TestName: "OpSub", OpFunc: "opSub", + Cases: []tmplTestCase{ + { + Name: "None Immediate", + Error: "nil", OpCode: "SUB", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(2)}}", + "{&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-2)}}", + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(10)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(2)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(2)}}", + "{&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(3)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}", + "{&Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(-4)}}", + "{&Raw{Value: decimal.NewFromFloat(20)}, &Raw{Value: decimal.NewFromFloat(0)}}", + "{&Raw{Value: decimal.NewFromFloat(-20)}, &Raw{Value: decimal.NewFromFloat(7)}}", + }, + }, + }, + { + Name: "Immediate", + Error: "nil", OpCode: "SUB", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + "{&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(9)}, &Raw{Value: decimal.NewFromFloat(12)}, &Raw{Value: decimal.NewFromFloat(20)}}", + "{&Raw{Value: decimal.NewFromFloat(-11)}, &Raw{Value: decimal.NewFromFloat(-8)}, &Raw{Value: decimal.NewFromFloat(0)}}", + "{&Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(4)}, &Raw{Value: decimal.NewFromFloat(10)}}", + }, + }, + }, + { + Name: "Immediate 2", + Error: "nil", OpCode: "SUB", + Inputs: []tmplOp{ + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + "{&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(-9)}, &Raw{Value: decimal.NewFromFloat(-12)}, &Raw{Value: decimal.NewFromFloat(-20)}}", + "{&Raw{Value: decimal.NewFromFloat(11)}, &Raw{Value: decimal.NewFromFloat(8)}, &Raw{Value: decimal.NewFromFloat(0)}}", + "{&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-4)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + }, + }, + }, + { + Name: "Overflow - Immediate", + Error: "errors.ErrorCodeOverflow", OpCode: "SUB", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(127)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(-1)}}", + }, + }, + }, + Output: tmplOp{}, + }, + { + Name: "Overflow None Immediate", + Error: "errors.ErrorCodeOverflow", OpCode: "SUB", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(126)}}", + "{&Raw{Value: decimal.NewFromFloat(126)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(-2)}}", + }, + }, + }, + Output: tmplOp{}, + }, + { + Name: "Underflow - Immediate", + Error: "errors.ErrorCodeUnderflow", OpCode: "SUB", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(-128)}}", + "{&Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(-1)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}}", + }, + }, + }, + Output: tmplOp{}, + }, + { + Name: "Underflow None Immediate", + Error: "errors.ErrorCodeUnderflow", OpCode: "SUB", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(-127)}}", + "{&Raw{Value: decimal.NewFromFloat(-127)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(2)}}", + }, + }, + }, + Output: tmplOp{}, + }, + }, + }, + // -- end of SUB + { + TestName: "OpMul", OpFunc: "opMul", + Cases: []tmplTestCase{ + { + Name: "None Immediate", + Error: "nil", OpCode: "MUL", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(4)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(-4)}, &Raw{Value: decimal.NewFromFloat(-100)}}", + "{&Raw{Value: decimal.NewFromFloat(100)}, &Raw{Value: decimal.NewFromFloat(100)}}", + }, + }, + }, + { + Name: "Immediate", + Error: "nil", OpCode: "MUL", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(-20)}, &Raw{Value: decimal.NewFromFloat(0)}}", + "{&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(20)}, &Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + }, + { + Name: "Immediate - 2", + Error: "nil", OpCode: "MUL", + Inputs: []tmplOp{ + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(-20)}, &Raw{Value: decimal.NewFromFloat(0)}}", + "{&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(20)}, &Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + }, + { + Name: "Overflow - Immediate", + Error: "errors.ErrorCodeOverflow", OpCode: "MUL", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(127)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(2)}}", + }, + }, + }, + Output: tmplOp{}, + }, + { + Name: "Overflow None Immediate", + Error: "errors.ErrorCodeOverflow", OpCode: "MUL", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(126)}}", + "{&Raw{Value: decimal.NewFromFloat(126)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(2)}}", + }, + }, + }, + Output: tmplOp{}, + }, + { + Name: "Underflow - Immediate", + Error: "errors.ErrorCodeUnderflow", OpCode: "MUL", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(-128)}}", + "{&Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(-1)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(2)}}", + }, + }, + }, + Output: tmplOp{}, + }, + { + Name: "Underflow None Immediate", + Error: "errors.ErrorCodeUnderflow", OpCode: "MUL", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(-127)}}", + "{&Raw{Value: decimal.NewFromFloat(-127)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(2)}}", + }, + }, + }, + Output: tmplOp{}, + }, + }, + }, + // -- end of MUL + { + TestName: "OpDiv", OpFunc: "opDiv", + Cases: []tmplTestCase{ + { + Name: "None Immediate", + Error: "nil", OpCode: "DIV", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}}", + }, + }, + }, + { + Name: "Immediate", + Error: "nil", OpCode: "DIV", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}", + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + "{&Raw{Value: decimal.NewFromFloat(13)}, &Raw{Value: decimal.NewFromFloat(13)}}", + "{&Raw{Value: decimal.NewFromFloat(-13)}, &Raw{Value: decimal.NewFromFloat(-13)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-2)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}", + "{&Raw{Value: decimal.NewFromFloat(5)}, &Raw{Value: decimal.NewFromFloat(-5)}}", + "{&Raw{Value: decimal.NewFromFloat(-5)}, &Raw{Value: decimal.NewFromFloat(5)}}", + "{&Raw{Value: decimal.NewFromFloat(6)}, &Raw{Value: decimal.NewFromFloat(-6)}}", + "{&Raw{Value: decimal.NewFromFloat(-6)}, &Raw{Value: decimal.NewFromFloat(6)}}", + }, + }, + }, + { + Name: "Immediate 2", + Error: "nil", OpCode: "DIV", + Inputs: []tmplOp{ + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(50)}, &Raw{Value: decimal.NewFromFloat(-50)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + "{&Raw{Value: decimal.NewFromFloat(9)}, &Raw{Value: decimal.NewFromFloat(9)}}", + "{&Raw{Value: decimal.NewFromFloat(-9)}, &Raw{Value: decimal.NewFromFloat(-9)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(5)}, &Raw{Value: decimal.NewFromFloat(-5)}}", + "{&Raw{Value: decimal.NewFromFloat(-5)}, &Raw{Value: decimal.NewFromFloat(5)}}", + "{&Raw{Value: decimal.NewFromFloat(5)}, &Raw{Value: decimal.NewFromFloat(-5)}}", + "{&Raw{Value: decimal.NewFromFloat(-5)}, &Raw{Value: decimal.NewFromFloat(5)}}", + }, + }, + }, + { + Name: "DivideByZero Immediate", + Error: "errors.ErrorCodeDividedByZero", OpCode: "DIV", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(10)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + }, + Output: tmplOp{}, + }, + { + Name: "DivideByZero None Immediate", + Error: "errors.ErrorCodeDividedByZero", OpCode: "DIV", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(10)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + }, + Output: tmplOp{}, + }, + { + Name: "Overflow - Immediate", + Error: "errors.ErrorCodeOverflow", OpCode: "DIV", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(-128)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(-1)}}", + }, + }, + }, + Output: tmplOp{}, + }, + { + Name: "Overflow None Immediate", + Error: "errors.ErrorCodeOverflow", OpCode: "DIV", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(-128)}}", + "{&Raw{Value: decimal.NewFromFloat(-128)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(-2)}}", + }, + }, + }, + Output: tmplOp{}, + }, + }, + }, + // -- end of DIV + { + TestName: "OpMod", OpFunc: "opMod", + Cases: []tmplTestCase{ + { + Name: "None Immediate", + Error: "nil", OpCode: "MOD", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-2)}}", + "{&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-2)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(3)}, &Raw{Value: decimal.NewFromFloat(3)}}", + "{&Raw{Value: decimal.NewFromFloat(-3)}, &Raw{Value: decimal.NewFromFloat(-3)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}", + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}", + "{&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-2)}}", + "{&Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-2)}}", + }, + }, + }, + { + Name: "Immediate", + Error: "nil", OpCode: "MOD", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}", + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + "{&Raw{Value: decimal.NewFromFloat(13)}, &Raw{Value: decimal.NewFromFloat(13)}}", + "{&Raw{Value: decimal.NewFromFloat(-13)}, &Raw{Value: decimal.NewFromFloat(-13)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(3)}, &Raw{Value: decimal.NewFromFloat(-3)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + }, + }, + }, + { + Name: "Immediate - 2", + Error: "nil", OpCode: "MOD", + Inputs: []tmplOp{ + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(31)}, &Raw{Value: decimal.NewFromFloat(-31)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(10)}, &Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(-10)}, &Raw{Value: decimal.NewFromFloat(-10)}}", + "{&Raw{Value: decimal.NewFromFloat(13)}, &Raw{Value: decimal.NewFromFloat(13)}}", + "{&Raw{Value: decimal.NewFromFloat(-13)}, &Raw{Value: decimal.NewFromFloat(-13)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(5)}, &Raw{Value: decimal.NewFromFloat(-5)}}", + "{&Raw{Value: decimal.NewFromFloat(5)}, &Raw{Value: decimal.NewFromFloat(-5)}}", + }, + }, + }, + { + Name: "ModideByZero Immediate", + Error: "errors.ErrorCodeDividedByZero", OpCode: "MOD", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(10)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + }, + Output: tmplOp{}, + }, + { + Name: "ModideByZero None Immediate", + Error: "errors.ErrorCodeDividedByZero", OpCode: "MOD", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(10)}}", + "{&Raw{Value: decimal.NewFromFloat(10)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + }, + Output: tmplOp{}, + }, + }, + }, + // -- end of MOD + { + TestName: "OpLt", OpFunc: "opLt", + Cases: []tmplTestCase{ + { + Name: "None Immediate", + Error: "nil", OpCode: "LT", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}", + "{&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawFalse, rawTrue, rawTrue}", + "{rawFalse, rawFalse, rawTrue}", + "{rawFalse, rawFalse, rawFalse}", + }, + }, + }, + { + Name: "Immediate", + Error: "nil", OpCode: "LT", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawFalse, rawFalse, rawTrue}", + }, + }, + }, + { + Name: "Immediate - 2", + Error: "nil", OpCode: "LT", + Inputs: []tmplOp{ + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawFalse, rawTrue, rawFalse}", + }, + }, + }, + }, + }, + // -- end of LT + { + TestName: "OpGt", OpFunc: "opGt", + Cases: []tmplTestCase{ + { + Name: "None Immediate", + Error: "nil", OpCode: "GT", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}}", + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}", + "{&Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawFalse, rawFalse, rawFalse}", + "{rawTrue, rawFalse, rawFalse}", + "{rawTrue, rawTrue, rawFalse}", + }, + }, + }, + { + Name: "Immediate", + Error: "nil", OpCode: "GT", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawFalse, rawTrue, rawFalse}", + }, + }, + }, + { + Name: "Immediate - 2", + Error: "nil", OpCode: "GT", + Inputs: []tmplOp{ + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawFalse, rawFalse, rawTrue}", + }, + }, + }, + }, + }, + // -- end of GT + { + TestName: "OpEq", OpFunc: "opEq", + Cases: []tmplTestCase{ + { + Name: "None Immediate", + Error: "nil", OpCode: "EQ", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(1)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawTrue, rawTrue}", + "{rawTrue, rawFalse, rawFalse}", + }, + }, + }, + { + Name: "Immediate", + Error: "nil", OpCode: "EQ", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}", + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(-1)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawTrue, rawTrue}", + "{rawTrue, rawFalse, rawFalse}", + }, + }, + }, + }, + }, + // -- end of EQ + { + TestName: "OpAnd", OpFunc: "opAnd", + Cases: []tmplTestCase{ + { + Name: "None Immediate", + Error: "nil", OpCode: "AND", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawFalse}", + "{rawFalse, rawTrue}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawTrue}", + "{rawFalse, rawFalse}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawFalse}", + "{rawFalse, rawFalse}", + }, + }, + }, + { + Name: "Immediate", + Error: "nil", OpCode: "AND", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawFalse}", + "{rawFalse, rawTrue}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawTrue}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawFalse}", + "{rawFalse, rawTrue}", + }, + }, + }, + { + Name: "Immediate - 2", + Error: "nil", OpCode: "AND", + Inputs: []tmplOp{ + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawTrue}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawFalse}", + "{rawFalse, rawTrue}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawFalse}", + "{rawFalse, rawTrue}", + }, + }, + }, + { + Name: "Invalid Data Type", + Error: "errors.ErrorCodeInvalidDataType", OpCode: "AND", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}}", + }, + }, + }, + Output: tmplOp{}, + }, + }, + }, + // -- end of AND + { + TestName: "OpOr", OpFunc: "opOr", + Cases: []tmplTestCase{ + { + Name: "None Immediate", + Error: "nil", OpCode: "OR", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawFalse}", + "{rawFalse, rawTrue}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawTrue}", + "{rawFalse, rawFalse}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawTrue}", + "{rawFalse, rawTrue}", + }, + }, + }, + { + Name: "Immediate", + Error: "nil", OpCode: "OR", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawFalse}", + "{rawFalse, rawTrue}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawTrue}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawTrue}", + "{rawTrue, rawTrue}", + }, + }, + }, + { + Name: "Immediate - 2", + Error: "nil", OpCode: "OR", + Inputs: []tmplOp{ + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawTrue}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawFalse}", + "{rawFalse, rawTrue}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawTrue}", + "{rawTrue, rawTrue}", + }, + }, + }, + { + Name: "Invalid Data Type", + Error: "errors.ErrorCodeInvalidDataType", OpCode: "OR", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}}", + }, + }, + }, + Output: tmplOp{}, + }, + }, + }, + // -- end of OR + { + TestName: "OpNot", OpFunc: "opNot", + Cases: []tmplTestCase{ + { + Name: "None Immediate", + Error: "nil", OpCode: "NOT", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawFalse}", + "{rawFalse, rawTrue}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawFalse, rawTrue}", + "{rawTrue, rawFalse}", + }, + }, + }, + { + Name: "Errors Invalid Data Type", + Error: "errors.ErrorCodeInvalidDataType", OpCode: "NOT", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}}", + }, + }, + }, + Output: tmplOp{}, + }, + }, + }, + // -- end of NOT + { + TestName: "OpUnion", OpFunc: "opUnion", + Cases: []tmplTestCase{ + { + Name: "None Immediate", + Error: "nil", OpCode: "UNION", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawFalse}", + "{rawFalse, rawTrue}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawTrue}", + "{rawFalse, rawFalse}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawFalse, rawFalse}", + "{rawFalse, rawTrue}", + "{rawTrue, rawFalse}", + "{rawTrue, rawTrue}", + }, + }, + }, + }, + }, + // -- end of UNION + { + TestName: "OpIntxn", OpFunc: "opIntxn", + Cases: []tmplTestCase{ + { + Name: "None Immediate", + Error: "nil", OpCode: "INTXN", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawFalse}", + "{rawFalse, rawTrue}", + "{rawTrue, rawTrue}", + "{rawFalse, rawFalse}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawTrue}", + "{rawFalse, rawFalse}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawFalse, rawFalse}", + "{rawTrue, rawTrue}", + }, + }, + }, + }, + }, + // -- end of INTXN + { + TestName: "OpLike", OpFunc: "opLike", + Cases: []tmplTestCase{ + { + Name: `Like %\\%b% escape \\`, // \\ is raw string escape for \ + Error: "nil", OpCode: "LIKE", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + }, + Data: []string{ + `{&Raw{Bytes: []byte("a%bcdefg")}, &Raw{Bytes: []byte("gfedcba")}}`, + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + }, + Data: []string{`{&Raw{Bytes: []byte("%\\%b%")}}`}, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + }, + Data: []string{`{&Raw{Bytes: []byte("\\")}}`}, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawFalse}", + }, + }, + }, + { + Name: `Like t1 escape t2`, + Error: "nil", OpCode: "LIKE", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + }, + Data: []string{ + `{&Raw{Bytes: []byte("a%bcdefg")}}`, + `{&Raw{Bytes: []byte("gfedcba")}}`, + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + }, + Data: []string{ + `{&Raw{Bytes: []byte("%\\%b%")}}`, + `{&Raw{Bytes: []byte("_fed%")}}`, + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + }, + Data: []string{ + `{&Raw{Bytes: []byte("\\")}}`, + `{&Raw{Bytes: []byte("")}}`, + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue}", + "{rawTrue}", + }, + }, + }, + { + Name: `Like with valid and invalid UTF8`, + Error: "nil", OpCode: "LIKE", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + }, + Data: []string{ + `{&Raw{Bytes: []byte{226, 40, 161, 228, 189, 160, 229, 165, 189}}, &Raw{Bytes: []byte("gfedcba")}}`, + // "\xe2(\xa1你好" + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + }, + Data: []string{ + `{&Raw{Bytes: []byte{37, 228, 189, 160, 37}}}`, + // "%你%" + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawFalse}", + }, + }, + }, + }, + }, + // -- end of LIKE + { + TestName: "OpZip", OpFunc: "opZip", + Cases: []tmplTestCase{ + { + Name: "Zip two array", + Error: "nil", OpCode: "ZIP", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + }, + Data: []string{ + `{&Raw{Bytes: []byte("abcdefg-1")}, &Raw{Bytes: []byte("gfedcba-1")}}`, + `{&Raw{Bytes: []byte("abcdefg-2")}, &Raw{Bytes: []byte("gfedcba-2")}}`, + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, rawTrue}", + "{&Raw{Value: decimal.NewFromFloat(2)}, rawFalse}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + `{&Raw{Bytes: []byte("abcdefg-1")}, &Raw{Bytes: []byte("gfedcba-1")}, &Raw{Value: decimal.NewFromFloat(1)}, rawTrue}`, + `{&Raw{Bytes: []byte("abcdefg-2")}, &Raw{Bytes: []byte("gfedcba-2")}, &Raw{Value: decimal.NewFromFloat(2)}, rawFalse}`, + }, + }, + }, + { + Name: "Zip immediate", + Error: "nil", OpCode: "ZIP", + Inputs: []tmplOp{ + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + }, + Data: []string{ + `{&Raw{Bytes: []byte("abcdefg-1")}, &Raw{Bytes: []byte("gfedcba-1")}}`, + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, rawTrue}", + "{&Raw{Value: decimal.NewFromFloat(2)}, rawFalse}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + `{&Raw{Bytes: []byte("abcdefg-1")}, &Raw{Bytes: []byte("gfedcba-1")}, &Raw{Value: decimal.NewFromFloat(1)}, rawTrue}`, + `{&Raw{Bytes: []byte("abcdefg-1")}, &Raw{Bytes: []byte("gfedcba-1")}, &Raw{Value: decimal.NewFromFloat(2)}, rawFalse}`, + }, + }, + }, + }, + }, + // -- end of ZIP + { + TestName: "OpField", OpFunc: "opField", + Cases: []tmplTestCase{ + { + Name: "Retrieve 2nd,3rd column", + Error: "nil", OpCode: "FIELD", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + `{&Raw{Bytes: []byte("abcdefg-1")}, &Raw{Bytes: []byte("gfedcba-1")}, &Raw{Value: decimal.NewFromFloat(1)}, rawTrue}`, + `{&Raw{Bytes: []byte("abcdefg-2")}, &Raw{Bytes: []byte("gfedcba-2")}, &Raw{Value: decimal.NewFromFloat(2)}, rawFalse}`, + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(2)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + `{&Raw{Bytes: []byte("gfedcba-1")}, &Raw{Value: decimal.NewFromFloat(1)}}`, + `{&Raw{Bytes: []byte("gfedcba-2")}, &Raw{Value: decimal.NewFromFloat(2)}}`, + }, + }, + }, + }, + }, + // -- end of FIELD + { + TestName: "OpPrune", OpFunc: "opPrune", + Cases: []tmplTestCase{ + { + Name: "Prune 2nd,4th,5th column", + Error: "nil", OpCode: "PRUNE", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + `{&Raw{Bytes: []byte("abcdefg-1")}, &Raw{Bytes: []byte("gfedcba-1")}, &Raw{Value: decimal.NewFromFloat(1)}, rawFalse, rawTrue}`, + `{&Raw{Bytes: []byte("abcdefg-2")}, &Raw{Bytes: []byte("gfedcba-2")}, &Raw{Value: decimal.NewFromFloat(2)}, rawTrue, rawFalse}`, + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(3)}, &Raw{Value: decimal.NewFromFloat(4)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + `{&Raw{Bytes: []byte("abcdefg-1")}, &Raw{Value: decimal.NewFromFloat(1)}}`, + `{&Raw{Bytes: []byte("abcdefg-2")}, &Raw{Value: decimal.NewFromFloat(2)}}`, + }, + }, + }, + }, + }, + // -- end of PRUNE + { + TestName: "OpFilter", OpFunc: "opFilter", + Cases: []tmplTestCase{ + { + Name: "Filter first 2 rows", + Error: "nil", OpCode: "FILTER", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawFalse}", + "{rawFalse, rawTrue}", + "{rawTrue, rawTrue}", + "{rawFalse, rawFalse}", + }, + }, + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue}", + "{rawTrue}", + "{rawFalse}", + "{rawFalse}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawFalse}", + "{rawFalse, rawTrue}", + }, + }, + }, + }, + }, + // -- end of FILTER + { + TestName: "OpCast", OpFunc: "opCast", + Cases: []tmplTestCase{ + { + Name: "None Immediate - int", + Error: "nil", OpCode: "CAST", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 1}, // int16 -> int8 + {Major: ast.DataTypeMajorInt, Minor: 1}, // int16 -> int24 + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(127)}, &Raw{Value: decimal.NewFromFloat(127)}}", + "{&Raw{Value: decimal.NewFromFloat(-128)}, &Raw{Value: decimal.NewFromFloat(-128)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 2}, + }, + Data: []string{}, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 2}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(127)}, &Raw{Value: decimal.NewFromFloat(127)}}", + "{&Raw{Value: decimal.NewFromFloat(-128)}, &Raw{Value: decimal.NewFromFloat(-128)}}", + }, + }, + }, + { + Name: "None Immediate - int2", + Error: "nil", OpCode: "CAST", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 1}, // int16 -> uint16 + {Major: ast.DataTypeMajorInt, Minor: 1}, // int16 -> uint16 + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(32767)}, &Raw{Value: decimal.NewFromFloat(-32768)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorUint, Minor: 1}, + {Major: ast.DataTypeMajorUint, Minor: 1}, + }, + Data: []string{}, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorUint, Minor: 1}, + {Major: ast.DataTypeMajorUint, Minor: 1}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(32767)}, &Raw{Value: decimal.NewFromFloat(32768)}}", + }, + }, + }, + { + Name: "None Immediate - int3", + Error: "nil", OpCode: "CAST", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 1}, // int16 -> bool + {Major: ast.DataTypeMajorInt, Minor: 1}, // int16 -> bool + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(32767)}, &Raw{Value: decimal.NewFromFloat(-32768)}}", + "{&Raw{Value: decimal.NewFromFloat(0)}, &Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{}, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawTrue}", "{rawFalse, rawFalse}", + }, + }, + }, + { + Name: "None Immediate - int4", + Error: "nil", OpCode: "CAST", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 1}, // int16 -> bytes16 + {Major: ast.DataTypeMajorInt, Minor: 1}, // int16 -> address + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(32767)}, &Raw{Value: decimal.NewFromFloat(-32768)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorFixedBytes, Minor: 1}, + {Major: ast.DataTypeMajorAddress, Minor: 0}, + }, + Data: []string{}, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorFixedBytes, Minor: 1}, + {Major: ast.DataTypeMajorAddress, Minor: 0}, + }, + Data: []string{ + "{&Raw{Bytes: []byte{0x7f, 0xff}}, &Raw{Bytes: []byte{255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0x80,0x00}}}", + }, + }, + }, + { + Name: "None Immediate - uint", + Error: "nil", OpCode: "CAST", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorUint, Minor: 1}, // uint16 -> uint8 + {Major: ast.DataTypeMajorUint, Minor: 1}, // uint16 -> uint24 + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(128)}, &Raw{Value: decimal.NewFromFloat(128)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorUint, Minor: 0}, + {Major: ast.DataTypeMajorUint, Minor: 2}, + }, + Data: []string{}, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorUint, Minor: 0}, + {Major: ast.DataTypeMajorUint, Minor: 2}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(128)}, &Raw{Value: decimal.NewFromFloat(128)}}", + }, + }, + }, + { + Name: "None Immediate - uint2", + Error: "nil", OpCode: "CAST", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorUint, Minor: 1}, // uint16 -> int16 + {Major: ast.DataTypeMajorUint, Minor: 1}, // uint16 -> byte16 + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(32767)}, &Raw{Value: decimal.NewFromFloat(32768)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 1}, + {Major: ast.DataTypeMajorFixedBytes, Minor: 1}, + }, + Data: []string{}, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 1}, + {Major: ast.DataTypeMajorFixedBytes, Minor: 1}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(32767)}, &Raw{Bytes: []byte{0x80,0x00}}}", + }, + }, + }, + { + Name: "None Immediate - uint3", + Error: "nil", OpCode: "CAST", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorUint, Minor: 1}, // uint16 -> bool + {Major: ast.DataTypeMajorUint, Minor: 1}, // uint16 -> bool + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(32767)}, &Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{}, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue, rawFalse}", + }, + }, + }, + { + Name: "None Immediate - uint4", + Error: "nil", OpCode: "CAST", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorUint, Minor: 1}, // uint16 -> bytes + {Major: ast.DataTypeMajorUint, Minor: 1}, // uint16 -> bytes + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(32767)}, &Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorFixedBytes, Minor: 1}, + {Major: ast.DataTypeMajorFixedBytes, Minor: 1}, + }, + Data: []string{}, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorFixedBytes, Minor: 1}, + {Major: ast.DataTypeMajorFixedBytes, Minor: 1}, + }, + Data: []string{ + "{&Raw{Bytes: []byte{0x7f, 0xff}}, &Raw{Bytes: []byte{0x00, 0x00}}}", + }, + }, + }, + { + Name: "None Immediate - uint5", + Error: "nil", OpCode: "CAST", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorUint, Minor: 1}, // uint16 -> address + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(32767)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorAddress, Minor: 1}, + }, + Data: []string{}, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorAddress, Minor: 1}, + }, + Data: []string{ + "{&Raw{Bytes: []byte{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x7f,0xff}}}", + }, + }, + }, + { + Name: "None Immediate - bytes", + Error: "nil", OpCode: "CAST", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorFixedBytes, Minor: 1}, // byte16 -> byte8 + {Major: ast.DataTypeMajorFixedBytes, Minor: 1}, // byte16 -> byte24 + }, + Data: []string{ + "{&Raw{Bytes: []byte{0xff, 0xff}}, &Raw{Bytes: []byte{0xff, 0xff}}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorFixedBytes, Minor: 0}, + {Major: ast.DataTypeMajorFixedBytes, Minor: 2}, + }, + Data: []string{}, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorFixedBytes, Minor: 0}, + {Major: ast.DataTypeMajorFixedBytes, Minor: 2}, + }, + Data: []string{ + "{&Raw{Bytes: []byte{0xff}}, &Raw{Bytes: []byte{0xff, 0xff, 0x00}}}", + }, + }, + }, + { + Name: "None Immediate - bytes2", + Error: "nil", OpCode: "CAST", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorFixedBytes, Minor: 1}, // byte16 -> int16 + {Major: ast.DataTypeMajorFixedBytes, Minor: 1}, // byte16 -> uint16 + }, + Data: []string{ + "{&Raw{Bytes: []byte{0x7f, 0xff}}, &Raw{Bytes: []byte{0x80, 0x00}}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 1}, + {Major: ast.DataTypeMajorUint, Minor: 1}, + }, + Data: []string{}, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 1}, + {Major: ast.DataTypeMajorUint, Minor: 1}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(32767)}, &Raw{Value: decimal.NewFromFloat(32768)}}", + }, + }, + }, + { + Name: "None Immediate - bytes3", + Error: "nil", OpCode: "CAST", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorFixedBytes, Minor: 1}, // byte16 -> dyn + }, + Data: []string{ + "{&Raw{Bytes: []byte{0x7f, 0xff}}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 1}, + }, + Data: []string{}, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 1}, + }, + Data: []string{ + "{&Raw{Bytes: []byte{0x7f, 0xff}}}", + }, + }, + }, + { + Name: "Same type", + Error: "nil", OpCode: "CAST", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{}, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + "{rawTrue}", + }, + }, + }, + { + Name: "Error Invalid Type", + Error: "errors.ErrorCodeInvalidCastType", OpCode: "CAST", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorInt, Minor: 2}, + }, + Data: []string{ + "{&Raw{Value: decimal.NewFromFloat(-32768)}}", + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + }, + Data: []string{}, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{}, + Data: []string{}, + }, + }, + }, + }, + // -- end of CAST + { + TestName: "OpSort", OpFunc: "opSort", + Cases: []tmplTestCase{ + { + Name: "Multi-column sorting", + Error: "nil", OpCode: "SORT", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + `{&Raw{Bytes: []byte("c")}, &Raw{Value: decimal.NewFromFloat(1)}, rawTrue}`, + `{&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(2)}, rawTrue}`, + `{&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(3)}, rawTrue}`, + `{&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(1)}, rawFalse}`, + `{&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(2)}, rawFalse}`, + `{&Raw{Bytes: []byte("c")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}`, + `{&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}`, + `{&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}`, + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{rawFalse, &Raw{Value: decimal.NewFromFloat(1)}}", + "{rawTrue, &Raw{Value: decimal.NewFromFloat(2)}}", + "{rawFalse, &Raw{Value: decimal.NewFromFloat(0)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + `{&Raw{Bytes: []byte("c")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}`, + `{&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}`, + `{&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}`, + `{&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(3)}, rawTrue}`, + `{&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(2)}, rawFalse}`, + `{&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(2)}, rawTrue}`, + `{&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(1)}, rawFalse}`, + `{&Raw{Bytes: []byte("c")}, &Raw{Value: decimal.NewFromFloat(1)}, rawTrue}`, + }, + }, + }, + { + Name: "Multi-column sorting - 2", + Error: "nil", OpCode: "SORT", + Inputs: []tmplOp{ + { + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + `{&Raw{Bytes: []byte("c")}, &Raw{Value: decimal.NewFromFloat(1)}, rawTrue}`, + `{&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(2)}, rawTrue}`, + `{&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(3)}, rawTrue}`, + `{&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(1)}, rawFalse}`, + `{&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(2)}, rawFalse}`, + `{&Raw{Bytes: []byte("c")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}`, + `{&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}`, + `{&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}`, + }, + }, + { + Im: true, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorBool, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + }, + Data: []string{ + "{rawTrue, &Raw{Value: decimal.NewFromFloat(0)}}", + "{rawTrue, &Raw{Value: decimal.NewFromFloat(1)}}", + "{rawFalse, &Raw{Value: decimal.NewFromFloat(2)}}", + }, + }, + }, + Output: tmplOp{ + Im: false, + Metas: []tmplOpMeta{ + {Major: ast.DataTypeMajorDynamicBytes, Minor: 0}, + {Major: ast.DataTypeMajorInt, Minor: 0}, + {Major: ast.DataTypeMajorBool, Minor: 0}, + }, + Data: []string{ + `{&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(1)}, rawFalse}`, + `{&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(3)}, rawTrue}`, + `{&Raw{Bytes: []byte("a")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}`, + `{&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(2)}, rawTrue}`, + `{&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(2)}, rawFalse}`, + `{&Raw{Bytes: []byte("b")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}`, + `{&Raw{Bytes: []byte("c")}, &Raw{Value: decimal.NewFromFloat(1)}, rawTrue}`, + `{&Raw{Bytes: []byte("c")}, &Raw{Value: decimal.NewFromFloat(3)}, rawFalse}`, + }, + }, + }, + }, + }, + // -- end of SORT + }, +} diff --git a/core/vm/sqlvm/runtime/jumptable.go b/core/vm/sqlvm/runtime/jumptable.go index 5ce6d9a8a..415a4e62b 100644 --- a/core/vm/sqlvm/runtime/jumptable.go +++ b/core/vm/sqlvm/runtime/jumptable.go @@ -1,6 +1,32 @@ package runtime var jumpTable = [256]OpFunction{ + // 0x10 + ADD: opAdd, + MUL: opMul, + SUB: opSub, + DIV: opDiv, + MOD: opMod, + + // 0x20 + LT: opLt, + GT: opGt, + EQ: opEq, + AND: opAnd, + OR: opOr, + NOT: opNot, + UNION: opUnion, + INTXN: opIntxn, + LIKE: opLike, + + // 0x40 + ZIP: opZip, + FIELD: opField, + PRUNE: opPrune, + SORT: opSort, + FILTER: opFilter, + CAST: opCast, + // 0x60 LOAD: opLoad, } diff --git a/core/vm/sqlvm/runtime/opcodes.go b/core/vm/sqlvm/runtime/opcodes.go index 22628b28e..88bc039e1 100644 --- a/core/vm/sqlvm/runtime/opcodes.go +++ b/core/vm/sqlvm/runtime/opcodes.go @@ -3,133 +3,54 @@ package runtime // OpCode type type OpCode byte +// 0x00 range - higher order ops. +const ( + NOP OpCode = iota +) + // 0x10 range - arithmetic ops. (array-based) const ( ADD OpCode = iota + 0x10 - // ADD(t1, t2) res - // res = t1 + t2 = [1, 2] + [2, 3] = [3, 5] MUL - // MUL(t1, t2) res - // res = t1 * t2 = [1, 2] * [2, 3] = [2, 6] SUB - // SUB(t1, t2) res - // res = t1 - t2 = [1, 2] - [2, 3] = [-1, -1] DIV - // DIV(t1, t2) res - // res = t1 / t2 = [1, 2] / [2, 3] = [0, 0] MOD - // MOD(t1, t2) res - // res = t1 % t2 = [1, 2] % [2, 3] = [1, 2] - ) // 0x20 range - comparison ops. const ( LT OpCode = iota + 0x20 - // LT(t1, t2) res - // res = t1 < t2 = [1, 2] < [2, 3] = [true, true] GT - // GT(t1, t2) res - // res = t1 > t2 = [1, 2] > [2, 3] = [false, false] EQ - // EQ(t1, t2) res - // res = t1 == t2 = [1, 2] == [2, 3] = [false, false] AND - // AND(t1, t2) res - // res = t1 && t2 = [true, true] && [true, false] = [true, false] OR - // OR(t1, t2) res - // res = t1 || t2 = [false, false] || [true, false] = [true, false] NOT - // NOT(t1) res - // res = !t1 = ![false, true] = [true, false] UNION - // UNION(t1, t2) res - // res = t1 ∪ t2 = [1, 2] ∪ [2, 3] = [1, 2, 3] INTXN - // INTXN(t1, t2) res - // res = t1 ∩ t2 = [1, 2] ∩ [2, 3] = [2] LIKE - // LIKE(t1, pattern) res - // res = t1 like '%abc%' = - // ['_abc_', '123'] like '%abc%' = [true, false] ) // 0x30 range - pk/index/field meta ops const ( REPEATPK OpCode = iota + 0x30 - // REPEATPK(pk) res res = [id1, id2, id3, ...] - // REPEATPK([tables, table_id, primary]) = [1, 2, 3, 5, 6, 7, ...] REPEATIDX - // Scan given index value(s) - // REPEATIDX(base, idxv) res res = [id2, id4, id5, id6] - // REPEATIDX( - // [tables, table_id, indices, name_idx], - // [val1, val3] - // ) = [5, 6, 7, 10, 11, ... ] REPEATIDXV - // Get possible values from index value meta - // REPEATIDXV(idx) res res = [val1, val2, val3, ...] - // REPEATIDXV( - // [tables, table_id, indices, name_idx] - // ) = ["alice", "bob", "foo", "bar", ... ] ) // 0x40 range - format/output ops const ( ZIP OpCode = iota + 0x40 - // ZIP(tgr, new) = res - // ZIP([f1, f2, f3], [c1, c2, c3]) = [(f1, c1), (f2, c2), (f3, c3)] - // ZIP( - // [(f1, c1), (f2, c2), (f3, c3)], - // [(x1, (y1)), (x2, (y2)), (x3, (y3))] - // ) = [(f1, c1, x1, (y1)), (f2, c2, x2, (y2)), (f3, c3, x2, (y2))] FIELD - // (src, fields) = res - // ( - // [(r1f1, r1f2, r1f3), (r2f1, r2f2, r2f3),...], [2,3] - // ) = [(r1f2, r1f3), (r2f2, r2f3), ...] + PRUNE SORT - // SORT(src, [(field, order, null order)] ) = res - // SORT( - // [(a1, a2, a3), (b1, a2, null), (a1, b2, null), ...], - // [(1, asc, null_first), (2, desc, null_last), (3, asc, null_last)] - // ) = [(a1, a2, a3), (a1, b2, null), (b1, a2, null), ...] FILTER - // FILTER(src, cond) = res - // FILTER([1, 2, 3, 4, 5], [true, false, true, false, false]) = [1, 3] CAST - // CAST(t1, types) t2 ) // 0x60 range - storage ops const ( - STOREPK OpCode = iota + 0x60 - // STOREPK(pk, [uint256, uint256, ...]) - LOADPK - // LOADPK(pk) array array = [uint256, uint256, ...] - STORE - // STORE(base, ids, values, fields, idxes) - // STORE( - // [tables, table_id, primary], - // [], - // [field1, field2, [field3.1, field3.2]], - // [] - // ) - // STORE( - // [tables, table_id, primary], - // [1, 2], - // [updated_field1, updated_field2], - // [1, 2] - // ) + INSERT = iota + 0x60 + UPDATE LOAD - // LOAD(base, ids, fields) array - // LOAD( - // [tables, table_id, primary], [1], [] - // ) = [(field1, field2, [field3.1, field3.2])] - // LOAD( - // [tables, table_id, primary, [1], [1,3] - // ) =[(field1, [field3.1, field3.2])] DELETE - // DELETE(base, ids, idxes) ) diff --git a/core/vm/sqlvm/runtime/runtime.go b/core/vm/sqlvm/runtime/runtime.go index 8c3a105ac..3b6d49a72 100644 --- a/core/vm/sqlvm/runtime/runtime.go +++ b/core/vm/sqlvm/runtime/runtime.go @@ -3,20 +3,23 @@ package runtime import ( "github.com/dexon-foundation/dexon/core/vm" "github.com/dexon-foundation/dexon/core/vm/sqlvm/common" - "github.com/dexon-foundation/dexon/core/vm/sqlvm/errors" + se "github.com/dexon-foundation/dexon/core/vm/sqlvm/errors" ) // Run is runtime entrypoint. func Run(stateDB vm.StateDB, ins []Instruction, registers []*Operand) (ret []byte, err error) { for _, in := range ins { - opFunc := jumpTable[in.Op] - loadRegister(in.Input, registers) - errCode := opFunc(&common.Context{}, in.Input, registers, in.Output) + for i := 0; i < len(in.Input); i++ { + if !in.Input[i].IsImmediate { + in.Input[i] = registers[in.Input[i].RegisterIndex] + } + } + errCode := jumpTable[in.Op](&common.Context{}, in.Input, registers, in.Output) if errCode != nil { - err = errors.Error{ + err = se.Error{ Position: in.Position, - Code: errCode.(errors.ErrorCode), - Category: errors.ErrorCategoryRuntime, + Code: errCode.(se.ErrorCode), + Category: se.ErrorCategoryRuntime, } return nil, err } @@ -24,11 +27,3 @@ func Run(stateDB vm.StateDB, ins []Instruction, registers []*Operand) (ret []byt // TODO: ret = ABIEncode(ins[len(ins)-1].Output) return } - -func loadRegister(input, registers []*Operand) { - for i, operand := range input { - if operand != nil && !operand.IsImmediate { - input[i] = registers[operand.RegisterIndex] - } - } -} |