diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2024-10-08 13:35:18 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2024-10-08 13:35:18 -0400 |
| commit | 954ed42934ad76ac101b27b6ff06be383ad5dcd2 (patch) | |
| tree | f0757e0e9bb87845d961be52a49c8f894e23e14c /structs.c | |
| parent | 6b9055db7c03c09654c0605b96a37d50bf563fa9 (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.c | 163 |
1 files changed, 17 insertions, 146 deletions
@@ -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); } |
