mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-01 06:29:53 +00:00
Add local arrays
This commit is contained in:
parent
96b5918728
commit
25c440f17d
@ -36,6 +36,8 @@
|
||||
|
||||
* Arrays can now be constant.
|
||||
|
||||
* Arrays can now be local.
|
||||
|
||||
* Added hint for identifiers with typos.
|
||||
|
||||
* Aliases now also support subfields.
|
||||
|
@ -104,6 +104,10 @@ This allows for overriding definitions of library functions by another library:
|
||||
|
||||
An array is a continuous sequence of bytes in memory.
|
||||
|
||||
An array declaration can happen at either top level of a file (*global* arrays),
|
||||
or a top level of a function (*local* arrays).
|
||||
Regardless of where they were declared, arrays are considered static.
|
||||
|
||||
Syntax:
|
||||
|
||||
`[segment(<segment>)] [const] array [(<element type>)] <name> [[<size>]] [align ( <alignment> )] [@<address>] [= <initial_values>]`
|
||||
@ -133,7 +137,8 @@ If the declared size and the size deduced from the `<initial_values>` don't matc
|
||||
|
||||
* `<address>` is a constant expression that defines where in the memory the array is or will be located.
|
||||
|
||||
* `<initial_values>` is an array literal, see [Literals](./literals.md)
|
||||
* `<initial_values>` is an array literal, see [Literals](./literals.md).
|
||||
Local arrays can have initial values only if they're const.
|
||||
|
||||
TODO
|
||||
|
||||
|
64
src/main/scala/millfork/env/Environment.scala
vendored
64
src/main/scala/millfork/env/Environment.scala
vendored
@ -375,7 +375,10 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
orElse(maybeGet[ThingInMemory](arrayName)).
|
||||
orElse(maybeGet[ThingInMemory](arrayName + ".array")).
|
||||
orElse(maybeGet[ConstantThing](arrayName)).
|
||||
getOrElse(log.fatal(s"`$arrayName` is not an array or a pointer"))
|
||||
getOrElse{
|
||||
log.error(s"`$arrayName` is not an array or a pointer")
|
||||
get[Thing]("nullptr")
|
||||
}
|
||||
}
|
||||
|
||||
def getPointy(name: String): Pointy = {
|
||||
@ -1018,6 +1021,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
case Some(statements) =>
|
||||
statements.foreach {
|
||||
case v: VariableDeclarationStatement => env.registerVariable(v, options, pointies(v.name))
|
||||
case a: ArrayDeclarationStatement => env.registerArray(a, options)
|
||||
case _ => ()
|
||||
}
|
||||
val executableStatements = statements.flatMap {
|
||||
@ -1293,6 +1297,10 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
if (options.flag(CompilationFlag.LUnixRelocatableCode) && stmt.alignment.exists(_.isMultiplePages)) {
|
||||
log.error("Invalid alignment for LUnix code", stmt.position)
|
||||
}
|
||||
if (stmt.elements.isDefined && !stmt.const && parent.isDefined) {
|
||||
log.error(s"Local array `${stmt.name}` cannot be initialized if it's not const", stmt.position)
|
||||
}
|
||||
val arrayName = prefix + stmt.name
|
||||
val b = get[VariableType]("byte")
|
||||
val w = get[VariableType]("word")
|
||||
val p = get[Type]("pointer")
|
||||
@ -1328,38 +1336,38 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
if (length > 0xffff || length < 0) log.error(s"Array `${stmt.name}` has invalid length", stmt.position)
|
||||
val alignment = stmt.alignment.getOrElse(defaultArrayAlignment(options, length))
|
||||
val array = address match {
|
||||
case None => UninitializedArray(stmt.name + ".array", length.toInt,
|
||||
case None => UninitializedArray(arrayName + ".array", length.toInt,
|
||||
declaredBank = stmt.bank, indexType, e, stmt.const, alignment)
|
||||
case Some(aa) => RelativeArray(stmt.name + ".array", aa, length.toInt,
|
||||
case Some(aa) => RelativeArray(arrayName + ".array", aa, length.toInt,
|
||||
declaredBank = stmt.bank, indexType, e, stmt.const)
|
||||
}
|
||||
addThing(array, stmt.position)
|
||||
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None, stmt.bank, alignment, isVolatile = false), stmt.position, options, Some(e))
|
||||
registerAddressConstant(UninitializedMemoryVariable(arrayName, p, VariableAllocationMethod.None, stmt.bank, alignment, isVolatile = false), stmt.position, options, Some(e))
|
||||
val a = address match {
|
||||
case None => array.toAddress
|
||||
case Some(aa) => aa
|
||||
}
|
||||
addThing(RelativeVariable(stmt.name + ".first", a, b, zeropage = false,
|
||||
addThing(RelativeVariable(arrayName + ".first", a, b, zeropage = false,
|
||||
declaredBank = stmt.bank, isVolatile = false), stmt.position)
|
||||
if (options.flag(CompilationFlag.LUnixRelocatableCode)) {
|
||||
val b = get[Type]("byte")
|
||||
val w = get[Type]("word")
|
||||
val relocatable = UninitializedMemoryVariable(stmt.name, w, VariableAllocationMethod.Static, None, NoAlignment, isVolatile = false)
|
||||
val relocatable = UninitializedMemoryVariable(arrayName, w, VariableAllocationMethod.Static, None, NoAlignment, isVolatile = false)
|
||||
val addr = relocatable.toAddress
|
||||
addThing(relocatable, stmt.position)
|
||||
addThing(RelativeVariable(stmt.name + ".addr.hi", addr + 1, b, zeropage = false, None, isVolatile = false), stmt.position)
|
||||
addThing(RelativeVariable(stmt.name + ".addr.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
|
||||
addThing(RelativeVariable(stmt.name + ".array.hi", addr + 1, b, zeropage = false, None, isVolatile = false), stmt.position)
|
||||
addThing(RelativeVariable(stmt.name + ".array.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
|
||||
addThing(RelativeVariable(arrayName + ".addr.hi", addr + 1, b, zeropage = false, None, isVolatile = false), stmt.position)
|
||||
addThing(RelativeVariable(arrayName + ".addr.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
|
||||
addThing(RelativeVariable(arrayName + ".array.hi", addr + 1, b, zeropage = false, None, isVolatile = false), stmt.position)
|
||||
addThing(RelativeVariable(arrayName + ".array.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
|
||||
} else {
|
||||
addThing(ConstantThing(stmt.name, a, p), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".lo", a.loByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".array.hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".array.lo", a.loByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(arrayName, a, p), stmt.position)
|
||||
addThing(ConstantThing(arrayName + ".hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(arrayName + ".lo", a.loByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(arrayName + ".array.hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(arrayName + ".array.lo", a.loByte.quickSimplify, b), stmt.position)
|
||||
}
|
||||
if (length < 256) {
|
||||
addThing(ConstantThing(stmt.name + ".length", lengthConst, b), stmt.position)
|
||||
addThing(ConstantThing(arrayName + ".length", lengthConst, b), stmt.position)
|
||||
}
|
||||
case _ => log.error(s"Array `${stmt.name}` has weird length", stmt.position)
|
||||
}
|
||||
@ -1406,33 +1414,33 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
for (element <- contents) {
|
||||
AbstractExpressionCompiler.checkAssignmentType(this, element, e)
|
||||
}
|
||||
val array = InitializedArray(stmt.name + ".array", address, contents, declaredBank = stmt.bank, indexType, e, readOnly = stmt.const, alignment)
|
||||
val array = InitializedArray(arrayName + ".array", address, contents, declaredBank = stmt.bank, indexType, e, readOnly = stmt.const, alignment)
|
||||
addThing(array, stmt.position)
|
||||
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None,
|
||||
registerAddressConstant(UninitializedMemoryVariable(arrayName, p, VariableAllocationMethod.None,
|
||||
declaredBank = stmt.bank, alignment, isVolatile = false), stmt.position, options, Some(e))
|
||||
val a = address match {
|
||||
case None => array.toAddress
|
||||
case Some(aa) => aa
|
||||
}
|
||||
addThing(RelativeVariable(stmt.name + ".first", a, e, zeropage = false,
|
||||
addThing(RelativeVariable(arrayName + ".first", a, e, zeropage = false,
|
||||
declaredBank = stmt.bank, isVolatile = false), stmt.position)
|
||||
if (options.flag(CompilationFlag.LUnixRelocatableCode)) {
|
||||
val b = get[Type]("byte")
|
||||
val w = get[Type]("word")
|
||||
val relocatable = UninitializedMemoryVariable(stmt.name, w, VariableAllocationMethod.Static, None, NoAlignment, isVolatile = false)
|
||||
val relocatable = UninitializedMemoryVariable(arrayName, w, VariableAllocationMethod.Static, None, NoAlignment, isVolatile = false)
|
||||
val addr = relocatable.toAddress
|
||||
addThing(relocatable, stmt.position)
|
||||
addThing(RelativeVariable(stmt.name + ".array.hi", addr + 1, b, zeropage = false, None, isVolatile = false), stmt.position)
|
||||
addThing(RelativeVariable(stmt.name + ".array.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
|
||||
addThing(RelativeVariable(arrayName + ".array.hi", addr + 1, b, zeropage = false, None, isVolatile = false), stmt.position)
|
||||
addThing(RelativeVariable(arrayName + ".array.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
|
||||
} else {
|
||||
addThing(ConstantThing(stmt.name, a, p), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".lo", a.loByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".array.hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".array.lo", a.loByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(arrayName, a, p), stmt.position)
|
||||
addThing(ConstantThing(arrayName + ".hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(arrayName + ".lo", a.loByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(arrayName + ".array.hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(arrayName + ".array.lo", a.loByte.quickSimplify, b), stmt.position)
|
||||
}
|
||||
if (length < 256) {
|
||||
addThing(ConstantThing(stmt.name + ".length", NumericConstant(length, 1), b), stmt.position)
|
||||
addThing(ConstantThing(arrayName + ".length", NumericConstant(length, 1), b), stmt.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ object UnusedLocalVariables extends NodeOptimization {
|
||||
|
||||
def getAllLocalVariables(statements: List[Statement]): List[String] = statements.flatMap {
|
||||
case v: VariableDeclarationStatement => List(v.name)
|
||||
case v: ArrayDeclarationStatement => List(v.name)
|
||||
case x: IfStatement => getAllLocalVariables(x.thenBranch) ++ getAllLocalVariables(x.elseBranch)
|
||||
case x: WhileStatement => getAllLocalVariables(x.body)
|
||||
case x: DoWhileStatement => getAllLocalVariables(x.body)
|
||||
@ -32,6 +33,7 @@ object UnusedLocalVariables extends NodeOptimization {
|
||||
case CompoundConstant(_, l, r) => getAllReadVariables(l) ++ getAllReadVariables(r)
|
||||
case MemoryAddressConstant(th) => List(
|
||||
th.name,
|
||||
th.name.stripSuffix(".array"),
|
||||
th.name.stripSuffix(".addr"),
|
||||
th.name.stripSuffix(".hi"),
|
||||
th.name.stripSuffix(".lo"),
|
||||
@ -78,6 +80,8 @@ object UnusedLocalVariables extends NodeOptimization {
|
||||
def removeVariables(statements: List[Statement], localsToRemove: Set[String]): List[Statement] = if (localsToRemove.isEmpty) statements else statements.flatMap {
|
||||
case s: VariableDeclarationStatement =>
|
||||
if (localsToRemove(s.name)) None else Some(s)
|
||||
case s: ArrayDeclarationStatement =>
|
||||
if (localsToRemove(s.name)) None else Some(s)
|
||||
case s@ExpressionStatement(FunctionCallExpression(op, VariableExpression(n) :: params)) if op.endsWith("=") =>
|
||||
if (localsToRemove(n)) {
|
||||
params.flatMap {
|
||||
|
@ -378,7 +378,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
|
||||
def asmStatement: P[ExecutableStatement]
|
||||
|
||||
def statement: P[Seq[Statement]] = (position() ~ P(keywordStatement | localVariableDefinition | expressionStatement)).map { case (p, s) => s.map(_.pos(p)) }
|
||||
def statement: P[Seq[Statement]] = (position() ~ P(keywordStatement | arrayDefinition | localVariableDefinition | expressionStatement)).map { case (p, s) => s.map(_.pos(p)) }
|
||||
|
||||
def asmStatements: P[List[ExecutableStatement]] = ("{" ~/ AWS ~/ asmStatement.rep(sep = NoCut(EOL) ~ !"}" ~/ Pass) ~/ AWS ~/ "}" ~/ Pass).map(_.toList)
|
||||
|
||||
|
@ -340,4 +340,39 @@ class ArraySuite extends FunSuite with Matchers {
|
||||
m.readByte(0xc027) should equal(0)
|
||||
}
|
||||
}
|
||||
|
||||
test("Local arrays") {
|
||||
EmuUnoptimizedRun(
|
||||
"""
|
||||
| byte output @$c000
|
||||
| void main () {
|
||||
| array square[5]
|
||||
| square[0] = 1
|
||||
| output = square[0]
|
||||
| }
|
||||
""".stripMargin).readByte(0xc000) should equal(1)
|
||||
ShouldNotCompile(
|
||||
"""
|
||||
| void main () {
|
||||
| array square = [0]
|
||||
| }
|
||||
""".stripMargin)
|
||||
ShouldNotCompile(
|
||||
"""
|
||||
| void f() {
|
||||
| square[1] = 1
|
||||
| }
|
||||
| void main () {
|
||||
| array square = [0]
|
||||
| }
|
||||
""".stripMargin)
|
||||
EmuUnoptimizedRun(
|
||||
"""
|
||||
| byte output @$c000
|
||||
| void main () {
|
||||
| const array square = [1]
|
||||
| output = square[0]
|
||||
| }
|
||||
""".stripMargin).readByte(0xc000) should equal(1)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user