mirror of
https://github.com/irmen/prog8.git
synced 2024-06-14 00:29:34 +00:00
293 lines
16 KiB
Kotlin
293 lines
16 KiB
Kotlin
package prog8.optimizing
|
|
|
|
import prog8.ast.*
|
|
import prog8.ast.base.DataType
|
|
import prog8.ast.base.ExpressionError
|
|
import prog8.ast.base.FatalAstException
|
|
import prog8.ast.base.Position
|
|
import prog8.ast.expressions.LiteralValue
|
|
import kotlin.math.pow
|
|
|
|
|
|
val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
|
|
|
|
|
class ConstExprEvaluator {
|
|
|
|
fun evaluate(left: LiteralValue, operator: String, right: LiteralValue): IExpression {
|
|
return when(operator) {
|
|
"+" -> plus(left, right)
|
|
"-" -> minus(left, right)
|
|
"*" -> multiply(left, right)
|
|
"/" -> divide(left, right)
|
|
"%" -> remainder(left, right)
|
|
"**" -> power(left, right)
|
|
"&" -> bitwiseand(left, right)
|
|
"|" -> bitwiseor(left, right)
|
|
"^" -> bitwisexor(left, right)
|
|
"and" -> logicaland(left, right)
|
|
"or" -> logicalor(left, right)
|
|
"xor" -> logicalxor(left, right)
|
|
"<" -> LiteralValue.fromBoolean(left < right, left.position)
|
|
">" -> LiteralValue.fromBoolean(left > right, left.position)
|
|
"<=" -> LiteralValue.fromBoolean(left <= right, left.position)
|
|
">=" -> LiteralValue.fromBoolean(left >= right, left.position)
|
|
"==" -> LiteralValue.fromBoolean(left == right, left.position)
|
|
"!=" -> LiteralValue.fromBoolean(left != right, left.position)
|
|
"<<" -> shiftedleft(left, right)
|
|
">>" -> shiftedright(left, right)
|
|
else -> throw FatalAstException("const evaluation for invalid operator $operator")
|
|
}
|
|
}
|
|
|
|
private fun shiftedright(left: LiteralValue, amount: LiteralValue): IExpression {
|
|
if(left.asIntegerValue==null || amount.asIntegerValue==null)
|
|
throw ExpressionError("cannot compute $left >> $amount", left.position)
|
|
val result =
|
|
if(left.type== DataType.UBYTE || left.type== DataType.UWORD)
|
|
left.asIntegerValue.ushr(amount.asIntegerValue)
|
|
else
|
|
left.asIntegerValue.shr(amount.asIntegerValue)
|
|
return LiteralValue.fromNumber(result, left.type, left.position)
|
|
}
|
|
|
|
private fun shiftedleft(left: LiteralValue, amount: LiteralValue): IExpression {
|
|
if(left.asIntegerValue==null || amount.asIntegerValue==null)
|
|
throw ExpressionError("cannot compute $left << $amount", left.position)
|
|
val result = left.asIntegerValue.shl(amount.asIntegerValue)
|
|
return LiteralValue.fromNumber(result, left.type, left.position)
|
|
}
|
|
|
|
private fun logicalxor(left: LiteralValue, right: LiteralValue): LiteralValue {
|
|
val error = "cannot compute $left locical-bitxor $right"
|
|
return when {
|
|
left.asIntegerValue!=null -> when {
|
|
right.asIntegerValue!=null -> LiteralValue.fromBoolean((left.asIntegerValue != 0) xor (right.asIntegerValue != 0), left.position)
|
|
right.floatvalue!=null -> LiteralValue.fromBoolean((left.asIntegerValue != 0) xor (right.floatvalue != 0.0), left.position)
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
left.floatvalue!=null -> when {
|
|
right.asIntegerValue!=null -> LiteralValue.fromBoolean((left.floatvalue != 0.0) xor (right.asIntegerValue != 0), left.position)
|
|
right.floatvalue!=null -> LiteralValue.fromBoolean((left.floatvalue != 0.0) xor (right.floatvalue != 0.0), left.position)
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
}
|
|
|
|
private fun logicalor(left: LiteralValue, right: LiteralValue): LiteralValue {
|
|
val error = "cannot compute $left locical-or $right"
|
|
return when {
|
|
left.asIntegerValue!=null -> when {
|
|
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.asIntegerValue != 0 || right.asIntegerValue != 0, left.position)
|
|
right.floatvalue!=null -> LiteralValue.fromBoolean(left.asIntegerValue != 0 || right.floatvalue != 0.0, left.position)
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
left.floatvalue!=null -> when {
|
|
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.floatvalue != 0.0 || right.asIntegerValue != 0, left.position)
|
|
right.floatvalue!=null -> LiteralValue.fromBoolean(left.floatvalue != 0.0 || right.floatvalue != 0.0, left.position)
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
}
|
|
|
|
private fun logicaland(left: LiteralValue, right: LiteralValue): LiteralValue {
|
|
val error = "cannot compute $left locical-and $right"
|
|
return when {
|
|
left.asIntegerValue!=null -> when {
|
|
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.asIntegerValue != 0 && right.asIntegerValue != 0, left.position)
|
|
right.floatvalue!=null -> LiteralValue.fromBoolean(left.asIntegerValue != 0 && right.floatvalue != 0.0, left.position)
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
left.floatvalue!=null -> when {
|
|
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.floatvalue != 0.0 && right.asIntegerValue != 0, left.position)
|
|
right.floatvalue!=null -> LiteralValue.fromBoolean(left.floatvalue != 0.0 && right.floatvalue != 0.0, left.position)
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
}
|
|
|
|
private fun bitwisexor(left: LiteralValue, right: LiteralValue): LiteralValue {
|
|
if(left.type== DataType.UBYTE) {
|
|
if(right.asIntegerValue!=null) {
|
|
return LiteralValue(DataType.UBYTE, bytevalue = (left.bytevalue!!.toInt() xor (right.asIntegerValue and 255)).toShort(), position = left.position)
|
|
}
|
|
} else if(left.type== DataType.UWORD) {
|
|
if(right.asIntegerValue!=null) {
|
|
return LiteralValue(DataType.UWORD, wordvalue = left.wordvalue!! xor right.asIntegerValue, position = left.position)
|
|
}
|
|
}
|
|
throw ExpressionError("cannot calculate $left ^ $right", left.position)
|
|
}
|
|
|
|
private fun bitwiseor(left: LiteralValue, right: LiteralValue): LiteralValue {
|
|
if(left.type== DataType.UBYTE) {
|
|
if(right.asIntegerValue!=null) {
|
|
return LiteralValue(DataType.UBYTE, bytevalue = (left.bytevalue!!.toInt() or (right.asIntegerValue and 255)).toShort(), position = left.position)
|
|
}
|
|
} else if(left.type== DataType.UWORD) {
|
|
if(right.asIntegerValue!=null) {
|
|
return LiteralValue(DataType.UWORD, wordvalue = left.wordvalue!! or right.asIntegerValue, position = left.position)
|
|
}
|
|
}
|
|
throw ExpressionError("cannot calculate $left | $right", left.position)
|
|
}
|
|
|
|
private fun bitwiseand(left: LiteralValue, right: LiteralValue): LiteralValue {
|
|
if(left.type== DataType.UBYTE) {
|
|
if(right.asIntegerValue!=null) {
|
|
return LiteralValue(DataType.UBYTE, bytevalue = (left.bytevalue!!.toInt() or (right.asIntegerValue and 255)).toShort(), position = left.position)
|
|
}
|
|
} else if(left.type== DataType.UWORD) {
|
|
if(right.asIntegerValue!=null) {
|
|
return LiteralValue(DataType.UWORD, wordvalue = left.wordvalue!! or right.asIntegerValue, position = left.position)
|
|
}
|
|
}
|
|
throw ExpressionError("cannot calculate $left & $right", left.position)
|
|
}
|
|
|
|
private fun power(left: LiteralValue, right: LiteralValue): LiteralValue {
|
|
val error = "cannot calculate $left ** $right"
|
|
return when {
|
|
left.asIntegerValue!=null -> when {
|
|
right.asIntegerValue!=null -> LiteralValue.optimalNumeric(left.asIntegerValue.toDouble().pow(right.asIntegerValue), left.position)
|
|
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue.toDouble().pow(right.floatvalue), position = left.position)
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
left.floatvalue!=null -> when {
|
|
right.asIntegerValue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue.pow(right.asIntegerValue), position = left.position)
|
|
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue.pow(right.floatvalue), position = left.position)
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
}
|
|
|
|
private fun plus(left: LiteralValue, right: LiteralValue): LiteralValue {
|
|
val error = "cannot add $left and $right"
|
|
return when {
|
|
left.asIntegerValue!=null -> when {
|
|
right.asIntegerValue!=null -> LiteralValue.optimalNumeric(left.asIntegerValue + right.asIntegerValue, left.position)
|
|
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue + right.floatvalue, position = left.position)
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
left.floatvalue!=null -> when {
|
|
right.asIntegerValue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue + right.asIntegerValue, position = left.position)
|
|
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue + right.floatvalue, position = left.position)
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
left.isString -> when {
|
|
right.isString -> {
|
|
val newStr = left.strvalue!! + right.strvalue!!
|
|
if(newStr.length > 255) throw ExpressionError("string too long", left.position)
|
|
LiteralValue(DataType.STR, strvalue = newStr, position = left.position)
|
|
}
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
}
|
|
|
|
private fun minus(left: LiteralValue, right: LiteralValue): LiteralValue {
|
|
val error = "cannot subtract $left and $right"
|
|
return when {
|
|
left.asIntegerValue!=null -> when {
|
|
right.asIntegerValue!=null -> LiteralValue.optimalNumeric(left.asIntegerValue - right.asIntegerValue, left.position)
|
|
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue - right.floatvalue, position = left.position)
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
left.floatvalue!=null -> when {
|
|
right.asIntegerValue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue - right.asIntegerValue, position = left.position)
|
|
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue - right.floatvalue, position = left.position)
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
}
|
|
|
|
private fun multiply(left: LiteralValue, right: LiteralValue): LiteralValue {
|
|
val error = "cannot multiply ${left.type} and ${right.type}"
|
|
return when {
|
|
left.asIntegerValue!=null -> when {
|
|
right.asIntegerValue!=null -> LiteralValue.optimalNumeric(left.asIntegerValue * right.asIntegerValue, left.position)
|
|
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue * right.floatvalue, position = left.position)
|
|
right.isString -> {
|
|
if(right.strvalue!!.length * left.asIntegerValue > 255) throw ExpressionError("string too long", left.position)
|
|
LiteralValue(DataType.STR, strvalue = right.strvalue.repeat(left.asIntegerValue), position = left.position)
|
|
}
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
left.floatvalue!=null -> when {
|
|
right.asIntegerValue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue * right.asIntegerValue, position = left.position)
|
|
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue * right.floatvalue, position = left.position)
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
}
|
|
|
|
private fun divideByZeroError(pos: Position): Unit =
|
|
throw ExpressionError("division by zero", pos)
|
|
|
|
private fun divide(left: LiteralValue, right: LiteralValue): LiteralValue {
|
|
val error = "cannot divide $left by $right"
|
|
return when {
|
|
left.asIntegerValue!=null -> when {
|
|
right.asIntegerValue!=null -> {
|
|
if(right.asIntegerValue==0) divideByZeroError(right.position)
|
|
val result: Int = left.asIntegerValue / right.asIntegerValue
|
|
LiteralValue.optimalNumeric(result, left.position)
|
|
}
|
|
right.floatvalue!=null -> {
|
|
if(right.floatvalue==0.0) divideByZeroError(right.position)
|
|
LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue / right.floatvalue, position = left.position)
|
|
}
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
left.floatvalue!=null -> when {
|
|
right.asIntegerValue!=null -> {
|
|
if(right.asIntegerValue==0) divideByZeroError(right.position)
|
|
LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue / right.asIntegerValue, position = left.position)
|
|
}
|
|
right.floatvalue!=null -> {
|
|
if(right.floatvalue==0.0) divideByZeroError(right.position)
|
|
LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue / right.floatvalue, position = left.position)
|
|
}
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
}
|
|
|
|
private fun remainder(left: LiteralValue, right: LiteralValue): LiteralValue {
|
|
val error = "cannot compute remainder of $left by $right"
|
|
return when {
|
|
left.asIntegerValue!=null -> when {
|
|
right.asIntegerValue!=null -> {
|
|
if(right.asIntegerValue==0) divideByZeroError(right.position)
|
|
LiteralValue.optimalNumeric(left.asIntegerValue.toDouble() % right.asIntegerValue.toDouble(), left.position)
|
|
}
|
|
right.floatvalue!=null -> {
|
|
if(right.floatvalue==0.0) divideByZeroError(right.position)
|
|
LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue % right.floatvalue, position = left.position)
|
|
}
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
left.floatvalue!=null -> when {
|
|
right.asIntegerValue!=null -> {
|
|
if(right.asIntegerValue==0) divideByZeroError(right.position)
|
|
LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue % right.asIntegerValue, position = left.position)
|
|
}
|
|
right.floatvalue!=null -> {
|
|
if(right.floatvalue==0.0) divideByZeroError(right.position)
|
|
LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue % right.floatvalue, position = left.position)
|
|
}
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
else -> throw ExpressionError(error, left.position)
|
|
}
|
|
}
|
|
}
|