diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ast/ast.c | 42 | ||||
| -rw-r--r-- | src/ast/ast.h | 16 | ||||
| -rw-r--r-- | src/codegen/codegen.c | 266 | ||||
| -rw-r--r-- | src/codegen/codegen.h | 4 | ||||
| -rw-r--r-- | src/codegen/codegen_decl.c | 35 | ||||
| -rw-r--r-- | src/codegen/codegen_main.c | 35 | ||||
| -rw-r--r-- | src/codegen/codegen_stmt.c | 15 | ||||
| -rw-r--r-- | src/codegen/codegen_utils.c | 52 | ||||
| -rw-r--r-- | src/codegen/compat.h | 84 | ||||
| -rw-r--r-- | src/lexer/token.c | 12 | ||||
| -rw-r--r-- | src/lsp/lsp_analysis.c | 71 | ||||
| -rw-r--r-- | src/lsp/lsp_index.c | 52 | ||||
| -rw-r--r-- | src/main.c | 13 | ||||
| -rw-r--r-- | src/parser/parser.h | 29 | ||||
| -rw-r--r-- | src/parser/parser_core.c | 32 | ||||
| -rw-r--r-- | src/parser/parser_decl.c | 70 | ||||
| -rw-r--r-- | src/parser/parser_expr.c | 200 | ||||
| -rw-r--r-- | src/parser/parser_stmt.c | 282 | ||||
| -rw-r--r-- | src/parser/parser_struct.c | 129 | ||||
| -rw-r--r-- | src/parser/parser_type.c | 103 | ||||
| -rw-r--r-- | src/parser/parser_utils.c | 67 | ||||
| -rw-r--r-- | src/repl/repl.c | 1589 | ||||
| -rw-r--r-- | src/utils/utils.c | 223 | ||||
| -rw-r--r-- | src/zprep.h | 2 |
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; } @@ -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, ¶m_names, &is_varargs); + char *args = parse_and_convert_args(ctx, l, &defaults, &count, &arg_types, ¶m_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, ¶m_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. |
