aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compile.c10
-rw-r--r--enums.c249
-rw-r--r--stdlib/metamethods.c164
-rw-r--r--stdlib/types.c4
-rw-r--r--stdlib/types.h43
-rw-r--r--structs.c163
-rw-r--r--types.c4
7 files changed, 264 insertions, 373 deletions
diff --git a/compile.c b/compile.c
index b93dac4d..a0262608 100644
--- a/compile.c
+++ b/compile.c
@@ -1461,9 +1461,7 @@ CORD expr_as_text(env_t *env, CORD expr, type_t *t, CORD color)
case FunctionType: case ClosureType: return CORD_asprintf("Func$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
case PointerType: return CORD_asprintf("Pointer$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
case OptionalType: return CORD_asprintf("Optional$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
- case StructType: case EnumType:
- return CORD_asprintf("(%r)->CustomInfo.as_text(stack(%r), %r, %r)",
- compile_type_info(env, t), expr, color, compile_type_info(env, t));
+ case StructType: case EnumType: return CORD_asprintf("generic_as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
default: compiler_err(NULL, NULL, NULL, "Stringifying is not supported for %T", t);
}
}
@@ -1952,6 +1950,7 @@ CORD compile(env_t *env, ast_t *ast)
code_err(ast, "I don't know how to get the negative value of type %T", t);
}
+ // TODO: for constructors, do new(T, ...) instead of heap((T){...})
case HeapAllocate: return CORD_asprintf("heap(%r)", compile(env, Match(ast, HeapAllocate)->value));
case StackReference: {
ast_t *subject = Match(ast, StackReference)->value;
@@ -3464,7 +3463,8 @@ CORD compile_type_info(env_t *env, type_t *t)
return CORD_asprintf("Closure$info(%r)", CORD_quoted(type_to_cord(t)));
}
case OptionalType: {
- return CORD_asprintf("Optional$info(%r)", compile_type_info(env, Match(t, OptionalType)->type));
+ type_t *non_optional = Match(t, OptionalType)->type;
+ return CORD_asprintf("Optional$info(%zu, %zu, %r)", type_size(t), type_align(t), compile_type_info(env, non_optional));
}
case TypeInfoType: return CORD_all("TypeInfo$info(", CORD_quoted(type_to_cord(Match(t, TypeInfoType)->type)), ")");
case MemoryType: return "&Memory$info";
@@ -3656,9 +3656,9 @@ CORD compile_file(env_t *env, ast_t *ast)
"#include <tomo/tomo.h>\n"
"#include \"", name, ".tm.h\"\n\n",
env->code->local_typedefs, "\n",
+ env->code->typeinfos, "\n",
env->code->staticdefs, "\n",
env->code->funcs, "\n",
- env->code->typeinfos, "\n",
"public void $", env->namespace->name, "$$initialize(void) {\n",
"static bool initialized = false;\n",
"if (initialized) return;\n",
diff --git a/enums.c b/enums.c
index 2fedca1a..da4dd082 100644
--- a/enums.c
+++ b/enums.c
@@ -13,193 +13,53 @@
#include "typecheck.h"
#include "stdlib/util.h"
-PUREFUNC static bool has_extra_data(tag_ast_t *tags)
-{
- for (tag_ast_t *tag = tags; tag; tag = tag->next) {
- if (tag->fields) return true;
- }
- return false;
-}
-
-static CORD compile_str_method(env_t *env, ast_t *ast)
-{
- auto def = Match(ast, EnumDef);
- CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name);
- CORD str_func = CORD_all("static Text_t ", full_name, "$as_text(", full_name, "_t *obj, bool use_color) {\n"
- "\tif (!obj) return Text(\"", def->name, "\");\n"
- "switch (obj->tag) {\n");
- for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
- if (!tag->fields) {
- str_func = CORD_all(str_func, "\tcase ", full_name, "$tag$", tag->name, ": return use_color ? Text(\"\\x1b[36;1m",
- def->name, ".", tag->name, "\\x1b[m\") : Text(\"", def->name, ".", tag->name, "\");\n");
- continue;
- }
-
- str_func = CORD_all(str_func, "\tcase ", full_name, "$tag$", tag->name, ": return Text$concat(use_color ? Text(\"\\x1b[36;1m",
- def->name, ".", tag->name, "\\x1b[m(\") : Text(\"", def->name, ".", tag->name, "(\")");
-
- if (tag->secret) {
- str_func = CORD_cat(str_func, ", use_color ? Text(\"\\x1b[2m...\\x1b[m\") : Text(\"...\", \")\"));\n");
- continue;
- }
-
- if (tag->fields && !tag->fields->next) { // Single-member tags don't need to print member names:
- type_t *field_t = get_arg_ast_type(env, tag->fields);
- CORD field_str = expr_as_text(env, CORD_all("obj->$", tag->name, ".$", tag->fields->name), field_t, "use_color");
- str_func = CORD_all(str_func, ", ", field_str);
- } else {
- for (arg_ast_t *field = tag->fields; field; field = field->next) {
- type_t *field_t = get_arg_ast_type(env, field);
- CORD field_str = expr_as_text(env, CORD_all("obj->$", tag->name, ".$", field->name), field_t, "use_color");
- str_func = CORD_all(str_func, ", Text(\"", field->name, "=\"), ", field_str);
- if (field->next) str_func = CORD_cat(str_func, ", Text(\", \")");
- }
- }
- str_func = CORD_cat(str_func, ", Text(\")\"));\n");
- }
- str_func = CORD_cat(str_func, "\tdefault: return (Text_t){.length=0};\n\t}\n}\n");
- return str_func;
-}
-
-static CORD compile_compare_method(env_t *env, ast_t *ast)
+void compile_enum_def(env_t *env, ast_t *ast)
{
auto def = Match(ast, EnumDef);
CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name);
- if (!has_extra_data(def->tags)) {
- // Comparisons are simpler if there is only a tag, no tagged data:
- return CORD_all("static int ", full_name, "$compare(const ", full_name, "_t *x, const ", full_name,
- "_t *y, const TypeInfo_t *info) {\n"
- "(void)info;\n"
- "return (x->tag - y->tag);\n"
- "}\n");
- }
- CORD cmp_func = CORD_all("static int ", full_name, "$compare(const ", full_name, "_t *x, const ", full_name,
- "_t *y, const TypeInfo_t *info) {\n"
- "(void)info;\n"
- "int diff = x->tag - y->tag;\n"
- "if (diff) return diff;\n"
- "switch (x->tag) {\n");
- for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
- if (tag->fields) {
- type_t *tag_type = Table$str_get(*env->types, CORD_to_const_char_star(CORD_all(def->name, "$", tag->name)));
- cmp_func = CORD_all(cmp_func, "\tcase ", full_name, "$tag$", tag->name, ": "
- "return generic_compare(&x->$", tag->name, ", &y->$", tag->name, ", ", compile_type_info(env, tag_type), ");\n");
- } else {
- cmp_func = CORD_all(cmp_func, "\tcase ", full_name, "$tag$", tag->name, ": return 0;\n");
- }
- }
- cmp_func = CORD_all(cmp_func, "default: return 0;\n}\n}\n");
- return cmp_func;
-}
-static CORD compile_equals_method(env_t *env, ast_t *ast)
-{
- auto def = Match(ast, EnumDef);
- CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name);
- if (!has_extra_data(def->tags)) {
- // Equality is simpler if there is only a tag, no tagged data:
- return CORD_all("static bool ", full_name, "$equal(const ", full_name, "_t *x, const ", full_name,
- "_t *y, const TypeInfo_t *info) {\n"
- "(void)info;\n"
- "return (x->tag == y->tag);\n"
- "}\n");
- }
- CORD eq_func = CORD_all("static bool ", full_name, "$equal(const ", full_name, "_t *x, const ", full_name,
- "_t *y, const TypeInfo_t *info) {\n"
- "(void)info;\n"
- "if (x->tag != y->tag) return no;\n"
- "switch (x->tag) {\n");
+ // Compile member types and constructors:
for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
- if (tag->fields) {
- type_t *tag_type = Table$str_get(*env->types, CORD_to_const_char_star(CORD_all(def->name, "$", tag->name)));
- eq_func = CORD_all(eq_func, "\tcase ", full_name, "$tag$", tag->name, ": "
- "return generic_equal(&x->$", tag->name, ", &y->$", tag->name, ", ", compile_type_info(env, tag_type), ");\n");
- } else {
- eq_func = CORD_all(eq_func, "\tcase ", full_name, "$tag$", tag->name, ": return yes;\n");
+ if (!tag->fields) continue;
+
+ const char *tag_name = heap_strf("%s$%s", def->name, tag->name);
+ compile_struct_def(env, WrapAST(ast, StructDef, .name=tag_name, .fields=tag->fields, .secret=tag->secret));
+ CORD arg_sig = CORD_EMPTY;
+ for (arg_ast_t *field = tag->fields; field; field = field->next) {
+ type_t *field_t = get_arg_ast_type(env, field);
+ arg_sig = CORD_all(arg_sig, compile_declaration(field_t, CORD_all("$", field->name)));
+ if (field->next) arg_sig = CORD_cat(arg_sig, ", ");
}
- }
- eq_func = CORD_all(eq_func, "default: return 0;\n}\n}\n");
- return eq_func;
-}
-
-static CORD compile_hash_method(env_t *env, ast_t *ast)
-{
- auto def = Match(ast, EnumDef);
- CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name);
- if (!has_extra_data(def->tags)) {
- // Hashing is simpler if there is only a tag, no tagged data:
- return CORD_all("static uint64_t ", full_name, "$hash(const ", full_name, "_t *obj, const TypeInfo_t *info) {\n"
- "(void)info;\n"
- "return siphash24((void*)&obj->tag, sizeof(obj->tag));\n"
- "\n}\n");
- }
- CORD hash_func = CORD_all("static uint64_t ", full_name, "$hash(const ", full_name, "_t *obj, const TypeInfo_t *info) {\n"
- "(void)info;\n"
- "uint64_t hashes[2] = {(uint64_t)obj->tag, 0};\n"
- "switch (obj->tag) {\n");
- for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
- if (tag->fields) {
- type_t *tag_type = Table$str_get(*env->types, CORD_to_const_char_star(CORD_all(def->name, "$", tag->name)));
- hash_func = CORD_all(hash_func, "\tcase ", full_name, "$tag$", tag->name, ": "
- "hashes[1] = generic_hash(&obj->$", tag->name, ", ", compile_type_info(env, tag_type), ");\n"
- "break;\n");
- } else {
- hash_func = CORD_all(hash_func, "\tcase ", full_name, "$tag$", tag->name, ": break;\n");
+ if (arg_sig == CORD_EMPTY) arg_sig = "void";
+ CORD constructor_impl = CORD_all("public inline ", full_name, "_t ", full_name, "$tagged$", tag->name, "(", arg_sig, ") { return (",
+ full_name, "_t){.tag=", full_name, "$tag$", tag->name, ", .$", tag->name, "={");
+ for (arg_ast_t *field = tag->fields; field; field = field->next) {
+ constructor_impl = CORD_all(constructor_impl, "$", field->name);
+ if (field->next) constructor_impl = CORD_cat(constructor_impl, ", ");
}
+ constructor_impl = CORD_cat(constructor_impl, "}}; }\n");
+ env->code->funcs = CORD_cat(env->code->funcs, constructor_impl);
}
- hash_func = CORD_all(hash_func, "}\n"
- "return siphash24((void*)&hashes, sizeof(hashes));\n}\n");
- return hash_func;
-}
-void compile_enum_def(env_t *env, ast_t *ast)
-{
- auto def = Match(ast, EnumDef);
- CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name);
- for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
- compile_struct_def(env, WrapAST(ast, StructDef, .name=CORD_to_const_char_star(CORD_all(def->name, "$", tag->name)), .fields=tag->fields));
- if (tag->fields) { // Constructor macros:
- CORD arg_sig = CORD_EMPTY;
- for (arg_ast_t *field = tag->fields; field; field = field->next) {
- type_t *field_t = get_arg_ast_type(env, field);
- arg_sig = CORD_all(arg_sig, compile_declaration(field_t, CORD_all("$", field->name)));
- if (field->next) arg_sig = CORD_cat(arg_sig, ", ");
- }
- if (arg_sig == CORD_EMPTY) arg_sig = "void";
- CORD constructor_impl = CORD_all("public inline ", full_name, "_t ", full_name, "$tagged$", tag->name, "(", arg_sig, ") { return (",
- full_name, "_t){.tag=", full_name, "$tag$", tag->name, ", .$", tag->name, "={");
- for (arg_ast_t *field = tag->fields; field; field = field->next) {
- constructor_impl = CORD_all(constructor_impl, "$", field->name);
- if (field->next) constructor_impl = CORD_cat(constructor_impl, ", ");
- }
- constructor_impl = CORD_cat(constructor_impl, "}}; }\n");
- env->code->funcs = CORD_cat(env->code->funcs, constructor_impl);
- }
- }
+ int num_tags = 0;
+ for (tag_ast_t *t = def->tags; t; t = t->next)
+ num_tags += 1;
type_t *t = Table$str_get(*env->types, def->name);
- CORD typeinfo = CORD_asprintf("public const TypeInfo_t %s = {%zu, %zu, {.tag=EnumInfo, .CustomInfo={",
- full_name, type_size(t), type_align(t));
+ CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {%zu, %zu, {.tag=EnumInfo, .EnumInfo={.name=\"%s\", "
+ ".num_tags=%d, .tags=(NamedType_t[]){",
+ full_name, type_size(t), type_align(t), def->name, num_tags);
- env->code->funcs = CORD_all(env->code->funcs, compile_str_method(env, ast));
- if (has_extra_data(def->tags)) {
- env->code->funcs = CORD_all(
- env->code->funcs,
- compile_equals_method(env, ast), compile_compare_method(env, ast),
- compile_hash_method(env, ast));
- typeinfo = CORD_all(
- typeinfo,
- ".as_text=(void*)", full_name, "$as_text, "
- ".equal=(void*)", full_name, "$equal, "
- ".hash=(void*)", full_name, "$hash, "
- ".compare=(void*)", full_name, "$compare");
- } else {
- // No need for custom methods if there is no tagged data
- typeinfo = CORD_all(typeinfo, ".as_text=(void*)", full_name, "$as_text");
+ for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
+ const char *tag_type_name = heap_strf("%s$%s", def->name, tag->name);
+ type_t *tag_type = Table$str_get(*env->types, tag_type_name);
+ if (tag_type && Match(tag_type, StructType)->fields)
+ typeinfo = CORD_all(typeinfo, "{\"", tag->name, "\", ", compile_type_info(env, tag_type), "}, ");
+ else
+ typeinfo = CORD_all(typeinfo, "{\"", tag->name, "\"}, ");
}
- typeinfo = CORD_cat(typeinfo, "}}};\n");
+ typeinfo = CORD_all(typeinfo, "}}}};\n");
env->code->typeinfos = CORD_all(env->code->typeinfos, typeinfo);
-
compile_namespace(env, def->name, def->namespace);
}
@@ -211,35 +71,40 @@ CORD compile_enum_header(env_t *env, ast_t *ast)
CORD enum_def = CORD_all("struct ", full_name, "_s {\n"
"\tenum { ", full_name, "$null=0, ");
+ bool has_any_tags_with_fields = false;
for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
enum_def = CORD_all(enum_def, full_name, "$tag$", tag->name);
if (tag->next) enum_def = CORD_all(enum_def, ", ");
+ has_any_tags_with_fields = has_any_tags_with_fields || (tag->fields != NULL);
}
- enum_def = CORD_all(enum_def, "} tag;\n"
- "union {\n");
- for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
- CORD field_def = compile_struct_header(env, WrapAST(ast, StructDef, .name=CORD_to_const_char_star(CORD_all(def->name, "$", tag->name)), .fields=tag->fields));
- all_defs = CORD_all(all_defs, field_def);
- enum_def = CORD_all(enum_def, full_name, "$", tag->name, "_t $", tag->name, ";\n");
+ enum_def = CORD_all(enum_def, "} tag;\n");
+
+ if (has_any_tags_with_fields) {
+ enum_def = CORD_all(enum_def, "union {\n");
+ for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
+ if (!tag->fields) continue;
+ CORD field_def = compile_struct_header(env, WrapAST(ast, StructDef, .name=CORD_to_const_char_star(CORD_all(def->name, "$", tag->name)), .fields=tag->fields));
+ all_defs = CORD_all(all_defs, field_def);
+ enum_def = CORD_all(enum_def, full_name, "$", tag->name, "_t $", tag->name, ";\n");
+ }
+ enum_def = CORD_all(enum_def, "};\n");
}
- enum_def = CORD_all(enum_def, "};\n};\n");
+ enum_def = CORD_all(enum_def, "};\n");
all_defs = CORD_all(all_defs, enum_def);
all_defs = CORD_all(all_defs, "extern const TypeInfo_t ", full_name, ";\n");
for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
- all_defs = CORD_all(all_defs,
- "extern const TypeInfo_t ", namespace_prefix(env, env->namespace), def->name, "$", tag->name, ";\n");
- if (tag->fields) { // Constructor macros:
- CORD arg_sig = CORD_EMPTY;
- for (arg_ast_t *field = tag->fields; field; field = field->next) {
- type_t *field_t = get_arg_ast_type(env, field);
- arg_sig = CORD_all(arg_sig, compile_declaration(field_t, CORD_all("$", field->name)));
- if (field->next) arg_sig = CORD_all(arg_sig, ", ");
- }
- if (arg_sig == CORD_EMPTY) arg_sig = "void";
- CORD constructor_def = CORD_all(full_name, "_t ", full_name, "$tagged$", tag->name, "(", arg_sig, ");\n");
- all_defs = CORD_all(all_defs, constructor_def);
+ if (!tag->fields) continue;
+
+ CORD arg_sig = CORD_EMPTY;
+ for (arg_ast_t *field = tag->fields; field; field = field->next) {
+ type_t *field_t = get_arg_ast_type(env, field);
+ arg_sig = CORD_all(arg_sig, compile_declaration(field_t, CORD_all("$", field->name)));
+ if (field->next) arg_sig = CORD_all(arg_sig, ", ");
}
+ if (arg_sig == CORD_EMPTY) arg_sig = "void";
+ CORD constructor_def = CORD_all(full_name, "_t ", full_name, "$tagged$", tag->name, "(", arg_sig, ");\n");
+ all_defs = CORD_all(all_defs, constructor_def);
}
return CORD_all(all_defs, compile_namespace_header(env, def->name, def->namespace));
}
diff --git a/stdlib/metamethods.c b/stdlib/metamethods.c
index 8243e644..0e7d3489 100644
--- a/stdlib/metamethods.c
+++ b/stdlib/metamethods.c
@@ -4,6 +4,7 @@
#include <string.h>
#include "arrays.h"
+#include "bools.h"
#include "channels.h"
#include "functiontype.h"
#include "metamethods.h"
@@ -15,6 +16,7 @@
#include "util.h"
+#pragma GCC diagnostic ignored "-Wstack-protector"
PUREFUNC public uint64_t generic_hash(const void *obj, const TypeInfo_t *type)
{
switch (type->tag) {
@@ -23,8 +25,54 @@ PUREFUNC public uint64_t generic_hash(const void *obj, const TypeInfo_t *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 EmptyStructInfo: return 0;
- case CustomInfo: case StructInfo: case EnumInfo: case CStringInfo: // These all share the same info
+ 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};
+
+ 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);
@@ -52,8 +100,52 @@ PUREFUNC public int32_t generic_compare(const void *x, const void *y, const Type
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 EmptyStructInfo: return 0;
- case CustomInfo: case StructInfo: case EnumInfo: case CStringInfo: // These all share the same info
+ 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;
+
+ 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);
@@ -73,7 +165,6 @@ PUREFUNC public bool generic_equal(const void *x, const void *y, const TypeInfo_
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 EmptyStructInfo: return true;
case OptionalInfo: {
bool x_is_null = is_null(x, type->OptionalInfo.type);
bool y_is_null = is_null(y, type->OptionalInfo.type);
@@ -81,7 +172,9 @@ PUREFUNC public bool generic_equal(const void *x, const void *y, const TypeInfo_
else if (x_is_null != y_is_null) return false;
else return generic_equal(x, y, type->OptionalInfo.type);
}
- case CustomInfo: case StructInfo: case EnumInfo: case CStringInfo: // These all share the same info
+ 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);
@@ -102,15 +195,64 @@ public Text_t generic_as_text(const void *obj, bool colorize, const TypeInfo_t *
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 EmptyStructInfo: return colorize ?
- Text$concat(Text("\x1b[0;1m"), Text$from_str(type->EmptyStructInfo.name), Text("\x1b[m()"))
- : Text$concat(Text$from_str(type->EmptyStructInfo.name), Text("()"));
- case CustomInfo: case StructInfo: case EnumInfo: case CStringInfo: // These all share the same info
+ case StructInfo: {
+ 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(")"));
+ }
+ 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: errx(1, "Invalid type tag: %d", type->tag);
+ default: fail("Invalid type tag: %d", type->tag);
}
}
diff --git a/stdlib/types.c b/stdlib/types.c
index ebc91563..073bb5b8 100644
--- a/stdlib/types.c
+++ b/stdlib/types.c
@@ -25,7 +25,7 @@ public Text_t Type$as_text(const void *typeinfo, bool colorize, const TypeInfo_t
return Text$from_str(type->TypeInfoInfo.type_str);
}
-public const TypeInfo_t Void$info = {.size=0, .align=0, .tag=EmptyStructInfo};
-public const TypeInfo_t Abort$info = {.size=0, .align=0, .tag=EmptyStructInfo};
+public const TypeInfo_t Void$info = {.size=0, .align=0, .tag=StructInfo};
+public const TypeInfo_t Abort$info = {.size=0, .align=0, .tag=StructInfo};
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/stdlib/types.h b/stdlib/types.h
index 7e113dc0..f9b32af5 100644
--- a/stdlib/types.h
+++ b/stdlib/types.h
@@ -7,18 +7,23 @@
#include "datatypes.h"
-struct TypeInfo_t;
+typedef struct TypeInfo_s TypeInfo_t;
-typedef uint64_t (*hash_fn_t)(const void*, const struct TypeInfo_t*);
-typedef int32_t (*compare_fn_t)(const void*, const void*, const struct TypeInfo_t*);
-typedef bool (*equal_fn_t)(const void*, const void*, const struct TypeInfo_t*);
-typedef Text_t (*text_fn_t)(const void*, bool, const struct 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 TypeInfo_t {
+typedef struct {
+ const char *name;
+ const TypeInfo_t *type;
+} NamedType_t;
+
+struct TypeInfo_s {
int64_t size, align;
struct { // Anonymous tagged union for convenience
- enum { CustomInfo, StructInfo, EnumInfo, PointerInfo, TextInfo, ArrayInfo, ChannelInfo, TableInfo, FunctionInfo,
- OptionalInfo, TypeInfoInfo, OpaqueInfo, EmptyStructInfo, CStringInfo } tag;
+ enum { Invalid, CustomInfo, StructInfo, EnumInfo, PointerInfo, TextInfo, ArrayInfo, ChannelInfo, TableInfo, FunctionInfo,
+ OptionalInfo, TypeInfoInfo, OpaqueInfo, CStringInfo } tag;
union {
struct {
equal_fn_t equal;
@@ -28,16 +33,16 @@ typedef struct TypeInfo_t {
} CustomInfo;
struct {
const char *sigil;
- const struct TypeInfo_t *pointed;
+ const TypeInfo_t *pointed;
} PointerInfo;
struct {
const char *lang;
} TextInfo;
struct {
- const struct TypeInfo_t *item;
+ const TypeInfo_t *item;
} ArrayInfo, ChannelInfo;
struct {
- const struct TypeInfo_t *key, *value;
+ const TypeInfo_t *key, *value;
} TableInfo;
struct {
const char *type_str;
@@ -46,16 +51,24 @@ typedef struct TypeInfo_t {
const char *type_str;
} TypeInfoInfo;
struct {
- const struct TypeInfo_t *type;
+ const TypeInfo_t *type;
} OptionalInfo;
#pragma GCC diagnostic ignored "-Wpedantic"
struct {} OpaqueInfo;
struct {
const char *name;
- } EmptyStructInfo;
+ int num_tags;
+ NamedType_t *tags;
+ } EnumInfo;
+ struct {
+ const char *name;
+ int num_fields;
+ bool is_secret:1;
+ NamedType_t *fields;
+ } StructInfo;
};
};
-} TypeInfo_t;
+};
#define Pointer$info(sigil_expr, pointed_info) &((TypeInfo_t){.size=sizeof(void*), .align=__alignof__(void*), \
.tag=PointerInfo, .PointerInfo={.sigil=sigil_expr, .pointed=pointed_info}})
@@ -73,7 +86,7 @@ typedef struct TypeInfo_t {
.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(t) &((TypeInfo_t){.size=(t)->size, .align=(t)->align, \
+#define Optional$info(_size, _align, t) &((TypeInfo_t){.size=_size, .align=_align, \
.tag=OptionalInfo, .OptionalInfo.type=t})
extern const TypeInfo_t Void$info;
diff --git a/structs.c b/structs.c
index f907d398..14cebb73 100644
--- a/structs.c
+++ b/structs.c
@@ -12,112 +12,6 @@
#include "typecheck.h"
#include "stdlib/util.h"
-static CORD compile_str_method(env_t *env, ast_t *ast)
-{
- auto def = Match(ast, StructDef);
- CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name);
- const char *name = def->name;
- const char *dollar = strrchr(name, '$');
- if (dollar) name = dollar + 1;
- CORD str_func = CORD_asprintf("static Text_t %r$as_text(%r_t *obj, bool use_color) {\n"
- "\tif (!obj) return Text(\"%s\");\n", full_name, full_name, name);
- if (def->secret) {
- CORD_appendf(&str_func, "\treturn use_color ? Text(\"\\x1b[0;1m%s\\x1b[m(\\x1b[2m...\\x1b[m)\") : Text(\"%s(...)\");\n}",
- name, name);
- } else if (def->fields && !def->fields->next) { // Single-member structs don't need to print names:
- type_t *field_type = get_arg_ast_type(env, def->fields);
- CORD field_str = expr_as_text(env, CORD_cat("obj->$", def->fields->name), field_type, "use_color");
- str_func = CORD_all(str_func, "\treturn Text$concat(use_color ? Text(\"\\x1b[0;1m", name, "\\x1b[m(\") : Text(\"", name, "(\"), ",
- field_str, ", Text(\")\"));\n}\n");
- } else {
- CORD_appendf(&str_func, "\treturn Text$concat(use_color ? Text(\"\\x1b[0;1m%s\\x1b[m(\") : Text(\"%s(\")", name, name);
- for (arg_ast_t *field = def->fields; field; field = field->next) {
- type_t *field_type = get_arg_ast_type(env, field);
- CORD field_str = expr_as_text(env, CORD_cat("obj->$", field->name), field_type, "use_color");
- CORD_appendf(&str_func, ", Text(\"%s=\"), %r", field->name, field_str);
- if (field->next) CORD_appendf(&str_func, ", Text(\", \")");
- }
- CORD_appendf(&str_func, ", Text(\")\"));\n}\n");
- }
- return str_func;
-}
-
-static CORD compile_compare_method(env_t *env, ast_t *ast)
-{
- auto def = Match(ast, StructDef);
- CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name);
- CORD cmp_func = CORD_all("static int ", full_name, "$compare(const ", full_name, "_t *x, const ", full_name,
- "_t *y, const TypeInfo_t *info) {\n"
- "(void)info;\n",
- "int diff;\n");
- for (arg_ast_t *field = def->fields; field; field = field->next) {
- type_t *field_type = get_arg_ast_type(env, field);
- switch (field_type->tag) {
- case BigIntType:
- cmp_func = CORD_all(cmp_func, "diff = Int$compare_value(x->$", field->name, ", y->$", field->name, ");");
- break;
- case BoolType: case IntType: case NumType: case PointerType: case FunctionType:
- cmp_func = CORD_all(cmp_func, "diff = (x->$", field->name, " > y->$", field->name, ") - (x->$", field->name, " < y->$", field->name, ");");
- break;
- default:
- cmp_func = CORD_all(cmp_func, "diff = generic_compare(&x->$", field->name, ", &y->$", field->name, ", ",
- compile_type_info(env, field_type), ");\n");
- break;
- }
- cmp_func = CORD_all(cmp_func, "if (diff != 0) return diff;\n");
- }
- cmp_func = CORD_all(cmp_func, "return 0;\n}\n");
- return cmp_func;
-}
-
-static CORD compile_equals_method(env_t *env, ast_t *ast)
-{
- auto def = Match(ast, StructDef);
- CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name);
- CORD eq_func = CORD_all("static bool ", full_name, "$equal(const ", full_name, "_t *x, const ", full_name,
- "_t *y, const TypeInfo_t *info) {\n"
- "(void)info;\n");
- CORD condition = CORD_EMPTY;
- for (arg_ast_t *field = def->fields; field; field = field->next) {
- if (condition != CORD_EMPTY)
- condition = CORD_all(condition, " && ");
- type_t *field_type = get_arg_ast_type(env, field);
- switch (field_type->tag) {
- case BigIntType:
- condition = CORD_all(condition, "Int$equal_value(x->$", field->name, ", y->$", field->name, ")");
- break;
- case BoolType: case IntType: case NumType: case PointerType: case FunctionType:
- condition = CORD_all(condition, "(x->$", field->name, " == y->$", field->name, ")");
- break;
- default:
- condition = CORD_all(condition, "generic_equal(&x->$", field->name, ", &y->$", field->name, ", ",
- compile_type_info(env, field_type), ")");
- break;
- }
- }
- eq_func = CORD_all(eq_func, "return ", condition, ";\n}\n");
- return eq_func;
-}
-
-static CORD compile_hash_method(env_t *env, ast_t *ast)
-{
- auto def = Match(ast, StructDef);
- CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name);
- CORD hash_func = CORD_all("static uint64_t ", full_name, "$hash(const ", full_name, "_t *obj, const TypeInfo_t *info) {\n"
- "(void)info;\n"
- "uint64_t field_hashes[] = {");
- for (arg_ast_t *field = def->fields; field; field = field->next) {
- type_t *field_type = get_arg_ast_type(env, field);
- if (field_type->tag == BoolType) // Bools can be bit fields, so you can't use *obj->field there:
- hash_func = CORD_all(hash_func, "\n(uint32_t)(obj->$", field->name, "),");
- else
- hash_func = CORD_all(hash_func, "\ngeneric_hash(&obj->$", field->name, ", ", compile_type_info(env, field_type), "),");
- }
- hash_func = CORD_all(hash_func, "};\n"
- "return siphash24((void*)&field_hashes, sizeof(field_hashes));\n}\n");
- return hash_func;
-}
-
void compile_struct_def(env_t *env, ast_t *ast)
{
auto def = Match(ast, StructDef);
@@ -125,48 +19,25 @@ void compile_struct_def(env_t *env, ast_t *ast)
type_t *t = Table$str_get(*env->types, def->name);
assert(t && t->tag == StructType);
- auto struct_ = Match(t, StructType);
- if (def->fields) {
- CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {%zu, %zu, {.tag=StructInfo, .CustomInfo={",
- full_name, type_size(t), type_align(t));
-
- typeinfo = CORD_all(typeinfo, ".as_text=(void*)", full_name, "$as_text, ");
- env->code->funcs = CORD_all(env->code->funcs, compile_str_method(env, ast));
+ int num_fields = 0;
+ for (arg_ast_t *f = def->fields; f; f = f->next)
+ num_fields += 1;
+ const char *short_name = def->name;
+ if (strchr(short_name, '$'))
+ short_name = strrchr(short_name, '$') + 1;
- if (struct_->fields && !struct_->fields->next) { // Single member, can just use its methods
- type_t *member_t = struct_->fields->type;
- switch (member_t->tag) {
- case IntType: case NumType:
- typeinfo = CORD_all(typeinfo, ".compare=(void*)", type_to_cord(member_t), "$compare, "
- ".equal=(void*)", type_to_cord(member_t), "$equal, ");
- goto got_methods;
- case BigIntType: case TextType:
- typeinfo = CORD_all(typeinfo, ".hash=(void*)", type_to_cord(member_t), "$hash", ", ",
- ".compare=(void*)", type_to_cord(member_t), "$compare, "
- ".equal=(void*)", type_to_cord(member_t), "$equal, ");
- goto got_methods;
- case BoolType: goto got_methods;
- default: break;
- }
- }
- if (struct_->fields) {
- env->code->funcs = CORD_all(env->code->funcs, compile_compare_method(env, ast),
- compile_equals_method(env, ast), compile_hash_method(env, ast));
- typeinfo = CORD_all(
- typeinfo,
- ".compare=(void*)", full_name, "$compare, "
- ".equal=(void*)", full_name, "$equal, "
- ".hash=(void*)", full_name, "$hash");
- }
- got_methods:;
- typeinfo = CORD_cat(typeinfo, "}}};\n");
- env->code->typeinfos = CORD_all(env->code->typeinfos, typeinfo);
- } else {
- // If there are no fields, we can use an EmptyStructInfo typeinfo, which generates less code:
- CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {%zu, %zu, {.tag=EmptyStructInfo, .EmptyStructInfo.name=%r}};\n",
- full_name, type_size(t), type_align(t), CORD_quoted(def->name));
- env->code->typeinfos = CORD_all(env->code->typeinfos, typeinfo);
+ env->code->typeinfos = CORD_all("public const TypeInfo_t ", full_name, ";\n", env->code->typeinfos);
+ CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {%zu, %zu, .tag=StructInfo, .StructInfo.name=\"%s\"%s, "
+ ".StructInfo.num_fields=%ld, .StructInfo.fields=(NamedType_t[%d]){",
+ full_name, type_size(t), type_align(t), short_name, def->secret ? ", .StructInfo.is_secret=true" : "",
+ num_fields, num_fields);
+ for (arg_ast_t *f = def->fields; f; f = f->next) {
+ type_t *field_type = get_arg_ast_type(env, f);
+ typeinfo = CORD_all(typeinfo, "{\"", f->name, "\", ", compile_type_info(env, field_type), "}");
+ if (f->next) typeinfo = CORD_all(typeinfo, ", ");
}
+ typeinfo = CORD_all(typeinfo, "}};\n");
+ env->code->typeinfos = CORD_all(env->code->typeinfos, typeinfo);
compile_namespace(env, def->name, def->namespace);
}
diff --git a/types.c b/types.c
index 17009f9e..a0e347ca 100644
--- a/types.c
+++ b/types.c
@@ -431,7 +431,7 @@ PUREFUNC size_t type_size(type_t *t)
type_t *nonnull = Match(t, OptionalType)->type;
switch (nonnull->tag) {
case IntType:
- switch (Match(t, IntType)->bits) {
+ switch (Match(nonnull, IntType)->bits) {
case TYPE_IBITS64: return sizeof(OptionalInt64_t);
case TYPE_IBITS32: return sizeof(OptionalInt32_t);
case TYPE_IBITS16: return sizeof(OptionalInt16_t);
@@ -511,7 +511,7 @@ PUREFUNC size_t type_align(type_t *t)
type_t *nonnull = Match(t, OptionalType)->type;
switch (nonnull->tag) {
case IntType:
- switch (Match(t, IntType)->bits) {
+ switch (Match(nonnull, IntType)->bits) {
case TYPE_IBITS64: return __alignof__(OptionalInt64_t);
case TYPE_IBITS32: return __alignof__(OptionalInt32_t);
case TYPE_IBITS16: return __alignof__(OptionalInt16_t);