diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index 2a8c44a..80676b5 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -5,6 +5,12 @@ import ( "monkey/object" ) +var ( + NULL = &object.Null{} + TRUE = &object.Boolean{Value: true} + FALSE = &object.Boolean{Value: false} +) + func Eval(node ast.Node) object.Object { switch node := node.(type) { @@ -18,6 +24,15 @@ func Eval(node ast.Node) object.Object { // 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 @@ -32,3 +47,82 @@ func evalStatements(stmts []ast.Statement) object.Object { return result } + +func nativeBoolToBooleanObject(input bool) object.Object { + if input { + return TRUE + } + return FALSE +} + +func evalPrefixExpression(operator string, right object.Object) object.Object { + switch operator { + case "!": + return evalBangOperatorExpression(right) + case "-": + return evalMinusPrefixOperatorExpression(right) + default: + return NULL + } +} + +func evalBangOperatorExpression(right object.Object) object.Object { + switch right { + case TRUE: + return FALSE + case FALSE: + return TRUE + case NULL: + return TRUE + default: + return FALSE + } +} + +func evalMinusPrefixOperatorExpression(right object.Object) object.Object { + if right.Type() != object.INTEGER_OBJ { + return NULL + } + + value := right.(*object.Integer).Value + return &object.Integer{Value: -value} +} + +func evalInfixExpression(operator string, left, right object.Object) object.Object { + switch { + case left.Type() == object.INTEGER_OBJ && right.Type() == object.INTEGER_OBJ: + return evalIntegerInfixExpression(operator, left, right) + case operator == "==": + return nativeBoolToBooleanObject(left == right) + case operator == "!=": + return nativeBoolToBooleanObject(left != right) + default: + return NULL + } +} + +func evalIntegerInfixExpression(operator string, left, right object.Object) object.Object { + leftVal := left.(*object.Integer).Value + rightVal := right.(*object.Integer).Value + + switch operator { + case "+": + return &object.Integer{Value: leftVal + rightVal} + case "-": + return &object.Integer{Value: leftVal - rightVal} + case "*": + return &object.Integer{Value: leftVal * rightVal} + case "/": + return &object.Integer{Value: leftVal / rightVal} + case "<": + return nativeBoolToBooleanObject(leftVal < rightVal) + case ">": + return nativeBoolToBooleanObject(leftVal > rightVal) + case "==": + return nativeBoolToBooleanObject(leftVal == rightVal) + case "!=": + return nativeBoolToBooleanObject(leftVal != rightVal) + default: + return NULL + } +} diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go index 0c6779c..68b84ef 100644 --- a/evaluator/evaluator_test.go +++ b/evaluator/evaluator_test.go @@ -14,6 +14,19 @@ func TestEvalIntegerExpression(t *testing.T) { }{ {"5", 5}, {"10", 10}, + {"-5", -5}, + {"-10", -10}, + {"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}, } for _, tt := range tests { @@ -22,6 +35,57 @@ func TestEvalIntegerExpression(t *testing.T) { } } +func TestEvalBooleanExpression(t *testing.T) { + tests := []struct { + input string + expected bool + }{ + {"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 { + evaluated := testEval(tt.input) + testBooleanObject(t, evaluated, tt.expected) + } +} + +func TestBangOperator(t *testing.T) { + tests := []struct { + input string + expected bool + }{ + {"!true", false}, + {"!false", true}, + {"!5", false}, + {"!!true", true}, + {"!!false", false}, + {"!!5", true}, + } + + for _, tt := range tests { + evaluated := testEval(tt.input) + testBooleanObject(t, evaluated, tt.expected) + } +} + func testEval(input string) object.Object { l := lexer.New(input) p := parser.New(l) @@ -43,3 +107,17 @@ func testIntegerObject(t *testing.T, obj object.Object, expected int64) bool { return true } + +func testBooleanObject(t *testing.T, obj object.Object, expected bool) bool { + result, ok := obj.(*object.Boolean) + if !ok { + t.Errorf("object is not Boolean. got=%T (%+v)", obj, obj) + return false + } + if result.Value != expected { + t.Errorf("object has wrong value. got=%t, want=%t", result.Value, expected) + return false + } + + return true +}