mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-08 22:30:34 +00:00
Support for expressions in file() (fixes #114)
This commit is contained in:
parent
307ad90ecf
commit
73beafd65e
32
src/main/scala/millfork/env/Environment.scala
vendored
32
src/main/scala/millfork/env/Environment.scala
vendored
@ -1749,6 +1749,38 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
|
||||
def extractArrayContents(contents1: ArrayContents): List[Expression] = contents1 match {
|
||||
case LiteralContents(xs) => xs
|
||||
case FileChunkContents(filePath, startE, lengthE) =>
|
||||
val data = Files.readAllBytes(filePath)
|
||||
val p = contents1.position
|
||||
val slice = (eval(startE).map(_.quickSimplify), lengthE.map(l => eval(l).map(_.quickSimplify))) match {
|
||||
case (Some(NumericConstant(start, _)), Some(Some(NumericConstant(length, _)))) =>
|
||||
if (data.length < start) {
|
||||
log.error(s"File $filePath is shorter (${data.length} B) that the start offset $start", p)
|
||||
Array.fill(length.toInt)(0.toByte)
|
||||
} else if (data.length < start + length) {
|
||||
log.error(s"File $filePath is shorter (${data.length} B) that the start offset plus length ${start + length}", p)
|
||||
Array.fill(length.toInt)(0.toByte)
|
||||
} else {
|
||||
data.slice(start.toInt, start.toInt + length.toInt)
|
||||
}
|
||||
case (Some(NumericConstant(start, _)), None) =>
|
||||
if (data.length < start) {
|
||||
log.error(s"File $filePath is shorter (${data.length} B) that the start offset $start", p)
|
||||
Array[Byte](0)
|
||||
} else {
|
||||
data.drop(start.toInt)
|
||||
}
|
||||
case (None, Some(Some(_))) =>
|
||||
log.error(s"Start offset is not a constant", p)
|
||||
Array[Byte](0)
|
||||
case (_, Some(None)) =>
|
||||
log.error(s"Length is not a constant", p)
|
||||
Array[Byte](0)
|
||||
case (None, Some(None)) =>
|
||||
log.error(s"Start offset and length are not constants", p)
|
||||
Array[Byte](0)
|
||||
}
|
||||
slice.map(c => LiteralExpression(c & 0xff, 1)).toList
|
||||
case CombinedContents(xs) => xs.flatMap(extractArrayContents)
|
||||
case pc@ProcessedContents("struct", xs: CombinedContents) =>
|
||||
checkIfArrayContentsAreSimple(xs)
|
||||
|
@ -483,6 +483,21 @@ trait ArrayContents extends Node {
|
||||
def replaceVariable(variableToReplace: String, expression: Expression): ArrayContents
|
||||
}
|
||||
|
||||
case class FileChunkContents(filePath: Path, start: Expression, length: Option[Expression]) extends ArrayContents {
|
||||
def getAllExpressions(bigEndian: Boolean): List[Expression] = length match {
|
||||
case Some(l) => List(start, l)
|
||||
case None => List(start)
|
||||
}
|
||||
def renameVariable(variableToRename: String, newVariable: String): ArrayContents =
|
||||
FileChunkContents(filePath,
|
||||
start.renameVariable(variableToRename, newVariable),
|
||||
length.map(_.renameVariable(variableToRename, newVariable)))
|
||||
def replaceVariable(variableToReplace: String, expression: Expression): ArrayContents =
|
||||
FileChunkContents(filePath,
|
||||
start.replaceVariable(variableToReplace, expression),
|
||||
length.map(_.replaceVariable(variableToReplace, expression)))
|
||||
}
|
||||
|
||||
case class LiteralContents(contents: List[Expression]) extends ArrayContents {
|
||||
override def getAllExpressions(bigEndian: Boolean): List[Expression] = contents
|
||||
|
||||
|
@ -340,35 +340,13 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
p <- "file" ~ HWS ~/ "(" ~/ HWS ~/ position("file name")
|
||||
filePath <- doubleQuotedString ~/ HWS
|
||||
_ <- position("file start")
|
||||
optStart <- ("," ~/ HWS ~/ literalAtom ~/ HWS ~/ Pass).?
|
||||
optStart <- ("," ~/ HWS ~/ mfExpression(nonStatementLevel, false) ~/ HWS ~/ Pass).?
|
||||
_ <- position("slice length")
|
||||
optLength <- ("," ~/ HWS ~/ literalAtom ~/ HWS ~/ Pass).?
|
||||
optLength <- ("," ~/ HWS ~/ mfExpression(nonStatementLevel, false) ~/ HWS ~/ Pass).?
|
||||
_ <- position("closing parentheses")
|
||||
_ <- ")" ~/ Pass
|
||||
} yield {
|
||||
val data = Files.readAllBytes(Paths.get(currentDirectory, filePath))
|
||||
val slice: Array[Byte] = (optStart.map(_.value.toInt), optLength.map(_.value.toInt)) match {
|
||||
case (Some(start), Some(length)) =>
|
||||
if (data.length < start) {
|
||||
log.error(s"File $filePath is shorter (${data.length} B) that the start offset $start", Some(p))
|
||||
Array.fill(length)(0.toByte)
|
||||
} else if (data.length < start + length) {
|
||||
log.error(s"File $filePath is shorter (${data.length} B) that the start offset plus length ${start + length}", Some(p))
|
||||
Array.fill(length)(0.toByte)
|
||||
} else {
|
||||
data.slice(start, start + length)
|
||||
}
|
||||
case (Some(start), None) =>
|
||||
if (data.length < start) {
|
||||
log.error(s"File $filePath is shorter (${data.length} B) that the start offset $start", Some(p))
|
||||
Array[Byte](0)
|
||||
} else {
|
||||
data.drop(start)
|
||||
}
|
||||
case (None, None) => data
|
||||
case _ => throw new IllegalStateException("error parsing file()")
|
||||
}
|
||||
LiteralContents(slice.map(c => LiteralExpression(c & 0xff, 1)).toList)
|
||||
FileChunkContents(Paths.get(currentDirectory, filePath), optStart.getOrElse(LiteralExpression(0, 1)), optLength)
|
||||
}
|
||||
|
||||
def arrayStringContents: P[ArrayContents] = textLiteral.map(LiteralContents)
|
||||
|
1
src/test/resources/binarydata
Normal file
1
src/test/resources/binarydata
Normal file
@ -0,0 +1 @@
|
||||
AAAAAAAAA123456789
|
@ -698,4 +698,20 @@ class ArraySuite extends FunSuite with Matchers with AppendedClues {
|
||||
m.readWord(0xc022) should equal(2)
|
||||
}
|
||||
}
|
||||
|
||||
test("Arrays from file") {
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos)(
|
||||
"""
|
||||
| const byte nine = 9
|
||||
| array dummy @$4000 = file("src/test/resources/binarydata", nine, 3*3)
|
||||
|
|
||||
| void main () {
|
||||
|
|
||||
| }
|
||||
| noinline byte f(byte x) = x
|
||||
""".stripMargin) { m =>
|
||||
m.readByte(0x4000) should equal('1'.toInt)
|
||||
m.readByte(0x4008) should equal('9'.toInt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user