From 4b5e4cd1f21582f5e5fa682ab4e4bff252963468 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Fri, 29 Nov 2024 12:55:14 -0500 Subject: [PATCH] Change how types handle metamethods --- Makefile | 3 +- compile.c | 6 +- enums.c | 2 +- repl.c | 10 +- stdlib/arrays.c | 25 +++-- stdlib/arrays.h | 21 +++- stdlib/bools.c | 20 ++-- stdlib/bools.h | 2 +- stdlib/bytes.c | 10 +- stdlib/bytes.h | 2 +- stdlib/c_strings.c | 36 ++++--- stdlib/c_strings.h | 6 +- stdlib/channels.c | 23 +++-- stdlib/channels.h | 21 +++- stdlib/enums.c | 92 +++++++++++++++++ stdlib/enums.h | 24 +++++ stdlib/functiontype.c | 8 ++ stdlib/functiontype.h | 18 ++++ stdlib/integers.c | 58 ++++++----- stdlib/integers.h | 14 +-- stdlib/memory.c | 8 +- stdlib/metamethods.c | 234 +++--------------------------------------- stdlib/moments.c | 16 ++- stdlib/moments.h | 4 +- stdlib/nums.c | 57 +++++----- stdlib/nums.h | 12 +-- stdlib/optionals.c | 77 ++++++-------- stdlib/optionals.h | 14 +++ stdlib/paths.c | 3 +- stdlib/patterns.c | 15 +++ stdlib/pointers.c | 14 +-- stdlib/pointers.h | 12 +++ stdlib/ranges.c | 38 ++++--- stdlib/rng.c | 10 +- stdlib/shell.c | 1 + stdlib/stdlib.c | 4 +- stdlib/structs.c | 170 ++++++++++++++++++++++++++++++ stdlib/structs.h | 24 +++++ stdlib/tables.c | 26 +++-- stdlib/tables.h | 22 +++- stdlib/text.c | 47 +++++---- stdlib/text.h | 16 ++- stdlib/threads.c | 16 ++- stdlib/threads.h | 2 +- stdlib/tomo.h | 2 + stdlib/types.h | 40 ++------ structs.c | 3 +- 47 files changed, 772 insertions(+), 516 deletions(-) create mode 100644 stdlib/enums.c create mode 100644 stdlib/enums.h create mode 100644 stdlib/structs.c create mode 100644 stdlib/structs.h diff --git a/Makefile b/Makefile index c8515b9..6e99c5e 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,8 @@ LDLIBS=-lgc -lcord -lm -lunistring -lgmp -ldl BUILTIN_OBJS=stdlib/siphash.o stdlib/arrays.o stdlib/bools.o stdlib/bytes.o stdlib/channels.o stdlib/nums.o stdlib/integers.o \ stdlib/pointers.o stdlib/memory.o stdlib/text.o stdlib/threads.o stdlib/c_strings.o stdlib/tables.o \ stdlib/types.o stdlib/util.o stdlib/files.o stdlib/ranges.o stdlib/shell.o stdlib/paths.o stdlib/rng.o \ - stdlib/optionals.o stdlib/patterns.o stdlib/metamethods.o stdlib/functiontype.o stdlib/stdlib.o stdlib/moments.o + stdlib/optionals.o stdlib/patterns.o stdlib/metamethods.o stdlib/functiontype.o stdlib/stdlib.o \ + stdlib/structs.o stdlib/enums.o stdlib/moments.o TESTS=$(patsubst %.tm,%.tm.testresult,$(wildcard test/*.tm)) all: libtomo.so tomo diff --git a/compile.c b/compile.c index 73a4f02..a426d09 100644 --- a/compile.c +++ b/compile.c @@ -817,7 +817,7 @@ CORD compile_statement(env_t *env, ast_t *ast) } case LangDef: { auto def = Match(ast, LangDef); - CORD_appendf(&env->code->typeinfos, "public const TypeInfo_t %r%s = {%zu, %zu, {.tag=TextInfo, .TextInfo={%r}}};\n", + CORD_appendf(&env->code->typeinfos, "public const TypeInfo_t %r%s = {%zu, %zu, .metamethods=Text$metamethods, .tag=TextInfo, .TextInfo={%r}};\n", namespace_prefix(env, env->namespace), def->name, sizeof(Text_t), __alignof__(Text_t), CORD_quoted(def->name)); compile_namespace(env, def->name, def->namespace); @@ -1507,9 +1507,7 @@ CORD expr_as_text(env_t *env, CORD expr, type_t *t, CORD color) CORD name = type_to_cord(t); return CORD_asprintf("%r$as_text(stack(%r), %r, &%r$info)", name, expr, color, name); } - case TextType: { - return CORD_asprintf("Text$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t)); - } + case TextType: return CORD_asprintf("Text$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t)); case ArrayType: return CORD_asprintf("Array$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t)); case SetType: return CORD_asprintf("Table$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t)); case ChannelType: return CORD_asprintf("Channel$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t)); diff --git a/enums.c b/enums.c index 0a4d336..2dcb714 100644 --- a/enums.c +++ b/enums.c @@ -46,7 +46,7 @@ void compile_enum_def(env_t *env, ast_t *ast) num_tags += 1; type_t *t = Table$str_get(*env->types, def->name); - CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {%zu, %zu, {.tag=EnumInfo, .EnumInfo={.name=\"%s\", " + CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {%zu, %zu, .metamethods=Enum$metamethods, {.tag=EnumInfo, .EnumInfo={.name=\"%s\", " ".num_tags=%d, .tags=(NamedType_t[]){", full_name, type_size(t), type_align(t), def->name, num_tags); diff --git a/repl.c b/repl.c index 7374ad3..801a16d 100644 --- a/repl.c +++ b/repl.c @@ -121,24 +121,20 @@ const TypeInfo_t *type_to_type_info(type_t *t) case TextType: return &Text$info; case ArrayType: { const TypeInfo_t *item_info = type_to_type_info(Match(t, ArrayType)->item_type); - const TypeInfo_t array_info = {.size=sizeof(Array_t), .align=__alignof__(Array_t), - .tag=ArrayInfo, .ArrayInfo.item=item_info}; + TypeInfo_t array_info = *Array$info(item_info); return memcpy(GC_MALLOC(sizeof(TypeInfo_t)), &array_info, sizeof(TypeInfo_t)); } case TableType: { const TypeInfo_t *key_info = type_to_type_info(Match(t, TableType)->key_type); const TypeInfo_t *value_info = type_to_type_info(Match(t, TableType)->value_type); - const TypeInfo_t table_info = { - .size=sizeof(Table_t), .align=__alignof__(Table_t), - .tag=TableInfo, .TableInfo.key=key_info, .TableInfo.value=value_info}; + const TypeInfo_t table_info = *Table$info(key_info, value_info); return memcpy(GC_MALLOC(sizeof(TypeInfo_t)), &table_info, sizeof(TypeInfo_t)); } case PointerType: { auto ptr = Match(t, PointerType); CORD sigil = ptr->is_view ? "&" : "@"; const TypeInfo_t *pointed_info = type_to_type_info(ptr->pointed); - const TypeInfo_t pointer_info = {.size=sizeof(void*), .align=__alignof__(void*), - .tag=PointerInfo, .PointerInfo={.sigil=sigil, .pointed=pointed_info}}; + const TypeInfo_t pointer_info = *Pointer$info(sigil, pointed_info); return memcpy(GC_MALLOC(sizeof(TypeInfo_t)), &pointer_info, sizeof(TypeInfo_t)); } default: errx(1, "Unsupported type: %T", t); diff --git a/stdlib/arrays.c b/stdlib/arrays.c index 4db3d2d..ff21920 100644 --- a/stdlib/arrays.c +++ b/stdlib/arrays.c @@ -283,8 +283,7 @@ public void *Array$random(Array_t arr, RNG_t rng) public Table_t Array$counts(Array_t arr, const TypeInfo_t *type) { Table_t counts = {}; - const TypeInfo_t count_type = {.size=sizeof(Table_t), .align=__alignof__(Table_t), - .tag=TableInfo, .TableInfo.key=type->ArrayInfo.item, .TableInfo.value=&Int$info}; + const TypeInfo_t count_type = *Table$info(type->ArrayInfo.item, &Int$info); for (int64_t i = 0; i < arr.length; i++) { void *key = arr.data + i*arr.stride; int64_t *count = Table$get(counts, key, &count_type); @@ -515,14 +514,15 @@ public void Array$clear(Array_t *array) *array = (Array_t){.data=0, .length=0}; } -public int32_t Array$compare(const Array_t *x, const Array_t *y, const TypeInfo_t *type) +public int32_t Array$compare(const void *vx, const void *vy, const TypeInfo_t *type) { + const Array_t *x = (Array_t*)vx, *y = (Array_t*)vy; // Early out for arrays with the same data, e.g. two copies of the same array: if (x->data == y->data && x->stride == y->stride) return (x->length > y->length) - (x->length < y->length); const TypeInfo_t *item = type->ArrayInfo.item; - if (item->tag == PointerInfo || (item->tag == CustomInfo && item->CustomInfo.compare == NULL)) { // data comparison + if (item->tag == PointerInfo || !item->metamethods.compare) { // data comparison int64_t item_padded_size = type->ArrayInfo.item->size; if (type->ArrayInfo.item->align > 1 && item_padded_size % type->ArrayInfo.item->align) errx(1, "Item size is not padded!"); @@ -545,13 +545,14 @@ public int32_t Array$compare(const Array_t *x, const Array_t *y, const TypeInfo_ return (x->length > y->length) - (x->length < y->length); } -public bool Array$equal(const Array_t *x, const Array_t *y, const TypeInfo_t *type) +public bool Array$equal(const void *x, const void *y, const TypeInfo_t *type) { - return x == y || (x->length == y->length && Array$compare(x, y, type) == 0); + return x == y || (((Array_t*)x)->length == ((Array_t*)y)->length && Array$compare(x, y, type) == 0); } -public Text_t Array$as_text(const Array_t *arr, bool colorize, const TypeInfo_t *type) +public Text_t Array$as_text(const void *obj, bool colorize, const TypeInfo_t *type) { + Array_t *arr = (Array_t*)obj; if (!arr) return Text$concat(Text("["), generic_as_text(NULL, false, type->ArrayInfo.item), Text("]")); @@ -567,12 +568,13 @@ public Text_t Array$as_text(const Array_t *arr, bool colorize, const TypeInfo_t return text; } -public uint64_t Array$hash(const Array_t *arr, const TypeInfo_t *type) +public uint64_t Array$hash(const void *obj, const TypeInfo_t *type) { + const Array_t *arr = (Array_t*)obj; const TypeInfo_t *item = type->ArrayInfo.item; siphash sh; siphashinit(&sh, sizeof(uint64_t[arr->length])); - if (item->tag == PointerInfo || (item->tag == CustomInfo && item->CustomInfo.hash == NULL && item->size == sizeof(void*))) { // Raw data hash + if (item->tag == PointerInfo || (!item->metamethods.hash && item->size == sizeof(void*))) { // Raw data hash for (int64_t i = 0; i < arr->length; i++) siphashadd64bits(&sh, (uint64_t)(arr->data + i*arr->stride)); } else { @@ -694,4 +696,9 @@ public Int_t Array$binary_search(Array_t array, void *target, Closure_t comparis return I(lo+1); // Return the index where the target would be inserted } +public PUREFUNC bool Array$is_none(const void *obj, const TypeInfo_t*) +{ + return ((Array_t*)obj)->length < 0; +} + // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/arrays.h b/stdlib/arrays.h index 251e9f9..192de80 100644 --- a/stdlib/arrays.h +++ b/stdlib/arrays.h @@ -85,10 +85,11 @@ PUREFUNC Array_t Array$to(Array_t array, Int_t last); PUREFUNC Array_t Array$by(Array_t array, Int_t stride, int64_t padded_item_size); PUREFUNC Array_t Array$reversed(Array_t array, int64_t padded_item_size); Array_t Array$concat(Array_t x, Array_t y, int64_t padded_item_size); -PUREFUNC uint64_t Array$hash(const Array_t *arr, const TypeInfo_t *type); -PUREFUNC int32_t Array$compare(const Array_t *x, const Array_t *y, const TypeInfo_t *type); -PUREFUNC bool Array$equal(const Array_t *x, const Array_t *y, const TypeInfo_t *type); -Text_t Array$as_text(const Array_t *arr, bool colorize, const TypeInfo_t *type); +PUREFUNC uint64_t Array$hash(const void *arr, const TypeInfo_t *type); +PUREFUNC int32_t Array$compare(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC bool Array$equal(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC bool Array$is_none(const void *obj, const TypeInfo_t*); +Text_t Array$as_text(const void *arr, bool colorize, const TypeInfo_t *type); void Array$heapify(Array_t *heap, Closure_t comparison, int64_t padded_item_size); void Array$heap_push(Array_t *heap, const void *item, Closure_t comparison, int64_t padded_item_size); #define Array$heap_push_value(heap, _value, comparison, padded_item_size) ({ __typeof(_value) value = _value; Array$heap_push(heap, &value, comparison, padded_item_size); }) @@ -100,4 +101,16 @@ Int_t Array$binary_search(Array_t array, void *target, Closure_t comparison); #define Array$binary_search_value(array, target, comparison) \ ({ __typeof(target) _target = target; Array$binary_search(array, &_target, comparison); }) +#define Array$metamethods ((metamethods_t){ \ + .as_text=Array$as_text, \ + .compare=Array$compare, \ + .equal=Array$equal, \ + .hash=Array$hash, \ + .is_none=Array$is_none, \ +}) + +#define Array$info(item_info) &((TypeInfo_t){.size=sizeof(Array_t), .align=__alignof__(Array_t), \ + .tag=ArrayInfo, .ArrayInfo.item=item_info, \ + .metamethods=Array$metamethods}) + // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/bools.c b/stdlib/bools.c index 50cd3f4..5e0ade3 100644 --- a/stdlib/bools.c +++ b/stdlib/bools.c @@ -12,14 +12,13 @@ #include "text.h" #include "util.h" -PUREFUNC public Text_t Bool$as_text(const bool *b, bool colorize, const TypeInfo_t *type) +PUREFUNC public Text_t Bool$as_text(const void *b, bool colorize, const TypeInfo_t*) { - (void)type; if (!b) return Text("Bool"); if (colorize) - return *b ? Text("\x1b[35myes\x1b[m") : Text("\x1b[35mno\x1b[m"); + return *(Bool_t*)b ? Text("\x1b[35myes\x1b[m") : Text("\x1b[35mno\x1b[m"); else - return *b ? Text("yes") : Text("no"); + return *(Bool_t*)b ? Text("yes") : Text("no"); } PUREFUNC public OptionalBool_t Bool$parse(Text_t text) @@ -39,11 +38,20 @@ PUREFUNC public OptionalBool_t Bool$parse(Text_t text) } } +static bool Bool$is_none(const void *b, const TypeInfo_t*) +{ + return *(OptionalBool_t*)b == NONE_BOOL; +} + +static const metamethods_t Bool$metamethods = { + .as_text=Bool$as_text, + .is_none=Bool$is_none, +}; + public const TypeInfo_t Bool$info = { .size=sizeof(bool), .align=__alignof__(bool), - .tag=CustomInfo, - .CustomInfo={.as_text=(void*)Bool$as_text}, + .metamethods=Bool$metamethods, }; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/bools.h b/stdlib/bools.h index 7dec571..c1b344b 100644 --- a/stdlib/bools.h +++ b/stdlib/bools.h @@ -13,7 +13,7 @@ #define yes (Bool_t)true #define no (Bool_t)false -PUREFUNC Text_t Bool$as_text(const bool *b, bool colorize, const TypeInfo_t *type); +PUREFUNC Text_t Bool$as_text(const void *b, bool colorize, const TypeInfo_t *type); OptionalBool_t Bool$parse(Text_t text); extern const TypeInfo_t Bool$info; diff --git a/stdlib/bytes.c b/stdlib/bytes.c index 0119cb4..6c94c05 100644 --- a/stdlib/bytes.c +++ b/stdlib/bytes.c @@ -10,11 +10,10 @@ public const Byte_t Byte$min = 0; public const Byte_t Byte$max = UINT8_MAX; -PUREFUNC public Text_t Byte$as_text(const Byte_t *b, bool colorize, const TypeInfo_t *type) +PUREFUNC public Text_t Byte$as_text(const void *b, bool colorize, const TypeInfo_t*) { - (void)type; if (!b) return Text("Byte"); - return Text$format(colorize ? "\x1b[35m0x%02X\x1b[m" : "0x%02X", *b); + return Text$format(colorize ? "\x1b[35m0x%02X\x1b[m" : "0x%02X", *(Byte_t*)b); } public Text_t Byte$hex(Byte_t byte, bool uppercase, bool prefix) { @@ -33,8 +32,9 @@ public Text_t Byte$hex(Byte_t byte, bool uppercase, bool prefix) { public const TypeInfo_t Byte$info = { .size=sizeof(Byte_t), .align=__alignof__(Byte_t), - .tag=CustomInfo, - .CustomInfo={.as_text=(void*)Byte$as_text}, + .metamethods={ + .as_text=Byte$as_text, + }, }; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/bytes.h b/stdlib/bytes.h index 8c14ef1..757f986 100644 --- a/stdlib/bytes.h +++ b/stdlib/bytes.h @@ -11,7 +11,7 @@ #define Byte_t uint8_t #define Byte(b) ((Byte_t)(b)) -PUREFUNC Text_t Byte$as_text(const Byte_t *b, bool colorize, const TypeInfo_t *type); +PUREFUNC Text_t Byte$as_text(const void *b, bool colorize, const TypeInfo_t *type); #define Byte_to_Int64(b, _) ((Int64_t)(b)) #define Byte_to_Int32(b, _) ((Int32_t)(b)) diff --git a/stdlib/c_strings.c b/stdlib/c_strings.c index 5ac7d4b..99632a5 100644 --- a/stdlib/c_strings.c +++ b/stdlib/c_strings.c @@ -10,11 +10,11 @@ #include "siphash.h" #include "util.h" -public Text_t CString$as_text(const char **c_string, bool colorize, const TypeInfo_t *info) +public Text_t CString$as_text(const void *c_string, bool colorize, const TypeInfo_t *info) { (void)info; if (!c_string) return Text("CString"); - Text_t text = Text$from_str(*c_string); + Text_t text = Text$from_str(*(char**)c_string); return Text$concat(colorize ? Text("\x1b[34mCString\x1b[m(") : Text("CString("), Text$quoted(text, colorize), Text(")")); } @@ -23,33 +23,43 @@ public Text_t CString$as_text_simple(const char *str) return Text$format("%s", str); } -PUREFUNC public int32_t CString$compare(const char **x, const char **y) +PUREFUNC public int32_t CString$compare(const void *x, const void *y, const TypeInfo_t*) { if (x == y) return 0; - if (!*x != !*y) - return (!*y) - (!*x); + if (!*(char**)x != !*(char**)y) + return (!*(char**)y) - (!*(char**)x); - return strcmp(*x, *y); + return strcmp(*(char**)x, *(char**)y); } -PUREFUNC public bool CString$equal(const char **x, const char **y) +PUREFUNC public bool CString$equal(const void *x, const void *y, const TypeInfo_t *type) { - return CString$compare(x, y) == 0; + return CString$compare(x, y, type) == 0; } -PUREFUNC public uint64_t CString$hash(const char **c_str) +PUREFUNC public uint64_t CString$hash(const void *c_str, const TypeInfo_t*) { - if (!*c_str) return 0; - return siphash24((void*)*c_str, strlen(*c_str)); + if (!*(char**)c_str) return 0; + return siphash24(*(void**)c_str, strlen(*(char**)c_str)); +} + +PUREFUNC public bool CString$is_none(const void *c_str, const TypeInfo_t*) +{ + return *(char**)c_str == NULL; } public const TypeInfo_t CString$info = { .size=sizeof(char*), .align=__alignof__(char*), - .tag=CStringInfo, - .CustomInfo={.as_text=(void*)CString$as_text, .compare=(void*)CString$compare, .equal=(void*)CString$equal, .hash=(void*)CString$hash}, + .metamethods={ + .hash=CString$hash, + .compare=CString$compare, + .equal=CString$equal, + .as_text=CString$as_text, + .is_none=CString$is_none, + }, }; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/c_strings.h b/stdlib/c_strings.h index 745333a..7b736a0 100644 --- a/stdlib/c_strings.h +++ b/stdlib/c_strings.h @@ -9,9 +9,9 @@ Text_t CString$as_text(char **str, bool colorize, const TypeInfo_t *info); Text_t CString$as_text_simple(const char *str); -PUREFUNC int CString$compare(const char **x, const char **y); -PUREFUNC bool CString$equal(const char **x, const char **y); -PUREFUNC uint64_t CString$hash(const char **str); +PUREFUNC int CString$compare(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC bool CString$equal(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC uint64_t CString$hash(const void *str, const TypeInfo_t *type); extern const TypeInfo_t CString$info; diff --git a/stdlib/channels.c b/stdlib/channels.c index ac3d697..8ab4e66 100644 --- a/stdlib/channels.c +++ b/stdlib/channels.c @@ -99,25 +99,23 @@ public void Channel$clear(Channel_t *channel) (void)pthread_cond_signal(&channel->cond); } -PUREFUNC public uint64_t Channel$hash(Channel_t **channel, const TypeInfo_t *type) +PUREFUNC public uint64_t Channel$hash(const void *channel, const TypeInfo_t *type) { (void)type; - return siphash24((void*)*channel, sizeof(Channel_t*)); + return siphash24(*(void**)channel, sizeof(Channel_t*)); } -PUREFUNC public int32_t Channel$compare(Channel_t **x, Channel_t **y, const TypeInfo_t *type) +PUREFUNC public int32_t Channel$compare(const void *x, const void *y, const TypeInfo_t*) { - (void)type; - return (*x > *y) - (*x < *y); + return (*(Channel_t**)x > *(Channel_t**)y) - (*(Channel_t**)x < *(Channel_t**)y); } -PUREFUNC public bool Channel$equal(Channel_t **x, Channel_t **y, const TypeInfo_t *type) +PUREFUNC public bool Channel$equal(const void *x, const void *y, const TypeInfo_t*) { - (void)type; - return (*x == *y); + return (*(void**)x == *(void**)y); } -public Text_t Channel$as_text(Channel_t **channel, bool colorize, const TypeInfo_t *type) +public Text_t Channel$as_text(const void *channel, bool colorize, const TypeInfo_t *type) { const TypeInfo_t *item_type = type->ChannelInfo.item; if (!channel) { @@ -129,9 +127,14 @@ public Text_t Channel$as_text(Channel_t **channel, bool colorize, const TypeInfo colorize ? Text("\x1b[34;1m|:") : Text("|:"), typename, Text("|<"), - Int64$hex((int64_t)(void*)*channel, I_small(0), true, true), + Int64$hex(*(int64_t*)channel, I_small(0), true, true), colorize ? Text(">\x1b[m") : Text(">") ); } +public PUREFUNC bool Channel$is_none(const void *obj, const TypeInfo_t*) +{ + return *(void**)obj == NULL; +} + // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/channels.h b/stdlib/channels.h index c960452..9a84810 100644 --- a/stdlib/channels.h +++ b/stdlib/channels.h @@ -20,9 +20,22 @@ void Channel$peek(Channel_t *channel, void *out, bool front, int64_t item_size); #define Channel$peek_value(channel, front, t) ({ t _val; Channel$peek(channel, &_val, front, sizeof(t)); _val; }) void Channel$clear(Channel_t *channel); Array_t Channel$view(Channel_t *channel); -PUREFUNC uint64_t Channel$hash(Channel_t **channel, const TypeInfo_t *type); -PUREFUNC int32_t Channel$compare(Channel_t **x, Channel_t **y, const TypeInfo_t *type); -PUREFUNC bool Channel$equal(Channel_t **x, Channel_t **y, const TypeInfo_t *type); -Text_t Channel$as_text(Channel_t **channel, bool colorize, const TypeInfo_t *type); +PUREFUNC uint64_t Channel$hash(const void *channel, const TypeInfo_t *type); +PUREFUNC int32_t Channel$compare(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC bool Channel$equal(const void *x, const void *y, const TypeInfo_t *type); +Text_t Channel$as_text(const void *channel, bool colorize, const TypeInfo_t *type); +PUREFUNC bool Channel$is_none(const void *obj, const TypeInfo_t*); + +#define Channel$metamethods ((metamethods_t){ \ + .as_text=Channel$as_text, \ + .compare=Channel$compare, \ + .equal=Channel$equal, \ + .hash=Channel$hash, \ + .is_none=Channel$is_none, \ +}) + +#define Channel$info(item_info) &((TypeInfo_t){.size=sizeof(Channel_t), .align=__alignof__(Channel_t), \ + .tag=ChannelInfo, .ChannelInfo.item=item_info, \ + .metamethods=Channel$metamethods}) // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/enums.c b/stdlib/enums.c new file mode 100644 index 0000000..ea2a443 --- /dev/null +++ b/stdlib/enums.c @@ -0,0 +1,92 @@ +// Metamethods for enums + +#include +#include + +#include "arrays.h" +#include "bools.h" +#include "channels.h" +#include "functiontype.h" +#include "metamethods.h" +#include "optionals.h" +#include "pointers.h" +#include "siphash.h" +#include "tables.h" +#include "text.h" +#include "util.h" + +PUREFUNC public uint64_t Enum$hash(const void *obj, const TypeInfo_t *type) +{ + int32_t tag = *(int32_t*)obj; + uint32_t components[2] = {(uint32_t)tag, 0}; + + const TypeInfo_t *value = type->EnumInfo.tags[tag-1].type; + if (value && value->size > 0) { + ptrdiff_t byte_offset = sizeof(int32_t); + if (value->align && byte_offset % value->align > 0) + byte_offset += value->align - (byte_offset % value->align); + components[1] = generic_hash(obj + byte_offset, value); + } + return siphash24((void*)components, sizeof(components)); +} + +PUREFUNC public int32_t Enum$compare(const void *x, const void *y, const TypeInfo_t *type) +{ + if (x == y) return 0; + + int32_t x_tag = *(int32_t*)x; + int32_t y_tag = *(int32_t*)y; + if (x_tag != y_tag) + return x_tag > y_tag ? 1 : -1; + + const TypeInfo_t *value = type->EnumInfo.tags[x_tag-1].type; + if (value && value->size > 0) { + ptrdiff_t byte_offset = sizeof(int32_t); + if (value->align && byte_offset % value->align > 0) + byte_offset += value->align - (byte_offset % value->align); + return generic_compare(x + byte_offset, y + byte_offset, value); + } + return 0; +} + +PUREFUNC public bool Enum$equal(const void *x, const void *y, const TypeInfo_t *type) +{ + if (x == y) return true; + + int32_t x_tag = *(int32_t*)x; + int32_t y_tag = *(int32_t*)y; + if (x_tag != y_tag) + return false; + + const TypeInfo_t *value = type->EnumInfo.tags[x_tag-1].type; + if (value && value->size > 0) { + ptrdiff_t byte_offset = sizeof(int32_t); + if (value->align && byte_offset % value->align > 0) + byte_offset += value->align - (byte_offset % value->align); + return generic_equal(x + byte_offset, y + byte_offset, value); + } + return true; +} + +public Text_t Enum$as_text(const void *obj, bool colorize, const TypeInfo_t *type) +{ + if (!obj) return Text$from_str(type->EnumInfo.name); + + int32_t tag = *(int32_t*)obj; + NamedType_t value = type->EnumInfo.tags[tag-1]; + if (!value.type || value.type->size == 0) + return Text$format(colorize ? "\x1b[36;1m%s\x1b[m.\x1b[1m%s\x1b[m" : "%s.%s", type->EnumInfo.name, value.name); + + ptrdiff_t byte_offset = sizeof(int32_t); + if (value.type->align && byte_offset % value.type->align > 0) + byte_offset += value.type->align - (byte_offset % value.type->align); + return Text$concat(Text$format(colorize ? "\x1b[36;1m%s\x1b[m." : "%s.", type->EnumInfo.name), + generic_as_text(obj + byte_offset, colorize, value.type)); +} + +PUREFUNC public bool Enum$is_none(const void *x, const TypeInfo_t*) +{ + return *(int32_t*)x == 0; +} + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/enums.h b/stdlib/enums.h new file mode 100644 index 0000000..8afa5c9 --- /dev/null +++ b/stdlib/enums.h @@ -0,0 +1,24 @@ +// Metamethods for enums + +#include +#include + +#include "datatypes.h" +#include "types.h" +#include "util.h" + +PUREFUNC uint64_t Enum$hash(const void *obj, const TypeInfo_t *type); +PUREFUNC int32_t Enum$compare(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC bool Enum$equal(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC Text_t Enum$as_text(const void *obj, bool colorize, const TypeInfo_t *type); +PUREFUNC bool Enum$is_none(const void *obj, const TypeInfo_t *type); + +#define Enum$metamethods ((metamethods_t){ \ + .as_text=Enum$as_text, \ + .compare=Enum$compare, \ + .equal=Enum$equal, \ + .hash=Enum$hash, \ + .is_none=Enum$is_none, \ +}) + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/functiontype.c b/stdlib/functiontype.c index ce07c06..b62ef2a 100644 --- a/stdlib/functiontype.c +++ b/stdlib/functiontype.c @@ -1,6 +1,9 @@ // Logic for handling function type values +#include + #include "datatypes.h" +#include "functiontype.h" #include "tables.h" #include "text.h" #include "types.h" @@ -32,4 +35,9 @@ public Text_t Func$as_text(const void *fn, bool colorize, const TypeInfo_t *type return text; } +public PUREFUNC bool Func$is_none(const void *obj, const TypeInfo_t*) +{ + return *(void**)obj == NULL; +} + // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/functiontype.h b/stdlib/functiontype.h index 8fdabb8..7745bff 100644 --- a/stdlib/functiontype.h +++ b/stdlib/functiontype.h @@ -1,9 +1,27 @@ #pragma once +#include + +#include "types.h" +#include "util.h" + // Logic for handling function type values void register_function(void *fn, Text_t name); Text_t *get_function_name(void *fn); Text_t Func$as_text(const void *fn, bool colorize, const TypeInfo_t *type); +PUREFUNC bool Func$is_none(const void *obj, const TypeInfo_t*); + +#define Func$metamethods ((metamethods_t){ \ + .as_text=Func$as_text, \ + .is_none=Func$is_none, \ +}) + +#define Function$info(typestr) &((TypeInfo_t){.size=sizeof(void*), .align=__alignof__(void*), \ + .tag=FunctionInfo, .FunctionInfo.type_str=typestr, \ + .metamethods=Func$metamethods}) +#define Closure$info(typestr) &((TypeInfo_t){.size=sizeof(void*[2]), .align=__alignof__(void*), \ + .tag=FunctionInfo, .FunctionInfo.type_str=typestr, \ + .metamethods=Func$metamethods}) // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/integers.c b/stdlib/integers.c index baf1b0c..6c00527 100644 --- a/stdlib/integers.c +++ b/stdlib/integers.c @@ -23,17 +23,16 @@ public Text_t Int$value_as_text(Int_t i) { } } -public Text_t Int$as_text(const Int_t *i, bool colorize, const TypeInfo_t *type) { - (void)type; +public Text_t Int$as_text(const void *i, bool colorize, const TypeInfo_t*) { if (!i) return Text("Int"); - - Text_t text = Int$value_as_text(*i); + Text_t text = Int$value_as_text(*(Int_t*)i); if (colorize) text = Text$concat(Text("\x1b[35m"), text, Text("\x1b[m")); return text; } -public PUREFUNC int32_t Int$compare(const Int_t *x, const Int_t *y, const TypeInfo_t *type) { - (void)type; +public PUREFUNC int32_t Int$compare(const void *vx, const void *vy, const TypeInfo_t*) { + Int_t *x = (Int_t*)vx; + Int_t *y = (Int_t*)vy; if (__builtin_expect(((x->small | y->small) & 1) == 0, 0)) return x->big == y->big ? 0 : mpz_cmp(*x->big, *y->big); return (x->small > y->small) - (x->small < y->small); @@ -45,8 +44,9 @@ public PUREFUNC int32_t Int$compare_value(const Int_t x, const Int_t y) { return (x.small > y.small) - (x.small < y.small); } -public PUREFUNC bool Int$equal(const Int_t *x, const Int_t *y, const TypeInfo_t *type) { - (void)type; +public PUREFUNC bool Int$equal(const void *vx, const void *vy, const TypeInfo_t*) { + Int_t *x = (Int_t*)vx; + Int_t *y = (Int_t*)vy; return x->small == y->small || (__builtin_expect(((x->small | y->small) & 1) == 0, 0) && mpz_cmp(*x->big, *y->big) == 0); } @@ -54,8 +54,8 @@ public PUREFUNC bool Int$equal_value(const Int_t x, const Int_t y) { return x.small == y.small || (__builtin_expect(((x.small | y.small) & 1) == 0, 0) && mpz_cmp(*x.big, *y.big) == 0); } -public PUREFUNC uint64_t Int$hash(const Int_t *x, const TypeInfo_t *type) { - (void)type; +public PUREFUNC uint64_t Int$hash(const void *vx, const TypeInfo_t*) { + Int_t *x = (Int_t*)vx; if (__builtin_expect(x->small & 1, 1)) { return siphash24((void*)x, sizeof(Int_t)); } else { @@ -362,31 +362,33 @@ public Int_t Int$prev_prime(Int_t x) return Int$from_mpz(p); } +static bool Int$is_none(const void *i, const TypeInfo_t*) +{ + return ((Int_t*)i)->small == 0; +} + public const TypeInfo_t Int$info = { .size=sizeof(Int_t), .align=__alignof__(Int_t), - .tag=CustomInfo, - .CustomInfo={ - .compare=(void*)Int$compare, - .equal=(void*)Int$equal, - .hash=(void*)Int$hash, - .as_text=(void*)Int$as_text, + .metamethods={ + .compare=Int$compare, + .equal=Int$equal, + .hash=Int$hash, + .as_text=Int$as_text, + .is_none=Int$is_none, }, }; #define DEFINE_INT_TYPE(c_type, KindOfInt, fmt, min_val, max_val, to_attr)\ - public Text_t KindOfInt ## $as_text(const c_type *i, bool colorize, const TypeInfo_t *type) { \ - (void)type; \ + public Text_t KindOfInt ## $as_text(const void *i, bool colorize, const TypeInfo_t*) { \ if (!i) return Text(#KindOfInt); \ - return Text$format(colorize ? "\x1b[35m" fmt "\x1b[m" : fmt, *i); \ + return Text$format(colorize ? "\x1b[35m" fmt "\x1b[m" : fmt, *(c_type*)i); \ } \ - public PUREFUNC int32_t KindOfInt ## $compare(const c_type *x, const c_type *y, const TypeInfo_t *type) { \ - (void)type; \ - return (*x > *y) - (*x < *y); \ + public PUREFUNC int32_t KindOfInt ## $compare(const void *x, const void *y, const TypeInfo_t*) { \ + return (*(c_type*)x > *(c_type*)y) - (*(c_type*)x < *(c_type*)y); \ } \ - public PUREFUNC bool KindOfInt ## $equal(const c_type *x, const c_type *y, const TypeInfo_t *type) { \ - (void)type; \ - return *x == *y; \ + public PUREFUNC bool KindOfInt ## $equal(const void *x, const void *y, const TypeInfo_t*) { \ + return *(c_type*)x == *(c_type*)y; \ } \ public Text_t KindOfInt ## $format(c_type i, Int_t digits_int) { \ Int_t as_int = KindOfInt##_to_Int(i); \ @@ -428,8 +430,10 @@ public const TypeInfo_t Int$info = { public const TypeInfo_t KindOfInt##$info = { \ .size=sizeof(c_type), \ .align=__alignof__(c_type), \ - .tag=CustomInfo, \ - .CustomInfo={.compare=(void*)KindOfInt##$compare, .as_text=(void*)KindOfInt##$as_text}, \ + .metamethods={ \ + .compare=KindOfInt##$compare, \ + .as_text=KindOfInt##$as_text, \ + }, \ }; DEFINE_INT_TYPE(int64_t, Int64, "%ld", INT64_MIN, INT64_MAX, __attribute__(())) diff --git a/stdlib/integers.h b/stdlib/integers.h index 12cb90f..a5016d4 100644 --- a/stdlib/integers.h +++ b/stdlib/integers.h @@ -27,9 +27,9 @@ c_type i; \ bool is_null:1; \ } Optional ## type_name ## _t; \ - Text_t type_name ## $as_text(const c_type *i, bool colorize, const TypeInfo_t *type); \ - PUREFUNC int32_t type_name ## $compare(const c_type *x, const c_type *y, const TypeInfo_t *type); \ - PUREFUNC bool type_name ## $equal(const c_type *x, const c_type *y, const TypeInfo_t *type); \ + Text_t type_name ## $as_text(const void *i, bool colorize, const TypeInfo_t *type); \ + PUREFUNC int32_t type_name ## $compare(const void *x, const void *y, const TypeInfo_t *type); \ + PUREFUNC bool type_name ## $equal(const void *x, const void *y, const TypeInfo_t *type); \ Text_t type_name ## $format(c_type i, Int_t digits); \ Text_t type_name ## $hex(c_type i, Int_t digits, bool uppercase, bool prefix); \ Text_t type_name ## $octal(c_type i, Int_t digits, bool prefix); \ @@ -85,12 +85,12 @@ DEFINE_INT_TYPE(int8_t, Int8, CONSTFUNC) #define OptionalInt_t Int_t -Text_t Int$as_text(const Int_t *i, bool colorize, const TypeInfo_t *type); +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 Int_t *x, const TypeInfo_t *type); -PUREFUNC int32_t Int$compare(const Int_t *x, const Int_t *y, const TypeInfo_t *type); +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); -PUREFUNC bool Int$equal(const Int_t *x, const Int_t *y, const TypeInfo_t *type); +PUREFUNC bool Int$equal(const void *x, const void *y, const TypeInfo_t *type); PUREFUNC bool Int$equal_value(const Int_t x, const Int_t y); Text_t Int$format(Int_t i, Int_t digits); Text_t Int$hex(Int_t i, Int_t digits, bool uppercase, bool prefix); diff --git a/stdlib/memory.c b/stdlib/memory.c index 17b6172..70ca574 100644 --- a/stdlib/memory.c +++ b/stdlib/memory.c @@ -12,8 +12,7 @@ #include "types.h" #include "util.h" -public Text_t Memory__as_text(const void *p, bool colorize, const TypeInfo_t *type) { - (void)type; +public Text_t Memory$as_text(const void *p, bool colorize, const TypeInfo_t *) { if (!p) return Text("Memory"); return Text$format(colorize ? "\x1b[0;34;1mMemory<%p>\x1b[m" : "Memory<%p>", p); } @@ -21,8 +20,9 @@ public Text_t Memory__as_text(const void *p, bool colorize, const TypeInfo_t *ty public const TypeInfo_t Memory$info = { .size=0, .align=0, - .tag=CustomInfo, - .CustomInfo={.as_text=(void*)Memory__as_text}, + .metamethods={ + .as_text=Memory$as_text, + }, }; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/metamethods.c b/stdlib/metamethods.c index 6c8583f..28d9773 100644 --- a/stdlib/metamethods.c +++ b/stdlib/metamethods.c @@ -15,247 +15,40 @@ #include "text.h" #include "util.h" - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstack-protector" PUREFUNC public uint64_t generic_hash(const void *obj, const TypeInfo_t *type) { - switch (type->tag) { - case TextInfo: return Text$hash((void*)obj); - case ArrayInfo: return Array$hash(obj, type); - case ChannelInfo: return Channel$hash((Channel_t**)obj, type); - case TableInfo: return Table$hash(obj, type); - case OptionalInfo: return is_null(obj, type->OptionalInfo.type) ? 0 : generic_hash(obj, type->OptionalInfo.type); - case StructInfo: { - if (type->StructInfo.num_fields == 0) { - return 0; - } else if (type->StructInfo.num_fields == 1) { - return generic_hash(obj, type->StructInfo.fields[0].type); - } else { - uint32_t field_hashes[type->StructInfo.num_fields] = {}; - ptrdiff_t byte_offset = 0; - ptrdiff_t bit_offset = 0; - for (int i = 0; i < type->StructInfo.num_fields; i++) { - NamedType_t field = type->StructInfo.fields[i]; - if (field.type == &Bool$info) { - bool b = ((*(char*)(obj + byte_offset)) >> bit_offset) & 0x1; - field_hashes[i] = (uint32_t)b; - bit_offset += 1; - if (bit_offset >= 8) { - byte_offset += 1; - bit_offset = 0; - } - } else { - if (bit_offset > 0) { - byte_offset += 1; - bit_offset = 0; - } - if (field.type->align && byte_offset % field.type->align > 0) - byte_offset += field.type->align - (byte_offset % field.type->align); - field_hashes[i] = generic_hash(obj + byte_offset, field.type); - byte_offset += field.type->size; - } - } - return siphash24((void*)field_hashes, sizeof(field_hashes)); - } - } - case EnumInfo: { - int32_t tag = *(int32_t*)obj; - uint32_t components[2] = {(uint32_t)tag, 0}; + if (type->metamethods.hash) + return type->metamethods.hash(obj, type); - const TypeInfo_t *value = type->EnumInfo.tags[tag-1].type; - if (value && value->size > 0) { - ptrdiff_t byte_offset = sizeof(int32_t); - if (value->align && byte_offset % value->align > 0) - byte_offset += value->align - (byte_offset % value->align); - components[1] = generic_hash(obj + byte_offset, value); - } - return siphash24((void*)components, sizeof(components)); - - } - case CustomInfo: case CStringInfo: // These all share the same info - if (!type->CustomInfo.hash) - goto hash_data; - return type->CustomInfo.hash(obj, type); - case PointerInfo: case FunctionInfo: case TypeInfoInfo: case OpaqueInfo: default: { - hash_data:; - return siphash24((void*)obj, (size_t)(type->size)); - } - } + return siphash24((void*)obj, (size_t)(type->size)); } -#pragma GCC diagnostic pop PUREFUNC public int32_t generic_compare(const void *x, const void *y, const TypeInfo_t *type) { if (x == y) return 0; - switch (type->tag) { - case PointerInfo: case FunctionInfo: return Pointer$compare(x, y, type); - case TextInfo: return Text$compare(x, y); - case ArrayInfo: return Array$compare(x, y, type); - case ChannelInfo: return Channel$compare((Channel_t**)x, (Channel_t**)y, type); - case TableInfo: return Table$compare(x, y, type); - case OptionalInfo: { - bool x_is_null = is_null(x, type->OptionalInfo.type); - bool y_is_null = is_null(y, type->OptionalInfo.type); - if (x_is_null && y_is_null) return 0; - else if (x_is_null != y_is_null) return (int32_t)y_is_null - (int32_t)x_is_null; - else return generic_compare(x, y, type->OptionalInfo.type); - } - case StructInfo: { - ptrdiff_t byte_offset = 0; - ptrdiff_t bit_offset = 0; - for (int i = 0; i < type->StructInfo.num_fields; i++) { - NamedType_t field = type->StructInfo.fields[i]; - if (field.type == &Bool$info) { - bool bx = ((*(char*)(x + byte_offset)) >> bit_offset) & 0x1; - bool by = ((*(char*)(y + byte_offset)) >> bit_offset) & 0x1; - if (bx != by) - return (int32_t)bx - (int32_t)by; - bit_offset += 1; - if (bit_offset >= 8) { - byte_offset += 1; - bit_offset = 0; - } - } else { - if (bit_offset > 0) { - byte_offset += 1; - bit_offset = 0; - } - if (field.type->align && byte_offset % field.type->align > 0) - byte_offset += field.type->align - (byte_offset % field.type->align); - int32_t cmp = generic_compare(x + byte_offset, y + byte_offset, field.type); - if (cmp != 0) - return cmp; - byte_offset += field.type->size; - } - } - return 0; - } - case EnumInfo: { - int32_t x_tag = *(int32_t*)x; - int32_t y_tag = *(int32_t*)y; - if (x_tag != y_tag) - return x_tag > y_tag ? 1 : -1; + if (type->metamethods.compare) + return type->metamethods.compare(x, y, type); - const TypeInfo_t *value = type->EnumInfo.tags[x_tag-1].type; - if (value && value->size > 0) { - ptrdiff_t byte_offset = sizeof(int32_t); - if (value->align && byte_offset % value->align > 0) - byte_offset += value->align - (byte_offset % value->align); - return generic_compare(x + byte_offset, y + byte_offset, value); - } - return 0; - } - case CustomInfo: case CStringInfo: // These all share the same info - if (!type->CustomInfo.compare) - goto compare_data; - return type->CustomInfo.compare(x, y, type); - case TypeInfoInfo: case OpaqueInfo: default: - compare_data: - return (int32_t)memcmp((void*)x, (void*)y, (size_t)(type->size)); - } + return (int32_t)memcmp((void*)x, (void*)y, (size_t)(type->size)); } PUREFUNC public bool generic_equal(const void *x, const void *y, const TypeInfo_t *type) { if (x == y) return true; - switch (type->tag) { - case PointerInfo: case FunctionInfo: return Pointer$equal(x, y, type); - case TextInfo: return Text$equal(x, y); - case ArrayInfo: return Array$equal(x, y, type); - case ChannelInfo: return Channel$equal((Channel_t**)x, (Channel_t**)y, type); - case TableInfo: return Table$equal(x, y, type); - case OptionalInfo: { - bool x_is_null = is_null(x, type->OptionalInfo.type); - bool y_is_null = is_null(y, type->OptionalInfo.type); - if (x_is_null && y_is_null) return true; - else if (x_is_null != y_is_null) return false; - else return generic_equal(x, y, type->OptionalInfo.type); - } - case StructInfo: case EnumInfo: - return (generic_compare(x, y, type) == 0); - case CustomInfo: case CStringInfo: // These all share the same info - if (!type->CustomInfo.equal) - goto use_generic_compare; - return type->CustomInfo.equal(x, y, type); - case TypeInfoInfo: case OpaqueInfo: default: - use_generic_compare: - return (generic_compare(x, y, type) == 0); - } + if (type->metamethods.equal) + return type->metamethods.equal(x, y, type); + + return (generic_compare(x, y, type) == 0); } public Text_t generic_as_text(const void *obj, bool colorize, const TypeInfo_t *type) { - switch (type->tag) { - case PointerInfo: return Pointer$as_text(obj, colorize, type); - case FunctionInfo: return Func$as_text(obj, colorize, type); - case TextInfo: return Text$as_text(obj, colorize, type); - case ArrayInfo: return Array$as_text(obj, colorize, type); - case ChannelInfo: return Channel$as_text((Channel_t**)obj, colorize, type); - case TableInfo: return Table$as_text(obj, colorize, type); - case TypeInfoInfo: return Type$as_text(obj, colorize, type); - case OptionalInfo: return Optional$as_text(obj, colorize, type); - case StructInfo: { - if (!obj) return Text$from_str(type->StructInfo.name); + if (!type->metamethods.as_text) + fail("No text metamethod provided for type!"); - if (type->StructInfo.is_secret) - return Text$format(colorize ? "\x1b[0;1m%s\x1b[m(...)" : "%s(...)", type->StructInfo.name); - - Text_t text = Text$format(colorize ? "\x1b[0;1m%s\x1b[m(" : "%s(", type->StructInfo.name); - ptrdiff_t byte_offset = 0; - ptrdiff_t bit_offset = 0; - for (int i = 0; i < type->StructInfo.num_fields; i++) { - NamedType_t field = type->StructInfo.fields[i]; - if (i > 0) - text = Text$concat(text, Text(", ")); - - if (type->StructInfo.num_fields > 1) - text = Text$concat(text, Text$from_str(field.name), Text("=")); - - if (field.type == &Bool$info) { - bool b = ((*(char*)(obj + byte_offset)) >> bit_offset) & 0x1; - text = Text$concat(text, Text$from_str(colorize ? (b ? "\x1b[35myes\x1b[m" : "\x1b[35mno\x1b[m") : (b ? "yes" : "no"))); - bit_offset += 1; - if (bit_offset >= 8) { - byte_offset += 1; - bit_offset = 0; - } - } else { - if (bit_offset > 0) { - byte_offset += 1; - bit_offset = 0; - } - if (field.type->align && byte_offset % field.type->align > 0) - byte_offset += field.type->align - (byte_offset % field.type->align); - text = Text$concat(text, generic_as_text(obj + byte_offset, colorize, field.type)); - byte_offset += field.type->size; - } - } - return Text$concat(text, Text(")")); - } - case EnumInfo: { - if (!obj) return Text$from_str(type->EnumInfo.name); - - int32_t tag = *(int32_t*)obj; - NamedType_t value = type->EnumInfo.tags[tag-1]; - if (!value.type || value.type->size == 0) - return Text$format(colorize ? "\x1b[36;1m%s\x1b[m.\x1b[1m%s\x1b[m" : "%s.%s", type->EnumInfo.name, value.name); - - ptrdiff_t byte_offset = sizeof(int32_t); - if (value.type->align && byte_offset % value.type->align > 0) - byte_offset += value.type->align - (byte_offset % value.type->align); - return Text$concat(Text$format(colorize ? "\x1b[36;1m%s\x1b[m." : "%s.", type->EnumInfo.name), - generic_as_text(obj + byte_offset, colorize, value.type)); - } - case CustomInfo: case CStringInfo: // These all share the same info - if (!type->CustomInfo.as_text) - fail("No text function provided for type!\n"); - return type->CustomInfo.as_text(obj, colorize, type); - case OpaqueInfo: return Text("???"); - default: fail("Invalid type tag: %d", type->tag); - } + return type->metamethods.as_text(obj, colorize, type); } public int generic_print(const void *obj, bool colorize, const TypeInfo_t *type) @@ -264,5 +57,4 @@ public int generic_print(const void *obj, bool colorize, const TypeInfo_t *type) return Text$print(stdout, text) + printf("\n"); } - // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/moments.c b/stdlib/moments.c index 848626c..ba72ff7 100644 --- a/stdlib/moments.c +++ b/stdlib/moments.c @@ -25,14 +25,13 @@ static OptionalText_t _local_timezone = NONE_TEXT; body; \ }}) -public Text_t Moment$as_text(const Moment_t *moment, bool colorize, const TypeInfo_t *type) +public Text_t Moment$as_text(const void *moment, bool colorize, const TypeInfo_t*) { - (void)type; if (!moment) return Text("Moment"); struct tm info; - struct tm *final_info = localtime_r(&moment->tv_sec, &info); + struct tm *final_info = localtime_r(&((Moment_t*)moment)->tv_sec, &info); static char buf[256]; size_t len = strftime(buf, sizeof(buf), "%c %Z", final_info); Text_t text = Text$format("%.*s", (int)len, buf); @@ -41,9 +40,9 @@ public Text_t Moment$as_text(const Moment_t *moment, bool colorize, const TypeIn return text; } -PUREFUNC public int32_t Moment$compare(const Moment_t *a, const Moment_t *b, const TypeInfo_t *type) +PUREFUNC public int32_t Moment$compare(const void *va, const void *vb, const TypeInfo_t*) { - (void)type; + Moment_t *a = (Moment_t*)va, *b = (Moment_t*)vb; if (a->tv_sec != b->tv_sec) return (a->tv_sec > b->tv_sec) - (a->tv_sec < b->tv_sec); return (a->tv_usec > b->tv_usec) - (a->tv_usec < b->tv_usec); @@ -307,10 +306,9 @@ public Text_t Moment$get_local_timezone(void) public const TypeInfo_t Moment$info = { .size=sizeof(Moment_t), .align=__alignof__(Moment_t), - .tag=CustomInfo, - .CustomInfo={ - .as_text=(void*)Moment$as_text, - .compare=(void*)Moment$compare, + .metamethods={ + .as_text=Moment$as_text, + .compare=Moment$compare, }, }; diff --git a/stdlib/moments.h b/stdlib/moments.h index 8e42312..48899b8 100644 --- a/stdlib/moments.h +++ b/stdlib/moments.h @@ -10,8 +10,8 @@ #include "types.h" #include "util.h" -Text_t Moment$as_text(const Moment_t *moment, bool colorize, const TypeInfo_t *type); -PUREFUNC int32_t Moment$compare(const Moment_t *a, const Moment_t *b, const TypeInfo_t *type); +Text_t Moment$as_text(const void *moment, bool colorize, const TypeInfo_t *type); +PUREFUNC int32_t Moment$compare(const void *a, const void *b, const TypeInfo_t *type); Moment_t Moment$now(void); Moment_t Moment$new(Int_t year, Int_t month, Int_t day, Int_t hour, Int_t minute, double second, OptionalText_t timezone); Moment_t Moment$after(Moment_t moment, double seconds, double minutes, double hours, Int_t days, Int_t weeks, Int_t months, Int_t years, OptionalText_t timezone); diff --git a/stdlib/nums.c b/stdlib/nums.c index 4c9be22..47eb685 100644 --- a/stdlib/nums.c +++ b/stdlib/nums.c @@ -13,13 +13,12 @@ #include "text.h" #include "types.h" -public PUREFUNC Text_t Num$as_text(const double *f, bool colorize, const TypeInfo_t *type) { - (void)type; +public PUREFUNC Text_t Num$as_text(const void *f, bool colorize, const TypeInfo_t*) { if (!f) return Text("Num"); - return Text$format(colorize ? "\x1b[35m%.16g\x1b[33;2m\x1b[m" : "%.16g", *f); + return Text$format(colorize ? "\x1b[35m%.16g\x1b[33;2m\x1b[m" : "%.16g", *(double*)f); } -public PUREFUNC int32_t Num$compare(const double *x, const double *y, const TypeInfo_t *) { +public PUREFUNC int32_t Num$compare(const void *x, const void *y, const TypeInfo_t*) { int64_t rx = *(int64_t*)x, ry = *(int64_t*)y; @@ -31,9 +30,8 @@ public PUREFUNC int32_t Num$compare(const double *x, const double *y, const Type return (rx > ry) - (rx < ry); } -public PUREFUNC bool Num$equal(const double *x, const double *y, const TypeInfo_t *type) { - (void)type; - return *x == *y; +public PUREFUNC bool Num$equal(const void *x, const void *y, const TypeInfo_t*) { + return *(double*)x == *(double*)y; } public CONSTFUNC bool Num$near(double a, double b, double ratio, double absolute) { @@ -78,6 +76,11 @@ public OptionalNum_t Num$parse(Text_t text) { return nan("null"); } +static bool Num$is_none(const void *n, const TypeInfo_t*) +{ + return isnan(*(Num_t*)n); +} + public CONSTFUNC bool Num$isinf(double n) { return (fpclassify(n) == FP_INFINITE); } public CONSTFUNC bool Num$finite(double n) { return (fpclassify(n) != FP_INFINITE); } public CONSTFUNC bool Num$isnan(double n) { return (fpclassify(n) == FP_NAN); } @@ -85,28 +88,25 @@ public CONSTFUNC bool Num$isnan(double n) { return (fpclassify(n) == FP_NAN); } public const TypeInfo_t Num$info = { .size=sizeof(double), .align=__alignof__(double), - .tag=CustomInfo, - .CustomInfo={ - .compare=(void*)Num$compare, - .equal=(void*)Num$equal, - .as_text=(void*)Num$as_text, + .metamethods={ + .compare=Num$compare, + .equal=Num$equal, + .as_text=Num$as_text, + .is_none=Num$is_none, }, }; -public PUREFUNC Text_t Num32$as_text(const float *f, bool colorize, const TypeInfo_t *type) { - (void)type; +public PUREFUNC Text_t Num32$as_text(const void *f, bool colorize, const TypeInfo_t*) { if (!f) return Text("Num32"); - return Text$format(colorize ? "\x1b[35m%.8g\x1b[33;2m\x1b[m" : "%.8g", (double)*f); + return Text$format(colorize ? "\x1b[35m%.8g\x1b[33;2m\x1b[m" : "%.8g", (double)*(float*)f); } -public PUREFUNC int32_t Num32$compare(const float *x, const float *y, const TypeInfo_t *type) { - (void)type; - return (*x > *y) - (*x < *y); +public PUREFUNC int32_t Num32$compare(const void *x, const void *y, const TypeInfo_t*) { + return (*(float*)x > *(float*)y) - (*(float*)x < *(float*)y); } -public PUREFUNC bool Num32$equal(const float *x, const float *y, const TypeInfo_t *type) { - (void)type; - return *x == *y; +public PUREFUNC bool Num32$equal(const void *x, const void *y, const TypeInfo_t*) { + return *(float*)x == *(float*)y; } public CONSTFUNC bool Num32$near(float a, float b, float ratio, float absolute) { @@ -151,6 +151,11 @@ public OptionalNum32_t Num32$parse(Text_t text) { return nan("null"); } +static bool Num32$is_none(const void *n, const TypeInfo_t*) +{ + return isnan(*(Num32_t*)n); +} + public CONSTFUNC bool Num32$isinf(float n) { return (fpclassify(n) == FP_INFINITE); } public CONSTFUNC bool Num32$finite(float n) { return (fpclassify(n) != FP_INFINITE); } public CONSTFUNC bool Num32$isnan(float n) { return (fpclassify(n) == FP_NAN); } @@ -158,11 +163,11 @@ public CONSTFUNC bool Num32$isnan(float n) { return (fpclassify(n) == FP_NAN); } public const TypeInfo_t Num32$info = { .size=sizeof(float), .align=__alignof__(float), - .tag=CustomInfo, - .CustomInfo={ - .compare=(void*)Num32$compare, - .equal=(void*)Num32$equal, - .as_text=(void*)Num32$as_text, + .metamethods={ + .compare=Num32$compare, + .equal=Num32$equal, + .as_text=Num32$as_text, + .is_none=Num32$is_none, }, }; diff --git a/stdlib/nums.h b/stdlib/nums.h index dc7f9ff..f000ca8 100644 --- a/stdlib/nums.h +++ b/stdlib/nums.h @@ -16,9 +16,9 @@ #define N32(n) ((float)n) #define N64(n) ((double)n) -Text_t Num$as_text(const double *f, bool colorize, const TypeInfo_t *type); -PUREFUNC int32_t Num$compare(const double *x, const double *y, const TypeInfo_t *type); -PUREFUNC bool Num$equal(const double *x, const double *y, const TypeInfo_t *type); +Text_t Num$as_text(const void *f, bool colorize, const TypeInfo_t *type); +PUREFUNC int32_t Num$compare(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC bool Num$equal(const void *x, const void *y, const TypeInfo_t *type); CONSTFUNC bool Num$near(double a, double b, double ratio, double absolute); Text_t Num$format(double f, Int_t precision); Text_t Num$scientific(double f, Int_t precision); @@ -34,9 +34,9 @@ MACROLIKE CONSTFUNC double Num$clamped(double x, double low, double high) { } extern const TypeInfo_t Num$info; -Text_t Num32$as_text(const float *f, bool colorize, const TypeInfo_t *type); -PUREFUNC int32_t Num32$compare(const float *x, const float *y, const TypeInfo_t *type); -PUREFUNC bool Num32$equal(const float *x, const float *y, const TypeInfo_t *type); +Text_t Num32$as_text(const void *f, bool colorize, const TypeInfo_t *type); +PUREFUNC int32_t Num32$compare(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC bool Num32$equal(const void *x, const void *y, const TypeInfo_t *type); CONSTFUNC bool Num32$near(float a, float b, float ratio, float absolute); Text_t Num32$format(float f, Int_t precision); Text_t Num32$scientific(float f, Int_t precision); diff --git a/stdlib/optionals.c b/stdlib/optionals.c index 2fb734f..810f617 100644 --- a/stdlib/optionals.c +++ b/stdlib/optionals.c @@ -15,55 +15,36 @@ public PUREFUNC bool is_null(const void *obj, const TypeInfo_t *non_optional_type) { - if (non_optional_type == &Int$info) - return ((Int_t*)obj)->small == 0; - else if (non_optional_type == &Bool$info) - return *((OptionalBool_t*)obj) == NONE_BOOL; - else if (non_optional_type == &Num$info) - return isnan(*((Num_t*)obj)); - else if (non_optional_type == &Num32$info) - return isnan(*((Num32_t*)obj)); - else if (non_optional_type == &Int64$info) - return ((OptionalInt64_t*)obj)->is_null; - else if (non_optional_type == &Int32$info) - return ((OptionalInt32_t*)obj)->is_null; - else if (non_optional_type == &Int16$info) - return ((OptionalInt16_t*)obj)->is_null; - else if (non_optional_type == &Int8$info) - return ((OptionalInt8_t*)obj)->is_null; - else if (non_optional_type == &Byte$info) - return ((OptionalByte_t*)obj)->is_null; - else if (non_optional_type == &Thread$info) - return *(pthread_t**)obj == NULL; - else if (non_optional_type == &Moment$info) - return ((OptionalMoment_t*)obj)->tv_usec < 0; - else if (non_optional_type == &Match$info) - return ((OptionalMatch_t*)obj)->index.small == 0; + if (non_optional_type->metamethods.is_none) + return non_optional_type->metamethods.is_none(obj, non_optional_type); - switch (non_optional_type->tag) { - case ChannelInfo: return *(Channel_t**)obj == NULL; - case PointerInfo: return *(void**)obj == NULL; - case TextInfo: return ((Text_t*)obj)->length < 0; - case ArrayInfo: return ((Array_t*)obj)->length < 0; - case TableInfo: return ((Table_t*)obj)->entries.length < 0; - case FunctionInfo: return *(void**)obj == NULL; - case StructInfo: { - int64_t offset = 0; - for (int i = 0; i < non_optional_type->StructInfo.num_fields; i++) { - NamedType_t field = non_optional_type->StructInfo.fields[i]; - if (offset > 0 && (offset % field.type->align) > 0) - offset += field.type->align - (offset % field.type->align); - offset += field.type->size; - } - return *(bool*)(obj + offset); - } - case EnumInfo: return (*(int*)obj) == 0; // NULL tag - case CStringInfo: return (*(char**)obj) == NULL; - default: { - Text_t t = generic_as_text(NULL, false, non_optional_type); - errx(1, "is_null() not implemented for: %k", &t); - } - } + return *(bool*)(obj + non_optional_type->size); +} + +PUREFUNC public uint64_t Optional$hash(const void *obj, const TypeInfo_t *type) +{ + return is_null(obj, type->OptionalInfo.type) ? 0 : generic_hash(obj, type->OptionalInfo.type); +} + +PUREFUNC public int32_t Optional$compare(const void *x, const void *y, const TypeInfo_t *type) +{ + if (x == y) return 0; + bool x_is_null = is_null(x, type->OptionalInfo.type); + bool y_is_null = is_null(y, type->OptionalInfo.type); + if (x_is_null && y_is_null) return 0; + else if (x_is_null != y_is_null) return (int32_t)y_is_null - (int32_t)x_is_null; + else return generic_compare(x, y, type->OptionalInfo.type); +} + +PUREFUNC public bool Optional$equal(const void *x, const void *y, const TypeInfo_t *type) +{ + if (x == y) return true; + + bool x_is_null = is_null(x, type->OptionalInfo.type); + bool y_is_null = is_null(y, type->OptionalInfo.type); + if (x_is_null && y_is_null) return true; + else if (x_is_null != y_is_null) return false; + else return generic_equal(x, y, type->OptionalInfo.type); } public Text_t Optional$as_text(const void *obj, bool colorize, const TypeInfo_t *type) diff --git a/stdlib/optionals.h b/stdlib/optionals.h index 7d52375..53f2aa9 100644 --- a/stdlib/optionals.h +++ b/stdlib/optionals.h @@ -25,6 +25,20 @@ #define NONE_MOMENT ((OptionalMoment_t){.tv_usec=-1}) PUREFUNC bool is_null(const void *obj, const TypeInfo_t *non_optional_type); +PUREFUNC uint64_t Optional$hash(const void *obj, const TypeInfo_t *type); +PUREFUNC int32_t Optional$compare(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC bool Optional$equal(const void *x, const void *y, const TypeInfo_t *type); Text_t Optional$as_text(const void *obj, bool colorize, const TypeInfo_t *type); +#define Optional$metamethods ((metamethods_t){ \ + .hash=Optional$hash, \ + .compare=Optional$compare, \ + .equal=Optional$equal, \ + .as_text=Optional$as_text, \ +}) + +#define Optional$info(_size, _align, t) &((TypeInfo_t){.size=_size, .align=_align, \ + .tag=OptionalInfo, .OptionalInfo.type=t, \ + .metamethods=Optional$metamethods}) + // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/paths.c b/stdlib/paths.c index ee73647..98bcab5 100644 --- a/stdlib/paths.c +++ b/stdlib/paths.c @@ -89,7 +89,7 @@ public Path_t Path$cleanup(Path_t path) } else { // (../..) -> (../..) i += 1; } - } else if (Text$equal(&component, (Path_t*)(components.data + (i-1)*components.stride))) { // (___/../..) -> (____/../..) + } else if (Text$equal(&component, (Path_t*)(components.data + (i-1)*components.stride), &Text$info)) { // (___/../..) -> (____/../..) i += 1; } else { // (___/foo/..) -> (___) Array$remove_at(&components, I(i), I(2), sizeof(Path_t)); @@ -569,6 +569,7 @@ public const TypeInfo_t Path$info = { .align=__alignof__(Path_t), .tag=TextInfo, .TextInfo={.lang="Path"}, + .metamethods=Text$metamethods, }; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/patterns.c b/stdlib/patterns.c index 3bf2ebf..f6a7aa8 100644 --- a/stdlib/patterns.c +++ b/stdlib/patterns.c @@ -9,6 +9,7 @@ #include "integers.h" #include "optionals.h" #include "patterns.h" +#include "structs.h" #include "tables.h" #include "text.h" #include "types.h" @@ -1160,6 +1161,7 @@ public const TypeInfo_t Pattern$info = { .align=__alignof__(Pattern_t), .tag=TextInfo, .TextInfo={.lang="Pattern"}, + .metamethods=Text$metamethods, }; static NamedType_t _match_fields[3] = { @@ -1167,6 +1169,12 @@ static NamedType_t _match_fields[3] = { {"index", &Int$info}, {"captures", Array$info(&Text$info)}, }; + +static bool Match$is_none(const void *m, const TypeInfo_t*) +{ + return ((OptionalMatch_t*)m)->index.small == 0; +} + public const TypeInfo_t Match$info = { .size=sizeof(Match_t), .align=__alignof__(Match_t), @@ -1176,6 +1184,13 @@ public const TypeInfo_t Match$info = { .num_fields=3, .fields=_match_fields, }, + .metamethods={ + .as_text=Struct$as_text, + .hash=Struct$hash, + .compare=Struct$compare, + .equal=Struct$equal, + .is_none=Match$is_none, + }, }; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/pointers.c b/stdlib/pointers.c index 41defb9..97c7f5d 100644 --- a/stdlib/pointers.c +++ b/stdlib/pointers.c @@ -45,8 +45,7 @@ public Text_t Pointer$as_text(const void *x, bool colorize, const TypeInfo_t *ty } else if (ptr == root) { return Text$format(colorize ? "\x1b[34;1m%s~1\x1b[m" : "%s~1", ptr_info.sigil); } else { - TypeInfo_t rec_table = ((TypeInfo_t){.size=sizeof(Table_t), .align=__alignof__(Table_t), \ - .tag=TableInfo, .TableInfo.key=type, .TableInfo.value=&Int64$info}); + TypeInfo_t rec_table = *Table$info(type, &Int64$info); int64_t *id = Table$get(pending, x, &rec_table); if (id) return Text$format(colorize ? "\x1b[34;1m%s~%ld\x1b[m" : "%s~%ld", ptr_info.sigil, *id); @@ -69,16 +68,19 @@ public Text_t Pointer$as_text(const void *x, bool colorize, const TypeInfo_t *ty return text; } -PUREFUNC public int32_t Pointer$compare(const void *x, const void *y, const TypeInfo_t *type) { - (void)type; +PUREFUNC public int32_t Pointer$compare(const void *x, const void *y, const TypeInfo_t*) { const void *xp = *(const void**)x, *yp = *(const void**)y; return (xp > yp) - (xp < yp); } -PUREFUNC public bool Pointer$equal(const void *x, const void *y, const TypeInfo_t *type) { - (void)type; +PUREFUNC public bool Pointer$equal(const void *x, const void *y, const TypeInfo_t*) { const void *xp = *(const void**)x, *yp = *(const void**)y; return xp == yp; } +PUREFUNC public bool Pointer$is_none(const void *x, const TypeInfo_t*) +{ + return *(void**)x == NULL; +} + // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/pointers.h b/stdlib/pointers.h index 116160f..0c9f2ad 100644 --- a/stdlib/pointers.h +++ b/stdlib/pointers.h @@ -11,9 +11,21 @@ Text_t Pointer$as_text(const void *x, bool colorize, const TypeInfo_t *type); PUREFUNC int32_t Pointer$compare(const void *x, const void *y, const TypeInfo_t *type); PUREFUNC bool Pointer$equal(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC bool Pointer$is_none(const void *x, const TypeInfo_t*); #define Null(t) (t*)NULL #define POINTER_TYPE(_sigil, _pointed) (&(TypeInfo_t){\ .size=sizeof(void*), .align=alignof(void*), .tag=PointerInfo, .PointerInfo.sigil=_sigil, .PointerInfo.pointed=_pointed}) +#define Pointer$metamethods ((metamethods_t){ \ + .as_text=Pointer$as_text, \ + .compare=Pointer$compare, \ + .equal=Pointer$equal, \ + .is_none=Pointer$is_none, \ +}) + +#define Pointer$info(sigil_expr, pointed_info) &((TypeInfo_t){.size=sizeof(void*), .align=__alignof__(void*), \ + .tag=PointerInfo, .PointerInfo={.sigil=sigil_expr, .pointed=pointed_info}, \ + .metamethods=Pointer$metamethods}) + // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/ranges.c b/stdlib/ranges.c index 3f673b5..dfb878e 100644 --- a/stdlib/ranges.c +++ b/stdlib/ranges.c @@ -15,10 +15,10 @@ #include "util.h" -PUREFUNC static int32_t Range$compare(const Range_t *x, const Range_t *y, const TypeInfo_t *type) +PUREFUNC static int32_t Range$compare(const void *vx, const void *vy, const TypeInfo_t *) { - (void)type; - if (x == y) return 0; + if (vx == vy) return 0; + Range_t *x = (Range_t*)vx, *y = (Range_t*)vy; int32_t diff = Int$compare(&x->first, &y->first, &Int$info); if (diff != 0) return diff; diff = Int$compare(&x->last, &y->last, &Int$info); @@ -26,18 +26,18 @@ PUREFUNC static int32_t Range$compare(const Range_t *x, const Range_t *y, const return Int$compare(&x->step, &y->step, &Int$info); } -PUREFUNC static bool Range$equal(const Range_t *x, const Range_t *y, const TypeInfo_t *type) +PUREFUNC static bool Range$equal(const void *vx, const void *vy, const TypeInfo_t*) { - (void)type; - if (x == y) return true; + if (vx == vy) return true; + Range_t *x = (Range_t*)vx, *y = (Range_t*)vy; return Int$equal(&x->first, &y->first, &Int$info) && Int$equal(&x->last, &y->last, &Int$info) && Int$equal(&x->step, &y->step, &Int$info); } -static Text_t Range$as_text(const Range_t *r, bool use_color, const TypeInfo_t *type) +static Text_t Range$as_text(const void *obj, bool use_color, const TypeInfo_t *) { - (void)type; - if (!r) return Text("Range"); + if (!obj) return Text("Range"); + Range_t *r = (Range_t*)obj; Text_t first = Int$as_text(&r->first, use_color, &Int$info); Text_t last = Int$as_text(&r->last, use_color, &Int$info); Text_t step = Int$as_text(&r->step, use_color, &Int$info); @@ -55,10 +55,20 @@ PUREFUNC public Range_t Range$by(Range_t r, Int_t step) return (Range_t){r.first, r.last, Int$times(step, r.step)}; } -public const TypeInfo_t Range$info = {sizeof(Range_t), __alignof(Range_t), {.tag=CustomInfo, .CustomInfo={ - .as_text=(void*)Range$as_text, - .compare=(void*)Range$compare, - .equal=(void*)Range$equal, -}}}; +static bool Range$is_none(const void *obj, const TypeInfo_t*) +{ + return ((Range_t*)obj)->step.small == 0x1; +} + +public const TypeInfo_t Range$info = { + .size=sizeof(Range_t), + .align=__alignof(Range_t), + .metamethods={ + .as_text=Range$as_text, + .compare=Range$compare, + .equal=Range$equal, + .is_none=Range$is_none, + }, +}; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/rng.c b/stdlib/rng.c index 357537b..8104c45 100644 --- a/stdlib/rng.c +++ b/stdlib/rng.c @@ -26,11 +26,10 @@ struct RNGState_t { uint8_t random_bytes[1024]; }; -PUREFUNC static Text_t RNG$as_text(const RNG_t *rng, bool colorize, const TypeInfo_t *type) +PUREFUNC static Text_t RNG$as_text(const void *rng, bool colorize, const TypeInfo_t*) { - (void)type; if (!rng) return Text("RNG"); - return Text$format(colorize ? "\x1b[34;1mRNG(%p)\x1b[m" : "RNG(%p)", *rng); + return Text$format(colorize ? "\x1b[34;1mRNG(%p)\x1b[m" : "RNG(%p)", *(RNG_t**)rng); } #define KEYSZ 32 @@ -259,8 +258,9 @@ public Array_t RNG$bytes(RNG_t rng, Int_t count) public const TypeInfo_t RNG$info = { .size=sizeof(void*), .align=__alignof__(void*), - .tag=CustomInfo, - .CustomInfo={.as_text=(void*)RNG$as_text}, + .metamethods={ + .as_text=RNG$as_text, + }, }; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/shell.c b/stdlib/shell.c index ec78f5e..4a48f5c 100644 --- a/stdlib/shell.c +++ b/stdlib/shell.c @@ -141,6 +141,7 @@ public const TypeInfo_t Shell$info = { .align=__alignof__(Shell_t), .tag=TextInfo, .TextInfo={.lang="Shell"}, + .metamethods=Text$metamethods, }; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/stdlib.c b/stdlib/stdlib.c index 5d51f65..6d0ddca 100644 --- a/stdlib/stdlib.c +++ b/stdlib/stdlib.c @@ -467,12 +467,12 @@ public void end_test(const void *expr, const TypeInfo_t *type, const char *expec if (expected && expected[0]) { Text_t expected_text = Text$from_str(expected); Text_t expr_plain = USE_COLOR ? generic_as_text(expr, false, type) : expr_text; - bool success = Text$equal(&expr_plain, &expected_text); + bool success = Text$equal(&expr_plain, &expected_text, &Text$info); if (!success) { OptionalMatch_t colon = Text$find(expected_text, Text(":"), I_small(1)); if (colon.index.small) { Text_t with_type = Text$concat(expr_plain, Text(" : "), type_name); - success = Text$equal(&with_type, &expected_text); + success = Text$equal(&with_type, &expected_text, &Text$info); } } diff --git a/stdlib/structs.c b/stdlib/structs.c new file mode 100644 index 0000000..c23e4b7 --- /dev/null +++ b/stdlib/structs.c @@ -0,0 +1,170 @@ +// Metamethods for structs + +#include +#include + +#include "arrays.h" +#include "bools.h" +#include "channels.h" +#include "functiontype.h" +#include "metamethods.h" +#include "optionals.h" +#include "pointers.h" +#include "siphash.h" +#include "tables.h" +#include "text.h" +#include "util.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstack-protector" +PUREFUNC public uint64_t Struct$hash(const void *obj, const TypeInfo_t *type) +{ + if (type->StructInfo.num_fields == 0) + return 0; + + if (type->StructInfo.num_fields == 1) + return generic_hash(obj, type->StructInfo.fields[0].type); + + uint32_t field_hashes[type->StructInfo.num_fields] = {}; + ptrdiff_t byte_offset = 0; + ptrdiff_t bit_offset = 0; + for (int i = 0; i < type->StructInfo.num_fields; i++) { + NamedType_t field = type->StructInfo.fields[i]; + if (field.type == &Bool$info) { + bool b = ((*(char*)(obj + byte_offset)) >> bit_offset) & 0x1; + field_hashes[i] = (uint32_t)b; + bit_offset += 1; + if (bit_offset >= 8) { + byte_offset += 1; + bit_offset = 0; + } + } else { + if (bit_offset > 0) { + byte_offset += 1; + bit_offset = 0; + } + if (field.type->align && byte_offset % field.type->align > 0) + byte_offset += field.type->align - (byte_offset % field.type->align); + field_hashes[i] = generic_hash(obj + byte_offset, field.type); + byte_offset += field.type->size; + } + } + return siphash24((void*)field_hashes, sizeof(field_hashes)); +} +#pragma GCC diagnostic pop + +PUREFUNC public int32_t Struct$compare(const void *x, const void *y, const TypeInfo_t *type) +{ + if (x == y) + return 0; + + ptrdiff_t byte_offset = 0; + ptrdiff_t bit_offset = 0; + for (int i = 0; i < type->StructInfo.num_fields; i++) { + NamedType_t field = type->StructInfo.fields[i]; + if (field.type == &Bool$info) { + bool bx = ((*(char*)(x + byte_offset)) >> bit_offset) & 0x1; + bool by = ((*(char*)(y + byte_offset)) >> bit_offset) & 0x1; + if (bx != by) + return (int32_t)bx - (int32_t)by; + bit_offset += 1; + if (bit_offset >= 8) { + byte_offset += 1; + bit_offset = 0; + } + } else { + if (bit_offset > 0) { + byte_offset += 1; + bit_offset = 0; + } + if (field.type->align && byte_offset % field.type->align > 0) + byte_offset += field.type->align - (byte_offset % field.type->align); + int32_t cmp = generic_compare(x + byte_offset, y + byte_offset, field.type); + if (cmp != 0) + return cmp; + byte_offset += field.type->size; + } + } + return 0; +} + +PUREFUNC public bool Struct$equal(const void *x, const void *y, const TypeInfo_t *type) +{ + if (x == y) + return true; + + ptrdiff_t byte_offset = 0; + ptrdiff_t bit_offset = 0; + for (int i = 0; i < type->StructInfo.num_fields; i++) { + NamedType_t field = type->StructInfo.fields[i]; + if (field.type == &Bool$info) { + bool bx = ((*(char*)(x + byte_offset)) >> bit_offset) & 0x1; + bool by = ((*(char*)(y + byte_offset)) >> bit_offset) & 0x1; + if (bx != by) + return false; + bit_offset += 1; + if (bit_offset >= 8) { + byte_offset += 1; + bit_offset = 0; + } + } else { + if (bit_offset > 0) { + byte_offset += 1; + bit_offset = 0; + } + if (field.type->align && byte_offset % field.type->align > 0) + byte_offset += field.type->align - (byte_offset % field.type->align); + if (!generic_equal(x + byte_offset, y + byte_offset, field.type)) + return false; + byte_offset += field.type->size; + } + } + return true; +} + +PUREFUNC public Text_t Struct$as_text(const void *obj, bool colorize, const TypeInfo_t *type) +{ + if (!obj) return Text$from_str(type->StructInfo.name); + + if (type->StructInfo.is_secret) + return Text$format(colorize ? "\x1b[0;1m%s\x1b[m(...)" : "%s(...)", type->StructInfo.name); + + Text_t text = Text$format(colorize ? "\x1b[0;1m%s\x1b[m(" : "%s(", type->StructInfo.name); + ptrdiff_t byte_offset = 0; + ptrdiff_t bit_offset = 0; + for (int i = 0; i < type->StructInfo.num_fields; i++) { + NamedType_t field = type->StructInfo.fields[i]; + if (i > 0) + text = Text$concat(text, Text(", ")); + + if (type->StructInfo.num_fields > 1) + text = Text$concat(text, Text$from_str(field.name), Text("=")); + + if (field.type == &Bool$info) { + bool b = ((*(char*)(obj + byte_offset)) >> bit_offset) & 0x1; + text = Text$concat(text, Text$from_str(colorize ? (b ? "\x1b[35myes\x1b[m" : "\x1b[35mno\x1b[m") : (b ? "yes" : "no"))); + bit_offset += 1; + if (bit_offset >= 8) { + byte_offset += 1; + bit_offset = 0; + } + } else { + if (bit_offset > 0) { + byte_offset += 1; + bit_offset = 0; + } + if (field.type->align && byte_offset % field.type->align > 0) + byte_offset += field.type->align - (byte_offset % field.type->align); + text = Text$concat(text, generic_as_text(obj + byte_offset, colorize, field.type)); + byte_offset += field.type->size; + } + } + return Text$concat(text, Text(")")); +} + +PUREFUNC public bool Struct$is_none(const void *obj, const TypeInfo_t *type) +{ + return *(bool*)(obj + type->size); +} + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/structs.h b/stdlib/structs.h new file mode 100644 index 0000000..b41dde7 --- /dev/null +++ b/stdlib/structs.h @@ -0,0 +1,24 @@ +// Metamethods for structs + +#include +#include + +#include "datatypes.h" +#include "types.h" +#include "util.h" + +PUREFUNC uint64_t Struct$hash(const void *obj, const TypeInfo_t *type); +PUREFUNC int32_t Struct$compare(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC bool Struct$equal(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC Text_t Struct$as_text(const void *obj, bool colorize, const TypeInfo_t *type); +PUREFUNC bool Struct$is_none(const void *obj, const TypeInfo_t *type); + +#define Struct$metamethods ((metamethods_t){ \ + .hash=Struct$hash, \ + .compare=Struct$compare, \ + .equal=Struct$equal, \ + .as_text=Struct$as_text, \ + .is_none=Struct$is_none, \ +}) + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/tables.c b/stdlib/tables.c index e55b1f5..97fee8a 100644 --- a/stdlib/tables.c +++ b/stdlib/tables.c @@ -21,6 +21,7 @@ #include "datatypes.h" #include "memory.h" #include "metamethods.h" +#include "pointers.h" #include "siphash.h" #include "tables.h" #include "text.h" @@ -42,7 +43,7 @@ #define GET_ENTRY(t, i) ((t).entries.data + (t).entries.stride*(i)) -static const TypeInfo_t MemoryPointer = { +static TypeInfo_t MemoryPointer = { .size=sizeof(void*), .align=__alignof__(void*), .tag=PointerInfo, @@ -50,6 +51,7 @@ static const TypeInfo_t MemoryPointer = { .sigil="@", .pointed=&Memory$info, }, + .metamethods=Pointer$metamethods, }; const TypeInfo_t CStrToVoidStarTable = { @@ -57,6 +59,7 @@ const TypeInfo_t CStrToVoidStarTable = { .align=__alignof__(Table_t), .tag=TableInfo, .TableInfo={.key=&CString$info, .value=&MemoryPointer}, + .metamethods=Table$metamethods, }; PUREFUNC static INLINE size_t entry_size(const TypeInfo_t *info) @@ -396,9 +399,10 @@ public Table_t Table$sorted(Table_t t, const TypeInfo_t *type) return Table$from_entries(entries, type); } -PUREFUNC public bool Table$equal(const Table_t *x, const Table_t *y, const TypeInfo_t *type) +PUREFUNC public bool Table$equal(const void *vx, const void *vy, const TypeInfo_t *type) { - if (x == y) return true; + if (vx == vy) return true; + Table_t *x = (Table_t*)vx, *y = (Table_t*)vy; assert(type->tag == TableInfo); if (Table$length(*x) != Table$length(*y)) @@ -410,10 +414,11 @@ PUREFUNC public bool Table$equal(const Table_t *x, const Table_t *y, const TypeI return (Table$compare(x, y, type) == 0); } -PUREFUNC public int32_t Table$compare(const Table_t *x, const Table_t *y, const TypeInfo_t *type) +PUREFUNC public int32_t Table$compare(const void *vx, const void *vy, const TypeInfo_t *type) { - if (x == y) return 0; + if (vx == vy) return 0; + Table_t *x = (Table_t*)vx, *y = (Table_t*)vy; assert(type->tag == TableInfo); auto table = type->TableInfo; if (x->entries.length == 0) @@ -441,9 +446,10 @@ PUREFUNC public int32_t Table$compare(const Table_t *x, const Table_t *y, const return 0; } -PUREFUNC public uint64_t Table$hash(const Table_t *t, const TypeInfo_t *type) +PUREFUNC public uint64_t Table$hash(const void *obj, const TypeInfo_t *type) { assert(type->tag == TableInfo); + Table_t *t = (Table_t*)obj; // Table hashes are computed as: // hash(hash(t.keys), hash(t.values), hash(t.fallback), hash(t.default)) // Where fallback and default hash to zero if absent @@ -456,8 +462,9 @@ PUREFUNC public uint64_t Table$hash(const Table_t *t, const TypeInfo_t *type) return siphash24((void*)&components, sizeof(components)); } -public Text_t Table$as_text(const Table_t *t, bool colorize, const TypeInfo_t *type) +public Text_t Table$as_text(const void *obj, bool colorize, const TypeInfo_t *type) { + Table_t *t = (Table_t*)obj; assert(type->tag == TableInfo); auto table = type->TableInfo; @@ -635,4 +642,9 @@ CONSTFUNC public void *Table$str_entry(Table_t t, int64_t n) return Table$entry(t, n); } +PUREFUNC public bool Table$is_none(const void *obj, const TypeInfo_t*) +{ + return ((Table_t*)obj)->entries.length < 0; +} + // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1 diff --git a/stdlib/tables.h b/stdlib/tables.h index 14b50ef..1f62a13 100644 --- a/stdlib/tables.h +++ b/stdlib/tables.h @@ -65,10 +65,11 @@ Table_t Table$sorted(Table_t t, const TypeInfo_t *type); void Table$mark_copy_on_write(Table_t *t); #define TABLE_INCREF(t) ({ ARRAY_INCREF((t).entries); if ((t).bucket_info) (t).bucket_info->data_refcount += ((t).bucket_info->data_refcount < TABLE_MAX_DATA_REFCOUNT); }) #define TABLE_COPY(t) ({ TABLE_INCREF(t); t; }) -PUREFUNC int32_t Table$compare(const Table_t *x, const Table_t *y, const TypeInfo_t *type); -PUREFUNC bool Table$equal(const Table_t *x, const Table_t *y, const TypeInfo_t *type); -PUREFUNC uint64_t Table$hash(const Table_t *t, const TypeInfo_t *type); -Text_t Table$as_text(const Table_t *t, bool colorize, const TypeInfo_t *type); +PUREFUNC int32_t Table$compare(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC bool Table$equal(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC uint64_t Table$hash(const void *t, const TypeInfo_t *type); +Text_t Table$as_text(const void *t, bool colorize, const TypeInfo_t *type); +PUREFUNC bool Table$is_none(const void *obj, const TypeInfo_t*); CONSTFUNC void *Table$str_entry(Table_t t, int64_t n); PUREFUNC void *Table$str_get(Table_t t, const char *key); @@ -81,4 +82,17 @@ void Table$str_remove(Table_t *t, const char *key); extern const TypeInfo_t CStrToVoidStarTable; +#define Table$metamethods ((metamethods_t){ \ + .as_text=Table$as_text, \ + .compare=Table$compare, \ + .equal=Table$equal, \ + .hash=Table$hash, \ + .is_none=Table$is_none, \ +}) + +#define Table$info(key_expr, value_expr) &((TypeInfo_t){.size=sizeof(Table_t), .align=__alignof__(Table_t), \ + .tag=TableInfo, .TableInfo.key=key_expr, .TableInfo.value=value_expr, .metamethods=Table$metamethods}) +#define Set$info(item_info) &((TypeInfo_t){.size=sizeof(Table_t), .align=__alignof__(Table_t), \ + .tag=TableInfo, .TableInfo.key=item_info, .TableInfo.value=&Void$info, .metamethods=Table$metamethods}) + // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1 diff --git a/stdlib/text.c b/stdlib/text.c index 8a05f96..40a3988 100644 --- a/stdlib/text.c +++ b/stdlib/text.c @@ -92,28 +92,27 @@ static int32_t num_synthetic_graphemes = 0; static Text_t text_from_u32(ucs4_t *codepoints, int64_t num_codepoints, bool normalize); -PUREFUNC static bool graphemes_equal(ucs4_t **a, ucs4_t **b) { - if ((*a)[0] != (*b)[0]) return false; - for (int i = 0; i < (int)(*a)[0]; i++) - if ((*a)[i] != (*b)[i]) return false; +PUREFUNC static bool graphemes_equal(const void *va, const void *vb, const TypeInfo_t*) { + ucs4_t *a = *(ucs4_t**)va; + ucs4_t *b = *(ucs4_t**)vb; + if (a[0] != b[0]) return false; + for (int i = 0; i < (int)a[0]; i++) + if (a[i] != b[i]) return false; return true; } -PUREFUNC static uint64_t grapheme_hash(ucs4_t **g) { - ucs4_t *cluster = *g; +PUREFUNC static uint64_t grapheme_hash(const void *g, const TypeInfo_t*) { + ucs4_t *cluster = *(ucs4_t**)g; return siphash24((void*)&cluster[1], sizeof(ucs4_t[cluster[0]])); } static const TypeInfo_t GraphemeClusterInfo = { .size=sizeof(ucs4_t*), .align=__alignof__(ucs4_t*), - .tag=CustomInfo, - .CustomInfo={.equal=(void*)graphemes_equal, .hash=(void*)grapheme_hash}, -}; - -static const TypeInfo_t GraphemeIDLookupTableInfo = { - .size=sizeof(Table_t), .align=__alignof__(Table_t), - .tag=TableInfo, .TableInfo={.key=&GraphemeClusterInfo, .value=&Int32$info}, + .metamethods={ + .equal=graphemes_equal, + .hash=grapheme_hash, + }, }; #pragma GCC diagnostic push @@ -128,9 +127,10 @@ public int32_t get_synthetic_grapheme(const ucs4_t *codepoints, int64_t utf32_le // Optimization for common case of one frequently used synthetic grapheme: static int32_t last_grapheme = 0; - if (last_grapheme != 0 && graphemes_equal(&ptr, &synthetic_graphemes[-last_grapheme-1].utf32_cluster)) + if (last_grapheme != 0 && graphemes_equal(&ptr, &synthetic_graphemes[-last_grapheme-1].utf32_cluster, NULL)) return last_grapheme; + TypeInfo_t GraphemeIDLookupTableInfo = *Table$info(&GraphemeClusterInfo, &Int32$info); int32_t *found = Table$get(grapheme_ids_by_codepoints, &ptr, &GraphemeIDLookupTableInfo); if (found) return *found; @@ -765,8 +765,9 @@ public char *Text$as_c_string(Text_t text) return buf; } -PUREFUNC public uint64_t Text$hash(Text_t *text) +PUREFUNC public uint64_t Text$hash(const void *obj, const TypeInfo_t*) { + Text_t *text = (Text_t*)obj; if (text->hash != 0) return text->hash; siphash sh; siphashinit(&sh, sizeof(int32_t[text->length])); @@ -906,9 +907,11 @@ public uint32_t Text$get_main_grapheme_fast(TextIter_t *state, int64_t index) return (g) >= 0 ? (ucs4_t)(g) : synthetic_graphemes[-(g)-1].main_codepoint; } -PUREFUNC public int32_t Text$compare(const Text_t *a, const Text_t *b) +PUREFUNC public int32_t Text$compare(const void *va, const void *vb, const TypeInfo_t*) { - if (a == b) return 0; + if (va == vb) return 0; + const Text_t *a = (const Text_t*)va; + const Text_t *b = (const Text_t*)vb; int64_t len = MAX(a->length, b->length); TextIter_t a_state = {*a, 0, 0}, b_state = {*b, 0, 0}; @@ -981,10 +984,10 @@ PUREFUNC public bool Text$equal_values(Text_t a, Text_t b) return true; } -PUREFUNC public bool Text$equal(const Text_t *a, const Text_t *b) +PUREFUNC public bool Text$equal(const void *a, const void *b, const TypeInfo_t*) { if (a == b) return true; - return Text$equal_values(*a, *b); + return Text$equal_values(*(Text_t*)a, *(Text_t*)b); } PUREFUNC public bool Text$equal_ignoring_case(Text_t a, Text_t b) @@ -1369,11 +1372,17 @@ public Array_t Text$lines(Text_t text) return lines; } +PUREFUNC public bool Text$is_none(const void *t, const TypeInfo_t*) +{ + return ((Text_t*)t)->length < 0; +} + public const TypeInfo_t Text$info = { .size=sizeof(Text_t), .align=__alignof__(Text_t), .tag=TextInfo, .TextInfo={.lang="Text"}, + .metamethods=Text$metamethods, }; public Pattern_t Pattern$escape_text(Text_t text) diff --git a/stdlib/text.h b/stdlib/text.h index 4f23834..04c867c 100644 --- a/stdlib/text.h +++ b/stdlib/text.h @@ -10,6 +10,7 @@ #include "datatypes.h" #include "integers.h" #include "optionals.h" +#include "types.h" #include "util.h" typedef struct { @@ -31,11 +32,12 @@ Text_t Text$slice(Text_t text, Int_t first_int, Int_t last_int); Text_t Text$cluster(Text_t text, Int_t index_int); OptionalText_t Text$from_str(const char *str); OptionalText_t Text$from_strn(const char *str, size_t len); -PUREFUNC uint64_t Text$hash(Text_t *text); -PUREFUNC int32_t Text$compare(const Text_t *a, const Text_t *b); -PUREFUNC bool Text$equal(const Text_t *a, const Text_t *b); +PUREFUNC uint64_t Text$hash(const void *text, const TypeInfo_t*); +PUREFUNC int32_t Text$compare(const void *va, const void *vb, const TypeInfo_t*); +PUREFUNC bool Text$equal(const void *a, const void *b, const TypeInfo_t*); PUREFUNC bool Text$equal_values(Text_t a, Text_t b); PUREFUNC bool Text$equal_ignoring_case(Text_t a, Text_t b); +PUREFUNC bool Text$is_none(const void *t, const TypeInfo_t*); Text_t Text$upper(Text_t text); Text_t Text$lower(Text_t text); Text_t Text$title(Text_t text); @@ -67,4 +69,12 @@ MACROLIKE int32_t Text$get_grapheme(Text_t text, int64_t index) extern const TypeInfo_t Text$info; +#define Text$metamethods ((metamethods_t){ \ + .as_text=Text$as_text, \ + .hash=Text$hash, \ + .compare=Text$compare, \ + .equal=Text$equal, \ + .is_none=Text$is_none, \ +}) + // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/threads.c b/stdlib/threads.c index 3ae2980..860b488 100644 --- a/stdlib/threads.c +++ b/stdlib/threads.c @@ -53,19 +53,25 @@ public void Thread$detach(Thread_t thread) pthread_detach(*thread); } -Text_t Thread$as_text(const Thread_t *thread, bool colorize, const TypeInfo_t *type) +Text_t Thread$as_text(const void *thread, bool colorize, const TypeInfo_t*) { - (void)type; if (!thread) { return colorize ? Text("\x1b[34;1mThread\x1b[m") : Text("Thread"); } - return Text$format(colorize ? "\x1b[34;1mThread(%p)\x1b[m" : "Thread(%p)", *thread); + return Text$format(colorize ? "\x1b[34;1mThread(%p)\x1b[m" : "Thread(%p)", *(Thread_t**)thread); +} + +static bool Thread$is_none(const void *obj, const TypeInfo_t*) +{ + return *(Thread_t*)obj == NULL; } public const TypeInfo_t Thread$info = { .size=sizeof(Thread_t), .align=__alignof(Thread_t), - .tag=CustomInfo, - .CustomInfo={.as_text=(void*)Thread$as_text}, + .metamethods={ + .as_text=Thread$as_text, + .is_none=Thread$is_none, + }, }; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/threads.h b/stdlib/threads.h index 049b011..9f1c3d3 100644 --- a/stdlib/threads.h +++ b/stdlib/threads.h @@ -15,7 +15,7 @@ Thread_t Thread$new(Closure_t fn); void Thread$cancel(Thread_t thread); void Thread$join(Thread_t thread); void Thread$detach(Thread_t thread); -Text_t Thread$as_text(const Thread_t *thread, bool colorize, const TypeInfo_t *type); +Text_t Thread$as_text(const void *thread, bool colorize, const TypeInfo_t *type); extern const TypeInfo_t Thread$info; diff --git a/stdlib/tomo.h b/stdlib/tomo.h index 159b642..0325793 100644 --- a/stdlib/tomo.h +++ b/stdlib/tomo.h @@ -13,6 +13,7 @@ #include "c_strings.h" #include "channels.h" #include "datatypes.h" +#include "enums.h" #include "functiontype.h" #include "integers.h" #include "memory.h" @@ -27,6 +28,7 @@ #include "rng.h" #include "shell.h" #include "siphash.h" +#include "structs.h" #include "tables.h" #include "text.h" #include "threads.h" diff --git a/stdlib/types.h b/stdlib/types.h index a8a9b4e..652a2e7 100644 --- a/stdlib/types.h +++ b/stdlib/types.h @@ -9,10 +9,13 @@ typedef struct TypeInfo_s TypeInfo_t; -typedef uint64_t (*hash_fn_t)(const void*, const TypeInfo_t*); -typedef int32_t (*compare_fn_t)(const void*, const void*, const TypeInfo_t*); -typedef bool (*equal_fn_t)(const void*, const void*, const TypeInfo_t*); -typedef Text_t (*text_fn_t)(const void*, bool, const TypeInfo_t*); +typedef struct { + uint64_t (*hash)(const void*, const TypeInfo_t*); + int32_t (*compare)(const void*, const void*, const TypeInfo_t*); + bool (*equal)(const void*, const void*, const TypeInfo_t*); + Text_t (*as_text)(const void*, bool, const TypeInfo_t*); + bool (*is_none)(const void*, const TypeInfo_t*); +} metamethods_t; typedef struct { const char *name; @@ -21,16 +24,12 @@ typedef struct { struct TypeInfo_s { int64_t size, align; + metamethods_t metamethods; struct { // Anonymous tagged union for convenience - enum { Invalid, CustomInfo, StructInfo, EnumInfo, PointerInfo, TextInfo, ArrayInfo, ChannelInfo, TableInfo, FunctionInfo, - OptionalInfo, TypeInfoInfo, OpaqueInfo, CStringInfo } tag; + enum { OpaqueInfo, StructInfo, EnumInfo, PointerInfo, TextInfo, ArrayInfo, ChannelInfo, TableInfo, FunctionInfo, + OptionalInfo, TypeInfoInfo } tag; union { - struct { - equal_fn_t equal; - compare_fn_t compare; - hash_fn_t hash; - text_fn_t as_text; - } CustomInfo; + struct {} OpaqueInfo; struct { const char *sigil; const TypeInfo_t *pointed; @@ -53,7 +52,6 @@ struct TypeInfo_s { struct { const TypeInfo_t *type; } OptionalInfo; - struct {} OpaqueInfo; struct { const char *name; int num_tags; @@ -69,24 +67,8 @@ struct TypeInfo_s { }; }; -#define Pointer$info(sigil_expr, pointed_info) &((TypeInfo_t){.size=sizeof(void*), .align=__alignof__(void*), \ - .tag=PointerInfo, .PointerInfo={.sigil=sigil_expr, .pointed=pointed_info}}) -#define Array$info(item_info) &((TypeInfo_t){.size=sizeof(Array_t), .align=__alignof__(Array_t), \ - .tag=ArrayInfo, .ArrayInfo.item=item_info}) -#define Set$info(item_info) &((TypeInfo_t){.size=sizeof(Table_t), .align=__alignof__(Table_t), \ - .tag=TableInfo, .TableInfo.key=item_info, .TableInfo.value=&Void$info}) -#define Channel$info(item_info) &((TypeInfo_t){.size=sizeof(Channel_t), .align=__alignof__(Channel_t), \ - .tag=ChannelInfo, .ChannelInfo.item=item_info}) -#define Table$info(key_expr, value_expr) &((TypeInfo_t){.size=sizeof(Table_t), .align=__alignof__(Table_t), \ - .tag=TableInfo, .TableInfo.key=key_expr, .TableInfo.value=value_expr}) -#define Function$info(typestr) &((TypeInfo_t){.size=sizeof(void*), .align=__alignof__(void*), \ - .tag=FunctionInfo, .FunctionInfo.type_str=typestr}) -#define Closure$info(typestr) &((TypeInfo_t){.size=sizeof(void*[2]), .align=__alignof__(void*), \ - .tag=FunctionInfo, .FunctionInfo.type_str=typestr}) #define Type$info(typestr) &((TypeInfo_t){.size=sizeof(TypeInfo_t), .align=__alignof__(TypeInfo_t), \ .tag=TypeInfoInfo, .TypeInfoInfo.type_str=typestr}) -#define Optional$info(_size, _align, t) &((TypeInfo_t){.size=_size, .align=_align, \ - .tag=OptionalInfo, .OptionalInfo.type=t}) extern const TypeInfo_t Void$info; extern const TypeInfo_t Abort$info; diff --git a/structs.c b/structs.c index 5915f11..da57ff4 100644 --- a/structs.c +++ b/structs.c @@ -27,7 +27,8 @@ void compile_struct_def(env_t *env, ast_t *ast) short_name = strrchr(short_name, '$') + 1; env->code->typeinfos = CORD_all("public const TypeInfo_t ", full_name, ";\n", env->code->typeinfos); - CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {.size=%zu, .align=%zu, .tag=StructInfo, .StructInfo.name=\"%s\"%s, " + CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {.size=%zu, .align=%zu, .metamethods=Struct$metamethods, " + ".tag=StructInfo, .StructInfo.name=\"%s\"%s, " ".StructInfo.num_fields=%ld", full_name, type_size(t), type_align(t), short_name, def->secret ? ", .StructInfo.is_secret=true" : "", num_fields);