diff options
| author | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-16 21:22:20 +0000 |
|---|---|---|
| committer | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-16 21:22:20 +0000 |
| commit | a5d5a97818fb4fbd26c4fb25a5c410b1a60a1b18 (patch) | |
| tree | 41b4fa9a4952db1496127031f22de988b7d45418 /src | |
| parent | 73d0a63df903445ecd32f5b95bb3ff34e3dc2976 (diff) | |
Added multi-type generics support.
Diffstat (limited to 'src')
| -rw-r--r-- | src/ast/ast.h | 3 | ||||
| -rw-r--r-- | src/parser/parser.h | 2 | ||||
| -rw-r--r-- | src/parser/parser_stmt.c | 57 | ||||
| -rw-r--r-- | src/parser/parser_type.c | 105 | ||||
| -rw-r--r-- | src/parser/parser_utils.c | 96 |
5 files changed, 214 insertions, 49 deletions
diff --git a/src/ast/ast.h b/src/ast/ast.h index e38453c..1b83a91 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -376,7 +376,8 @@ struct ASTNode char *name; ASTNode *fields; int is_template; - char *generic_param; + char **generic_params; // Array of generic parameter names (for example, ["K", "V"]) + int generic_param_count; // Number of generic parameters char *parent; int is_union; int is_packed; // @packed attribute. diff --git a/src/parser/parser.h b/src/parser/parser.h index 1e047b4..ca8c447 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -321,6 +321,8 @@ void add_to_global_list(ParserContext *ctx, ASTNode *node); void register_builtins(ParserContext *ctx); void add_instantiated_func(ParserContext *ctx, ASTNode *fn); void instantiate_generic(ParserContext *ctx, const char *name, const char *concrete_type, Token t); +void instantiate_generic_multi(ParserContext *ctx, const char *name, char **args, int arg_count, + Token t); char *sanitize_mangled_name(const char *name); void register_type_alias(ParserContext *ctx, const char *alias, const char *original); const char *find_type_alias(ParserContext *ctx, const char *alias); diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index f391f97..ac2d622 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -3238,14 +3238,33 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) Token n = lexer_next(l); char *name = token_strdup(n); - // Generic Param <T> - char *gp = NULL; + // Generic Params <T> or <K, V> + char **gps = NULL; + int gp_count = 0; if (lexer_peek(l).type == TOK_LANGLE) { - lexer_next(l); - Token g = lexer_next(l); - gp = token_strdup(g); - lexer_next(l); + lexer_next(l); // eat < + while (1) + { + Token g = lexer_next(l); + gps = realloc(gps, sizeof(char *) * (gp_count + 1)); + gps[gp_count++] = token_strdup(g); + + Token next = lexer_peek(l); + if (next.type == TOK_COMMA) + { + lexer_next(l); // eat , + } + else if (next.type == TOK_RANGLE) + { + lexer_next(l); // eat > + break; + } + else + { + zpanic_at(next, "Expected ',' or '>' in generic parameter list"); + } + } register_generic(ctx, name); } @@ -3255,16 +3274,13 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) lexer_next(l); ASTNode *n = ast_create(NODE_STRUCT); n->strct.name = name; - n->strct.is_template = (gp != NULL); - n->strct.generic_param = gp; + n->strct.is_template = (gp_count > 0); + n->strct.generic_params = gps; + n->strct.generic_param_count = gp_count; n->strct.is_union = is_union; n->strct.fields = NULL; n->strct.is_incomplete = 1; - if (!gp) - { - add_to_struct_list(ctx, n); - } return n; } @@ -3390,7 +3406,7 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) add_to_struct_list(ctx, node); // Auto-prefix struct name if in module context - if (ctx->current_module_prefix && !gp) + if (ctx->current_module_prefix && gp_count == 0) { // Don't prefix generic templates char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(name) + 2); sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name); @@ -3403,24 +3419,25 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) // Initialize Type Info so we can track traits (like Drop) node->type_info = type_new(TYPE_STRUCT); node->type_info->name = xstrdup(name); - if (gp) + if (gp_count > 0) { node->type_info->kind = TYPE_GENERIC; // TODO: track generic params } node->strct.fields = h; - node->strct.generic_param = gp; + node->strct.generic_params = gps; + node->strct.generic_param_count = gp_count; node->strct.is_union = is_union; - if (gp) + if (gp_count > 0) { node->strct.is_template = 1; register_template(ctx, name, node); } // Register definition for 'use' lookups and LSP - if (!gp) + if (gp_count == 0) { register_struct_def(ctx, name, node); } @@ -3430,10 +3447,10 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) Type *parse_type_obj(ParserContext *ctx, Lexer *l) { - // 1. Parse the base type (int, U32, MyStruct, etc.) + // Parse the base type (int, U32, MyStruct, etc.) Type *t = parse_type_base(ctx, l); - // 2. Handle Pointers (e.g. int***) + // Handle Pointers while (lexer_peek(l).type == TOK_OP && lexer_peek(l).start[0] == '*') { lexer_next(l); // eat * @@ -3443,8 +3460,6 @@ Type *parse_type_obj(ParserContext *ctx, Lexer *l) t = ptr; } - // (Optional: You can add array parsing here later if needed) - return t; } diff --git a/src/parser/parser_type.c b/src/parser/parser_type.c index 3b4abf3..7dded00 100644 --- a/src/parser/parser_type.c +++ b/src/parser/parser_type.c @@ -382,44 +382,99 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l) Type *ty = type_new(TYPE_STRUCT); ty->name = name; - // Handle Generics <T> + // Handle Generics <T> or <K, V> if (lexer_peek(l).type == TOK_LANGLE) { lexer_next(l); // eat < - Type *arg = parse_type_formal(ctx, l); + Type *first_arg = parse_type_formal(ctx, l); + char *first_arg_str = type_to_string(first_arg); - // Handle nested generics like Vec<Vec<int>> where >> is tokenized as one - // op + // Check for multi-arg: <K, V> Token next_tok = lexer_peek(l); - if (next_tok.type == TOK_RANGLE) - { - lexer_next(l); // Consume > - } - else if (next_tok.type == TOK_OP && next_tok.len == 2 && - strncmp(next_tok.start, ">>", 2) == 0) + if (next_tok.type == TOK_COMMA) { - // Split >> into two > tokens - // Consume the first > by advancing lexer manually - l->pos += 1; - l->col += 1; + // Multi-arg case + char **args = xmalloc(sizeof(char *) * 8); + int arg_count = 0; + args[arg_count++] = xstrdup(first_arg_str); + + while (lexer_peek(l).type == TOK_COMMA) + { + lexer_next(l); // eat , + Type *arg = parse_type_formal(ctx, l); + char *arg_str = type_to_string(arg); + args = realloc(args, sizeof(char *) * (arg_count + 1)); + args[arg_count++] = xstrdup(arg_str); + free(arg_str); + } + + // Consume > + next_tok = lexer_peek(l); + if (next_tok.type == TOK_RANGLE) + { + lexer_next(l); + } + else if (next_tok.type == TOK_OP && next_tok.len == 2 && + strncmp(next_tok.start, ">>", 2) == 0) + { + l->pos += 1; + l->col += 1; + } + else + { + zpanic_at(t, "Expected > after generic"); + } + + // Call multi-arg instantiation + instantiate_generic_multi(ctx, name, args, arg_count, t); + + // Build mangled name + char mangled[256]; + strcpy(mangled, name); + for (int i = 0; i < arg_count; i++) + { + char *clean = sanitize_mangled_name(args[i]); + strcat(mangled, "_"); + strcat(mangled, clean); + free(clean); + free(args[i]); + } + free(args); + + free(ty->name); + ty->name = xstrdup(mangled); } else { - zpanic_at(t, "Expected > after generic"); - } + // Single-arg case - PRESERVE ORIGINAL FLOW EXACTLY + if (next_tok.type == TOK_RANGLE) + { + lexer_next(l); // Consume > + } + else if (next_tok.type == TOK_OP && next_tok.len == 2 && + strncmp(next_tok.start, ">>", 2) == 0) + { + // Split >> into two > tokens + l->pos += 1; + l->col += 1; + } + else + { + zpanic_at(t, "Expected > after generic"); + } - char *arg_str = type_to_string(arg); - instantiate_generic(ctx, name, arg_str, t); + instantiate_generic(ctx, name, first_arg_str, t); - char *clean_arg = sanitize_mangled_name(arg_str); - char mangled[256]; - sprintf(mangled, "%s_%s", name, clean_arg); - free(clean_arg); + char *clean_arg = sanitize_mangled_name(first_arg_str); + char mangled[256]; + sprintf(mangled, "%s_%s", name, clean_arg); + free(clean_arg); - free(ty->name); - ty->name = xstrdup(mangled); - free(arg_str); + free(ty->name); + ty->name = xstrdup(mangled); + } + free(first_arg_str); ty->kind = TYPE_STRUCT; ty->args = NULL; ty->arg_count = 0; diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c index 6129990..51d89d7 100644 --- a/src/parser/parser_utils.c +++ b/src/parser/parser_utils.c @@ -1948,8 +1948,11 @@ void instantiate_generic(ParserContext *ctx, const char *tpl, const char *arg, T ASTNode *i = ast_create(NODE_STRUCT); i->strct.name = xstrdup(m); i->strct.is_template = 0; - i->strct.fields = copy_fields_replacing(ctx, t->struct_node->strct.fields, - t->struct_node->strct.generic_param, arg); + // Use first generic param for substitution (single-param backward compat) + const char *gp = (t->struct_node->strct.generic_param_count > 0) + ? t->struct_node->strct.generic_params[0] + : "T"; + i->strct.fields = copy_fields_replacing(ctx, t->struct_node->strct.fields, gp, arg); struct_node_copy = i; register_struct_def(ctx, m, i); } @@ -2004,6 +2007,95 @@ void instantiate_generic(ParserContext *ctx, const char *tpl, const char *arg, T } } +void instantiate_generic_multi(ParserContext *ctx, const char *tpl, char **args, int arg_count, + Token token) +{ + // Build mangled name from all args + char m[256]; + strcpy(m, tpl); + for (int i = 0; i < arg_count; i++) + { + char *clean = sanitize_mangled_name(args[i]); + strcat(m, "_"); + strcat(m, clean); + free(clean); + } + + // Check if already instantiated + Instantiation *c = ctx->instantiations; + while (c) + { + if (strcmp(c->name, m) == 0) + { + return; // Already done + } + c = c->next; + } + + // Find the template + GenericTemplate *t = ctx->templates; + while (t) + { + if (strcmp(t->name, tpl) == 0) + { + break; + } + t = t->next; + } + if (!t) + { + zpanic_at(token, "Unknown generic: %s", tpl); + } + + // Register instantiation first (to break cycles) + Instantiation *ni = xmalloc(sizeof(Instantiation)); + ni->name = xstrdup(m); + ni->template_name = xstrdup(tpl); + ni->concrete_arg = (arg_count > 0) ? xstrdup(args[0]) : xstrdup("T"); + ni->struct_node = NULL; + ni->next = ctx->instantiations; + ctx->instantiations = ni; + + if (t->struct_node->type == NODE_STRUCT) + { + ASTNode *i = ast_create(NODE_STRUCT); + i->strct.name = xstrdup(m); + i->strct.is_template = 0; + + // Copy fields with sequential substitutions for each param + ASTNode *fields = t->struct_node->strct.fields; + int param_count = t->struct_node->strct.generic_param_count; + + // Perform substitution for each param (simple approach: copy for first param, then replace + // in-place) + if (param_count > 0 && arg_count > 0) + { + // First substitution + i->strct.fields = copy_fields_replacing( + ctx, fields, t->struct_node->strct.generic_params[0], args[0]); + + // Subsequent substitutions (for params B, C, etc.) + for (int j = 1; j < param_count && j < arg_count; j++) + { + ASTNode *tmp = copy_fields_replacing( + ctx, i->strct.fields, t->struct_node->strct.generic_params[j], args[j]); + // This leaks prev fields, but that's acceptable for now, still, TODO. + i->strct.fields = tmp; + } + } + else + { + i->strct.fields = copy_fields_replacing(ctx, fields, "T", "int"); + } + + ni->struct_node = i; + register_struct_def(ctx, m, i); + + i->next = ctx->instantiated_structs; + ctx->instantiated_structs = i; + } +} + int is_file_imported(ParserContext *ctx, const char *p) { ImportedFile *c = ctx->imported_files; |
