diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2026-01-02 15:10:48 -0500 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2026-01-02 15:10:48 -0500 |
| commit | 9653a7c2e53e2bc5e8f146a7d9ea1e71eed19e08 (patch) | |
| tree | 7f026a142b4f8efcdbf517cc58adc97eb3b37cd5 /src/stdlib | |
| parent | e4d5bf73e4ad9dc51f923a32903011edfeae2908 (diff) | |
| parent | ce49f93da58d007c0a52ee82e2421adfe06012f9 (diff) | |
Merge branch 'dev' into constructive-reals
Diffstat (limited to 'src/stdlib')
| -rw-r--r-- | src/stdlib/bigint.c | 12 | ||||
| -rw-r--r-- | src/stdlib/bigint.h | 24 | ||||
| -rw-r--r-- | src/stdlib/bools.h | 24 | ||||
| -rw-r--r-- | src/stdlib/bytes.c | 10 | ||||
| -rw-r--r-- | src/stdlib/bytes.h | 8 | ||||
| -rw-r--r-- | src/stdlib/c_strings.c | 4 | ||||
| -rw-r--r-- | src/stdlib/enums.c | 2 | ||||
| -rw-r--r-- | src/stdlib/floatX.c.h | 20 | ||||
| -rw-r--r-- | src/stdlib/intX.c.h | 22 | ||||
| -rw-r--r-- | src/stdlib/intX.h | 44 | ||||
| -rw-r--r-- | src/stdlib/lists.c | 12 | ||||
| -rw-r--r-- | src/stdlib/lists.h | 15 | ||||
| -rw-r--r-- | src/stdlib/mapmacro.h | 4 | ||||
| -rw-r--r-- | src/stdlib/memory.c | 2 | ||||
| -rw-r--r-- | src/stdlib/numX.h | 115 | ||||
| -rw-r--r-- | src/stdlib/paths.c | 105 | ||||
| -rw-r--r-- | src/stdlib/paths.h | 2 | ||||
| -rw-r--r-- | src/stdlib/pointers.c | 8 | ||||
| -rw-r--r-- | src/stdlib/print.h | 16 | ||||
| -rw-r--r-- | src/stdlib/stacktrace.c | 2 | ||||
| -rw-r--r-- | src/stdlib/stdlib.c | 98 | ||||
| -rw-r--r-- | src/stdlib/stdlib.h | 5 | ||||
| -rw-r--r-- | src/stdlib/structs.c | 4 | ||||
| -rw-r--r-- | src/stdlib/tables.c | 87 | ||||
| -rw-r--r-- | src/stdlib/tables.h | 6 | ||||
| -rw-r--r-- | src/stdlib/text.c | 28 | ||||
| -rw-r--r-- | src/stdlib/text.h | 12 |
27 files changed, 519 insertions, 172 deletions
diff --git a/src/stdlib/bigint.c b/src/stdlib/bigint.c index 7b81e319..a1ffc12e 100644 --- a/src/stdlib/bigint.c +++ b/src/stdlib/bigint.c @@ -72,8 +72,8 @@ static bool Int$is_none(const void *i, const TypeInfo_t *info) { public PUREFUNC int32_t Int$compare_value(const Int_t x, const Int_t y) { if (likely(x.small & y.small & 1L)) return (x.small > y.small) - (x.small < y.small); - else if (x.small & 1) return -mpz_cmp_si(y.big, x.small); - else if (y.small & 1) return mpz_cmp_si(x.big, y.small); + else if (x.small & 1) return -mpz_cmp_si(y.big, (x.small >> 2)); + else if (y.small & 1) return mpz_cmp_si(x.big, (y.small >> 2)); else return x.big == y.big ? 0 : mpz_cmp(x.big, y.big); } @@ -102,7 +102,9 @@ CONSTFUNC Int_t Int$clamped(Int_t x, Int_t low, Int_t high) { public CONSTFUNC bool Int$is_between(const Int_t x, const Int_t low, const Int_t high) { - return Int$compare_value(low, x) <= 0 && Int$compare_value(x, high) <= 0; + int32_t low_cmp = Int$compare_value(x, low); + int32_t high_cmp = Int$compare_value(x, high); + return (low_cmp >= 0 && high_cmp <= 0) || (low_cmp <= 0 && high_cmp >= 0); } public @@ -395,7 +397,9 @@ PUREFUNC Closure_t Int$onward(Int_t first, Int_t step) { } public -Int_t Int$from_str(const char *str) { return Int$parse(Text$from_str(str), NONE_INT, NULL); } +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, OptionalInt_t base, Text_t *remainder) { diff --git a/src/stdlib/bigint.h b/src/stdlib/bigint.h index 2936f2cd..a948e1e7 100644 --- a/src/stdlib/bigint.h +++ b/src/stdlib/bigint.h @@ -198,18 +198,30 @@ MACROLIKE PUREFUNC Int_t Int$from_float64(double n, bool truncate) { if (!truncate && unlikely(mpz_get_d(result) != n)) fail("Could not convert to an integer without truncation: ", n); return Int$from_mpz(result); } -MACROLIKE PUREFUNC Int_t Int$from_float32(float n, bool truncate) { return Int$from_float64((double)n, truncate); } +MACROLIKE PUREFUNC Int_t Int$from_num32(float n, bool truncate) { + return Int$from_float64((double)n, truncate); +} MACROLIKE Int_t Int$from_int64(int64_t i) { if likely (i >= SMALLEST_SMALL_INT && i <= BIGGEST_SMALL_INT) return (Int_t){.small = (i << 2L) | 1L}; mpz_t result; mpz_init_set_si(result, i); return Int$from_mpz(result); } -MACROLIKE CONSTFUNC Int_t Int$from_int32(Int32_t i) { return Int$from_int64((Int32_t)i); } -MACROLIKE CONSTFUNC Int_t Int$from_int16(Int16_t i) { return I_small(i); } -MACROLIKE CONSTFUNC Int_t Int$from_int8(Int8_t i) { return I_small(i); } -MACROLIKE CONSTFUNC Int_t Int$from_byte(Byte_t b) { return I_small(b); } -MACROLIKE CONSTFUNC Int_t Int$from_bool(Bool_t b) { return I_small(b); } +MACROLIKE CONSTFUNC Int_t Int$from_int32(Int32_t i) { + return Int$from_int64((Int32_t)i); +} +MACROLIKE CONSTFUNC Int_t Int$from_int16(Int16_t i) { + return I_small(i); +} +MACROLIKE CONSTFUNC Int_t Int$from_int8(Int8_t i) { + return I_small(i); +} +MACROLIKE CONSTFUNC Int_t Int$from_byte(Byte_t b) { + return I_small(b); +} +MACROLIKE CONSTFUNC Int_t Int$from_bool(Bool_t b) { + return I_small(b); +} #ifdef __GNUC__ #pragma GCC diagnostic pop diff --git a/src/stdlib/bools.h b/src/stdlib/bools.h index 52bd45a8..17ff3681 100644 --- a/src/stdlib/bools.h +++ b/src/stdlib/bools.h @@ -13,11 +13,23 @@ PUREFUNC Text_t Bool$as_text(const void *b, bool colorize, const TypeInfo_t *type); OptionalBool_t Bool$parse(Text_t text, Text_t *remainder); -MACROLIKE Bool_t Bool$from_int(Int_t i) { return (i.small != 0); } -MACROLIKE Bool_t Bool$from_int64(Int64_t i) { return (i != 0); } -MACROLIKE Bool_t Bool$from_int32(Int32_t i) { return (i != 0); } -MACROLIKE Bool_t Bool$from_int16(Int16_t i) { return (i != 0); } -MACROLIKE Bool_t Bool$from_int8(Int8_t i) { return (i != 0); } -MACROLIKE Bool_t Bool$from_byte(uint8_t b) { return (b != 0); } +MACROLIKE Bool_t Bool$from_int(Int_t i) { + return (i.small != 0); +} +MACROLIKE Bool_t Bool$from_int64(Int64_t i) { + return (i != 0); +} +MACROLIKE Bool_t Bool$from_int32(Int32_t i) { + return (i != 0); +} +MACROLIKE Bool_t Bool$from_int16(Int16_t i) { + return (i != 0); +} +MACROLIKE Bool_t Bool$from_int8(Int8_t i) { + return (i != 0); +} +MACROLIKE Bool_t Bool$from_byte(uint8_t b) { + return (b != 0); +} extern const TypeInfo_t Bool$info; diff --git a/src/stdlib/bytes.c b/src/stdlib/bytes.c index 4416d804..9874f222 100644 --- a/src/stdlib/bytes.c +++ b/src/stdlib/bytes.c @@ -25,12 +25,14 @@ PUREFUNC public Text_t Byte$as_text(const void *b, bool colorize, const TypeInfo '\0', }; Text_t text = Text$from_str(digits); - if (colorize) text = Texts(Text("\x1b[35m"), text, Text("\x1b[m")); + if (colorize) text = Text$concat(Text("\x1b[35m"), text, Text("\x1b[m")); return text; } public -CONSTFUNC bool Byte$is_between(const Byte_t x, const Byte_t low, const Byte_t high) { return low <= x && x <= high; } +CONSTFUNC bool Byte$is_between(const Byte_t x, const Byte_t low, const Byte_t high) { + return (low <= x && x <= high) || (high <= x && x <= low); +} public OptionalByte_t Byte$parse(Text_t text, OptionalInt_t base, Text_t *remainder) { @@ -67,7 +69,9 @@ public bool Byte$get_bit(Byte_t x, Int_t bit_index) { if (Int$compare_value(bit_index, I(1)) < 0) fail("Invalid bit index (expected 1 or higher): ", bit_index); if (Int$compare_value(bit_index, I(8)) > 0) - fail("Bit index is too large! There are only 8 bits in a byte, but index is: ", bit_index); + fail("Bit index is too large! There are only 8 bits in a byte, but index " + "is: ", + bit_index); return ((x & (Byte_t)(1L << (Int64$from_int(bit_index, true) - 1L))) != 0); } diff --git a/src/stdlib/bytes.h b/src/stdlib/bytes.h index 6581f300..b0a1c213 100644 --- a/src/stdlib/bytes.h +++ b/src/stdlib/bytes.h @@ -21,8 +21,12 @@ Byte_t Byte$from_int16(int16_t i, bool truncate); 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; } -MACROLIKE Byte_t Byte$from_bool(bool b) { return (Byte_t)b; } +MACROLIKE Byte_t Byte$from_int8(int8_t i) { + return (Byte_t)i; +} +MACROLIKE Byte_t Byte$from_bool(bool b) { + return (Byte_t)b; +} CONSTFUNC bool Byte$is_between(const Byte_t x, const Byte_t low, const Byte_t high); extern const Byte_t Byte$min; diff --git a/src/stdlib/c_strings.c b/src/stdlib/c_strings.c index 57960577..cbe46b68 100644 --- a/src/stdlib/c_strings.c +++ b/src/stdlib/c_strings.c @@ -70,8 +70,8 @@ const char *CString$join(const char *glue, List_t strings) { Text_t ret = EMPTY_TEXT; Text_t glue_text = Text$from_str(glue); for (int64_t i = 0; i < (int64_t)strings.length; i++) { - if (i > 0) ret = Texts(ret, glue_text); - ret = Texts(ret, Text$from_str(*(const char **)(strings.data + i * strings.stride))); + if (i > 0) ret = Text$concat(ret, glue_text); + ret = Text$concat(ret, Text$from_str(*(const char **)(strings.data + i * strings.stride))); } return Text$as_c_string(ret); } diff --git a/src/stdlib/enums.c b/src/stdlib/enums.c index b9b970fa..9cc16c5d 100644 --- a/src/stdlib/enums.c +++ b/src/stdlib/enums.c @@ -65,7 +65,7 @@ Text_t Enum$as_text(const void *obj, bool colorize, const TypeInfo_t *type) { NamedType_t value = type->EnumInfo.tags[tag - 1]; if (!value.type || value.type->size == 0) { Text_t text = Text$from_str(value.name); - return colorize ? Texts(Text("\x1b[1m"), text, Text("\x1b[m")) : text; + return colorize ? Text$concat(Text("\x1b[1m"), text, Text("\x1b[m")) : text; } return generic_as_text(obj + value_offset(type), colorize, value.type); diff --git a/src/stdlib/floatX.c.h b/src/stdlib/floatX.c.h index 54477ee3..cfef29fd 100644 --- a/src/stdlib/floatX.c.h +++ b/src/stdlib/floatX.c.h @@ -50,7 +50,7 @@ PUREFUNC Text_t NAMESPACED(as_text)(const void *x, bool colorize, const TypeInfo if (!x) return Text(TYPE_STR); static const Text_t color_prefix = Text("\x1b[35m"), color_suffix = Text("\x1b[m"); Text_t text = NAMESPACED(value_as_text)(*(FLOAT_T *)x); - return colorize ? Texts(color_prefix, text, color_suffix) : text; + return colorize ? Text$concat(color_prefix, text, color_suffix) : text; } public PUREFUNC int32_t NAMESPACED(compare)(const void *x, const void *y, const TypeInfo_t *info) { @@ -66,7 +66,9 @@ PUREFUNC int32_t NAMESPACED(compare)(const void *x, const void *y, const TypeInf } #elif FLOATX_C_H__BITS == 32 public -PUREFUNC Text_t NAMESPACED(value_as_text)(FLOAT_T x) { return Float64$value_as_text((double)x); } +PUREFUNC Text_t NAMESPACED(value_as_text)(FLOAT_T x) { + return Float64$value_as_text((double)x); +} public PUREFUNC Text_t NAMESPACED(as_text)(const void *x, bool colorize, const TypeInfo_t *info) { (void)info; @@ -115,7 +117,7 @@ public Text_t NAMESPACED(percent)(FLOAT_T x, FLOAT_T precision) { FLOAT_T d = SUFFIXED(100.) * x; d = NAMESPACED(with_precision)(d, precision); - return Texts(NAMESPACED(value_as_text)(d), Text("%")); + return Text$concat(NAMESPACED(value_as_text)(d), Text("%")); } public @@ -186,11 +188,17 @@ CONSTFUNC bool NAMESPACED(is_none)(const void *n, const TypeInfo_t *info) { } public -CONSTFUNC bool NAMESPACED(isinf)(FLOAT_T n) { return (fpclassify(n) == FP_INFINITE); } +CONSTFUNC bool NAMESPACED(isinf)(FLOAT_T n) { + return (fpclassify(n) == FP_INFINITE); +} public -CONSTFUNC bool NAMESPACED(finite)(FLOAT_T n) { return (fpclassify(n) != FP_INFINITE); } +CONSTFUNC bool NAMESPACED(finite)(FLOAT_T n) { + return (fpclassify(n) != FP_INFINITE); +} public -CONSTFUNC bool NAMESPACED(isnan)(FLOAT_T n) { return (fpclassify(n) == FP_NAN); } +CONSTFUNC bool NAMESPACED(isnan)(FLOAT_T n) { + return (fpclassify(n) == FP_NAN); +} public const TypeInfo_t NAMESPACED(info) = { diff --git a/src/stdlib/intX.c.h b/src/stdlib/intX.c.h index 0910c7f1..04c8ef3b 100644 --- a/src/stdlib/intX.c.h +++ b/src/stdlib/intX.c.h @@ -55,12 +55,12 @@ public void NAMESPACED(serialize)(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) { (void)info, (void)pointers; #if INTX_C_H__INT_BITS < 32 - fwrite(obj, sizeof(INT_T), 1, out); + if (fwrite(obj, sizeof(INT_T), 1, out) != sizeof(INT_T)) fail("Failed to write whole integer"); #else INT_T i = *(INT_T *)obj; UINT_T z = (UINT_T)((i << 1L) ^ (i >> (INTX_C_H__INT_BITS - 1L))); // Zigzag encode while (z >= 0x80L) { - fputc((uint8_t)(z | 0x80L), out); + if (fputc((uint8_t)(z | 0x80L), out) == EOF) fail("Failed to write full integer"); z >>= 7L; } fputc((uint8_t)z, out); @@ -71,11 +71,13 @@ public void NAMESPACED(deserialize)(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *info) { (void)info, (void)pointers; #if INTX_C_H__INT_BITS < 32 - fread(outval, sizeof(INT_T), 1, in); + if (fread(outval, sizeof(INT_T), 1, in) != sizeof(INT_T)) fail("Failed to read full integer"); #else UINT_T z = 0; for (size_t shift = 0;; shift += 7) { - uint8_t byte = (uint8_t)fgetc(in); + int i = fgetc(in); + if (i == EOF) fail("Failed to read whole integer"); + uint8_t byte = (uint8_t)i; z |= ((UINT_T)(byte & 0x7F)) << shift; if ((byte & 0x80) == 0) break; } @@ -96,10 +98,12 @@ Text_t NAMESPACED(as_text)(const void *i, bool colorize, const TypeInfo_t *info) (void)info; if (!i) return Text(NAME_STR); Text_t text = _int64_to_text((int64_t)(*(INT_T *)i)); - return colorize ? Texts(Text("\033[35m"), text, Text("\033[m")) : text; + return colorize ? Text$concat(Text("\033[35m"), text, Text("\033[m")) : text; } public -Text_t NAMESPACED(value_as_text)(INT_T i) { return _int64_to_text((int64_t)i); } +Text_t NAMESPACED(value_as_text)(INT_T i) { + return _int64_to_text((int64_t)i); +} public PUREFUNC int32_t NAMESPACED(compare)(const void *x, const void *y, const TypeInfo_t *info) { (void)info; @@ -112,10 +116,12 @@ PUREFUNC bool NAMESPACED(equal)(const void *x, const void *y, const TypeInfo_t * } public CONSTFUNC bool NAMESPACED(is_between)(const INT_T x, const INT_T low, const INT_T high) { - return low <= x && x <= high; + return (low <= x && x <= high) || (high <= x && x <= low); } public -CONSTFUNC INT_T NAMESPACED(clamped)(INT_T x, INT_T min, INT_T max) { return x < min ? min : (x > max ? max : x); } +CONSTFUNC INT_T NAMESPACED(clamped)(INT_T x, INT_T min, INT_T max) { + return x < min ? min : (x > max ? max : x); +} public Text_t NAMESPACED(hex)(INT_T i, Int_t digits_int, bool uppercase, bool prefix) { Int_t as_int = Int$from_int64((int64_t)i); diff --git a/src/stdlib/intX.h b/src/stdlib/intX.h index 4d8f8e3d..c90babcb 100644 --- a/src/stdlib/intX.h +++ b/src/stdlib/intX.h @@ -49,8 +49,12 @@ Closure_t NAMESPACED(onward)(INTX_T first, INTX_T step); 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; } -MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_bool)(Bool_t b) { return (INTX_T)b; } +MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_byte)(Byte_t b) { + return (INTX_T)b; +} +MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_bool)(Bool_t b) { + return (INTX_T)b; +} CONSTFUNC INTX_T NAMESPACED(gcd)(INTX_T x, INTX_T y); extern const INTX_T NAMESPACED(min), NAMESPACED(max); extern const TypeInfo_t NAMESPACED(info); @@ -75,15 +79,25 @@ MACROLIKE INTX_T NAMESPACED(modulo)(INTX_T D, INTX_T d) { return r; } -MACROLIKE INTX_T NAMESPACED(modulo1)(INTX_T D, INTX_T d) { return NAMESPACED(modulo)(D - 1, d) + 1; } +MACROLIKE INTX_T NAMESPACED(modulo1)(INTX_T D, INTX_T d) { + return NAMESPACED(modulo)(D - 1, d) + 1; +} -MACROLIKE PUREFUNC INTX_T NAMESPACED(wrapping_plus)(INTX_T x, INTX_T y) { return (INTX_T)((UINTX_T)x + (UINTX_T)y); } +MACROLIKE PUREFUNC INTX_T NAMESPACED(wrapping_plus)(INTX_T x, INTX_T y) { + return (INTX_T)((UINTX_T)x + (UINTX_T)y); +} -MACROLIKE PUREFUNC INTX_T NAMESPACED(wrapping_minus)(INTX_T x, INTX_T y) { return (INTX_T)((UINTX_T)x + (UINTX_T)y); } +MACROLIKE PUREFUNC INTX_T NAMESPACED(wrapping_minus)(INTX_T x, INTX_T y) { + return (INTX_T)((UINTX_T)x + (UINTX_T)y); +} -MACROLIKE PUREFUNC INTX_T NAMESPACED(unsigned_left_shifted)(INTX_T x, INTX_T y) { return (INTX_T)((UINTX_T)x << y); } +MACROLIKE PUREFUNC INTX_T NAMESPACED(unsigned_left_shifted)(INTX_T x, INTX_T y) { + return (INTX_T)((UINTX_T)x << y); +} -MACROLIKE PUREFUNC INTX_T NAMESPACED(unsigned_right_shifted)(INTX_T x, INTX_T y) { return (INTX_T)((UINTX_T)x >> y); } +MACROLIKE PUREFUNC INTX_T NAMESPACED(unsigned_right_shifted)(INTX_T x, INTX_T y) { + return (INTX_T)((UINTX_T)x >> y); +} void NAMESPACED(serialize)(const void *obj, FILE *out, Table_t *, const TypeInfo_t *); void NAMESPACED(deserialize)(FILE *in, void *outval, List_t *, const TypeInfo_t *); @@ -122,7 +136,9 @@ MACROLIKE PUREFUNC INTX_T NAMESPACED(from_int64)(Int64_t i64, bool truncate) { return i; } #elif INTX_H__INT_BITS > 64 -MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int64)(Int64_t i) { return (INTX_T)i; } +MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int64)(Int64_t i) { + return (INTX_T)i; +} #endif #if INTX_H__INT_BITS < 32 @@ -132,7 +148,9 @@ MACROLIKE PUREFUNC INTX_T NAMESPACED(from_int32)(Int32_t i32, bool truncate) { return i; } #elif INTX_H__INT_BITS > 32 -MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int32)(Int32_t i) { return (INTX_T)i; } +MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int32)(Int32_t i) { + return (INTX_T)i; +} #endif #if INTX_H__INT_BITS < 16 @@ -142,11 +160,15 @@ MACROLIKE PUREFUNC INTX_T NAMESPACED(from_int16)(Int16_t i16, bool truncate) { return i; } #elif INTX_H__INT_BITS > 16 -MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int16)(Int16_t i) { return (INTX_T)i; } +MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int16)(Int16_t i) { + return (INTX_T)i; +} #endif #if INTX_H__INT_BITS > 8 -MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int8)(Int8_t i) { return (INTX_T)i; } +MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int8)(Int8_t i) { + return (INTX_T)i; +} #endif #undef PASTE3_ diff --git a/src/stdlib/lists.c b/src/stdlib/lists.c index 1a47f2e3..93dc63e9 100644 --- a/src/stdlib/lists.c +++ b/src/stdlib/lists.c @@ -440,10 +440,14 @@ List_t List$sample(List_t list, Int_t int_n, List_t weights, OptionalClosure_t r } public -List_t List$from(List_t list, Int_t first) { return List$slice(list, first, I_small(-1)); } +List_t List$from(List_t list, Int_t first) { + return List$slice(list, first, I_small(-1)); +} public -List_t List$to(List_t list, Int_t last) { return List$slice(list, I_small(1), last); } +List_t List$to(List_t list, Int_t last) { + return List$slice(list, I_small(1), last); +} public List_t List$by(List_t list, Int_t int_stride, int64_t padded_item_size) { @@ -554,7 +558,9 @@ bool List$has(List_t list, void *item, const TypeInfo_t *type) { } public -void List$clear(List_t *list) { *list = list->atomic ? EMPTY_ATOMIC_LIST : EMPTY_LIST; } +void List$clear(List_t *list) { + *list = list->atomic ? EMPTY_ATOMIC_LIST : EMPTY_LIST; +} public int32_t List$compare(const void *vx, const void *vy, const TypeInfo_t *type) { diff --git a/src/stdlib/lists.h b/src/stdlib/lists.h index 457fed52..9ac8bf1b 100644 --- a/src/stdlib/lists.h +++ b/src/stdlib/lists.h @@ -20,8 +20,9 @@ extern char _EMPTY_LIST_SENTINEL; int64_t index = index_expr; \ int64_t off = index + (index < 0) * (list.length + 1) - 1; \ if (unlikely(off < 0 || off >= list.length)) \ - fail_source(__SOURCE_FILE__, start, end, "Invalid list index: ", index, " (list has length ", \ - (int64_t)list.length, ")\n"); \ + fail_source(__SOURCE_FILE__, start, end, \ + Text$concat(Text("Invalid list index: "), convert_to_text(index), Text(" (list has length "), \ + convert_to_text((int64_t)list.length), Text(")\n"))); \ *(item_type *)(list.data + list.stride * off); \ }) #define List_get(list_expr, index_expr, item_type, var, optional_expr, none_expr) \ @@ -40,8 +41,9 @@ extern char _EMPTY_LIST_SENTINEL; int64_t index = index_expr; \ int64_t off = index + (index < 0) * (list->length + 1) - 1; \ if (unlikely(off < 0 || off >= list->length)) \ - fail_source(__SOURCE_FILE__, start, end, "Invalid list index: ", index, " (list has length ", \ - (int64_t)list->length, ")\n"); \ + fail_source(__SOURCE_FILE__, start, end, \ + Text$concat(Text("Invalid list index: "), convert_to_text(index), Text(" (list has length "), \ + convert_to_text((int64_t)list->length), Text(")\n"))); \ if (list->data_refcount > 0) List$compact(list, sizeof(item_type)); \ (item_type *)(list->data + list->stride * off); \ }) @@ -61,7 +63,8 @@ extern char _EMPTY_LIST_SENTINEL; t items[] = {__VA_ARGS__}; \ (List_t){.length = sizeof(items) / sizeof(items[0]), \ .stride = (int64_t)&items[1] - (int64_t)&items[0], \ - .data = memcpy(GC_MALLOC(sizeof(items)), items, sizeof(items)), \ + .data = sizeof(items) == 0 ? &_EMPTY_LIST_SENTINEL \ + : memcpy(GC_MALLOC(sizeof(items)), items, sizeof(items)), \ .atomic = 0, \ .data_refcount = 0}; \ }) @@ -70,7 +73,7 @@ extern char _EMPTY_LIST_SENTINEL; t items[N] = {__VA_ARGS__}; \ (List_t){.length = N, \ .stride = (int64_t)&items[1] - (int64_t)&items[0], \ - .data = memcpy(GC_MALLOC(sizeof(items)), items, sizeof(items)), \ + .data = N == 0 ? &_EMPTY_LIST_SENTINEL : memcpy(GC_MALLOC(sizeof(items)), items, sizeof(items)), \ .atomic = 0, \ .data_refcount = 0}; \ }) diff --git a/src/stdlib/mapmacro.h b/src/stdlib/mapmacro.h index 7b0e3c4e..5e9eaa36 100644 --- a/src/stdlib/mapmacro.h +++ b/src/stdlib/mapmacro.h @@ -7,9 +7,7 @@ #define EVAL0(...) __VA_ARGS__ #define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__))) #define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) -#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) -#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) -#define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) +#define EVAL(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) #define MAP_END(...) #define MAP_OUT diff --git a/src/stdlib/memory.c b/src/stdlib/memory.c index fd396463..53a180fb 100644 --- a/src/stdlib/memory.c +++ b/src/stdlib/memory.c @@ -18,7 +18,7 @@ Text_t Memory$as_text(const void *p, bool colorize, const TypeInfo_t *info) { (void)info; if (!p) return Text("Memory"); Text_t text = Text$from_str(String("Memory<", (void *)p, ">")); - return colorize ? Texts(Text("\x1b[0;34;1m"), text, Text("\x1b[m")) : text; + return colorize ? Text$concat(Text("\x1b[0;34;1m"), text, Text("\x1b[m")) : text; } public diff --git a/src/stdlib/numX.h b/src/stdlib/numX.h new file mode 100644 index 00000000..87794762 --- /dev/null +++ b/src/stdlib/numX.h @@ -0,0 +1,115 @@ +// Template header for 64 and 32 bit Nums +// This file expects `NUMX_H__BITS` to be defined before including: +// +// #define NUMX_H__BITS 64 +// #include "numX.h" +// + +#include <stdbool.h> +#include <stdint.h> + +#include "datatypes.h" +#include "stdlib.h" +#include "types.h" +#include "util.h" + +#ifndef NUMX_H__BITS +#define NUMX_H__BITS 64 +#endif + +#if NUMX_H__BITS == 64 +#define NUM_T double +#define OPT_T double +#define NAMESPACED(x) Num$##x +#define TYPE_STR "Num" +#define SUFFIXED(x) x +#elif NUMX_H__BITS == 32 +#define NUM_T float +#define OPT_T float +#define NAMESPACED(x) Num32$##x +#define TYPE_STR "Num32" +#define SUFFIXED(x) x##f +#else +#error "Unsupported bit width for Num" +#endif + +Text_t NAMESPACED(as_text)(const void *x, bool colorize, const TypeInfo_t *type); +Text_t NAMESPACED(value_as_text)(NUM_T x); +PUREFUNC int32_t NAMESPACED(compare)(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC bool NAMESPACED(equal)(const void *x, const void *y, const TypeInfo_t *type); +CONSTFUNC bool NAMESPACED(near)(NUM_T a, NUM_T b, NUM_T ratio, NUM_T absolute); +Text_t NAMESPACED(percent)(NUM_T x, NUM_T precision); +NUM_T CONSTFUNC NAMESPACED(with_precision)(NUM_T num, NUM_T precision); +NUM_T NAMESPACED(mod)(NUM_T num, NUM_T modulus); +NUM_T NAMESPACED(mod1)(NUM_T num, NUM_T modulus); +CONSTFUNC bool NAMESPACED(isinf)(NUM_T n); +CONSTFUNC bool NAMESPACED(finite)(NUM_T n); +CONSTFUNC bool NAMESPACED(isnan)(NUM_T n); +bool NAMESPACED(is_none)(const void *n, const TypeInfo_t *info); +NUM_T NAMESPACED(nan)(Text_t tag); +CONSTFUNC NUM_T NAMESPACED(mix)(NUM_T amount, NUM_T x, NUM_T y); +OPT_T NAMESPACED(parse)(Text_t text, Text_t *remainder); +CONSTFUNC bool NAMESPACED(is_between)(const NUM_T x, const NUM_T low, const NUM_T high); +CONSTFUNC NUM_T NAMESPACED(clamped)(NUM_T x, NUM_T low, NUM_T high); + +#if NUMX_H__BITS == 64 +MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_num32)(float n) { + return (NUM_T)n; +} +#elif NUMX_H__BITS == 32 +MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_num64)(double n) { + return (NUM_T)n; +} +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif +MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_int)(Int_t i, bool truncate) { + if likely (i.small & 0x1) { + NUM_T ret = (NUM_T)(i.small >> 2); + if unlikely (!truncate && (int64_t)ret != (i.small >> 2)) + fail("Could not convert integer to " TYPE_STR " without losing precision: ", i.small >> 2); + return ret; + } else { + NUM_T ret = mpz_get_d(i.big); + if (!truncate) { + mpz_t roundtrip; + mpz_init_set_d(roundtrip, (double)ret); + if unlikely (mpz_cmp(i.big, roundtrip) != 0) + fail("Could not convert integer to " TYPE_STR " without losing precision: ", i); + } + return ret; + } +} +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_int64)(Int64_t i, bool truncate) { + NUM_T n = (NUM_T)i; + if unlikely (!truncate && (Int64_t)n != i) + fail("Could not convert integer to " TYPE_STR " without losing precision: ", i); + return n; +} +MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_int32)(Int32_t i) { + return (NUM_T)i; +} +MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_int16)(Int16_t i) { + return (NUM_T)i; +} +MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_int8)(Int8_t i) { + return (NUM_T)i; +} +MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_byte)(Byte_t i) { + return (NUM_T)i; +} + +extern const TypeInfo_t NAMESPACED(info); + +#undef NUM_T +#undef OPT_T +#undef NAMESPACED +#undef TYPE_STR +#undef SUFFIXED +#undef NUMX_H__BITS diff --git a/src/stdlib/paths.c b/src/stdlib/paths.c index ed8383fd..dcedcd03 100644 --- a/src/stdlib/paths.c +++ b/src/stdlib/paths.c @@ -98,7 +98,9 @@ Path_t Path$from_str(const char *str) { } public -Path_t Path$from_text(Text_t text) { return Path$from_str(Text$as_c_string(text)); } +Path_t Path$from_text(Text_t text) { + return Path$from_str(Text$as_c_string(text)); +} public Path_t Path$expand_home(Path_t path) { @@ -324,6 +326,79 @@ Result_t Path$append_bytes(Path_t path, List_t bytes, int permissions) { return _write(path, bytes, O_WRONLY | O_APPEND | O_CREAT, permissions); } +typedef struct { + const char *path_str; + int fd; + int mode; + int permissions; +} writer_data_t; + +static Result_t _write_bytes_to_fd(List_t bytes, bool close_file, void *userdata) { + writer_data_t *data = userdata; + if (bytes.length > 0) { + if (data->fd == -1) { + data->fd = open(data->path_str, data->mode, data->permissions); + if (data->fd == -1) { + if (errno == EMFILE || errno == ENFILE) { + // If we hit file handle limits, run GC collection to try to clean up any lingering file handles + // that will be closed by GC finalizers. + GC_gcollect(); + data->fd = open(data->path_str, data->mode, data->permissions); + } + if (data->fd == -1) + return FailureResult("Could not write to file: ", data->path_str, " (", strerror(errno), ")"); + } + } + + if (bytes.stride != 1) List$compact(&bytes, 1); + ssize_t written = write(data->fd, bytes.data, (size_t)bytes.length); + if (written != (ssize_t)bytes.length) + return FailureResult("Could not write to file: ", data->path_str, " (", strerror(errno), ")"); + } + // After first successful write, all writes are appends + data->mode = (O_WRONLY | O_CREAT | O_APPEND); + + if (close_file && data->fd != -1) { + if (close(data->fd) == -1) + return FailureResult("Failed to close file: ", data->path_str, " (", strerror(errno), ")"); + data->fd = -1; + } + return SuccessResult; +} + +static Result_t _write_text_to_fd(Text_t text, bool close_file, void *userdata) { + return _write_bytes_to_fd(Text$utf8(text), close_file, userdata); +} + +static void _writer_cleanup(writer_data_t *data) { + if (data && data->fd != -1) { + close(data->fd); + data->fd = -1; + } +} + +public +Closure_t Path$byte_writer(Path_t path, bool append, int permissions) { + path = Path$expand_home(path); + const char *path_str = Path$as_c_string(path); + int mode = append ? (O_WRONLY | O_CREAT | O_APPEND) : (O_WRONLY | O_CREAT | O_TRUNC); + writer_data_t *userdata = + new (writer_data_t, .fd = -1, .path_str = path_str, .mode = mode, .permissions = permissions); + GC_register_finalizer(userdata, (void *)_writer_cleanup, NULL, NULL, NULL); + return (Closure_t){.fn = _write_bytes_to_fd, .userdata = userdata}; +} + +public +Closure_t Path$writer(Path_t path, bool append, int permissions) { + path = Path$expand_home(path); + const char *path_str = Path$as_c_string(path); + int mode = append ? (O_WRONLY | O_CREAT | O_APPEND) : (O_WRONLY | O_CREAT | O_TRUNC); + writer_data_t *userdata = + new (writer_data_t, .fd = -1, .path_str = path_str, .mode = mode, .permissions = permissions); + GC_register_finalizer(userdata, (void *)_writer_cleanup, NULL, NULL, NULL); + return (Closure_t){.fn = _write_text_to_fd, .userdata = userdata}; +} + public OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t count) { path = Path$expand_home(path); @@ -331,8 +406,8 @@ OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t count) { int fd = open(path_str, O_RDONLY); if (fd == -1) { if (errno == EMFILE || errno == ENFILE) { - // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that will - // be closed by GC finalizers. + // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that + // will be closed by GC finalizers. GC_gcollect(); fd = open(path_str, O_RDONLY); } @@ -574,7 +649,9 @@ OptionalPath_t Path$write_unique_bytes(Path_t path, List_t bytes) { } public -OptionalPath_t Path$write_unique(Path_t path, Text_t text) { return Path$write_unique_bytes(path, Text$utf8(text)); } +OptionalPath_t Path$write_unique(Path_t path, Text_t text) { + return Path$write_unique_bytes(path, Text$utf8(text)); +} public OptionalPath_t Path$parent(Path_t path) { @@ -619,7 +696,7 @@ bool Path$has_extension(Path_t path, Text_t extension) { if (extension.length == 0) return !Text$has(Text$from(last, I(2)), Text(".")) || Text$equal_values(last, Text("..")); - if (!Text$starts_with(extension, Text("."), NULL)) extension = Texts(Text("."), extension); + if (!Text$starts_with(extension, Text("."), NULL)) extension = Text$concat(Text("."), extension); return Text$ends_with(Text$from(last, I(2)), extension, NULL); } @@ -638,7 +715,9 @@ Path_t Path$child(Path_t path, Text_t name) { } public -Path_t Path$sibling(Path_t path, Text_t name) { return Path$child(Path$parent(path), name); } +Path_t Path$sibling(Path_t path, Text_t name) { + return Path$child(Path$parent(path), name); +} public OptionalPath_t Path$with_extension(Path_t path, Text_t extension, bool replace) { @@ -705,8 +784,8 @@ OptionalClosure_t Path$by_line(Path_t path) { FILE *f = fopen(path_str, "r"); if (f == NULL) { if (errno == EMFILE || errno == ENFILE) { - // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that will - // be closed by GC finalizers. + // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that + // will be closed by GC finalizers. GC_gcollect(); f = fopen(path_str, "r"); } @@ -726,8 +805,8 @@ OptionalList_t Path$lines(Path_t path) { FILE *f = fopen(path_str, "r"); if (f == NULL) { if (errno == EMFILE || errno == ENFILE) { - // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that will - // be closed by GC finalizers. + // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that + // will be closed by GC finalizers. GC_gcollect(); f = fopen(path_str, "r"); } @@ -794,7 +873,9 @@ int Path$print(FILE *f, Path_t path) { } public -const char *Path$as_c_string(Path_t path) { return String(path); } +const char *Path$as_c_string(Path_t path) { + return String(path); +} public Text_t Path$as_text(const void *obj, bool color, const TypeInfo_t *type) { @@ -808,7 +889,7 @@ Text_t Path$as_text(const void *obj, bool color, const TypeInfo_t *type) { && (path->components.length == 0 || !Text$equal_values(*(Text_t *)(path->components.data), Text("..")))) text = Text$concat(path->components.length > 0 ? Text("./") : Text("."), text); - if (color) text = Texts(Text("\033[32;1m"), text, Text("\033[m")); + if (color) text = Text$concat(Text("\033[32;1m"), text, Text("\033[m")); return text; } diff --git a/src/stdlib/paths.h b/src/stdlib/paths.h index 881a3c78..c272314c 100644 --- a/src/stdlib/paths.h +++ b/src/stdlib/paths.h @@ -39,6 +39,8 @@ Result_t Path$write(Path_t path, Text_t text, int permissions); Result_t Path$write_bytes(Path_t path, List_t bytes, int permissions); Result_t Path$append(Path_t path, Text_t text, int permissions); Result_t Path$append_bytes(Path_t path, List_t bytes, int permissions); +Closure_t Path$byte_writer(Path_t path, bool append, int permissions); +Closure_t Path$writer(Path_t path, bool append, int permissions); OptionalText_t Path$read(Path_t path); OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t limit); Result_t Path$set_owner(Path_t path, OptionalText_t owner, OptionalText_t group, bool follow_symlinks); diff --git a/src/stdlib/pointers.c b/src/stdlib/pointers.c index 74037613..0bf9a274 100644 --- a/src/stdlib/pointers.c +++ b/src/stdlib/pointers.c @@ -38,14 +38,14 @@ Text_t Pointer$as_text(const void *x, bool colorize, const TypeInfo_t *type) { if (top_level) { root = ptr; } else if (ptr == root) { - Text_t text = Texts(Text$from_str(ptr_info.sigil), Text("~1")); - return colorize ? Texts(Text("\x1b[34;1m"), text, Text("\x1b[m")) : text; + Text_t text = Text$concat(Text$from_str(ptr_info.sigil), Text("~1")); + return colorize ? Text$concat(Text("\x1b[34;1m"), text, Text("\x1b[m")) : text; } else { TypeInfo_t rec_table = *Table$info(type, &Int64$info); int64_t *id = Table$get(pending, x, &rec_table); if (id) { - Text_t text = Texts(Text$from_str(ptr_info.sigil), Int64$value_as_text(*id)); - return colorize ? Texts(Text("\x1b[34;1m"), text, Text("\x1b[m")) : text; + Text_t text = Text$concat(Text$from_str(ptr_info.sigil), Int64$value_as_text(*id)); + return colorize ? Text$concat(Text("\x1b[34;1m"), text, Text("\x1b[m")) : text; } int64_t next_id = (int64_t)pending.entries.length + 2; Table$set(&pending, x, &next_id, &rec_table); diff --git a/src/stdlib/print.h b/src/stdlib/print.h index 65de72a0..0837390f 100644 --- a/src/stdlib/print.h +++ b/src/stdlib/print.h @@ -81,10 +81,18 @@ int _print_real(FILE *f, Real_t x); int _print_hex(FILE *f, hex_format_t hex); int _print_hex_double(FILE *f, hex_double_t hex); int _print_oct(FILE *f, oct_format_t oct); -PRINT_FN _print_float(FILE *f, float x) { return _print_double(f, (double)x); } -PRINT_FN _print_pointer(FILE *f, void *p) { return _print_hex(f, hex((uint64_t)p)); } -PRINT_FN _print_bool(FILE *f, bool b) { return fputs(b ? "yes" : "no", f); } -PRINT_FN _print_str(FILE *f, const char *s) { return fputs(s ? s : "(null)", f); } +PRINT_FN _print_float(FILE *f, float x) { + return _print_double(f, (double)x); +} +PRINT_FN _print_pointer(FILE *f, void *p) { + return _print_hex(f, hex((uint64_t)p)); +} +PRINT_FN _print_bool(FILE *f, bool b) { + return fputs(b ? "yes" : "no", f); +} +PRINT_FN _print_str(FILE *f, const char *s) { + return fputs(s ? s : "(null)", f); +} int _print_char(FILE *f, char c); int _print_quoted(FILE *f, quoted_t quoted); PRINT_FN _print_string_slice(FILE *f, string_slice_t slice) { diff --git a/src/stdlib/stacktrace.c b/src/stdlib/stacktrace.c index ea939f62..1eba8188 100644 --- a/src/stdlib/stacktrace.c +++ b/src/stdlib/stacktrace.c @@ -98,7 +98,7 @@ void print_stacktrace(FILE *out, int offset) { cwd[cwd_len++] = '/'; cwd[cwd_len] = '\0'; - const char *install_dir = String(TOMO_PATH, "/lib/tomo@" TOMO_VERSION "/"); + const char *install_dir = String(TOMO_PATH, "/lib/tomo@", TOMO_VERSION, "/"); static void *stack[1024]; int64_t size = (int64_t)backtrace(stack, sizeof(stack) / sizeof(stack[0])); diff --git a/src/stdlib/stdlib.c b/src/stdlib/stdlib.c index f4e6d678..a05b8753 100644 --- a/src/stdlib/stdlib.c +++ b/src/stdlib/stdlib.c @@ -17,6 +17,7 @@ #include "files.h" #include "metamethods.h" #include "optionals.h" +#include "paths.h" #include "print.h" #include "siphash.h" #include "stacktrace.h" @@ -39,11 +40,91 @@ static ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) { public bool USE_COLOR; + +public +const char *TOMO_PATH = "/usr/local"; + public -Text_t TOMO_VERSION_TEXT = Text(TOMO_VERSION); +const char *TOMO_VERSION = "v0"; + +public +Text_t TOMO_VERSION_TEXT = Text("v0"); + +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include <dlfcn.h> + +static inline const char *get_library_path(void *func) { + static Dl_info info; + if (dladdr(func, &info)) { + return info.dli_fname; // full path of the library + } + return NULL; +} + +#elif defined(_WIN32) +#include <windows.h> + +static inline const char *get_library_path(void *func) { + static char path[MAX_PATH]; + HMODULE hm = NULL; + if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCSTR)func, &hm)) { + if (GetModuleFileName(hm, path, MAX_PATH)) { + return path; + } + } + return NULL; +} + +#else +#error "Unsupported platform" +#endif + +char *find_in_path(const char *name) { + if (strchr(name, '/')) { + // name contains a slash → treat as path + char *abs = realpath(name, NULL); + if (abs == NULL) fail("Couldn't find real path of: ", name); + char *ret = String(abs); + free(abs); + return ret; + } + + char *path_env = getenv("PATH"); + if (!path_env) return NULL; + + char *paths = strdup(path_env); + if (!paths) return NULL; + + char *token = strtok(paths, ":"); + while (token) { + char candidate[PATH_MAX]; + snprintf(candidate, sizeof(candidate), "%s/%s", token, name); + if (access(candidate, X_OK) == 0) { + char *abs = realpath(candidate, NULL); + free(paths); + char *ret = String(abs); + free(abs); + return ret; + } + token = strtok(NULL, ":"); + } + + free(paths); + return NULL; // not found +} public -const char *TOMO_PATH = TOMO_INSTALL; +void tomo_configure(void) { + const char *p = get_library_path(get_library_path); + p = find_in_path(p); + Path_t path = Path$from_str(p); + TOMO_PATH = Path$as_c_string(Path$parent(Path$parent(path))); + Text_t base_name = Path$base_name(path); + TOMO_VERSION_TEXT = Text$without_suffix( + Text$without_prefix(Text$without_prefix(base_name, Text("lib")), Text("tomo@")), Text(".so")); + TOMO_VERSION = Text$as_c_string(TOMO_VERSION_TEXT); +} static _Noreturn void signal_handler(int sig, siginfo_t *info, void *userdata) { (void)info, (void)userdata; @@ -60,6 +141,7 @@ static _Noreturn void signal_handler(int sig, siginfo_t *info, void *userdata) { public void tomo_init(void) { GC_INIT(); + tomo_configure(); const char *color_env = getenv("COLOR"); USE_COLOR = color_env ? strcmp(color_env, "1") == 0 : isatty(STDOUT_FILENO); const char *no_color_env = getenv("NO_COLOR"); @@ -77,10 +159,14 @@ void tomo_init(void) { } public -_Noreturn void fail_text(Text_t message) { fail(message); } +_Noreturn void fail_text(Text_t message) { + fail(message); +} public -Text_t builtin_last_err() { return Text$from_str(strerror(errno)); } +Text_t builtin_last_err() { + return Text$from_str(strerror(errno)); +} static int _inspect_depth = 0; static file_t *file = NULL; @@ -246,7 +332,9 @@ typedef struct cleanup_s { static cleanup_t *cleanups = NULL; public -void tomo_at_cleanup(Closure_t fn) { cleanups = new (cleanup_t, .cleanup_fn = fn, .next = cleanups); } +void tomo_at_cleanup(Closure_t fn) { + cleanups = new (cleanup_t, .cleanup_fn = fn, .next = cleanups); +} public void tomo_cleanup(void) { diff --git a/src/stdlib/stdlib.h b/src/stdlib/stdlib.h index 3afe3529..087ed4bf 100644 --- a/src/stdlib/stdlib.h +++ b/src/stdlib/stdlib.h @@ -15,6 +15,7 @@ extern bool USE_COLOR; extern Text_t TOMO_VERSION_TEXT; +void tomo_configure(void); void tomo_init(void); void tomo_at_cleanup(Closure_t fn); void tomo_cleanup(void); @@ -36,7 +37,7 @@ void tomo_cleanup(void); exit(1); \ }) -#define fail_source(filename, start, end, ...) \ +#define fail_source(filename, start, end, message) \ ({ \ tomo_cleanup(); \ fflush(stdout); \ @@ -45,7 +46,7 @@ void tomo_cleanup(void); print_stacktrace(stderr, 0); \ fputs("\n", stderr); \ if (USE_COLOR) fputs("\x1b[31;1m", stderr); \ - fprint_inline(stderr, __VA_ARGS__); \ + Text$print(stderr, message); \ file_t *_file = (filename) ? load_file(filename) : NULL; \ if ((filename) && _file) { \ fputs("\n", stderr); \ diff --git a/src/stdlib/structs.c b/src/stdlib/structs.c index da8f1461..d1b9f824 100644 --- a/src/stdlib/structs.c +++ b/src/stdlib/structs.c @@ -126,10 +126,10 @@ PUREFUNC public Text_t Struct$as_text(const void *obj, bool colorize, const Type Text_t name = Text$from_str(type->StructInfo.name); if (type->StructInfo.is_secret || type->StructInfo.is_opaque) { - return colorize ? Texts(Text("\x1b[0;1m"), name, Text("\x1b[m(...)")) : Texts(name, Text("(...)")); + return colorize ? Text$concat(Text("\x1b[0;1m"), name, Text("\x1b[m(...)")) : Text$concat(name, Text("(...)")); } - Text_t text = colorize ? Texts(Text("\x1b[0;1m"), name, Text("\x1b[m(")) : Texts(name, Text("(")); + Text_t text = colorize ? Text$concat(Text("\x1b[0;1m"), name, Text("\x1b[m(")) : Text$concat(name, Text("(")); ptrdiff_t byte_offset = 0; ptrdiff_t bit_offset = 0; for (int i = 0; i < type->StructInfo.num_fields; i++) { diff --git a/src/stdlib/tables.c b/src/stdlib/tables.c index a801957f..753059c8 100644 --- a/src/stdlib/tables.c +++ b/src/stdlib/tables.c @@ -24,14 +24,6 @@ #include "types.h" #include "util.h" -// #define DEBUG_TABLES - -#ifdef DEBUG_TABLES -#define hdebug(...) print_inline("\x1b[2m", __VA_ARGS__, "\x1b[m") -#else -#define hdebug(...) (void)0 -#endif - // Helper accessors for type functions/values: #define HASH_KEY(t, k) (generic_hash((k), type->TableInfo.key) % ((t).bucket_info->count)) #define EQUAL_KEYS(x, y) (generic_equal((x), (y), type->TableInfo.key)) @@ -76,18 +68,6 @@ PUREFUNC static INLINE size_t value_offset(const TypeInfo_t *info) { return offset; } -static INLINE void hshow(const Table_t *t) { - hdebug("{"); - for (uint32_t i = 0; t->bucket_info && i < t->bucket_info->count; i++) { - if (i > 0) hdebug(" "); - if (t->bucket_info->buckets[i].occupied) - hdebug("[", i, "]=", (uint32_t)t->bucket_info->buckets[i].index, "(", - t->bucket_info->buckets[i].next_bucket, ")"); - else hdebug("[", i, "]=_"); - } - hdebug("}\n"); -} - static void maybe_copy_on_write(Table_t *t, const TypeInfo_t *type) { if (t->entries.data_refcount != 0) List$compact(&t->entries, (int64_t)entry_size(type)); @@ -104,14 +84,10 @@ PUREFUNC public void *Table$get_raw(Table_t t, const void *key, const TypeInfo_t if (!key || !t.bucket_info) return NULL; uint64_t hash = HASH_KEY(t, key); - hshow(&t); - hdebug("Getting value with initial probe at ", hash, "\n"); bucket_t *buckets = t.bucket_info->buckets; for (uint64_t i = hash; buckets[i].occupied; i = buckets[i].next_bucket) { - hdebug("Checking against key in bucket ", i, "\n"); void *entry = GET_ENTRY(t, buckets[i].index); if (EQUAL_KEYS(entry, key)) { - hdebug("Found key!\n"); return entry + value_offset(type); } if (buckets[i].next_bucket == END_OF_CHAIN) break; @@ -130,24 +106,18 @@ PUREFUNC public void *Table$get(Table_t t, const void *key, const TypeInfo_t *ty static void Table$set_bucket(Table_t *t, const void *entry, int32_t index, const TypeInfo_t *type) { assert(t->bucket_info); - hshow(t); const void *key = entry; bucket_t *buckets = t->bucket_info->buckets; uint64_t hash = HASH_KEY(*t, key); - hdebug("Hash value (mod ", (int32_t)t->bucket_info->count, ") = ", hash, "\n"); bucket_t *bucket = &buckets[hash]; if (!bucket->occupied) { - hdebug("Got an empty space\n"); // Empty space: bucket->occupied = 1; bucket->index = index; bucket->next_bucket = END_OF_CHAIN; - hshow(t); return; } - hdebug("Collision detected in bucket ", hash, " (entry ", (uint32_t)bucket->index, ")\n"); - while (buckets[t->bucket_info->last_free].occupied) { assert(t->bucket_info->last_free > 0); --t->bucket_info->last_free; @@ -155,7 +125,6 @@ static void Table$set_bucket(Table_t *t, const void *entry, int32_t index, const uint64_t collided_hash = HASH_KEY(*t, GET_ENTRY(*t, bucket->index)); if (collided_hash != hash) { // Collided with a mid-chain entry - hdebug("Hit a mid-chain entry at bucket ", hash, " (chain starting at ", collided_hash, ")\n"); // Find chain predecessor uint64_t predecessor = collided_hash; while (buckets[predecessor].next_bucket != hash) @@ -168,20 +137,18 @@ static void Table$set_bucket(Table_t *t, const void *entry, int32_t index, const bucket->occupied = 1; bucket->index = index; bucket->next_bucket = END_OF_CHAIN; - } else { // Collided with the start of a chain, put the new entry in chain position #2 - hdebug("Hit start of a chain\n"); + } else { // Collided with the start of a chain, put the new entry in chain + // position #2 buckets[t->bucket_info->last_free] = (bucket_t){.occupied = 1, .index = index, .next_bucket = bucket->next_bucket}; bucket->next_bucket = t->bucket_info->last_free; } - hshow(t); } static void hashmap_resize_buckets(Table_t *t, uint32_t new_capacity, const TypeInfo_t *type) { if (unlikely(new_capacity > TABLE_MAX_BUCKETS)) - fail("Table has exceeded the maximum table size (2^31) and cannot grow further!"); - hdebug("About to resize from ", t->bucket_info ? (int32_t)t->bucket_info->count : 0, " to ", new_capacity, "\n"); - hshow(t); + fail("Table has exceeded the maximum table size (2^31) and cannot grow " + "further!"); size_t alloc_size = sizeof(bucket_info_t) + sizeof(bucket_t[new_capacity]); t->bucket_info = GC_MALLOC_ATOMIC(alloc_size); memset(t->bucket_info->buckets, 0, sizeof(bucket_t[new_capacity])); @@ -189,12 +156,8 @@ static void hashmap_resize_buckets(Table_t *t, uint32_t new_capacity, const Type t->bucket_info->last_free = new_capacity - 1; // Rehash: for (int64_t i = 0; i < (int64_t)Table$length(*t); i++) { - hdebug("Rehashing ", i, "\n"); Table$set_bucket(t, GET_ENTRY(*t, i), i, type); } - - hshow(t); - hdebug("Finished resizing\n"); } // Return address of value @@ -206,7 +169,6 @@ public void *Table$reserve(Table_t *t, const void *key, const void *value, const TypeInfo_t *type) { assert(type->tag == TableInfo); if (!t || !key) return NULL; - hshow(t); t->hash = 0; @@ -295,12 +257,10 @@ void Table$remove(Table_t *t, const void *key, const TypeInfo_t *type) { // maybe update lastfree_index1 to removed bucket's index uint64_t hash = HASH_KEY(*t, key); - hdebug("Removing key with hash ", hash, "\n"); bucket_t *bucket, *prev = NULL; for (uint64_t i = hash; t->bucket_info->buckets[i].occupied; i = t->bucket_info->buckets[i].next_bucket) { if (EQUAL_KEYS(GET_ENTRY(*t, t->bucket_info->buckets[i].index), key)) { bucket = &t->bucket_info->buckets[i]; - hdebug("Found key to delete in bucket ", i, "\n"); goto found_it; } if (t->bucket_info->buckets[i].next_bucket == END_OF_CHAIN) return; @@ -319,8 +279,6 @@ found_it:; // instead of O(N) int64_t last_entry = (int64_t)t->entries.length - 1; if (bucket->index != last_entry) { - hdebug("Removing key/value from the middle of the entries list\n"); - // Find the bucket that points to the last entry's index: uint64_t i = HASH_KEY(*t, GET_ENTRY(*t, last_entry)); while (t->bucket_info->buckets[i].index != last_entry) @@ -341,22 +299,17 @@ found_it:; int64_t bucket_to_clear; if (prev) { // Middle (or end) of a chain - hdebug("Removing from middle of a chain\n"); bucket_to_clear = (bucket - t->bucket_info->buckets); prev->next_bucket = bucket->next_bucket; } else if (bucket->next_bucket != END_OF_CHAIN) { // Start of a chain - hdebug("Removing from start of a chain\n"); bucket_to_clear = bucket->next_bucket; *bucket = t->bucket_info->buckets[bucket_to_clear]; } else { // Empty chain - hdebug("Removing from empty chain\n"); bucket_to_clear = (bucket - t->bucket_info->buckets); } t->bucket_info->buckets[bucket_to_clear] = (bucket_t){0}; if (bucket_to_clear > t->bucket_info->last_free) t->bucket_info->last_free = bucket_to_clear; - - hshow(t); } CONSTFUNC public void *Table$entry(Table_t t, int64_t n) { @@ -365,7 +318,9 @@ CONSTFUNC public void *Table$entry(Table_t t, int64_t n) { } public -void Table$clear(Table_t *t) { memset(t, 0, sizeof(Table_t)); } +void Table$clear(Table_t *t) { + *t = EMPTY_TABLE; +} public Table_t Table$sorted(Table_t t, const TypeInfo_t *type) { @@ -537,9 +492,9 @@ Text_t Table$as_text(const void *obj, bool colorize, const TypeInfo_t *type) { __typeof(type->TableInfo) table = type->TableInfo; if (!t) { - return table.value->size > 0 ? Texts("{", generic_as_text(NULL, false, table.key), ":", - generic_as_text(NULL, false, table.value), "}") - : Texts("{", generic_as_text(NULL, false, table.key), "}"); + return table.value->size > 0 ? Text$concat(Text("{"), generic_as_text(NULL, false, table.key), Text(":"), + generic_as_text(NULL, false, table.value), Text("}")) + : Text$concat(Text("{"), generic_as_text(NULL, false, table.key), Text("}")); } int64_t val_off = (int64_t)value_offset(type); @@ -584,7 +539,8 @@ Table_t Table$from_entries(List_t entries, const TypeInfo_t *type) { // "set intersection" in formal terms public Table_t Table$intersection(Table_t a, Table_t b, const TypeInfo_t *type) { - // Return a table such that t[k]==a[k] for all k such that a.has(k), b.has(k), and a[k]==b[k] + // Return a table such that t[k]==a[k] for all k such that a.has(k), b.has(k), + // and a[k]==b[k] Table_t result = EMPTY_TABLE; const size_t offset = value_offset(type); for (Table_t *t = &a; t; t = t->fallback) { @@ -602,8 +558,8 @@ Table_t Table$intersection(Table_t a, Table_t b, const TypeInfo_t *type) { // "set union" in formal terms public Table_t Table$with(Table_t a, Table_t b, const TypeInfo_t *type) { - // return a table such that t[k]==b[k] for all k such that b.has(k), and t[k]==a[k] for all k such that a.has(k) and - // not b.has(k) + // return a table such that t[k]==b[k] for all k such that b.has(k), and + // t[k]==a[k] for all k such that a.has(k) and not b.has(k) Table_t result = EMPTY_TABLE; const size_t offset = value_offset(type); for (Table_t *t = &a; t; t = t->fallback) { @@ -645,7 +601,8 @@ Table_t Table$difference(Table_t a, Table_t b, const TypeInfo_t *type) { // "without" is "set difference" in formal terms public Table_t Table$without(Table_t a, Table_t b, const TypeInfo_t *type) { - // Return a table such that t[k]==a[k] for all k such that not b.has(k) or b[k] != a[k] + // Return a table such that t[k]==a[k] for all k such that not b.has(k) or + // b[k] != a[k] Table_t result = EMPTY_TABLE; const size_t offset = value_offset(type); for (Table_t *t = &a; t; t = t->fallback) { @@ -704,12 +661,18 @@ void *Table$str_reserve(Table_t *t, const char *key, const void *value) { } public -void Table$str_set(Table_t *t, const char *key, const void *value) { Table$set(t, &key, &value, &CStrToVoidStarTable); } +void Table$str_set(Table_t *t, const char *key, const void *value) { + Table$set(t, &key, &value, &CStrToVoidStarTable); +} public -void Table$str_remove(Table_t *t, const char *key) { return Table$remove(t, &key, &CStrToVoidStarTable); } +void Table$str_remove(Table_t *t, const char *key) { + return Table$remove(t, &key, &CStrToVoidStarTable); +} -CONSTFUNC public void *Table$str_entry(Table_t t, int64_t n) { return Table$entry(t, n); } +CONSTFUNC public void *Table$str_entry(Table_t t, int64_t n) { + return Table$entry(t, n); +} PUREFUNC public bool Table$is_none(const void *obj, const TypeInfo_t *info) { (void)info; diff --git a/src/stdlib/tables.h b/src/stdlib/tables.h index cf1c3625..f3f9d2c7 100644 --- a/src/stdlib/tables.h +++ b/src/stdlib/tables.h @@ -4,7 +4,7 @@ #include <stdbool.h> #include <stdint.h> -#include <string.h> +#include <string.h> // IWYU pragma: export #include "datatypes.h" #include "lists.h" @@ -47,8 +47,8 @@ void *Table$get(Table_t t, const void *key, const TypeInfo_t *type); val_t *value = Table$get(t, &key, info); \ if (unlikely(value == NULL)) \ fail_source(__SOURCE_FILE__, start, end, \ - "This key was not found in the table: ", generic_as_text(&key, false, info->TableInfo.key), \ - "\n"); \ + Text$concat(Text("This key was not found in the table: "), \ + generic_as_text(&key, false, info->TableInfo.key), Text("\n"))); \ *value; \ }) #define Table$get_or_setdefault(table_expr, key_t, val_t, key_expr, default_expr, info_expr) \ diff --git a/src/stdlib/text.c b/src/stdlib/text.c index b4b27fed..4bf6d999 100644 --- a/src/stdlib/text.c +++ b/src/stdlib/text.c @@ -606,8 +606,8 @@ Text_t Text$middle_pad(Text_t text, Int_t width, Text_t padding, Text_t language if (padding.length == 0) fail("Cannot pad with an empty text!"); int64_t needed = Int64$from_int(width, false) - Int64$from_int(Text$width(text, language), false); - return Texts(Text$repeat_to_width(padding, needed / 2, language), text, - Text$repeat_to_width(padding, (needed + 1) / 2, language)); + return Text$concat(Text$repeat_to_width(padding, needed / 2, language), text, + Text$repeat_to_width(padding, (needed + 1) / 2, language)); } public @@ -669,10 +669,14 @@ Text_t Text$slice(Text_t text, Int_t first_int, Int_t last_int) { } public -Text_t Text$from(Text_t text, Int_t first) { return Text$slice(text, first, I_small(-1)); } +Text_t Text$from(Text_t text, Int_t first) { + return Text$slice(text, first, I_small(-1)); +} public -Text_t Text$to(Text_t text, Int_t last) { return Text$slice(text, I_small(1), last); } +Text_t Text$to(Text_t text, Int_t last) { + return Text$slice(text, I_small(1), last); +} public Text_t Text$reversed(Text_t text) { @@ -814,7 +818,9 @@ OptionalText_t Text$from_strn(const char *str, size_t len) { } public -OptionalText_t Text$from_str(const char *str) { return str ? Text$from_strn(str, strlen(str)) : Text(""); } +OptionalText_t Text$from_str(const char *str) { + return str ? Text$from_strn(str, strlen(str)) : Text(""); +} static void u8_buf_append(Text_t text, Byte_t **buf, int64_t *capacity, int64_t *i) { switch (text.tag) { @@ -1506,8 +1512,8 @@ Text_t Text$quoted(Text_t text, bool colorize, Text_t quotation_mark) { Text_t ret = Text$escaped(text, colorize, quotation_mark); if (!(Text$equal_values(quotation_mark, Text("\"")) || Text$equal_values(quotation_mark, Text("'")) || Text$equal_values(quotation_mark, Text("`")))) - ret = Texts("$", quotation_mark, ret, quotation_mark); - else ret = Texts(quotation_mark, ret, quotation_mark); + ret = Text$concat(Text("$"), quotation_mark, ret, quotation_mark); + else ret = Text$concat(quotation_mark, ret, quotation_mark); return ret; } @@ -1803,11 +1809,11 @@ Int_t Text$memory_size(Text_t text) { public Text_t Text$layout(Text_t text) { switch (text.tag) { - case TEXT_ASCII: return Texts(Text("ASCII("), Int64$value_as_text(text.length), Text(")")); - case TEXT_GRAPHEMES: return Texts(Text("Graphemes("), Int64$value_as_text(text.length), Text(")")); - case TEXT_BLOB: return Texts(Text("Blob("), Int64$value_as_text(text.length), Text(")")); + case TEXT_ASCII: return Text$concat(Text("ASCII("), Int64$value_as_text(text.length), Text(")")); + case TEXT_GRAPHEMES: return Text$concat(Text("Graphemes("), Int64$value_as_text(text.length), Text(")")); + case TEXT_BLOB: return Text$concat(Text("Blob("), Int64$value_as_text(text.length), Text(")")); case TEXT_CONCAT: - return Texts(Text("Concat("), Text$layout(*text.left), Text(", "), Text$layout(*text.right), Text(")")); + return Text$concat(Text("Concat("), Text$layout(*text.left), Text(", "), Text$layout(*text.right), Text(")")); default: errx(1, "Invalid text tag: %d", text.tag); } } diff --git a/src/stdlib/text.h b/src/stdlib/text.h index 8c0c43b3..989fcb8b 100644 --- a/src/stdlib/text.h +++ b/src/stdlib/text.h @@ -33,7 +33,9 @@ static inline Text_t Text_from_str_literal(const char *str) { return (Text_t){.length = strlen(str), .tag = TEXT_ASCII, .ascii = str}; } -static inline Text_t Text_from_text(Text_t t) { return t; } +static inline Text_t Text_from_text(Text_t t) { + return t; +} #define convert_to_text(x) \ _Generic(x, \ @@ -45,7 +47,8 @@ static inline Text_t Text_from_text(Text_t t) { return t; } int32_t: Int32$value_as_text, \ int64_t: Int64$value_as_text, \ double: Float64$value_as_text, \ - float: Float32$value_as_text)(x) + float: Float32$value_as_text, \ + Int_t: Int$value_as_text)(x) Text_t Text$_concat(int n, Text_t items[n]); #define Text$concat(...) Text$_concat(sizeof((Text_t[]){__VA_ARGS__}) / sizeof(Text_t), (Text_t[]){__VA_ARGS__}) @@ -63,8 +66,9 @@ OptionalText_t Text$cluster(Text_t text, Int_t index_int); Int_t index = index_expr; \ OptionalText_t cluster = Text$cluster(text, index); \ if (unlikely(cluster.tag == TEXT_NONE)) \ - fail_source(__SOURCE_FILE__, start, end, "Invalid text index: ", index, " (text has length ", \ - (int64_t)text.length, ")\n"); \ + fail_source(__SOURCE_FILE__, start, end, \ + Text$concat(Text("Invalid text index: "), convert_to_text(index), Text(" (text has length "), \ + convert_to_text((int64_t)text.length), Text(")\n"))); \ cluster; \ }) OptionalText_t Text$from_str(const char *str); |
