From baf990e65c62f42e45fe25ac385db9536d3f1788 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 27 Apr 2025 16:49:38 -0400 Subject: Update stdlib to use `print` instead of `printf` in all cases. This means bringing in fpconv to do float-to-string conversion and a few updates to integer and number methods for string formatting. --- src/environment.c | 17 +-- src/stdlib/bytes.c | 32 +++-- src/stdlib/c_strings.c | 5 - src/stdlib/c_strings.h | 2 +- src/stdlib/enums.c | 6 +- src/stdlib/files.c | 28 ++-- src/stdlib/fpconv.c | 341 ++++++++++++++++++++++++++++++++++++++++++++++ src/stdlib/fpconv.h | 36 +++++ src/stdlib/functiontype.c | 2 +- src/stdlib/integers.c | 76 +++++------ src/stdlib/integers.h | 2 - src/stdlib/memory.c | 3 +- src/stdlib/metamethods.c | 2 +- src/stdlib/nums.c | 56 +++++--- src/stdlib/nums.h | 10 +- src/stdlib/pointers.c | 9 +- src/stdlib/powers.h | 91 +++++++++++++ src/stdlib/print.c | 123 +++++++++++++---- src/stdlib/print.h | 53 +++---- src/stdlib/stacktrace.c | 28 ++-- src/stdlib/stdlib.c | 23 ++-- src/stdlib/structs.c | 8 +- src/stdlib/tables.c | 27 ++-- src/stdlib/text.c | 55 +------- src/stdlib/text.h | 2 - src/tomo.c | 3 +- 26 files changed, 784 insertions(+), 256 deletions(-) create mode 100644 src/stdlib/fpconv.c create mode 100644 src/stdlib/fpconv.h create mode 100644 src/stdlib/powers.h (limited to 'src') diff --git a/src/environment.c b/src/environment.c index 8bf77733..290ebe12 100644 --- a/src/environment.c +++ b/src/environment.c @@ -102,7 +102,6 @@ env_t *global_env(bool source_mapping) {"clamped", "Int$clamped", "func(x,low,high:Int -> Int)"}, {"divided_by", "Int$divided_by", "func(x,y:Int -> Int)"}, {"factorial", "Int$factorial", "func(x:Int -> Int)"}, - {"format", "Int$format", "func(i:Int, digits=0 -> Text)"}, {"gcd", "Int$gcd", "func(x,y:Int -> Int)"}, {"hex", "Int$hex", "func(i:Int, digits=0, uppercase=yes, prefix=yes -> Text)"}, {"is_between", "Int$is_between", "func(x:Int,low:Int,high:Int -> Bool)"}, @@ -134,7 +133,6 @@ env_t *global_env(bool source_mapping) {"bits", "Int64$bits", "func(x:Int64 -> [Bool])"}, {"clamped", "Int64$clamped", "func(x,low,high:Int64 -> Int64)"}, {"divided_by", "Int64$divided_by", "func(x,y:Int64 -> Int64)"}, - {"format", "Int64$format", "func(i:Int64, digits=0 -> Text)"}, {"gcd", "Int64$gcd", "func(x,y:Int64 -> Int64)"}, {"parse", "Int64$parse", "func(text:Text -> Int64?)"}, {"hex", "Int64$hex", "func(i:Int64, digits=0, uppercase=yes, prefix=yes -> Text)"}, @@ -156,7 +154,6 @@ env_t *global_env(bool source_mapping) {"bits", "Int32$bits", "func(x:Int32 -> [Bool])"}, {"clamped", "Int32$clamped", "func(x,low,high:Int32 -> Int32)"}, {"divided_by", "Int32$divided_by", "func(x,y:Int32 -> Int32)"}, - {"format", "Int32$format", "func(i:Int32, digits=0 -> Text)"}, {"gcd", "Int32$gcd", "func(x,y:Int32 -> Int32)"}, {"parse", "Int32$parse", "func(text:Text -> Int32?)"}, {"hex", "Int32$hex", "func(i:Int32, digits=0, uppercase=yes, prefix=yes -> Text)"}, @@ -178,7 +175,6 @@ env_t *global_env(bool source_mapping) {"bits", "Int16$bits", "func(x:Int16 -> [Bool])"}, {"clamped", "Int16$clamped", "func(x,low,high:Int16 -> Int16)"}, {"divided_by", "Int16$divided_by", "func(x,y:Int16 -> Int16)"}, - {"format", "Int16$format", "func(i:Int16, digits=0 -> Text)"}, {"gcd", "Int16$gcd", "func(x,y:Int16 -> Int16)"}, {"parse", "Int16$parse", "func(text:Text -> Int16?)"}, {"hex", "Int16$hex", "func(i:Int16, digits=0, uppercase=yes, prefix=yes -> Text)"}, @@ -200,7 +196,6 @@ env_t *global_env(bool source_mapping) {"bits", "Int8$bits", "func(x:Int8 -> [Bool])"}, {"clamped", "Int8$clamped", "func(x,low,high:Int8 -> Int8)"}, {"divided_by", "Int8$divided_by", "func(x,y:Int8 -> Int8)"}, - {"format", "Int8$format", "func(i:Int8, digits=0 -> Text)"}, {"gcd", "Int8$gcd", "func(x,y:Int8 -> Int8)"}, {"parse", "Int8$parse", "func(text:Text -> Int8?)"}, {"hex", "Int8$hex", "func(i:Int8, digits=0, uppercase=yes, prefix=yes -> Text)"}, @@ -224,9 +219,8 @@ env_t *global_env(bool source_mapping) {"Num", Type(NumType, .bits=TYPE_NBITS64), "Num_t", "Num$info", TypedList(ns_entry_t, {"near", "Num$near", "func(x,y:Num, ratio=1e-9, min_epsilon=1e-9 -> Bool)"}, {"clamped", "Num$clamped", "func(x,low,high:Num -> Num)"}, - {"format", "Num$format", "func(n:Num, precision=16 -> Text)"}, - {"scientific", "Num$scientific", "func(n:Num,precision=0 -> Text)"}, - {"percent", "Num$percent", "func(n:Num,precision=0 -> Text)"}, + {"percent", "Num$percent", "func(n:Num,precision=0.01 -> Text)"}, + {"with_precision", "Num$with_precision", "func(n:Num,precision:Num -> Num)"}, {"is_between", "Num$is_between", "func(x:Num,low:Num,high:Num -> Bool)"}, {"isinf", "Num$isinf", "func(n:Num -> Bool)"}, {"isfinite", "Num$isfinite", "func(n:Num -> Bool)"}, @@ -257,9 +251,8 @@ env_t *global_env(bool source_mapping) {"Num32", Type(NumType, .bits=TYPE_NBITS32), "Num32_t", "Num32$info", TypedList(ns_entry_t, {"near", "Num32$near", "func(x,y:Num32, ratio=Num32(1e-9), min_epsilon=Num32(1e-9) -> Bool)"}, {"clamped", "Num32$clamped", "func(x,low,high:Num32 -> Num32)"}, - {"format", "Num32$format", "func(n:Num32, precision=8 -> Text)"}, - {"scientific", "Num32$scientific", "func(n:Num32, precision=0 -> Text)"}, - {"percent", "Num32$percent", "func(n:Num32,precision=0 -> Text)"}, + {"percent", "Num32$percent", "func(n:Num32,precision=Num32(.01) -> Text)"}, + {"with_precision", "Num32$with_precision", "func(n:Num32,precision:Num32 -> Num32)"}, {"is_between", "Num32$is_between", "func(x:Num32,low:Num32,high:Num32 -> Bool)"}, {"isinf", "Num32$isinf", "func(n:Num32 -> Bool)"}, {"isfinite", "Num32$isfinite", "func(n:Num32 -> Bool)"}, @@ -280,7 +273,7 @@ env_t *global_env(bool source_mapping) F2(atan2), F2(copysign), F2(fdim), F2(hypot), F2(nextafter), )}, {"CString", Type(CStringType), "char*", "CString$info", TypedList(ns_entry_t, - {"as_text", "CString$as_text_simple", "func(str:CString -> Text)"}, + {"as_text", "Text$from_str", "func(str:CString -> Text)"}, )}, #undef F2 #undef F_opt diff --git a/src/stdlib/bytes.c b/src/stdlib/bytes.c index fffce06c..5605b2c8 100644 --- a/src/stdlib/bytes.c +++ b/src/stdlib/bytes.c @@ -14,7 +14,15 @@ PUREFUNC public Text_t Byte$as_text(const void *b, bool colorize, const TypeInfo { (void)info; if (!b) return Text("Byte"); - return Text$format(colorize ? "\x1b[35m0x%02X\x1b[m" : "0x%02X", *(Byte_t*)b); + Byte_t byte = *(Byte_t*)b; + char digits[] = {'0', 'x', + (byte / 16) <= 9 ? '0' + (byte / 16) : 'a' + (byte / 16) - 10, + (byte & 15) <= 9 ? '0' + (byte & 15) : 'a' + (byte & 15) - 10, + '\0', + }; + Text_t text = Text$from_str(digits); + if (colorize) text = Texts(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) { @@ -24,14 +32,20 @@ public CONSTFUNC bool Byte$is_between(const Byte_t x, const Byte_t low, const By public Text_t Byte$hex(Byte_t byte, bool uppercase, bool prefix) { struct Text_s text = {.tag=TEXT_ASCII}; text.ascii = GC_MALLOC_ATOMIC(8); - if (prefix && uppercase) - text.length = (int64_t)snprintf((char*)text.ascii, 8, "0x%02X", byte); - else if (prefix && !uppercase) - text.length = (int64_t)snprintf((char*)text.ascii, 8, "0x%02x", byte); - else if (!prefix && uppercase) - text.length = (int64_t)snprintf((char*)text.ascii, 8, "%02X", byte); - else if (!prefix && !uppercase) - text.length = (int64_t)snprintf((char*)text.ascii, 8, "%02x", byte); + char *p = (char*)text.ascii; + if (prefix) { + *(p++) = '0'; + *(p++) = 'x'; + } + + if (uppercase) { + *(p++) = (byte/16) > 9 ? 'A' + (byte/16) - 10 : '0' + (byte/16); + *(p++) = (byte & 15) > 9 ? 'A' + (byte & 15) - 10 : '0' + (byte & 15); + } else { + *(p++) = (byte/16) > 9 ? 'a' + (byte/16) - 10 : '0' + (byte/16); + *(p++) = (byte & 15) > 9 ? 'a' + (byte & 15) - 10 : '0' + (byte & 15); + } + text.length = (int64_t)(p - text.ascii); return text; } diff --git a/src/stdlib/c_strings.c b/src/stdlib/c_strings.c index e74ccb68..c153a8a9 100644 --- a/src/stdlib/c_strings.c +++ b/src/stdlib/c_strings.c @@ -19,11 +19,6 @@ public Text_t CString$as_text(const void *c_string, bool colorize, const TypeInf return Text$concat(colorize ? Text("\x1b[34mCString\x1b[m(") : Text("CString("), Text$quoted(text, colorize, Text("\"")), Text(")")); } -public Text_t CString$as_text_simple(const char *str) -{ - return Text$format("%s", str); -} - PUREFUNC public int32_t CString$compare(const void *x, const void *y, const TypeInfo_t *info) { (void)info; diff --git a/src/stdlib/c_strings.h b/src/stdlib/c_strings.h index 24cf99da..4df5c3ac 100644 --- a/src/stdlib/c_strings.h +++ b/src/stdlib/c_strings.h @@ -6,9 +6,9 @@ #include #include "types.h" +#include "util.h" Text_t CString$as_text(const char **str, bool colorize, const TypeInfo_t *info); -Text_t CString$as_text_simple(const char *str); PUREFUNC int CString$compare(const void *x, const void *y, const TypeInfo_t *type); PUREFUNC bool CString$equal(const void *x, const void *y, const TypeInfo_t *type); PUREFUNC uint64_t CString$hash(const void *str, const TypeInfo_t *type); diff --git a/src/stdlib/enums.c b/src/stdlib/enums.c index 0c5cf196..404e3c50 100644 --- a/src/stdlib/enums.c +++ b/src/stdlib/enums.c @@ -74,8 +74,10 @@ public Text_t Enum$as_text(const void *obj, bool colorize, const TypeInfo_t *typ int32_t tag = *(int32_t*)obj; NamedType_t value = type->EnumInfo.tags[tag-1]; - if (!value.type || value.type->size == 0) - return Text$format(colorize ? "\x1b[1m%s\x1b[m" : "%s", value.name); + 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; + } ptrdiff_t byte_offset = sizeof(int32_t); if (value.type->align && byte_offset % value.type->align > 0) diff --git a/src/stdlib/files.c b/src/stdlib/files.c index fa063697..900129f8 100644 --- a/src/stdlib/files.c +++ b/src/stdlib/files.c @@ -236,25 +236,27 @@ public int highlight_error(file_t *file, const char *start, const char *end, con if (end < file->text || end > file->text + file->len) start = end = file->text; - const char *lineno_fmt, *normal_color, *empty_marker; + const char *lineno_prefix, *lineno_suffix, *normal_color, *empty_marker; bool print_carets = false; int printed = 0; if (use_color) { - lineno_fmt = "\x1b[0;2m%*lu\x1b(0\x78\x1b(B\x1b[m "; + lineno_prefix = "\x1b[0;2m"; + lineno_suffix = "\x1b(0\x78\x1b(B\x1b[m "; normal_color = "\x1b[m"; empty_marker = "\x1b(0\x61\x1b(B"; - printed += fprintf(stderr, "\x1b[33;4;1m%s\x1b[m\n", file->relative_filename); + printed += fprint(stderr, "\x1b[33;4;1m", file->relative_filename, "\x1b[m"); } else { - lineno_fmt = "%*lu| "; + lineno_prefix = ""; + lineno_suffix = "| "; hl_color = ""; normal_color = ""; empty_marker = " "; print_carets = true; - printed += fprintf(stderr, "%s\n", file->relative_filename); + printed += fprint(stderr, file->relative_filename); } if (context_lines == 0) - return fprintf(stderr, "%s%.*s%s", hl_color, (int)(end - start), start, normal_color); + return fprint(stderr, hl_color, string_slice(start, (size_t)(end - start)), normal_color); int64_t start_line = get_line_number(file, start), end_line = get_line_number(file, end); @@ -271,14 +273,18 @@ public int highlight_error(file_t *file, const char *start, const char *end, con for (int64_t line_no = first_line; line_no <= last_line; ++line_no) { if (line_no > first_line + 5 && line_no < last_line - 5) { if (use_color) - printed += fprintf(stderr, "\x1b[0;2;3;4m ... %ld lines omitted ... \x1b[m\n", (last_line - first_line) - 11); + printed += fprint(stderr, "\x1b[0;2;3;4m ... ", (last_line - first_line) - 11, " lines omitted ... \x1b[m"); else - printed += fprintf(stderr, " ... %ld lines omitted ...\n", (last_line - first_line) - 11); + printed += fprint(stderr, " ... ", (last_line - first_line) - 11, " lines omitted ..."); line_no = last_line - 6; continue; } - printed += fprintf(stderr, lineno_fmt, digits, line_no); + int needed_spaces = digits; + for (int64_t n = line_no; n > 0; n /= 10) + needed_spaces -= 1; + + printed += fprint_inline(stderr, lineno_prefix, repeated_char(' ', needed_spaces), line_no, lineno_suffix); const char *line = get_line(file, line_no); if (!line) break; @@ -290,7 +296,7 @@ public int highlight_error(file_t *file, const char *start, const char *end, con // Zero-width matches if (p == start && start == end) { - printed += fprintf(stderr, "%s%s%s", hl_color, empty_marker, normal_color); + printed += fprint_inline(stderr, hl_color, empty_marker, normal_color); column += 1; } @@ -306,7 +312,7 @@ public int highlight_error(file_t *file, const char *start, const char *end, con for (; *p && *p != '\r' && *p != '\n'; ++p) printed += fputc_column(stderr, *p, *p, &column); - printed += fprintf(stderr, "\n"); + printed += fprint_inline(stderr, "\n"); const char *eol = line + strcspn(line, "\r\n"); if (print_carets && start >= line && start < eol && line <= start) { diff --git a/src/stdlib/fpconv.c b/src/stdlib/fpconv.c new file mode 100644 index 00000000..97699784 --- /dev/null +++ b/src/stdlib/fpconv.c @@ -0,0 +1,341 @@ +// This file defines a function to convert floating point numbers to strings. +// For license, see: fpconv_license.txt +#include +#include + +#include "fpconv.h" +#include "powers.h" + +#define fracmask 0x000FFFFFFFFFFFFFU +#define expmask 0x7FF0000000000000U +#define hiddenbit 0x0010000000000000U +#define signmask 0x8000000000000000U +#define expbias (1023 + 52) + +#define absv(n) ((n) < 0 ? -(n) : (n)) +#define minv(a, b) ((a) < (b) ? (a) : (b)) + +static uint64_t tens[] = { + 10000000000000000000U, 1000000000000000000U, 100000000000000000U, + 10000000000000000U, 1000000000000000U, 100000000000000U, + 10000000000000U, 1000000000000U, 100000000000U, + 10000000000U, 1000000000U, 100000000U, + 10000000U, 1000000U, 100000U, + 10000U, 1000U, 100U, + 10U, 1U +}; + +static inline uint64_t get_dbits(double d) +{ + union { + double dbl; + uint64_t i; + } dbl_bits = { d }; + + return dbl_bits.i; +} + +static Fp build_fp(double d) +{ + uint64_t bits = get_dbits(d); + + Fp fp; + fp.frac = bits & fracmask; + fp.exp = (bits & expmask) >> 52; + + if (fp.exp) { + fp.frac += hiddenbit; + fp.exp -= expbias; + + } else { + fp.exp = -expbias + 1; + } + + return fp; +} + +static void normalize(Fp* fp) +{ + while ((fp->frac & hiddenbit) == 0) { + fp->frac <<= 1; + fp->exp--; + } + + int shift = 64 - 52 - 1; + fp->frac <<= shift; + fp->exp -= shift; +} + +static void get_normalized_boundaries(Fp* fp, Fp* lower, Fp* upper) +{ + upper->frac = (fp->frac << 1) + 1; + upper->exp = fp->exp - 1; + + while ((upper->frac & (hiddenbit << 1)) == 0) { + upper->frac <<= 1; + upper->exp--; + } + + int u_shift = 64 - 52 - 2; + + upper->frac <<= u_shift; + upper->exp = upper->exp - u_shift; + + + int l_shift = fp->frac == hiddenbit ? 2 : 1; + + lower->frac = (fp->frac << l_shift) - 1; + lower->exp = fp->exp - l_shift; + + + lower->frac <<= lower->exp - upper->exp; + lower->exp = upper->exp; +} + +static Fp multiply(Fp* a, Fp* b) +{ + const uint64_t lomask = 0x00000000FFFFFFFF; + + uint64_t ah_bl = (a->frac >> 32) * (b->frac & lomask); + uint64_t al_bh = (a->frac & lomask) * (b->frac >> 32); + uint64_t al_bl = (a->frac & lomask) * (b->frac & lomask); + uint64_t ah_bh = (a->frac >> 32) * (b->frac >> 32); + + uint64_t tmp = (ah_bl & lomask) + (al_bh & lomask) + (al_bl >> 32); + /* round up */ + tmp += 1U << 31; + + Fp fp = { + ah_bh + (ah_bl >> 32) + (al_bh >> 32) + (tmp >> 32), + a->exp + b->exp + 64 + }; + + return fp; +} + +static void round_digit(char* digits, int ndigits, uint64_t delta, uint64_t rem, uint64_t kappa, uint64_t frac) +{ + while (rem < frac && delta - rem >= kappa && + (rem + kappa < frac || frac - rem > rem + kappa - frac)) { + + digits[ndigits - 1]--; + rem += kappa; + } +} + +static int generate_digits(Fp* fp, Fp* upper, Fp* lower, char* digits, int* K) +{ + uint64_t wfrac = upper->frac - fp->frac; + uint64_t delta = upper->frac - lower->frac; + + Fp one; + one.frac = 1ULL << -upper->exp; + one.exp = upper->exp; + + uint64_t part1 = upper->frac >> -one.exp; + uint64_t part2 = upper->frac & (one.frac - 1); + + int idx = 0, kappa = 10; + uint64_t* divp; + /* 1000000000 */ + for(divp = tens + 10; kappa > 0; divp++) { + + uint64_t div = *divp; + unsigned digit = part1 / div; + + if (digit || idx) { + digits[idx++] = digit + '0'; + } + + part1 -= digit * div; + kappa--; + + uint64_t tmp = (part1 <<-one.exp) + part2; + if (tmp <= delta) { + *K += kappa; + round_digit(digits, idx, delta, tmp, div << -one.exp, wfrac); + + return idx; + } + } + + /* 10 */ + uint64_t* unit = tens + 18; + + while (true) { + part2 *= 10; + delta *= 10; + kappa--; + + unsigned digit = part2 >> -one.exp; + if (digit || idx) { + digits[idx++] = digit + '0'; + } + + part2 &= one.frac - 1; + if (part2 < delta) { + *K += kappa; + round_digit(digits, idx, delta, part2, one.frac, wfrac * *unit); + + return idx; + } + + unit--; + } +} + +static int grisu2(double d, char* digits, int* K) +{ + Fp w = build_fp(d); + + Fp lower, upper; + get_normalized_boundaries(&w, &lower, &upper); + + normalize(&w); + + int k; + Fp cp = find_cachedpow10(upper.exp, &k); + + w = multiply(&w, &cp); + upper = multiply(&upper, &cp); + lower = multiply(&lower, &cp); + + lower.frac++; + upper.frac--; + + *K = -k; + + return generate_digits(&w, &upper, &lower, digits, K); +} + +static int emit_digits(char* digits, int ndigits, char* dest, int K, bool neg) +{ + int exp = absv(K + ndigits - 1); + + int max_trailing_zeros = 7; + + if (neg) { + max_trailing_zeros -= 1; + } + + /* write plain integer */ + if (K >= 0 && (exp < (ndigits + max_trailing_zeros))) { + + memcpy(dest, digits, (size_t)ndigits); + memset(dest + ndigits, '0', (size_t)K); + + return ndigits + K; + } + + /* write decimal w/o scientific notation */ + if (K < 0 && (K > -7 || exp < 4)) { + int offset = ndigits - absv(K); + /* fp < 1.0 -> write leading zero */ + if (offset <= 0) { + offset = -offset; + dest[0] = '0'; + dest[1] = '.'; + memset(dest + 2, '0', (size_t)offset); + memcpy(dest + offset + 2, digits, (size_t)ndigits); + + return ndigits + 2 + offset; + + /* fp > 1.0 */ + } else { + memcpy(dest, digits, (size_t)offset); + dest[offset] = '.'; + memcpy(dest + offset + 1, digits + offset, (size_t)(ndigits - offset)); + + return ndigits + 1; + } + } + + /* write decimal w/ scientific notation */ + ndigits = minv(ndigits, 18 - neg); + + int idx = 0; + dest[idx++] = digits[0]; + + if (ndigits > 1) { + dest[idx++] = '.'; + memcpy(dest + idx, digits + 1, (size_t)ndigits - 1); + idx += ndigits - 1; + } + + dest[idx++] = 'e'; + + char sign = K + ndigits - 1 < 0 ? '-' : '+'; + dest[idx++] = sign; + + int cent = 0; + + if (exp > 99) { + cent = exp / 100; + dest[idx++] = cent + '0'; + exp -= cent * 100; + } + if (exp > 9) { + int dec = exp / 10; + dest[idx++] = dec + '0'; + exp -= dec * 10; + + } else if (cent) { + dest[idx++] = '0'; + } + + dest[idx++] = exp % 10 + '0'; + + return idx; +} + +static int filter_special(double fp, char* dest) +{ + if (fp == 0.0) { + dest[0] = '0'; + return 1; + } + + uint64_t bits = get_dbits(fp); + + bool nan = (bits & expmask) == expmask; + + if (!nan) { + return 0; + } + + if (bits & fracmask) { + dest[0] = 'n'; dest[1] = 'a'; dest[2] = 'n'; + + } else { + dest[0] = 'i'; dest[1] = 'n'; dest[2] = 'f'; + } + + return 3; +} + +int fpconv_dtoa(double d, char dest[24]) +{ + char digits[18]; + + int str_len = 0; + bool neg = false; + + if (get_dbits(d) & signmask) { + dest[0] = '-'; + str_len++; + neg = true; + } + + int spec = filter_special(d, dest + str_len); + + if (spec) { + return str_len + spec; + } + + int K = 0; + int ndigits = grisu2(d, digits, &K); + + str_len += emit_digits(digits, ndigits, dest + str_len, K, neg); + + return str_len; +} diff --git a/src/stdlib/fpconv.h b/src/stdlib/fpconv.h new file mode 100644 index 00000000..360c1f96 --- /dev/null +++ b/src/stdlib/fpconv.h @@ -0,0 +1,36 @@ +#ifndef FPCONV_H +#define FPCONV_H + +// This file defines a function to convert floating point numbers to strings. +// For license, see: fpconv_license.txt + +/* Fast and accurate double to string conversion based on Florian Loitsch's + * Grisu-algorithm[1]. + * + * Input: + * fp -> the double to convert, dest -> destination buffer. + * The generated string will never be longer than 24 characters. + * Make sure to pass a pointer to at least 24 bytes of memory. + * The emitted string will not be null terminated. + * + * Output: + * The number of written characters. + * + * Exemplary usage: + * + * void print(double d) + * { + * char buf[24 + 1] // plus null terminator + * int str_len = fpconv_dtoa(d, buf); + * + * buf[str_len] = '\0'; + * printf("%s", buf); + * } + * + */ + +int fpconv_dtoa(double fp, char dest[24]); + +#endif + +/* [1] http://florian.loitsch.com/publications/dtoa-pldi2010.pdf */ diff --git a/src/stdlib/functiontype.c b/src/stdlib/functiontype.c index f169057a..3769be2d 100644 --- a/src/stdlib/functiontype.c +++ b/src/stdlib/functiontype.c @@ -82,7 +82,7 @@ public Text_t Func$as_text(const void *fn, bool colorize, const TypeInfo_t *type OptionalText_t filename = get_function_filename(*(void**)fn); int64_t line_num = get_function_line_num(*(void**)fn); if (filename.length >= 0) - text = Texts(text, Text(" ["), filename, Text$format(":%ld]", line_num)); + text = Texts(text, Text(" ["), filename, Text(":"), Int64$as_text(&line_num, false, &Int64$info), Text("]")); } if (fn && colorize) text = Text$concat(Text("\x1b[32;1m"), text, Text("\x1b[m")); diff --git a/src/stdlib/integers.c b/src/stdlib/integers.c index bb39d99c..86cdeb56 100644 --- a/src/stdlib/integers.c +++ b/src/stdlib/integers.c @@ -9,26 +9,44 @@ #include #include -#include "lists.h" #include "datatypes.h" #include "integers.h" +#include "lists.h" #include "optionals.h" +#include "print.h" #include "siphash.h" #include "text.h" #include "types.h" public int Int$print(FILE *f, Int_t i) { if (likely(i.small & 1L)) { - return fprintf(f, "%ld", (i.small)>>2L); + return _print_int(f, (int64_t)((i.small)>>2L)); } else { - char *str = mpz_get_str(NULL, 10, *i.big); - return fputs(str, f); + return gmp_fprintf(f, "%Zd", *i.big); } } +static inline Text_t _int64_to_text(int64_t n) +{ + char buf[21] = {[20]=0}; // Big enough for INT64_MIN + '\0' + char *p = &buf[19]; + bool negative = n < 0; + + if (n == 0) + *(p--) = '0'; + + for (; n > 0; n /= 10) + *(p--) = '0' + (n % 10); + + if (negative) + *(p--) = '-'; + + return Text$from_strn(p + 1, (size_t)(&buf[19] - p)); +} + public Text_t Int$value_as_text(Int_t i) { if (likely(i.small & 1L)) { - return Text$format("%ld", i.small>>2L); + return _int64_to_text(i.small >> 2L); } else { char *str = mpz_get_str(NULL, 10, *i.big); return Text$from_str(str); @@ -96,40 +114,20 @@ public PUREFUNC uint64_t Int$hash(const void *vx, const TypeInfo_t *info) { } } -public Text_t Int$format(Int_t i, Int_t digits_int) { - int64_t digits = Int64$from_int(digits_int, false); - if (likely(i.small & 1L)) { - return Text$format("%0.*ld", digits, (int64_t)((i.small)>>2L)); - } else { - char *str = mpz_get_str(NULL, 10, *i.big); - bool negative = (str[0] == '-'); - int64_t needed_zeroes = digits - (int64_t)strlen(str); - if (needed_zeroes <= 0) - return Text$from_str(str); - - char *zeroes = GC_MALLOC_ATOMIC((size_t)(needed_zeroes)); - memset(zeroes, '0', (size_t)(needed_zeroes)); - if (negative) - return Text$concat(Text("-"), Text$from_str(zeroes), Text$from_str(str + 1)); - else - return Text$concat(Text$from_str(zeroes), Text$from_str(str)); - } -} - public Text_t Int$hex(Int_t i, Int_t digits_int, bool uppercase, bool prefix) { if (Int$is_negative(i)) return Text$concat(Text("-"), Int$hex(Int$negative(i), digits_int, uppercase, prefix)); - int64_t digits = Int64$from_int(digits_int, false); if (likely(i.small & 1L)) { - const char *hex_fmt = uppercase ? (prefix ? "0x%0.*lX" : "%0.*lX") : (prefix ? "0x%0.*lx" : "%0.*lx"); - return Text$format(hex_fmt, digits, (i.small)>>2L); + uint64_t u64 = (uint64_t)(i.small >> 2); + return Text$from_str(String(hex(u64, .no_prefix=!prefix, .digits=Int32$from_int(digits_int, false), .uppercase=uppercase))); } else { char *str = mpz_get_str(NULL, 16, *i.big); if (uppercase) { for (char *c = str; *c; c++) *c = (char)toupper(*c); } + int64_t digits = Int64$from_int(digits_int, false); int64_t needed_zeroes = digits - (int64_t)strlen(str); if (needed_zeroes <= 0) return prefix ? Text$concat(Text("0x"), Text$from_str(str)) : Text$from_str(str); @@ -147,11 +145,11 @@ public Text_t Int$octal(Int_t i, Int_t digits_int, bool prefix) { if (Int$is_negative(i)) return Text$concat(Text("-"), Int$octal(Int$negative(i), digits_int, prefix)); - int64_t digits = Int64$from_int(digits_int, false); if (likely(i.small & 1L)) { - const char *octal_fmt = prefix ? "0o%0.*lo" : "%0.*lo"; - return Text$format(octal_fmt, digits, (i.small)>>2L); + uint64_t u64 = (uint64_t)(i.small >> 2); + return Text$from_str(String(oct(u64, .no_prefix=!prefix, .digits=Int32$from_int(digits_int, false)))); } else { + int64_t digits = Int64$from_int(digits_int, false); char *str = mpz_get_str(NULL, 8, *i.big); int64_t needed_zeroes = digits - (int64_t)strlen(str); if (needed_zeroes <= 0) @@ -581,11 +579,12 @@ public void Int32$deserialize(FILE *in, void *outval, List_t *pointers, const Ty #define __builtin_add_overflow(x, y, result) ({ *(result) = (x) + (y); false; }) #endif -#define DEFINE_INT_TYPE(c_type, KindOfInt, fmt, min_val, max_val, to_attr)\ +#define DEFINE_INT_TYPE(c_type, KindOfInt, min_val, max_val, to_attr)\ public Text_t KindOfInt ## $as_text(const void *i, bool colorize, const TypeInfo_t *info) { \ (void)info; \ if (!i) return Text(#KindOfInt); \ - return Text$format(colorize ? "\x1b[35m" fmt "\x1b[m" : fmt, *(c_type*)i); \ + Text_t text = _int64_to_text((int64_t)(*(c_type*)i)); \ + return colorize ? Texts(Text("\033[35m"), text, Text("\033[m")) : text; \ } \ public PUREFUNC int32_t KindOfInt ## $compare(const void *x, const void *y, const TypeInfo_t *info) { \ (void)info; \ @@ -601,9 +600,6 @@ public void Int32$deserialize(FILE *in, void *outval, List_t *pointers, const Ty public CONSTFUNC c_type KindOfInt ## $clamped(c_type x, c_type min, c_type max) { \ return x < min ? min : (x > max ? max : x); \ } \ - public Text_t KindOfInt ## $format(c_type i, Int_t digits_int) { \ - return Text$format("%0*ld", Int32$from_int(digits_int, false), (int64_t)i); \ - } \ public Text_t KindOfInt ## $hex(c_type i, Int_t digits_int, bool uppercase, bool prefix) { \ Int_t as_int = Int$from_int64((int64_t)i); \ return Int$hex(as_int, digits_int, uppercase, prefix); \ @@ -685,10 +681,10 @@ public void Int32$deserialize(FILE *in, void *outval, List_t *pointers, const Ty }, \ }; -DEFINE_INT_TYPE(int64_t, Int64, "%ld", INT64_MIN, INT64_MAX, __attribute__(())) -DEFINE_INT_TYPE(int32_t, Int32, "%d", INT32_MIN, INT32_MAX, CONSTFUNC) -DEFINE_INT_TYPE(int16_t, Int16, "%d", INT16_MIN, INT16_MAX, CONSTFUNC) -DEFINE_INT_TYPE(int8_t, Int8, "%d", INT8_MIN, INT8_MAX, CONSTFUNC) +DEFINE_INT_TYPE(int64_t, Int64, INT64_MIN, INT64_MAX, __attribute__(())) +DEFINE_INT_TYPE(int32_t, Int32, INT32_MIN, INT32_MAX, CONSTFUNC) +DEFINE_INT_TYPE(int16_t, Int16, INT16_MIN, INT16_MAX, CONSTFUNC) +DEFINE_INT_TYPE(int8_t, Int8, INT8_MIN, INT8_MAX, CONSTFUNC) #undef DEFINE_INT_TYPE // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/integers.h b/src/stdlib/integers.h index 7b7cf12e..4eaac916 100644 --- a/src/stdlib/integers.h +++ b/src/stdlib/integers.h @@ -26,7 +26,6 @@ Text_t type_name ## $as_text(const void *i, bool colorize, const TypeInfo_t *type); \ PUREFUNC int32_t type_name ## $compare(const void *x, const void *y, const TypeInfo_t *type); \ PUREFUNC bool type_name ## $equal(const void *x, const void *y, const TypeInfo_t *type); \ - Text_t type_name ## $format(c_type i, Int_t digits); \ Text_t type_name ## $hex(c_type i, Int_t digits, bool uppercase, bool prefix); \ Text_t type_name ## $octal(c_type i, Int_t digits, bool prefix); \ List_t type_name ## $bits(c_type x); \ @@ -96,7 +95,6 @@ CONSTFUNC bool Int$is_between(const Int_t x, const Int_t low, const Int_t high); CONSTFUNC Int_t Int$clamped(Int_t x, Int_t low, Int_t high); PUREFUNC bool Int$equal(const void *x, const void *y, const TypeInfo_t *type); PUREFUNC bool Int$equal_value(const Int_t x, const Int_t y); -Text_t Int$format(Int_t i, Int_t digits); Text_t Int$hex(Int_t i, Int_t digits, bool uppercase, bool prefix); 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); diff --git a/src/stdlib/memory.c b/src/stdlib/memory.c index ed2ded40..46729f83 100644 --- a/src/stdlib/memory.c +++ b/src/stdlib/memory.c @@ -16,7 +16,8 @@ public Text_t Memory$as_text(const void *p, bool colorize, const TypeInfo_t *info) { (void)info; if (!p) return Text("Memory"); - return Text$format(colorize ? "\x1b[0;34;1mMemory<%p>\x1b[m" : "Memory<%p>", p); + Text_t text = Text$from_str(String("Memory<", *(void**)p, ">")); + return colorize ? Texts(Text("\x1b[0;34;1m"), text, Text("\x1b[m")) : text; } public const TypeInfo_t Memory$info = { diff --git a/src/stdlib/metamethods.c b/src/stdlib/metamethods.c index a7c9424b..ab7bb26e 100644 --- a/src/stdlib/metamethods.c +++ b/src/stdlib/metamethods.c @@ -104,7 +104,7 @@ public void generic_deserialize(List_t bytes, void *outval, const TypeInfo_t *ty public int generic_print(const void *obj, bool colorize, const TypeInfo_t *type) { Text_t text = generic_as_text(obj, colorize, type); - return Text$print(stdout, text) + printf("\n"); + return Text$print(stdout, text) + fputc('\n', stdout); } __attribute__((noreturn)) diff --git a/src/stdlib/nums.c b/src/stdlib/nums.c index c488b551..3213fd2f 100644 --- a/src/stdlib/nums.c +++ b/src/stdlib/nums.c @@ -7,6 +7,7 @@ #include #include +#include "fpconv.h" #include "lists.h" #include "nums.h" #include "string.h" @@ -16,7 +17,11 @@ public PUREFUNC Text_t Num$as_text(const void *f, bool colorize, const TypeInfo_t *info) { (void)info; if (!f) return Text("Num"); - return Text$format(colorize ? "\x1b[35m%.16g\x1b[33;2m\x1b[m" : "%.16g", *(double*)f); + char *str = GC_MALLOC_ATOMIC(24); + int len = fpconv_dtoa(*(double*)f, str); + static const Text_t color_prefix = Text("\x1b[35m"), color_suffix = Text("\x1b[m"); + Text_t text = Text$from_strn(str, (size_t)len); + return colorize ? Texts(color_prefix, text, color_suffix) : text; } public PUREFUNC int32_t Num$compare(const void *x, const void *y, const TypeInfo_t *info) { @@ -52,16 +57,23 @@ public CONSTFUNC bool Num$near(double a, double b, double ratio, double absolute return (diff < epsilon); } -public Text_t Num$format(double f, Int_t precision) { - return Text$format("%.*f", (int)Int64$from_int(precision, false), f); +public Text_t Num$percent(double f, double precision) { + double d = 100. * f; + d = Num$with_precision(d, precision); + return Texts(Num$as_text(&d, false, &Num$info), Text("%")); } -public Text_t Num$scientific(double f, Int_t precision) { - return Text$format("%.*e", (int)Int64$from_int(precision, false), f); -} - -public Text_t Num$percent(double f, Int_t precision) { - return Text$format("%.*f%%", (int)Int64$from_int(precision, false), 100.*f); +public CONSTFUNC double Num$with_precision(double num, double precision) { + if (precision == 0.0) return num; + // Precision will be, e.g. 0.01 or 100. + if (precision < 1.) { + double inv = round(1./precision); // Necessary to make the math work + double k = num * inv; + return round(k) / inv; + } else { + double k = num / precision; + return round(k) * precision; + } } public CONSTFUNC double Num$mod(double num, double modulus) { @@ -120,7 +132,8 @@ public const TypeInfo_t Num$info = { public PUREFUNC Text_t Num32$as_text(const void *f, bool colorize, const TypeInfo_t *info) { (void)info; if (!f) return Text("Num32"); - return Text$format(colorize ? "\x1b[35m%.8g\x1b[33;2m\x1b[m" : "%.8g", (double)*(float*)f); + double d = (double)(*(float*)f); + return Num$as_text(&d, colorize, &Num$info); } public PUREFUNC int32_t Num32$compare(const void *x, const void *y, const TypeInfo_t *info) { @@ -148,16 +161,23 @@ public CONSTFUNC bool Num32$near(float a, float b, float ratio, float absolute) return (diff < epsilon); } -public Text_t Num32$format(float f, Int_t precision) { - return Text$format("%.*f", (int)Int64$from_int(precision, false), (double)f); -} - -public Text_t Num32$scientific(float f, Int_t precision) { - return Text$format("%.*e", (int)Int64$from_int(precision, false), (double)f); +public Text_t Num32$percent(float f, float precision) { + double d = 100. * (double)f; + d = Num$with_precision(d, (double)precision); + return Texts(Num$as_text(&d, false, &Num$info), Text("%")); } -public Text_t Num32$percent(float f, Int_t precision) { - return Text$format("%.*f%%", (int)Int64$from_int(precision, false), 100.*(double)f); +public CONSTFUNC float Num32$with_precision(float num, float precision) { + if (precision == 0.0f) return num; + // Precision will be, e.g. 0.01 or 100. + if (precision < 1.f) { + float inv = roundf(1.f/precision); // Necessary to make the math work + float k = num * inv; + return roundf(k) / inv; + } else { + float k = num / precision; + return roundf(k) * precision; + } } public CONSTFUNC float Num32$mod(float num, float modulus) { diff --git a/src/stdlib/nums.h b/src/stdlib/nums.h index 5871c904..fdd9e227 100644 --- a/src/stdlib/nums.h +++ b/src/stdlib/nums.h @@ -21,9 +21,8 @@ Text_t Num$as_text(const void *f, bool colorize, const TypeInfo_t *type); PUREFUNC int32_t Num$compare(const void *x, const void *y, const TypeInfo_t *type); PUREFUNC bool Num$equal(const void *x, const void *y, const TypeInfo_t *type); CONSTFUNC bool Num$near(double a, double b, double ratio, double absolute); -Text_t Num$format(double f, Int_t precision); -Text_t Num$scientific(double f, Int_t precision); -Text_t Num$percent(double f, Int_t precision); +Text_t Num$percent(double f, double precision); +double CONSTFUNC Num$with_precision(double num, double precision); double Num$mod(double num, double modulus); double Num$mod1(double num, double modulus); CONSTFUNC bool Num$isinf(double n); @@ -76,9 +75,8 @@ Text_t Num32$as_text(const void *f, bool colorize, const TypeInfo_t *type); PUREFUNC int32_t Num32$compare(const void *x, const void *y, const TypeInfo_t *type); PUREFUNC bool Num32$equal(const void *x, const void *y, const TypeInfo_t *type); CONSTFUNC bool Num32$near(float a, float b, float ratio, float absolute); -Text_t Num32$format(float f, Int_t precision); -Text_t Num32$scientific(float f, Int_t precision); -Text_t Num32$percent(float f, Int_t precision); +Text_t Num32$percent(float f, float precision); +float CONSTFUNC Num32$with_precision(float num, float precision); float Num32$mod(float num, float modulus); float Num32$mod1(float num, float modulus); CONSTFUNC bool Num32$isinf(float n); diff --git a/src/stdlib/pointers.c b/src/stdlib/pointers.c index d766a9b3..6874dd3c 100644 --- a/src/stdlib/pointers.c +++ b/src/stdlib/pointers.c @@ -41,12 +41,15 @@ public Text_t Pointer$as_text(const void *x, bool colorize, const TypeInfo_t *ty if (top_level) { root = ptr; } else if (ptr == root) { - return Text$format(colorize ? "\x1b[34;1m%s~1\x1b[m" : "%s~1", ptr_info.sigil); + Text_t text = Texts(Text$from_str(ptr_info.sigil), Text("~1")); + return colorize ? Texts(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) - return Text$format(colorize ? "\x1b[34;1m%s~%ld\x1b[m" : "%s~%ld", ptr_info.sigil, *id); + if (id) { + Text_t text = Texts(Text$from_str(ptr_info.sigil), Int64$as_text(id, false, &Int64$info)); + return colorize ? Texts(Text("\x1b[34;1m"), text, Text("\x1b[m")) : text; + } int64_t next_id = pending.entries.length + 2; Table$set(&pending, x, &next_id, &rec_table); } diff --git a/src/stdlib/powers.h b/src/stdlib/powers.h new file mode 100644 index 00000000..d1829b60 --- /dev/null +++ b/src/stdlib/powers.h @@ -0,0 +1,91 @@ +// This file defines a function to get power-of-10 information about a floating +// point number, used in fpconv.c +// For license, see: fpconv_license.txt + +#include + +#define npowers 87 +#define steppowers 8 +#define firstpower -348 /* 10 ^ -348 */ + +#define expmax -32 +#define expmin -60 + + +typedef struct Fp { + uint64_t frac; + int exp; +} Fp; + +static const Fp powers_ten[] = { + { 18054884314459144840U, -1220 }, { 13451937075301367670U, -1193 }, + { 10022474136428063862U, -1166 }, { 14934650266808366570U, -1140 }, + { 11127181549972568877U, -1113 }, { 16580792590934885855U, -1087 }, + { 12353653155963782858U, -1060 }, { 18408377700990114895U, -1034 }, + { 13715310171984221708U, -1007 }, { 10218702384817765436U, -980 }, + { 15227053142812498563U, -954 }, { 11345038669416679861U, -927 }, + { 16905424996341287883U, -901 }, { 12595523146049147757U, -874 }, + { 9384396036005875287U, -847 }, { 13983839803942852151U, -821 }, + { 10418772551374772303U, -794 }, { 15525180923007089351U, -768 }, + { 11567161174868858868U, -741 }, { 17236413322193710309U, -715 }, + { 12842128665889583758U, -688 }, { 9568131466127621947U, -661 }, + { 14257626930069360058U, -635 }, { 10622759856335341974U, -608 }, + { 15829145694278690180U, -582 }, { 11793632577567316726U, -555 }, + { 17573882009934360870U, -529 }, { 13093562431584567480U, -502 }, + { 9755464219737475723U, -475 }, { 14536774485912137811U, -449 }, + { 10830740992659433045U, -422 }, { 16139061738043178685U, -396 }, + { 12024538023802026127U, -369 }, { 17917957937422433684U, -343 }, + { 13349918974505688015U, -316 }, { 9946464728195732843U, -289 }, + { 14821387422376473014U, -263 }, { 11042794154864902060U, -236 }, + { 16455045573212060422U, -210 }, { 12259964326927110867U, -183 }, + { 18268770466636286478U, -157 }, { 13611294676837538539U, -130 }, + { 10141204801825835212U, -103 }, { 15111572745182864684U, -77 }, + { 11258999068426240000U, -50 }, { 16777216000000000000U, -24 }, + { 12500000000000000000U, 3 }, { 9313225746154785156U, 30 }, + { 13877787807814456755U, 56 }, { 10339757656912845936U, 83 }, + { 15407439555097886824U, 109 }, { 11479437019748901445U, 136 }, + { 17105694144590052135U, 162 }, { 12744735289059618216U, 189 }, + { 9495567745759798747U, 216 }, { 14149498560666738074U, 242 }, + { 10542197943230523224U, 269 }, { 15709099088952724970U, 295 }, + { 11704190886730495818U, 322 }, { 17440603504673385349U, 348 }, + { 12994262207056124023U, 375 }, { 9681479787123295682U, 402 }, + { 14426529090290212157U, 428 }, { 10748601772107342003U, 455 }, + { 16016664761464807395U, 481 }, { 11933345169920330789U, 508 }, + { 17782069995880619868U, 534 }, { 13248674568444952270U, 561 }, + { 9871031767461413346U, 588 }, { 14708983551653345445U, 614 }, + { 10959046745042015199U, 641 }, { 16330252207878254650U, 667 }, + { 12166986024289022870U, 694 }, { 18130221999122236476U, 720 }, + { 13508068024458167312U, 747 }, { 10064294952495520794U, 774 }, + { 14996968138956309548U, 800 }, { 11173611982879273257U, 827 }, + { 16649979327439178909U, 853 }, { 12405201291620119593U, 880 }, + { 9242595204427927429U, 907 }, { 13772540099066387757U, 933 }, + { 10261342003245940623U, 960 }, { 15290591125556738113U, 986 }, + { 11392378155556871081U, 1013 }, { 16975966327722178521U, 1039 }, + { 12648080533535911531U, 1066 } +}; + +static Fp find_cachedpow10(int exp, int* k) +{ + const double one_log_ten = 0.30102999566398114; + + int approx = -(exp + npowers) * one_log_ten; + int idx = (approx - firstpower) / steppowers; + + while (1) { + int current = exp + powers_ten[idx].exp + 64; + + if (current < expmin) { + idx++; + continue; + } + + if (current > expmax) { + idx--; + continue; + } + + *k = (firstpower + idx * steppowers); + + return powers_ten[idx]; + } +} diff --git a/src/stdlib/print.c b/src/stdlib/print.c index aeb71fae..df10a54f 100644 --- a/src/stdlib/print.c +++ b/src/stdlib/print.c @@ -1,16 +1,103 @@ // This file defines some of the helper functions used for printing values +#include +#include +#include +#include + +#include "fpconv.h" #include "print.h" #include "util.h" -#include +public int _print_int(FILE *f, int64_t n) +{ + char buf[21] = {[20]=0}; // Big enough for INT64_MIN + '\0' + char *p = &buf[19]; + bool negative = n < 0; + + if (n == 0) + *(p--) = '0'; + + for (; n > 0; n /= 10) + *(p--) = '0' + (n % 10); + + if (negative) + *(p--) = '-'; + + return fwrite(p + 1, sizeof(char), (size_t)(&buf[19] - p), f); +} + +public int _print_uint(FILE *f, uint64_t n) +{ + char buf[21] = {[20]=0}; // Big enough for UINT64_MAX + '\0' + char *p = &buf[19]; + + if (n == 0) + *(p--) = '0'; + + for (; n > 0; n /= 10) + *(p--) = '0' + (n % 10); + + return fwrite(p + 1, sizeof(char), (size_t)(&buf[19] - p), f); +} + +public int _print_hex(FILE *f, hex_format_t hex) +{ + int printed = 0; + if (!hex.no_prefix) printed += fputs("0x", f); + if (hex.digits > 0) { + for (uint64_t n = hex.n; n > 0 && hex.digits > 0; n /= 16) { + hex.digits -= 1; + } + for (; hex.digits > 0; hex.digits -= 1) { + printed += fputc('0', f); + } + } + char buf[9] = {[8]='\0'}; // Enough space for FFFFFFFF + '\0' + char *p = &buf[7]; + for (uint64_t n = hex.n; n > 0; n /= 16) { + uint8_t digit = n % 16; + if (digit <= 9) + *(p--) = '0' + digit; + else if (hex.uppercase) + *(p--) = 'A' + digit - 10; + else + *(p--) = 'a' + digit - 10; + } + printed += (int)fwrite(p + 1, sizeof(char), (size_t)(&buf[7] - p), f); + return printed; +} + +public int _print_oct(FILE *f, oct_format_t oct) +{ + int printed = 0; + if (!oct.no_prefix) printed += fputs("0o", f); + if (oct.digits > 0) { + for (uint64_t n = oct.n; n > 0 && oct.digits > 0; n /= 8) { + oct.digits -= 1; + } + for (; oct.digits > 0; oct.digits -= 1) { + printed += fputc('0', f); + } + } + char buf[12] = {[11]='\0'}; // Enough space for octal UINT64_MAX + '\0' + char *p = &buf[10]; + for (uint64_t n = oct.n; n > 0; n /= 8) { + *(p--) = '0' + (n % 8); + } + printed += (int)fwrite(p + 1, sizeof(char), (size_t)(&buf[10] - p), f); + return printed; +} + +public int _print_double(FILE *f, double n) +{ + static char buf[24]; + int len = fpconv_dtoa(n, buf); + return (int)fwrite(buf, sizeof(char), (size_t)len, f); +} public int _print_char(FILE *f, char c) { -#if PRINT_COLOR -#define ESC(e) "\033[35m'\033[34;1m\\" e "\033[0;35m'\033[m" -#else #define ESC(e) "'\\" e "'" -#endif const char *named[256] = {['\'']=ESC("'"), ['\\']=ESC("\\"), ['\n']=ESC("n"), ['\t']=ESC("t"), ['\r']=ESC("r"), ['\033']=ESC("e"), ['\v']=ESC("v"), ['\a']=ESC("a"), ['\b']=ESC("b")}; @@ -18,32 +105,20 @@ public int _print_char(FILE *f, char c) if (name != NULL) return fputs(name, f); else if (isprint(c)) -#if PRINT_COLOR - return fprintf(f, "\033[35m'%c'\033[m"), c); -#else - return fprintf(f, "'%c'", c); -#endif + return fputc('\'', f) + fputc(c, f) + fputc('\'', f); else - return fprintf(f, ESC("x%02X"), (uint8_t)c); + return (fputs("'\\x", f) + _print_hex(f, hex((uint64_t)c, .digits=2, .no_prefix=true, .uppercase=true)) + + fputs("'", f)); #undef ESC } public int _print_quoted(FILE *f, quoted_t quoted) { -#if PRINT_COLOR -#define ESC(e) "\033[34;1m\\" e "\033[0;35m" -#else #define ESC(e) "\\" e -#endif const char *named[256] = {['"']=ESC("\""), ['\\']=ESC("\\"), ['\n']=ESC("n"), ['\t']=ESC("t"), ['\r']=ESC("r"), ['\033']=ESC("e"), ['\v']=ESC("v"), ['\a']=ESC("a"), ['\b']=ESC("b")}; - int printed = -#if PRINT_COLOR - fputs("\033[35m\"", f); -#else - fputc('"', f); -#endif + int printed = fputc('"', f); for (const char *p = quoted.str; *p; p++) { const char *name = named[(uint8_t)*p]; if (name != NULL) { @@ -51,14 +126,10 @@ public int _print_quoted(FILE *f, quoted_t quoted) } else if (isprint(*p) || (uint8_t)*p > 0x7F) { printed += fputc(*p, f); } else { - printed += fprintf(f, ESC("x%02X"), (uint8_t)*p); + printed += fputs("\\x", f) + _print_hex(f, hex((uint64_t)*p, .digits=2, .no_prefix=true, .uppercase=true)); } } -#if PRINT_COLOR - printed += fputs("\"\033[m", f); -#else printed += fputc('"', f); -#endif #undef ESC return printed; } diff --git a/src/stdlib/print.h b/src/stdlib/print.h index 243826e0..454ef6b8 100644 --- a/src/stdlib/print.h +++ b/src/stdlib/print.h @@ -8,8 +8,6 @@ // print_err(...) - print an error message and exit with EXIT_FAILURE // String(...) - return an allocated string // -// If you put `#define PRINT_COLOR 1` before the import, text will be printed -// with terminal colors. #include #include @@ -26,10 +24,6 @@ #include "datatypes.h" #include "mapmacro.h" -#ifndef PRINT_COLOR -#define PRINT_COLOR 0 -#endif - // GCC lets you define macro-like functions which are always inlined and never // compiled using this combination of flags. See: https://gcc.gnu.org/onlinedocs/gcc/Inline.html #ifndef PRINT_FN @@ -55,12 +49,6 @@ typedef struct { } oct_format_t; #define oct(x, ...) ((oct_format_t){.n=x, __VA_ARGS__}) -typedef struct { - double n; - int precision; -} num_format_t; -#define num_fmt(x, ...) ((num_format_t){.n=x, __VA_ARGS__}) - typedef struct { const char *str; } quoted_t; @@ -72,37 +60,36 @@ typedef struct { } string_slice_t; #define string_slice(...) ((string_slice_t){__VA_ARGS__}) +typedef struct { + char c; + int length; +} repeated_char_t; +#define repeated_char(ch, len) ((repeated_char_t){.c=ch, .length=len}) + #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #define FMT64 "ll" #else #define FMT64 "l" #endif -#if PRINT_COLOR -#define hl(s) "\033[35m" s "\033[m" -#else -#define hl(s) s -#endif -PRINT_FN _print_int(FILE *f, int64_t x) { return fprintf(f, hl("%"FMT64"d"), x); } -PRINT_FN _print_uint(FILE *f, uint64_t x) { return fprintf(f, hl("%"FMT64"u"), x); } -PRINT_FN _print_float(FILE *f, float x) { return fprintf(f, hl("%g"), (double)x); } -PRINT_FN _print_double(FILE *f, double x) { return fprintf(f, hl("%g"), x); } -PRINT_FN _print_pointer(FILE *f, void *p) { return fprintf(f, hl("%p"), p); } -PRINT_FN _print_bool(FILE *f, bool b) { return fputs(b ? hl("yes") : hl("no"), f); } +int _print_int(FILE *f, int64_t x); +int _print_uint(FILE *f, uint64_t x); +int _print_double(FILE *f, double x); +int _print_hex(FILE *f, hex_format_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); } 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) { return slice.str ? fwrite(slice.str, 1, slice.length, f) : (size_t)fputs("(null)", f); } -PRINT_FN _print_hex(FILE *f, hex_format_t hex) { - return fprintf(f, hex.no_prefix ? (hex.uppercase ? hl("%0*"FMT64"X") : hl("%0*"FMT64"x")) : (hex.uppercase ? hl("0x%0*"FMT64"X") : hl("%#0*"FMT64"x")), hex.digits, hex.n); -} -PRINT_FN _print_oct(FILE *f, oct_format_t oct) { - return fprintf(f, oct.no_prefix ? hl("%0*"FMT64"o") : hl("%#0*"FMT64"o"), oct.digits, oct.n); -} -PRINT_FN _print_num_format(FILE *f, num_format_t num) { - return fprintf(f, hl("%.*lf"), num.precision, num.n); +PRINT_FN _print_repeated_char(FILE *f, repeated_char_t repeated) { + int len = 0; + for (int n = 0; n < repeated.length; n++) + len += fputc(repeated.c, f); + return len; } -#undef hl extern int Text$print(FILE *stream, Text_t text); extern int Path$print(FILE *stream, Path_t path); @@ -125,9 +112,9 @@ extern int Int$print(FILE *f, Int_t i); double: _print_double, \ hex_format_t: _print_hex, \ oct_format_t: _print_oct, \ - num_format_t: _print_num_format, \ quoted_t: _print_quoted, \ string_slice_t: _print_string_slice, \ + repeated_char_t: _print_repeated_char, \ Text_t: Text$print, \ Path_t: Path$print, \ Int_t: Int$print, \ diff --git a/src/stdlib/stacktrace.c b/src/stdlib/stacktrace.c index 798da218..d460ba58 100644 --- a/src/stdlib/stacktrace.c +++ b/src/stdlib/stacktrace.c @@ -32,10 +32,21 @@ static void fprint_context(FILE *out, const char *filename, int lineno, int cont if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = '\0'; - if (cur_line >= lineno - context_before) - fprintf(out, USE_COLOR ? "%s\033[2m%*d\033(0\x78\x1b(B%s%s\033[m\n" : "%s%*d| %s%s\n", - cur_line == lineno ? (USE_COLOR ? "\033[31;1m>\033[m " : "> ") : " ", - num_width, cur_line, USE_COLOR ? (cur_line == lineno ? "\033[0;31;1m" : "\033[0m") : "", line); + if (cur_line >= lineno - context_before) { + int w = 1; + for (int n = cur_line; n >= 10; n /= 10) w += 1; + + if (USE_COLOR) { + fprint(out, cur_line == lineno ? "\033[31;1m>\033[m " : " ", "\033[2m", + repeated_char(' ', num_width-w), + cur_line, "\033(0\x78\033(B", cur_line == lineno ? "\033[0;31;1m" : "\033[0m", + line, "\033[m"); + } else { + fprint(out, cur_line == lineno ? "> " : " ", + repeated_char(' ', num_width-w), + cur_line, "| ", line); + } + } cur_line += 1; if (cur_line > lineno + context_after) @@ -69,15 +80,14 @@ static void _print_stack_frame(FILE *out, const char *cwd, const char *install_d if (strncmp(filename, cwd, strlen(cwd)) == 0) filename += strlen(cwd); - fprintf(out, USE_COLOR ? "\033[1mIn \033[33m%s()\033[37m" : "In %s()", function_display); + fprint_inline(out, USE_COLOR ? "\033[1mIn \033[33m" : "In ", function_display, USE_COLOR ? "()\033[37m" : "()"); if (filename) { if (install_dir[0] && strncmp(filename, install_dir, strlen(install_dir)) == 0) - fprintf(out, USE_COLOR ? " in library \033[35m%s:%d" : " in library %s:%d", - filename, lineno); + fprint_inline(out, USE_COLOR ? " in library \033[35m" : " in library ", filename, ":", lineno); else - fprintf(out, USE_COLOR ? " in \033[35m%s:%d" : " in %s:%d", filename, lineno); + fprint(out, USE_COLOR ? " in \033[35m" : " in ", filename, ":", lineno); } - fprintf(out, USE_COLOR ? "\033[m\n" : "\n"); + fprint(out, USE_COLOR ? "\033[m" : ""); if (filename) fprint_context(out, filename, lineno, 3, 1); } else { diff --git a/src/stdlib/stdlib.c b/src/stdlib/stdlib.c index 7ee249ab..71257b8e 100644 --- a/src/stdlib/stdlib.c +++ b/src/stdlib/stdlib.c @@ -465,26 +465,33 @@ public void start_inspect(const char *filename, int64_t start, int64_t end) file = load_file(filename); if (file) { - const char *spaces = " "; - int64_t first_line_len = (int64_t)strcspn(file->text + start, "\r\n"); + size_t first_line_len = strcspn(file->text + start, "\r\n"); const char *slash = strrchr(filename, '/'); const char *file_base = slash ? slash + 1 : filename; int64_t line_num = get_line_number(file, file->text + start); - fprintf(stdout, USE_COLOR ? "%.*s\x1b[33;1m>> \x1b[m%.*s %.*s\x1b[32;2m[%s:%ld]\x1b[m\n" : "%.*s>> %.*s %.*s[%s:%ld]\n", - 3*_inspect_depth, spaces, first_line_len, file->text + start, - MAX(0, 35-first_line_len-3*_inspect_depth), spaces, file_base, line_num); + if (USE_COLOR) { + print(repeated_char(' ', 3*_inspect_depth), "\x1b[33;1m>> \x1b[m", + string_slice(file->text + start, first_line_len), + " ", repeated_char(' ', MAX(0, 35-(int64_t)first_line_len-3*_inspect_depth)), + "\x1b[32;2m[", file_base, ":", line_num, "]\x1b[m"); + } else { + print(repeated_char(' ', 3*_inspect_depth), ">> ", + string_slice(file->text + start, first_line_len), + " ", repeated_char(' ', MAX(0, 35-(int64_t)first_line_len-3*_inspect_depth)), + "[", file_base, ":", line_num, "]"); + } // For multi-line expressions, dedent each and print it on a new line with ".. " in front: - if (end > start + first_line_len) { + if (end > start + (int64_t)first_line_len) { const char *line_start = get_line(file, line_num); int64_t indent_len = (int64_t)strspn(line_start, " \t"); for (const char *line = file->text + start + first_line_len; line < file->text + end; line += strcspn(line, "\r\n")) { line += strspn(line, "\r\n"); if ((int64_t)strspn(line, " \t") >= indent_len) line += indent_len; - fprintf(stdout, USE_COLOR ? "%.*s\x1b[33m.. \x1b[m%.*s\n" : "%.*s.. %.*s\n", - 3*_inspect_depth, spaces, strcspn(line, "\r\n"), line); + print(repeated_char(' ', 3*_inspect_depth), USE_COLOR ? "\x1b[33m.. " : ".. ", + string_slice(line, strcspn(line, "\r\n"))); } } } diff --git a/src/stdlib/structs.c b/src/stdlib/structs.c index 9a14779e..d4a22d93 100644 --- a/src/stdlib/structs.c +++ b/src/stdlib/structs.c @@ -143,10 +143,12 @@ PUREFUNC public Text_t Struct$as_text(const void *obj, bool colorize, const Type { if (!obj) return Text$from_str(type->StructInfo.name); - if (type->StructInfo.is_secret || type->StructInfo.is_opaque) - return Text$format(colorize ? "\x1b[0;1m%s\x1b[m(...)" : "%s(...)", type->StructInfo.name); + 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("(...)")); + } - Text_t text = Text$format(colorize ? "\x1b[0;1m%s\x1b[m(" : "%s(", type->StructInfo.name); + Text_t text = colorize ? Texts(Text("\x1b[0;1m"), name, Text("\x1b[m(")) : Texts(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 c26fd798..226f9ebb 100644 --- a/src/stdlib/tables.c +++ b/src/stdlib/tables.c @@ -16,12 +16,13 @@ #include #include -#include "lists.h" #include "c_strings.h" #include "datatypes.h" +#include "lists.h" #include "memory.h" #include "metamethods.h" #include "pointers.h" +#include "print.h" #include "siphash.h" #include "tables.h" #include "text.h" @@ -31,7 +32,7 @@ // #define DEBUG_TABLES #ifdef DEBUG_TABLES -#define hdebug(fmt, ...) printf("\x1b[2m" fmt "\x1b[m" __VA_OPT__(,) __VA_ARGS__) +#define hdebug(fmt, ...) print_inline("\x1b[2m", __VA_ARGS__, "\x1b[m") #else #define hdebug(...) (void)0 #endif @@ -87,9 +88,9 @@ static INLINE void hshow(const Table_t *t) 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("[%d]=%d(%d)", i, t->bucket_info->buckets[i].index, t->bucket_info->buckets[i].next_bucket); + hdebug("[", i, "]=", t->bucket_info->buckets[i].index, "(", t->bucket_info->buckets[i].next_bucket, ")"); else - hdebug("[%d]=_", i); + hdebug("[", i, "]=_"); } hdebug("}\n"); } @@ -114,10 +115,10 @@ PUREFUNC public void *Table$get_raw(Table_t t, const void *key, const TypeInfo_t uint64_t hash = HASH_KEY(t, key); hshow(&t); - hdebug("Getting value with initial probe at %u\n", hash); + 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 %u\n", i); + 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"); @@ -146,7 +147,7 @@ static void Table$set_bucket(Table_t *t, const void *entry, int32_t index, const const void *key = entry; bucket_t *buckets = t->bucket_info->buckets; uint64_t hash = HASH_KEY(*t, key); - hdebug("Hash value (mod %u) = %u\n", t->bucket_info->count, hash); + hdebug("Hash value (mod ", t->bucket_info->count, ") = ", hash, "\n"); bucket_t *bucket = &buckets[hash]; if (!bucket->occupied) { hdebug("Got an empty space\n"); @@ -158,7 +159,7 @@ static void Table$set_bucket(Table_t *t, const void *entry, int32_t index, const return; } - hdebug("Collision detected in bucket %u (entry %u)\n", hash, bucket->index); + hdebug("Collision detected in bucket ", hash, " (entry ", bucket->index, ")\n"); while (buckets[t->bucket_info->last_free].occupied) { assert(t->bucket_info->last_free > 0); @@ -167,7 +168,7 @@ 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 %u (chain starting at %u)\n", hash, collided_hash); + 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) @@ -197,7 +198,7 @@ static void hashmap_resize_buckets(Table_t *t, uint32_t new_capacity, const 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 %u to %u\n", t->bucket_info ? t->bucket_info->count : 0, new_capacity); + hdebug("About to resize from ", t->bucket_info ? t->bucket_info->count : 0, " to ", new_capacity, "\n"); hshow(t); size_t alloc_size = sizeof(bucket_info_t) + sizeof(bucket_t[new_capacity]); t->bucket_info = GC_MALLOC_ATOMIC(alloc_size); @@ -206,7 +207,7 @@ 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 < Table$length(*t); i++) { - hdebug("Rehashing %u\n", i); + hdebug("Rehashing ", i, "\n"); Table$set_bucket(t, GET_ENTRY(*t, i), i, type); } @@ -318,12 +319,12 @@ public 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 %u\n", hash); + 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 %u\n", i); + hdebug("Found key to delete in bucket ", i, "\n"); goto found_it; } if (t->bucket_info->buckets[i].next_bucket == END_OF_CHAIN) diff --git a/src/stdlib/text.c b/src/stdlib/text.c index cc4158e5..2b49cc12 100644 --- a/src/stdlib/text.c +++ b/src/stdlib/text.c @@ -232,35 +232,6 @@ public int32_t get_synthetic_grapheme(const ucs4_t *codepoints, int64_t utf32_le #pragma GCC diagnostic pop #endif -int text_visualize(FILE *stream, Text_t t, int depth) -{ - switch (t.tag) { - case TEXT_ASCII: return fprintf(stream, "%.*s", t.length, t.length, t.ascii); - case TEXT_GRAPHEMES: { - int printed = fprintf(stream, "", t.length); - printed += Text$print(stream, t); - printed += fprintf(stream, ""); - return printed; - } - case TEXT_CONCAT: { - int printed = fprintf(stream, "\n", t.depth, t.length); - for (int i = 0; i < depth+1; i++) - printed += fputc(' ', stream); - printed += text_visualize(stream, *t.left, depth+1); - printed += fputc('\n', stream); - for (int i = 0; i < depth+1; i++) - printed += fputc(' ', stream); - printed += text_visualize(stream, *t.right, depth+1); - printed += fputc('\n', stream); - for (int i = 0; i < depth; i++) - printed += fputc(' ', stream); - printed += fprintf(stream, ""); - return printed; - } - default: return 0; - } -} - public int Text$print(FILE *stream, Text_t t) { if (t.length == 0) return 0; @@ -1389,8 +1360,11 @@ public Text_t Text$quoted(Text_t text, bool colorize, Text_t quotation_mark) case '\x1C' ... '\x1F': case '\x7F' ... '\x7F': { if (colorize) ret = concat2_assuming_safe(ret, Text("\x1b[34;1m")); ret = concat2_assuming_safe(ret, Text("\\x")); - char tmp[3]; - snprintf(tmp, sizeof(tmp), "%02X", g); + char tmp[3] = { + (g / 16) > 9 ? 'a' + (g / 16) - 10 : '0' + (g / 16), + (g & 15) > 9 ? 'a' + (g & 15) - 10 : '0' + (g & 15), + '\0', + }; ret = concat2_assuming_safe(ret, Text$from_strn(tmp, 2)); if (colorize) ret = concat2_assuming_safe(ret, Text("\x1b[0;35m")); @@ -1472,22 +1446,6 @@ public Text_t Text$join(Text_t glue, List_t pieces) return result; } -__attribute__((format(printf, 1, 2))) -public Text_t Text$format(const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - int len = vsnprintf(NULL, 0, fmt, args); - va_end(args); - - char *str = GC_MALLOC_ATOMIC((size_t)(len+1)); - va_start(args, fmt); - vsnprintf(str, (size_t)(len+1), fmt, args); - va_end(args); - Text_t ret = Text$from_strn(str, (size_t)len); - return ret; -} - public List_t Text$clusters(Text_t text) { List_t clusters = {}; @@ -1529,8 +1487,7 @@ static INLINE const char *codepoint_name(ucs4_t c) if (found_name) return found_name; const uc_block_t *block = uc_block(c); assert(block); - snprintf(name, UNINAME_MAX, "%s-%X", block->name, c); - return name; + return String(block->name, "-", hex(c, .no_prefix=true, .uppercase=true)); } public List_t Text$codepoint_names(Text_t text) diff --git a/src/stdlib/text.h b/src/stdlib/text.h index 604cea44..ffdebf68 100644 --- a/src/stdlib/text.h +++ b/src/stdlib/text.h @@ -61,8 +61,6 @@ Closure_t Text$by_split(Text_t text, Text_t delimiter); Closure_t Text$by_split_any(Text_t text, Text_t delimiters); Text_t Text$trim(Text_t text, Text_t to_trim, bool left, bool right); char *Text$as_c_string(Text_t text); -__attribute__((format(printf, 1, 2))) -public Text_t Text$format(const char *fmt, ...); List_t Text$clusters(Text_t text); List_t Text$utf32_codepoints(Text_t text); List_t Text$utf8_bytes(Text_t text); diff --git a/src/tomo.c b/src/tomo.c index 48e554b7..816fd78a 100644 --- a/src/tomo.c +++ b/src/tomo.c @@ -686,7 +686,8 @@ void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_l break; } case USE_MODULE: { - Text_t lib = Text$format("'%s/installed/%s/lib%s%s'", TOMO_HOME, use->path, use->path, SHARED_SUFFIX); + Text_t lib = Texts(Text("'" TOMO_HOME "/installed/"), + Text$from_str(use->path), Text("/lib"), Text$from_str(use->path), Text(SHARED_SUFFIX "'")); Table$set(to_link, &lib, ((Bool_t[1]){1}), Table$info(&Text$info, &Bool$info)); List_t children = Path$glob(Path$from_str(String(TOMO_HOME"/installed/", use->path, "/*.tm"))); -- cgit v1.2.3