commit d879cb5ebc90f1eb8925311f0695c77534f69c10
parent ed5362ca03dff4a3b9d7b3c0958a450b9a46db7d
Author: bsandro <email@bsandro.tech>
Date: Fri, 24 Jun 2022 01:06:02 +0300
eval return statements support
Diffstat:
3 files changed, 68 insertions(+), 9 deletions(-)
diff --git a/eval/eval.go b/eval/eval.go
@@ -22,7 +22,7 @@ func boolToObject(value bool) *object.Boolean {
func Eval(node ast.Node) object.Object {
switch node := node.(type) {
case *ast.Program:
- return evalStatements(node.Statements)
+ return evalProgram(node)
case *ast.ExpressionStatement:
return Eval(node.Expression)
case *ast.IntegerLiteral:
@@ -37,9 +37,12 @@ func Eval(node ast.Node) object.Object {
right := Eval(node.Right)
return evalInfixExpression(node.Operator, left, right)
case *ast.BlockStatement:
- return evalStatements(node.Statements)
+ return evalBlockStatement(node)
case *ast.IfExpression:
return evalIfExpression(node)
+ case *ast.ReturnStatement:
+ value := Eval(node.ReturnValue)
+ return &object.ReturnValue{Value: value}
}
return nil
}
@@ -48,6 +51,9 @@ func evalStatements(statements []ast.Statement) object.Object {
var result object.Object
for _, statement := range statements {
result = Eval(statement)
+ if returnValue, ok := result.(*object.ReturnValue); ok {
+ return returnValue.Value
+ }
}
return result
}
@@ -145,3 +151,25 @@ func isTruthy(obj object.Object) bool {
return true
}
}
+
+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 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
+}
diff --git a/eval/eval_test.go b/eval/eval_test.go
@@ -131,3 +131,23 @@ func testNullObject(t *testing.T, obj object.Object) bool {
}
return true
}
+
+func TestReturnStatement(t *testing.T) {
+ tests := []struct {
+ input string
+ expected int64
+ }{
+ {"return 10;", 10},
+ {"return 20; 9;", 20},
+ {"9; return 2*5; 20;", 10},
+ {`
+if (1<2) {
+ return 10;
+}
+return 20;`, 10},
+ }
+ for _, tt := range tests {
+ evaluated := testEval(tt.input)
+ testIntegerObject(t, evaluated, tt.expected)
+ }
+}
diff --git a/object/object.go b/object/object.go
@@ -5,9 +5,10 @@ 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"
)
type Object interface {
@@ -18,15 +19,25 @@ type Object interface {
type Integer struct {
Value int64
}
-func (i *Integer) Inspect() string { return fmt.Sprintf("%d", i.Value) }
+
+func (i *Integer) Inspect() string { return fmt.Sprintf("%d", i.Value) }
func (i *Integer) Type() ObjectType { return INTEGER_OBJ }
type Boolean struct {
Value bool
}
-func (b *Boolean) Inspect() string { return fmt.Sprintf("%t", b.Value) }
+
+func (b *Boolean) Inspect() string { return fmt.Sprintf("%t", b.Value) }
func (b *Boolean) Type() ObjectType { return BOOLEAN_OBJ }
-type Null struct {}
-func (n *Null) Inspect() string { return "null" }
+type Null struct{}
+
+func (n *Null) Inspect() string { return "null" }
func (n *Null) Type() ObjectType { return NULL_OBJ }
+
+type ReturnValue struct {
+ Value Object
+}
+
+func (rv *ReturnValue) Inspect() string { return rv.Value.Inspect() }
+func (rv *ReturnValue) Type() ObjectType { return RETURN_VALUE_OBJ }