From 5456f60038bae5718e572b7eb1e1f514fc0b60a3 Mon Sep 17 00:00:00 2001 From: tijani Date: Thu, 11 Aug 2022 05:04:44 +0000 Subject: [PATCH] Infix Expression, Prefix Expression, Return Statement. git-svn-id: https://svn.tlawal.org/svn/monkey@49 f6afcba9-9ef1-4bdd-9b72-7484f5705bac --- evaluator/evaluator.go | 117 ++++++++++++++++++++++++++++++++++-- evaluator/evaluator_test.go | 81 ++++++++++++++++++++++++- object/object.go | 15 ++++- 3 files changed, 205 insertions(+), 8 deletions(-) diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index 241c241..eadad71 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -15,7 +15,7 @@ func Eval(node ast.Node) object.Object { switch node := node.(type) { // Statements case *ast.Program: - return eval_statements(node.Statements) + return eval_program(node) case *ast.ExpressionStatement: return Eval(node.Expression) @@ -31,20 +31,52 @@ func Eval(node ast.Node) object.Object { right := Eval(node.Right) return eval_prefix_expression(node.Operator, right) + case *ast.InfixExpression: + left := Eval(node.Left) + right := Eval(node.Right) + return eval_infix_expression(node.Operator, left, right) + + case *ast.BlockStatement: + return eval_block_statement(node) + + case *ast.IfExpression: + return eval_if_expression(node) + + case *ast.ReturnStatement: + val := Eval(node.ReturnValue) + return &object.ReturnValue{Value: val} } return nil } -func eval_statements(statements []ast.Statement) object.Object { +func eval_program(program *ast.Program) object.Object { var result object.Object - for _, statement := range statements { + for _, statement := range program.Statements { result = Eval(statement) + + if return_value, ok := result.(*object.ReturnValue); ok { + return return_value.Value + } } return result } +func eval_block_statement(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_OBJECT { + return result + } + } + + return result +} + func native_bool_to_boolean_object(input bool) *object.Boolean { if input { return TRUE @@ -81,9 +113,86 @@ func eval_bang_operator_expression(right object.Object) object.Object { } func eval_minus_prefix_operator_expression(right object.Object) object.Object { - if right.Type() != object.INTEGER_OBJECT{ + if right.Type() != object.INTEGER_OBJECT { return NULL } value := right.(*object.Integer).Value return &object.Integer{Value: -value} } + +func eval_infix_expression(operator string, left object.Object, right object.Object) object.Object { + switch { + case left.Type() == object.INTEGER_OBJECT && right.Type() == object.INTEGER_OBJECT: + return eval_integer_infix_expression(operator, left, right) + + case operator == "==": + return native_bool_to_boolean_object(left == right) + + case operator == "!=": + return native_bool_to_boolean_object(left != right) + default: + return NULL + } +} + +func eval_integer_infix_expression(operator string, left object.Object, right object.Object) object.Object { + left_value := left.(*object.Integer).Value + right_value := right.(*object.Integer).Value + + switch operator { + case "+": + return &object.Integer{Value: left_value + right_value} + + case "-": + return &object.Integer{Value: left_value - right_value} + + case "*": + return &object.Integer{Value: left_value * right_value} + + case "/": + return &object.Integer{Value: left_value / right_value} + + case "<": + return native_bool_to_boolean_object(left_value < right_value) + + case ">": + return native_bool_to_boolean_object(left_value > right_value) + + case "==": + return native_bool_to_boolean_object(left_value == right_value) + + case "!=": + return native_bool_to_boolean_object(left_value != right_value) + + default: + return NULL + } +} + +func eval_if_expression(ie *ast.IfExpression) object.Object { + condition := Eval(ie.Condition) + + if is_truthy(condition) { + return Eval(ie.Consequence) + } else if ie.Alternative != nil { + return Eval(ie.Alternative) + } else { + return NULL + } +} + +func is_truthy(object object.Object) bool { + switch object { + case NULL: + return false + + case TRUE: + return true + + case FALSE: + return false + + default: + return true + } +} diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go index 63055ff..6985b88 100644 --- a/evaluator/evaluator_test.go +++ b/evaluator/evaluator_test.go @@ -16,7 +16,18 @@ func TestEvalIntegerExpression(l_test *testing.T) { {"10", 10}, {"-10", -10}, {"-5", -5}, - + {"5 + 5 + 5 + 5 - 10", 10}, + {"2 * 2 * 2 * 2 * 2", 32}, + {"-50 + 100 + -50", 0}, + {"5 * 2 + 10", 20}, + {"5 + 2 * 10", 25}, + {"20 + 2 * -10", 0}, + {"50 / 2 * 2 + 10", 60}, + {"2 * (5 + 10)", 30}, + {"3 * 3 * 3 + 10", 37}, + {"3 * (3 * 3) + 10", 37}, + {"(5 + 10 * 2 + 15 / 3) * 2 + -10", 50}, + {"8 * (4 + 32 - (64 / 2) + 5) / (100 / 2 + (25 - 10 + 15) * 18)", 0}, } for _, tt := range tests { @@ -32,6 +43,23 @@ func TestEvalBooleanExpression(l_test *testing.T) { }{ {"true", true}, {"false", false}, + {"1 < 2", true}, + {"1 > 2", false}, + {"1 < 1", false}, + {"1 > 1", false}, + {"1 == 1", true}, + {"1 != 1", false}, + {"1 == 2", false}, + {"1 != 2", true}, + {"true == true", true}, + {"false == false", true}, + {"true == false", false}, + {"true != false", true}, + {"false != true", true}, + {"(1 < 2) == true", true}, + {"(1 < 2) == false", false}, + {"(1 > 2) == true", false}, + {"(1 > 2) == false", true}, } for _, tt := range tests { @@ -60,6 +88,49 @@ func TestBangOperator(l_test *testing.T) { } } +func TestIfElseExpressions(l_test *testing.T) { + tests := []struct { + input string + expected interface{} + }{ + {"if (true) { 10 }", 10}, + {"if (false) { 10 }", nil}, + {"if (1) { 10 }", 10}, + {"if (1 < 2) { 10 }", 10}, + {"if (1 > 2) { 10 }", nil}, + {"if (1 > 2) { 10 } else {20}", 20}, + {"if (1 < 2) { 10 } else {20}", 10}, + {"if(10 > 1){if(10 > 1){ return 10;} return 1;}", 10}, + } + for _, tt := range tests { + evaluated := test_eval(tt.input) + integer, ok := tt.expected.(int) + + if ok { + test_integer_object(l_test, evaluated, int64(integer)) + } else { + test_null_object(l_test, evaluated) + } + } +} + +func TestReturnStatements(l_test *testing.T) { + tests := []struct { + input string + expected int64 + }{ + {"return 10;", 10}, + {"return 10; 9;", 10}, + {"return 2 * 5; 9;", 10}, + {"9; return 2 * 5; 9;", 10}, + } + + for _, tt := range tests { + evaluated := test_eval(tt.input) + test_integer_object(l_test, evaluated, tt.expected) + } +} + // Helpers func test_eval(input string) object.Object { l_lexer := lexer.New(input) @@ -95,3 +166,11 @@ func test_boolean_object(l_test *testing.T, l_object object.Object, expected boo } return true } + +func test_null_object(l_test *testing.T, object object.Object) bool { + if object != NULL { + l_test.Errorf("object is not NULL, got=%T (%+v)", object, object) + return false + } + return true +} diff --git a/object/object.go b/object/object.go index 1b48711..fde670c 100644 --- a/object/object.go +++ b/object/object.go @@ -5,9 +5,10 @@ import "fmt" type ObjectType string const ( - INTEGER_OBJECT = "INTEGER" - BOOLEAN_OBJECT = "BOOLEAN" - NULL_OBJECT = "NULL" + INTEGER_OBJECT = "INTEGER" + BOOLEAN_OBJECT = "BOOLEAN" + NULL_OBJECT = "NULL" + RETURN_VALUE_OBJECT = "RETURN_VALUE" ) type Object interface { @@ -51,3 +52,11 @@ func (n *Null) Type() ObjectType { func (n *Null) Inspect() string { return "null" } + +// Return +type ReturnValue struct { + Value Object +} + +func (rv *ReturnValue) Type() ObjectType { return RETURN_VALUE_OBJECT } +func (rv *ReturnValue) Inspect() string { return rv.Value.Inspect() }