mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 16:29:21 +00:00
fix handling of (too) large integer literals, and range check crash
This commit is contained in:
parent
0f53f87895
commit
6681787288
@ -24,10 +24,10 @@
|
||||
_vm_gfx_text(5, 5, 7, "Calculating Mandelbrot Fractal...")
|
||||
|
||||
for pixely in yoffset to yoffset+height-1 {
|
||||
yy = flt((pixely-yoffset))/height/3.6+0.4
|
||||
yy = flt((pixely-yoffset))/height/3.6+0.4 ; @todo why is /height/3.6 not const-folded???
|
||||
|
||||
for pixelx in xoffset to xoffset+width-1 {
|
||||
xx = flt((pixelx-xoffset))/width/3+0.2
|
||||
xx = flt((pixelx-xoffset))/width/3+0.2 ; @todo why is /width/3 not const-folded???
|
||||
|
||||
x = 0.0
|
||||
y = 0.0
|
||||
|
@ -729,7 +729,7 @@ class BinaryExpression(var left: IExpression, val operator: String, var right: I
|
||||
val leftDt = left.resultingDatatype(namespace)
|
||||
val rightDt = right.resultingDatatype(namespace)
|
||||
return when(operator) {
|
||||
"+", "-", "*", "**", "/", "%" -> if(leftDt==null || rightDt==null) null else arithmeticOpDt(leftDt, rightDt)
|
||||
"+", "-", "*", "**", "%" -> if(leftDt==null || rightDt==null) null else arithmeticOpDt(leftDt, rightDt)
|
||||
"//" -> if(leftDt==null || rightDt==null) null else integerDivisionOpDt(leftDt, rightDt)
|
||||
"&" -> leftDt
|
||||
"|" -> leftDt
|
||||
@ -738,6 +738,28 @@ class BinaryExpression(var left: IExpression, val operator: String, var right: I
|
||||
"<", ">",
|
||||
"<=", ">=",
|
||||
"==", "!=" -> DataType.BYTE
|
||||
"/" -> {
|
||||
val rightNum = right.constValue(namespace)?.asNumericValue?.toDouble()
|
||||
if(rightNum!=null) {
|
||||
when(leftDt) {
|
||||
DataType.BYTE ->
|
||||
when(rightDt) {
|
||||
DataType.BYTE -> DataType.BYTE
|
||||
DataType.WORD -> if(rightNum <= -256 || rightNum >= 256) DataType.BYTE else DataType.WORD
|
||||
DataType.FLOAT -> if(rightNum <= -256 || rightNum >= 256) DataType.BYTE else DataType.FLOAT
|
||||
else -> throw FatalAstException("invalid rightDt $rightDt")
|
||||
}
|
||||
DataType.WORD ->
|
||||
when(rightDt) {
|
||||
DataType.BYTE, DataType.WORD -> DataType.WORD
|
||||
DataType.FLOAT -> if(rightNum <= -65536 || rightNum >= 65536) DataType.WORD else DataType.FLOAT
|
||||
else -> throw FatalAstException("invalid rightDt $rightDt")
|
||||
}
|
||||
DataType.FLOAT -> DataType.FLOAT
|
||||
else -> throw FatalAstException("invalid leftDt $leftDt")
|
||||
}
|
||||
} else if(leftDt==null || rightDt==null) null else arithmeticOpDt(leftDt, rightDt)
|
||||
}
|
||||
else -> throw FatalAstException("resulting datatype check for invalid operator $operator")
|
||||
}
|
||||
}
|
||||
@ -784,10 +806,8 @@ class BinaryExpression(var left: IExpression, val operator: String, var right: I
|
||||
}
|
||||
}
|
||||
|
||||
private data class ByteOrWordLiteral(val intvalue: Int, val datatype: DataType) {
|
||||
fun asWord() = ByteOrWordLiteral(intvalue, DataType.WORD)
|
||||
fun asByte() = ByteOrWordLiteral(intvalue, DataType.BYTE)
|
||||
}
|
||||
private data class NumericLiteral(val number: Number, val datatype: DataType)
|
||||
|
||||
|
||||
class LiteralValue(val type: DataType,
|
||||
val bytevalue: Short? = null,
|
||||
@ -835,7 +855,8 @@ class LiteralValue(val type: DataType,
|
||||
return when (value) {
|
||||
// note: we cheat a little here and allow negative integers during expression evaluations
|
||||
in -128..255 -> LiteralValue(DataType.BYTE, bytevalue = value.toShort(), position = position)
|
||||
else -> LiteralValue(DataType.WORD, wordvalue = value.toInt(), position = position)
|
||||
in -32768..65535 -> LiteralValue(DataType.WORD, wordvalue = value.toInt(), position = position)
|
||||
else -> throw FatalAstException("integer overflow: $value")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1412,7 +1433,7 @@ private fun prog8Parser.ModulestatementContext.toAst() : IStatement {
|
||||
|
||||
|
||||
private fun prog8Parser.BlockContext.toAst() : IStatement =
|
||||
Block(identifier().text, integerliteral()?.toAst()?.intvalue, statement_block().toAst(), toPosition())
|
||||
Block(identifier().text, integerliteral()?.toAst()?.number?.toInt(), statement_block().toAst(), toPosition())
|
||||
|
||||
|
||||
private fun prog8Parser.Statement_blockContext.toAst(): MutableList<IStatement> =
|
||||
@ -1552,7 +1573,7 @@ private fun prog8Parser.ReturnstmtContext.toAst() : IStatement {
|
||||
|
||||
private fun prog8Parser.UnconditionaljumpContext.toAst(): IStatement {
|
||||
|
||||
val address = integerliteral()?.toAst()?.intvalue
|
||||
val address = integerliteral()?.toAst()?.number?.toInt()
|
||||
val identifier =
|
||||
if(identifier()!=null) identifier()?.toAst()
|
||||
else scoped_identifier()?.toAst()
|
||||
@ -1569,7 +1590,7 @@ private fun prog8Parser.SubroutineContext.toAst() : Subroutine {
|
||||
return Subroutine(identifier().text,
|
||||
if(sub_params() ==null) emptyList() else sub_params().toAst(),
|
||||
if(sub_returns() ==null) emptyList() else sub_returns().toAst(),
|
||||
sub_address()?.integerliteral()?.toAst()?.intvalue,
|
||||
sub_address()?.integerliteral()?.toAst()?.number?.toInt(),
|
||||
if(statement_block() ==null) mutableListOf() else statement_block().toAst(),
|
||||
toPosition())
|
||||
}
|
||||
@ -1614,17 +1635,20 @@ private fun prog8Parser.DirectiveContext.toAst() : Directive =
|
||||
|
||||
|
||||
private fun prog8Parser.DirectiveargContext.toAst() : DirectiveArg =
|
||||
DirectiveArg(stringliteral()?.text, identifier()?.text, integerliteral()?.toAst()?.intvalue, toPosition())
|
||||
DirectiveArg(stringliteral()?.text, identifier()?.text, integerliteral()?.toAst()?.number?.toInt(), toPosition())
|
||||
|
||||
|
||||
private fun prog8Parser.IntegerliteralContext.toAst(): ByteOrWordLiteral {
|
||||
fun makeLiteral(text: String, radix: Int, forceWord: Boolean): ByteOrWordLiteral {
|
||||
private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
||||
fun makeLiteral(text: String, radix: Int, forceWord: Boolean): NumericLiteral {
|
||||
val integer: Int
|
||||
var datatype = DataType.BYTE
|
||||
if(radix==10) {
|
||||
integer = text.toInt()
|
||||
if(integer in 256..65535)
|
||||
datatype = DataType.WORD
|
||||
datatype = when(integer) {
|
||||
in 0..255 -> DataType.BYTE
|
||||
in 256..65535 -> DataType.WORD
|
||||
else -> DataType.FLOAT
|
||||
}
|
||||
} else if(radix==2) {
|
||||
if(text.length>8)
|
||||
datatype = DataType.WORD
|
||||
@ -1636,7 +1660,7 @@ private fun prog8Parser.IntegerliteralContext.toAst(): ByteOrWordLiteral {
|
||||
} else {
|
||||
throw FatalAstException("invalid radix")
|
||||
}
|
||||
return ByteOrWordLiteral(integer, if(forceWord) DataType.WORD else datatype)
|
||||
return NumericLiteral(integer, if(forceWord) DataType.WORD else datatype)
|
||||
}
|
||||
val terminal: TerminalNode = children[0] as TerminalNode
|
||||
val integerPart = this.intpart.text
|
||||
@ -1661,9 +1685,10 @@ private fun prog8Parser.ExpressionContext.toAst() : IExpression {
|
||||
val intLit = litval.integerliteral()?.toAst()
|
||||
when {
|
||||
intLit!=null -> when(intLit.datatype) {
|
||||
DataType.BYTE -> LiteralValue(DataType.BYTE, bytevalue = intLit.intvalue.toShort(), position = litval.toPosition())
|
||||
DataType.WORD -> LiteralValue(DataType.WORD, wordvalue = intLit.intvalue, position = litval.toPosition())
|
||||
else -> throw FatalAstException("invalid datatype for integer literal")
|
||||
DataType.BYTE -> LiteralValue(DataType.BYTE, bytevalue = intLit.number.toShort(), position = litval.toPosition())
|
||||
DataType.WORD -> LiteralValue(DataType.WORD, wordvalue = intLit.number.toInt(), position = litval.toPosition())
|
||||
DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue= intLit.number.toDouble(), position = litval.toPosition())
|
||||
else -> throw FatalAstException("invalid datatype for numeric literal")
|
||||
}
|
||||
litval.floatliteral()!=null -> LiteralValue(DataType.FLOAT, floatvalue = litval.floatliteral().toAst(), position = litval.toPosition())
|
||||
litval.stringliteral()!=null -> LiteralValue(DataType.STR, strvalue = litval.stringliteral().text, position = litval.toPosition())
|
||||
|
@ -267,8 +267,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
||||
val targetDatatype = assignment.target.determineDatatype(namespace, assignment)
|
||||
val constVal = assignment.value.constValue(namespace)
|
||||
if(constVal!=null) {
|
||||
checkValueTypeAndRange(targetDatatype, null, assignment.value as LiteralValue)
|
||||
// todo: fix crash here when assignment value is a functioncall sounch as round()
|
||||
checkValueTypeAndRange(targetDatatype, null, constVal)
|
||||
} else {
|
||||
val sourceDatatype: DataType? = assignment.value.resultingDatatype(namespace)
|
||||
if(sourceDatatype==null) {
|
||||
@ -587,17 +586,17 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
val number = value.asIntegerValue ?: return if (value.floatvalue!=null)
|
||||
err("unsigned byte integer value expected instead of float; possible loss of precision")
|
||||
err("unsigned byte value expected instead of float; possible loss of precision")
|
||||
else
|
||||
err("unsigned byte integer value expected")
|
||||
err("unsigned byte value expected")
|
||||
if (number < 0 || number > 255)
|
||||
return err("value '$number' out of range for unsigned byte")
|
||||
}
|
||||
DataType.WORD -> {
|
||||
val number = value.asIntegerValue ?: return if (value.floatvalue!=null)
|
||||
err("unsigned word integer value expected instead of float; possible loss of precision")
|
||||
err("unsigned word value expected instead of float; possible loss of precision")
|
||||
else
|
||||
err("unsigned word integer value expected")
|
||||
err("unsigned word value expected")
|
||||
if (number < 0 || number > 65535)
|
||||
return err("value '$number' out of range for unsigned word")
|
||||
}
|
||||
@ -638,9 +637,9 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
||||
return err("initialization value must be an array of bytes")
|
||||
} else {
|
||||
val number = value.bytevalue ?: return if (value.floatvalue!=null)
|
||||
err("unsigned byte integer value expected instead of float; possible loss of precision")
|
||||
err("unsigned byte value expected instead of float; possible loss of precision")
|
||||
else
|
||||
err("unsigned byte integer value expected")
|
||||
err("unsigned byte value expected")
|
||||
if (number < 0 || number > 255)
|
||||
return err("value '$number' out of range for unsigned byte")
|
||||
}
|
||||
@ -671,9 +670,9 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
||||
}
|
||||
} else {
|
||||
val number = value.asIntegerValue ?: return if (value.floatvalue!=null)
|
||||
err("unsigned byte or word integer value expected instead of float; possible loss of precision")
|
||||
err("unsigned byte or word value expected instead of float; possible loss of precision")
|
||||
else
|
||||
err("unsigned byte or word integer value expected")
|
||||
err("unsigned byte or word value expected")
|
||||
if (number < 0 || number > 65535)
|
||||
return err("value '$number' out of range for unsigned word")
|
||||
}
|
||||
@ -698,7 +697,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
||||
}
|
||||
} else {
|
||||
val number = value.bytevalue
|
||||
?: return err("unsigned byte integer value expected")
|
||||
?: return err("unsigned byte value expected")
|
||||
if (number < 0 || number > 255)
|
||||
return err("value '$number' out of range for unsigned byte")
|
||||
}
|
||||
@ -734,7 +733,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
||||
if(sourceDatatype==DataType.WORD && targetDatatype==DataType.BYTE)
|
||||
checkResult.add(ExpressionError("cannot assign word to byte, use msb() or lsb()?", position))
|
||||
else if(sourceDatatype==DataType.FLOAT && (targetDatatype==DataType.BYTE || targetDatatype==DataType.WORD))
|
||||
checkResult.add(ExpressionError("cannot assign float to ${targetDatatype.toString().toLowerCase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position))
|
||||
checkResult.add(ExpressionError("cannot assign float to ${targetDatatype.toString().toLowerCase()}; possible loss of precision. Suggestion: round the value or revert to byte/word arithmetic", position))
|
||||
else
|
||||
checkResult.add(ExpressionError("cannot assign ${sourceDatatype.toString().toLowerCase()} to ${targetDatatype.toString().toLowerCase()}", position))
|
||||
|
||||
|
@ -294,7 +294,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
||||
val rightDt = right.resultingDatatype(namespace)
|
||||
if (leftDt == DataType.BYTE || leftDt == DataType.WORD) {
|
||||
if(rightDt==DataType.FLOAT)
|
||||
printWarning("byte or word value implicitly converted to float. Suggestion: use explicit flt() conversion or revert to integer arithmetic", left.position)
|
||||
printWarning("byte or word value implicitly converted to float. Suggestion: use explicit flt() conversion or revert to byte/word arithmetic", left.position)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,7 +208,8 @@ class ConstExprEvaluator {
|
||||
}
|
||||
|
||||
|
||||
private fun divideByZeroError(pos: Position): Unit = throw ExpressionError("division by zero", pos)
|
||||
private fun divideByZeroError(pos: Position): Unit =
|
||||
throw ExpressionError("division by zero", pos)
|
||||
|
||||
|
||||
private fun divide(left: LiteralValue, right: LiteralValue): LiteralValue {
|
||||
|
Loading…
Reference in New Issue
Block a user