1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-05-31 18:41:30 +00:00

Added array initialization syntax with for

This commit is contained in:
Karol Stasiak 2018-03-25 22:57:15 +02:00
parent 6aa70184cf
commit e32887b6b7
8 changed files with 121 additions and 13 deletions

View File

@ -2,6 +2,8 @@
## Current version
* Added array initialization syntax with `for` (not yet finalized).
* Fixed several bugs, most importantly invalid offsets for branching instructions.
## 0.2.2

View File

@ -625,6 +625,29 @@ class Environment(val parent: Option[Environment], val prefix: String) {
addThing(array, None)
}
def extractArrayContents(contents1: ArrayContents): List[Expression] = contents1 match {
case LiteralContents(xs) => xs
case CombinedContents(xs) => xs.flatMap(extractArrayContents)
case ForLoopContents(v, start, end, direction, body) =>
(eval(start), eval(end)) match {
case (Some(NumericConstant(s, sz1)), Some(NumericConstant(e, sz2))) =>
val size = sz1 max sz2
val range = (direction match {
case ForDirection.To | ForDirection.ParallelTo => s.to(e)
case ForDirection.Until | ForDirection.ParallelUntil => s.until(e)
case ForDirection.DownTo => s.to(e, -1)
}).toList
range.flatMap(i => extractArrayContents(body).map(_.replaceVariable(v, LiteralExpression(i, size))))
case (Some(_), Some(_)) =>
ErrorReporting.error("Array range bounds cannot be evaluated")
Nil
case _ =>
ErrorReporting.error("Non-constant array range bounds")
Nil
}
}
def registerArray(stmt: ArrayDeclarationStatement): Unit = {
val b = get[Type]("byte")
val p = get[Type]("pointer")
@ -663,7 +686,8 @@ class Environment(val parent: Option[Environment], val prefix: String) {
case _ => ErrorReporting.error(s"Array `${stmt.name}` has weird length", stmt.position)
}
}
case Some(contents) =>
case Some(contents1) =>
val contents = extractArrayContents(contents1)
stmt.length match {
case None =>
case Some(l) =>

View File

@ -104,12 +104,28 @@ case class VariableDeclarationStatement(name: String,
override def getAllExpressions: List[Expression] = List(initialValue, address).flatten
}
trait ArrayContents extends Node {
def getAllExpressions: List[Expression]
}
case class LiteralContents(contents: List[Expression]) extends ArrayContents {
override def getAllExpressions: List[Expression] = contents
}
case class ForLoopContents(variable: String, start: Expression, end: Expression, direction: ForDirection.Value, body: ArrayContents) extends ArrayContents {
override def getAllExpressions: List[Expression] = start :: end :: body.getAllExpressions.map(_.replaceVariable(variable, LiteralExpression(0, 1)))
}
case class CombinedContents(contents: List[ArrayContents]) extends ArrayContents {
override def getAllExpressions: List[Expression] = contents.flatMap(_.getAllExpressions)
}
case class ArrayDeclarationStatement(name: String,
bank: Option[String],
length: Option[Expression],
address: Option[Expression],
elements: Option[List[Expression]]) extends DeclarationStatement {
override def getAllExpressions: List[Expression] = List(length, address).flatten ++ elements.getOrElse(Nil)
elements: Option[ArrayContents]) extends DeclarationStatement {
override def getAllExpressions: List[Expression] = List(length, address).flatten ++ elements.fold(List[Expression]())(_.getAllExpressions)
}
case class ParameterDeclaration(typ: String,

View File

@ -56,7 +56,8 @@ object UnusedFunctions extends NodeOptimization {
def getAllCalledFunctions(expressions: List[Node]): List[String] = expressions.flatMap {
case s: VariableDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.initialValue.toList)
case s: ArrayDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.elements.getOrElse(Nil))
case s: ArrayDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.elements.toList)
case s: ArrayContents => getAllCalledFunctions(s.getAllExpressions)
case s: FunctionDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.statements.getOrElse(Nil))
case Assignment(VariableExpression(_), expr) => getAllCalledFunctions(expr :: Nil)
case s: ReturnDispatchStatement =>

View File

@ -49,7 +49,8 @@ object UnusedGlobalVariables extends NodeOptimization {
def getAllReadVariables(expressions: List[Node]): List[String] = expressions.flatMap {
case s: VariableDeclarationStatement => getAllReadVariables(s.address.toList) ++ getAllReadVariables(s.initialValue.toList)
case s: ArrayDeclarationStatement => getAllReadVariables(s.address.toList) ++ getAllReadVariables(s.elements.getOrElse(Nil))
case s: ArrayDeclarationStatement => getAllReadVariables(s.address.toList) ++ getAllReadVariables(s.elements.toList)
case s: ArrayContents => getAllReadVariables(s.getAllExpressions)
case s: FunctionDeclarationStatement => getAllReadVariables(s.address.toList) ++ getAllReadVariables(s.statements.getOrElse(Nil))
case Assignment(VariableExpression(_), expr) => getAllReadVariables(expr :: Nil)
case ExpressionStatement(FunctionCallExpression(op, VariableExpression(_) :: params)) if op.endsWith("=") => getAllReadVariables(params)

View File

@ -59,7 +59,8 @@ object InliningCalculator {
case s: VariableDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.initialValue.toList)
case ReturnDispatchStatement(index, params, branches) =>
getAllCalledFunctions(List(index)) ++ getAllCalledFunctions(params) ++ getAllCalledFunctions(branches.map(b => b.function))
case s: ArrayDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.elements.getOrElse(Nil))
case s: ArrayDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.elements.toList)
case s: ArrayContents => getAllCalledFunctions(s.getAllExpressions)
case s: FunctionDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.statements.getOrElse(Nil))
case Assignment(VariableExpression(_), expr) => getAllCalledFunctions(expr :: Nil)
case AssemblyStatement(JSR, _, VariableExpression(name), true) => (name -> false) :: Nil

