summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorZuhaitz <zuhaitz.zechhub@gmail.com>2026-02-02 00:22:19 +0000
committerGitHub <noreply@github.com>2026-02-02 00:22:19 +0000
commit6fde408251dcc0e32c6f513b5d65cc7a9d8c8912 (patch)
treea50440a6148113ebf0e017da8111338e8e067a31 /src
parentc4b73a1e99bda3cdfabe7ff3b64066b310ee7292 (diff)
parentfb57d746d706fbae822de17087f4a3991d3079cb (diff)
Merge branch 'main' into dev/weilun/thread
Diffstat (limited to 'src')
-rw-r--r--src/ast/ast.c56
-rw-r--r--src/ast/ast.h58
-rw-r--r--src/codegen/codegen.c92
-rw-r--r--src/codegen/codegen_decl.c19
-rw-r--r--src/parser/parser_expr.c53
-rw-r--r--src/parser/parser_struct.c16
-rw-r--r--src/parser/parser_type.c62
-rw-r--r--src/parser/parser_utils.c22
8 files changed, 329 insertions, 49 deletions
diff --git a/src/ast/ast.c b/src/ast/ast.c
index 439a9f5..1b35500 100644
--- a/src/ast/ast.c
+++ b/src/ast/ast.c
@@ -259,6 +259,25 @@ static char *type_to_string_impl(Type *t)
return xstrdup("int32_t");
case TYPE_UINT:
return xstrdup("unsigned int");
+
+ // Portable C Types
+ case TYPE_C_INT:
+ return xstrdup("c_int");
+ case TYPE_C_UINT:
+ return xstrdup("c_uint");
+ case TYPE_C_LONG:
+ return xstrdup("c_long");
+ case TYPE_C_ULONG:
+ return xstrdup("c_ulong");
+ case TYPE_C_SHORT:
+ return xstrdup("c_short");
+ case TYPE_C_USHORT:
+ return xstrdup("c_ushort");
+ case TYPE_C_CHAR:
+ return xstrdup("c_char");
+ case TYPE_C_UCHAR:
+ return xstrdup("c_uchar");
+
case TYPE_INT:
return xstrdup("int");
case TYPE_FLOAT:
@@ -461,8 +480,29 @@ static char *type_to_c_string_impl(Type *t)
return xstrdup("int32_t");
case TYPE_UINT:
return xstrdup("unsigned int");
- case TYPE_INT:
+
+ // Portable C Types (Map directly to C types)
+ case TYPE_C_INT:
return xstrdup("int");
+ case TYPE_C_UINT:
+ return xstrdup("unsigned int");
+ case TYPE_C_LONG:
+ return xstrdup("long");
+ case TYPE_C_ULONG:
+ return xstrdup("unsigned long");
+ case TYPE_C_SHORT:
+ return xstrdup("short");
+ case TYPE_C_USHORT:
+ return xstrdup("unsigned short");
+ case TYPE_C_CHAR:
+ return xstrdup("char");
+ case TYPE_C_UCHAR:
+ return xstrdup("unsigned char");
+
+ case TYPE_INT:
+ // 'int' in Zen C maps to 'i32' now for portability.
+ // FFI should use c_int.
+ return xstrdup("int32_t");
case TYPE_FLOAT:
return xstrdup("float");
case TYPE_BITINT:
@@ -519,8 +559,11 @@ static char *type_to_c_string_impl(Type *t)
return res;
}
- char *res = xmalloc(strlen(inner) + 7);
- sprintf(res, "Slice_%s", inner);
+ char *inner_zens = type_to_string(t->inner);
+ char *res = xmalloc(strlen(inner_zens) + 7);
+ sprintf(res, "Slice_%s", inner_zens);
+ free(inner_zens);
+ free(inner);
return res;
}
@@ -561,7 +604,12 @@ static char *type_to_c_string_impl(Type *t)
return xstrdup("z_closure_T");
case TYPE_GENERIC:
- return xstrdup(t->name);
+ // Use type_to_string to get the mangled name (e.g. Option_int) instead of raw C string
+ // composition This ensures consistency with struct definitions.
+ {
+ char *s = type_to_string(t);
+ return s;
+ }
case TYPE_ALIAS:
return type_to_c_string(t->inner);
diff --git a/src/ast/ast.h b/src/ast/ast.h
index 4498d7c..fa67043 100644
--- a/src/ast/ast.h
+++ b/src/ast/ast.h
@@ -28,30 +28,40 @@ typedef enum
*/
typedef enum
{
- TYPE_VOID, ///< `void` type.
- TYPE_BOOL, ///< `bool` type.
- TYPE_CHAR, ///< `char` type.
- TYPE_STRING, ///< `string` type.
- TYPE_U0, ///< `u0` type.
- TYPE_I8, ///< `i8` type.
- TYPE_U8, ///< `u8` type.
- TYPE_I16, ///< `i16` type.
- TYPE_U16, ///< `u16` type.
- TYPE_I32, ///< `i32` type.
- TYPE_U32, ///< `u32` type.
- TYPE_I64, ///< `i64` type.
- TYPE_U64, ///< `u64` type.
- TYPE_I128, ///< `i128` type.
- TYPE_U128, ///< `u128` type.
- TYPE_F32, ///< `f32` type.
- TYPE_F64, ///< `f64` type.
- TYPE_INT, ///< `int` (alias, usually i32).
- TYPE_FLOAT, ///< `float` (alias).
- TYPE_USIZE, ///< `usize` (pointer size unsigned).
- TYPE_ISIZE, ///< `isize` (pointer size signed).
- TYPE_BYTE, ///< `byte`.
- TYPE_RUNE, ///< `rune`.
- TYPE_UINT, ///< `uint` (alias).
+ TYPE_VOID, ///< `void` type.
+ TYPE_BOOL, ///< `bool` type.
+ TYPE_CHAR, ///< `char` type.
+ TYPE_STRING, ///< `string` type.
+ TYPE_U0, ///< `u0` type.
+ TYPE_I8, ///< `i8` type.
+ TYPE_U8, ///< `u8` type.
+ TYPE_I16, ///< `i16` type.
+ TYPE_U16, ///< `u16` type.
+ TYPE_I32, ///< `i32` type.
+ TYPE_U32, ///< `u32` type.
+ TYPE_I64, ///< `i64` type.
+ TYPE_U64, ///< `u64` type.
+ TYPE_I128, ///< `i128` type.
+ TYPE_U128, ///< `u128` type.
+ TYPE_F32, ///< `f32` type.
+ TYPE_F64, ///< `f64` type.
+ TYPE_INT, ///< `int` (alias, usually i32).
+ TYPE_FLOAT, ///< `float` (alias).
+ TYPE_USIZE, ///< `usize` (pointer size unsigned).
+ TYPE_ISIZE, ///< `isize` (pointer size signed).
+ TYPE_BYTE, ///< `byte`.
+ TYPE_RUNE, ///< `rune`.
+ TYPE_UINT, ///< `uint` (alias).
+ // Portable C Types (FFI)
+ TYPE_C_INT, ///< `c_int` (int).
+ TYPE_C_UINT, ///< `c_uint` (unsigned int).
+ TYPE_C_LONG, ///< `c_long` (long).
+ TYPE_C_ULONG, ///< `c_ulong` (unsigned long).
+ TYPE_C_SHORT, ///< `c_short` (short).
+ TYPE_C_USHORT, ///< `c_ushort` (unsigned short).
+ TYPE_C_CHAR, ///< `c_char` (char).
+ TYPE_C_UCHAR, ///< `c_uchar` (unsigned char).
+
TYPE_STRUCT, ///< Struct type.
TYPE_ENUM, ///< Enum type.
TYPE_POINTER, ///< Pointer type (*).
diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c
index 0496a46..384820b 100644
--- a/src/codegen/codegen.c
+++ b/src/codegen/codegen.c
@@ -1150,14 +1150,102 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
break;
}
case NODE_EXPR_CAST:
- fprintf(out, "((%s)(", node->cast.target_type);
+ {
+ const char *t = node->cast.target_type;
+ const char *mapped = t;
+ if (strcmp(t, "c_int") == 0)
+ {
+ mapped = "int";
+ }
+ else if (strcmp(t, "c_uint") == 0)
+ {
+ mapped = "unsigned int";
+ }
+ else if (strcmp(t, "c_long") == 0)
+ {
+ mapped = "long";
+ }
+ else if (strcmp(t, "c_ulong") == 0)
+ {
+ mapped = "unsigned long";
+ }
+ else if (strcmp(t, "c_short") == 0)
+ {
+ mapped = "short";
+ }
+ else if (strcmp(t, "c_ushort") == 0)
+ {
+ mapped = "unsigned short";
+ }
+ else if (strcmp(t, "c_char") == 0)
+ {
+ mapped = "char";
+ }
+ else if (strcmp(t, "c_uchar") == 0)
+ {
+ mapped = "unsigned char";
+ }
+ else if (strcmp(t, "int") == 0)
+ {
+ mapped = "int32_t";
+ }
+ else if (strcmp(t, "uint") == 0)
+ {
+ mapped = "unsigned int";
+ }
+
+ fprintf(out, "((%s)(", mapped);
codegen_expression(ctx, node->cast.expr, out);
fprintf(out, "))");
break;
+ }
case NODE_EXPR_SIZEOF:
if (node->size_of.target_type)
{
- fprintf(out, "sizeof(%s)", node->size_of.target_type);
+ const char *t = node->size_of.target_type;
+ const char *mapped = t;
+ if (strcmp(t, "c_int") == 0)
+ {
+ mapped = "int";
+ }
+ else if (strcmp(t, "c_uint") == 0)
+ {
+ mapped = "unsigned int";
+ }
+ else if (strcmp(t, "c_long") == 0)
+ {
+ mapped = "long";
+ }
+ else if (strcmp(t, "c_ulong") == 0)
+ {
+ mapped = "unsigned long";
+ }
+ else if (strcmp(t, "c_short") == 0)
+ {
+ mapped = "short";
+ }
+ else if (strcmp(t, "c_ushort") == 0)
+ {
+ mapped = "unsigned short";
+ }
+ else if (strcmp(t, "c_char") == 0)
+ {
+ mapped = "char";
+ }
+ else if (strcmp(t, "c_uchar") == 0)
+ {
+ mapped = "unsigned char";
+ }
+ else if (strcmp(t, "int") == 0)
+ {
+ mapped = "int32_t"; // Strict mapping
+ }
+ else if (strcmp(t, "uint") == 0)
+ {
+ mapped = "unsigned int"; // uint alias
+ }
+
+ fprintf(out, "sizeof(%s)", mapped);
}
else
{
diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c
index 31bd2ee..1623ffc 100644
--- a/src/codegen/codegen_decl.c
+++ b/src/codegen/codegen_decl.c
@@ -1130,12 +1130,23 @@ void print_type_defs(ParserContext *ctx, FILE *out, ASTNode *nodes)
fprintf(out, "typedef struct Tuple_%s Tuple_%s;\nstruct Tuple_%s { ", t->sig, t->sig,
t->sig);
char *s = xstrdup(t->sig);
- char *p = strtok(s, "_");
+ char *current = s;
+ char *next_sep = strstr(current, "__");
int i = 0;
- while (p)
+ while (current)
{
- fprintf(out, "%s v%d; ", p, i++);
- p = strtok(NULL, "_");
+ if (next_sep)
+ {
+ *next_sep = 0;
+ fprintf(out, "%s v%d; ", current, i++);
+ current = next_sep + 2;
+ next_sep = strstr(current, "__");
+ }
+ else
+ {
+ fprintf(out, "%s v%d; ", current, i++);
+ break;
+ }
}
free(s);
fprintf(out, "};\n");
diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c
index 7c53d96..a732448 100644
--- a/src/parser/parser_expr.c
+++ b/src/parser/parser_expr.c
@@ -1908,7 +1908,7 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l)
{
Type *formal_type = parse_type_formal(ctx, l);
concrete_types[arg_count] = type_to_string(formal_type);
- unmangled_types[arg_count] = type_to_c_string(formal_type);
+ unmangled_types[arg_count] = type_to_string(formal_type);
arg_count++;
if (lexer_peek(l).type == TOK_COMMA)
@@ -2944,7 +2944,7 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l)
{
if (i > 0)
{
- strcat(sig, "_");
+ strcat(sig, "__");
}
strcat(sig, type_strs[i]);
}
@@ -4116,6 +4116,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
break;
}
ASTNode *node = ast_create(NODE_EXPR_MEMBER);
+ node->token = field;
node->member.target = lhs;
node->member.field = token_strdup(field);
node->member.is_pointer_access = 1;
@@ -4169,6 +4170,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
break;
}
ASTNode *node = ast_create(NODE_EXPR_MEMBER);
+ node->token = field;
node->member.target = lhs;
node->member.field = token_strdup(field);
node->member.is_pointer_access = 2;
@@ -4350,6 +4352,52 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
if (sig)
{
+ // Check if this is a static method being called with dot operator
+ // Static methods don't have 'self' as first parameter
+ int is_static_method = 0;
+ if (sig->total_args == 0)
+ {
+ // No arguments at all - definitely static
+ is_static_method = 1;
+ }
+ else if (sig->arg_types[0])
+ {
+ // Check if first parameter is a pointer to the struct type
+ // Instance methods have: fn method(self) where self is StructType*
+ // Static methods have: fn method(x: int, y: int) etc.
+ Type *first_param = sig->arg_types[0];
+
+ // If first param is not a pointer, it's likely static
+ // OR if it's a pointer but not to this struct type
+ if (first_param->kind != TYPE_POINTER)
+ {
+ is_static_method = 1;
+ }
+ else if (first_param->inner)
+ {
+ // Check if the inner type matches the struct
+ char *inner_name = NULL;
+ if (first_param->inner->kind == TYPE_STRUCT)
+ {
+ inner_name = first_param->inner->name;
+ }
+
+ if (!inner_name || strcmp(inner_name, struct_name) != 0)
+ {
+ is_static_method = 1;
+ }
+ }
+ }
+
+ if (is_static_method)
+ {
+ zpanic_at(lhs->token,
+ "Cannot call static method '%s' with dot operator\n"
+ " = help: Use '%s::%s(...)' instead of instance.%s(...)",
+ lhs->member.field, struct_name, lhs->member.field,
+ lhs->member.field);
+ }
+
resolved_name = xstrdup(mangled);
resolved_sig = sig;
@@ -4772,6 +4820,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
break;
}
ASTNode *node = ast_create(NODE_EXPR_MEMBER);
+ node->token = field;
node->member.target = lhs;
node->member.field = token_strdup(field);
node->member.is_pointer_access = 0;
diff --git a/src/parser/parser_struct.c b/src/parser/parser_struct.c
index e53b56c..0c984a6 100644
--- a/src/parser/parser_struct.c
+++ b/src/parser/parser_struct.c
@@ -315,6 +315,16 @@ ASTNode *parse_impl(ParserContext *ctx, Lexer *l)
register_generic(ctx, target_gen_param);
}
+ // Check for common error: swapped Struct and Trait
+ // impl MyStruct for MyTrait (Wrong) vs impl MyTrait for MyStruct (Correct)
+ if (!is_trait(name1) && is_trait(name2))
+ {
+ zpanic_at(t1,
+ "Incorrect usage of impl. Did you mean 'impl %s for %s'? Syntax is 'impl "
+ "<Trait> for <Struct>'",
+ name2, name1);
+ }
+
// Auto-import std/mem.zc if implementing Drop, Copy, or Clone traits
if (strcmp(name1, "Drop") == 0 || strcmp(name1, "Copy") == 0 || strcmp(name1, "Clone") == 0)
{
@@ -863,7 +873,7 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union, int is_opaque)
Token field_name = lexer_next(l);
lexer_next(l); // eat :
Type *ft = parse_type_formal(ctx, l);
- char *field_type_str = type_to_string(ft);
+ char *field_type_str = type_to_c_string(ft);
expect(l, TOK_SEMICOLON, "Expected ;");
ASTNode *nf = ast_create(NODE_FIELD);
@@ -947,7 +957,7 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union, int is_opaque)
Token f_name = lexer_next(l);
expect(l, TOK_COLON, "Expected :");
Type *ft = parse_type_formal(ctx, l);
- char *f_type = type_to_string(ft);
+ char *f_type = type_to_c_string(ft);
ASTNode *f = ast_create(NODE_FIELD);
f->field.name = token_strdup(f_name);
@@ -1120,7 +1130,7 @@ ASTNode *parse_enum(ParserContext *ctx, Lexer *l)
while (lexer_peek(l).type == TOK_COMMA)
{
lexer_next(l); // eat ,
- strcat(sig, "_");
+ strcat(sig, "__");
Type *next_t = parse_type_obj(ctx, l);
char *ns = type_to_string(next_t);
if (strlen(sig) + strlen(ns) + 2 > 510)
diff --git a/src/parser/parser_type.c b/src/parser/parser_type.c
index 49e961c..fcbe12d 100644
--- a/src/parser/parser_type.c
+++ b/src/parser/parser_type.c
@@ -427,13 +427,13 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l)
if (strcmp(name, "uint") == 0)
{
free(name);
- return type_new(TYPE_UINT);
+ return type_new(TYPE_U32); // Strict uint32_t
}
if (strcmp(name, "int") == 0)
{
free(name);
- return type_new(TYPE_INT);
+ return type_new(TYPE_I32); // Strict int32_t
}
if (strcmp(name, "float") == 0)
{
@@ -467,23 +467,31 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l)
}
if (strcmp(name, "long") == 0)
{
+ zwarn_at(t, "'long' is treated as portable 'int64_t' in Zen C. Use 'c_long' for "
+ "platform-dependent C long.");
free(name);
return type_new(TYPE_I64);
}
if (strcmp(name, "short") == 0)
{
+ zwarn_at(t, "'short' is treated as portable 'int16_t' in Zen C. Use 'c_short' for "
+ "platform-dependent C short.");
free(name);
return type_new(TYPE_I16);
}
if (strcmp(name, "unsigned") == 0)
{
+ zwarn_at(t, "'unsigned' is treated as portable 'uint32_t' in Zen C. Use 'c_uint' for "
+ "platform-dependent C unsigned int.");
free(name);
- return type_new(TYPE_UINT);
+ return type_new(TYPE_U32);
}
if (strcmp(name, "signed") == 0)
{
+ zwarn_at(t, "'signed' is treated as portable 'int32_t' in Zen C. Use 'c_int' for "
+ "platform-dependent C int.");
free(name);
- return type_new(TYPE_INT);
+ return type_new(TYPE_I32);
}
if (strcmp(name, "int8_t") == 0)
{
@@ -536,6 +544,48 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l)
return type_new(TYPE_ISIZE);
}
+ // Portable C Types
+ if (strcmp(name, "c_int") == 0)
+ {
+ free(name);
+ return type_new(TYPE_C_INT);
+ }
+ if (strcmp(name, "c_uint") == 0)
+ {
+ free(name);
+ return type_new(TYPE_C_UINT);
+ }
+ if (strcmp(name, "c_long") == 0)
+ {
+ free(name);
+ return type_new(TYPE_C_LONG);
+ }
+ if (strcmp(name, "c_ulong") == 0)
+ {
+ free(name);
+ return type_new(TYPE_C_ULONG);
+ }
+ if (strcmp(name, "c_short") == 0)
+ {
+ free(name);
+ return type_new(TYPE_C_SHORT);
+ }
+ if (strcmp(name, "c_ushort") == 0)
+ {
+ free(name);
+ return type_new(TYPE_C_USHORT);
+ }
+ if (strcmp(name, "c_char") == 0)
+ {
+ free(name);
+ return type_new(TYPE_C_CHAR);
+ }
+ if (strcmp(name, "c_uchar") == 0)
+ {
+ free(name);
+ return type_new(TYPE_C_UCHAR);
+ }
+
// Relaxed Type Check: If explicit 'struct Name', trust the user.
if (explicit_struct)
{
@@ -677,7 +727,7 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l)
zpanic_at(t, "Expected > after generic");
}
- char *unmangled_arg = type_to_c_string(first_arg);
+ char *unmangled_arg = type_to_string(first_arg);
int is_single_dep = 0;
for (int k = 0; k < ctx->known_generics_count; ++k)
@@ -791,7 +841,7 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l)
if (lexer_peek(l).type == TOK_COMMA)
{
lexer_next(l);
- strcat(sig, "_");
+ strcat(sig, "__");
}
else
{
diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c
index 28d2c11..8ea2934 100644
--- a/src/parser/parser_utils.c
+++ b/src/parser/parser_utils.c
@@ -691,16 +691,22 @@ void register_tuple(ParserContext *ctx, const char *sig)
s_def->strct.name = xstrdup(struct_name);
char *s_sig = xstrdup(sig);
- char *tok = strtok(s_sig, "_");
+ char *current = s_sig;
+ char *next_sep = strstr(current, "__");
ASTNode *head = NULL, *tail = NULL;
int i = 0;
- while (tok)
+ while (current)
{
+ if (next_sep)
+ {
+ *next_sep = 0;
+ }
+
ASTNode *f = ast_create(NODE_FIELD);
char fname[32];
sprintf(fname, "v%d", i++);
f->field.name = xstrdup(fname);
- f->field.type = xstrdup(tok);
+ f->field.type = xstrdup(current);
if (!head)
{
@@ -712,7 +718,15 @@ void register_tuple(ParserContext *ctx, const char *sig)
}
tail = f;
- tok = strtok(NULL, "_");
+ if (next_sep)
+ {
+ current = next_sep + 2;
+ next_sep = strstr(current, "__");
+ }
+ else
+ {
+ break;
+ }
}
free(s_sig);
s_def->strct.fields = head;