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 | |
| parent | d302aaec38b9d295d39c4d87b53ee610bc9e0e07 (diff) | |
Add `base` parameter to integer parsing functions
Diffstat (limited to 'src')
| -rw-r--r-- | src/compile/functions.c | 3 | ||||
| -rw-r--r-- | src/environment.c | 18 | ||||
| -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 | ||||
| -rw-r--r-- | src/typecheck.c | 3 |
10 files changed, 106 insertions, 61 deletions
diff --git a/src/compile/functions.c b/src/compile/functions.c index cce93e3d..63d7d23d 100644 --- a/src/compile/functions.c +++ b/src/compile/functions.c @@ -6,6 +6,7 @@ #include "../stdlib/datatypes.h" #include "../stdlib/integers.h" #include "../stdlib/nums.h" +#include "../stdlib/optionals.h" #include "../stdlib/tables.h" #include "../stdlib/text.h" #include "../stdlib/util.h" @@ -703,7 +704,7 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static definition = Texts(definition, wrapper); } else if (cache && cache->tag == Int) { assert(args); - OptionalInt64_t cache_size = Int64$parse(Text$from_str(Match(cache, Int)->str), NULL); + OptionalInt64_t cache_size = Int64$parse(Text$from_str(Match(cache, Int)->str), NONE_INT, NULL); Text_t pop_code = EMPTY_TEXT; if (cache->tag == Int && cache_size.has_value && cache_size.value > 0) { // FIXME: this currently just deletes the first entry, but this diff --git a/src/environment.c b/src/environment.c index 43d22c3d..7e04fe79 100644 --- a/src/environment.c +++ b/src/environment.c @@ -93,7 +93,7 @@ env_t *global_env(bool source_mapping) { MAKE_TYPE("Empty", EMPTY_TYPE, Text("Empty$$type"), Text("Empty$$info")), MAKE_TYPE( // "Bool", Type(BoolType), Text("Bool_t"), Text("Bool$info"), - {"parse", "Bool$parse", "func(text:Text, remainder:&Text? = none -> Bool?)"}), + {"parse", "Bool$parse", "func(text:Text, remainder:&Text?=none -> Bool?)"}), MAKE_TYPE( // "Byte", Type(ByteType), Text("Byte_t"), Text("Byte$info"), {"get_bit", "Byte$get_bit", "func(x:Byte, bit_index:Int -> Bool)"}, // @@ -101,7 +101,7 @@ env_t *global_env(bool source_mapping) { {"is_between", "Byte$is_between", "func(x:Byte, low:Byte, high:Byte -> Bool)"}, // {"max", "Byte$max", "Byte"}, // {"min", "Byte$min", "Byte"}, // - {"parse", "Byte$parse", "func(text:Text, remainder:&Text? = none -> Byte?)"}, // + {"parse", "Byte$parse", "func(text:Text, base:Int?=none, remainder:&Text?=none -> Byte?)"}, // {"to", "Byte$to", "func(first:Byte, last:Byte, step:Int8?=none -> func(->Byte?))"}), MAKE_TYPE( // "Int", Type(BigIntType), Text("Int_t"), Text("Int$info"), {"abs", "Int$abs", "func(x:Int -> Int)"}, // @@ -126,7 +126,7 @@ env_t *global_env(bool source_mapping) { {"next_prime", "Int$next_prime", "func(x:Int -> Int)"}, // {"octal", "Int$octal", "func(i:Int, digits=0, prefix=yes -> Text)"}, // {"onward", "Int$onward", "func(first:Int,step=1 -> func(->Int?))"}, // - {"parse", "Int$parse", "func(text:Text, remainder:&Text? = none -> Int?)"}, // + {"parse", "Int$parse", "func(text:Text, base:Int?=none, remainder:&Text?=none -> Int?)"}, // {"plus", "Int$plus", "func(x,y:Int -> Int)"}, // {"power", "Int$power", "func(base:Int,exponent:Int -> Int)"}, // #if __GNU_MP_VERSION >= 6 @@ -145,7 +145,7 @@ env_t *global_env(bool source_mapping) { {"clamped", "Int64$clamped", "func(x,low,high:Int64 -> Int64)"}, // {"divided_by", "Int64$divided_by", "func(x,y:Int64 -> Int64)"}, // {"gcd", "Int64$gcd", "func(x,y:Int64 -> Int64)"}, // - {"parse", "Int64$parse", "func(text:Text, remainder:&Text? = none -> Int64?)"}, // + {"parse", "Int64$parse", "func(text:Text, base:Int?=none, remainder:&Text?=none -> Int64?)"}, // {"get_bit", "Int64$get_bit", "func(x:Int64, bit_index:Int -> Bool)"}, // {"hex", "Int64$hex", "func(i:Int64, digits=0, uppercase=yes, prefix=yes -> Text)"}, // {"is_between", "Int64$is_between", "func(x:Int64,low:Int64,high:Int64 -> Bool)"}, // @@ -167,7 +167,7 @@ env_t *global_env(bool source_mapping) { {"clamped", "Int32$clamped", "func(x,low,high:Int32 -> Int32)"}, // {"divided_by", "Int32$divided_by", "func(x,y:Int32 -> Int32)"}, // {"gcd", "Int32$gcd", "func(x,y:Int32 -> Int32)"}, // - {"parse", "Int32$parse", "func(text:Text, remainder:&Text? = none -> Int32?)"}, // + {"parse", "Int32$parse", "func(text:Text, base:Int?=none, remainder:&Text?=none -> Int32?)"}, // {"get_bit", "Int32$get_bit", "func(x:Int32, bit_index:Int -> Bool)"}, // {"hex", "Int32$hex", "func(i:Int32, digits=0, uppercase=yes, prefix=yes -> Text)"}, // {"is_between", "Int32$is_between", "func(x:Int32,low:Int32,high:Int32 -> Bool)"}, // @@ -189,7 +189,7 @@ env_t *global_env(bool source_mapping) { {"clamped", "Int16$clamped", "func(x,low,high:Int16 -> Int16)"}, // {"divided_by", "Int16$divided_by", "func(x,y:Int16 -> Int16)"}, // {"gcd", "Int16$gcd", "func(x,y:Int16 -> Int16)"}, // - {"parse", "Int16$parse", "func(text:Text, remainder:&Text? = none -> Int16?)"}, // + {"parse", "Int16$parse", "func(text:Text, base:Int?=none, remainder:&Text?=none -> Int16?)"}, // {"get_bit", "Int16$get_bit", "func(x:Int16, bit_index:Int -> Bool)"}, // {"hex", "Int16$hex", "func(i:Int16, digits=0, uppercase=yes, prefix=yes -> Text)"}, // {"is_between", "Int16$is_between", "func(x:Int16,low:Int16,high:Int16 -> Bool)"}, // @@ -211,7 +211,7 @@ env_t *global_env(bool source_mapping) { {"clamped", "Int8$clamped", "func(x,low,high:Int8 -> Int8)"}, // {"divided_by", "Int8$divided_by", "func(x,y:Int8 -> Int8)"}, // {"gcd", "Int8$gcd", "func(x,y:Int8 -> Int8)"}, // - {"parse", "Int8$parse", "func(text:Text, remainder:&Text? = none -> Int8?)"}, // + {"parse", "Int8$parse", "func(text:Text, base:Int?=none, remainder:&Text?=none -> Int8?)"}, // {"get_bit", "Int8$get_bit", "func(x:Int8, bit_index:Int -> Bool)"}, // {"hex", "Int8$hex", "func(i:Int8, digits=0, uppercase=yes, prefix=yes -> Text)"}, // {"is_between", "Int8$is_between", "func(x:Int8,low:Int8,high:Int8 -> Bool)"}, // @@ -245,7 +245,7 @@ env_t *global_env(bool source_mapping) { C(SQRT1_2), {"INF", "(Num_t)(INFINITY)", "Num"}, // {"TAU", "(Num_t)(2.*M_PI)", "Num"}, // {"mix", "Num$mix", "func(amount,x,y:Num -> Num)"}, // - {"parse", "Num$parse", "func(text:Text, remainder:&Text? = none -> Num?)"}, // + {"parse", "Num$parse", "func(text:Text, remainder:&Text?=none -> Num?)"}, // {"abs", "fabs", "func(n:Num -> Num)"}, // F_opt(acos), F_opt(acosh), F_opt(asin), F(asinh), F(atan), F_opt(atanh), F(cbrt), F(ceil), F_opt(cos), F(cosh), F(erf), F(erfc), F(exp), F(exp2), F(expm1), F(floor), F(j0), F(j1), F_opt(log), F_opt(log10), @@ -274,7 +274,7 @@ env_t *global_env(bool source_mapping) { {"INF", "(Num32_t)(INFINITY)", "Num32"}, // {"TAU", "(Num32_t)(2.f*M_PI)", "Num32"}, // {"mix", "Num32$mix", "func(amount,x,y:Num32 -> Num32)"}, // - {"parse", "Num32$parse", "func(text:Text, remainder:&Text? = none -> Num32?)"}, // + {"parse", "Num32$parse", "func(text:Text, remainder:&Text?=none -> Num32?)"}, // {"abs", "fabsf", "func(n:Num32 -> Num32)"}, // {"modulo", "Num32$mod", "func(x,y:Num32 -> Num32)"}, // {"modulo1", "Num32$mod1", "func(x,y:Num32 -> Num32)"}, // 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; } diff --git a/src/typecheck.c b/src/typecheck.c index 37f4fcab..e432759b 100644 --- a/src/typecheck.c +++ b/src/typecheck.c @@ -14,6 +14,7 @@ #include "naming.h" #include "parse/files.h" #include "parse/types.h" +#include "stdlib/optionals.h" #include "stdlib/paths.h" #include "stdlib/tables.h" #include "stdlib/text.h" @@ -1659,7 +1660,7 @@ PUREFUNC bool is_constant(env_t *env, ast_t *ast) { case None: return true; case Int: { DeclareMatch(info, ast, Int); - Int_t int_val = Int$parse(Text$from_str(info->str), NULL); + Int_t int_val = Int$parse(Text$from_str(info->str), NONE_INT, NULL); if (int_val.small == 0) return false; // Failed to parse return (Int$compare_value(int_val, I(BIGGEST_SMALL_INT)) <= 0); } |
