mirror of
https://github.com/irmen/prog8.git
synced 2024-12-25 08:29:25 +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...")
|
_vm_gfx_text(5, 5, 7, "Calculating Mandelbrot Fractal...")
|
||||||
|
|
||||||
for pixely in yoffset to yoffset+height-1 {
|
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 {
|
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
|
x = 0.0
|
||||||
y = 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 leftDt = left.resultingDatatype(namespace)
|
||||||
val rightDt = right.resultingDatatype(namespace)
|
val rightDt = right.resultingDatatype(namespace)
|
||||||
return when(operator) {
|
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)
|
"//" -> if(leftDt==null || rightDt==null) null else integerDivisionOpDt(leftDt, rightDt)
|
||||||
"&" -> leftDt
|
"&" -> leftDt
|
||||||
"|" -> leftDt
|
"|" -> leftDt
|
||||||
@ -738,6 +738,28 @@ class BinaryExpression(var left: IExpression, val operator: String, var right: I
|
|||||||
"<", ">",
|
"<", ">",
|
||||||
"<=", ">=",
|
"<=", ">=",
|
||||||
"==", "!=" -> DataType.BYTE
|
"==", "!=" -> 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")
|
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) {
|
private data class NumericLiteral(val number: Number, val datatype: DataType)
|
||||||
fun asWord() = ByteOrWordLiteral(intvalue, DataType.WORD)
|
|
||||||
fun asByte() = ByteOrWordLiteral(intvalue, DataType.BYTE)
|
|
||||||
}
|
|
||||||
|
|
||||||
class LiteralValue(val type: DataType,
|
class LiteralValue(val type: DataType,
|
||||||
val bytevalue: Short? = null,
|
val bytevalue: Short? = null,
|
||||||
@ -835,7 +855,8 @@ class LiteralValue(val type: DataType,
|
|||||||
return when (value) {
|
return when (value) {
|
||||||
// note: we cheat a little here and allow negative integers during expression evaluations
|
// note: we cheat a little here and allow negative integers during expression evaluations
|
||||||
in -128..255 -> LiteralValue(DataType.BYTE, bytevalue = value.toShort(), position = position)
|
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 =
|
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> =
|
private fun prog8Parser.Statement_blockContext.toAst(): MutableList<IStatement> =
|
||||||
@ -1552,7 +1573,7 @@ private fun prog8Parser.ReturnstmtContext.toAst() : IStatement {
|
|||||||
|
|
||||||
private fun prog8Parser.UnconditionaljumpContext.toAst(): IStatement {
|
private fun prog8Parser.UnconditionaljumpContext.toAst(): IStatement {
|
||||||
|
|
||||||
val address = integerliteral()?.toAst()?.intvalue
|
val address = integerliteral()?.toAst()?.number?.toInt()
|
||||||
val identifier =
|
val identifier =
|
||||||
if(identifier()!=null) identifier()?.toAst()
|
if(identifier()!=null) identifier()?.toAst()
|
||||||
else scoped_identifier()?.toAst()
|
else scoped_identifier()?.toAst()
|
||||||
@ -1569,7 +1590,7 @@ private fun prog8Parser.SubroutineContext.toAst() : Subroutine {
|
|||||||
return Subroutine(identifier().text,
|
return Subroutine(identifier().text,
|
||||||
if(sub_params() ==null) emptyList() else sub_params().toAst(),
|
if(sub_params() ==null) emptyList() else sub_params().toAst(),
|
||||||
if(sub_returns() ==null) emptyList() else sub_returns().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(),
|
if(statement_block() ==null) mutableListOf() else statement_block().toAst(),
|
||||||
toPosition())
|
toPosition())
|
||||||
}
|
}
|
||||||
@ -1614,17 +1635,20 @@ private fun prog8Parser.DirectiveContext.toAst() : Directive =
|
|||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.DirectiveargContext.toAst() : DirectiveArg =
|
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 {
|
private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
||||||
fun makeLiteral(text: String, radix: Int, forceWord: Boolean): ByteOrWordLiteral {
|
fun makeLiteral(text: String, radix: Int, forceWord: Boolean): NumericLiteral {
|
||||||
val integer: Int
|
val integer: Int
|
||||||
var datatype = DataType.BYTE
|
var datatype = DataType.BYTE
|
||||||
if(radix==10) {
|
if(radix==10) {
|
||||||
integer = text.toInt()
|
integer = text.toInt()
|
||||||
if(integer in 256..65535)
|
datatype = when(integer) {
|
||||||
datatype = DataType.WORD
|
in 0..255 -> DataType.BYTE
|
||||||
|
in 256..65535 -> DataType.WORD
|
||||||
|
else -> DataType.FLOAT
|
||||||
|
}
|
||||||
} else if(radix==2) {
|
} else if(radix==2) {
|
||||||
if(text.length>8)
|
if(text.length>8)
|
||||||
datatype = DataType.WORD
|
datatype = DataType.WORD
|
||||||
@ -1636,7 +1660,7 @@ private fun prog8Parser.IntegerliteralContext.toAst(): ByteOrWordLiteral {
|
|||||||
} else {
|
} else {
|
||||||
throw FatalAstException("invalid radix")
|
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 terminal: TerminalNode = children[0] as TerminalNode
|
||||||
val integerPart = this.intpart.text
|
val integerPart = this.intpart.text
|
||||||
@ -1661,9 +1685,10 @@ private fun prog8Parser.ExpressionContext.toAst() : IExpression {
|
|||||||
val intLit = litval.integerliteral()?.toAst()
|
val intLit = litval.integerliteral()?.toAst()
|
||||||
when {
|
when {
|
||||||
intLit!=null -> when(intLit.datatype) {
|
intLit!=null -> when(intLit.datatype) {
|
||||||
DataType.BYTE -> LiteralValue(DataType.BYTE, bytevalue = intLit.intvalue.toShort(), position = litval.toPosition())
|
DataType.BYTE -> LiteralValue(DataType.BYTE, bytevalue = intLit.number.toShort(), position = litval.toPosition())
|
||||||
DataType.WORD -> LiteralValue(DataType.WORD, wordvalue = intLit.intvalue, position = litval.toPosition())
|
DataType.WORD -> LiteralValue(DataType.WORD, wordvalue = intLit.number.toInt(), position = litval.toPosition())
|
||||||
else -> throw FatalAstException("invalid datatype for integer literal")
|
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.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())
|
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 targetDatatype = assignment.target.determineDatatype(namespace, assignment)
|
||||||
val constVal = assignment.value.constValue(namespace)
|
val constVal = assignment.value.constValue(namespace)
|
||||||
if(constVal!=null) {
|
if(constVal!=null) {
|
||||||
checkValueTypeAndRange(targetDatatype, null, assignment.value as LiteralValue)
|
checkValueTypeAndRange(targetDatatype, null, constVal)
|
||||||
// todo: fix crash here when assignment value is a functioncall sounch as round()
|
|
||||||
} else {
|
} else {
|
||||||
val sourceDatatype: DataType? = assignment.value.resultingDatatype(namespace)
|
val sourceDatatype: DataType? = assignment.value.resultingDatatype(namespace)
|
||||||
if(sourceDatatype==null) {
|
if(sourceDatatype==null) {
|
||||||
@ -587,17 +586,17 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
|||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
val number = value.asIntegerValue ?: return if (value.floatvalue!=null)
|
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
|
else
|
||||||
err("unsigned byte integer value expected")
|
err("unsigned byte value expected")
|
||||||
if (number < 0 || number > 255)
|
if (number < 0 || number > 255)
|
||||||
return err("value '$number' out of range for unsigned byte")
|
return err("value '$number' out of range for unsigned byte")
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
val number = value.asIntegerValue ?: return if (value.floatvalue!=null)
|
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
|
else
|
||||||
err("unsigned word integer value expected")
|
err("unsigned word value expected")
|
||||||
if (number < 0 || number > 65535)
|
if (number < 0 || number > 65535)
|
||||||
return err("value '$number' out of range for unsigned word")
|
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")
|
return err("initialization value must be an array of bytes")
|
||||||
} else {
|
} else {
|
||||||
val number = value.bytevalue ?: return if (value.floatvalue!=null)
|
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
|
else
|
||||||
err("unsigned byte integer value expected")
|
err("unsigned byte value expected")
|
||||||
if (number < 0 || number > 255)
|
if (number < 0 || number > 255)
|
||||||
return err("value '$number' out of range for unsigned byte")
|
return err("value '$number' out of range for unsigned byte")
|
||||||
}
|
}
|
||||||
@ -671,9 +670,9 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val number = value.asIntegerValue ?: return if (value.floatvalue!=null)
|
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
|
else
|
||||||
err("unsigned byte or word integer value expected")
|
err("unsigned byte or word value expected")
|
||||||
if (number < 0 || number > 65535)
|
if (number < 0 || number > 65535)
|
||||||
return err("value '$number' out of range for unsigned word")
|
return err("value '$number' out of range for unsigned word")
|
||||||
}
|
}
|
||||||
@ -698,7 +697,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val number = value.bytevalue
|
val number = value.bytevalue
|
||||||
?: return err("unsigned byte integer value expected")
|
?: return err("unsigned byte value expected")
|
||||||
if (number < 0 || number > 255)
|
if (number < 0 || number > 255)
|
||||||
return err("value '$number' out of range for unsigned byte")
|
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)
|
if(sourceDatatype==DataType.WORD && targetDatatype==DataType.BYTE)
|
||||||
checkResult.add(ExpressionError("cannot assign word to byte, use msb() or lsb()?", position))
|
checkResult.add(ExpressionError("cannot assign word to byte, use msb() or lsb()?", position))
|
||||||
else if(sourceDatatype==DataType.FLOAT && (targetDatatype==DataType.BYTE || targetDatatype==DataType.WORD))
|
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
|
else
|
||||||
checkResult.add(ExpressionError("cannot assign ${sourceDatatype.toString().toLowerCase()} to ${targetDatatype.toString().toLowerCase()}", position))
|
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)
|
val rightDt = right.resultingDatatype(namespace)
|
||||||
if (leftDt == DataType.BYTE || leftDt == DataType.WORD) {
|
if (leftDt == DataType.BYTE || leftDt == DataType.WORD) {
|
||||||
if(rightDt==DataType.FLOAT)
|
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 {
|
private fun divide(left: LiteralValue, right: LiteralValue): LiteralValue {
|
||||||
|
Loading…
Reference in New Issue
Block a user