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...")
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

View File

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

View File

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

View File

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

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 {