Conversion.java
package com.mackenziehigh.autumn.lang.compiler.utils;
import com.mackenziehigh.autumn.lang.compiler.compilers.TypeSystem;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IDeclaredType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IExpressionType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IPrimitiveType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IReturnType;
import com.mackenziehigh.autumn.resources.Finished;
/**
* An instance of this class describes either an as-conversion or an is-conversion.
*
* @author Mackenzie High
*/
@Finished("2014/07/12")
public final class Conversion
{
/**
* This is the name of the conversion method, if the conversion is predefined.
*/
public final String name;
/**
* This is the type of value that the conversion method takes as input.
* If this conversion is really a cast, then this is the type of the object being cast.
*/
public final IExpressionType value;
/**
* This is the type of values that the conversion method returns.
* If this conversion is really a cast, then this is the type that the object is being cast to.
*/
public final IReturnType type;
/**
* This flag is true, if this conversion is really a cast.
*/
public final boolean cast;
/**
* Sole Constructor.
*
* @param name is the name of the conversion method.
* @param value is the input type of the conversion.
* @param type is the output type of the conversion.
* @param cast is true, iff this conversion is really a cast.
*/
public Conversion(final String name,
final IExpressionType value,
final IReturnType type,
final boolean cast)
{
this.name = name;
this.value = value;
this.type = type;
this.cast = cast;
assert (cast == true && name == null) || (cast == false && name != null);
}
/**
* This method creates a description of a conversion between two types.
*
* @param typesystem is the type-system containing the types.
* @param value is the type of the conversion's input.
* @param type is the type of the conversion's output.
* @return an object that describes the conversion; or null, if the conversion is impossible.
*/
public static Conversion findConversion(final TypeSystem typesystem,
final IExpressionType value,
final IReturnType type)
{
final IExpressionType expression = value;
// Primitive Types
final IPrimitiveType P_boolean = typesystem.utils.PRIMITIVE_BOOLEAN;
final IPrimitiveType P_char = typesystem.utils.PRIMITIVE_CHAR;
final IPrimitiveType P_byte = typesystem.utils.PRIMITIVE_BYTE;
final IPrimitiveType P_short = typesystem.utils.PRIMITIVE_SHORT;
final IPrimitiveType P_int = typesystem.utils.PRIMITIVE_INT;
final IPrimitiveType P_long = typesystem.utils.PRIMITIVE_LONG;
final IPrimitiveType P_float = typesystem.utils.PRIMITIVE_FLOAT;
final IPrimitiveType P_double = typesystem.utils.PRIMITIVE_DOUBLE;
// Boxed Types
final IDeclaredType B_boolean = typesystem.utils.BOXED_BOOLEAN;
final IDeclaredType B_char = typesystem.utils.BOXED_CHAR;
final IDeclaredType B_byte = typesystem.utils.BOXED_BYTE;
final IDeclaredType B_short = typesystem.utils.BOXED_SHORT;
final IDeclaredType B_int = typesystem.utils.BOXED_INT;
final IDeclaredType B_long = typesystem.utils.BOXED_LONG;
final IDeclaredType B_float = typesystem.utils.BOXED_FLOAT;
final IDeclaredType B_double = typesystem.utils.BOXED_DOUBLE;
// Other Types
final IDeclaredType STRING = typesystem.utils.STRING;
final IDeclaredType OBJECT = typesystem.utils.OBJECT;
Conversion best = null;
// The order of the following method calls is important.
// I did this using a helper method, rather than an if-elif-else statement, for readability.
best = isPredefined(best, "box", B_boolean, P_boolean, expression, type);
best = isPredefined(best, "box", B_char, P_char, expression, type);
best = isPredefined(best, "box", B_byte, P_byte, expression, type);
best = isPredefined(best, "box", B_short, P_short, expression, type);
best = isPredefined(best, "box", B_int, P_int, expression, type);
best = isPredefined(best, "box", B_long, P_long, expression, type);
best = isPredefined(best, "box", B_float, P_float, expression, type);
best = isPredefined(best, "box", B_double, P_double, expression, type);
best = isPredefined(best, "boxToObject", OBJECT, P_boolean, expression, type);
best = isPredefined(best, "boxToObject", OBJECT, P_char, expression, type);
best = isPredefined(best, "boxToObject", OBJECT, P_byte, expression, type);
best = isPredefined(best, "boxToObject", OBJECT, P_short, expression, type);
best = isPredefined(best, "boxToObject", OBJECT, P_int, expression, type);
best = isPredefined(best, "boxToObject", OBJECT, P_long, expression, type);
best = isPredefined(best, "boxToObject", OBJECT, P_float, expression, type);
best = isPredefined(best, "boxToObject", OBJECT, P_double, expression, type);
best = isPredefined(best, "unbox", P_boolean, B_boolean, expression, type);
best = isPredefined(best, "unbox", P_char, B_char, expression, type);
best = isPredefined(best, "unbox", P_byte, B_byte, expression, type);
best = isPredefined(best, "unbox", P_short, B_short, expression, type);
best = isPredefined(best, "unbox", P_int, B_int, expression, type);
best = isPredefined(best, "unbox", P_long, B_long, expression, type);
best = isPredefined(best, "unbox", P_float, B_float, expression, type);
best = isPredefined(best, "unbox", P_double, B_double, expression, type);
best = isPredefined(best, "convertToBoolean", P_boolean, P_boolean, expression, type);
best = isPredefined(best, "convertToBoolean", P_boolean, P_char, expression, type);
best = isPredefined(best, "convertToBoolean", P_boolean, P_byte, expression, type);
best = isPredefined(best, "convertToBoolean", P_boolean, P_short, expression, type);
best = isPredefined(best, "convertToBoolean", P_boolean, P_int, expression, type);
best = isPredefined(best, "convertToBoolean", P_boolean, P_long, expression, type);
best = isPredefined(best, "convertToBoolean", P_boolean, P_float, expression, type);
best = isPredefined(best, "convertToBoolean", P_boolean, P_double, expression, type);
best = isPredefined(best, "convertToChar", P_char, P_boolean, expression, type);
best = isPredefined(best, "convertToChar", P_char, P_char, expression, type);
best = isPredefined(best, "convertToChar", P_char, P_byte, expression, type);
best = isPredefined(best, "convertToChar", P_char, P_short, expression, type);
best = isPredefined(best, "convertToChar", P_char, P_int, expression, type);
best = isPredefined(best, "convertToChar", P_char, P_long, expression, type);
best = isPredefined(best, "convertToChar", P_char, P_float, expression, type);
best = isPredefined(best, "convertToChar", P_char, P_double, expression, type);
best = isPredefined(best, "convertToByte", P_byte, P_boolean, expression, type);
best = isPredefined(best, "convertToByte", P_byte, P_char, expression, type);
best = isPredefined(best, "convertToByte", P_byte, P_byte, expression, type);
best = isPredefined(best, "convertToByte", P_byte, P_short, expression, type);
best = isPredefined(best, "convertToByte", P_byte, P_int, expression, type);
best = isPredefined(best, "convertToByte", P_byte, P_long, expression, type);
best = isPredefined(best, "convertToByte", P_byte, P_float, expression, type);
best = isPredefined(best, "convertToByte", P_byte, P_double, expression, type);
best = isPredefined(best, "convertToShort", P_short, P_boolean, expression, type);
best = isPredefined(best, "convertToShort", P_short, P_char, expression, type);
best = isPredefined(best, "convertToShort", P_short, P_byte, expression, type);
best = isPredefined(best, "convertToShort", P_short, P_short, expression, type);
best = isPredefined(best, "convertToShort", P_short, P_int, expression, type);
best = isPredefined(best, "convertToShort", P_short, P_long, expression, type);
best = isPredefined(best, "convertToShort", P_short, P_float, expression, type);
best = isPredefined(best, "convertToShort", P_short, P_double, expression, type);
best = isPredefined(best, "convertToInt", P_int, P_boolean, expression, type);
best = isPredefined(best, "convertToInt", P_int, P_char, expression, type);
best = isPredefined(best, "convertToInt", P_int, P_byte, expression, type);
best = isPredefined(best, "convertToInt", P_int, P_short, expression, type);
best = isPredefined(best, "convertToInt", P_int, P_int, expression, type);
best = isPredefined(best, "convertToInt", P_int, P_long, expression, type);
best = isPredefined(best, "convertToInt", P_int, P_float, expression, type);
best = isPredefined(best, "convertToInt", P_int, P_double, expression, type);
best = isPredefined(best, "convertToLong", P_long, P_boolean, expression, type);
best = isPredefined(best, "convertToLong", P_long, P_char, expression, type);
best = isPredefined(best, "convertToLong", P_long, P_byte, expression, type);
best = isPredefined(best, "convertToLong", P_long, P_short, expression, type);
best = isPredefined(best, "convertToLong", P_long, P_int, expression, type);
best = isPredefined(best, "convertToLong", P_long, P_long, expression, type);
best = isPredefined(best, "convertToLong", P_long, P_float, expression, type);
best = isPredefined(best, "convertToLong", P_long, P_double, expression, type);
best = isPredefined(best, "convertToFloat", P_float, P_boolean, expression, type);
best = isPredefined(best, "convertToFloat", P_float, P_char, expression, type);
best = isPredefined(best, "convertToFloat", P_float, P_byte, expression, type);
best = isPredefined(best, "convertToFloat", P_float, P_short, expression, type);
best = isPredefined(best, "convertToFloat", P_float, P_int, expression, type);
best = isPredefined(best, "convertToFloat", P_float, P_long, expression, type);
best = isPredefined(best, "convertToFloat", P_float, P_float, expression, type);
best = isPredefined(best, "convertToFloat", P_float, P_double, expression, type);
best = isPredefined(best, "convertToDouble", P_double, P_boolean, expression, type);
best = isPredefined(best, "convertToDouble", P_double, P_char, expression, type);
best = isPredefined(best, "convertToDouble", P_double, P_byte, expression, type);
best = isPredefined(best, "convertToDouble", P_double, P_short, expression, type);
best = isPredefined(best, "convertToDouble", P_double, P_int, expression, type);
best = isPredefined(best, "convertToDouble", P_double, P_long, expression, type);
best = isPredefined(best, "convertToDouble", P_double, P_float, expression, type);
best = isPredefined(best, "convertToDouble", P_double, P_double, expression, type);
best = isPredefined(best, "convertToString", STRING, P_boolean, expression, type);
best = isPredefined(best, "convertToString", STRING, P_char, expression, type);
best = isPredefined(best, "convertToString", STRING, P_byte, expression, type);
best = isPredefined(best, "convertToString", STRING, P_short, expression, type);
best = isPredefined(best, "convertToString", STRING, P_int, expression, type);
best = isPredefined(best, "convertToString", STRING, P_long, expression, type);
best = isPredefined(best, "convertToString", STRING, P_float, expression, type);
best = isPredefined(best, "convertToString", STRING, P_double, expression, type);
best = isPredefined(best, "convertToString", STRING, OBJECT, expression, type);
if (best != null)
{
return best;
}
// The conversion must be a cast.
/**
* A type can be upcast to a supertype and a supertype can be downcast to a subtype.
* However, there is no such thing as a cross-cast.
*/
final boolean case1 = value.isSubtypeOf(type) || type.isSubtypeOf(value);
/**
* The input-type must be a reference-type,
* because a primitive-type cannot be *cast* to another type.
*/
final boolean case2 = value.isReferenceType();
/**
* The output-type must be an reference-type,
* because a value cannot be *cast* to a primitive-type.
*/
final boolean case3 = value.isReferenceType();
if (case1 && case2 && case3)
{
// The conversion is an apparently valid cast.
return new Conversion(null, value, type, true);
}
else
{
return null;
}
}
/**
* This method helps simplify the selection of a conversion method
*
* @param best is selected conversion, or null, if no conversion has been selected.
* @param name is the name of the conversion method.
* @param method_output is the return-type of the possible conversion method.
* @param method_input is the type of value that the possible conversion method accepts.
* @param value is the expression that produces the value to convert.
* @param type is the type that the value will be converted to, as requested by the user.
* @return a new conversion object, if the specified method is applicable; otherwise, null.
*/
private static Conversion isPredefined(final Conversion best,
final String name,
final IReturnType method_output,
final IExpressionType method_input,
final IExpressionType value,
final IReturnType type)
{
if (best != null)
{
return best;
}
else if (method_output.equals(type) && value.isSubtypeOf(method_input))
{
return new Conversion(name, method_input, method_output, false);
}
else
{
return null;
}
}
}