/*****************************************************************************
* *
* This file is part of the BeanShell Java Scripting distribution. *
* Documentation and updates may be found at http://www.beanshell.org/ *
* *
* Sun Public License Notice: *
* *
* The contents of this file are subject to the Sun Public License Version *
* 1.0 (the "License"); you may not use this file except in compliance with *
* the License. A copy of the License is available at http://www.sun.com *
* *
* The Original Code is BeanShell. The Initial Developer of the Original *
* Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright *
* (C) 2000. All Rights Reserved. *
* *
* GNU Public License Notice: *
* *
* Alternatively, the contents of this file may be used under the terms of *
* the GNU Lesser General Public License (the "LGPL"), in which case the *
* provisions of LGPL are applicable instead of those above. If you wish to *
* allow use of your version of this file only under the terms of the LGPL *
* and not to allow others to use your version of this file under the SPL, *
* indicate your decision by deleting the provisions above and replace *
* them with the notice and other provisions required by the LGPL. If you *
* do not delete the provisions above, a recipient may use your version of *
* this file under either the SPL or the LGPL. *
* *
* Patrick Niemeyer (pat@pat.net) *
* Author of Learning Java, O'Reilly & Associates *
* http://www.pat.net/~pat/ *
* *
*****************************************************************************/
package bsh;
/**
Implement binary expressions...
Note: this is too complicated... need some cleanup and simplification.
@see Primitive.binaryOperation
*/
class
BSHBinaryExpression extends
SimpleNode
implements
ParserConstants
{
public int
kind;
BSHBinaryExpression(int
id) { super(
id); }
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 == 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 );
}
}
/*
object is a non-null and non-void Primitive type
*/
private boolean
isPrimitiveValue(
Object obj ) {
return ( (
obj instanceof
Primitive)
&& (
obj !=
Primitive.
VOID) && (
obj !=
Primitive.
NULL) );
}
/*
object is a java.lang wrapper for boolean, char, or number type
*/
private boolean
isWrapper(
Object obj ) {
return (
obj instanceof
Boolean ||
obj instanceof
Character ||
obj instanceof
Number );
}
}