mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
1 parent
ccc26a0eb2
commit
af14562e2b
9 files changed
+133
-36
No files matched your search
@@ -2142,7 +2142,7 @@ internal class AstChecker(private val program: Program,
|
||||
val deref = arrayIndexedExpression.pointerderef!!
|
||||
val target = deref.definingScope.lookup(deref.chain)
|
||||
val arraysize: Int? = when(target) {
|
||||
is StructFieldRef -> target.field.arraySize
|
||||
is StructFieldRef -> target.field.constSize()
|
||||
is VarDecl -> target.arraysize?.constIndex()
|
||||
else -> null
|
||||
}
|
||||
@@ -2342,10 +2342,17 @@ internal class AstChecker(private val program: Program,
|
||||
|| dt.isPointer
|
||||
|| dt.isArray
|
||||
}
|
||||
|
||||
|
||||
struct.fields.forEach { field ->
|
||||
if(!validFieldType(field))
|
||||
errors.err("invalid struct field type at '${field.name}'", struct.position)
|
||||
if(field.isArray) {
|
||||
val size = field.constSize()
|
||||
if(size==null)
|
||||
errors.err("array length must be known at compile-time", field.arraySize?.position ?: struct.position)
|
||||
else if(size<=0)
|
||||
errors.err("array length must be positive", field.arraySize?.position ?: struct.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2747,7 +2754,7 @@ internal class AstChecker(private val program: Program,
|
||||
val struct = initializer.structname.targetStructDecl()
|
||||
if(struct!=null) {
|
||||
val expectedFieldCount = struct.fields.size
|
||||
val expectedFlattenedElements = struct.fields.sumOf { it.arraySize ?: 1 }
|
||||
val expectedFlattenedElements = struct.fields.sumOf { it.constSize() ?: 1 }
|
||||
val flattenedArgs = args.flattenArgs()
|
||||
|
||||
if (args.size != expectedFieldCount) {
|
||||
|
||||
@@ -528,9 +528,9 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
private fun transform(initializer: StaticStructInitializer): PtFunctionCall {
|
||||
val targetStruct = initializer.structname.targetStructDecl()!!
|
||||
val call = PtFunctionCall("prog8_lib_structalloc", true, true, arrayOf(DataType.pointer(targetStruct)), initializer.position)
|
||||
|
||||
|
||||
val flattenedArgs = initializer.args.flattenArgs()
|
||||
val expectedTotalElements = targetStruct.fields.sumOf { it.arraySize ?: 1 }
|
||||
val expectedTotalElements = targetStruct.fields.sumOf { it.constSize() ?: 1 }
|
||||
if (flattenedArgs.isNotEmpty() && flattenedArgs.size != expectedTotalElements) {
|
||||
errors.err("invalid number of field values: expected $expectedTotalElements flattened elements for struct '${targetStruct.name}' but got ${flattenedArgs.size}", initializer.position)
|
||||
}
|
||||
@@ -817,7 +817,12 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
}
|
||||
|
||||
private fun transform(struct: StructDecl): PtStructDecl {
|
||||
return PtStructDecl(struct.name, struct.fields.map { PtStructField(it.type, it.name, it.arraySize) }, struct.position)
|
||||
return PtStructDecl(struct.name, struct.fields.map {
|
||||
val size = it.constSize()
|
||||
if(it.isArray && size==null)
|
||||
errors.err("array length must be known at compile-time", it.arraySize?.position ?: struct.position)
|
||||
PtStructField(it.type, it.name, size)
|
||||
}, struct.position)
|
||||
}
|
||||
|
||||
private fun transform(srcWhen: When): PtWhen {
|
||||
|
||||
@@ -695,6 +695,73 @@ main {
|
||||
errors.printedErrors.any { it.contains("2D arrays are not allowed as struct fields") } shouldBe true
|
||||
}
|
||||
|
||||
test("struct field array size can be const expression") {
|
||||
val src = """
|
||||
main {
|
||||
const ubyte SIZE = 5
|
||||
struct Node {
|
||||
ubyte[SIZE] data
|
||||
uword top
|
||||
}
|
||||
sub start() {
|
||||
^^Node n = [ [1,2,3,4,5], 9999 ]
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(VMTarget(), false, src, outputDir, errors, false)
|
||||
errors.errors shouldBe emptyList()
|
||||
}
|
||||
|
||||
test("struct field array size can be folded constant expression") {
|
||||
val src = """
|
||||
main {
|
||||
const ubyte BASE = 3
|
||||
struct Node {
|
||||
ubyte[BASE*2] data
|
||||
}
|
||||
sub start() {
|
||||
^^Node n = [ [1,2,3,4,5,6] ]
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(VMTarget(), false, src, outputDir, errors, false)
|
||||
errors.errors shouldBe emptyList()
|
||||
}
|
||||
|
||||
test("struct field array size rejects non-constant expression") {
|
||||
val src = """
|
||||
main {
|
||||
uword dyn
|
||||
struct Node {
|
||||
uword[dyn] data
|
||||
}
|
||||
sub start() {
|
||||
dyn = 5
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(VMTarget(), false, src, outputDir, errors) shouldBe null
|
||||
errors.errors.any { it.contains("array length must be known at compile-time") } shouldBe true
|
||||
}
|
||||
|
||||
test("struct field array size rejects zero size") {
|
||||
val src = """
|
||||
main {
|
||||
const ubyte SIZE = 0
|
||||
struct Node {
|
||||
ubyte[SIZE] data
|
||||
}
|
||||
sub start() { }
|
||||
}
|
||||
""".trimIndent()
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(VMTarget(), false, src, outputDir, errors) shouldBe null
|
||||
errors.errors.any { it.contains("array length must be positive") } shouldBe true
|
||||
}
|
||||
|
||||
test("variable declaration followed by statement on same line should give error") {
|
||||
val src = """
|
||||
main {
|
||||
|
||||
@@ -194,8 +194,13 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
|
||||
output("private ")
|
||||
outputln("struct ${struct.name} {")
|
||||
for(member in struct.fields) {
|
||||
val arraySuffix = if(member.isArray) "[${member.arraySize}]" else ""
|
||||
outputlni( " ${member.type}$arraySuffix ${member.name}")
|
||||
outputi(" ${member.type}")
|
||||
if(member.isArray) {
|
||||
output("[")
|
||||
member.arraySize?.indexExpr?.accept(this)
|
||||
output("]")
|
||||
}
|
||||
outputln(" ${member.name}")
|
||||
}
|
||||
outputlni("}")
|
||||
}
|
||||
|
||||
@@ -814,11 +814,11 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
|
||||
override fun visitStructdeclaration(ctx: StructdeclarationContext): StructDecl {
|
||||
val name = getname(ctx.identifier())
|
||||
val fieldDefs = ctx.structfielddecl().map { getStructField(it) }
|
||||
val flattened = fieldDefs.flatMap { (dt, names, arrSize) -> names.map { StructField(dt, it, arrSize) }}
|
||||
val flattened = fieldDefs.flatMap { (dt, names, arrSize) -> names.map { StructField(dt, it, arrSize?.copy()) }}
|
||||
return StructDecl(name, flattened.toTypedArray(), ctx.PRIVATE() != null, ctx.toPosition())
|
||||
}
|
||||
|
||||
private data class StructFieldDef(val type: DataType, val names: List<String>, val arraySize: Int?)
|
||||
private data class StructFieldDef(val type: DataType, val names: List<String>, val arraySize: ArrayIndex?)
|
||||
|
||||
private fun getStructField(ctx: StructfielddeclContext): StructFieldDef {
|
||||
val identifiers = ctx.identifierlist()?.identifier() ?: emptyList()
|
||||
@@ -833,9 +833,7 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
|
||||
throw SyntaxError("2D arrays are not allowed as struct fields", ctx.toPosition())
|
||||
val dt = baseDt.elementToArray()
|
||||
val arrayIndexExpr = arrayIndices[0].accept(this) as ArrayIndex
|
||||
val size = (arrayIndexExpr.indexExpr as? NumericLiteral)?.number?.toInt()
|
||||
?: throw SyntaxError("array field size must be a constant integer expression", ctx.toPosition())
|
||||
dt to size
|
||||
dt to arrayIndexExpr
|
||||
} else {
|
||||
baseDt to null
|
||||
}
|
||||
|
||||
@@ -517,20 +517,37 @@ class StructDecl(override val name: String, val fields: Array<StructField>, val
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
fields.forEach { it.arraySize?.linkParents(this) }
|
||||
}
|
||||
|
||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||
override fun referencesIdentifier(nameInSource: List<String>) = false
|
||||
override fun copy() = StructDecl(name, fields.clone(), isPrivate, position)
|
||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||
require(replacement is ArrayIndex)
|
||||
val field = fields.firstOrNull { it.arraySize === node }
|
||||
?: throw FatalAstException("invalid replace in $this at ${node.position}: $node -> $replacement")
|
||||
field.arraySize = replacement
|
||||
replacement.linkParents(this)
|
||||
}
|
||||
override fun referencesIdentifier(nameInSource: List<String>) = fields.any { it.arraySize?.referencesIdentifier(nameInSource)==true }
|
||||
override fun copy() = StructDecl(name, fields.map { it.copy() }.toTypedArray(), isPrivate, position)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||
override fun memsize(sizer: IMemSizer): Int = fields.sumOf { field ->
|
||||
if(field.isArray)
|
||||
sizer.memorySize(field.type, field.arraySize!!)
|
||||
sizer.memorySize(field.type, field.constSize() ?: 1)
|
||||
else
|
||||
sizer.memorySize(field.type, 1)
|
||||
}
|
||||
override fun sameas(other: ISubType): Boolean = other is StructDecl && other.name==name && other.fields.contentEquals(fields)
|
||||
override fun sameas(other: ISubType): Boolean {
|
||||
if(other !is StructDecl || other.name!=name || other.fields.size!=fields.size)
|
||||
return false
|
||||
return fields.zip(other.fields).all { (a, b) ->
|
||||
a.name==b.name && a.type==b.type && when {
|
||||
a.arraySize==null && b.arraySize==null -> true
|
||||
a.arraySize!=null && b.arraySize!=null -> a.arraySize!! isSameAs b.arraySize!!
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getFieldType(name: String): DataType? = fields.firstOrNull { it.name==name }?.type
|
||||
fun getField(name: String): StructField? = fields.firstOrNull { it.name==name }
|
||||
@@ -540,7 +557,7 @@ class StructDecl(override val name: String, val fields: Array<StructField>, val
|
||||
fields.fold(0) { offset, field ->
|
||||
if (field.name == fieldname)
|
||||
return offset.toUByte()
|
||||
val numElements = if(field.isArray) field.arraySize!! else 1
|
||||
val numElements = if(field.isArray) field.constSize() ?: 1 else 1
|
||||
offset + sizer.memorySize(field.type, numElements)
|
||||
}
|
||||
return null
|
||||
@@ -548,8 +565,10 @@ class StructDecl(override val name: String, val fields: Array<StructField>, val
|
||||
}
|
||||
|
||||
|
||||
data class StructField(val type: DataType, val name: String, val arraySize: Int? = null) {
|
||||
data class StructField(val type: DataType, val name: String, var arraySize: ArrayIndex? = null) {
|
||||
val isArray: Boolean get() = arraySize != null
|
||||
fun constSize(): Int? = arraySize?.constIndex()
|
||||
fun copy(): StructField = StructField(type, name, arraySize?.copy())
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -380,6 +380,7 @@ abstract class AstWalker {
|
||||
|
||||
fun visit(struct: StructDecl, parent: Node) {
|
||||
track(before(struct, parent), struct, parent)
|
||||
struct.fields.forEach { it.arraySize?.accept(this) }
|
||||
track(after(struct, parent), struct, parent)
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ interface IAstVisitor {
|
||||
}
|
||||
|
||||
fun visit(struct: StructDecl) {
|
||||
struct.fields.forEach { it.arraySize?.accept(this) }
|
||||
}
|
||||
|
||||
fun visit(field: StructFieldRef) {
|
||||
|
||||
+10
-16
@@ -1,20 +1,14 @@
|
||||
main {
|
||||
const uword STACK_SIZE = 10
|
||||
struct Stack {
|
||||
uword[STACK_SIZE] stack1
|
||||
uword[20] stack2
|
||||
uword top
|
||||
}
|
||||
|
||||
sub start() {
|
||||
cx16.r1 = private.foo()
|
||||
cx16.r2 = mkword.foo()
|
||||
}
|
||||
}
|
||||
|
||||
private {
|
||||
sub foo() -> uword {
|
||||
cx16.r0++
|
||||
return cx16.r0
|
||||
}
|
||||
}
|
||||
|
||||
mkword {
|
||||
sub foo() -> uword {
|
||||
cx16.r0++
|
||||
return cx16.r0
|
||||
^^Stack st = []
|
||||
st.stack1[5] = 9999
|
||||
st.stack2[5] = 8888
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user