diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index 6b20be7..e500ec3 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -53,6 +53,11 @@ func Eval(node ast.Node) object.Object { // this evaluation with the operator. 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 @@ -123,3 +128,56 @@ func evalMinusPrefixOperatorExpression(right object.Object) object.Object { // Allocate a new object to wrap a negated version of this value. return &object.Integer{Value: -value} } + +func evalInfixExpression( + operator string, + left, right object.Object, +) object.Object { + // Monkey's object system doesn't allow pointer comparison for integer + // objects. It has to unwrap the value before a comparison can be made. + // Thus the comparison between booleans is faster. + + switch { + case left.Type() == object.INTEGER_OBJ && right.Type() == object.INTEGER_OBJ: + // The check for integer operands has to be higher up in the switch + // statement. + return evalIntegerInfixExpression(operator, left, right) + case operator == "==": + // Using pointer comparison to check for equality between booleans. + return nativeBoolToBooleanObject(left == right) + case operator == "!=": + // Using pointer comparison to check for equality between booleans. + 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 5d3e48a..0578b89 100644 --- a/evaluator/evaluator_test.go +++ b/evaluator/evaluator_test.go @@ -14,6 +14,7 @@ func TestEvalIntegerExpression(t *testing.T) { // position. Second, because this test function should grow to encompass // all integer arithmetic in order to have one place that shows the desired // behaviour in a clear and neat way. + // This test is also extended for the infix expressions. tests := []struct { input string @@ -23,6 +24,17 @@ func TestEvalIntegerExpression(t *testing.T) { {"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 { @@ -38,6 +50,23 @@ func TestEvalBooleanExpression(t *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 {