From 48a6a051099749ba5958920b07fe0b4d4f103a8f Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 22 Nov 2018 00:01:41 +0100 Subject: [PATCH] improve datatype assignment checks --- compiler/examples/numbergame-c64.p8 | 24 +++++---- compiler/examples/test.p8 | 21 ++++++++ compiler/src/prog8/ast/AST.kt | 53 ++++++++++++++----- compiler/src/prog8/ast/AstChecker.kt | 25 +++++---- compiler/src/prog8/compiler/Compiler.kt | 5 +- .../src/prog8/functions/BuiltinFunctions.kt | 2 +- .../prog8/optimizing/ConstExprEvaluator.kt | 25 ++++----- .../src/prog8/optimizing/ConstantFolding.kt | 44 +++++++++++---- .../prog8/optimizing/StatementOptimizer.kt | 2 +- 9 files changed, 139 insertions(+), 62 deletions(-) diff --git a/compiler/examples/numbergame-c64.p8 b/compiler/examples/numbergame-c64.p8 index a349383b2..d15aa586b 100644 --- a/compiler/examples/numbergame-c64.p8 +++ b/compiler/examples/numbergame-c64.p8 @@ -10,19 +10,23 @@ ubyte secretnumber = 0 ubyte attempts_left = 10 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() c64.VMCSB |= 2 ; activate lowercase charset ; greeting c64scr.print_string("Enter your name: ") - Y = c64scr.input_chars(name) ; @todo fix argument type check - c64.CHROUT("\n") ; @todo fix argument type check - c64.CHROUT("\n") + Y = c64scr.input_chars(name) + c64.CHROUT("\n") ; @todo fix argument conversion to UBYTE + c64.CHROUT("\n") ; @todo fix argument conversion to UBYTE c64scr.print_string("Hello, ") c64scr.print_string(name) - c64.CHROUT(".") - c64.CHROUT("\n") + c64.CHROUT(".") ; @todo fix argument conversion to UBYTE + c64.CHROUT("\n") ; @todo fix argument conversion to UBYTE ; create a secret random number from 1-100 c64.RNDA(0) ; fac = rnd(0) @@ -30,7 +34,7 @@ c64.MUL10() ; .. and now *100 c64.FADDH() ; add 0.5.. c64.FADDH() ; and again, so +1 total - A, Y = c64flt.GETADRAY() + A, Y = c64flt.GETADRAY() ; @todo fix return value type check secretnumber = A ;A=math.randbyte() ;A+=c64.RASTER @@ -47,10 +51,10 @@ ask_guess: c64scr.print_string(" left.\nWhat is your next guess? ") Y = c64scr.input_chars(guess) - c64.CHROUT("\n") - freadstr_arg = guess + c64.CHROUT("\n") ; @todo fix argument conversion to UBYTE + freadstr_arg = guess ; @todo put string's adress in uword variable c64.FREADSTR(A) - A, Y = c64flt.GETADRAY() + A, Y = c64flt.GETADRAY() ; @todo fix return value type check if(A==secretnumber) { c64scr.print_string("\nThat's my number, impressive!\n") goto goodbye @@ -68,7 +72,7 @@ ask_guess: ; game over. c64scr.print_string("\nToo bad! It was: ") c64scr.print_byte_decimal(secretnumber) - c64.CHROUT("\n") + c64.CHROUT("\n") ; @todo fix argument conversion to UBYTE goodbye: c64scr.print_string("\nThanks for playing. Bye!\n") diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index bcebb40aa..9d3c00960 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -16,6 +16,27 @@ sub start() { 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, address) ; ok! address =c64.MEMBOT(1, memaddr) ; ok! diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index 3ee36b5c0..74aab5028 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -30,7 +30,25 @@ enum class DataType { ARRAY_B, ARRAY_UW, 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 { @@ -948,11 +966,12 @@ class LiteralValue(val type: DataType, val bytevalue: Short? = null, val wordvalue: Int? = null, val floatvalue: Double? = null, - val strvalue: String? = null, + strvalue: String? = null, val arrayvalue: Array? = null, val heapId: Int? =null, override val position: Position) : IExpression { override lateinit var parent: Node + private val initialstrvalue = strvalue 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.FLOAT -> if(floatvalue==null) throw FatalAstException("literal value missing floatvalue") 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 -> if(arrayvalue==null && heapId==null) throw FatalAstException("literal value missing arrayvalue/heapId") 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") } @@ -1031,7 +1050,7 @@ class LiteralValue(val type: DataType, (floatvalue!=null && floatvalue != 0.0) || (bytevalue!=null && bytevalue != 0.toShort()) || (wordvalue!=null && wordvalue != 0) || - (strvalue!=null && strvalue.isNotEmpty()) || + (initialstrvalue!=null && initialstrvalue.isNotEmpty()) || (arrayvalue != null && arrayvalue.isNotEmpty()) override fun linkParents(parent: Node) { @@ -1051,7 +1070,7 @@ class LiteralValue(val type: DataType, DataType.FLOAT -> "float:$floatvalue" DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS-> { 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 -> { if(heapId!=null) "arrayspec:#$heapId" @@ -1069,7 +1088,7 @@ class LiteralValue(val type: DataType, val bh = bytevalue?.hashCode() ?: 0x10001234 val wh = wordvalue?.hashCode() ?: 0x01002345 val fh = floatvalue?.hashCode() ?: 0x00103456 - val sh = strvalue?.hashCode() ?: 0x00014567 + val sh = initialstrvalue?.hashCode() ?: 0x00014567 val ah = arrayvalue?.hashCode() ?: 0x11119876 var hash = bh * 31 xor wh hash = hash*31 xor fh @@ -1091,8 +1110,8 @@ class LiteralValue(val type: DataType, if(numLeft!=null && numRight!=null) return numLeft.compareTo(numRight) - if(strvalue!=null && other.strvalue!=null) - return strvalue.compareTo(other.strvalue) + if(initialstrvalue!=null && other.initialstrvalue!=null) + return initialstrvalue.compareTo(other.initialstrvalue) 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 } + + 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)" } - fun size(): Int? { + fun size(heap: HeapValues): Int? { val fromLv = (from as? LiteralValue) val toLv = (to as? LiteralValue) if(fromLv==null || toLv==null) return null - return toConstantIntegerRange()?.count() + return toConstantIntegerRange(heap)?.count() } - fun toConstantIntegerRange(): IntProgression? { + fun toConstantIntegerRange(heap: HeapValues): IntProgression? { val fromLv = from as? LiteralValue val toLv = to as? LiteralValue if(fromLv==null || toLv==null) @@ -1217,8 +1242,8 @@ class RangeExpr(var from: IExpression, val toVal: Int if(fromLv.isString && toLv.isString) { // string range -> int range over petscii values - fromVal = Petscii.encodePetscii(fromLv.strvalue!!, true)[0].toInt() - toVal = Petscii.encodePetscii(toLv.strvalue!!, true)[0].toInt() + fromVal = Petscii.encodePetscii(fromLv.strvalue(heap), true)[0].toInt() + toVal = Petscii.encodePetscii(toLv.strvalue(heap), true)[0].toInt() } else { // integer range fromVal = (from as LiteralValue).asIntegerValue!! diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index 6b108ff01..a5229775b 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -635,14 +635,16 @@ class AstChecker(private val namespace: INameScope, else if(from.asIntegerValue > to.asIntegerValue && step>=0) err("descending range requires step < 0") } - from.strvalue!=null && to.strvalue!=null -> { - if(from.strvalue.length!=1 || to.strvalue.length!=1) + from.isString && to.isString -> { + 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") - if(from.strvalue[0] == to.strvalue[0]) + if(fromString[0] == toString[0]) 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") - else if(from.strvalue[0] > to.strvalue[0] && step>=0) + else if(fromString[0] > toString[0] && step>=0) err("descending range requires step < 0") } else -> err("range expression must be over integers or over characters") @@ -683,7 +685,7 @@ class AstChecker(private val namespace: INameScope, else { for (arg in args.withIndex().zip(func.parameters)) { 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) { @@ -691,8 +693,9 @@ class AstChecker(private val namespace: INameScope, checkResult.add(SyntaxError("invalid number of arguments", position)) else { for (arg in args.withIndex().zip(target.parameters)) { - if(arg.first.value.resultingDatatype(namespace, heap) != arg.second.type) - checkResult.add(ExpressionError("argument ${arg.first.index+1} has invalid type, expected ${arg.second.type}", position)) + val argDt = arg.first.value.resultingDatatype(namespace, heap) + 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(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)) return false } - val rangeSize=range.size() + val rangeSize=range.size(heap) if(rangeSize!=null && (rangeSize<0 || rangeSize>255)) { checkResult.add(ExpressionError("size of range for string must be 0..255, instead of $rangeSize", range.position)) return false @@ -797,7 +800,7 @@ class AstChecker(private val namespace: INameScope, in ArrayDatatypes -> { // range and length check bytes val expectedSize = arrayspec!!.size() - val rangeSize=range.size() + val rangeSize=range.size(heap) if(rangeSize!=null && rangeSize != expectedSize) { checkResult.add(ExpressionError("range size doesn't match array size, expected $expectedSize found $rangeSize", range.position)) return false @@ -859,7 +862,7 @@ class AstChecker(private val namespace: INameScope, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> { if(!value.isString) return err("string value expected") - val str = value.strvalue ?: heap.get(value.heapId!!).str!! + val str = value.strvalue(heap) if (str.length > 255) return err("string length must be 0-255") } diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 804df0eb6..73d6fa11d 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -1,6 +1,5 @@ package prog8.compiler -import com.sun.org.apache.xalan.internal.xsltc.cmdline.Compile import prog8.ast.* import prog8.ast.RegisterOrPair.* import prog8.compiler.intermediate.IntermediateProgram @@ -1470,7 +1469,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, } if(loop.iterable is RangeExpr) { - val range = (loop.iterable as RangeExpr).toConstantIntegerRange() + val range = (loop.iterable as RangeExpr).toConstantIntegerRange(heap) if(range!=null) { // loop over a range with constant start, last and step values if (range.isEmpty()) @@ -1536,7 +1535,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, DataType.STR_P, DataType.STR_S, 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") } DataType.ARRAY_UB, DataType.ARRAY_B, diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index a66b2ea1d..3424dd63b 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -345,7 +345,7 @@ private fun builtinLen(args: List, position: Position, namespace:IN LiteralValue(DataType.UWORD, wordvalue=arraySize, position=args[0].position) } 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) } DataType.UBYTE, DataType.BYTE, diff --git a/compiler/src/prog8/optimizing/ConstExprEvaluator.kt b/compiler/src/prog8/optimizing/ConstExprEvaluator.kt index 67e1917fb..67912f745 100644 --- a/compiler/src/prog8/optimizing/ConstExprEvaluator.kt +++ b/compiler/src/prog8/optimizing/ConstExprEvaluator.kt @@ -1,6 +1,7 @@ package prog8.optimizing import prog8.ast.* +import prog8.compiler.HeapValues import kotlin.math.pow @@ -9,11 +10,11 @@ val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "= 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) { - "+" -> plus(left, right) + "+" -> plus(left, right, heap) "-" -> minus(left, right) - "*" -> multiply(left, right) + "*" -> multiply(left, right, heap) "/" -> divide(left, right) "//" -> floordivide(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" return 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) else -> throw ExpressionError(error, left.position) } - left.strvalue!=null -> when { - right.strvalue!=null -> { - val newStr = left.strvalue + right.strvalue + left.isString -> when { + right.isString -> { + val newStr = left.strvalue(heap) + right.strvalue(heap) if(newStr.length > 255) throw ExpressionError("string too long", 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}" return when { left.asIntegerValue!=null -> when { 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.strvalue!=null -> { - if(right.strvalue.length * left.asIntegerValue > 255) throw ExpressionError("string too long", left.position) - LiteralValue(DataType.STR, strvalue = right.strvalue.repeat(left.asIntegerValue), position = left.position) + right.isString -> { + if(right.strvalue(heap).length * left.asIntegerValue > 255) throw ExpressionError("string too long", left.position) + LiteralValue(DataType.STR, strvalue = right.strvalue(heap).repeat(left.asIntegerValue), position = left.position) } else -> throw ExpressionError(error, left.position) } @@ -294,4 +295,4 @@ class ConstExprEvaluator { else -> throw ExpressionError(error, left.position) } } -} \ No newline at end of file +} diff --git a/compiler/src/prog8/optimizing/ConstantFolding.kt b/compiler/src/prog8/optimizing/ConstantFolding.kt index a10b9b128..60dc16e23 100644 --- a/compiler/src/prog8/optimizing/ConstantFolding.kt +++ b/compiler/src/prog8/optimizing/ConstantFolding.kt @@ -45,8 +45,8 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV in IntegerDatatypes -> { // vardecl: for byte/word vars, convert char/string of length 1 initialization values to ubyte integer val literal = decl.value as? LiteralValue - if (literal != null && literal.isString && literal.strvalue?.length == 1) { - val petscii = Petscii.encodePetscii(literal.strvalue)[0] + if (literal != null && literal.isString && literal.strvalue(heap).length == 1) { + val petscii = Petscii.encodePetscii(literal.strvalue(heap))[0] val newValue = LiteralValue(DataType.UBYTE, bytevalue = petscii, position = literal.position) decl.value = newValue } @@ -138,7 +138,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV return try { val cval = identifier.constValue(namespace, heap) ?: return identifier 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 } else @@ -256,7 +256,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV return when { leftconst != null && rightconst != null -> { optimizationsDone++ - evaluator.evaluate(leftconst, expr.operator, rightconst) + evaluator.evaluate(leftconst, expr.operator, rightconst, heap) } else -> expr } @@ -467,12 +467,12 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV } override fun process(literalValue: LiteralValue): LiteralValue { - if(literalValue.strvalue!=null) { + if(literalValue.isString) { // 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)) 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) 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 when(targetDt) { 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) assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position=lv.position) 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) 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 -> { - // 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) assignment.value = LiteralValue(DataType.UBYTE, lv.wordvalue.toShort(), position=lv.position) 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) 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 -> { // 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 -> { - // 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) assignment.value = LiteralValue(DataType.WORD, wordvalue=lv.bytevalue!!.toInt(), position=lv.position) 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) 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 -> { if(lv.isNumeric) diff --git a/compiler/src/prog8/optimizing/StatementOptimizer.kt b/compiler/src/prog8/optimizing/StatementOptimizer.kt index 70e2bd75a..6c7b7183a 100644 --- a/compiler/src/prog8/optimizing/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizing/StatementOptimizer.kt @@ -102,7 +102,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He super.process(forLoop) val range = forLoop.iterable as? RangeExpr 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 // loopvar/reg = range value , follow by block val assignment = Assignment(listOf(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, forLoop.position)), null, range.from, forLoop.position)