summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ast/ast.c42
-rw-r--r--src/ast/ast.h16
-rw-r--r--src/codegen/codegen.c266
-rw-r--r--src/codegen/codegen.h4
-rw-r--r--src/codegen/codegen_decl.c35
-rw-r--r--src/codegen/codegen_main.c35
-rw-r--r--src/codegen/codegen_stmt.c15
-rw-r--r--src/codegen/codegen_utils.c52
-rw-r--r--src/codegen/compat.h84
-rw-r--r--src/lexer/token.c12
-rw-r--r--src/lsp/lsp_analysis.c71
-rw-r--r--src/lsp/lsp_index.c52
-rw-r--r--src/main.c13
-rw-r--r--src/parser/parser.h29
-rw-r--r--src/parser/parser_core.c32
-rw-r--r--src/parser/parser_decl.c70
-rw-r--r--src/parser/parser_expr.c200
-rw-r--r--src/parser/parser_stmt.c282
-rw-r--r--src/parser/parser_struct.c129
-rw-r--r--src/parser/parser_type.c103
-rw-r--r--src/parser/parser_utils.c67
-rw-r--r--src/repl/repl.c1589
-rw-r--r--src/utils/utils.c223
-rw-r--r--src/zprep.h2
24 files changed, 2871 insertions, 552 deletions
diff --git a/src/ast/ast.c b/src/ast/ast.c
index 0799845..439a9f5 100644
--- a/src/ast/ast.c
+++ b/src/ast/ast.c
@@ -100,6 +100,7 @@ int is_integer_type(Type *t)
t->kind == TYPE_I64 || t->kind == TYPE_U64 || t->kind == TYPE_USIZE ||
t->kind == TYPE_ISIZE || t->kind == TYPE_BYTE || t->kind == TYPE_RUNE ||
t->kind == TYPE_UINT || t->kind == TYPE_I128 || t->kind == TYPE_U128 ||
+ t->kind == TYPE_BITINT || t->kind == TYPE_UBITINT ||
(t->kind == TYPE_STRUCT && t->name &&
(0 == strcmp(t->name, "int8_t") || 0 == strcmp(t->name, "uint8_t") ||
0 == strcmp(t->name, "int16_t") || 0 == strcmp(t->name, "uint16_t") ||
@@ -168,6 +169,18 @@ int type_eq(Type *a, Type *b)
{
return 0 == strcmp(a->name, b->name);
}
+ if (a->kind == TYPE_ALIAS)
+ {
+ if (a->alias.is_opaque_alias)
+ {
+ if (b->kind != TYPE_ALIAS || !b->alias.is_opaque_alias)
+ {
+ return 0;
+ }
+ return 0 == strcmp(a->name, b->name);
+ }
+ return type_eq(a->inner, b);
+ }
if (a->kind == TYPE_POINTER || a->kind == TYPE_ARRAY)
{
return type_eq(a->inner, b->inner);
@@ -250,6 +263,18 @@ static char *type_to_string_impl(Type *t)
return xstrdup("int");
case TYPE_FLOAT:
return xstrdup("float");
+ case TYPE_BITINT:
+ {
+ char *res = xmalloc(32);
+ sprintf(res, "i%d", t->array_size);
+ return res;
+ }
+ case TYPE_UBITINT:
+ {
+ char *res = xmalloc(32);
+ sprintf(res, "u%d", t->array_size);
+ return res;
+ }
case TYPE_POINTER:
{
@@ -340,6 +365,8 @@ static char *type_to_string_impl(Type *t)
}
return xstrdup(t->name);
}
+ case TYPE_ALIAS:
+ return xstrdup(t->name);
default:
return xstrdup("unknown");
@@ -438,6 +465,18 @@ static char *type_to_c_string_impl(Type *t)
return xstrdup("int");
case TYPE_FLOAT:
return xstrdup("float");
+ case TYPE_BITINT:
+ {
+ char *res = xmalloc(32);
+ sprintf(res, "_BitInt(%d)", t->array_size);
+ return res;
+ }
+ case TYPE_UBITINT:
+ {
+ char *res = xmalloc(40);
+ sprintf(res, "unsigned _BitInt(%d)", t->array_size);
+ return res;
+ }
case TYPE_POINTER:
{
@@ -524,6 +563,9 @@ static char *type_to_c_string_impl(Type *t)
case TYPE_GENERIC:
return xstrdup(t->name);
+ case TYPE_ALIAS:
+ return type_to_c_string(t->inner);
+
case TYPE_ENUM:
return xstrdup(t->name);
diff --git a/src/ast/ast.h b/src/ast/ast.h
index b272cae..4498d7c 100644
--- a/src/ast/ast.h
+++ b/src/ast/ast.h
@@ -58,6 +58,9 @@ typedef enum
TYPE_ARRAY, ///< Fixed size array [N].
TYPE_FUNCTION, ///< Function pointer or reference.
TYPE_GENERIC, ///< Generic type parameter (T).
+ TYPE_ALIAS, ///< Opaque type alias.
+ TYPE_BITINT, ///< C23 _BitInt(N).
+ TYPE_UBITINT, ///< C23 unsigned _BitInt(N).
TYPE_UNKNOWN ///< Unknown/unresolved type.
} TypeKind;
@@ -74,7 +77,7 @@ typedef struct Type
int is_const; ///< 1 if const-qualified.
int is_explicit_struct; ///< 1 if defined with "struct" keyword explicitly.
int is_raw; // Raw function pointer (fn*)
- int array_size; ///< Size for fixed-size arrays.
+ int array_size; ///< Size for fixed-size arrays. For TYPE_BITINT, this is the bit width.
union
{
int is_varargs; ///< 1 if function type is variadic.
@@ -84,6 +87,11 @@ typedef struct Type
int has_drop; ///< 1 if type implements Drop trait (RAII).
int has_iterable; ///< 1 if type implements Iterable trait.
} traits;
+ struct
+ {
+ int is_opaque_alias;
+ char *alias_defined_in_file;
+ } alias;
};
} Type;
@@ -221,6 +229,8 @@ struct ASTNode
int cuda_device; // @device -> __device__
int cuda_host; // @host -> __host__
+ char **c_type_overrides; // @ctype("...") per parameter
+
Attribute *attributes; // Custom attributes
} func;
@@ -263,6 +273,8 @@ struct ASTNode
{
char *alias;
char *original_type;
+ int is_opaque;
+ char *defined_in_file;
} type_alias;
struct
@@ -436,6 +448,8 @@ struct ASTNode
Attribute *attributes; // Custom attributes
char **used_structs; // Names of structs used/mixed-in
int used_struct_count;
+ int is_opaque;
+ char *defined_in_file; // File where the struct is defined (for privacy check)
} strct;
struct
diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c
index 7c58943..37415c2 100644
--- a/src/codegen/codegen.c
+++ b/src/codegen/codegen.c
@@ -1,4 +1,3 @@
-
#include "codegen.h"
#include "zprep.h"
#include "../constants.h"
@@ -59,12 +58,58 @@ static void codegen_var_expr(ParserContext *ctx, ASTNode *node, FILE *out)
if (node->var_ref.suggestion && !ctx->silent_warnings)
{
char msg[256];
- sprintf(msg, "Undefined variable '%s'", node->var_ref.name);
char help[256];
- sprintf(help, "Did you mean '%s'?", node->var_ref.suggestion);
+ snprintf(msg, sizeof(msg), "Undefined variable '%s'", node->var_ref.name);
+ snprintf(help, sizeof(help), "Did you mean '%s'?", node->var_ref.suggestion);
zwarn_at(node->token, "%s\n = help: %s", msg, help);
}
}
+
+ // Check for static method call pattern: Type::method or Slice<T>::method
+ char *double_colon = strstr(node->var_ref.name, "::");
+ if (double_colon)
+ {
+ // Extract type name and method name
+ int type_len = double_colon - node->var_ref.name;
+ char *type_name = xmalloc(type_len + 1);
+ strncpy(type_name, node->var_ref.name, type_len);
+ type_name[type_len] = 0;
+
+ char *method_name = double_colon + 2; // Skip ::
+
+ // Handle generic types: Slice<int> -> Slice_int
+ char mangled_type[256];
+ if (strchr(type_name, '<'))
+ {
+ // Generic type - need to mangle it
+ char *lt = strchr(type_name, '<');
+ char *gt = strchr(type_name, '>');
+
+ if (lt && gt)
+ {
+ // Extract base type and type argument
+ *lt = 0;
+ char *type_arg = lt + 1;
+ *gt = 0;
+
+ sprintf(mangled_type, "%s_%s", type_name, type_arg);
+ }
+ else
+ {
+ strcpy(mangled_type, type_name);
+ }
+ }
+ else
+ {
+ strcpy(mangled_type, type_name);
+ }
+
+ // Output as Type__method
+ fprintf(out, "%s__%s", mangled_type, method_name);
+ free(type_name);
+ return;
+ }
+
fprintf(out, "%s", node->var_ref.name);
}
@@ -146,7 +191,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
else if ((strcmp(node->binary.op, "==") == 0 || strcmp(node->binary.op, "!=") == 0))
{
char *t1 = infer_type(ctx, node->binary.left);
-
int is_ptr = 0;
if (t1)
{
@@ -161,19 +205,16 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
}
int resolved = 0;
ASTNode *alias = global_user_structs;
- if (alias)
+ while (alias)
{
- while (alias)
+ if (alias->type == NODE_TYPE_ALIAS &&
+ strcmp(check, alias->type_alias.alias) == 0)
{
- if (alias->type == NODE_TYPE_ALIAS &&
- strcmp(check, alias->type_alias.alias) == 0)
- {
- check = alias->type_alias.original_type;
- resolved = 1;
- break;
- }
- alias = alias->next;
+ check = alias->type_alias.original_type;
+ resolved = 1;
+ break;
}
+ alias = alias->next;
}
if (!resolved)
{
@@ -183,10 +224,9 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
}
int is_basic = IS_BASIC_TYPE(t1);
-
ASTNode *def = t1 ? find_struct_def(ctx, t1) : NULL;
- if (t1 && def && (def->type == NODE_STRUCT || def->type == NODE_ENUM) && !is_basic &&
- !is_ptr)
+
+ if (t1 && def && (def->type == NODE_STRUCT || def->type == NODE_ENUM) && !is_basic && !is_ptr)
{
char *base = t1;
if (strncmp(base, "struct ", 7) == 0)
@@ -239,7 +279,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
else if (t1 && (strcmp(t1, "string") == 0 || strcmp(t1, "char*") == 0 ||
strcmp(t1, "const char*") == 0))
{
- // Check if comparing to NULL - don't use strcmp for NULL comparisons
int is_null_compare = 0;
if (node->binary.right->type == NODE_EXPR_VAR &&
strcmp(node->binary.right->var_ref.name, "NULL") == 0)
@@ -251,10 +290,21 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
{
is_null_compare = 1;
}
+ else if (node->binary.right->type == NODE_EXPR_LITERAL &&
+ node->binary.right->literal.type_kind == LITERAL_INT &&
+ node->binary.right->literal.int_val == 0)
+ {
+ is_null_compare = 1;
+ }
+ else if (node->binary.left->type == NODE_EXPR_LITERAL &&
+ node->binary.left->literal.type_kind == LITERAL_INT &&
+ node->binary.left->literal.int_val == 0)
+ {
+ is_null_compare = 1;
+ }
if (is_null_compare)
{
- // Direct pointer comparison for NULL
fprintf(out, "(");
codegen_expression(ctx, node->binary.left, out);
fprintf(out, " %s ", node->binary.op);
@@ -285,6 +335,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
codegen_expression(ctx, node->binary.right, out);
fprintf(out, ")");
}
+ if (t1) free(t1);
}
else
{
@@ -348,7 +399,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
if (def && def->type == NODE_ENUM)
{
char mangled[256];
- sprintf(mangled, "%s_%s", target->var_ref.name, method);
+ snprintf(mangled, sizeof(mangled), "%s_%s", target->var_ref.name, method);
FuncSig *sig = find_func(ctx, mangled);
if (sig)
{
@@ -357,15 +408,13 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
int arg_idx = 0;
while (arg)
{
- if (arg_idx > 0 && arg)
+ if (arg_idx > 0)
{
fprintf(out, ", ");
}
- Type *param_t =
- (arg_idx < sig->total_args) ? sig->arg_types[arg_idx] : NULL;
+ Type *param_t = (arg_idx < sig->total_args) ? sig->arg_types[arg_idx] : NULL;
- // Tuple Packing Logic
if (param_t && param_t->kind == TYPE_STRUCT &&
strncmp(param_t->name, "Tuple_", 6) == 0 && sig->total_args == 1 &&
node->call.arg_count > 1)
@@ -383,7 +432,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
arg = arg->next;
}
fprintf(out, "}");
- break; // All args consumed
+ break;
}
codegen_expression(ctx, arg, out);
@@ -403,7 +452,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
char *ptr = strchr(clean, '*');
if (ptr)
{
- *ptr = 0;
+ *ptr = '\0';
}
char *base = clean;
@@ -412,11 +461,43 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
base += 7;
}
+ char *mangled_base = base;
+ char base_buf[256];
+
+ // Mangle generic types: Slice<int> -> Slice_int, Vec<Point> -> Vec_Point
+ char *lt = strchr(base, '<');
+ if (lt)
+ {
+ char *gt = strchr(lt, '>');
+ if (gt)
+ {
+ int prefix_len = lt - base;
+ int arg_len = gt - lt - 1;
+ snprintf(base_buf, 255, "%.*s_%.*s", prefix_len, base, arg_len, lt + 1);
+ mangled_base = base_buf;
+ }
+ }
+
if (!strchr(type, '*') && target->type == NODE_EXPR_CALL)
{
- fprintf(out, "({ %s _t = ", type);
+ char *type_mangled = type;
+ char type_buf[256];
+ char *t_lt = strchr(type, '<');
+ if (t_lt)
+ {
+ char *t_gt = strchr(t_lt, '>');
+ if (t_gt)
+ {
+ int p_len = t_lt - type;
+ int a_len = t_gt - t_lt - 1;
+ snprintf(type_buf, 255, "%.*s_%.*s", p_len, type, a_len, t_lt + 1);
+ type_mangled = type_buf;
+ }
+ }
+
+ fprintf(out, "({ %s _t = ", type_mangled);
codegen_expression(ctx, target, out);
- fprintf(out, "; %s__%s(&_t", base, method);
+ fprintf(out, "; %s__%s(&_t", mangled_base, method);
ASTNode *arg = node->call.args;
while (arg)
{
@@ -429,36 +510,34 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
else
{
// Mixin Lookup Logic
- char *call_base = base;
+ char *call_base = mangled_base;
+
int need_cast = 0;
char mixin_func_name[128];
- sprintf(mixin_func_name, "%s__%s", base, method);
+ snprintf(mixin_func_name, sizeof(mixin_func_name), "%s__%s", call_base, method);
char *resolved_method_suffix = NULL;
if (!find_func(ctx, mixin_func_name))
{
- // Try resolving as a trait method: Struct__Trait_Method
StructRef *ref = ctx->parsed_impls_list;
while (ref)
{
- if (ref->node && ref->node->type == NODE_IMPL_TRAIT)
+ if (ref->node && ref->node->type == NODE_IMPL_TRAIT &&
+ strcmp(ref->node->impl_trait.target_type, base) == 0)
{
- if (strcmp(ref->node->impl_trait.target_type, base) == 0)
+ char trait_mangled[256];
+ snprintf(trait_mangled, sizeof(trait_mangled), "%s__%s_%s", base,
+ ref->node->impl_trait.trait_name, method);
+ if (find_func(ctx, trait_mangled))
{
- char trait_mangled[256];
- sprintf(trait_mangled, "%s__%s_%s", base,
- ref->node->impl_trait.trait_name, method);
- if (find_func(ctx, trait_mangled))
- {
- char *suffix =
- xmalloc(strlen(ref->node->impl_trait.trait_name) +
- strlen(method) + 2);
- sprintf(suffix, "%s_%s", ref->node->impl_trait.trait_name,
- method);
- resolved_method_suffix = suffix;
- break;
- }
+ size_t suffix_len = strlen(ref->node->impl_trait.trait_name) +
+ strlen(method) + 2;
+ char *suffix = xmalloc(suffix_len);
+ snprintf(suffix, suffix_len, "%s_%s",
+ ref->node->impl_trait.trait_name, method);
+ resolved_method_suffix = suffix;
+ break;
}
}
ref = ref->next;
@@ -473,15 +552,14 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
if (it->impl_node && it->impl_node->type == NODE_IMPL_TRAIT)
{
tname = it->impl_node->impl_trait.trait_name;
- }
- if (tname)
- {
char trait_mangled[512];
- sprintf(trait_mangled, "%s__%s_%s", base, tname, method);
+ snprintf(trait_mangled, sizeof(trait_mangled),
+ "%s__%s_%s", base, tname, method);
if (find_func(ctx, trait_mangled))
{
- char *suffix = xmalloc(strlen(tname) + strlen(method) + 2);
- sprintf(suffix, "%s_%s", tname, method);
+ size_t suffix_len = strlen(tname) + strlen(method) + 2;
+ char *suffix = xmalloc(suffix_len);
+ snprintf(suffix, suffix_len, "%s_%s", tname, method);
resolved_method_suffix = suffix;
break;
}
@@ -496,15 +574,14 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
}
else
{
- // Method not found on primary struct, check mixins
ASTNode *def = find_struct_def(ctx, base);
if (def && def->type == NODE_STRUCT && def->strct.used_structs)
{
for (int k = 0; k < def->strct.used_struct_count; k++)
{
char mixin_check[128];
- sprintf(mixin_check, "%s__%s", def->strct.used_structs[k],
- method);
+ snprintf(mixin_check, sizeof(mixin_check), "%s__%s",
+ def->strct.used_structs[k], method);
if (find_func(ctx, mixin_check))
{
call_base = def->strct.used_structs[k];
@@ -534,11 +611,19 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
arg = arg->next;
}
fprintf(out, ")");
+
+ if (resolved_method_suffix)
+ {
+ free(resolved_method_suffix);
+ }
}
free(clean);
+ free(type);
return;
}
+ if (type) free(type);
}
+
if (node->call.callee->type == NODE_EXPR_VAR)
{
ASTNode *def = find_struct_def(ctx, node->call.callee->var_ref.name);
@@ -594,26 +679,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
if (node->call.arg_names && node->call.callee->type == NODE_EXPR_VAR)
{
- char *fn_name = node->call.callee->var_ref.name;
- FuncSig *sig = find_func(ctx, fn_name);
-
- if (sig && sig->arg_types)
- {
- for (int p = 0; p < sig->total_args; p++)
- {
- ASTNode *arg = node->call.args;
-
- for (int i = 0; i < node->call.arg_count && arg; i++, arg = arg->next)
- {
- if (node->call.arg_names[i] && p < node->call.arg_count)
- {
-
- // For now, emit in order provided...
- }
- }
- }
- }
-
ASTNode *arg = node->call.args;
int first = 1;
while (arg)
@@ -660,11 +725,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
strncmp(param_t->name, "Tuple_", 6) == 0 && sig->total_args == 1 &&
node->call.arg_count > 1)
{
- // Implicit Tuple Packing:
- // Function expects 1 Tuple argument, but call has multiple args -> Pack
- // them
fprintf(out, "(%s){", param_t->name);
-
ASTNode *curr = arg;
int first_field = 1;
while (curr)
@@ -679,8 +740,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
}
fprintf(out, "}");
handled = 1;
-
- // Advance main loop iterator to end
arg = NULL;
}
}
@@ -689,7 +748,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
{
if (arg == NULL)
{
- break; // Tuple packed all args
+ break;
}
}
else
@@ -714,16 +773,12 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
case NODE_EXPR_MEMBER:
if (strcmp(node->member.field, "len") == 0)
{
- if (node->member.target->type_info)
+ if (node->member.target->type_info &&
+ node->member.target->type_info->kind == TYPE_ARRAY &&
+ node->member.target->type_info->array_size > 0)
{
- if (node->member.target->type_info->kind == TYPE_ARRAY)
- {
- if (node->member.target->type_info->array_size > 0)
- {
- fprintf(out, "%d", node->member.target->type_info->array_size);
- break;
- }
- }
+ fprintf(out, "%d", node->member.target->type_info->array_size);
+ break;
}
}
@@ -746,17 +801,14 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
else
{
codegen_expression(ctx, node->member.target, out);
- // Verify actual type instead of trusting is_pointer_access flag
char *lt = infer_type(ctx, node->member.target);
int actually_ptr = 0;
if (lt && (lt[strlen(lt) - 1] == '*' || strstr(lt, "*")))
{
actually_ptr = 1;
}
- if (lt)
- {
- free(lt);
- }
+ if (lt) free(lt);
+
char *field = node->member.field;
if (field && field[0] >= '0' && field[0] <= '9')
{
@@ -779,7 +831,8 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
is_slice_struct = 1;
}
}
- if (node->index.array->resolved_type)
+
+ if (!is_slice_struct && node->index.array->resolved_type)
{
if (strncmp(node->index.array->resolved_type, "Slice_", 6) == 0)
{
@@ -794,10 +847,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
{
is_slice_struct = 1;
}
- if (inferred)
- {
- free(inferred);
- }
+ if (inferred) free(inferred);
}
if (is_slice_struct)
@@ -896,7 +946,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
}
else
{
- fprintf(out, "/* UNSAFE: Full Slice on unknown size */ 0; ");
+ fprintf(out, "0; ");
}
}
@@ -912,6 +962,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
fprintf(out, "(Slice_%s){ .data = _arr + _start, .len = _len, .cap = _len }; })",
tname);
}
+ if (tname && strcmp(tname, "unknown") != 0) free(tname);
break;
}
case NODE_BLOCK:
@@ -1002,9 +1053,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
break;
case NODE_PLUGIN:
{
- // Plugin registry - declare external plugins
ZPlugin *found = zptr_find_plugin(node->plugin_stmt.plugin_name);
-
if (found)
{
ZApi api = {.filename = g_current_filename ? g_current_filename : "input.zc",
@@ -1084,9 +1133,9 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
break;
}
case NODE_EXPR_CAST:
- fprintf(out, "(%s)(", node->cast.target_type);
+ fprintf(out, "((%s)(", node->cast.target_type);
codegen_expression(ctx, node->cast.expr, out);
- fprintf(out, ")");
+ fprintf(out, "))");
break;
case NODE_EXPR_SIZEOF:
if (node->size_of.target_type)
@@ -1117,20 +1166,19 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
{
Type *t = node->reflection.target_type;
if (node->reflection.kind == 0)
- { // @type_name
+ {
char *s = codegen_type_to_string(t);
fprintf(out, "\"%s\"", s);
free(s);
}
else
- { // @fields
+ {
if (t->kind != TYPE_STRUCT || !t->name)
{
fprintf(out, "((void*)0)");
break;
}
char *sname = t->name;
- // Find definition
ASTNode *def = find_struct_def(ctx, sname);
if (!def)
{
diff --git a/src/codegen/codegen.h b/src/codegen/codegen.h
index b3e971d..89614f7 100644
--- a/src/codegen/codegen.h
+++ b/src/codegen/codegen.h
@@ -48,7 +48,7 @@ char *replace_string_type(const char *args);
const char *parse_original_method_name(const char *mangled);
void emit_auto_type(ParserContext *ctx, ASTNode *init_expr, Token t, FILE *out);
char *codegen_type_to_string(Type *t);
-void emit_func_signature(FILE *out, ASTNode *func, const char *name_override);
+void emit_func_signature(ParserContext *ctx, FILE *out, ASTNode *func, const char *name_override);
char *strip_template_suffix(const char *name);
int emit_move_invalidation(ParserContext *ctx, ASTNode *node, FILE *out);
void codegen_expression_with_move(ParserContext *ctx, ASTNode *node, FILE *out);
@@ -66,7 +66,7 @@ void emit_trait_defs(ASTNode *node, FILE *out);
void emit_enum_protos(ASTNode *node, FILE *out);
void emit_globals(ParserContext *ctx, ASTNode *node, FILE *out);
void emit_lambda_defs(ParserContext *ctx, FILE *out);
-void emit_protos(ASTNode *node, FILE *out);
+void emit_protos(ParserContext *ctx, ASTNode *node, FILE *out);
void emit_impl_vtables(ParserContext *ctx, FILE *out);
/**
diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c
index 5fb9f54..31bd2ee 100644
--- a/src/codegen/codegen_decl.c
+++ b/src/codegen/codegen_decl.c
@@ -3,6 +3,7 @@
#include "../parser/parser.h"
#include "../zprep.h"
#include "codegen.h"
+#include "compat.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -12,7 +13,7 @@ static void emit_freestanding_preamble(FILE *out)
fputs("#include <stddef.h>\n#include <stdint.h>\n#include "
"<stdbool.h>\n#include <stdarg.h>\n",
out);
- fputs("#ifdef __TINYC__\n#define __auto_type __typeof__\n#endif\n", out);
+ fputs(ZC_TCC_COMPAT_STR, out);
fputs("typedef size_t usize;\ntypedef char* string;\n", out);
fputs("#define U0 void\n#define I8 int8_t\n#define U8 uint8_t\n#define I16 "
"int16_t\n#define U16 uint16_t\n",
@@ -49,6 +50,7 @@ void emit_preamble(ParserContext *ctx, FILE *out)
else
{
// Standard hosted preamble.
+ fputs("#define _GNU_SOURCE\n", out);
fputs("#include <stdio.h>\n#include <stdlib.h>\n#include "
"<stddef.h>\n#include <string.h>\n",
out);
@@ -84,20 +86,18 @@ void emit_preamble(ParserContext *ctx, FILE *out)
else
{
// C mode
+ fputs("#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202300L\n", out);
+ fputs("#define ZC_AUTO auto\n", out);
+ fputs("#else\n", out);
fputs("#define ZC_AUTO __auto_type\n", out);
+ fputs("#endif\n", out);
fputs("#define ZC_CAST(T, x) ((T)(x))\n", out);
- fputs("#ifdef __TINYC__\n#define __auto_type __typeof__\n#endif\n", out);
+ fputs(ZC_TCC_COMPAT_STR, out);
fputs("static inline const char* _z_bool_str(_Bool b) { return b ? \"true\" : "
"\"false\"; }\n",
out);
- fputs("#define _z_str(x) _Generic((x), _Bool: \"%s\", char: \"%c\", "
- "signed char: \"%c\", unsigned char: \"%u\", short: \"%d\", "
- "unsigned short: \"%u\", int: \"%d\", unsigned int: \"%u\", "
- "long: \"%ld\", unsigned long: \"%lu\", long long: \"%lld\", "
- "unsigned long long: \"%llu\", float: \"%f\", double: \"%f\", "
- "char*: \"%s\", void*: \"%p\")\n",
- out);
- fputs("#define _z_arg(x) _Generic((x), _Bool: _z_bool_str(x), default: (x))\n", out);
+ fputs(ZC_C_GENERIC_STR, out);
+ fputs(ZC_C_ARG_GENERIC_STR, out);
}
fputs("typedef size_t usize;\ntypedef char* string;\n", out);
@@ -107,12 +107,11 @@ void emit_preamble(ParserContext *ctx, FILE *out)
fputs("typedef struct { pthread_t thread; void *result; } Async;\n", out);
}
fputs("typedef struct { void *func; void *ctx; } z_closure_T;\n", out);
- fputs("#define U0 void\n#define I8 int8_t\n#define U8 uint8_t\n#define I16 "
- "int16_t\n#define U16 uint16_t\n",
+ fputs("typedef void U0;\ntypedef int8_t I8;\ntypedef uint8_t U8;\ntypedef "
+ "int16_t I16;\ntypedef uint16_t U16;\n",
out);
- fputs("#define I32 int32_t\n#define U32 uint32_t\n#define I64 "
- "int64_t\n#define U64 "
- "uint64_t\n",
+ fputs("typedef int32_t I32;\ntypedef uint32_t U32;\ntypedef int64_t I64;\ntypedef "
+ "uint64_t U64;\n",
out);
fputs("#define F32 float\n#define F64 double\n", out);
@@ -700,7 +699,7 @@ void emit_globals(ParserContext *ctx, ASTNode *node, FILE *out)
}
// Emit function prototypes
-void emit_protos(ASTNode *node, FILE *out)
+void emit_protos(ParserContext *ctx, ASTNode *node, FILE *out)
{
ASTNode *f = node;
while (f)
@@ -723,7 +722,7 @@ void emit_protos(ASTNode *node, FILE *out)
}
else
{
- emit_func_signature(out, f, NULL);
+ emit_func_signature(ctx, out, f, NULL);
fprintf(out, ";\n");
}
}
@@ -801,7 +800,7 @@ void emit_protos(ASTNode *node, FILE *out)
}
else
{
- emit_func_signature(out, m, proto);
+ emit_func_signature(ctx, out, m, proto);
fprintf(out, ";\n");
}
diff --git a/src/codegen/codegen_main.c b/src/codegen/codegen_main.c
index a140070..82fc3ce 100644
--- a/src/codegen/codegen_main.c
+++ b/src/codegen/codegen_main.c
@@ -448,6 +448,39 @@ void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out)
emit_type_aliases(kids, out); // Emit local aliases (redundant but safe)
emit_trait_defs(kids, out);
+ // Also emit traits from parsed_globals_list (from auto-imported files like std/mem.zc)
+ // but only if they weren't already emitted from kids
+ StructRef *trait_ref = ctx->parsed_globals_list;
+ while (trait_ref)
+ {
+ if (trait_ref->node && trait_ref->node->type == NODE_TRAIT)
+ {
+ // Check if this trait was already in kids (explicitly imported)
+ int already_in_kids = 0;
+ ASTNode *k = kids;
+ while (k)
+ {
+ if (k->type == NODE_TRAIT && k->trait.name && trait_ref->node->trait.name &&
+ strcmp(k->trait.name, trait_ref->node->trait.name) == 0)
+ {
+ already_in_kids = 1;
+ break;
+ }
+ k = k->next;
+ }
+
+ if (!already_in_kids)
+ {
+ // Create a temporary single-node list for emit_trait_defs
+ ASTNode *saved_next = trait_ref->node->next;
+ trait_ref->node->next = NULL;
+ emit_trait_defs(trait_ref->node, out);
+ trait_ref->node->next = saved_next;
+ }
+ }
+ trait_ref = trait_ref->next;
+ }
+
// Track emitted raw statements to prevent duplicates
EmittedContent *emitted_raw = NULL;
@@ -616,7 +649,7 @@ void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out)
}
}
- emit_protos(merged_funcs, out);
+ emit_protos(ctx, merged_funcs, out);
emit_impl_vtables(ctx, out);
diff --git a/src/codegen/codegen_stmt.c b/src/codegen/codegen_stmt.c
index bd0c816..7828ecf 100644
--- a/src/codegen/codegen_stmt.c
+++ b/src/codegen/codegen_stmt.c
@@ -750,7 +750,7 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
{
fprintf(out, "inline ");
}
- emit_func_signature(out, node, NULL);
+ emit_func_signature(ctx, out, node, NULL);
fprintf(out, "\n");
fprintf(out, "{\n");
char *prev_ret = g_current_func_ret_type;
@@ -884,8 +884,7 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
}
else
{
- fprintf(out, " __auto_type %s = _tmp_%d.%s;\n", node->destruct.names[0], id,
- check);
+ fprintf(out, " ZC_AUTO %s = _tmp_%d.%s;\n", node->destruct.names[0], id, check);
}
}
else
@@ -903,8 +902,8 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
}
else
{
- fprintf(out, " __auto_type %s = _tmp_%d.%s;\n", node->destruct.names[i],
- id, field);
+ fprintf(out, " ZC_AUTO %s = _tmp_%d.%s;\n", node->destruct.names[i], id,
+ field);
}
}
else
@@ -916,8 +915,8 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
}
else
{
- fprintf(out, " __auto_type %s = _tmp_%d.v%d;\n", node->destruct.names[i],
- id, i);
+ fprintf(out, " ZC_AUTO %s = _tmp_%d.v%d;\n", node->destruct.names[i], id,
+ i);
}
}
}
@@ -1528,7 +1527,7 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
}
else
{
- fprintf(out, "__auto_type");
+ fprintf(out, "ZC_AUTO");
}
fprintf(out, " _z_ret_mv = ");
codegen_expression(ctx, node->ret.value, out);
diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c
index 8de3cf6..08707cc 100644
--- a/src/codegen/codegen_utils.c
+++ b/src/codegen/codegen_utils.c
@@ -41,7 +41,7 @@ char *strip_template_suffix(const char *name)
}
// Helper to emit C declaration (handle arrays, function pointers correctly)
-void emit_c_decl(FILE *out, const char *type_str, const char *name)
+void emit_c_decl(ParserContext *ctx, FILE *out, const char *type_str, const char *name)
{
char *bracket = strchr(type_str, '[');
char *generic = strchr(type_str, '<');
@@ -64,9 +64,34 @@ void emit_c_decl(FILE *out, const char *type_str, const char *name)
}
else if (generic && (!bracket || generic < bracket))
{
- // Strip generic part for C output
- int base_len = generic - type_str;
- fprintf(out, "%.*s %s", base_len, type_str, name);
+ char mangled_candidate[256];
+ char *gt = strchr(generic, '>');
+ int success = 0;
+
+ if (gt)
+ {
+ int base_len = generic - type_str;
+ int arg_len = gt - generic - 1;
+
+ // Limit check
+ if (base_len + arg_len + 2 < 256)
+ {
+ snprintf(mangled_candidate, 256, "%.*s_%.*s", base_len, type_str, arg_len,
+ generic + 1);
+
+ if (find_struct_def_codegen(ctx, mangled_candidate))
+ {
+ fprintf(out, "%s %s", mangled_candidate, name);
+ success = 1;
+ }
+ }
+ }
+
+ if (!success)
+ {
+ int base_len = generic - type_str;
+ fprintf(out, "%.*s %s", base_len, type_str, name);
+ }
if (bracket)
{
@@ -87,8 +112,7 @@ void emit_c_decl(FILE *out, const char *type_str, const char *name)
// Helper to emit variable declarations with array types.
void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str, const char *var_name)
{
- (void)ctx;
- emit_c_decl(out, type_str, var_name);
+ emit_c_decl(ctx, out, type_str, var_name);
}
// Find struct definition
@@ -411,6 +435,10 @@ char *infer_type(ParserContext *ctx, ASTNode *node)
char *inner = infer_type(ctx, node->unary.operand);
if (inner)
{
+ if (strcmp(inner, "string") == 0)
+ {
+ return xstrdup("char");
+ }
char *ptr = strchr(inner, '*');
if (ptr)
{
@@ -640,7 +668,7 @@ char *codegen_type_to_string(Type *t)
}
// Emit function signature using Type info for correct C codegen
-void emit_func_signature(FILE *out, ASTNode *func, const char *name_override)
+void emit_func_signature(ParserContext *ctx, FILE *out, ASTNode *func, const char *name_override)
{
if (!func || func->type != NODE_FUNCTION)
{
@@ -710,7 +738,12 @@ void emit_func_signature(FILE *out, ASTNode *func, const char *name_override)
}
char *type_str = NULL;
- if (func->func.arg_types && func->func.arg_types[i])
+ // Check for @ctype override first
+ if (func->func.c_type_overrides && func->func.c_type_overrides[i])
+ {
+ type_str = xstrdup(func->func.c_type_overrides[i]);
+ }
+ else if (func->func.arg_types && func->func.arg_types[i])
{
type_str = codegen_type_to_string(func->func.arg_types[i]);
}
@@ -720,13 +753,14 @@ void emit_func_signature(FILE *out, ASTNode *func, const char *name_override)
}
const char *name = "";
+
if (func->func.param_names && func->func.param_names[i])
{
name = func->func.param_names[i];
}
// check if array type
- emit_c_decl(out, type_str, name);
+ emit_c_decl(ctx, out, type_str, name);
free(type_str);
}
if (func->func.is_varargs)
diff --git a/src/codegen/compat.h b/src/codegen/compat.h
index f2d221a..f8d9a4e 100644
--- a/src/codegen/compat.h
+++ b/src/codegen/compat.h
@@ -14,7 +14,11 @@
#define ZC_EXTERN_C_END }
#else
/* C mode */
-#define ZC_AUTO __auto_type ///< Auto type inference.
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202300L
+#define ZC_AUTO auto ///< C23 standard auto.
+#else
+#define ZC_AUTO __auto_type ///< GCC/Clang extension.
+#endif
#define ZC_CAST(T, x) ((T)(x)) ///< Explicit cast.
#define ZC_REINTERPRET(T, x) ((T)(x)) ///< Reinterpret cast.
#define ZC_EXTERN_C ///< Extern "C" (no-op in C).
@@ -22,6 +26,58 @@
#define ZC_EXTERN_C_END
#endif
+#ifdef __TINYC__
+/* TCC compatibility */
+#ifndef __auto_type
+#define __auto_type __typeof__
+#endif
+
+#ifndef __builtin_expect
+#define __builtin_expect(x, v) (x)
+#endif
+
+#ifndef __builtin_unreachable
+#define __builtin_unreachable()
+#endif
+#endif
+
+/* Centralized string definition for codegen emission */
+#define ZC_TCC_COMPAT_STR \
+ "#ifdef __TINYC__\n" \
+ "#ifndef __auto_type\n" \
+ "#define __auto_type __typeof__\n" \
+ "#endif\n" \
+ "\n" \
+ "#ifndef __builtin_expect\n" \
+ "#define __builtin_expect(x, v) (x)\n" \
+ "#endif\n" \
+ "\n" \
+ "#ifndef __builtin_unreachable\n" \
+ "#define __builtin_unreachable()\n" \
+ "#endif\n" \
+ "#endif\n"
+
+/* Generic selection string for C mode */
+#define ZC_C_GENERIC_STR \
+ "#ifdef __OBJC__\n" \
+ "#define _z_objc_map ,id: \"%s\", Class: \"%s\", SEL: \"%s\"\n" \
+ "#define _z_objc_arg_map(x) ,id: [(id)(x) description].UTF8String, Class: " \
+ "class_getName((Class)(x)), SEL: sel_getName((SEL)(x))\n" \
+ "#else\n" \
+ "#define _z_objc_map\n" \
+ "#define _z_objc_arg_map(x)\n" \
+ "#endif\n" \
+ "\n" \
+ "#define _z_str(x) _Generic((x), _Bool: \"%s\", char: \"%c\", " \
+ "signed char: \"%c\", unsigned char: \"%u\", short: \"%d\", " \
+ "unsigned short: \"%u\", int: \"%d\", unsigned int: \"%u\", " \
+ "long: \"%ld\", unsigned long: \"%lu\", long long: \"%lld\", " \
+ "unsigned long long: \"%llu\", float: \"%f\", double: \"%f\", " \
+ "char*: \"%s\", void*: \"%p\" _z_objc_map)\n"
+
+#define ZC_C_ARG_GENERIC_STR \
+ "#define _z_arg(x) _Generic((x), _Bool: _z_bool_str(x) _z_objc_arg_map(x), default: (x))\n"
+
#ifdef __cplusplus
#include <type_traits>
@@ -95,6 +151,32 @@ inline const char *_zc_fmt(void *)
}
#define _z_str(x) _zc_fmt(x)
+
+#ifdef __OBJC__
+#include <objc/objc.h>
+#include <objc/runtime.h>
+#include <objc/message.h> // for direct calls if needed, but [x description] is fine
+
+inline const char *_zc_fmt(id x)
+{
+ return [[x description] UTF8String];
+}
+inline const char *_zc_fmt(Class x)
+{
+ return class_getName(x);
+}
+inline const char *_zc_fmt(SEL x)
+{
+ return sel_getName(x);
+}
+// BOOL is signed char usually, already handled?
+// "typedef signed char BOOL;" on standard apple headers.
+// If it maps to signed char, `_zc_fmt(signed char)` handles it ("%c").
+// We might want "YES"/"NO" for BOOL.
+// But we can't distinguish typedefs in C++ function overloads easily if underlying type is same.
+// We'll leave BOOL as %c or %d for now to avoid ambiguity errors.
+#endif
+
#endif
#endif
diff --git a/src/lexer/token.c b/src/lexer/token.c
index decabbe..095815d 100644
--- a/src/lexer/token.c
+++ b/src/lexer/token.c
@@ -149,6 +149,14 @@ Token lexer_next(Lexer *l)
{
return (Token){TOK_DEF, s, 3, start_line, start_col};
}
+ if (len == 5 && strncmp(s, "trait", 5) == 0)
+ {
+ return (Token){TOK_TRAIT, s, 5, start_line, start_col};
+ }
+ if (len == 4 && strncmp(s, "impl", 4) == 0)
+ {
+ return (Token){TOK_IMPL, s, 4, start_line, start_col};
+ }
if (len == 8 && strncmp(s, "autofree", 8) == 0)
{
return (Token){TOK_AUTOFREE, s, 8, start_line, start_col};
@@ -193,6 +201,10 @@ Token lexer_next(Lexer *l)
{
return (Token){TOK_OR, s, 2, start_line, start_col};
}
+ if (len == 6 && strncmp(s, "opaque", 6) == 0)
+ {
+ return (Token){TOK_OPAQUE, s, 6, start_line, start_col};
+ }
// F-Strings
if (len == 1 && s[0] == 'f' && s[1] == '"')
diff --git a/src/lsp/lsp_analysis.c b/src/lsp/lsp_analysis.c
index 088bede..0367d93 100644
--- a/src/lsp/lsp_analysis.c
+++ b/src/lsp/lsp_analysis.c
@@ -454,11 +454,80 @@ void lsp_completion(const char *uri, int line, int col, int id)
cJSON_AddStringToObject(item, "label", s->name);
cJSON_AddNumberToObject(item, "kind", 22); // Struct
char detail[256];
- sprintf(detail, "struct %s", s->name);
+ sprintf(detail, "%sstruct %s",
+ (s->node && s->node->type == NODE_STRUCT && s->node->strct.is_opaque)
+ ? "opaque "
+ : "",
+ s->name);
cJSON_AddStringToObject(item, "detail", detail);
cJSON_AddItemToArray(items, item);
s = s->next;
}
+
+ // Globals and Constants
+ StructRef *g = g_project->ctx->parsed_globals_list;
+ while (g)
+ {
+ if (g->node)
+ {
+ cJSON *item = cJSON_CreateObject();
+ char *name =
+ (g->node->type == NODE_CONST) ? g->node->var_decl.name : g->node->var_decl.name;
+ cJSON_AddStringToObject(item, "label", name);
+ cJSON_AddNumberToObject(item, "kind", 21); // Constant/Variable
+ char detail[256];
+ sprintf(detail, "%s %s", (g->node->type == NODE_CONST) ? "const" : "var", name);
+ cJSON_AddStringToObject(item, "detail", detail);
+ cJSON_AddItemToArray(items, item);
+ }
+ g = g->next;
+ }
+
+ // Enums
+ StructRef *e = g_project->ctx->parsed_enums_list;
+ while (e)
+ {
+ if (e->node)
+ {
+ cJSON *item = cJSON_CreateObject();
+ cJSON_AddStringToObject(item, "label", e->node->enm.name);
+ cJSON_AddNumberToObject(item, "kind", 13); // Enum
+ char detail[256];
+ sprintf(detail, "enum %s", e->node->enm.name);
+ cJSON_AddStringToObject(item, "detail", detail);
+ cJSON_AddItemToArray(items, item);
+ }
+ e = e->next;
+ }
+
+ // Type Aliases
+ TypeAlias *ta = g_project->ctx->type_aliases;
+ while (ta)
+ {
+ cJSON *item = cJSON_CreateObject();
+ cJSON_AddStringToObject(item, "label", ta->alias);
+ cJSON_AddNumberToObject(item, "kind", 8); // Interface/Reference
+ char detail[256];
+ sprintf(detail, "alias %s = %s", ta->alias, ta->original_type);
+ cJSON_AddStringToObject(item, "detail", detail);
+ cJSON_AddItemToArray(items, item);
+ ta = ta->next;
+ }
+
+ // Keywords
+ const char *keywords[] = {
+ "fn", "struct", "enum", "alias", "return", "if", "else", "for", "while",
+ "break", "continue", "true", "false", "int", "char", "bool", "string", "void",
+ "import", "module", "test", "assert", "defer", "sizeof", "opaque", "unsafe", "asm",
+ "trait", "impl", "u8", "u16", "u32", "u64", "i8", "i16", "i32",
+ "i64", "f32", "f64", "usize", "isize", "const", "var", NULL};
+ for (int i = 0; keywords[i]; i++)
+ {
+ cJSON *item = cJSON_CreateObject();
+ cJSON_AddStringToObject(item, "label", keywords[i]);
+ cJSON_AddNumberToObject(item, "kind", 14); // Keyword
+ cJSON_AddItemToArray(items, item);
+ }
}
cJSON_AddItemToObject(root, "result", items);
diff --git a/src/lsp/lsp_index.c b/src/lsp/lsp_index.c
index 285dec3..975e153 100644
--- a/src/lsp/lsp_index.c
+++ b/src/lsp/lsp_index.c
@@ -151,7 +151,32 @@ void lsp_walk_node(LSPIndex *idx, ASTNode *node)
else if (node->type == NODE_STRUCT)
{
char hover[256];
- sprintf(hover, "struct %s", node->strct.name);
+ if (node->strct.is_opaque)
+ {
+ sprintf(hover, "opaque struct %s", node->strct.name);
+ }
+ else
+ {
+ sprintf(hover, "struct %s", node->strct.name);
+ }
+ lsp_index_add_def(idx, node->token, hover, node);
+ }
+ else if (node->type == NODE_ENUM)
+ {
+ char hover[256];
+ sprintf(hover, "enum %s", node->enm.name);
+ lsp_index_add_def(idx, node->token, hover, node);
+ }
+ else if (node->type == NODE_TYPE_ALIAS)
+ {
+ char hover[256];
+ sprintf(hover, "alias %s = %s", node->type_alias.alias, node->type_alias.original_type);
+ lsp_index_add_def(idx, node->token, hover, node);
+ }
+ else if (node->type == NODE_TRAIT)
+ {
+ char hover[256];
+ sprintf(hover, "trait %s", node->trait.name);
lsp_index_add_def(idx, node->token, hover, node);
}
@@ -196,6 +221,31 @@ void lsp_walk_node(LSPIndex *idx, ASTNode *node)
lsp_walk_node(idx, node->call.callee);
lsp_walk_node(idx, node->call.args);
break;
+ case NODE_MATCH:
+ lsp_walk_node(idx, node->match_stmt.expr);
+ lsp_walk_node(idx, node->match_stmt.cases);
+ break;
+ case NODE_MATCH_CASE:
+ lsp_walk_node(idx, node->match_case.guard);
+ lsp_walk_node(idx, node->match_case.body);
+ break;
+ case NODE_FOR:
+ lsp_walk_node(idx, node->for_stmt.init);
+ lsp_walk_node(idx, node->for_stmt.condition);
+ lsp_walk_node(idx, node->for_stmt.step);
+ lsp_walk_node(idx, node->for_stmt.body);
+ break;
+ case NODE_FOR_RANGE:
+ lsp_walk_node(idx, node->for_range.start);
+ lsp_walk_node(idx, node->for_range.end);
+ lsp_walk_node(idx, node->for_range.body);
+ break;
+ case NODE_LOOP:
+ lsp_walk_node(idx, node->loop_stmt.body);
+ break;
+ case NODE_DEFER:
+ lsp_walk_node(idx, node->defer_stmt.stmt);
+ break;
default:
break;
}
diff --git a/src/main.c b/src/main.c
index e2b3cde..f5d8c1b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -172,6 +172,10 @@ int main(int argc, char **argv)
g_config.use_cuda = 1;
g_config.use_cpp = 1; // CUDA implies C++ mode.
}
+ else if (strcmp(arg, "--objc") == 0)
+ {
+ g_config.use_objc = 1;
+ }
else if (strcmp(arg, "--check") == 0)
{
g_config.mode_check = 1;
@@ -304,6 +308,10 @@ int main(int argc, char **argv)
{
temp_source_file = "out.cpp";
}
+ else if (g_config.use_objc)
+ {
+ temp_source_file = "out.m";
+ }
// Codegen to C/C++/CUDA
FILE *out = fopen(temp_source_file, "w");
@@ -362,8 +370,9 @@ int main(int argc, char **argv)
// If using cosmocc, it handles these usually, but keeping them is okay for Linux targets
snprintf(cmd, sizeof(cmd), "%s %s %s %s %s -o %s %s %s %s -I./src %s", g_config.cc,
- g_config.gcc_flags, g_cflags, g_config.is_freestanding ? "-ffreestanding" : "", "",
- outfile, temp_source_file, math_flag, thread_flag, g_link_flags);
+ g_config.gcc_flags, g_cflags, g_config.is_freestanding ? "-ffreestanding" : "",
+ g_config.quiet ? "-w" : "", outfile, temp_source_file, math_flag, thread_flag,
+ g_link_flags);
if (g_config.verbose)
{
diff --git a/src/parser/parser.h b/src/parser/parser.h
index cf57971..23c2920 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.
@@ -562,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.
@@ -681,10 +681,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 +737,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 +890,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 +900,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 +964,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 e9a418e..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)
{
@@ -700,7 +722,7 @@ static ASTNode *generate_derive_impls(ParserContext *ctx, ASTNode *strct, char *
{
// Simplistic Debug for now, I know.
code = xmalloc(1024);
- sprintf(code, "impl %s { fn to_string(self) -> char* { return \"%s { ... }\"; } }",
+ sprintf(code, "impl %s { fn to_string(self) -> char* { return \"%s {{ ... }}\"; } }",
name, name);
}
else if (0 == strcmp(trait, "Copy"))
diff --git a/src/parser/parser_decl.c b/src/parser/parser_decl.c
index 98f46e1..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)
{
@@ -579,6 +581,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 +637,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 +898,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 +918,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 df2dcf6..7c53d96 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>
@@ -108,6 +148,8 @@ int is_type_copy(ParserContext *ctx, Type *t)
case TYPE_POINTER: // Pointers are Copy
case TYPE_FUNCTION:
case TYPE_ENUM: // Enums are integers
+ case TYPE_BITINT:
+ case TYPE_UBITINT:
return 1;
case TYPE_STRUCT:
@@ -116,6 +158,14 @@ int is_type_copy(ParserContext *ctx, Type *t)
{
return 1;
}
+
+ // If the struct is NOT defined (opaque/C type) and does NOT implement Drop,
+ // treat it as Copy (C behavior).
+ if (!find_struct_def(ctx, t->name) && !check_impl(ctx, "Drop", t->name))
+ {
+ return 1;
+ }
+
return 0;
case TYPE_ARRAY:
@@ -124,6 +174,13 @@ int is_type_copy(ParserContext *ctx, Type *t)
// but if it's a value assignment, C doesn't support it anyway unless wrapped in struct.
return 0;
+ case TYPE_ALIAS:
+ if (t->alias.is_opaque_alias)
+ {
+ return 1;
+ }
+ return is_type_copy(ctx, t->inner);
+
default:
return 1;
}
@@ -935,6 +992,17 @@ static ASTNode *create_fstring_block(ParserContext *ctx, const char *content)
free(txt);
}
+ // Handle escape {{
+ if (brace[1] == '{')
+ {
+ ASTNode *cat = ast_create(NODE_RAW_STMT);
+ cat->raw_stmt.content = xstrdup("strcat(_b, \"{\");");
+ tail->next = cat;
+ tail = cat;
+ cur = brace + 2;
+ continue;
+ }
+
char *end_brace = strchr(brace, '}');
if (!end_brace)
{
@@ -1072,6 +1140,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);
@@ -1093,6 +1162,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);
@@ -1100,9 +1170,32 @@ static ASTNode *parse_float_literal(Token t)
}
// Parse string literal
-static ASTNode *parse_string_literal(Token t)
+static ASTNode *parse_string_literal(ParserContext *ctx, Token t)
{
+ // Check for implicit interpolation
+ int has_interpolation = 0;
+ for (int i = 1; i < t.len - 1; i++)
+ {
+ if (t.start[i] == '{')
+ {
+ has_interpolation = 1;
+ break;
+ }
+ }
+
+ if (has_interpolation)
+ {
+
+ char *inner = xmalloc(t.len);
+ strncpy(inner, t.start + 1, t.len - 2);
+ inner[t.len - 2] = 0;
+ ASTNode *node = create_fstring_block(ctx, inner);
+ free(inner);
+ return node;
+ }
+
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);
@@ -1126,6 +1219,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);
@@ -1276,7 +1370,7 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l)
}
else if (t.type == TOK_STRING)
{
- node = parse_string_literal(t);
+ node = parse_string_literal(ctx, t);
}
else if (t.type == TOK_FSTRING)
{
@@ -2018,6 +2112,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;
@@ -3592,7 +3700,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
}
else if (t->kind == TYPE_STRING)
{
- strcat(fmt, "%s");
+ strcat(fmt, "%ms");
}
else if (t->kind == TYPE_CHAR || t->kind == TYPE_I8 || t->kind == TYPE_U8 ||
t->kind == TYPE_BYTE)
@@ -4012,6 +4120,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)
{
@@ -4041,6 +4173,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)
{
@@ -4620,6 +4776,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;
@@ -5241,6 +5421,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);
@@ -5463,7 +5644,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)
@@ -5507,7 +5698,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_stmt.c b/src/parser/parser_stmt.c
index ab5b89c..0677cf5 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)
{
@@ -462,13 +574,7 @@ ASTNode *parse_defer(ParserContext *ctx, Lexer *l)
}
else
{
- s = ast_create(NODE_RAW_STMT);
- char *raw_content = consume_and_rewrite(ctx, l);
- // consume_and_rewrite strips the semicolon, so we must add it back for proper C generation
- char *safe_content = xmalloc(strlen(raw_content) + 2);
- sprintf(safe_content, "%s;", raw_content);
- free(raw_content);
- s->raw_stmt.content = safe_content;
+ s = parse_statement(ctx, l);
}
ctx->in_defer_block = prev_in_defer;
@@ -1139,6 +1245,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 &&
@@ -1148,6 +1255,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");
@@ -1188,6 +1369,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");
@@ -1198,6 +1407,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;
@@ -1210,15 +1423,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();
@@ -1231,25 +1451,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;
@@ -1262,10 +1485,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;
}
@@ -1877,7 +2111,7 @@ ASTNode *parse_statement(ParserContext *ctx, Lexer *l)
lexer_next(&lookahead);
TokenType next_type = lexer_peek(&lookahead).type;
- if (next_type == TOK_SEMICOLON || next_type == TOK_DOTDOT)
+ if (next_type == TOK_SEMICOLON || next_type == TOK_DOTDOT || next_type == TOK_RBRACE)
{
Token t = lexer_next(l); // consume string
@@ -1894,8 +2128,7 @@ ASTNode *parse_statement(ParserContext *ctx, Lexer *l)
inner[t.len - 2] = 0;
}
- // ; means println (end of line), .. means print (continuation)
- int is_ln = (next_type == TOK_SEMICOLON);
+ int is_ln = (next_type == TOK_SEMICOLON || next_type == TOK_RBRACE);
char **used_syms = NULL;
int used_count = 0;
char *code =
@@ -1913,6 +2146,7 @@ ASTNode *parse_statement(ParserContext *ctx, Lexer *l)
lexer_next(l); // consume optional ;
}
}
+ // If TOK_RBRACE, do not consume it, so parse_block can see it and terminate loop.
ASTNode *n = ast_create(NODE_RAW_STMT);
// Append semicolon to Statement Expression to make it a valid statement
@@ -3185,7 +3419,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 84450ba..e53b56c 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,12 @@ ASTNode *parse_impl(ParserContext *ctx, Lexer *l)
register_generic(ctx, target_gen_param);
}
+ // 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
@@ -652,7 +767,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 +820,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 +916,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 +941,6 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union)
free(use_name);
continue;
}
- // ---------------------------------------
if (t.type == TOK_IDENT)
{
@@ -904,8 +1015,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 +1215,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..49e961c 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);
}
@@ -287,6 +300,90 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l)
free(name);
return type_new(TYPE_I16);
}
+
+ // C23 BitInt Support (i42, u256, etc.)
+ if ((name[0] == 'i' || name[0] == 'u') && isdigit(name[1]))
+ {
+ // Verify it is a purely numeric suffix
+ int valid = 1;
+ for (size_t k = 1; k < strlen(name); k++)
+ {
+ if (!isdigit(name[k]))
+ {
+ valid = 0;
+ break;
+ }
+ }
+ if (valid)
+ {
+ int width = atoi(name + 1);
+ if (width > 0)
+ {
+ // Map standard widths to standard types for standard ABI/C compabitility
+ if (name[0] == 'i')
+ {
+ if (width == 8)
+ {
+ free(name);
+ return type_new(TYPE_I8);
+ }
+ if (width == 16)
+ {
+ free(name);
+ return type_new(TYPE_I16);
+ }
+ if (width == 32)
+ {
+ free(name);
+ return type_new(TYPE_I32);
+ }
+ if (width == 64)
+ {
+ free(name);
+ return type_new(TYPE_I64);
+ }
+ if (width == 128)
+ {
+ free(name);
+ return type_new(TYPE_I128);
+ }
+ }
+ else
+ {
+ if (width == 8)
+ {
+ free(name);
+ return type_new(TYPE_U8);
+ }
+ if (width == 16)
+ {
+ free(name);
+ return type_new(TYPE_U16);
+ }
+ if (width == 32)
+ {
+ free(name);
+ return type_new(TYPE_U32);
+ }
+ if (width == 64)
+ {
+ free(name);
+ return type_new(TYPE_U64);
+ }
+ if (width == 128)
+ {
+ free(name);
+ return type_new(TYPE_U128);
+ }
+ }
+
+ Type *t = type_new(name[0] == 'u' ? TYPE_UBITINT : TYPE_BITINT);
+ t->array_size = width;
+ free(name);
+ return t;
+ }
+ }
+ }
if (strcmp(name, "u16") == 0)
{
free(name);
diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c
index 4e85500..28d2c11 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;
}
@@ -3382,7 +3392,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)
@@ -3396,18 +3407,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)
@@ -3460,6 +3505,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
@@ -3504,11 +3550,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), "="))
@@ -3583,6 +3638,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;
}
diff --git a/src/repl/repl.c b/src/repl/repl.c
index cb63293..b04b2c2 100644
--- a/src/repl/repl.c
+++ b/src/repl/repl.c
@@ -6,22 +6,977 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <ctype.h>
ASTNode *parse_program(ParserContext *ctx, Lexer *l);
static int is_header_line(const char *line)
{
- return (strncmp(line, "import ", 7) == 0 || strncmp(line, "include ", 8) == 0 ||
- strncmp(line, "#include", 8) == 0);
+ // Skip whitespace
+ while (*line && (*line == ' ' || *line == '\t'))
+ {
+ line++;
+ }
+ if (strncmp(line, "struct", 6) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "impl", 4) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "fn", 2) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "use", 3) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "include", 7) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "typedef", 7) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "enum", 4) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "const", 5) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "def", 3) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "#include", 8) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "import", 6) == 0)
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void repl_error_callback(void *data, Token t, const char *msg)
+{
+ (void)data;
+ (void)t;
+ fprintf(stderr, "\033[1;31merror:\033[0m %s\n", msg);
+}
+
+static struct termios orig_termios;
+static int raw_mode_enabled = 0;
+
+static void disable_raw_mode()
+{
+ if (raw_mode_enabled)
+ {
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
+ raw_mode_enabled = 0;
+ }
+}
+
+static void enable_raw_mode()
+{
+ if (!raw_mode_enabled)
+ {
+ tcgetattr(STDIN_FILENO, &orig_termios);
+ atexit(disable_raw_mode);
+ struct termios raw = orig_termios;
+ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+ raw.c_oflag &= ~(OPOST);
+ raw.c_cflag |= (CS8);
+ raw.c_cc[VMIN] = 1;
+ raw.c_cc[VTIME] = 0;
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
+ raw_mode_enabled = 1;
+ }
+}
+
+static const char *KEYWORDS[] = {
+ "fn", "struct", "var", "let", "def", "const", "return", "if",
+ "else", "for", "while", "do", "switch", "case", "default", "break",
+ "continue", "typedef", "enum", "union", "sizeof", "typeof", "import", "include",
+ "defer", "guard", "match", "impl", "trait", "comptime", "asm", "plugin",
+ "true", "false", "null", "NULL", NULL};
+
+static const char *TYPES[] = {"void", "int", "char", "float", "double", "long",
+ "short", "unsigned", "signed", "bool", NULL};
+
+static int find_matching_brace(const char *buf, int pos)
+{
+ if (pos < 0 || pos >= (int)strlen(buf))
+ {
+ return -1;
+ }
+ char c = buf[pos];
+ int dir = 0;
+ char match = 0;
+ if (c == '{')
+ {
+ match = '}';
+ dir = 1;
+ }
+ else if (c == '(')
+ {
+ match = ')';
+ dir = 1;
+ }
+ else if (c == '[')
+ {
+ match = ']';
+ dir = 1;
+ }
+ else if (c == '}')
+ {
+ match = '{';
+ dir = -1;
+ }
+ else if (c == ')')
+ {
+ match = '(';
+ dir = -1;
+ }
+ else if (c == ']')
+ {
+ match = '[';
+ dir = -1;
+ }
+ else
+ {
+ return -1;
+ }
+
+ int depth = 1;
+ int p = pos + dir;
+ int len = strlen(buf);
+ while (p >= 0 && p < len)
+ {
+ if (buf[p] == c)
+ {
+ depth++;
+ }
+ else if (buf[p] == match)
+ {
+ depth--;
+ if (depth == 0)
+ {
+ return p;
+ }
+ }
+ p += dir;
+ }
+ return -1;
+}
+
+// Calculate visible length of a string (ignoring ANSI codes)
+static int get_visible_length(const char *str)
+{
+ int len = 0;
+ int in_esc = 0;
+ while (*str)
+ {
+ if (*str == '\033')
+ {
+ in_esc = 1;
+ }
+ else if (in_esc)
+ {
+ if (*str == 'm' || *str == 'K') // End of SGR or EL
+ {
+ in_esc = 0;
+ }
+ if (isalpha(*str))
+ {
+ in_esc = 0; // Terminating char
+ }
+ }
+ else
+ {
+ len++;
+ }
+ str++;
+ }
+ return len;
+}
+
+// Simple syntax highlighter for the REPL
+static void repl_highlight(const char *buf, int cursor_pos);
+
+static int is_definition_of(const char *code, const char *name)
+{
+ Lexer l;
+ lexer_init(&l, code);
+ Token t = lexer_next(&l);
+ int is_header = 0;
+
+ if (t.type == TOK_UNION)
+ {
+ is_header = 1;
+ }
+ else if (t.type == TOK_IDENT)
+ {
+ if ((t.len == 2 && strncmp(t.start, "fn", 2) == 0) ||
+ (t.len == 6 && strncmp(t.start, "struct", 6) == 0) ||
+ (t.len == 4 && strncmp(t.start, "enum", 4) == 0) ||
+ (t.len == 7 && strncmp(t.start, "typedef", 7) == 0) ||
+ (t.len == 5 && strncmp(t.start, "const", 5) == 0))
+ {
+ is_header = 1;
+ }
+ }
+
+ if (is_header)
+ {
+ Token name_tok = lexer_next(&l);
+ if (name_tok.type == TOK_IDENT)
+ {
+ if (strlen(name) == (size_t)name_tok.len &&
+ strncmp(name, name_tok.start, name_tok.len) == 0)
+ {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static void repl_highlight(const char *buf, int cursor_pos)
+{
+ const char *p = buf;
+
+ int match_pos = -1;
+ int brace_pos = -1;
+
+ // Check under cursor
+ if (find_matching_brace(buf, cursor_pos) != -1)
+ {
+ brace_pos = cursor_pos;
+ match_pos = find_matching_brace(buf, cursor_pos);
+ }
+ // Check before cursor (common behavior when typing)
+ else if (cursor_pos > 0 && find_matching_brace(buf, cursor_pos - 1) != -1)
+ {
+ brace_pos = cursor_pos - 1;
+ match_pos = find_matching_brace(buf, cursor_pos - 1);
+ }
+
+ while (*p)
+ {
+ long idx = p - buf;
+
+ // Highlight matching braces
+ if (idx == brace_pos || idx == match_pos)
+ {
+ printf("\033[1;44;37m"); // Bright White on Blue background
+ putchar(*p);
+ printf("\033[0m");
+ p++;
+ continue;
+ }
+
+ if (strncmp(p, "//", 2) == 0)
+ {
+ printf("\033[1;30m");
+ printf("%s", p);
+ printf("\033[0m");
+ break;
+ }
+ else if (*p == ':' && isalpha(p[1]))
+ {
+ printf("\033[1;35m");
+ while (*p && !isspace(*p))
+ {
+ putchar(*p);
+ p++;
+ }
+ printf("\033[0m");
+ }
+ else if (isdigit(*p))
+ {
+ printf("\033[1;35m");
+ while (isdigit(*p) || *p == '.' || *p == 'x' || *p == 'X')
+ {
+ putchar(*p);
+ p++;
+ }
+ printf("\033[0m");
+ }
+ else if (*p == '"' || *p == '\'')
+ {
+ char quote = *p;
+ printf("\033[1;32m");
+ putchar(*p);
+ p++;
+ while (*p && *p != quote)
+ {
+ if (*p == '\\' && p[1])
+ {
+ putchar(*p);
+ p++;
+ }
+ putchar(*p);
+ p++;
+ }
+ if (*p == quote)
+ {
+ putchar(*p);
+ p++;
+ }
+ printf("\033[0m");
+ }
+ else if (strchr(",;.", *p))
+ {
+ printf("\033[1;30m");
+ putchar(*p);
+ printf("\033[0m");
+ p++;
+ }
+ else if (strchr("{}[]()", *p))
+ {
+ printf("\033[0;36m");
+ putchar(*p);
+ printf("\033[0m");
+ p++;
+ }
+ else if (strchr("+-*/=<>!&|^~%", *p))
+ {
+ printf("\033[1;37m");
+ putchar(*p);
+ printf("\033[0m");
+ p++;
+ }
+ else if (isalpha(*p) || *p == '_')
+ {
+ const char *start = p;
+ while (isalnum(*p) || *p == '_')
+ {
+ p++;
+ }
+ int len = p - start;
+ char word[256];
+ if (len < 256)
+ {
+ strncpy(word, start, len);
+ word[len] = 0;
+
+ int is_keyword = 0;
+ for (int i = 0; KEYWORDS[i]; i++)
+ {
+ if (strcmp(word, KEYWORDS[i]) == 0)
+ {
+ is_keyword = 1;
+ break;
+ }
+ }
+
+ int is_type = 0;
+ if (!is_keyword)
+ {
+ for (int i = 0; TYPES[i]; i++)
+ {
+ if (strcmp(word, TYPES[i]) == 0)
+ {
+ is_type = 1;
+ break;
+ }
+ }
+ }
+
+ int is_func = 0;
+ if (!is_keyword && !is_type)
+ {
+ const char *peek = p;
+ while (*peek && isspace(*peek))
+ {
+ peek++;
+ }
+ if (*peek == '(')
+ {
+ is_func = 1;
+ }
+ }
+
+ int is_const = 0;
+ if (!is_keyword && !is_type && !is_func && len > 1)
+ {
+ int all_upper = 1;
+ int has_upper = 0;
+ for (int i = 0; word[i]; i++)
+ {
+ if (islower(word[i]))
+ {
+ all_upper = 0;
+ }
+ if (isupper(word[i]))
+ {
+ has_upper = 1;
+ }
+ }
+ if (all_upper && has_upper)
+ {
+ is_const = 1;
+ }
+ }
+
+ if (is_keyword)
+ {
+ printf("\033[1;36m");
+ }
+ else if (is_type)
+ {
+ printf("\033[1;33m");
+ }
+ else if (is_func)
+ {
+ printf("\033[1;34m");
+ }
+ else if (is_const)
+ {
+ printf("\033[1;31m");
+ }
+
+ printf("%s", word);
+ printf("\033[0m");
+ }
+ else
+ {
+ printf("%.*s", len, start);
+ }
+ }
+ else
+ {
+ putchar(*p);
+ p++;
+ }
+ }
+}
+
+static char *repl_complete(const char *buf, int pos)
+{
+ int start = pos;
+ while (start > 0 && (isalnum(buf[start - 1]) || buf[start - 1] == '_' ||
+ buf[start - 1] == ':' || buf[start - 1] == '!'))
+ {
+ start--;
+ }
+
+ int len = pos - start;
+ if (len == 0)
+ {
+ return NULL;
+ }
+
+ char prefix[256];
+ if (len >= 255)
+ {
+ return NULL;
+ }
+ strncpy(prefix, buf + start, len);
+ prefix[len] = 0;
+
+ char *match = NULL;
+ int match_count = 0;
+
+ for (int i = 0; KEYWORDS[i]; i++)
+ {
+ if (strncmp(KEYWORDS[i], prefix, len) == 0)
+ {
+ match = (char *)KEYWORDS[i];
+ match_count++;
+ }
+ }
+
+ static const char *COMMANDS[] = {
+ ":help", ":reset", ":imports", ":vars", ":funcs", ":structs", ":history", ":type",
+ ":time", ":c", ":doc", ":run", ":edit", ":save", ":load", ":watch",
+ ":unwatch", ":undo", ":delete", ":clear", ":quit", NULL};
+
+ if (prefix[0] == ':')
+ {
+ for (int i = 0; COMMANDS[i]; i++)
+ {
+ if (strncmp(COMMANDS[i], prefix, len) == 0)
+ {
+ match = (char *)COMMANDS[i];
+ match_count++;
+ }
+ }
+ }
+
+ if (match_count == 1)
+ {
+ return strdup(match + len);
+ }
+
+ return NULL;
+}
+
+static char *repl_readline(const char *prompt, char **history, int history_len, int indent_level)
+{
+ enable_raw_mode();
+
+ int buf_size = 1024;
+ char *buf = malloc(buf_size);
+ buf[0] = 0;
+ int len = 0;
+ int pos = 0;
+
+ if (indent_level > 0)
+ {
+ for (int i = 0; i < indent_level * 4; i++)
+ {
+ if (len >= buf_size - 1)
+ {
+ buf_size *= 2;
+ buf = realloc(buf, buf_size);
+ }
+ buf[len++] = ' ';
+ }
+ buf[len] = 0;
+ pos = len;
+ }
+
+ int history_idx = history_len;
+ char *saved_current_line = NULL;
+
+ int in_search_mode = 0;
+ char search_buf[256];
+ search_buf[0] = 0;
+ int search_match_idx = -1;
+
+ printf("\r\033[K%s", prompt);
+ repl_highlight(buf, pos);
+ fflush(stdout);
+
+ while (1)
+ {
+ char c;
+ if (read(STDIN_FILENO, &c, 1) != 1)
+ {
+ break;
+ }
+
+ if (c == '\x1b')
+ {
+ char seq[3];
+ if (read(STDIN_FILENO, &seq[0], 1) != 1)
+ {
+ continue;
+ }
+ if (read(STDIN_FILENO, &seq[1], 1) != 1)
+ {
+ continue;
+ }
+
+ if (seq[0] == '[')
+ {
+ if (seq[1] == 'A')
+ {
+ if (history_idx > 0)
+ {
+ if (history_idx == history_len)
+ {
+ if (saved_current_line)
+ {
+ free(saved_current_line);
+ }
+ saved_current_line = strdup(buf);
+ }
+ history_idx--;
+ if (history_idx >= 0 && history_idx < history_len)
+ {
+ free(buf);
+ buf = strdup(history[history_idx]);
+ buf_size = strlen(buf) + 1;
+ len = strlen(buf);
+ pos = len;
+ }
+ }
+ }
+ else if (seq[1] == 'B')
+ {
+ if (history_idx < history_len)
+ {
+ history_idx++;
+ free(buf);
+ if (history_idx == history_len)
+ {
+ if (saved_current_line)
+ {
+ buf = strdup(saved_current_line);
+ }
+ else
+ {
+ buf = strdup("");
+ }
+ }
+ else
+ {
+ buf = strdup(history[history_idx]);
+ }
+ buf_size = strlen(buf) + 1;
+ len = strlen(buf);
+ pos = len;
+ }
+ }
+ else if (seq[1] == 'C')
+ {
+ if (pos < len)
+ {
+ pos++;
+ }
+ }
+ else if (seq[1] == 'D')
+ {
+ if (pos > 0)
+ {
+ pos--;
+ }
+ }
+ else if (seq[1] == 'H')
+ {
+ pos = 0;
+ }
+ else if (seq[1] == 'F')
+ {
+ pos = len;
+ }
+ }
+ }
+ else if (c == 127 || c == 8)
+ {
+ if (pos > 0)
+ {
+ memmove(buf + pos - 1, buf + pos, len - pos + 1);
+ len--;
+ pos--;
+ }
+ }
+ else if (c == '\r' || c == '\n')
+ {
+ printf("\r\n");
+ break;
+ }
+ else if (c == 3)
+ {
+ printf("^C\r\n");
+ free(buf);
+ if (saved_current_line)
+ {
+ free(saved_current_line);
+ }
+ disable_raw_mode();
+ return strdup("");
+ }
+ else if (c == 4)
+ {
+ if (len == 0)
+ {
+ free(buf);
+ if (saved_current_line)
+ {
+ free(saved_current_line);
+ }
+ disable_raw_mode();
+ return NULL;
+ }
+ }
+ else if (c == '\t')
+ {
+ char *completion = repl_complete(buf, pos);
+ if (completion)
+ {
+ int clen = strlen(completion);
+ if (len + clen < buf_size - 1)
+ {
+ // Insert completion
+ memmove(buf + pos + clen, buf + pos, len - pos + 1);
+ memcpy(buf + pos, completion, clen);
+ len += clen;
+ pos += clen;
+ }
+ free(completion);
+ }
+ }
+ else if (c == 18)
+ {
+ if (!in_search_mode)
+ {
+ in_search_mode = 1;
+ search_buf[0] = 0;
+ search_match_idx = history_len;
+ }
+
+ int found = -1;
+ int start_idx = search_match_idx - 1;
+ if (start_idx >= history_len)
+ {
+ start_idx = history_len - 1;
+ }
+
+ for (int i = start_idx; i >= 0; i--)
+ {
+ if (strstr(history[i], search_buf))
+ {
+ found = i;
+ break;
+ }
+ }
+
+ if (found != -1)
+ {
+ search_match_idx = found;
+ free(buf);
+ buf = strdup(history[found]);
+ buf_size = strlen(buf) + 1;
+ len = strlen(buf);
+ pos = len;
+ history_idx = found; // Sync history navigation
+ }
+ }
+ else if (in_search_mode)
+ {
+ if (c == 127 || c == 8) // Backspace
+ {
+ int sl = strlen(search_buf);
+ if (sl > 0)
+ {
+ search_buf[sl - 1] = 0;
+ search_match_idx = history_len;
+ int found = -1;
+ for (int i = history_len - 1; i >= 0; i--)
+ {
+ if (strstr(history[i], search_buf))
+ {
+ found = i;
+ break;
+ }
+ }
+ if (found != -1)
+ {
+ search_match_idx = found;
+ free(buf);
+ buf = strdup(history[found]);
+ buf_size = strlen(buf) + 1;
+ len = strlen(buf);
+ pos = len;
+ history_idx = found;
+ }
+ }
+ }
+ else if (c == '\r' || c == '\n' || c == 27 || c == 7 ||
+ c == 3) // Enter/Esc/Ctrl+G/Ctrl+C
+ {
+ in_search_mode = 0;
+ if (c == 3)
+ {
+ // Abort
+ free(buf);
+ buf = strdup("");
+ len = 0;
+ pos = 0;
+ printf("^C\r\n");
+ return buf;
+ }
+ if (c == 7)
+ {
+ // Keep current match
+ }
+ else if (c == '\r' || c == '\n')
+ {
+ printf("\r\n");
+ break;
+ }
+ }
+ else if (!iscntrl(c))
+ {
+ int sl = strlen(search_buf);
+ if (sl < 255)
+ {
+ search_buf[sl] = c;
+ search_buf[sl + 1] = 0;
+
+ int found = -1;
+ for (int i = history_len - 1; i >= 0; i--)
+ {
+ if (strstr(history[i], search_buf))
+ {
+ found = i;
+ break;
+ }
+ }
+ if (found != -1)
+ {
+ search_match_idx = found;
+ free(buf);
+ buf = strdup(history[found]);
+ buf_size = strlen(buf) + 1;
+ len = strlen(buf);
+ pos = len;
+ history_idx = found;
+ }
+ }
+ }
+ }
+ else if (c == 1)
+ {
+ pos = 0;
+ }
+ else if (c == 5)
+ {
+ pos = len;
+ }
+ else if (c == 12)
+ {
+ printf("\033[2J\033[H");
+ }
+ else if (c == 21)
+ {
+ if (pos > 0)
+ {
+ memmove(buf, buf + pos, len - pos + 1);
+ len -= pos;
+ pos = 0;
+ }
+ }
+ else if (c == 11)
+ {
+ buf[pos] = 0;
+ len = pos;
+ }
+ else if (c == 14)
+ {
+ printf("^N\r\n");
+ free(buf);
+ if (saved_current_line)
+ {
+ free(saved_current_line);
+ }
+ disable_raw_mode();
+ return strdup(":reset");
+ }
+ else if (!iscntrl(c))
+ {
+ if (len >= buf_size - 1)
+ {
+ buf_size *= 2;
+ buf = realloc(buf, buf_size);
+ }
+ memmove(buf + pos + 1, buf + pos, len - pos + 1);
+ buf[pos] = c;
+ len++;
+ pos++;
+ }
+
+ if (in_search_mode)
+ {
+ printf("\r\033[K(reverse-i-search)`%s': %s", search_buf, buf);
+ }
+ else
+ {
+ printf("\r\033[K%s", prompt);
+ repl_highlight(buf, pos);
+ int prompt_len = get_visible_length(prompt);
+ if (pos + prompt_len > 0)
+ {
+ printf("\r\033[%dC", pos + prompt_len);
+ }
+ else
+ {
+ printf("\r");
+ }
+ }
+
+ fflush(stdout);
+ }
+
+ if (saved_current_line)
+ {
+ free(saved_current_line);
+ }
+ disable_raw_mode();
+
+ return buf;
+}
+
+static void repl_get_code(char **history, int len, char **out_global, char **out_main)
+{
+ size_t total_len = 0;
+ for (int i = 0; i < len; i++)
+ {
+ total_len += strlen(history[i]) + 2;
+ }
+
+ char *global_buf = malloc(total_len + 1);
+ char *main_buf = malloc(total_len + 1);
+ global_buf[0] = 0;
+ main_buf[0] = 0;
+
+ int brace_depth = 0;
+ int in_global = 0;
+
+ for (int i = 0; i < len; i++)
+ {
+ char *line = history[i];
+
+ if (brace_depth == 0)
+ {
+ if (is_header_line(line))
+ {
+ in_global = 1;
+ }
+ else
+ {
+ in_global = 0;
+ }
+ }
+
+ if (in_global)
+ {
+ strcat(global_buf, line);
+ strcat(global_buf, "\n");
+ }
+ else
+ {
+ strcat(main_buf, line);
+ strcat(main_buf, " ");
+ }
+
+ for (char *p = line; *p; p++)
+ {
+ if (*p == '{')
+ {
+ brace_depth++;
+ }
+ else if (*p == '}')
+ {
+ brace_depth--;
+ }
+ }
+ }
+
+ *out_global = global_buf;
+ *out_main = main_buf;
}
void run_repl(const char *self_path)
{
- printf("\033[1;36mZen C REPL (v0.1)\033[0m\n");
+ printf("\033[1;36mZen C REPL (%s)\033[0m\n", ZEN_VERSION);
printf("Type 'exit' or 'quit' to leave.\n");
printf("Type :help for commands.\n");
- // Dynamic history.
int history_cap = 64;
int history_len = 0;
char **history = xmalloc(history_cap * sizeof(char *));
@@ -69,7 +1024,6 @@ void run_repl(const char *self_path)
history_path[0] = 0;
}
- // Watch list.
char *watches[16];
int watches_len = 0;
for (int i = 0; i < 16; i++)
@@ -77,7 +1031,6 @@ void run_repl(const char *self_path)
watches[i] = NULL;
}
- // Load startup file (~/.zprep_init.zc) if exists
if (home)
{
char init_path[512];
@@ -128,21 +1081,39 @@ void run_repl(const char *self_path)
while (1)
{
- if (brace_depth > 0 || paren_depth > 0)
+ char cwd[1024];
+ char prompt_text[1280];
+ if (getcwd(cwd, sizeof(cwd)))
{
- printf("... ");
+ char *base = strrchr(cwd, '/');
+ if (base)
+ {
+ base++;
+ }
+ else
+ {
+ base = cwd;
+ }
+ snprintf(prompt_text, sizeof(prompt_text), "\033[1;32m%s >>>\033[0m ", base);
}
else
{
- printf("\033[1;32m>>>\033[0m ");
+ strcpy(prompt_text, "\033[1;32m>>>\033[0m ");
}
- if (!fgets(line_buf, sizeof(line_buf), stdin))
+ const char *prompt = (brace_depth > 0 || paren_depth > 0) ? "... " : prompt_text;
+ int indent = (brace_depth > 0) ? brace_depth : 0;
+ char *rline = repl_readline(prompt, history, history_len, indent);
+
+ if (!rline)
{
break;
}
+ strncpy(line_buf, rline, sizeof(line_buf) - 2);
+ line_buf[sizeof(line_buf) - 2] = 0;
+ strcat(line_buf, "\n");
+ free(rline);
- // Handle commands (only on fresh line).
if (NULL == input_buffer)
{
size_t len = strlen(line_buf);
@@ -162,8 +1133,13 @@ void run_repl(const char *self_path)
break;
}
- // Commands
- if (cmd_buf[0] == ':' || cmd_buf[0] == '!')
+ if (cmd_buf[0] == '!')
+ {
+ int ret = system(cmd_buf + 1);
+ printf("(exit code: %d)\n", ret);
+ continue;
+ }
+ if (cmd_buf[0] == ':')
{
if (0 == strcmp(cmd_buf, ":help"))
{
@@ -183,14 +1159,21 @@ void run_repl(const char *self_path)
printf(" :edit [n] Edit command n (default: last) in $EDITOR\n");
printf(" :save <f> Save session to file\n");
printf(" :load <f> Load file into session\n");
- printf(" :load <f> Load file into session\n");
- printf(" :watch <x> Watch expression output\n");
- printf(" :unwatch <n> Remove watch n\n");
printf(" :undo Remove last command\n");
printf(" :delete <n> Remove command at index n\n");
+ printf(" :watch <x> Watch expression output\n");
+ printf(" :unwatch <n> Remove watch n\n");
printf(" :clear Clear screen\n");
printf(" ! <cmd> Run shell command\n");
printf(" :quit Exit REPL\n");
+ printf("\nShortcuts:\n");
+ printf(" Up/Down History navigation\n");
+ printf(" Tab Completion\n");
+ printf(" Ctrl+A Go to start\n");
+ printf(" Ctrl+E Go to end\n");
+ printf(" Ctrl+L Clear screen\n");
+ printf(" Ctrl+U Clear line to start\n");
+ printf(" Ctrl+K Clear line to end\n");
continue;
}
else if (0 == strcmp(cmd_buf, ":reset"))
@@ -207,9 +1190,107 @@ void run_repl(const char *self_path)
{
break;
}
+ else if (0 == strncmp(cmd_buf, ":show ", 6))
+ {
+ char *name = cmd_buf + 6;
+ while (*name && isspace(*name))
+ {
+ name++;
+ }
+
+ int found = 0;
+ printf("Source definition for '%s':\n", name);
+
+ for (int i = history_len - 1; i >= 0; i--)
+ {
+ if (is_definition_of(history[i], name))
+ {
+ printf(" \033[90m// Found in history:\033[0m\n");
+ printf(" ");
+ repl_highlight(history[i], -1);
+ printf("\n");
+ found = 1;
+ break;
+ }
+ }
+
+ if (found)
+ {
+ continue;
+ }
+
+ printf("Source definition for '%s':\n", name);
+
+ size_t show_code_size = 4096;
+ for (int i = 0; i < history_len; i++)
+ {
+ show_code_size += strlen(history[i]) + 2;
+ }
+ char *show_code = malloc(show_code_size);
+ strcpy(show_code, "");
+ for (int i = 0; i < history_len; i++)
+ {
+ strcat(show_code, history[i]);
+ strcat(show_code, "\n");
+ }
+
+ ParserContext ctx = {0};
+ ctx.is_repl = 1;
+ ctx.skip_preamble = 1;
+ ctx.is_fault_tolerant = 1;
+ ctx.on_error = repl_error_callback;
+ Lexer l;
+ lexer_init(&l, show_code);
+ ASTNode *nodes = parse_program(&ctx, &l);
+
+ ASTNode *search = nodes;
+ if (search && search->type == NODE_ROOT)
+ {
+ search = search->root.children;
+ }
+
+ for (ASTNode *n = search; n; n = n->next)
+ {
+ if (n->type == NODE_FUNCTION && 0 == strcmp(n->func.name, name))
+ {
+ printf(" fn %s(%s) -> %s\n", n->func.name,
+ n->func.args ? n->func.args : "",
+ n->func.ret_type ? n->func.ret_type : "void");
+ found = 1;
+ break;
+ }
+ else if (n->type == NODE_STRUCT && 0 == strcmp(n->strct.name, name))
+ {
+ printf(" struct %s {\n", n->strct.name);
+ for (ASTNode *field = n->strct.fields; field; field = field->next)
+ {
+ if (field->type == NODE_FIELD)
+ {
+ printf(" %s: %s;\n", field->field.name, field->field.type);
+ }
+ else if (field->type == NODE_VAR_DECL)
+ {
+ // Fields might be VAR_DECLs in some parses? No, usually
+ // NODE_FIELD for structs.
+ printf(" %s: %s;\n", field->var_decl.name,
+ field->var_decl.type_str);
+ }
+ }
+ printf(" }\n");
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ {
+ printf(" (not found)\n");
+ }
+ free(show_code);
+ continue;
+ }
else if (0 == strcmp(cmd_buf, ":clear"))
{
- printf("\033[2J\033[H"); // ANSI clear screen
+ printf("\033[2J\033[H");
continue;
}
else if (0 == strcmp(cmd_buf, ":undo"))
@@ -289,9 +1370,7 @@ void run_repl(const char *self_path)
const char *editor = getenv("EDITOR");
if (!editor)
{
- editor = "nano"; // Default fallback,
- // 'cause I know some of you
- // don't know how to exit Vim.
+ editor = "nano";
}
char cmd[1024];
@@ -300,7 +1379,6 @@ void run_repl(const char *self_path)
if (0 == status)
{
- // Read back file.
FILE *fr = fopen(edit_path, "r");
if (fr)
{
@@ -415,23 +1493,15 @@ void run_repl(const char *self_path)
FILE *f = fopen(filename, "w");
if (f)
{
- for (int i = 0; i < history_len; i++)
- {
- if (is_header_line(history[i]))
- {
- fprintf(f, "%s\n", history[i]);
- }
- }
- // Write main function body.
- fprintf(f, "\nfn main() {\n");
- for (int i = 0; i < history_len; i++)
- {
- if (!is_header_line(history[i]))
- {
- fprintf(f, " %s\n", history[i]);
- }
- }
- fprintf(f, "}\n");
+ char *global_code = NULL;
+ char *main_code = NULL;
+ repl_get_code(history, history_len, &global_code, &main_code);
+
+ fprintf(f, "%s\n", global_code);
+ fprintf(f, "\nfn main() {\n%s\n}\n", main_code);
+
+ free(global_code);
+ free(main_code);
fclose(f);
printf("Session saved to %s\n", filename);
}
@@ -501,36 +1571,21 @@ void run_repl(const char *self_path)
else if (0 == strcmp(cmd_buf, ":vars") || 0 == strcmp(cmd_buf, ":funcs") ||
0 == strcmp(cmd_buf, ":structs"))
{
- size_t code_size = 4096;
- for (int i = 0; i < history_len; i++)
- {
- code_size += strlen(history[i]) + 2;
- }
- char *code = malloc(code_size + 128);
- strcpy(code, "");
+ char *global_code = NULL;
+ char *main_code = NULL;
+ repl_get_code(history, history_len, &global_code, &main_code);
- for (int i = 0; i < history_len; i++)
- {
- if (is_header_line(history[i]))
- {
- strcat(code, history[i]);
- strcat(code, "\n");
- }
- }
- strcat(code, "fn main() { ");
- for (int i = 0; i < history_len; i++)
- {
- if (!is_header_line(history[i]))
- {
- strcat(code, history[i]);
- strcat(code, " ");
- }
- }
- strcat(code, " }");
+ size_t code_size = strlen(global_code) + strlen(main_code) + 128;
+ char *code = malloc(code_size);
+ sprintf(code, "%s\nfn main() { %s }", global_code, main_code);
+ free(global_code);
+ free(main_code);
ParserContext ctx = {0};
ctx.is_repl = 1;
ctx.skip_preamble = 1;
+ ctx.is_fault_tolerant = 1;
+ ctx.on_error = repl_error_callback;
Lexer l;
lexer_init(&l, code);
@@ -553,11 +1608,27 @@ void run_repl(const char *self_path)
break;
}
}
- printf("Variables:\n");
+
+ // Generate probe code to print values
+ char *global_code = NULL;
+ char *main_code = NULL;
+ repl_get_code(history, history_len, &global_code, &main_code);
+
+ // Generate probe code to print values
+ size_t probe_size = strlen(global_code) + strlen(main_code) + 4096;
+ char *probe_code = malloc(probe_size);
+
+ sprintf(probe_code,
+ "%s\nfn main() { _z_suppress_stdout(); %s _z_restore_stdout(); "
+ "printf(\"Variables:\\n\"); ",
+ global_code, main_code);
+ free(global_code);
+ free(main_code);
+
+ int found_vars = 0;
if (main_func && main_func->func.body &&
main_func->func.body->type == NODE_BLOCK)
{
- int found = 0;
for (ASTNode *s = main_func->func.body->block.statements; s;
s = s->next)
{
@@ -565,19 +1636,92 @@ void run_repl(const char *self_path)
{
char *t =
s->var_decl.type_str ? s->var_decl.type_str : "Inferred";
- printf(" %s: %s\n", s->var_decl.name, t);
- found = 1;
+ // Heuristic for format
+ char fmt[64];
+ char val_expr[128];
+
+ if (s->var_decl.type_str)
+ {
+ if (strcmp(t, "int") == 0 || strcmp(t, "i32") == 0)
+ {
+ strcpy(fmt, "%d");
+ strcpy(val_expr, s->var_decl.name);
+ }
+ else if (strcmp(t, "i64") == 0)
+ {
+ strcpy(fmt, "%ld");
+ sprintf(val_expr, "(long)%s", s->var_decl.name);
+ }
+ else if (strcmp(t, "float") == 0 ||
+ strcmp(t, "double") == 0 ||
+ strcmp(t, "f32") == 0 || strcmp(t, "f64") == 0)
+ {
+ strcpy(fmt, "%f");
+ strcpy(val_expr, s->var_decl.name);
+ }
+ else if (strcmp(t, "bool") == 0)
+ {
+ strcpy(fmt, "%s");
+ sprintf(val_expr, "%s ? \"true\" : \"false\"",
+ s->var_decl.name);
+ }
+ else if (strcmp(t, "string") == 0 ||
+ strcmp(t, "char*") == 0)
+ {
+ strcpy(fmt, "\\\"%s\\\"");
+ strcpy(val_expr, s->var_decl.name);
+ } // quote strings
+ else if (strcmp(t, "char") == 0)
+ {
+ strcpy(fmt, "'%c'");
+ strcpy(val_expr, s->var_decl.name);
+ }
+ else
+ {
+ // Fallback: address
+ strcpy(fmt, "@%p");
+ sprintf(val_expr, "(void*)&%s", s->var_decl.name);
+ }
+ }
+ else
+ {
+ // Inferred: Safe fallback? Or try to guess?
+ // For now, minimal safety: print address
+ strcpy(fmt, "? @%p");
+ sprintf(val_expr, "(void*)&%s", s->var_decl.name);
+ }
+
+ char print_stmt[512];
+ snprintf(print_stmt, sizeof(print_stmt),
+ "printf(\" %s (%s): %s\\n\", %s); ", s->var_decl.name,
+ t, fmt, val_expr);
+ strcat(probe_code, print_stmt);
+ found_vars = 1;
}
}
- if (!found)
- {
- printf(" (none)\n");
- }
}
- else
+
+ if (!found_vars)
{
- printf(" (none)\n");
+ strcat(probe_code, "printf(\" (none)\\n\");");
+ }
+
+ strcat(probe_code, " }");
+
+ // Execute
+ char tmp_path[256];
+ snprintf(tmp_path, sizeof(tmp_path), "/tmp/zen_repl_vars_%d.zc", getpid());
+ FILE *f = fopen(tmp_path, "w");
+ if (f)
+ {
+ fprintf(f, "%s", probe_code);
+ fclose(f);
+ char cmd[512];
+ snprintf(cmd, sizeof(cmd), "%s run -q %s", self_path, tmp_path);
+ system(cmd);
+ remove(tmp_path);
}
+ free(probe_code);
}
else if (0 == strcmp(cmd_buf, ":funcs"))
{
@@ -621,36 +1765,21 @@ void run_repl(const char *self_path)
{
char *expr = cmd_buf + 6;
- size_t probe_size = 4096;
- for (int i = 0; i < history_len; i++)
- {
- probe_size += strlen(history[i]) + 2;
- }
+ char *global_code = NULL;
+ char *main_code = NULL;
+ repl_get_code(history, history_len, &global_code, &main_code);
- char *probe_code = malloc(probe_size + strlen(expr) + 256);
- strcpy(probe_code, "");
+ size_t probe_size =
+ strlen(global_code) + strlen(main_code) + strlen(expr) + 4096;
+ char *probe_code = malloc(probe_size);
- for (int i = 0; i < history_len; i++)
- {
- if (is_header_line(history[i]))
- {
- strcat(probe_code, history[i]);
- strcat(probe_code, "\n");
- }
- }
-
- strcat(probe_code, "fn main() { _z_suppress_stdout(); ");
- for (int i = 0; i < history_len; i++)
- {
- if (!is_header_line(history[i]))
- {
- strcat(probe_code, history[i]);
- strcat(probe_code, " ");
- }
- }
+ sprintf(probe_code, "%s\nfn main() { _z_suppress_stdout(); %s", global_code,
+ main_code);
+ free(global_code);
+ free(main_code);
strcat(probe_code, " raw { typedef struct { int _u; } __REVEAL_TYPE__; } ");
- strcat(probe_code, " var _z_type_probe: __REVEAL_TYPE__; _z_type_probe = (");
+ strcat(probe_code, " let _z_type_probe: __REVEAL_TYPE__; _z_type_probe = (");
strcat(probe_code, expr);
strcat(probe_code, "); }");
@@ -686,18 +1815,51 @@ void run_repl(const char *self_path)
int found = 0;
while (fgets(buf, sizeof(buf), p))
{
- char *marker = "right operand has type '";
- char *start = strstr(buf, marker);
+ char *start = strstr(buf, "from type ");
+ char quote = 0;
+ if (!start)
+ {
+ start = strstr(buf, "incompatible type ");
+ }
+
if (start)
{
- start += strlen(marker);
- char *end = strchr(start, '\'');
- if (end)
+ char *q = strchr(start, '\'');
+ if (!q)
{
- *end = 0;
- printf("\033[1;36mType: %s\033[0m\n", start);
- found = 1;
- break;
+ q = strstr(start, "\xe2\x80\x98");
+ }
+
+ if (q)
+ {
+ if (*q == '\'')
+ {
+ start = q + 1;
+ quote = '\'';
+ }
+ else
+ {
+ start = q + 3;
+ quote = 0;
+ }
+
+ char *end = NULL;
+ if (quote)
+ {
+ end = strchr(start, quote);
+ }
+ else
+ {
+ end = strstr(start, "\xe2\x80\x99");
+ }
+
+ if (end)
+ {
+ *end = 0;
+ printf("\033[1;36mType: %s\033[0m\n", start);
+ found = 1;
+ break;
+ }
}
}
}
@@ -716,33 +1878,21 @@ void run_repl(const char *self_path)
// Benchmark an expression.
char *expr = cmd_buf + 6;
- size_t code_size = 4096;
- for (int i = 0; i < history_len; i++)
- {
- code_size += strlen(history[i]) + 2;
- }
- char *code = malloc(code_size + strlen(expr) + 256);
- strcpy(code, "");
+ char *global_code = NULL;
+ char *main_code = NULL;
+ repl_get_code(history, history_len, &global_code, &main_code);
+
+ size_t code_size =
+ strlen(global_code) + strlen(main_code) + strlen(expr) + 4096;
+ char *code = malloc(code_size);
+
+ sprintf(code,
+ "%s\ninclude \"time.h\"\nfn main() { _z_suppress_stdout();\n%s "
+ "_z_restore_stdout();\n",
+ global_code, main_code);
+ free(global_code);
+ free(main_code);
- for (int i = 0; i < history_len; i++)
- {
- if (is_header_line(history[i]))
- {
- strcat(code, history[i]);
- strcat(code, "\n");
- }
- }
- strcat(code, "include \"time.h\"\n");
- strcat(code, "fn main() { _z_suppress_stdout();\n");
- for (int i = 0; i < history_len; i++)
- {
- if (!is_header_line(history[i]))
- {
- strcat(code, history[i]);
- strcat(code, " ");
- }
- }
- strcat(code, "_z_restore_stdout();\n");
strcat(code, "raw { clock_t _start = clock(); }\n");
strcat(code, "for _i in 0..1000 { ");
strcat(code, expr);
@@ -800,17 +1950,11 @@ void run_repl(const char *self_path)
while (brace_depth > 0)
{
- printf("... ");
- char more[1024];
- if (!fgets(more, sizeof(more), stdin))
+ char *more = repl_readline("... ", history, history_len, brace_depth);
+ if (!more)
{
break;
}
- size_t mlen = strlen(more);
- if (mlen > 0 && more[mlen - 1] == '\n')
- {
- more[--mlen] = 0;
- }
strcat(expr_buf, "\n");
strcat(expr_buf, more);
for (char *p = more; *p; p++)
@@ -824,35 +1968,20 @@ void run_repl(const char *self_path)
brace_depth--;
}
}
+ free(more);
}
- size_t code_size = 4096 + strlen(expr_buf);
- for (int i = 0; i < history_len; i++)
- {
- code_size += strlen(history[i]) + 2;
- }
- char *code = malloc(code_size + 128);
- strcpy(code, "");
+ char *global_code = NULL;
+ char *main_code = NULL;
+ repl_get_code(history, history_len, &global_code, &main_code);
- for (int i = 0; i < history_len; i++)
- {
- if (is_header_line(history[i]))
- {
- strcat(code, history[i]);
- strcat(code, "\n");
- }
- }
- strcat(code, "fn main() {\n");
- for (int i = 0; i < history_len; i++)
- {
- if (!is_header_line(history[i]))
- {
- strcat(code, history[i]);
- strcat(code, " ");
- }
- }
- strcat(code, expr_buf);
- strcat(code, "\n}");
+ size_t code_size =
+ strlen(global_code) + strlen(main_code) + strlen(expr_buf) + 128;
+ char *code = malloc(code_size);
+
+ sprintf(code, "%s\nfn main() { %s %s }", global_code, main_code, expr_buf);
+ free(global_code);
+ free(main_code);
free(expr_buf);
char tmp_path[256];
@@ -877,33 +2006,16 @@ void run_repl(const char *self_path)
}
else if (0 == strcmp(cmd_buf, ":run"))
{
- size_t code_size = 4096;
- for (int i = 0; i < history_len; i++)
- {
- code_size += strlen(history[i]) + 2;
- }
+ char *global_code = NULL;
+ char *main_code = NULL;
+ repl_get_code(history, history_len, &global_code, &main_code);
+
+ size_t code_size = strlen(global_code) + strlen(main_code) + 128;
char *code = malloc(code_size);
- strcpy(code, "");
- for (int i = 0; i < history_len; i++)
- {
- if (is_header_line(history[i]))
- {
- strcat(code, history[i]);
- strcat(code, "\n");
- }
- }
- strcat(code, "fn main() {\n");
- for (int i = 0; i < history_len; i++)
- {
- if (!is_header_line(history[i]))
- {
- strcat(code, " ");
- strcat(code, history[i]);
- strcat(code, "\n");
- }
- }
- strcat(code, "}\n");
+ sprintf(code, "%s\nfn main() { %s }", global_code, main_code);
+ free(global_code);
+ free(main_code);
char tmp_path[256];
sprintf(tmp_path, "/tmp/zprep_repl_run_%d.zc", rand());
@@ -1082,7 +2194,6 @@ void run_repl(const char *self_path)
}
if (!found)
{
- // Fallback: try man pages, show only SYNOPSIS.
char man_cmd[256];
sprintf(man_cmd,
"man 3 %s 2>/dev/null | sed -n '/^SYNOPSIS/,/^[A-Z]/p' | "
@@ -1187,7 +2298,6 @@ void run_repl(const char *self_path)
continue;
}
- // Add to history.
if (history_len >= history_cap)
{
history_cap *= 2;
@@ -1201,40 +2311,20 @@ void run_repl(const char *self_path)
brace_depth = 0;
paren_depth = 0;
- size_t total_size = 4096;
- for (int i = 0; i < history_len; i++)
- {
- total_size += strlen(history[i]) + 2;
- }
+ char *global_code = NULL;
+ char *main_code = NULL;
+ repl_get_code(history, history_len, &global_code, &main_code);
+
+ size_t total_size = strlen(global_code) + strlen(main_code) + 4096;
if (watches_len > 0)
{
- total_size += 16 * 1024; // Plenty of space for watches. Yeah static ik.
+ total_size += 16 * 1024;
}
char *full_code = malloc(total_size);
- strcpy(full_code, "");
-
- // Hoisting pass.
- for (int i = 0; i < history_len; i++)
- {
- if (is_header_line(history[i]))
- {
- strcat(full_code, history[i]);
- strcat(full_code, "\n");
- }
- }
-
- strcat(full_code, "fn main() { _z_suppress_stdout(); ");
-
- for (int i = 0; i < history_len - 1; i++)
- {
- if (is_header_line(history[i]))
- {
- continue;
- }
- strcat(full_code, history[i]);
- strcat(full_code, " ");
- }
+ sprintf(full_code, "%s\nfn main() { _z_suppress_stdout(); %s", global_code, main_code);
+ free(global_code);
+ free(main_code);
strcat(full_code, "_z_restore_stdout(); ");
@@ -1249,6 +2339,8 @@ void run_repl(const char *self_path)
ParserContext ctx = {0};
ctx.is_repl = 1;
ctx.skip_preamble = 1;
+ ctx.is_fault_tolerant = 1;
+ ctx.on_error = repl_error_callback;
Lexer l;
lexer_init(&l, check_buf);
ASTNode *node = parse_statement(&ctx, &l);
@@ -1272,33 +2364,17 @@ void run_repl(const char *self_path)
if (is_expr)
{
- size_t probesz = 4096;
- for (int i = 0; i < history_len - 1; i++)
- {
- probesz += strlen(history[i]) + 2;
- }
- char *probe_code = malloc(probesz + strlen(last_line) + 512);
- strcpy(probe_code, "");
+ char *global_code = NULL;
+ char *main_code = NULL;
+ repl_get_code(history, history_len - 1, &global_code, &main_code);
- for (int i = 0; i < history_len - 1; i++)
- {
- if (is_header_line(history[i]))
- {
- strcat(probe_code, history[i]);
- strcat(probe_code, "\n");
- }
- }
-
- strcat(probe_code, "fn main() { _z_suppress_stdout(); ");
+ size_t probesz = strlen(global_code) + strlen(main_code) + strlen(last_line) + 4096;
+ char *probe_code = malloc(probesz);
- for (int i = 0; i < history_len - 1; i++)
- {
- if (!is_header_line(history[i]))
- {
- strcat(probe_code, history[i]);
- strcat(probe_code, " ");
- }
- }
+ sprintf(probe_code, "%s\nfn main() { _z_suppress_stdout(); %s", global_code,
+ main_code);
+ free(global_code);
+ free(main_code);
strcat(probe_code, " raw { typedef struct { int _u; } __REVEAL_TYPE__; } ");
strcat(probe_code, " var _z_type_probe: __REVEAL_TYPE__; _z_type_probe = (");
@@ -1356,10 +2432,9 @@ void run_repl(const char *self_path)
if (watches_len > 0)
{
- strcat(full_code, "; "); // separator.
+ strcat(full_code, "; ");
for (int i = 0; i < watches_len; i++)
{
- // Use printf for label, then print "{expr}" for value.
char wbuf[1024];
sprintf(wbuf,
"printf(\"\\033[90mwatch:%s = \\033[0m\"); print \"{%s}\"; "
@@ -1401,7 +2476,8 @@ void run_repl(const char *self_path)
FILE *hf = fopen(history_path, "w");
if (hf)
{
- for (int i = 0; i < history_len; i++)
+ int start = history_len > 1000 ? history_len - 1000 : 0;
+ for (int i = start; i < history_len; i++)
{
fprintf(hf, "%s\n", history[i]);
}
@@ -1409,11 +2485,14 @@ void run_repl(const char *self_path)
}
}
- for (int i = 0; i < history_len; i++)
+ if (history)
{
- free(history[i]);
+ for (int i = 0; i < history_len; i++)
+ {
+ free(history[i]);
+ }
+ free(history);
}
- free(history);
if (input_buffer)
{
free(input_buffer);
diff --git a/src/utils/utils.c b/src/utils/utils.c
index 56a7690..d6d9853 100644
--- a/src/utils/utils.c
+++ b/src/utils/utils.c
@@ -533,9 +533,81 @@ char g_cflags[MAX_FLAGS_SIZE] = "";
int g_warning_count = 0;
CompilerConfig g_config = {0};
+// Helper for environment expansion
+static void expand_env_vars(char *dest, size_t dest_size, const char *src)
+{
+ char *d = dest;
+ const char *s = src;
+ size_t remaining = dest_size - 1;
+
+ while (*s && remaining > 0)
+ {
+ if (*s == '$' && *(s + 1) == '{')
+ {
+ const char *end = strchr(s + 2, '}');
+ if (end)
+ {
+ char var_name[256];
+ int len = end - (s + 2);
+ if (len < 255)
+ {
+ strncpy(var_name, s + 2, len);
+ var_name[len] = 0;
+ char *val = getenv(var_name);
+ if (val)
+ {
+ size_t val_len = strlen(val);
+ if (val_len < remaining)
+ {
+ strcpy(d, val);
+ d += val_len;
+ remaining -= val_len;
+ s = end + 1;
+ continue;
+ }
+ }
+ }
+ }
+ }
+ *d++ = *s++;
+ remaining--;
+ }
+ *d = 0;
+}
+
+// Helper to determine active OS
+static int is_os_active(const char *os_name)
+{
+ if (0 == strcmp(os_name, "linux"))
+ {
+#ifdef __linux__
+ return 1;
+#else
+ return 0;
+#endif
+ }
+ else if (0 == strcmp(os_name, "windows"))
+ {
+#ifdef _WIN32
+ return 1;
+#else
+ return 0;
+#endif
+ }
+ else if (0 == strcmp(os_name, "macos") || 0 == strcmp(os_name, "darwin"))
+ {
+#ifdef __APPLE__
+ return 1;
+#else
+ return 0;
+#endif
+ }
+ return 0;
+}
+
void scan_build_directives(ParserContext *ctx, const char *src)
{
- (void)ctx; // Currently unused, reserved for future use
+ (void)ctx;
const char *p = src;
while (*p)
{
@@ -554,103 +626,113 @@ void scan_build_directives(ParserContext *ctx, const char *src)
len++;
}
- char line[2048];
+ char raw_line[2048];
if (len >= 2047)
{
len = 2047;
}
- strncpy(line, start, len);
- line[len] = 0;
+ strncpy(raw_line, start, len);
+ raw_line[len] = 0;
+
+ char line[2048];
+ expand_env_vars(line, sizeof(line), raw_line);
- if (0 == strncmp(line, "link:", 5))
+ char *directive = line;
+ char *colon = strchr(line, ':');
+ if (colon)
{
- char *val = line + 5;
- while (*val == ' ')
+ *colon = 0;
+ if (is_os_active(line))
+ {
+ directive = colon + 1;
+ while (*directive == ' ')
+ {
+ directive++;
+ }
+ }
+ else if (0 == strcmp(line, "linux") || 0 == strcmp(line, "windows") ||
+ 0 == strcmp(line, "macos"))
{
- val++;
+ goto next_line;
}
- if (strlen(g_link_flags) > 0)
+ else
{
- strcat(g_link_flags, " ");
+ *colon = ':';
+ directive = line;
}
- strcat(g_link_flags, val);
}
- else if (0 == strncmp(line, "cflags:", 7))
+
+ // Process Directive
+ if (0 == strncmp(directive, "link:", 5))
{
- char *val = line + 7;
- while (*val == ' ')
+ if (strlen(g_link_flags) > 0)
{
- val++;
+ strcat(g_link_flags, " ");
}
+ strcat(g_link_flags, directive + 5);
+ }
+ else if (0 == strncmp(directive, "cflags:", 7))
+ {
if (strlen(g_cflags) > 0)
{
strcat(g_cflags, " ");
}
- strcat(g_cflags, val);
+ strcat(g_cflags, directive + 7);
}
- else if (0 == strncmp(line, "include:", 8))
+ else if (0 == strncmp(directive, "include:", 8))
{
- char *val = line + 8;
- while (*val == ' ')
- {
- val++;
- }
char flags[2048];
- sprintf(flags, "-I%s", val);
+ sprintf(flags, "-I%s", directive + 8);
if (strlen(g_cflags) > 0)
{
strcat(g_cflags, " ");
}
strcat(g_cflags, flags);
}
- else if (strncmp(line, "lib:", 4) == 0)
+ else if (strncmp(directive, "lib:", 4) == 0)
{
- char *val = line + 4;
- while (*val == ' ')
- {
- val++;
- }
char flags[2048];
- sprintf(flags, "-L%s", val);
+ sprintf(flags, "-L%s", directive + 4);
if (strlen(g_link_flags) > 0)
{
strcat(g_link_flags, " ");
}
strcat(g_link_flags, flags);
}
- else if (strncmp(line, "define:", 7) == 0)
+ else if (strncmp(directive, "framework:", 10) == 0)
{
- char *val = line + 7;
- while (*val == ' ')
+ char flags[2048];
+ sprintf(flags, "-framework %s", directive + 10);
+ if (strlen(g_link_flags) > 0)
{
- val++;
+ strcat(g_link_flags, " ");
}
+ strcat(g_link_flags, flags);
+ }
+ else if (strncmp(directive, "define:", 7) == 0)
+ {
char flags[2048];
- sprintf(flags, "-D%s", val);
+ sprintf(flags, "-D%s", directive + 7);
if (strlen(g_cflags) > 0)
{
strcat(g_cflags, " ");
}
strcat(g_cflags, flags);
}
- else if (0 == strncmp(line, "shell:", 6))
+ else if (0 == strncmp(directive, "shell:", 6))
{
- char *cmd = line + 6;
- // printf("[zprep] Running shell: %s\n", cmd);
- int res = system(cmd);
- if (res != 0)
+ if (system(directive + 6) != 0)
{
- zwarn("Shell directive failed: %s", cmd);
+ zwarn("Shell directive failed: %s", directive + 6);
}
}
- else if (strncmp(line, "get:", 4) == 0)
+ else if (strncmp(directive, "get:", 4) == 0)
{
- char *url = line + 4;
+ char *url = directive + 4;
while (*url == ' ')
{
url++;
}
-
char *filename = strrchr(url, '/');
if (!filename)
{
@@ -660,8 +742,6 @@ void scan_build_directives(ParserContext *ctx, const char *src)
{
filename++;
}
-
- // Check if file exists to avoid redownloading.
FILE *f = fopen(filename, "r");
if (f)
{
@@ -669,16 +749,13 @@ void scan_build_directives(ParserContext *ctx, const char *src)
}
else
{
- printf("[zprep] Downloading %s...\n", filename);
char cmd[8192];
if (z_is_windows())
{
- // On Windows, try curl which is often built-in now
sprintf(cmd, "curl -s -L \"%s\" -o \"%s\"", url, filename);
}
else
{
- // Try wget, then curl.
sprintf(cmd, "wget -q \"%s\" -O \"%s\" || curl -s -L \"%s\" -o \"%s\"", url,
filename, url, filename);
}
@@ -688,40 +765,27 @@ void scan_build_directives(ParserContext *ctx, const char *src)
}
}
}
- else if (strncmp(line, "pkg-config:", 11) == 0)
+ else if (strncmp(directive, "pkg-config:", 11) == 0)
{
- char *libs = line + 11;
- while (*libs == ' ')
- {
- libs++;
- }
-
- if (z_is_windows())
- {
- zwarn("pkg-config is usually not available on Windows. Build directive "
- "'pkg-config:%s' might fail.",
- libs);
- }
-
+ char *libs = directive + 11;
char cmd[4096];
sprintf(cmd, "pkg-config --cflags %s", libs);
FILE *fp = popen(cmd, "r");
if (fp)
{
- char flags[4096];
- flags[0] = 0;
- if (fgets(flags, sizeof(flags), fp))
+ char buf[1024];
+ if (fgets(buf, sizeof(buf), fp))
{
- int len = strlen(flags);
- if (len > 0 && flags[len - 1] == '\n')
+ size_t l = strlen(buf);
+ if (l > 0 && buf[l - 1] == '\n')
{
- flags[len - 1] = 0;
+ buf[l - 1] = 0;
}
if (strlen(g_cflags) > 0)
{
strcat(g_cflags, " ");
}
- strcat(g_cflags, flags);
+ strcat(g_cflags, buf);
}
pclose(fp);
}
@@ -730,32 +794,35 @@ void scan_build_directives(ParserContext *ctx, const char *src)
fp = popen(cmd, "r");
if (fp)
{
- char flags[4096];
- flags[0] = 0;
- if (fgets(flags, sizeof(flags), fp))
+ char buf[1024];
+ if (fgets(buf, sizeof(buf), fp))
{
- int len = strlen(flags);
- if (len > 0 && flags[len - 1] == '\n')
+ size_t l = strlen(buf);
+ if (l > 0 && buf[l - 1] == '\n')
{
- flags[len - 1] = 0;
+ buf[l - 1] = 0;
}
if (strlen(g_link_flags) > 0)
{
strcat(g_link_flags, " ");
}
- strcat(g_link_flags, flags);
+ strcat(g_link_flags, buf);
}
pclose(fp);
}
}
+ else
+ {
+ zwarn("Unknown build directive: '%s'", directive);
+ }
p += len;
}
+ next_line:
while (*p && *p != '\n')
{
p++;
}
-
if (*p == '\n')
{
p++;
diff --git a/src/zprep.h b/src/zprep.h
index e248871..84400b3 100644
--- a/src/zprep.h
+++ b/src/zprep.h
@@ -108,6 +108,7 @@ typedef enum
TOK_PREPROC, ///< Preprocessor directive (#...).
TOK_ALIAS, ///< 'alias' keyword.
TOK_COMMENT, ///< Comment (usually skipped).
+ TOK_OPAQUE, ///< 'opaque' keyword.
TOK_UNKNOWN ///< Unknown token.
} TokenType;
@@ -358,6 +359,7 @@ typedef struct
int mode_transpile; ///< 1 if 'transpile' command (to C).
int use_cpp; ///< 1 if --cpp (emit C++ compatible code).
int use_cuda; ///< 1 if --cuda (emit CUDA-compatible code).
+ int use_objc; ///< 1 if --objc (emit Objective-C compatible code).
// GCC Flags accumulator.
char gcc_flags[4096]; ///< Flags passed to the backend compiler.