// FILE: interpreter_test.go package interpreter_test import ( "golox/ast" "golox/interpreter" "golox/token" "testing" ) func TestInterpretLiteralExpr(t *testing.T) { i := interpreter.New() literal := &ast.LiteralExpr{Value: 42} result := i.VisitLiteralExpr(literal) if result != 42 { t.Errorf("expected 42, got %v", result) } } func TestInterpretGroupingExpr(t *testing.T) { i := interpreter.New() literal := &ast.LiteralExpr{Value: 42} grouping := &ast.GroupingExpr{Expression: literal} result := i.VisitGroupingExpr(grouping) if result != 42 { t.Errorf("expected 42, got %v", result) } } func TestInterpretUnaryExpr(t *testing.T) { i := interpreter.New() literal := &ast.LiteralExpr{Value: 42.0} unary := &ast.UnaryExpr{ Operator: token.Token{Type: token.MINUS, Lexeme: "-"}, Right: literal, } result := i.VisitUnaryExpr(unary) if result != -42.0 { t.Errorf("expected -42, got %v", result) } } func TestInterpretUnaryExprBang(t *testing.T) { i := interpreter.New() literal := &ast.LiteralExpr{Value: true} unary := &ast.UnaryExpr{ Operator: token.Token{Type: token.BANG, Lexeme: "!"}, Right: literal, } result := i.VisitUnaryExpr(unary) if result != false { t.Errorf("expected false, got %v", result) } } func TestInterpretErrorExpr(t *testing.T) { i := interpreter.New() errorExpr := &ast.ErrorExpr{Value: "error"} defer func() { if r := recover(); r != "error" { t.Errorf("expected panic with 'error', got %v", r) } }() i.VisitErrorExpr(errorExpr) } func TestInterpretExpr(t *testing.T) { i := interpreter.New() literal := &ast.LiteralExpr{Value: 42.0} defer func() { if r := recover(); r != nil { t.Errorf("unexpected panic: %v", r) } }() result := i.InterpretExpr(literal) if result != "42" { t.Errorf("expected '42', got %v", result) } } func TestInterpretBinaryExpr(t *testing.T) { i := interpreter.New() left := &ast.LiteralExpr{Value: 42.0} right := &ast.LiteralExpr{Value: 2.0} binary := &ast.BinaryExpr{ Left: left, Operator: token.Token{Type: token.STAR, Lexeme: "*"}, Right: right, } result := i.VisitBinaryExpr(binary) if result != 84.0 { t.Errorf("expected 84, got %v", result) } } func TestInterpretBinaryExprDivisionByZero(t *testing.T) { i := interpreter.New() left := &ast.LiteralExpr{Value: 42.0} right := &ast.LiteralExpr{Value: 0.0} binary := &ast.BinaryExpr{ Left: left, Operator: token.Token{Type: token.SLASH, Lexeme: "/"}, Right: right, } defer func() { if r := recover(); r != "Division by zero [line 0]" { t.Errorf("expected panic with 'division by zero', got %v", r) } }() i.VisitBinaryExpr(binary) } func TestInterpretBinaryExprAddition(t *testing.T) { i := interpreter.New() left := &ast.LiteralExpr{Value: 42.0} right := &ast.LiteralExpr{Value: 2.0} binary := &ast.BinaryExpr{ Left: left, Operator: token.Token{Type: token.PLUS, Lexeme: "+"}, Right: right, } result := i.VisitBinaryExpr(binary) if result != 44.0 { t.Errorf("expected 44, got %v", result) } } func TestInterpretBinaryExprSubtraction(t *testing.T) { i := interpreter.New() left := &ast.LiteralExpr{Value: 42.0} right := &ast.LiteralExpr{Value: 2.0} binary := &ast.BinaryExpr{ Left: left, Operator: token.Token{Type: token.MINUS, Lexeme: "-"}, Right: right, } result := i.VisitBinaryExpr(binary) if result != 40.0 { t.Errorf("expected 40, got %v", result) } } func TestInterpretBinaryExprStringConcatenation(t *testing.T) { i := interpreter.New() left := &ast.LiteralExpr{Value: "foo"} right := &ast.LiteralExpr{Value: "bar"} binary := &ast.BinaryExpr{ Left: left, Operator: token.Token{Type: token.PLUS, Lexeme: "+"}, Right: right, } result := i.VisitBinaryExpr(binary) if result != "foobar" { t.Errorf("expected 'foobar', got %v", result) } } func TestInterpretBinaryExprInvalidOperands(t *testing.T) { i := interpreter.New() left := &ast.LiteralExpr{Value: "foo"} right := &ast.LiteralExpr{Value: 42.0} binary := &ast.BinaryExpr{ Left: left, Operator: token.Token{Type: token.PLUS, Lexeme: "+"}, Right: right, } defer func() { if r := recover(); r != "Operands must be two numbers or two strings [line 0]" { t.Errorf("expected panic with 'operands must be two numbers or two strings', got %v", r) } }() i.VisitBinaryExpr(binary) } func TestInterpretBinaryExprComparison(t *testing.T) { i := interpreter.New() left := &ast.LiteralExpr{Value: 42.0} right := &ast.LiteralExpr{Value: 2.0} binary := &ast.BinaryExpr{ Left: left, Operator: token.Token{Type: token.GREATER, Lexeme: ">"}, Right: right, } result := i.VisitBinaryExpr(binary) if result != true { t.Errorf("expected true, got %v", result) } } func TestInterpretBinaryExprComparisonEqual(t *testing.T) { i := interpreter.New() left := &ast.LiteralExpr{Value: 42.0} right := &ast.LiteralExpr{Value: 42.0} binary := &ast.BinaryExpr{ Left: left, Operator: token.Token{Type: token.EQUAL_EQUAL, Lexeme: "=="}, Right: right, } result := i.VisitBinaryExpr(binary) if result != true { t.Errorf("expected true, got %v", result) } } func TestInterpretBinaryExprComparisonNotEqual(t *testing.T) { i := interpreter.New() left := &ast.LiteralExpr{Value: 42.0} right := &ast.LiteralExpr{Value: 2.0} binary := &ast.BinaryExpr{ Left: left, Operator: token.Token{Type: token.BANG_EQUAL, Lexeme: "!="}, Right: right, } result := i.VisitBinaryExpr(binary) if result != true { t.Errorf("expected true, got %v", result) } } func TestInterpretBinaryExprComparisonInvalidOperands(t *testing.T) { i := interpreter.New() left := &ast.LiteralExpr{Value: "foo"} right := &ast.LiteralExpr{Value: 42.0} binary := &ast.BinaryExpr{ Left: left, Operator: token.Token{Type: token.GREATER, Lexeme: ">"}, Right: right, } defer func() { if r := recover(); r != "Operands of operator '>' must be numbers [line 0]" { t.Errorf("expected panic with 'operands must be numbers', got %v", r) } }() i.VisitBinaryExpr(binary) } func TestInterpretBinaryExprInvalidOperatorType(t *testing.T) { i := interpreter.New() left := &ast.LiteralExpr{Value: 42.0} right := &ast.LiteralExpr{Value: 2.0} binary := &ast.BinaryExpr{ Left: left, Operator: token.Token{Type: token.EOF, Lexeme: ""}, Right: right, } defer func() { if r := recover(); r != "Unknown binary operator '' [line 0]" { t.Errorf("expected panic with 'unknown operator type', got %v", r) } }() i.VisitBinaryExpr(binary) }