package me.august.lumen.compile.parser.ast.expr;
import me.august.lumen.common.BytecodeUtil;
import me.august.lumen.compile.codegen.BuildContext;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
// NOTE: AddExpr does NOT necessarily indicate addition,
// it is *additive* (which includes subtraction)
public class AddExpr extends BinaryExpression {
public enum Op {
ADD, SUB
}
private Type type;
private Op op;
public AddExpr(Expression left, Expression right, Op op) {
super(left, right);
this.op = op;
}
@Override
public Type expressionType() {
if (type != null) return type; // return if already cached
Type leftType = left.expressionType();
Type rightType = right.expressionType();
// if either operands is a String type, entire expression is a String type
if (BytecodeUtil.isString(leftType) || BytecodeUtil.isString(rightType)) {
return (type = BytecodeUtil.STRING_TYPE);
}
if (BytecodeUtil.isPrimitive(leftType) && BytecodeUtil.isPrimitive(rightType)) {
Type widest = BytecodeUtil.widest(leftType, rightType);
// if the widest of the two is less than int,
// promote to int
if (widest.getSort() < Type.INT) {
widest = Type.INT_TYPE;
}
return (type = widest);
}
throw new RuntimeException("Incompatible operands: " + leftType + ", " + rightType);
}
@Override
public void generate(MethodVisitor visitor, BuildContext context) {
if (BytecodeUtil.isString(expressionType())) {
BytecodeUtil.concatStringsBytecode(
visitor, context, left, right
);
return;
}
int opcode;
Type leftType = left.expressionType();
Type rightType = right.expressionType();
left.generate(visitor, context);
// the opcode we need to convert the left hand value
// to the target value
opcode = BytecodeUtil.castNumberOpcode(leftType, expressionType());
// opcode will be -1 if casting is not possible or no
// casting is possible
if (opcode >= 0)
visitor.visitInsn(opcode);
right.generate(visitor, context);
opcode = BytecodeUtil.castNumberOpcode(rightType, expressionType());
if (opcode >= 0)
visitor.visitInsn(opcode);
// add
if (op == Op.ADD) {
opcode = BytecodeUtil.addInstruction(expressionType());
} else { // subtract
opcode = BytecodeUtil.subtractInstruction(expressionType());
}
visitor.visitInsn(opcode);
}
}