public void eval(BinaryExpression expression) {
    switch (expression.getOperation().getType()) {
      case EQUAL: // = assignment
        evaluateEqual(expression, false);
        break;

      case COMPARE_EQUAL: // ==
        evaluateCompareExpression(compareEqualMethod, expression);
        break;

      case COMPARE_NOT_EQUAL:
        evaluateCompareExpression(compareNotEqualMethod, expression);
        break;

      case COMPARE_TO:
        evaluateCompareTo(expression);
        break;

      case COMPARE_GREATER_THAN:
        evaluateCompareExpression(compareGreaterThanMethod, expression);
        break;

      case COMPARE_GREATER_THAN_EQUAL:
        evaluateCompareExpression(compareGreaterThanEqualMethod, expression);
        break;

      case COMPARE_LESS_THAN:
        evaluateCompareExpression(compareLessThanMethod, expression);
        break;

      case COMPARE_LESS_THAN_EQUAL:
        evaluateCompareExpression(compareLessThanEqualMethod, expression);
        break;

      case LOGICAL_AND:
        evaluateLogicalAndExpression(expression);
        break;

      case LOGICAL_OR:
        evaluateLogicalOrExpression(expression);
        break;

      case BITWISE_AND:
        evaluateBinaryExpression("and", expression);
        break;

      case BITWISE_AND_EQUAL:
        evaluateBinaryExpressionWithAssignment("and", expression);
        break;

      case BITWISE_OR:
        evaluateBinaryExpression("or", expression);
        break;

      case BITWISE_OR_EQUAL:
        evaluateBinaryExpressionWithAssignment("or", expression);
        break;

      case BITWISE_XOR:
        evaluateBinaryExpression("xor", expression);
        break;

      case BITWISE_XOR_EQUAL:
        evaluateBinaryExpressionWithAssignment("xor", expression);
        break;

      case PLUS:
        evaluateBinaryExpression("plus", expression);
        break;

      case PLUS_EQUAL:
        evaluateBinaryExpressionWithAssignment("plus", expression);
        break;

      case MINUS:
        evaluateBinaryExpression("minus", expression);
        break;

      case MINUS_EQUAL:
        evaluateBinaryExpressionWithAssignment("minus", expression);
        break;

      case MULTIPLY:
        evaluateBinaryExpression("multiply", expression);
        break;

      case MULTIPLY_EQUAL:
        evaluateBinaryExpressionWithAssignment("multiply", expression);
        break;

      case DIVIDE:
        evaluateBinaryExpression("div", expression);
        break;

      case DIVIDE_EQUAL:
        // SPG don't use divide since BigInteger implements directly
        // and we want to dispatch through DefaultGroovyMethods to get a BigDecimal result
        evaluateBinaryExpressionWithAssignment("div", expression);
        break;

      case INTDIV:
        evaluateBinaryExpression("intdiv", expression);
        break;

      case INTDIV_EQUAL:
        evaluateBinaryExpressionWithAssignment("intdiv", expression);
        break;

      case MOD:
        evaluateBinaryExpression("mod", expression);
        break;

      case MOD_EQUAL:
        evaluateBinaryExpressionWithAssignment("mod", expression);
        break;

      case POWER:
        evaluateBinaryExpression("power", expression);
        break;

      case POWER_EQUAL:
        evaluateBinaryExpressionWithAssignment("power", expression);
        break;

      case LEFT_SHIFT:
        evaluateBinaryExpression("leftShift", expression);
        break;

      case LEFT_SHIFT_EQUAL:
        evaluateBinaryExpressionWithAssignment("leftShift", expression);
        break;

      case RIGHT_SHIFT:
        evaluateBinaryExpression("rightShift", expression);
        break;

      case RIGHT_SHIFT_EQUAL:
        evaluateBinaryExpressionWithAssignment("rightShift", expression);
        break;

      case RIGHT_SHIFT_UNSIGNED:
        evaluateBinaryExpression("rightShiftUnsigned", expression);
        break;

      case RIGHT_SHIFT_UNSIGNED_EQUAL:
        evaluateBinaryExpressionWithAssignment("rightShiftUnsigned", expression);
        break;

      case KEYWORD_INSTANCEOF:
        evaluateInstanceof(expression);
        break;

      case FIND_REGEX:
        evaluateCompareExpression(findRegexMethod, expression);
        break;

      case MATCH_REGEX:
        evaluateCompareExpression(matchRegexMethod, expression);
        break;

      case LEFT_SQUARE_BRACKET:
        if (controller.getCompileStack().isLHS()) {
          evaluateEqual(expression, false);
        } else {
          evaluateBinaryExpression("getAt", expression);
        }
        break;

      case KEYWORD_IN:
        evaluateCompareExpression(isCaseMethod, expression);
        break;

      case COMPARE_IDENTICAL:
      case COMPARE_NOT_IDENTICAL:
        Token op = expression.getOperation();
        Throwable cause =
            new SyntaxException(
                "Operator " + op + " not supported",
                op.getStartLine(),
                op.getStartColumn(),
                op.getStartLine(),
                op.getStartColumn() + 3);
        throw new GroovyRuntimeException(cause);

      default:
        throw new GroovyBugError("Operation: " + expression.getOperation() + " not supported");
    }
  }
  Expression transformBinaryExpression(final BinaryExpression bin) {
    if (bin instanceof DeclarationExpression) {
      Expression optimized = transformDeclarationExpression(bin);
      if (optimized != null) {
        return optimized;
      }
    }
    Object[] list = bin.getNodeMetaData(BINARY_EXP_TARGET);
    Token operation = bin.getOperation();
    int operationType = operation.getType();
    Expression rightExpression = bin.getRightExpression();
    Expression leftExpression = bin.getLeftExpression();
    if (bin instanceof DeclarationExpression && leftExpression instanceof VariableExpression) {
      ClassNode declarationType = ((VariableExpression) leftExpression).getOriginType();
      if (rightExpression instanceof ConstantExpression) {
        ClassNode unwrapper = ClassHelper.getUnwrapper(declarationType);
        ClassNode wrapper = ClassHelper.getWrapper(declarationType);
        if (!rightExpression.getType().equals(declarationType)
            && wrapper.isDerivedFrom(ClassHelper.Number_TYPE)
            && WideningCategories.isDoubleCategory(unwrapper)) {
          ConstantExpression constant = (ConstantExpression) rightExpression;
          if (constant.getValue() != null) {
            return optimizeConstantInitialization(
                bin, operation, constant, leftExpression, declarationType);
          }
        }
      }
    }
    if (operationType == Types.EQUAL && leftExpression instanceof PropertyExpression) {
      MethodNode directMCT =
          leftExpression.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
      if (directMCT != null) {
        return transformPropertyAssignmentToSetterCall(
            (PropertyExpression) leftExpression, rightExpression, directMCT);
      }
    }
    if (operationType == Types.COMPARE_EQUAL || operationType == Types.COMPARE_NOT_EQUAL) {
      // let's check if one of the operands is the null constant
      CompareToNullExpression compareToNullExpression = null;
      if (isNullConstant(leftExpression)) {
        compareToNullExpression =
            new CompareToNullExpression(
                staticCompilationTransformer.transform(rightExpression),
                operationType == Types.COMPARE_EQUAL);
      } else if (isNullConstant(rightExpression)) {
        compareToNullExpression =
            new CompareToNullExpression(
                staticCompilationTransformer.transform(leftExpression),
                operationType == Types.COMPARE_EQUAL);
      }
      if (compareToNullExpression != null) {
        compareToNullExpression.setSourcePosition(bin);
        return compareToNullExpression;
      }
    } else if (operationType == Types.KEYWORD_IN) {
      return convertInOperatorToTernary(bin, rightExpression, leftExpression);
    }
    if (list != null) {
      if (operationType == Types.COMPARE_TO) {
        StaticTypesTypeChooser typeChooser = staticCompilationTransformer.getTypeChooser();
        ClassNode classNode = staticCompilationTransformer.getClassNode();
        ClassNode leftType = typeChooser.resolveType(leftExpression, classNode);
        if (leftType.implementsInterface(ClassHelper.COMPARABLE_TYPE)) {
          ClassNode rightType = typeChooser.resolveType(rightExpression, classNode);
          if (rightType.implementsInterface(ClassHelper.COMPARABLE_TYPE)) {
            Expression left = staticCompilationTransformer.transform(leftExpression);
            Expression right = staticCompilationTransformer.transform(rightExpression);
            MethodCallExpression call =
                new MethodCallExpression(left, "compareTo", new ArgumentListExpression(right));
            call.setImplicitThis(false);
            call.setMethodTarget(COMPARE_TO_METHOD);

            CompareIdentityExpression compareIdentity = new CompareIdentityExpression(left, right);
            compareIdentity.putNodeMetaData(
                StaticTypesMarker.INFERRED_RETURN_TYPE, ClassHelper.boolean_TYPE);
            TernaryExpression result =
                new TernaryExpression(
                    new BooleanExpression(compareIdentity), // a==b
                    CONSTANT_ZERO,
                    new TernaryExpression(
                        new BooleanExpression(new CompareToNullExpression(left, true)), // a==null
                        CONSTANT_MINUS_ONE,
                        new TernaryExpression(
                            new BooleanExpression(
                                new CompareToNullExpression(right, true)), // b==null
                            CONSTANT_ONE,
                            call)));
            compareIdentity.putNodeMetaData(
                StaticTypesMarker.INFERRED_RETURN_TYPE, ClassHelper.int_TYPE);
            result.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, ClassHelper.int_TYPE);
            TernaryExpression expr = (TernaryExpression) result.getFalseExpression();
            expr.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, ClassHelper.int_TYPE);
            expr.getFalseExpression()
                .putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, ClassHelper.int_TYPE);
            return result;
          }
        }
      }
      boolean isAssignment = StaticTypeCheckingSupport.isAssignment(operationType);
      MethodCallExpression call;
      MethodNode node = (MethodNode) list[0];
      String name = (String) list[1];
      Expression left = staticCompilationTransformer.transform(leftExpression);
      Expression right = staticCompilationTransformer.transform(rightExpression);
      BinaryExpression optimized = tryOptimizeCharComparison(left, right, bin);
      if (optimized != null) {
        optimized.removeNodeMetaData(BINARY_EXP_TARGET);
        return transformBinaryExpression(optimized);
      }
      call = new MethodCallExpression(left, name, new ArgumentListExpression(right));
      call.setImplicitThis(false);
      call.setMethodTarget(node);
      MethodNode adapter = StaticCompilationTransformer.BYTECODE_BINARY_ADAPTERS.get(operationType);
      if (adapter != null) {
        ClassExpression sba =
            new ClassExpression(StaticCompilationTransformer.BYTECODE_ADAPTER_CLASS);
        // replace with compareEquals
        call =
            new MethodCallExpression(sba, "compareEquals", new ArgumentListExpression(left, right));
        call.setMethodTarget(adapter);
        call.setImplicitThis(false);
      }
      if (!isAssignment) return call;
      // case of +=, -=, /=, ...
      // the method represents the operation type only, and we must add an assignment
      return new BinaryExpression(
          left, Token.newSymbol("=", operation.getStartLine(), operation.getStartColumn()), call);
    }
    if (bin.getOperation().getType() == Types.EQUAL
        && leftExpression instanceof TupleExpression
        && rightExpression instanceof ListExpression) {
      // multiple assignment
      ListOfExpressionsExpression cle = new ListOfExpressionsExpression();
      boolean isDeclaration = bin instanceof DeclarationExpression;
      List<Expression> leftExpressions = ((TupleExpression) leftExpression).getExpressions();
      List<Expression> rightExpressions = ((ListExpression) rightExpression).getExpressions();
      Iterator<Expression> leftIt = leftExpressions.iterator();
      Iterator<Expression> rightIt = rightExpressions.iterator();
      if (isDeclaration) {
        while (leftIt.hasNext()) {
          Expression left = leftIt.next();
          if (rightIt.hasNext()) {
            Expression right = rightIt.next();
            BinaryExpression bexp = new DeclarationExpression(left, bin.getOperation(), right);
            bexp.setSourcePosition(right);
            cle.addExpression(bexp);
          }
        }
      } else {
        // (next, result) = [ result, next+result ]
        // -->
        // def tmp1 = result
        // def tmp2 = next+result
        // next = tmp1
        // result = tmp2
        int size = rightExpressions.size();
        List<Expression> tmpAssignments = new ArrayList<Expression>(size);
        List<Expression> finalAssignments = new ArrayList<Expression>(size);
        for (int i = 0; i < Math.min(size, leftExpressions.size()); i++) {
          Expression left = leftIt.next();
          Expression right = rightIt.next();
          VariableExpression tmpVar = new VariableExpression("$tmpVar$" + tmpVarCounter++);
          BinaryExpression bexp = new DeclarationExpression(tmpVar, bin.getOperation(), right);
          bexp.setSourcePosition(right);
          tmpAssignments.add(bexp);
          bexp = new BinaryExpression(left, bin.getOperation(), new VariableExpression(tmpVar));
          bexp.setSourcePosition(left);
          finalAssignments.add(bexp);
        }
        for (Expression tmpAssignment : tmpAssignments) {
          cle.addExpression(tmpAssignment);
        }
        for (Expression finalAssignment : finalAssignments) {
          cle.addExpression(finalAssignment);
        }
      }
      return staticCompilationTransformer.transform(cle);
    }
    return staticCompilationTransformer.superTransform(bin);
  }