diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index 42741797b..7d88f1510 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -4,46 +4,13 @@ ~ main { - byte mainb = 44 + byte[2] barray = 0 + byte[2] barray2 =0 sub start() { - test() - test() -} - -sub test() { - - ; @todo test assigning values to array vars - ; @todo test creating const array vars - - - byte subb=42 - - for byte i in 0 to 2 { - float ff=3.3 - byte bb=99 - word ww=1999 - - _vm_write_num(ff) - _vm_write_char('\n') - _vm_write_num(bb) - _vm_write_char('\n') - _vm_write_num(ww) - _vm_write_char('\n') - _vm_write_num(subb) - _vm_write_char('\n') - _vm_write_num(mainb) - _vm_write_char('\n') - _vm_write_char('\n') - - ff += 3 - bb += 3 - ww += 3 - subb += 3 - mainb += 3 - } - + X=barray[0] return } + } diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index e6b74b5dc..ce3d4534e 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -254,9 +254,19 @@ class AstChecker(private val namespace: INameScope, } } + // it is not possible to assign a new array to something. + // currently, it's also not possible to assign a new string to a string variable. + when(assignment.value.resultingDatatype(namespace, heap)) { + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> + checkResult.add(SyntaxError("it's not possible to reassign a string", assignment.position)) // todo allow reassigning strings + DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX -> + checkResult.add(SyntaxError("it's not possible to reassign an array", assignment.position)) + else -> {} + } + if(assignment.aug_op!=null) { // check augmented assignment: - // A /= 3 -> check is if it was A = A / 3 + // A /= 3 -> check as if it was A = A / 3 val target: IExpression = if(assignment.target.register!=null) RegisterExpr(assignment.target.register!!, assignment.target.position) @@ -278,7 +288,13 @@ class AstChecker(private val namespace: INameScope, if(targetDatatype!=null) { val constVal = assignment.value.constValue(namespace, heap) if(constVal!=null) { - checkValueTypeAndRange(targetDatatype, null, constVal, heap) + val arrayspec = if(assignment.target.identifier!=null) { + val targetVar = namespace.lookup(assignment.target.identifier!!.nameInSource, assignment) as? VarDecl + targetVar?.arrayspec + } else null + checkValueTypeAndRange(targetDatatype, + arrayspec ?: ArraySpec(LiteralValue.optimalInteger(-1, assignment.position), null, assignment.position), + constVal, heap) } else { val sourceDatatype: DataType? = assignment.value.resultingDatatype(namespace, heap) if(sourceDatatype==null) { @@ -312,6 +328,12 @@ class AstChecker(private val namespace: INameScope, err("recursive var declaration") } + // CONST can only occur on simple types (byte, word, float) + if(decl.type==VarDeclType.CONST) { + if (decl.datatype != DataType.BYTE && decl.datatype != DataType.WORD && decl.datatype != DataType.FLOAT) + err("const modifier can only be used on simple types (byte, word, float)") + } + when(decl.type) { VarDeclType.VAR, VarDeclType.CONST -> { if (decl.value == null) { @@ -328,7 +350,9 @@ class AstChecker(private val namespace: INameScope, } when { decl.value is RangeExpr -> checkValueTypeAndRange(decl.datatype, decl.arrayspec, decl.value as RangeExpr) - decl.value is LiteralValue -> checkValueTypeAndRange(decl.datatype, decl.arrayspec, decl.value as LiteralValue, heap) + decl.value is LiteralValue -> checkValueTypeAndRange(decl.datatype, + decl.arrayspec ?: ArraySpec(LiteralValue.optimalInteger(-2, decl.position), null, decl.position), + decl.value as LiteralValue, heap) else -> { err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!::class.simpleName}") return super.process(decl) @@ -421,7 +445,9 @@ class AstChecker(private val namespace: INameScope, if(!compilerOptions.floats && literalValue.type==DataType.FLOAT) { checkResult.add(SyntaxError("floating point value used, but floating point is not enabled via options", literalValue.position)) } - checkValueTypeAndRange(literalValue.type, null, literalValue, heap) + checkValueTypeAndRange(literalValue.type, + ArraySpec(LiteralValue.optimalInteger(-3, literalValue.position), null, literalValue.position), + literalValue, heap) val lv = super.process(literalValue) when(lv.type) { @@ -632,7 +658,7 @@ class AstChecker(private val namespace: INameScope, } } - private fun checkValueTypeAndRange(targetDt: DataType, arrayspec: ArraySpec?, value: LiteralValue, heap: HeapValues) : Boolean { + private fun checkValueTypeAndRange(targetDt: DataType, arrayspec: ArraySpec, value: LiteralValue, heap: HeapValues) : Boolean { fun err(msg: String) : Boolean { checkResult.add(ExpressionError(msg, value.position)) return false @@ -673,7 +699,8 @@ class AstChecker(private val namespace: INameScope, // value may be either a single byte, or a byte array (of all constant values) if(value.type==DataType.ARRAY) { val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).array!!.size - if(arrayspec!=null) { + val arraySpecSize = arrayspec.size() + if(arraySpecSize!=null && arraySpecSize>0) { val constX = arrayspec.x.constValue(namespace, heap) if(constX?.asIntegerValue==null) return err("array size specifier must be constant integer value") @@ -696,7 +723,8 @@ class AstChecker(private val namespace: INameScope, // value may be either a single word, or a word array if(value.type==DataType.ARRAY || value.type==DataType.ARRAY_W) { val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).array!!.size - if(arrayspec!=null) { + val arraySpecSize = arrayspec.size() + if(arraySpecSize!=null && arraySpecSize>0) { // arrayspec is not always known when checking val constX = arrayspec.x.constValue(namespace, heap) if(constX?.asIntegerValue==null) @@ -717,8 +745,9 @@ class AstChecker(private val namespace: INameScope, DataType.ARRAY_F -> { // value may be either a single float, or a float array if(value.type==DataType.ARRAY || value.type==DataType.ARRAY_W || value.type==DataType.ARRAY_F) { - val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).doubleArray!!.size - if(arrayspec!=null) { + val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize + val arraySpecSize = arrayspec.size() + if(arraySpecSize!=null && arraySpecSize>0) { // arrayspec is not always known when checking val constX = arrayspec.x.constValue(namespace, heap) if(constX?.asIntegerValue==null) @@ -728,16 +757,18 @@ class AstChecker(private val namespace: INameScope, return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)") } } else { - val number = value.asNumericValue!!.toDouble() - if (number < FLOAT_MAX_NEGATIVE || number > FLOAT_MAX_POSITIVE) + val number = value.asNumericValue?.toDouble() + if(number==null) + return err("expected numerical value") + else if (number < FLOAT_MAX_NEGATIVE || number > FLOAT_MAX_POSITIVE) return err("value '$number' out of range for mfplt5 floating point") } } DataType.MATRIX -> { // value can only be a single byte, or a byte array (which represents the matrix) if(value.type==DataType.ARRAY || value.type==DataType.MATRIX) { - if(arrayspec!=null) { - // arrayspec is not always known when checking + val arraySpecSize = arrayspec.size() + if(arraySpecSize!=null && arraySpecSize>0) { val constX = arrayspec.x.constValue(namespace, heap) val constY = arrayspec.y!!.constValue(namespace, heap) if (constX?.asIntegerValue == null || constY?.asIntegerValue == null) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 56d01547f..5323f4734 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -66,6 +66,8 @@ class HeapValues { result = 31 * result + (doubleArray?.let { Arrays.hashCode(it) } ?: 0) return result } + + val arraysize: Int = array?.size ?: doubleArray?.size ?: 0 } private val heap = mutableListOf() @@ -1204,10 +1206,10 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, else -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") } } + // todo: maybe if you assign byte or word to array/matrix, clear it with that value? DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") null -> throw CompilerException("could not determine targetdt") - // todo: maybe if you assign byte or word to array/matrix, clear it with that value? } } diff --git a/compiler/src/prog8/optimizing/ConstantFolding.kt b/compiler/src/prog8/optimizing/ConstantFolding.kt index 9d92b328a..1c58057d8 100644 --- a/compiler/src/prog8/optimizing/ConstantFolding.kt +++ b/compiler/src/prog8/optimizing/ConstantFolding.kt @@ -105,14 +105,17 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV } /** - * replace identifiers that refer to const value, with the value itself + * replace identifiers that refer to const value, with the value itself (if it's a simple type) */ override fun process(identifier: IdentifierReference): IExpression { return try { val cval = identifier.constValue(namespace, heap) ?: return identifier - val copy = LiteralValue(cval.type, cval.bytevalue, cval.wordvalue, cval.floatvalue, cval.strvalue, cval.arrayvalue, position=identifier.position) - copy.parent = identifier.parent - return copy + return if(cval.isNumeric) { + val copy = LiteralValue(cval.type, cval.bytevalue, cval.wordvalue, cval.floatvalue, cval.strvalue, cval.arrayvalue, position = identifier.position) + copy.parent = identifier.parent + copy + } else + identifier } catch (ax: AstException) { addError(ax) identifier diff --git a/docs/source/programming.rst b/docs/source/programming.rst index cdc576ed7..080835bb4 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -233,7 +233,7 @@ Special types: const and memory-mapped When using ``const``, the value of the 'variable' can no longer be changed. You'll have to specify the initial value expression. This value is then used by the compiler everywhere you refer to the constant (and no storage is allocated -for the constant itself). +for the constant itself). This is only valid for the simple numeric types (byte, word, float). When using ``memory``, the variable will point to specific location in memory, rather than being newly allocated. The initial value (mandatory) must be a valid diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index 4cbfcbb84..b9f66a569 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -288,11 +288,9 @@ Constants All variables can be assigned new values unless you use the ``const`` keyword. The initial value will now be evaluated at compile time (it must be a compile time constant expression). -Storage is allocated only for the constant values that remain a the end of this process and that -require it (floats, strings, arrays, matrices):: +This is only valid for the simple numeric types (byte, word, float):: const byte max_age = 99 - const str someName = "Peter" Reserved names