diff --git a/vm/binop_inplace_test.go b/vm/binop_inplace_test.go index 873c48f..48b788d 100644 --- a/vm/binop_inplace_test.go +++ b/vm/binop_inplace_test.go @@ -10,6 +10,7 @@ import ( func TestBinopInplaceParsing(t *testing.T) { expected_type := "*vm.Instruction_BINOP_INPLACE" + ExpectParseSame(t, `BINOP_INPLACE add "sum"`, expected_type) ExpectParseSame(t, `BINOP_INPLACE add "foo"`, expected_type) ExpectParseSame(t, `BINOP_INPLACE subtract "foo"`, expected_type) ExpectParseSame(t, `BINOP_INPLACE multiply "foo"`, expected_type) diff --git a/vm/instruction_binop_test.go b/vm/instruction_binop_test.go index 68db7ea..8d9f267 100644 --- a/vm/instruction_binop_test.go +++ b/vm/instruction_binop_test.go @@ -1,13 +1,24 @@ package vm import ( + "fmt" "testing" + "yolk/types" ) func TestBinopParsing(t *testing.T) { expected_type := "*vm.Instruction_BINOP" - ExpectParse(t, "BINOP add", expected_type, "BINOP add") + ExpectParseSame(t, "BINOP add", expected_type) + ExpectParseSame(t, "BINOP subtract", expected_type) + ExpectParseSame(t, "BINOP multiply", expected_type) + ExpectParseSame(t, "BINOP divide", expected_type) + ExpectParseSame(t, "BINOP int_divide", expected_type) + ExpectParseSame(t, "BINOP power", expected_type) + ExpectParseSame(t, "BINOP modulus", expected_type) + ExpectParseSame(t, "BINOP concat", expected_type) + ExpectParseSame(t, "BINOP and", expected_type) + ExpectParseSame(t, "BINOP or", expected_type) ExpectParse(t, "BINOP add", expected_type, "BINOP add") ExpectParse(t, "BINOP add ", expected_type, "BINOP add") ExpectParse(t, " BINOP add ", expected_type, "BINOP add") @@ -16,351 +27,41 @@ func TestBinopParsing(t *testing.T) { ExpectParseFailure(t, "BINOP foo", `unexpected operator "foo"`) } -func TestBinopAdd(t *testing.T) { - program := []string{ - "PUSH_NUM 5", - "PUSH_NUM 8", - "PUSH_NUM 12", - "BINOP add", - "BINOP add", - } - expected := "25" - - vm := VirtualMachine{} - - for _, line := range program { - line_instruction, err := ParseInstruction(line) - if err != nil { - t.Fatalf("Error parsing instruction %q: %v", line_instruction, err) - } - if err := line_instruction.Perform(&vm); err != nil { - t.Fatalf("Unexpected error executing %q: %v", line, err) - } - } - if actual := vm.stack.Size(); actual != 1 { - t.Fatalf("Stack had %d items after operations, expected 1", actual) - } - if value, err := vm.stack.Pop(); err != nil { - t.Fatalf("Unexpected error popping stack: %v", err) - } else { - if num, err := value.RequireNum(); err != nil { - t.Fatalf("Output is not a number: %v", err) - } else if actual := num.Display(); actual != expected { - t.Fatalf("Calculating (12 + 8) + 5 gave %s, expected %s", actual, expected) - } - } -} - -func TestBinopSubtract(t *testing.T) { - program := []string{ - "PUSH_NUM 5", - "PUSH_NUM 8", - "PUSH_NUM 12", - "BINOP subtract", - "BINOP subtract", - } - expected := "-1" - - vm := VirtualMachine{} - - for _, line := range program { - line_instruction, err := ParseInstruction(line) - if err != nil { - t.Fatalf("Error parsing instruction %q: %v", line_instruction, err) - } - if err := line_instruction.Perform(&vm); err != nil { - t.Fatalf("Unexpected error executing %q: %v", line, err) - } - } - if actual := vm.stack.Size(); actual != 1 { - t.Fatalf("Stack had %d items after operations, expected 1", actual) - } - if value, err := vm.stack.Pop(); err != nil { - t.Fatalf("Unexpected error popping stack: %v", err) - } else { - if num, err := value.RequireNum(); err != nil { - t.Fatalf("Output is not a number: %v", err) - } else if actual := num.Display(); actual != expected { - t.Fatalf("Calculating (12 - 8) - 5 gave %s, expected %s", actual, expected) - } - } -} - -func TestBinopMuiltiply(t *testing.T) { - program := []string{ - "PUSH_NUM 5", - "PUSH_NUM 8", - "PUSH_NUM 12", - "BINOP multiply", - "BINOP multiply", - } - expected := "480" - - vm := VirtualMachine{} - - for _, line := range program { - line_instruction, err := ParseInstruction(line) - if err != nil { - t.Fatalf("Error parsing instruction %q: %v", line_instruction, err) - } - if err := line_instruction.Perform(&vm); err != nil { - t.Fatalf("Unexpected error executing %q: %v", line, err) - } - } - if actual := vm.stack.Size(); actual != 1 { - t.Fatalf("Stack had %d items after operations, expected 1", actual) - } - if value, err := vm.stack.Pop(); err != nil { - t.Fatalf("Unexpected error popping stack: %v", err) - } else { - if num, err := value.RequireNum(); err != nil { - t.Fatalf("Output is not a number: %v", err) - } else if actual := num.Display(); actual != expected { - t.Fatalf("Calculating (12 * 8) * 5 gave %s, expected %s", actual, expected) - } - } -} - -func TestBinopDivide(t *testing.T) { - program := []string{ - "PUSH_NUM 5", - "PUSH_NUM 8", - "PUSH_NUM 12", - "BINOP divide", - "BINOP divide", - } - expected := "0.3" - - vm := VirtualMachine{} - - for _, line := range program { - line_instruction, err := ParseInstruction(line) - if err != nil { - t.Fatalf("Error parsing instruction %q: %v", line_instruction, err) - } - if err := line_instruction.Perform(&vm); err != nil { - t.Fatalf("Unexpected error executing %q: %v", line, err) - } - } - if actual := vm.stack.Size(); actual != 1 { - t.Fatalf("Stack had %d items after operations, expected 1", actual) - } - if value, err := vm.stack.Pop(); err != nil { - t.Fatalf("Unexpected error popping stack: %v", err) - } else { - if num, err := value.RequireNum(); err != nil { - t.Fatalf("Output is not a number: %v", err) - } else if actual := num.Display(); actual != expected { - t.Fatalf("Calculating (12 / 8) / 5 gave %s, expected %s", actual, expected) - } - } +type BinOpTestCase struct { + operation string + lhs types.Primitive + rhs types.Primitive + result types.Primitive } -func TestBinopIntDivide(t *testing.T) { - program := []string{ - "PUSH_NUM 2", - "PUSH_NUM 10", - "PUSH_NUM 99", - "BINOP int_divide", - "BINOP int_divide", - } - expected := "4" - - vm := VirtualMachine{} - - for _, line := range program { - line_instruction, err := ParseInstruction(line) - if err != nil { - t.Fatalf("Error parsing instruction %q: %v", line_instruction, err) - } - if err := line_instruction.Perform(&vm); err != nil { - t.Fatalf("Unexpected error executing %q: %v", line, err) - } - } - if actual := vm.stack.Size(); actual != 1 { - t.Fatalf("Stack had %d items after operations, expected 1", actual) - } - if value, err := vm.stack.Pop(); err != nil { - t.Fatalf("Unexpected error popping stack: %v", err) - } else { - if num, err := value.RequireNum(); err != nil { - t.Fatalf("Output is not a number: %v", err) - } else if actual := num.Display(); actual != expected { - t.Fatalf("Calculating (99 // 10) // 2 gave %s, expected %s", actual, expected) - } - } -} -func TestBinopPower(t *testing.T) { - program := []string{ - "PUSH_NUM 2", - "PUSH_NUM 3", - "PUSH_NUM 4", - "BINOP power", - "BINOP power", - } - expected := "4096" - - vm := VirtualMachine{} - - for _, line := range program { - line_instruction, err := ParseInstruction(line) - if err != nil { - t.Fatalf("Error parsing instruction %q: %v", line_instruction, err) - } - if err := line_instruction.Perform(&vm); err != nil { - t.Fatalf("Unexpected error executing %q: %v", line, err) - } - } - if actual := vm.stack.Size(); actual != 1 { - t.Fatalf("Stack had %d items after operations, expected 1", actual) - } - if value, err := vm.stack.Pop(); err != nil { - t.Fatalf("Unexpected error popping stack: %v", err) - } else { - if num, err := value.RequireNum(); err != nil { - t.Fatalf("Output is not a number: %v", err) - } else if actual := num.Display(); actual != expected { - t.Fatalf("Calculating (4 ** 3) ** 2 gave %s, expected %s", actual, expected) - } - } -} - -func TestBinopMod(t *testing.T) { - program := []string{ - "PUSH_NUM 12", - "PUSH_NUM 30", - "PUSH_NUM 44", - "BINOP modulus", - "BINOP modulus", - } - expected := "2" - - vm := VirtualMachine{} - - for _, line := range program { - line_instruction, err := ParseInstruction(line) - if err != nil { - t.Fatalf("Error parsing instruction %q: %v", line_instruction, err) - } - if err := line_instruction.Perform(&vm); err != nil { - t.Fatalf("Unexpected error executing %q: %v", line, err) - } - } - if actual := vm.stack.Size(); actual != 1 { - t.Fatalf("Stack had %d items after operations, expected 1", actual) - } - if value, err := vm.stack.Pop(); err != nil { - t.Fatalf("Unexpected error popping stack: %v", err) - } else { - if num, err := value.RequireNum(); err != nil { - t.Fatalf("Output is not a number: %v", err) - } else if actual := num.Display(); actual != expected { - t.Fatalf("Calculating (44 mod 30) mod 12 gave %s, expected %s", actual, expected) - } - } -} - -func TestBinopConcat(t *testing.T) { - program := []string{ - `PUSH_STR "bar"`, - "PUSH_NUM 0", - `PUSH_STR "fo"`, - "BINOP concat", - "BINOP concat", - } - expected := "fo0bar" - - vm := VirtualMachine{} - - for _, line := range program { - line_instruction, err := ParseInstruction(line) - if err != nil { - t.Fatalf("Error parsing instruction %q: %v", line_instruction, err) - } - if err := line_instruction.Perform(&vm); err != nil { - t.Fatalf("Unexpected error executing %q: %v", line, err) - } - } - if actual := vm.stack.Size(); actual != 1 { - t.Fatalf("Stack had %d items after operations, expected 1", actual) - } - if value, err := vm.stack.Pop(); err != nil { - t.Fatalf("Unexpected error popping stack: %v", err) - } else { - if str, err := value.RequireStr(); err != nil { - t.Fatalf("Output is not a string: %v", err) - } else if actual := str.Display(); actual != expected { - t.Fatalf("Calculating (44 mod 30) mod 12 gave %s, expected %s", actual, expected) - } - } -} - -func TestBinopAnd(t *testing.T) { - program := []string{ - `PUSH_BOOL true`, - "PUSH_BOOL true", - `PUSH_BOOL false`, - "BINOP and", - "BINOP and", - } - expected := "false" - - vm := VirtualMachine{} - - for _, line := range program { - line_instruction, err := ParseInstruction(line) - if err != nil { - t.Fatalf("Error parsing instruction %q: %v", line_instruction, err) - } - if err := line_instruction.Perform(&vm); err != nil { - t.Fatalf("Unexpected error executing %q: %v", line, err) - } - } - if actual := vm.stack.Size(); actual != 1 { - t.Fatalf("Stack had %d items after operations, expected 1", actual) - } - if value, err := vm.stack.Pop(); err != nil { - t.Fatalf("Unexpected error popping stack: %v", err) - } else { - if as_bool, err := value.RequireBool(); err != nil { - t.Fatalf("Output is not a string: %v", err) - } else if actual := as_bool.Display(); actual != expected { - t.Fatalf("Calculating (true && true) && false gave %s, expected %s", actual, expected) - } - } -} - -func TestBinopOr(t *testing.T) { - program := []string{ - `PUSH_BOOL true`, - "PUSH_BOOL true", - `PUSH_BOOL false`, - "BINOP or", - "BINOP or", - } - expected := "true" - - vm := VirtualMachine{} - - for _, line := range program { - line_instruction, err := ParseInstruction(line) - if err != nil { - t.Fatalf("Error parsing instruction %q: %v", line_instruction, err) - } - if err := line_instruction.Perform(&vm); err != nil { - t.Fatalf("Unexpected error executing %q: %v", line, err) - } - } - if actual := vm.stack.Size(); actual != 1 { - t.Fatalf("Stack had %d items after operations, expected 1", actual) - } - if value, err := vm.stack.Pop(); err != nil { - t.Fatalf("Unexpected error popping stack: %v", err) - } else { - if as_bool, err := value.RequireBool(); err != nil { - t.Fatalf("Output is not a string: %v", err) - } else if actual := as_bool.Display(); actual != expected { - t.Fatalf("Calculating (true || true) || false gave %s, expected %s", actual, expected) +func TestBinop(t *testing.T) { + tests := []BinOpTestCase{ + {"add", RequireNum(t, "10"), RequireNum(t, "5"), RequireNum(t, "15")}, + {"subtract", RequireNum(t, "10"), RequireNum(t, "5"), RequireNum(t, "5")}, + {"multiply", RequireNum(t, "11"), RequireNum(t, "5"), RequireNum(t, "55")}, + {"divide", RequireNum(t, "12"), RequireNum(t, "5"), RequireNum(t, "2.4")}, + {"int_divide", RequireNum(t, "12"), RequireNum(t, "5"), RequireNum(t, "2")}, + {"power", RequireNum(t, "12"), RequireNum(t, "5"), RequireNum(t, "248832")}, + {"modulus", RequireNum(t, "12"), RequireNum(t, "5"), RequireNum(t, "2")}, + {"concat", types.MakeString("foo"), types.MakeString("bar"), types.MakeString("foobar")}, + {"and", types.MakeBool(true), types.MakeBool(false), types.MakeBool(false)}, + {"or", types.MakeBool(true), types.MakeBool(false), types.MakeBool(true)}, + } + + for _, test := range tests { + vm := NewVM() + vm.stack.Push(test.rhs) + vm.stack.Push(test.lhs) + instruction := fmt.Sprintf("BINOP %s", test.operation) + if err := RequireParse(t, instruction).Perform(&vm); err != nil { + t.Fatalf("Unexpected error performing %q: %v", instruction, err) + } else if vm.stack.Size() != 1 { + t.Fatalf("Expected stack size after BINOP to be %d, got %d", 1, vm.stack.Size()) + } else if value, err := vm.stack.Pop(); err != nil { + t.Fatalf("Unexpected error performing popping stack after BINOP: %v", err) + } else if !value.Equal(test.result) { + t.Fatalf("Expected %q with values %q and %q to give %q, instead got %q", + instruction, test.lhs.Display(), test.rhs.Display(), test.result.Display(), value.Display()) } } }