public Object eval(CallStack callstack, Interpreter interpreter) throws EvalError {
    Object lhs = ((SimpleNode) jjtGetChild(0)).eval(callstack, interpreter);

    /*
    	Doing instanceof?  Next node is a type.
    */
    if (kind == INSTANCEOF) {
      // null object ref is not instance of any type
      if (lhs == Primitive.NULL) return new Primitive(false);

      Class rhs = ((BSHType) jjtGetChild(1)).getType(callstack, interpreter);
      /*
      	// primitive (number or void) cannot be tested for instanceof
                if (lhs instanceof Primitive)
      		throw new EvalError("Cannot be instance of primitive type." );
      */
      /*
      	Primitive (number or void) is not normally an instanceof
      	anything.  But for internal use we'll test true for the
      	bsh.Primitive class.
      	i.e. (5 instanceof bsh.Primitive) will be true
      */
      if (lhs instanceof Primitive)
        if (rhs == lib.bsh.Primitive.class) return new Primitive(true);
        else return new Primitive(false);

      // General case - performe the instanceof based on assignability
      boolean ret = Types.isJavaBaseAssignable(rhs, lhs.getClass());
      return new Primitive(ret);
    }

    // The following two boolean checks were tacked on.
    // This could probably be smoothed out.

    /*
    	Look ahead and short circuit evaluation of the rhs if:
    		we're a boolean AND and the lhs is false.
    */
    if (kind == BOOL_AND || kind == BOOL_ANDX) {
      Object obj = lhs;
      if (isPrimitiveValue(lhs)) obj = ((Primitive) lhs).getValue();
      if (obj instanceof Boolean && (((Boolean) obj).booleanValue() == false))
        return new Primitive(false);
    }
    /*
    	Look ahead and short circuit evaluation of the rhs if:
    		we're a boolean AND and the lhs is false.
    */
    if (kind == BOOL_OR || kind == BOOL_ORX) {
      Object obj = lhs;
      if (isPrimitiveValue(lhs)) obj = ((Primitive) lhs).getValue();
      if (obj instanceof Boolean && (((Boolean) obj).booleanValue() == true))
        return new Primitive(true);
    }

    // end stuff that was tacked on for boolean short-circuiting.

    /*
    	Are both the lhs and rhs either wrappers or primitive values?
    	do binary op
    */
    boolean isLhsWrapper = isWrapper(lhs);
    Object rhs = ((SimpleNode) jjtGetChild(1)).eval(callstack, interpreter);
    boolean isRhsWrapper = isWrapper(rhs);
    if ((isLhsWrapper || isPrimitiveValue(lhs)) && (isRhsWrapper || isPrimitiveValue(rhs))) {
      // Special case for EQ on two wrapper objects
      if ((isLhsWrapper && isRhsWrapper && kind == EQ)) {
        /*
        	Don't auto-unwrap wrappers (preserve identity semantics)
        	FALL THROUGH TO OBJECT OPERATIONS BELOW.
        */
      } else
        try {
          return Primitive.binaryOperation(lhs, rhs, kind);
        } catch (UtilEvalError e) {
          throw e.toEvalError(this, callstack);
        }
    }
    /*
    Doing the following makes it hard to use untyped vars...
    e.g. if ( arg == null ) ...what if arg is a primitive?
    The answer is that we should test only if the var is typed...?
    need to get that info here...

    	else
    	{
    	// Do we have a mixture of primitive values and non-primitives ?
    	// (primitiveValue = not null, not void)

    	int primCount = 0;
    	if ( isPrimitiveValue( lhs ) )
    		++primCount;
    	if ( isPrimitiveValue( rhs ) )
    		++primCount;

    	if ( primCount > 1 )
    		// both primitive types, should have been handled above
    		throw new InterpreterError("should not be here");
    	else
    	if ( primCount == 1 )
    		// mixture of one and the other
    		throw new EvalError("Operator: '" + tokenImage[kind]
    			+"' inappropriate for object / primitive combination.",
    			this, callstack );

    	// else fall through to handle both non-primitive types

    	// end check for primitive and non-primitive mix
    	}
    */

    /*
    	Treat lhs and rhs as arbitrary objects and do the operation.
    	(including NULL and VOID represented by their Primitive types)
    */
    // System.out.println("binary op arbitrary obj: {"+lhs+"}, {"+rhs+"}");
    switch (kind) {
      case EQ:
        return new Primitive((lhs == rhs));

      case NE:
        return new Primitive((lhs != rhs));

      case PLUS:
        if (lhs instanceof String || rhs instanceof String) return lhs.toString() + rhs.toString();

        // FALL THROUGH TO DEFAULT CASE!!!

      default:
        if (lhs instanceof Primitive || rhs instanceof Primitive)
          if (lhs == Primitive.VOID || rhs == Primitive.VOID)
            throw new EvalError(
                "illegal use of undefined variable, class, or 'void' literal", this, callstack);
          else if (lhs == Primitive.NULL || rhs == Primitive.NULL)
            throw new EvalError("illegal use of null value or 'null' literal", this, callstack);

        throw new EvalError(
            "Operator: '" + tokenImage[kind] + "' inappropriate for objects", this, callstack);
    }
  }