summaryrefslogtreecommitdiff
path: root/src/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser')
-rw-r--r--src/parser/parser.h3
-rw-r--r--src/parser/parser_decl.c6
-rw-r--r--src/parser/parser_expr.c67
-rw-r--r--src/parser/parser_stmt.c303
-rw-r--r--src/parser/parser_struct.c133
-rw-r--r--src/parser/parser_type.c62
-rw-r--r--src/parser/parser_utils.c75
7 files changed, 604 insertions, 45 deletions
diff --git a/src/parser/parser.h b/src/parser/parser.h
index 262c359..23c2920 100644
--- a/src/parser/parser.h
+++ b/src/parser/parser.h
@@ -561,7 +561,8 @@ ASTNode *parse_arrow_lambda_multi(ParserContext *ctx, Lexer *l, char **param_nam
* @brief Parses and converts arguments.
*/
char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out, int *count_out,
- Type ***types_out, char ***names_out, int *is_varargs_out);
+ Type ***types_out, char ***names_out, int *is_varargs_out,
+ char ***ctype_overrides_out);
/**
* @brief Checks if a file has been imported.
diff --git a/src/parser/parser_decl.c b/src/parser/parser_decl.c
index c96ca36..93a124d 100644
--- a/src/parser/parser_decl.c
+++ b/src/parser/parser_decl.c
@@ -99,10 +99,11 @@ ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async)
int count;
Type **arg_types;
char **param_names;
+ char **ctype_overrides;
int is_varargs = 0;
- char *args =
- parse_and_convert_args(ctx, l, &defaults, &count, &arg_types, &param_names, &is_varargs);
+ char *args = parse_and_convert_args(ctx, l, &defaults, &count, &arg_types, &param_names,
+ &is_varargs, &ctype_overrides);
char *ret = "void";
Type *ret_type_obj = type_new(TYPE_VOID);
@@ -191,6 +192,7 @@ ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async)
node->func.defaults = defaults;
node->func.ret_type_info = ret_type_obj;
node->func.is_varargs = is_varargs;
+ node->func.c_type_overrides = ctype_overrides;
if (gen_param)
{
diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c
index 6156cc0..f27e2c3 100644
--- a/src/parser/parser_expr.c
+++ b/src/parser/parser_expr.c
@@ -370,8 +370,7 @@ static void check_format_string(ASTNode *call, Token t)
if (spec == 'd' || spec == 'i' || spec == 'u' || spec == 'x' || spec == 'X' ||
spec == 'o')
{
- if (vt && vt->kind != TYPE_INT && vt->kind != TYPE_I64 && !type_is_unsigned(vt) &&
- vt->kind != TYPE_CHAR)
+ if (vt && !is_integer_type(vt))
{
warn_format_string(t, arg_num, "integer", got_type);
}
@@ -1908,7 +1907,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 +2943,7 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l)
{
if (i > 0)
{
- strcat(sig, "_");
+ strcat(sig, "__");
}
strcat(sig, type_strs[i]);
}
@@ -4116,6 +4115,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 +4169,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 +4351,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 +4819,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;
@@ -5421,6 +5469,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
// This gives a warning as "unused" but it's needed for the rewrite.
char *r_name =
resolve_struct_name_from_type(ctx, rhs->type_info, &is_rhs_ptr, &r_alloc);
+ (void)r_name;
if (r_alloc)
{
free(r_alloc);
@@ -5643,7 +5692,17 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
char *t1 = type_to_string(lhs->type_info);
char *t2 = type_to_string(rhs->type_info);
// Skip type check if either operand is void* (escape hatch type)
+ // or if either operand is a generic type parameter (T, K, V, etc.)
int skip_check = (strcmp(t1, "void*") == 0 || strcmp(t2, "void*") == 0);
+ if (lhs->type_info->kind == TYPE_GENERIC || rhs->type_info->kind == TYPE_GENERIC)
+ {
+ skip_check = 1;
+ }
+ // Also check if type name is a single uppercase letter (common generic param)
+ if ((strlen(t1) == 1 && isupper(t1[0])) || (strlen(t2) == 1 && isupper(t2[0])))
+ {
+ skip_check = 1;
+ }
// Allow comparing pointers/strings with integer literal 0 (NULL)
if (!skip_check)
diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c
index a471fe6..ae16243 100644
--- a/src/parser/parser_stmt.c
+++ b/src/parser/parser_stmt.c
@@ -14,6 +14,118 @@
char *curr_func_ret = NULL;
char *run_comptime_block(ParserContext *ctx, Lexer *l);
+extern char *g_current_filename;
+
+/**
+ * @brief Auto-imports std/slice.zc if not already imported.
+ *
+ * This is called when array iteration is detected in for-in loops,
+ * to ensure the Slice<T>, SliceIter<T>, and Option<T> templates are available.
+ */
+static void auto_import_std_slice(ParserContext *ctx)
+{
+ // Check if already imported via templates
+ GenericTemplate *t = ctx->templates;
+ while (t)
+ {
+ if (strcmp(t->name, "Slice") == 0)
+ {
+ return; // Already have the Slice template
+ }
+ t = t->next;
+ }
+
+ // Try to find and import std/slice.zc
+ static const char *std_paths[] = {"std/slice.zc", "./std/slice.zc", NULL};
+ static const char *system_paths[] = {"/usr/local/share/zenc", "/usr/share/zenc", NULL};
+
+ char resolved_path[1024];
+ int found = 0;
+
+ // First, try relative to current file
+ if (g_current_filename)
+ {
+ char *current_dir = xstrdup(g_current_filename);
+ char *last_slash = strrchr(current_dir, '/');
+ if (last_slash)
+ {
+ *last_slash = 0;
+ snprintf(resolved_path, sizeof(resolved_path), "%s/std/slice.zc", current_dir);
+ if (access(resolved_path, R_OK) == 0)
+ {
+ found = 1;
+ }
+ }
+ free(current_dir);
+ }
+
+ // Try relative paths
+ if (!found)
+ {
+ for (int i = 0; std_paths[i] && !found; i++)
+ {
+ if (access(std_paths[i], R_OK) == 0)
+ {
+ strncpy(resolved_path, std_paths[i], sizeof(resolved_path) - 1);
+ resolved_path[sizeof(resolved_path) - 1] = '\0';
+ found = 1;
+ }
+ }
+ }
+
+ // Try system paths
+ if (!found)
+ {
+ for (int i = 0; system_paths[i] && !found; i++)
+ {
+ snprintf(resolved_path, sizeof(resolved_path), "%s/std/slice.zc", system_paths[i]);
+ if (access(resolved_path, R_OK) == 0)
+ {
+ found = 1;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ return; // Could not find std/slice.zc, instantiate_generic will error
+ }
+
+ // Canonicalize path
+ char *real_fn = realpath(resolved_path, NULL);
+ if (real_fn)
+ {
+ strncpy(resolved_path, real_fn, sizeof(resolved_path) - 1);
+ resolved_path[sizeof(resolved_path) - 1] = '\0';
+ free(real_fn);
+ }
+
+ // Check if already imported
+ if (is_file_imported(ctx, resolved_path))
+ {
+ return;
+ }
+ mark_file_imported(ctx, resolved_path);
+
+ // Load and parse the file
+ char *src = load_file(resolved_path);
+ if (!src)
+ {
+ return; // Could not load file
+ }
+
+ Lexer i;
+ lexer_init(&i, src);
+
+ // Save and restore filename context
+ char *saved_fn = g_current_filename;
+ g_current_filename = resolved_path;
+
+ // Parse the slice module contents
+ parse_program_nodes(ctx, &i);
+
+ g_current_filename = saved_fn;
+}
static void check_assignment_condition(ASTNode *cond)
{
@@ -730,7 +842,7 @@ ASTNode *parse_asm(ParserContext *ctx, Lexer *l)
}
}
- // Parse clobbers (: "eax", "memory")
+ // Parse clobbers (: "eax", "memory" OR : clobber("eax"), clobber("memory"))
char **clobbers = NULL;
int num_clobbers = 0;
@@ -753,17 +865,36 @@ ASTNode *parse_asm(ParserContext *ctx, Lexer *l)
continue;
}
- if (t.type == TOK_STRING)
+ // check for clobber("...")
+ if (t.type == TOK_IDENT && strncmp(t.start, "clobber", 7) == 0)
{
- lexer_next(l);
- // Extract string content
- char *clob = xmalloc(t.len);
- strncpy(clob, t.start + 1, t.len - 2);
- clob[t.len - 2] = 0;
- clobbers[num_clobbers++] = clob;
+ lexer_next(l); // eat clobber
+ if (lexer_peek(l).type != TOK_LPAREN)
+ {
+ zpanic_at(lexer_peek(l), "Expected ( after clobber");
+ }
+ lexer_next(l); // eat (
+
+ Token clob = lexer_next(l);
+ if (clob.type != TOK_STRING)
+ {
+ zpanic_at(clob, "Expected string literal for clobber");
+ }
+
+ if (lexer_peek(l).type != TOK_RPAREN)
+ {
+ zpanic_at(lexer_peek(l), "Expected ) after clobber string");
+ }
+ lexer_next(l); // eat )
+
+ char *c = xmalloc(clob.len);
+ strncpy(c, clob.start + 1, clob.len - 2);
+ c[clob.len - 2] = 0;
+ clobbers[num_clobbers++] = c;
}
else
{
+ zpanic_at(t, "Expected 'clobber(\"...\")' in clobber list");
break;
}
}
@@ -1133,6 +1264,7 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l)
ASTNode *obj_expr = start_expr;
char *iter_method = "iterator";
+ ASTNode *slice_decl = NULL; // Track if we need to add a slice declaration
// Check for reference iteration: for x in &vec
if (obj_expr->type == NODE_EXPR_UNARY && obj_expr->unary.op &&
@@ -1142,6 +1274,80 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l)
iter_method = "iter_ref";
}
+ // Check for array iteration: wrap with Slice<T>::from_array
+ if (obj_expr->type_info && obj_expr->type_info->kind == TYPE_ARRAY &&
+ obj_expr->type_info->array_size > 0)
+ {
+ // Create a var decl for the slice
+ slice_decl = ast_create(NODE_VAR_DECL);
+ slice_decl->var_decl.name = xstrdup("__zc_arr_slice");
+
+ // Build type string: Slice<elem_type>
+ char *elem_type_str = type_to_string(obj_expr->type_info->inner);
+ char slice_type[256];
+ sprintf(slice_type, "Slice<%s>", elem_type_str);
+ slice_decl->var_decl.type_str = xstrdup(slice_type);
+
+ ASTNode *from_array_call = ast_create(NODE_EXPR_CALL);
+ ASTNode *static_method = ast_create(NODE_EXPR_VAR);
+
+ // The function name for static methods is Type::method format
+ char func_name[512];
+ snprintf(func_name, 511, "%s::from_array", slice_type);
+ static_method->var_ref.name = xstrdup(func_name);
+
+ from_array_call->call.callee = static_method;
+
+ // Create arguments
+ ASTNode *arr_addr = ast_create(NODE_EXPR_UNARY);
+ arr_addr->unary.op = xstrdup("&");
+ arr_addr->unary.operand = obj_expr;
+
+ ASTNode *arr_cast = ast_create(NODE_EXPR_CAST);
+ char cast_type[256];
+ sprintf(cast_type, "%s*", elem_type_str);
+ arr_cast->cast.target_type = xstrdup(cast_type);
+ arr_cast->cast.expr = arr_addr;
+
+ ASTNode *size_arg = ast_create(NODE_EXPR_LITERAL);
+ size_arg->literal.type_kind = LITERAL_INT;
+ size_arg->literal.int_val = obj_expr->type_info->array_size;
+ char size_buf[32];
+ sprintf(size_buf, "%d", obj_expr->type_info->array_size);
+ size_arg->literal.string_val = xstrdup(size_buf);
+
+ arr_cast->next = size_arg;
+ from_array_call->call.args = arr_cast;
+ from_array_call->call.arg_count = 2;
+
+ slice_decl->var_decl.init_expr = from_array_call;
+
+ // Manually trigger generic instantiation for Slice<T>
+ // This ensures that Slice_int, Slice_float, etc. structures are generated
+ // First, ensure std/slice.zc is imported (auto-import if needed)
+ auto_import_std_slice(ctx);
+ Token dummy_tok = {0};
+ instantiate_generic(ctx, "Slice", elem_type_str, elem_type_str, dummy_tok);
+
+ // Instantiate SliceIter and Option too for the loop logic
+ char iter_type[256];
+ sprintf(iter_type, "SliceIter<%s>", elem_type_str);
+ instantiate_generic(ctx, "SliceIter", elem_type_str, elem_type_str, dummy_tok);
+
+ char option_type[256];
+ sprintf(option_type, "Option<%s>", elem_type_str);
+ instantiate_generic(ctx, "Option", elem_type_str, elem_type_str, dummy_tok);
+
+ // Replace obj_expr with a reference to the slice variable
+ ASTNode *slice_ref = ast_create(NODE_EXPR_VAR);
+ slice_ref->var_ref.name = xstrdup("__zc_arr_slice");
+ slice_ref->resolved_type =
+ xstrdup(slice_type); // Explicitly set type for codegen
+ obj_expr = slice_ref;
+
+ free(elem_type_str);
+ }
+
// var __it = obj.iterator();
ASTNode *it_decl = ast_create(NODE_VAR_DECL);
it_decl->var_decl.name = xstrdup("__it");
@@ -1182,6 +1388,34 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l)
stmts_tail = node; \
}
+ char *iter_type_ptr = NULL;
+ char *option_type_ptr = NULL;
+
+ if (slice_decl)
+ {
+ char *slice_t = slice_decl->var_decl.type_str;
+ char *start = strchr(slice_t, '<');
+ if (start)
+ {
+ char *end = strrchr(slice_t, '>');
+ if (end)
+ {
+ int len = end - start - 1;
+ char *elem = xmalloc(len + 1);
+ strncpy(elem, start + 1, len);
+ elem[len] = 0;
+
+ iter_type_ptr = xmalloc(256);
+ sprintf(iter_type_ptr, "SliceIter<%s>", elem);
+
+ option_type_ptr = xmalloc(256);
+ sprintf(option_type_ptr, "Option<%s>", elem);
+
+ free(elem);
+ }
+ }
+ }
+
// var __opt = __it.next();
ASTNode *opt_decl = ast_create(NODE_VAR_DECL);
opt_decl->var_decl.name = xstrdup("__opt");
@@ -1192,6 +1426,10 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l)
ASTNode *memb_next = ast_create(NODE_EXPR_MEMBER);
ASTNode *it_ref = ast_create(NODE_EXPR_VAR);
it_ref->var_ref.name = xstrdup("__it");
+ if (iter_type_ptr)
+ {
+ it_ref->resolved_type = xstrdup(iter_type_ptr);
+ }
memb_next->member.target = it_ref;
memb_next->member.field = xstrdup("next");
call_next->call.callee = memb_next;
@@ -1204,15 +1442,22 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l)
ASTNode *memb_is_none = ast_create(NODE_EXPR_MEMBER);
ASTNode *opt_ref1 = ast_create(NODE_EXPR_VAR);
opt_ref1->var_ref.name = xstrdup("__opt");
+ if (option_type_ptr)
+ {
+ opt_ref1->resolved_type = xstrdup(option_type_ptr);
+ }
memb_is_none->member.target = opt_ref1;
memb_is_none->member.field = xstrdup("is_none");
call_is_none->call.callee = memb_is_none;
+ call_is_none->call.args = NULL;
+ call_is_none->call.arg_count = 0;
- ASTNode *break_stmt = ast_create(NODE_BREAK);
-
+ // if (__opt.is_none()) break;
ASTNode *if_break = ast_create(NODE_IF);
if_break->if_stmt.condition = call_is_none;
+ ASTNode *break_stmt = ast_create(NODE_BREAK);
if_break->if_stmt.then_body = break_stmt;
+ if_break->if_stmt.else_body = NULL;
APPEND_STMT(if_break);
// var <user_var> = __opt.unwrap();
@@ -1225,25 +1470,28 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l)
ASTNode *memb_unwrap = ast_create(NODE_EXPR_MEMBER);
ASTNode *opt_ref2 = ast_create(NODE_EXPR_VAR);
opt_ref2->var_ref.name = xstrdup("__opt");
+ if (option_type_ptr)
+ {
+ opt_ref2->resolved_type = xstrdup(option_type_ptr);
+ }
memb_unwrap->member.target = opt_ref2;
memb_unwrap->member.field = xstrdup("unwrap");
call_unwrap->call.callee = memb_unwrap;
+ call_unwrap->call.args = NULL;
+ call_unwrap->call.arg_count = 0;
user_var_decl->var_decl.init_expr = call_unwrap;
APPEND_STMT(user_var_decl);
- // User Body
+ // User body statements
enter_scope(ctx);
add_symbol(ctx, var_name, NULL, NULL);
- ASTNode *user_body_node;
- if (lexer_peek(l).type == TOK_LBRACE)
+ // Body block
+ ASTNode *stmt = parse_statement(ctx, l);
+ ASTNode *user_body_node = stmt;
+ if (stmt && stmt->type != NODE_BLOCK)
{
- user_body_node = parse_block(ctx, l);
- }
- else
- {
- ASTNode *stmt = parse_statement(ctx, l);
ASTNode *blk = ast_create(NODE_BLOCK);
blk->block.statements = stmt;
user_body_node = blk;
@@ -1256,10 +1504,21 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l)
loop_body->block.statements = stmts_head;
while_loop->while_stmt.body = loop_body;
- // Wrap entire thing in a block to scope _it
+ // Wrap entire thing in a block to scope __it (and __zc_arr_slice if present)
ASTNode *outer_block = ast_create(NODE_BLOCK);
- it_decl->next = while_loop;
- outer_block->block.statements = it_decl;
+ if (slice_decl)
+ {
+ // Chain: slice_decl -> it_decl -> while_loop
+ slice_decl->next = it_decl;
+ it_decl->next = while_loop;
+ outer_block->block.statements = slice_decl;
+ }
+ else
+ {
+ // Chain: it_decl -> while_loop
+ it_decl->next = while_loop;
+ outer_block->block.statements = it_decl;
+ }
return outer_block;
}
@@ -3179,7 +3438,7 @@ char *run_comptime_block(ParserContext *ctx, Lexer *l)
ASTNode *fn = ref->node;
if (fn && fn->type == NODE_FUNCTION && fn->func.is_comptime)
{
- emit_func_signature(f, fn, NULL);
+ emit_func_signature(ctx, f, fn, NULL);
fprintf(f, ";\n");
codegen_node_single(ctx, fn, f);
}
diff --git a/src/parser/parser_struct.c b/src/parser/parser_struct.c
index 82dd346..0c984a6 100644
--- a/src/parser/parser_struct.c
+++ b/src/parser/parser_struct.c
@@ -12,6 +12,114 @@
#include "zprep_plugin.h"
#include "../codegen/codegen.h"
+extern char *g_current_filename;
+
+/**
+ * @brief Auto-imports std/mem.zc if not already imported.
+ *
+ * This is called when the Drop trait is used (impl Drop for X).
+ */
+static void auto_import_std_mem(ParserContext *ctx)
+{
+ // Check if Drop trait is already registered (means mem.zc was imported)
+ if (check_impl(ctx, "Drop", "__trait_marker__"))
+ {
+ // Check_impl returns 0 if not found, but we need a different check
+ // Let's check if we can find any indicator that mem.zc was loaded
+ }
+
+ // Try to find and import std/mem.zc
+ static const char *std_paths[] = {"std/mem.zc", "./std/mem.zc", NULL};
+ static const char *system_paths[] = {"/usr/local/share/zenc", "/usr/share/zenc", NULL};
+
+ char resolved_path[1024];
+ int found = 0;
+
+ // First, try relative to current file
+ if (g_current_filename)
+ {
+ char *current_dir = xstrdup(g_current_filename);
+ char *last_slash = strrchr(current_dir, '/');
+ if (last_slash)
+ {
+ *last_slash = 0;
+ snprintf(resolved_path, sizeof(resolved_path), "%s/std/mem.zc", current_dir);
+ if (access(resolved_path, R_OK) == 0)
+ {
+ found = 1;
+ }
+ }
+ free(current_dir);
+ }
+
+ // Try relative paths
+ if (!found)
+ {
+ for (int i = 0; std_paths[i] && !found; i++)
+ {
+ if (access(std_paths[i], R_OK) == 0)
+ {
+ strncpy(resolved_path, std_paths[i], sizeof(resolved_path) - 1);
+ resolved_path[sizeof(resolved_path) - 1] = '\0';
+ found = 1;
+ }
+ }
+ }
+
+ // Try system paths
+ if (!found)
+ {
+ for (int i = 0; system_paths[i] && !found; i++)
+ {
+ snprintf(resolved_path, sizeof(resolved_path), "%s/std/mem.zc", system_paths[i]);
+ if (access(resolved_path, R_OK) == 0)
+ {
+ found = 1;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ return; // Could not find std/mem.zc
+ }
+
+ // Canonicalize path
+ char *real_fn = realpath(resolved_path, NULL);
+ if (real_fn)
+ {
+ strncpy(resolved_path, real_fn, sizeof(resolved_path) - 1);
+ resolved_path[sizeof(resolved_path) - 1] = '\0';
+ free(real_fn);
+ }
+
+ // Check if already imported
+ if (is_file_imported(ctx, resolved_path))
+ {
+ return;
+ }
+ mark_file_imported(ctx, resolved_path);
+
+ // Load and parse the file
+ char *src = load_file(resolved_path);
+ if (!src)
+ {
+ return; // Could not load file
+ }
+
+ Lexer i;
+ lexer_init(&i, src);
+
+ // Save and restore filename context
+ char *saved_fn = g_current_filename;
+ g_current_filename = resolved_path;
+
+ // Parse the mem module contents
+ parse_program_nodes(ctx, &i);
+
+ g_current_filename = saved_fn;
+}
+
// Trait Parsing
ASTNode *parse_trait(ParserContext *ctx, Lexer *l)
{
@@ -100,7 +208,7 @@ ASTNode *parse_trait(ParserContext *ctx, Lexer *l)
char **param_names = NULL;
int is_varargs = 0;
char *args = parse_and_convert_args(ctx, l, &defaults, &arg_count, &arg_types, &param_names,
- &is_varargs);
+ &is_varargs, NULL);
char *ret = xstrdup("void");
if (lexer_peek(l).type == TOK_ARROW)
@@ -149,6 +257,7 @@ ASTNode *parse_trait(ParserContext *ctx, Lexer *l)
}
register_trait(name);
+ add_to_global_list(ctx, n_node); // Track for codegen (VTable emission)
return n_node;
}
@@ -206,6 +315,22 @@ 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)
+ {
+ auto_import_std_mem(ctx);
+ }
+
register_impl(ctx, name1, name2);
// RAII: Check for "Drop" trait implementation
@@ -748,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);
@@ -832,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);
@@ -1005,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 48418b6..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;
@@ -3392,7 +3406,8 @@ char *consume_and_rewrite(ParserContext *ctx, Lexer *l)
}
char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out, int *count_out,
- Type ***types_out, char ***names_out, int *is_varargs_out)
+ Type ***types_out, char ***names_out, int *is_varargs_out,
+ char ***ctype_overrides_out)
{
Token t = lexer_next(l);
if (t.type != TOK_LPAREN)
@@ -3406,18 +3421,52 @@ char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out,
char **defaults = xmalloc(sizeof(char *) * 16);
Type **types = xmalloc(sizeof(Type *) * 16);
char **names = xmalloc(sizeof(char *) * 16);
+ char **ctype_overrides = xmalloc(sizeof(char *) * 16);
for (int i = 0; i < 16; i++)
{
defaults[i] = NULL;
types[i] = NULL;
names[i] = NULL;
+ ctype_overrides[i] = NULL;
}
if (lexer_peek(l).type != TOK_RPAREN)
{
while (1)
{
+ // Check for @ctype("...") before parameter
+ char *ctype_override = NULL;
+ if (lexer_peek(l).type == TOK_AT)
+ {
+ lexer_next(l); // eat @
+ Token attr = lexer_next(l);
+ if (attr.type == TOK_IDENT && attr.len == 5 && strncmp(attr.start, "ctype", 5) == 0)
+ {
+ if (lexer_next(l).type != TOK_LPAREN)
+ {
+ zpanic_at(lexer_peek(l), "Expected ( after @ctype");
+ }
+ Token ctype_tok = lexer_next(l);
+ if (ctype_tok.type != TOK_STRING)
+ {
+ zpanic_at(ctype_tok, "@ctype requires a string argument");
+ }
+ // Extract string content (strip quotes)
+ ctype_override = xmalloc(ctype_tok.len - 1);
+ strncpy(ctype_override, ctype_tok.start + 1, ctype_tok.len - 2);
+ ctype_override[ctype_tok.len - 2] = 0;
+ if (lexer_next(l).type != TOK_RPAREN)
+ {
+ zpanic_at(lexer_peek(l), "Expected ) after @ctype string");
+ }
+ }
+ else
+ {
+ zpanic_at(attr, "Unknown parameter attribute @%.*s", attr.len, attr.start);
+ }
+ }
+
Token t = lexer_next(l);
// Handle 'self'
if (t.type == TOK_IDENT && strncmp(t.start, "self", 4) == 0 && t.len == 4)
@@ -3470,6 +3519,7 @@ char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out,
types[count] = type_new_ptr(type_new(TYPE_VOID));
add_symbol(ctx, "self", "void*", types[count]);
}
+ ctype_overrides[count] = ctype_override;
count++;
}
else
@@ -3514,11 +3564,20 @@ char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out,
}
else
{
- strcat(buf, type_str);
+ // Use @ctype override if present
+ if (ctype_override)
+ {
+ strcat(buf, ctype_override);
+ }
+ else
+ {
+ strcat(buf, type_str);
+ }
strcat(buf, " ");
strcat(buf, name);
}
+ ctype_overrides[count] = ctype_override;
count++;
if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "="))
@@ -3593,6 +3652,10 @@ char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out,
*count_out = count;
*types_out = types;
*names_out = names;
+ if (ctype_overrides_out)
+ {
+ *ctype_overrides_out = ctype_overrides;
+ }
return buf;
}