From 0333b339ca5c072af7162061f9a1286e1d9ba753 Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Fri, 10 Jan 2020 18:37:49 +0100 Subject: [PATCH] Allow omitting length in `file` array initalizers (fixes #35) --- docs/lang/literals.md | 6 ++++- examples/nes/pong.mfk | 2 +- src/main/scala/millfork/parser/MfParser.scala | 25 ++++++++++++++++--- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/docs/lang/literals.md b/docs/lang/literals.md index aa9dd071..f878cf56 100644 --- a/docs/lang/literals.md +++ b/docs/lang/literals.md @@ -175,6 +175,7 @@ An array is initialized with either: array b = "----" scr array c = ["hello world!" ascii, 13] array d = file("d.bin") + array d1 = file("d.bin", 128) array e = file("d.bin", 128, 256) array f = for x,0,until,8 [x * 3 + 5] // equivalent to [5, 8, 11, 14, 17, 20, 23, 26] array(point) g = [point(2,3), point(5,6)] @@ -183,7 +184,10 @@ An array is initialized with either: Trailing commas (`[1, 2,]`) are not allowed. The parameters for `file` are: file path, optional start offset, optional length -(start offset and length have to be either both present or both absent). +(if only two parameters are present, then the second one is assumed to be the start offset). +The `file` expression is expanded at the compile time to an array of bytes equal to the bytes contained in the file. +If the start offset is present, then that many bytes at the start of the file are skipped. +If the length is present, then only that many bytes are taken, otherwise, all bytes until the end of the file are taken. The `for`-style expression has a variable, a starting index, a direction, a final index, and a parameterizable array initializer. diff --git a/examples/nes/pong.mfk b/examples/nes/pong.mfk index 1b88dd9c..118491e9 100644 --- a/examples/nes/pong.mfk +++ b/examples/nes/pong.mfk @@ -627,4 +627,4 @@ const array attribute = [ // *CHARACTER ROM (GRAPHICS)* -segment(chrrom) const array graphics @ $0000 = file("tiles.chr") +segment(chrrom) const array graphics @ $0000 = file("tiles.chr", 0) diff --git a/src/main/scala/millfork/parser/MfParser.scala b/src/main/scala/millfork/parser/MfParser.scala index e0be18a7..a0e18cc8 100644 --- a/src/main/scala/millfork/parser/MfParser.scala +++ b/src/main/scala/millfork/parser/MfParser.scala @@ -229,12 +229,31 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri val arrayFileContents: P[ArrayContents] = for { p <- "file" ~ HWS ~/ "(" ~/ HWS ~/ position("file name") filePath <- doubleQuotedString ~/ HWS - optSlice <- ("," ~/ HWS ~/ literalAtom ~/ HWS ~/ "," ~/ HWS ~/ literalAtom ~/ HWS ~/ Pass).? + optStart <- ("," ~/ HWS ~/ literalAtom ~/ HWS ~/ Pass).? + optLength <- ("," ~/ HWS ~/ literalAtom ~/ HWS ~/ Pass).? _ <- ")" ~/ Pass } yield { val data = Files.readAllBytes(Paths.get(currentDirectory, filePath)) - val slice = optSlice.fold(data) { - case (start, length) => data.slice(start.value.toInt, start.value.toInt + length.value.toInt) + 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) }