diff options
Diffstat (limited to 'src/stdlib')
| -rw-r--r-- | src/stdlib/arrays.c | 6 | ||||
| -rw-r--r-- | src/stdlib/arrays.h | 4 | ||||
| -rw-r--r-- | src/stdlib/bytes.c | 10 | ||||
| -rw-r--r-- | src/stdlib/datatypes.h | 8 | ||||
| -rw-r--r-- | src/stdlib/functiontype.c | 2 | ||||
| -rw-r--r-- | src/stdlib/integers.c | 14 | ||||
| -rw-r--r-- | src/stdlib/integers.h | 38 | ||||
| -rw-r--r-- | src/stdlib/metamethods.c | 4 | ||||
| -rw-r--r-- | src/stdlib/moments.c | 2 | ||||
| -rw-r--r-- | src/stdlib/mutexeddata.c | 3 | ||||
| -rw-r--r-- | src/stdlib/nums.h | 14 | ||||
| -rw-r--r-- | src/stdlib/optionals.h | 6 | ||||
| -rw-r--r-- | src/stdlib/paths.c | 86 | ||||
| -rw-r--r-- | src/stdlib/paths.h | 1 | ||||
| -rw-r--r-- | src/stdlib/patterns.c | 18 | ||||
| -rw-r--r-- | src/stdlib/patterns.h | 1 | ||||
| -rw-r--r-- | src/stdlib/print.c | 76 | ||||
| -rw-r--r-- | src/stdlib/print.h | 166 | ||||
| -rw-r--r-- | src/stdlib/rng.c | 17 | ||||
| -rw-r--r-- | src/stdlib/stdlib.c | 124 | ||||
| -rw-r--r-- | src/stdlib/stdlib.h | 41 | ||||
| -rw-r--r-- | src/stdlib/text.c | 29 | ||||
| -rw-r--r-- | src/stdlib/text.h | 6 |
23 files changed, 447 insertions, 229 deletions
diff --git a/src/stdlib/arrays.c b/src/stdlib/arrays.c index cd403c5f..b7384504 100644 --- a/src/stdlib/arrays.c +++ b/src/stdlib/arrays.c @@ -57,7 +57,7 @@ public void Array$insert(Array_t *arr, const void *item, Int_t int_index, int64_ if (index < 1) index = 1; else if (index > (int64_t)arr->length + 1) - fail("Invalid insertion index %ld for an array with length %ld", index, arr->length); + fail("Invalid insertion index ", index, " for an array with length ", (int64_t)arr->length); if (!arr->data) { arr->free = 4; @@ -105,7 +105,7 @@ public void Array$insert_all(Array_t *arr, Array_t to_insert, Int_t int_index, i if (index < 1) index = 1; else if (index > (int64_t)arr->length + 1) - fail("Invalid insertion index %ld for an array with length %ld", index, arr->length); + fail("Invalid insertion index ", index, " for an array with length ", (int64_t)arr->length); if ((int64_t)arr->free >= (int64_t)to_insert.length // Adequate free space && arr->data_refcount == 0 // Not aliased memory @@ -322,7 +322,7 @@ public Array_t Array$sample(Array_t arr, Int_t int_n, Array_t weights, RNG_t rng } if (weights.length != arr.length) - fail("Array has %ld elements, but there are %ld weights given", arr.length, weights.length); + fail("Array has ", (int64_t)arr.length, " elements, but there are ", (int64_t)weights.length, " weights given"); double total = 0.0; for (int64_t i = 0; i < weights.length && i < arr.length; i++) { diff --git a/src/stdlib/arrays.h b/src/stdlib/arrays.h index dc7efee6..e286dfdb 100644 --- a/src/stdlib/arrays.h +++ b/src/stdlib/arrays.h @@ -14,7 +14,7 @@ const Array_t arr = arr_expr; int64_t index = index_expr; \ int64_t off = index + (index < 0) * (arr.length + 1) - 1; \ if (unlikely(off < 0 || off >= arr.length)) \ - fail_source(__SOURCE_FILE__, start, end, "Invalid array index: %s (array has length %ld)\n", Text$as_c_string(Int64$as_text(&index, no, NULL)), arr.length); \ + fail_source(__SOURCE_FILE__, start, end, "Invalid array index: ", index, " (array has length ", (int64_t)arr.length, ")\n"); \ (item_type*)(arr.data + arr.stride * off);}) #define Array_get_unchecked(type, x, i) *({ const Array_t arr = x; int64_t index = i; \ int64_t off = index + (index < 0) * (arr.length + 1) - 1; \ @@ -23,7 +23,7 @@ Array_t *arr = arr_expr; int64_t index = index_expr; \ int64_t off = index + (index < 0) * (arr->length + 1) - 1; \ if (unlikely(off < 0 || off >= arr->length)) \ - fail_source(__SOURCE_FILE__, start, end, "Invalid array index: %s (array has length %ld)\n", Text$as_c_string(Int64$as_text(&index, no, NULL)), arr->length); \ + fail_source(__SOURCE_FILE__, start, end, "Invalid array index: ", index, " (array has length ", (int64_t)arr->length, ")\n"); \ if (arr->data_refcount > 0) \ Array$compact(arr, sizeof(item_type)); \ (item_type*)(arr->data + arr->stride * off); }) diff --git a/src/stdlib/bytes.c b/src/stdlib/bytes.c index b24a721b..09e19c40 100644 --- a/src/stdlib/bytes.c +++ b/src/stdlib/bytes.c @@ -32,24 +32,24 @@ public Text_t Byte$hex(Byte_t byte, bool uppercase, bool prefix) { public PUREFUNC Byte_t Byte$from_int(Int_t i, bool truncate) { if unlikely (truncate && Int$compare_value(i, I_small(0xFF)) > 0) - fail("This value is too large to convert to a byte without truncation: %k", (Text_t[1]){Int$value_as_text(i)}); + fail("This value is too large to convert to a byte without truncation: ", i); else if unlikely (truncate && Int$compare_value(i, I_small(0)) < 0) - fail("Negative values can't be converted to bytes: %k", (Text_t[1]){Int$value_as_text(i)}); + fail("Negative values can't be converted to bytes: ", i); return (i.small != 0); } public PUREFUNC Byte_t Byte$from_int64(Int64_t i, bool truncate) { if unlikely (truncate && i != (Int64_t)(Byte_t)i) - fail("This value can't be converted to a byte without truncation: %ld", i); + fail("This value can't be converted to a byte without truncation: ", i); return (Byte_t)i; } public PUREFUNC Byte_t Byte$from_int32(Int32_t i, bool truncate) { if unlikely (truncate && i != (Int32_t)(Byte_t)i) - fail("This value can't be converted to a byte without truncation: %d", i); + fail("This value can't be converted to a byte without truncation: ", i); return (Byte_t)i; } public PUREFUNC Byte_t Byte$from_int16(Int16_t i, bool truncate) { if unlikely (truncate && i != (Int16_t)(Byte_t)i) - fail("This value can't be converted to a byte without truncation: %d", i); + fail("This value can't be converted to a byte without truncation: ", i); return (Byte_t)i; } diff --git a/src/stdlib/datatypes.h b/src/stdlib/datatypes.h index b81ff741..40b4712a 100644 --- a/src/stdlib/datatypes.h +++ b/src/stdlib/datatypes.h @@ -34,6 +34,8 @@ typedef union { mpz_t *big; } Int_t; +#define OptionalInt_t Int_t + typedef struct { void *data; // All of the following fields add up to 64 bits, which means that array @@ -117,4 +119,10 @@ typedef struct MutexedData_s { void *data; } *MutexedData_t; +#define OptionalBool_t uint8_t +#define OptionalArray_t Array_t +#define OptionalTable_t Table_t +#define OptionalText_t Text_t +#define OptionalClosure_t Closure_t + // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/functiontype.c b/src/stdlib/functiontype.c index b02739a2..8c864611 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 = Text$format("%k [%k:%ld]", &text, &filename, line_num); + text = Texts(text, Text(" ["), filename, Text$format(":%ld]", line_num)); } 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 4d5d0a80..c5764d46 100644 --- a/src/stdlib/integers.c +++ b/src/stdlib/integers.c @@ -6,6 +6,7 @@ #include <gmp.h> #include <stdbool.h> #include <stdint.h> +#include <stdio.h> #include <stdlib.h> #include "arrays.h" @@ -16,9 +17,18 @@ #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); + } else { + char *str = mpz_get_str(NULL, 10, *i.big); + return fputs(str, f); + } +} + public Text_t Int$value_as_text(Int_t i) { if (likely(i.small & 1L)) { - return Text$format("%ld", (i.small)>>2L); + return Text$format("%ld", i.small>>2L); } else { char *str = mpz_get_str(NULL, 10, *i.big); return Text$from_str(str); @@ -416,7 +426,7 @@ public Int_t Int$prev_prime(Int_t x) mpz_t p; mpz_init_set_int(p, x); if (unlikely(mpz_prevprime(p, p) == 0)) - fail("There is no prime number before %k", (Text_t[1]){Int$as_text(&x, false, &Int$info)}); + fail("There is no prime number before ", x); return Int$from_mpz(p); } #endif diff --git a/src/stdlib/integers.h b/src/stdlib/integers.h index e0586882..356e791c 100644 --- a/src/stdlib/integers.h +++ b/src/stdlib/integers.h @@ -7,6 +7,7 @@ #include <stdlib.h> #include <gmp.h> +#include "print.h" #include "datatypes.h" #include "stdlib.h" #include "types.h" @@ -87,8 +88,7 @@ void Int64$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t*); void Int32$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo_t*); void Int32$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t*); -#define OptionalInt_t Int_t - +int Int$print(FILE *f, Int_t i); Text_t Int$as_text(const void *i, bool colorize, const TypeInfo_t *type); Text_t Int$value_as_text(Int_t i); PUREFUNC uint64_t Int$hash(const void *x, const TypeInfo_t *type); @@ -283,7 +283,7 @@ MACROLIKE PUREFUNC Int_t Int$from_num(double n, bool truncate) { mpz_t result; mpz_init_set_d(result, n); if (!truncate && unlikely(mpz_get_d(result) != n)) - fail("Could not convert to an integer without truncation: %g", n); + fail("Could not convert to an integer without truncation: ", n); return Int$from_mpz(result); } MACROLIKE PUREFUNC Int_t Int$from_num32(float n, bool truncate) { return Int$from_num((double)n, truncate); } @@ -304,20 +304,20 @@ MACROLIKE CONSTFUNC Int_t Int$from_bool(Bool_t b) { return I_small(b); } MACROLIKE PUREFUNC Int64_t Int64$from_num(Num_t n, bool truncate) { int64_t i64 = (int64_t)n; if (!truncate && unlikely((Num_t)i64 != n)) - fail("Could not convert Num to Int64 without truncation: %g\n", n); + fail("Could not convert Num to Int64 without truncation: ", n); return i64; } MACROLIKE PUREFUNC Int64_t Int64$from_num32(Num32_t n, bool truncate) { int64_t i64 = (int64_t)n; if (!truncate && unlikely((Num32_t)i64 != n)) - fail("Could not convert Num32 to Int64 without truncation: %g\n", (double)n); + fail("Could not convert Num32 to Int64 without truncation: ", n); return i64; } MACROLIKE PUREFUNC Int64_t Int64$from_int(Int_t i, bool truncate) { if likely (i.small & 1L) return (int64_t)(i.small >> 2L); if (!truncate && unlikely(!mpz_fits_slong_p(*i.big))) - fail("Integer is too big to fit in a 64-bit integer: %k", (Text_t[1]){Int$value_as_text(i)}); + fail("Integer is too big to fit in a 64-bit integer: ", i); return mpz_get_si(*i.big); } MACROLIKE CONSTFUNC Int64_t Int64$from_int32(Int32_t i) { return (Int64_t)i; } @@ -328,26 +328,26 @@ MACROLIKE CONSTFUNC Int64_t Int64$from_int8(Int8_t i) { return (Int64_t)i; } MACROLIKE PUREFUNC Int32_t Int32$from_num(Num_t n, bool truncate) { int32_t i32 = (int32_t)n; if (!truncate && unlikely((Num_t)i32 != n)) - fail("Could not convert Num to Int32 without truncation: %g\n", n); + fail("Could not convert Num to Int32 without truncation: ", n); return i32; } MACROLIKE PUREFUNC Int32_t Int32$from_num32(Num32_t n, bool truncate) { int32_t i32 = (int32_t)n; if (!truncate && unlikely((Num32_t)i32 != n)) - fail("Could not convert Num32 to Int32 without truncation: %g\n", (double)n); + fail("Could not convert Num32 to Int32 without truncation: ", n); return i32; } MACROLIKE PUREFUNC Int32_t Int32$from_int(Int_t i, bool truncate) { int64_t i64 = Int64$from_int(i, truncate); int32_t i32 = (int32_t)i64; if (!truncate && unlikely((int64_t)i32 != i64)) - fail("Integer is too big to fit in a 32-bit integer: %k", (Text_t[1]){Int$value_as_text(i)}); + fail("Integer is too big to fit in a 32-bit integer: ", i); return i32; } MACROLIKE PUREFUNC Int32_t Int32$from_int64(Int64_t i64, bool truncate) { int32_t i32 = (int32_t)i64; if (!truncate && unlikely((int64_t)i32 != i64)) - fail("Integer is too big to fit in a 32-bit integer: %ld", i64); + fail("Integer is too big to fit in a 32-bit integer: ", i64); return i32; } MACROLIKE CONSTFUNC Int32_t Int32$from_int16(Int16_t i) { return (Int32_t)i; } @@ -357,13 +357,13 @@ MACROLIKE CONSTFUNC Int32_t Int32$from_int8(Int8_t i) { return (Int32_t)i; } MACROLIKE PUREFUNC Int16_t Int16$from_num(Num_t n, bool truncate) { int16_t i16 = (int16_t)n; if (!truncate && unlikely((Num_t)i16 != n)) - fail("Could not convert Num to Int16 without truncation: %g\n", n); + fail("Could not convert Num to Int16 without truncation: ", n); return i16; } MACROLIKE PUREFUNC Int16_t Int16$from_num32(Num32_t n, bool truncate) { int16_t i16 = (int16_t)n; if (!truncate && unlikely((Num32_t)i16 != n)) - fail("Could not convert Num32 to Int16 without truncation: %g\n", (double)n); + fail("Could not convert Num32 to Int16 without truncation: ", (double)n); return i16; } MACROLIKE PUREFUNC Int16_t Int16$from_int(Int_t i, bool truncate) { @@ -376,13 +376,13 @@ MACROLIKE PUREFUNC Int16_t Int16$from_int(Int_t i, bool truncate) { MACROLIKE PUREFUNC Int16_t Int16$from_int64(Int64_t i64, bool truncate) { int16_t i16 = (int16_t)i64; if (!truncate && unlikely((int64_t)i16 != i64)) - fail("Integer is too big to fit in a 16-bit integer: %ld", i64); + fail("Integer is too big to fit in a 16-bit integer: ", i64); return i16; } MACROLIKE PUREFUNC Int16_t Int16$from_int32(Int32_t i32, bool truncate) { int16_t i16 = (int16_t)i32; if (!truncate && unlikely((int32_t)i16 != i32)) - fail("Integer is too big to fit in a 16-bit integer: %ld", i32); + fail("Integer is too big to fit in a 16-bit integer: ", i32); return i16; } MACROLIKE CONSTFUNC Int16_t Int16$from_int8(Int8_t i) { return (Int16_t)i; } @@ -391,13 +391,13 @@ MACROLIKE CONSTFUNC Int16_t Int16$from_int8(Int8_t i) { return (Int16_t)i; } MACROLIKE PUREFUNC Int8_t Int8$from_num(Num_t n, bool truncate) { int8_t i8 = (int8_t)n; if (!truncate && unlikely((Num_t)i8 != n)) - fail("Could not convert Num to Int8 without truncation: %g\n", n); + fail("Could not convert Num to Int8 without truncation: ", n); return i8; } MACROLIKE PUREFUNC Int8_t Int8$from_num32(Num32_t n, bool truncate) { int8_t i8 = (int8_t)n; if (!truncate && unlikely((Num32_t)i8 != n)) - fail("Could not convert Num32 to Int8 without truncation: %g\n", (double)n); + fail("Could not convert Num32 to Int8 without truncation: ", n); return i8; } MACROLIKE PUREFUNC Int8_t Int8$from_int(Int_t i, bool truncate) { @@ -410,19 +410,19 @@ MACROLIKE PUREFUNC Int8_t Int8$from_int(Int_t i, bool truncate) { MACROLIKE PUREFUNC Int8_t Int8$from_int64(Int64_t i64, bool truncate) { int8_t i8 = (int8_t)i64; if (!truncate && unlikely((int64_t)i8 != i64)) - fail("Integer is too big to fit in a 8-bit integer: %ld", i64); + fail("Integer is too big to fit in a 8-bit integer: ", i64); return i8; } MACROLIKE PUREFUNC Int8_t Int8$from_int32(Int32_t i32, bool truncate) { int8_t i8 = (int8_t)i32; if (!truncate && unlikely((int32_t)i8 != i32)) - fail("Integer is too big to fit in a 8-bit integer: %ld", i32); + fail("Integer is too big to fit in a 8-bit integer: ", i32); return i8; } MACROLIKE PUREFUNC Int8_t Int8$from_int16(Int16_t i16, bool truncate) { int8_t i8 = (int8_t)i16; if (!truncate && unlikely((int16_t)i8 != i16)) - fail("Integer is too big to fit in a 8-bit integer: %ld", i16); + fail("Integer is too big to fit in a 8-bit integer: ", i16); return i8; } #pragma GCC diagnostic pop diff --git a/src/stdlib/metamethods.c b/src/stdlib/metamethods.c index c0e11cfc..a7622e0b 100644 --- a/src/stdlib/metamethods.c +++ b/src/stdlib/metamethods.c @@ -111,14 +111,14 @@ __attribute__((noreturn)) public void cannot_serialize(const void*, FILE*, Table_t*, const TypeInfo_t *type) { Text_t typestr = generic_as_text(NULL, false, type); - fail("Values of type %k cannot be serialized or deserialized!", &typestr); + fail("Values of type ", typestr, " cannot be serialized or deserialized!"); } __attribute__((noreturn)) public void cannot_deserialize(FILE*, void*, Array_t*, const TypeInfo_t *type) { Text_t typestr = generic_as_text(NULL, false, type); - fail("Values of type %k cannot be serialized or deserialized!", &typestr); + fail("Values of type ", typestr, " cannot be serialized or deserialized!"); } // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/moments.c b/src/stdlib/moments.c index bb3d70a9..a48e40b5 100644 --- a/src/stdlib/moments.c +++ b/src/stdlib/moments.c @@ -222,7 +222,7 @@ public OptionalMoment_t Moment$parse(Text_t text, Text_t format) const char *str = Text$as_c_string(text); const char *fmt = Text$as_c_string(format); if (strstr(fmt, "%Z")) - fail("The %%Z specifier is not supported for time parsing!"); + fail("The %Z specifier is not supported for time parsing!"); char *invalid = strptime(str, fmt, &info); if (!invalid || invalid[0] != '\0') diff --git a/src/stdlib/mutexeddata.c b/src/stdlib/mutexeddata.c index f47adfc1..ead154e7 100644 --- a/src/stdlib/mutexeddata.c +++ b/src/stdlib/mutexeddata.c @@ -20,7 +20,8 @@ static Text_t MutexedData$as_text(const void *m, bool colorize, const TypeInfo_t if (!m) { return Texts(colorize ? Text("\x1b[34;1mmutexed\x1b[m(") : Text("mutexed("), typename, Text(")")); } - return Text$format(colorize ? "\x1b[34;1mmutexed %k<%p>\x1b[m" : "mutexed %k<%p>", &typename, *((MutexedData_t*)m)); + return Texts(colorize ? Text("\x1b[34;1mmutexed ") : Text("mutexed "), typename, + Text$format(colorize ? "<%p>\x1b[m" : "<%p>", *((MutexedData_t*)m))); } static bool MutexedData$is_none(const void *m, const TypeInfo_t *) diff --git a/src/stdlib/nums.h b/src/stdlib/nums.h index be270f51..f355fb6f 100644 --- a/src/stdlib/nums.h +++ b/src/stdlib/nums.h @@ -42,7 +42,7 @@ MACROLIKE CONSTFUNC double Num$from_int(Int_t i, bool truncate) { if likely (i.small & 0x1) { double ret = (double)(i.small >> 2); if unlikely (!truncate && (int64_t)ret != (i.small >> 2)) - fail("Could not convert integer to 64-bit floating point without losing precision: %ld", i.small >> 2); + fail("Could not convert integer to 64-bit floating point without losing precision: ", i.small >> 2); return ret; } else { double ret = mpz_get_d(*i.big); @@ -50,7 +50,7 @@ MACROLIKE CONSTFUNC double Num$from_int(Int_t i, bool truncate) { mpz_t roundtrip; mpz_init_set_d(roundtrip, ret); if unlikely (mpz_cmp(*i.big, roundtrip) != 0) - fail("Could not convert integer to 64-bit floating point without losing precision: %k", (Text_t[1]){Int$value_as_text(i)}); + fail("Could not convert integer to 64-bit floating point without losing precision: ", i); } return ret; } @@ -59,7 +59,7 @@ MACROLIKE CONSTFUNC double Num$from_int(Int_t i, bool truncate) { MACROLIKE CONSTFUNC double Num$from_int64(Int64_t i, bool truncate) { double n = (double)i; if unlikely (!truncate && (Int64_t)n != i) - fail("Could not convert integer to 64-bit floating point without losing precision: %ld", i); + fail("Could not convert integer to 64-bit floating point without losing precision: ", i); return n; } MACROLIKE CONSTFUNC double Num$from_int32(Int32_t i) { return (double)i; } @@ -94,7 +94,7 @@ MACROLIKE CONSTFUNC float Num32$from_int(Int_t i, bool truncate) { if likely (i.small & 0x1) { float ret = (float)(i.small >> 2); if unlikely (!truncate && (int64_t)ret != (i.small >> 2)) - fail("Could not convert integer to 32-bit floating point without losing precision: %ld", i.small >> 2); + fail("Could not convert integer to 32-bit floating point without losing precision: ", i.small >> 2); return ret; } else { float ret = (float)mpz_get_d(*i.big); @@ -102,7 +102,7 @@ MACROLIKE CONSTFUNC float Num32$from_int(Int_t i, bool truncate) { mpz_t roundtrip; mpz_init_set_d(roundtrip, ret); if unlikely (mpz_cmp(*i.big, roundtrip) != 0) - fail("Could not convert integer to 32-bit floating point without losing precision: %k", (Text_t[1]){Int$value_as_text(i)}); + fail("Could not convert integer to 32-bit floating point without losing precision: ", i); } return ret; } @@ -111,13 +111,13 @@ MACROLIKE CONSTFUNC float Num32$from_int(Int_t i, bool truncate) { MACROLIKE CONSTFUNC float Num32$from_int64(Int64_t i, bool truncate) { float n = (float)i; if unlikely (!truncate && (Int64_t)n != i) - fail("Could not convert integer to 32-bit floating point without losing precision: %ld", i); + fail("Could not convert integer to 32-bit floating point without losing precision: ", i); return n; } MACROLIKE CONSTFUNC float Num32$from_int32(Int32_t i, bool truncate) { float n = (float)i; if unlikely (!truncate && (Int32_t)n != i) - fail("Could not convert integer to 32-bit floating point without losing precision: %d", i); + fail("Could not convert integer to 32-bit floating point without losing precision: ", i); return n; } MACROLIKE CONSTFUNC float Num32$from_int16(Int16_t i) { return (float)i; } diff --git a/src/stdlib/optionals.h b/src/stdlib/optionals.h index ccf1b963..94e4d900 100644 --- a/src/stdlib/optionals.h +++ b/src/stdlib/optionals.h @@ -10,12 +10,6 @@ #include "types.h" #include "util.h" -#define OptionalBool_t uint8_t -#define OptionalArray_t Array_t -#define OptionalTable_t Table_t -#define OptionalText_t Text_t -#define OptionalClosure_t Closure_t - #define NONE_ARRAY ((Array_t){.length=-1}) #define NONE_BOOL ((OptionalBool_t)2) #define NONE_INT ((OptionalInt_t){.small=0}) diff --git a/src/stdlib/paths.c b/src/stdlib/paths.c index fae75e98..83e480e1 100644 --- a/src/stdlib/paths.c +++ b/src/stdlib/paths.c @@ -17,6 +17,7 @@ #include <unistd.h> #include <unistr.h> +#include "print.h" #include "arrays.h" #include "enums.h" #include "files.h" @@ -122,8 +123,7 @@ public Path_t Path$_concat(int n, Path_t items[n]) ARRAY_INCREF(result.components); for (int i = 1; i < n; i++) { if (items[i].type.$tag != PATH_RELATIVE) - fail("Cannot concatenate an absolute or home-based path onto another path: (%s)\n", - Path$as_c_string(items[i])); + fail("Cannot concatenate an absolute or home-based path onto another path: (", items[i], ")"); Array$insert_all(&result.components, items[i].components, I(0), sizeof(Text_t)); } clean_components(&result.components); @@ -146,8 +146,7 @@ public Path_t Path$resolved(Path_t path, Path_t relative_to) public Path_t Path$relative_to(Path_t path, Path_t relative_to) { if (path.type.$tag != relative_to.type.$tag) - fail("Cannot create a path relative to a different path with a mismatching type: (%k) relative to (%k)", - (Text_t[1]){Path$as_text(&path, false, &Path$info)}, (Text_t[1]){Path$as_text(&relative_to, false, &Path$info)}); + fail("Cannot create a path relative to a different path with a mismatching type: (", path, ") relative to (", relative_to, ")"); Path_t result = {.type.$tag=PATH_RELATIVE}; int64_t shared = 0; @@ -274,13 +273,13 @@ static void _write(Path_t path, Array_t bytes, int mode, int permissions) const char *path_str = Path$as_c_string(path); int fd = open(path_str, mode, permissions); if (fd == -1) - fail("Could not write to file: %s\n%s", path_str, strerror(errno)); + fail("Could not write to file: ", path_str, "\n", strerror(errno)); if (bytes.stride != 1) Array$compact(&bytes, 1); ssize_t written = write(fd, bytes.data, (size_t)bytes.length); if (written != (ssize_t)bytes.length) - fail("Could not write to file: %s\n%s", path_str, strerror(errno)); + fail("Could not write to file: ", path_str, "\n", strerror(errno)); close(fd); } @@ -328,7 +327,7 @@ public OptionalArray_t Path$read_bytes(Path_t path, OptionalInt_t count) content[sb.st_size] = '\0'; close(fd); if (count.small && (int64_t)sb.st_size < target_count) - fail("Could not read %ld bytes from %k (only got %zu)", target_count, &path, sb.st_size); + fail("Could not read ", target_count, " bytes from ", path, " (only got ", sb.st_size, ")"); int64_t len = count.small ? target_count : (int64_t)sb.st_size; return (Array_t){.data=content, .atomic=1, .stride=1, .length=len}; } else { @@ -358,7 +357,7 @@ public OptionalArray_t Path$read_bytes(Path_t path, OptionalInt_t count) } close(fd); if (count.small != 0 && (int64_t)len < target_count) - fail("Could not read %ld bytes from %k (only got %zu)", target_count, &path, len); + fail("Could not read ", target_count, " bytes from ", path, " (only got ", len, ")"); return (Array_t){.data=content, .atomic=1, .stride=1, .length=len}; } } @@ -393,14 +392,14 @@ public void Path$set_owner(Path_t path, OptionalText_t owner, OptionalText_t gro uid_t owner_id = (uid_t)-1; if (owner.length >= 0) { struct passwd *pwd = getpwnam(Text$as_c_string(owner)); - if (pwd == NULL) fail("Not a valid user: %k", &owner); + if (pwd == NULL) fail("Not a valid user: ", owner); owner_id = pwd->pw_uid; } gid_t group_id = (gid_t)-1; if (group.length >= 0) { struct group *grp = getgrnam(Text$as_c_string(group)); - if (grp == NULL) fail("Not a valid group: %k", &group); + if (grp == NULL) fail("Not a valid group: ", group); group_id = grp->gr_gid; } const char *path_str = Path$as_c_string(path); @@ -415,17 +414,16 @@ static int _remove_files(const char *path, const struct stat *sbuf, int type, st switch (type) { case FTW_F: case FTW_SL: case FTW_SLN: if (remove(path) < 0) { - fail("Could not remove file: %s (%s)", path, strerror(errno)); + fail("Could not remove file: ", path, " (", strerror(errno), ")"); return -1; } return 0; case FTW_DP: if (rmdir(path) != 0) - fail("Could not remove directory: %s (%s)", path, strerror(errno)); + fail("Could not remove directory: ", path, " (", strerror(errno), ")"); return 0; default: - printf("Type: %d\n", type); - fail("Could not remove path: %s (not a file or directory)", path, strerror(errno)); + fail("Could not remove path: ", path, " (not a file or directory)"); return -1; } } @@ -437,19 +435,19 @@ public void Path$remove(Path_t path, bool ignore_missing) struct stat sb; if (lstat(path_str, &sb) != 0) { if (!ignore_missing) - fail("Could not remove file: %s (%s)", path_str, strerror(errno)); + fail("Could not remove file: ", path_str, " (", strerror(errno), ")"); return; } if ((sb.st_mode & S_IFMT) == S_IFREG || (sb.st_mode & S_IFMT) == S_IFLNK) { if (unlink(path_str) != 0 && !ignore_missing) - fail("Could not remove file: %s (%s)", path_str, strerror(errno)); + fail("Could not remove file: ", path_str, " (", strerror(errno), ")"); } else if ((sb.st_mode & S_IFMT) == S_IFDIR) { const int num_open_fd = 10; if (nftw(path_str, _remove_files, num_open_fd, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0) fail("Could not remove directory: %s (%s)", path_str, strerror(errno)); } else { - fail("Could not remove path: %s (not a file or directory)", path_str, strerror(errno)); + fail("Could not remove path: ", path_str, " (not a file or directory)"); } } @@ -459,7 +457,7 @@ public void Path$create_directory(Path_t path, int permissions) const char *c_path = Path$as_c_string(path); int status = mkdir(c_path, (mode_t)permissions); if (status != 0 && errno != EEXIST) - fail("Could not create directory: %s (%s)", c_path, strerror(errno)); + fail("Could not create directory: ", c_path, " (", strerror(errno), ")"); } static Array_t _filtered_children(Path_t path, bool include_hidden, mode_t filter) @@ -471,7 +469,7 @@ static Array_t _filtered_children(Path_t path, bool include_hidden, mode_t filte size_t path_len = strlen(path_str); DIR *d = opendir(path_str); if (!d) - fail("Could not open directory: %k (%s)", &path, strerror(errno)); + fail("Could not open directory: ", path, " (", strerror(errno), ")"); if (path_str[path_len-1] == '/') --path_len; @@ -516,13 +514,13 @@ public Path_t Path$unique_directory(Path_t path) path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); size_t len = strlen(path_str); - if (len >= PATH_MAX) fail("Path is too long: %s", path_str); + if (len >= PATH_MAX) fail("Path is too long: ", path_str); char buf[PATH_MAX] = {}; strcpy(buf, path_str); if (buf[len-1] == '/') buf[--len] = '\0'; char *created = mkdtemp(buf); - if (!created) fail("Failed to create temporary directory: %s (%s)", path_str, strerror(errno)); + if (!created) fail("Failed to create temporary directory: ", path_str, " (", strerror(errno), ")"); return Path$from_str(created); } @@ -531,7 +529,7 @@ public Path_t Path$write_unique_bytes(Path_t path, Array_t bytes) path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); size_t len = strlen(path_str); - if (len >= PATH_MAX) fail("Path is too long: %s", path_str); + if (len >= PATH_MAX) fail("Path is too long: ", path_str); char buf[PATH_MAX] = {}; strcpy(buf, path_str); @@ -543,14 +541,14 @@ public Path_t Path$write_unique_bytes(Path_t path, Array_t bytes) int fd = mkstemps(buf, suffixlen); if (fd == -1) - fail("Could not write to unique file: %s\n%s", buf, strerror(errno)); + fail("Could not write to unique file: ", buf, "\n", strerror(errno)); if (bytes.stride != 1) Array$compact(&bytes, 1); ssize_t written = write(fd, bytes.data, (size_t)bytes.length); if (written != (ssize_t)bytes.length) - fail("Could not write to file: %s\n%s", buf, strerror(errno)); + fail("Could not write to file: ", buf, "\n", strerror(errno)); close(fd); return Path$from_str(buf); } @@ -736,42 +734,36 @@ public PUREFUNC bool Path$equal_values(Path_t a, Path_t b) return Array$equal(&a.components, &b.components, Array$info(&Text$info)); } -public const char *Path$as_c_string(Path_t path) +public int Path$print(FILE *f, Path_t path) { if (path.components.length == 0) { - if (path.type.$tag == PATH_ABSOLUTE) return "/"; - else if (path.type.$tag == PATH_RELATIVE) return "."; - else if (path.type.$tag == PATH_HOME) return "~"; + if (path.type.$tag == PATH_ABSOLUTE) return fputs("/", f); + else if (path.type.$tag == PATH_RELATIVE) return fputs(".", f); + else if (path.type.$tag == PATH_HOME) return fputs("~", f); } - size_t len = 0, capacity = 16; - char *buf = GC_MALLOC_ATOMIC(capacity); + int n = 0; if (path.type.$tag == PATH_ABSOLUTE) { - buf[len++] = '/'; + n += fputc('/', f); } else if (path.type.$tag == PATH_HOME) { - buf[len++] = '~'; - buf[len++] = '/'; + n += fputs("~/", f); } else if (path.type.$tag == PATH_RELATIVE) { - if (!Text$equal_values(*(Text_t*)path.components.data, Text(".."))) { - buf[len++] = '.'; - buf[len++] = '/'; - } + if (!Text$equal_values(*(Text_t*)path.components.data, Text(".."))) + n += fputs("./", f); } for (int64_t i = 0; i < path.components.length; i++) { Text_t *comp = (Text_t*)(path.components.data + i*path.components.stride); - const char *comp_str = Text$as_c_string(*comp); - size_t comp_len = strlen(comp_str); - if (len + comp_len + 1 > capacity) { - buf = GC_REALLOC(buf, (capacity += MIN(comp_len + 2, 16))); - } - memcpy(&buf[len], comp_str, comp_len); - len += comp_len; + n += Text$print(f, *comp); if (i + 1 < path.components.length) - buf[len++] = '/'; + n += fputc('/', f); } - buf[len++] = '\0'; - return buf; + return n; +} + +public const char *Path$as_c_string(Path_t path) +{ + return String(path); } public Text_t Path$as_text(const void *obj, bool color, const TypeInfo_t *type) diff --git a/src/stdlib/paths.h b/src/stdlib/paths.h index 6c6cebd3..8bdbb2f3 100644 --- a/src/stdlib/paths.h +++ b/src/stdlib/paths.h @@ -11,6 +11,7 @@ Path_t Path$from_str(const char *str); Path_t Path$from_text(Text_t text); +// int Path$print(FILE *f, Path_t path); const char *Path$as_c_string(Path_t path); #define Path(str) Path$from_str(str) Path_t Path$_concat(int n, Path_t items[n]); diff --git a/src/stdlib/patterns.c b/src/stdlib/patterns.c index 7f7d711b..500d29d2 100644 --- a/src/stdlib/patterns.c +++ b/src/stdlib/patterns.c @@ -518,7 +518,7 @@ static pat_t parse_next_pat(TextIter_t *state, int64_t *index) int32_t close = open; uc_mirror_char((ucs4_t)open, (ucs4_t*)&close); if (!match_grapheme(state, index, close)) - fail("Pattern's closing quote is missing: %k", &state->stack[0].text); + fail("Pattern's closing quote is missing: ", state->stack[0].text); return (pat_t){ .tag=PAT_QUOTE, @@ -533,7 +533,7 @@ static pat_t parse_next_pat(TextIter_t *state, int64_t *index) int32_t close = open; uc_mirror_char((ucs4_t)open, (ucs4_t*)&close); if (!match_grapheme(state, index, close)) - fail("Pattern's closing brace is missing: %k", &state->stack[0].text); + fail("Pattern's closing brace is missing: ", state->stack[0].text); return (pat_t){ .tag=PAT_PAIR, @@ -553,7 +553,7 @@ static pat_t parse_next_pat(TextIter_t *state, int64_t *index) } else { max = min; } - if (min > max) fail("Minimum repetitions (%ld) is less than the maximum (%ld)", min, max); + if (min > max) fail("Minimum repetitions (", min, ") is less than the maximum (", max, ")"); } else { min = -1, max = -1; } @@ -573,19 +573,19 @@ static pat_t parse_next_pat(TextIter_t *state, int64_t *index) skip_whitespace(state, index); int32_t grapheme = Text$get_grapheme_fast(state, (*index)++); if (!match_grapheme(state, index, '}')) - fail("Missing closing '}' in pattern: %k", &state->stack[0].text); + fail("Missing closing '}' in pattern: ", state->stack[0].text); return PAT(PAT_GRAPHEME, .grapheme=grapheme); } else if (strlen(prop_name) == 1) { // Single letter names: {1+ A} skip_whitespace(state, index); if (!match_grapheme(state, index, '}')) - fail("Missing closing '}' in pattern: %k", &state->stack[0].text); + fail("Missing closing '}' in pattern: ", state->stack[0].text); return PAT(PAT_GRAPHEME, .grapheme=prop_name[0]); } skip_whitespace(state, index); if (!match_grapheme(state, index, '}')) - fail("Missing closing '}' in pattern: %k", &state->stack[0].text); + fail("Missing closing '}' in pattern: ", state->stack[0].text); switch (tolower(prop_name[0])) { case '.': @@ -673,7 +673,7 @@ static pat_t parse_next_pat(TextIter_t *state, int64_t *index) ucs4_t grapheme = unicode_name_character(prop_name); if (grapheme == UNINAME_INVALID) - fail("Not a valid property or character name: %s", prop_name); + fail("Not a valid property or character name: ", prop_name); return PAT(PAT_GRAPHEME, .grapheme=(int32_t)grapheme); #undef PAT } else { @@ -942,14 +942,14 @@ static Text_t apply_backrefs(Text_t text, Array_t recursive_replacements, Text_t pos += 1; continue; } - if (backref < 0 || backref > 9) fail("Invalid backref index: %ld (only 0-%d are allowed)", backref, MAX_BACKREFS-1); + if (backref < 0 || backref > 9) fail("Invalid backref index: ", backref, " (only 0-", MAX_BACKREFS-1, " are allowed)"); backref_len = (after_backref - pos); if (Text$get_grapheme_fast(&replacement_state, pos + backref_len) == ';') backref_len += 1; // skip optional semicolon if (!captures[backref].occupied) - fail("There is no capture number %ld!", backref); + fail("There is no capture number ", backref, "!"); Text_t backref_text = Text$slice(text, I(captures[backref].index+1), I(captures[backref].index + captures[backref].length)); diff --git a/src/stdlib/patterns.h b/src/stdlib/patterns.h index 53db0978..2b77e490 100644 --- a/src/stdlib/patterns.h +++ b/src/stdlib/patterns.h @@ -3,7 +3,6 @@ // The type representing text patterns for pattern matching. #include <stdbool.h> -#include <printf.h> #include <stdint.h> #include "datatypes.h" diff --git a/src/stdlib/print.c b/src/stdlib/print.c new file mode 100644 index 00000000..023af986 --- /dev/null +++ b/src/stdlib/print.c @@ -0,0 +1,76 @@ +// This file defines some of the helper functions used for printing values +#include "print.h" + +#include <stdio.h> + +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] = {['\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)) +#if PRINT_COLOR + return fprintf(f, "\033[35m'%c'\033[m"), c); +#else + return fprintf(f, "'%c'", c); +#endif + else + return fprintf(f, ESC("x%02X"), (uint8_t)c); +#undef ESC +} + +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] = {['\n']=ESC("n"), ['\t']=ESC("t"), ['\r']=ESC("r"), + ['\033']=ESC("e"), ['\v']=ESC("v"), ['\a']=ESC("a"), ['\b']=ESC("b")}; + int printed = fputs("\033[35m\"", 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 += fprintf(f, ESC("x%02X"), (uint8_t)*p); + } + } + printed += fputs("\"\033[m", f); +#undef ESC + return printed; +} + +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(16, *stream->size/2), size + 1))); + memcpy(&(*stream->buffer)[stream->position], buf, size); + stream->position += size; + (*stream->buffer)[stream->position] = '\0'; + return (ssize_t)size; +} + +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); +} + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/print.h b/src/stdlib/print.h new file mode 100644 index 00000000..16400e7d --- /dev/null +++ b/src/stdlib/print.h @@ -0,0 +1,166 @@ +#pragma once + +// 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 +// +// If you put `#define PRINT_COLOR 1` before the import, text will be printed +// with terminal colors. + +#include <assert.h> +#include <ctype.h> +#include <gc.h> +#include <gc/cord.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" + +#ifndef PRINT_COLOR +#define PRINT_COLOR 0 +#endif + +#define EVAL0(...) __VA_ARGS__ +#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__))) +#define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) +#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) +#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) +#define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) + +#define MAP_END(...) +#define MAP_OUT +#define MAP_COMMA , + +#define MAP_GET_END2() 0, MAP_END +#define MAP_GET_END1(...) MAP_GET_END2 +#define MAP_GET_END(...) MAP_GET_END1 +#define MAP_NEXT0(test, next, ...) next MAP_OUT + +#define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0) +#define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next) + +#define MAP_LIST0(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__) +#define MAP_LIST1(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__) + +#define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +// 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 +#define PRINT_FN extern inline __attribute__((gnu_inline, always_inline)) int +#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 { + uint64_t n; + bool no_prefix; + bool uppercase; + int digits; +} oct_format_t; +#define oct(x, ...) ((oct_format_t){.n=x, __VA_ARGS__}) + +typedef struct { + double n; + int precision; +} num_format_t; +#define num_fmt(x, ...) ((num_format_t){.n=x, __VA_ARGS__}) + +typedef struct { + const char *str; +} quoted_t; +#define quoted(s) ((quoted_t){s}) + +#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("%ld"), x); } +PRINT_FN _print_uint(FILE *f, uint64_t x) { return fprintf(f, hl("%lu"), 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); } +PRINT_FN _print_str(FILE *f, const char *s) { return fputs(s, f); } +int _print_char(FILE *f, char c); +int _print_quoted(FILE *f, quoted_t quoted); +PRINT_FN _print_hex(FILE *f, hex_format_t hex) { + return fprintf(f, hex.no_prefix ? (hex.uppercase ? hl("%0*lX") : hl("%0*lx")) : (hex.uppercase ? hl("0x%0*lX") : hl("%#0*lx")), hex.digits, hex.n); +} +PRINT_FN _print_oct(FILE *f, oct_format_t oct) { + return fprintf(f, oct.no_prefix ? (oct.uppercase ? hl("%0*lO") : hl("%0*lo")) : (oct.uppercase ? hl("%#0*lO") : hl("%#0*lo")), oct.digits, oct.n); +} +PRINT_FN _print_num_format(FILE *f, num_format_t num) { + return fprintf(f, hl("%.*lf"), num.precision, num.n); +} +#undef hl + +extern int Text$print(FILE *stream, Text_t text); +extern int Path$print(FILE *stream, Path_t path); +#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, \ + oct_format_t: _print_oct, \ + num_format_t: _print_num_format, \ + quoted_t: _print_quoted, \ + 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, __VA_ARGS__); exit(EXIT_FAILURE); }) + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/rng.c b/src/stdlib/rng.c index 07b2f36c..82dd65d2 100644 --- a/src/stdlib/rng.c +++ b/src/stdlib/rng.c @@ -109,11 +109,8 @@ public Int_t RNG$int(RNG_t rng, Int_t min, Int_t max) } int32_t cmp = Int$compare_value(min, max); - if (cmp > 0) { - Text_t min_text = Int$as_text(&min, false, &Int$info), max_text = Int$as_text(&max, false, &Int$info); - fail("Random minimum value (%k) is larger than the maximum value (%k)", - &min_text, &max_text); - } + if (cmp > 0) + fail("Random minimum value (", min, ") is larger than the maximum value (", max, ")"); if (cmp == 0) return min; mpz_t range_size; @@ -140,7 +137,7 @@ public Int_t RNG$int(RNG_t rng, Int_t min, Int_t max) public Int64_t RNG$int64(RNG_t rng, Int64_t min, Int64_t max) { - if (min > max) fail("Random minimum value (%ld) is larger than the maximum value (%ld)", min, max); + if (min > max) fail("Random minimum value (", min, ") is larger than the maximum value (", max, ")"); if (min == max) return min; if (min == INT64_MIN && max == INT64_MAX) { int64_t r; @@ -159,7 +156,7 @@ public Int64_t RNG$int64(RNG_t rng, Int64_t min, Int64_t max) public Int32_t RNG$int32(RNG_t rng, Int32_t min, Int32_t max) { - if (min > max) fail("Random minimum value (%d) is larger than the maximum value (%d)", min, max); + if (min > max) fail("Random minimum value (", min, ") is larger than the maximum value (", max, ")"); if (min == max) return min; if (min == INT32_MIN && max == INT32_MAX) { int32_t r; @@ -178,7 +175,7 @@ public Int32_t RNG$int32(RNG_t rng, Int32_t min, Int32_t max) public Int16_t RNG$int16(RNG_t rng, Int16_t min, Int16_t max) { - if (min > max) fail("Random minimum value (%d) is larger than the maximum value (%d)", min, max); + if (min > max) fail("Random minimum value (", min, ") is larger than the maximum value (", max, ")"); if (min == max) return min; if (min == INT16_MIN && max == INT16_MAX) { int16_t r; @@ -197,7 +194,7 @@ public Int16_t RNG$int16(RNG_t rng, Int16_t min, Int16_t max) public Int8_t RNG$int8(RNG_t rng, Int8_t min, Int8_t max) { - if (min > max) fail("Random minimum value (%d) is larger than the maximum value (%d)", min, max); + if (min > max) fail("Random minimum value (", min, ") is larger than the maximum value (", max, ")"); if (min == max) return min; if (min == INT8_MIN && max == INT8_MAX) { int8_t r; @@ -216,7 +213,7 @@ public Int8_t RNG$int8(RNG_t rng, Int8_t min, Int8_t max) public Num_t RNG$num(RNG_t rng, Num_t min, Num_t max) { - if (min > max) fail("Random minimum value (%g) is larger than the maximum value (%g)", min, max); + if (min > max) fail("Random minimum value (", min, ") is larger than the maximum value (", max, ")"); if (min == max) return min; union { diff --git a/src/stdlib/stdlib.c b/src/stdlib/stdlib.c index d2800dcd..d2819c17 100644 --- a/src/stdlib/stdlib.c +++ b/src/stdlib/stdlib.c @@ -12,6 +12,7 @@ #include <sys/random.h> #include <time.h> +#include "print.h" #include "bools.h" #include "files.h" #include "functiontype.h" @@ -57,9 +58,6 @@ public void tomo_init(void) Array_t rng_seed = {.length=sizeof(random_bytes), .data=random_bytes, .stride=1, .atomic=1}; RNG$set_seed(default_rng, rng_seed); - if (register_printf_specifier('k', printf_text, printf_text_size)) - errx(1, "Couldn't set printf specifier"); - struct sigaction sigact; sigact.sa_sigaction = signal_handler; sigemptyset(&sigact.sa_mask); @@ -136,7 +134,7 @@ static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest) // Single-argument tag: if (arg[len] != ':') - errx(1, "Invalid value for %k.%s: %s", &t, named.name, arg); + print_err("Invalid value for ", t, ".", named.name, ": ", arg); size_t offset = sizeof(int32_t); if (named.type->align > 0 && offset % (size_t)named.type->align > 0) offset += (size_t)named.type->align - (offset % (size_t)named.type->align); @@ -145,7 +143,7 @@ static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest) return true; } } - errx(1, "Invalid value for %s: %s", info->EnumInfo.name, arg); + print_err("Invalid value for ", info->EnumInfo.name, ": ", arg); } else if (info->tag == StructInfo) { if (info->StructInfo.num_fields == 0) return true; @@ -153,14 +151,14 @@ static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest) return parse_single_arg(info->StructInfo.fields[0].type, arg, dest); Text_t t = generic_as_text(NULL, false, info); - errx(1, "Unsupported multi-argument struct type for argument parsing: %k", &t); + print_err("Unsupported multi-argument struct type for argument parsing: ", t); } else if (info->tag == ArrayInfo) { - errx(1, "Array arguments must be specified as `--flag ...` not `--flag=...`"); + print_err("Array arguments must be specified as `--flag ...` not `--flag=...`"); } else if (info->tag == TableInfo) { - errx(1, "Table arguments must be specified as `--flag ...` not `--flag=...`"); + print_err("Table arguments must be specified as `--flag ...` not `--flag=...`"); } else { Text_t t = generic_as_text(NULL, false, info); - errx(1, "Unsupported type for argument parsing: %k", &t); + print_err("Unsupported type for argument parsing: ", t); } } @@ -178,7 +176,7 @@ static Array_t parse_array(const TypeInfo_t *item_info, int n, char *args[]) for (int i = 0; i < n; i++) { bool success = parse_single_arg(item_info, args[i], items.data + items.stride*i); if (!success) - errx(1, "Couldn't parse argument: %s", args[i]); + print_err("Couldn't parse argument: ", args[i]); } return items; } @@ -209,11 +207,11 @@ static Table_t parse_table(const TypeInfo_t *table, int n, char *args[]) bool success = parse_single_arg(key, key_arg, entries.data + entries.stride*i); if (!success) - errx(1, "Couldn't parse table key: %s", key_arg); + print_err("Couldn't parse table key: ", key_arg); success = parse_single_arg(value, value_arg, entries.data + entries.stride*i + value_offset); if (!success) - errx(1, "Couldn't parse table value: %s", value_arg); + print_err("Couldn't parse table value: ", value_arg); *equals = '='; } @@ -281,18 +279,18 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, *(OptionalBool_t*)spec[s].dest = true; } else { if (i + 1 >= argc) - errx(1, "Missing argument: %s\n%k", argv[i], &usage); + print_err("Missing argument: ", argv[i], "\n", usage); used_args[i+1] = true; populated_args[s] = parse_single_arg(spec[s].type, argv[i+1], spec[s].dest); if (!populated_args[s]) - errx(1, "Couldn't parse argument: %s %s\n%k", argv[i], argv[i+1], &usage); + print_err("Couldn't parse argument: ", argv[i], " ", argv[i+1], "\n", usage); } goto next_arg; } else if (after_name == '=') { // --foo=val used_args[i] = true; populated_args[s] = parse_single_arg(spec[s].type, 2 + argv[i] + strlen(spec[s].name) + 1, spec[s].dest); if (!populated_args[s]) - errx(1, "Couldn't parse argument: %s\n%k", argv[i], &usage); + print_err("Couldn't parse argument: ", argv[i], "\n", usage); goto next_arg; } else { continue; @@ -303,10 +301,11 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, say(help, true); exit(0); } - errx(1, "Unrecognized argument: %s\n%k", argv[i], &usage); + print_err("Unrecognized argument: ", argv[i], "\n", usage); } else if (argv[i][0] == '-' && argv[i][1] && argv[i][1] != '-') { // Single flag args used_args[i] = true; for (char *f = argv[i] + 1; *f; f++) { + char flag[] = {'-', *f, 0}; for (int s = 0; s < spec_len; s++) { if (spec[s].name[0] != *f || strlen(spec[s].name) > 1) continue; @@ -316,7 +315,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, non_opt_type = non_opt_type->OptionalInfo.type; if (non_opt_type->tag == ArrayInfo) { - if (f[1]) errx(1, "No value provided for -%c\n%k", *f, &usage); + if (f[1]) print_err("No value provided for ", flag, "\n", usage); int num_args = 0; while (i + 1 + num_args < argc) { if (argv[i+1+num_args][0] == '-') @@ -340,11 +339,11 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, populated_args[s] = true; *(OptionalBool_t*)spec[s].dest = true; } else { - if (f[1] || i+1 >= argc) errx(1, "No value provided for -%c\n%k", *f, &usage); + if (f[1] || i+1 >= argc) print_err("No value provided for ", flag, "\n", usage); used_args[i+1] = true; populated_args[s] = parse_single_arg(spec[s].type, argv[i+1], spec[s].dest); if (!populated_args[s]) - errx(1, "Couldn't parse argument: %s %s\n%k", argv[i], argv[i+1], &usage); + print_err("Couldn't parse argument: ", argv[i], " ", argv[i+1], "\n", usage); } goto next_flag; } @@ -353,7 +352,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, say(help, true); exit(0); } - errx(1, "Unrecognized flag: -%c\n%k", *f, &usage); + print_err("Unrecognized flag: ", flag, "\n", usage); next_flag:; } } else { @@ -380,7 +379,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, next_non_bool_flag: ++s; if (s >= spec_len) - errx(1, "Extra argument: %s\n%k", argv[i], &usage); + print_err("Extra argument: ", argv[i], "\n", usage); } const TypeInfo_t *non_opt_type = spec[s].type; @@ -416,7 +415,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, } if (!populated_args[s]) - errx(1, "Invalid value for %s: %s\n%k", spec[s].name, argv[i], &usage); + print_err("Invalid value for ", spec[s].name, ": ", argv[i], "\n", usage); } for (int s = 0; s < spec_len; s++) { @@ -426,7 +425,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, else if (spec[s].type->tag == TableInfo) *(OptionalTable_t*)spec[s].dest = (Table_t){}; else - errx(1, "The required argument '%s' was not provided\n%k", spec[s].name, &usage); + print_err("The required argument '", spec[s].name, "' was not provided\n", usage); } } } @@ -438,14 +437,17 @@ static void print_stack_line(FILE *out, OptionalText_t fn_name, const char *file // do a linear scan through the whole file. However, performance shouldn't // really matter if we only print stack lines when there's a crash. if (filename) { - fprintf(out, "\033[34mFile\033[m \033[35;1m%s\033[m", filename); + fprint_inline(out, "\033[34mFile\033[m \033[35;1m", filename, "\033[m"); if (line_num >= 1) - fprintf(out, "\033[34m line\033[m \033[35;1m%ld\033[m", line_num); + fprint_inline(out, "\033[34m line\033[m \033[35;1m", line_num, "\033[m"); } if (fn_name.length > 0) { - fprintf(out, filename ? "\033[34m, in \033[m \033[36;1m%k\033[m" : "\033[36;1m%k\033[m", &fn_name); + if (filename) + fprint_inline(out, "\033[34m, in \033[m \033[36;1m", fn_name, "\033[m"); + else + fprint_inline(out, "\033[36;1m", fn_name, "\033[m"); } - fprintf(out, "\n"); + fprint_inline(out, "\n"); FILE *f = fopen(filename, "r"); if (!f) return; @@ -458,7 +460,7 @@ static void print_stack_line(FILE *out, OptionalText_t fn_name, const char *file line[strlen(line)-1] = '\0'; if (cur_line >= line_num) - fprintf(out, "\033[33;1m%s\033[m\n", line); + fprint(out, "\033[33;1m", line, "\033[m"); cur_line += 1; if (cur_line > line_num) @@ -468,7 +470,7 @@ static void print_stack_line(FILE *out, OptionalText_t fn_name, const char *file fclose(f); } -void print_stack_trace(FILE *out, int start, int stop) +public void print_stack_trace(FILE *out, int start, int stop) { // Print stack trace: void *stack[1024]; @@ -498,52 +500,9 @@ void print_stack_trace(FILE *out, int start, int stop) } } -__attribute__((format(printf, 1, 2))) -public _Noreturn void fail(const char *fmt, ...) -{ - fflush(stdout); - if (USE_COLOR) fputs("\x1b[31;7m ==================== ERROR ==================== \n\n\x1b[0;1m", stderr); - else fputs("==================== ERROR ====================\n\n", stderr); - va_list args; - va_start(args, fmt); - vfprintf(stderr, fmt, args); - if (USE_COLOR) fputs("\x1b[m", stderr); - fputs("\n\n", stderr); - va_end(args); - print_stack_trace(stderr, 2, 4); - fflush(stderr); - raise(SIGABRT); - _exit(1); -} - public _Noreturn void fail_text(Text_t message) { - fail("%k", &message); -} - -__attribute__((format(printf, 4, 5))) -public _Noreturn void fail_source(const char *filename, int64_t start, int64_t end, const char *fmt, ...) -{ - if (USE_COLOR) fputs("\n\x1b[31;7m ==================== ERROR ==================== \n\n\x1b[0;1m", stderr); - else fputs("\n==================== ERROR ====================\n\n", stderr); - - va_list args; - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - - 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", 2, USE_COLOR); - fputs("\n", stderr); - } - if (USE_COLOR) fputs("\x1b[m", stderr); - - print_stack_trace(stderr, 2, 4); - fflush(stderr); - raise(SIGABRT); - _exit(1); + fail(message); } public Text_t builtin_last_err() @@ -595,9 +554,8 @@ public void end_inspect(const void *expr, const TypeInfo_t *type) if (type->metamethods.as_text) { Text_t expr_text = generic_as_text(expr, USE_COLOR, type); Text_t type_name = generic_as_text(NULL, false, type); - for (int i = 0; i < 3*_inspect_depth; i++) fputc(' ', stdout); - fprintf(stdout, USE_COLOR ? "\x1b[33;1m=\x1b[0m %k \x1b[2m: \x1b[36m%k\x1b[m\n" : "= %k : %k\n", &expr_text, &type_name); + fprint(stdout, USE_COLOR ? "\x1b[33;1m=\x1b[0m " : " =", expr_text, USE_COLOR ? " \x1b[2m: \x1b[36m" : " : ", type_name); } } @@ -610,11 +568,17 @@ public void test_value(const void *expr, const void *expected, const TypeInfo_t bool success = Text$equal_values(expr_text, expected_text); if (!success) { print_stack_trace(stderr, 2, 4); - fprintf(stderr, - USE_COLOR - ? "\n\x1b[31;7m ==================== TEST FAILED ==================== \x1b[0;1m\n\nYou expected: \x1b[m%k\x1b[0m\n\x1b[1m But I got:\x1b[m %k\n\n" - : "\n==================== TEST FAILED ====================\n\nYou expected: %k\n But I got: %k\n\n", - &expected_text, &expr_text); + if (USE_COLOR) { + fprint(stderr, + "\n\x1b[31;7m ==================== TEST FAILED ==================== \x1b[0;1m\n\n" + "You expected: \x1b[m", expected_text, "\x1b[0m\n" + "\x1b[1m But I got:\x1b[m ", expr_text, "\n"); + } else { + fprint(stderr, + "\n==================== TEST FAILED ====================\n\n" + "You expected: ", expected_text, "\n" + " But I got: ", expr_text, "\n"); + } fflush(stderr); raise(SIGABRT); diff --git a/src/stdlib/stdlib.h b/src/stdlib/stdlib.h index 49ec43fb..d8b26eb4 100644 --- a/src/stdlib/stdlib.h +++ b/src/stdlib/stdlib.h @@ -7,6 +7,8 @@ #include <stdio.h> #include "datatypes.h" +#include "files.h" +#include "print.h" #include "types.h" #include "util.h" @@ -23,11 +25,42 @@ void tomo_init(void); void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, int spec_len, cli_arg_t spec[spec_len]); #define tomo_parse_args(argc, argv, usage, help, ...) \ _tomo_parse_args(argc, argv, usage, help, sizeof((cli_arg_t[]){__VA_ARGS__})/sizeof(cli_arg_t), (cli_arg_t[]){__VA_ARGS__}) -__attribute__((format(printf, 1, 2))) -_Noreturn void fail(const char *fmt, ...); + +#define fail(...) ({ \ + fflush(stdout); \ + if (USE_COLOR) fputs("\x1b[31;7m ==================== ERROR ==================== \n\n\x1b[0;1m", stderr); \ + else fputs("==================== ERROR ====================\n\n", stderr); \ + fprint_inline(stderr, __VA_ARGS__); \ + if (USE_COLOR) fputs("\x1b[m", stderr); \ + fputs("\n\n", stderr); \ + print_stack_trace(stderr, 2, 4); \ + fflush(stderr); \ + raise(SIGABRT); \ + _exit(1); \ +}) + +#define fail_source(filename, start, end, ...) ({ \ + fflush(stdout); \ + if (USE_COLOR) fputs("\x1b[31;7m ==================== ERROR ==================== \n\n\x1b[0;1m", stderr); \ + else fputs("==================== ERROR ====================\n\n", stderr); \ + fprint_inline(stderr, __VA_ARGS__); \ + if (USE_COLOR) fputs("\x1b[m", stderr); \ + 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", 2, USE_COLOR); \ + fputs("\n", stderr); \ + } \ + if (USE_COLOR) fputs("\x1b[m", stderr); \ + print_stack_trace(stderr, 2, 4); \ + fputs("\n\n", stderr); \ + print_stack_trace(stderr, 2, 4); \ + fflush(stderr); \ + raise(SIGABRT); \ + _exit(1); \ +}) + _Noreturn void fail_text(Text_t message); -__attribute__((format(printf, 4, 5))) -_Noreturn void fail_source(const char *filename, int64_t start, int64_t end, const char *fmt, ...); Text_t builtin_last_err(); __attribute__((nonnull)) void start_inspect(const char *filename, int64_t start, int64_t end); diff --git a/src/stdlib/text.c b/src/stdlib/text.c index c2ee4613..73941717 100644 --- a/src/stdlib/text.c +++ b/src/stdlib/text.c @@ -55,7 +55,6 @@ #include <assert.h> #include <ctype.h> #include <gc.h> -#include <printf.h> #include <stdbool.h> #include <stdint.h> #include <stdlib.h> @@ -681,8 +680,7 @@ public PUREFUNC Text_t Text$cluster(Text_t text, Int_t index_int) if (index < 0) index = text.length + index + 1; if (index > text.length || index < 1) - fail("Invalid index: %ld is beyond the length of the text (length = %ld)", - Int64$from_int(index_int, false), text.length); + fail("Invalid index: ", index_int, " is beyond the length of the text (length = ", (int64_t)text.length, ")"); while (text.tag == TEXT_CONCAT) { if (index <= text.left->length) @@ -1104,24 +1102,6 @@ public Text_t Text$title(Text_t text, Text_t language) return ret; } -public int printf_text_size(const struct printf_info *info, size_t n, int argtypes[n], int sizes[n]) -{ - if (n < 1) return -1; - (void)info; - argtypes[0] = PA_POINTER; - sizes[0] = sizeof(Text_t); - return 1; -} - -public int printf_text(FILE *stream, const struct printf_info *info, const void *const args[]) -{ - Text_t *t = *(Text_t**)args[0]; - if (info->alt) - return text_visualize(stream, *t, 0); - else - return Text$print(stream, *t); -} - static INLINE Text_t _quoted(Text_t text, bool colorize, char quote_char) { Text_t ret = colorize ? Text("\x1b[35m") : EMPTY_TEXT; @@ -1273,13 +1253,14 @@ 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 buf[9]; - int len = vsnprintf(buf, sizeof(buf), fmt, args); char *str = GC_MALLOC_ATOMIC((size_t)(len+1)); + va_start(args, fmt); vsnprintf(str, (size_t)(len+1), fmt, args); - Text_t ret = Text$from_str(str); va_end(args); + Text_t ret = Text$from_strn(str, (size_t)len); return ret; } diff --git a/src/stdlib/text.h b/src/stdlib/text.h index 0a44f4e4..e5c6a14f 100644 --- a/src/stdlib/text.h +++ b/src/stdlib/text.h @@ -4,7 +4,6 @@ // Raku's string representation and libunistr #include <stdbool.h> -#include <printf.h> #include <stdint.h> #include "datatypes.h" @@ -25,12 +24,9 @@ typedef struct { #define NEW_TEXT_ITER_STATE(t) (TextIter_t){.stack={{t, 0}}, .stack_index=0} -int printf_text(FILE *stream, const struct printf_info *info, const void *const args[]); -int printf_text_size(const struct printf_info *info, size_t n, int argtypes[n], int sizes[n]); - #define Text(str) ((Text_t){.length=sizeof(str)-1, .tag=TEXT_ASCII, .ascii="" str}) -int Text$print(FILE *stream, Text_t t); +//int Text$print(FILE *stream, Text_t 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(__VA_ARGS__) |
