/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.ir.transform.expression;
import gw.internal.gosu.ir.nodes.IRMethodFactory;
import gw.internal.gosu.ir.nodes.JavaClassIRType;
import gw.internal.gosu.ir.transform.ExpressionTransformer;
import gw.internal.gosu.ir.transform.TopLevelTransformationContext;
import gw.internal.gosu.parser.BeanAccess;
import gw.internal.gosu.parser.expressions.MultiplicativeExpression;
import gw.lang.ir.IRExpression;
import gw.lang.ir.expression.IRArithmeticExpression;
import gw.lang.reflect.IMethodInfo;
import gw.lang.reflect.IRelativeTypeInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.java.JavaTypes;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.util.Arrays;
import java.util.Collections;
/**
*/
public class MultiplicativeExpressionTransformer extends AbstractExpressionTransformer<MultiplicativeExpression>
{
public static IRExpression compile( TopLevelTransformationContext cc, MultiplicativeExpression expr )
{
MultiplicativeExpressionTransformer gen = new MultiplicativeExpressionTransformer( cc, expr );
return gen.compile();
}
private MultiplicativeExpressionTransformer( TopLevelTransformationContext cc, MultiplicativeExpression expr )
{
super( cc, expr );
}
protected IRExpression compile_impl()
{
IMethodInfo operatorOverload = _expr().getOverride();
if( operatorOverload != null )
{
// The operator is overloaded, call into it...
IRExpression lhs = ExpressionTransformer.compile( _expr().getLHS(), _cc() );
IRExpression rhs = ExpressionTransformer.compile( _expr().getRHS(), _cc() );
return callMethod( IRMethodFactory.createIRMethod( operatorOverload, null ), lhs, exprList( rhs ) );
}
else
{
if( isSimpleOperation() )
{
return simpleOperation( );
}
else if( isBigDecimalMultiplication() )
{
return bigDecimalMultiplication();
}
else if( isBigIntegerMultiplication() )
{
return bigIntegerMultiplication();
}
else
{
return complexOperation( );
}
}
}
private boolean isBigDecimalMultiplication()
{
return !_expr().isNullSafe() &&
JavaTypes.BIG_DECIMAL().equals( _expr().getType() ) &&
JavaTypes.BIG_DECIMAL().equals( _expr().getLHS().getType() ) &&
JavaTypes.BIG_DECIMAL().equals( _expr().getRHS().getType() );
}
private IRExpression bigDecimalMultiplication( )
{
IRExpression lhs = ExpressionTransformer.compile( _expr().getLHS(), _cc() );
IRExpression rhs = ExpressionTransformer.compile( _expr().getRHS(), _cc() );
if( _expr().getOperator().equals( "*" ) )
{
return callMethod( BigDecimal.class, "multiply", new Class[] {BigDecimal.class}, lhs, Collections.singletonList( rhs ) );
}
else if( _expr().getOperator().equals( "/" ) )
{
return callMethod( BigDecimal.class, "divide",
new Class[] {BigDecimal.class, MathContext.class}, lhs,
Arrays.asList( rhs, getStaticField( TypeSystem.get( MathContext.class ), "DECIMAL128", JavaClassIRType.get( MathContext.class ), IRelativeTypeInfo.Accessibility.PUBLIC ) ) );
}
else if( _expr().getOperator().equals( "%" ) )
{
IRExpression remainder = callMethod( BigDecimal.class, "remainder",
new Class[] {BigDecimal.class, MathContext.class}, lhs,
Arrays.asList( rhs, getStaticField( TypeSystem.get( MathContext.class ), "DECIMAL128", JavaClassIRType.get( MathContext.class ), IRelativeTypeInfo.Accessibility.PUBLIC ) ) );
return callMethod( BigDecimal.class, "abs", new Class[] {}, remainder, Collections.<IRExpression>emptyList() );
}
throw new IllegalStateException();
}
private boolean isBigIntegerMultiplication()
{
return !_expr().isNullSafe() &&
JavaTypes.BIG_INTEGER().equals( _expr().getType() ) &&
JavaTypes.BIG_INTEGER().equals( _expr().getLHS().getType() ) &&
JavaTypes.BIG_INTEGER().equals( _expr().getRHS().getType() );
}
private IRExpression bigIntegerMultiplication( )
{
IRExpression lhs = ExpressionTransformer.compile( _expr().getLHS(), _cc() );
IRExpression rhs = ExpressionTransformer.compile( _expr().getRHS(), _cc() );
if( _expr().getOperator().equals( "*" ) )
{
return callMethod( BigInteger.class, "multiply", new Class[] {BigInteger.class}, lhs, Collections.singletonList( rhs ) );
}
else if( _expr().getOperator().equals( "/" ) )
{
return callMethod( BigInteger.class, "divide", new Class[] {BigInteger.class}, lhs, Collections.singletonList( rhs ) );
}
else if( _expr().getOperator().equals( "%" ) )
{
return callMethod( BigInteger.class, "mod", new Class[]{BigInteger.class}, lhs, Collections.singletonList( rhs ) );
}
throw new IllegalStateException();
}
private IRExpression simpleOperation( )
{
IType type = _expr().getType();
// Push LHS
IRExpression lhs = ExpressionTransformer.compile( _expr().getLHS(), _cc() );
// Maybe convert to expression type
lhs = numberConvert( _expr().getLHS().getType(), type, lhs );
// Push RHS
IRExpression rhs = ExpressionTransformer.compile( _expr().getRHS(), _cc() );
// Maybe convert to expression type
rhs = numberConvert( _expr().getRHS().getType(), type, rhs );
IRArithmeticExpression.Operation op;
// Do the addition
if( _expr().getOperator().equals( "*" ) ||
_expr().getOperator().equals( "?*" ) )
{
op = IRArithmeticExpression.Operation.Multiplication;
}
else if( _expr().getOperator().equals( "/" ) ||
_expr().getOperator().equals( "?/" ) )
{
op = IRArithmeticExpression.Operation.Division;
}
else if( _expr().getOperator().equals( "%" ) ||
_expr().getOperator().equals( "?%" ) )
{
op = IRArithmeticExpression.Operation.Remainder;
}
else
{
throw new IllegalArgumentException( "Unexpected operator " + _expr().getOperator() );
}
return new IRArithmeticExpression( getDescriptor( type ), lhs, rhs, op );
}
private IRExpression complexOperation( )
{
// Call into Gosu's runtime for the answer. The arguments are the result type, the boxed LHS, the boxed RHS,
// the LHS type, the RHS type, whether it's addition, and whether it's numeric
IRExpression evaluateCall = callStaticMethod( MultiplicativeExpression.class, "evaluate",
new Class[]{IType.class, Object.class, Object.class, IType.class, IType.class, int.class, boolean.class },
exprList( pushType( _expr().getType() ),
boxValue( _expr().getLHS().getType(), ExpressionTransformer.compile( _expr().getLHS(), _cc() ) ),
boxValue( _expr().getRHS().getType(), ExpressionTransformer.compile( _expr().getRHS(), _cc() ) ),
pushType( _expr().getLHS().getType() ),
pushType( _expr().getRHS().getType() ),
pushConstant( _expr().getOperator().charAt( _expr().getOperator().length()-1 ) ),
pushConstant( _expr().isNullSafe() ) ) );
// Ensure value is unboxed if type is primitive
return unboxValueToType( _expr().getType(), evaluateCall );
}
public boolean isSimpleOperation()
{
return _expr().getType().isPrimitive() && BeanAccess.isNumericType( _expr().getType() ) &&
_expr().getLHS().getType().isPrimitive() && BeanAccess.isNumericType( _expr().getLHS().getType() ) &&
_expr().getRHS().getType().isPrimitive() && BeanAccess.isNumericType( _expr().getRHS().getType() );
}
}