1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-12 22:29:33 +00:00

The @struct array format; fix the @long format

This commit is contained in:
Karol Stasiak 2019-04-30 01:30:22 +02:00
parent d9f88cdfad
commit 1862fed70f
5 changed files with 139 additions and 9 deletions

View File

@ -20,7 +20,7 @@
* Added `nullptr`.
* Arrays can now have elements of types other than `byte` (still limited in size to 1 byte though).
* Arrays can now have elements of types other than `byte` (still limited in size to 1 byte though) and be built out of struct literals.
* Arrays can now be constant.
@ -42,6 +42,8 @@
* Fixed `#pragma` not respecting `#if`.
* Fixed `@long` and `@long_be` array filters.
* 8080 and LR35902: fixed large stack variables.
* Optimization improvements.

View File

@ -110,6 +110,11 @@ An array is initialized with either:
* `@long` (=`@long_le`), `@long_be`: similar, but with four bytes
`@long [$11223344]` is equivalent to `[$44, $33, $22, $11]`
`@long_be [$11223344]` is equivalent to `[$11, $22, $33, $44]`
* `@struct`: every term of the initializer is interpreted as a struct constructor (see below)
and treated as a list of bytes with no padding
`@struct [s(1, 2)]` is equivalent to `[1, 2]` when `struct s {byte x, byte y}` is defined
`@struct [s(1, 2), s(3, 4)]` is equivalent to `[1, 0, 2, 0, 3, 0, 4, 0]` when `struct s {word x, word y}` is defined
* a list of byte literals and/or other array initializers, surrounded by brackets:
@ -131,6 +136,12 @@ The `for`-style expression has a variable, a starting index, a direction, a fina
and a parameterizable array initializer.
The initializer is repeated for every value of the variable in the given range.
Struct constructors look like a function call, where the callee name is the name of the struct type
and the parameters are the values of fields in the order of declaration.
Fields of arithmetic, pointer and enum types are declared using normal expressions.
Fields of struct types are declared using struct constructors.
Fields of union types cannot be declared.
What might be useful is the fact that the compiler allows for built-in trigonometric functions
in constant expressions only:

View File

