fix handling of (too) large integer literals, and range check crash

This commit is contained in:
Irmen de Jong 2018-09-26 21:21:33 +02:00
parent 0f53f87895
commit 6681787288
5 changed files with 59 additions and 34 deletions

View File

@ -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

View File

@ -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())

View File

@ -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))

View File

@ -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)
} }
} }

View File

@ -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 {