aboutsummaryrefslogtreecommitdiff
path: root/structs.c
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-10-08 13:35:18 -0400
committerBruce Hill <bruce@bruce-hill.com>2024-10-08 13:35:18 -0400
commit954ed42934ad76ac101b27b6ff06be383ad5dcd2 (patch)
treef0757e0e9bb87845d961be52a49c8f894e23e14c /structs.c
parent6b9055db7c03c09654c0605b96a37d50bf563fa9 (diff)
Simplify enum/struct codegen by using reusable general-purpose
metamethods for structs/enums instead of metamethod codegen for each struct/enum defined.
Diffstat (limited to 'structs.c')
-rw-r--r--structs.c163
1 files changed, 17 insertions, 146 deletions
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);
}