diff --git a/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala b/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala index 9983b2d6..8dedf4ec 100644 --- a/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala +++ b/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala @@ -366,16 +366,17 @@ object AbstractExpressionCompiler { val (actualFieldName, pointerWrap): (String, Int) = getActualFieldNameAndPointerWrap(fieldName) currentType match { case PointerType(_, _, Some(targetType)) => - val tuples = env.getSubvariables(targetType).filter(x => x._1 == "." + actualFieldName) + val tuples = env.getSubvariables(targetType).filter(x => x.suffix == "." + actualFieldName) if (tuples.isEmpty) { log.error(s"Type `$targetType` doesn't have field named `$actualFieldName`", expr.position) ok = false } else { + if (tuples.head.arraySize.isDefined) ??? // TODO pointerWrap match { case 0 => - currentType = tuples.head._3 + currentType = tuples.head.typ case 1 => - currentType = env.get[Type]("pointer." + tuples.head._3) + currentType = env.get[Type]("pointer." + tuples.head.typ) case 2 => currentType = env.get[Type]("pointer") case 10 | 11 => diff --git a/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala b/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala index af0f8c9f..4e4be85a 100644 --- a/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala +++ b/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala @@ -390,15 +390,16 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte val currentResultType = AbstractExpressionCompiler.getExpressionType(env, env.log, result) result = currentResultType match { case PointerType(_, _, Some(target)) => - val subvariables = env.getSubvariables(target).filter(x => x._1 == "." + actualFieldName) + val subvariables = env.getSubvariables(target).filter(x => x.suffix == "." + actualFieldName) if (subvariables.isEmpty) { ctx.log.error(s"Type `${target.name}` does not contain field `$actualFieldName`", result.position) ok = false LiteralExpression(0, 1) } else { + if (subvariables.head.arraySize.isDefined) ??? // TODO val inner = optimizeExpr(result, currentVarValues, optimizeSum = true).pos(pos) - val fieldOffset = subvariables.head._2 - val fieldType = subvariables.head._3 + val fieldOffset = subvariables.head.offset + val fieldType = subvariables.head.typ pointerWrap match { case 0 => DerefExpression(inner, fieldOffset, fieldType) diff --git a/src/main/scala/millfork/env/Constant.scala b/src/main/scala/millfork/env/Constant.scala index 49524512..51b05e6d 100644 --- a/src/main/scala/millfork/env/Constant.scala +++ b/src/main/scala/millfork/env/Constant.scala @@ -1,6 +1,7 @@ package millfork.env import millfork.DecimalUtils._ +import millfork.node.ResolvedFieldDesc import millfork.output.DivisibleAlignment object Constant { @@ -167,25 +168,27 @@ case class StructureConstant(typ: StructType, fields: List[Constant]) extends Co override def subbyte(index: Int): Constant = { var offset = 0 - for ((fv, (ft, _)) <- fields.zip(typ.mutableFieldsWithTypes)) { + for ((fv, ResolvedFieldDesc(ft, _, arraySize)) <- fields.zip(typ.mutableFieldsWithTypes)) { + // TODO: handle array members? val fs = ft.size if (index < offset + fs) { val indexInField = index - offset return fv.subbyte(indexInField) } - offset += fs + offset += fs * arraySize.getOrElse(1) } Constant.Zero } override def subbyteBe(index: Int, totalSize: Int): Constant = { var offset = 0 - for ((fv, (ft, _)) <- fields.zip(typ.mutableFieldsWithTypes)) { + for ((fv, ResolvedFieldDesc(ft, _, arraySize)) <- fields.zip(typ.mutableFieldsWithTypes)) { + // TODO: handle array members? val fs = ft.size if (index < offset + fs) { val indexInField = index - offset return fv.subbyteBe(indexInField, fs) } - offset += fs + offset += fs * arraySize.getOrElse(1) } Constant.Zero } diff --git a/src/main/scala/millfork/env/Environment.scala b/src/main/scala/millfork/env/Environment.scala index 63dd42fa..445e8534 100644 --- a/src/main/scala/millfork/env/Environment.scala +++ b/src/main/scala/millfork/env/Environment.scala @@ -518,8 +518,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa BranchingOpcodeMapping(Opcode.BPL, IfFlagClear(ZFlag.S), MOpcode.BPL), BranchingOpcodeMapping(Opcode.BMI, IfFlagSet(ZFlag.S), MOpcode.BMI)), None) - val byte_and_pointer$ = StructType("byte_and_pointer$", List(FieldDesc("byte", "zp"), FieldDesc("pointer", "branch"))) - val hudson_transfer$ = StructType("hudson_transfer$", List(FieldDesc("word", "a"), FieldDesc("word", "b"), FieldDesc("word", "c"))) + val byte_and_pointer$ = StructType("byte_and_pointer$", List(FieldDesc("byte", "zp", None), FieldDesc("pointer", "branch", None))) + val hudson_transfer$ = StructType("hudson_transfer$", List(FieldDesc("word", "a", None), FieldDesc("word", "b", None), FieldDesc("word", "c", None))) addThing(byte_and_pointer$, None) addThing(hudson_transfer$, None) builtinsAdded = true @@ -960,17 +960,17 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa addThing(UnionType(stmt.name, stmt.fields), stmt.position) } - def getTypeSize(name: String, path: Set[String]): Int = { + def getTypeSize(t: VariableType, path: Set[String]): Int = { + val name = t.name if (path.contains(name)) return -1 - val t = get[Type](name) t match { case s: StructType => if (s.mutableSize >= 0) s.mutableSize else { val newPath = path + name var sum = 0 - for( FieldDesc(fieldType, _) <- s.fields) { - val fieldSize = getTypeSize(fieldType, newPath) + for( ResolvedFieldDesc(fieldType, _, count) <- s.mutableFieldsWithTypes) { + val fieldSize = getTypeSize(fieldType, newPath) * count.getOrElse(1) if (fieldSize < 0) return -1 sum += fieldSize } @@ -980,9 +980,9 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa } val b = get[Type]("byte") var offset = 0 - for( FieldDesc(fieldType, fieldName) <- s.fields) { + for( ResolvedFieldDesc(fieldType, fieldName, count) <- s.mutableFieldsWithTypes) { addThing(ConstantThing(s"$name.$fieldName.offset", NumericConstant(offset, 1), b), None) - offset += getTypeSize(fieldType, newPath) + offset += getTypeSize(fieldType, newPath) * count.getOrElse(1) } sum } @@ -991,8 +991,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa else { val newPath = path + name var max = 0 - for( FieldDesc(fieldType, _) <- s.fields) { - val fieldSize = getTypeSize(fieldType, newPath) + for( ResolvedFieldDesc(fieldType, _, count) <- s.mutableFieldsWithTypes) { + val fieldSize = getTypeSize(fieldType, newPath) * count.getOrElse(1) if (fieldSize < 0) return -1 max = max max fieldSize } @@ -1001,7 +1001,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa log.error(s"Union `$name` is larger than 255 bytes") } val b = get[Type]("byte") - for (FieldDesc(fieldType, fieldName) <- s.fields) { + for (ResolvedFieldDesc(fieldType, fieldName, _) <- s.mutableFieldsWithTypes) { addThing(ConstantThing(s"$name.$fieldName.offset", NumericConstant(0, 1), b), None) } max @@ -1361,7 +1361,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa addThing(v, stmt.position) registerAddressConstant(v, stmt.position, options, Some(typ)) val addr = v.toAddress - for((suffix, offset, t) <- getSubvariables(typ)) { + for(Subvariable(suffix, offset, t, arraySize) <- getSubvariables(typ)) { + if (arraySize.isDefined) ??? // TODO val subv = RelativeVariable(v.name + suffix, addr + offset, t, zeropage = zp, None, isVolatile = v.isVolatile) addThing(subv, stmt.position) registerAddressConstant(subv, stmt.position, options, Some(t)) @@ -1403,7 +1404,10 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa List.fill(tt.size)(LiteralExpression(0, 1)) } else { tt.fields.zip(fieldValues).flatMap { - case (FieldDesc(fieldTypeName, _), expr) => extractStructArrayContents(expr, Some(get[Type](fieldTypeName))) + case (FieldDesc(fieldTypeName, _, count), expr) => + // TODO: handle array fields + if (count.isDefined) ??? + extractStructArrayContents(expr, Some(get[Type](fieldTypeName))) } } case _ => @@ -1437,7 +1441,10 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa List.fill(tt.size)(LiteralExpression(0, 1)) } else { tt.fields.zip(fieldValues).flatMap { - case (FieldDesc(fieldTypeName, _), expr) => extractStructArrayContents(expr, Some(get[Type](fieldTypeName))) + case (FieldDesc(fieldTypeName, _, count), expr) => + // TODO: handle array fields + if (count.isDefined) ??? + extractStructArrayContents(expr, Some(get[Type](fieldTypeName))) } } case _ => @@ -1697,7 +1704,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa val constantValue: Constant = stmt.initialValue.flatMap(eval).getOrElse(errorConstant(s"`$name` has a non-constant value", position)).fitInto(typ) if (constantValue.requiredSize > typ.size) log.error(s"`$name` is has an invalid value: not in the range of `$typ`", position) addThing(ConstantThing(prefix + name, constantValue, typ), stmt.position) - for((suffix, offset, t) <- getSubvariables(typ)) { + for(Subvariable(suffix, offset, t, arraySize) <- getSubvariables(typ)) { + if (arraySize.isDefined) ??? // TODO addThing(ConstantThing(prefix + name + suffix, constantValue.subconstant(offset, t.size), t), stmt.position) } } else { @@ -1763,145 +1771,165 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa variable match { case v: StackVariable => addThing(localName, v, position) - for ((suffix, offset, t) <- getSubvariables(v.typ)) { - addThing(StackVariable(prefix + localName + suffix, t, baseStackOffset + offset), position) + for (Subvariable(suffix, offset, t, arraySize) <- getSubvariables(v.typ)) { + if (arraySize.isDefined) { + log.error(s"Cannot create a stack variable $localName of compound type ${v.typ.name} that contains an array member") + } else { + addThing(StackVariable(prefix + localName + suffix, t, baseStackOffset + offset), position) + } } case v: MemoryVariable => addThing(localName, v, position) - for ((suffix, offset, t) <- getSubvariables(v.typ)) { - val subv = RelativeVariable(prefix + localName + suffix, v.toAddress + offset, t, zeropage = v.zeropage, declaredBank = v.declaredBank, isVolatile = v.isVolatile) - addThing(subv, position) - registerAddressConstant(subv, position, options, Some(t)) + for (Subvariable(suffix, offset, t, arraySize) <- getSubvariables(v.typ)) { + arraySize match { + case None => + val subv = RelativeVariable(prefix + localName + suffix, v.toAddress + offset, t, zeropage = v.zeropage, declaredBank = v.declaredBank, isVolatile = v.isVolatile) + addThing(subv, position) + registerAddressConstant(subv, position, options, Some(t)) + case Some(_) => + ??? // TODO + } } case v: VariableInMemory => addThing(localName, v, position) addThing(ConstantThing(v.name + "`", v.toAddress, get[Type]("word")), position) - for ((suffix, offset, t) <- getSubvariables(v.typ)) { - val subv = RelativeVariable(prefix + localName + suffix, v.toAddress + offset, t, zeropage = v.zeropage, declaredBank = v.declaredBank, isVolatile = v.isVolatile) - addThing(subv, position) - registerAddressConstant(subv, position, options, Some(t)) + for (Subvariable(suffix, offset, t, arraySize) <- getSubvariables(v.typ)) { + arraySize match { + case None => + val subv = RelativeVariable(prefix + localName + suffix, v.toAddress + offset, t, zeropage = v.zeropage, declaredBank = v.declaredBank, isVolatile = v.isVolatile) + addThing(subv, position) + registerAddressConstant(subv, position, options, Some(t)) + case Some(_) => + ??? // TODO + } } case _ => ??? } } - def getSubvariables(typ: Type): List[(String, Int, VariableType)] = { + def getSubvariables(typ: Type): List[Subvariable] = { val b = get[VariableType]("byte") val w = get[VariableType]("word") if (typ.name == "__reg$type") { if (options.isBigEndian) { throw new IllegalArgumentException("__reg$type on 6809???") } - return (".lo", 0, b) :: - (".hi", 1, b) :: - (".loword", 0, w) :: - (".loword.lo", 0, b) :: - (".loword.hi", 1, b) :: - (".b2b3", 2, w) :: - (".b2b3.lo", 2, b) :: - (".b2b3.hi", 3, b) :: - List.tabulate(typ.size) { i => (".b" + i, i, b) } + return Subvariable(".lo", 0, b) :: + Subvariable(".hi", 1, b) :: + Subvariable(".loword", 0, w) :: + Subvariable(".loword.lo", 0, b) :: + Subvariable(".loword.hi", 1, b) :: + Subvariable(".b2b3", 2, w) :: + Subvariable(".b2b3.lo", 2, b) :: + Subvariable(".b2b3.hi", 3, b) :: + List.tabulate(typ.size) { i => Subvariable(".b" + i, i, b) } } typ match { case _: PlainType => typ.size match { case 2 => if (options.isBigEndian) List( - (".lo", 1, b), - (".hi", 0, b) + Subvariable(".lo", 1, b), + Subvariable(".hi", 0, b) ) else List( - (".lo", 0, b), - (".hi", 1, b)) + Subvariable(".lo", 0, b), + Subvariable(".hi", 1, b)) case 3 => if (options.isBigEndian) List( - (".loword", 1, w), - (".loword.lo", 2, b), - (".loword.hi", 1, b), - (".hiword", 0, w), - (".hiword.lo", 1, b), - (".hiword.hi", 0, b), - (".b0", 2, b), - (".b1", 1, b), - (".b2", 0, b) + Subvariable(".loword", 1, w), + Subvariable(".loword.lo", 2, b), + Subvariable(".loword.hi", 1, b), + Subvariable(".hiword", 0, w), + Subvariable(".hiword.lo", 1, b), + Subvariable(".hiword.hi", 0, b), + Subvariable(".b0", 2, b), + Subvariable(".b1", 1, b), + Subvariable(".b2", 0, b) ) else List( - (".loword", 0, w), - (".loword.lo", 0, b), - (".loword.hi", 1, b), - (".hiword", 1, w), - (".hiword.lo", 1, b), - (".hiword.hi", 2, b), - (".b0", 0, b), - (".b1", 1, b), - (".b2", 2, b)) + Subvariable(".loword", 0, w), + Subvariable(".loword.lo", 0, b), + Subvariable(".loword.hi", 1, b), + Subvariable(".hiword", 1, w), + Subvariable(".hiword.lo", 1, b), + Subvariable(".hiword.hi", 2, b), + Subvariable(".b0", 0, b), + Subvariable(".b1", 1, b), + Subvariable(".b2", 2, b)) case 4 => if (options.isBigEndian) List( - (".loword", 2, w), - (".hiword", 0, w), - (".loword.lo", 3, b), - (".loword.hi", 2, b), - (".hiword.lo", 1, b), - (".hiword.hi", 0, b), - (".b0", 3, b), - (".b1", 2, b), - (".b2", 1, b), - (".b3", 0, b) + Subvariable(".loword", 2, w), + Subvariable(".hiword", 0, w), + Subvariable(".loword.lo", 3, b), + Subvariable(".loword.hi", 2, b), + Subvariable(".hiword.lo", 1, b), + Subvariable(".hiword.hi", 0, b), + Subvariable(".b0", 3, b), + Subvariable(".b1", 2, b), + Subvariable(".b2", 1, b), + Subvariable(".b3", 0, b) ) else List( - (".loword", 0, w), - (".hiword", 2, w), - (".loword.lo", 0, b), - (".loword.hi", 1, b), - (".hiword.lo", 2, b), - (".hiword.hi", 3, b), - (".b0", 0, b), - (".b1", 1, b), - (".b2", 2, b), - (".b3", 3, b) + Subvariable(".loword", 0, w), + Subvariable(".hiword", 2, w), + Subvariable(".loword.lo", 0, b), + Subvariable(".loword.hi", 1, b), + Subvariable(".hiword.lo", 2, b), + Subvariable(".hiword.hi", 3, b), + Subvariable(".b0", 0, b), + Subvariable(".b1", 1, b), + Subvariable(".b2", 2, b), + Subvariable(".b3", 3, b) ) case sz if sz > 4 => if (options.isBigEndian) { - (".lo", sz - 1, b) :: - (".loword", sz - 2, w) :: - (".loword.lo", sz - 1, b) :: - (".loword.hi", sz - 2, b) :: - List.tabulate(sz){ i => (".b" + i, sz - 1 - i, b) } + Subvariable(".lo", sz - 1, b) :: + Subvariable(".loword", sz - 2, w) :: + Subvariable(".loword.lo", sz - 1, b) :: + Subvariable(".loword.hi", sz - 2, b) :: + List.tabulate(sz){ i => Subvariable(".b" + i, sz - 1 - i, b) } } else { - (".lo", 0, b) :: - (".loword", 0, w) :: - (".loword.lo", 0, b) :: - (".loword.hi", 1, b) :: - List.tabulate(sz){ i => (".b" + i, i, b) } + Subvariable(".lo", 0, b) :: + Subvariable(".loword", 0, w) :: + Subvariable(".loword.lo", 0, b) :: + Subvariable(".loword.hi", 1, b) :: + List.tabulate(sz){ i => Subvariable(".b" + i, i, b) } } case _ => Nil } case p: PointerType => if (options.isBigEndian) List( - (".raw", 0, p), - (".raw.lo", 1, b), - (".raw.hi", 0, b), - (".lo", 1, b), - (".hi", 0, b) + Subvariable(".raw", 0, p), + Subvariable(".raw.lo", 1, b), + Subvariable(".raw.hi", 0, b), + Subvariable(".lo", 1, b), + Subvariable(".hi", 0, b) ) else List( - (".raw", 0, p), - (".raw.lo", 0, b), - (".raw.hi", 1, b), - (".lo", 0, b), - (".hi", 1, b)) + Subvariable(".raw", 0, p), + Subvariable(".raw.lo", 0, b), + Subvariable(".raw.hi", 1, b), + Subvariable(".lo", 0, b), + Subvariable(".hi", 1, b)) case s: StructType => - val builder = new ListBuffer[(String, Int, VariableType)] + val builder = new ListBuffer[Subvariable] var offset = 0 - for(FieldDesc(typeName, fieldName) <- s.fields) { - val typ = get[VariableType](typeName) - val suffix = "." + fieldName - builder += ((suffix, offset, typ)) - builder ++= getSubvariables(typ).map { - case (innerSuffix, innerOffset, innerType) => (suffix + innerSuffix, offset + innerOffset, innerType) + for(ResolvedFieldDesc(typ, fieldName, arraySize) <- s.mutableFieldsWithTypes) { + arraySize match { + case None => + val suffix = "." + fieldName + builder += Subvariable(suffix, offset, typ, arraySize) + if (arraySize.isEmpty) { + builder ++= getSubvariables(typ).map { + case Subvariable(innerSuffix, innerOffset, innerType, innerSize) => Subvariable(suffix + innerSuffix, offset + innerOffset, innerType, innerSize) + } + } + case Some(_) => + // TODO } - offset += typ.size + offset += typ.size * arraySize.getOrElse(1) } builder.toList case s: UnionType => - val builder = new ListBuffer[(String, Int, VariableType)] - for(FieldDesc(typeName, fieldName) <- s.fields) { + val builder = new ListBuffer[Subvariable] + for(FieldDesc(typeName, fieldName, _) <- s.fields) { val typ = get[VariableType](typeName) val suffix = "." + fieldName - builder += ((suffix, 0, typ)) + builder += Subvariable(suffix, 0, typ) builder ++= getSubvariables(typ).map { - case (innerSuffix, innerOffset, innerType) => (suffix + innerSuffix, innerOffset, innerType) + case Subvariable(innerSuffix, innerOffset, innerType, innerSize) => Subvariable(suffix + innerSuffix, innerOffset, innerType, innerSize) } } builder.toList @@ -1961,9 +1989,9 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa } def fixStructSizes(): Unit = { - val allStructTypes = things.values.flatMap { - case StructType(name, _) => Some(name) - case UnionType(name, _) => Some(name) + val allStructTypes: Iterable[VariableType] = things.values.flatMap { + case s@StructType(name, _) => Some(s) + case s@UnionType(name, _) => Some(s) case _ => None } var iterations = allStructTypes.size @@ -1979,14 +2007,29 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa } def fixStructFields(): Unit = { + // TODO: handle arrays? things.values.foreach { case st@StructType(_, fields) => st.mutableFieldsWithTypes = fields.map { - case FieldDesc(tn, name) => get[Type](tn) -> name + case FieldDesc(tn, name, arraySize) => ResolvedFieldDesc(get[VariableType](tn), name, arraySize.map { x => + eval(x) match { + case Some(NumericConstant(c, _)) if c >= 0 && c < 0x10000 => c.toInt + case _ => + log.error(s"Invalid array size for member array $name in type ${st.toString}") + 0 + } + }) } case ut@UnionType(_, fields) => ut.mutableFieldsWithTypes = fields.map { - case FieldDesc(tn, name) => get[Type](tn) -> name + case FieldDesc(tn, name, arraySize) => ResolvedFieldDesc(get[VariableType](tn), name, arraySize.map { x => + eval(x) match { + case Some(NumericConstant(c, _)) if c >= 0 && c < 0x10000 => c.toInt + case _ => + log.error(s"Invalid array size for member array $name in type ${ut.toString}") + 0 + } + }) } case _ => () } @@ -2012,8 +2055,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa case s: UnionDefinitionStatement => registerUnion(s) case _ => } - fixStructSizes() fixStructFields() + fixStructSizes() val pointies = collectPointies(program.declarations) pointiesUsed("") = pointies program.declarations.foreach { decl => diff --git a/src/main/scala/millfork/env/Thing.scala b/src/main/scala/millfork/env/Thing.scala index d053ab3f..7b09ac0b 100644 --- a/src/main/scala/millfork/env/Thing.scala +++ b/src/main/scala/millfork/env/Thing.scala @@ -44,6 +44,8 @@ sealed trait Type extends CallableThing { sealed trait VariableType extends Type +case class Subvariable(suffix: String, offset: Int, typ: VariableType, arraySize: Option[Int] = None) + case object VoidType extends Type { def size = 0 @@ -119,14 +121,14 @@ sealed trait CompoundVariableType extends VariableType case class StructType(name: String, fields: List[FieldDesc]) extends CompoundVariableType { override def size: Int = mutableSize var mutableSize: Int = -1 - var mutableFieldsWithTypes: List[(Type, String)] = Nil + var mutableFieldsWithTypes: List[ResolvedFieldDesc] = Nil override def isSigned: Boolean = false } case class UnionType(name: String, fields: List[FieldDesc]) extends CompoundVariableType { override def size: Int = mutableSize var mutableSize: Int = -1 - var mutableFieldsWithTypes: List[(Type, String)] = Nil + var mutableFieldsWithTypes: List[ResolvedFieldDesc] = Nil override def isSigned: Boolean = false } diff --git a/src/main/scala/millfork/node/Node.scala b/src/main/scala/millfork/node/Node.scala index b2259c3d..9c683968 100644 --- a/src/main/scala/millfork/node/Node.scala +++ b/src/main/scala/millfork/node/Node.scala @@ -5,12 +5,14 @@ import millfork.assembly.m6809.{MAddrMode, MOpcode} import millfork.assembly.mos.opt.SourceOfNZ import millfork.assembly.mos.{AddrMode, Opcode} import millfork.assembly.z80.{ZOpcode, ZRegisters} -import millfork.env.{Constant, ParamPassingConvention, Type} +import millfork.env.{Constant, ParamPassingConvention, Type, VariableType} import millfork.output.MemoryAlignment case class Position(moduleName: String, line: Int, column: Int, cursor: Int) -case class FieldDesc(typeName:String, fieldName: String) +case class FieldDesc(typeName:String, fieldName: String, arraySize: Option[Expression]) + +case class ResolvedFieldDesc(typ:VariableType, fieldName: String, arraySize: Option[Int]) sealed trait Node { var position: Option[Position] = None diff --git a/src/main/scala/millfork/parser/MfParser.scala b/src/main/scala/millfork/parser/MfParser.scala index ae4df02e..e0be18a7 100644 --- a/src/main/scala/millfork/parser/MfParser.scala +++ b/src/main/scala/millfork/parser/MfParser.scala @@ -605,10 +605,21 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri variants <- enumVariants ~/ Pass } yield Seq(EnumDefinitionStatement(name, variants).pos(p)) - val compoundTypeField: P[FieldDesc] = for { - typ <- identifier ~/ HWS - name <- identifier ~ HWS - } yield FieldDesc(typ, name) + val compoundTypeField: P[FieldDesc] = ("array".! ~ HWS ~/ Pass).?.flatMap { + case None => + for { + typ <- identifier ~/ HWS + name <- identifier ~ HWS + } yield FieldDesc(typ, name, None) + + case Some(_) => + for { + elementType <- ("(" ~/ AWS ~/ identifier ~ AWS ~ ")").? ~/ HWS + if enableDebuggingOptions + name <- identifier ~ HWS + length <- "[" ~/ AWS ~/ mfExpression(nonStatementLevel, false) ~ AWS ~ "]" ~ HWS + } yield FieldDesc(elementType.getOrElse("byte"), name, Some(length)) + } val compoundTypeFields: P[List[FieldDesc]] = ("{" ~/ AWS ~ compoundTypeField.rep(sep = NoCut(EOLOrComma) ~ !"}" ~/ Pass) ~/ AWS ~/ "}" ~/ Pass).map(_.toList)