Skip to content

Commit

Permalink
3.5 Evaluation (infix expressions)
Browse files Browse the repository at this point in the history
Implement support for:
* group of operators that don't produces booleans as their result
* integer operands on either side of the operator
* boolean operands for the equality operators `==` and `!=`
  • Loading branch information
cedrickchee committed Mar 30, 2020
1 parent 659c1fa commit c9cb6f0
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 0 deletions.
58 changes: 58 additions & 0 deletions evaluator/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}
29 changes: 29 additions & 0 deletions evaluator/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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 {
Expand Down

0 comments on commit c9cb6f0

Please sign in to comment.