improve datatype assignment checks

This commit is contained in:
Irmen de Jong 2018-11-22 00:01:41 +01:00
parent c40fc584d6
commit 48a6a05109
9 changed files with 139 additions and 62 deletions

View File

@ -10,19 +10,23 @@
ubyte secretnumber = 0 ubyte secretnumber = 0
ubyte attempts_left = 10 ubyte attempts_left = 10
memory uword freadstr_arg = $22 ; argument for FREADSTR memory uword freadstr_arg = $22 ; argument for FREADSTR
uword testword
testword = guess ; @todo fix str addrss
testword = "sadfsafsdf" ; @todo fix str address
secretnumber = "\n" ; @todo fix argument conversion to UBYTE
c64utils.init_system() c64utils.init_system()
c64.VMCSB |= 2 ; activate lowercase charset c64.VMCSB |= 2 ; activate lowercase charset
; greeting ; greeting
c64scr.print_string("Enter your name: ") c64scr.print_string("Enter your name: ")
Y = c64scr.input_chars(name) ; @todo fix argument type check Y = c64scr.input_chars(name)
c64.CHROUT("\n") ; @todo fix argument type check c64.CHROUT("\n") ; @todo fix argument conversion to UBYTE
c64.CHROUT("\n") c64.CHROUT("\n") ; @todo fix argument conversion to UBYTE
c64scr.print_string("Hello, ") c64scr.print_string("Hello, ")
c64scr.print_string(name) c64scr.print_string(name)
c64.CHROUT(".") c64.CHROUT(".") ; @todo fix argument conversion to UBYTE
c64.CHROUT("\n") c64.CHROUT("\n") ; @todo fix argument conversion to UBYTE
; create a secret random number from 1-100 ; create a secret random number from 1-100
c64.RNDA(0) ; fac = rnd(0) c64.RNDA(0) ; fac = rnd(0)
@ -30,7 +34,7 @@
c64.MUL10() ; .. and now *100 c64.MUL10() ; .. and now *100
c64.FADDH() ; add 0.5.. c64.FADDH() ; add 0.5..
c64.FADDH() ; and again, so +1 total c64.FADDH() ; and again, so +1 total
A, Y = c64flt.GETADRAY() A, Y = c64flt.GETADRAY() ; @todo fix return value type check
secretnumber = A secretnumber = A
;A=math.randbyte() ;A=math.randbyte()
;A+=c64.RASTER ;A+=c64.RASTER
@ -47,10 +51,10 @@ ask_guess:
c64scr.print_string(" left.\nWhat is your next guess? ") c64scr.print_string(" left.\nWhat is your next guess? ")
Y = c64scr.input_chars(guess) Y = c64scr.input_chars(guess)
c64.CHROUT("\n") c64.CHROUT("\n") ; @todo fix argument conversion to UBYTE
freadstr_arg = guess freadstr_arg = guess ; @todo put string's adress in uword variable
c64.FREADSTR(A) c64.FREADSTR(A)
A, Y = c64flt.GETADRAY() A, Y = c64flt.GETADRAY() ; @todo fix return value type check
if(A==secretnumber) { if(A==secretnumber) {
c64scr.print_string("\nThat's my number, impressive!\n") c64scr.print_string("\nThat's my number, impressive!\n")
goto goodbye goto goodbye
@ -68,7 +72,7 @@ ask_guess:
; game over. ; game over.
c64scr.print_string("\nToo bad! It was: ") c64scr.print_string("\nToo bad! It was: ")
c64scr.print_byte_decimal(secretnumber) c64scr.print_byte_decimal(secretnumber)
c64.CHROUT("\n") c64.CHROUT("\n") ; @todo fix argument conversion to UBYTE
goodbye: goodbye:
c64scr.print_string("\nThanks for playing. Bye!\n") c64scr.print_string("\nThanks for playing. Bye!\n")

View File

@ -16,6 +16,27 @@ sub start() {
byte b1 byte b1
str stringvar = "??????????"
ubyte secretnumber = 0
memory uword freadstr_arg = $22 ; argument for FREADSTR
uword testword
ubyte char1 = "@"
ubyte char2 = "\n" ; @todo escapechar
ubyte char3 = "\t" ; @todo escapechar
;testword = stringvar ; @todo fix str address
;testword = "sadfsafsdf" ; @todo fix str address
testword = "@" ; @todo fix argument conversion to UBYTE
testword = "\n" ; @todo fix argument conversion to UBYTE (escapechar)
;freadstr_arg = stringvar
;freadstr_arg = "asdfasdfasdfasdf"
freadstr_arg = "@" ; @todo fix argument conversion to UBYTE
freadstr_arg = "\n" ; @todo fix argument conversion to UBYTE (escapechar)
secretnumber = "@" ; @todo fix argument conversion to UBYTE
secretnumber = "\n" ; @todo fix argument conversion to UBYTE (escapechar)
;secretnumber = "asdfsdf"
address =c64.MEMBOT(1, 40000.w) ; ok! address =c64.MEMBOT(1, 40000.w) ; ok!
address =c64.MEMBOT(1, address) ; ok! address =c64.MEMBOT(1, address) ; ok!
address =c64.MEMBOT(1, memaddr) ; ok! address =c64.MEMBOT(1, memaddr) ; ok!

View File

@ -30,7 +30,25 @@ enum class DataType {
ARRAY_B, ARRAY_B,
ARRAY_UW, ARRAY_UW,
ARRAY_W, ARRAY_W,
ARRAY_F ARRAY_F;
fun assignableTo(type: DataType) =
when(this) {
UBYTE -> type in NumericDatatypes
BYTE -> type in NumericDatatypes
UWORD -> type in NumericDatatypes
WORD -> type in NumericDatatypes
FLOAT -> type in NumericDatatypes
STR -> type == STR || type==STR_S || type == UWORD
STR_P -> type == STR_P || type==STR_PS || type == UWORD
STR_S -> type == STR || type==STR_S || type == UWORD
STR_PS -> type == STR_P || type==STR_PS || type == UWORD
ARRAY_UB -> type == UWORD
ARRAY_B -> type == UWORD
ARRAY_UW -> type == UWORD
ARRAY_W -> type == UWORD
ARRAY_F -> type == UWORD
}
} }
enum class Register { enum class Register {
@ -948,11 +966,12 @@ class LiteralValue(val type: DataType,
val bytevalue: Short? = null, val bytevalue: Short? = null,
val wordvalue: Int? = null, val wordvalue: Int? = null,
val floatvalue: Double? = null, val floatvalue: Double? = null,
val strvalue: String? = null, strvalue: String? = null,
val arrayvalue: Array<IExpression>? = null, val arrayvalue: Array<IExpression>? = null,
val heapId: Int? =null, val heapId: Int? =null,
override val position: Position) : IExpression { override val position: Position) : IExpression {
override lateinit var parent: Node override lateinit var parent: Node
private val initialstrvalue = strvalue
override fun referencesIdentifier(name: String) = arrayvalue?.any { it.referencesIdentifier(name) } ?: false override fun referencesIdentifier(name: String) = arrayvalue?.any { it.referencesIdentifier(name) } ?: false
@ -1004,12 +1023,12 @@ class LiteralValue(val type: DataType,
DataType.UWORD, DataType.WORD -> if(wordvalue==null) throw FatalAstException("literal value missing wordvalue") DataType.UWORD, DataType.WORD -> if(wordvalue==null) throw FatalAstException("literal value missing wordvalue")
DataType.FLOAT -> if(floatvalue==null) throw FatalAstException("literal value missing floatvalue") DataType.FLOAT -> if(floatvalue==null) throw FatalAstException("literal value missing floatvalue")
in StringDatatypes -> in StringDatatypes ->
if(strvalue==null && heapId==null) throw FatalAstException("literal value missing strvalue/heapId") if(initialstrvalue==null && heapId==null) throw FatalAstException("literal value missing strvalue/heapId")
in ArrayDatatypes -> in ArrayDatatypes ->
if(arrayvalue==null && heapId==null) throw FatalAstException("literal value missing arrayvalue/heapId") if(arrayvalue==null && heapId==null) throw FatalAstException("literal value missing arrayvalue/heapId")
else -> throw FatalAstException("invalid type $type") else -> throw FatalAstException("invalid type $type")
} }
if(bytevalue==null && wordvalue==null && floatvalue==null && arrayvalue==null && strvalue==null && heapId==null) if(bytevalue==null && wordvalue==null && floatvalue==null && arrayvalue==null && initialstrvalue==null && heapId==null)
throw FatalAstException("literal value without actual value") throw FatalAstException("literal value without actual value")
} }
@ -1031,7 +1050,7 @@ class LiteralValue(val type: DataType,
(floatvalue!=null && floatvalue != 0.0) || (floatvalue!=null && floatvalue != 0.0) ||
(bytevalue!=null && bytevalue != 0.toShort()) || (bytevalue!=null && bytevalue != 0.toShort()) ||
(wordvalue!=null && wordvalue != 0) || (wordvalue!=null && wordvalue != 0) ||
(strvalue!=null && strvalue.isNotEmpty()) || (initialstrvalue!=null && initialstrvalue.isNotEmpty()) ||
(arrayvalue != null && arrayvalue.isNotEmpty()) (arrayvalue != null && arrayvalue.isNotEmpty())
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
@ -1051,7 +1070,7 @@ class LiteralValue(val type: DataType,
DataType.FLOAT -> "float:$floatvalue" DataType.FLOAT -> "float:$floatvalue"
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS-> { DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS-> {
if(heapId!=null) "str:#$heapId" if(heapId!=null) "str:#$heapId"
else "str:$strvalue" else "str:$initialstrvalue"
} }
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F -> { DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F -> {
if(heapId!=null) "arrayspec:#$heapId" if(heapId!=null) "arrayspec:#$heapId"
@ -1069,7 +1088,7 @@ class LiteralValue(val type: DataType,
val bh = bytevalue?.hashCode() ?: 0x10001234 val bh = bytevalue?.hashCode() ?: 0x10001234
val wh = wordvalue?.hashCode() ?: 0x01002345 val wh = wordvalue?.hashCode() ?: 0x01002345
val fh = floatvalue?.hashCode() ?: 0x00103456 val fh = floatvalue?.hashCode() ?: 0x00103456
val sh = strvalue?.hashCode() ?: 0x00014567 val sh = initialstrvalue?.hashCode() ?: 0x00014567
val ah = arrayvalue?.hashCode() ?: 0x11119876 val ah = arrayvalue?.hashCode() ?: 0x11119876
var hash = bh * 31 xor wh var hash = bh * 31 xor wh
hash = hash*31 xor fh hash = hash*31 xor fh
@ -1091,8 +1110,8 @@ class LiteralValue(val type: DataType,
if(numLeft!=null && numRight!=null) if(numLeft!=null && numRight!=null)
return numLeft.compareTo(numRight) return numLeft.compareTo(numRight)
if(strvalue!=null && other.strvalue!=null) if(initialstrvalue!=null && other.initialstrvalue!=null)
return strvalue.compareTo(other.strvalue) return initialstrvalue.compareTo(other.initialstrvalue)
throw ExpressionError("cannot compare type $type with ${other.type}", other.position) throw ExpressionError("cannot compare type $type with ${other.type}", other.position)
} }
@ -1160,6 +1179,12 @@ class LiteralValue(val type: DataType,
} }
return null // invalid type conversion from $this to $targettype return null // invalid type conversion from $this to $targettype
} }
fun strvalue(heap: HeapValues): String {
if(initialstrvalue!=null)
return initialstrvalue
return heap.get(heapId!!).str!!
}
} }
@ -1200,15 +1225,15 @@ class RangeExpr(var from: IExpression,
return "RangeExpr(from $from, to $to, step $step, pos=$position)" return "RangeExpr(from $from, to $to, step $step, pos=$position)"
} }
fun size(): Int? { fun size(heap: HeapValues): Int? {
val fromLv = (from as? LiteralValue) val fromLv = (from as? LiteralValue)
val toLv = (to as? LiteralValue) val toLv = (to as? LiteralValue)
if(fromLv==null || toLv==null) if(fromLv==null || toLv==null)
return null return null
return toConstantIntegerRange()?.count() return toConstantIntegerRange(heap)?.count()
} }
fun toConstantIntegerRange(): IntProgression? { fun toConstantIntegerRange(heap: HeapValues): IntProgression? {
val fromLv = from as? LiteralValue val fromLv = from as? LiteralValue
val toLv = to as? LiteralValue val toLv = to as? LiteralValue
if(fromLv==null || toLv==null) if(fromLv==null || toLv==null)
@ -1217,8 +1242,8 @@ class RangeExpr(var from: IExpression,
val toVal: Int val toVal: Int
if(fromLv.isString && toLv.isString) { if(fromLv.isString && toLv.isString) {
// string range -> int range over petscii values // string range -> int range over petscii values
fromVal = Petscii.encodePetscii(fromLv.strvalue!!, true)[0].toInt() fromVal = Petscii.encodePetscii(fromLv.strvalue(heap), true)[0].toInt()
toVal = Petscii.encodePetscii(toLv.strvalue!!, true)[0].toInt() toVal = Petscii.encodePetscii(toLv.strvalue(heap), true)[0].toInt()
} else { } else {
// integer range // integer range
fromVal = (from as LiteralValue).asIntegerValue!! fromVal = (from as LiteralValue).asIntegerValue!!

View File

@ -635,14 +635,16 @@ class AstChecker(private val namespace: INameScope,
else if(from.asIntegerValue > to.asIntegerValue && step>=0) else if(from.asIntegerValue > to.asIntegerValue && step>=0)
err("descending range requires step < 0") err("descending range requires step < 0")
} }
from.strvalue!=null && to.strvalue!=null -> { from.isString && to.isString -> {
if(from.strvalue.length!=1 || to.strvalue.length!=1) val fromString = from.strvalue(heap)
val toString = to.strvalue(heap)
if(fromString.length!=1 || toString.length!=1)
err("range from and to must be a single character") err("range from and to must be a single character")
if(from.strvalue[0] == to.strvalue[0]) if(fromString[0] == toString[0])
printWarning("range contains just a single character", range.position) printWarning("range contains just a single character", range.position)
else if(from.strvalue[0] < to.strvalue[0] && step<=0) else if(fromString[0] < toString[0] && step<=0)
err("ascending range requires step > 0") err("ascending range requires step > 0")
else if(from.strvalue[0] > to.strvalue[0] && step>=0) else if(fromString[0] > toString[0] && step>=0)
err("descending range requires step < 0") err("descending range requires step < 0")
} }
else -> err("range expression must be over integers or over characters") else -> err("range expression must be over integers or over characters")
@ -683,7 +685,7 @@ class AstChecker(private val namespace: INameScope,
else { else {
for (arg in args.withIndex().zip(func.parameters)) { for (arg in args.withIndex().zip(func.parameters)) {
if(arg.first.value.resultingDatatype(namespace, heap) !in arg.second.possibleDatatypes) if(arg.first.value.resultingDatatype(namespace, heap) !in arg.second.possibleDatatypes)
checkResult.add(ExpressionError("argument ${arg.first.index+1} has invalid type, expected ${arg.second.possibleDatatypes}", position)) checkResult.add(ExpressionError("builtin function argument ${arg.first.index+1} has invalid type, expected ${arg.second.possibleDatatypes}", position))
} }
} }
} else if(target is Subroutine) { } else if(target is Subroutine) {
@ -691,8 +693,9 @@ class AstChecker(private val namespace: INameScope,
checkResult.add(SyntaxError("invalid number of arguments", position)) checkResult.add(SyntaxError("invalid number of arguments", position))
else { else {
for (arg in args.withIndex().zip(target.parameters)) { for (arg in args.withIndex().zip(target.parameters)) {
if(arg.first.value.resultingDatatype(namespace, heap) != arg.second.type) val argDt = arg.first.value.resultingDatatype(namespace, heap)
checkResult.add(ExpressionError("argument ${arg.first.index+1} has invalid type, expected ${arg.second.type}", position)) if(argDt!=null && !argDt.assignableTo(arg.second.type))
checkResult.add(ExpressionError("subroutine argument ${arg.first.index+1} has invalid type, expected ${arg.second.type}", position))
if(target.asmParameterRegisters[arg.first.index].registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.XY, RegisterOrPair.X)) { if(target.asmParameterRegisters[arg.first.index].registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.XY, RegisterOrPair.X)) {
if(arg.first.value !is LiteralValue && arg.first.value !is IdentifierReference) if(arg.first.value !is LiteralValue && arg.first.value !is IdentifierReference)
@ -787,7 +790,7 @@ class AstChecker(private val namespace: INameScope,
checkResult.add(ExpressionError("range for string must have single characters from and to values", range.position)) checkResult.add(ExpressionError("range for string must have single characters from and to values", range.position))
return false return false
} }
val rangeSize=range.size() val rangeSize=range.size(heap)
if(rangeSize!=null && (rangeSize<0 || rangeSize>255)) { if(rangeSize!=null && (rangeSize<0 || rangeSize>255)) {
checkResult.add(ExpressionError("size of range for string must be 0..255, instead of $rangeSize", range.position)) checkResult.add(ExpressionError("size of range for string must be 0..255, instead of $rangeSize", range.position))
return false return false
@ -797,7 +800,7 @@ class AstChecker(private val namespace: INameScope,
in ArrayDatatypes -> { in ArrayDatatypes -> {
// range and length check bytes // range and length check bytes
val expectedSize = arrayspec!!.size() val expectedSize = arrayspec!!.size()
val rangeSize=range.size() val rangeSize=range.size(heap)
if(rangeSize!=null && rangeSize != expectedSize) { if(rangeSize!=null && rangeSize != expectedSize) {
checkResult.add(ExpressionError("range size doesn't match array size, expected $expectedSize found $rangeSize", range.position)) checkResult.add(ExpressionError("range size doesn't match array size, expected $expectedSize found $rangeSize", range.position))
return false return false
@ -859,7 +862,7 @@ class AstChecker(private val namespace: INameScope,
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> { DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
if(!value.isString) if(!value.isString)
return err("string value expected") return err("string value expected")
val str = value.strvalue ?: heap.get(value.heapId!!).str!! val str = value.strvalue(heap)
if (str.length > 255) if (str.length > 255)
return err("string length must be 0-255") return err("string length must be 0-255")
} }

View File

@ -1,6 +1,5 @@
package prog8.compiler package prog8.compiler
import com.sun.org.apache.xalan.internal.xsltc.cmdline.Compile
import prog8.ast.* import prog8.ast.*
import prog8.ast.RegisterOrPair.* import prog8.ast.RegisterOrPair.*
import prog8.compiler.intermediate.IntermediateProgram import prog8.compiler.intermediate.IntermediateProgram
@ -1470,7 +1469,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
} }
if(loop.iterable is RangeExpr) { if(loop.iterable is RangeExpr) {
val range = (loop.iterable as RangeExpr).toConstantIntegerRange() val range = (loop.iterable as RangeExpr).toConstantIntegerRange(heap)
if(range!=null) { if(range!=null) {
// loop over a range with constant start, last and step values // loop over a range with constant start, last and step values
if (range.isEmpty()) if (range.isEmpty())
@ -1536,7 +1535,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
DataType.STR_P, DataType.STR_P,
DataType.STR_S, DataType.STR_S,
DataType.STR_PS -> { DataType.STR_PS -> {
numElements = iterableValue.strvalue?.length ?: heap.get(iterableValue.heapId!!).str!!.length numElements = iterableValue.strvalue(heap).length
if(numElements>255) throw CompilerException("string length > 255") if(numElements>255) throw CompilerException("string length > 255")
} }
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UB, DataType.ARRAY_B,

View File

@ -345,7 +345,7 @@ private fun builtinLen(args: List<IExpression>, position: Position, namespace:IN
LiteralValue(DataType.UWORD, wordvalue=arraySize, position=args[0].position) LiteralValue(DataType.UWORD, wordvalue=arraySize, position=args[0].position)
} }
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> { DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
val str = argument.strvalue ?: heap.get(argument.heapId!!).str!! val str = argument.strvalue(heap)
LiteralValue(DataType.UWORD, wordvalue=str.length, position=args[0].position) LiteralValue(DataType.UWORD, wordvalue=str.length, position=args[0].position)
} }
DataType.UBYTE, DataType.BYTE, DataType.UBYTE, DataType.BYTE,

View File

@ -1,6 +1,7 @@
package prog8.optimizing package prog8.optimizing
import prog8.ast.* import prog8.ast.*
import prog8.compiler.HeapValues
import kotlin.math.pow import kotlin.math.pow
@ -9,11 +10,11 @@ val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "=
class ConstExprEvaluator { class ConstExprEvaluator {
fun evaluate(left: LiteralValue, operator: String, right: LiteralValue): IExpression { fun evaluate(left: LiteralValue, operator: String, right: LiteralValue, heap: HeapValues): IExpression {
return when(operator) { return when(operator) {
"+" -> plus(left, right) "+" -> plus(left, right, heap)
"-" -> minus(left, right) "-" -> minus(left, right)
"*" -> multiply(left, right) "*" -> multiply(left, right, heap)
"/" -> divide(left, right) "/" -> divide(left, right)
"//" -> floordivide(left, right) "//" -> floordivide(left, right)
"%" -> remainder(left, right) "%" -> remainder(left, right)
@ -141,7 +142,7 @@ class ConstExprEvaluator {
} }
} }
private fun plus(left: LiteralValue, right: LiteralValue): LiteralValue { private fun plus(left: LiteralValue, right: LiteralValue, heap: HeapValues): LiteralValue {
val error = "cannot add $left and $right" val error = "cannot add $left and $right"
return when { return when {
left.asIntegerValue!=null -> when { left.asIntegerValue!=null -> when {
@ -154,9 +155,9 @@ class ConstExprEvaluator {
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue + right.floatvalue, 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)
} }
left.strvalue!=null -> when { left.isString -> when {
right.strvalue!=null -> { right.isString -> {
val newStr = left.strvalue + right.strvalue val newStr = left.strvalue(heap) + right.strvalue(heap)
if(newStr.length > 255) throw ExpressionError("string too long", left.position) if(newStr.length > 255) throw ExpressionError("string too long", left.position)
LiteralValue(DataType.STR, strvalue = newStr, position = left.position) LiteralValue(DataType.STR, strvalue = newStr, position = left.position)
} }
@ -183,15 +184,15 @@ class ConstExprEvaluator {
} }
} }
private fun multiply(left: LiteralValue, right: LiteralValue): LiteralValue { private fun multiply(left: LiteralValue, right: LiteralValue, heap: HeapValues): LiteralValue {
val error = "cannot multiply ${left.type} and ${right.type}" val error = "cannot multiply ${left.type} and ${right.type}"
return when { return when {
left.asIntegerValue!=null -> when { left.asIntegerValue!=null -> when {
right.asIntegerValue!=null -> LiteralValue.optimalNumeric(left.asIntegerValue * right.asIntegerValue, left.position) 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.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue * right.floatvalue, position = left.position)
right.strvalue!=null -> { right.isString -> {
if(right.strvalue.length * left.asIntegerValue > 255) throw ExpressionError("string too long", left.position) if(right.strvalue(heap).length * left.asIntegerValue > 255) throw ExpressionError("string too long", left.position)
LiteralValue(DataType.STR, strvalue = right.strvalue.repeat(left.asIntegerValue), position = left.position) LiteralValue(DataType.STR, strvalue = right.strvalue(heap).repeat(left.asIntegerValue), position = left.position)
} }
else -> throw ExpressionError(error, left.position) else -> throw ExpressionError(error, left.position)
} }
@ -294,4 +295,4 @@ class ConstExprEvaluator {
else -> throw ExpressionError(error, left.position) else -> throw ExpressionError(error, left.position)
} }
} }
} }

View File

@ -45,8 +45,8 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
in IntegerDatatypes -> { in IntegerDatatypes -> {
// vardecl: for byte/word vars, convert char/string of length 1 initialization values to ubyte integer // vardecl: for byte/word vars, convert char/string of length 1 initialization values to ubyte integer
val literal = decl.value as? LiteralValue val literal = decl.value as? LiteralValue
if (literal != null && literal.isString && literal.strvalue?.length == 1) { if (literal != null && literal.isString && literal.strvalue(heap).length == 1) {
val petscii = Petscii.encodePetscii(literal.strvalue)[0] val petscii = Petscii.encodePetscii(literal.strvalue(heap))[0]
val newValue = LiteralValue(DataType.UBYTE, bytevalue = petscii, position = literal.position) val newValue = LiteralValue(DataType.UBYTE, bytevalue = petscii, position = literal.position)
decl.value = newValue decl.value = newValue
} }
@ -138,7 +138,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
return try { return try {
val cval = identifier.constValue(namespace, heap) ?: return identifier val cval = identifier.constValue(namespace, heap) ?: return identifier
return if(cval.isNumeric) { return if(cval.isNumeric) {
val copy = LiteralValue(cval.type, cval.bytevalue, cval.wordvalue, cval.floatvalue, cval.strvalue, cval.arrayvalue, position = identifier.position) val copy = LiteralValue(cval.type, cval.bytevalue, cval.wordvalue, cval.floatvalue, null, cval.arrayvalue, position = identifier.position)
copy.parent = identifier.parent copy.parent = identifier.parent
copy copy
} else } else
@ -256,7 +256,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
return when { return when {
leftconst != null && rightconst != null -> { leftconst != null && rightconst != null -> {
optimizationsDone++ optimizationsDone++
evaluator.evaluate(leftconst, expr.operator, rightconst) evaluator.evaluate(leftconst, expr.operator, rightconst, heap)
} }
else -> expr else -> expr
} }
@ -467,12 +467,12 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
} }
override fun process(literalValue: LiteralValue): LiteralValue { override fun process(literalValue: LiteralValue): LiteralValue {
if(literalValue.strvalue!=null) { if(literalValue.isString) {
// intern the string; move it into the heap // intern the string; move it into the heap
if(literalValue.strvalue.length !in 1..255) if(literalValue.strvalue(heap).length !in 1..255)
addError(ExpressionError("string literal length must be between 1 and 255", literalValue.position)) addError(ExpressionError("string literal length must be between 1 and 255", literalValue.position))
else { else {
val heapId = heap.add(literalValue.type, literalValue.strvalue) // TODO: we don't know the actual string type yet, STR != STR_P etc... val heapId = heap.add(literalValue.type, literalValue.strvalue(heap)) // TODO: we don't know the actual string type yet, STR != STR_P etc...
val newValue = LiteralValue(literalValue.type, heapId = heapId, position = literalValue.position) val newValue = LiteralValue(literalValue.type, heapId = heapId, position = literalValue.position)
return super.process(newValue) return super.process(newValue)
} }
@ -540,7 +540,8 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
// see if we can promote/convert a literal value to the required datatype // see if we can promote/convert a literal value to the required datatype
when(targetDt) { when(targetDt) {
DataType.UWORD -> { DataType.UWORD -> {
// we can convert to UWORD: any UBYTE, BYTE/WORD that are >=0, FLOAT that's an integer 0..65535 // we can convert to UWORD: any UBYTE, BYTE/WORD that are >=0, FLOAT that's an integer 0..65535,
// STR of length 1 (take the character's byte value)
if(lv.type==DataType.UBYTE) if(lv.type==DataType.UBYTE)
assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position=lv.position) assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position=lv.position)
else if(lv.type==DataType.BYTE && lv.bytevalue!!>=0) else if(lv.type==DataType.BYTE && lv.bytevalue!!>=0)
@ -552,9 +553,17 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
if(floor(d)==d && d>=0 && d<=65535) if(floor(d)==d && d>=0 && d<=65535)
assignment.value = LiteralValue(DataType.UWORD, wordvalue=floor(d).toInt(), position=lv.position) assignment.value = LiteralValue(DataType.UWORD, wordvalue=floor(d).toInt(), position=lv.position)
} }
else if(lv.type in StringDatatypes) {
val str = lv.strvalue(heap)
if(str.length==1) {
val petscii = Petscii.encodePetscii(str)[0]
assignment.value = LiteralValue(DataType.UWORD, wordvalue = petscii.toInt(), position = lv.position)
}
}
} }
DataType.UBYTE -> { DataType.UBYTE -> {
// we can convert to UBYTE: UWORD <=255, BYTE >=0, FLOAT that's an integer 0..255 // we can convert to UBYTE: UWORD <=255, BYTE >=0, FLOAT that's an integer 0..255,
// STR of length 1 (take the character's byte value)
if(lv.type==DataType.UWORD && lv.wordvalue!! <= 255) if(lv.type==DataType.UWORD && lv.wordvalue!! <= 255)
assignment.value = LiteralValue(DataType.UBYTE, lv.wordvalue.toShort(), position=lv.position) assignment.value = LiteralValue(DataType.UBYTE, lv.wordvalue.toShort(), position=lv.position)
else if(lv.type==DataType.BYTE && lv.bytevalue!! >=0) else if(lv.type==DataType.BYTE && lv.bytevalue!! >=0)
@ -564,6 +573,13 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
if(floor(d)==d && d >=0 && d<=255) if(floor(d)==d && d >=0 && d<=255)
assignment.value = LiteralValue(DataType.UBYTE, floor(d).toShort(), position=lv.position) assignment.value = LiteralValue(DataType.UBYTE, floor(d).toShort(), position=lv.position)
} }
else if(lv.type in StringDatatypes) {
val str = lv.strvalue(heap)
if(str.length==1) {
val petscii = Petscii.encodePetscii(str)[0]
assignment.value = LiteralValue(DataType.UBYTE, bytevalue = petscii, position = lv.position)
}
}
} }
DataType.BYTE -> { DataType.BYTE -> {
// we can convert to BYTE: UWORD/UBYTE <= 127, FLOAT that's an integer 0..127 // we can convert to BYTE: UWORD/UBYTE <= 127, FLOAT that's an integer 0..127
@ -578,7 +594,8 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
} }
} }
DataType.WORD -> { DataType.WORD -> {
// we can convert to WORD: any UBYTE/BYTE, UWORD <= 32767, FLOAT that's an integer -32768..32767 // we can convert to WORD: any UBYTE/BYTE, UWORD <= 32767, FLOAT that's an integer -32768..32767,
// STR of length 1 (take the character's byte value)
if(lv.type==DataType.UBYTE || lv.type==DataType.BYTE) if(lv.type==DataType.UBYTE || lv.type==DataType.BYTE)
assignment.value = LiteralValue(DataType.WORD, wordvalue=lv.bytevalue!!.toInt(), position=lv.position) assignment.value = LiteralValue(DataType.WORD, wordvalue=lv.bytevalue!!.toInt(), position=lv.position)
else if(lv.type==DataType.UWORD && lv.wordvalue!! <= 32767) else if(lv.type==DataType.UWORD && lv.wordvalue!! <= 32767)
@ -588,6 +605,13 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
if(floor(d)==d && d>=-32768 && d<=32767) if(floor(d)==d && d>=-32768 && d<=32767)
assignment.value = LiteralValue(DataType.BYTE, floor(d).toShort(), position=lv.position) assignment.value = LiteralValue(DataType.BYTE, floor(d).toShort(), position=lv.position)
} }
else if(lv.type in StringDatatypes) {
val str = lv.strvalue(heap)
if(str.length==1) {
val petscii = Petscii.encodePetscii(str)[0]
assignment.value = LiteralValue(DataType.WORD, wordvalue= petscii.toInt(), position = lv.position)
}
}
} }
DataType.FLOAT -> { DataType.FLOAT -> {
if(lv.isNumeric) if(lv.isNumeric)

View File

@ -102,7 +102,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
super.process(forLoop) super.process(forLoop)
val range = forLoop.iterable as? RangeExpr val range = forLoop.iterable as? RangeExpr
if(range!=null) { if(range!=null) {
if(range.size()==1) { if(range.size(heap)==1) {
// for loop over a (constant) range of just a single value-- optimize the loop away // for loop over a (constant) range of just a single value-- optimize the loop away
// loopvar/reg = range value , follow by block // loopvar/reg = range value , follow by block
val assignment = Assignment(listOf(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, forLoop.position)), null, range.from, forLoop.position) val assignment = Assignment(listOf(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, forLoop.position)), null, range.from, forLoop.position)