mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-19 19:30:08 +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 constant.
|
||||||
|
|
||||||
|
* Arrays can now be local.
|
||||||
|
|
||||||
* Added hint for identifiers with typos.
|
* Added hint for identifiers with typos.
|
||||||
|
|
||||||
* Aliases now also support subfields.
|
* 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 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:
|
Syntax:
|
||||||
|
|
||||||
`[segment(<segment>)] [const] array [(<element type>)] <name> [[<size>]] [align ( <alignment> )] [@<address>] [= <initial_values>]`
|
`[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.
|
* `<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
|
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)).
|
||||||
orElse(maybeGet[ThingInMemory](arrayName + ".array")).
|
orElse(maybeGet[ThingInMemory](arrayName + ".array")).
|
||||||
orElse(maybeGet[ConstantThing](arrayName)).
|
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 = {
|
def getPointy(name: String): Pointy = {
|
||||||
@ -1018,6 +1021,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||||||
case Some(statements) =>
|
case Some(statements) =>
|
||||||
statements.foreach {
|
statements.foreach {
|
||||||
case v: VariableDeclarationStatement => env.registerVariable(v, options, pointies(v.name))
|
case v: VariableDeclarationStatement => env.registerVariable(v, options, pointies(v.name))
|
||||||
|
case a: ArrayDeclarationStatement => env.registerArray(a, options)
|
||||||
case _ => ()
|
case _ => ()
|
||||||
}
|
}
|
||||||
val executableStatements = statements.flatMap {
|
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)) {
|
if (options.flag(CompilationFlag.LUnixRelocatableCode) && stmt.alignment.exists(_.isMultiplePages)) {
|
||||||
log.error("Invalid alignment for LUnix code", stmt.position)
|
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 b = get[VariableType]("byte")
|
||||||
val w = get[VariableType]("word")
|
val w = get[VariableType]("word")
|
||||||
val p = get[Type]("pointer")
|
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)
|
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 alignment = stmt.alignment.getOrElse(defaultArrayAlignment(options, length))
|
||||||
val array = address match {
|
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)
|
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)
|
declaredBank = stmt.bank, indexType, e, stmt.const)
|
||||||
}
|
}
|
||||||
addThing(array, stmt.position)
|
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 {
|
val a = address match {
|
||||||
case None => array.toAddress
|
case None => array.toAddress
|
||||||
case Some(aa) => aa
|
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)
|
declaredBank = stmt.bank, isVolatile = false), stmt.position)
|
||||||
if (options.flag(CompilationFlag.LUnixRelocatableCode)) {
|
if (options.flag(CompilationFlag.LUnixRelocatableCode)) {
|
||||||
val b = get[Type]("byte")
|
val b = get[Type]("byte")
|
||||||
val w = get[Type]("word")
|
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
|
val addr = relocatable.toAddress
|
||||||
addThing(relocatable, stmt.position)
|
addThing(relocatable, stmt.position)
|
||||||
addThing(RelativeVariable(stmt.name + ".addr.hi", addr + 1, 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(stmt.name + ".addr.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
|
addThing(RelativeVariable(arrayName + ".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(arrayName + ".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.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
|
||||||
} else {
|
} else {
|
||||||
addThing(ConstantThing(stmt.name, a, p), stmt.position)
|
addThing(ConstantThing(arrayName, a, p), stmt.position)
|
||||||
addThing(ConstantThing(stmt.name + ".hi", a.hiByte.quickSimplify, b), stmt.position)
|
addThing(ConstantThing(arrayName + ".hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||||
addThing(ConstantThing(stmt.name + ".lo", a.loByte.quickSimplify, b), stmt.position)
|
addThing(ConstantThing(arrayName + ".lo", a.loByte.quickSimplify, b), stmt.position)
|
||||||
addThing(ConstantThing(stmt.name + ".array.hi", a.hiByte.quickSimplify, b), stmt.position)
|
addThing(ConstantThing(arrayName + ".array.hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||||
addThing(ConstantThing(stmt.name + ".array.lo", a.loByte.quickSimplify, b), stmt.position)
|
addThing(ConstantThing(arrayName + ".array.lo", a.loByte.quickSimplify, b), stmt.position)
|
||||||
}
|
}
|
||||||
if (length < 256) {
|
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)
|
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) {
|
for (element <- contents) {
|
||||||
AbstractExpressionCompiler.checkAssignmentType(this, element, e)
|
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)
|
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))
|
declaredBank = stmt.bank, alignment, isVolatile = false), stmt.position, options, Some(e))
|
||||||
val a = address match {
|
val a = address match {
|
||||||
case None => array.toAddress
|
case None => array.toAddress
|
||||||
case Some(aa) => aa
|
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)
|
declaredBank = stmt.bank, isVolatile = false), stmt.position)
|
||||||
if (options.flag(CompilationFlag.LUnixRelocatableCode)) {
|
if (options.flag(CompilationFlag.LUnixRelocatableCode)) {
|
||||||
val b = get[Type]("byte")
|
val b = get[Type]("byte")
|
||||||
val w = get[Type]("word")
|
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
|
val addr = relocatable.toAddress
|
||||||
addThing(relocatable, stmt.position)
|
addThing(relocatable, stmt.position)
|
||||||
addThing(RelativeVariable(stmt.name + ".array.hi", addr + 1, 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(stmt.name + ".array.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
|
addThing(RelativeVariable(arrayName + ".array.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
|
||||||
} else {
|
} else {
|
||||||
addThing(ConstantThing(stmt.name, a, p), stmt.position)
|
addThing(ConstantThing(arrayName, a, p), stmt.position)
|
||||||
addThing(ConstantThing(stmt.name + ".hi", a.hiByte.quickSimplify, b), stmt.position)
|
addThing(ConstantThing(arrayName + ".hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||||
addThing(ConstantThing(stmt.name + ".lo", a.loByte.quickSimplify, b), stmt.position)
|
addThing(ConstantThing(arrayName + ".lo", a.loByte.quickSimplify, b), stmt.position)
|
||||||
addThing(ConstantThing(stmt.name + ".array.hi", a.hiByte.quickSimplify, b), stmt.position)
|
addThing(ConstantThing(arrayName + ".array.hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||||
addThing(ConstantThing(stmt.name + ".array.lo", a.loByte.quickSimplify, b), stmt.position)
|
addThing(ConstantThing(arrayName + ".array.lo", a.loByte.quickSimplify, b), stmt.position)
|
||||||
}
|
}
|
||||||
if (length < 256) {
|
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 {
|
def getAllLocalVariables(statements: List[Statement]): List[String] = statements.flatMap {
|
||||||
case v: VariableDeclarationStatement => List(v.name)
|
case v: VariableDeclarationStatement => List(v.name)
|
||||||
|
case v: ArrayDeclarationStatement => List(v.name)
|
||||||
case x: IfStatement => getAllLocalVariables(x.thenBranch) ++ getAllLocalVariables(x.elseBranch)
|
case x: IfStatement => getAllLocalVariables(x.thenBranch) ++ getAllLocalVariables(x.elseBranch)
|
||||||
case x: WhileStatement => getAllLocalVariables(x.body)
|
case x: WhileStatement => getAllLocalVariables(x.body)
|
||||||
case x: DoWhileStatement => 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 CompoundConstant(_, l, r) => getAllReadVariables(l) ++ getAllReadVariables(r)
|
||||||
case MemoryAddressConstant(th) => List(
|
case MemoryAddressConstant(th) => List(
|
||||||
th.name,
|
th.name,
|
||||||
|
th.name.stripSuffix(".array"),
|
||||||
th.name.stripSuffix(".addr"),
|
th.name.stripSuffix(".addr"),
|
||||||
th.name.stripSuffix(".hi"),
|
th.name.stripSuffix(".hi"),
|
||||||
th.name.stripSuffix(".lo"),
|
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 {
|
def removeVariables(statements: List[Statement], localsToRemove: Set[String]): List[Statement] = if (localsToRemove.isEmpty) statements else statements.flatMap {
|
||||||
case s: VariableDeclarationStatement =>
|
case s: VariableDeclarationStatement =>
|
||||||
if (localsToRemove(s.name)) None else Some(s)
|
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("=") =>
|
case s@ExpressionStatement(FunctionCallExpression(op, VariableExpression(n) :: params)) if op.endsWith("=") =>
|
||||||
if (localsToRemove(n)) {
|
if (localsToRemove(n)) {
|
||||||
params.flatMap {
|
params.flatMap {
|
||||||
|
@ -378,7 +378,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
|||||||
|
|
||||||
def asmStatement: P[ExecutableStatement]
|
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)
|
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)
|
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…
x
Reference in New Issue
Block a user