aboutsummaryrefslogtreecommitdiff
path: root/src/stdlib
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-12-24 19:04:37 -0500
committerBruce Hill <bruce@bruce-hill.com>2025-12-24 19:06:23 -0500
commitcb336b312e7012dc05fe7d8ac1c0e924dbc6c840 (patch)
treed2b8242e46452d12bfb1f7812f6965e64ddf9dfa /src/stdlib
parent649977aae7e5922f992cd69eb84da0a2db368580 (diff)
Shuffle dependencies around so header files aren't needed after tomo has
been compiled
Diffstat (limited to 'src/stdlib')
-rw-r--r--src/stdlib/bigint.c57
-rw-r--r--src/stdlib/bigint.h14
-rw-r--r--src/stdlib/bytes.c3
-rw-r--r--src/stdlib/c_strings.c4
-rw-r--r--src/stdlib/cli.c2
-rw-r--r--src/stdlib/datatypes.h3
-rw-r--r--src/stdlib/enums.c1
-rw-r--r--src/stdlib/fail.c38
-rw-r--r--src/stdlib/fail.h9
-rw-r--r--src/stdlib/files.c2
-rw-r--r--src/stdlib/int64.c16
-rw-r--r--src/stdlib/int64.h2
-rw-r--r--src/stdlib/intX.c.h84
-rw-r--r--src/stdlib/intX.h56
-rw-r--r--src/stdlib/lists.c1
-rw-r--r--src/stdlib/memory.c2
-rw-r--r--src/stdlib/metamethods.c15
-rw-r--r--src/stdlib/numX.c.h38
-rw-r--r--src/stdlib/numX.h33
-rw-r--r--src/stdlib/paths.c2
-rw-r--r--src/stdlib/paths.h3
-rw-r--r--src/stdlib/print.c251
-rw-r--r--src/stdlib/print.h165
-rw-r--r--src/stdlib/stacktrace.c2
-rw-r--r--src/stdlib/stdlib.c9
-rw-r--r--src/stdlib/stdlib.h44
-rw-r--r--src/stdlib/tables.c3
-rw-r--r--src/stdlib/text.h3
-rw-r--r--src/stdlib/tomo.h3
29 files changed, 255 insertions, 610 deletions
diff --git a/src/stdlib/bigint.c b/src/stdlib/bigint.c
index 84da1468..4a5b2565 100644
--- a/src/stdlib/bigint.c
+++ b/src/stdlib/bigint.c
@@ -10,19 +10,28 @@
#include <stdio.h>
#include <stdlib.h>
+#include "../print.h"
+#include "../util.h"
#include "datatypes.h"
#include "integers.h"
#include "optionals.h"
-#include "print.h"
#include "siphash.h"
#include "text.h"
#include "types.h"
+#define mpz_init_set_int(mpz, i) \
+ do { \
+ if likely ((i).small & 1L) mpz_init_set_si(mpz, (i).small >> 2L); \
+ else mpz_init_set(mpz, (i).big); \
+ } while (0)
+
#define Int$from_mpz(mpz) \
(mpz_cmpabs_ui(mpz, BIGGEST_SMALL_INT) <= 0 \
? ((Int_t){.small = (mpz_get_si(mpz) << 2L) | 1L}) \
: ((Int_t){.big = memcpy(GC_MALLOC(sizeof(__mpz_struct)), mpz, sizeof(__mpz_struct))}))
+#define Int_mpz(i) (__mpz_struct *)((i).big)
+
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
@@ -50,9 +59,9 @@ PUREFUNC Int_t Int$from_int64(int64_t i) {
public
int Int$print(FILE *f, Int_t i) {
if (likely(i.small & 1L)) {
- return _print_int(f, (int64_t)((i.small) >> 2L));
+ return Int64$print(f, (int64_t)((i.small) >> 2L));
} else {
- return gmp_fprintf(f, "%Zd", i.big);
+ return gmp_fprintf(f, "%Zd", Int_mpz(i));
}
}
@@ -79,7 +88,7 @@ Text_t Int$value_as_text(Int_t i) {
if (likely(i.small & 1L)) {
return _int64_to_text(i.small >> 2L);
} else {
- char *str = mpz_get_str(NULL, 10, i.big);
+ char *str = mpz_get_str(NULL, 10, Int_mpz(i));
return Text$from_str(str);
}
}
@@ -101,9 +110,9 @@ static bool Int$is_none(const void *i, const TypeInfo_t *info) {
public
PUREFUNC int32_t Int$compare_value(const Int_t x, const Int_t y) {
if (likely(x.small & y.small & 1L)) return (x.small > y.small) - (x.small < y.small);
- else if (x.small & 1) return -mpz_cmp_si(y.big, x.small);
- else if (y.small & 1) return mpz_cmp_si(x.big, y.small);
- else return x.big == y.big ? 0 : mpz_cmp(x.big, y.big);
+ else if (x.small & 1) return -mpz_cmp_si(Int_mpz(y), x.small);
+ else if (y.small & 1) return mpz_cmp_si(Int_mpz(x), y.small);
+ else return x.big == y.big ? 0 : mpz_cmp(Int_mpz(x), Int_mpz(y));
}
public
@@ -115,7 +124,7 @@ PUREFUNC int32_t Int$compare(const void *x, const void *y, const TypeInfo_t *inf
public
PUREFUNC bool Int$equal_value(const Int_t x, const Int_t y) {
if (likely((x.small | y.small) & 1L)) return x.small == y.small;
- else return x.big == y.big ? 0 : (mpz_cmp(x.big, y.big) == 0);
+ else return x.big == y.big ? 0 : (mpz_cmp(Int_mpz(x), Int_mpz(y)) == 0);
}
public
@@ -141,7 +150,7 @@ PUREFUNC uint64_t Int$hash(const void *vx, const TypeInfo_t *info) {
if (likely(x->small & 1L)) {
return siphash24((void *)x, sizeof(Int_t));
} else {
- char *str = mpz_get_str(NULL, 16, x->big);
+ char *str = mpz_get_str(NULL, 16, Int_mpz(*x));
return siphash24((void *)str, strlen(str));
}
}
@@ -155,7 +164,7 @@ Text_t Int$hex(Int_t i, Int_t digits_int, bool uppercase, bool prefix) {
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);
+ char *str = mpz_get_str(NULL, 16, Int_mpz(i));
if (uppercase) {
for (char *c = str; *c; c++)
*c = (char)toupper(*c);
@@ -180,7 +189,7 @@ Text_t Int$octal(Int_t i, Int_t digits_int, bool prefix) {
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);
+ char *str = mpz_get_str(NULL, 8, Int_mpz(i));
int64_t needed_zeroes = digits - (int64_t)strlen(str);
if (needed_zeroes <= 0) return prefix ? Text$concat(Text("0o"), Text$from_str(str)) : Text$from_str(str);
@@ -199,7 +208,7 @@ Int_t Int$slow_plus(Int_t x, Int_t y) {
if (y.small < 0L) mpz_sub_ui(result, result, (uint64_t)(-(y.small >> 2L)));
else mpz_add_ui(result, result, (uint64_t)(y.small >> 2L));
} else {
- mpz_add(result, result, y.big);
+ mpz_add(result, result, Int_mpz(y));
}
return Int$from_mpz(result);
}
@@ -212,7 +221,7 @@ Int_t Int$slow_minus(Int_t x, Int_t y) {
if (y.small < 0L) mpz_add_ui(result, result, (uint64_t)(-(y.small >> 2L)));
else mpz_sub_ui(result, result, (uint64_t)(y.small >> 2L));
} else {
- mpz_sub(result, result, y.big);
+ mpz_sub(result, result, Int_mpz(y));
}
return Int$from_mpz(result);
}
@@ -222,7 +231,7 @@ Int_t Int$slow_times(Int_t x, Int_t y) {
mpz_t result;
mpz_init_set_int(result, x);
if (y.small & 1L) mpz_mul_si(result, result, y.small >> 2L);
- else mpz_mul(result, result, y.big);
+ else mpz_mul(result, result, Int_mpz(y));
return Int$from_mpz(result);
}
@@ -235,7 +244,7 @@ Int_t Int$slow_divided_by(Int_t dividend, Int_t divisor) {
mpz_init_set_int(remainder, divisor);
mpz_tdiv_qr(quotient, remainder, quotient, remainder);
if (mpz_sgn(remainder) < 0) {
- bool d_positive = likely(divisor.small & 1L) ? divisor.small > 0x1L : mpz_sgn(divisor.big) > 0;
+ bool d_positive = likely(divisor.small & 1L) ? divisor.small > 0x1L : mpz_sgn(Int_mpz(divisor)) > 0;
if (d_positive) mpz_sub_ui(quotient, quotient, 1);
else mpz_add_ui(quotient, quotient, 1);
}
@@ -357,9 +366,9 @@ Int_t Int$gcd(Int_t x, Int_t y) {
mpz_t result;
mpz_init(result);
- if (x.small & 0x1L) mpz_gcd_ui(result, y.big, (uint64_t)labs(x.small >> 2L));
- else if (y.small & 0x1L) mpz_gcd_ui(result, x.big, (uint64_t)labs(y.small >> 2L));
- else mpz_gcd(result, x.big, y.big);
+ if (x.small & 0x1L) mpz_gcd_ui(result, Int_mpz(y), (uint64_t)labs(x.small >> 2L));
+ else if (y.small & 0x1L) mpz_gcd_ui(result, Int_mpz(x), (uint64_t)labs(y.small >> 2L));
+ else mpz_gcd(result, Int_mpz(x), Int_mpz(y));
return Int$from_mpz(result);
}
@@ -535,18 +544,6 @@ Int_t Int$next_prime(Int_t x) {
return Int$from_mpz(p);
}
-#if __GNU_MP_VERSION >= 6
-#if __GNU_MP_VERSION_MINOR >= 3
-public
-OptionalInt_t Int$prev_prime(Int_t x) {
- mpz_t p;
- mpz_init_set_int(p, x);
- if (unlikely(mpz_prevprime(p, p) == 0)) return NONE_INT;
- return Int$from_mpz(p);
-}
-#endif
-#endif
-
public
Int_t Int$choose(Int_t n, Int_t k) {
if unlikely (Int$compare_value(n, I_small(0)) < 0) fail("Negative inputs are not supported for choose()");
diff --git a/src/stdlib/bigint.h b/src/stdlib/bigint.h
index 387d47f5..d5bad85f 100644
--- a/src/stdlib/bigint.h
+++ b/src/stdlib/bigint.h
@@ -1,6 +1,6 @@
// Big integer type (`Int` in Tomo)
+#pragma once
-#include <gmp.h>
#include <stdbool.h>
#include <stdint.h>
@@ -10,6 +10,7 @@
Text_t Int$as_text(const void *i, bool colorize, const TypeInfo_t *type);
Text_t Int$value_as_text(Int_t i);
+int Int$print(FILE *f, Int_t i);
PUREFUNC uint64_t Int$hash(const void *x, const TypeInfo_t *type);
PUREFUNC int32_t Int$compare(const void *x, const void *y, const TypeInfo_t *type);
PUREFUNC int32_t Int$compare_value(const Int_t x, const Int_t y);
@@ -32,12 +33,6 @@ bool Int$get_bit(Int_t x, Int_t bit_index);
#define BIGGEST_SMALL_INT 0x3fffffff
#define SMALLEST_SMALL_INT -0x40000000
-#define mpz_init_set_int(mpz, i) \
- do { \
- if likely ((i).small & 1L) mpz_init_set_si(mpz, (i).small >> 2L); \
- else mpz_init_set(mpz, (i).big); \
- } while (0)
-
#define I_small(i) ((Int_t){.small = (int64_t)((uint64_t)(i) << 2L) | 1L})
#define I(i) _Generic(i, int8_t: I_small(i), int16_t: I_small(i), default: Int$from_int64(i))
#define I_is_zero(i) ((i).small == 1L)
@@ -57,11 +52,6 @@ Int_t Int$slow_negative(Int_t x);
Int_t Int$slow_negated(Int_t x);
bool Int$is_prime(Int_t x, Int_t reps);
Int_t Int$next_prime(Int_t x);
-#if __GNU_MP_VERSION >= 6
-#if __GNU_MP_VERSION_MINOR >= 3
-OptionalInt_t Int$prev_prime(Int_t x);
-#endif
-#endif
Int_t Int$choose(Int_t n, Int_t k);
Int_t Int$factorial(Int_t n);
diff --git a/src/stdlib/bytes.c b/src/stdlib/bytes.c
index a1e953a0..23a7ced8 100644
--- a/src/stdlib/bytes.c
+++ b/src/stdlib/bytes.c
@@ -1,10 +1,11 @@
// The logic for unsigned bytes
+#include <gc.h>
#include <stdbool.h>
#include <stdint.h>
+#include "../util.h"
#include "bytes.h"
#include "integers.h"
-#include "stdlib.h"
#include "text.h"
#include "util.h"
diff --git a/src/stdlib/c_strings.c b/src/stdlib/c_strings.c
index cbe46b68..d581c220 100644
--- a/src/stdlib/c_strings.c
+++ b/src/stdlib/c_strings.c
@@ -7,6 +7,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include "fail.h"
#include "integers.h"
#include "siphash.h"
#include "stdlib.h"
@@ -60,7 +61,8 @@ static void CString$deserialize(FILE *in, void *out, List_t *pointers, const Typ
int64_t len = -1;
Int64$deserialize(in, &len, pointers, &Int64$info);
char *str = GC_MALLOC_ATOMIC((size_t)len + 1);
- if (fread(str, sizeof(char), (size_t)len, in) != (size_t)len) fail("Not enough data in stream to deserialize");
+ if (fread(str, sizeof(char), (size_t)len, in) != (size_t)len)
+ fail_text(Text("Not enough data in stream to deserialize"));
str[len + 1] = '\0';
*(const char **)out = str;
}
diff --git a/src/stdlib/cli.c b/src/stdlib/cli.c
index e30f7ced..d424f316 100644
--- a/src/stdlib/cli.c
+++ b/src/stdlib/cli.c
@@ -11,6 +11,7 @@
#include <time.h>
#include "../config.h"
+#include "../print.h"
#include "bools.h"
#include "bytes.h"
#include "c_strings.h"
@@ -20,7 +21,6 @@
#include "nums.h"
#include "optionals.h"
#include "paths.h"
-#include "print.h"
#include "stdlib.h"
#include "tables.h"
#include "text.h"
diff --git a/src/stdlib/datatypes.h b/src/stdlib/datatypes.h
index 3cd99f38..90a116e6 100644
--- a/src/stdlib/datatypes.h
+++ b/src/stdlib/datatypes.h
@@ -2,7 +2,6 @@
#pragma once
-#include <gmp.h>
#include <stdbool.h>
#include <stdint.h>
@@ -30,7 +29,7 @@
typedef union {
int64_t small;
- __mpz_struct *big;
+ void *big;
} Int_t;
#define OptionalInt_t Int_t
diff --git a/src/stdlib/enums.c b/src/stdlib/enums.c
index 9cc16c5d..a0aa55be 100644
--- a/src/stdlib/enums.c
+++ b/src/stdlib/enums.c
@@ -1,6 +1,7 @@
// Metamethods for enums
#include <stdint.h>
+#include <sys/param.h>
#include "integers.h"
#include "metamethods.h"
diff --git a/src/stdlib/fail.c b/src/stdlib/fail.c
new file mode 100644
index 00000000..704624d3
--- /dev/null
+++ b/src/stdlib/fail.c
@@ -0,0 +1,38 @@
+// Failure functions
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h> // IWYU pragma: export
+
+#include "../util.h"
+#include "fail.h"
+#include "files.h"
+#include "stacktrace.h"
+#include "stdlib.h"
+#include "text.h"
+#include "util.h"
+
+public
+_Noreturn void fail_text(Text_t message) { fail(message); }
+
+public
+Text_t builtin_last_err() { return Text$from_str(strerror(errno)); }
+
+_Noreturn void fail_source(const char *filename, int start, int end, Text_t message) {
+ tomo_cleanup();
+ fflush(stdout);
+ if (USE_COLOR) fputs("\x1b[31;7m ==================== ERROR ==================== \n\n\x1b[0;1m", stderr);
+ else fputs("==================== ERROR ====================\n\n", stderr);
+ print_stacktrace(stderr, 1);
+ fputs("\n", stderr);
+ if (USE_COLOR) fputs("\x1b[31;1m", stderr);
+ Text$print(stderr, message);
+ file_t *_file = (filename) ? load_file(filename) : NULL;
+ if ((filename) && _file) {
+ fputs("\n", stderr);
+ highlight_error(_file, _file->text + (start), _file->text + (end), "\x1b[31;1m", 1, USE_COLOR);
+ }
+ if (USE_COLOR) fputs("\x1b[m", stderr);
+ fflush(stderr);
+ raise(SIGABRT);
+ exit(1);
+}
diff --git a/src/stdlib/fail.h b/src/stdlib/fail.h
new file mode 100644
index 00000000..165b8db8
--- /dev/null
+++ b/src/stdlib/fail.h
@@ -0,0 +1,9 @@
+// Failure functions
+
+#pragma once
+
+#include "datatypes.h"
+
+_Noreturn void fail_text(Text_t message);
+_Noreturn void fail_source(const char *filename, int start, int end, Text_t message);
+Text_t builtin_last_err();
diff --git a/src/stdlib/files.c b/src/stdlib/files.c
index 7d56fcfc..78c1bc94 100644
--- a/src/stdlib/files.c
+++ b/src/stdlib/files.c
@@ -12,8 +12,8 @@
#include <string.h>
#include <sys/param.h>
+#include "../print.h"
#include "files.h"
-#include "print.h"
#include "util.h"
static const int tabstop = 4;
diff --git a/src/stdlib/int64.c b/src/stdlib/int64.c
index 754ac619..131d77ab 100644
--- a/src/stdlib/int64.c
+++ b/src/stdlib/int64.c
@@ -1,3 +1,19 @@
#define INTX_C_H__INT_BITS 64
#include "intX.c.h"
#undef INTX_C_H__INT_BITS
+
+public
+int Int64$print(FILE *f, int64_t n) {
+ char buf[21] = {[20] = 0}; // Big enough for INT64_MIN + '\0'
+ char *p = &buf[19];
+ bool negative = n < 0;
+
+ do {
+ *(p--) = '0' + (n % 10);
+ n /= 10;
+ } while (n > 0);
+
+ if (negative) *(p--) = '-';
+
+ return fwrite(p + 1, sizeof(char), (size_t)(&buf[19] - p), f);
+}
diff --git a/src/stdlib/int64.h b/src/stdlib/int64.h
index dc47fa95..99df60a8 100644
--- a/src/stdlib/int64.h
+++ b/src/stdlib/int64.h
@@ -2,3 +2,5 @@
#define I64(i) (int64_t)(i)
#include "intX.h" // IWYU pragma: export
#define NONE_INT64 ((OptionalInt64_t){.has_value = false})
+
+int Int64$print(FILE *f, int64_t n);
diff --git a/src/stdlib/intX.c.h b/src/stdlib/intX.c.h
index 03322e5b..cba25bae 100644
--- a/src/stdlib/intX.c.h
+++ b/src/stdlib/intX.c.h
@@ -6,12 +6,15 @@
//
#include <gc.h>
+#include <gmp.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include "../util.h"
#include "datatypes.h"
+#include "fail.h"
#include "integers.h"
#include "text.h"
#include "types.h"
@@ -55,12 +58,12 @@ public
void NAMESPACED(serialize)(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) {
(void)info, (void)pointers;
#if INTX_C_H__INT_BITS < 32
- if (fwrite(obj, sizeof(INT_T), 1, out) != sizeof(INT_T)) fail("Failed to write whole integer");
+ if (fwrite(obj, sizeof(INT_T), 1, out) != sizeof(INT_T)) fail_text(Text("Failed to write whole integer"));
#else
INT_T i = *(INT_T *)obj;
UINT_T z = (UINT_T)((i << 1L) ^ (i >> (INTX_C_H__INT_BITS - 1L))); // Zigzag encode
while (z >= 0x80L) {
- if (fputc((uint8_t)(z | 0x80L), out) == EOF) fail("Failed to write full integer");
+ if (fputc((uint8_t)(z | 0x80L), out) == EOF) fail_text(Text("Failed to write full integer"));
z >>= 7L;
}
fputc((uint8_t)z, out);
@@ -71,12 +74,12 @@ public
void NAMESPACED(deserialize)(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *info) {
(void)info, (void)pointers;
#if INTX_C_H__INT_BITS < 32
- if (fread(outval, sizeof(INT_T), 1, in) != sizeof(INT_T)) fail("Failed to read full integer");
+ if (fread(outval, sizeof(INT_T), 1, in) != sizeof(INT_T)) fail_text(Text("Failed to read full integer"));
#else
UINT_T z = 0;
for (size_t shift = 0;; shift += 7) {
int i = fgetc(in);
- if (i == EOF) fail("Failed to read whole integer");
+ if (i == EOF) fail_text(Text("Failed to read whole integer"));
uint8_t byte = (uint8_t)i;
z |= ((UINT_T)(byte & 0x7F)) << shift;
if ((byte & 0x80) == 0) break;
@@ -203,6 +206,79 @@ PUREFUNC OPT_T NAMESPACED(parse)(Text_t text, OptionalInt_t base, Text_t *remain
}
public
+PUREFUNC INT_T NAMESPACED(from_num64)(Num_t n, bool truncate) {
+ INT_T i = (INT_T)n;
+ if (!truncate && unlikely((Num_t)i != n))
+ fail_text(
+ Text$concat(Text("Could not convert Num to an " NAME_STR " without truncation: "), Num$value_as_text(n)));
+ return i;
+}
+
+public
+PUREFUNC INT_T NAMESPACED(from_num32)(Num32_t n, bool truncate) {
+ INT_T i = (INT_T)n;
+ if (!truncate && unlikely((Num32_t)i != n))
+ fail_text(Text$concat(Text("Could not convert Num32 to an " NAME_STR " without truncation: "),
+ Num32$value_as_text(n)));
+ return i;
+}
+
+public
+PUREFUNC INT_T NAMESPACED(from_int)(Int_t i, bool truncate) {
+ if likely (i.small & 1L) {
+ INT_T ret = i.small >> 2L;
+#if INTX_C_H__INT_BITS < 32
+ if (!truncate && unlikely((int64_t)ret != (i.small >> 2L)))
+ fail("Integer is too big to fit in an " NAME_STR ": ", i);
+#endif
+ return ret;
+ }
+ if (!truncate && unlikely(!mpz_fits_slong_p(i.big))) fail("Integer is too big to fit in an " NAME_STR ": ", i);
+ return mpz_get_si(i.big);
+}
+
+#if INTX_C_H__INT_BITS < 64
+public
+PUREFUNC INT_T NAMESPACED(from_int64)(Int64_t i64, bool truncate) {
+ INT_T i = (INT_T)i64;
+ if (!truncate && unlikely((int64_t)i != i64)) fail("Integer is too big to fit in an " NAME_STR ": ", i64);
+ return i;
+}
+#elif INTX_C_H__INT_BITS > 64
+public
+CONSTFUNC INT_T NAMESPACED(from_int64)(Int64_t i) { return (INT_T)i; }
+#endif
+
+#if INTX_C_H__INT_BITS < 32
+public
+PUREFUNC INT_T NAMESPACED(from_int32)(Int32_t i32, bool truncate) {
+ INT_T i = (INT_T)i32;
+ if (!truncate && unlikely((int32_t)i != i32)) fail("Integer is too big to fit in an " NAME_STR ": ", i32);
+ return i;
+}
+#elif INTX_C_H__INT_BITS > 32
+public
+CONSTFUNC INT_T NAMESPACED(from_int32)(Int32_t i) { return (INT_T)i; }
+#endif
+
+#if INTX_C_H__INT_BITS < 16
+public
+PUREFUNC INT_T NAMESPACED(from_int16)(Int16_t i16, bool truncate) {
+ INT_T i = (INT_T)i16;
+ if (!truncate && unlikely((int16_t)i != i16)) fail("Integer is too big to fit in an " NAME_STR ": ", i16);
+ return i;
+}
+#elif INTX_C_H__INT_BITS > 16
+public
+CONSTFUNC INT_T NAMESPACED(from_int16)(Int16_t i) { return (INT_T)i; }
+#endif
+
+#if INTX_C_H__INT_BITS > 8
+public
+CONSTFUNC INT_T NAMESPACED(from_int8)(Int8_t i) { return (INT_T)i; }
+#endif
+
+public
CONSTFUNC INT_T NAMESPACED(gcd)(INT_T x, INT_T y) {
if (x == 0 || y == 0) return 0;
x = NAMESPACED(abs)(x);
diff --git a/src/stdlib/intX.h b/src/stdlib/intX.h
index 1c8b4a05..e9ecbfa1 100644
--- a/src/stdlib/intX.h
+++ b/src/stdlib/intX.h
@@ -6,9 +6,9 @@
//
#include <stdbool.h>
#include <stdint.h>
+#include <stdlib.h>
#include "datatypes.h"
-#include "stdlib.h"
#include "types.h"
#include "util.h"
@@ -88,64 +88,30 @@ MACROLIKE PUREFUNC INTX_T NAMESPACED(unsigned_right_shifted)(INTX_T x, INTX_T y)
void NAMESPACED(serialize)(const void *obj, FILE *out, Table_t *, const TypeInfo_t *);
void NAMESPACED(deserialize)(FILE *in, void *outval, List_t *, const TypeInfo_t *);
-MACROLIKE PUREFUNC INTX_T NAMESPACED(from_num64)(Num_t n, bool truncate) {
- INTX_T i = (INTX_T)n;
- if (!truncate && unlikely((Num_t)i != n)) fail("Could not convert Num to an " NAME_STR " without truncation: ", n);
- return i;
-}
-
-MACROLIKE PUREFUNC INTX_T NAMESPACED(from_num32)(Num32_t n, bool truncate) {
- INTX_T i = (INTX_T)n;
- if (!truncate && unlikely((Num32_t)i != n))
- fail("Could not convert Num32 to an " NAME_STR " without truncation: ", n);
- return i;
-}
-
-MACROLIKE PUREFUNC INTX_T NAMESPACED(from_int)(Int_t i, bool truncate) {
- if likely (i.small & 1L) {
- INTX_T ret = i.small >> 2L;
-#if INTX_H__INT_BITS < 32
- if (!truncate && unlikely((int64_t)ret != (i.small >> 2L)))
- fail("Integer is too big to fit in an " NAME_STR ": ", i);
-#endif
- return ret;
- }
- if (!truncate && unlikely(!mpz_fits_slong_p(i.big))) fail("Integer is too big to fit in an " NAME_STR ": ", i);
- return mpz_get_si(i.big);
-}
+PUREFUNC INTX_T NAMESPACED(from_num64)(Num_t n, bool truncate);
+PUREFUNC INTX_T NAMESPACED(from_num32)(Num32_t n, bool truncate);
+PUREFUNC INTX_T NAMESPACED(from_int)(Int_t i, bool truncate);
#if INTX_H__INT_BITS < 64
-MACROLIKE PUREFUNC INTX_T NAMESPACED(from_int64)(Int64_t i64, bool truncate) {
- INTX_T i = (INTX_T)i64;
- if (!truncate && unlikely((int64_t)i != i64)) fail("Integer is too big to fit in an " NAME_STR ": ", i64);
- return i;
-}
+PUREFUNC INTX_T NAMESPACED(from_int64)(Int64_t i64, bool truncate);
#elif INTX_H__INT_BITS > 64
-MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int64)(Int64_t i) { return (INTX_T)i; }
+CONSTFUNC INTX_T NAMESPACED(from_int64)(Int64_t i);
#endif
#if INTX_H__INT_BITS < 32
-MACROLIKE PUREFUNC INTX_T NAMESPACED(from_int32)(Int32_t i32, bool truncate) {
- INTX_T i = (INTX_T)i32;
- if (!truncate && unlikely((int32_t)i != i32)) fail("Integer is too big to fit in an " NAME_STR ": ", i32);
- return i;
-}
+PUREFUNC INTX_T NAMESPACED(from_int32)(Int32_t i32, bool truncate);
#elif INTX_H__INT_BITS > 32
-MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int32)(Int32_t i) { return (INTX_T)i; }
+CONSTFUNC INTX_T NAMESPACED(from_int32)(Int32_t i);
#endif
#if INTX_H__INT_BITS < 16
-MACROLIKE PUREFUNC INTX_T NAMESPACED(from_int16)(Int16_t i16, bool truncate) {
- INTX_T i = (INTX_T)i16;
- if (!truncate && unlikely((int16_t)i != i16)) fail("Integer is too big to fit in an " NAME_STR ": ", i16);
- return i;
-}
+PUREFUNC INTX_T NAMESPACED(from_int16)(Int16_t i16, bool truncate);
#elif INTX_H__INT_BITS > 16
-MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int16)(Int16_t i) { return (INTX_T)i; }
+CONSTFUNC INTX_T NAMESPACED(from_int16)(Int16_t i);
#endif
#if INTX_H__INT_BITS > 8
-MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int8)(Int8_t i) { return (INTX_T)i; }
+CONSTFUNC INTX_T NAMESPACED(from_int8)(Int8_t i);
#endif
#undef PASTE3_
diff --git a/src/stdlib/lists.c b/src/stdlib/lists.c
index 80c407da..3149cf82 100644
--- a/src/stdlib/lists.c
+++ b/src/stdlib/lists.c
@@ -6,6 +6,7 @@
#include <stdint.h>
#include <sys/param.h>
+#include "../util.h"
#include "integers.h"
#include "lists.h"
#include "math.h"
diff --git a/src/stdlib/memory.c b/src/stdlib/memory.c
index 53a180fb..fa92da3e 100644
--- a/src/stdlib/memory.c
+++ b/src/stdlib/memory.c
@@ -6,9 +6,9 @@
#include <stdint.h>
#include <sys/param.h>
+#include "../print.h"
#include "memory.h"
#include "metamethods.h"
-#include "print.h"
#include "text.h"
#include "types.h"
#include "util.h"
diff --git a/src/stdlib/metamethods.c b/src/stdlib/metamethods.c
index 70b8e4e1..b3fd233a 100644
--- a/src/stdlib/metamethods.c
+++ b/src/stdlib/metamethods.c
@@ -1,12 +1,15 @@
// Metamethods are methods that all types share for hashing, equality, comparison, and textifying
+#include <gc.h>
#include <stdint.h>
#include <string.h>
+#include "fail.h"
#include "lists.h"
#include "metamethods.h"
#include "siphash.h"
#include "tables.h"
+#include "text.h"
#include "types.h"
#include "util.h"
@@ -34,7 +37,7 @@ PUREFUNC public bool generic_equal(const void *x, const void *y, const TypeInfo_
public
Text_t generic_as_text(const void *obj, bool colorize, const TypeInfo_t *type) {
- if (!type->metamethods.as_text) fail("No text metamethod provided for type!");
+ if (!type->metamethods.as_text) fail_text(Text("No text metamethod provided for type!"));
return type->metamethods.as_text(obj, colorize, type);
}
@@ -72,7 +75,7 @@ void _deserialize(FILE *input, void *outval, List_t *pointers, const TypeInfo_t
return;
}
- if (fread(outval, (size_t)type->size, 1, input) != 1) fail("Not enough data in stream to deserialize");
+ if (fread(outval, (size_t)type->size, 1, input) != 1) fail_text(Text("Not enough data in stream to deserialize"));
}
public
@@ -88,13 +91,13 @@ void generic_deserialize(List_t bytes, void *outval, const TypeInfo_t *type) {
__attribute__((noreturn)) public
void cannot_serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) {
(void)obj, (void)out, (void)pointers;
- Text_t typestr = generic_as_text(NULL, false, type);
- fail("Values of type ", typestr, " cannot be serialized or deserialized!");
+ Text_t type_text = generic_as_text(NULL, false, type);
+ fail_text(Text$concat(Text("Values of type "), type_text, Text(" cannot be serialized or deserialized!")));
}
__attribute__((noreturn)) public
void cannot_deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *type) {
(void)obj, (void)in, (void)pointers;
- Text_t typestr = generic_as_text(NULL, false, type);
- fail("Values of type ", typestr, " cannot be serialized or deserialized!");
+ Text_t type_text = generic_as_text(NULL, false, type);
+ fail_text(Text$concat(Text("Values of type "), type_text, Text(" cannot be serialized or deserialized!")));
}
diff --git a/src/stdlib/numX.c.h b/src/stdlib/numX.c.h
index 2fde8c45..a8ab0de8 100644
--- a/src/stdlib/numX.c.h
+++ b/src/stdlib/numX.c.h
@@ -6,11 +6,13 @@
//
#include <float.h>
#include <gc.h>
+#include <gmp.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
+#include "fail.h"
#include "text.h"
#include "types.h"
@@ -89,6 +91,42 @@ PUREFUNC int32_t NAMESPACED(compare)(const void *x, const void *y, const TypeInf
}
#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+public
+CONSTFUNC NUM_T NAMESPACED(from_int)(Int_t i, bool truncate) {
+ if likely (i.small & 0x1) {
+ NUM_T ret = (NUM_T)(i.small >> 2);
+ if unlikely (!truncate && (int64_t)ret != (i.small >> 2))
+ fail_text(Text$concat(Text("Could not convert integer to " TYPE_STR " without losing precision: "),
+ Int64$value_as_text(i.small >> 2)));
+ return ret;
+ } else {
+ NUM_T ret = mpz_get_d(i.big);
+ if (!truncate) {
+ mpz_t roundtrip;
+ mpz_init_set_d(roundtrip, (double)ret);
+ if unlikely (mpz_cmp(i.big, roundtrip) != 0)
+ fail_text(Text$concat(Text("Could not convert integer to " TYPE_STR " without losing precision: "),
+ Int$value_as_text(i)));
+ }
+ return ret;
+ }
+}
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+public
+CONSTFUNC NUM_T NAMESPACED(from_int64)(Int64_t i, bool truncate) {
+ NUM_T n = (NUM_T)i;
+ if unlikely (!truncate && (Int64_t)n != i)
+ fail_text(Text$concat(Text("Could not convert integer to " TYPE_STR " without losing precision: "),
+ Int64$value_as_text(i)));
+ return n;
+}
+
public
PUREFUNC bool NAMESPACED(equal)(const void *x, const void *y, const TypeInfo_t *info) {
(void)info;
diff --git a/src/stdlib/numX.h b/src/stdlib/numX.h
index 3d65cb59..210c4cc7 100644
--- a/src/stdlib/numX.h
+++ b/src/stdlib/numX.h
@@ -9,7 +9,6 @@
#include <stdint.h>
#include "datatypes.h"
-#include "stdlib.h"
#include "types.h"
#include "util.h"
@@ -58,36 +57,8 @@ MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_num32)(float n) { return (NUM_T)n; }
MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_num64)(double n) { return (NUM_T)n; }
#endif
-#ifdef __GNUC__
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_int)(Int_t i, bool truncate) {
- if likely (i.small & 0x1) {
- NUM_T ret = (NUM_T)(i.small >> 2);
- if unlikely (!truncate && (int64_t)ret != (i.small >> 2))
- fail("Could not convert integer to " TYPE_STR " without losing precision: ", i.small >> 2);
- return ret;
- } else {
- NUM_T ret = mpz_get_d(i.big);
- if (!truncate) {
- mpz_t roundtrip;
- mpz_init_set_d(roundtrip, (double)ret);
- if unlikely (mpz_cmp(i.big, roundtrip) != 0)
- fail("Could not convert integer to " TYPE_STR " without losing precision: ", i);
- }
- return ret;
- }
-}
-#ifdef __GNUC__
-#pragma GCC diagnostic pop
-#endif
-MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_int64)(Int64_t i, bool truncate) {
- NUM_T n = (NUM_T)i;
- if unlikely (!truncate && (Int64_t)n != i)
- fail("Could not convert integer to " TYPE_STR " without losing precision: ", i);
- return n;
-}
+CONSTFUNC NUM_T NAMESPACED(from_int)(Int_t i, bool truncate);
+CONSTFUNC NUM_T NAMESPACED(from_int64)(Int64_t i, bool truncate);
MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_int32)(Int32_t i) { return (NUM_T)i; }
MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_int16)(Int16_t i) { return (NUM_T)i; }
MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_int8)(Int8_t i) { return (NUM_T)i; }
diff --git a/src/stdlib/paths.c b/src/stdlib/paths.c
index 0198fda8..678e143a 100644
--- a/src/stdlib/paths.c
+++ b/src/stdlib/paths.c
@@ -17,6 +17,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include "../print.h"
#include "../unistr-fixed.h"
#include "../util.h"
#include "enums.h"
@@ -24,7 +25,6 @@
#include "lists.h"
#include "optionals.h"
#include "paths.h"
-#include "print.h"
#include "structs.h"
#include "text.h"
#include "types.h"
diff --git a/src/stdlib/paths.h b/src/stdlib/paths.h
index c272314c..a9d49a34 100644
--- a/src/stdlib/paths.h
+++ b/src/stdlib/paths.h
@@ -11,8 +11,7 @@
Path_t Path$from_str(const char *str);
Path_t Path$from_text(Text_t text);
-// This function is defined as an extern in `src/stdlib/print.h`
-// int Path$print(FILE *f, Path_t path);
+int Path$print(FILE *f, Path_t path);
// UNSAFE: this works because each type of path has a .components in the same place
#define Path$components(path) ((path).components)
// END UNSAFE
diff --git a/src/stdlib/print.c b/src/stdlib/print.c
deleted file mode 100644
index 7da1343f..00000000
--- a/src/stdlib/print.c
+++ /dev/null
@@ -1,251 +0,0 @@
-// This file defines some of the helper functions used for printing values
-
-#include <ctype.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "fpconv.h"
-#include "print.h"
-#include "util.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;
-
- do {
- *(p--) = '0' + (n % 10);
- n /= 10;
- } while (n > 0);
-
- 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];
-
- do {
- *(p--) = '0' + (n % 10);
- n /= 10;
- } while (n > 0);
-
- 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) {
- hex.digits -= (hex.n == 0); // Don't need a leading zero for a zero
- 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];
- do {
- uint8_t digit = hex.n % 16;
- if (digit <= 9) *(p--) = '0' + digit;
- else if (hex.uppercase) *(p--) = 'A' + digit - 10;
- else *(p--) = 'a' + digit - 10;
- hex.n /= 16;
- } while (hex.n > 0);
- 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) {
- oct.digits -= (oct.n == 0); // Don't need a leading zero for a zero
- 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];
- do {
- *(p--) = '0' + (oct.n % 8);
- oct.n /= 8;
- } while (oct.n > 0);
- 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_hex_double(FILE *f, hex_double_t hex) {
- if (hex.d != hex.d) return fputs("NAN", f);
- else if (hex.d == 1.0 / 0.0) return fputs("INF", f);
- else if (hex.d == -1.0 / 0.0) return fputs("-INF", f);
- else if (hex.d == 0.0) return fputs("0.0", f);
-
- union {
- double d;
- uint64_t u;
- } bits = {.d = hex.d};
-
- int sign = (bits.u >> 63) & 1ull;
- int exp = (int)((bits.u >> 52) & 0x7FF) - 1023ull;
- uint64_t frac = bits.u & 0xFFFFFFFFFFFFFull;
-
- char buf[25];
- char *p = buf;
-
- if (sign) *p++ = '-';
- *p++ = '0';
- *p++ = 'x';
-
- uint64_t mantissa = (1ull << 52) | frac; // implicit 1
- int mantissa_shift = 52;
-
- while ((mantissa & 0xF) == 0 && mantissa_shift > 0) {
- mantissa >>= 4;
- mantissa_shift -= 4;
- }
-
- uint64_t int_part = mantissa >> mantissa_shift;
- *p++ = "0123456789abcdef"[int_part];
-
- *p++ = '.';
-
- while (mantissa_shift > 0) {
- mantissa_shift -= 4;
- uint64_t digit = (mantissa >> mantissa_shift) & 0xF;
- *p++ = "0123456789abcdef"[digit];
- }
-
- *p++ = 'p';
-
- if (exp >= 0) {
- *p++ = '+';
- } else {
- *p++ = '-';
- exp = -exp;
- }
-
- char expbuf[6];
- int ei = 5;
- expbuf[ei--] = '\0';
- do {
- expbuf[ei--] = '0' + (exp % 10);
- exp /= 10;
- } while (exp && ei >= 0);
-
- ei++;
- while (expbuf[ei])
- *p++ = expbuf[ei++];
-
- *p = '\0';
- return fwrite(buf, sizeof(char), (size_t)(p - buf), f);
-}
-
-public
-int _print_char(FILE *f, char c) {
-#define ESC(e) "'\\" e "'"
- 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")};
- const char *name = named[(uint8_t)c];
- if (name != NULL) return fputs(name, f);
- else if (isprint(c)) return fputc('\'', f) + fputc(c, f) + fputc('\'', f);
- else
- 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) {
-#define ESC(e) "\\" e
- 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 = fputc('"', f);
- for (const char *p = quoted.str; *p; p++) {
- const char *name = named[(uint8_t)*p];
- if (name != NULL) {
- printed += fputs(name, f);
- } else if (isprint(*p) || (uint8_t)*p > 0x7F) {
- printed += fputc(*p, f);
- } else {
- printed +=
- fputs("\\x", f) + _print_hex(f, hex((uint64_t)*p, .digits = 2, .no_prefix = true, .uppercase = true));
- }
- }
- printed += fputc('"', f);
-#undef ESC
- return printed;
-}
-
-#if defined(__GLIBC__) && defined(_GNU_SOURCE)
-// GLIBC has fopencookie()
-static ssize_t _gc_stream_write(void *cookie, const char *buf, size_t size) {
- gc_stream_t *stream = (gc_stream_t *)cookie;
- if (stream->position + size + 1 > *stream->size)
- *stream->buffer =
- GC_REALLOC(*stream->buffer, (*stream->size += MAX(MAX(16UL, *stream->size / 2UL), size + 1UL)));
- memcpy(&(*stream->buffer)[stream->position], buf, size);
- stream->position += size;
- (*stream->buffer)[stream->position] = '\0';
- return (ssize_t)size;
-}
-
-public
-FILE *gc_memory_stream(char **buf, size_t *size) {
- gc_stream_t *stream = GC_MALLOC(sizeof(gc_stream_t));
- stream->size = size;
- stream->buffer = buf;
- *stream->size = 16;
- *stream->buffer = GC_MALLOC_ATOMIC(*stream->size);
- (*stream->buffer)[0] = '\0';
- stream->position = 0;
- cookie_io_functions_t functions = {.write = _gc_stream_write};
- return fopencookie(stream, "w", functions);
-}
-#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
-// BSDs have funopen() and fwopen()
-static int _gc_stream_write(void *cookie, const char *buf, int size) {
- gc_stream_t *stream = (gc_stream_t *)cookie;
- if (stream->position + size + 1 > *stream->size)
- *stream->buffer =
- GC_REALLOC(*stream->buffer, (*stream->size += MAX(MAX(16UL, *stream->size / 2UL), size + 1UL)));
- memcpy(&(*stream->buffer)[stream->position], buf, size);
- stream->position += size;
- (*stream->buffer)[stream->position] = '\0';
- return size;
-}
-
-public
-FILE *gc_memory_stream(char **buf, size_t *size) {
- gc_stream_t *stream = GC_MALLOC(sizeof(gc_stream_t));
- stream->size = size;
- stream->buffer = buf;
- *stream->size = 16;
- *stream->buffer = GC_MALLOC_ATOMIC(*stream->size);
- (*stream->buffer)[0] = '\0';
- stream->position = 0;
- return fwopen(stream, _gc_stream_write);
-}
-#else
-#error "This platform doesn't support fopencookie() or funopen()!"
-#endif
diff --git a/src/stdlib/print.h b/src/stdlib/print.h
deleted file mode 100644
index 7106d561..00000000
--- a/src/stdlib/print.h
+++ /dev/null
@@ -1,165 +0,0 @@
-// This file defines some functions to make it easy to do formatted text
-// without using printf style specifiers:
-//
-// print(...) - print text
-// fprint(file, ...) - print text to file
-// print_err(...) - print an error message and exit with EXIT_FAILURE
-// String(...) - return an allocated string
-
-#pragma once
-
-#include <assert.h>
-#include <gc.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/param.h>
-#include <unistd.h>
-
-#include "datatypes.h"
-#include "mapmacro.h"
-
-// 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
-#ifdef __TINYC__
-#define PRINT_FN static inline __attribute__((gnu_inline, always_inline)) int
-#else
-#define PRINT_FN extern inline __attribute__((gnu_inline, always_inline)) int
-#endif
-#endif
-
-typedef struct {
- uint64_t n;
- bool no_prefix;
- bool uppercase;
- int digits;
-} hex_format_t;
-#define hex(x, ...) ((hex_format_t){.n = x, __VA_ARGS__})
-
-typedef struct {
- double d;
-} hex_double_t;
-#define hex_double(x, ...) ((hex_double_t){.d = x, __VA_ARGS__})
-
-typedef struct {
- uint64_t n;
- bool no_prefix;
- int digits;
-} oct_format_t;
-#define oct(x, ...) ((oct_format_t){.n = x, __VA_ARGS__})
-
-typedef struct {
- const char *str;
-} quoted_t;
-#define quoted(s) ((quoted_t){s})
-
-typedef struct {
- const char *str;
- size_t length;
-} 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
-
-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_hex_double(FILE *f, hex_double_t hex);
-int _print_oct(FILE *f, oct_format_t oct);
-PRINT_FN _print_float(FILE *f, float x) { return _print_double(f, (double)x); }
-PRINT_FN _print_pointer(FILE *f, void *p) { return _print_hex(f, hex((uint64_t)p)); }
-PRINT_FN _print_bool(FILE *f, bool b) { return fputs(b ? "yes" : "no", f); }
-PRINT_FN _print_str(FILE *f, const char *s) { return fputs(s ? s : "(null)", f); }
-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_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;
-}
-
-extern int Text$print(FILE *stream, Text_t text);
-extern int Path$print(FILE *stream, Path_t path);
-extern int Int$print(FILE *f, Int_t i);
-#ifndef _fprint1
-#define _fprint1(f, x) \
- _Generic((x), \
- char *: _print_str, \
- const char *: _print_str, \
- char: _print_char, \
- bool: _print_bool, \
- int64_t: _print_int, \
- int32_t: _print_int, \
- int16_t: _print_int, \
- int8_t: _print_int, \
- uint64_t: _print_uint, \
- uint32_t: _print_uint, \
- uint16_t: _print_uint, \
- uint8_t: _print_uint, \
- float: _print_float, \
- double: _print_double, \
- hex_format_t: _print_hex, \
- hex_double_t: _print_hex_double, \
- oct_format_t: _print_oct, \
- 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, \
- void *: _print_pointer)(f, x)
-#endif
-
-typedef struct {
- char **buffer;
- size_t *size;
- size_t position;
-} gc_stream_t;
-
-FILE *gc_memory_stream(char **buf, size_t *size);
-
-#define _print(x) _n += _fprint1(_printing, x)
-#define _fprint(f, ...) \
- ({ \
- FILE *_printing = f; \
- int _n = 0; \
- MAP_LIST(_print, __VA_ARGS__); \
- _n; \
- })
-#define fprint(f, ...) _fprint(f, __VA_ARGS__, "\n")
-#define print(...) fprint(stdout, __VA_ARGS__)
-#define fprint_inline(f, ...) _fprint(f, __VA_ARGS__)
-#define print_inline(...) fprint_inline(stdout, __VA_ARGS__)
-#define String(...) \
- ({ \
- char *_buf = NULL; \
- size_t _size = 0; \
- FILE *_stream = gc_memory_stream(&_buf, &_size); \
- assert(_stream); \
- _fprint(_stream, __VA_ARGS__); \
- fflush(_stream); \
- _buf; \
- })
-#define print_err(...) \
- ({ \
- fprint(stderr, "\033[31;1m", __VA_ARGS__, "\033[m"); \
- exit(EXIT_FAILURE); \
- })
diff --git a/src/stdlib/stacktrace.c b/src/stdlib/stacktrace.c
index 1eba8188..7bc42755 100644
--- a/src/stdlib/stacktrace.c
+++ b/src/stdlib/stacktrace.c
@@ -15,7 +15,7 @@
#include <unistd.h>
#include "../config.h"
-#include "print.h"
+#include "../print.h"
#include "simpleparse.h"
#include "util.h"
diff --git a/src/stdlib/stdlib.c b/src/stdlib/stdlib.c
index 7ed18af1..b156692c 100644
--- a/src/stdlib/stdlib.c
+++ b/src/stdlib/stdlib.c
@@ -14,11 +14,12 @@
#include <time.h>
#include "../config.h"
+#include "../print.h"
+#include "../util.h"
#include "files.h"
#include "metamethods.h"
#include "optionals.h"
#include "paths.h"
-#include "print.h"
#include "siphash.h"
#include "stacktrace.h"
#include "stdlib.h"
@@ -158,12 +159,6 @@ void tomo_init(void) {
atexit(tomo_cleanup);
}
-public
-_Noreturn void fail_text(Text_t message) { fail(message); }
-
-public
-Text_t builtin_last_err() { return Text$from_str(strerror(errno)); }
-
static int _inspect_depth = 0;
static file_t *file = NULL;
diff --git a/src/stdlib/stdlib.h b/src/stdlib/stdlib.h
index 087ed4bf..134f8ffa 100644
--- a/src/stdlib/stdlib.h
+++ b/src/stdlib/stdlib.h
@@ -2,14 +2,10 @@
#pragma once
-#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
-#include <stdio.h> // IWYU pragma: export
#include "datatypes.h"
-#include "print.h"
-#include "stacktrace.h" // IWYU pragma: export
#include "types.h"
extern bool USE_COLOR;
@@ -20,46 +16,6 @@ void tomo_init(void);
void tomo_at_cleanup(Closure_t fn);
void tomo_cleanup(void);
-#define fail(...) \
- ({ \
- tomo_cleanup(); \
- fflush(stdout); \
- if (USE_COLOR) fputs("\x1b[31;7m ==================== ERROR ==================== \033[m\n\n", stderr); \
- else fputs("==================== ERROR ====================\n\n", stderr); \
- print_stacktrace(stderr, 1); \
- if (USE_COLOR) fputs("\n\x1b[31;1m", stderr); \
- else fputs("\n", stderr); \
- fprint_inline(stderr, "Error: ", __VA_ARGS__); \
- if (USE_COLOR) fputs("\x1b[m\n", stderr); \
- else fputs("\n", stderr); \
- fflush(stderr); \
- raise(SIGABRT); \
- exit(1); \
- })
-
-#define fail_source(filename, start, end, message) \
- ({ \
- tomo_cleanup(); \
- fflush(stdout); \
- if (USE_COLOR) fputs("\x1b[31;7m ==================== ERROR ==================== \n\n\x1b[0;1m", stderr); \
- else fputs("==================== ERROR ====================\n\n", stderr); \
- print_stacktrace(stderr, 0); \
- fputs("\n", stderr); \
- if (USE_COLOR) fputs("\x1b[31;1m", stderr); \
- Text$print(stderr, message); \
- file_t *_file = (filename) ? load_file(filename) : NULL; \
- if ((filename) && _file) { \
- fputs("\n", stderr); \
- highlight_error(_file, _file->text + (start), _file->text + (end), "\x1b[31;1m", 1, USE_COLOR); \
- } \
- if (USE_COLOR) fputs("\x1b[m", stderr); \
- fflush(stderr); \
- raise(SIGABRT); \
- exit(1); \
- })
-
-_Noreturn void fail_text(Text_t message);
-Text_t builtin_last_err();
__attribute__((nonnull)) void start_inspect(const char *filename, int64_t start, int64_t end);
void end_inspect(const void *expr, const TypeInfo_t *type);
#define inspect(type, expr, typeinfo, start, end) \
diff --git a/src/stdlib/tables.c b/src/stdlib/tables.c
index d417545d..d4a51276 100644
--- a/src/stdlib/tables.c
+++ b/src/stdlib/tables.c
@@ -13,6 +13,7 @@
#include "c_strings.h"
#include "datatypes.h"
+#include "fail.h"
#include "lists.h"
#include "memory.h"
#include "metamethods.h"
@@ -146,7 +147,7 @@ static void Table$set_bucket(Table_t *t, const void *entry, int32_t index, const
static void hashmap_resize_buckets(Table_t *t, uint32_t new_capacity, const TypeInfo_t *type) {
if (unlikely(new_capacity > TABLE_MAX_BUCKETS))
- fail("Table has exceeded the maximum table size (2^31) and cannot grow further!");
+ fail_text(Text("Table has exceeded the maximum table size (2^31) and cannot grow further!"));
size_t alloc_size = sizeof(bucket_info_t) + sizeof(bucket_t[new_capacity]);
t->bucket_info = GC_MALLOC_ATOMIC(alloc_size);
memset(t->bucket_info->buckets, 0, sizeof(bucket_t[new_capacity]));
diff --git a/src/stdlib/text.h b/src/stdlib/text.h
index 08423cfe..d3ed032b 100644
--- a/src/stdlib/text.h
+++ b/src/stdlib/text.h
@@ -51,8 +51,7 @@ static inline Text_t Text_from_text(Text_t t) { return t; }
Text_t Text$_concat(int n, Text_t items[n]);
#define Text$concat(...) Text$_concat(sizeof((Text_t[]){__VA_ARGS__}) / sizeof(Text_t), (Text_t[]){__VA_ARGS__})
#define Texts(...) Text$concat(MAP_LIST(convert_to_text, __VA_ARGS__))
-// This function is defined as an extern in `src/stdlib/print.h`
-// int Text$print(FILE *stream, Text_t t);
+int Text$print(FILE *stream, Text_t t);
Text_t Text$slice(Text_t text, Int_t first_int, Int_t last_int);
Text_t Text$from(Text_t text, Int_t first);
Text_t Text$to(Text_t text, Int_t last);
diff --git a/src/stdlib/tomo.h b/src/stdlib/tomo.h
index ff8f90a2..fce9cdb5 100644
--- a/src/stdlib/tomo.h
+++ b/src/stdlib/tomo.h
@@ -14,6 +14,7 @@
#include "cli.h" // IWYU pragma: export
#include "datatypes.h" // IWYU pragma: export
#include "enums.h" // IWYU pragma: export
+#include "fail.h" // IWYU pragma: export
#include "files.h" // IWYU pragma: export
#include "functiontype.h" // IWYU pragma: export
#include "integers.h" // IWYU pragma: export
@@ -24,10 +25,10 @@
#include "optionals.h" // IWYU pragma: export
#include "paths.h" // IWYU pragma: export
#include "pointers.h" // IWYU pragma: export
-#include "print.h" // IWYU pragma: export
#include "result.h" // IWYU pragma: export
#include "siphash.h" // IWYU pragma: export
#include "stacktrace.h" // IWYU pragma: export
+#include "stdlib.h" // IWYU pragma: export
#include "structs.h" // IWYU pragma: export
#include "tables.h" // IWYU pragma: export
#include "text.h" // IWYU pragma: export