mirror of
https://github.com/irmen/prog8.git
synced 2024-12-22 18:30:01 +00:00
stricter array literal element type handling (number,bool,address-of).
More consistent implicit address-of handling if array literals contain by-ref identifiers (such as subroutine names)
This commit is contained in:
parent
66829203d8
commit
aef211e5f3
@ -131,7 +131,6 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
TODO("address-of array element $it in initial array value")
|
||||
StArrayElement(null, it.identifier.name, null)
|
||||
}
|
||||
is PtIdentifier -> StArrayElement(null, it.name, null)
|
||||
is PtNumber -> StArrayElement(it.number, null, null)
|
||||
is PtBool -> StArrayElement(null, null, it.value)
|
||||
else -> throw AssemblyError("invalid array element $it")
|
||||
|
@ -163,6 +163,7 @@ class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(el
|
||||
|
||||
|
||||
class PtArray(type: DataType, position: Position): PtExpression(type, position) {
|
||||
// children are always one of 3 types: PtBool, PtNumber or PtAddressOf.
|
||||
override fun hashCode(): Int = Objects.hash(children, type)
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if(other==null || other !is PtArray)
|
||||
|
@ -24,6 +24,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
||||
is PtArray -> {
|
||||
val valuelist = node.children.map {
|
||||
when (it) {
|
||||
is PtBool -> it.toString()
|
||||
is PtNumber -> it.number.toString()
|
||||
is PtIdentifier -> it.name
|
||||
else -> "?"
|
||||
|
@ -145,7 +145,6 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
|
||||
val newValue = PtArray(arrayValue.type, arrayValue.position)
|
||||
arrayValue.children.forEach { elt ->
|
||||
when(elt) {
|
||||
is PtIdentifier -> newValue.add(elt.prefix(arrayValue, st))
|
||||
is PtBool -> newValue.add(elt)
|
||||
is PtNumber -> newValue.add(elt)
|
||||
is PtAddressOf -> {
|
||||
|
@ -122,7 +122,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
errors.warn("resulting array has length zero", part.position)
|
||||
val tmp = mutableListOf<Expression>()
|
||||
repeat(rightconst.number.toInt()) {
|
||||
tmp += part.value
|
||||
part.value.forEach { tmp += it.copy() }
|
||||
}
|
||||
val newArray = ArrayLiteral(part.type, tmp.toTypedArray(), part.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newArray, parent))
|
||||
|
@ -1010,19 +1010,8 @@ internal class AstChecker(private val program: Program,
|
||||
checkValueTypeAndRangeArray(array.type.getOr(DataType.UNDEFINED), arrayspec, array)
|
||||
}
|
||||
|
||||
fun isPassByReferenceElement(e: Expression): Boolean {
|
||||
if(e is IdentifierReference) {
|
||||
val decl = e.targetVarDecl(program)
|
||||
return if(decl!=null)
|
||||
decl.datatype in PassByReferenceDatatypes
|
||||
else
|
||||
true // is probably a symbol that needs addr-of
|
||||
}
|
||||
return e is StringLiteral
|
||||
}
|
||||
|
||||
if(array.parent is VarDecl) {
|
||||
if (!array.value.all { it is NumericLiteral || it is AddressOf || isPassByReferenceElement(it) })
|
||||
if (!array.value.all { it is NumericLiteral || it is AddressOf })
|
||||
errors.err("array literal for variable initialization contains non-constant elements", array.position)
|
||||
} else if(array.parent is ForLoop) {
|
||||
if (!array.value.all { it.constValue(program) != null })
|
||||
@ -1036,6 +1025,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(arraydt!=targetDt)
|
||||
errors.err("value has incompatible type ($arraydt) for the variable ($targetDt)", array.position)
|
||||
}
|
||||
|
||||
super.visit(array)
|
||||
}
|
||||
|
||||
|
@ -566,8 +566,11 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
||||
|
||||
private fun transform(srcArr: ArrayLiteral): PtArray {
|
||||
val arr = PtArray(srcArr.inferType(program).getOrElse { throw FatalAstException("array must know its type") }, srcArr.position)
|
||||
for (elt in srcArr.value)
|
||||
arr.add(transformExpression(elt))
|
||||
for (elt in srcArr.value) {
|
||||
val child = transformExpression(elt)
|
||||
require(child is PtAddressOf || child is PtBool || child is PtNumber) { "array element invalid type $child" }
|
||||
arr.add(child)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,8 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
|
||||
}
|
||||
} else {
|
||||
val arrayDt = array.guessDatatype(program)
|
||||
if(arrayDt.isUnknown)
|
||||
return noModifications
|
||||
val elementDt = ArrayToElementTypes.getValue(arrayDt.getOr(DataType.UNDEFINED))
|
||||
val maxSize = when(elementDt) {
|
||||
in ByteDatatypesWithBoolean -> PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_BYTE
|
||||
|
@ -1,6 +1,7 @@
|
||||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.FatalAstException
|
||||
@ -363,6 +364,25 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
return adjustRangeDts(range, fromConst, fromDt, toConst, toDt, varDt.getOr(DataType.UNDEFINED), parent)
|
||||
}
|
||||
|
||||
override fun after(array: ArrayLiteral, parent: Node): Iterable<IAstModification> {
|
||||
// Arrays can contain booleans, numbers, or address-ofs.
|
||||
// if there is an identifier here (that is of a pass-by-reference type), take its address explicitly.
|
||||
|
||||
for((index, elt) in array.value.withIndex()) {
|
||||
if (elt is IdentifierReference) {
|
||||
val eltType = elt.inferType(program)
|
||||
val tgt = elt.targetStatement(program)
|
||||
if(eltType.isPassByReference || tgt is Subroutine || tgt is Label || tgt is Block) {
|
||||
val addressof = AddressOf(elt, null, elt.position)
|
||||
addressof.linkParents(array)
|
||||
array.value[index] = addressof
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun adjustRangeDts(
|
||||
range: RangeExpression,
|
||||
fromConst: NumericLiteral?,
|
||||
|
@ -4,6 +4,7 @@ import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import io.kotest.matchers.types.instanceOf
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.SymbolTableMaker
|
||||
import prog8.code.ast.*
|
||||
@ -456,6 +457,46 @@ main {
|
||||
array3.children.map { (it as PtNumber).number } shouldBe listOf(100, 101, 102)
|
||||
}
|
||||
|
||||
test("identifiers in array literals getting implicit address-of") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
label:
|
||||
str @shared name = "name"
|
||||
uword[] @shared array1 = [name, label, start, main]
|
||||
uword[] @shared array2 = [&name, &label, &start, &main]
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), false, src, writeAssembly = true)!!
|
||||
val x = result.codegenAst!!.entrypoint()!!
|
||||
x.children.size shouldBe 5
|
||||
val array1 = (x.children[1] as PtVariable).value as PtArray
|
||||
val array2 = (x.children[2] as PtVariable).value as PtArray
|
||||
array1.children.forEach {
|
||||
it shouldBe instanceOf<PtAddressOf>()
|
||||
}
|
||||
array2.children.forEach {
|
||||
it shouldBe instanceOf<PtAddressOf>()
|
||||
}
|
||||
}
|
||||
|
||||
test("variable identifiers in array literals not getting implicit address-of") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
label:
|
||||
str @shared name = "name"
|
||||
ubyte @shared bytevar
|
||||
uword[] @shared array1 = [cx16.r0] ; error, is variables
|
||||
uword[] @shared array2 = [bytevar] ; error, is variables
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, src, writeAssembly = true, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "contains non-constant"
|
||||
errors.errors[1] shouldContain "contains non-constant"
|
||||
}
|
||||
|
||||
fun getTestOptions(): CompilationOptions {
|
||||
val target = VMTarget()
|
||||
|
@ -1,7 +1,6 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
- word arrays (after ast processing) should no longer contain identifiers, these should have been replaced by &identifier.
|
||||
- should the array-to-array assignment support be removed and instead require an explicit copy function call? What prog8_lib_arraycopy() now does. Or just use memcopy.
|
||||
- should we add a cleararray builtin function that can efficiently set every element in the array to the given value
|
||||
|
||||
@ -14,6 +13,7 @@ Maybe this routine can be made more intelligent. See usesOtherRegistersWhileEva
|
||||
|
||||
Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
- keep boolean array intact in IR so that it might be represented as a bitmask in the resulting code (8 times storage improvement)
|
||||
- improve detection that a variable is not read before being written so that initializing it to zero can be omitted (only happens now if a vardecl is immediately followed by a for loop for instance)
|
||||
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
|
||||
- Can we support signed % (remainder) somehow?
|
||||
|
@ -1,80 +1,11 @@
|
||||
%import floats
|
||||
%import textio
|
||||
|
||||
%option no_sysinit
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
|
||||
sub arrayinit_with_multiplier() {
|
||||
str name = "xyz" * 3
|
||||
bool[3] boolarray = [true] * 3
|
||||
ubyte[3] bytearray = [42] * 3
|
||||
uword[3] wordarray = [5555] * 3
|
||||
float[3] floatarray = [123.45] * 3
|
||||
|
||||
txt.print(name)
|
||||
txt.nl()
|
||||
for cx16.r1L in 0 to 2 {
|
||||
txt.print_bool(boolarray[cx16.r1L])
|
||||
txt.spc()
|
||||
txt.print_ub(bytearray[cx16.r1L])
|
||||
txt.spc()
|
||||
txt.print_uw(wordarray[cx16.r1L])
|
||||
txt.spc()
|
||||
floats.print(floatarray[cx16.r1L])
|
||||
txt.nl()
|
||||
}
|
||||
txt.nl()
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
sub arrayinit_with_range() {
|
||||
ubyte[3] bytearray2 = 10 to 12
|
||||
uword[3] wordarray2 = 5000 to 5002
|
||||
float[3] floatarray2 = 100 to 102
|
||||
|
||||
for cx16.r1L in 0 to 2 {
|
||||
txt.print_ub(bytearray2[cx16.r1L])
|
||||
txt.spc()
|
||||
txt.print_uw(wordarray2[cx16.r1L])
|
||||
txt.spc()
|
||||
floats.print(floatarray2[cx16.r1L])
|
||||
txt.nl()
|
||||
}
|
||||
txt.nl()
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
sub arrayassign() {
|
||||
bool[4] boolarray3
|
||||
ubyte[4] bytearray3
|
||||
uword[4] wordarray3
|
||||
float[4] floatarray3
|
||||
|
||||
boolarray3 = [true] *4
|
||||
bytearray3 = [42]*4
|
||||
wordarray3 = [999]*4
|
||||
wordarray3 = [&bytearray3]*4
|
||||
wordarray3 = [bytearray3]*4
|
||||
floatarray3 = [99.77]*4
|
||||
|
||||
for cx16.r1L in 0 to 2 {
|
||||
txt.print_bool(boolarray3[cx16.r1L])
|
||||
txt.spc()
|
||||
txt.print_ub(bytearray3[cx16.r1L])
|
||||
txt.spc()
|
||||
txt.print_uw(wordarray3[cx16.r1L])
|
||||
txt.spc()
|
||||
floats.print(floatarray3[cx16.r1L])
|
||||
txt.nl()
|
||||
}
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
sub start() {
|
||||
arrayinit_with_multiplier()
|
||||
arrayinit_with_range()
|
||||
arrayassign()
|
||||
label:
|
||||
str @shared name = "name"
|
||||
ubyte @shared bytevar
|
||||
uword[] @shared array = [name, label, start, main, 9999]
|
||||
uword[] @shared array2 = [&name, &label, &start, &main, 9999]
|
||||
uword[] @shared array3 = [cx16.r0] ; error, is variables
|
||||
uword[] @shared array4 = [bytevar] ; error, is variables
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user