diff --git a/CHANGELOG.md b/CHANGELOG.md index d67ecf29..4c805d05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ * Added multiple new text codecs. +* Added character literals. + * Fixed several bugs, most importantly invalid offsets for branching instructions. * Other improvements. diff --git a/docs/lang/literals.md b/docs/lang/literals.md index 1b7a644e..00a52fa2 100644 --- a/docs/lang/literals.md +++ b/docs/lang/literals.md @@ -36,7 +36,7 @@ Currently available encodings: * `bbc` – BBC Micro and ZX Spectrum character set -* `jis` – JIS X 0201 +* `jis` or `jisx` – JIS X 0201 * `iso_de`, `iso_no`, `iso_se`, `iso_yu` – various variants of ISO/IEC-646 @@ -46,6 +46,13 @@ When programming for Commodore, use `pet` for strings you're printing using standard I/O routines and `scr` for strings you're copying to screen memory directly. +## Character literals + +Character literals are surrounded by single quotes and followed by the name of the encoding: + + 'x' ascii + +From the type system point of view, they are constants of type byte. ## Array initialisers diff --git a/src/main/scala/millfork/parser/MfParser.scala b/src/main/scala/millfork/parser/MfParser.scala index 68e6987c..81249ce3 100644 --- a/src/main/scala/millfork/parser/MfParser.scala +++ b/src/main/scala/millfork/parser/MfParser.scala @@ -53,6 +53,28 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o // def operator: P[String] = P(CharsWhileIn("!-+*/><=~|&^", min=1).!) // TODO: only valid operators + private val invalidCharLiteralTypes = Set[Int]( + Character.LINE_SEPARATOR, + Character.PARAGRAPH_SEPARATOR, + Character.CONTROL, + Character.PRIVATE_USE, + Character.SURROGATE, + Character.UNASSIGNED) + + def charAtom: P[LiteralExpression] = for { + p <- position() + c <- "'" ~/ CharPred(c => c >= ' ' && !invalidCharLiteralTypes(Character.getType(c))).! ~/ "'" + co <- HWS ~ codec + } yield { + co.encode(Some(p), c.charAt(0)) match { + case List(value) => + LiteralExpression(value, 1) + case _ => + ErrorReporting.error(s"Character `$c` cannot be encoded as one byte", Some(p)) + LiteralExpression(0, 1) + } + } + // TODO: 3-byte types def size(value: Int, wordLiteral: Boolean, longLiteral: Boolean): Int = if (value > 255 || value < -128 || wordLiteral) @@ -120,7 +142,7 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o LiteralExpression(value, size(value, s.length > 4, s.length > 8)).pos(p) } - val literalAtom: P[LiteralExpression] = binaryAtom | hexAtom | octalAtom | quaternaryAtom | decimalAtom + val literalAtom: P[LiteralExpression] = charAtom | binaryAtom | hexAtom | octalAtom | quaternaryAtom | decimalAtom val atom: P[Expression] = P(literalAtom | (position() ~ identifier).map { case (p, i) => VariableExpression(i).pos(p) }) @@ -200,7 +222,7 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o val doubleQuotedString: P[List[Char]] = P("\"" ~/ CharsWhile(c => c != '\"' && c != '\n' && c != '\r').! ~ "\"").map(_.toList) - val codec: P[TextCodec] = P(position() ~ identifier).map { + def codec: P[TextCodec] = P(position() ~ identifier).map { case (_, "ascii") => TextCodec.Ascii case (_, "petscii") => TextCodec.Petscii case (_, "pet") => TextCodec.Petscii diff --git a/src/test/scala/millfork/test/TextCodecSuite.scala b/src/test/scala/millfork/test/TextCodecSuite.scala new file mode 100644 index 00000000..b5fdf0bc --- /dev/null +++ b/src/test/scala/millfork/test/TextCodecSuite.scala @@ -0,0 +1,25 @@ +package millfork.test + +import millfork.test.emu.{EmuBenchmarkRun, EmuUnoptimizedRun} +import org.scalatest.{FunSuite, Matchers} + +/** + * @author Karol Stasiak + */ +class TextCodecSuite extends FunSuite with Matchers { + + test("Char literals") { + val m = EmuUnoptimizedRun( + """ + | void main() { + | if 'a'ascii != 'a' ascii { poke($bfff, 0) } + | if '¥'jis != '\' ascii { poke($bffe, 0) } + | if '£'pet != '\' ascii { poke($bffd, 0) } + | if '£'bbc != 'é' iso_se { poke($bffc, 0) } + | } + | macro asm void poke(word const addr, byte a) { + | STA addr + | } + """.stripMargin) + } +}