aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/environment.c17
-rw-r--r--src/stdlib/bytes.c32
-rw-r--r--src/stdlib/c_strings.c5
-rw-r--r--src/stdlib/c_strings.h2
-rw-r--r--src/stdlib/enums.c6
-rw-r--r--src/stdlib/files.c28
-rw-r--r--src/stdlib/fpconv.c341
-rw-r--r--src/stdlib/fpconv.h36
-rw-r--r--src/stdlib/functiontype.c2
-rw-r--r--src/stdlib/integers.c76
-rw-r--r--src/stdlib/integers.h2
-rw-r--r--src/stdlib/memory.c3
-rw-r--r--src/stdlib/metamethods.c2
-rw-r--r--src/stdlib/nums.c56
-rw-r--r--src/stdlib/nums.h10
-rw-r--r--src/stdlib/pointers.c9
-rw-r--r--src/stdlib/powers.h91
-rw-r--r--src/stdlib/print.c123
-rw-r--r--src/stdlib/print.h53
-rw-r--r--src/stdlib/stacktrace.c28
-rw-r--r--src/stdlib/stdlib.c23
-rw-r--r--src/stdlib/structs.c8
-rw-r--r--src/stdlib/tables.c27
-rw-r--r--src/stdlib/text.c55
-rw-r--r--src/stdlib/text.h2
-rw-r--r--src/tomo.c3
26 files changed, 784 insertions, 256 deletions
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 <stdint.h>
#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 <stdbool.h>
+#include <string.h>
+
+#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 <stdio.h>
#include <stdlib.h>
-#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 <stdint.h>
#include <stdlib.h>
+#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 <stdint.h>
+
+#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 <math.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "fpconv.h"
#include "print.h"
#include "util.h"
-#include <stdio.h>
+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 <assert.h>
#include <ctype.h>
@@ -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
@@ -56,12 +50,6 @@ typedef struct {
#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;
#define quoted(s) ((quoted_t){s})
@@ -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 <string.h>
#include <sys/param.h>
-#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, "<ascii length=%ld>%.*s</ascii>", t.length, t.length, t.ascii);
- case TEXT_GRAPHEMES: {
- int printed = fprintf(stream, "<graphemes length=%ld>", t.length);
- printed += Text$print(stream, t);
- printed += fprintf(stream, "</graphemes>");
- return printed;
- }
- case TEXT_CONCAT: {
- int printed = fprintf(stream, "<concat depth=%ld length=%ld>\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, "</concat>");
- 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")));