I'm writing an interpreter, and I'm designing the abstract syntax tree currently. Right now, I'm trying to figure out a way to add (or do other operations with) two Numbers, without casting to double or using a long switch statement
I've currently implemented addition with a switch statement, but I don't want to copy this for every operator. Is there a better way to do this?
AdditionNumNum.java
@Override
public ValueNumeric simplify(Context c) {
ValueNumeric left = this.left.simplify(c);
ValueNumeric right = this.right.simplify(c);
NumberType type = NumberType.leastCommonType(left.getType(), right.getType());
Number leftNum = left.get();
Number rightNum = right.get();
switch(type) {
case BYTE:
return new ValueNumeric(leftNum.byteValue() + rightNum.byteValue(), type);
case SHORT:
return new ValueNumeric(leftNum.shortValue() + rightNum.shortValue(), type);
case INT:
return new ValueNumeric(leftNum.intValue() + rightNum.intValue(), type);
case LONG:
return new ValueNumeric(leftNum.longValue() + rightNum.longValue(), type);
case FLOAT:
return new ValueNumeric(leftNum.floatValue() + rightNum.floatValue(), type);
case DOUBLE:
return new ValueNumeric(leftNum.doubleValue() + rightNum.doubleValue(), type);
}
throw new InterpreterException("Unknown type " + type);
}
ValueNumeric.java
public class ValueNumeric extends Value<Number> {
private final Number value;
private final NumberType type;
public ValueNumeric(Number value) {
this(value, NumberType.forClass(value.getClass()));
}
public ValueNumeric(Number value, NumberType type) {
if (!value.getClass().equals(type.clazz)) {
throw new IllegalArgumentException("Value (" + value
+ ") is not of the right type for " + type + " ("
+ value.getClass() + "/" + type.clazz + ")");
}
this.value = value;
this.type = type;
}
@Override
public Number get(){
return value;
}
public NumberType getType() {
return type;
}
public ValueNumeric castTo(NumberType newType) {
if (newType == this.type) {
return this;
}
Number newValue = type.cast(this.value);
return new ValueNumeric(newValue, newType);
}
@Override
public String toString() {
return "ValueNumeric [value=" + value + ", type=" + type + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((type == null) ? 0 : type.hashCode());
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
ValueNumeric other = (ValueNumeric) obj;
if (type != other.type)
return false;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
public enum NumberType {
BYTE(Byte.class, n -> Byte.valueOf(n.byteValue())),
SHORT(Short.class, n -> Short.valueOf(n.shortValue())),
INT(Integer.class, n -> Integer.valueOf(n.intValue())),
LONG(Long.class, n -> Long.valueOf(n.longValue())),
FLOAT(Float.class, n -> Float.valueOf(n.floatValue())),
DOUBLE(Double.class, n -> Double.valueOf(n.doubleValue()));
public final Class<? extends Number> clazz;
private Function<Number, ? extends Number> castFunc;
private <T extends Number> NumberType(Class<T> clazz,
Function<Number, T> castFunc) {
this.clazz = clazz;
this.castFunc = castFunc;
}
public Number cast(Number number) {
return castFunc.apply(number);
}
private static Map<Class<? extends Number>, NumberType> BY_CLASS = new HashMap<>();
static {
for (NumberType type : values()) {
BY_CLASS.put(type.clazz, type);
}
}
/**
*
*/
public static NumberType forClass(Class<? extends Number> clazz) {
return BY_CLASS.get(clazz);
}
/**
* Returns the lowest common type between these two types of numbers,
* for automatic type conversions.
*/
public static NumberType leastCommonType(NumberType left, NumberType right) {
if (left.ordinal() >= right.ordinal()) {
return left;
} else {
return right;
}
}
}
}
I would also appreciate any general comments on the code.