@ -1163,9 +1163,82 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
addThing(array, None)
}
def extractStructArrayContents(expr: Expression, targetType: Option[Type]): List[Expression] = {
(targetType, expr) match {
case (Some(tt: StructType), FunctionCallExpression(fname, fieldValues)) =>
maybeGet[Thing](fname) match {
case Some(tt2:StructType) if tt2.name == tt.name =>
if (tt.fields.length != fieldValues.length) {
log.error(s"Invalid number of struct fields for struct const `${tt.name}`", fieldValues.headOption.flatMap(_.position))
List.fill(tt.size)(LiteralExpression(0, 1))
} else {
tt.fields.zip(fieldValues).flatMap {
case ((fieldTypeName, _), expr) => extractStructArrayContents(expr, Some(get[Type](fieldTypeName)))
}
}
case _ =>
log.error(s"Invalid struct type: `$fname`", expr.position)
List.fill(tt.size)(LiteralExpression(0, 1))
}
case (Some(tt: StructType), _) =>
log.error(s"Invalid struct initializer for type `${tt.name}`", expr.position)
List.fill(tt.size)(LiteralExpression(0, 1))
case (Some(tt: PlainType), _) =>
tt.size match {
case 1 => List(expr)
case 2 => List(FunctionCallExpression("lo", List(expr)), FunctionCallExpression("hi", List(expr)))
case n => List.tabulate(n)(i => FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(8 * i, 1))))))
}
case (Some(tt: PointerType), _) => List(FunctionCallExpression("lo", List(expr)), FunctionCallExpression("hi", List(expr)))
case (Some(tt: EnumType), _) => List(FunctionCallExpression("byte", List(expr)))
case (Some(tt), _) =>
log.error("Invalid field type for use in array initializers", expr.position)
List.fill(tt.size)(LiteralExpression(0, 1))
case (None, FunctionCallExpression(fname, fieldValues)) =>
maybeGet[Thing](fname) match {
case Some(tt:StructType) =>
if (tt.fields.length != fieldValues.length) {
log.error(s"Invalid number of struct fields for struct const `${tt.name}`", fieldValues.headOption.flatMap(_.position))
List.fill(tt.size)(LiteralExpression(0, 1))
} else {
tt.fields.zip(fieldValues).flatMap {
case ((fieldTypeName, _), expr) => extractStructArrayContents(expr, Some(get[Type](fieldTypeName)))
}
}
case _ =>
log.error(s"Invalid struct type: `$fname`", expr.position)
Nil
}
case _ =>
log.error(s"Invalid struct initializer for unknown type", expr.position)
Nil
}
}
def checkIfArrayContentsAreSimple(xs: CombinedContents): Unit = {
xs.contents.foreach{
case x:CombinedContents => checkIfArrayContentsAreSimple(x)
case x:LiteralContents => ()
case x => log.error(s"Invalid struct array contents", x.position)
}
}
def extractArrayContents(contents1: ArrayContents): List[Expression] = contents1 match {
case LiteralContents(xs) => xs
case CombinedContents(xs) => xs.flatMap(extractArrayContents)
case pc@ProcessedContents("struct", xs: CombinedContents) =>
checkIfArrayContentsAreSimple(xs)
xs.getAllExpressions.flatMap(x => extractStructArrayContents(x, None))
case pc@ProcessedContents("struct", _) =>
log.error(s"Invalid struct array contents", pc.position)
Nil
case pc@ProcessedContents(f, xs) => pc.getAllExpressions
case ForLoopContents(v, start, end, direction, body) =>
(eval(start), eval(end)) match {

View File

@ -327,18 +327,19 @@ case class ProcessedContents(processor: String, values: ArrayContents) extends A
))
case "long" | "long_le" =>
values.getAllExpressions.flatMap(expr => List(
FunctionCallExpression("byte", List(expr)).pos(expr.position),
FunctionCallExpression("byte", List(FunctionCallExpression(">>", List(expr, LiteralExpression(8, 1))).pos(expr.position))).pos(expr.position),
FunctionCallExpression("byte", List(FunctionCallExpression(">>", List(expr, LiteralExpression(16, 1))).pos(expr.position))).pos(expr.position),
FunctionCallExpression("byte", List(FunctionCallExpression(">>", List(expr, LiteralExpression(24, 1))).pos(expr.position))).pos(expr.position)
FunctionCallExpression("lo", List(expr)).pos(expr.position),
FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(8, 1))).pos(expr.position))).pos(expr.position),
FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(16, 1))).pos(expr.position))).pos(expr.position),
FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(24, 1))).pos(expr.position))).pos(expr.position)
))
case "long_be" =>
values.getAllExpressions.flatMap(expr => List(
FunctionCallExpression("byte", List(FunctionCallExpression(">>", List(expr, LiteralExpression(24, 1))).pos(expr.position))).pos(expr.position),
FunctionCallExpression("byte", List(FunctionCallExpression(">>", List(expr, LiteralExpression(16, 1))).pos(expr.position))).pos(expr.position),
FunctionCallExpression("byte", List(FunctionCallExpression(">>", List(expr, LiteralExpression(8, 1))).pos(expr.position))).pos(expr.position),
FunctionCallExpression("byte", List(expr)).pos(expr.position)
FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(24, 1))).pos(expr.position))).pos(expr.position),
FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(16, 1))).pos(expr.position))).pos(expr.position),
FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(8, 1))).pos(expr.position))).pos(expr.position),
FunctionCallExpression("lo", List(expr)).pos(expr.position)
))
case "struct" => values.getAllExpressions // not used for emitting actual arrays
}
override def replaceVariable(variableToReplace: String, expression: Expression): ArrayContents =

View File

@ -271,6 +271,19 @@ class ArraySuite extends FunSuite with Matchers {
}
}
test("Array filters 2") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
"""
| array x = @long [$1144]
| byte output @$c000
| void main () {
| output = x[0]
| }
""".stripMargin) { m =>
m.readByte(0xc000) should equal(0x44)
}
}
test("Const arrays") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
"""
@ -297,4 +310,34 @@ class ArraySuite extends FunSuite with Matchers {
| }
""".stripMargin)
}
test("Struct array initializers") {
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80)(
"""
| struct p { byte x, byte y }
| struct line { p from, p to }
| struct ratio { int32 num, int32 den }
| const array data1 @$c000 = @struct [p(2,3), p(4,5)]
| const array data2 @$c010 = @struct [line(p(6,7), p(8,9))]
| const array data3 @$c020 = @struct [ratio($666, $777)]
| void main () { }
""".stripMargin) { m =>
m.readByte(0xc000) should equal(2)
m.readByte(0xc001) should equal(3)
m.readByte(0xc002) should equal(4)
m.readByte(0xc003) should equal(5)
m.readByte(0xc010) should equal(6)
m.readByte(0xc011) should equal(7)
m.readByte(0xc012) should equal(8)
m.readByte(0xc013) should equal(9)
m.readByte(0xc020) should equal(0x66)
m.readByte(0xc021) should equal(6)
m.readByte(0xc022) should equal(0)
m.readByte(0xc023) should equal(0)
m.readByte(0xc024) should equal(0x77)
m.readByte(0xc025) should equal(7)
m.readByte(0xc026) should equal(0)
m.readByte(0xc027) should equal(0)
}
}
}