From e6d5567681eaaf8ce0233c068e45d730032fba4b Mon Sep 17 00:00:00 2001 From: Chuck Smith Date: Fri, 19 Jan 2024 15:46:10 -0500 Subject: [PATCH] If statements --- evaluator/evaluator.go | 35 +++++++++++++++++++++++++++++++++++ evaluator/evaluator_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index 80676b5..8a2fbc4 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -21,18 +21,28 @@ func Eval(node ast.Node) object.Object { case *ast.ExpressionStatement: return Eval(node.Expression) + case *ast.BlockStatement: + return evalStatements(node.Statements) + + case *ast.IfExpression: + return evalIfExpression(node) + // Expressions case *ast.IntegerLiteral: return &object.Integer{Value: node.Value} + case *ast.Boolean: return nativeBoolToBooleanObject(node.Value) + case *ast.PrefixExpression: right := Eval(node.Right) return evalPrefixExpression(node.Operator, right) + case *ast.InfixExpression: left := Eval(node.Left) right := Eval(node.Right) return evalInfixExpression(node.Operator, left, right) + } return nil @@ -126,3 +136,28 @@ func evalIntegerInfixExpression(operator string, left, right object.Object) obje return NULL } } + +func evalIfExpression(ie *ast.IfExpression) object.Object { + condition := Eval(ie.Condition) + + if isTruthy(condition) { + return Eval(ie.Consequence) + } else if ie.Alternative != nil { + return Eval(ie.Alternative) + } else { + return NULL + } +} + +func isTruthy(obj object.Object) bool { + switch obj { + 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 68b84ef..33be284 100644 --- a/evaluator/evaluator_test.go +++ b/evaluator/evaluator_test.go @@ -86,6 +86,31 @@ func TestBangOperator(t *testing.T) { } } +func TestIfElseExpression(t *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}, + } + + for _, tt := range tests { + evaluated := testEval(tt.input) + integer, ok := tt.expected.(int) + if ok { + testIntegerObject(t, evaluated, int64(integer)) + } else { + testNullObject(t, evaluated) + } + } +} + func testEval(input string) object.Object { l := lexer.New(input) p := parser.New(l) @@ -121,3 +146,11 @@ func testBooleanObject(t *testing.T, obj object.Object, expected bool) bool { return true } + +func testNullObject(t *testing.T, obj object.Object) bool { + if obj != NULL { + t.Errorf("object is not NULL. got=%T (%+v)", obj, obj) + return false + } + return true +}