summaryrefslogtreecommitdiff
path: root/src/parser
diff options
context:
space:
mode:
authorZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-28 19:52:37 +0000
committerZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-28 19:52:37 +0000
commitfd692ab7bb9f7b1e8f5d878a16154a4a03d0f6f9 (patch)
treee01847818752b226826405fca5f6e69c0a63a608 /src/parser
parentf8d9b233952357d327e856100835adf3cef47f23 (diff)
Opaque structs and aliases + some improvements
Diffstat (limited to 'src/parser')
-rw-r--r--src/parser/parser.h26
-rw-r--r--src/parser/parser_core.c30
-rw-r--r--src/parser/parser_decl.c64
-rw-r--r--src/parser/parser_expr.c133
-rw-r--r--src/parser/parser_struct.c12
-rw-r--r--src/parser/parser_type.c19
-rw-r--r--src/parser/parser_utils.c14
7 files changed, 266 insertions, 32 deletions
diff --git a/src/parser/parser.h b/src/parser/parser.h
index cf57971..262c359 100644
--- a/src/parser/parser.h
+++ b/src/parser/parser.h
@@ -261,6 +261,8 @@ typedef struct TypeAlias
char *alias; ///< New type name.
char *original_type; ///< Original type.
struct TypeAlias *next;
+ int is_opaque;
+ char *defined_in_file;
} TypeAlias;
/**
@@ -514,12 +516,9 @@ char *sanitize_mangled_name(const char *name);
/**
* @brief Registers a type alias.
*/
-void register_type_alias(ParserContext *ctx, const char *alias, const char *original);
-
-/**
- * @brief Finds a type alias.
- */
-const char *find_type_alias(ParserContext *ctx, const char *alias);
+TypeAlias *find_type_alias_node(ParserContext *ctx, const char *name);
+void register_type_alias(ParserContext *ctx, const char *alias, const char *original, int is_opaque,
+ const char *defined_in_file);
/**
* @brief Registers an implementation.
@@ -681,10 +680,6 @@ void register_selective_import(ParserContext *ctx, const char *symbol, const cha
SelectiveImport *find_selective_import(ParserContext *ctx, const char *name);
// Type Aliases
-/**
- * @brief Registers a type alias.
- */
-void register_type_alias(ParserContext *ctx, const char *alias, const char *original);
/**
* @brief Finds a type alias.
@@ -741,6 +736,11 @@ FuncSig *find_func(ParserContext *ctx, const char *name);
Type *parse_type_formal(ParserContext *ctx, Lexer *l);
/**
+ * @brief Checks compatibility of opaque aliases (allows access within defining file).
+ */
+int check_opaque_alias_compat(ParserContext *ctx, Type *a, Type *b);
+
+/**
* @brief Parses a type.
*/
char *parse_type(ParserContext *ctx, Lexer *l);
@@ -889,7 +889,7 @@ ASTNode *parse_def(ParserContext *ctx, Lexer *l);
/**
* @brief Parses a type alias.
*/
-ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l);
+ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l, int is_opaque);
/**
* @brief Parses a function definition.
@@ -899,7 +899,7 @@ ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async);
/**
* @brief Parses a struct definition.
*/
-ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union);
+ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union, int is_opaque);
/**
* @brief Parses an enum definition.
@@ -963,4 +963,4 @@ char *patch_self_args(const char *args, const char *struct_name);
*/
ASTNode *parse_program_nodes(ParserContext *ctx, Lexer *l);
-#endif // PARSER_H
+#endif // PARSER_H \ No newline at end of file
diff --git a/src/parser/parser_core.c b/src/parser/parser_core.c
index ba6f2fe..43137b1 100644
--- a/src/parser/parser_core.c
+++ b/src/parser/parser_core.c
@@ -338,7 +338,7 @@ ASTNode *parse_program_nodes(ParserContext *ctx, Lexer *l)
}
else if (0 == strncmp(t.start, "struct", 6) && 6 == t.len)
{
- s = parse_struct(ctx, l, 0);
+ s = parse_struct(ctx, l, 0, 0);
if (s && s->type == NODE_STRUCT)
{
s->strct.is_packed = attr_packed;
@@ -436,7 +436,7 @@ ASTNode *parse_program_nodes(ParserContext *ctx, Lexer *l)
}
else if (0 == strncmp(t.start, "type", 4) && 4 == t.len)
{
- s = parse_type_alias(ctx, l);
+ s = parse_type_alias(ctx, l, 0);
}
else if (0 == strncmp(t.start, "raw", 3) && 3 == t.len)
{
@@ -482,9 +482,31 @@ ASTNode *parse_program_nodes(ParserContext *ctx, Lexer *l)
lexer_next(l);
}
}
+ else if (t.type == TOK_OPAQUE)
+ {
+ lexer_next(l); // eat opaque
+ Token next = lexer_peek(l);
+ if (0 == strncmp(next.start, "struct", 6) && 6 == next.len)
+ {
+ s = parse_struct(ctx, l, 0, 1);
+ if (s && s->type == NODE_STRUCT)
+ {
+ s->strct.is_packed = attr_packed;
+ s->strct.align = attr_align;
+ }
+ }
+ else if (next.type == TOK_ALIAS)
+ {
+ s = parse_type_alias(ctx, l, 1);
+ }
+ else
+ {
+ zpanic_at(next, "Expected 'struct' or 'alias' after 'opaque'");
+ }
+ }
else if (t.type == TOK_ALIAS)
{
- s = parse_type_alias(ctx, l);
+ s = parse_type_alias(ctx, l, 0);
}
else if (t.type == TOK_ASYNC)
{
@@ -506,7 +528,7 @@ ASTNode *parse_program_nodes(ParserContext *ctx, Lexer *l)
else if (t.type == TOK_UNION)
{
- s = parse_struct(ctx, l, 1);
+ s = parse_struct(ctx, l, 1, 0);
}
else if (t.type == TOK_TRAIT)
{
diff --git a/src/parser/parser_decl.c b/src/parser/parser_decl.c
index 98f46e1..c96ca36 100644
--- a/src/parser/parser_decl.c
+++ b/src/parser/parser_decl.c
@@ -579,6 +579,10 @@ ASTNode *parse_var_decl(ParserContext *ctx, Lexer *l)
{
type_obj->inner = init->type_info->inner; // Shallow copy for inner
}
+ if (init->type_info->kind == TYPE_ALIAS)
+ {
+ type_obj->alias = init->type_info->alias;
+ }
// Copy function type args for lambda/closure support
if (init->type_info->args && init->type_info->arg_count > 0)
{
@@ -631,6 +635,59 @@ ASTNode *parse_var_decl(ParserContext *ctx, Lexer *l)
// Register in symbol table with actual token
add_symbol_with_token(ctx, name, type, type_obj, name_tok);
+ if (init && type_obj)
+ {
+ Type *t = init->type_info;
+ if (!t && init->type == NODE_EXPR_VAR)
+ {
+ t = find_symbol_type_info(ctx, init->var_ref.name);
+ }
+
+ // Literal type construction for validation
+ Type *temp_literal_type = NULL;
+ if (!t && init->type == NODE_EXPR_LITERAL)
+ {
+ if (init->literal.type_kind == LITERAL_INT)
+ {
+ temp_literal_type = type_new(TYPE_INT);
+ }
+ else if (init->literal.type_kind == LITERAL_FLOAT)
+ {
+ temp_literal_type = type_new(TYPE_FLOAT);
+ }
+ else if (init->literal.type_kind == LITERAL_STRING)
+ {
+ temp_literal_type = type_new(TYPE_STRING);
+ }
+ else if (init->literal.type_kind == LITERAL_CHAR)
+ {
+ temp_literal_type = type_new(TYPE_CHAR);
+ }
+ t = temp_literal_type;
+ }
+
+ // Special case for literals: if implicit conversion works
+ if (t && !type_eq(type_obj, t))
+ {
+ // Allow integer compatibility if types are roughly ints (lax check in type_eq handles
+ // most, but let's be safe)
+ if (!check_opaque_alias_compat(ctx, type_obj, t))
+ {
+ char *expected = type_to_string(type_obj);
+ char *got = type_to_string(t);
+ zpanic_at(init->token, "Type validation failed. Expected '%s', but got '%s'",
+ expected, got);
+ free(expected);
+ free(got);
+ }
+ }
+
+ if (temp_literal_type)
+ {
+ free(temp_literal_type); // Simple free, shallow
+ }
+ }
+
// NEW: Capture Const Integer Values
if (init && init->type == NODE_EXPR_LITERAL && init->literal.type_kind == LITERAL_INT)
{
@@ -839,7 +896,7 @@ ASTNode *parse_def(ParserContext *ctx, Lexer *l)
return o;
}
-ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l)
+ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l, int is_opaque)
{
lexer_next(l); // consume 'type' or 'alias'
Token n = lexer_next(l);
@@ -859,8 +916,11 @@ ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l)
strncpy(node->type_alias.alias, n.start, n.len);
node->type_alias.alias[n.len] = 0;
node->type_alias.original_type = o;
+ node->type_alias.is_opaque = is_opaque;
+ node->type_alias.defined_in_file = g_current_filename ? xstrdup(g_current_filename) : NULL;
- register_type_alias(ctx, node->type_alias.alias, o);
+ register_type_alias(ctx, node->type_alias.alias, o, is_opaque,
+ node->type_alias.defined_in_file);
return node;
}
diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c
index ee1f96f..8f3579a 100644
--- a/src/parser/parser_expr.c
+++ b/src/parser/parser_expr.c
@@ -1,3 +1,43 @@
+#include "../codegen/codegen.h"
+
+int check_opaque_alias_compat(ParserContext *ctx, Type *a, Type *b)
+{
+ if (!a || !b)
+ {
+ return 0;
+ }
+
+ int a_is_opaque = (a->kind == TYPE_ALIAS && a->alias.is_opaque_alias);
+ int b_is_opaque = (b->kind == TYPE_ALIAS && b->alias.is_opaque_alias);
+
+ if (!a_is_opaque && !b_is_opaque)
+ {
+ return 1;
+ }
+
+ if (a_is_opaque)
+ {
+ if (a->alias.alias_defined_in_file && g_current_filename &&
+ strcmp(a->alias.alias_defined_in_file, g_current_filename) == 0)
+ {
+ return check_opaque_alias_compat(ctx, a->inner, b);
+ }
+ return 0;
+ }
+
+ if (b_is_opaque)
+ {
+ if (b->alias.alias_defined_in_file && g_current_filename &&
+ strcmp(b->alias.alias_defined_in_file, g_current_filename) == 0)
+ {
+ return check_opaque_alias_compat(ctx, a, b->inner);
+ }
+ return 0;
+ }
+
+ return 0;
+}
+
#include "../zen/zen_facts.h"
#include "parser.h"
#include <ctype.h>
@@ -1083,6 +1123,7 @@ static ASTNode *create_fstring_block(ParserContext *ctx, const char *content)
static ASTNode *parse_int_literal(Token t)
{
ASTNode *node = ast_create(NODE_EXPR_LITERAL);
+ node->token = t;
node->literal.type_kind = LITERAL_INT;
node->type_info = type_new(TYPE_INT);
char *s = token_strdup(t);
@@ -1104,6 +1145,7 @@ static ASTNode *parse_int_literal(Token t)
static ASTNode *parse_float_literal(Token t)
{
ASTNode *node = ast_create(NODE_EXPR_LITERAL);
+ node->token = t;
node->literal.type_kind = LITERAL_FLOAT;
node->literal.float_val = atof(t.start);
node->type_info = type_new(TYPE_F64);
@@ -1136,6 +1178,7 @@ static ASTNode *parse_string_literal(ParserContext *ctx, Token t)
}
ASTNode *node = ast_create(NODE_EXPR_LITERAL);
+ node->token = t;
node->literal.type_kind = LITERAL_STRING;
node->literal.string_val = xmalloc(t.len);
strncpy(node->literal.string_val, t.start + 1, t.len - 2);
@@ -1159,6 +1202,7 @@ static ASTNode *parse_fstring_literal(ParserContext *ctx, Token t)
static ASTNode *parse_char_literal(Token t)
{
ASTNode *node = ast_create(NODE_EXPR_LITERAL);
+ node->token = t;
node->literal.type_kind = LITERAL_CHAR;
node->literal.string_val = token_strdup(t);
node->type_info = type_new(TYPE_I8);
@@ -2051,6 +2095,20 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l)
sprintf(prefixed, "%s_%s", ctx->current_module_prefix, acc);
struct_name = prefixed;
}
+
+ // Opaque Struct Check
+ ASTNode *def = find_struct_def(ctx, struct_name);
+ if (def && def->type == NODE_STRUCT && def->strct.is_opaque)
+ {
+ if (!def->strct.defined_in_file ||
+ (g_current_filename &&
+ strcmp(def->strct.defined_in_file, g_current_filename) != 0))
+ {
+ zpanic_at(lexer_peek(l),
+ "Cannot initialize opaque struct '%s' outside its module",
+ struct_name);
+ }
+ }
lexer_next(l);
node = ast_create(NODE_EXPR_STRUCT_INIT);
node->struct_init.struct_name = struct_name;
@@ -4045,6 +4103,30 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
node->member.field = token_strdup(field);
node->member.is_pointer_access = 1;
+ // Opaque Check
+ int is_ptr_dummy = 0;
+ char *alloc_name = NULL;
+ char *sname =
+ resolve_struct_name_from_type(ctx, lhs->type_info, &is_ptr_dummy, &alloc_name);
+ if (sname)
+ {
+ ASTNode *def = find_struct_def(ctx, sname);
+ if (def && def->type == NODE_STRUCT && def->strct.is_opaque)
+ {
+ if (!def->strct.defined_in_file ||
+ (g_current_filename &&
+ strcmp(def->strct.defined_in_file, g_current_filename) != 0))
+ {
+ zpanic_at(field, "Cannot access private field '%s' of opaque struct '%s'",
+ node->member.field, sname);
+ }
+ }
+ if (alloc_name)
+ {
+ free(alloc_name);
+ }
+ }
+
node->type_info = get_field_type(ctx, lhs->type_info, node->member.field);
if (node->type_info)
{
@@ -4074,6 +4156,30 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
node->member.field = token_strdup(field);
node->member.is_pointer_access = 2;
+ // Opaque Check
+ int is_ptr_dummy = 0;
+ char *alloc_name = NULL;
+ char *sname =
+ resolve_struct_name_from_type(ctx, lhs->type_info, &is_ptr_dummy, &alloc_name);
+ if (sname)
+ {
+ ASTNode *def = find_struct_def(ctx, sname);
+ if (def && def->type == NODE_STRUCT && def->strct.is_opaque)
+ {
+ if (!def->strct.defined_in_file ||
+ (g_current_filename &&
+ strcmp(def->strct.defined_in_file, g_current_filename) != 0))
+ {
+ zpanic_at(field, "Cannot access private field '%s' of opaque struct '%s'",
+ node->member.field, sname);
+ }
+ }
+ if (alloc_name)
+ {
+ free(alloc_name);
+ }
+ }
+
node->type_info = get_field_type(ctx, lhs->type_info, node->member.field);
if (node->type_info)
{
@@ -4653,6 +4759,30 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
node->member.field = token_strdup(field);
node->member.is_pointer_access = 0;
+ // Opaque Check
+ int is_ptr_dummy = 0;
+ char *alloc_name = NULL;
+ char *sname =
+ resolve_struct_name_from_type(ctx, lhs->type_info, &is_ptr_dummy, &alloc_name);
+ if (sname)
+ {
+ ASTNode *def = find_struct_def(ctx, sname);
+ if (def && def->type == NODE_STRUCT && def->strct.is_opaque)
+ {
+ if (!def->strct.defined_in_file ||
+ (g_current_filename &&
+ strcmp(def->strct.defined_in_file, g_current_filename) != 0))
+ {
+ zpanic_at(field, "Cannot access private field '%s' of opaque struct '%s'",
+ node->member.field, sname);
+ }
+ }
+ if (alloc_name)
+ {
+ free(alloc_name);
+ }
+ }
+
node->member.field = token_strdup(field);
node->member.is_pointer_access = 0;
@@ -5540,7 +5670,8 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
}
else
{
- if (type_eq(lhs->type_info, rhs->type_info))
+ if (type_eq(lhs->type_info, rhs->type_info) ||
+ check_opaque_alias_compat(ctx, lhs->type_info, rhs->type_info))
{
bin->type_info = lhs->type_info;
}
diff --git a/src/parser/parser_struct.c b/src/parser/parser_struct.c
index 84450ba..82dd346 100644
--- a/src/parser/parser_struct.c
+++ b/src/parser/parser_struct.c
@@ -652,7 +652,7 @@ ASTNode *parse_impl(ParserContext *ctx, Lexer *l)
}
}
-ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union)
+ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union, int is_opaque)
{
lexer_next(l); // eat struct or union
@@ -705,6 +705,7 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union)
n->strct.is_union = is_union;
n->strct.fields = NULL;
n->strct.is_incomplete = 1;
+ n->strct.is_opaque = is_opaque;
return n;
}
@@ -800,10 +801,6 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union)
ASTNode *nf = ast_create(NODE_FIELD);
nf->field.name = xstrdup(f->field.name);
nf->field.type = xstrdup(f->field.type);
- // Copy type info? Ideally deep copy or ref
- // For now, we leave it NULL or shallow copy if needed, but mixins usually
- // aren't generic params themselves in the same way.
- // Let's shallow copy for safety if it exists.
nf->type_info = f->type_info;
if (!h)
@@ -829,7 +826,6 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union)
free(use_name);
continue;
}
- // ---------------------------------------
if (t.type == TOK_IDENT)
{
@@ -904,8 +900,10 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union)
node->strct.generic_params = gps;
node->strct.generic_param_count = gp_count;
node->strct.is_union = is_union;
+ node->strct.is_opaque = is_opaque;
node->strct.used_structs = temp_used_structs;
node->strct.used_struct_count = temp_used_count;
+ node->strct.defined_in_file = g_current_filename ? xstrdup(g_current_filename) : NULL;
if (gp_count > 0)
{
@@ -1102,7 +1100,7 @@ ASTNode *parse_enum(ParserContext *ctx, Lexer *l)
node->enm.name = ename;
node->enm.variants = h;
- node->enm.generic_param = gp; // 3. Store generic param
+ node->enm.generic_param = gp; // Store generic param
if (gp)
{
diff --git a/src/parser/parser_type.c b/src/parser/parser_type.c
index c01f061..65f2848 100644
--- a/src/parser/parser_type.c
+++ b/src/parser/parser_type.c
@@ -33,12 +33,25 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l)
char *name = token_strdup(t);
// Check for alias
- const char *aliased = find_type_alias(ctx, name);
- if (aliased)
+ TypeAlias *alias_node = find_type_alias_node(ctx, name);
+ if (alias_node)
{
free(name);
Lexer tmp;
- lexer_init(&tmp, aliased);
+ lexer_init(&tmp, alias_node->original_type);
+
+ if (alias_node->is_opaque)
+ {
+ Type *underlying = parse_type_formal(ctx, &tmp);
+ Type *wrapper = type_new(TYPE_ALIAS);
+ wrapper->name = xstrdup(alias_node->alias);
+ wrapper->inner = underlying;
+ wrapper->alias.is_opaque_alias = 1;
+ wrapper->alias.alias_defined_in_file =
+ alias_node->defined_in_file ? xstrdup(alias_node->defined_in_file) : NULL;
+ return wrapper;
+ }
+
return parse_type_formal(ctx, &tmp);
}
diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c
index 4e85500..48418b6 100644
--- a/src/parser/parser_utils.c
+++ b/src/parser/parser_utils.c
@@ -402,23 +402,33 @@ void add_to_struct_list(ParserContext *ctx, ASTNode *node)
ctx->parsed_structs_list = r;
}
-void register_type_alias(ParserContext *ctx, const char *alias, const char *original)
+void register_type_alias(ParserContext *ctx, const char *alias, const char *original, int is_opaque,
+ const char *defined_in_file)
{
TypeAlias *ta = xmalloc(sizeof(TypeAlias));
ta->alias = xstrdup(alias);
ta->original_type = xstrdup(original);
+ ta->is_opaque = is_opaque;
+ ta->defined_in_file = defined_in_file ? xstrdup(defined_in_file) : NULL;
ta->next = ctx->type_aliases;
ctx->type_aliases = ta;
}
const char *find_type_alias(ParserContext *ctx, const char *alias)
{
+ TypeAlias *ta = find_type_alias_node(ctx, alias);
+ return ta ? ta->original_type : NULL;
+}
+
+TypeAlias *find_type_alias_node(ParserContext *ctx, const char *alias)
+{
TypeAlias *ta = ctx->type_aliases;
while (ta)
{
if (strcmp(ta->alias, alias) == 0)
{
- return ta->original_type;
+ // printf("DEBUG: Found Alias '%s' (Opaque: %d)\n", alias, ta->is_opaque);
+ return ta;
}
ta = ta->next;
}