You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
276 lines
6.4 KiB
Go
276 lines
6.4 KiB
Go
// 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)
|
|
}
|