Skip to content

Commit 8a26b7b

Browse files
committed
- fixed lookup of members in structs defined in another scope
- preserve order of variable definitions in the Ast (and thus, the output)
1 parent 87c28cf commit 8a26b7b

File tree

11 files changed

+113
-97
lines changed

11 files changed

+113
-97
lines changed

compiler/src/prog8/ast/AstToplevel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,4 @@ object BuiltinFunctionScopePlaceholder : INameScope {
105105

106106

107107
// prefix for struct member variables
108-
internal fun mangledStructMemberName(varName: String, memberName: String) = "_prog8struct_${varName}_$memberName"
108+
internal fun mangledStructMemberName(varName: String, memberName: String) = "prog8struct_${varName}_$memberName"

compiler/src/prog8/ast/Interfaces.kt

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -132,16 +132,17 @@ interface INameScope {
132132

133133
fun lookup(scopedName: List<String>, localContext: Node) : IStatement? {
134134
if(scopedName.size>1) {
135-
// it's a qualified name, can either be:
136-
// - the name of a field in a struct
137-
// - the name of a symbol somewhere else starting from the root of the namespace.
138-
139-
// check struct first
140-
if(scopedName.size==2) { // TODO support for referencing structs in other scopes . see GlobalNamespace?
141-
val mangledname = mangledStructMemberName(scopedName[0], scopedName[1])
142-
val vardecl = localContext.definingScope().getLabelOrVariable(mangledname)
143-
if(vardecl!=null)
144-
return vardecl
135+
// a scoped name can a) refer to a member of a struct, or b) refer to a name in another module.
136+
// try the struct first.
137+
val thing = lookup(scopedName.dropLast(1), localContext) as? VarDecl
138+
val struct = thing?.struct
139+
if (struct != null) {
140+
if(struct.statements.any { (it as VarDecl).name == scopedName.last()}) {
141+
// return ref to the mangled name variable
142+
val mangled = mangledStructMemberName(thing.name, scopedName.last())
143+
val mangledVar = thing.definingScope().getLabelOrVariable(mangled)
144+
return mangledVar
145+
}
145146
}
146147

147148
// it's a qualified name, look it up from the root of the module's namespace (consider all modules in the program)

compiler/src/prog8/ast/base/Base.kt

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,34 @@ import prog8.ast.Node
55
/**************************** AST Data classes ****************************/
66

77
enum class DataType {
8-
UBYTE,
9-
BYTE,
10-
UWORD,
11-
WORD,
12-
FLOAT,
13-
STR,
14-
STR_S,
15-
ARRAY_UB,
16-
ARRAY_B,
17-
ARRAY_UW,
18-
ARRAY_W,
19-
ARRAY_F,
20-
STRUCT;
8+
UBYTE, // pass by value
9+
BYTE, // pass by value
10+
UWORD, // pass by value
11+
WORD, // pass by value
12+
FLOAT, // pass by value
13+
STR, // pass by reference
14+
STR_S, // pass by reference
15+
ARRAY_UB, // pass by reference
16+
ARRAY_B, // pass by reference
17+
ARRAY_UW, // pass by reference
18+
ARRAY_W, // pass by reference
19+
ARRAY_F, // pass by reference
20+
STRUCT; // pass by reference
2121

2222
/**
2323
* is the type assignable to the given other type?
2424
*/
2525
infix fun isAssignableTo(targetType: DataType) =
2626
// what types are assignable to others without loss of precision?
2727
when(this) {
28-
UBYTE -> targetType == UBYTE || targetType == UWORD || targetType==WORD || targetType == FLOAT
29-
BYTE -> targetType == BYTE || targetType == UBYTE || targetType == UWORD || targetType==WORD || targetType == FLOAT
30-
UWORD -> targetType == UWORD || targetType == FLOAT
31-
WORD -> targetType == WORD || targetType==UWORD || targetType == FLOAT
28+
UBYTE -> targetType in setOf(UBYTE, UWORD, WORD, FLOAT)
29+
BYTE -> targetType in setOf(BYTE, UBYTE, UWORD, WORD, FLOAT)
30+
UWORD -> targetType in setOf(UWORD, FLOAT)
31+
WORD -> targetType in setOf(WORD, UWORD, FLOAT)
3232
FLOAT -> targetType == FLOAT
3333
STR -> targetType == STR || targetType==STR_S
3434
STR_S -> targetType == STR || targetType==STR_S
35-
in ArrayDatatypes -> targetType === this
35+
in ArrayDatatypes -> targetType == this
3636
else -> false
3737
}
3838

@@ -97,17 +97,19 @@ enum class VarDeclType {
9797
MEMORY
9898
}
9999

100-
val IterableDatatypes = setOf(
101-
DataType.STR, DataType.STR_S,
102-
DataType.ARRAY_UB, DataType.ARRAY_B,
103-
DataType.ARRAY_UW, DataType.ARRAY_W,
104-
DataType.ARRAY_F)
105100
val ByteDatatypes = setOf(DataType.UBYTE, DataType.BYTE)
106101
val WordDatatypes = setOf(DataType.UWORD, DataType.WORD)
107102
val IntegerDatatypes = setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
108103
val NumericDatatypes = setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
109104
val StringDatatypes = setOf(DataType.STR, DataType.STR_S)
110105
val ArrayDatatypes = setOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F)
106+
val IterableDatatypes = setOf(
107+
DataType.STR, DataType.STR_S,
108+
DataType.ARRAY_UB, DataType.ARRAY_B,
109+
DataType.ARRAY_UW, DataType.ARRAY_W,
110+
DataType.ARRAY_F)
111+
val PassByValueDatatypes = NumericDatatypes
112+
val PassByReferenceDatatypes = IterableDatatypes.plus(DataType.STRUCT)
111113
val ArrayElementTypes = mapOf(
112114
DataType.ARRAY_B to DataType.BYTE,
113115
DataType.ARRAY_UB to DataType.UBYTE,

compiler/src/prog8/ast/processing/AstChecker.kt

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ internal class AstChecker(private val program: Program,
451451
checkResult.add(ExpressionError("pointer-of operand must be the name of a heap variable", addressOf.position))
452452
else {
453453
if(variable.datatype !in ArrayDatatypes && variable.datatype !in StringDatatypes && variable.datatype!=DataType.STRUCT)
454-
checkResult.add(ExpressionError("pointer-of operand must be the name of a string or array heap variable", addressOf.position))
454+
checkResult.add(ExpressionError("invalid pointer-of operand type", addressOf.position))
455455
}
456456
if(addressOf.scopedname==null)
457457
throw FatalAstException("the scopedname of AddressOf should have been set by now $addressOf")
@@ -858,8 +858,8 @@ internal class AstChecker(private val program: Program,
858858
for (arg in args.withIndex().zip(target.parameters)) {
859859
val argDt = arg.first.value.inferType(program)
860860
if(argDt!=null && !(argDt isAssignableTo arg.second.type)) {
861-
// for asm subroutines having STR param it's okay to provide a UWORD too (pointer value)
862-
if(!(target.isAsmSubroutine && arg.second.type in StringDatatypes && argDt== DataType.UWORD))
861+
// for asm subroutines having STR param it's okay to provide a UWORD (address value)
862+
if(!(target.isAsmSubroutine && arg.second.type in StringDatatypes && argDt == DataType.UWORD))
863863
checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.type}", position))
864864
}
865865

@@ -1270,8 +1270,13 @@ internal class AstChecker(private val program: Program,
12701270
}
12711271
else if(sourceDatatype== DataType.FLOAT && targetDatatype in IntegerDatatypes)
12721272
checkResult.add(ExpressionError("cannot assign float to ${targetDatatype.name.toLowerCase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position))
1273-
else
1274-
checkResult.add(ExpressionError("cannot assign ${sourceDatatype.name.toLowerCase()} to ${targetDatatype.name.toLowerCase()}", position))
1273+
else {
1274+
if(targetDatatype==DataType.UWORD && sourceDatatype in PassByReferenceDatatypes)
1275+
checkResult.add(ExpressionError("cannot assign ${sourceDatatype.name.toLowerCase()} to ${targetDatatype.name.toLowerCase()}, perhaps forgot '&' ?", position))
1276+
else
1277+
checkResult.add(ExpressionError("cannot assign ${sourceDatatype.name.toLowerCase()} to ${targetDatatype.name.toLowerCase()}", position))
1278+
}
1279+
12751280

12761281
return false
12771282
}

compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -65,24 +65,8 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
6565
if(decl.struct!!.statements.any { (it as VarDecl).datatype !in NumericDatatypes})
6666
return decl // a non-numeric member, not supported. proper error is given by AstChecker later
6767

68-
val decls: MutableList<IStatement> = decl.struct!!.statements.withIndex().map {
69-
val member = it.value as VarDecl
70-
val initvalue = if(decl.value!=null) (decl.value as LiteralValue).arrayvalue!![it.index] else null
71-
VarDecl(
72-
VarDeclType.VAR,
73-
member.datatype,
74-
ZeropageWish.NOT_IN_ZEROPAGE,
75-
member.arraysize,
76-
mangledStructMemberName(decl.name, member.name),
77-
decl.struct!!.name,
78-
initvalue,
79-
member.isArray,
80-
true,
81-
member.position
82-
)
83-
}.toMutableList()
68+
val decls = decl.flattenStructMembers()
8469
decls.add(decl)
85-
decl.structHasBeenFlattened = true
8670
val result = AnonymousScope(decls, decl.position)
8771
result.linkParents(decl.parent)
8872
return result

compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,17 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
2323

2424
// Also takes care to insert AddressOf (&) expression where required (string params to a UWORD function param etc).
2525

26-
private val vardeclsToAdd = mutableMapOf<INameScope, MutableMap<String, VarDecl>>()
26+
private val vardeclsToAdd = mutableMapOf<INameScope, MutableList<VarDecl>>()
2727

2828
override fun visit(module: Module) {
2929
vardeclsToAdd.clear()
3030
super.visit(module)
3131

3232
// add any new vardecls to the various scopes
33-
for(decl in vardeclsToAdd)
34-
for(d in decl.value) {
35-
d.value.linkParents(decl.key as Node)
36-
decl.key.statements.add(0, d.value)
37-
}
33+
for((where, decls) in vardeclsToAdd) {
34+
where.statements.addAll(0, decls)
35+
decls.forEach { it.linkParents(where as Node) }
36+
}
3837
}
3938

4039
override fun visit(decl: VarDecl): IStatement {
@@ -61,25 +60,6 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
6160
decl.position
6261
)
6362
}
64-
65-
// if(decl.datatype==DataType.STRUCT) {
66-
// println("STRUCT INIT DECL $decl")
67-
// // a struct initialization value perhaps
68-
// // flatten it to assignment statements
69-
// val sourceArray = (decl.value as LiteralValue).arrayvalue!!
70-
// val memberAssignments = decl.struct!!.statements.zip(sourceArray).map { member ->
71-
// val memberDecl = member.first as VarDecl
72-
// val mangled = mangledStructMemberName(decl.name, memberDecl.name)
73-
// val idref = IdentifierReference(listOf(mangled), decl.position)
74-
// val target = AssignTarget(null, idref, null, null, decl.position)
75-
// val assign = VariableInitializationAssignment(target, null, member.second, member.second.position)
76-
// assign
77-
// }
78-
// val scope = AnonymousScope(memberAssignments.toMutableList(), decl.position)
79-
// scope.linkParents(decl.parent)
80-
// return scope
81-
// }
82-
8363
return decl
8464
}
8565

@@ -104,7 +84,7 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
10484
private fun addAddressOfExprIfNeeded(subroutine: Subroutine, arglist: MutableList<IExpression>, parent: IStatement) {
10585
// functions that accept UWORD and are given an array type, or string, will receive the AddressOf (memory location) of that value instead.
10686
for(argparam in subroutine.parameters.withIndex().zip(arglist)) {
107-
if(argparam.first.value.type== DataType.UWORD || argparam.first.value.type in StringDatatypes) {
87+
if(argparam.first.value.type==DataType.UWORD || argparam.first.value.type in StringDatatypes) {
10888
if(argparam.second is AddressOf)
10989
continue
11090
val idref = argparam.second as? IdentifierReference
@@ -139,8 +119,10 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
139119

140120
private fun addVarDecl(scope: INameScope, variable: VarDecl) {
141121
if(scope !in vardeclsToAdd)
142-
vardeclsToAdd[scope] = mutableMapOf()
143-
vardeclsToAdd.getValue(scope)[variable.name]=variable
122+
vardeclsToAdd[scope] = mutableListOf()
123+
val declList = vardeclsToAdd.getValue(scope)
124+
if(declList.all{it.name!=variable.name})
125+
declList.add(variable)
144126
}
145127

146128
}

compiler/src/prog8/ast/statements/AstStatements.kt

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,10 @@ class VarDecl(val type: VarDeclType,
154154
val hiddenButDoNotRemove: Boolean,
155155
override val position: Position) : IStatement {
156156
override lateinit var parent: Node
157-
var struct: StructDecl? = null // set later
158-
var structHasBeenFlattened = false
157+
var struct: StructDecl? = null // set later (because at parse time, we only know the name)
158+
private set
159+
var structHasBeenFlattened = false // set later
160+
private set
159161

160162
override val expensiveToInline
161163
get() = value!=null && value !is LiteralValue
@@ -202,11 +204,32 @@ class VarDecl(val type: VarDeclType,
202204
DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue = 0.0, position = position)
203205
else -> throw FatalAstException("can only set a default value for a numeric type")
204206
}
205-
val decl = VarDecl(type, declaredDatatype, zeropage, arraysize, name, null, constValue, isArray, true, position)
207+
val decl = VarDecl(type, declaredDatatype, zeropage, arraysize, name, structName, constValue, isArray, true, position)
206208
if(parent!=null)
207209
decl.linkParents(parent)
208210
return decl
209211
}
212+
213+
fun flattenStructMembers(): MutableList<IStatement> {
214+
val result = struct!!.statements.withIndex().map {
215+
val member = it.value as VarDecl
216+
val initvalue = if(value!=null) (value as LiteralValue).arrayvalue!![it.index] else null
217+
VarDecl(
218+
VarDeclType.VAR,
219+
member.datatype,
220+
ZeropageWish.NOT_IN_ZEROPAGE,
221+
member.arraysize,
222+
mangledStructMemberName(name, member.name),
223+
struct!!.name,
224+
initvalue,
225+
member.isArray,
226+
true,
227+
member.position
228+
) as IStatement
229+
}.toMutableList()
230+
structHasBeenFlattened = true
231+
return result
232+
}
210233
}
211234

212235
class ArrayIndex(var index: IExpression, override val position: Position) : Node {

compiler/src/prog8/compiler/Compiler.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2010,7 +2010,7 @@ internal class Compiler(private val program: Program) {
20102010
DataType.UBYTE -> when(sourceDt) {
20112011
DataType.UBYTE -> {}
20122012
DataType.BYTE -> prog.instr(Opcode.CAST_B_TO_UB)
2013-
DataType.UWORD-> prog.instr(Opcode.CAST_UW_TO_UB)
2013+
DataType.UWORD -> prog.instr(Opcode.CAST_UW_TO_UB)
20142014
DataType.WORD-> prog.instr(Opcode.CAST_W_TO_UB)
20152015
DataType.FLOAT -> prog.instr(Opcode.CAST_F_TO_UB)
20162016
else -> throw CompilerException("invalid cast $sourceDt to ${expr.type} -- should be an Ast check")

compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,8 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
525525
if(parameters.zp==ZeropageWish.REQUIRE_ZEROPAGE)
526526
throw CompilerException("zp conflict")
527527
val valuestr = value.toString()
528-
out.println("$vname ${value.type.name.toLowerCase()} $valuestr")
528+
val struct = if(parameters.memberOfStruct==null) "" else "struct=${parameters.memberOfStruct.name}"
529+
out.println("$vname ${value.type.name.toLowerCase()} $valuestr zp=${parameters.zp} $struct")
529530
}
530531
out.println("%end_variables")
531532
out.println("%memorypointers")

compiler/src/prog8/compiler/target/c64/AsmGen.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,10 +299,12 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
299299

300300
// these are the non-zeropage variables.
301301
// first get all the flattened struct members, they MUST remain in order
302+
out("; flattened struct members")
302303
val (structMembers, normalVars) = block.variables.partition { it.third.memberOfStruct!=null }
303304
structMembers.forEach { vardecl2asm(it.first, it.second, it.third) }
304305

305306
// leave outsort the other variables by type
307+
out("; other variables sorted by type")
306308
val sortedVars = normalVars.sortedBy { it.second.type }
307309
for ((varname, value, parameters) in sortedVars) {
308310
if(varname in block.variablesMarkedForZeropage)

0 commit comments

Comments
 (0)