umx_compiler

UMX virtual machine "Monkey" interpreter / bytecode compiler
git clone git://bsandro.tech/umx_compiler
Log | Files | Refs

commit d879cb5ebc90f1eb8925311f0695c77534f69c10
parent ed5362ca03dff4a3b9d7b3c0958a450b9a46db7d
Author: bsandro <email@bsandro.tech>
Date:   Fri, 24 Jun 2022 01:06:02 +0300

eval return statements support

Diffstat:
Meval/eval.go | 32++++++++++++++++++++++++++++++--
Meval/eval_test.go | 20++++++++++++++++++++
Mobject/object.go | 25++++++++++++++++++-------
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 }