package vm import ( "fmt" "github/runnignwater/monkey/ast" "github/runnignwater/monkey/compiler" "github/runnignwater/monkey/lexer" "github/runnignwater/monkey/object" "github/runnignwater/monkey/parser" "testing" ) type vmTestCase struct { input string expected interface{} } func TestIntegerArithmetic(t *testing.T) { tests := []vmTestCase{ {"1", 1}, {"2", 2}, {"1 + 2", 3}, {"1 - 2", -1}, {"1 * 2", 2}, {"4 / 2", 2}, {"50 / 2 * 2 + 10 - 5", 55}, {"5 * (2 + 10)", 60}, {"5 + 5 + 5 + 5 - 10", 10}, {"2 * 2 * 2 * 2 * 2", 32}, {"5 * 2 + 10", 20}, {"5 + 2 * 10", 25}, {"5 * (2 + 10)", 60}, {"-5", -5}, {"-10", -10}, {"-50 + 100 + -50", 0}, {"(5 + 10 * 2 + 15 / 3) * 2 + -10", 50}, } runVmTests(t, tests) } func TestBooleanExpression(t *testing.T) { tests := []vmTestCase{ {"true", true}, {"false", false}, {"1 < 2", true}, {"1 > 2", false}, {"1 < 1", false}, {"1 > 1", false}, {"1 == 1", true}, {"1 != 1", false}, {"true == true", true}, {"false == false", true}, {"true == false", false}, {"false != true", true}, {"(1 < 2) == true", true}, {"(1 < 2) == false", false}, {"!true", false}, {"!false", true}, {"!5", false}, {"!!true", true}, {"!!false", false}, {"!!5", true}, } runVmTests(t, tests) } func runVmTests(t *testing.T, tests []vmTestCase) { t.Helper() for _, tt := range tests { program := parse(tt.input) comp := compiler.New() err := comp.Compile(program) if err != nil { t.Fatalf("compler error: %s", err) } vm := New(comp.ByteCode()) err = vm.Run() if err != nil { t.Fatalf("vm error: %s", err) } stackElem := vm.LastPopStackElem() testExpectedObject(t, tt.expected, stackElem) } } func parse(input string) *ast.Program { l := lexer.New(input) p := parser.New(l) return p.ParseProgram() } func testExpectedObject( t *testing.T, expected interface{}, actual object.Object, ) { t.Helper() switch expected := expected.(type) { case int: err := testIntegerObject(int64(expected), actual) if err != nil { t.Errorf("testIntegerObject failed: %s", err) } case bool: err := testBooleanObject(expected, actual) if err != nil { t.Errorf("testBooleanObject failed: %s", err) } } } func testIntegerObject(expected int64, actual object.Object) error { result, ok := actual.(*object.Integer) if !ok { return fmt.Errorf("object is not Integer. got=%T (%+v)", actual, actual) } if result.Value != expected { return fmt.Errorf("object has wrong value. got=%d, want=%d", result.Value, expected) } return nil } func testBooleanObject(expected bool, actual object.Object) error { result, ok := actual.(*object.Boolean) if !ok { return fmt.Errorf("object is not Boolean. got=%T (%+v)", actual, actual) } if result.Value != expected { return fmt.Errorf("object has wrong value. got=%t, want=%t", result.Value, expected) } return nil }