hare

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

stou.ha (5642B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use ascii;
      5 use encoding::utf8;
      6 use strings;
      7 use types;
      8 
      9 fn rune_to_integer(r: rune) (u64 | void) = {
     10 	if (ascii::isdigit(r))
     11 		return (r: u32 - '0'): u64
     12 	else if (ascii::isalpha(r) && ascii::islower(r))
     13 		return (r: u32 - 'a'): u64 + 10
     14 	else if (ascii::isalpha(r) && ascii::isupper(r))
     15 		return (r: u32 - 'A'): u64 + 10;
     16 };
     17 
     18 fn parseint(s: str, base: base) ((bool, u64) | invalid | overflow) = {
     19 	if (base == base::DEFAULT) {
     20 		base = base::DEC;
     21 	};
     22 	assert(base == 2 || base == 8 || base == 10 || base == 16);
     23 
     24 	if (len(s) == 0) {
     25 		return 0: invalid;
     26 	};
     27 
     28 	let buf = strings::toutf8(s);
     29 	let i = 0z;
     30 
     31 	let sign = buf[i] == '-';
     32 	if (sign || buf[i] == '+') {
     33 		i += 1;
     34 	};
     35 
     36 	// Require at least one digit.
     37 	if (i == len(buf)) {
     38 		return i: invalid;
     39 	};
     40 
     41 	let n = 0u64;
     42 	for (i < len(buf); i += 1) {
     43 		const digit = match (rune_to_integer(buf[i]: rune)) {
     44 		case void =>
     45 			return i: invalid;
     46 		case let d: u64 =>
     47 			yield d;
     48 		};
     49 
     50 		if (digit >= base) {
     51 			return i: invalid;
     52 		};
     53 
     54 		const old = n;
     55 
     56 		n *= base;
     57 		n += digit;
     58 
     59 		if (n < old) {
     60 			return overflow;
     61 		};
     62 	};
     63 	return (sign, n);
     64 };
     65 
     66 // Converts a string to a u64 in the given base. Returns [[invalid]] if the
     67 // string is empty or contains invalid characters. Returns [[overflow]] if the
     68 // number is too large to be represented by a u64.
     69 export fn stou64b(s: str, base: base) (u64 | invalid | overflow) = {
     70 	let (sign, u) = parseint(s, base)?;
     71 	if (sign) {
     72 		return overflow;
     73 	};
     74 	return u;
     75 };
     76 
     77 fn stoumax(s: str, base: base, max: u64) (u64 | invalid | overflow) = {
     78 	const n = stou64b(s, base)?;
     79 	if (n > max) {
     80 		return overflow;
     81 	};
     82 	return n;
     83 };
     84 
     85 // Converts a string to a u32 in the given base. Returns [[invalid]] if the
     86 // string is empty or contains invalid characters. Returns [[overflow]] if the
     87 // number is too large to be represented by a u32.
     88 export fn stou32b(s: str, base: base) (u32 | invalid | overflow) =
     89 	stoumax(s, base, types::U32_MAX)?: u32;
     90 
     91 // Converts a string to a u16 in the given base. Returns [[invalid]] if the
     92 // string is empty or contains invalid characters. Returns [[overflow]] if the
     93 // number is too large to be represented by a u16.
     94 export fn stou16b(s: str, base: base) (u16 | invalid | overflow) =
     95 	stoumax(s, base, types::U16_MAX)?: u16;
     96 
     97 // Converts a string to a u8 in the given base. Returns [[invalid]] if the
     98 // string is empty or contains invalid characters. Returns [[overflow]] if the
     99 // number is too large to be represented by a u8.
    100 export fn stou8b(s: str, base: base) (u8 | invalid | overflow) =
    101 	stoumax(s, base, types::U8_MAX)?: u8;
    102 
    103 // Converts a string to a uint in the given base. Returns [[invalid]] if the
    104 // string is empty or contains invalid characters. Returns [[overflow]] if the
    105 // number is too large to be represented by a uint.
    106 export fn stoub(s: str, base: base) (uint | invalid | overflow) =
    107 	stoumax(s, base, types::UINT_MAX)?: uint;
    108 
    109 // Converts a string to a size in the given base. Returns [[invalid]] if the
    110 // string is empty or contains invalid characters. Returns [[overflow]] if the
    111 // number is too large to be represented by a size.
    112 export fn stozb(s: str, base: base) (size | invalid | overflow) =
    113 	stoumax(s, base, types::SIZE_MAX)?: size;
    114 
    115 // Converts a string to a u64 in base 10. Returns [[invalid]] if the string is
    116 // empty or contains invalid characters. Returns [[overflow]] if the number is
    117 // too large to be represented by a u64.
    118 export fn stou64(s: str) (u64 | invalid | overflow) = stou64b(s, base::DEC);
    119 
    120 // Converts a string to a u32 in base 10. Returns [[invalid]] if the string is
    121 // empty or contains invalid characters. Returns [[overflow]] if the number is
    122 // too large to be represented by a u32.
    123 export fn stou32(s: str) (u32 | invalid | overflow) = stou32b(s, base::DEC);
    124 
    125 // Converts a string to a u16 in base 10. Returns [[invalid]] if the string is
    126 // empty or contains invalid characters. Returns [[overflow]] if the number is
    127 // too large to be represented by a u16.
    128 export fn stou16(s: str) (u16 | invalid | overflow) = stou16b(s, base::DEC);
    129 
    130 // Converts a string to a u8 in base 10. Returns [[invalid]] if the string is
    131 // empty or contains invalid characters. Returns [[overflow]] if the number is
    132 // too large to be represented by a u8.
    133 export fn stou8(s: str) (u8 | invalid | overflow) = stou8b(s, base::DEC);
    134 
    135 // Converts a string to a uint in base 10. Returns [[invalid]] if the string is
    136 // empty or contains invalid characters. Returns [[overflow]] if the number is
    137 // too large to be represented by a uint.
    138 export fn stou(s: str) (uint | invalid | overflow) = stoub(s, base::DEC);
    139 
    140 // Converts a string to a size in base 10. Returns [[invalid]] if the string is
    141 // empty or contains invalid characters. Returns [[overflow]] if the number is
    142 // too large to be represented by a size.
    143 export fn stoz(s: str) (size | invalid | overflow) = stozb(s, base::DEC);
    144 
    145 @test fn stou() void = {
    146 	assert(stou64("") as invalid == 0);
    147 	assert(stou64("+") as invalid == 1);
    148 	assert(stou64("+a") as invalid == 1);
    149 	assert(stou64("abc") as invalid == 0);
    150 	assert(stou64("1a") as invalid == 1);
    151 
    152 	assert(stou64("18446744073709551616") is overflow);
    153 	assert(stou64("184467440737095516150") is overflow);
    154 	assert(stou64("-1") is overflow);
    155 
    156 	assert(stou64("0") as u64 == 0);
    157 	assert(stou64("1") as u64 == 1);
    158 	assert(stou64("18446744073709551615") as u64 == 18446744073709551615);
    159 };
    160 
    161 @test fn stoub() void = {
    162 	assert(stou64b("7f", 16) as u64 == 0x7f);
    163 	assert(stou64b("7F", 16) as u64 == 0x7f);
    164 	assert(stou64b("37", 8) as u64 == 0o37);
    165 	assert(stou64b("110101", 2) as u64 == 0b110101);
    166 };