hare

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

stou.ha (4055B)


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