Skip to content

Commit

Permalink
feat(eval): return statement
Browse files Browse the repository at this point in the history
vit0rr committed Oct 2, 2024
1 parent e282258 commit 3dba307
Showing 3 changed files with 116 additions and 4 deletions.
37 changes: 36 additions & 1 deletion evaluator/evaluator.go
Original file line number Diff line number Diff line change
@@ -23,9 +23,12 @@ func Eval(node ast.Node) object.Object {
right := Eval(node.Right)
return evalPrefixExpression(node.Operator, right)
case *ast.BlockStatement:
return evalStatements(node.Statements)
return evalBlockStatement(node)
case *ast.IfExpression:
return evalIfExpression(node)
case *ast.ReturnStatement:
val := Eval(node.ReturnValue)
return &object.ReturnValue{Value: val}
case *ast.IntegerLiteral:
return &object.Integer{Value: node.Value}
case *ast.InfixExpression:
@@ -37,6 +40,34 @@ func Eval(node ast.Node) object.Object {
return nil
}

func evalBlockStatement(block *ast.BlockStatement) object.Object {
var result object.Object

for _, statement := range block.Statements {
result = Eval(statement)

if result != nil && result.Type() == object.RETURN_VALUE_OBJ {
return result
}
}

return result
}

func evalProgram(program *ast.Program) object.Object {
var result object.Object

for _, statement := range program.Statements {
result = Eval(statement)

if returnValue, ok := result.(*object.ReturnValue); ok {
return returnValue.Value
}
}

return result
}

func evalIfExpression(ie *ast.IfExpression) object.Object {
condition := Eval(ie.Condition)

@@ -147,6 +178,10 @@ func evalStatements(stmts []ast.Statement) object.Object {

for _, statement := range stmts {
result = Eval(statement)

if returnValue, ok := result.(*object.ReturnValue); ok {
return returnValue.Value
}
}

return result
61 changes: 61 additions & 0 deletions evaluator/evaluator_test.go
Original file line number Diff line number Diff line change
@@ -113,6 +113,67 @@ func TestIfElseExpressions(t *testing.T) {
}
}

func TestReturnStatements(t *testing.T) {
tests := []struct {
input string
exepcted int64
}{
{"return 10;", 10},
{"return 10; 9;", 10},
{"return 2 * 5; 9;", 10},
{"9; return 2 * 5; 9;", 10},
{`
if (10 > 1) {
if (10 > 1) {
return 10;
}
return 1;
}
`, 10},
}

for _, tt := range tests {
evaluated := testEval(tt.input)
testIntegerObject(t, evaluated, tt.exepcted)
}
}

func TestErrorHandling(t *testing.T) {
tests := []struct {
input string
expectedMessage string
}{
{"5 + true;", "type mismatch: INTEGER + BOOLEAN"},
{"5 + true; 5;", "type mismatch: INTEGER + BOOLEAN"},
{"-true", "unknown operator: -BOOLEAN"},
{"true + false;", "unknown operator: BOOLEAN + BOOLEAN"},
{"5; true + false; 5", "unknown operator: BOOLEAN + BOOLEAN"},
{"if (10 > 1) { true + false; }", "unknown operator: BOOLEAN + BOOLEAN"},
{`
if (10 > 1) {
if (10 > 1) {
return true + false;
}
return 1;
`, "unknown operator: BOOLEAN + BOOLEAN"},
}

for _, tt := range tests {
evaluated := testEval(tt.input)

errObj, ok := evaluated.(*object.Error)
if !ok {
t.Errorf("no error object returned. got=%T (%+v)", evaluated, evaluated)
continue
}

if errObj.Message != tt.expectedMessage {
t.Errorf("wrong error message. expected=%q, got=%q", tt.expectedMessage, errObj.Message)
}
}
}

func testNullObject(t *testing.T, obj object.Object) bool {
if obj != NULL {
t.Errorf("object is not NULL. got=%T (%+v)", obj, obj)
22 changes: 19 additions & 3 deletions object/object.go
Original file line number Diff line number Diff line change
@@ -5,9 +5,11 @@ import "fmt"
type ObjectType string

const (
INTEGER_OBJ = "INTEGER"
BOOLEAN_OBJ = "BOOLEAN"
NULL_OBJ = "NULL"
INTEGER_OBJ = "INTEGER"
BOOLEAN_OBJ = "BOOLEAN"
NULL_OBJ = "NULL"
RETURN_VALUE_OBJ = "RETURN_VALUE"
ERROR_OBJ = "ERROR"
)

type Object interface {
@@ -33,3 +35,17 @@ type Null struct{}

func (n *Null) Type() ObjectType { return NULL_OBJ }
func (n *Null) Inspect() string { return "null" }

type ReturnValue struct {
Value Object
}

func (rv *ReturnValue) Type() ObjectType { return RETURN_VALUE_OBJ }
func (rv *ReturnValue) Inspect() string { return rv.Value.Inspect() }

type Error struct {
Message string
}

func (e *Error) Type() ObjectType { return ERROR_OBJ }
func (e *Error) Inspect() string { return "ERROR: " + e.Message }

0 comments on commit 3dba307

Please sign in to comment.