diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2025-11-30 14:12:01 -0500 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2025-11-30 14:12:01 -0500 |
| commit | 4d8aa867c7f4661167a4742fbdd865ed2449503e (patch) | |
| tree | 67c9aeedaa3eb36585d1747e1f96e8c4708fc707 /src/stdlib | |
| parent | d302aaec38b9d295d39c4d87b53ee610bc9e0e07 (diff) | |
Add `base` parameter to integer parsing functions
Diffstat (limited to 'src/stdlib')
| -rw-r--r-- | src/stdlib/bigint.c | 119 | ||||
| -rw-r--r-- | src/stdlib/bigint.h | 2 | ||||
| -rw-r--r-- | src/stdlib/bytes.c | 4 | ||||
| -rw-r--r-- | src/stdlib/bytes.h | 2 | ||||
| -rw-r--r-- | src/stdlib/cli.c | 10 | ||||
| -rw-r--r-- | src/stdlib/intX.c.h | 4 | ||||
| -rw-r--r-- | src/stdlib/intX.h | 2 |
7 files changed, 93 insertions, 50 deletions
diff --git a/src/stdlib/bigint.c b/src/stdlib/bigint.c index 46957c2d..2d145bd5 100644 --- a/src/stdlib/bigint.c +++ b/src/stdlib/bigint.c @@ -393,55 +393,98 @@ PUREFUNC Closure_t Int$onward(Int_t first, Int_t step) { } public -Int_t Int$from_str(const char *str) { - mpz_t i; - int result; - if (strncmp(str, "0x", 2) == 0) { - result = mpz_init_set_str(i, str + 2, 16); - } else if (strncmp(str, "0o", 2) == 0) { - result = mpz_init_set_str(i, str + 2, 8); - } else if (strncmp(str, "0b", 2) == 0) { - result = mpz_init_set_str(i, str + 2, 2); - } else { - result = mpz_init_set_str(i, str, 10); - } - if (result != 0) return NONE_INT; - return Int$from_mpz(i); -} +Int_t Int$from_str(const char *str) { return Int$parse(Text$from_str(str), NONE_INT, NULL); } public -OptionalInt_t Int$parse(Text_t text, Text_t *remainder) { +OptionalInt_t Int$parse(Text_t text, OptionalInt_t base, Text_t *remainder) { const char *str = Text$as_c_string(text); - mpz_t i; - int result; - if (strncmp(str, "0x", 2) == 0) { - str += 2; - const char *end = str + strspn(str, "0123456789abcdefABCDEF"); - if (remainder) *remainder = Text$from_str(end); - else if (*end != '\0') return NONE_INT; - result = mpz_init_set_str(i, String(string_slice(str, (size_t)(end - str))), 16); + bool negative = (*str == '-'); + if (negative || *str == '+') str += 1; + const char *end = str; + int32_t base32; + if (base.small != 0) { + base32 = Int32$from_int(base, false); + switch (base32) { + case 16: + if (strncmp(str, "0x", 2) == 0) { + base16_prefix: + str += 2; + } + end = str + strspn(str, "0123456789abcdefABCDEF"); + break; + case 10: + base10: + end = str + strspn(str, "0123456789"); + break; + case 8: + if (strncmp(str, "0o", 2) == 0) { + base8_prefix: + str += 2; + } + end = str + strspn(str, "01234567"); + break; + case 2: + if (strncmp(str, "0b", 2) == 0) { + base2_prefix: + str += 2; + } + end = str + strspn(str, "01"); + break; + case 1: { + str += strspn(str, "0"); + size_t n = strspn(str, "1"); + end = str + n; + if (remainder) *remainder = Text$from_str(end); + else if (*end != '\0') return NONE_INT; + return Int$from_int64((int64_t)n); + } + default: { + if (base32 < 1 || base32 > 36) { + if (remainder) *remainder = text; + return NONE_INT; + } + for (; *end; end++) { + char c = *end; + int32_t digit; + if ('0' <= c && c <= '9') { + digit = (c - (int)'0'); + } else if ('a' <= c && c <= 'z') { + digit = (c - (int)'a'); + } else if ('A' <= c && c <= 'Z') { + digit = (c - (int)'A'); + } else { + break; + } + if (digit >= base32) break; + } + } + } + } else if (strncmp(str, "0x", 2) == 0) { + base32 = 16; + goto base16_prefix; } else if (strncmp(str, "0o", 2) == 0) { - str += 2; - const char *end = str + strspn(str, "01234567"); - if (remainder) *remainder = Text$from_str(end); - else if (*end != '\0') return NONE_INT; - result = mpz_init_set_str(i, String(string_slice(str, (size_t)(end - str))), 8); + base32 = 8; + goto base8_prefix; } else if (strncmp(str, "0b", 2) == 0) { - str += 2; - const char *end = str + strspn(str, "01"); - if (remainder) *remainder = Text$from_str(end); - else if (*end != '\0') return NONE_INT; - result = mpz_init_set_str(i, String(string_slice(str, (size_t)(end - str))), 2); + base32 = 2; + goto base2_prefix; } else { - const char *end = str + strspn(str, "0123456789"); - if (remainder) *remainder = Text$from_str(end); - else if (*end != '\0') return NONE_INT; - result = mpz_init_set_str(i, String(string_slice(str, (size_t)(end - str))), 10); + base32 = 10; + goto base10; } + + if (remainder) *remainder = Text$from_str(end); + else if (*end != '\0') return NONE_INT; + + mpz_t i; + int result = mpz_init_set_str(i, String(string_slice(str, (size_t)(end - str))), base32); if (result != 0) { if (remainder) *remainder = text; return NONE_INT; } + if (negative) { + mpz_neg(i, i); + } return Int$from_mpz(i); } diff --git a/src/stdlib/bigint.h b/src/stdlib/bigint.h index a00bdf2f..9ce4c800 100644 --- a/src/stdlib/bigint.h +++ b/src/stdlib/bigint.h @@ -23,7 +23,7 @@ Text_t Int$octal(Int_t i, Int_t digits, bool prefix); PUREFUNC Closure_t Int$to(Int_t first, Int_t last, OptionalInt_t step); PUREFUNC Closure_t Int$onward(Int_t first, Int_t step); OptionalInt_t Int$from_str(const char *str); -OptionalInt_t Int$parse(Text_t text, Text_t *remainder); +OptionalInt_t Int$parse(Text_t text, OptionalInt_t base, Text_t *remainder); Int_t Int$abs(Int_t x); Int_t Int$power(Int_t base, Int_t exponent); Int_t Int$gcd(Int_t x, Int_t y); diff --git a/src/stdlib/bytes.c b/src/stdlib/bytes.c index ab689ae4..4416d804 100644 --- a/src/stdlib/bytes.c +++ b/src/stdlib/bytes.c @@ -33,8 +33,8 @@ public CONSTFUNC bool Byte$is_between(const Byte_t x, const Byte_t low, const Byte_t high) { return low <= x && x <= high; } public -OptionalByte_t Byte$parse(Text_t text, Text_t *remainder) { - OptionalInt_t full_int = Int$parse(text, remainder); +OptionalByte_t Byte$parse(Text_t text, OptionalInt_t base, Text_t *remainder) { + OptionalInt_t full_int = Int$parse(text, base, remainder); if (full_int.small != 0L && Int$compare_value(full_int, I(0)) >= 0 && Int$compare_value(full_int, I(255)) <= 0) { return (OptionalByte_t){.has_value = true, .value = Byte$from_int(full_int, true)}; } else { diff --git a/src/stdlib/bytes.h b/src/stdlib/bytes.h index 2f948177..6581f300 100644 --- a/src/stdlib/bytes.h +++ b/src/stdlib/bytes.h @@ -18,7 +18,7 @@ Byte_t Byte$from_int(Int_t i, bool truncate); Byte_t Byte$from_int64(int64_t i, bool truncate); Byte_t Byte$from_int32(int32_t i, bool truncate); Byte_t Byte$from_int16(int16_t i, bool truncate); -OptionalByte_t Byte$parse(Text_t text, Text_t *remainder); +OptionalByte_t Byte$parse(Text_t text, OptionalInt_t base, Text_t *remainder); Closure_t Byte$to(Byte_t first, Byte_t last, OptionalInt8_t step); MACROLIKE Byte_t Byte$from_int8(int8_t i) { return (Byte_t)i; } diff --git a/src/stdlib/cli.c b/src/stdlib/cli.c index 04538796..18da5c5e 100644 --- a/src/stdlib/cli.c +++ b/src/stdlib/cli.c @@ -238,23 +238,23 @@ static List_t parse_arg_list(List_t args, const char *flag, void *dest, const Ty if (parsed.small == 0) print_err("Could not parse argument for ", flag, ": ", arg); *(Int_t *)dest = parsed; } else if (type == &Int64$info) { - OptionalInt64_t parsed = Int64$parse(Text$from_str(arg), NULL); + OptionalInt64_t parsed = Int64$parse(Text$from_str(arg), NONE_INT, NULL); if (!parsed.has_value) print_err("Could not parse argument for ", flag, ": ", arg); *(Int64_t *)dest = parsed.value; } else if (type == &Int32$info) { - OptionalInt32_t parsed = Int32$parse(Text$from_str(arg), NULL); + OptionalInt32_t parsed = Int32$parse(Text$from_str(arg), NONE_INT, NULL); if (!parsed.has_value) print_err("Could not parse argument for ", flag, ": ", arg); *(Int32_t *)dest = parsed.value; } else if (type == &Int16$info) { - OptionalInt16_t parsed = Int16$parse(Text$from_str(arg), NULL); + OptionalInt16_t parsed = Int16$parse(Text$from_str(arg), NONE_INT, NULL); if (!parsed.has_value) print_err("Could not parse argument for ", flag, ": ", arg); *(Int16_t *)dest = parsed.value; } else if (type == &Int8$info) { - OptionalInt8_t parsed = Int8$parse(Text$from_str(arg), NULL); + OptionalInt8_t parsed = Int8$parse(Text$from_str(arg), NONE_INT, NULL); if (!parsed.has_value) print_err("Could not parse argument for ", flag, ": ", arg); *(Int8_t *)dest = parsed.value; } else if (type == &Byte$info) { - OptionalByte_t parsed = Byte$parse(Text$from_str(arg), NULL); + OptionalByte_t parsed = Byte$parse(Text$from_str(arg), NONE_INT, NULL); if (!parsed.has_value) print_err("Could not parse argument for ", flag, ": ", arg); *(Byte_t *)dest = parsed.value; } else if (type == &Bool$info) { diff --git a/src/stdlib/intX.c.h b/src/stdlib/intX.c.h index 0e665591..0910c7f1 100644 --- a/src/stdlib/intX.c.h +++ b/src/stdlib/intX.c.h @@ -188,8 +188,8 @@ Closure_t NAMESPACED(onward)(INT_T first, INT_T step) { return (Closure_t){.fn = _next_int, .userdata = range}; } public -PUREFUNC OPT_T NAMESPACED(parse)(Text_t text, Text_t *remainder) { - OptionalInt_t full_int = Int$parse(text, remainder); +PUREFUNC OPT_T NAMESPACED(parse)(Text_t text, OptionalInt_t base, Text_t *remainder) { + OptionalInt_t full_int = Int$parse(text, base, remainder); if (full_int.small == 0L) return (OPT_T){.has_value = false}; if (Int$compare_value(full_int, I(NAMESPACED(min))) < 0) { return (OPT_T){.has_value = false}; diff --git a/src/stdlib/intX.h b/src/stdlib/intX.h index 0f4632c2..1c8b4a05 100644 --- a/src/stdlib/intX.h +++ b/src/stdlib/intX.h @@ -46,7 +46,7 @@ List_t NAMESPACED(bits)(INTX_T x); bool NAMESPACED(get_bit)(INTX_T x, Int_t bit_index); Closure_t NAMESPACED(to)(INTX_T first, INTX_T last, OPT_T step); Closure_t NAMESPACED(onward)(INTX_T first, INTX_T step); -PUREFUNC OPT_T NAMESPACED(parse)(Text_t text, Text_t *remainder); +PUREFUNC OPT_T NAMESPACED(parse)(Text_t text, OptionalInt_t base, Text_t *remainder); CONSTFUNC bool NAMESPACED(is_between)(const INTX_T x, const INTX_T low, const INTX_T high); CONSTFUNC INTX_T NAMESPACED(clamped)(INTX_T x, INTX_T min, INTX_T max); MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_byte)(Byte_t b) { return (INTX_T)b; } |
