|
|
|
|
@ -1,16 +1,18 @@
|
|
|
|
|
// FILE: interpreter_test.go
|
|
|
|
|
package interpreter_test
|
|
|
|
|
package interpreter
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"golox/ast"
|
|
|
|
|
"golox/errors"
|
|
|
|
|
"golox/interpreter"
|
|
|
|
|
"golox/token"
|
|
|
|
|
"io"
|
|
|
|
|
"os"
|
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestInterpretLiteralExpr(t *testing.T) {
|
|
|
|
|
i := interpreter.New(errors.NewMockErrorLogger())
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
literal := &ast.LiteralExpr{Value: 42}
|
|
|
|
|
|
|
|
|
|
result := i.VisitLiteralExpr(literal)
|
|
|
|
|
@ -20,7 +22,7 @@ func TestInterpretLiteralExpr(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretGroupingExpr(t *testing.T) {
|
|
|
|
|
i := interpreter.New(errors.NewMockErrorLogger())
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
literal := &ast.LiteralExpr{Value: 42}
|
|
|
|
|
grouping := &ast.GroupingExpr{Expression: literal}
|
|
|
|
|
|
|
|
|
|
@ -31,7 +33,7 @@ func TestInterpretGroupingExpr(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretUnaryExpr(t *testing.T) {
|
|
|
|
|
i := interpreter.New(errors.NewMockErrorLogger())
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
literal := &ast.LiteralExpr{Value: 42.0}
|
|
|
|
|
unary := &ast.UnaryExpr{
|
|
|
|
|
Operator: token.Token{Type: token.MINUS, Lexeme: "-"},
|
|
|
|
|
@ -45,7 +47,7 @@ func TestInterpretUnaryExpr(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretUnaryExprBang(t *testing.T) {
|
|
|
|
|
i := interpreter.New(errors.NewMockErrorLogger())
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
literal := &ast.LiteralExpr{Value: true}
|
|
|
|
|
unary := &ast.UnaryExpr{
|
|
|
|
|
Operator: token.Token{Type: token.BANG, Lexeme: "!"},
|
|
|
|
|
@ -59,7 +61,7 @@ func TestInterpretUnaryExprBang(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretErrorExpr(t *testing.T) {
|
|
|
|
|
i := interpreter.New(errors.NewMockErrorLogger())
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
errorExpr := &ast.ErrorExpr{Value: "error"}
|
|
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
|
@ -72,7 +74,7 @@ func TestInterpretErrorExpr(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretExpr(t *testing.T) {
|
|
|
|
|
i := interpreter.New(errors.NewMockErrorLogger())
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
literal := &ast.LiteralExpr{Value: 42.0}
|
|
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
|
@ -88,7 +90,7 @@ func TestInterpretExpr(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretBinaryExpr(t *testing.T) {
|
|
|
|
|
i := interpreter.New(errors.NewMockErrorLogger())
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
left := &ast.LiteralExpr{Value: 42.0}
|
|
|
|
|
right := &ast.LiteralExpr{Value: 2.0}
|
|
|
|
|
binary := &ast.BinaryExpr{
|
|
|
|
|
@ -104,7 +106,7 @@ func TestInterpretBinaryExpr(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretBinaryExprDivisionByZero(t *testing.T) {
|
|
|
|
|
i := interpreter.New(errors.NewMockErrorLogger())
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
left := &ast.LiteralExpr{Value: 42.0}
|
|
|
|
|
right := &ast.LiteralExpr{Value: 0.0}
|
|
|
|
|
binary := &ast.BinaryExpr{
|
|
|
|
|
@ -123,7 +125,7 @@ func TestInterpretBinaryExprDivisionByZero(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretBinaryExprAddition(t *testing.T) {
|
|
|
|
|
i := interpreter.New(errors.NewMockErrorLogger())
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
left := &ast.LiteralExpr{Value: 42.0}
|
|
|
|
|
right := &ast.LiteralExpr{Value: 2.0}
|
|
|
|
|
binary := &ast.BinaryExpr{
|
|
|
|
|
@ -139,7 +141,7 @@ func TestInterpretBinaryExprAddition(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretBinaryExprSubtraction(t *testing.T) {
|
|
|
|
|
i := interpreter.New(errors.NewMockErrorLogger())
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
left := &ast.LiteralExpr{Value: 42.0}
|
|
|
|
|
right := &ast.LiteralExpr{Value: 2.0}
|
|
|
|
|
binary := &ast.BinaryExpr{
|
|
|
|
|
@ -155,7 +157,7 @@ func TestInterpretBinaryExprSubtraction(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretBinaryExprStringConcatenation(t *testing.T) {
|
|
|
|
|
i := interpreter.New(errors.NewMockErrorLogger())
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
left := &ast.LiteralExpr{Value: "foo"}
|
|
|
|
|
right := &ast.LiteralExpr{Value: "bar"}
|
|
|
|
|
binary := &ast.BinaryExpr{
|
|
|
|
|
@ -171,7 +173,7 @@ func TestInterpretBinaryExprStringConcatenation(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretBinaryExprInvalidOperands(t *testing.T) {
|
|
|
|
|
i := interpreter.New(errors.NewMockErrorLogger())
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
left := &ast.LiteralExpr{Value: "foo"}
|
|
|
|
|
right := &ast.LiteralExpr{Value: 42.0}
|
|
|
|
|
binary := &ast.BinaryExpr{
|
|
|
|
|
@ -190,7 +192,7 @@ func TestInterpretBinaryExprInvalidOperands(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretBinaryExprComparison(t *testing.T) {
|
|
|
|
|
i := interpreter.New(errors.NewMockErrorLogger())
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
left := &ast.LiteralExpr{Value: 42.0}
|
|
|
|
|
right := &ast.LiteralExpr{Value: 2.0}
|
|
|
|
|
binary := &ast.BinaryExpr{
|
|
|
|
|
@ -206,7 +208,7 @@ func TestInterpretBinaryExprComparison(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretBinaryExprComparisonEqual(t *testing.T) {
|
|
|
|
|
i := interpreter.New(errors.NewMockErrorLogger())
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
left := &ast.LiteralExpr{Value: 42.0}
|
|
|
|
|
right := &ast.LiteralExpr{Value: 42.0}
|
|
|
|
|
binary := &ast.BinaryExpr{
|
|
|
|
|
@ -222,7 +224,7 @@ func TestInterpretBinaryExprComparisonEqual(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretBinaryExprComparisonNotEqual(t *testing.T) {
|
|
|
|
|
i := interpreter.New(errors.NewMockErrorLogger())
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
left := &ast.LiteralExpr{Value: 42.0}
|
|
|
|
|
right := &ast.LiteralExpr{Value: 2.0}
|
|
|
|
|
binary := &ast.BinaryExpr{
|
|
|
|
|
@ -238,7 +240,7 @@ func TestInterpretBinaryExprComparisonNotEqual(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretBinaryExprComparisonInvalidOperands(t *testing.T) {
|
|
|
|
|
i := interpreter.New(errors.NewMockErrorLogger())
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
left := &ast.LiteralExpr{Value: "foo"}
|
|
|
|
|
right := &ast.LiteralExpr{Value: 42.0}
|
|
|
|
|
binary := &ast.BinaryExpr{
|
|
|
|
|
@ -257,7 +259,7 @@ func TestInterpretBinaryExprComparisonInvalidOperands(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretBinaryExprInvalidOperatorType(t *testing.T) {
|
|
|
|
|
i := interpreter.New(errors.NewMockErrorLogger())
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
left := &ast.LiteralExpr{Value: 42.0}
|
|
|
|
|
right := &ast.LiteralExpr{Value: 2.0}
|
|
|
|
|
binary := &ast.BinaryExpr{
|
|
|
|
|
@ -274,3 +276,65 @@ func TestInterpretBinaryExprInvalidOperatorType(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
i.VisitBinaryExpr(binary)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretExprStatement(t *testing.T) {
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
literal := &ast.LiteralExpr{Value: 42.0}
|
|
|
|
|
exprStmt := &ast.ExpressionStmt{Expression: literal}
|
|
|
|
|
|
|
|
|
|
result := i.VisitExpressionStmt(exprStmt)
|
|
|
|
|
if result != nil {
|
|
|
|
|
t.Errorf("expected nil, got %v", result)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretPrintStatement(t *testing.T) {
|
|
|
|
|
old := os.Stdout // keep backup of the real stdout
|
|
|
|
|
r, w, err := os.Pipe()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
os.Stdout = w
|
|
|
|
|
|
|
|
|
|
outC := make(chan string)
|
|
|
|
|
// copy the output in a separate goroutine so printing can't block indefinitely
|
|
|
|
|
go func() {
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
|
io.Copy(&buf, r)
|
|
|
|
|
outC <- buf.String()
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
literal := &ast.LiteralExpr{Value: 42.0}
|
|
|
|
|
printStmt := &ast.PrintStmt{Expression: literal}
|
|
|
|
|
|
|
|
|
|
result := i.VisitPrintStmt(printStmt)
|
|
|
|
|
if result != nil {
|
|
|
|
|
t.Errorf("expected nil, got %v", result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// back to normal state
|
|
|
|
|
w.Close()
|
|
|
|
|
os.Stdout = old // restoring the real stdout
|
|
|
|
|
out := <-outC
|
|
|
|
|
|
|
|
|
|
// reading our temp stdout
|
|
|
|
|
expected := "42\n"
|
|
|
|
|
if out != expected {
|
|
|
|
|
t.Errorf("run() = %v; want %v", out, expected)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterpretVarStatement(t *testing.T) {
|
|
|
|
|
i := New(errors.NewMockErrorLogger())
|
|
|
|
|
varStmt := &ast.VarStmt{
|
|
|
|
|
Name: token.Token{Type: token.IDENTIFIER, Lexeme: "foo"},
|
|
|
|
|
Initializer: &ast.LiteralExpr{Value: 42.0},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
i.VisitVarStmt(varStmt)
|
|
|
|
|
result := i.env.get("foo")
|
|
|
|
|
if result != 42.0 {
|
|
|
|
|
t.Errorf("expected 42, got %v", result)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|