From fb2d7b5379663e929ffabfbd8428de5b35ad67c4 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 11 Mar 2025 17:03:03 -0400 Subject: [PATCH] Add extern structs --- ast.h | 2 +- compile.c | 18 ++++++++++++++---- enums.c | 4 +++- parse.c | 7 +++++-- structs.c | 22 ++++++++-------------- structs.h | 2 +- typecheck.c | 11 ++--------- types.h | 3 ++- 8 files changed, 36 insertions(+), 33 deletions(-) diff --git a/ast.h b/ast.h index 5ae03bb..76485a2 100644 --- a/ast.h +++ b/ast.h @@ -293,7 +293,7 @@ struct ast_s { const char *name; arg_ast_t *fields; ast_t *namespace; - bool secret:1; + bool secret:1, external:1; } StructDef; struct { const char *name; diff --git a/compile.c b/compile.c index 4c97c57..203c44a 100644 --- a/compile.c +++ b/compile.c @@ -547,6 +547,7 @@ CORD compile_type(type_t *t) case PointerType: return CORD_cat(compile_type(Match(t, PointerType)->pointed), "*"); case StructType: { auto s = Match(t, StructType); + if (s->external) return CORD_all("struct ", s->name); return CORD_all("struct ", namespace_prefix(s->env, s->env->namespace->parent), s->name, "$$struct"); } case EnumType: { @@ -3395,10 +3396,15 @@ CORD compile(env_t *env, ast_t *ast) return "\"\""; else if (call->args->value->tag == TextJoin && Match(call->args->value, TextJoin)->children->next == NULL) return compile_string_literal(Match(Match(call->args->value, TextJoin)->children->ast, TextLiteral)->cord); - return CORD_all("Text$as_c_string(", expr_as_text(env, compile(env, call->args->value), actual, "no"), ")"); - } else { - code_err(ast, "I could not find a constructor matching these arguments for %T", t); + return CORD_all("Text$as_c_string(", expr_as_text(compile(env, call->args->value), actual, "no"), ")"); + } else if (t->tag == StructType) { + auto struct_ = Match(t, StructType); + if (!struct_->opaque && is_valid_call(env, struct_->fields, call->args, true)) { + return CORD_all("((", compile_type(t), "){", + compile_arguments(env, ast, struct_->fields, call->args), "})"); + } } + code_err(ast, "I could not find a constructor matching these arguments for %T", t); } else if (fn_t->tag == ClosureType) { fn_t = Match(fn_t, ClosureType)->fn; arg_t *type_args = Match(fn_t, FunctionType)->args; @@ -4258,7 +4264,9 @@ CORD compile_top_level_code(env_t *env, ast_t *ast) } case StructDef: { auto def = Match(ast, StructDef); - CORD code = compile_struct_typeinfo(env, ast); + type_t *t = Table$str_get(*env->types, def->name); + assert(t && t->tag == StructType); + CORD code = compile_struct_typeinfo(env, t, def->name, def->fields, def->secret); env_t *ns_env = namespace_env(env, def->name); return CORD_all(code, def->namespace ? compile_top_level_code(ns_env, def->namespace) : CORD_EMPTY); } @@ -4277,6 +4285,7 @@ CORD compile_top_level_code(env_t *env, ast_t *ast) env_t *ns_env = namespace_env(env, def->name); return CORD_all(code, def->namespace ? compile_top_level_code(ns_env, def->namespace) : CORD_EMPTY); } + case Extern: return CORD_EMPTY; case Block: { CORD code = CORD_EMPTY; for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) { @@ -4536,6 +4545,7 @@ static void _make_typedefs(compile_typedef_info_t *info, ast_t *ast) { if (ast->tag == StructDef) { auto def = Match(ast, StructDef); + if (def->external) return; CORD full_name = CORD_cat(namespace_prefix(info->env, info->env->namespace), def->name); *info->header = CORD_all(*info->header, "typedef struct ", full_name, "$$struct ", full_name, "$$type;\n"); } else if (ast->tag == EnumDef) { diff --git a/enums.c b/enums.c index 9cb7a66..811b42b 100644 --- a/enums.c +++ b/enums.c @@ -24,8 +24,10 @@ CORD compile_enum_typeinfo(env_t *env, ast_t *ast) if (!tag->fields) continue; const char *tag_name = heap_strf("%s$%s", def->name, tag->name); + type_t *tag_type = Table$str_get(*env->types, tag_name); + assert(tag_type && tag_type->tag == StructType); member_typeinfos = CORD_all( - member_typeinfos, compile_struct_typeinfo(env, WrapAST(ast, StructDef, .name=tag_name, .fields=tag->fields, .secret=tag->secret))); + member_typeinfos, compile_struct_typeinfo(env, tag_type, tag_name, tag->fields, tag->secret)); } int num_tags = 0; diff --git a/parse.c b/parse.c index ed8d041..81b239a 100644 --- a/parse.c +++ b/parse.c @@ -2111,12 +2111,14 @@ PARSER(parse_struct_def) { arg_ast_t *fields = parse_args(ctx, &pos); whitespace(&pos); - bool secret = false; + bool secret = false, external = false; if (match(&pos, ";")) { // Extra flags whitespace(&pos); for (;;) { if (match_word(&pos, "secret")) secret = true; + else if (match_word(&pos, "extern")) + external = true; else break; @@ -2139,7 +2141,8 @@ PARSER(parse_struct_def) { } if (!namespace) namespace = NewAST(ctx->file, pos, pos, Block, .statements=NULL); - return NewAST(ctx->file, start, pos, StructDef, .name=name, .fields=fields, .namespace=namespace, .secret=secret); + return NewAST(ctx->file, start, pos, StructDef, .name=name, .fields=fields, .namespace=namespace, + .secret=secret, .external=external); } PARSER(parse_enum_def) { diff --git a/structs.c b/structs.c index cf86e31..27f3a29 100644 --- a/structs.c +++ b/structs.c @@ -12,17 +12,14 @@ #include "typecheck.h" #include "stdlib/util.h" -CORD compile_struct_typeinfo(env_t *env, ast_t *ast) +CORD compile_struct_typeinfo(env_t *env, type_t *t, const char *name, arg_ast_t *fields, bool is_secret) { - auto def = Match(ast, StructDef); - CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name); + CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), name); - type_t *t = Table$str_get(*env->types, def->name); - assert(t && t->tag == StructType); int num_fields = 0; - for (arg_ast_t *f = def->fields; f; f = f->next) + for (arg_ast_t *f = fields; f; f = f->next) num_fields += 1; - const char *short_name = def->name; + const char *short_name = name; if (strchr(short_name, '$')) short_name = strrchr(short_name, '$') + 1; @@ -30,11 +27,11 @@ CORD compile_struct_typeinfo(env_t *env, ast_t *ast) CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r$$info = {.size=%zu, .align=%zu, .metamethods=%s, " ".tag=StructInfo, .StructInfo.name=\"%s\"%s, " ".StructInfo.num_fields=%ld", - full_name, type_size(t), type_align(t), metamethods, short_name, def->secret ? ", .StructInfo.is_secret=true" : "", + full_name, type_size(t), type_align(t), metamethods, short_name, is_secret ? ", .StructInfo.is_secret=true" : "", num_fields); - if (def->fields) { + if (fields) { typeinfo = CORD_asprintf("%r, .StructInfo.fields=(NamedType_t[%d]){", typeinfo, num_fields); - for (arg_ast_t *f = def->fields; f; f = f->next) { + for (arg_ast_t *f = fields; f; f = f->next) { type_t *field_type = get_arg_ast_type(env, f); typeinfo = CORD_all(typeinfo, "{\"", f->name, "\", ", compile_type_info(field_type), "}"); if (f->next) typeinfo = CORD_all(typeinfo, ", "); @@ -61,11 +58,8 @@ CORD compile_struct_header(env_t *env, ast_t *ast) "struct ", full_name, "$$struct {\n", fields, "};\n", - "DEFINE_OPTIONAL_TYPE(", full_name, "$$type, ", heap_strf("%zu", unpadded_struct_size(t)), + "DEFINE_OPTIONAL_TYPE(", compile_type(t), ", ", heap_strf("%zu", unpadded_struct_size(t)), ", ", namespace_prefix(env, env->namespace), "$Optional", def->name, "$$type);\n" - // Constructor macro: - "#define ", namespace_prefix(env, env->namespace), def->name, - "(...) ((", namespace_prefix(env, env->namespace), def->name, "$$type){__VA_ARGS__})\n" "extern const TypeInfo_t ", full_name, "$$info;\n"); } diff --git a/structs.h b/structs.h index d38f7c4..b189b0d 100644 --- a/structs.h +++ b/structs.h @@ -7,7 +7,7 @@ #include "ast.h" #include "environment.h" -CORD compile_struct_typeinfo(env_t *env, ast_t *ast); +CORD compile_struct_typeinfo(env_t *env, type_t *t, const char *name, arg_ast_t *fields, bool is_secret); CORD compile_struct_header(env_t *env, ast_t *ast); // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/typecheck.c b/typecheck.c index 3f95168..c69432d 100644 --- a/typecheck.c +++ b/typecheck.c @@ -232,7 +232,7 @@ void prebind_statement(env_t *env, ast_t *statement) code_err(statement, "A %T called '%s' has already been defined", get_binding(env, def->name)->type, def->name); env_t *ns_env = namespace_env(env, def->name); - type_t *type = Type(StructType, .name=def->name, .opaque=true, .env=ns_env); // placeholder + type_t *type = Type(StructType, .name=def->name, .opaque=true, .external=def->external, .env=ns_env); // placeholder Table$str_set(env->types, def->name, type); set_binding(env, def->name, Type(TypeInfoType, .name=def->name, .type=type, .env=ns_env), CORD_all(namespace_prefix(env, env->namespace), def->name, "$$info")); @@ -355,15 +355,8 @@ void bind_statement(env_t *env, ast_t *statement) type->__data.StructType.fields = fields; // populate placeholder type->__data.StructType.opaque = false; - // Default constructor: - type_t *constructor_t = Type(FunctionType, .args=fields, .ret=type); - Array$insert(&ns_env->namespace->constructors, - new(binding_t, .code=CORD_all(namespace_prefix(env, env->namespace), def->name), .type=constructor_t), - I(0), sizeof(binding_t)); - - for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next) { + for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next) bind_statement(ns_env, stmt->ast); - } break; } case EnumDef: { diff --git a/types.h b/types.h index e72aa3e..780c4d5 100644 --- a/types.h +++ b/types.h @@ -91,6 +91,7 @@ struct type_s { } SetType; struct { type_t *key_type, *value_type; + struct env_s *env; ast_t *default_value; } TableType; struct { @@ -108,7 +109,7 @@ struct type_s { const char *name; arg_t *fields; struct env_s *env; - bool opaque; + bool opaque:1, external:1; } StructType; struct { const char *name;