From c4e6485e899fe8a5c32e85afb9daa7bb508214b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Tue, 1 Apr 2025 11:43:12 +0200 Subject: [PATCH 01/23] Add binstream as in stdlib/draw --- libraries/common/binstream.effekt | 401 ++++++++++++++++++++++++++++++ 1 file changed, 401 insertions(+) create mode 100644 libraries/common/binstream.effekt diff --git a/libraries/common/binstream.effekt b/libraries/common/binstream.effekt new file mode 100644 index 000000000..413485766 --- /dev/null +++ b/libraries/common/binstream.effekt @@ -0,0 +1,401 @@ +import option +import stringbuffer +import stream +import char +import test +import io/error +import bytearray + +// assumes by default: +// BE byteorder, BE bitorder, unsigned + +// Wrappers +// -------- +record BE[A](raw: A) +record LE[A](raw: A) +record OfWidth[A](raw: A, width: Int) +record Signed[A](raw: A) + +/// Bits +type Bit { B0(); B1() } + +/// Effect alias +effect HexSplices = { + splice[Char], splice[String], + splice[Unit], + splice[Int], + splice[Byte], + splice[BE[Int]], splice[LE[Int]], + splice[LE[Signed[Int]]], splice[OfWidth[LE[Int]]], splice[OfWidth[LE[Signed[Int]]]], + splice[BE[Signed[Int]]], splice[OfWidth[BE[Int]]], splice[OfWidth[BE[Signed[Int]]]], + splice[ByteArray] +} + +// Splitting +// --------- +def bytesLE(int: Int, w: Int): Unit / emit[Byte] = { + var c = int + repeat(w){ + do emit(mod(c, 256).toByte) + c = c / 256 + } +} +def bytesLE(int: Int): Unit / emit[Byte] = bytesLE(int, 4) +def bytesBE(n: Int, width: Int): Unit / emit[Byte] = { + var pos = pow(256, width - 1) + repeat(width){ + do emit((bitwiseAnd(n, pos * 255) / pos).toByte) + pos = pos / 256 + } +} +def bytesBE(n: Int): Unit / emit[Byte] = bytesBE(n, 4) +def bytes(n: Int): Unit / emit[Byte] = bytesBE(n) +def signedBytesLE(int: Int, width: Int): Unit / emit[Byte] = { + if (int < 0) { + bytesLE(bitwiseNot(neg(int)) + 1, width) + } else { + bytesLE(int, width) + } +} +def signedBytesBE(int: Int, width: Int): Unit / emit[Byte] = { + if (int < 0) { + bytesBE(bitwiseNot(neg(int)) + 1, width) + } else { + bytesBE(int, width) + } +} +def signedBytesLE(int: Int): Unit / emit[Byte] = signedBytesLE(int, 4) +def signedBytesBE(int: Int): Unit / emit[Byte] = signedBytesBE(int, 4) +def bitsBE(int: Int): Unit / emit[Bit] = bitsBE(int, 32) +def collectBitsBE{ body: => Unit / emit[Bit] }: Int = { + var res = 0 + try body() with emit[Bit] { b => + res = b match { + case B0() => res * 2 + case B1() => res * 2 + 1 + } + resume(()) + } + res +} +def not(b: Bit): Bit = b match { + case B0() => B1() + case B1() => B0() +} +def bitwiseNot(n: Int): Int = { + collectBitsBE{ + try bitsBE(n) with emit[Bit]{ b => resume(do emit(not(b))) } + } +} + +// Splicers +// -------- + +def hex{ body: => Unit / { literal, HexSplices } }: Unit / emit[Byte] = { + try { + try { + body() + } + with splice[String] { s => + feed(s){ exhaustively{ do splice[Char](do read[Char]()) } } + resume(()) + } + with splice[ByteArray] { ba => + ba.foreach{ b => do splice[Byte](b) } + resume(()) + } + } + with literal { s => + feed(s){ + exhaustively { + with on[MissingValue].default{ () } + val upper: Int = hexDigitValue(do read[Char]()).value + val lower: Int = hexDigitValue(do read[Char]()).value + do emit[Byte]((16 * upper + lower).toByte) + } + } + resume(()) + } + with splice[Char] { c => do emit[Byte](c.toInt.toByte); resume(()) } + with splice[Byte] { b => do emit(b); resume(()) } + with splice[Unit] { u => resume(()) } + with splice[Int] { n => bytesBE(n); resume(()) } + with splice[LE[Int]] { w => bytesLE(w.raw); resume(()) } + with splice[BE[Int]] { v => bytesBE(v.raw); resume(()) } + with splice[LE[Signed[Int]]] { w => signedBytesLE(w.raw.raw); resume(()) } + with splice[OfWidth[LE[Int]]] { w => bytesLE(w.raw.raw, w.width); resume(()) } + with splice[OfWidth[LE[Signed[Int]]]] { w => signedBytesLE(w.raw.raw.raw, w.width); resume(()) } + with splice[BE[Signed[Int]]] { w => signedBytesBE(w.raw.raw); resume(()) } + with splice[OfWidth[BE[Int]]] { w => bytesBE(w.raw.raw, w.width); resume(()) } + with splice[OfWidth[BE[Signed[Int]]]] { w => signedBytesBE(w.raw.raw.raw, w.width); resume(()) } +} + +def x{ body: => Unit / { literal, HexSplices } }: Int = { + var res = 0 + for[Byte]{ hex{body} }{ v => res = res * 256 + v.toInt } + res +} + +// Counting and padding +// -------------------- +effect pad[A](fac: Int){ gen: => A }: Unit +effect getPos(): Int +def tracking[A]{ body: => Unit / { emit[A], getPos, pad[A] } }: Unit / emit[A] = { + var n = 0 + try body() + with emit[A] { b => n = n + 1; resume(do emit[A](b)) } + with getPos{ resume(n) } + with pad[A] { fac => + resume { {gen} => + while(mod(n, fac) != 0){ + do emit[A](gen()) + n = n + 1 + } + } + } +} + +// Sub-Byte +// ======== + +// From/to Bytes +// ------------- +def bitsLE(byte: Byte): Unit / emit[Bit] = { + val v = byte.toInt + var mask = 1 + repeat(8){ + bitwiseAnd(v, mask) match { + case 0 => do emit(B0()) + case _ => do emit(B1()) + } + mask = mask * 2 + } +} +def bitsBE(byte: Byte): Unit / emit[Bit] = { + val v = byte.toInt + var mask = 128 + repeat(8){ + bitwiseAnd(v, mask) match { + case 0 => do emit(B0()) + case _ => do emit(B1()) + } + mask = mask / 2 + } +} +def bits(byte: Byte): Unit / emit[Bit] = bitsBE(byte) +def bitsLE(v: Int, width: Int): Unit / emit[Bit] = { + var mask = 1 + repeat(width){ + bitwiseAnd(v, mask) match { + case 0 => do emit(B0()) + case _ => do emit(B1()) + } + mask = mask * 2 + } +} +def pow(n: Int, exp: Int): Int = { + def go(n: Int, exp: Int, acc: Int): Int = { + if (exp == 0) { + acc + } else if (mod(exp, 2) == 0) { + go(n * n, exp / 2, acc) + } else { + go(n * n, exp / 2, acc * n) + } + } + go(n, exp, 1) +} +def bitsBE(v: Int, width: Int): Unit / emit[Bit] = { + var mask = pow(2, width - 1) + repeat(width){ + bitwiseAnd(v, mask) match { + case 0 => do emit(B0()) + case _ => do emit(B1()) + } + mask = mask / 2 + } +} +def ungroupBytes{ body: => Unit / emit[Byte] }: Unit / emit[Bit] = + for[Byte]{body}{ b => bits(b) } +def twoscomplementLE{ body: => Unit / emit[Bit] }: Unit / emit[Bit] = { + var carry = true + try body() + with emit[Bit] { + case B0() => if(carry) { do emit(B0()) } else { do emit(B1()) }; resume(()) + case B1() => if(carry) { do emit(B1()); carry = false } else { do emit(B0()) }; resume(()) + } +} +def groupBytesBE{ body: => Unit / emit[Bit] }: Unit / emit[Byte] = { + var next = 0 + var p = 128 + for[Bit]{body}{ b => + b match { + case B0() => () + case B1() => next = next + p + } + p = p / 2 + if(p == 0) { + do emit(next.toByte) + next = 0 + p = 128 + } + } +} +def groupBytesLE{ body: => Unit / emit[Bit] }: Unit / emit[Byte] = { + var next = 0 + var p = 1 + for[Bit]{body}{ b => + b match { + case B0() => () + case B1() => next = next + p + } + p = p * 2 + if(p == 256) { + do emit(next.toByte) + next = 0 + p = 1 + } + } +} +def groupBytes{ body: => Unit / emit[Bit] }: Unit / emit[Byte] = + groupBytesBE{body} + +def nth[A](n: Int){ body: => Unit / emit[A] }: A / Exception[MissingValue] = { + var m = n + try { + body() + val r: A = do raise[MissingValue](MissingValue(), "code in first did not emit any values") + r + } with emit[A] { a => + if (m == 0) { + a + } else { + m = m - 1 + resume(()) + } + } +} +def first[A]{ body: => Unit / emit[A] }: A / Exception[MissingValue] = { + try { + body() + val r: A = do raise[MissingValue](MissingValue(), "code in first did not emit any values") + r + } with emit[A] { a => a } +} +// Literals/splices +// ---------------- +effect BinSplices = { + splice[Unit], splice[Bit], + splice[Byte], + splice[LE[Int]], splice[BE[Int]], + splice[LE[Signed[Int]]], splice[BE[Signed[Int]]], + splice[OfWidth[LE[Int]]], splice[OfWidth[BE[Int]]], + splice[OfWidth[LE[Signed[Int]]]], splice[OfWidth[BE[Signed[Int]]]] +} +def bit{ body: => Unit / { literal, BinSplices } }: Unit / emit[Bit] = { + try { + ungroupBytes{ + try { + body() + } + with splice[LE[Int]] { i => bytesLE(i.raw); resume(()) } + with splice[BE[Int]] { i => bytesBE(i.raw); resume(()) } + with splice[LE[Signed[Int]]] { i => signedBytesLE(i.raw.raw); resume(()) } + with splice[BE[Signed[Int]]] { i => signedBytesBE(i.raw.raw); resume(()) } + } + } + with literal { s => + feed(s){ + exhaustively { + do read[Char]() match { + case '0' => do emit(B0()) + case '1' => do emit(B1()) + case _ => () + } + } + } + resume(()) + } + with splice[Unit] { _ => resume(()) } + with splice[Bit] { b => do emit(b); resume(()) } + with splice[Byte] { b => bits(b); resume(()) } + with splice[OfWidth[LE[Int]]] { i => + bitsLE(i.raw.raw, i.width) + resume(()) + } + with splice[OfWidth[LE[Signed[Int]]]] { i => + if(i.raw.raw.raw < 0){ + twoscomplementLE{ bitsLE(0 - i.raw.raw.raw, i.width) } + } else { + bitsLE(i.raw.raw.raw, i.width) + } + resume(()) + } + with splice[OfWidth[BE[Int]]] { i => + bitsBE(i.raw.raw, i.width) + resume(()) + } + with splice[OfWidth[BE[Signed[Int]]]] { i => + collectList[Bit]{ + if(i.raw.raw.raw < 0){ + twoscomplementLE{ bitsLE(0 - i.raw.raw.raw, i.width) } + } else { + bitsLE(i.raw.raw.raw, i.width) + } + }.reverse.each + resume(()) + } +} + +namespace examples { + def main() = { + mainSuite("Simple literals"){ + test("literal hex 10"){ assertEqual(x"10${()}", 16) } + test("literal hex ff"){ assertEqual(x"ff${()}", 255) } + test("literal char a"){ assertEqual(x"${'a'}", x"61${()}") } + test("literal string ba"){ assertEqual(x"${"ba"}", x"62${()}" * 256 + x"61${()}") } + test("int back-and-forth (17)"){ assertEqual(x"${17}", 17)} + test("int back-and-forth (17), explicit BE"){ assertEqual(x"${17.BE}", 17) } + test("int back-and-forth (17), explicit LE"){ assertEqual(x"${17.LE}", 17 * 256 * 256 * 256) } + test("byte 00101010"){ + with on[MissingValue].default{ assertEqual(true, false) } + assertEqual(first[Byte]{groupBytes{ bit"00101010${()}" }}.toInt, 42) + } + test("to bits and back"){ + with on[MissingValue].default{ assertEqual(true, false) } + [42.toByte, 12.toByte, 113.toByte, 0.toByte, 255.toByte].foreach{ v => + assertEqual(first[Byte]{ groupBytes{ bits(v) } }, v) + } + } + test("to bits and back LE bitorder"){ + with on[MissingValue].default{ assertEqual(true, false) } + [42.toByte, 12.toByte, 113.toByte, 0.toByte, 255.toByte].foreach{ v => + assertEqual(first[Byte]{ groupBytesLE{ bitsLE(v) } }, v) + } + } + test("to bits and back BE bitorder"){ + with on[MissingValue].default{ assertEqual(true, false) } + [42.toByte, 12.toByte, 113.toByte, 0.toByte, 255.toByte].foreach{ v => + assertEqual(first[Byte]{ groupBytesBE{ bitsBE(v) } }, v) + } + } + test("append 0 means *2"){ + with on[MissingValue].default{ assertEqual(true, false) } + [42.toByte, 12.toByte, 127.toByte].foreach{ v => + assertEqual(nth[Byte](1){ groupBytes{ repeat(7){ do emit(B0()) }; bits(v); do emit(B0()) } }, (v.toInt * 2).toByte) + } + } + test("pow agrees with double one"){ + assertEqual(pow(2,5), pow(2.0,5).toInt) + } + test("LE 2s-complement"){ + with on[MissingValue].default{ assertEqual(true, false) } + assertEqual(first[Byte]{ groupBytesLE{ twoscomplementLE{ bitsLE(6.toByte) } } }, 250.toByte) + } + test("BE 2s-complement"){ + with on[MissingValue].default{ assertEqual(true, false) } + assertEqual(first[Byte]{ groupBytesBE{ bit"${-6.Signed.BE.OfWidth(8)}" } }, 250.toByte) + } + } + } +} \ No newline at end of file From c501ec0dd57c34cf469fa954800f1ba954276d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Tue, 1 Apr 2025 11:43:52 +0200 Subject: [PATCH 02/23] Allow tracking with a given start position --- libraries/common/binstream.effekt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/common/binstream.effekt b/libraries/common/binstream.effekt index 413485766..45a604fbd 100644 --- a/libraries/common/binstream.effekt +++ b/libraries/common/binstream.effekt @@ -140,8 +140,8 @@ def x{ body: => Unit / { literal, HexSplices } }: Int = { // -------------------- effect pad[A](fac: Int){ gen: => A }: Unit effect getPos(): Int -def tracking[A]{ body: => Unit / { emit[A], getPos, pad[A] } }: Unit / emit[A] = { - var n = 0 +def tracking[A](init: Int){ body: => Unit / { emit[A], getPos, pad[A] } }: Unit / emit[A] = { + var n = init try body() with emit[A] { b => n = n + 1; resume(do emit[A](b)) } with getPos{ resume(n) } @@ -154,6 +154,8 @@ def tracking[A]{ body: => Unit / { emit[A], getPos, pad[A] } }: Unit / emit[A] = } } } +def tracking[A]{ body: => Unit / { emit[A], getPos, pad[A] } }: Unit / emit[A] = + tracking[A](0){body} // Sub-Byte // ======== From dfaa06bb7bf6a152bc6aaced78e89c3fdebb8717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Tue, 1 Apr 2025 11:50:27 +0200 Subject: [PATCH 03/23] Start adding doc comments --- libraries/common/binstream.effekt | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/libraries/common/binstream.effekt b/libraries/common/binstream.effekt index 45a604fbd..519255b53 100644 --- a/libraries/common/binstream.effekt +++ b/libraries/common/binstream.effekt @@ -11,15 +11,20 @@ import bytearray // Wrappers // -------- +/// A with explicit big-endian order record BE[A](raw: A) +/// A with explicit little-endian order record LE[A](raw: A) +/// A with explicit width in current unit +/// (bits for bitstreams, bytes for bytestreams) record OfWidth[A](raw: A, width: Int) +/// explicitly signed A record Signed[A](raw: A) /// Bits type Bit { B0(); B1() } -/// Effect alias +/// Splices allowed in hex/byte stream literals effect HexSplices = { splice[Char], splice[String], splice[Unit], @@ -33,6 +38,8 @@ effect HexSplices = { // Splitting // --------- + +/// emit bytes of the given int as a w bytes in little-endian byte order def bytesLE(int: Int, w: Int): Unit / emit[Byte] = { var c = int repeat(w){ @@ -40,7 +47,11 @@ def bytesLE(int: Int, w: Int): Unit / emit[Byte] = { c = c / 256 } } + +/// emit bytes of the given int as a 4 bytes in little-endian byte order def bytesLE(int: Int): Unit / emit[Byte] = bytesLE(int, 4) + +/// emit bytes of the given int as a w bytes in big-endian byte order def bytesBE(n: Int, width: Int): Unit / emit[Byte] = { var pos = pow(256, width - 1) repeat(width){ @@ -48,8 +59,14 @@ def bytesBE(n: Int, width: Int): Unit / emit[Byte] = { pos = pos / 256 } } + +/// emit bytes of the given int as a 4 bytes in big-endian byte order def bytesBE(n: Int): Unit / emit[Byte] = bytesBE(n, 4) + +/// emit bytes of the given int as a 4 bytes in big-endian byte order def bytes(n: Int): Unit / emit[Byte] = bytesBE(n) + +/// emit bytes of the given int as width bytes (in 2s-complement) in little-endian byte order def signedBytesLE(int: Int, width: Int): Unit / emit[Byte] = { if (int < 0) { bytesLE(bitwiseNot(neg(int)) + 1, width) @@ -57,6 +74,7 @@ def signedBytesLE(int: Int, width: Int): Unit / emit[Byte] = { bytesLE(int, width) } } +/// emit bytes of the given int as width bytes (in 2s-complement) in big-endian byte order def signedBytesBE(int: Int, width: Int): Unit / emit[Byte] = { if (int < 0) { bytesBE(bitwiseNot(neg(int)) + 1, width) @@ -64,8 +82,13 @@ def signedBytesBE(int: Int, width: Int): Unit / emit[Byte] = { bytesBE(int, width) } } + +/// emit bytes of the given int as 4 bytes (in 2s-complement) in little-endian byte order def signedBytesLE(int: Int): Unit / emit[Byte] = signedBytesLE(int, 4) + +/// emit bytes of the given int as 4 bytes (in 2s-complement) in big-endian byte order def signedBytesBE(int: Int): Unit / emit[Byte] = signedBytesBE(int, 4) + def bitsBE(int: Int): Unit / emit[Bit] = bitsBE(int, 32) def collectBitsBE{ body: => Unit / emit[Bit] }: Int = { var res = 0 From 5e0f2c6d764754d76dc81a6cca38b2389b5250ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Tue, 1 Apr 2025 11:55:46 +0200 Subject: [PATCH 04/23] More docs and structure --- libraries/common/binstream.effekt | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/libraries/common/binstream.effekt b/libraries/common/binstream.effekt index 519255b53..d9a88de2f 100644 --- a/libraries/common/binstream.effekt +++ b/libraries/common/binstream.effekt @@ -24,6 +24,12 @@ record Signed[A](raw: A) /// Bits type Bit { B0(); B1() } +/// not on Bits +def not(b: Bit): Bit = b match { + case B0() => B1() + case B1() => B0() +} + /// Splices allowed in hex/byte stream literals effect HexSplices = { splice[Char], splice[String], @@ -89,7 +95,10 @@ def signedBytesLE(int: Int): Unit / emit[Byte] = signedBytesLE(int, 4) /// emit bytes of the given int as 4 bytes (in 2s-complement) in big-endian byte order def signedBytesBE(int: Int): Unit / emit[Byte] = signedBytesBE(int, 4) +/// emit bits of the given int as 32 Bits in big-endian bit order def bitsBE(int: Int): Unit / emit[Bit] = bitsBE(int, 32) + +/// collect bits in big-endian bit order into an Int def collectBitsBE{ body: => Unit / emit[Bit] }: Int = { var res = 0 try body() with emit[Bit] { b => @@ -101,11 +110,10 @@ def collectBitsBE{ body: => Unit / emit[Bit] }: Int = { } res } -def not(b: Bit): Bit = b match { - case B0() => B1() - case B1() => B0() -} + +/// bitwise not on integers def bitwiseNot(n: Int): Int = { + // TODO currently implemented in Effekt, should use extern ? collectBitsBE{ try bitsBE(n) with emit[Bit]{ b => resume(do emit(not(b))) } } @@ -114,6 +122,8 @@ def bitwiseNot(n: Int): Int = { // Splicers // -------- +/// Splicer to emit the bytes in hex notation given, plus evenutal splices +/// Ignores whitespace def hex{ body: => Unit / { literal, HexSplices } }: Unit / emit[Byte] = { try { try { @@ -153,6 +163,7 @@ def hex{ body: => Unit / { literal, HexSplices } }: Unit / emit[Byte] = { with splice[OfWidth[BE[Signed[Int]]]] { w => signedBytesBE(w.raw.raw.raw, w.width); resume(()) } } +/// convert the given hex notation to an integer (big-endian) def x{ body: => Unit / { literal, HexSplices } }: Int = { var res = 0 for[Byte]{ hex{body} }{ v => res = res * 256 + v.toInt } @@ -161,8 +172,16 @@ def x{ body: => Unit / { literal, HexSplices } }: Int = { // Counting and padding // -------------------- +// TODO could also be part of stream library + +/// Request padding to a multiple of fac, by calling gen for each effect pad[A](fac: Int){ gen: => A }: Unit + +/// get current position in stream effect getPos(): Int + +/// Track position in stream, starting with init +/// Handles getPos and pad def tracking[A](init: Int){ body: => Unit / { emit[A], getPos, pad[A] } }: Unit / emit[A] = { var n = init try body() @@ -177,6 +196,9 @@ def tracking[A](init: Int){ body: => Unit / { emit[A], getPos, pad[A] } }: Unit } } } + +/// Track how many bytes were emitted +/// Handles getPos and pad def tracking[A]{ body: => Unit / { emit[A], getPos, pad[A] } }: Unit / emit[A] = tracking[A](0){body} From d185d7db2f22477a88c005b01c495b51b121250a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Tue, 1 Apr 2025 12:00:45 +0200 Subject: [PATCH 05/23] more docs and structure --- libraries/common/binstream.effekt | 68 +++++++++++++++---------------- libraries/common/effekt.effekt | 16 ++++++++ 2 files changed, 50 insertions(+), 34 deletions(-) diff --git a/libraries/common/binstream.effekt b/libraries/common/binstream.effekt index d9a88de2f..cf0c8b92f 100644 --- a/libraries/common/binstream.effekt +++ b/libraries/common/binstream.effekt @@ -95,29 +95,7 @@ def signedBytesLE(int: Int): Unit / emit[Byte] = signedBytesLE(int, 4) /// emit bytes of the given int as 4 bytes (in 2s-complement) in big-endian byte order def signedBytesBE(int: Int): Unit / emit[Byte] = signedBytesBE(int, 4) -/// emit bits of the given int as 32 Bits in big-endian bit order -def bitsBE(int: Int): Unit / emit[Bit] = bitsBE(int, 32) - -/// collect bits in big-endian bit order into an Int -def collectBitsBE{ body: => Unit / emit[Bit] }: Int = { - var res = 0 - try body() with emit[Bit] { b => - res = b match { - case B0() => res * 2 - case B1() => res * 2 + 1 - } - resume(()) - } - res -} -/// bitwise not on integers -def bitwiseNot(n: Int): Int = { - // TODO currently implemented in Effekt, should use extern ? - collectBitsBE{ - try bitsBE(n) with emit[Bit]{ b => resume(do emit(not(b))) } - } -} // Splicers // -------- @@ -207,6 +185,7 @@ def tracking[A]{ body: => Unit / { emit[A], getPos, pad[A] } }: Unit / emit[A] = // From/to Bytes // ------------- +/// emit bits of the given Byte as 8 Bits in little-endian bit order def bitsLE(byte: Byte): Unit / emit[Bit] = { val v = byte.toInt var mask = 1 @@ -218,6 +197,8 @@ def bitsLE(byte: Byte): Unit / emit[Bit] = { mask = mask * 2 } } + +/// emit bits of the given Byte as 8 Bits in big-endian bit order def bitsBE(byte: Byte): Unit / emit[Bit] = { val v = byte.toInt var mask = 128 @@ -229,7 +210,11 @@ def bitsBE(byte: Byte): Unit / emit[Bit] = { mask = mask / 2 } } + +/// emit bits of the given Byte as 8 Bits in big-endian bit order def bits(byte: Byte): Unit / emit[Bit] = bitsBE(byte) + +/// emit bits of the given Byte as width Bits in little-endian bit order def bitsLE(v: Int, width: Int): Unit / emit[Bit] = { var mask = 1 repeat(width){ @@ -240,18 +225,8 @@ def bitsLE(v: Int, width: Int): Unit / emit[Bit] = { mask = mask * 2 } } -def pow(n: Int, exp: Int): Int = { - def go(n: Int, exp: Int, acc: Int): Int = { - if (exp == 0) { - acc - } else if (mod(exp, 2) == 0) { - go(n * n, exp / 2, acc) - } else { - go(n * n, exp / 2, acc * n) - } - } - go(n, exp, 1) -} + +/// emit bits of the given int as width Bits in big-endian bit order def bitsBE(v: Int, width: Int): Unit / emit[Bit] = { var mask = pow(2, width - 1) repeat(width){ @@ -262,6 +237,31 @@ def bitsBE(v: Int, width: Int): Unit / emit[Bit] = { mask = mask / 2 } } + +/// emit bits of the given int as 32 Bits in big-endian bit order +def bitsBE(int: Int): Unit / emit[Bit] = bitsBE(int, 32) + +/// collect bits in big-endian bit order into an Int +def collectBitsBE{ body: => Unit / emit[Bit] }: Int = { + var res = 0 + try body() with emit[Bit] { b => + res = b match { + case B0() => res * 2 + case B1() => res * 2 + 1 + } + resume(()) + } + res +} + +/// bitwise not on integers +def bitwiseNot(n: Int): Int = { + // TODO currently implemented in Effekt, should use extern ? + collectBitsBE{ + try bitsBE(n) with emit[Bit]{ b => resume(do emit(not(b))) } + } +} + def ungroupBytes{ body: => Unit / emit[Byte] }: Unit / emit[Bit] = for[Byte]{body}{ b => bits(b) } def twoscomplementLE{ body: => Unit / emit[Bit] }: Unit / emit[Bit] = { diff --git a/libraries/common/effekt.effekt b/libraries/common/effekt.effekt index 0fe82a777..c90d33bb1 100644 --- a/libraries/common/effekt.effekt +++ b/libraries/common/effekt.effekt @@ -419,6 +419,22 @@ def pow(base: Double, exponent: Int): Double = { } } +/// Computes n^exp for integers. +/// +/// Precondition: exp >= 0 +def pow(n: Int, exp: Int): Int = { + def go(n: Int, exp: Int, acc: Int): Int = { + if (exp == 0) { + acc + } else if (mod(exp, 2) == 0) { + go(n * n, exp / 2, acc) + } else { + go(n * n, exp / 2, acc * n) + } + } + go(n, exp, 1) +} + extern pure def pow(base: Double, exponent: Double): Double = js "Math.pow(${base}, ${exponent})" chez "(expt ${base} ${exponent})" From 1f5d0ac61d63dd22d37e98faaa6f4fc6be89f861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Tue, 1 Apr 2025 12:07:51 +0200 Subject: [PATCH 06/23] more docs --- libraries/common/binstream.effekt | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/libraries/common/binstream.effekt b/libraries/common/binstream.effekt index cf0c8b92f..9357544dc 100644 --- a/libraries/common/binstream.effekt +++ b/libraries/common/binstream.effekt @@ -262,8 +262,11 @@ def bitwiseNot(n: Int): Int = { } } +/// split emitted bytes and emit the individual bits in big-endian bit order def ungroupBytes{ body: => Unit / emit[Byte] }: Unit / emit[Bit] = for[Byte]{body}{ b => bits(b) } + +/// streaming negation in 2s-complement for little-endian bitstreams def twoscomplementLE{ body: => Unit / emit[Bit] }: Unit / emit[Bit] = { var carry = true try body() @@ -272,6 +275,9 @@ def twoscomplementLE{ body: => Unit / emit[Bit] }: Unit / emit[Bit] = { case B1() => if(carry) { do emit(B1()); carry = false } else { do emit(B0()) }; resume(()) } } + +/// group 8 bits into a byte each, big-endian bit order. +/// NOTE: The remainder is dropped. def groupBytesBE{ body: => Unit / emit[Bit] }: Unit / emit[Byte] = { var next = 0 var p = 128 @@ -288,6 +294,9 @@ def groupBytesBE{ body: => Unit / emit[Bit] }: Unit / emit[Byte] = { } } } + +/// group 8 bits into a byte each, little-endian bit order. +/// NOTE: The remainder is dropped. def groupBytesLE{ body: => Unit / emit[Bit] }: Unit / emit[Byte] = { var next = 0 var p = 1 @@ -304,9 +313,15 @@ def groupBytesLE{ body: => Unit / emit[Bit] }: Unit / emit[Byte] = { } } } + +/// group 8 bits into a byte each, big-endian bit order. +/// NOTE: The remainder is dropped. def groupBytes{ body: => Unit / emit[Bit] }: Unit / emit[Byte] = groupBytesBE{body} +/// Return just nth element of the given stream. +/// Raises MissingValue if not enough elements are emitted +// TODO could be in stream def nth[A](n: Int){ body: => Unit / emit[A] }: A / Exception[MissingValue] = { var m = n try { @@ -322,6 +337,10 @@ def nth[A](n: Int){ body: => Unit / emit[A] }: A / Exception[MissingValue] = { } } } + +/// Return just first element of the given stream. +/// Raises MissingValue if no elements are emitted +// TODO could be in stream def first[A]{ body: => Unit / emit[A] }: A / Exception[MissingValue] = { try { body() @@ -331,7 +350,8 @@ def first[A]{ body: => Unit / emit[A] }: A / Exception[MissingValue] = { } // Literals/splices // ---------------- -effect BinSplices = { +/// Splices allowed in bit stream literals +effect BitSplices = { splice[Unit], splice[Bit], splice[Byte], splice[LE[Int]], splice[BE[Int]], @@ -339,7 +359,9 @@ effect BinSplices = { splice[OfWidth[LE[Int]]], splice[OfWidth[BE[Int]]], splice[OfWidth[LE[Signed[Int]]]], splice[OfWidth[BE[Signed[Int]]]] } -def bit{ body: => Unit / { literal, BinSplices } }: Unit / emit[Bit] = { +/// Splicer to emit the bits in binary notation given, plus evenutal splices +/// Ignores whitespace +def bit{ body: => Unit / { literal, BitSplices } }: Unit / emit[Bit] = { try { ungroupBytes{ try { @@ -394,6 +416,8 @@ def bit{ body: => Unit / { literal, BinSplices } }: Unit / emit[Bit] = { } } +// Simple usage examples +// ===================== namespace examples { def main() = { mainSuite("Simple literals"){ From fedae8e4321694f0bdd4026952351fcedc1c8dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Tue, 1 Apr 2025 14:13:54 +0200 Subject: [PATCH 07/23] Add binstream to acme --- examples/stdlib/acme.effekt | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/stdlib/acme.effekt b/examples/stdlib/acme.effekt index cffcd9178..f1afdadba 100644 --- a/examples/stdlib/acme.effekt +++ b/examples/stdlib/acme.effekt @@ -5,6 +5,7 @@ module acme import args import array import bench +import binstream import buffer import bytearray import char From 1a905aa68a3294869bec9dd39f95c7bb1396ff9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Tue, 1 Apr 2025 14:40:06 +0200 Subject: [PATCH 08/23] Fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jiří Beneš --- libraries/common/binstream.effekt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/common/binstream.effekt b/libraries/common/binstream.effekt index 9357544dc..7e191de38 100644 --- a/libraries/common/binstream.effekt +++ b/libraries/common/binstream.effekt @@ -100,7 +100,7 @@ def signedBytesBE(int: Int): Unit / emit[Byte] = signedBytesBE(int, 4) // Splicers // -------- -/// Splicer to emit the bytes in hex notation given, plus evenutal splices +/// Splicer to emit the bytes in hex notation given, plus eventual splices /// Ignores whitespace def hex{ body: => Unit / { literal, HexSplices } }: Unit / emit[Byte] = { try { From a70dd90d9ce27935b510fdc488b1b0aa6f3ed381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Tue, 1 Apr 2025 14:54:58 +0200 Subject: [PATCH 09/23] Make bitwiseNot an extern --- libraries/common/binstream.effekt | 8 -------- libraries/common/effekt.effekt | 6 ++++++ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/libraries/common/binstream.effekt b/libraries/common/binstream.effekt index 7e191de38..ac073bae1 100644 --- a/libraries/common/binstream.effekt +++ b/libraries/common/binstream.effekt @@ -254,14 +254,6 @@ def collectBitsBE{ body: => Unit / emit[Bit] }: Int = { res } -/// bitwise not on integers -def bitwiseNot(n: Int): Int = { - // TODO currently implemented in Effekt, should use extern ? - collectBitsBE{ - try bitsBE(n) with emit[Bit]{ b => resume(do emit(not(b))) } - } -} - /// split emitted bytes and emit the individual bits in big-endian bit order def ungroupBytes{ body: => Unit / emit[Byte] }: Unit / emit[Bit] = for[Byte]{body}{ b => bits(b) } diff --git a/libraries/common/effekt.effekt b/libraries/common/effekt.effekt index c90d33bb1..12356c69a 100644 --- a/libraries/common/effekt.effekt +++ b/libraries/common/effekt.effekt @@ -665,6 +665,12 @@ extern pure def bitwiseXor(x: Int, y: Int): Int = llvm "%z = xor %Int ${x}, ${y} ret %Int %z" vm "effekt::bitwiseXor(Int, Int)" +extern pure def bitwiseNot(x: Int): Int = + js "(~${x})" + chez "(lognot ${x})" + llvm "%z = xor %Int ${x}, -1 ret %Int %z" + vm "effekt::bitwiseNot(Int)" + // Byte operations // =============== From a7f7745064bd2145632342e98c0bab8a5148a335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Fri, 2 May 2025 14:12:48 +0200 Subject: [PATCH 10/23] Make pad a function instead of an effect --- libraries/common/binstream.effekt | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/libraries/common/binstream.effekt b/libraries/common/binstream.effekt index ac073bae1..aa543997b 100644 --- a/libraries/common/binstream.effekt +++ b/libraries/common/binstream.effekt @@ -152,32 +152,28 @@ def x{ body: => Unit / { literal, HexSplices } }: Int = { // -------------------- // TODO could also be part of stream library -/// Request padding to a multiple of fac, by calling gen for each -effect pad[A](fac: Int){ gen: => A }: Unit - /// get current position in stream effect getPos(): Int /// Track position in stream, starting with init /// Handles getPos and pad -def tracking[A](init: Int){ body: => Unit / { emit[A], getPos, pad[A] } }: Unit / emit[A] = { +def tracking[A](init: Int){ body: => Unit / { emit[A], getPos } }: Unit / emit[A] = { var n = init try body() with emit[A] { b => n = n + 1; resume(do emit[A](b)) } with getPos{ resume(n) } - with pad[A] { fac => - resume { {gen} => - while(mod(n, fac) != 0){ - do emit[A](gen()) - n = n + 1 - } - } +} + +/// Pad to a multiple of fac, by calling gen for each +def pad[A](fac: Int){ gen: => A }: Unit / {getPos, emit[A]} = { + while(mod(do getPos(), fac) != 0) { + do emit[A](gen()) } } /// Track how many bytes were emitted /// Handles getPos and pad -def tracking[A]{ body: => Unit / { emit[A], getPos, pad[A] } }: Unit / emit[A] = +def tracking[A]{ body: => Unit / { emit[A], getPos } }: Unit / emit[A] = tracking[A](0){body} // Sub-Byte From 10f8efd507db1035980662cf23362d5e842e0f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Fri, 2 May 2025 14:15:14 +0200 Subject: [PATCH 11/23] Move tracking, pad to streams --- libraries/common/binstream.effekt | 27 --------------------------- libraries/common/stream.effekt | 27 +++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/libraries/common/binstream.effekt b/libraries/common/binstream.effekt index aa543997b..965dfc5f5 100644 --- a/libraries/common/binstream.effekt +++ b/libraries/common/binstream.effekt @@ -148,33 +148,6 @@ def x{ body: => Unit / { literal, HexSplices } }: Int = { res } -// Counting and padding -// -------------------- -// TODO could also be part of stream library - -/// get current position in stream -effect getPos(): Int - -/// Track position in stream, starting with init -/// Handles getPos and pad -def tracking[A](init: Int){ body: => Unit / { emit[A], getPos } }: Unit / emit[A] = { - var n = init - try body() - with emit[A] { b => n = n + 1; resume(do emit[A](b)) } - with getPos{ resume(n) } -} - -/// Pad to a multiple of fac, by calling gen for each -def pad[A](fac: Int){ gen: => A }: Unit / {getPos, emit[A]} = { - while(mod(do getPos(), fac) != 0) { - do emit[A](gen()) - } -} - -/// Track how many bytes were emitted -/// Handles getPos and pad -def tracking[A]{ body: => Unit / { emit[A], getPos } }: Unit / emit[A] = - tracking[A](0){body} // Sub-Byte // ======== diff --git a/libraries/common/stream.effekt b/libraries/common/stream.effekt index 4f3f1382a..53f239657 100644 --- a/libraries/common/stream.effekt +++ b/libraries/common/stream.effekt @@ -504,6 +504,33 @@ def tee[A]{ cons1: { => Unit / emit[A] } => Unit }{ cons2: { => Unit / emit[A] } } } +// Counting and padding +// -------------------- + +/// get current position in stream +effect getPos(): Int + +/// Track position in stream, starting with init +/// Handles getPos +def tracking[A](init: Int){ body: => Unit / { emit[A], getPos } }: Unit / emit[A] = { + var n = init + try body() + with emit[A] { b => n = n + 1; resume(do emit[A](b)) } + with getPos{ resume(n) } +} + +/// Pad to a multiple of fac emitted values, by emitting gen repeatedly +def pad[A](fac: Int){ gen: => A }: Unit / {getPos, emit[A]} = { + while(mod(do getPos(), fac) != 0) { + do emit[A](gen()) + } +} + +/// Track how many bytes were emitted (in the body) +/// Handles getPos +def tracking[A]{ body: => Unit / { emit[A], getPos } }: Unit / emit[A] = + tracking[A](0){body} + namespace returning { /// Canonical handler of push streams that performs `action` for every From 8fdc5bd3f83ea1ab01586bf65d9bbce536dfe3af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Fri, 2 May 2025 14:17:10 +0200 Subject: [PATCH 12/23] Move nth, first to stream --- libraries/common/binstream.effekt | 29 ----------------------------- libraries/common/stream.effekt | 28 ++++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/libraries/common/binstream.effekt b/libraries/common/binstream.effekt index 965dfc5f5..b2c66dcb8 100644 --- a/libraries/common/binstream.effekt +++ b/libraries/common/binstream.effekt @@ -280,35 +280,6 @@ def groupBytesLE{ body: => Unit / emit[Bit] }: Unit / emit[Byte] = { def groupBytes{ body: => Unit / emit[Bit] }: Unit / emit[Byte] = groupBytesBE{body} -/// Return just nth element of the given stream. -/// Raises MissingValue if not enough elements are emitted -// TODO could be in stream -def nth[A](n: Int){ body: => Unit / emit[A] }: A / Exception[MissingValue] = { - var m = n - try { - body() - val r: A = do raise[MissingValue](MissingValue(), "code in first did not emit any values") - r - } with emit[A] { a => - if (m == 0) { - a - } else { - m = m - 1 - resume(()) - } - } -} - -/// Return just first element of the given stream. -/// Raises MissingValue if no elements are emitted -// TODO could be in stream -def first[A]{ body: => Unit / emit[A] }: A / Exception[MissingValue] = { - try { - body() - val r: A = do raise[MissingValue](MissingValue(), "code in first did not emit any values") - r - } with emit[A] { a => a } -} // Literals/splices // ---------------- /// Splices allowed in bit stream literals diff --git a/libraries/common/stream.effekt b/libraries/common/stream.effekt index 53f239657..c1d5c8560 100644 --- a/libraries/common/stream.effekt +++ b/libraries/common/stream.effekt @@ -531,6 +531,34 @@ def pad[A](fac: Int){ gen: => A }: Unit / {getPos, emit[A]} = { def tracking[A]{ body: => Unit / { emit[A], getPos } }: Unit / emit[A] = tracking[A](0){body} +/// Return just the nth element of the given stream. +/// Raises MissingValue if not enough elements are emitted +def nth[A](n: Int){ body: => Unit / emit[A] }: A / Exception[MissingValue] = { + var m = n + try { + body() + val r: A = do raise[MissingValue](MissingValue(), "code in first did not emit any values") + r + } with emit[A] { a => + if (m == 0) { + a + } else { + m = m - 1 + resume(()) + } + } +} + +/// Return just the first element of the given stream. +/// Raises MissingValue if no elements are emitted +def first[A]{ body: => Unit / emit[A] }: A / Exception[MissingValue] = { + try { + body() + val r: A = do raise[MissingValue](MissingValue(), "code in first did not emit any values") + r + } with emit[A] { a => a } +} + namespace returning { /// Canonical handler of push streams that performs `action` for every From 68b108bcdee52604c178ce6861d1990f8b06ed3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Fri, 2 May 2025 14:22:46 +0200 Subject: [PATCH 13/23] Move tests to test --- examples/stdlib/binstream.check | 21 ++++++++++++ examples/stdlib/binstream.effekt | 55 +++++++++++++++++++++++++++++++ libraries/common/binstream.effekt | 55 ------------------------------- 3 files changed, 76 insertions(+), 55 deletions(-) create mode 100644 examples/stdlib/binstream.check create mode 100644 examples/stdlib/binstream.effekt diff --git a/examples/stdlib/binstream.check b/examples/stdlib/binstream.check new file mode 100644 index 000000000..bcf8439b2 --- /dev/null +++ b/examples/stdlib/binstream.check @@ -0,0 +1,21 @@ +Binstream +✓ literal hex 10 +✓ literal hex ff +✓ literal char a +✓ literal string ba +✓ int back-and-forth (17) +✓ int back-and-forth (17), explicit BE +✓ int back-and-forth (17), explicit LE +✓ byte 00101010 +✓ to bits and back +✓ to bits and back LE bitorder +✓ to bits and back BE bitorder +✓ append 0 means *2 +✓ pow agrees with double one +✓ LE 2s-complement +✓ BE 2s-complement + + 15 pass + 0 fail + 15 tests total + \ No newline at end of file diff --git a/examples/stdlib/binstream.effekt b/examples/stdlib/binstream.effekt new file mode 100644 index 000000000..20a136280 --- /dev/null +++ b/examples/stdlib/binstream.effekt @@ -0,0 +1,55 @@ +import binstream +import stream +import test + +def main() = { + suite("Binstream", false){ + test("literal hex 10"){ assertEqual(x"10${()}", 16) } + test("literal hex ff"){ assertEqual(x"ff${()}", 255) } + test("literal char a"){ assertEqual(x"${'a'}", x"61${()}") } + test("literal string ba"){ assertEqual(x"${"ba"}", x"62${()}" * 256 + x"61${()}") } + test("int back-and-forth (17)"){ assertEqual(x"${17}", 17)} + test("int back-and-forth (17), explicit BE"){ assertEqual(x"${17.BE}", 17) } + test("int back-and-forth (17), explicit LE"){ assertEqual(x"${17.LE}", 17 * 256 * 256 * 256) } + test("byte 00101010"){ + with on[MissingValue].default{ assertEqual(true, false) } + assertEqual(first[Byte]{groupBytes{ bit"00101010${()}" }}.toInt, 42) + } + test("to bits and back"){ + with on[MissingValue].default{ assertEqual(true, false) } + [42.toByte, 12.toByte, 113.toByte, 0.toByte, 255.toByte].foreach{ v => + assertEqual(first[Byte]{ groupBytes{ bits(v) } }, v) + } + } + test("to bits and back LE bitorder"){ + with on[MissingValue].default{ assertEqual(true, false) } + [42.toByte, 12.toByte, 113.toByte, 0.toByte, 255.toByte].foreach{ v => + assertEqual(first[Byte]{ groupBytesLE{ bitsLE(v) } }, v) + } + } + test("to bits and back BE bitorder"){ + with on[MissingValue].default{ assertEqual(true, false) } + [42.toByte, 12.toByte, 113.toByte, 0.toByte, 255.toByte].foreach{ v => + assertEqual(first[Byte]{ groupBytesBE{ bitsBE(v) } }, v) + } + } + test("append 0 means *2"){ + with on[MissingValue].default{ assertEqual(true, false) } + [42.toByte, 12.toByte, 127.toByte].foreach{ v => + assertEqual(nth[Byte](1){ groupBytes{ repeat(7){ do emit(B0()) }; bits(v); do emit(B0()) } }, (v.toInt * 2).toByte) + } + } + test("pow agrees with double one"){ + assertEqual(pow(2,5), pow(2.0,5).toInt) + } + test("LE 2s-complement"){ + with on[MissingValue].default{ assertEqual(true, false) } + assertEqual(first[Byte]{ groupBytesLE{ twoscomplementLE{ bitsLE(6.toByte) } } }, 250.toByte) + } + test("BE 2s-complement"){ + with on[MissingValue].default{ assertEqual(true, false) } + assertEqual(first[Byte]{ groupBytesBE{ bit"${-6.Signed.BE.OfWidth(8)}" } }, 250.toByte) + } + } + () +} \ No newline at end of file diff --git a/libraries/common/binstream.effekt b/libraries/common/binstream.effekt index b2c66dcb8..e6089e3f7 100644 --- a/libraries/common/binstream.effekt +++ b/libraries/common/binstream.effekt @@ -346,59 +346,4 @@ def bit{ body: => Unit / { literal, BitSplices } }: Unit / emit[Bit] = { }.reverse.each resume(()) } -} - -// Simple usage examples -// ===================== -namespace examples { - def main() = { - mainSuite("Simple literals"){ - test("literal hex 10"){ assertEqual(x"10${()}", 16) } - test("literal hex ff"){ assertEqual(x"ff${()}", 255) } - test("literal char a"){ assertEqual(x"${'a'}", x"61${()}") } - test("literal string ba"){ assertEqual(x"${"ba"}", x"62${()}" * 256 + x"61${()}") } - test("int back-and-forth (17)"){ assertEqual(x"${17}", 17)} - test("int back-and-forth (17), explicit BE"){ assertEqual(x"${17.BE}", 17) } - test("int back-and-forth (17), explicit LE"){ assertEqual(x"${17.LE}", 17 * 256 * 256 * 256) } - test("byte 00101010"){ - with on[MissingValue].default{ assertEqual(true, false) } - assertEqual(first[Byte]{groupBytes{ bit"00101010${()}" }}.toInt, 42) - } - test("to bits and back"){ - with on[MissingValue].default{ assertEqual(true, false) } - [42.toByte, 12.toByte, 113.toByte, 0.toByte, 255.toByte].foreach{ v => - assertEqual(first[Byte]{ groupBytes{ bits(v) } }, v) - } - } - test("to bits and back LE bitorder"){ - with on[MissingValue].default{ assertEqual(true, false) } - [42.toByte, 12.toByte, 113.toByte, 0.toByte, 255.toByte].foreach{ v => - assertEqual(first[Byte]{ groupBytesLE{ bitsLE(v) } }, v) - } - } - test("to bits and back BE bitorder"){ - with on[MissingValue].default{ assertEqual(true, false) } - [42.toByte, 12.toByte, 113.toByte, 0.toByte, 255.toByte].foreach{ v => - assertEqual(first[Byte]{ groupBytesBE{ bitsBE(v) } }, v) - } - } - test("append 0 means *2"){ - with on[MissingValue].default{ assertEqual(true, false) } - [42.toByte, 12.toByte, 127.toByte].foreach{ v => - assertEqual(nth[Byte](1){ groupBytes{ repeat(7){ do emit(B0()) }; bits(v); do emit(B0()) } }, (v.toInt * 2).toByte) - } - } - test("pow agrees with double one"){ - assertEqual(pow(2,5), pow(2.0,5).toInt) - } - test("LE 2s-complement"){ - with on[MissingValue].default{ assertEqual(true, false) } - assertEqual(first[Byte]{ groupBytesLE{ twoscomplementLE{ bitsLE(6.toByte) } } }, 250.toByte) - } - test("BE 2s-complement"){ - with on[MissingValue].default{ assertEqual(true, false) } - assertEqual(first[Byte]{ groupBytesBE{ bit"${-6.Signed.BE.OfWidth(8)}" } }, 250.toByte) - } - } - } } \ No newline at end of file From 28544c099a119d23bdd2c9a2c8cc864bc21446f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Tue, 6 May 2025 10:28:12 +0200 Subject: [PATCH 14/23] Implement bitwiseNot in vm --- effekt/shared/src/main/scala/effekt/core/vm/Builtin.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/effekt/shared/src/main/scala/effekt/core/vm/Builtin.scala b/effekt/shared/src/main/scala/effekt/core/vm/Builtin.scala index 1b3209fa3..be2a2818b 100644 --- a/effekt/shared/src/main/scala/effekt/core/vm/Builtin.scala +++ b/effekt/shared/src/main/scala/effekt/core/vm/Builtin.scala @@ -150,6 +150,9 @@ lazy val integers: Builtins = Map( builtin("effekt::bitwiseXor(Int, Int)") { case As.Int(x) :: As.Int(y) :: Nil => Value.Int(x ^ y) }, + builtin("effekt::bitwiseNot(Int)") { + case As.Int(x) :: Nil => Value.Int(~x) + }, // Comparison // ---------- From 691200c274bf1cf08d1b385e98bc5c4e14d6a68a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Tue, 6 May 2025 11:32:48 +0200 Subject: [PATCH 15/23] Fix test for LLVM (assert, infixEq on Byte) --- examples/stdlib/binstream.effekt | 44 ++++++++++++++++---------------- libraries/common/effekt.effekt | 4 +++ libraries/common/test.effekt | 3 +++ 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/examples/stdlib/binstream.effekt b/examples/stdlib/binstream.effekt index 20a136280..eab4a07be 100644 --- a/examples/stdlib/binstream.effekt +++ b/examples/stdlib/binstream.effekt @@ -4,51 +4,51 @@ import test def main() = { suite("Binstream", false){ - test("literal hex 10"){ assertEqual(x"10${()}", 16) } - test("literal hex ff"){ assertEqual(x"ff${()}", 255) } - test("literal char a"){ assertEqual(x"${'a'}", x"61${()}") } - test("literal string ba"){ assertEqual(x"${"ba"}", x"62${()}" * 256 + x"61${()}") } - test("int back-and-forth (17)"){ assertEqual(x"${17}", 17)} - test("int back-and-forth (17), explicit BE"){ assertEqual(x"${17.BE}", 17) } - test("int back-and-forth (17), explicit LE"){ assertEqual(x"${17.LE}", 17 * 256 * 256 * 256) } + test("literal hex 10"){ assert(x"10${()}", 16) } + test("literal hex ff"){ assert(x"ff${()}", 255) } + test("literal char a"){ assert(x"${'a'}", x"61${()}") } + test("literal string ba"){ assert(x"${"ba"}", x"62${()}" * 256 + x"61${()}") } + test("int back-and-forth (17)"){ assert(x"${17}", 17)} + test("int back-and-forth (17), explicit BE"){ assert(x"${17.BE}", 17) } + test("int back-and-forth (17), explicit LE"){ assert(x"${17.LE}", 17 * 256 * 256 * 256) } test("byte 00101010"){ - with on[MissingValue].default{ assertEqual(true, false) } - assertEqual(first[Byte]{groupBytes{ bit"00101010${()}" }}.toInt, 42) + with on[MissingValue].default{ assert(true, false) } + assert(first[Byte]{groupBytes{ bit"00101010${()}" }}.toInt, 42) } test("to bits and back"){ - with on[MissingValue].default{ assertEqual(true, false) } + with on[MissingValue].default{ assert(true, false) } [42.toByte, 12.toByte, 113.toByte, 0.toByte, 255.toByte].foreach{ v => - assertEqual(first[Byte]{ groupBytes{ bits(v) } }, v) + assert(first[Byte]{ groupBytes{ bits(v) } }, v) } } test("to bits and back LE bitorder"){ - with on[MissingValue].default{ assertEqual(true, false) } + with on[MissingValue].default{ assert(true, false) } [42.toByte, 12.toByte, 113.toByte, 0.toByte, 255.toByte].foreach{ v => - assertEqual(first[Byte]{ groupBytesLE{ bitsLE(v) } }, v) + assert(first[Byte]{ groupBytesLE{ bitsLE(v) } }, v) } } test("to bits and back BE bitorder"){ - with on[MissingValue].default{ assertEqual(true, false) } + with on[MissingValue].default{ assert(true, false) } [42.toByte, 12.toByte, 113.toByte, 0.toByte, 255.toByte].foreach{ v => - assertEqual(first[Byte]{ groupBytesBE{ bitsBE(v) } }, v) + assert(first[Byte]{ groupBytesBE{ bitsBE(v) } }, v) } } test("append 0 means *2"){ - with on[MissingValue].default{ assertEqual(true, false) } + with on[MissingValue].default{ assert(true, false) } [42.toByte, 12.toByte, 127.toByte].foreach{ v => - assertEqual(nth[Byte](1){ groupBytes{ repeat(7){ do emit(B0()) }; bits(v); do emit(B0()) } }, (v.toInt * 2).toByte) + assert(nth[Byte](1){ groupBytes{ repeat(7){ do emit(B0()) }; bits(v); do emit(B0()) } }, (v.toInt * 2).toByte) } } test("pow agrees with double one"){ - assertEqual(pow(2,5), pow(2.0,5).toInt) + assert(pow(2,5), pow(2.0,5).toInt) } test("LE 2s-complement"){ - with on[MissingValue].default{ assertEqual(true, false) } - assertEqual(first[Byte]{ groupBytesLE{ twoscomplementLE{ bitsLE(6.toByte) } } }, 250.toByte) + with on[MissingValue].default{ assert(true, false) } + assert(first[Byte]{ groupBytesLE{ twoscomplementLE{ bitsLE(6.toByte) } } }, 250.toByte) } test("BE 2s-complement"){ - with on[MissingValue].default{ assertEqual(true, false) } - assertEqual(first[Byte]{ groupBytesBE{ bit"${-6.Signed.BE.OfWidth(8)}" } }, 250.toByte) + with on[MissingValue].default{ assert(true, false) } + assert(first[Byte]{ groupBytesBE{ bit"${-6.Signed.BE.OfWidth(8)}" } }, 250.toByte) } } () diff --git a/libraries/common/effekt.effekt b/libraries/common/effekt.effekt index 12356c69a..aeea94c74 100644 --- a/libraries/common/effekt.effekt +++ b/libraries/common/effekt.effekt @@ -234,6 +234,10 @@ extern pure def infixNeq(x: Int, y: Int): Bool = """ vm "effekt::infixNeq(Int, Int)" +def infixEq(x: Byte, y: Byte): Bool = { + x.toInt == y.toInt +} + extern pure def infixEq(x: Char, y: Char): Bool = js "${x} === ${y}" chez "(equal? ${x} ${y})" diff --git a/libraries/common/test.effekt b/libraries/common/test.effekt index d5cafc894..47123081c 100644 --- a/libraries/common/test.effekt +++ b/libraries/common/test.effekt @@ -33,6 +33,9 @@ def assert(obtained: String, expected: String, msg: String): Unit / Assertion = def assert(obtained: Int, expected: Int): Unit / { Assertion, Formatted } = assertEqual(obtained, expected) { (x, y) => x == y } { x => show(x) } +def assert(obtained: Byte, expected: Byte): Unit / { Assertion, Formatted } = + assertEqual(obtained, expected) { (x, y) => x == y } { x => show(x) } + def assert(obtained: Bool, expected: Bool): Unit / { Assertion, Formatted } = assertEqual(obtained, expected) { (x, y) => x == y } { x => show(x) } From 8de82cd1f3122c10df847dc46ee0cd285f438081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Tue, 6 May 2025 17:26:07 +0200 Subject: [PATCH 16/23] Fix show on Byte for chez --- libraries/common/effekt.effekt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/common/effekt.effekt b/libraries/common/effekt.effekt index aeea94c74..4672f1272 100644 --- a/libraries/common/effekt.effekt +++ b/libraries/common/effekt.effekt @@ -90,7 +90,7 @@ extern pure def show(value: Char): String = extern pure def show(value: Byte): String = js "'' + ${value}" - chez "(string ${value})" + chez "(string (integer->char ${value}))" llvm """ %z = call %Pos @c_bytearray_show_Byte(i8 ${value}) ret %Pos %z From d5c61a895b14443ca4a850b21ad909e19f95060b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Tue, 6 May 2025 17:30:13 +0200 Subject: [PATCH 17/23] Show bytes as ints on chez, too --- libraries/common/effekt.effekt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/common/effekt.effekt b/libraries/common/effekt.effekt index 4672f1272..f6db00052 100644 --- a/libraries/common/effekt.effekt +++ b/libraries/common/effekt.effekt @@ -90,7 +90,7 @@ extern pure def show(value: Char): String = extern pure def show(value: Byte): String = js "'' + ${value}" - chez "(string (integer->char ${value}))" + chez "(show-number ${value})" llvm """ %z = call %Pos @c_bytearray_show_Byte(i8 ${value}) ret %Pos %z From dfccb53c79023f20a30d4a3b739d1f01fba2df7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Tue, 13 May 2025 09:31:50 +0200 Subject: [PATCH 18/23] Remove left-over test import --- libraries/common/binstream.effekt | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/common/binstream.effekt b/libraries/common/binstream.effekt index e6089e3f7..d2d86b226 100644 --- a/libraries/common/binstream.effekt +++ b/libraries/common/binstream.effekt @@ -2,7 +2,6 @@ import option import stringbuffer import stream import char -import test import io/error import bytearray From 1fd6dd32d49f6932a5288d9b4a9922048564d849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Tue, 13 May 2025 09:32:19 +0200 Subject: [PATCH 19/23] Minor formatting changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jiří Beneš --- libraries/common/binstream.effekt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/common/binstream.effekt b/libraries/common/binstream.effekt index d2d86b226..e9399a67b 100644 --- a/libraries/common/binstream.effekt +++ b/libraries/common/binstream.effekt @@ -72,13 +72,12 @@ def bytesBE(n: Int): Unit / emit[Byte] = bytesBE(n, 4) def bytes(n: Int): Unit / emit[Byte] = bytesBE(n) /// emit bytes of the given int as width bytes (in 2s-complement) in little-endian byte order -def signedBytesLE(int: Int, width: Int): Unit / emit[Byte] = { +def signedBytesLE(int: Int, width: Int): Unit / emit[Byte] = if (int < 0) { bytesLE(bitwiseNot(neg(int)) + 1, width) } else { bytesLE(int, width) } -} /// emit bytes of the given int as width bytes (in 2s-complement) in big-endian byte order def signedBytesBE(int: Int, width: Int): Unit / emit[Byte] = { if (int < 0) { From 58ed2154830351219e5261cf68b71676232acf8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Wed, 14 May 2025 10:37:42 +0200 Subject: [PATCH 20/23] Remove bits/bytes and force using bits/bytesBE/LE --- examples/stdlib/binstream.effekt | 10 ++-------- libraries/common/binstream.effekt | 19 ++++--------------- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/examples/stdlib/binstream.effekt b/examples/stdlib/binstream.effekt index eab4a07be..2bb8d4ed9 100644 --- a/examples/stdlib/binstream.effekt +++ b/examples/stdlib/binstream.effekt @@ -13,13 +13,7 @@ def main() = { test("int back-and-forth (17), explicit LE"){ assert(x"${17.LE}", 17 * 256 * 256 * 256) } test("byte 00101010"){ with on[MissingValue].default{ assert(true, false) } - assert(first[Byte]{groupBytes{ bit"00101010${()}" }}.toInt, 42) - } - test("to bits and back"){ - with on[MissingValue].default{ assert(true, false) } - [42.toByte, 12.toByte, 113.toByte, 0.toByte, 255.toByte].foreach{ v => - assert(first[Byte]{ groupBytes{ bits(v) } }, v) - } + assert(first[Byte]{groupBytesBE{ bit"00101010${()}" }}.toInt, 42) } test("to bits and back LE bitorder"){ with on[MissingValue].default{ assert(true, false) } @@ -36,7 +30,7 @@ def main() = { test("append 0 means *2"){ with on[MissingValue].default{ assert(true, false) } [42.toByte, 12.toByte, 127.toByte].foreach{ v => - assert(nth[Byte](1){ groupBytes{ repeat(7){ do emit(B0()) }; bits(v); do emit(B0()) } }, (v.toInt * 2).toByte) + assert(nth[Byte](1){ groupBytesBE{ repeat(7){ do emit(B0()) }; bitsBE(v); do emit(B0()) } }, (v.toInt * 2).toByte) } } test("pow agrees with double one"){ diff --git a/libraries/common/binstream.effekt b/libraries/common/binstream.effekt index e9399a67b..cf8250231 100644 --- a/libraries/common/binstream.effekt +++ b/libraries/common/binstream.effekt @@ -68,9 +68,6 @@ def bytesBE(n: Int, width: Int): Unit / emit[Byte] = { /// emit bytes of the given int as a 4 bytes in big-endian byte order def bytesBE(n: Int): Unit / emit[Byte] = bytesBE(n, 4) -/// emit bytes of the given int as a 4 bytes in big-endian byte order -def bytes(n: Int): Unit / emit[Byte] = bytesBE(n) - /// emit bytes of the given int as width bytes (in 2s-complement) in little-endian byte order def signedBytesLE(int: Int, width: Int): Unit / emit[Byte] = if (int < 0) { @@ -178,9 +175,6 @@ def bitsBE(byte: Byte): Unit / emit[Bit] = { } } -/// emit bits of the given Byte as 8 Bits in big-endian bit order -def bits(byte: Byte): Unit / emit[Bit] = bitsBE(byte) - /// emit bits of the given Byte as width Bits in little-endian bit order def bitsLE(v: Int, width: Int): Unit / emit[Bit] = { var mask = 1 @@ -222,8 +216,8 @@ def collectBitsBE{ body: => Unit / emit[Bit] }: Int = { } /// split emitted bytes and emit the individual bits in big-endian bit order -def ungroupBytes{ body: => Unit / emit[Byte] }: Unit / emit[Bit] = - for[Byte]{body}{ b => bits(b) } +def ungroupBytesBE{ body: => Unit / emit[Byte] }: Unit / emit[Bit] = + for[Byte]{body}{ b => bitsBE(b) } /// streaming negation in 2s-complement for little-endian bitstreams def twoscomplementLE{ body: => Unit / emit[Bit] }: Unit / emit[Bit] = { @@ -273,11 +267,6 @@ def groupBytesLE{ body: => Unit / emit[Bit] }: Unit / emit[Byte] = { } } -/// group 8 bits into a byte each, big-endian bit order. -/// NOTE: The remainder is dropped. -def groupBytes{ body: => Unit / emit[Bit] }: Unit / emit[Byte] = - groupBytesBE{body} - // Literals/splices // ---------------- /// Splices allowed in bit stream literals @@ -293,7 +282,7 @@ effect BitSplices = { /// Ignores whitespace def bit{ body: => Unit / { literal, BitSplices } }: Unit / emit[Bit] = { try { - ungroupBytes{ + ungroupBytesBE{ try { body() } @@ -317,7 +306,7 @@ def bit{ body: => Unit / { literal, BitSplices } }: Unit / emit[Bit] = { } with splice[Unit] { _ => resume(()) } with splice[Bit] { b => do emit(b); resume(()) } - with splice[Byte] { b => bits(b); resume(()) } + with splice[Byte] { b => bitsBE(b); resume(()) } with splice[OfWidth[LE[Int]]] { i => bitsLE(i.raw.raw, i.width) resume(()) From f6f3d028b3dba4ba069b95f702efc8babbcb74e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Wed, 14 May 2025 11:09:27 +0200 Subject: [PATCH 21/23] Fix test --- examples/stdlib/binstream.check | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/stdlib/binstream.check b/examples/stdlib/binstream.check index bcf8439b2..2f0bc23f8 100644 --- a/examples/stdlib/binstream.check +++ b/examples/stdlib/binstream.check @@ -7,7 +7,6 @@ Binstream ✓ int back-and-forth (17), explicit BE ✓ int back-and-forth (17), explicit LE ✓ byte 00101010 -✓ to bits and back ✓ to bits and back LE bitorder ✓ to bits and back BE bitorder ✓ append 0 means *2 @@ -15,7 +14,7 @@ Binstream ✓ LE 2s-complement ✓ BE 2s-complement - 15 pass + 14 pass 0 fail - 15 tests total + 14 tests total \ No newline at end of file From f7c6b3e16bcb2d0fe7b5e7f87b62a8220ef07d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Wed, 14 May 2025 11:10:38 +0200 Subject: [PATCH 22/23] Add assertThrows-like test functionality --- examples/stdlib/test/exceptions.check | 17 ++++++++++ examples/stdlib/test/exceptions.effekt | 44 ++++++++++++++++++++++++++ libraries/common/test.effekt | 12 +++++++ 3 files changed, 73 insertions(+) create mode 100644 examples/stdlib/test/exceptions.check create mode 100644 examples/stdlib/test/exceptions.effekt diff --git a/examples/stdlib/test/exceptions.check b/examples/stdlib/test/exceptions.check new file mode 100644 index 000000000..c39838f74 --- /dev/null +++ b/examples/stdlib/test/exceptions.check @@ -0,0 +1,17 @@ +Test test suite on Exceptions +✓ Test test assertNotThrown pass +✕ Test test assertNotThrown fail + Unexpected Exception +✕ Test test assertThrown fail + Expected Exception, but exited normally +✓ Test test assertThrows pass +✓ Test test assertNoThrow pass +✕ Test test assertNoThrow fail + Unexpected Exception +✕ Test test assertThrows fail + Expected Exception, but exited normally +✓ Test test assertThrows pass + + 4 pass + 4 fail + 8 tests total diff --git a/examples/stdlib/test/exceptions.effekt b/examples/stdlib/test/exceptions.effekt new file mode 100644 index 000000000..224840897 --- /dev/null +++ b/examples/stdlib/test/exceptions.effekt @@ -0,0 +1,44 @@ +import test + +def main() = { + // Don't print out times in CI. + suite("Test test suite on Exceptions", false) { + // on[E] variant + // ------------- + test("Test test assertNotThrown pass") { + with on[MissingValue].assertNotThrown + assert(Some(12).value, 12) + } + test("Test test assertNotThrown fail") { + with on[MissingValue].assertNotThrown + assert(None().value, 12) + } + test("Test test assertThrown fail") { + with on[MissingValue].assertThrown + assert(Some(12).value, 12) + } + test("Test test assertThrows pass") { + with on[MissingValue].assertThrown[MissingValue] + assert(None().value, 12) + } + // direct variant + // -------------- + test("Test test assertNoThrow pass") { + with assertNoThrow[MissingValue] + assert(Some(12).value, 12) + } + test("Test test assertNoThrow fail") { + with assertNoThrow[MissingValue] + assert(None().value, 12) + } + test("Test test assertThrows fail") { + with assertThrows[MissingValue] + assert(Some(12).value, 12) + } + test("Test test assertThrows pass") { + with assertThrows[MissingValue] + assert(None().value, 12) + } + }; + () +} diff --git a/libraries/common/test.effekt b/libraries/common/test.effekt index 47123081c..a393b296c 100644 --- a/libraries/common/test.effekt +++ b/libraries/common/test.effekt @@ -59,6 +59,18 @@ def assertEqual[A](obtained: A, expected: A) { equals: (A, A) => Bool } { show: // NOTE: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // Here's an accidental capture! Can we prevent this somehow nicely? +def assertNotThrown[E](ex: on[E]){ body: => Unit / Exception[E] }: Unit / Assertion = { + ex.default{ do assert(false, "Unexpected Exception") }{body} +} +def assertNoThrow[E]{ body: => Unit / Exception[E] }: Unit / Assertion = { + on[E].default{ do assert(false, "Unexpected Exception") }{body} +} +def assertThrown[E](ex: on[E]){ body: => Unit / Exception[E] }: Unit / Assertion = { + do assert(ex.default{ true }{ body(); false }, "Expected Exception, but exited normally") +} +def assertThrows[E]{body: => Unit / Exception[E] }: Unit / Assertion = { + do assert(on[E].default{ true }{ body(); false }, "Expected Exception, but exited normally") +} interface Test { def success(name: String, duration: Int): Unit From 3d973b2789b5f655ea60d6582d427dd0e94d1467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcial=20Gai=C3=9Fert?= Date: Wed, 14 May 2025 11:13:23 +0200 Subject: [PATCH 23/23] Use assertNotThrown --- examples/stdlib/binstream.effekt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/stdlib/binstream.effekt b/examples/stdlib/binstream.effekt index 2bb8d4ed9..14d08dca6 100644 --- a/examples/stdlib/binstream.effekt +++ b/examples/stdlib/binstream.effekt @@ -12,23 +12,23 @@ def main() = { test("int back-and-forth (17), explicit BE"){ assert(x"${17.BE}", 17) } test("int back-and-forth (17), explicit LE"){ assert(x"${17.LE}", 17 * 256 * 256 * 256) } test("byte 00101010"){ - with on[MissingValue].default{ assert(true, false) } + with on[MissingValue].assertNotThrown assert(first[Byte]{groupBytesBE{ bit"00101010${()}" }}.toInt, 42) } test("to bits and back LE bitorder"){ - with on[MissingValue].default{ assert(true, false) } + with on[MissingValue].assertNotThrown [42.toByte, 12.toByte, 113.toByte, 0.toByte, 255.toByte].foreach{ v => assert(first[Byte]{ groupBytesLE{ bitsLE(v) } }, v) } } test("to bits and back BE bitorder"){ - with on[MissingValue].default{ assert(true, false) } + with on[MissingValue].assertNotThrown [42.toByte, 12.toByte, 113.toByte, 0.toByte, 255.toByte].foreach{ v => assert(first[Byte]{ groupBytesBE{ bitsBE(v) } }, v) } } test("append 0 means *2"){ - with on[MissingValue].default{ assert(true, false) } + with on[MissingValue].assertNotThrown [42.toByte, 12.toByte, 127.toByte].foreach{ v => assert(nth[Byte](1){ groupBytesBE{ repeat(7){ do emit(B0()) }; bitsBE(v); do emit(B0()) } }, (v.toInt * 2).toByte) } @@ -37,11 +37,11 @@ def main() = { assert(pow(2,5), pow(2.0,5).toInt) } test("LE 2s-complement"){ - with on[MissingValue].default{ assert(true, false) } + with on[MissingValue].assertNotThrown assert(first[Byte]{ groupBytesLE{ twoscomplementLE{ bitsLE(6.toByte) } } }, 250.toByte) } test("BE 2s-complement"){ - with on[MissingValue].default{ assert(true, false) } + with on[MissingValue].assertNotThrown assert(first[Byte]{ groupBytesBE{ bit"${-6.Signed.BE.OfWidth(8)}" } }, 250.toByte) } }