View File

@ -194,9 +194,9 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
appc <- appcSimple | appcComplex
} yield ParameterDeclaration(typ, appc).pos(p)
def arrayListElement: P[List[Expression]] = arrayStringContents | mlExpression(nonStatementLevel).map(List(_))
def arrayListElement: P[ArrayContents] = arrayStringContents | arrayLoopContents | mlExpression(nonStatementLevel).map(e => LiteralContents(List(e)))
def arrayListContents: P[List[Expression]] = ("[" ~/ AWS ~/ arrayListElement.rep(sep = AWS ~ "," ~/ AWS) ~ AWS ~ "]" ~/ Pass).map(_.flatten.toList)
def arrayListContents: P[ArrayContents] = ("[" ~/ AWS ~/ arrayListElement.rep(sep = AWS ~ "," ~/ AWS) ~ AWS ~ "]" ~/ Pass).map(c => CombinedContents(c.toList))
val doubleQuotedString: P[List[Char]] = P("\"" ~/ CharsWhile(c => c != '\"' && c != '\n' && c != '\r').! ~ "\"").map(_.toList)
@ -210,7 +210,7 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
TextCodec.Ascii
}
def arrayFileContents: P[List[Expression]] = for {
def arrayFileContents: P[ArrayContents] = for {
p <- "file" ~ HWS ~/ "(" ~/ HWS ~/ position()
filePath <- doubleQuotedString ~/ HWS
optSlice <- ("," ~/ HWS ~/ literalAtom ~/ HWS ~/ "," ~/ HWS ~/ literalAtom ~/ HWS ~/ Pass).?
@ -220,14 +220,34 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
val slice = optSlice.fold(data) {
case (start, length) => data.slice(start.value.toInt, start.value.toInt + length.value.toInt)
}
slice.map(c => LiteralExpression(c & 0xff, 1)).toList
LiteralContents(slice.map(c => LiteralExpression(c & 0xff, 1)).toList)
}
def arrayStringContents: P[List[Expression]] = P(position() ~ doubleQuotedString ~/ HWS ~ codec).map {
case (p, s, co) => s.map(c => LiteralExpression(co.decode(None, c), 1).pos(p))
def arrayStringContents: P[ArrayContents] = P(position() ~ doubleQuotedString ~/ HWS ~ codec).map {
case (p, s, co) => LiteralContents(s.map(c => LiteralExpression(co.decode(None, c), 1).pos(p)))
}
def arrayContents: P[List[Expression]] = arrayListContents | arrayFileContents | arrayStringContents
def arrayLoopContents: P[ArrayContents] = for {
identifier <- "for" ~ SWS ~/ identifier ~/ HWS ~ "," ~/ HWS ~ Pass
start <- mlExpression(nonStatementLevel) ~ HWS ~ "," ~/ HWS ~/ Pass
pos <- position()
direction <- forDirection ~/ HWS ~/ "," ~/ HWS ~/ Pass
end <- mlExpression(nonStatementLevel)
body <- AWS ~ arrayContents
} yield {
val fixedDirection = direction match {
case ForDirection.ParallelUntil =>
ErrorReporting.warn("`paralleluntil` is not allowed in array definitions, assuming `until`", options, Some(pos))
ForDirection.Until
case ForDirection.ParallelTo =>
ErrorReporting.warn("`parallelto` is not allowed in array definitions, assuming `to`", options, Some(pos))
ForDirection.To
case x => x
}
ForLoopContents(identifier, start, end, fixedDirection, body)
}
def arrayContents: P[ArrayContents] = arrayListContents | arrayLoopContents | arrayFileContents | arrayStringContents
def arrayDefinition: P[Seq[ArrayDeclarationStatement]] = for {
p <- position()

View File

@ -0,0 +1,43 @@
package millfork.test
import millfork.{Cpu, OptimizationPresets}
import millfork.assembly.opt.{AlwaysGoodOptimizations, DangerousOptimizations}
import millfork.test.emu._
import org.scalatest.{FunSuite, Matchers}
/**
* @author Karol Stasiak
*/
class ForArraySuite extends FunSuite with Matchers {
test("Basic for-array test") {
val m = EmuSuperOptimizedRun(
"""
| byte output @$c000
| array input = for i,0,until,8 [i + i]
|
| array useless0 = for x,0,until,8 for y,0,until,8 [0]
| array useless1 = for x,0,until,8 [for y,0,until,8 [0]]
| array useless2 = for x,0,until,8 "test" scr
| array useless3 = for x,0,until,8 [1 << x]
| array useless4 = for x,0,until,4 [(3 << (x * 2)) ^ 0xff]
| array useless5 = [
| for x,0,until,4 [3]
| ]
| array useless6 = [
| "foo" ascii,
| for x,0,until,4 [3]
| ]
| array useless7 = [
| 7,
| for x,0,until,4 [3]
| ]
|
| void main () {
| output = useless0[0] + useless1[0] + useless2[0] + useless3[0] + useless4[0] + useless5[0] + useless6[0] + useless7[0]
| output = input.length + input[5]
| }
""".stripMargin)
m.readByte(0xc000) should equal(18)
}
}