mirror of
https://github.com/irmen/prog8.git
synced 2024-12-25 23:29:55 +00:00
left and right of a binary expression should usually have the same datatype, insert typecast if needed
This commit is contained in:
parent
75d8c832ad
commit
3ee1b2efdd
@ -992,20 +992,21 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
|
||||
|
||||
// binary expression should actually have been optimized away into a single value, before const value was requested...
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
override fun referencesIdentifier(name: String) = left.referencesIdentifier(name) || right.referencesIdentifier(name)
|
||||
override fun inferType(program: Program): DataType? {
|
||||
val leftDt = left.inferType(program)
|
||||
val rightDt = right.inferType(program)
|
||||
return when(operator) {
|
||||
"+", "-", "*", "**", "%" -> if(leftDt==null || rightDt==null) null else {
|
||||
return when (operator) {
|
||||
"+", "-", "*", "**", "%" -> if (leftDt == null || rightDt == null) null else {
|
||||
try {
|
||||
arithmeticOpDt(leftDt, rightDt)
|
||||
} catch(x: FatalAstException) {
|
||||
} catch (x: FatalAstException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
"/" -> if(leftDt==null || rightDt==null) null else divisionOpDt(leftDt, rightDt)
|
||||
"/" -> if (leftDt == null || rightDt == null) null else divisionOpDt(leftDt, rightDt)
|
||||
"&" -> leftDt
|
||||
"|" -> leftDt
|
||||
"^" -> leftDt
|
||||
@ -1020,28 +1021,28 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
|
||||
|
||||
companion object {
|
||||
fun divisionOpDt(leftDt: DataType, rightDt: DataType): DataType {
|
||||
return when(leftDt) {
|
||||
DataType.UBYTE -> when(rightDt) {
|
||||
return when (leftDt) {
|
||||
DataType.UBYTE -> when (rightDt) {
|
||||
DataType.UBYTE, DataType.UWORD -> DataType.UBYTE
|
||||
DataType.BYTE, DataType.WORD -> DataType.WORD
|
||||
DataType.FLOAT -> DataType.BYTE
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
DataType.BYTE -> when(rightDt) {
|
||||
DataType.BYTE -> when (rightDt) {
|
||||
in NumericDatatypes -> DataType.BYTE
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
DataType.UWORD -> when(rightDt) {
|
||||
DataType.UWORD -> when (rightDt) {
|
||||
DataType.UBYTE, DataType.UWORD -> DataType.UWORD
|
||||
DataType.BYTE, DataType.WORD -> DataType.WORD
|
||||
DataType.FLOAT -> DataType.FLOAT
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
DataType.WORD -> when(rightDt) {
|
||||
DataType.WORD -> when (rightDt) {
|
||||
in NumericDatatypes -> DataType.WORD
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
DataType.FLOAT -> when(rightDt) {
|
||||
DataType.FLOAT -> when (rightDt) {
|
||||
in NumericDatatypes -> DataType.FLOAT
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
@ -1050,8 +1051,8 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
|
||||
}
|
||||
|
||||
fun arithmeticOpDt(leftDt: DataType, rightDt: DataType): DataType {
|
||||
return when(leftDt) {
|
||||
DataType.UBYTE -> when(rightDt) {
|
||||
return when (leftDt) {
|
||||
DataType.UBYTE -> when (rightDt) {
|
||||
DataType.UBYTE -> DataType.UBYTE
|
||||
DataType.BYTE -> DataType.BYTE
|
||||
DataType.UWORD -> DataType.UWORD
|
||||
@ -1059,24 +1060,24 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
|
||||
DataType.FLOAT -> DataType.FLOAT
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
DataType.BYTE -> when(rightDt) {
|
||||
DataType.BYTE -> when (rightDt) {
|
||||
in ByteDatatypes -> DataType.BYTE
|
||||
in WordDatatypes -> DataType.WORD
|
||||
DataType.FLOAT -> DataType.FLOAT
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
DataType.UWORD -> when(rightDt) {
|
||||
DataType.UWORD -> when (rightDt) {
|
||||
DataType.UBYTE, DataType.UWORD -> DataType.UWORD
|
||||
DataType.BYTE, DataType.WORD -> DataType.WORD
|
||||
DataType.FLOAT -> DataType.FLOAT
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
DataType.WORD -> when(rightDt) {
|
||||
DataType.WORD -> when (rightDt) {
|
||||
in IntegerDatatypes -> DataType.WORD
|
||||
DataType.FLOAT -> DataType.FLOAT
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
DataType.FLOAT -> when(rightDt) {
|
||||
DataType.FLOAT -> when (rightDt) {
|
||||
in NumericDatatypes -> DataType.FLOAT
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
@ -1085,6 +1086,83 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
|
||||
}
|
||||
}
|
||||
|
||||
fun commonDatatype(leftDt: DataType, rightDt: DataType,
|
||||
left: IExpression, right: IExpression): Pair<DataType, IExpression?> {
|
||||
// byte + byte -> byte
|
||||
// byte + word -> word
|
||||
// word + byte -> word
|
||||
// word + word -> word
|
||||
// a combination with a float will be float (but give a warning about this!)
|
||||
|
||||
val floatWarning = "byte or word value implicitly converted to float. Suggestion: use explicit cast as float, a float number, or revert to integer arithmetic"
|
||||
|
||||
if(this.operator=="/") {
|
||||
// division is a bit weird, don't cast the operands
|
||||
val commondt = divisionOpDt(leftDt, rightDt)
|
||||
return Pair(commondt, null)
|
||||
}
|
||||
|
||||
return when (leftDt) {
|
||||
DataType.UBYTE -> {
|
||||
when (rightDt) {
|
||||
DataType.UBYTE -> Pair(DataType.UBYTE, null)
|
||||
DataType.BYTE -> Pair(DataType.BYTE, left)
|
||||
DataType.UWORD -> Pair(DataType.UWORD, left)
|
||||
DataType.WORD -> Pair(DataType.WORD, left)
|
||||
DataType.FLOAT -> {
|
||||
printWarning(floatWarning, left.position)
|
||||
Pair(DataType.FLOAT, left)
|
||||
}
|
||||
else -> throw FatalAstException("non-numeric datatype $rightDt")
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
when (rightDt) {
|
||||
DataType.UBYTE -> Pair(DataType.BYTE, right)
|
||||
DataType.BYTE -> Pair(DataType.BYTE, null)
|
||||
DataType.UWORD -> Pair(DataType.WORD, left)
|
||||
DataType.WORD -> Pair(DataType.WORD, left)
|
||||
DataType.FLOAT -> {
|
||||
printWarning(floatWarning, left.position)
|
||||
Pair(DataType.FLOAT, left)
|
||||
}
|
||||
else -> throw FatalAstException("non-numeric datatype $rightDt")
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
when (rightDt) {
|
||||
DataType.UBYTE -> Pair(DataType.UWORD, right)
|
||||
DataType.BYTE -> Pair(DataType.UWORD, right)
|
||||
DataType.UWORD -> Pair(DataType.UWORD, null)
|
||||
DataType.WORD -> Pair(DataType.WORD, left)
|
||||
DataType.FLOAT -> {
|
||||
printWarning(floatWarning, left.position)
|
||||
Pair(DataType.FLOAT, left)
|
||||
}
|
||||
else -> throw FatalAstException("non-numeric datatype $rightDt")
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
when (rightDt) {
|
||||
DataType.UBYTE -> Pair(DataType.WORD, right)
|
||||
DataType.BYTE -> Pair(DataType.WORD, right)
|
||||
DataType.UWORD -> Pair(DataType.WORD, right)
|
||||
DataType.WORD -> Pair(DataType.WORD, null)
|
||||
DataType.FLOAT -> {
|
||||
printWarning(floatWarning, left.position)
|
||||
Pair(DataType.FLOAT, left)
|
||||
}
|
||||
else -> throw FatalAstException("non-numeric datatype $rightDt")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if (rightDt != DataType.FLOAT)
|
||||
printWarning(floatWarning, left.position)
|
||||
Pair(DataType.FLOAT, right)
|
||||
}
|
||||
else -> throw FatalAstException("non-numeric datatype $leftDt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ArrayIndexedExpression(val identifier: IdentifierReference,
|
||||
|
@ -172,6 +172,29 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
|
||||
return super.process(decl)
|
||||
}
|
||||
|
||||
override fun process(expr: BinaryExpression): IExpression {
|
||||
val leftDt = expr.left.inferType(program)
|
||||
val rightDt = expr.right.inferType(program)
|
||||
if(leftDt!=null && rightDt!=null && leftDt!=rightDt) {
|
||||
// determine common datatype and add typecast as required to make left and right equal types
|
||||
val (commonDt, toFix) = expr.commonDatatype(leftDt, rightDt, expr.left, expr.right)
|
||||
if(toFix!=null) {
|
||||
when {
|
||||
toFix===expr.left -> {
|
||||
expr.left = TypecastExpression(expr.left, commonDt, expr.left.position)
|
||||
expr.left.linkParents(expr)
|
||||
}
|
||||
toFix===expr.right -> {
|
||||
expr.right = TypecastExpression(expr.right, commonDt, expr.right.position)
|
||||
expr.right.linkParents(expr)
|
||||
}
|
||||
else -> throw FatalAstException("confused binary expression side")
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.process(expr)
|
||||
}
|
||||
|
||||
private fun sortConstantAssignments(statements: MutableList<IStatement>) {
|
||||
// sort assignments by datatype and value, so multiple initializations with the isSameAs value can be optimized (to load the value just once)
|
||||
val result = mutableListOf<IStatement>()
|
||||
|
@ -546,70 +546,6 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
||||
prog.instr(Opcode.NOP)
|
||||
}
|
||||
|
||||
private fun commonDatatype(leftDt: DataType, rightDt: DataType, leftpos: Position, rightpos: Position): DataType {
|
||||
// byte + byte -> byte
|
||||
// byte + word -> word
|
||||
// word + byte -> word
|
||||
// word + word -> word
|
||||
// a combination with a float will be float (but give a warning about this!)
|
||||
|
||||
val floatWarning = "byte or word value implicitly converted to float. Suggestion: use explicit cast as float, a float number, or revert to integer arithmetic"
|
||||
|
||||
return when(leftDt) {
|
||||
DataType.UBYTE -> {
|
||||
when(rightDt) {
|
||||
DataType.UBYTE -> DataType.UBYTE
|
||||
DataType.BYTE -> DataType.BYTE
|
||||
DataType.UWORD -> DataType.UWORD
|
||||
DataType.WORD -> DataType.WORD
|
||||
DataType.FLOAT -> {
|
||||
printWarning(floatWarning, leftpos)
|
||||
DataType.FLOAT
|
||||
}
|
||||
else -> throw CompilerException("non-numeric datatype $rightDt")
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
when(rightDt) {
|
||||
in ByteDatatypes -> DataType.BYTE
|
||||
in WordDatatypes -> DataType.WORD
|
||||
DataType.FLOAT -> {
|
||||
printWarning(floatWarning, leftpos)
|
||||
DataType.FLOAT
|
||||
}
|
||||
else -> throw CompilerException("non-numeric datatype $rightDt")
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
when(rightDt) {
|
||||
DataType.UBYTE, DataType.UWORD -> DataType.UWORD
|
||||
DataType.BYTE, DataType.WORD -> DataType.WORD
|
||||
DataType.FLOAT -> {
|
||||
printWarning(floatWarning, leftpos)
|
||||
DataType.FLOAT
|
||||
}
|
||||
else -> throw CompilerException("non-numeric datatype $rightDt")
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
when(rightDt) {
|
||||
DataType.UBYTE, DataType.UWORD, DataType.BYTE, DataType.WORD -> DataType.WORD
|
||||
DataType.FLOAT -> {
|
||||
printWarning(floatWarning, leftpos)
|
||||
DataType.FLOAT
|
||||
}
|
||||
else -> throw CompilerException("non-numeric datatype $rightDt")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if(rightDt!=DataType.FLOAT)
|
||||
printWarning(floatWarning, rightpos)
|
||||
DataType.FLOAT
|
||||
}
|
||||
else -> throw CompilerException("non-numeric datatype $leftDt")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(expr: IExpression) {
|
||||
when(expr) {
|
||||
is RegisterExpr -> {
|
||||
@ -622,11 +558,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
||||
is BinaryExpression -> {
|
||||
val leftDt = expr.left.inferType(program)!!
|
||||
val rightDt = expr.right.inferType(program)!!
|
||||
val commonDt =
|
||||
if(expr.operator=="/")
|
||||
BinaryExpression.divisionOpDt(leftDt, rightDt)
|
||||
else
|
||||
commonDatatype(leftDt, rightDt, expr.left.position, expr.right.position)
|
||||
val (commonDt, _) = expr.commonDatatype(leftDt, rightDt, expr.left, expr.right)
|
||||
translate(expr.left)
|
||||
if(leftDt!=commonDt)
|
||||
convertType(leftDt, commonDt)
|
||||
|
Loading…
Reference in New Issue
Block a user