Expand bytecode expression support for mixed ops
This commit is contained in:
parent
8ae6eb8d69
commit
9c56cf751b
@ -149,6 +149,8 @@ Note: Any opcode can be compiled to FALLBACK if not implemented in a VM pass.
|
||||
- CMP_GT_REAL_INT S, S -> S
|
||||
- CMP_GTE_INT_REAL S, S -> S
|
||||
- CMP_GTE_REAL_INT S, S -> S
|
||||
- CMP_NEQ_INT_REAL S, S -> S
|
||||
- CMP_NEQ_REAL_INT S, S -> S
|
||||
|
||||
### Boolean ops
|
||||
- NOT_BOOL S -> S
|
||||
|
||||
@ -132,7 +132,7 @@ class BytecodeBuilder {
|
||||
Opcode.CMP_EQ_BOOL, Opcode.CMP_NEQ_BOOL,
|
||||
Opcode.CMP_EQ_INT_REAL, Opcode.CMP_EQ_REAL_INT, Opcode.CMP_LT_INT_REAL, Opcode.CMP_LT_REAL_INT,
|
||||
Opcode.CMP_LTE_INT_REAL, Opcode.CMP_LTE_REAL_INT, Opcode.CMP_GT_INT_REAL, Opcode.CMP_GT_REAL_INT,
|
||||
Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT,
|
||||
Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT, Opcode.CMP_NEQ_INT_REAL, Opcode.CMP_NEQ_REAL_INT,
|
||||
Opcode.AND_BOOL, Opcode.OR_BOOL ->
|
||||
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||
Opcode.INC_INT, Opcode.DEC_INT, Opcode.RET ->
|
||||
|
||||
@ -26,6 +26,7 @@ import net.sergeych.lyng.obj.*
|
||||
class BytecodeCompiler {
|
||||
private val builder = BytecodeBuilder()
|
||||
private var nextSlot = 0
|
||||
private val slotTypes = mutableMapOf<Int, SlotType>()
|
||||
|
||||
fun compileStatement(name: String, stmt: net.sergeych.lyng.Statement): BytecodeFunction? {
|
||||
return when (stmt) {
|
||||
@ -52,7 +53,7 @@ class BytecodeCompiler {
|
||||
is LocalSlotRef -> {
|
||||
if (ref.name.isEmpty()) return null
|
||||
if (refDepth(ref) != 0) return null
|
||||
CompiledValue(refSlot(ref), SlotType.UNKNOWN)
|
||||
CompiledValue(refSlot(ref), slotTypes[refSlot(ref)] ?: SlotType.UNKNOWN)
|
||||
}
|
||||
is BinaryOpRef -> compileBinary(ref)
|
||||
is UnaryOpRef -> compileUnary(ref)
|
||||
@ -131,50 +132,101 @@ class BytecodeCompiler {
|
||||
}
|
||||
val a = compileRef(binaryLeft(ref)) ?: return null
|
||||
val b = compileRef(binaryRight(ref)) ?: return null
|
||||
if (a.type != b.type && a.type != SlotType.UNKNOWN && b.type != SlotType.UNKNOWN) return null
|
||||
val typesMismatch = a.type != b.type && a.type != SlotType.UNKNOWN && b.type != SlotType.UNKNOWN
|
||||
if (typesMismatch && op !in setOf(BinOp.EQ, BinOp.NEQ, BinOp.LT, BinOp.LTE, BinOp.GT, BinOp.GTE)) {
|
||||
return null
|
||||
}
|
||||
val out = allocSlot()
|
||||
return when (op) {
|
||||
BinOp.PLUS -> when (a.type) {
|
||||
SlotType.INT -> {
|
||||
builder.emit(Opcode.ADD_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.INT)
|
||||
when (b.type) {
|
||||
SlotType.INT -> {
|
||||
builder.emit(Opcode.ADD_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.INT)
|
||||
}
|
||||
SlotType.REAL -> compileRealArithmeticWithCoercion(Opcode.ADD_REAL, a, b, out)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
SlotType.REAL -> {
|
||||
builder.emit(Opcode.ADD_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.REAL)
|
||||
when (b.type) {
|
||||
SlotType.REAL -> {
|
||||
builder.emit(Opcode.ADD_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.REAL)
|
||||
}
|
||||
SlotType.INT -> compileRealArithmeticWithCoercion(Opcode.ADD_REAL, a, b, out)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
BinOp.MINUS -> when (a.type) {
|
||||
SlotType.INT -> {
|
||||
builder.emit(Opcode.SUB_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.INT)
|
||||
when (b.type) {
|
||||
SlotType.INT -> {
|
||||
builder.emit(Opcode.SUB_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.INT)
|
||||
}
|
||||
SlotType.REAL -> compileRealArithmeticWithCoercion(Opcode.SUB_REAL, a, b, out)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
SlotType.REAL -> {
|
||||
builder.emit(Opcode.SUB_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.REAL)
|
||||
when (b.type) {
|
||||
SlotType.REAL -> {
|
||||
builder.emit(Opcode.SUB_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.REAL)
|
||||
}
|
||||
SlotType.INT -> compileRealArithmeticWithCoercion(Opcode.SUB_REAL, a, b, out)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
BinOp.STAR -> when (a.type) {
|
||||
SlotType.INT -> {
|
||||
builder.emit(Opcode.MUL_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.INT)
|
||||
when (b.type) {
|
||||
SlotType.INT -> {
|
||||
builder.emit(Opcode.MUL_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.INT)
|
||||
}
|
||||
SlotType.REAL -> compileRealArithmeticWithCoercion(Opcode.MUL_REAL, a, b, out)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
SlotType.REAL -> {
|
||||
builder.emit(Opcode.MUL_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.REAL)
|
||||
when (b.type) {
|
||||
SlotType.REAL -> {
|
||||
builder.emit(Opcode.MUL_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.REAL)
|
||||
}
|
||||
SlotType.INT -> compileRealArithmeticWithCoercion(Opcode.MUL_REAL, a, b, out)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
BinOp.SLASH -> when (a.type) {
|
||||
SlotType.INT -> {
|
||||
builder.emit(Opcode.DIV_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.INT)
|
||||
when (b.type) {
|
||||
SlotType.INT -> {
|
||||
builder.emit(Opcode.DIV_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.INT)
|
||||
}
|
||||
SlotType.REAL -> compileRealArithmeticWithCoercion(Opcode.DIV_REAL, a, b, out)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
SlotType.REAL -> {
|
||||
builder.emit(Opcode.DIV_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.REAL)
|
||||
when (b.type) {
|
||||
SlotType.REAL -> {
|
||||
builder.emit(Opcode.DIV_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.REAL)
|
||||
}
|
||||
SlotType.INT -> compileRealArithmeticWithCoercion(Opcode.DIV_REAL, a, b, out)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
@ -184,90 +236,22 @@ class BytecodeCompiler {
|
||||
CompiledValue(out, SlotType.INT)
|
||||
}
|
||||
BinOp.EQ -> {
|
||||
when (a.type) {
|
||||
SlotType.INT -> {
|
||||
builder.emit(Opcode.CMP_EQ_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
SlotType.REAL -> {
|
||||
builder.emit(Opcode.CMP_EQ_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
SlotType.BOOL -> {
|
||||
builder.emit(Opcode.CMP_EQ_BOOL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
compileCompareEq(a, b, out)
|
||||
}
|
||||
BinOp.NEQ -> {
|
||||
when (a.type) {
|
||||
SlotType.INT -> {
|
||||
builder.emit(Opcode.CMP_NEQ_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
SlotType.REAL -> {
|
||||
builder.emit(Opcode.CMP_NEQ_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
SlotType.BOOL -> {
|
||||
builder.emit(Opcode.CMP_NEQ_BOOL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
compileCompareNeq(a, b, out)
|
||||
}
|
||||
BinOp.LT -> {
|
||||
when (a.type) {
|
||||
SlotType.INT -> {
|
||||
builder.emit(Opcode.CMP_LT_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
SlotType.REAL -> {
|
||||
builder.emit(Opcode.CMP_LT_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
compileCompareLt(a, b, out)
|
||||
}
|
||||
BinOp.LTE -> {
|
||||
when (a.type) {
|
||||
SlotType.INT -> {
|
||||
builder.emit(Opcode.CMP_LTE_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
SlotType.REAL -> {
|
||||
builder.emit(Opcode.CMP_LTE_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
compileCompareLte(a, b, out)
|
||||
}
|
||||
BinOp.GT -> {
|
||||
when (a.type) {
|
||||
SlotType.INT -> {
|
||||
builder.emit(Opcode.CMP_GT_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
SlotType.REAL -> {
|
||||
builder.emit(Opcode.CMP_GT_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
compileCompareGt(a, b, out)
|
||||
}
|
||||
BinOp.GTE -> {
|
||||
when (a.type) {
|
||||
SlotType.INT -> {
|
||||
builder.emit(Opcode.CMP_GTE_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
SlotType.REAL -> {
|
||||
builder.emit(Opcode.CMP_GTE_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
compileCompareGte(a, b, out)
|
||||
}
|
||||
BinOp.AND -> {
|
||||
if (a.type != SlotType.BOOL) return null
|
||||
@ -308,6 +292,173 @@ class BytecodeCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
private fun compileRealArithmeticWithCoercion(
|
||||
op: Opcode,
|
||||
a: CompiledValue,
|
||||
b: CompiledValue,
|
||||
out: Int
|
||||
): CompiledValue? {
|
||||
if (a.type == SlotType.INT && b.type == SlotType.REAL) {
|
||||
val left = allocSlot()
|
||||
builder.emit(Opcode.INT_TO_REAL, a.slot, left)
|
||||
builder.emit(op, left, b.slot, out)
|
||||
return CompiledValue(out, SlotType.REAL)
|
||||
}
|
||||
if (a.type == SlotType.REAL && b.type == SlotType.INT) {
|
||||
val right = allocSlot()
|
||||
builder.emit(Opcode.INT_TO_REAL, b.slot, right)
|
||||
builder.emit(op, a.slot, right, out)
|
||||
return CompiledValue(out, SlotType.REAL)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun compileCompareEq(a: CompiledValue, b: CompiledValue, out: Int): CompiledValue? {
|
||||
if (a.type == SlotType.UNKNOWN || b.type == SlotType.UNKNOWN) return null
|
||||
return when {
|
||||
a.type == SlotType.INT && b.type == SlotType.INT -> {
|
||||
builder.emit(Opcode.CMP_EQ_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.REAL && b.type == SlotType.REAL -> {
|
||||
builder.emit(Opcode.CMP_EQ_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.BOOL && b.type == SlotType.BOOL -> {
|
||||
builder.emit(Opcode.CMP_EQ_BOOL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.INT && b.type == SlotType.REAL -> {
|
||||
builder.emit(Opcode.CMP_EQ_INT_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.REAL && b.type == SlotType.INT -> {
|
||||
builder.emit(Opcode.CMP_EQ_REAL_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun compileCompareNeq(a: CompiledValue, b: CompiledValue, out: Int): CompiledValue? {
|
||||
if (a.type == SlotType.UNKNOWN || b.type == SlotType.UNKNOWN) return null
|
||||
return when {
|
||||
a.type == SlotType.INT && b.type == SlotType.INT -> {
|
||||
builder.emit(Opcode.CMP_NEQ_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.REAL && b.type == SlotType.REAL -> {
|
||||
builder.emit(Opcode.CMP_NEQ_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.BOOL && b.type == SlotType.BOOL -> {
|
||||
builder.emit(Opcode.CMP_NEQ_BOOL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.INT && b.type == SlotType.REAL -> {
|
||||
builder.emit(Opcode.CMP_NEQ_INT_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.REAL && b.type == SlotType.INT -> {
|
||||
builder.emit(Opcode.CMP_NEQ_REAL_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun compileCompareLt(a: CompiledValue, b: CompiledValue, out: Int): CompiledValue? {
|
||||
if (a.type == SlotType.UNKNOWN || b.type == SlotType.UNKNOWN) return null
|
||||
return when {
|
||||
a.type == SlotType.INT && b.type == SlotType.INT -> {
|
||||
builder.emit(Opcode.CMP_LT_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.REAL && b.type == SlotType.REAL -> {
|
||||
builder.emit(Opcode.CMP_LT_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.INT && b.type == SlotType.REAL -> {
|
||||
builder.emit(Opcode.CMP_LT_INT_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.REAL && b.type == SlotType.INT -> {
|
||||
builder.emit(Opcode.CMP_LT_REAL_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun compileCompareLte(a: CompiledValue, b: CompiledValue, out: Int): CompiledValue? {
|
||||
if (a.type == SlotType.UNKNOWN || b.type == SlotType.UNKNOWN) return null
|
||||
return when {
|
||||
a.type == SlotType.INT && b.type == SlotType.INT -> {
|
||||
builder.emit(Opcode.CMP_LTE_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.REAL && b.type == SlotType.REAL -> {
|
||||
builder.emit(Opcode.CMP_LTE_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.INT && b.type == SlotType.REAL -> {
|
||||
builder.emit(Opcode.CMP_LTE_INT_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.REAL && b.type == SlotType.INT -> {
|
||||
builder.emit(Opcode.CMP_LTE_REAL_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun compileCompareGt(a: CompiledValue, b: CompiledValue, out: Int): CompiledValue? {
|
||||
if (a.type == SlotType.UNKNOWN || b.type == SlotType.UNKNOWN) return null
|
||||
return when {
|
||||
a.type == SlotType.INT && b.type == SlotType.INT -> {
|
||||
builder.emit(Opcode.CMP_GT_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.REAL && b.type == SlotType.REAL -> {
|
||||
builder.emit(Opcode.CMP_GT_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.INT && b.type == SlotType.REAL -> {
|
||||
builder.emit(Opcode.CMP_GT_INT_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.REAL && b.type == SlotType.INT -> {
|
||||
builder.emit(Opcode.CMP_GT_REAL_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun compileCompareGte(a: CompiledValue, b: CompiledValue, out: Int): CompiledValue? {
|
||||
if (a.type == SlotType.UNKNOWN || b.type == SlotType.UNKNOWN) return null
|
||||
return when {
|
||||
a.type == SlotType.INT && b.type == SlotType.INT -> {
|
||||
builder.emit(Opcode.CMP_GTE_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.REAL && b.type == SlotType.REAL -> {
|
||||
builder.emit(Opcode.CMP_GTE_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.INT && b.type == SlotType.REAL -> {
|
||||
builder.emit(Opcode.CMP_GTE_INT_REAL, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
a.type == SlotType.REAL && b.type == SlotType.INT -> {
|
||||
builder.emit(Opcode.CMP_GTE_REAL_INT, a.slot, b.slot, out)
|
||||
CompiledValue(out, SlotType.BOOL)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun compileLogical(op: BinOp, left: ObjRef, right: ObjRef, pos: Pos): CompiledValue? {
|
||||
val leftValue = compileRefWithFallback(left, SlotType.BOOL, pos) ?: return null
|
||||
if (leftValue.type != SlotType.BOOL) return null
|
||||
@ -346,6 +497,7 @@ class BytecodeCompiler {
|
||||
SlotType.BOOL -> builder.emit(Opcode.MOVE_BOOL, value.slot, slot)
|
||||
else -> builder.emit(Opcode.MOVE_OBJ, value.slot, slot)
|
||||
}
|
||||
updateSlotType(slot, value.type)
|
||||
return CompiledValue(slot, value.type)
|
||||
}
|
||||
|
||||
@ -412,6 +564,7 @@ class BytecodeCompiler {
|
||||
}
|
||||
val id = builder.addFallback(stmt)
|
||||
builder.emit(Opcode.EVAL_FALLBACK, id, slot)
|
||||
updateSlotType(slot, forceType ?: SlotType.OBJ)
|
||||
return CompiledValue(slot, forceType ?: SlotType.OBJ)
|
||||
}
|
||||
|
||||
@ -425,4 +578,12 @@ class BytecodeCompiler {
|
||||
private fun assignTarget(ref: AssignRef): LocalSlotRef? = ref.target as? LocalSlotRef
|
||||
private fun assignValue(ref: AssignRef): ObjRef = ref.value
|
||||
private fun refPos(ref: BinaryOpRef): Pos = Pos.builtIn
|
||||
|
||||
private fun updateSlotType(slot: Int, type: SlotType) {
|
||||
if (type == SlotType.UNKNOWN) {
|
||||
slotTypes.remove(slot)
|
||||
} else {
|
||||
slotTypes[slot] = type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ object BytecodeDisassembler {
|
||||
Opcode.CMP_EQ_BOOL, Opcode.CMP_NEQ_BOOL,
|
||||
Opcode.CMP_EQ_INT_REAL, Opcode.CMP_EQ_REAL_INT, Opcode.CMP_LT_INT_REAL, Opcode.CMP_LT_REAL_INT,
|
||||
Opcode.CMP_LTE_INT_REAL, Opcode.CMP_LTE_REAL_INT, Opcode.CMP_GT_INT_REAL, Opcode.CMP_GT_REAL_INT,
|
||||
Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT,
|
||||
Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT, Opcode.CMP_NEQ_INT_REAL, Opcode.CMP_NEQ_REAL_INT,
|
||||
Opcode.AND_BOOL, Opcode.OR_BOOL ->
|
||||
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||
Opcode.INC_INT, Opcode.DEC_INT, Opcode.RET ->
|
||||
|
||||
@ -115,6 +115,34 @@ class BytecodeVm {
|
||||
ip += fn.slotWidth
|
||||
frame.setObj(dst, frame.getObj(src))
|
||||
}
|
||||
Opcode.INT_TO_REAL -> {
|
||||
val src = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setReal(dst, frame.getInt(src).toDouble())
|
||||
}
|
||||
Opcode.REAL_TO_INT -> {
|
||||
val src = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setInt(dst, frame.getReal(src).toLong())
|
||||
}
|
||||
Opcode.BOOL_TO_INT -> {
|
||||
val src = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setInt(dst, if (frame.getBool(src)) 1L else 0L)
|
||||
}
|
||||
Opcode.INT_TO_BOOL -> {
|
||||
val src = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getInt(src) != 0L)
|
||||
}
|
||||
Opcode.ADD_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
@ -124,6 +152,163 @@ class BytecodeVm {
|
||||
ip += fn.slotWidth
|
||||
frame.setInt(dst, frame.getInt(a) + frame.getInt(b))
|
||||
}
|
||||
Opcode.SUB_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setInt(dst, frame.getInt(a) - frame.getInt(b))
|
||||
}
|
||||
Opcode.MUL_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setInt(dst, frame.getInt(a) * frame.getInt(b))
|
||||
}
|
||||
Opcode.DIV_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setInt(dst, frame.getInt(a) / frame.getInt(b))
|
||||
}
|
||||
Opcode.MOD_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setInt(dst, frame.getInt(a) % frame.getInt(b))
|
||||
}
|
||||
Opcode.NEG_INT -> {
|
||||
val src = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setInt(dst, -frame.getInt(src))
|
||||
}
|
||||
Opcode.INC_INT -> {
|
||||
val slot = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setInt(slot, frame.getInt(slot) + 1L)
|
||||
}
|
||||
Opcode.DEC_INT -> {
|
||||
val slot = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setInt(slot, frame.getInt(slot) - 1L)
|
||||
}
|
||||
Opcode.ADD_REAL -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setReal(dst, frame.getReal(a) + frame.getReal(b))
|
||||
}
|
||||
Opcode.SUB_REAL -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setReal(dst, frame.getReal(a) - frame.getReal(b))
|
||||
}
|
||||
Opcode.MUL_REAL -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setReal(dst, frame.getReal(a) * frame.getReal(b))
|
||||
}
|
||||
Opcode.DIV_REAL -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setReal(dst, frame.getReal(a) / frame.getReal(b))
|
||||
}
|
||||
Opcode.NEG_REAL -> {
|
||||
val src = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setReal(dst, -frame.getReal(src))
|
||||
}
|
||||
Opcode.AND_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setInt(dst, frame.getInt(a) and frame.getInt(b))
|
||||
}
|
||||
Opcode.OR_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setInt(dst, frame.getInt(a) or frame.getInt(b))
|
||||
}
|
||||
Opcode.XOR_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setInt(dst, frame.getInt(a) xor frame.getInt(b))
|
||||
}
|
||||
Opcode.SHL_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setInt(dst, frame.getInt(a) shl frame.getInt(b).toInt())
|
||||
}
|
||||
Opcode.SHR_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setInt(dst, frame.getInt(a) shr frame.getInt(b).toInt())
|
||||
}
|
||||
Opcode.USHR_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setInt(dst, frame.getInt(a) ushr frame.getInt(b).toInt())
|
||||
}
|
||||
Opcode.INV_INT -> {
|
||||
val src = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setInt(dst, frame.getInt(src).inv())
|
||||
}
|
||||
Opcode.CMP_LT_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
@ -133,6 +318,33 @@ class BytecodeVm {
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getInt(a) < frame.getInt(b))
|
||||
}
|
||||
Opcode.CMP_LTE_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getInt(a) <= frame.getInt(b))
|
||||
}
|
||||
Opcode.CMP_GT_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getInt(a) > frame.getInt(b))
|
||||
}
|
||||
Opcode.CMP_GTE_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getInt(a) >= frame.getInt(b))
|
||||
}
|
||||
Opcode.CMP_EQ_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
@ -151,6 +363,60 @@ class BytecodeVm {
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getInt(a) != frame.getInt(b))
|
||||
}
|
||||
Opcode.CMP_EQ_REAL -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getReal(a) == frame.getReal(b))
|
||||
}
|
||||
Opcode.CMP_NEQ_REAL -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getReal(a) != frame.getReal(b))
|
||||
}
|
||||
Opcode.CMP_LT_REAL -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getReal(a) < frame.getReal(b))
|
||||
}
|
||||
Opcode.CMP_LTE_REAL -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getReal(a) <= frame.getReal(b))
|
||||
}
|
||||
Opcode.CMP_GT_REAL -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getReal(a) > frame.getReal(b))
|
||||
}
|
||||
Opcode.CMP_GTE_REAL -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getReal(a) >= frame.getReal(b))
|
||||
}
|
||||
Opcode.CMP_EQ_BOOL -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
@ -169,6 +435,121 @@ class BytecodeVm {
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getBool(a) != frame.getBool(b))
|
||||
}
|
||||
Opcode.CMP_EQ_INT_REAL -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getInt(a).toDouble() == frame.getReal(b))
|
||||
}
|
||||
Opcode.CMP_EQ_REAL_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getReal(a) == frame.getInt(b).toDouble())
|
||||
}
|
||||
Opcode.CMP_LT_INT_REAL -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getInt(a).toDouble() < frame.getReal(b))
|
||||
}
|
||||
Opcode.CMP_LT_REAL_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getReal(a) < frame.getInt(b).toDouble())
|
||||
}
|
||||
Opcode.CMP_LTE_INT_REAL -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getInt(a).toDouble() <= frame.getReal(b))
|
||||
}
|
||||
Opcode.CMP_LTE_REAL_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getReal(a) <= frame.getInt(b).toDouble())
|
||||
}
|
||||
Opcode.CMP_GT_INT_REAL -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getInt(a).toDouble() > frame.getReal(b))
|
||||
}
|
||||
Opcode.CMP_GT_REAL_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getReal(a) > frame.getInt(b).toDouble())
|
||||
}
|
||||
Opcode.CMP_GTE_INT_REAL -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getInt(a).toDouble() >= frame.getReal(b))
|
||||
}
|
||||
Opcode.CMP_GTE_REAL_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getReal(a) >= frame.getInt(b).toDouble())
|
||||
}
|
||||
Opcode.CMP_NEQ_INT_REAL -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getInt(a).toDouble() != frame.getReal(b))
|
||||
}
|
||||
Opcode.CMP_NEQ_REAL_INT -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val b = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, frame.getReal(a) != frame.getInt(b).toDouble())
|
||||
}
|
||||
Opcode.NOT_BOOL -> {
|
||||
val src = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
val dst = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
frame.setBool(dst, !frame.getBool(src))
|
||||
}
|
||||
Opcode.AND_BOOL -> {
|
||||
val a = decoder.readSlot(code, ip)
|
||||
ip += fn.slotWidth
|
||||
|
||||
@ -81,6 +81,8 @@ enum class Opcode(val code: Int) {
|
||||
CMP_GT_REAL_INT(0x67),
|
||||
CMP_GTE_INT_REAL(0x68),
|
||||
CMP_GTE_REAL_INT(0x69),
|
||||
CMP_NEQ_INT_REAL(0x6A),
|
||||
CMP_NEQ_REAL_INT(0x6B),
|
||||
|
||||
NOT_BOOL(0x70),
|
||||
AND_BOOL(0x71),
|
||||
|
||||
@ -25,12 +25,16 @@ import net.sergeych.lyng.bytecode.Opcode
|
||||
import net.sergeych.lyng.obj.BinaryOpRef
|
||||
import net.sergeych.lyng.obj.BinOp
|
||||
import net.sergeych.lyng.obj.ConstRef
|
||||
import net.sergeych.lyng.obj.LocalSlotRef
|
||||
import net.sergeych.lyng.obj.ObjFalse
|
||||
import net.sergeych.lyng.obj.ObjInt
|
||||
import net.sergeych.lyng.obj.ObjTrue
|
||||
import net.sergeych.lyng.obj.ObjReal
|
||||
import net.sergeych.lyng.obj.AssignRef
|
||||
import net.sergeych.lyng.obj.ValueFnRef
|
||||
import net.sergeych.lyng.obj.ObjVoid
|
||||
import net.sergeych.lyng.obj.toBool
|
||||
import net.sergeych.lyng.obj.toDouble
|
||||
import net.sergeych.lyng.obj.toInt
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
@ -129,4 +133,97 @@ class BytecodeVmTest {
|
||||
val result = BytecodeVm().execute(fn, Scope(), emptyList())
|
||||
assertEquals(true, result.toBool())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun realArithmeticUsesBytecodeOps() = kotlinx.coroutines.test.runTest {
|
||||
val expr = ExpressionStatement(
|
||||
BinaryOpRef(
|
||||
BinOp.PLUS,
|
||||
ConstRef(ObjReal.of(2.5).asReadonly),
|
||||
ConstRef(ObjReal.of(3.25).asReadonly),
|
||||
),
|
||||
net.sergeych.lyng.Pos.builtIn
|
||||
)
|
||||
val fn = BytecodeCompiler().compileExpression("realPlus", expr) ?: error("bytecode compile failed")
|
||||
val result = BytecodeVm().execute(fn, Scope(), emptyList())
|
||||
assertEquals(5.75, result.toDouble())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mixedIntRealComparisonUsesBytecodeOps() = kotlinx.coroutines.test.runTest {
|
||||
val ltExpr = ExpressionStatement(
|
||||
BinaryOpRef(
|
||||
BinOp.LT,
|
||||
ConstRef(ObjInt.of(2).asReadonly),
|
||||
ConstRef(ObjReal.of(2.5).asReadonly),
|
||||
),
|
||||
net.sergeych.lyng.Pos.builtIn
|
||||
)
|
||||
val ltFn = BytecodeCompiler().compileExpression("mixedLt", ltExpr) ?: error("bytecode compile failed")
|
||||
val ltResult = BytecodeVm().execute(ltFn, Scope(), emptyList())
|
||||
assertEquals(true, ltResult.toBool())
|
||||
|
||||
val eqExpr = ExpressionStatement(
|
||||
BinaryOpRef(
|
||||
BinOp.EQ,
|
||||
ConstRef(ObjReal.of(4.0).asReadonly),
|
||||
ConstRef(ObjInt.of(4).asReadonly),
|
||||
),
|
||||
net.sergeych.lyng.Pos.builtIn
|
||||
)
|
||||
val eqFn = BytecodeCompiler().compileExpression("mixedEq", eqExpr) ?: error("bytecode compile failed")
|
||||
val eqResult = BytecodeVm().execute(eqFn, Scope(), emptyList())
|
||||
assertEquals(true, eqResult.toBool())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mixedIntRealArithmeticUsesBytecodeOps() = kotlinx.coroutines.test.runTest {
|
||||
val expr = ExpressionStatement(
|
||||
BinaryOpRef(
|
||||
BinOp.PLUS,
|
||||
ConstRef(ObjInt.of(2).asReadonly),
|
||||
ConstRef(ObjReal.of(3.5).asReadonly),
|
||||
),
|
||||
net.sergeych.lyng.Pos.builtIn
|
||||
)
|
||||
val fn = BytecodeCompiler().compileExpression("mixedPlus", expr) ?: error("bytecode compile failed")
|
||||
val result = BytecodeVm().execute(fn, Scope(), emptyList())
|
||||
assertEquals(5.5, result.toDouble())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mixedIntRealNotEqualUsesBytecodeOps() = kotlinx.coroutines.test.runTest {
|
||||
val expr = ExpressionStatement(
|
||||
BinaryOpRef(
|
||||
BinOp.NEQ,
|
||||
ConstRef(ObjInt.of(3).asReadonly),
|
||||
ConstRef(ObjReal.of(2.5).asReadonly),
|
||||
),
|
||||
net.sergeych.lyng.Pos.builtIn
|
||||
)
|
||||
val fn = BytecodeCompiler().compileExpression("mixedNeq", expr) ?: error("bytecode compile failed")
|
||||
val result = BytecodeVm().execute(fn, Scope(), emptyList())
|
||||
assertEquals(true, result.toBool())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun localSlotTypeTrackingEnablesArithmetic() = kotlinx.coroutines.test.runTest {
|
||||
val slotRef = LocalSlotRef("a", 0, 0, net.sergeych.lyng.Pos.builtIn)
|
||||
val assign = AssignRef(
|
||||
slotRef,
|
||||
ConstRef(ObjInt.of(2).asReadonly),
|
||||
net.sergeych.lyng.Pos.builtIn
|
||||
)
|
||||
val expr = ExpressionStatement(
|
||||
BinaryOpRef(
|
||||
BinOp.PLUS,
|
||||
assign,
|
||||
slotRef
|
||||
),
|
||||
net.sergeych.lyng.Pos.builtIn
|
||||
)
|
||||
val fn = BytecodeCompiler().compileExpression("localSlotAdd", expr) ?: error("bytecode compile failed")
|
||||
val result = BytecodeVm().execute(fn, Scope(), emptyList())
|
||||
assertEquals(4, result.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user