hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 184ac22373487aab4ff720789137bc8e27d9165b
parent 580380fa449328ed07e933e7f9140f0cf988c278
Author: Drew DeVault <sir@cmpwn.com>
Date:   Fri, 12 Feb 2021 13:29:05 -0500

strconv: implement signed base conversion

Diffstat:
Mstrconv/itos.ha | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mstrconv/numeric.ha | 27+++++++++++++++++++++------
Mstrconv/utos.ha | 58++++++++++++++++++++++++++++++----------------------------
3 files changed, 120 insertions(+), 49 deletions(-)

diff --git a/strconv/itos.ha b/strconv/itos.ha @@ -1,18 +1,36 @@ use bytes; use types; -// Converts an i64 to a string, in base 10. The return value is statically +// Converts an i64 to a string in the given base. The return value is statically // allocated and will be overwritten on subsequent calls; see [strings::dup] to // duplicate the result. -export fn i64tos(i: i64) const str = { - static assert(types::I64_MAX == 9223372036854775807); +export fn i64tosb(i: i64, b: base) const str = { if (i == types::I64_MIN) { - return "-9223372036854775808"; + return switch (b) { + base::BIN => "-1000000000000000000000000000000000000000000000000000000000000000", + base::OCT => "-1000000000000000000000", + base::DEC => "-9223372036854775808", + base::HEX_LOWER, base::HEX_UPPER => "-8000000000000000", + }; }; - static let buf: [22]u8 = [0...]; // 20 chars plus NUL and - + static assert(types::I64_MAX == 9223372036854775807); + static let buf: [66]u8 = [0...]; // 64 binary digits plus NUL and - buf = [0...]; + static const lut_upper: [_]rune = [ + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', + ]; + static const lut_lower: [_]rune = [ + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', + ]; + let lut = if (b != base::HEX_LOWER) lut_upper else { + b = base::HEX_UPPER; + lut_lower; + }; + let s = types::string { data = &buf, ... }; const isneg = i < 0; @@ -26,9 +44,9 @@ export fn i64tos(i: i64) const str = { }; for (i > 0) { - buf[s.length] = '0': u32: u8 + (i % 10): u8; + buf[s.length] = lut[i % b: i64]: u32: u8; s.length += 1; - i /= 10; + i /= b: i64; }; const x: size = if (isneg) 1 else 0; @@ -38,25 +56,61 @@ export fn i64tos(i: i64) const str = { return *(&s: *str); }; -// Converts an i8 to a string, in base 10. The return value is statically +// Converts a i32 to a string in the given base. The return value is statically // allocated and will be overwritten on subsequent calls; see [strings::dup] to // duplicate the result. -export fn i8tos(i: i8) const str = i64tos(i: i64); +export fn i32tosb(i: i32, b: base) const str = i64tosb(i, b); -// Converts an i16 to a string, in base 10. The return value is statically +// Converts a i16 to a string in the given base. The return value is statically // allocated and will be overwritten on subsequent calls; see [strings::dup] to // duplicate the result. -export fn i16tos(i: i16) const str = i64tos(i: i64); +export fn i16tosb(i: i16, b: base) const str = i64tosb(i, b); -// Converts an i32 to a string, in base 10. The return value is statically +// Converts a i8 to a string in the given base. The return value is statically // allocated and will be overwritten on subsequent calls; see [strings::dup] to // duplicate the result. -export fn i32tos(i: i32) const str = i64tos(i: i64); +export fn i8tosb(i: i8, b: base) const str = i64tosb(i, b); -// Converts an int to a string, in base 10. The return value is statically +// Converts an int to a string in the given base. The return value is +// statically allocated and will be overwritten on subsequent calls; see +// [strings::dup] to duplicate the result. +export fn itosb(i: int, b: base) const str = i64tosb(i, b); + +// Converts a u64 to a string in base 10. The return value is statically // allocated and will be overwritten on subsequent calls; see [strings::dup] to // duplicate the result. -export fn itos(i: int) const str = i64tos(i: i64); +export fn i64tos(i: i64) const str = i64tosb(i, base::DEC); + +// Converts a u8 to a string in base 10. The return value is statically +// allocated and will be overwritten on subsequent calls; see [strings::dup] to +// duplicate the result. +export fn i8tos(i: i8) const str = i64tos(i); + +// Converts a u16 to a string in base 10. The return value is statically +// allocated and will be overwritten on subsequent calls; see [strings::dup] to +// duplicate the result. +export fn i16tos(i: i16) const str = i64tos(i); + +// Converts a u32 to a string in base 10. The return value is statically +// allocated and will be overwritten on subsequent calls; see [strings::dup] to +// duplicate the result. +export fn i32tos(i: i32) const str = i64tos(i); + +// Converts a uint to a string in base 10. The return value is statically +// allocated and will be overwritten on subsequent calls; see [strings::dup] to +// duplicate the result. +export fn itos(i: int) const str = i64tos(i); + +@test fn itosb() void = { + assert("11010" == i64tosb(0b11010, base::BIN)); + assert("1234567" == i64tosb(0o1234567, base::OCT)); + assert("123456789" == i64tosb(123456789, base::DEC)); + assert("123456789ABCDEF" == i64tosb(0x123456789ABCDEF, base::HEX)); + assert("123456789ABCDEF" == i64tosb(0x123456789ABCDEF, base::HEX_UPPER)); + assert("123456789abcdef" == i64tosb(0x123456789ABCDEF, base::HEX_LOWER)); + assert("-1000000000000000000000000000000000000000000000000000000000000000" + == i64tosb(types::I64_MIN, base::BIN)); +}; @test fn itos() void = { const samples: [_]i64 = [ diff --git a/strconv/numeric.ha b/strconv/numeric.ha @@ -4,13 +4,12 @@ use types; // statically allocated and will be overwritten on subsequent calls; see // [strings::dup] to duplicate the result. export fn signedtosb(n: types::signed, b: base) const str = { - assert(b == base::DEC); // TODO return match (n) { - i: int => itos(i), - i: i8 => i8tos(i), - i: i16 => i16tos(i), - i: i32 => i32tos(i), - i: i64 => i64tos(i), + i: int => itosb(i, b), + i: i8 => i8tosb(i, b), + i: i16 => i16tosb(i, b), + i: i32 => i32tosb(i, b), + i: i64 => i64tosb(i, b), }; }; @@ -79,3 +78,19 @@ export fn numerictosb(n: types::numeric, b: base) const str = { // statically allocated and will be overwritten on subsequent calls; see // [strings::dup] to duplicate the result. export fn numerictos(n: types::numeric) const str = numerictosb(n, base::DEC); + +@test fn numeric() void = { + const cases: [_]types::numeric = [ + 42u8, 1337u16, 1337u32, 1337u64, 42i8, -42i8, 1337i16, -1337i16, + 1337i32, -1337i32, 1337i64, -1337i64, 1337i, -1337i, 1337u, + -1337i, + ]; + const expected = [ + "42", "1337", "1337", "1337", "42", "-42", "1337", "-1337", + "1337", "-1337", "1337", "-1337", "1337", "-1337", "1337", + "-1337", + ]; + for (let i = 0z; i < len(cases); i += 1) { + assert(numerictos(cases[i]) == expected[i]); + }; +}; diff --git a/strconv/utos.ha b/strconv/utos.ha @@ -1,11 +1,14 @@ use bytes; use types; -// Converts a u64 to a string, in the given base. The return value is statically +// Converts a u64 to a string in the given base. The return value is statically // allocated and will be overwritten on subsequent calls; see [strings::dup] to // duplicate the result. export fn u64tosb(u: u64, b: base) const str = { static assert(types::U64_MAX == 18446744073709551615); + static let buf: [65]u8 = [0...]; // 64 binary digits plus NUL + buf = [0...]; + static const lut_upper: [_]rune = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', @@ -19,9 +22,6 @@ export fn u64tosb(u: u64, b: base) const str = { lut_lower; }; - static let buf: [21]u8 = [0...]; // 20 digits plus NUL - buf = [0...]; - let s = types::string { data = &buf, ... }; if (u == 0) { buf[s.length] = '0': u32: u8; @@ -29,7 +29,7 @@ export fn u64tosb(u: u64, b: base) const str = { }; for (u > 0u64) { - buf[s.length] = lut[(u % b: u64)]: u32: u8; + buf[s.length] = lut[u % b: u64]: u32: u8; s.length += 1; u /= b; }; @@ -39,67 +39,67 @@ export fn u64tosb(u: u64, b: base) const str = { return *(&s: *str); }; -// Converts a u32 to a string, in the given base. The return value is statically +// Converts a u32 to a string in the given base. The return value is statically // allocated and will be overwritten on subsequent calls; see [strings::dup] to // duplicate the result. -export fn u32tosb(u: u32, b: base) const str = u64tosb(u: u32, b); +export fn u32tosb(u: u32, b: base) const str = u64tosb(u, b); -// Converts a u16 to a string, in the given base. The return value is statically +// Converts a u16 to a string in the given base. The return value is statically // allocated and will be overwritten on subsequent calls; see [strings::dup] to // duplicate the result. -export fn u16tosb(u: u16, b: base) const str = u64tosb(u: u16, b); +export fn u16tosb(u: u16, b: base) const str = u64tosb(u, b); -// Converts a u8 to a string, in the given base. The return value is statically +// Converts a u8 to a string in the given base. The return value is statically // allocated and will be overwritten on subsequent calls; see [strings::dup] to // duplicate the result. -export fn u8tosb(u: u8, b: base) const str = u64tosb(u: u8, b); +export fn u8tosb(u: u8, b: base) const str = u64tosb(u, b); -// Converts a uint to a string, in the given base. The return value is +// Converts a uint to a string in the given base. The return value is // statically allocated and will be overwritten on subsequent calls; see // [strings::dup] to duplicate the result. -export fn utosb(u: uint, b: base) const str = u64tosb(u: uint, b); +export fn utosb(u: uint, b: base) const str = u64tosb(u, b); -// Converts a size to a string, in the given base. The return value is +// Converts a size to a string in the given base. The return value is // statically allocated and will be overwritten on subsequent calls; see // [strings::dup] to duplicate the result. -export fn ztosb(u: size, b: base) const str = u64tosb(u: u64, b); +export fn ztosb(u: size, b: base) const str = u64tosb(u, b); -// Converts a size to a string, in the given base. The return value is +// Converts a size to a string in the given base. The return value is // statically allocated and will be overwritten on subsequent calls; see // [strings::dup] to duplicate the result. export fn uptrtosb(uptr: uintptr, b: base) const str = u64tosb(uptr: u64, b); -// Converts a u64 to a string, in base 10. The return value is statically +// Converts a u64 to a string in base 10. The return value is statically // allocated and will be overwritten on subsequent calls; see [strings::dup] to // duplicate the result. export fn u64tos(u: u64) const str = u64tosb(u, base::DEC); -// Converts a u8 to a string, in base 10. The return value is statically +// Converts a u8 to a string in base 10. The return value is statically // allocated and will be overwritten on subsequent calls; see [strings::dup] to // duplicate the result. -export fn u8tos(u: u8) const str = u64tos(u: u64); +export fn u8tos(u: u8) const str = u64tos(u); -// Converts a u16 to a string, in base 10. The return value is statically +// Converts a u16 to a string in base 10. The return value is statically // allocated and will be overwritten on subsequent calls; see [strings::dup] to // duplicate the result. -export fn u16tos(u: u16) const str = u64tos(u: u64); +export fn u16tos(u: u16) const str = u64tos(u); -// Converts a u32 to a string, in base 10. The return value is statically +// Converts a u32 to a string in base 10. The return value is statically // allocated and will be overwritten on subsequent calls; see [strings::dup] to // duplicate the result. -export fn u32tos(u: u32) const str = u64tos(u: u64); +export fn u32tos(u: u32) const str = u64tos(u); -// Converts a uint to a string, in base 10. The return value is statically +// Converts a uint to a string in base 10. The return value is statically // allocated and will be overwritten on subsequent calls; see [strings::dup] to // duplicate the result. -export fn utos(u: uint) const str = u64tos(u: u64); +export fn utos(u: uint) const str = u64tos(u); -// Converts a size to a string, in base 10. The return value is statically +// Converts a size to a string in base 10. The return value is statically // allocated and will be overwritten on subsequent calls; see [strings::dup] to // duplicate the result, or [strconv::itosb] to pass your own string buffer. -export fn ztos(z: size) const str = u64tos(z: u64); +export fn ztos(z: size) const str = u64tos(z); -// Converts a uintptr to a string, in base 10. The return value is statically +// Converts a uintptr to a string in base 10. The return value is statically // allocated and will be overwritten on subsequent calls; see [strings::dup] to // duplicate the result. export fn uptrtos(uptr: uintptr) const str = u64tos(uptr: u64); @@ -111,6 +111,8 @@ export fn uptrtos(uptr: uintptr) const str = u64tos(uptr: u64); assert("123456789ABCDEF" == u64tosb(0x123456789ABCDEF, base::HEX)); assert("123456789ABCDEF" == u64tosb(0x123456789ABCDEF, base::HEX_UPPER)); assert("123456789abcdef" == u64tosb(0x123456789ABCDEF, base::HEX_LOWER)); + assert("1111111111111111111111111111111111111111111111111111111111111111" + == u64tosb(types::U64_MAX, base::BIN)); }; @test fn utos() void = {