mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-09 13:31:32 +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 {
|
def extractArrayContents(contents1: ArrayContents): List[Expression] = contents1 match {
|
||||||
case LiteralContents(xs) => xs
|
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 CombinedContents(xs) => xs.flatMap(extractArrayContents)
|
||||||
case pc@ProcessedContents("struct", xs: CombinedContents) =>
|
case pc@ProcessedContents("struct", xs: CombinedContents) =>
|
||||||
checkIfArrayContentsAreSimple(xs)
|
checkIfArrayContentsAreSimple(xs)
|
||||||
|
@ -483,6 +483,21 @@ trait ArrayContents extends Node {
|
|||||||
def replaceVariable(variableToReplace: String, expression: Expression): ArrayContents
|
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 {
|
case class LiteralContents(contents: List[Expression]) extends ArrayContents {
|
||||||
override def getAllExpressions(bigEndian: Boolean): List[Expression] = contents
|
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")
|
p <- "file" ~ HWS ~/ "(" ~/ HWS ~/ position("file name")
|
||||||
filePath <- doubleQuotedString ~/ HWS
|
filePath <- doubleQuotedString ~/ HWS
|
||||||
_ <- position("file start")
|
_ <- position("file start")
|
||||||
optStart <- ("," ~/ HWS ~/ literalAtom ~/ HWS ~/ Pass).?
|
optStart <- ("," ~/ HWS ~/ mfExpression(nonStatementLevel, false) ~/ HWS ~/ Pass).?
|
||||||
_ <- position("slice length")
|
_ <- position("slice length")
|
||||||
optLength <- ("," ~/ HWS ~/ literalAtom ~/ HWS ~/ Pass).?
|
optLength <- ("," ~/ HWS ~/ mfExpression(nonStatementLevel, false) ~/ HWS ~/ Pass).?
|
||||||
_ <- position("closing parentheses")
|
_ <- position("closing parentheses")
|
||||||
_ <- ")" ~/ Pass
|
_ <- ")" ~/ Pass
|
||||||
} yield {
|
} yield {
|
||||||
val data = Files.readAllBytes(Paths.get(currentDirectory, filePath))
|
FileChunkContents(Paths.get(currentDirectory, filePath), optStart.getOrElse(LiteralExpression(0, 1)), optLength)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def arrayStringContents: P[ArrayContents] = textLiteral.map(LiteralContents)
|
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)
|
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