summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/analysis/typecheck.c494
-rw-r--r--src/analysis/typecheck.h14
-rw-r--r--src/ast/ast.c445
-rw-r--r--src/ast/ast.h975
-rw-r--r--src/codegen/codegen.c3531
-rw-r--r--src/codegen/codegen.h6
-rw-r--r--src/codegen/codegen_decl.c1519
-rw-r--r--src/codegen/codegen_main.c902
-rw-r--r--src/codegen/codegen_utils.c782
-rw-r--r--src/lexer/token.c730
-rw-r--r--src/lsp/json_rpc.c283
-rw-r--r--src/lsp/lsp_analysis.c620
-rw-r--r--src/lsp/lsp_index.c329
-rw-r--r--src/lsp/lsp_index.h41
-rw-r--r--src/lsp/lsp_main.c90
-rw-r--r--src/main.c540
-rw-r--r--src/parser/parser.h429
-rw-r--r--src/parser/parser_core.c995
-rw-r--r--src/parser/parser_expr.c6407
-rw-r--r--src/parser/parser_stmt.c6899
-rw-r--r--src/parser/parser_type.c1351
-rw-r--r--src/parser/parser_utils.c4528
-rw-r--r--src/plugins/plugin_manager.c135
-rw-r--r--src/repl/repl.c2415
-rw-r--r--src/utils/utils.c1342
-rw-r--r--src/zen/zen_facts.c259
-rw-r--r--src/zen/zen_facts.h29
-rw-r--r--src/zprep.h188
28 files changed, 16444 insertions, 19834 deletions
diff --git a/src/analysis/typecheck.c b/src/analysis/typecheck.c
index 903cb75..a19393b 100644
--- a/src/analysis/typecheck.c
+++ b/src/analysis/typecheck.c
@@ -1,279 +1,327 @@
-
#include "typecheck.h"
+#include "../ast/ast.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// ** Internal Helpers **
-static void tc_error(TypeChecker *tc, Token t, const char *msg)
-{
- fprintf(stderr, "Type Error at %s:%d:%d: %s\n", g_current_filename, t.line, t.col, msg);
- tc->error_count++;
+static void tc_error(TypeChecker *tc, Token t, const char *msg) {
+ fprintf(stderr, "Type Error at %s:%d:%d: %s\n", g_current_filename, t.line,
+ t.col, msg);
+ tc->error_count++;
}
-static void tc_enter_scope(TypeChecker *tc)
-{
- Scope *s = malloc(sizeof(Scope));
- s->symbols = NULL;
- s->parent = tc->current_scope;
- tc->current_scope = s;
+static void tc_enter_scope(TypeChecker *tc) {
+ Scope *s = malloc(sizeof(Scope));
+ s->symbols = NULL;
+ s->parent = tc->current_scope;
+ tc->current_scope = s;
}
-static void tc_exit_scope(TypeChecker *tc)
-{
- if (!tc->current_scope)
- {
- return;
- }
- Scope *old = tc->current_scope;
- tc->current_scope = old->parent;
-
- Symbol *sym = old->symbols;
- while (sym)
- {
- Symbol *next = sym->next;
- free(sym);
- sym = next;
- }
- free(old);
+static void tc_exit_scope(TypeChecker *tc) {
+ if (!tc->current_scope) {
+ return;
+ }
+ Scope *old = tc->current_scope;
+ tc->current_scope = old->parent;
+
+ Symbol *sym = old->symbols;
+ while (sym) {
+ Symbol *next = sym->next;
+ free(sym);
+ sym = next;
+ }
+ free(old);
}
-static void tc_add_symbol(TypeChecker *tc, const char *name, Type *type, Token t)
-{
- Symbol *s = malloc(sizeof(Symbol));
- memset(s, 0, sizeof(Symbol));
- s->name = strdup(name);
- s->type_info = type;
- s->decl_token = t;
- s->next = tc->current_scope->symbols;
- tc->current_scope->symbols = s;
+static void tc_add_symbol(TypeChecker *tc, const char *name, Type *type,
+ Token t) {
+ Symbol *s = malloc(sizeof(Symbol));
+ memset(s, 0, sizeof(Symbol));
+ s->name = strdup(name);
+ s->type_info = type;
+ s->decl_token = t;
+ s->next = tc->current_scope->symbols;
+ tc->current_scope->symbols = s;
}
-static Symbol *tc_lookup(TypeChecker *tc, const char *name)
-{
- Scope *s = tc->current_scope;
- while (s)
- {
- Symbol *curr = s->symbols;
- while (curr)
- {
- if (0 == strcmp(curr->name, name))
- {
- return curr;
- }
- curr = curr->next;
- }
- s = s->parent;
+static Symbol *tc_lookup(TypeChecker *tc, const char *name) {
+ Scope *s = tc->current_scope;
+ while (s) {
+ Symbol *curr = s->symbols;
+ while (curr) {
+ if (0 == strcmp(curr->name, name)) {
+ return curr;
+ }
+ curr = curr->next;
}
- return NULL;
+ s = s->parent;
+ }
+ return NULL;
+}
+
+static int is_integer_type(Type *t) {
+ if (!t)
+ return 0;
+ // Check all integer kinds defined in TypeKind
+ return (t->kind >= TYPE_I8 && t->kind <= TYPE_U128) || t->kind == TYPE_INT ||
+ t->kind == TYPE_UINT || t->kind == TYPE_USIZE || t->kind == TYPE_ISIZE;
+}
+
+static int is_signed_integer(Type *t) {
+ if (!t)
+ return 0;
+ return (t->kind == TYPE_I8 || t->kind == TYPE_I16 || t->kind == TYPE_I32 ||
+ t->kind == TYPE_I64 || t->kind == TYPE_I128 || t->kind == TYPE_INT ||
+ t->kind == TYPE_ISIZE);
+}
+
+// Check if node is a literal integer
+// This allows '0' to be used with unsigned types without casting.
+static int is_safe_integer_literal(ASTNode *node) {
+ if (!node || node->type != NODE_EXPR_LITERAL)
+ return 0;
+
+ // In Zen C, string/char literals have string_val set.
+ if (node->literal.string_val != NULL)
+ return 0;
+
+ // In codegen.c, type_kind == 1 represents floats
+ if (node->literal.type_kind == 1)
+ return 0;
+
+ // If it's not a string, char, or float, it's an integer literal.
+ // Parsed integer literals (e.g. 123) are positive.
+ // Negative numbers are handled as UnaryOp(-, Literal(123)), so this
+ // safe check correctly returns false for negative literals.
+ return 1;
}
// ** Node Checkers **
static void check_node(TypeChecker *tc, ASTNode *node);
-static void check_block(TypeChecker *tc, ASTNode *block)
-{
- tc_enter_scope(tc);
- ASTNode *stmt = block->block.statements;
- while (stmt)
- {
- check_node(tc, stmt);
- stmt = stmt->next;
- }
- tc_exit_scope(tc);
+static void check_block(TypeChecker *tc, ASTNode *block) {
+ tc_enter_scope(tc);
+ ASTNode *stmt = block->block.statements;
+ while (stmt) {
+ check_node(tc, stmt);
+ stmt = stmt->next;
+ }
+ tc_exit_scope(tc);
}
-static int check_type_compatibility(TypeChecker *tc, Type *target, Type *value, Token t)
-{
- if (!target || !value)
- {
- return 1; // Can't check
- }
+static int check_type_compatibility(TypeChecker *tc, Type *target,
+ ASTNode *value_expr, Token t) {
+ if (!target || !value_expr)
+ return 1;
+
+ Type *value_type = value_expr->type_info;
+ if (!value_type)
+ return 1; // Can't check yet
- // Simple equality check for now... This will be changed.
- if (!type_eq(target, value))
- {
-
- // For now we have strict equality on structure.
-
- // In Zen C (like C), void* is generic.
- if (TYPE_POINTER == target->kind && TYPE_VOID == target->inner->kind)
- {
- return 1;
- }
- if (TYPE_POINTER == value->kind && TYPE_VOID == value->inner->kind)
- {
- return 1;
- }
-
- // Exception: integer promotion/demotion.
-
- if (target->kind >= TYPE_I8 && target->kind <= TYPE_U64 && value->kind >= TYPE_I8 &&
- value->kind <= TYPE_U64)
- {
- return 1;
- }
-
- char *t_str = type_to_string(target);
- char *v_str = type_to_string(value);
- char msg[256];
- snprintf(msg, 255, "Type mismatch: expected '%s', got '%s'", t_str, v_str);
- tc_error(tc, t, msg);
- free(t_str);
- free(v_str);
- return 0;
+ // 1. Exact Match
+ if (type_eq(target, value_type))
+ return 1;
+
+ // 2. Void Pointer Generics (void* matches any pointer)
+ if (target->kind == TYPE_POINTER && target->inner->kind == TYPE_VOID)
+ return 1;
+ if (value_type->kind == TYPE_POINTER && value_type->inner->kind == TYPE_VOID)
+ return 1;
+
+ // 3. Integer Promotion / Safety Checks
+ if (is_integer_type(target) && is_integer_type(value_type)) {
+ int target_signed = is_signed_integer(target);
+ int value_signed = is_signed_integer(value_type);
+
+ // A. Signed/Unsigned Mismatch
+ if (target_signed != value_signed) {
+ // ERGONOMIC FIX: Allow implicit conversion IF it is a safe positive
+ // literal e.g. 'usize x = 0;' or 'if (len > 0)'
+ if (is_safe_integer_literal(value_expr)) {
+ return 1; // Safe!
+ }
+
+ // Otherwise, warn strict
+ char *t_str = type_to_string(target);
+ char *v_str = type_to_string(value_type);
+ char msg[256];
+ snprintf(msg, 255,
+ "Sign mismatch: cannot implicitly convert '%s' to '%s' (use "
+ "cast or unsigned literal)",
+ v_str, t_str);
+ tc_error(tc, t, msg);
+ free(t_str);
+ free(v_str);
+ return 0;
}
+
+ // B. Size truncation could be checked here (optional)
return 1;
+ }
+
+ // 4. Default Failure
+ char *t_str = type_to_string(target);
+ char *v_str = type_to_string(value_type);
+ char msg[256];
+ snprintf(msg, 255, "Type mismatch: expected '%s', got '%s'", t_str, v_str);
+ tc_error(tc, t, msg);
+ free(t_str);
+ free(v_str);
+ return 0;
}
-static void check_var_decl(TypeChecker *tc, ASTNode *node)
-{
- if (node->var_decl.init_expr)
- {
- check_node(tc, node->var_decl.init_expr);
+static void check_var_decl(TypeChecker *tc, ASTNode *node) {
+ if (node->var_decl.init_expr) {
+ check_node(tc, node->var_decl.init_expr);
- Type *decl_type = node->type_info;
- Type *init_type = node->var_decl.init_expr->type_info;
+ Type *decl_type = node->type_info;
- if (decl_type && init_type)
- {
- check_type_compatibility(tc, decl_type, init_type, node->token);
- }
+ if (decl_type) {
+ // Pass the full expression node to allow literal checking
+ check_type_compatibility(tc, decl_type, node->var_decl.init_expr,
+ node->token);
}
+ }
- // If type is not explicit, we should ideally infer it from init_expr.
- Type *t = node->type_info;
- if (!t && node->var_decl.init_expr)
- {
- t = node->var_decl.init_expr->type_info;
- }
+ Type *t = node->type_info;
+ if (!t && node->var_decl.init_expr) {
+ t = node->var_decl.init_expr->type_info;
+ }
- tc_add_symbol(tc, node->var_decl.name, t, node->token);
+ tc_add_symbol(tc, node->var_decl.name, t, node->token);
+ node->type_info = t;
}
-static void check_function(TypeChecker *tc, ASTNode *node)
-{
- // Just to suppress the warning.
- (void)tc_error;
+static void check_function(TypeChecker *tc, ASTNode *node) {
+ (void)tc_error;
- tc->current_func = node;
- tc_enter_scope(tc);
+ tc->current_func = node;
+ tc_enter_scope(tc);
- for (int i = 0; i < node->func.arg_count; i++)
- {
- if (node->func.param_names && node->func.param_names[i])
- {
- tc_add_symbol(tc, node->func.param_names[i], NULL, (Token){0});
- }
+ for (int i = 0; i < node->func.arg_count; i++) {
+ if (node->func.param_names && node->func.param_names[i]) {
+ // FIXED: use arg_types from struct, not param_types
+ Type *t = (node->func.arg_types) ? node->func.arg_types[i] : NULL;
+ tc_add_symbol(tc, node->func.param_names[i], t, (Token){0});
}
+ }
- check_node(tc, node->func.body);
+ check_node(tc, node->func.body);
- tc_exit_scope(tc);
- tc->current_func = NULL;
+ tc_exit_scope(tc);
+ tc->current_func = NULL;
}
-static void check_expr_var(TypeChecker *tc, ASTNode *node)
-{
- Symbol *sym = tc_lookup(tc, node->var_ref.name);
- if (!sym)
- {
- // Check global functions/contexts if not found in locals
- // This is a naive check.
- // We really want to warn here if it's truly unknown.
- }
- if (sym && sym->type_info)
- {
- node->type_info = sym->type_info;
- }
+static void check_expr_var(TypeChecker *tc, ASTNode *node) {
+ Symbol *sym = tc_lookup(tc, node->var_ref.name);
+ if (sym && sym->type_info) {
+ node->type_info = sym->type_info;
+ }
}
-static void check_node(TypeChecker *tc, ASTNode *node)
-{
- if (!node)
- {
- return;
+static void check_node(TypeChecker *tc, ASTNode *node) {
+ if (!node) {
+ return;
+ }
+
+ switch (node->type) {
+ case NODE_ROOT:
+ check_node(tc, node->root.children);
+ break;
+ case NODE_BLOCK:
+ check_block(tc, node);
+ break;
+ case NODE_VAR_DECL:
+ check_var_decl(tc, node);
+ break;
+ case NODE_FUNCTION:
+ check_function(tc, node);
+ break;
+ case NODE_EXPR_VAR:
+ check_expr_var(tc, node);
+ break;
+ case NODE_RETURN:
+ if (node->ret.value) {
+ check_node(tc, node->ret.value);
+ // Check return type matches function signature
+ if (tc->current_func && tc->current_func->type_info) {
+ check_type_compatibility(tc, tc->current_func->type_info,
+ node->ret.value, node->token);
+ }
}
+ break;
+
+ case NODE_IF:
+ check_node(tc, node->if_stmt.condition);
+ check_node(tc, node->if_stmt.then_body);
+ check_node(tc, node->if_stmt.else_body);
+ break;
+ case NODE_WHILE:
+ check_node(tc, node->while_stmt.condition);
+ check_node(tc, node->while_stmt.body);
+ break;
+ case NODE_FOR:
+ tc_enter_scope(tc);
+ check_node(tc, node->for_stmt.init);
+ check_node(tc, node->for_stmt.condition);
+ check_node(tc, node->for_stmt.step);
+ check_node(tc, node->for_stmt.body);
+ tc_exit_scope(tc);
+ break;
- switch (node->type)
- {
- case NODE_ROOT:
- check_node(tc, node->root.children);
- break;
- case NODE_BLOCK:
- check_block(tc, node);
- break;
- case NODE_VAR_DECL:
- check_var_decl(tc, node);
- break;
- case NODE_FUNCTION:
- check_function(tc, node);
- break;
- case NODE_EXPR_VAR:
- check_expr_var(tc, node);
- break;
- case NODE_RETURN:
- if (node->ret.value)
- {
- check_node(tc, node->ret.value);
- }
-
- break;
-
- // Control flow with nested nodes.
- case NODE_IF:
- check_node(tc, node->if_stmt.condition);
- check_node(tc, node->if_stmt.then_body);
- check_node(tc, node->if_stmt.else_body);
- break;
- case NODE_WHILE:
- check_node(tc, node->while_stmt.condition);
- check_node(tc, node->while_stmt.body);
- break;
- case NODE_FOR:
- tc_enter_scope(tc); // For loop init variable is scoped
- check_node(tc, node->for_stmt.init);
- check_node(tc, node->for_stmt.condition);
- check_node(tc, node->for_stmt.step);
- check_node(tc, node->for_stmt.body);
- tc_exit_scope(tc);
- break;
- case NODE_EXPR_BINARY:
- check_node(tc, node->binary.left);
- check_node(tc, node->binary.right);
- break;
- case NODE_EXPR_CALL:
- check_node(tc, node->call.callee);
- check_node(tc, node->call.args);
- break;
- default:
- // Generic recursion for lists.
- break;
- }
+ case NODE_EXPR_BINARY:
+ check_node(tc, node->binary.left);
+ check_node(tc, node->binary.right);
- if (node->next)
- {
- check_node(tc, node->next);
+ // Infer type from left operand
+ if (node->binary.left->type_info) {
+ node->type_info = node->binary.left->type_info;
+
+ // Perform the comparison check using the robust compatibility logic
+ // This will trigger is_safe_integer_literal for cases like (usize > 0)
+ check_type_compatibility(tc, node->binary.left->type_info,
+ node->binary.right, node->token);
+ }
+ break;
+
+ case NODE_EXPR_CALL:
+ check_node(tc, node->call.callee);
+ check_node(tc, node->call.args);
+ // Propagate return type
+ if (node->call.callee->type_info) {
+ node->type_info = node->call.callee->type_info;
}
+ break;
+
+ case NODE_EXPR_LITERAL:
+ // Literals usually have their type set during parsing/inference phases
+ break;
+
+ default:
+ break;
+ }
+
+ if (node->next) {
+ check_node(tc, node->next);
+ }
}
// ** Entry Point **
-int check_program(ParserContext *ctx, ASTNode *root)
-{
- TypeChecker tc = {0};
- tc.pctx = ctx;
+int check_program(ParserContext *ctx, ASTNode *root) {
+ TypeChecker tc = {0};
+ tc.pctx = ctx;
- printf("[TypeCheck] Starting semantic analysis...\n");
- check_node(&tc, root);
+ printf("[TypeCheck] Starting semantic analysis...\n");
+ check_node(&tc, root);
- if (tc.error_count > 0)
- {
- printf("[TypeCheck] Found %d errors.\n", tc.error_count);
- return 1;
- }
- printf("[TypeCheck] Passed.\n");
- return 0;
+ if (tc.error_count > 0) {
+ printf("[TypeCheck] Found %d errors.\n", tc.error_count);
+ return 1;
+ }
+ printf("[TypeCheck] Passed.\n");
+ return 0;
}
diff --git a/src/analysis/typecheck.h b/src/analysis/typecheck.h
index fe51c4d..a680f3a 100644
--- a/src/analysis/typecheck.h
+++ b/src/analysis/typecheck.h
@@ -7,13 +7,13 @@
// Type Checker Context
// Holds the state during the semantic analysis pass.
// Unlike the parser, this focuses on semantic validity (types, definitions).
-typedef struct TypeChecker
-{
- ParserContext *pctx; // Reference to global parser context (for lookups)
- Scope *current_scope; // Current lexical scope
- ASTNode *current_func; // Current function being checked (for return type checks)
- int error_count; // Number of errors found
- int warning_count; // Number of recommendations/warnings
+typedef struct TypeChecker {
+ ParserContext *pctx; // Reference to global parser context (for lookups)
+ Scope *current_scope; // Current lexical scope
+ ASTNode
+ *current_func; // Current function being checked (for return type checks)
+ int error_count; // Number of errors found
+ int warning_count; // Number of recommendations/warnings
} TypeChecker;
// Main Entry Point
diff --git a/src/ast/ast.c b/src/ast/ast.c
index f34370e..023180d 100644
--- a/src/ast/ast.c
+++ b/src/ast/ast.c
@@ -5,279 +5,242 @@
#include <stdlib.h>
#include <string.h>
-typedef struct TraitReg
-{
- char *name;
- struct TraitReg *next;
+typedef struct TraitReg {
+ char *name;
+ struct TraitReg *next;
} TraitReg;
static TraitReg *registered_traits = NULL;
-void register_trait(const char *name)
-{
- TraitReg *r = xmalloc(sizeof(TraitReg));
- r->name = xstrdup(name);
- r->next = registered_traits;
- registered_traits = r;
+void register_trait(const char *name) {
+ TraitReg *r = xmalloc(sizeof(TraitReg));
+ r->name = xstrdup(name);
+ r->next = registered_traits;
+ registered_traits = r;
}
-int is_trait(const char *name)
-{
- TraitReg *r = registered_traits;
- while (r)
- {
- if (0 == strcmp(r->name, name))
- {
- return 1;
- }
- r = r->next;
+int is_trait(const char *name) {
+ TraitReg *r = registered_traits;
+ while (r) {
+ if (0 == strcmp(r->name, name)) {
+ return 1;
}
- return 0;
+ r = r->next;
+ }
+ return 0;
}
-ASTNode *ast_create(NodeType type)
-{
- ASTNode *node = xmalloc(sizeof(ASTNode));
- memset(node, 0, sizeof(ASTNode));
- node->type = type;
- return node;
+ASTNode *ast_create(NodeType type) {
+ ASTNode *node = xmalloc(sizeof(ASTNode));
+ memset(node, 0, sizeof(ASTNode));
+ node->type = type;
+ return node;
}
-void ast_free(ASTNode *node)
-{
- (void)node;
- return;
+void ast_free(ASTNode *node) {
+ (void)node;
+ return;
}
-Type *type_new(TypeKind kind)
-{
- Type *t = xmalloc(sizeof(Type));
- t->kind = kind;
- t->name = NULL;
- t->inner = NULL;
- t->args = NULL;
- t->arg_count = 0;
- t->is_const = 0;
- t->array_size = 0;
- return t;
+Type *type_new(TypeKind kind) {
+ Type *t = xmalloc(sizeof(Type));
+ t->kind = kind;
+ t->name = NULL;
+ t->inner = NULL;
+ t->args = NULL;
+ t->arg_count = 0;
+ t->is_const = 0;
+ t->array_size = 0;
+ return t;
}
-Type *type_new_ptr(Type *inner)
-{
- Type *t = type_new(TYPE_POINTER);
- t->inner = inner;
- return t;
+Type *type_new_ptr(Type *inner) {
+ Type *t = type_new(TYPE_POINTER);
+ t->inner = inner;
+ return t;
}
-int is_char_ptr(Type *t)
-{
- // Handle both primitive char* and legacy struct char*.
- if (TYPE_POINTER == t->kind && TYPE_CHAR == t->inner->kind)
- {
- return 1;
- }
- if (TYPE_POINTER == t->kind && TYPE_STRUCT == t->inner->kind &&
- 0 == strcmp(t->inner->name, "char"))
- {
- return 1;
- }
- return 0;
+int is_char_ptr(Type *t) {
+ // Handle both primitive char* and legacy struct char*.
+ if (TYPE_POINTER == t->kind && TYPE_CHAR == t->inner->kind) {
+ return 1;
+ }
+ if (TYPE_POINTER == t->kind && TYPE_STRUCT == t->inner->kind &&
+ 0 == strcmp(t->inner->name, "char")) {
+ return 1;
+ }
+ return 0;
}
-int is_integer_type(Type *t)
-{
- if (!t)
- {
- return 0;
- }
-
- return (t->kind == TYPE_INT || t->kind == TYPE_CHAR || t->kind == TYPE_BOOL ||
- t->kind == TYPE_I8 || t->kind == TYPE_U8 || t->kind == TYPE_I16 ||
- t->kind == TYPE_U16 || t->kind == TYPE_I32 || t->kind == TYPE_U32 ||
- 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);
+int is_integer_type(Type *t) {
+ if (!t) {
+ return 0;
+ }
+
+ return (t->kind == TYPE_INT || t->kind == TYPE_CHAR || t->kind == TYPE_BOOL ||
+ t->kind == TYPE_I8 || t->kind == TYPE_U8 || t->kind == TYPE_I16 ||
+ t->kind == TYPE_U16 || t->kind == TYPE_I32 || t->kind == TYPE_U32 ||
+ 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);
}
-int is_float_type(Type *t)
-{
- if (!t)
- {
- return 0;
- }
+int is_float_type(Type *t) {
+ if (!t) {
+ return 0;
+ }
- return (t->kind == TYPE_FLOAT || t->kind == TYPE_F32 || t->kind == TYPE_F64);
+ return (t->kind == TYPE_FLOAT || t->kind == TYPE_F32 || t->kind == TYPE_F64);
}
-int type_eq(Type *a, Type *b)
-{
- if (!a || !b)
- {
- return 0;
- }
-
- if (a == b)
- {
- return 1;
- }
-
- // Lax integer matching (bool == int, char == i8, etc.).
- if (is_integer_type(a) && is_integer_type(b))
- {
- return 1;
- }
-
- // Lax float matching.
- if (is_float_type(a) && is_float_type(b))
- {
- return 1;
- }
-
- // String Literal vs char*
- if (a->kind == TYPE_STRING && is_char_ptr(b))
- {
- return 1;
- }
-
- if (b->kind == TYPE_STRING && is_char_ptr(a))
- {
- return 1;
- }
-
- if (a->kind != b->kind)
- {
- return 0;
- }
+int type_eq(Type *a, Type *b) {
+ if (!a || !b) {
+ return 0;
+ }
- if (a->kind == TYPE_STRUCT || a->kind == TYPE_GENERIC)
- {
- return 0 == strcmp(a->name, b->name);
- }
- if (a->kind == TYPE_POINTER || a->kind == TYPE_ARRAY)
- {
- return type_eq(a->inner, b->inner);
- }
+ if (a == b) {
+ return 1;
+ }
+ // Lax integer matching (bool == int, char == i8, etc.).
+ if (is_integer_type(a) && is_integer_type(b)) {
return 1;
-}
+ }
-char *type_to_string(Type *t)
-{
- if (!t)
- {
- return xstrdup("void");
- }
+ // Lax float matching.
+ if (is_float_type(a) && is_float_type(b)) {
+ return 1;
+ }
- switch (t->kind)
- {
- case TYPE_VOID:
- return xstrdup("void");
- case TYPE_BOOL:
- return xstrdup("bool");
- case TYPE_STRING:
- return xstrdup("string");
- case TYPE_CHAR:
- return xstrdup("char");
- case TYPE_I8:
- return xstrdup("int8_t");
- case TYPE_U8:
- return xstrdup("uint8_t");
- case TYPE_I16:
- return xstrdup("int16_t");
- case TYPE_U16:
- return xstrdup("uint16_t");
- case TYPE_I32:
- return xstrdup("int32_t");
- case TYPE_U32:
- return xstrdup("uint32_t");
- case TYPE_I64:
- return xstrdup("int64_t");
- case TYPE_U64:
- return xstrdup("uint64_t");
- case TYPE_F32:
- return xstrdup("float");
- case TYPE_F64:
- return xstrdup("double");
- case TYPE_USIZE:
- return xstrdup("size_t");
- case TYPE_ISIZE:
- return xstrdup("ptrdiff_t");
- case TYPE_BYTE:
- return xstrdup("uint8_t");
- case TYPE_I128:
- return xstrdup("__int128");
- case TYPE_U128:
- return xstrdup("unsigned __int128");
- case TYPE_RUNE:
- return xstrdup("int32_t");
- case TYPE_UINT:
- return xstrdup("unsigned int");
- case TYPE_INT:
- return xstrdup("int");
- case TYPE_FLOAT:
- return xstrdup("float");
-
- case TYPE_POINTER:
- {
- char *inner = type_to_string(t->inner);
- if (t->is_restrict)
- {
- char *res = xmalloc(strlen(inner) + 16);
- sprintf(res, "%s* __restrict", inner);
- return res;
- }
- else
- {
- char *res = xmalloc(strlen(inner) + 2);
- sprintf(res, "%s*", inner);
- return res;
- }
- }
+ // String Literal vs char*
+ if (a->kind == TYPE_STRING && is_char_ptr(b)) {
+ return 1;
+ }
- case TYPE_ARRAY:
- {
- char *inner = type_to_string(t->inner);
+ if (b->kind == TYPE_STRING && is_char_ptr(a)) {
+ return 1;
+ }
- if (t->array_size > 0)
- {
- char *res = xmalloc(strlen(inner) + 20);
- sprintf(res, "%s[%d]", inner, t->array_size);
- return res;
- }
+ if (a->kind != b->kind) {
+ return 0;
+ }
- char *res = xmalloc(strlen(inner) + 7);
- sprintf(res, "Slice_%s", inner);
- return res;
- }
+ if (a->kind == TYPE_STRUCT || a->kind == TYPE_GENERIC) {
+ return 0 == strcmp(a->name, b->name);
+ }
+ if (a->kind == TYPE_POINTER || a->kind == TYPE_ARRAY) {
+ return type_eq(a->inner, b->inner);
+ }
- case TYPE_FUNCTION:
- if (t->inner)
- {
- free(type_to_string(t->inner));
- }
-
- return xstrdup("z_closure_T");
-
- case TYPE_STRUCT:
- case TYPE_GENERIC:
- {
- if (t->arg_count > 0)
- {
- char *base = t->name;
- char *arg = type_to_string(t->args[0]);
- char *clean_arg = sanitize_mangled_name(arg);
-
- char *res = xmalloc(strlen(base) + strlen(clean_arg) + 2);
- sprintf(res, "%s_%s", base, clean_arg);
-
- free(arg);
- free(clean_arg);
- return res;
- }
- return xstrdup(t->name);
- }
+ return 1;
+}
- default:
- return xstrdup("unknown");
- }
+char *type_to_string(Type *t) {
+ if (!t) {
+ return xstrdup("void");
+ }
+
+ switch (t->kind) {
+ case TYPE_VOID:
+ return xstrdup("void");
+ case TYPE_BOOL:
+ return xstrdup("bool");
+ case TYPE_STRING:
+ return xstrdup("string");
+ case TYPE_CHAR:
+ return xstrdup("char");
+ case TYPE_I8:
+ return xstrdup("int8_t");
+ case TYPE_U8:
+ return xstrdup("uint8_t");
+ case TYPE_I16:
+ return xstrdup("int16_t");
+ case TYPE_U16:
+ return xstrdup("uint16_t");
+ case TYPE_I32:
+ return xstrdup("int32_t");
+ case TYPE_U32:
+ return xstrdup("uint32_t");
+ case TYPE_I64:
+ return xstrdup("int64_t");
+ case TYPE_U64:
+ return xstrdup("uint64_t");
+ case TYPE_F32:
+ return xstrdup("float");
+ case TYPE_F64:
+ return xstrdup("double");
+ case TYPE_USIZE:
+ return xstrdup("size_t");
+ case TYPE_ISIZE:
+ return xstrdup("ptrdiff_t");
+ case TYPE_BYTE:
+ return xstrdup("uint8_t");
+ case TYPE_I128:
+ return xstrdup("__int128");
+ case TYPE_U128:
+ return xstrdup("unsigned __int128");
+ case TYPE_RUNE:
+ return xstrdup("int32_t");
+ case TYPE_UINT:
+ return xstrdup("unsigned int");
+ case TYPE_INT:
+ return xstrdup("int");
+ case TYPE_FLOAT:
+ return xstrdup("float");
+
+ case TYPE_POINTER: {
+ char *inner = type_to_string(t->inner);
+ if (t->is_restrict) {
+ char *res = xmalloc(strlen(inner) + 16);
+ sprintf(res, "%s* __restrict", inner);
+ return res;
+ } else {
+ char *res = xmalloc(strlen(inner) + 2);
+ sprintf(res, "%s*", inner);
+ return res;
+ }
+ }
+
+ case TYPE_ARRAY: {
+ char *inner = type_to_string(t->inner);
+
+ if (t->array_size > 0) {
+ char *res = xmalloc(strlen(inner) + 20);
+ sprintf(res, "%s[%d]", inner, t->array_size);
+ return res;
+ }
+
+ char *res = xmalloc(strlen(inner) + 7);
+ sprintf(res, "Slice_%s", inner);
+ return res;
+ }
+
+ case TYPE_FUNCTION:
+ if (t->inner) {
+ free(type_to_string(t->inner));
+ }
+
+ return xstrdup("z_closure_T");
+
+ case TYPE_STRUCT:
+ case TYPE_GENERIC: {
+ if (t->arg_count > 0) {
+ char *base = t->name;
+ char *arg = type_to_string(t->args[0]);
+ char *clean_arg = sanitize_mangled_name(arg);
+
+ char *res = xmalloc(strlen(base) + strlen(clean_arg) + 2);
+ sprintf(res, "%s_%s", base, clean_arg);
+
+ free(arg);
+ free(clean_arg);
+ return res;
+ }
+ return xstrdup(t->name);
+ }
+
+ default:
+ return xstrdup("unknown");
+ }
}
diff --git a/src/ast/ast.h b/src/ast/ast.h
index a5d3d0a..0fa2e24 100644
--- a/src/ast/ast.h
+++ b/src/ast/ast.h
@@ -11,531 +11,472 @@ typedef struct ASTNode ASTNode;
// ** Formal Type System **
// Used for Generics, Type Inference, and robust pointer handling.
-typedef enum
-{
- TYPE_VOID,
- TYPE_BOOL,
- TYPE_CHAR,
- TYPE_STRING,
- TYPE_U0,
- TYPE_I8,
- TYPE_U8,
- TYPE_I16,
- TYPE_U16,
- TYPE_I32,
- TYPE_U32,
- TYPE_I64,
- TYPE_U64,
- TYPE_I128,
- TYPE_U128,
- TYPE_F32,
- TYPE_F64,
- TYPE_INT,
- TYPE_FLOAT,
- TYPE_USIZE,
- TYPE_ISIZE,
- TYPE_BYTE,
- TYPE_RUNE,
- TYPE_UINT,
- TYPE_STRUCT,
- TYPE_ENUM,
- TYPE_POINTER,
- TYPE_ARRAY,
- TYPE_FUNCTION,
- TYPE_GENERIC,
- TYPE_UNKNOWN
+typedef enum {
+ TYPE_VOID,
+ TYPE_BOOL,
+ TYPE_CHAR,
+ TYPE_STRING,
+ TYPE_U0,
+ TYPE_I8,
+ TYPE_U8,
+ TYPE_I16,
+ TYPE_U16,
+ TYPE_I32,
+ TYPE_U32,
+ TYPE_I64,
+ TYPE_U64,
+ TYPE_I128,
+ TYPE_U128,
+ TYPE_F32,
+ TYPE_F64,
+ TYPE_INT,
+ TYPE_FLOAT,
+ TYPE_USIZE,
+ TYPE_ISIZE,
+ TYPE_BYTE,
+ TYPE_RUNE,
+ TYPE_UINT,
+ TYPE_STRUCT,
+ TYPE_ENUM,
+ TYPE_POINTER,
+ TYPE_ARRAY,
+ TYPE_FUNCTION,
+ TYPE_GENERIC,
+ TYPE_UNKNOWN
} TypeKind;
-typedef struct Type
-{
- TypeKind kind;
- char *name; // For STRUCT, GENERIC, ENUM.
- struct Type *inner; // For POINTER, ARRAY.
- struct Type **args; // For GENERIC args.
- int arg_count;
- int is_const;
- union
- {
- int array_size; // For fixed-size arrays [T; N].
- int is_varargs; // For function types (...).
- int is_restrict; // For restrict pointers.
- int has_drop; // For RAII: does this type implement Drop?
- };
+typedef struct Type {
+ TypeKind kind;
+ char *name; // For STRUCT, GENERIC, ENUM.
+ struct Type *inner; // For POINTER, ARRAY.
+ struct Type **args; // For GENERIC args.
+ int arg_count;
+ int is_const;
+ union {
+ int array_size; // For fixed-size arrays [T; N].
+ int is_varargs; // For function types (...).
+ int is_restrict; // For restrict pointers.
+ int has_drop; // For RAII: does this type implement Drop?
+ };
} Type;
// ** AST Node Types **
-typedef enum
-{
- NODE_ROOT,
- NODE_FUNCTION,
- NODE_BLOCK,
- NODE_RETURN,
- NODE_VAR_DECL,
- NODE_CONST,
- NODE_TYPE_ALIAS,
- NODE_IF,
- NODE_WHILE,
- NODE_FOR,
- NODE_FOR_RANGE,
- NODE_LOOP,
- NODE_REPEAT,
- NODE_UNLESS,
- NODE_GUARD,
- NODE_BREAK,
- NODE_CONTINUE,
- NODE_MATCH,
- NODE_MATCH_CASE,
- NODE_EXPR_BINARY,
- NODE_EXPR_UNARY,
- NODE_EXPR_LITERAL,
- NODE_EXPR_VAR,
- NODE_EXPR_CALL,
- NODE_EXPR_MEMBER,
- NODE_EXPR_INDEX,
- NODE_EXPR_CAST,
- NODE_EXPR_SIZEOF,
- NODE_EXPR_STRUCT_INIT,
- NODE_EXPR_ARRAY_LITERAL,
- NODE_EXPR_SLICE,
- NODE_STRUCT,
- NODE_FIELD,
- NODE_ENUM,
- NODE_ENUM_VARIANT,
- NODE_TRAIT,
- NODE_IMPL,
- NODE_IMPL_TRAIT,
- NODE_INCLUDE,
- NODE_RAW_STMT,
- NODE_TEST,
- NODE_ASSERT,
- NODE_DEFER,
- NODE_DESTRUCT_VAR,
- NODE_TERNARY,
- NODE_ASM,
- NODE_LAMBDA,
- NODE_PLUGIN,
- NODE_GOTO,
- NODE_LABEL,
- NODE_DO_WHILE,
- NODE_TYPEOF,
- NODE_TRY,
- NODE_REFLECTION,
- NODE_AWAIT,
- NODE_REPL_PRINT
+typedef enum {
+ NODE_ROOT,
+ NODE_FUNCTION,
+ NODE_BLOCK,
+ NODE_RETURN,
+ NODE_VAR_DECL,
+ NODE_CONST,
+ NODE_TYPE_ALIAS,
+ NODE_IF,
+ NODE_WHILE,
+ NODE_FOR,
+ NODE_FOR_RANGE,
+ NODE_LOOP,
+ NODE_REPEAT,
+ NODE_UNLESS,
+ NODE_GUARD,
+ NODE_BREAK,
+ NODE_CONTINUE,
+ NODE_MATCH,
+ NODE_MATCH_CASE,
+ NODE_EXPR_BINARY,
+ NODE_EXPR_UNARY,
+ NODE_EXPR_LITERAL,
+ NODE_EXPR_VAR,
+ NODE_EXPR_CALL,
+ NODE_EXPR_MEMBER,
+ NODE_EXPR_INDEX,
+ NODE_EXPR_CAST,
+ NODE_EXPR_SIZEOF,
+ NODE_EXPR_STRUCT_INIT,
+ NODE_EXPR_ARRAY_LITERAL,
+ NODE_EXPR_SLICE,
+ NODE_STRUCT,
+ NODE_FIELD,
+ NODE_ENUM,
+ NODE_ENUM_VARIANT,
+ NODE_TRAIT,
+ NODE_IMPL,
+ NODE_IMPL_TRAIT,
+ NODE_INCLUDE,
+ NODE_RAW_STMT,
+ NODE_TEST,
+ NODE_ASSERT,
+ NODE_DEFER,
+ NODE_DESTRUCT_VAR,
+ NODE_TERNARY,
+ NODE_ASM,
+ NODE_LAMBDA,
+ NODE_PLUGIN,
+ NODE_GOTO,
+ NODE_LABEL,
+ NODE_DO_WHILE,
+ NODE_TYPEOF,
+ NODE_TRY,
+ NODE_REFLECTION,
+ NODE_AWAIT,
+ NODE_REPL_PRINT
} NodeType;
// ** AST Node Structure **
-struct ASTNode
-{
- NodeType type;
- ASTNode *next;
- int line; // Source line number for debugging.
-
- // Type information.
- char *resolved_type; // Legacy string representation (for example: "int",
- // "User*"). > Yes, 'legacy' is a thing, this is the
- // third iteration > of this project (for now).
- Type *type_info; // Formal type object (for inference/generics).
- Token token;
- Token definition_token; // For LSP: Location where the symbol used in this
- // node was defined.
-
- union
- {
- struct
- {
- ASTNode *children;
- } root;
-
- struct
- {
- char *name;
- char *args; // Legacy string args.
- char *ret_type; // Legacy string return type.
- ASTNode *body;
- Type **arg_types;
- char **defaults;
- char **param_names; // Explicit parameter names.
- int arg_count;
- Type *ret_type_info;
- int is_varargs;
- int is_inline;
- int must_use; // @must_use: warn if return value is discarded.
- // GCC attributes
- int noinline; // @noinline
- int constructor; // @constructor
- int destructor; // @destructor
- int unused; // @unused
- int weak; // @weak
- int is_export; // @export (visibility default).
- int cold; // @cold
- int hot; // @hot
- int noreturn; // @noreturn
- int pure; // @pure
- char *section; // @section("name")
- int is_async; // async function
- int is_comptime; // @comptime function
- } func;
-
- struct
- {
- ASTNode *statements;
- } block;
-
- struct
- {
- ASTNode *value;
- } ret;
-
- struct
- {
- char *name;
- char *type_str;
- ASTNode *init_expr;
- Type *type_info;
- int is_autofree;
- int is_mutable;
- int is_static;
- } var_decl;
-
- struct
- {
- char *name;
- Type *payload;
- int tag_id;
- } variant;
-
- struct
- {
- char *name;
- ASTNode *variants;
- int is_template;
- char *generic_param;
- } enm;
-
- struct
- {
- char *alias;
- char *original_type;
- } type_alias;
-
- struct
- {
- ASTNode *condition;
- ASTNode *then_body;
- ASTNode *else_body;
- } if_stmt;
-
- struct
- {
- ASTNode *condition;
- ASTNode *body;
- char *loop_label;
- } while_stmt;
-
- struct
- {
- ASTNode *init;
- ASTNode *condition;
- ASTNode *step;
- ASTNode *body;
- char *loop_label;
- } for_stmt;
-
- struct
- {
- char *var_name;
- ASTNode *start;
- ASTNode *end;
- char *step;
- ASTNode *body;
- } for_range;
-
- struct
- {
- ASTNode *body;
- char *loop_label;
- } loop_stmt;
-
- struct
- {
- char *count;
- ASTNode *body;
- } repeat_stmt;
-
- struct
- {
- ASTNode *condition;
- ASTNode *body;
- } unless_stmt;
-
- struct
- {
- ASTNode *condition;
- ASTNode *body;
- } guard_stmt;
-
- struct
- {
- ASTNode *condition;
- ASTNode *body;
- char *loop_label;
- } do_while_stmt;
-
- struct
- {
- ASTNode *expr;
- ASTNode *cases;
- } match_stmt;
-
- struct
- {
- char *pattern;
- char *binding_name;
- int is_destructuring;
- ASTNode *guard;
- ASTNode *body;
- int is_default;
- } match_case;
-
- struct
- {
- char *op;
- ASTNode *left;
- ASTNode *right;
- } binary;
-
- struct
- {
- char *op;
- ASTNode *operand;
- } unary;
-
- struct
- {
- int type_kind;
- unsigned long long int_val;
- double float_val;
- char *string_val;
- } literal;
-
- struct
- {
- char *name;
- char *suggestion;
- } var_ref;
-
- struct
- {
- ASTNode *callee;
- ASTNode *args;
- char **arg_names;
- int arg_count;
- } call;
-
- struct
- {
- ASTNode *target;
- char *field;
- int is_pointer_access;
- } member;
-
- struct
- {
- ASTNode *array;
- ASTNode *index;
- } index;
-
- struct
- {
- ASTNode *array;
- ASTNode *start;
- ASTNode *end;
- } slice;
-
- struct
- {
- char *target_type;
- ASTNode *expr;
- } cast;
-
- struct
- {
- char *struct_name;
- ASTNode *fields;
- } struct_init;
-
- struct
- {
- ASTNode *elements;
- int count;
- } array_literal;
-
- struct
- {
- char *name;
- ASTNode *fields;
- int is_template;
- char *generic_param;
- char *parent;
- int is_union;
- int is_packed; // @packed attribute.
- int align; // @align(N) attribute, 0 = default.
- int is_incomplete; // Forward declaration (prototype)
- } strct;
-
- struct
- {
- char *name;
- char *type;
- int bit_width;
- } field;
-
- struct
- {
- char *name;
- ASTNode *methods;
- } trait;
-
- struct
- {
- char *struct_name;
- ASTNode *methods;
- } impl;
-
- struct
- {
- char *trait_name;
- char *target_type;
- ASTNode *methods;
- } impl_trait;
-
- struct
- {
- char *path;
- int is_system;
- } include;
-
- struct
- {
- char *content;
- char **used_symbols;
- int used_symbol_count;
- } raw_stmt;
-
- struct
- {
- char *name;
- ASTNode *body;
- } test_stmt;
-
- struct
- {
- ASTNode *condition;
- char *message;
- } assert_stmt;
-
- struct
- {
- ASTNode *stmt;
- } defer_stmt;
-
- struct
- {
- char *plugin_name;
- char *body;
- } plugin_stmt;
-
- struct
- {
- char **names;
- int count;
- ASTNode *init_expr;
- int is_struct_destruct;
- char *struct_name; // "Point" (or NULL if unchecked/inferred).
- char **field_names; // NULL if same as 'names', otherwise mapped.
- int is_guard;
- char *guard_variant; // "Some", "Ok".
- ASTNode *else_block;
- } destruct;
-
- struct
- {
- ASTNode *cond;
- ASTNode *true_expr;
- ASTNode *false_expr;
- } ternary;
-
- struct
- {
- char *code;
- int is_volatile;
- char **outputs;
- char **output_modes;
- char **inputs;
- char **clobbers;
- int num_outputs;
- int num_inputs;
- int num_clobbers;
- } asm_stmt;
-
- struct
- {
- char **param_names;
- char **param_types;
- char *return_type;
- ASTNode *body;
- int num_params;
- int lambda_id;
- int is_expression;
- char **captured_vars;
- char **captured_types;
- int num_captures;
- } lambda;
-
- struct
- {
- char *target_type;
- ASTNode *expr;
- } size_of;
-
- struct
- {
- char *label_name;
- ASTNode *goto_expr;
- } goto_stmt;
-
- struct
- {
- char *label_name;
- } label_stmt;
-
- struct
- {
- char *target_label;
- } break_stmt;
-
- struct
- {
- char *target_label;
- } continue_stmt;
-
- struct
- {
- ASTNode *expr;
- } try_stmt;
-
- struct
- {
- int kind; // 0=type_name, 1=fields.
- Type *target_type;
- } reflection;
-
- struct
- {
- ASTNode *expr;
- } repl_print;
- };
+struct ASTNode {
+ NodeType type;
+ ASTNode *next;
+ int line; // Source line number for debugging.
+
+ // Type information.
+ char *resolved_type; // Legacy string representation (for example: "int",
+ // "User*"). > Yes, 'legacy' is a thing, this is the
+ // third iteration > of this project (for now).
+ Type *type_info; // Formal type object (for inference/generics).
+ Token token;
+ Token definition_token; // For LSP: Location where the symbol used in this
+ // node was defined.
+
+ union {
+ struct {
+ ASTNode *children;
+ } root;
+
+ struct {
+ char *name;
+ char *args; // Legacy string args.
+ char *ret_type; // Legacy string return type.
+ ASTNode *body;
+ Type **arg_types;
+ char **defaults;
+ char **param_names; // Explicit parameter names.
+ int arg_count;
+ Type *ret_type_info;
+ int is_varargs;
+ int is_inline;
+ int must_use; // @must_use: warn if return value is discarded.
+ // GCC attributes
+ int noinline; // @noinline
+ int constructor; // @constructor
+ int destructor; // @destructor
+ int unused; // @unused
+ int weak; // @weak
+ int is_export; // @export (visibility default).
+ int cold; // @cold
+ int hot; // @hot
+ int noreturn; // @noreturn
+ int pure; // @pure
+ char *section; // @section("name")
+ int is_async; // async function
+ int is_comptime; // @comptime function
+ } func;
+
+ struct {
+ ASTNode *statements;
+ } block;
+
+ struct {
+ ASTNode *value;
+ } ret;
+
+ struct {
+ char *name;
+ char *type_str;
+ ASTNode *init_expr;
+ Type *type_info;
+ int is_autofree;
+ int is_mutable;
+ int is_static;
+ } var_decl;
+
+ struct {
+ char *name;
+ Type *payload;
+ int tag_id;
+ } variant;
+
+ struct {
+ char *name;
+ ASTNode *variants;
+ int is_template;
+ char *generic_param;
+ } enm;
+
+ struct {
+ char *alias;
+ char *original_type;
+ } type_alias;
+
+ struct {
+ ASTNode *condition;
+ ASTNode *then_body;
+ ASTNode *else_body;
+ } if_stmt;
+
+ struct {
+ ASTNode *condition;
+ ASTNode *body;
+ char *loop_label;
+ } while_stmt;
+
+ struct {
+ ASTNode *init;
+ ASTNode *condition;
+ ASTNode *step;
+ ASTNode *body;
+ char *loop_label;
+ } for_stmt;
+
+ struct {
+ char *var_name;
+ ASTNode *start;
+ ASTNode *end;
+ char *step;
+ ASTNode *body;
+ } for_range;
+
+ struct {
+ ASTNode *body;
+ char *loop_label;
+ } loop_stmt;
+
+ struct {
+ char *count;
+ ASTNode *body;
+ } repeat_stmt;
+
+ struct {
+ ASTNode *condition;
+ ASTNode *body;
+ } unless_stmt;
+
+ struct {
+ ASTNode *condition;
+ ASTNode *body;
+ } guard_stmt;
+
+ struct {
+ ASTNode *condition;
+ ASTNode *body;
+ char *loop_label;
+ } do_while_stmt;
+
+ struct {
+ ASTNode *expr;
+ ASTNode *cases;
+ } match_stmt;
+
+ struct {
+ char *pattern;
+ char *binding_name;
+ int is_destructuring;
+ ASTNode *guard;
+ ASTNode *body;
+ int is_default;
+ } match_case;
+
+ struct {
+ char *op;
+ ASTNode *left;
+ ASTNode *right;
+ } binary;
+
+ struct {
+ char *op;
+ ASTNode *operand;
+ } unary;
+
+ struct {
+ int type_kind;
+ unsigned long long int_val;
+ double float_val;
+ char *string_val;
+ } literal;
+
+ struct {
+ char *name;
+ char *suggestion;
+ } var_ref;
+
+ struct {
+ ASTNode *callee;
+ ASTNode *args;
+ char **arg_names;
+ int arg_count;
+ } call;
+
+ struct {
+ ASTNode *target;
+ char *field;
+ int is_pointer_access;
+ } member;
+
+ struct {
+ ASTNode *array;
+ ASTNode *index;
+ } index;
+
+ struct {
+ ASTNode *array;
+ ASTNode *start;
+ ASTNode *end;
+ } slice;
+
+ struct {
+ char *target_type;
+ ASTNode *expr;
+ } cast;
+
+ struct {
+ char *struct_name;
+ ASTNode *fields;
+ } struct_init;
+
+ struct {
+ ASTNode *elements;
+ int count;
+ } array_literal;
+
+ struct {
+ char *name;
+ ASTNode *fields;
+ int is_template;
+ char *generic_param;
+ char *parent;
+ int is_union;
+ int is_packed; // @packed attribute.
+ int align; // @align(N) attribute, 0 = default.
+ int is_incomplete; // Forward declaration (prototype)
+ } strct;
+
+ struct {
+ char *name;
+ char *type;
+ int bit_width;
+ } field;
+
+ struct {
+ char *name;
+ ASTNode *methods;
+ } trait;
+
+ struct {
+ char *struct_name;
+ ASTNode *methods;
+ } impl;
+
+ struct {
+ char *trait_name;
+ char *target_type;
+ ASTNode *methods;
+ } impl_trait;
+
+ struct {
+ char *path;
+ int is_system;
+ } include;
+
+ struct {
+ char *content;
+ char **used_symbols;
+ int used_symbol_count;
+ } raw_stmt;
+
+ struct {
+ char *name;
+ ASTNode *body;
+ } test_stmt;
+
+ struct {
+ ASTNode *condition;
+ char *message;
+ } assert_stmt;
+
+ struct {
+ ASTNode *stmt;
+ } defer_stmt;
+
+ struct {
+ char *plugin_name;
+ char *body;
+ } plugin_stmt;
+
+ struct {
+ char **names;
+ int count;
+ ASTNode *init_expr;
+ int is_struct_destruct;
+ char *struct_name; // "Point" (or NULL if unchecked/inferred).
+ char **field_names; // NULL if same as 'names', otherwise mapped.
+ int is_guard;
+ char *guard_variant; // "Some", "Ok".
+ ASTNode *else_block;
+ } destruct;
+
+ struct {
+ ASTNode *cond;
+ ASTNode *true_expr;
+ ASTNode *false_expr;
+ } ternary;
+
+ struct {
+ char *code;
+ int is_volatile;
+ char **outputs;
+ char **output_modes;
+ char **inputs;
+ char **clobbers;
+ int num_outputs;
+ int num_inputs;
+ int num_clobbers;
+ } asm_stmt;
+
+ struct {
+ char **param_names;
+ char **param_types;
+ char *return_type;
+ ASTNode *body;
+ int num_params;
+ int lambda_id;
+ int is_expression;
+ char **captured_vars;
+ char **captured_types;
+ int num_captures;
+ } lambda;
+
+ struct {
+ char *target_type;
+ ASTNode *expr;
+ } size_of;
+
+ struct {
+ char *label_name;
+ ASTNode *goto_expr;
+ } goto_stmt;
+
+ struct {
+ char *label_name;
+ } label_stmt;
+
+ struct {
+ char *target_label;
+ } break_stmt;
+
+ struct {
+ char *target_label;
+ } continue_stmt;
+
+ struct {
+ ASTNode *expr;
+ } try_stmt;
+
+ struct {
+ int kind; // 0=type_name, 1=fields.
+ Type *target_type;
+ } reflection;
+
+ struct {
+ ASTNode *expr;
+ } repl_print;
+ };
};
// ** Functions **
diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c
index 86295e4..e3e1ac6 100644
--- a/src/codegen/codegen.c
+++ b/src/codegen/codegen.c
@@ -20,2073 +20,1652 @@
// static function for internal use.
static char *g_current_func_ret_type = NULL;
-static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, int use_result)
-{
- int id = tmp_counter++;
- int is_self = (node->match_stmt.expr->type == NODE_EXPR_VAR &&
- strcmp(node->match_stmt.expr->var_ref.name, "self") == 0);
-
- char *ret_type = infer_type(ctx, node);
- int is_expr = (use_result && ret_type && strcmp(ret_type, "void") != 0);
+static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out,
+ int use_result) {
+ int id = tmp_counter++;
+ int is_self = (node->match_stmt.expr->type == NODE_EXPR_VAR &&
+ strcmp(node->match_stmt.expr->var_ref.name, "self") == 0);
+
+ char *ret_type = infer_type(ctx, node);
+ int is_expr = (use_result && ret_type && strcmp(ret_type, "void") != 0);
+
+ fprintf(out, "({ ");
+ emit_auto_type(ctx, node->match_stmt.expr, node->token, out);
+ fprintf(out, " _m_%d = ", id);
+ if (is_self) {
+ fprintf(out, "*(");
+ }
+ codegen_expression(ctx, node->match_stmt.expr, out);
+ if (is_self) {
+ fprintf(out, ")");
+ }
+ fprintf(out, "; ");
+
+ if (is_expr) {
+ fprintf(out, "%s _r_%d; ", ret_type, id);
+ }
+
+ char *expr_type = infer_type(ctx, node->match_stmt.expr);
+ int is_option = (expr_type && strncmp(expr_type, "Option_", 7) == 0);
+ int is_result = (expr_type && strncmp(expr_type, "Result_", 7) == 0);
+
+ char *enum_name = NULL;
+ ASTNode *chk = node->match_stmt.cases;
+ int has_wildcard = 0;
+ while (chk) {
+ if (strcmp(chk->match_case.pattern, "_") == 0) {
+ has_wildcard = 1;
+ } else if (!enum_name) {
+ EnumVariantReg *reg = find_enum_variant(ctx, chk->match_case.pattern);
+ if (reg) {
+ enum_name = reg->enum_name;
+ }
+ }
+ chk = chk->next;
+ }
+
+ if (enum_name && !has_wildcard) {
+ // Iterate through all registered variants for this enum
+ EnumVariantReg *v = ctx->enum_variants;
+ while (v) {
+ if (strcmp(v->enum_name, enum_name) == 0) {
+ int covered = 0;
+ ASTNode *c2 = node->match_stmt.cases;
+ while (c2) {
+ if (strcmp(c2->match_case.pattern, v->variant_name) == 0) {
+ covered = 1;
+ break;
+ }
+ c2 = c2->next;
+ }
+ if (!covered) {
+ zwarn_at(node->token, "Non-exhaustive match: Missing variant '%s'",
+ v->variant_name);
+ }
+ }
+ v = v->next;
+ }
+ }
- fprintf(out, "({ ");
- emit_auto_type(ctx, node->match_stmt.expr, node->token, out);
- fprintf(out, " _m_%d = ", id);
- if (is_self)
- {
- fprintf(out, "*(");
+ ASTNode *c = node->match_stmt.cases;
+ int first = 1;
+ while (c) {
+ if (!first) {
+ fprintf(out, " else ");
}
- codegen_expression(ctx, node->match_stmt.expr, out);
- if (is_self)
- {
- fprintf(out, ")");
+ fprintf(out, "if (");
+ if (strcmp(c->match_case.pattern, "_") == 0) {
+ fprintf(out, "1");
+ } else if (is_option) {
+ if (strcmp(c->match_case.pattern, "Some") == 0) {
+ fprintf(out, "_m_%d.is_some", id);
+ } else if (strcmp(c->match_case.pattern, "None") == 0) {
+ fprintf(out, "!_m_%d.is_some", id);
+ } else {
+ fprintf(out, "1");
+ }
+ } else if (is_result) {
+ if (strcmp(c->match_case.pattern, "Ok") == 0) {
+ fprintf(out, "_m_%d.is_ok", id);
+ } else if (strcmp(c->match_case.pattern, "Err") == 0) {
+ fprintf(out, "!_m_%d.is_ok", id);
+ } else {
+ fprintf(out, "1");
+ }
+ } else {
+ EnumVariantReg *reg = find_enum_variant(ctx, c->match_case.pattern);
+ if (reg) {
+ fprintf(out, "_m_%d.tag == %d", id, reg->tag_id);
+ } else if (c->match_case.pattern[0] == '"') {
+ fprintf(out, "strcmp(_m_%d, %s) == 0", id, c->match_case.pattern);
+ } else if (isdigit(c->match_case.pattern[0]) ||
+ c->match_case.pattern[0] == '-') {
+ // Numeric pattern
+ fprintf(out, "_m_%d == %s", id, c->match_case.pattern);
+ } else if (c->match_case.pattern[0] == '\'') {
+ // Char literal pattern
+ fprintf(out, "_m_%d == %s", id, c->match_case.pattern);
+ } else {
+ fprintf(out, "1");
+ }
+ }
+ fprintf(out, ") { ");
+ if (c->match_case.binding_name) {
+ if (is_option) {
+ if (strstr(g_config.cc, "tcc")) {
+ fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id,
+ c->match_case.binding_name, id);
+ } else {
+ fprintf(out, "__auto_type %s = _m_%d.val; ",
+ c->match_case.binding_name, id);
+ }
+ }
+ if (is_result) {
+ if (strcmp(c->match_case.pattern, "Ok") == 0) {
+ if (strstr(g_config.cc, "tcc")) {
+ fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id,
+ c->match_case.binding_name, id);
+ } else {
+ fprintf(out, "__auto_type %s = _m_%d.val; ",
+ c->match_case.binding_name, id);
+ }
+ } else {
+ if (strstr(g_config.cc, "tcc")) {
+ fprintf(out, "__typeof__(_m_%d.err) %s = _m_%d.err; ", id,
+ c->match_case.binding_name, id);
+ } else {
+ fprintf(out, "__auto_type %s = _m_%d.err; ",
+ c->match_case.binding_name, id);
+ }
+ }
+ } else {
+ char *f = strrchr(c->match_case.pattern, '_');
+ if (f) {
+ f++;
+ } else {
+ f = c->match_case.pattern;
+ }
+ fprintf(out, "__auto_type %s = _m_%d.data.%s; ",
+ c->match_case.binding_name, id, f);
+ }
}
- fprintf(out, "; ");
- if (is_expr)
- {
- fprintf(out, "%s _r_%d; ", ret_type, id);
+ // Check if body is a string literal (should auto-print).
+ ASTNode *body = c->match_case.body;
+ int is_string_literal =
+ (body->type == NODE_EXPR_LITERAL && body->literal.type_kind == 2);
+
+ if (is_expr) {
+ fprintf(out, "_r_%d = ", id);
+ if (is_string_literal) {
+ codegen_node_single(ctx, body, out);
+ } else {
+ if (body->type == NODE_BLOCK) {
+ int saved = defer_count;
+ fprintf(out, "({ ");
+ ASTNode *stmt = body->block.statements;
+ while (stmt) {
+ codegen_node_single(ctx, stmt, out);
+ stmt = stmt->next;
+ }
+ for (int i = defer_count - 1; i >= saved; i--) {
+ codegen_node_single(ctx, defer_stack[i], out);
+ }
+ defer_count = saved;
+ fprintf(out, " })");
+ } else {
+ codegen_node_single(ctx, body, out);
+ }
+ }
+ fprintf(out, ";");
+ } else {
+ if (is_string_literal) {
+ fprintf(out, "({ printf(\"%%s\", ");
+ codegen_expression(ctx, body, out);
+ fprintf(out, "); printf(\"\\n\"); 0; })");
+ } else {
+ codegen_node_single(ctx, body, out);
+ }
}
- char *expr_type = infer_type(ctx, node->match_stmt.expr);
- int is_option = (expr_type && strncmp(expr_type, "Option_", 7) == 0);
- int is_result = (expr_type && strncmp(expr_type, "Result_", 7) == 0);
+ fprintf(out, " }");
+ first = 0;
+ c = c->next;
+ }
- char *enum_name = NULL;
- ASTNode *chk = node->match_stmt.cases;
- int has_wildcard = 0;
- while (chk)
- {
- if (strcmp(chk->match_case.pattern, "_") == 0)
- {
- has_wildcard = 1;
- }
- else if (!enum_name)
- {
- EnumVariantReg *reg = find_enum_variant(ctx, chk->match_case.pattern);
- if (reg)
- {
- enum_name = reg->enum_name;
- }
- }
- chk = chk->next;
- }
+ if (is_expr) {
+ fprintf(out, " _r_%d; })", id);
+ } else {
+ fprintf(out, " })");
+ }
+}
- if (enum_name && !has_wildcard)
- {
- // Iterate through all registered variants for this enum
- EnumVariantReg *v = ctx->enum_variants;
- while (v)
- {
- if (strcmp(v->enum_name, enum_name) == 0)
- {
- int covered = 0;
- ASTNode *c2 = node->match_stmt.cases;
- while (c2)
- {
- if (strcmp(c2->match_case.pattern, v->variant_name) == 0)
- {
- covered = 1;
- break;
- }
- c2 = c2->next;
- }
- if (!covered)
- {
- zwarn_at(node->token, "Non-exhaustive match: Missing variant '%s'",
- v->variant_name);
- }
+void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) {
+ if (!node) {
+ return;
+ }
+ switch (node->type) {
+ case NODE_MATCH:
+ codegen_match_internal(ctx, node, out, 1);
+ break;
+ case NODE_EXPR_BINARY:
+ if (strncmp(node->binary.op, "??", 2) == 0 &&
+ strlen(node->binary.op) == 2) {
+ fprintf(out, "({ ");
+ emit_auto_type(ctx, node->binary.left, node->token, out);
+ fprintf(out, " _l = (");
+ codegen_expression(ctx, node->binary.left, out);
+ fprintf(out, "); _l ? _l : (");
+ codegen_expression(ctx, node->binary.right, out);
+ fprintf(out, "); })");
+ } else if (strcmp(node->binary.op, "?\?=") == 0) {
+ fprintf(out, "({ if (!(");
+ codegen_expression(ctx, node->binary.left, out);
+ fprintf(out, ")) ");
+ codegen_expression(ctx, node->binary.left, out);
+ fprintf(out, " = (");
+ codegen_expression(ctx, node->binary.right, out);
+ fprintf(out, "); ");
+ codegen_expression(ctx, node->binary.left, out);
+ fprintf(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) {
+ char *check = t1;
+ int depth = 0;
+ while (depth++ < 10) {
+ if (strchr(check, '*')) {
+ is_ptr = 1;
+ break;
+ }
+ int resolved = 0;
+ ASTNode *alias = global_user_structs;
+ if (alias) {
+ while (alias) {
+ 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;
}
- v = v->next;
+ }
+ if (!resolved) {
+ break;
+ }
+ }
+ }
+
+ int is_basic = 0;
+ if (t1) {
+ is_basic =
+ (strcmp(t1, "int") == 0 || strcmp(t1, "bool") == 0 ||
+ strcmp(t1, "char") == 0 || strcmp(t1, "void") == 0 ||
+ strcmp(t1, "float") == 0 || strcmp(t1, "double") == 0 ||
+ strcmp(t1, "usize") == 0 || strcmp(t1, "size_t") == 0 ||
+ strcmp(t1, "ssize_t") == 0 || strcmp(t1, "__auto_type") == 0);
+ }
+
+ ASTNode *def = t1 ? find_struct_def(ctx, t1) : NULL;
+ if (t1 && def && (def->type == NODE_STRUCT || def->type == NODE_ENUM) &&
+ !is_basic && !is_ptr) {
+ char *base = t1;
+ if (strncmp(base, "struct ", 7) == 0) {
+ base += 7;
+ }
+
+ if (strcmp(node->binary.op, "!=") == 0) {
+ fprintf(out, "(!");
+ }
+ fprintf(out, "%s_eq(&", base);
+ codegen_expression(ctx, node->binary.left, out);
+ fprintf(out, ", ");
+ codegen_expression(ctx, node->binary.right, out);
+ fprintf(out, ")");
+ if (strcmp(node->binary.op, "!=") == 0) {
+ fprintf(out, ")");
}
+ } else {
+ fprintf(out, "(");
+ codegen_expression(ctx, node->binary.left, out);
+ fprintf(out, " %s ", node->binary.op);
+ codegen_expression(ctx, node->binary.right, out);
+ fprintf(out, ")");
+ }
+ } else {
+ fprintf(out, "(");
+ codegen_expression(ctx, node->binary.left, out);
+ fprintf(out, " %s ", node->binary.op);
+ codegen_expression(ctx, node->binary.right, out);
+ fprintf(out, ")");
+ }
+ break;
+ case NODE_EXPR_VAR:
+ if (g_current_lambda) {
+ for (int i = 0; i < g_current_lambda->lambda.num_captures; i++) {
+ if (strcmp(node->var_ref.name,
+ g_current_lambda->lambda.captured_vars[i]) == 0) {
+ fprintf(out, "ctx->%s", node->var_ref.name);
+ return;
+ }
+ }
}
- ASTNode *c = node->match_stmt.cases;
- int first = 1;
- while (c)
- {
- if (!first)
- {
- fprintf(out, " else ");
- }
- fprintf(out, "if (");
- if (strcmp(c->match_case.pattern, "_") == 0)
- {
- fprintf(out, "1");
- }
- else if (is_option)
- {
- if (strcmp(c->match_case.pattern, "Some") == 0)
- {
- fprintf(out, "_m_%d.is_some", id);
- }
- else if (strcmp(c->match_case.pattern, "None") == 0)
- {
- fprintf(out, "!_m_%d.is_some", id);
- }
- else
- {
- fprintf(out, "1");
- }
- }
- else if (is_result)
- {
- if (strcmp(c->match_case.pattern, "Ok") == 0)
- {
- fprintf(out, "_m_%d.is_ok", id);
- }
- else if (strcmp(c->match_case.pattern, "Err") == 0)
- {
- fprintf(out, "!_m_%d.is_ok", id);
- }
- else
- {
- fprintf(out, "1");
- }
- }
- else
- {
- EnumVariantReg *reg = find_enum_variant(ctx, c->match_case.pattern);
- if (reg)
- {
- fprintf(out, "_m_%d.tag == %d", id, reg->tag_id);
- }
- else if (c->match_case.pattern[0] == '"')
- {
- fprintf(out, "strcmp(_m_%d, %s) == 0", id, c->match_case.pattern);
- }
- else if (isdigit(c->match_case.pattern[0]) || c->match_case.pattern[0] == '-')
- {
- // Numeric pattern
- fprintf(out, "_m_%d == %s", id, c->match_case.pattern);
- }
- else if (c->match_case.pattern[0] == '\'')
- {
- // Char literal pattern
- fprintf(out, "_m_%d == %s", id, c->match_case.pattern);
- }
- else
- {
- fprintf(out, "1");
- }
- }
- fprintf(out, ") { ");
- if (c->match_case.binding_name)
- {
- if (is_option)
- {
- if (strstr(g_config.cc, "tcc"))
- {
- fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id,
- c->match_case.binding_name, id);
- }
- else
- {
- fprintf(out, "__auto_type %s = _m_%d.val; ", c->match_case.binding_name, id);
- }
- }
- if (is_result)
- {
- if (strcmp(c->match_case.pattern, "Ok") == 0)
- {
- if (strstr(g_config.cc, "tcc"))
- {
- fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id,
- c->match_case.binding_name, id);
- }
- else
- {
- fprintf(out, "__auto_type %s = _m_%d.val; ", c->match_case.binding_name,
- id);
- }
- }
- else
- {
- if (strstr(g_config.cc, "tcc"))
- {
- fprintf(out, "__typeof__(_m_%d.err) %s = _m_%d.err; ", id,
- c->match_case.binding_name, id);
- }
- else
- {
- fprintf(out, "__auto_type %s = _m_%d.err; ", c->match_case.binding_name,
- id);
- }
- }
- }
- else
- {
- char *f = strrchr(c->match_case.pattern, '_');
- if (f)
- {
- f++;
- }
- else
- {
- f = c->match_case.pattern;
- }
- fprintf(out, "__auto_type %s = _m_%d.data.%s; ", c->match_case.binding_name, id, f);
- }
- }
-
- // Check if body is a string literal (should auto-print).
- ASTNode *body = c->match_case.body;
- int is_string_literal = (body->type == NODE_EXPR_LITERAL && body->literal.type_kind == 2);
+ if (node->resolved_type && strcmp(node->resolved_type, "unknown") == 0) {
+ if (node->var_ref.suggestion) {
+ 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);
- if (is_expr)
- {
- fprintf(out, "_r_%d = ", id);
- if (is_string_literal)
- {
- codegen_node_single(ctx, body, out);
- }
- else
- {
- if (body->type == NODE_BLOCK)
- {
- int saved = defer_count;
- fprintf(out, "({ ");
- ASTNode *stmt = body->block.statements;
- while (stmt)
- {
- codegen_node_single(ctx, stmt, out);
- stmt = stmt->next;
- }
- for (int i = defer_count - 1; i >= saved; i--)
- {
- codegen_node_single(ctx, defer_stack[i], out);
- }
- defer_count = saved;
- fprintf(out, " })");
- }
- else
- {
- codegen_node_single(ctx, body, out);
- }
- }
- fprintf(out, ";");
- }
- else
- {
- if (is_string_literal)
- {
- fprintf(out, "({ printf(\"%%s\", ");
- codegen_expression(ctx, body, out);
- fprintf(out, "); printf(\"\\n\"); 0; })");
- }
- else
- {
- codegen_node_single(ctx, body, out);
- }
+ zwarn_at(node->token, "%s\n = help: %s", msg, help);
+ }
+ }
+ fprintf(out, "%s", node->var_ref.name);
+ break;
+ case NODE_LAMBDA:
+ if (node->lambda.num_captures > 0) {
+ fprintf(out,
+ "({ struct Lambda_%d_Ctx *ctx = malloc(sizeof(struct "
+ "Lambda_%d_Ctx));\n",
+ node->lambda.lambda_id, node->lambda.lambda_id);
+ for (int i = 0; i < node->lambda.num_captures; i++) {
+ fprintf(out, "ctx->%s = ", node->lambda.captured_vars[i]);
+ int found = 0;
+ if (g_current_lambda) {
+ for (int k = 0; k < g_current_lambda->lambda.num_captures; k++) {
+ if (strcmp(node->lambda.captured_vars[i],
+ g_current_lambda->lambda.captured_vars[k]) == 0) {
+ fprintf(out, "ctx->%s", node->lambda.captured_vars[i]);
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ fprintf(out, "%s", node->lambda.captured_vars[i]);
}
-
- fprintf(out, " }");
- first = 0;
- c = c->next;
+ fprintf(out, ";\n");
+ }
+ fprintf(out, "(z_closure_T){.func = _lambda_%d, .ctx = ctx}; })",
+ node->lambda.lambda_id);
+ } else {
+ fprintf(out, "((z_closure_T){.func = (void*)_lambda_%d, .ctx = NULL})",
+ node->lambda.lambda_id);
+ }
+ break;
+ case NODE_EXPR_LITERAL:
+ if (node->literal.type_kind == TOK_STRING) {
+ fprintf(out, "\"%s\"", node->literal.string_val);
+ } else if (node->literal.type_kind == TOK_CHAR) {
+ fprintf(out, "%s", node->literal.string_val);
+ } else if (node->literal.type_kind == 1) {
+ fprintf(out, "%f", node->literal.float_val);
}
- if (is_expr)
- {
- fprintf(out, " _r_%d; })", id);
+ else {
+ if (node->literal.int_val > 9223372036854775807ULL) {
+ fprintf(out, "%lluULL", (unsigned long long)node->literal.int_val);
+ } else {
+ fprintf(out, "%llu", (unsigned long long)node->literal.int_val);
+ }
}
- else
- {
- fprintf(out, " })");
+ break;
+ case NODE_EXPR_CALL: {
+ if (node->call.callee->type == NODE_EXPR_MEMBER) {
+ ASTNode *target = node->call.callee->member.target;
+ char *method = node->call.callee->member.field;
+
+ if (strcmp(method, "len") == 0) {
+ if (target->type_info && target->type_info->kind == TYPE_ARRAY) {
+ if (target->type_info->array_size > 0) {
+ fprintf(out, "%d", target->type_info->array_size);
+ } else {
+ codegen_expression(ctx, target, out);
+ fprintf(out, ".len");
+ }
+ return;
+ }
+ }
+
+ char *type = infer_type(ctx, target);
+ if (type) {
+ char *clean = xstrdup(type);
+ char *ptr = strchr(clean, '*');
+ if (ptr) {
+ *ptr = 0;
+ }
+
+ char *base = clean;
+ if (strncmp(base, "struct ", 7) == 0) {
+ base += 7;
+ }
+
+ if (!strchr(type, '*') && target->type == NODE_EXPR_CALL) {
+ fprintf(out, "({ %s _t = ", type);
+ codegen_expression(ctx, target, out);
+ fprintf(out, "; %s_%s(&_t", base, method);
+ ASTNode *arg = node->call.args;
+ while (arg) {
+ fprintf(out, ", ");
+ codegen_expression(ctx, arg, out);
+ arg = arg->next;
+ }
+ fprintf(out, "); })");
+ } else {
+ fprintf(out, "%s_%s(", base, method);
+ if (!strchr(type, '*')) {
+ fprintf(out, "&");
+ }
+ codegen_expression(ctx, target, out);
+ ASTNode *arg = node->call.args;
+ while (arg) {
+ fprintf(out, ", ");
+ codegen_expression(ctx, arg, out);
+ arg = arg->next;
+ }
+ fprintf(out, ")");
+ }
+ free(clean);
+ return;
+ }
}
-}
-
-void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
-{
- if (!node)
- {
+ if (node->call.callee->type == NODE_EXPR_VAR) {
+ ASTNode *def = find_struct_def(ctx, node->call.callee->var_ref.name);
+ if (def && def->type == NODE_STRUCT) {
+ fprintf(out, "(struct %s){0}", node->call.callee->var_ref.name);
return;
+ }
}
- switch (node->type)
- {
- case NODE_MATCH:
- codegen_match_internal(ctx, node, out, 1);
- break;
- case NODE_EXPR_BINARY:
- if (strncmp(node->binary.op, "??", 2) == 0 && strlen(node->binary.op) == 2)
- {
- fprintf(out, "({ ");
- emit_auto_type(ctx, node->binary.left, node->token, out);
- fprintf(out, " _l = (");
- codegen_expression(ctx, node->binary.left, out);
- fprintf(out, "); _l ? _l : (");
- codegen_expression(ctx, node->binary.right, out);
- fprintf(out, "); })");
- }
- else if (strcmp(node->binary.op, "?\?=") == 0)
- {
- fprintf(out, "({ if (!(");
- codegen_expression(ctx, node->binary.left, out);
- fprintf(out, ")) ");
- codegen_expression(ctx, node->binary.left, out);
- fprintf(out, " = (");
- codegen_expression(ctx, node->binary.right, out);
- fprintf(out, "); ");
- codegen_expression(ctx, node->binary.left, out);
- fprintf(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)
- {
- char *check = t1;
- int depth = 0;
- while (depth++ < 10)
- {
- if (strchr(check, '*'))
- {
- is_ptr = 1;
- break;
- }
- int resolved = 0;
- ASTNode *alias = global_user_structs;
- if (alias)
- {
- while (alias)
- {
- 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;
- }
- }
- if (!resolved)
- {
- break;
- }
- }
- }
- int is_basic = 0;
- if (t1)
- {
- is_basic = (strcmp(t1, "int") == 0 || strcmp(t1, "bool") == 0 ||
- strcmp(t1, "char") == 0 || strcmp(t1, "void") == 0 ||
- strcmp(t1, "float") == 0 || strcmp(t1, "double") == 0 ||
- strcmp(t1, "usize") == 0 || strcmp(t1, "size_t") == 0 ||
- strcmp(t1, "ssize_t") == 0 || strcmp(t1, "__auto_type") == 0);
- }
+ if (node->call.callee->type_info &&
+ node->call.callee->type_info->kind == TYPE_FUNCTION) {
+ fprintf(out, "({ z_closure_T _c = ");
+ codegen_expression(ctx, node->call.callee, out);
+ fprintf(out, "; ");
+
+ Type *ft = node->call.callee->type_info;
+ char *ret = type_to_string(ft->inner);
+ if (strcmp(ret, "string") == 0) {
+ free(ret);
+ ret = xstrdup("char*");
+ }
+
+ fprintf(out, "((%s (*)(void*", ret);
+ for (int i = 0; i < ft->arg_count; i++) {
+ char *as = type_to_string(ft->args[i]);
+ fprintf(out, ", %s", as);
+ free(as);
+ }
+ if (ft->is_varargs) {
+ fprintf(out, ", ...");
+ }
+ fprintf(out, "))_c.func)(_c.ctx");
+
+ ASTNode *arg = node->call.args;
+ while (arg) {
+ fprintf(out, ", ");
+ codegen_expression(ctx, arg, out);
+ arg = arg->next;
+ }
+ fprintf(out, "); })");
+ free(ret);
+ break;
+ }
- ASTNode *def = t1 ? find_struct_def(ctx, t1) : NULL;
- if (t1 && def && (def->type == NODE_STRUCT || def->type == NODE_ENUM) && !is_basic &&
- !is_ptr)
- {
- char *base = t1;
- if (strncmp(base, "struct ", 7) == 0)
- {
- base += 7;
- }
-
- if (strcmp(node->binary.op, "!=") == 0)
- {
- fprintf(out, "(!");
- }
- fprintf(out, "%s_eq(&", base);
- codegen_expression(ctx, node->binary.left, out);
- fprintf(out, ", ");
- codegen_expression(ctx, node->binary.right, out);
- fprintf(out, ")");
- if (strcmp(node->binary.op, "!=") == 0)
- {
- fprintf(out, ")");
- }
- }
- else
- {
- fprintf(out, "(");
- codegen_expression(ctx, node->binary.left, out);
- fprintf(out, " %s ", node->binary.op);
- codegen_expression(ctx, node->binary.right, out);
- fprintf(out, ")");
- }
- }
- else
- {
- fprintf(out, "(");
- codegen_expression(ctx, node->binary.left, out);
- fprintf(out, " %s ", node->binary.op);
- codegen_expression(ctx, node->binary.right, out);
- fprintf(out, ")");
- }
- break;
- case NODE_EXPR_VAR:
- if (g_current_lambda)
- {
- for (int i = 0; i < g_current_lambda->lambda.num_captures; i++)
- {
- if (strcmp(node->var_ref.name, g_current_lambda->lambda.captured_vars[i]) == 0)
- {
- fprintf(out, "ctx->%s", node->var_ref.name);
- return;
- }
- }
- }
+ codegen_expression(ctx, node->call.callee, out);
+ fprintf(out, "(");
- if (node->resolved_type && strcmp(node->resolved_type, "unknown") == 0)
- {
- if (node->var_ref.suggestion)
- {
- 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);
+ 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);
- zwarn_at(node->token, "%s\n = help: %s", msg, help);
- }
- }
- fprintf(out, "%s", node->var_ref.name);
- break;
- case NODE_LAMBDA:
- if (node->lambda.num_captures > 0)
- {
- fprintf(out,
- "({ struct Lambda_%d_Ctx *ctx = malloc(sizeof(struct "
- "Lambda_%d_Ctx));\n",
- node->lambda.lambda_id, node->lambda.lambda_id);
- for (int i = 0; i < node->lambda.num_captures; i++)
- {
- fprintf(out, "ctx->%s = ", node->lambda.captured_vars[i]);
- int found = 0;
- if (g_current_lambda)
- {
- for (int k = 0; k < g_current_lambda->lambda.num_captures; k++)
- {
- if (strcmp(node->lambda.captured_vars[i],
- g_current_lambda->lambda.captured_vars[k]) == 0)
- {
- fprintf(out, "ctx->%s", node->lambda.captured_vars[i]);
- found = 1;
- break;
- }
- }
- }
- if (!found)
- {
- fprintf(out, "%s", node->lambda.captured_vars[i]);
- }
- fprintf(out, ";\n");
- }
- fprintf(out, "(z_closure_T){.func = _lambda_%d, .ctx = ctx}; })",
- node->lambda.lambda_id);
- }
- else
- {
- fprintf(out, "((z_closure_T){.func = (void*)_lambda_%d, .ctx = NULL})",
- node->lambda.lambda_id);
- }
- break;
- case NODE_EXPR_LITERAL:
- if (node->literal.type_kind == TOK_STRING)
- {
- fprintf(out, "\"%s\"", node->literal.string_val);
- }
- else if (node->literal.type_kind == TOK_CHAR)
- {
- fprintf(out, "%s", node->literal.string_val);
- }
- else if (node->literal.type_kind == 1)
- {
- fprintf(out, "%f", node->literal.float_val);
- }
-
- else
- {
- if (node->literal.int_val > 9223372036854775807ULL)
- {
- fprintf(out, "%lluULL", (unsigned long long)node->literal.int_val);
- }
- else
- {
- fprintf(out, "%llu", (unsigned long long)node->literal.int_val);
- }
- }
- break;
- case NODE_EXPR_CALL:
- {
- if (node->call.callee->type == NODE_EXPR_MEMBER)
- {
- ASTNode *target = node->call.callee->member.target;
- char *method = node->call.callee->member.field;
-
- if (strcmp(method, "len") == 0)
- {
- if (target->type_info && target->type_info->kind == TYPE_ARRAY)
- {
- if (target->type_info->array_size > 0)
- {
- fprintf(out, "%d", target->type_info->array_size);
- }
- else
- {
- codegen_expression(ctx, target, out);
- fprintf(out, ".len");
- }
- return;
- }
- }
-
- char *type = infer_type(ctx, target);
- if (type)
- {
- char *clean = xstrdup(type);
- char *ptr = strchr(clean, '*');
- if (ptr)
- {
- *ptr = 0;
- }
-
- char *base = clean;
- if (strncmp(base, "struct ", 7) == 0)
- {
- base += 7;
- }
-
- if (!strchr(type, '*') && target->type == NODE_EXPR_CALL)
- {
- fprintf(out, "({ %s _t = ", type);
- codegen_expression(ctx, target, out);
- fprintf(out, "; %s_%s(&_t", base, method);
- ASTNode *arg = node->call.args;
- while (arg)
- {
- fprintf(out, ", ");
- codegen_expression(ctx, arg, out);
- arg = arg->next;
- }
- fprintf(out, "); })");
- }
- else
- {
- fprintf(out, "%s_%s(", base, method);
- if (!strchr(type, '*'))
- {
- fprintf(out, "&");
- }
- codegen_expression(ctx, target, out);
- ASTNode *arg = node->call.args;
- while (arg)
- {
- fprintf(out, ", ");
- codegen_expression(ctx, arg, out);
- arg = arg->next;
- }
- fprintf(out, ")");
- }
- free(clean);
- return;
- }
- }
- if (node->call.callee->type == NODE_EXPR_VAR)
- {
- ASTNode *def = find_struct_def(ctx, node->call.callee->var_ref.name);
- if (def && def->type == NODE_STRUCT)
- {
- fprintf(out, "(struct %s){0}", node->call.callee->var_ref.name);
- return;
- }
- }
+ if (sig && sig->arg_types) {
+ for (int p = 0; p < sig->total_args; p++) {
+ ASTNode *arg = node->call.args;
- if (node->call.callee->type_info && node->call.callee->type_info->kind == TYPE_FUNCTION)
- {
- fprintf(out, "({ z_closure_T _c = ");
- codegen_expression(ctx, node->call.callee, out);
- fprintf(out, "; ");
-
- Type *ft = node->call.callee->type_info;
- char *ret = type_to_string(ft->inner);
- if (strcmp(ret, "string") == 0)
- {
- free(ret);
- ret = xstrdup("char*");
- }
+ 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) {
- fprintf(out, "((%s (*)(void*", ret);
- for (int i = 0; i < ft->arg_count; i++)
- {
- char *as = type_to_string(ft->args[i]);
- fprintf(out, ", %s", as);
- free(as);
- }
- if (ft->is_varargs)
- {
- fprintf(out, ", ...");
- }
- fprintf(out, "))_c.func)(_c.ctx");
-
- ASTNode *arg = node->call.args;
- while (arg)
- {
- fprintf(out, ", ");
- codegen_expression(ctx, arg, out);
- arg = arg->next;
+ // For now, emit in order provided...
}
- fprintf(out, "); })");
- free(ret);
- break;
+ }
}
+ }
- codegen_expression(ctx, node->call.callee, out);
- fprintf(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)
- {
- if (!first)
- {
- fprintf(out, ", ");
- }
- first = 0;
- codegen_expression(ctx, arg, out);
- arg = arg->next;
- }
- }
- else
- {
- ASTNode *arg = node->call.args;
- while (arg)
- {
- codegen_expression(ctx, arg, out);
- if (arg->next)
- {
- fprintf(out, ", ");
- }
- arg = arg->next;
- }
+ ASTNode *arg = node->call.args;
+ int first = 1;
+ while (arg) {
+ if (!first) {
+ fprintf(out, ", ");
}
- fprintf(out, ")");
- break;
+ first = 0;
+ codegen_expression(ctx, arg, out);
+ arg = arg->next;
+ }
+ } else {
+ ASTNode *arg = node->call.args;
+ while (arg) {
+ codegen_expression(ctx, arg, out);
+ if (arg->next) {
+ fprintf(out, ", ");
+ }
+ arg = arg->next;
+ }
}
- case NODE_EXPR_MEMBER:
- if (strcmp(node->member.field, "len") == 0)
- {
- if (node->member.target->type_info)
- {
- 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;
- }
- }
- }
- }
-
- if (node->member.is_pointer_access == 2)
- {
- fprintf(out, "({ ");
- emit_auto_type(ctx, node->member.target, node->token, out);
- fprintf(out, " _t = (");
- codegen_expression(ctx, node->member.target, out);
- fprintf(out, "); _t ? _t->%s : 0; })", node->member.field);
- }
- else
- {
- codegen_expression(ctx, node->member.target, out);
- fprintf(out, "%s%s", node->member.is_pointer_access ? "->" : ".", node->member.field);
- }
- break;
- case NODE_EXPR_INDEX:
- {
- int is_slice_struct = 0;
- if (node->index.array->type_info)
- {
- if (node->index.array->type_info->kind == TYPE_ARRAY &&
- node->index.array->type_info->array_size == 0)
- {
- is_slice_struct = 1;
- }
- }
- if (node->index.array->resolved_type)
- {
- if (strncmp(node->index.array->resolved_type, "Slice_", 6) == 0)
- {
- is_slice_struct = 1;
- }
- }
-
- if (is_slice_struct)
- {
- if (node->index.array->type == NODE_EXPR_VAR)
- {
- codegen_expression(ctx, node->index.array, out);
- fprintf(out, ".data[_z_check_bounds(");
- codegen_expression(ctx, node->index.index, out);
- fprintf(out, ", ");
- codegen_expression(ctx, node->index.array, out);
- fprintf(out, ".len)]");
- }
- else
- {
- codegen_expression(ctx, node->index.array, out);
- fprintf(out, ".data[");
- codegen_expression(ctx, node->index.index, out);
- fprintf(out, "]");
- }
+ fprintf(out, ")");
+ break;
+ }
+ case NODE_EXPR_MEMBER:
+ if (strcmp(node->member.field, "len") == 0) {
+ if (node->member.target->type_info) {
+ 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;
+ }
}
- else
- {
- int fixed_size = -1;
- if (node->index.array->type_info && node->index.array->type_info->kind == TYPE_ARRAY)
- {
- fixed_size = node->index.array->type_info->array_size;
- }
+ }
+ }
- codegen_expression(ctx, node->index.array, out);
- fprintf(out, "[");
- if (fixed_size > 0)
- {
- fprintf(out, "_z_check_bounds(");
- }
- codegen_expression(ctx, node->index.index, out);
- if (fixed_size > 0)
- {
- fprintf(out, ", %d)", fixed_size);
- }
- fprintf(out, "]");
- }
+ if (node->member.is_pointer_access == 2) {
+ fprintf(out, "({ ");
+ emit_auto_type(ctx, node->member.target, node->token, out);
+ fprintf(out, " _t = (");
+ codegen_expression(ctx, node->member.target, out);
+ fprintf(out, "); _t ? _t->%s : 0; })", node->member.field);
+ } else {
+ codegen_expression(ctx, node->member.target, out);
+ fprintf(out, "%s%s", node->member.is_pointer_access ? "->" : ".",
+ node->member.field);
}
break;
- case NODE_EXPR_SLICE:
- {
- int known_size = -1;
- int is_slice_struct = 0;
- if (node->slice.array->type_info)
- {
- if (node->slice.array->type_info->kind == TYPE_ARRAY)
- {
- known_size = node->slice.array->type_info->array_size;
- if (known_size == 0)
- {
- is_slice_struct = 1;
- }
- }
- }
-
- char *tname = "unknown";
- if (node->type_info && node->type_info->inner)
- {
- tname = type_to_string(node->type_info->inner);
- }
+ case NODE_EXPR_INDEX: {
+ int is_slice_struct = 0;
+ if (node->index.array->type_info) {
+ if (node->index.array->type_info->kind == TYPE_ARRAY &&
+ node->index.array->type_info->array_size == 0) {
+ is_slice_struct = 1;
+ }
+ }
+ if (node->index.array->resolved_type) {
+ if (strncmp(node->index.array->resolved_type, "Slice_", 6) == 0) {
+ is_slice_struct = 1;
+ }
+ }
- fprintf(out, "({ ");
- emit_auto_type(ctx, node->slice.array, node->token, out);
- fprintf(out, " _arr = ");
- codegen_expression(ctx, node->slice.array, out);
- fprintf(out, "; int _start = ");
- if (node->slice.start)
- {
- codegen_expression(ctx, node->slice.start, out);
- }
- else
- {
- fprintf(out, "0");
- }
- fprintf(out, "; int _len = ");
+ if (is_slice_struct) {
+ if (node->index.array->type == NODE_EXPR_VAR) {
+ codegen_expression(ctx, node->index.array, out);
+ fprintf(out, ".data[_z_check_bounds(");
+ codegen_expression(ctx, node->index.index, out);
+ fprintf(out, ", ");
+ codegen_expression(ctx, node->index.array, out);
+ fprintf(out, ".len)]");
+ } else {
+ codegen_expression(ctx, node->index.array, out);
+ fprintf(out, ".data[");
+ codegen_expression(ctx, node->index.index, out);
+ fprintf(out, "]");
+ }
+ } else {
+ int fixed_size = -1;
+ if (node->index.array->type_info &&
+ node->index.array->type_info->kind == TYPE_ARRAY) {
+ fixed_size = node->index.array->type_info->array_size;
+ }
+
+ codegen_expression(ctx, node->index.array, out);
+ fprintf(out, "[");
+ if (fixed_size > 0) {
+ fprintf(out, "_z_check_bounds(");
+ }
+ codegen_expression(ctx, node->index.index, out);
+ if (fixed_size > 0) {
+ fprintf(out, ", %d)", fixed_size);
+ }
+ fprintf(out, "]");
+ }
+ } break;
+ case NODE_EXPR_SLICE: {
+ int known_size = -1;
+ int is_slice_struct = 0;
+ if (node->slice.array->type_info) {
+ if (node->slice.array->type_info->kind == TYPE_ARRAY) {
+ known_size = node->slice.array->type_info->array_size;
+ if (known_size == 0) {
+ is_slice_struct = 1;
+ }
+ }
+ }
- if (node->slice.end)
- {
- codegen_expression(ctx, node->slice.end, out);
- fprintf(out, " - _start; ");
- }
- else
- {
- if (known_size > 0)
- {
- fprintf(out, "%d - _start; ", known_size);
- }
- else if (is_slice_struct)
- {
- fprintf(out, "_arr.len - _start; ");
- }
- else
- {
- fprintf(out, "/* UNSAFE: Full Slice on unknown size */ 0; ");
- }
- }
+ char *tname = "unknown";
+ if (node->type_info && node->type_info->inner) {
+ tname = type_to_string(node->type_info->inner);
+ }
- if (is_slice_struct)
- {
- fprintf(out,
- "(Slice_%s){ .data = _arr.data + _start, .len = _len, .cap = "
- "_len }; })",
- tname);
- }
- else
- {
- fprintf(out, "(Slice_%s){ .data = _arr + _start, .len = _len, .cap = _len }; })",
- tname);
- }
- break;
+ fprintf(out, "({ ");
+ emit_auto_type(ctx, node->slice.array, node->token, out);
+ fprintf(out, " _arr = ");
+ codegen_expression(ctx, node->slice.array, out);
+ fprintf(out, "; int _start = ");
+ if (node->slice.start) {
+ codegen_expression(ctx, node->slice.start, out);
+ } else {
+ fprintf(out, "0");
}
- case NODE_BLOCK:
- {
- int saved = defer_count;
- fprintf(out, "({ ");
- codegen_walker(ctx, node->block.statements, out);
- for (int i = defer_count - 1; i >= saved; i--)
- {
- codegen_node_single(ctx, defer_stack[i], out);
- }
- defer_count = saved;
- fprintf(out, " })");
- break;
+ fprintf(out, "; int _len = ");
+
+ if (node->slice.end) {
+ codegen_expression(ctx, node->slice.end, out);
+ fprintf(out, " - _start; ");
+ } else {
+ if (known_size > 0) {
+ fprintf(out, "%d - _start; ", known_size);
+ } else if (is_slice_struct) {
+ fprintf(out, "_arr.len - _start; ");
+ } else {
+ fprintf(out, "/* UNSAFE: Full Slice on unknown size */ 0; ");
+ }
}
- case NODE_TRY:
- {
- char *type_name = "Result";
- if (g_current_func_ret_type)
- {
- type_name = g_current_func_ret_type;
- }
- else if (node->try_stmt.expr->type_info && node->try_stmt.expr->type_info->name)
- {
- type_name = node->try_stmt.expr->type_info->name;
- }
- if (strcmp(type_name, "__auto_type") == 0 || strcmp(type_name, "unknown") == 0)
- {
- type_name = "Result";
- }
+ if (is_slice_struct) {
+ fprintf(out,
+ "(Slice_%s){ .data = _arr.data + _start, .len = _len, .cap = "
+ "_len }; })",
+ tname);
+ } else {
+ fprintf(
+ out,
+ "(Slice_%s){ .data = _arr + _start, .len = _len, .cap = _len }; })",
+ tname);
+ }
+ break;
+ }
+ case NODE_BLOCK: {
+ int saved = defer_count;
+ fprintf(out, "({ ");
+ codegen_walker(ctx, node->block.statements, out);
+ for (int i = defer_count - 1; i >= saved; i--) {
+ codegen_node_single(ctx, defer_stack[i], out);
+ }
+ defer_count = saved;
+ fprintf(out, " })");
+ break;
+ }
+ case NODE_TRY: {
+ char *type_name = "Result";
+ if (g_current_func_ret_type) {
+ type_name = g_current_func_ret_type;
+ } else if (node->try_stmt.expr->type_info &&
+ node->try_stmt.expr->type_info->name) {
+ type_name = node->try_stmt.expr->type_info->name;
+ }
- char *search_name = type_name;
- if (strncmp(search_name, "struct ", 7) == 0)
- {
- search_name += 7;
- }
+ if (strcmp(type_name, "__auto_type") == 0 ||
+ strcmp(type_name, "unknown") == 0) {
+ type_name = "Result";
+ }
- int is_enum = 0;
- StructRef *er = ctx->parsed_enums_list;
- while (er)
- {
- if (er->node && er->node->type == NODE_ENUM &&
- strcmp(er->node->enm.name, search_name) == 0)
- {
- is_enum = 1;
- break;
- }
- er = er->next;
- }
- if (!is_enum)
- {
- ASTNode *ins = ctx->instantiated_structs;
- while (ins)
- {
- if (ins->type == NODE_ENUM && strcmp(ins->enm.name, search_name) == 0)
- {
- is_enum = 1;
- break;
- }
- ins = ins->next;
- }
- }
+ char *search_name = type_name;
+ if (strncmp(search_name, "struct ", 7) == 0) {
+ search_name += 7;
+ }
- fprintf(out, "({ ");
- emit_auto_type(ctx, node->try_stmt.expr, node->token, out);
- fprintf(out, " _try = ");
- codegen_expression(ctx, node->try_stmt.expr, out);
-
- if (is_enum)
- {
- fprintf(out,
- "; if (_try.tag == %s_Err_Tag) return (%s_Err(_try.data.Err)); "
- "_try.data.Ok; })",
- search_name, search_name);
- }
- else
- {
- fprintf(out,
- "; if (!_try.is_ok) return %s_Err(_try.err); "
- "_try.val; })",
- search_name);
- }
+ int is_enum = 0;
+ StructRef *er = ctx->parsed_enums_list;
+ while (er) {
+ if (er->node && er->node->type == NODE_ENUM &&
+ strcmp(er->node->enm.name, search_name) == 0) {
+ is_enum = 1;
break;
+ }
+ er = er->next;
}
- case NODE_RAW_STMT:
- fprintf(out, "%s", node->raw_stmt.content);
- 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",
- .current_line = node->line,
- .out = out,
- .hoist_out = ctx->hoist_out};
- found->fn(node->plugin_stmt.body, &api);
- }
- else
- {
- fprintf(out, "/* Unknown plugin: %s */\n", node->plugin_stmt.plugin_name);
- }
- break;
+ if (!is_enum) {
+ ASTNode *ins = ctx->instantiated_structs;
+ while (ins) {
+ if (ins->type == NODE_ENUM && strcmp(ins->enm.name, search_name) == 0) {
+ is_enum = 1;
+ break;
+ }
+ ins = ins->next;
+ }
}
- case NODE_EXPR_UNARY:
- if (node->unary.op && strcmp(node->unary.op, "&_rval") == 0)
- {
- fprintf(out, "({ ");
- emit_auto_type(ctx, node->unary.operand, node->token, out);
- fprintf(out, " _t = (");
- codegen_expression(ctx, node->unary.operand, out);
- fprintf(out, "); &_t; })");
- }
- else if (node->unary.op && strcmp(node->unary.op, "?") == 0)
- {
- fprintf(out, "({ ");
- emit_auto_type(ctx, node->unary.operand, node->token, out);
- fprintf(out, " _t = (");
- codegen_expression(ctx, node->unary.operand, out);
- fprintf(out, "); if (_t.tag != 0) return _t; _t.data.Ok; })");
- }
- else if (node->unary.op && strcmp(node->unary.op, "_post++") == 0)
- {
- fprintf(out, "(");
- codegen_expression(ctx, node->unary.operand, out);
- fprintf(out, "++)");
- }
- else if (node->unary.op && strcmp(node->unary.op, "_post--") == 0)
- {
- fprintf(out, "(");
- codegen_expression(ctx, node->unary.operand, out);
- fprintf(out, "--)");
- }
- else
- {
- fprintf(out, "(%s", node->unary.op);
- codegen_expression(ctx, node->unary.operand, out);
- fprintf(out, ")");
- }
- break;
- case NODE_EXPR_CAST:
- fprintf(out, "(%s)(", node->cast.target_type);
- codegen_expression(ctx, node->cast.expr, out);
- fprintf(out, ")");
- break;
- case NODE_EXPR_SIZEOF:
- if (node->size_of.target_type)
- {
- fprintf(out, "sizeof(%s)", node->size_of.target_type);
- }
- else
- {
- fprintf(out, "sizeof(");
- codegen_expression(ctx, node->size_of.expr, out);
- fprintf(out, ")");
- }
- break;
- case NODE_TYPEOF:
- if (node->size_of.target_type)
- {
- fprintf(out, "typeof(%s)", node->size_of.target_type);
- }
- else
- {
- fprintf(out, "typeof(");
- codegen_expression(ctx, node->size_of.expr, out);
- fprintf(out, ")");
- }
- break;
-
- case NODE_REFLECTION:
- {
- Type *t = node->reflection.target_type;
- if (node->reflection.kind == 0)
- { // @type_name
- char *s = 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)
- {
- fprintf(out, "((void*)0)");
- break;
- }
- fprintf(out,
- "({ static struct { char *name; char *type; unsigned long offset; } "
- "_fields_%s[] = {",
- sname);
- ASTNode *f = def->strct.fields;
- while (f)
- {
- if (f->type == NODE_FIELD)
- {
- fprintf(out, "{ \"%s\", \"%s\", __builtin_offsetof(struct %s, %s) }, ",
- f->field.name, f->field.type, sname, f->field.name);
- }
- f = f->next;
- }
- fprintf(out, "{ 0 } }; (void*)_fields_%s; })", sname);
- }
- break;
+ fprintf(out, "({ ");
+ emit_auto_type(ctx, node->try_stmt.expr, node->token, out);
+ fprintf(out, " _try = ");
+ codegen_expression(ctx, node->try_stmt.expr, out);
+
+ if (is_enum) {
+ fprintf(out,
+ "; if (_try.tag == %s_Err_Tag) return (%s_Err(_try.data.Err)); "
+ "_try.data.Ok; })",
+ search_name, search_name);
+ } else {
+ fprintf(out,
+ "; if (!_try.is_ok) return %s_Err(_try.err); "
+ "_try.val; })",
+ search_name);
}
- case NODE_EXPR_STRUCT_INIT:
- {
- const char *struct_name = node->struct_init.struct_name;
- if (strcmp(struct_name, "Self") == 0 && g_current_impl_type)
- {
- struct_name = g_current_impl_type;
- }
- fprintf(out, "(struct %s){", struct_name);
- ASTNode *f = node->struct_init.fields;
- while (f)
- {
- fprintf(out, ".%s = ", f->var_decl.name);
- codegen_expression(ctx, f->var_decl.init_expr, out);
- if (f->next)
- {
- fprintf(out, ", ");
- }
- f = f->next;
- }
- fprintf(out, "}");
- break;
+ break;
+ }
+ case NODE_RAW_STMT:
+ fprintf(out, "%s", node->raw_stmt.content);
+ 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",
+ .current_line = node->line,
+ .out = out,
+ .hoist_out = ctx->hoist_out};
+ found->fn(node->plugin_stmt.body, &api);
+ } else {
+ fprintf(out, "/* Unknown plugin: %s */\n", node->plugin_stmt.plugin_name);
}
- case NODE_EXPR_ARRAY_LITERAL:
- fprintf(out, "{");
- ASTNode *elem = node->array_literal.elements;
- int first = 1;
- while (elem)
- {
- if (!first)
- {
- fprintf(out, ", ");
- }
- codegen_expression(ctx, elem, out);
- elem = elem->next;
- first = 0;
- }
- fprintf(out, "}");
+ break;
+ }
+ case NODE_EXPR_UNARY:
+ if (node->unary.op && strcmp(node->unary.op, "&_rval") == 0) {
+ fprintf(out, "({ ");
+ emit_auto_type(ctx, node->unary.operand, node->token, out);
+ fprintf(out, " _t = (");
+ codegen_expression(ctx, node->unary.operand, out);
+ fprintf(out, "); &_t; })");
+ } else if (node->unary.op && strcmp(node->unary.op, "?") == 0) {
+ fprintf(out, "({ ");
+ emit_auto_type(ctx, node->unary.operand, node->token, out);
+ fprintf(out, " _t = (");
+ codegen_expression(ctx, node->unary.operand, out);
+ fprintf(out, "); if (_t.tag != 0) return _t; _t.data.Ok; })");
+ } else if (node->unary.op && strcmp(node->unary.op, "_post++") == 0) {
+ fprintf(out, "(");
+ codegen_expression(ctx, node->unary.operand, out);
+ fprintf(out, "++)");
+ } else if (node->unary.op && strcmp(node->unary.op, "_post--") == 0) {
+ fprintf(out, "(");
+ codegen_expression(ctx, node->unary.operand, out);
+ fprintf(out, "--)");
+ } else {
+ fprintf(out, "(%s", node->unary.op);
+ codegen_expression(ctx, node->unary.operand, out);
+ fprintf(out, ")");
+ }
+ break;
+ case NODE_EXPR_CAST:
+ fprintf(out, "(%s)(", node->cast.target_type);
+ codegen_expression(ctx, node->cast.expr, out);
+ fprintf(out, ")");
+ break;
+ case NODE_EXPR_SIZEOF:
+ if (node->size_of.target_type) {
+ fprintf(out, "sizeof(%s)", node->size_of.target_type);
+ } else {
+ fprintf(out, "sizeof(");
+ codegen_expression(ctx, node->size_of.expr, out);
+ fprintf(out, ")");
+ }
+ break;
+ case NODE_TYPEOF:
+ if (node->size_of.target_type) {
+ fprintf(out, "typeof(%s)", node->size_of.target_type);
+ } else {
+ fprintf(out, "typeof(");
+ codegen_expression(ctx, node->size_of.expr, out);
+ fprintf(out, ")");
+ }
+ break;
+
+ case NODE_REFLECTION: {
+ Type *t = node->reflection.target_type;
+ if (node->reflection.kind == 0) { // @type_name
+ char *s = type_to_string(t);
+ fprintf(out, "\"%s\"", s);
+ free(s);
+ } else { // @fields
+ if (t->kind != TYPE_STRUCT || !t->name) {
+ fprintf(out, "((void*)0)");
break;
- case NODE_TERNARY:
- fprintf(out, "((");
- codegen_expression(ctx, node->ternary.cond, out);
- fprintf(out, ") ? (");
- codegen_expression(ctx, node->ternary.true_expr, out);
- fprintf(out, ") : (");
- codegen_expression(ctx, node->ternary.false_expr, out);
- fprintf(out, "))");
+ }
+ char *sname = t->name;
+ // Find definition
+ ASTNode *def = find_struct_def(ctx, sname);
+ if (!def) {
+ fprintf(out, "((void*)0)");
break;
- case NODE_AWAIT:
- {
- char *ret_type = "void*";
- int free_ret = 0;
- if (node->type_info)
- {
- char *t = type_to_string(node->type_info);
- if (t)
- {
- ret_type = t;
- free_ret = 1;
- }
- }
- else if (node->resolved_type)
- {
- ret_type = node->resolved_type;
- }
+ }
+
+ fprintf(
+ out,
+ "({ static struct { char *name; char *type; unsigned long offset; } "
+ "_fields_%s[] = {",
+ sname);
+ ASTNode *f = def->strct.fields;
+ while (f) {
+ if (f->type == NODE_FIELD) {
+ fprintf(out,
+ "{ \"%s\", \"%s\", __builtin_offsetof(struct %s, %s) }, ",
+ f->field.name, f->field.type, sname, f->field.name);
+ }
+ f = f->next;
+ }
+ fprintf(out, "{ 0 } }; (void*)_fields_%s; })", sname);
+ }
+ break;
+ }
+ case NODE_EXPR_STRUCT_INIT: {
+ const char *struct_name = node->struct_init.struct_name;
+ if (strcmp(struct_name, "Self") == 0 && g_current_impl_type) {
+ struct_name = g_current_impl_type;
+ }
+ fprintf(out, "(struct %s){", struct_name);
+ ASTNode *f = node->struct_init.fields;
+ while (f) {
+ fprintf(out, ".%s = ", f->var_decl.name);
+ codegen_expression(ctx, f->var_decl.init_expr, out);
+ if (f->next) {
+ fprintf(out, ", ");
+ }
+ f = f->next;
+ }
+ fprintf(out, "}");
+ break;
+ }
+ case NODE_EXPR_ARRAY_LITERAL:
+ fprintf(out, "{");
+ ASTNode *elem = node->array_literal.elements;
+ int first = 1;
+ while (elem) {
+ if (!first) {
+ fprintf(out, ", ");
+ }
+ codegen_expression(ctx, elem, out);
+ elem = elem->next;
+ first = 0;
+ }
+ fprintf(out, "}");
+ break;
+ case NODE_TERNARY:
+ fprintf(out, "((");
+ codegen_expression(ctx, node->ternary.cond, out);
+ fprintf(out, ") ? (");
+ codegen_expression(ctx, node->ternary.true_expr, out);
+ fprintf(out, ") : (");
+ codegen_expression(ctx, node->ternary.false_expr, out);
+ fprintf(out, "))");
+ break;
+ case NODE_AWAIT: {
+ char *ret_type = "void*";
+ int free_ret = 0;
+ if (node->type_info) {
+ char *t = type_to_string(node->type_info);
+ if (t) {
+ ret_type = t;
+ free_ret = 1;
+ }
+ } else if (node->resolved_type) {
+ ret_type = node->resolved_type;
+ }
- if (strcmp(ret_type, "Async") == 0 || strcmp(ret_type, "void*") == 0)
- {
- char *inf = infer_type(ctx, node);
- if (inf && strcmp(inf, "Async") != 0 && strcmp(inf, "void*") != 0)
- {
- if (free_ret)
- {
- free(ret_type);
- }
- ret_type = inf;
- free_ret = 0;
- }
+ if (strcmp(ret_type, "Async") == 0 || strcmp(ret_type, "void*") == 0) {
+ char *inf = infer_type(ctx, node);
+ if (inf && strcmp(inf, "Async") != 0 && strcmp(inf, "void*") != 0) {
+ if (free_ret) {
+ free(ret_type);
}
+ ret_type = inf;
+ free_ret = 0;
+ }
+ }
- int needs_long_cast = 0;
- int returns_struct = 0;
- if (strstr(ret_type, "*") == NULL && strcmp(ret_type, "string") != 0 &&
- strcmp(ret_type, "void") != 0 && strcmp(ret_type, "Async") != 0)
- {
- if (strcmp(ret_type, "int") != 0 && strcmp(ret_type, "bool") != 0 &&
- strcmp(ret_type, "char") != 0 && strcmp(ret_type, "float") != 0 &&
- strcmp(ret_type, "double") != 0 && strcmp(ret_type, "long") != 0 &&
- strcmp(ret_type, "usize") != 0 && strcmp(ret_type, "isize") != 0 &&
- strncmp(ret_type, "uint", 4) != 0 && strncmp(ret_type, "int", 3) != 0)
- {
- returns_struct = 1;
- }
- else
- {
- needs_long_cast = 1;
- }
- if (strncmp(ret_type, "struct", 6) == 0)
- {
- returns_struct = 1;
- }
- }
+ int needs_long_cast = 0;
+ int returns_struct = 0;
+ if (strstr(ret_type, "*") == NULL && strcmp(ret_type, "string") != 0 &&
+ strcmp(ret_type, "void") != 0 && strcmp(ret_type, "Async") != 0) {
+ if (strcmp(ret_type, "int") != 0 && strcmp(ret_type, "bool") != 0 &&
+ strcmp(ret_type, "char") != 0 && strcmp(ret_type, "float") != 0 &&
+ strcmp(ret_type, "double") != 0 && strcmp(ret_type, "long") != 0 &&
+ strcmp(ret_type, "usize") != 0 && strcmp(ret_type, "isize") != 0 &&
+ strncmp(ret_type, "uint", 4) != 0 &&
+ strncmp(ret_type, "int", 3) != 0) {
+ returns_struct = 1;
+ } else {
+ needs_long_cast = 1;
+ }
+ if (strncmp(ret_type, "struct", 6) == 0) {
+ returns_struct = 1;
+ }
+ }
- fprintf(out, "({ Async _a = ");
- codegen_expression(ctx, node->unary.operand, out);
- fprintf(out, "; void* _r; pthread_join(_a.thread, &_r); ");
- if (strcmp(ret_type, "void") == 0)
- {
- fprintf(out, "})");
- }
- else
- {
- if (returns_struct)
- {
- fprintf(out, "%s _val = *(%s*)_r; free(_r); _val; })", ret_type, ret_type);
- }
- else
- {
- if (needs_long_cast)
- {
- fprintf(out, "(%s)(long)_r; })", ret_type);
- }
- else
- {
- fprintf(out, "(%s)_r; })", ret_type);
- }
- }
- }
- if (free_ret)
- {
- free(ret_type);
- }
- break;
+ fprintf(out, "({ Async _a = ");
+ codegen_expression(ctx, node->unary.operand, out);
+ fprintf(out, "; void* _r; pthread_join(_a.thread, &_r); ");
+ if (strcmp(ret_type, "void") == 0) {
+ fprintf(out, "})");
+ } else {
+ if (returns_struct) {
+ fprintf(out, "%s _val = *(%s*)_r; free(_r); _val; })", ret_type,
+ ret_type);
+ } else {
+ if (needs_long_cast) {
+ fprintf(out, "(%s)(long)_r; })", ret_type);
+ } else {
+ fprintf(out, "(%s)_r; })", ret_type);
+ }
+ }
}
- default:
- break;
+ if (free_ret) {
+ free(ret_type);
}
+ break;
+ }
+ default:
+ break;
+ }
}
-void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
-{
- if (!node)
- {
- return;
+void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) {
+ if (!node) {
+ return;
+ }
+ switch (node->type) {
+ case NODE_MATCH:
+ codegen_match_internal(ctx, node, out, 0); // 0 = statement context
+ fprintf(out, ";\n");
+ break;
+ case NODE_FUNCTION:
+ if (!node->func.body) {
+ break;
}
- switch (node->type)
- {
- case NODE_MATCH:
- codegen_match_internal(ctx, node, out, 0); // 0 = statement context
- fprintf(out, ";\n");
- break;
- case NODE_FUNCTION:
- if (!node->func.body)
- {
- break;
- }
- if (node->func.is_async)
- {
- fprintf(out, "struct %s_Args {\n", node->func.name);
- char *args_copy = xstrdup(node->func.args);
- char *token = strtok(args_copy, ",");
- int arg_count = 0;
- char **arg_names = xmalloc(32 * sizeof(char *));
-
- while (token)
- {
- while (*token == ' ')
- {
- token++; // trim leading
- }
- char *last_space = strrchr(token, ' ');
- if (last_space)
- {
- *last_space = 0;
- char *type = token;
- char *name = last_space + 1;
- fprintf(out, "%s %s;\n", type, name);
-
- arg_names[arg_count++] = xstrdup(name);
- }
- token = strtok(NULL, ",");
- }
- free(args_copy);
- fprintf(out, "};\n");
-
- fprintf(out, "void* _runner_%s(void* _args)\n", node->func.name);
- fprintf(out, "{\n");
- fprintf(out, " struct %s_Args* args = (struct %s_Args*)_args;\n", node->func.name,
- node->func.name);
-
- // Determine mechanism: struct/large-type? -> malloc; primitive -> cast
- int returns_struct = 0;
- char *rt = node->func.ret_type;
- if (strcmp(rt, "void") != 0 && strcmp(rt, "Async") != 0)
- {
- if (strstr(rt, "*") == NULL && strcmp(rt, "string") != 0 &&
- strcmp(rt, "int") != 0 && strcmp(rt, "bool") != 0 && strcmp(rt, "char") != 0 &&
- strcmp(rt, "float") != 0 && strcmp(rt, "double") != 0 &&
- strcmp(rt, "long") != 0 && strcmp(rt, "usize") != 0 &&
- strcmp(rt, "isize") != 0 && strncmp(rt, "uint", 4) != 0 &&
- strncmp(rt, "int", 3) != 0)
- {
- returns_struct = 1;
- }
- }
-
- // Call Impl
- if (returns_struct)
- {
- fprintf(out, " %s *res_ptr = malloc(sizeof(%s));\n", rt, rt);
- fprintf(out, " *res_ptr = ");
- }
- else if (strcmp(rt, "void") != 0 && strcmp(rt, "Async") != 0)
- {
- fprintf(out, " %s res = ", rt);
- }
- else
- {
- fprintf(out, " ");
- }
-
- fprintf(out, "_impl_%s(", node->func.name);
- for (int i = 0; i < arg_count; i++)
- {
- fprintf(out, "%sargs->%s", i > 0 ? ", " : "", arg_names[i]);
- }
- fprintf(out, ");\n");
- fprintf(out, " free(args);\n");
-
- if (returns_struct)
- {
- fprintf(out, " return (void*)res_ptr;\n");
- }
- else if (strcmp(rt, "void") != 0)
- {
- fprintf(out, " return (void*)(long)res;\n");
- }
- else
- {
- fprintf(out, " return NULL;\n");
- }
- fprintf(out, "}\n");
-
- fprintf(out, "%s _impl_%s(%s)\n", node->func.ret_type, node->func.name,
- node->func.args);
- fprintf(out, "{\n");
- defer_count = 0;
- codegen_walker(ctx, node->func.body, out);
- for (int i = defer_count - 1; i >= 0; i--)
- {
- codegen_node_single(ctx, defer_stack[i], out);
- }
- fprintf(out, "}\n");
-
- // 4. Define Public Wrapper (Spawns Thread)
- fprintf(out, "Async %s(%s)\n", node->func.name, node->func.args);
- fprintf(out, "{\n");
- fprintf(out, " struct %s_Args* args = malloc(sizeof(struct %s_Args));\n",
- node->func.name, node->func.name);
- for (int i = 0; i < arg_count; i++)
- {
- fprintf(out, " args->%s = %s;\n", arg_names[i], arg_names[i]);
- }
+ if (node->func.is_async) {
+ fprintf(out, "struct %s_Args {\n", node->func.name);
+ char *args_copy = xstrdup(node->func.args);
+ char *token = strtok(args_copy, ",");
+ int arg_count = 0;
+ char **arg_names = xmalloc(32 * sizeof(char *));
+
+ while (token) {
+ while (*token == ' ') {
+ token++; // trim leading
+ }
+ char *last_space = strrchr(token, ' ');
+ if (last_space) {
+ *last_space = 0;
+ char *type = token;
+ char *name = last_space + 1;
+ fprintf(out, "%s %s;\n", type, name);
+
+ arg_names[arg_count++] = xstrdup(name);
+ }
+ token = strtok(NULL, ",");
+ }
+ free(args_copy);
+ fprintf(out, "};\n");
+
+ fprintf(out, "void* _runner_%s(void* _args)\n", node->func.name);
+ fprintf(out, "{\n");
+ fprintf(out, " struct %s_Args* args = (struct %s_Args*)_args;\n",
+ node->func.name, node->func.name);
+
+ // Determine mechanism: struct/large-type? -> malloc; primitive -> cast
+ int returns_struct = 0;
+ char *rt = node->func.ret_type;
+ if (strcmp(rt, "void") != 0 && strcmp(rt, "Async") != 0) {
+ if (strstr(rt, "*") == NULL && strcmp(rt, "string") != 0 &&
+ strcmp(rt, "int") != 0 && strcmp(rt, "bool") != 0 &&
+ strcmp(rt, "char") != 0 && strcmp(rt, "float") != 0 &&
+ strcmp(rt, "double") != 0 && strcmp(rt, "long") != 0 &&
+ strcmp(rt, "usize") != 0 && strcmp(rt, "isize") != 0 &&
+ strncmp(rt, "uint", 4) != 0 && strncmp(rt, "int", 3) != 0) {
+ returns_struct = 1;
+ }
+ }
+
+ // Call Impl
+ if (returns_struct) {
+ fprintf(out, " %s *res_ptr = malloc(sizeof(%s));\n", rt, rt);
+ fprintf(out, " *res_ptr = ");
+ } else if (strcmp(rt, "void") != 0 && strcmp(rt, "Async") != 0) {
+ fprintf(out, " %s res = ", rt);
+ } else {
+ fprintf(out, " ");
+ }
+
+ fprintf(out, "_impl_%s(", node->func.name);
+ for (int i = 0; i < arg_count; i++) {
+ fprintf(out, "%sargs->%s", i > 0 ? ", " : "", arg_names[i]);
+ }
+ fprintf(out, ");\n");
+ fprintf(out, " free(args);\n");
+
+ if (returns_struct) {
+ fprintf(out, " return (void*)res_ptr;\n");
+ } else if (strcmp(rt, "void") != 0) {
+ fprintf(out, " return (void*)(long)res;\n");
+ } else {
+ fprintf(out, " return NULL;\n");
+ }
+ fprintf(out, "}\n");
+
+ fprintf(out, "%s _impl_%s(%s)\n", node->func.ret_type, node->func.name,
+ node->func.args);
+ fprintf(out, "{\n");
+ defer_count = 0;
+ codegen_walker(ctx, node->func.body, out);
+ for (int i = defer_count - 1; i >= 0; i--) {
+ codegen_node_single(ctx, defer_stack[i], out);
+ }
+ fprintf(out, "}\n");
+
+ // 4. Define Public Wrapper (Spawns Thread)
+ fprintf(out, "Async %s(%s)\n", node->func.name, node->func.args);
+ fprintf(out, "{\n");
+ fprintf(out,
+ " struct %s_Args* args = malloc(sizeof(struct %s_Args));\n",
+ node->func.name, node->func.name);
+ for (int i = 0; i < arg_count; i++) {
+ fprintf(out, " args->%s = %s;\n", arg_names[i], arg_names[i]);
+ }
+
+ fprintf(out, " pthread_t th;\n");
+ fprintf(out, " pthread_create(&th, NULL, _runner_%s, args);\n",
+ node->func.name);
+ fprintf(out, " return (Async){.thread=th, .result=NULL};\n");
+ fprintf(out, "}\n");
+
+ break;
+ }
- fprintf(out, " pthread_t th;\n");
- fprintf(out, " pthread_create(&th, NULL, _runner_%s, args);\n", node->func.name);
- fprintf(out, " return (Async){.thread=th, .result=NULL};\n");
- fprintf(out, "}\n");
+ defer_count = 0;
+ fprintf(out, "\n");
- break;
+ // Emit GCC attributes before function
+ {
+ int has_attrs = node->func.constructor || node->func.destructor ||
+ node->func.noinline || node->func.unused ||
+ node->func.weak || node->func.cold || node->func.hot ||
+ node->func.noreturn || node->func.pure ||
+ node->func.section;
+ if (has_attrs) {
+ fprintf(out, "__attribute__((");
+ int first = 1;
+#define EMIT_ATTR(cond, name) \
+ if (cond) { \
+ if (!first) \
+ fprintf(out, ", "); \
+ fprintf(out, name); \
+ first = 0; \
+ }
+ EMIT_ATTR(node->func.constructor, "constructor");
+ EMIT_ATTR(node->func.destructor, "destructor");
+ EMIT_ATTR(node->func.noinline, "noinline");
+ EMIT_ATTR(node->func.unused, "unused");
+ EMIT_ATTR(node->func.weak, "weak");
+ EMIT_ATTR(node->func.cold, "cold");
+ EMIT_ATTR(node->func.hot, "hot");
+ EMIT_ATTR(node->func.noreturn, "noreturn");
+ EMIT_ATTR(node->func.pure, "pure");
+ if (node->func.section) {
+ if (!first) {
+ fprintf(out, ", ");
+ }
+ fprintf(out, "section(\"%s%s%s\")", SEGMENT_NAME_PREFIX,
+ node->func.section, SEGMENT_NAME_SUFFIX);
}
-
- defer_count = 0;
- fprintf(out, "\n");
-
- // Emit GCC attributes before function
- {
- int has_attrs = node->func.constructor || node->func.destructor ||
- node->func.noinline || node->func.unused || node->func.weak ||
- node->func.cold || node->func.hot || node->func.noreturn ||
- node->func.pure || node->func.section;
- if (has_attrs)
- {
- fprintf(out, "__attribute__((");
- int first = 1;
-#define EMIT_ATTR(cond, name) \
- if (cond) \
- { \
- if (!first) \
- fprintf(out, ", "); \
- fprintf(out, name); \
- first = 0; \
- }
- EMIT_ATTR(node->func.constructor, "constructor");
- EMIT_ATTR(node->func.destructor, "destructor");
- EMIT_ATTR(node->func.noinline, "noinline");
- EMIT_ATTR(node->func.unused, "unused");
- EMIT_ATTR(node->func.weak, "weak");
- EMIT_ATTR(node->func.cold, "cold");
- EMIT_ATTR(node->func.hot, "hot");
- EMIT_ATTR(node->func.noreturn, "noreturn");
- EMIT_ATTR(node->func.pure, "pure");
- if (node->func.section)
- {
- if (!first)
- {
- fprintf(out, ", ");
- }
- fprintf(out, "section(\"%s%s%s\")", SEGMENT_NAME_PREFIX, node->func.section, SEGMENT_NAME_SUFFIX);
- }
#undef EMIT_ATTR
- fprintf(out, ")) ");
- }
- }
-
- if (node->func.is_inline)
- {
- fprintf(out, "inline ");
- }
- fprintf(out, "%s %s(%s)\n", node->func.ret_type, node->func.name, node->func.args);
- fprintf(out, "{\n");
- char *prev_ret = g_current_func_ret_type;
- g_current_func_ret_type = node->func.ret_type;
- codegen_walker(ctx, node->func.body, out);
- for (int i = defer_count - 1; i >= 0; i--)
- {
- codegen_node_single(ctx, defer_stack[i], out);
- }
- g_current_func_ret_type = prev_ret;
- fprintf(out, "}\n");
- break;
-
- case NODE_DEFER:
- if (defer_count < MAX_DEFER)
- {
- defer_stack[defer_count++] = node->defer_stmt.stmt;
- }
- break;
- case NODE_IMPL:
- g_current_impl_type = node->impl.struct_name;
- codegen_walker(ctx, node->impl.methods, out);
- g_current_impl_type = NULL;
- break;
- case NODE_IMPL_TRAIT:
- g_current_impl_type = node->impl_trait.target_type;
- codegen_walker(ctx, node->impl_trait.methods, out);
-
- if (strcmp(node->impl_trait.trait_name, "Drop") == 0)
- {
- char *tname = node->impl_trait.target_type;
- fprintf(out, "\n// RAII Glue\n");
- fprintf(out, "void %s_Drop_glue(%s *self) {\n", tname, tname);
- fprintf(out, " %s_Drop_drop(self);\n", tname);
- fprintf(out, "}\n");
- }
- g_current_impl_type = NULL;
- break;
- case NODE_DESTRUCT_VAR:
- {
- int id = tmp_counter++;
- fprintf(out, " ");
- emit_auto_type(ctx, node->destruct.init_expr, node->token, out);
- fprintf(out, " _tmp_%d = ", id);
- codegen_expression(ctx, node->destruct.init_expr, out);
- fprintf(out, ";\n");
-
- if (node->destruct.is_guard)
- {
- // var Some(val) = opt else ...
- char *variant = node->destruct.guard_variant;
- char *check = "val"; // field to access
-
- if (strcmp(variant, "Some") == 0)
- {
- fprintf(out, " if (!_tmp_%d.is_some) {\n", id);
- }
- else if (strcmp(variant, "Ok") == 0)
- {
- fprintf(out, " if (!_tmp_%d.is_ok) {\n", id);
- }
- else if (strcmp(variant, "Err") == 0)
- {
- fprintf(out, " if (_tmp_%d.is_ok) {\n", id); // Err if NOT ok
- check = "err";
- }
- else
- {
- // Generic guard? Assume .is_variant present?
- fprintf(out, " if (!_tmp_%d.is_%s) {\n", id, variant);
- }
+ fprintf(out, ")) ");
+ }
+ }
- // Else block
- codegen_walker(ctx, node->destruct.else_block->block.statements, out);
- fprintf(out, " }\n");
+ if (node->func.is_inline) {
+ fprintf(out, "inline ");
+ }
+ fprintf(out, "%s %s(%s)\n", node->func.ret_type, node->func.name,
+ node->func.args);
+ fprintf(out, "{\n");
+ char *prev_ret = g_current_func_ret_type;
+ g_current_func_ret_type = node->func.ret_type;
+ codegen_walker(ctx, node->func.body, out);
+ for (int i = defer_count - 1; i >= 0; i--) {
+ codegen_node_single(ctx, defer_stack[i], out);
+ }
+ g_current_func_ret_type = prev_ret;
+ fprintf(out, "}\n");
+ break;
- // Bind value
- if (strstr(g_config.cc, "tcc"))
- {
- fprintf(out, " __typeof__(_tmp_%d.%s) %s = _tmp_%d.%s;\n", id, check,
- node->destruct.names[0], id, check);
- }
- else
- {
- fprintf(out, " __auto_type %s = _tmp_%d.%s;\n", node->destruct.names[0], id,
- check);
- }
- }
- else
- {
- for (int i = 0; i < node->destruct.count; i++)
- {
- if (node->destruct.is_struct_destruct)
- {
- char *field = node->destruct.field_names ? node->destruct.field_names[i]
- : node->destruct.names[i];
- if (strstr(g_config.cc, "tcc"))
- {
- fprintf(out, " __typeof__(_tmp_%d.%s) %s = _tmp_%d.%s;\n", id, field,
- node->destruct.names[i], id, field);
- }
- else
- {
- fprintf(out, " __auto_type %s = _tmp_%d.%s;\n", node->destruct.names[i],
- id, field);
- }
- }
- else
- {
- if (strstr(g_config.cc, "tcc"))
- {
- fprintf(out, " __typeof__(_tmp_%d.v%d) %s = _tmp_%d.v%d;\n", id, i,
- node->destruct.names[i], id, i);
- }
- else
- {
- fprintf(out, " __auto_type %s = _tmp_%d.v%d;\n", node->destruct.names[i],
- id, i);
- }
- }
- }
- }
- break;
+ case NODE_DEFER:
+ if (defer_count < MAX_DEFER) {
+ defer_stack[defer_count++] = node->defer_stmt.stmt;
+ }
+ break;
+ case NODE_IMPL:
+ g_current_impl_type = node->impl.struct_name;
+ codegen_walker(ctx, node->impl.methods, out);
+ g_current_impl_type = NULL;
+ break;
+ case NODE_IMPL_TRAIT:
+ g_current_impl_type = node->impl_trait.target_type;
+ codegen_walker(ctx, node->impl_trait.methods, out);
+
+ if (strcmp(node->impl_trait.trait_name, "Drop") == 0) {
+ char *tname = node->impl_trait.target_type;
+ fprintf(out, "\n// RAII Glue\n");
+ fprintf(out, "void %s_Drop_glue(%s *self) {\n", tname, tname);
+ fprintf(out, " %s_Drop_drop(self);\n", tname);
+ fprintf(out, "}\n");
+ }
+ g_current_impl_type = NULL;
+ break;
+ case NODE_DESTRUCT_VAR: {
+ int id = tmp_counter++;
+ fprintf(out, " ");
+ emit_auto_type(ctx, node->destruct.init_expr, node->token, out);
+ fprintf(out, " _tmp_%d = ", id);
+ codegen_expression(ctx, node->destruct.init_expr, out);
+ fprintf(out, ";\n");
+
+ if (node->destruct.is_guard) {
+ // var Some(val) = opt else ...
+ char *variant = node->destruct.guard_variant;
+ char *check = "val"; // field to access
+
+ if (strcmp(variant, "Some") == 0) {
+ fprintf(out, " if (!_tmp_%d.is_some) {\n", id);
+ } else if (strcmp(variant, "Ok") == 0) {
+ fprintf(out, " if (!_tmp_%d.is_ok) {\n", id);
+ } else if (strcmp(variant, "Err") == 0) {
+ fprintf(out, " if (_tmp_%d.is_ok) {\n", id); // Err if NOT ok
+ check = "err";
+ } else {
+ // Generic guard? Assume .is_variant present?
+ fprintf(out, " if (!_tmp_%d.is_%s) {\n", id, variant);
+ }
+
+ // Else block
+ codegen_walker(ctx, node->destruct.else_block->block.statements, out);
+ fprintf(out, " }\n");
+
+ // Bind value
+ if (strstr(g_config.cc, "tcc")) {
+ fprintf(out, " __typeof__(_tmp_%d.%s) %s = _tmp_%d.%s;\n", id, check,
+ node->destruct.names[0], id, check);
+ } else {
+ fprintf(out, " __auto_type %s = _tmp_%d.%s;\n",
+ node->destruct.names[0], id, check);
+ }
+ } else {
+ for (int i = 0; i < node->destruct.count; i++) {
+ if (node->destruct.is_struct_destruct) {
+ char *field = node->destruct.field_names
+ ? node->destruct.field_names[i]
+ : node->destruct.names[i];
+ if (strstr(g_config.cc, "tcc")) {
+ fprintf(out, " __typeof__(_tmp_%d.%s) %s = _tmp_%d.%s;\n", id,
+ field, node->destruct.names[i], id, field);
+ } else {
+ fprintf(out, " __auto_type %s = _tmp_%d.%s;\n",
+ node->destruct.names[i], id, field);
+ }
+ } else {
+ if (strstr(g_config.cc, "tcc")) {
+ fprintf(out, " __typeof__(_tmp_%d.v%d) %s = _tmp_%d.v%d;\n", id,
+ i, node->destruct.names[i], id, i);
+ } else {
+ fprintf(out, " __auto_type %s = _tmp_%d.v%d;\n",
+ node->destruct.names[i], id, i);
+ }
+ }
+ }
+ }
+ break;
+ }
+ case NODE_BLOCK: {
+ int saved = defer_count;
+ fprintf(out, " {\n");
+ codegen_walker(ctx, node->block.statements, out);
+ for (int i = defer_count - 1; i >= saved; i--) {
+ codegen_node_single(ctx, defer_stack[i], out);
+ }
+ defer_count = saved;
+ fprintf(out, " }\n");
+ break;
+ }
+ case NODE_VAR_DECL:
+ fprintf(out, " ");
+ if (node->var_decl.is_static) {
+ fprintf(out, "static ");
+ }
+ if (node->var_decl.is_autofree) {
+ fprintf(out, "__attribute__((cleanup(_z_autofree_impl))) ");
}
- case NODE_BLOCK:
{
- int saved = defer_count;
- fprintf(out, " {\n");
- codegen_walker(ctx, node->block.statements, out);
- for (int i = defer_count - 1; i >= saved; i--)
- {
- codegen_node_single(ctx, defer_stack[i], out);
- }
- defer_count = saved;
- fprintf(out, " }\n");
- break;
+ char *tname = NULL;
+ Type *tinfo = node->var_decl.type_info;
+ if (tinfo && tinfo->name) {
+ tname = tinfo->name;
+ } else if (node->var_decl.type_str &&
+ strcmp(node->var_decl.type_str, "__auto_type") != 0) {
+ tname = node->var_decl.type_str;
+ }
+
+ if (tname) {
+ ASTNode *def = find_struct_def(ctx, tname);
+ if (def && def->type_info && def->type_info->has_drop) {
+ fprintf(out, "__attribute__((cleanup(%s_Drop_glue))) ", tname);
+ }
+ }
}
- case NODE_VAR_DECL:
- fprintf(out, " ");
- if (node->var_decl.is_static)
- {
- fprintf(out, "static ");
- }
- if (node->var_decl.is_autofree)
- {
- fprintf(out, "__attribute__((cleanup(_z_autofree_impl))) ");
- }
- {
- char *tname = NULL;
- Type *tinfo = node->var_decl.type_info;
- if (tinfo && tinfo->name)
- {
- tname = tinfo->name;
- }
- else if (node->var_decl.type_str && strcmp(node->var_decl.type_str, "__auto_type") != 0)
- {
- tname = node->var_decl.type_str;
- }
-
- if (tname)
- {
- ASTNode *def = find_struct_def(ctx, tname);
- if (def && def->type_info && def->type_info->has_drop)
- {
- fprintf(out, "__attribute__((cleanup(%s_Drop_glue))) ", tname);
- }
- }
- }
- if (node->var_decl.type_str && strcmp(node->var_decl.type_str, "__auto_type") != 0)
- {
- emit_var_decl_type(ctx, out, node->var_decl.type_str, node->var_decl.name);
- add_symbol(ctx, node->var_decl.name, node->var_decl.type_str, node->var_decl.type_info);
- if (node->var_decl.init_expr)
- {
- fprintf(out, " = ");
- codegen_expression(ctx, node->var_decl.init_expr, out);
- }
- fprintf(out, ";\n");
- }
- else
- {
- char *inferred = NULL;
- if (node->var_decl.init_expr)
- {
- inferred = infer_type(ctx, node->var_decl.init_expr);
- }
-
- if (inferred && strcmp(inferred, "__auto_type") != 0)
- {
- emit_var_decl_type(ctx, out, inferred, node->var_decl.name);
- add_symbol(ctx, node->var_decl.name, inferred, NULL);
- }
- else
- {
- emit_auto_type(ctx, node->var_decl.init_expr, node->token, out);
- fprintf(out, " %s", node->var_decl.name);
-
- if (inferred)
- {
- add_symbol(ctx, node->var_decl.name, inferred, NULL);
- }
- else
- {
- // Here we are cooked.
- }
- }
-
- fprintf(out, " = ");
- codegen_expression(ctx, node->var_decl.init_expr, out);
- fprintf(out, ";\n");
- }
- break;
- case NODE_CONST:
- fprintf(out, " const ");
- if (node->var_decl.type_str)
- {
- fprintf(out, "%s %s", node->var_decl.type_str, node->var_decl.name);
- }
- else
- {
- emit_auto_type(ctx, node->var_decl.init_expr, node->token, out);
- fprintf(out, " %s", node->var_decl.name);
- }
+ if (node->var_decl.type_str &&
+ strcmp(node->var_decl.type_str, "__auto_type") != 0) {
+ emit_var_decl_type(ctx, out, node->var_decl.type_str,
+ node->var_decl.name);
+ add_symbol(ctx, node->var_decl.name, node->var_decl.type_str,
+ node->var_decl.type_info);
+ if (node->var_decl.init_expr) {
fprintf(out, " = ");
codegen_expression(ctx, node->var_decl.init_expr, out);
- fprintf(out, ";\n");
- break;
- case NODE_FIELD:
- if (node->field.bit_width > 0)
- {
- fprintf(out, " %s %s : %d;\n", node->field.type, node->field.name,
- node->field.bit_width);
- }
- else
- {
- fprintf(out, " ");
- emit_var_decl_type(ctx, out, node->field.type, node->field.name);
- fprintf(out, ";\n");
- }
- break;
- case NODE_IF:
- fprintf(out, "if (");
- codegen_expression(ctx, node->if_stmt.condition, out);
- fprintf(out, ") ");
- codegen_node_single(ctx, node->if_stmt.then_body, out);
- if (node->if_stmt.else_body)
- {
- fprintf(out, " else ");
- codegen_node_single(ctx, node->if_stmt.else_body, out);
- }
- break;
- case NODE_UNLESS:
- fprintf(out, "if (!(");
- codegen_expression(ctx, node->unless_stmt.condition, out);
- fprintf(out, ")) ");
- codegen_node_single(ctx, node->unless_stmt.body, out);
- break;
- case NODE_GUARD:
- fprintf(out, "if (!(");
- codegen_expression(ctx, node->guard_stmt.condition, out);
- fprintf(out, ")) ");
- codegen_node_single(ctx, node->guard_stmt.body, out);
- break;
- case NODE_WHILE:
- fprintf(out, "while (");
- codegen_expression(ctx, node->while_stmt.condition, out);
- fprintf(out, ") ");
- codegen_node_single(ctx, node->while_stmt.body, out);
- break;
- case NODE_FOR:
- fprintf(out, "for (");
- if (node->for_stmt.init)
- {
- if (node->for_stmt.init->type == NODE_VAR_DECL)
- {
- ASTNode *v = node->for_stmt.init;
- if (v->var_decl.type_str && strcmp(v->var_decl.type_str, "__auto_type") != 0)
- {
- fprintf(out, "%s %s = (%s)(", v->var_decl.type_str, v->var_decl.name,
- v->var_decl.type_str);
- codegen_expression(ctx, v->var_decl.init_expr, out);
- fprintf(out, ")");
- }
- else
- {
- emit_auto_type(ctx, v->var_decl.init_expr, v->token, out);
- fprintf(out, " %s = ", v->var_decl.name);
- codegen_expression(ctx, v->var_decl.init_expr, out);
- }
- }
- else
- {
- codegen_expression(ctx, node->for_stmt.init, out);
- }
- }
- fprintf(out, "; ");
- if (node->for_stmt.condition)
- {
- codegen_expression(ctx, node->for_stmt.condition, out);
- }
- fprintf(out, "; ");
- if (node->for_stmt.step)
- {
- codegen_expression(ctx, node->for_stmt.step, out);
- }
- fprintf(out, ") ");
- codegen_node_single(ctx, node->for_stmt.body, out);
- break;
- case NODE_BREAK:
- if (node->break_stmt.target_label)
- {
- fprintf(out, "goto __break_%s;\n", node->break_stmt.target_label);
- }
- else
- {
- fprintf(out, "break;\n");
- }
- break;
- case NODE_CONTINUE:
- if (node->continue_stmt.target_label)
- {
- fprintf(out, "goto __continue_%s;\n", node->continue_stmt.target_label);
- }
- else
- {
- fprintf(out, "continue;\n");
- }
- break;
- case NODE_GOTO:
- if (node->goto_stmt.goto_expr)
- {
- // Computed goto: goto *expr;
- fprintf(out, "goto *(");
- codegen_expression(ctx, node->goto_stmt.goto_expr, out);
- fprintf(out, ");\n");
- }
- else
- {
- fprintf(out, "goto %s;\n", node->goto_stmt.label_name);
- }
- break;
- case NODE_LABEL:
- fprintf(out, "%s:;\n", node->label_stmt.label_name);
- break;
- case NODE_DO_WHILE:
- fprintf(out, "do ");
- codegen_node_single(ctx, node->do_while_stmt.body, out);
- fprintf(out, " while (");
- codegen_expression(ctx, node->do_while_stmt.condition, out);
- fprintf(out, ");\n");
- break;
- // Loop constructs: loop, repeat, for-in
- case NODE_LOOP:
- // loop { ... } => while (1) { ... }
- fprintf(out, "while (1) ");
- codegen_node_single(ctx, node->loop_stmt.body, out);
- break;
- case NODE_REPEAT:
- fprintf(out, "for (int _rpt_i = 0; _rpt_i < (%s); _rpt_i++) ", node->repeat_stmt.count);
- codegen_node_single(ctx, node->repeat_stmt.body, out);
- break;
- case NODE_FOR_RANGE:
- fprintf(out, "for (");
- if (strstr(g_config.cc, "tcc"))
- {
- fprintf(out, "__typeof__((");
- codegen_expression(ctx, node->for_range.start, out);
- fprintf(out, ")) %s = ", node->for_range.var_name);
- }
- else
- {
- fprintf(out, "__auto_type %s = ", node->for_range.var_name);
- }
- codegen_expression(ctx, node->for_range.start, out);
- fprintf(out, "; %s < ", node->for_range.var_name);
- codegen_expression(ctx, node->for_range.end, out);
- fprintf(out, "; %s", node->for_range.var_name);
- if (node->for_range.step)
- {
- fprintf(out, " += %s) ", node->for_range.step);
- }
- else
- {
- fprintf(out, "++) ");
- }
- codegen_node_single(ctx, node->for_range.body, out);
- break;
- case NODE_ASM:
- {
- int is_extended = (node->asm_stmt.num_outputs > 0 || node->asm_stmt.num_inputs > 0 ||
- node->asm_stmt.num_clobbers > 0);
+ }
+ fprintf(out, ";\n");
+ } else {
+ char *inferred = NULL;
+ if (node->var_decl.init_expr) {
+ inferred = infer_type(ctx, node->var_decl.init_expr);
+ }
+
+ if (inferred && strcmp(inferred, "__auto_type") != 0) {
+ emit_var_decl_type(ctx, out, inferred, node->var_decl.name);
+ add_symbol(ctx, node->var_decl.name, inferred, NULL);
+ } else {
+ emit_auto_type(ctx, node->var_decl.init_expr, node->token, out);
+ fprintf(out, " %s", node->var_decl.name);
+
+ if (inferred) {
+ add_symbol(ctx, node->var_decl.name, inferred, NULL);
+ } else {
+ // Here we are cooked.
+ }
+ }
+
+ fprintf(out, " = ");
+ codegen_expression(ctx, node->var_decl.init_expr, out);
+ fprintf(out, ";\n");
+ }
+ break;
+ case NODE_CONST:
+ fprintf(out, " const ");
+ if (node->var_decl.type_str) {
+ fprintf(out, "%s %s", node->var_decl.type_str, node->var_decl.name);
+ } else {
+ emit_auto_type(ctx, node->var_decl.init_expr, node->token, out);
+ fprintf(out, " %s", node->var_decl.name);
+ }
+ fprintf(out, " = ");
+ codegen_expression(ctx, node->var_decl.init_expr, out);
+ fprintf(out, ";\n");
+ break;
+ case NODE_FIELD:
+ if (node->field.bit_width > 0) {
+ fprintf(out, " %s %s : %d;\n", node->field.type, node->field.name,
+ node->field.bit_width);
+ } else {
+ fprintf(out, " ");
+ emit_var_decl_type(ctx, out, node->field.type, node->field.name);
+ fprintf(out, ";\n");
+ }
+ break;
+ case NODE_IF:
+ fprintf(out, "if (");
+ codegen_expression(ctx, node->if_stmt.condition, out);
+ fprintf(out, ") ");
+ codegen_node_single(ctx, node->if_stmt.then_body, out);
+ if (node->if_stmt.else_body) {
+ fprintf(out, " else ");
+ codegen_node_single(ctx, node->if_stmt.else_body, out);
+ }
+ break;
+ case NODE_UNLESS:
+ fprintf(out, "if (!(");
+ codegen_expression(ctx, node->unless_stmt.condition, out);
+ fprintf(out, ")) ");
+ codegen_node_single(ctx, node->unless_stmt.body, out);
+ break;
+ case NODE_GUARD:
+ fprintf(out, "if (!(");
+ codegen_expression(ctx, node->guard_stmt.condition, out);
+ fprintf(out, ")) ");
+ codegen_node_single(ctx, node->guard_stmt.body, out);
+ break;
+ case NODE_WHILE:
+ fprintf(out, "while (");
+ codegen_expression(ctx, node->while_stmt.condition, out);
+ fprintf(out, ") ");
+ codegen_node_single(ctx, node->while_stmt.body, out);
+ break;
+ case NODE_FOR:
+ fprintf(out, "for (");
+ if (node->for_stmt.init) {
+ if (node->for_stmt.init->type == NODE_VAR_DECL) {
+ ASTNode *v = node->for_stmt.init;
+ if (v->var_decl.type_str &&
+ strcmp(v->var_decl.type_str, "__auto_type") != 0) {
+ fprintf(out, "%s %s = (%s)(", v->var_decl.type_str, v->var_decl.name,
+ v->var_decl.type_str);
+ codegen_expression(ctx, v->var_decl.init_expr, out);
+ fprintf(out, ")");
+ } else {
+ emit_auto_type(ctx, v->var_decl.init_expr, v->token, out);
+ fprintf(out, " %s = ", v->var_decl.name);
+ codegen_expression(ctx, v->var_decl.init_expr, out);
+ }
+ } else {
+ codegen_expression(ctx, node->for_stmt.init, out);
+ }
+ }
+ fprintf(out, "; ");
+ if (node->for_stmt.condition) {
+ codegen_expression(ctx, node->for_stmt.condition, out);
+ }
+ fprintf(out, "; ");
+ if (node->for_stmt.step) {
+ codegen_expression(ctx, node->for_stmt.step, out);
+ }
+ fprintf(out, ") ");
+ codegen_node_single(ctx, node->for_stmt.body, out);
+ break;
+ case NODE_BREAK:
+ if (node->break_stmt.target_label) {
+ fprintf(out, "goto __break_%s;\n", node->break_stmt.target_label);
+ } else {
+ fprintf(out, "break;\n");
+ }
+ break;
+ case NODE_CONTINUE:
+ if (node->continue_stmt.target_label) {
+ fprintf(out, "goto __continue_%s;\n", node->continue_stmt.target_label);
+ } else {
+ fprintf(out, "continue;\n");
+ }
+ break;
+ case NODE_GOTO:
+ if (node->goto_stmt.goto_expr) {
+ // Computed goto: goto *expr;
+ fprintf(out, "goto *(");
+ codegen_expression(ctx, node->goto_stmt.goto_expr, out);
+ fprintf(out, ");\n");
+ } else {
+ fprintf(out, "goto %s;\n", node->goto_stmt.label_name);
+ }
+ break;
+ case NODE_LABEL:
+ fprintf(out, "%s:;\n", node->label_stmt.label_name);
+ break;
+ case NODE_DO_WHILE:
+ fprintf(out, "do ");
+ codegen_node_single(ctx, node->do_while_stmt.body, out);
+ fprintf(out, " while (");
+ codegen_expression(ctx, node->do_while_stmt.condition, out);
+ fprintf(out, ");\n");
+ break;
+ // Loop constructs: loop, repeat, for-in
+ case NODE_LOOP:
+ // loop { ... } => while (1) { ... }
+ fprintf(out, "while (1) ");
+ codegen_node_single(ctx, node->loop_stmt.body, out);
+ break;
+ case NODE_REPEAT:
+ fprintf(out, "for (int _rpt_i = 0; _rpt_i < (%s); _rpt_i++) ",
+ node->repeat_stmt.count);
+ codegen_node_single(ctx, node->repeat_stmt.body, out);
+ break;
+ case NODE_FOR_RANGE:
+ fprintf(out, "for (");
+ if (strstr(g_config.cc, "tcc")) {
+ fprintf(out, "__typeof__((");
+ codegen_expression(ctx, node->for_range.start, out);
+ fprintf(out, ")) %s = ", node->for_range.var_name);
+ } else {
+ fprintf(out, "__auto_type %s = ", node->for_range.var_name);
+ }
+ codegen_expression(ctx, node->for_range.start, out);
+ fprintf(out, "; %s < ", node->for_range.var_name);
+ codegen_expression(ctx, node->for_range.end, out);
+ fprintf(out, "; %s", node->for_range.var_name);
+ if (node->for_range.step) {
+ fprintf(out, " += %s) ", node->for_range.step);
+ } else {
+ fprintf(out, "++) ");
+ }
+ codegen_node_single(ctx, node->for_range.body, out);
+ break;
+ case NODE_ASM: {
+ int is_extended =
+ (node->asm_stmt.num_outputs > 0 || node->asm_stmt.num_inputs > 0 ||
+ node->asm_stmt.num_clobbers > 0);
+
+ if (node->asm_stmt.is_volatile) {
+ fprintf(out, " __asm__ __volatile__(");
+ } else {
+ fprintf(out, " __asm__(");
+ }
- if (node->asm_stmt.is_volatile)
- {
- fprintf(out, " __asm__ __volatile__(");
- }
- else
- {
- fprintf(out, " __asm__(");
- }
+ char *code = node->asm_stmt.code;
+ char *transformed = xmalloc(strlen(code) * 3); // Generous buffer
+ char *dst = transformed;
+
+ for (char *p = code; *p; p++) {
+ if (*p == '{') {
+ // Find matching }
+ char *end = strchr(p + 1, '}');
+ if (end) {
+ // Extract variable name
+ int var_len = end - p - 1;
+ char var_name[64];
+ strncpy(var_name, p + 1, var_len);
+ var_name[var_len] = 0;
+
+ // Find variable index
+ int idx = -1;
+
+ // Check outputs first
+ for (int i = 0; i < node->asm_stmt.num_outputs; i++) {
+ if (strcmp(node->asm_stmt.outputs[i], var_name) == 0) {
+ idx = i;
+ break;
+ }
+ }
+
+ // Then check inputs
+ if (idx == -1) {
+ for (int i = 0; i < node->asm_stmt.num_inputs; i++) {
+ if (strcmp(node->asm_stmt.inputs[i], var_name) == 0) {
+ idx = node->asm_stmt.num_outputs + i;
+ break;
+ }
+ }
+ }
+
+ if (idx >= 0) {
+ // Replace with %N
+ dst += sprintf(dst, "%%%d", idx);
+ } else {
+ // Variable not found - error or keep as-is?
+ dst += sprintf(dst, "{%s}", var_name);
+ }
+
+ p = end; // Skip past }
+ } else {
+ *dst++ = *p;
+ }
+ } else if (*p == '%') {
+ if (is_extended) {
+ *dst++ = '%';
+ *dst++ = '%';
+ } else {
+ *dst++ = '%';
+ }
+ } else {
+ *dst++ = *p;
+ }
+ }
+ *dst = 0;
+
+ fprintf(out, "\"");
+ for (char *p = transformed; *p; p++) {
+ if (*p == '\n') {
+ fprintf(out, "\\n\"\n \"");
+ } else if (*p == '"') {
+ fprintf(out, "\\\"");
+ } else if (*p == '\\') {
+ fprintf(out, "\\\\");
+ } else {
+ fputc(*p, out);
+ }
+ }
+ fprintf(out, "\\n\"");
- char *code = node->asm_stmt.code;
- char *transformed = xmalloc(strlen(code) * 3); // Generous buffer
- char *dst = transformed;
-
- for (char *p = code; *p; p++)
- {
- if (*p == '{')
- {
- // Find matching }
- char *end = strchr(p + 1, '}');
- if (end)
- {
- // Extract variable name
- int var_len = end - p - 1;
- char var_name[64];
- strncpy(var_name, p + 1, var_len);
- var_name[var_len] = 0;
-
- // Find variable index
- int idx = -1;
-
- // Check outputs first
- for (int i = 0; i < node->asm_stmt.num_outputs; i++)
- {
- if (strcmp(node->asm_stmt.outputs[i], var_name) == 0)
- {
- idx = i;
- break;
- }
- }
-
- // Then check inputs
- if (idx == -1)
- {
- for (int i = 0; i < node->asm_stmt.num_inputs; i++)
- {
- if (strcmp(node->asm_stmt.inputs[i], var_name) == 0)
- {
- idx = node->asm_stmt.num_outputs + i;
- break;
- }
- }
- }
-
- if (idx >= 0)
- {
- // Replace with %N
- dst += sprintf(dst, "%%%d", idx);
- }
- else
- {
- // Variable not found - error or keep as-is?
- dst += sprintf(dst, "{%s}", var_name);
- }
-
- p = end; // Skip past }
- }
- else
- {
- *dst++ = *p;
- }
- }
- else if (*p == '%')
- {
- if (is_extended)
- {
- *dst++ = '%';
- *dst++ = '%';
- }
- else
- {
- *dst++ = '%';
- }
- }
- else
- {
- *dst++ = *p;
- }
- }
- *dst = 0;
-
- fprintf(out, "\"");
- for (char *p = transformed; *p; p++)
- {
- if (*p == '\n')
- {
- fprintf(out, "\\n\"\n \"");
- }
- else if (*p == '"')
- {
- fprintf(out, "\\\"");
- }
- else if (*p == '\\')
- {
- fprintf(out, "\\\\");
- }
- else
- {
- fputc(*p, out);
- }
- }
- fprintf(out, "\\n\"");
-
- if (node->asm_stmt.num_outputs > 0)
- {
- fprintf(out, "\n : ");
- for (int i = 0; i < node->asm_stmt.num_outputs; i++)
- {
- if (i > 0)
- {
- fprintf(out, ", ");
- }
-
- // Determine constraint
- char *mode = node->asm_stmt.output_modes[i];
- if (strcmp(mode, "out") == 0)
- {
- fprintf(out, "\"=r\"(%s)", node->asm_stmt.outputs[i]);
- }
- else if (strcmp(mode, "inout") == 0)
- {
- fprintf(out, "\"+r\"(%s)", node->asm_stmt.outputs[i]);
- }
- else
- {
- fprintf(out, "\"=r\"(%s)", node->asm_stmt.outputs[i]);
- }
- }
+ if (node->asm_stmt.num_outputs > 0) {
+ fprintf(out, "\n : ");
+ for (int i = 0; i < node->asm_stmt.num_outputs; i++) {
+ if (i > 0) {
+ fprintf(out, ", ");
}
- if (node->asm_stmt.num_inputs > 0)
- {
- fprintf(out, "\n : ");
- for (int i = 0; i < node->asm_stmt.num_inputs; i++)
- {
- if (i > 0)
- {
- fprintf(out, ", ");
- }
- fprintf(out, "\"r\"(%s)", node->asm_stmt.inputs[i]);
- }
+ // Determine constraint
+ char *mode = node->asm_stmt.output_modes[i];
+ if (strcmp(mode, "out") == 0) {
+ fprintf(out, "\"=r\"(%s)", node->asm_stmt.outputs[i]);
+ } else if (strcmp(mode, "inout") == 0) {
+ fprintf(out, "\"+r\"(%s)", node->asm_stmt.outputs[i]);
+ } else {
+ fprintf(out, "\"=r\"(%s)", node->asm_stmt.outputs[i]);
}
- else if (node->asm_stmt.num_outputs > 0)
- {
- fprintf(out, "\n : ");
+ }
+ }
+
+ if (node->asm_stmt.num_inputs > 0) {
+ fprintf(out, "\n : ");
+ for (int i = 0; i < node->asm_stmt.num_inputs; i++) {
+ if (i > 0) {
+ fprintf(out, ", ");
}
+ fprintf(out, "\"r\"(%s)", node->asm_stmt.inputs[i]);
+ }
+ } else if (node->asm_stmt.num_outputs > 0) {
+ fprintf(out, "\n : ");
+ }
- if (node->asm_stmt.num_clobbers > 0)
- {
- fprintf(out, "\n : ");
- for (int i = 0; i < node->asm_stmt.num_clobbers; i++)
- {
- if (i > 0)
- {
- fprintf(out, ", ");
- }
- fprintf(out, "\"%s\"", node->asm_stmt.clobbers[i]);
- }
+ if (node->asm_stmt.num_clobbers > 0) {
+ fprintf(out, "\n : ");
+ for (int i = 0; i < node->asm_stmt.num_clobbers; i++) {
+ if (i > 0) {
+ fprintf(out, ", ");
}
+ fprintf(out, "\"%s\"", node->asm_stmt.clobbers[i]);
+ }
+ }
- fprintf(out, ");\n");
- break;
+ fprintf(out, ");\n");
+ break;
+ }
+ case NODE_RETURN:
+ fprintf(out, " return ");
+ codegen_expression(ctx, node->ret.value, out);
+ fprintf(out, ";\n");
+ break;
+ case NODE_EXPR_MEMBER: {
+ codegen_expression(ctx, node->member.target, out);
+ char *lt = infer_type(ctx, node->member.target);
+ if (lt && (lt[strlen(lt) - 1] == '*' || strstr(lt, "*"))) {
+ fprintf(out, "->%s", node->member.field);
+ } else {
+ fprintf(out, ".%s", node->member.field);
}
- case NODE_RETURN:
- fprintf(out, " return ");
- codegen_expression(ctx, node->ret.value, out);
- fprintf(out, ";\n");
- break;
- case NODE_EXPR_MEMBER:
- {
- codegen_expression(ctx, node->member.target, out);
- char *lt = infer_type(ctx, node->member.target);
- if (lt && (lt[strlen(lt) - 1] == '*' || strstr(lt, "*")))
- {
- fprintf(out, "->%s", node->member.field);
- }
- else
- {
- fprintf(out, ".%s", node->member.field);
- }
- if (lt)
- {
- free(lt);
- }
- break;
+ if (lt) {
+ free(lt);
}
- case NODE_REPL_PRINT:
- {
- fprintf(out, "{ ");
- emit_auto_type(ctx, node->repl_print.expr, node->token, out);
- fprintf(out, " _zval = (");
- codegen_expression(ctx, node->repl_print.expr, out);
- fprintf(out, "); fprintf(stdout, _z_str(_zval), _zval); fprintf(stdout, "
- "\"\\n\"); }\n");
- break;
+ break;
+ }
+ case NODE_REPL_PRINT: {
+ fprintf(out, "{ ");
+ emit_auto_type(ctx, node->repl_print.expr, node->token, out);
+ fprintf(out, " _zval = (");
+ codegen_expression(ctx, node->repl_print.expr, out);
+ fprintf(out, "); fprintf(stdout, _z_str(_zval), _zval); fprintf(stdout, "
+ "\"\\n\"); }\n");
+ break;
+ }
+ case NODE_AWAIT: {
+ char *ret_type = "void*";
+ int free_ret = 0;
+ if (node->type_info) {
+ char *t = type_to_string(node->type_info);
+ if (t) {
+ ret_type = t;
+ free_ret = 1;
+ }
+ } else if (node->resolved_type) {
+ ret_type = node->resolved_type;
}
- case NODE_AWAIT:
- {
- char *ret_type = "void*";
- int free_ret = 0;
- if (node->type_info)
- {
- char *t = type_to_string(node->type_info);
- if (t)
- {
- ret_type = t;
- free_ret = 1;
- }
- }
- else if (node->resolved_type)
- {
- ret_type = node->resolved_type;
- }
-
- // Fallback: If type is still Async/void* (likely from Future type, not
- // Result type), try to infer
- if (strcmp(ret_type, "Async") == 0 || strcmp(ret_type, "void*") == 0)
- {
- char *inf = infer_type(ctx, node);
- if (inf && strcmp(inf, "Async") != 0 && strcmp(inf, "void*") != 0)
- {
- if (free_ret)
- {
- free(ret_type);
- }
- ret_type = inf;
- free_ret = 0; // infer_type ownership ambiguous, avoid double free
- }
- }
- int needs_long_cast = 0;
- int returns_struct = 0;
- if (strstr(ret_type, "*") == NULL && strcmp(ret_type, "string") != 0 &&
- strcmp(ret_type, "void") != 0 && strcmp(ret_type, "Async") != 0)
- {
- if (strcmp(ret_type, "int") != 0 && strcmp(ret_type, "bool") != 0 &&
- strcmp(ret_type, "char") != 0 && strcmp(ret_type, "float") != 0 &&
- strcmp(ret_type, "double") != 0 && strcmp(ret_type, "long") != 0 &&
- strcmp(ret_type, "usize") != 0 && strcmp(ret_type, "isize") != 0 &&
- strncmp(ret_type, "uint", 4) != 0 && strncmp(ret_type, "int", 3) != 0)
- {
- returns_struct = 1;
- }
- else
- {
- needs_long_cast = 1;
- }
+ // Fallback: If type is still Async/void* (likely from Future type, not
+ // Result type), try to infer
+ if (strcmp(ret_type, "Async") == 0 || strcmp(ret_type, "void*") == 0) {
+ char *inf = infer_type(ctx, node);
+ if (inf && strcmp(inf, "Async") != 0 && strcmp(inf, "void*") != 0) {
+ if (free_ret) {
+ free(ret_type);
+ }
+ ret_type = inf;
+ free_ret = 0; // infer_type ownership ambiguous, avoid double free
+ }
+ }
- if (strncmp(ret_type, "struct", 6) == 0)
- {
- returns_struct = 1;
- }
- }
+ int needs_long_cast = 0;
+ int returns_struct = 0;
+ if (strstr(ret_type, "*") == NULL && strcmp(ret_type, "string") != 0 &&
+ strcmp(ret_type, "void") != 0 && strcmp(ret_type, "Async") != 0) {
+ if (strcmp(ret_type, "int") != 0 && strcmp(ret_type, "bool") != 0 &&
+ strcmp(ret_type, "char") != 0 && strcmp(ret_type, "float") != 0 &&
+ strcmp(ret_type, "double") != 0 && strcmp(ret_type, "long") != 0 &&
+ strcmp(ret_type, "usize") != 0 && strcmp(ret_type, "isize") != 0 &&
+ strncmp(ret_type, "uint", 4) != 0 &&
+ strncmp(ret_type, "int", 3) != 0) {
+ returns_struct = 1;
+ } else {
+ needs_long_cast = 1;
+ }
+
+ if (strncmp(ret_type, "struct", 6) == 0) {
+ returns_struct = 1;
+ }
+ }
- fprintf(out, "({ Async _a = ");
- codegen_expression(ctx, node->unary.operand, out);
- fprintf(out, "; void* _r; pthread_join(_a.thread, &_r); ");
- if (strcmp(ret_type, "void") == 0)
- {
- fprintf(out, "})"); // result unused
- }
- else
- {
- if (returns_struct)
- {
- // Dereference and free
- fprintf(out, "%s _val = *(%s*)_r; free(_r); _val; })", ret_type, ret_type);
- }
- else
- {
- if (needs_long_cast)
- {
- fprintf(out, "(%s)(long)_r; })", ret_type);
- }
- else
- {
- fprintf(out, "(%s)_r; })", ret_type);
- }
- }
- }
- if (free_ret)
- {
- free(ret_type);
- }
- fprintf(out, ";\n"); // Statement terminator
- break;
+ fprintf(out, "({ Async _a = ");
+ codegen_expression(ctx, node->unary.operand, out);
+ fprintf(out, "; void* _r; pthread_join(_a.thread, &_r); ");
+ if (strcmp(ret_type, "void") == 0) {
+ fprintf(out, "})"); // result unused
+ } else {
+ if (returns_struct) {
+ // Dereference and free
+ fprintf(out, "%s _val = *(%s*)_r; free(_r); _val; })", ret_type,
+ ret_type);
+ } else {
+ if (needs_long_cast) {
+ fprintf(out, "(%s)(long)_r; })", ret_type);
+ } else {
+ fprintf(out, "(%s)_r; })", ret_type);
+ }
+ }
}
- default:
- codegen_expression(ctx, node, out);
- fprintf(out, ";\n");
- break;
+ if (free_ret) {
+ free(ret_type);
}
+ fprintf(out, ";\n"); // Statement terminator
+ break;
+ }
+ default:
+ codegen_expression(ctx, node, out);
+ fprintf(out, ";\n");
+ break;
+ }
}
// Walks AST nodes and generates code.
-void codegen_walker(ParserContext *ctx, ASTNode *node, FILE *out)
-{
- while (node)
- {
- codegen_node_single(ctx, node, out);
- node = node->next;
- }
+void codegen_walker(ParserContext *ctx, ASTNode *node, FILE *out) {
+ while (node) {
+ codegen_node_single(ctx, node, out);
+ node = node->next;
+ }
}
diff --git a/src/codegen/codegen.h b/src/codegen/codegen.h
index 4b66f99..4d13d65 100644
--- a/src/codegen/codegen.h
+++ b/src/codegen/codegen.h
@@ -16,9 +16,11 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out);
// Utility functions (codegen_utils.c).
char *infer_type(ParserContext *ctx, ASTNode *node);
ASTNode *find_struct_def_codegen(ParserContext *ctx, const char *name);
-char *get_field_type_str(ParserContext *ctx, const char *struct_name, const char *field_name);
+char *get_field_type_str(ParserContext *ctx, const char *struct_name,
+ const char *field_name);
char *extract_call_args(const char *args);
-void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str, const char *var_name);
+void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str,
+ const char *var_name);
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);
diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c
index 026824e..53c4eb2 100644
--- a/src/codegen/codegen_decl.c
+++ b/src/codegen/codegen_decl.c
@@ -8,912 +8,763 @@
#include <string.h>
// Emit C preamble with standard includes and type definitions.
-void emit_preamble(ParserContext *ctx, FILE *out)
-{
- if (g_config.is_freestanding)
- {
- // Freestanding preamble.
- // It actually needs more work, but yk.
- 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("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",
- out);
- fputs("#define I32 int32_t\n#define U32 uint32_t\n#define I64 "
- "int64_t\n#define U64 "
- "uint64_t\n",
- out);
- fputs("#define F32 float\n#define F64 double\n", out);
- fputs("#define _z_str(x) _Generic((x), _Bool: \"%d\", 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("typedef struct { void *func; void *ctx; } z_closure_T;\n", out);
-
- fputs("__attribute__((weak)) void* z_malloc(usize sz) { return NULL; }\n", out);
- fputs("__attribute__((weak)) void* z_realloc(void* ptr, usize sz) { return "
- "NULL; }\n",
- out);
- fputs("__attribute__((weak)) void z_free(void* ptr) { }\n", out);
- fputs("__attribute__((weak)) void z_print(const char* fmt, ...) { }\n", out);
- fputs("__attribute__((weak)) void z_panic(const char* msg) { while(1); }\n", out);
- }
- else
- {
- // Standard hosted preamble.
- fputs("#include <stdio.h>\n#include <stdlib.h>\n#include "
- "<stddef.h>\n#include <string.h>\n",
- out);
- fputs("#include <stdarg.h>\n#include <stdint.h>\n#include <stdbool.h>\n", out);
- fputs("#include <unistd.h>\n#include <fcntl.h>\n", out); // POSIX functions
- fputs("#ifdef __TINYC__\n#define __auto_type __typeof__\n#endif\n", out);
- fputs("typedef size_t usize;\ntypedef char* string;\n", out);
- if (ctx->has_async)
- {
- fputs("#include <pthread.h>\n", 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",
- out);
- fputs("#define I32 int32_t\n#define U32 uint32_t\n#define I64 "
- "int64_t\n#define U64 "
- "uint64_t\n",
- out);
- fputs("#define F32 float\n#define F64 double\n", out);
- fputs("#define _z_str(x) _Generic((x), _Bool: \"%d\", 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);
-
- // Memory Mapping.
- fputs("#define z_malloc malloc\n#define z_realloc realloc\n#define z_free "
- "free\n#define "
- "z_print printf\n",
- out);
- fputs("void z_panic(const char* msg) { fprintf(stderr, \"Panic: %s\\n\", "
- "msg); exit(1); }\n",
- out);
-
- fputs("void _z_autofree_impl(void *p) { void **pp = (void**)p; if(*pp) { "
- "z_free(*pp); *pp "
- "= NULL; } }\n",
- out);
- fputs("#define assert(cond, ...) if (!(cond)) { fprintf(stderr, "
- "\"Assertion failed: \" "
- "__VA_ARGS__); exit(1); }\n",
- out);
- fputs("string _z_readln_raw() { "
- "size_t cap = 64; size_t len = 0; "
- "char *line = z_malloc(cap); "
- "if(!line) return NULL; "
- "int c; "
- "while((c = fgetc(stdin)) != EOF) { "
- "if(c == '\\n') break; "
- "if(len + 1 >= cap) { cap *= 2; char *n = z_realloc(line, cap); "
- "if(!n) { z_free(line); return NULL; } line = n; } "
- "line[len++] = c; } "
- "if(len == 0 && c == EOF) { z_free(line); return NULL; } "
- "line[len] = 0; return line; }\n",
- out);
- fputs("int _z_scan_helper(const char *fmt, ...) { char *l = "
- "_z_readln_raw(); if(!l) return "
- "0; va_list ap; va_start(ap, fmt); int r = vsscanf(l, fmt, ap); "
- "va_end(ap); "
- "z_free(l); return r; }\n",
- out);
-
- // REPL helpers: suppress/restore stdout.
- fputs("int _z_orig_stdout = -1;\n", out);
- fputs("void _z_suppress_stdout() {\n", out);
- fputs(" fflush(stdout);\n", out);
- fputs(" if (_z_orig_stdout == -1) _z_orig_stdout = dup(STDOUT_FILENO);\n", out);
- fputs(" int nullfd = open(\"/dev/null\", O_WRONLY);\n", out);
- fputs(" dup2(nullfd, STDOUT_FILENO);\n", out);
- fputs(" close(nullfd);\n", out);
- fputs("}\n", out);
- fputs("void _z_restore_stdout() {\n", out);
- fputs(" fflush(stdout);\n", out);
- fputs(" if (_z_orig_stdout != -1) {\n", out);
- fputs(" dup2(_z_orig_stdout, STDOUT_FILENO);\n", out);
- fputs(" close(_z_orig_stdout);\n", out);
- fputs(" _z_orig_stdout = -1;\n", out);
- fputs(" }\n", out);
- fputs("}\n", out);
+void emit_preamble(ParserContext *ctx, FILE *out) {
+ if (g_config.is_freestanding) {
+ // Freestanding preamble.
+ // It actually needs more work, but yk.
+ 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("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",
+ out);
+ fputs("#define I32 int32_t\n#define U32 uint32_t\n#define I64 "
+ "int64_t\n#define U64 "
+ "uint64_t\n",
+ out);
+ fputs("#define F32 float\n#define F64 double\n", out);
+ fputs("#define _z_str(x) _Generic((x), _Bool: \"%d\", 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("typedef struct { void *func; void *ctx; } z_closure_T;\n", out);
+
+ fputs("__attribute__((weak)) void* z_malloc(usize sz) { return NULL; }\n",
+ out);
+ fputs("__attribute__((weak)) void* z_realloc(void* ptr, usize sz) { return "
+ "NULL; }\n",
+ out);
+ fputs("__attribute__((weak)) void z_free(void* ptr) { }\n", out);
+ fputs("__attribute__((weak)) void z_print(const char* fmt, ...) { }\n",
+ out);
+ fputs("__attribute__((weak)) void z_panic(const char* msg) { while(1); }\n",
+ out);
+ } else {
+ // Standard hosted preamble.
+ fputs("#include <stdio.h>\n#include <stdlib.h>\n#include "
+ "<stddef.h>\n#include <string.h>\n",
+ out);
+ fputs("#include <stdarg.h>\n#include <stdint.h>\n#include <stdbool.h>\n",
+ out);
+ fputs("#include <unistd.h>\n#include <fcntl.h>\n", out); // POSIX functions
+ fputs("#ifdef __TINYC__\n#define __auto_type __typeof__\n#endif\n", out);
+ fputs("typedef size_t usize;\ntypedef char* string;\n", out);
+ if (ctx->has_async) {
+ fputs("#include <pthread.h>\n", 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",
+ out);
+ fputs("#define I32 int32_t\n#define U32 uint32_t\n#define I64 "
+ "int64_t\n#define U64 "
+ "uint64_t\n",
+ out);
+ fputs("#define F32 float\n#define F64 double\n", out);
+ fputs("#define _z_str(x) _Generic((x), _Bool: \"%d\", 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);
+
+ // Memory Mapping.
+ fputs("#define z_malloc malloc\n#define z_realloc realloc\n#define z_free "
+ "free\n#define "
+ "z_print printf\n",
+ out);
+ fputs("void z_panic(const char* msg) { fprintf(stderr, \"Panic: %s\\n\", "
+ "msg); exit(1); }\n",
+ out);
+
+ fputs("void _z_autofree_impl(void *p) { void **pp = (void**)p; if(*pp) { "
+ "z_free(*pp); *pp "
+ "= NULL; } }\n",
+ out);
+ fputs("#define assert(cond, ...) if (!(cond)) { fprintf(stderr, "
+ "\"Assertion failed: \" "
+ "__VA_ARGS__); exit(1); }\n",
+ out);
+ fputs("string _z_readln_raw() { "
+ "size_t cap = 64; size_t len = 0; "
+ "char *line = z_malloc(cap); "
+ "if(!line) return NULL; "
+ "int c; "
+ "while((c = fgetc(stdin)) != EOF) { "
+ "if(c == '\\n') break; "
+ "if(len + 1 >= cap) { cap *= 2; char *n = z_realloc(line, cap); "
+ "if(!n) { z_free(line); return NULL; } line = n; } "
+ "line[len++] = c; } "
+ "if(len == 0 && c == EOF) { z_free(line); return NULL; } "
+ "line[len] = 0; return line; }\n",
+ out);
+ fputs("int _z_scan_helper(const char *fmt, ...) { char *l = "
+ "_z_readln_raw(); if(!l) return "
+ "0; va_list ap; va_start(ap, fmt); int r = vsscanf(l, fmt, ap); "
+ "va_end(ap); "
+ "z_free(l); return r; }\n",
+ out);
+
+ // REPL helpers: suppress/restore stdout.
+ fputs("int _z_orig_stdout = -1;\n", out);
+ fputs("void _z_suppress_stdout() {\n", out);
+ fputs(" fflush(stdout);\n", out);
+ fputs(
+ " if (_z_orig_stdout == -1) _z_orig_stdout = dup(STDOUT_FILENO);\n",
+ out);
+ fputs(" int nullfd = open(\"/dev/null\", O_WRONLY);\n", out);
+ fputs(" dup2(nullfd, STDOUT_FILENO);\n", out);
+ fputs(" close(nullfd);\n", out);
+ fputs("}\n", out);
+ fputs("void _z_restore_stdout() {\n", out);
+ fputs(" fflush(stdout);\n", out);
+ fputs(" if (_z_orig_stdout != -1) {\n", out);
+ fputs(" dup2(_z_orig_stdout, STDOUT_FILENO);\n", out);
+ fputs(" close(_z_orig_stdout);\n", out);
+ fputs(" _z_orig_stdout = -1;\n", out);
+ fputs(" }\n", out);
+ fputs("}\n", out);
+ }
}
// Emit includes and type aliases.
-void emit_includes_and_aliases(ASTNode *node, FILE *out)
-{
- while (node)
- {
- if (node->type == NODE_INCLUDE)
- {
- if (node->include.is_system)
- {
- fprintf(out, "#include <%s>\n", node->include.path);
- }
- else
- {
- fprintf(out, "#include \"%s\"\n", node->include.path);
- }
- }
- else if (node->type == NODE_TYPE_ALIAS)
- {
- fprintf(out, "typedef %s %s;\n", node->type_alias.original_type,
- node->type_alias.alias);
- }
- node = node->next;
+void emit_includes_and_aliases(ASTNode *node, FILE *out) {
+ while (node) {
+ if (node->type == NODE_INCLUDE) {
+ if (node->include.is_system) {
+ fprintf(out, "#include <%s>\n", node->include.path);
+ } else {
+ fprintf(out, "#include \"%s\"\n", node->include.path);
+ }
+ } else if (node->type == NODE_TYPE_ALIAS) {
+ fprintf(out, "typedef %s %s;\n", node->type_alias.original_type,
+ node->type_alias.alias);
}
+ node = node->next;
+ }
}
// Emit enum constructor prototypes
-void emit_enum_protos(ASTNode *node, FILE *out)
-{
- while (node)
- {
- if (node->type == NODE_ENUM && !node->enm.is_template)
- {
- ASTNode *v = node->enm.variants;
- while (v)
- {
- if (v->variant.payload)
- {
- char *tstr = type_to_string(v->variant.payload);
- fprintf(out, "%s %s_%s(%s v);\n", node->enm.name, node->enm.name,
- v->variant.name, tstr);
- free(tstr);
- }
- else
- {
- fprintf(out, "%s %s_%s();\n", node->enm.name, node->enm.name, v->variant.name);
- }
- v = v->next;
- }
+void emit_enum_protos(ASTNode *node, FILE *out) {
+ while (node) {
+ if (node->type == NODE_ENUM && !node->enm.is_template) {
+ ASTNode *v = node->enm.variants;
+ while (v) {
+ if (v->variant.payload) {
+ char *tstr = type_to_string(v->variant.payload);
+ fprintf(out, "%s %s_%s(%s v);\n", node->enm.name, node->enm.name,
+ v->variant.name, tstr);
+ free(tstr);
+ } else {
+ fprintf(out, "%s %s_%s();\n", node->enm.name, node->enm.name,
+ v->variant.name);
}
- node = node->next;
+ v = v->next;
+ }
}
+ node = node->next;
+ }
}
// Emit lambda definitions.
-void emit_lambda_defs(ParserContext *ctx, FILE *out)
-{
- LambdaRef *cur = ctx->global_lambdas;
- while (cur)
- {
- ASTNode *node = cur->node;
- int saved_defer = defer_count;
- defer_count = 0;
-
- if (node->lambda.num_captures > 0)
- {
- fprintf(out, "struct Lambda_%d_Ctx {\n", node->lambda.lambda_id);
- for (int i = 0; i < node->lambda.num_captures; i++)
- {
- fprintf(out, " %s %s;\n", node->lambda.captured_types[i],
- node->lambda.captured_vars[i]);
- }
- fprintf(out, "};\n");
- }
+void emit_lambda_defs(ParserContext *ctx, FILE *out) {
+ LambdaRef *cur = ctx->global_lambdas;
+ while (cur) {
+ ASTNode *node = cur->node;
+ int saved_defer = defer_count;
+ defer_count = 0;
+
+ if (node->lambda.num_captures > 0) {
+ fprintf(out, "struct Lambda_%d_Ctx {\n", node->lambda.lambda_id);
+ for (int i = 0; i < node->lambda.num_captures; i++) {
+ fprintf(out, " %s %s;\n", node->lambda.captured_types[i],
+ node->lambda.captured_vars[i]);
+ }
+ fprintf(out, "};\n");
+ }
- fprintf(out, "%s _lambda_%d(void* _ctx", node->lambda.return_type, node->lambda.lambda_id);
+ fprintf(out, "%s _lambda_%d(void* _ctx", node->lambda.return_type,
+ node->lambda.lambda_id);
- for (int i = 0; i < node->lambda.num_params; i++)
- {
- fprintf(out, ", %s %s", node->lambda.param_types[i], node->lambda.param_names[i]);
- }
- fprintf(out, ") {\n");
+ for (int i = 0; i < node->lambda.num_params; i++) {
+ fprintf(out, ", %s %s", node->lambda.param_types[i],
+ node->lambda.param_names[i]);
+ }
+ fprintf(out, ") {\n");
- if (node->lambda.num_captures > 0)
- {
- fprintf(out, " struct Lambda_%d_Ctx* ctx = (struct Lambda_%d_Ctx*)_ctx;\n",
- node->lambda.lambda_id, node->lambda.lambda_id);
- }
+ if (node->lambda.num_captures > 0) {
+ fprintf(out,
+ " struct Lambda_%d_Ctx* ctx = (struct Lambda_%d_Ctx*)_ctx;\n",
+ node->lambda.lambda_id, node->lambda.lambda_id);
+ }
- g_current_lambda = node;
- if (node->lambda.body && node->lambda.body->type == NODE_BLOCK)
- {
- codegen_walker(ctx, node->lambda.body->block.statements, out);
- }
- g_current_lambda = NULL;
+ g_current_lambda = node;
+ if (node->lambda.body && node->lambda.body->type == NODE_BLOCK) {
+ codegen_walker(ctx, node->lambda.body->block.statements, out);
+ }
+ g_current_lambda = NULL;
- for (int i = defer_count - 1; i >= 0; i--)
- {
- codegen_node_single(ctx, defer_stack[i], out);
- }
+ for (int i = defer_count - 1; i >= 0; i--) {
+ codegen_node_single(ctx, defer_stack[i], out);
+ }
- fprintf(out, "}\n\n");
+ fprintf(out, "}\n\n");
- defer_count = saved_defer;
- cur = cur->next;
- }
+ defer_count = saved_defer;
+ cur = cur->next;
+ }
}
// Emit struct and enum definitions.
-void emit_struct_defs(ParserContext *ctx, ASTNode *node, FILE *out)
-{
- while (node)
- {
- if (node->type == NODE_STRUCT && node->strct.is_template)
- {
- node = node->next;
- continue;
+void emit_struct_defs(ParserContext *ctx, ASTNode *node, FILE *out) {
+ while (node) {
+ if (node->type == NODE_STRUCT && node->strct.is_template) {
+ node = node->next;
+ continue;
+ }
+ if (node->type == NODE_ENUM && node->enm.is_template) {
+ node = node->next;
+ continue;
+ }
+ if (node->type == NODE_STRUCT) {
+ if (node->strct.is_incomplete) {
+ // Forward declaration - no body needed (typedef handles it)
+ node = node->next;
+ continue;
+ }
+
+ if (node->strct.is_union) {
+ fprintf(out, "union %s {", node->strct.name);
+ } else {
+ fprintf(out, "struct %s {", node->strct.name);
+ }
+ fprintf(out, "\n");
+ codegen_walker(ctx, node->strct.fields, out);
+ fprintf(out, "}");
+
+ if (node->strct.is_packed && node->strct.align) {
+ fprintf(out, " __attribute__((packed, aligned(%d)))",
+ node->strct.align);
+ } else if (node->strct.is_packed) {
+ fprintf(out, " __attribute__((packed))");
+ } else if (node->strct.align) {
+ fprintf(out, " __attribute__((aligned(%d)))", node->strct.align);
+ }
+ fprintf(out, ";\n\n");
+ } else if (node->type == NODE_ENUM) {
+ fprintf(out, "typedef enum { ");
+ ASTNode *v = node->enm.variants;
+ while (v) {
+ fprintf(out, "%s_%s_Tag, ", node->enm.name, v->variant.name);
+ v = v->next;
+ }
+ fprintf(out, "} %s_Tag;\n", node->enm.name);
+ fprintf(out, "struct %s { %s_Tag tag; union { ", node->enm.name,
+ node->enm.name);
+ v = node->enm.variants;
+ while (v) {
+ if (v->variant.payload) {
+ char *tstr = type_to_string(v->variant.payload);
+ fprintf(out, "%s %s; ", tstr, v->variant.name);
+ free(tstr);
}
- if (node->type == NODE_ENUM && node->enm.is_template)
- {
- node = node->next;
- continue;
+ v = v->next;
+ }
+ fprintf(out, "} data; };\n\n");
+ v = node->enm.variants;
+ while (v) {
+ if (v->variant.payload) {
+ char *tstr = type_to_string(v->variant.payload);
+ fprintf(out,
+ "%s %s_%s(%s v) { return (%s){.tag=%s_%s_Tag, "
+ ".data.%s=v}; }\n",
+ node->enm.name, node->enm.name, v->variant.name, tstr,
+ node->enm.name, node->enm.name, v->variant.name,
+ v->variant.name);
+ free(tstr);
+ } else {
+ fprintf(out, "%s %s_%s() { return (%s){.tag=%s_%s_Tag}; }\n",
+ node->enm.name, node->enm.name, v->variant.name,
+ node->enm.name, node->enm.name, v->variant.name);
}
- if (node->type == NODE_STRUCT)
- {
- if (node->strct.is_incomplete)
- {
- // Forward declaration - no body needed (typedef handles it)
- node = node->next;
- continue;
- }
+ v = v->next;
+ }
+ }
+ node = node->next;
+ }
+}
- if (node->strct.is_union)
- {
- fprintf(out, "union %s {", node->strct.name);
- }
- else
- {
- fprintf(out, "struct %s {", node->strct.name);
- }
- fprintf(out, "\n");
- codegen_walker(ctx, node->strct.fields, out);
- fprintf(out, "}");
+// Emit trait definitions.
+void emit_trait_defs(ASTNode *node, FILE *out) {
+ while (node) {
+ if (node->type == NODE_TRAIT) {
+ fprintf(out, "typedef struct %s_VTable {\n", node->trait.name);
+ ASTNode *m = node->trait.methods;
+ while (m) {
+ fprintf(out, " %s (*%s)(", m->func.ret_type,
+ parse_original_method_name(m->func.name));
+ int has_self = (m->func.args && strstr(m->func.args, "self"));
+ if (!has_self) {
+ fprintf(out, "void* self");
+ }
- if (node->strct.is_packed && node->strct.align)
- {
- fprintf(out, " __attribute__((packed, aligned(%d)))", node->strct.align);
- }
- else if (node->strct.is_packed)
- {
- fprintf(out, " __attribute__((packed))");
- }
- else if (node->strct.align)
- {
- fprintf(out, " __attribute__((aligned(%d)))", node->strct.align);
- }
- fprintf(out, ";\n\n");
+ if (m->func.args) {
+ if (!has_self) {
+ fprintf(out, ", ");
+ }
+ fprintf(out, "%s", m->func.args);
}
- else if (node->type == NODE_ENUM)
- {
- fprintf(out, "typedef enum { ");
- ASTNode *v = node->enm.variants;
- while (v)
- {
- fprintf(out, "%s_%s_Tag, ", node->enm.name, v->variant.name);
- v = v->next;
- }
- fprintf(out, "} %s_Tag;\n", node->enm.name);
- fprintf(out, "struct %s { %s_Tag tag; union { ", node->enm.name, node->enm.name);
- v = node->enm.variants;
- while (v)
- {
- if (v->variant.payload)
- {
- char *tstr = type_to_string(v->variant.payload);
- fprintf(out, "%s %s; ", tstr, v->variant.name);
- free(tstr);
- }
- v = v->next;
- }
- fprintf(out, "} data; };\n\n");
- v = node->enm.variants;
- while (v)
- {
- if (v->variant.payload)
- {
- char *tstr = type_to_string(v->variant.payload);
- fprintf(out,
- "%s %s_%s(%s v) { return (%s){.tag=%s_%s_Tag, "
- ".data.%s=v}; }\n",
- node->enm.name, node->enm.name, v->variant.name, tstr, node->enm.name,
- node->enm.name, v->variant.name, v->variant.name);
- free(tstr);
- }
- else
- {
- fprintf(out, "%s %s_%s() { return (%s){.tag=%s_%s_Tag}; }\n", node->enm.name,
- node->enm.name, v->variant.name, node->enm.name, node->enm.name,
- v->variant.name);
- }
- v = v->next;
- }
+ fprintf(out, ");\n");
+ m = m->next;
+ }
+ fprintf(out, "} %s_VTable;\n", node->trait.name);
+ fprintf(out, "typedef struct %s { void *self; %s_VTable *vtable; } %s;\n",
+ node->trait.name, node->trait.name, node->trait.name);
+
+ m = node->trait.methods;
+ while (m) {
+ const char *orig = parse_original_method_name(m->func.name);
+ fprintf(out, "%s %s_%s(%s* self", m->func.ret_type, node->trait.name,
+ orig, node->trait.name);
+
+ int has_self = (m->func.args && strstr(m->func.args, "self"));
+ if (m->func.args) {
+ if (has_self) {
+ char *comma = strchr(m->func.args, ',');
+ if (comma) {
+ fprintf(out, ", %s", comma + 1);
+ }
+ } else {
+ fprintf(out, ", %s", m->func.args);
+ }
}
- node = node->next;
- }
-}
+ fprintf(out, ") {\n");
-// Emit trait definitions.
-void emit_trait_defs(ASTNode *node, FILE *out)
-{
- while (node)
- {
- if (node->type == NODE_TRAIT)
- {
- fprintf(out, "typedef struct %s_VTable {\n", node->trait.name);
- ASTNode *m = node->trait.methods;
- while (m)
- {
- fprintf(out, " %s (*%s)(", m->func.ret_type,
- parse_original_method_name(m->func.name));
- int has_self = (m->func.args && strstr(m->func.args, "self"));
- if (!has_self)
- {
- fprintf(out, "void* self");
- }
-
- if (m->func.args)
- {
- if (!has_self)
- {
- fprintf(out, ", ");
- }
- fprintf(out, "%s", m->func.args);
- }
- fprintf(out, ");\n");
- m = m->next;
+ fprintf(out, " return self->vtable->%s(self->self", orig);
+
+ if (m->func.args) {
+ char *call_args = extract_call_args(m->func.args);
+ if (has_self) {
+ char *comma = strchr(call_args, ',');
+ if (comma) {
+ fprintf(out, ", %s", comma + 1);
}
- fprintf(out, "} %s_VTable;\n", node->trait.name);
- fprintf(out, "typedef struct %s { void *self; %s_VTable *vtable; } %s;\n",
- node->trait.name, node->trait.name, node->trait.name);
-
- m = node->trait.methods;
- while (m)
- {
- const char *orig = parse_original_method_name(m->func.name);
- fprintf(out, "%s %s_%s(%s* self", m->func.ret_type, node->trait.name, orig,
- node->trait.name);
-
- int has_self = (m->func.args && strstr(m->func.args, "self"));
- if (m->func.args)
- {
- if (has_self)
- {
- char *comma = strchr(m->func.args, ',');
- if (comma)
- {
- fprintf(out, ", %s", comma + 1);
- }
- }
- else
- {
- fprintf(out, ", %s", m->func.args);
- }
- }
- fprintf(out, ") {\n");
-
- fprintf(out, " return self->vtable->%s(self->self", orig);
-
- if (m->func.args)
- {
- char *call_args = extract_call_args(m->func.args);
- if (has_self)
- {
- char *comma = strchr(call_args, ',');
- if (comma)
- {
- fprintf(out, ", %s", comma + 1);
- }
- }
- else
- {
- if (strlen(call_args) > 0)
- {
- fprintf(out, ", %s", call_args);
- }
- }
- free(call_args);
- }
- fprintf(out, ");\n}\n");
-
- m = m->next;
+ } else {
+ if (strlen(call_args) > 0) {
+ fprintf(out, ", %s", call_args);
}
- fprintf(out, "\n");
+ }
+ free(call_args);
}
- node = node->next;
+ fprintf(out, ");\n}\n");
+
+ m = m->next;
+ }
+ fprintf(out, "\n");
}
+ node = node->next;
+ }
}
// Emit global variables
-void emit_globals(ParserContext *ctx, ASTNode *node, FILE *out)
-{
- while (node)
- {
- if (node->type == NODE_VAR_DECL || node->type == NODE_CONST)
- {
- if (node->type == NODE_CONST)
- {
- fprintf(out, "const ");
- }
- if (node->var_decl.type_str)
- {
- emit_var_decl_type(ctx, out, node->var_decl.type_str, node->var_decl.name);
- }
- else
- {
- char *inferred = NULL;
- if (node->var_decl.init_expr)
- {
- inferred = infer_type(ctx, node->var_decl.init_expr);
- }
-
- if (inferred && strcmp(inferred, "__auto_type") != 0)
- {
- emit_var_decl_type(ctx, out, inferred, node->var_decl.name);
- }
- else
- {
- emit_auto_type(ctx, node->var_decl.init_expr, node->token, out);
- fprintf(out, " %s", node->var_decl.name);
- }
- }
- if (node->var_decl.init_expr)
- {
- fprintf(out, " = ");
- codegen_expression(ctx, node->var_decl.init_expr, out);
- }
- fprintf(out, ";\n");
+void emit_globals(ParserContext *ctx, ASTNode *node, FILE *out) {
+ while (node) {
+ if (node->type == NODE_VAR_DECL || node->type == NODE_CONST) {
+ if (node->type == NODE_CONST) {
+ fprintf(out, "const ");
+ }
+ if (node->var_decl.type_str) {
+ emit_var_decl_type(ctx, out, node->var_decl.type_str,
+ node->var_decl.name);
+ } else {
+ char *inferred = NULL;
+ if (node->var_decl.init_expr) {
+ inferred = infer_type(ctx, node->var_decl.init_expr);
}
- node = node->next;
+
+ if (inferred && strcmp(inferred, "__auto_type") != 0) {
+ emit_var_decl_type(ctx, out, inferred, node->var_decl.name);
+ } else {
+ emit_auto_type(ctx, node->var_decl.init_expr, node->token, out);
+ fprintf(out, " %s", node->var_decl.name);
+ }
+ }
+ if (node->var_decl.init_expr) {
+ fprintf(out, " = ");
+ codegen_expression(ctx, node->var_decl.init_expr, out);
+ }
+ fprintf(out, ";\n");
}
+ node = node->next;
+ }
}
// Emit function prototypes
-void emit_protos(ASTNode *node, FILE *out)
-{
- ASTNode *f = node;
- while (f)
- {
- if (f->type == NODE_FUNCTION)
- {
- if (f->func.is_async)
- {
- fprintf(out, "Async %s(%s);\n", f->func.name, f->func.args);
- // Also emit _impl_ prototype
- if (f->func.ret_type)
- {
- fprintf(out, "%s _impl_%s(%s);\n", f->func.ret_type, f->func.name,
- f->func.args);
- }
- else
- {
- fprintf(out, "void _impl_%s(%s);\n", f->func.name, f->func.args);
- }
- }
- else
- {
- fprintf(out, "%s %s(%s);\n", f->func.ret_type, f->func.name, f->func.args);
- }
+void emit_protos(ASTNode *node, FILE *out) {
+ ASTNode *f = node;
+ while (f) {
+ if (f->type == NODE_FUNCTION) {
+ if (f->func.is_async) {
+ fprintf(out, "Async %s(%s);\n", f->func.name, f->func.args);
+ // Also emit _impl_ prototype
+ if (f->func.ret_type) {
+ fprintf(out, "%s _impl_%s(%s);\n", f->func.ret_type, f->func.name,
+ f->func.args);
+ } else {
+ fprintf(out, "void _impl_%s(%s);\n", f->func.name, f->func.args);
}
- else if (f->type == NODE_IMPL)
- {
- char *sname = f->impl.struct_name;
- if (!sname)
- {
- f = f->next;
- continue;
- }
-
- char *mangled = replace_string_type(sname);
- ASTNode *def = find_struct_def_codegen(g_parser_ctx, mangled);
- int skip = 0;
- if (def)
- {
- if (def->type == NODE_STRUCT && def->strct.is_template)
- {
- skip = 1;
- }
- else if (def->type == NODE_ENUM && def->enm.is_template)
- {
- skip = 1;
- }
- }
- else
- {
- char *lt = strchr(sname, '<');
- if (lt)
- {
- int len = lt - sname;
- char *buf = xmalloc(len + 1);
- strncpy(buf, sname, len);
- buf[len] = 0;
- def = find_struct_def_codegen(g_parser_ctx, buf);
- if (def && def->strct.is_template)
- {
- skip = 1;
- }
- free(buf);
- }
- }
- if (mangled)
- {
- free(mangled);
- }
-
- if (skip)
- {
- f = f->next;
- continue;
- }
-
- ASTNode *m = f->impl.methods;
- while (m)
- {
- char *fname = m->func.name;
- char *proto = xmalloc(strlen(fname) + strlen(sname) + 2);
- int slen = strlen(sname);
- if (strncmp(fname, sname, slen) == 0 && fname[slen] == '_')
- {
- strcpy(proto, fname);
- }
- else
- {
- sprintf(proto, "%s_%s", sname, fname);
- }
-
- if (m->func.is_async)
- {
- fprintf(out, "Async %s(%s);\n", proto, m->func.args);
- }
- else
- {
- fprintf(out, "%s %s(%s);\n", m->func.ret_type, proto, m->func.args);
- }
-
- free(proto);
- m = m->next;
- }
+ } else {
+ fprintf(out, "%s %s(%s);\n", f->func.ret_type, f->func.name,
+ f->func.args);
+ }
+ } else if (f->type == NODE_IMPL) {
+ char *sname = f->impl.struct_name;
+ if (!sname) {
+ f = f->next;
+ continue;
+ }
+
+ char *mangled = replace_string_type(sname);
+ ASTNode *def = find_struct_def_codegen(g_parser_ctx, mangled);
+ int skip = 0;
+ if (def) {
+ if (def->type == NODE_STRUCT && def->strct.is_template) {
+ skip = 1;
+ } else if (def->type == NODE_ENUM && def->enm.is_template) {
+ skip = 1;
}
- else if (f->type == NODE_IMPL_TRAIT)
- {
- char *sname = f->impl_trait.target_type;
- if (!sname)
- {
- f = f->next;
- continue;
- }
+ } else {
+ char *lt = strchr(sname, '<');
+ if (lt) {
+ int len = lt - sname;
+ char *buf = xmalloc(len + 1);
+ strncpy(buf, sname, len);
+ buf[len] = 0;
+ def = find_struct_def_codegen(g_parser_ctx, buf);
+ if (def && def->strct.is_template) {
+ skip = 1;
+ }
+ free(buf);
+ }
+ }
+ if (mangled) {
+ free(mangled);
+ }
- char *mangled = replace_string_type(sname);
- ASTNode *def = find_struct_def_codegen(g_parser_ctx, mangled);
- int skip = 0;
- if (def)
- {
- if (def->strct.is_template)
- {
- skip = 1;
- }
- }
- else
- {
- char *lt = strchr(sname, '<');
- if (lt)
- {
- int len = lt - sname;
- char *buf = xmalloc(len + 1);
- strncpy(buf, sname, len);
- buf[len] = 0;
- def = find_struct_def_codegen(g_parser_ctx, buf);
- if (def && def->strct.is_template)
- {
- skip = 1;
- }
- free(buf);
- }
- }
- if (mangled)
- {
- free(mangled);
- }
+ if (skip) {
+ f = f->next;
+ continue;
+ }
+
+ ASTNode *m = f->impl.methods;
+ while (m) {
+ char *fname = m->func.name;
+ char *proto = xmalloc(strlen(fname) + strlen(sname) + 2);
+ int slen = strlen(sname);
+ if (strncmp(fname, sname, slen) == 0 && fname[slen] == '_') {
+ strcpy(proto, fname);
+ } else {
+ sprintf(proto, "%s_%s", sname, fname);
+ }
- if (skip)
- {
- f = f->next;
- continue;
- }
+ if (m->func.is_async) {
+ fprintf(out, "Async %s(%s);\n", proto, m->func.args);
+ } else {
+ fprintf(out, "%s %s(%s);\n", m->func.ret_type, proto, m->func.args);
+ }
- ASTNode *m = f->impl_trait.methods;
- while (m)
- {
- if (m->func.is_async)
- {
- fprintf(out, "Async %s(%s);\n", m->func.name, m->func.args);
- }
- else
- {
- fprintf(out, "%s %s(%s);\n", m->func.ret_type, m->func.name, m->func.args);
- }
- m = m->next;
- }
- // RAII: Emit glue prototype
- if (strcmp(f->impl_trait.trait_name, "Drop") == 0)
- {
- char *tname = f->impl_trait.target_type;
- fprintf(out, "void %s_Drop_glue(%s *self);\n", tname, tname);
- }
+ free(proto);
+ m = m->next;
+ }
+ } else if (f->type == NODE_IMPL_TRAIT) {
+ char *sname = f->impl_trait.target_type;
+ if (!sname) {
+ f = f->next;
+ continue;
+ }
+
+ char *mangled = replace_string_type(sname);
+ ASTNode *def = find_struct_def_codegen(g_parser_ctx, mangled);
+ int skip = 0;
+ if (def) {
+ if (def->strct.is_template) {
+ skip = 1;
}
+ } else {
+ char *lt = strchr(sname, '<');
+ if (lt) {
+ int len = lt - sname;
+ char *buf = xmalloc(len + 1);
+ strncpy(buf, sname, len);
+ buf[len] = 0;
+ def = find_struct_def_codegen(g_parser_ctx, buf);
+ if (def && def->strct.is_template) {
+ skip = 1;
+ }
+ free(buf);
+ }
+ }
+ if (mangled) {
+ free(mangled);
+ }
+
+ if (skip) {
f = f->next;
+ continue;
+ }
+
+ ASTNode *m = f->impl_trait.methods;
+ while (m) {
+ if (m->func.is_async) {
+ fprintf(out, "Async %s(%s);\n", m->func.name, m->func.args);
+ } else {
+ fprintf(out, "%s %s(%s);\n", m->func.ret_type, m->func.name,
+ m->func.args);
+ }
+ m = m->next;
+ }
+ // RAII: Emit glue prototype
+ if (strcmp(f->impl_trait.trait_name, "Drop") == 0) {
+ char *tname = f->impl_trait.target_type;
+ fprintf(out, "void %s_Drop_glue(%s *self);\n", tname, tname);
+ }
}
+ f = f->next;
+ }
}
// Emit VTable instances for trait implementations.
-void emit_impl_vtables(ParserContext *ctx, FILE *out)
-{
- StructRef *ref = ctx->parsed_impls_list;
- struct
- {
- char *trait;
- char *strct;
- } emitted[1024];
- int count = 0;
-
- while (ref)
- {
- ASTNode *node = ref->node;
- if (node && node->type == NODE_IMPL_TRAIT)
- {
- char *trait = node->impl_trait.trait_name;
- char *strct = node->impl_trait.target_type;
-
- // Filter templates
- char *mangled = replace_string_type(strct);
- ASTNode *def = find_struct_def_codegen(ctx, mangled);
- int skip = 0;
- if (def)
- {
- if (def->type == NODE_STRUCT && def->strct.is_template)
- {
- skip = 1;
- }
- else if (def->type == NODE_ENUM && def->enm.is_template)
- {
- skip = 1;
- }
- }
- else
- {
- char *lt = strchr(strct, '<');
- if (lt)
- {
- int len = lt - strct;
- char *buf = xmalloc(len + 1);
- strncpy(buf, strct, len);
- buf[len] = 0;
- def = find_struct_def_codegen(ctx, buf);
- if (def && def->strct.is_template)
- {
- skip = 1;
- }
- free(buf);
- }
- }
- if (mangled)
- {
- free(mangled);
- }
- if (skip)
- {
- ref = ref->next;
- continue;
- }
-
- // Check duplication
- int dup = 0;
- for (int i = 0; i < count; i++)
- {
- if (strcmp(emitted[i].trait, trait) == 0 && strcmp(emitted[i].strct, strct) == 0)
- {
- dup = 1;
- break;
- }
- }
- if (dup)
- {
- ref = ref->next;
- continue;
- }
-
- emitted[count].trait = trait;
- emitted[count].strct = strct;
- count++;
-
- fprintf(out, "%s_VTable %s_%s_VTable = {", trait, strct, trait);
-
- ASTNode *m = node->impl_trait.methods;
- while (m)
- {
- const char *orig = parse_original_method_name(m->func.name);
- fprintf(out, ".%s = (__typeof__(((%s_VTable*)0)->%s))%s_%s_%s", orig, trait, orig,
- strct, trait, orig);
- if (m->next)
- {
- fprintf(out, ", ");
- }
- m = m->next;
- }
- fprintf(out, "};\n");
+void emit_impl_vtables(ParserContext *ctx, FILE *out) {
+ StructRef *ref = ctx->parsed_impls_list;
+ struct {
+ char *trait;
+ char *strct;
+ } emitted[1024];
+ int count = 0;
+
+ while (ref) {
+ ASTNode *node = ref->node;
+ if (node && node->type == NODE_IMPL_TRAIT) {
+ char *trait = node->impl_trait.trait_name;
+ char *strct = node->impl_trait.target_type;
+
+ // Filter templates
+ char *mangled = replace_string_type(strct);
+ ASTNode *def = find_struct_def_codegen(ctx, mangled);
+ int skip = 0;
+ if (def) {
+ if (def->type == NODE_STRUCT && def->strct.is_template) {
+ skip = 1;
+ } else if (def->type == NODE_ENUM && def->enm.is_template) {
+ skip = 1;
+ }
+ } else {
+ char *lt = strchr(strct, '<');
+ if (lt) {
+ int len = lt - strct;
+ char *buf = xmalloc(len + 1);
+ strncpy(buf, strct, len);
+ buf[len] = 0;
+ def = find_struct_def_codegen(ctx, buf);
+ if (def && def->strct.is_template) {
+ skip = 1;
+ }
+ free(buf);
+ }
+ }
+ if (mangled) {
+ free(mangled);
+ }
+ if (skip) {
+ ref = ref->next;
+ continue;
+ }
+
+ // Check duplication
+ int dup = 0;
+ for (int i = 0; i < count; i++) {
+ if (strcmp(emitted[i].trait, trait) == 0 &&
+ strcmp(emitted[i].strct, strct) == 0) {
+ dup = 1;
+ break;
}
+ }
+ if (dup) {
ref = ref->next;
+ continue;
+ }
+
+ emitted[count].trait = trait;
+ emitted[count].strct = strct;
+ count++;
+
+ fprintf(out, "%s_VTable %s_%s_VTable = {", trait, strct, trait);
+
+ ASTNode *m = node->impl_trait.methods;
+ while (m) {
+ const char *orig = parse_original_method_name(m->func.name);
+ fprintf(out, ".%s = (__typeof__(((%s_VTable*)0)->%s))%s_%s_%s", orig,
+ trait, orig, strct, trait, orig);
+ if (m->next) {
+ fprintf(out, ", ");
+ }
+ m = m->next;
+ }
+ fprintf(out, "};\n");
}
+ ref = ref->next;
+ }
}
// Emit test functions and runner
-void emit_tests_and_runner(ParserContext *ctx, ASTNode *node, FILE *out)
-{
- ASTNode *cur = node;
- int test_count = 0;
- while (cur)
- {
- if (cur->type == NODE_TEST)
- {
- fprintf(out, "static void _z_test_%d() {\n", test_count);
- codegen_walker(ctx, cur->test_stmt.body, out);
- fprintf(out, "}\n");
- test_count++;
- }
- cur = cur->next;
+void emit_tests_and_runner(ParserContext *ctx, ASTNode *node, FILE *out) {
+ ASTNode *cur = node;
+ int test_count = 0;
+ while (cur) {
+ if (cur->type == NODE_TEST) {
+ fprintf(out, "static void _z_test_%d() {\n", test_count);
+ codegen_walker(ctx, cur->test_stmt.body, out);
+ fprintf(out, "}\n");
+ test_count++;
}
- if (test_count > 0)
- {
- fprintf(out, "\nvoid _z_run_tests() {\n");
- for (int i = 0; i < test_count; i++)
- {
- fprintf(out, " _z_test_%d();\n", i);
- }
- fprintf(out, "}\n\n");
- }
- else
- {
- fprintf(out, "void _z_run_tests() {}\n");
+ cur = cur->next;
+ }
+ if (test_count > 0) {
+ fprintf(out, "\nvoid _z_run_tests() {\n");
+ for (int i = 0; i < test_count; i++) {
+ fprintf(out, " _z_test_%d();\n", i);
}
+ fprintf(out, "}\n\n");
+ } else {
+ fprintf(out, "void _z_run_tests() {}\n");
+ }
}
// Emit type definitions-
-void print_type_defs(ParserContext *ctx, FILE *out, ASTNode *nodes)
-{
- if (!g_config.is_freestanding)
- {
- fprintf(out, "typedef char* string;\n");
- }
-
- fprintf(out, "typedef struct { void **data; int len; int cap; } Vec;\n");
- fprintf(out, "#define Vec_new() (Vec){.data=0, .len=0, .cap=0}\n");
- fprintf(out, "void _z_vec_push(Vec *v, void *item) { if(v->len >= v->cap) { "
- "v->cap = v->cap?v->cap*2:8; "
- "v->data = z_realloc(v->data, v->cap * sizeof(void*)); } "
- "v->data[v->len++] = item; }\n");
- fprintf(out, "#define Vec_push(v, i) _z_vec_push(&(v), (void*)(long)(i))\n");
- fprintf(out, "static inline Vec _z_make_vec(int count, ...) { Vec v = {0}; v.cap = "
- "count > 8 ? "
- "count : 8; v.data = z_malloc(v.cap * sizeof(void*)); v.len = 0; va_list "
- "args; "
- "va_start(args, count); for(int i=0; i<count; i++) { v.data[v.len++] = "
- "va_arg(args, void*); } va_end(args); return v; }\n");
-
- if (g_config.is_freestanding)
- {
- fprintf(out, "#define _z_check_bounds(index, limit) ({ __auto_type _i = "
- "(index); if(_i < 0 "
- "|| _i >= (limit)) { z_panic(\"index out of bounds\"); } _i; })\n");
+void print_type_defs(ParserContext *ctx, FILE *out, ASTNode *nodes) {
+ if (!g_config.is_freestanding) {
+ fprintf(out, "typedef char* string;\n");
+ }
+
+ fprintf(out, "typedef struct { void **data; int len; int cap; } Vec;\n");
+ fprintf(out, "#define Vec_new() (Vec){.data=0, .len=0, .cap=0}\n");
+ fprintf(out, "void _z_vec_push(Vec *v, void *item) { if(v->len >= v->cap) { "
+ "v->cap = v->cap?v->cap*2:8; "
+ "v->data = z_realloc(v->data, v->cap * sizeof(void*)); } "
+ "v->data[v->len++] = item; }\n");
+ fprintf(out, "#define Vec_push(v, i) _z_vec_push(&(v), (void*)(long)(i))\n");
+ fprintf(
+ out,
+ "static inline Vec _z_make_vec(int count, ...) { Vec v = {0}; v.cap = "
+ "count > 8 ? "
+ "count : 8; v.data = z_malloc(v.cap * sizeof(void*)); v.len = 0; va_list "
+ "args; "
+ "va_start(args, count); for(int i=0; i<count; i++) { v.data[v.len++] = "
+ "va_arg(args, void*); } va_end(args); return v; }\n");
+
+ if (g_config.is_freestanding) {
+ fprintf(out,
+ "#define _z_check_bounds(index, limit) ({ __auto_type _i = "
+ "(index); if(_i < 0 "
+ "|| _i >= (limit)) { z_panic(\"index out of bounds\"); } _i; })\n");
+ } else {
+ fprintf(out, "#define _z_check_bounds(index, limit) ({ __auto_type _i = "
+ "(index); if(_i < 0 "
+ "|| _i >= (limit)) { fprintf(stderr, \"Index out of bounds: "
+ "%%ld (limit "
+ "%%d)\\n\", (long)_i, (int)(limit)); exit(1); } _i; })\n");
+ }
+
+ SliceType *c = ctx->used_slices;
+ while (c) {
+ fprintf(out,
+ "typedef struct Slice_%s Slice_%s;\nstruct Slice_%s { %s *data; "
+ "int len; int cap; };\n",
+ c->name, c->name, c->name, c->name);
+ c = c->next;
+ }
+
+ TupleType *t = ctx->used_tuples;
+ while (t) {
+ fprintf(out, "typedef struct Tuple_%s Tuple_%s;\nstruct Tuple_%s { ",
+ t->sig, t->sig, t->sig);
+ char *s = xstrdup(t->sig);
+ char *p = strtok(s, "_");
+ int i = 0;
+ while (p) {
+ fprintf(out, "%s v%d; ", p, i++);
+ p = strtok(NULL, "_");
}
- else
- {
- fprintf(out, "#define _z_check_bounds(index, limit) ({ __auto_type _i = "
- "(index); if(_i < 0 "
- "|| _i >= (limit)) { fprintf(stderr, \"Index out of bounds: "
- "%%ld (limit "
- "%%d)\\n\", (long)_i, (int)(limit)); exit(1); } _i; })\n");
+ free(s);
+ fprintf(out, "};\n");
+ t = t->next;
+ }
+ fprintf(out, "\n");
+
+ // FIRST: Emit typedefs for ALL structs and enums in the current compilation
+ // unit (local definitions)
+ ASTNode *local = nodes;
+ while (local) {
+ if (local->type == NODE_STRUCT && !local->strct.is_template) {
+ const char *keyword = local->strct.is_union ? "union" : "struct";
+ fprintf(out, "typedef %s %s %s;\n", keyword, local->strct.name,
+ local->strct.name);
}
-
- SliceType *c = ctx->used_slices;
- while (c)
- {
- fprintf(out,
- "typedef struct Slice_%s Slice_%s;\nstruct Slice_%s { %s *data; "
- "int len; int cap; };\n",
- c->name, c->name, c->name, c->name);
- c = c->next;
+ if (local->type == NODE_ENUM && !local->enm.is_template) {
+ fprintf(out, "typedef struct %s %s;\n", local->enm.name, local->enm.name);
}
-
- TupleType *t = ctx->used_tuples;
- while (t)
- {
- fprintf(out, "typedef struct Tuple_%s Tuple_%s;\nstruct Tuple_%s { ", t->sig, t->sig,
- t->sig);
- char *s = xstrdup(t->sig);
- char *p = strtok(s, "_");
- int i = 0;
- while (p)
- {
- fprintf(out, "%s v%d; ", p, i++);
- p = strtok(NULL, "_");
- }
- free(s);
- fprintf(out, "};\n");
- t = t->next;
+ local = local->next;
+ }
+
+ // THEN: Emit typedefs for instantiated generics
+ Instantiation *i = ctx->instantiations;
+ while (i) {
+ if (i->struct_node->type == NODE_RAW_STMT) {
+ fprintf(out, "%s\n", i->struct_node->raw_stmt.content);
+ } else {
+ fprintf(out, "typedef struct %s %s;\n", i->struct_node->strct.name,
+ i->struct_node->strct.name);
+ codegen_node(ctx, i->struct_node, out);
}
- fprintf(out, "\n");
-
- // FIRST: Emit typedefs for ALL structs and enums in the current compilation
- // unit (local definitions)
- ASTNode *local = nodes;
- while (local)
- {
- if (local->type == NODE_STRUCT && !local->strct.is_template)
- {
- const char *keyword = local->strct.is_union ? "union" : "struct";
- fprintf(out, "typedef %s %s %s;\n", keyword, local->strct.name, local->strct.name);
- }
- if (local->type == NODE_ENUM && !local->enm.is_template)
- {
- fprintf(out, "typedef struct %s %s;\n", local->enm.name, local->enm.name);
- }
- local = local->next;
+ i = i->next;
+ }
+
+ StructRef *sr = ctx->parsed_structs_list;
+ while (sr) {
+ if (sr->node && sr->node->type == NODE_STRUCT &&
+ !sr->node->strct.is_template) {
+ const char *keyword = sr->node->strct.is_union ? "union" : "struct";
+ fprintf(out, "typedef %s %s %s;\n", keyword, sr->node->strct.name,
+ sr->node->strct.name);
}
- // THEN: Emit typedefs for instantiated generics
- Instantiation *i = ctx->instantiations;
- while (i)
- {
- if (i->struct_node->type == NODE_RAW_STMT)
- {
- fprintf(out, "%s\n", i->struct_node->raw_stmt.content);
- }
- else
- {
- fprintf(out, "typedef struct %s %s;\n", i->struct_node->strct.name,
- i->struct_node->strct.name);
- codegen_node(ctx, i->struct_node, out);
- }
- i = i->next;
+ if (sr->node && sr->node->type == NODE_ENUM && !sr->node->enm.is_template) {
+ fprintf(out, "typedef struct %s %s;\n", sr->node->enm.name,
+ sr->node->enm.name);
}
-
- StructRef *sr = ctx->parsed_structs_list;
- while (sr)
- {
- if (sr->node && sr->node->type == NODE_STRUCT && !sr->node->strct.is_template)
- {
- const char *keyword = sr->node->strct.is_union ? "union" : "struct";
- fprintf(out, "typedef %s %s %s;\n", keyword, sr->node->strct.name,
- sr->node->strct.name);
- }
-
- if (sr->node && sr->node->type == NODE_ENUM && !sr->node->enm.is_template)
- {
- fprintf(out, "typedef struct %s %s;\n", sr->node->enm.name, sr->node->enm.name);
- }
- sr = sr->next;
+ sr = sr->next;
+ }
+
+ // Also check instantiated_structs list.
+ ASTNode *inst_s = ctx->instantiated_structs;
+ while (inst_s) {
+ if (inst_s->type == NODE_STRUCT && !inst_s->strct.is_template) {
+ const char *keyword = inst_s->strct.is_union ? "union" : "struct";
+ fprintf(out, "typedef %s %s %s;\n", keyword, inst_s->strct.name,
+ inst_s->strct.name);
}
- // Also check instantiated_structs list.
- ASTNode *inst_s = ctx->instantiated_structs;
- while (inst_s)
- {
- if (inst_s->type == NODE_STRUCT && !inst_s->strct.is_template)
- {
- const char *keyword = inst_s->strct.is_union ? "union" : "struct";
- fprintf(out, "typedef %s %s %s;\n", keyword, inst_s->strct.name, inst_s->strct.name);
- }
-
- if (inst_s->type == NODE_ENUM && !inst_s->enm.is_template)
- {
- fprintf(out, "typedef struct %s %s;\n", inst_s->enm.name, inst_s->enm.name);
- }
- inst_s = inst_s->next;
+ if (inst_s->type == NODE_ENUM && !inst_s->enm.is_template) {
+ fprintf(out, "typedef struct %s %s;\n", inst_s->enm.name,
+ inst_s->enm.name);
}
+ inst_s = inst_s->next;
+ }
}
diff --git a/src/codegen/codegen_main.c b/src/codegen/codegen_main.c
index 3634af3..3b0e4dc 100644
--- a/src/codegen/codegen_main.c
+++ b/src/codegen/codegen_main.c
@@ -7,563 +7,457 @@
#include <string.h>
// Helper: Check if a struct depends on another struct/enum by-value.
-static int struct_depends_on(ASTNode *s1, const char *target_name)
-{
- if (!s1)
- {
- return 0;
- }
-
- // Only structs have dependencies that matter for ordering.
- if (s1->type != NODE_STRUCT)
- {
- return 0;
- }
-
- ASTNode *field = s1->strct.fields;
- while (field)
- {
- if (field->type == NODE_FIELD && field->field.type)
- {
- char *type_str = field->field.type;
- // Skip pointers - they don't create ordering dependency.
- if (strchr(type_str, '*'))
- {
- field = field->next;
- continue;
- }
+static int struct_depends_on(ASTNode *s1, const char *target_name) {
+ if (!s1) {
+ return 0;
+ }
- // Check if this field's type matches target (struct or enum).
- if (strcmp(type_str, target_name) == 0)
- {
- return 1;
- }
- }
+ // Only structs have dependencies that matter for ordering.
+ if (s1->type != NODE_STRUCT) {
+ return 0;
+ }
+
+ ASTNode *field = s1->strct.fields;
+ while (field) {
+ if (field->type == NODE_FIELD && field->field.type) {
+ char *type_str = field->field.type;
+ // Skip pointers - they don't create ordering dependency.
+ if (strchr(type_str, '*')) {
field = field->next;
+ continue;
+ }
+
+ // Check if this field's type matches target (struct or enum).
+ if (strcmp(type_str, target_name) == 0) {
+ return 1;
+ }
}
- return 0;
+ field = field->next;
+ }
+ return 0;
}
// Topologically sort a list of struct/enum nodes.
-static ASTNode *topo_sort_structs(ASTNode *head)
-{
- if (!head)
- {
- return NULL;
+static ASTNode *topo_sort_structs(ASTNode *head) {
+ if (!head) {
+ return NULL;
+ }
+
+ // Count all nodes (structs + enums).
+ int count = 0;
+ ASTNode *n = head;
+ while (n) {
+ if (n->type == NODE_STRUCT || n->type == NODE_ENUM) {
+ count++;
+ }
+ n = n->next;
+ }
+ if (count == 0) {
+ return head;
+ }
+
+ // Build array of all nodes.
+ ASTNode **nodes = malloc(count * sizeof(ASTNode *));
+ int *emitted = calloc(count, sizeof(int));
+ n = head;
+ int idx = 0;
+ while (n) {
+ if (n->type == NODE_STRUCT || n->type == NODE_ENUM) {
+ nodes[idx++] = n;
}
+ n = n->next;
+ }
+
+ // Build order array (indices in emission order).
+ int *order = malloc(count * sizeof(int));
+ int order_idx = 0;
+
+ int changed = 1;
+ int max_iterations = count * count;
+ int iterations = 0;
+
+ while (changed && iterations < max_iterations) {
+ changed = 0;
+ iterations++;
+
+ for (int i = 0; i < count; i++) {
+ if (emitted[i]) {
+ continue;
+ }
+
+ // Enums have no dependencies, emit first.
+ if (nodes[i]->type == NODE_ENUM) {
+ order[order_idx++] = i;
+ emitted[i] = 1;
+ changed = 1;
+ continue;
+ }
+
+ // For structs, check if all dependencies are emitted.
+ int can_emit = 1;
+ for (int j = 0; j < count; j++) {
+ if (i == j || emitted[j]) {
+ continue;
+ }
- // Count all nodes (structs + enums).
- int count = 0;
- ASTNode *n = head;
- while (n)
- {
- if (n->type == NODE_STRUCT || n->type == NODE_ENUM)
- {
- count++;
+ // Get the name of the potential dependency.
+ const char *dep_name = NULL;
+ if (nodes[j]->type == NODE_STRUCT) {
+ dep_name = nodes[j]->strct.name;
+ } else if (nodes[j]->type == NODE_ENUM) {
+ dep_name = nodes[j]->enm.name;
}
- n = n->next;
- }
- if (count == 0)
- {
- return head;
- }
- // Build array of all nodes.
- ASTNode **nodes = malloc(count * sizeof(ASTNode *));
- int *emitted = calloc(count, sizeof(int));
- n = head;
- int idx = 0;
- while (n)
- {
- if (n->type == NODE_STRUCT || n->type == NODE_ENUM)
- {
- nodes[idx++] = n;
+ if (dep_name && struct_depends_on(nodes[i], dep_name)) {
+ can_emit = 0;
+ break;
}
- n = n->next;
- }
+ }
- // Build order array (indices in emission order).
- int *order = malloc(count * sizeof(int));
- int order_idx = 0;
+ if (can_emit) {
+ order[order_idx++] = i;
+ emitted[i] = 1;
+ changed = 1;
+ }
+ }
+ }
- int changed = 1;
- int max_iterations = count * count;
- int iterations = 0;
+ // Add any remaining nodes (cycles).
+ for (int i = 0; i < count; i++) {
+ if (!emitted[i]) {
+ order[order_idx++] = i;
+ }
+ }
+
+ // Now build the linked list in the correct order.
+ ASTNode *result = NULL;
+ ASTNode *result_tail = NULL;
+
+ for (int i = 0; i < order_idx; i++) {
+ ASTNode *node = nodes[order[i]];
+ if (!result) {
+ result = node;
+ result_tail = node;
+ } else {
+ result_tail->next = node;
+ result_tail = node;
+ }
+ }
+ if (result_tail) {
+ result_tail->next = NULL;
+ }
+
+ free(nodes);
+ free(emitted);
+ free(order);
+ return result;
+}
- while (changed && iterations < max_iterations)
- {
- changed = 0;
- iterations++;
+// Main entry point for code generation.
+void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out) {
+ if (node->type == NODE_ROOT) {
+ ASTNode *kids = node->root.children;
+ // Recursive Unwrap of Nested Roots (if accidentally wrapped multiple
+ // times).
+ while (kids && kids->type == NODE_ROOT) {
+ kids = kids->root.children;
+ }
- for (int i = 0; i < count; i++)
- {
- if (emitted[i])
- {
- continue;
- }
+ global_user_structs = kids;
- // Enums have no dependencies, emit first.
- if (nodes[i]->type == NODE_ENUM)
- {
- order[order_idx++] = i;
- emitted[i] = 1;
- changed = 1;
- continue;
- }
+ if (!ctx->skip_preamble) {
+ emit_preamble(ctx, out);
+ }
+ emit_includes_and_aliases(kids, out);
+
+ // Emit Hoisted Code (from plugins)
+ if (ctx->hoist_out) {
+ long pos = ftell(ctx->hoist_out);
+ rewind(ctx->hoist_out);
+ char buf[4096];
+ size_t n;
+ while ((n = fread(buf, 1, sizeof(buf), ctx->hoist_out)) > 0) {
+ fwrite(buf, 1, n, out);
+ }
+ fseek(ctx->hoist_out, pos, SEEK_SET);
+ }
- // For structs, check if all dependencies are emitted.
- int can_emit = 1;
- for (int j = 0; j < count; j++)
- {
- if (i == j || emitted[j])
- {
- continue;
- }
-
- // Get the name of the potential dependency.
- const char *dep_name = NULL;
- if (nodes[j]->type == NODE_STRUCT)
- {
- dep_name = nodes[j]->strct.name;
- }
- else if (nodes[j]->type == NODE_ENUM)
- {
- dep_name = nodes[j]->enm.name;
- }
-
- if (dep_name && struct_depends_on(nodes[i], dep_name))
- {
- can_emit = 0;
- break;
- }
- }
+ ASTNode *merged = NULL;
+ ASTNode *merged_tail = NULL;
+
+ ASTNode *s = ctx->instantiated_structs;
+ while (s) {
+ ASTNode *copy = xmalloc(sizeof(ASTNode));
+ *copy = *s;
+ copy->next = NULL;
+ if (!merged) {
+ merged = copy;
+ merged_tail = copy;
+ } else {
+ merged_tail->next = copy;
+ merged_tail = copy;
+ }
+ s = s->next;
+ }
- if (can_emit)
- {
- order[order_idx++] = i;
- emitted[i] = 1;
- changed = 1;
- }
+ StructRef *sr = ctx->parsed_structs_list;
+ while (sr) {
+ if (sr->node) {
+ ASTNode *copy = xmalloc(sizeof(ASTNode));
+ *copy = *sr->node;
+ copy->next = NULL;
+ if (!merged) {
+ merged = copy;
+ merged_tail = copy;
+ } else {
+ merged_tail->next = copy;
+ merged_tail = copy;
}
+ }
+ sr = sr->next;
}
- // Add any remaining nodes (cycles).
- for (int i = 0; i < count; i++)
- {
- if (!emitted[i])
- {
- order[order_idx++] = i;
+ StructRef *er = ctx->parsed_enums_list;
+ while (er) {
+ if (er->node) {
+ ASTNode *copy = xmalloc(sizeof(ASTNode));
+ *copy = *er->node;
+ copy->next = NULL;
+ if (!merged) {
+ merged = copy;
+ merged_tail = copy;
+ } else {
+ merged_tail->next = copy;
+ merged_tail = copy;
}
+ }
+ er = er->next;
}
- // Now build the linked list in the correct order.
- ASTNode *result = NULL;
- ASTNode *result_tail = NULL;
-
- for (int i = 0; i < order_idx; i++)
- {
- ASTNode *node = nodes[order[i]];
- if (!result)
- {
- result = node;
- result_tail = node;
+ ASTNode *k = kids;
+ while (k) {
+ if (k->type == NODE_STRUCT || k->type == NODE_ENUM) {
+ int found = 0;
+ ASTNode *chk = merged;
+ while (chk) {
+ if (chk->type == k->type) {
+ const char *n1 =
+ (k->type == NODE_STRUCT) ? k->strct.name : k->enm.name;
+ const char *n2 =
+ (chk->type == NODE_STRUCT) ? chk->strct.name : chk->enm.name;
+ if (n1 && n2 && strcmp(n1, n2) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ chk = chk->next;
}
- else
- {
- result_tail->next = node;
- result_tail = node;
+
+ if (!found) {
+ ASTNode *copy = xmalloc(sizeof(ASTNode));
+ *copy = *k;
+ copy->next = NULL;
+ if (!merged) {
+ merged = copy;
+ merged_tail = copy;
+ } else {
+ merged_tail->next = copy;
+ merged_tail = copy;
+ }
}
- }
- if (result_tail)
- {
- result_tail->next = NULL;
+ }
+ k = k->next;
}
- free(nodes);
- free(emitted);
- free(order);
- return result;
-}
+ // Topologically sort.
+ ASTNode *sorted = topo_sort_structs(merged);
-// Main entry point for code generation.
-void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out)
-{
- if (node->type == NODE_ROOT)
- {
- ASTNode *kids = node->root.children;
- // Recursive Unwrap of Nested Roots (if accidentally wrapped multiple
- // times).
- while (kids && kids->type == NODE_ROOT)
- {
- kids = kids->root.children;
- }
+ print_type_defs(ctx, out, sorted);
+ emit_enum_protos(sorted, out);
- global_user_structs = kids;
+ if (sorted) {
+ emit_struct_defs(ctx, sorted, out);
+ }
+ emit_trait_defs(kids, out);
+
+ ASTNode *raw_iter = kids;
+ while (raw_iter) {
+ if (raw_iter->type == NODE_RAW_STMT) {
+ fprintf(out, "%s\n", raw_iter->raw_stmt.content);
+ }
+ raw_iter = raw_iter->next;
+ }
- if (!ctx->skip_preamble)
- {
- emit_preamble(ctx, out);
- }
- emit_includes_and_aliases(kids, out);
-
- // Emit Hoisted Code (from plugins)
- if (ctx->hoist_out)
- {
- long pos = ftell(ctx->hoist_out);
- rewind(ctx->hoist_out);
- char buf[4096];
- size_t n;
- while ((n = fread(buf, 1, sizeof(buf), ctx->hoist_out)) > 0)
- {
- fwrite(buf, 1, n, out);
- }
- fseek(ctx->hoist_out, pos, SEEK_SET);
- }
+ ASTNode *merged_globals = NULL; // Head
- ASTNode *merged = NULL;
- ASTNode *merged_tail = NULL;
-
- ASTNode *s = ctx->instantiated_structs;
- while (s)
- {
- ASTNode *copy = xmalloc(sizeof(ASTNode));
- *copy = *s;
- copy->next = NULL;
- if (!merged)
- {
- merged = copy;
- merged_tail = copy;
- }
- else
- {
- merged_tail->next = copy;
- merged_tail = copy;
- }
- s = s->next;
- }
+ if (ctx->parsed_globals_list) {
+ StructRef *s = ctx->parsed_globals_list;
+ while (s) {
+ ASTNode *copy = xmalloc(sizeof(ASTNode));
+ *copy = *s->node;
+ copy->next = merged_globals;
+ merged_globals = copy;
- StructRef *sr = ctx->parsed_structs_list;
- while (sr)
- {
- if (sr->node)
- {
- ASTNode *copy = xmalloc(sizeof(ASTNode));
- *copy = *sr->node;
- copy->next = NULL;
- if (!merged)
- {
- merged = copy;
- merged_tail = copy;
- }
- else
- {
- merged_tail->next = copy;
- merged_tail = copy;
- }
- }
- sr = sr->next;
- }
+ s = s->next;
+ }
+ }
- StructRef *er = ctx->parsed_enums_list;
- while (er)
- {
- if (er->node)
- {
- ASTNode *copy = xmalloc(sizeof(ASTNode));
- *copy = *er->node;
- copy->next = NULL;
- if (!merged)
- {
- merged = copy;
- merged_tail = copy;
- }
- else
- {
- merged_tail->next = copy;
- merged_tail = copy;
- }
- }
- er = er->next;
+ emit_globals(ctx, merged_globals, out);
+
+ ASTNode *merged_funcs = NULL;
+ ASTNode *merged_funcs_tail = NULL;
+
+ if (ctx->instantiated_funcs) {
+ ASTNode *s = ctx->instantiated_funcs;
+ while (s) {
+ ASTNode *copy = xmalloc(sizeof(ASTNode));
+ *copy = *s;
+ copy->next = NULL;
+ if (!merged_funcs) {
+ merged_funcs = copy;
+ merged_funcs_tail = copy;
+ } else {
+ merged_funcs_tail->next = copy;
+ merged_funcs_tail = copy;
}
+ s = s->next;
+ }
+ }
- ASTNode *k = kids;
- while (k)
- {
- if (k->type == NODE_STRUCT || k->type == NODE_ENUM)
- {
- int found = 0;
- ASTNode *chk = merged;
- while (chk)
- {
- if (chk->type == k->type)
- {
- const char *n1 = (k->type == NODE_STRUCT) ? k->strct.name : k->enm.name;
- const char *n2 =
- (chk->type == NODE_STRUCT) ? chk->strct.name : chk->enm.name;
- if (n1 && n2 && strcmp(n1, n2) == 0)
- {
- found = 1;
- break;
- }
- }
- chk = chk->next;
- }
-
- if (!found)
- {
- ASTNode *copy = xmalloc(sizeof(ASTNode));
- *copy = *k;
- copy->next = NULL;
- if (!merged)
- {
- merged = copy;
- merged_tail = copy;
- }
- else
- {
- merged_tail->next = copy;
- merged_tail = copy;
- }
- }
- }
- k = k->next;
+ if (ctx->parsed_funcs_list) {
+ StructRef *s = ctx->parsed_funcs_list;
+ while (s) {
+ ASTNode *copy = xmalloc(sizeof(ASTNode));
+ *copy = *s->node;
+ copy->next = NULL;
+ if (!merged_funcs) {
+ merged_funcs = copy;
+ merged_funcs_tail = copy;
+ } else {
+ merged_funcs_tail->next = copy;
+ merged_funcs_tail = copy;
}
+ s = s->next;
+ }
+ }
- // Topologically sort.
- ASTNode *sorted = topo_sort_structs(merged);
+ if (ctx->parsed_impls_list) {
+ StructRef *s = ctx->parsed_impls_list;
+ while (s) {
+ ASTNode *copy = xmalloc(sizeof(ASTNode));
+ *copy = *s->node;
+ copy->next = NULL;
+ if (!merged_funcs) {
+ merged_funcs = copy;
+ merged_funcs_tail = copy;
+ } else {
+ merged_funcs_tail->next = copy;
+ merged_funcs_tail = copy;
+ }
+ s = s->next;
+ }
+ }
- print_type_defs(ctx, out, sorted);
- emit_enum_protos(sorted, out);
+ emit_protos(merged_funcs, out);
- if (sorted)
- {
- emit_struct_defs(ctx, sorted, out);
- }
- emit_trait_defs(kids, out);
-
- ASTNode *raw_iter = kids;
- while (raw_iter)
- {
- if (raw_iter->type == NODE_RAW_STMT)
- {
- fprintf(out, "%s\n", raw_iter->raw_stmt.content);
- }
- raw_iter = raw_iter->next;
- }
+ emit_impl_vtables(ctx, out);
- ASTNode *merged_globals = NULL; // Head
+ emit_lambda_defs(ctx, out);
- if (ctx->parsed_globals_list)
- {
- StructRef *s = ctx->parsed_globals_list;
- while (s)
- {
- ASTNode *copy = xmalloc(sizeof(ASTNode));
- *copy = *s->node;
- copy->next = merged_globals;
- merged_globals = copy;
+ emit_tests_and_runner(ctx, kids, out);
- s = s->next;
- }
+ ASTNode *iter = merged_funcs;
+ while (iter) {
+ if (iter->type == NODE_IMPL) {
+ char *sname = iter->impl.struct_name;
+ if (!sname) {
+ iter = iter->next;
+ continue;
}
- emit_globals(ctx, merged_globals, out);
-
- ASTNode *merged_funcs = NULL;
- ASTNode *merged_funcs_tail = NULL;
-
- if (ctx->instantiated_funcs)
- {
- ASTNode *s = ctx->instantiated_funcs;
- while (s)
- {
- ASTNode *copy = xmalloc(sizeof(ASTNode));
- *copy = *s;
- copy->next = NULL;
- if (!merged_funcs)
- {
- merged_funcs = copy;
- merged_funcs_tail = copy;
- }
- else
- {
- merged_funcs_tail->next = copy;
- merged_funcs_tail = copy;
- }
- s = s->next;
+ char *mangled = replace_string_type(sname);
+ ASTNode *def = find_struct_def_codegen(ctx, mangled);
+ int skip = 0;
+ if (def) {
+ if (def->type == NODE_STRUCT && def->strct.is_template) {
+ skip = 1;
+ } else if (def->type == NODE_ENUM && def->enm.is_template) {
+ skip = 1;
+ }
+ } else {
+ char *lt = strchr(sname, '<');
+ if (lt) {
+ int len = lt - sname;
+ char *buf = xmalloc(len + 1);
+ strncpy(buf, sname, len);
+ buf[len] = 0;
+ def = find_struct_def_codegen(ctx, buf);
+ if (def && def->strct.is_template) {
+ skip = 1;
}
+ free(buf);
+ }
}
-
- if (ctx->parsed_funcs_list)
- {
- StructRef *s = ctx->parsed_funcs_list;
- while (s)
- {
- ASTNode *copy = xmalloc(sizeof(ASTNode));
- *copy = *s->node;
- copy->next = NULL;
- if (!merged_funcs)
- {
- merged_funcs = copy;
- merged_funcs_tail = copy;
- }
- else
- {
- merged_funcs_tail->next = copy;
- merged_funcs_tail = copy;
- }
- s = s->next;
- }
+ if (mangled) {
+ free(mangled);
}
-
- if (ctx->parsed_impls_list)
- {
- StructRef *s = ctx->parsed_impls_list;
- while (s)
- {
- ASTNode *copy = xmalloc(sizeof(ASTNode));
- *copy = *s->node;
- copy->next = NULL;
- if (!merged_funcs)
- {
- merged_funcs = copy;
- merged_funcs_tail = copy;
- }
- else
- {
- merged_funcs_tail->next = copy;
- merged_funcs_tail = copy;
- }
- s = s->next;
- }
+ if (skip) {
+ iter = iter->next;
+ continue;
}
-
- emit_protos(merged_funcs, out);
-
- emit_impl_vtables(ctx, out);
-
- emit_lambda_defs(ctx, out);
-
- emit_tests_and_runner(ctx, kids, out);
-
- ASTNode *iter = merged_funcs;
- while (iter)
- {
- if (iter->type == NODE_IMPL)
- {
- char *sname = iter->impl.struct_name;
- if (!sname)
- {
- iter = iter->next;
- continue;
- }
-
- char *mangled = replace_string_type(sname);
- ASTNode *def = find_struct_def_codegen(ctx, mangled);
- int skip = 0;
- if (def)
- {
- if (def->type == NODE_STRUCT && def->strct.is_template)
- {
- skip = 1;
- }
- else if (def->type == NODE_ENUM && def->enm.is_template)
- {
- skip = 1;
- }
- }
- else
- {
- char *lt = strchr(sname, '<');
- if (lt)
- {
- int len = lt - sname;
- char *buf = xmalloc(len + 1);
- strncpy(buf, sname, len);
- buf[len] = 0;
- def = find_struct_def_codegen(ctx, buf);
- if (def && def->strct.is_template)
- {
- skip = 1;
- }
- free(buf);
- }
- }
- if (mangled)
- {
- free(mangled);
- }
- if (skip)
- {
- iter = iter->next;
- continue;
- }
- }
- if (iter->type == NODE_IMPL_TRAIT)
- {
- char *sname = iter->impl_trait.target_type;
- if (!sname)
- {
- iter = iter->next;
- continue;
- }
-
- char *mangled = replace_string_type(sname);
- ASTNode *def = find_struct_def_codegen(ctx, mangled);
- int skip = 0;
- if (def)
- {
- if (def->strct.is_template)
- {
- skip = 1;
- }
- }
- else
- {
- char *lt = strchr(sname, '<');
- if (lt)
- {
- int len = lt - sname;
- char *buf = xmalloc(len + 1);
- strncpy(buf, sname, len);
- buf[len] = 0;
- def = find_struct_def_codegen(ctx, buf);
- if (def && def->strct.is_template)
- {
- skip = 1;
- }
- free(buf);
- }
- }
- if (mangled)
- {
- free(mangled);
- }
- if (skip)
- {
- iter = iter->next;
- continue;
- }
- }
- codegen_node_single(ctx, iter, out);
- iter = iter->next;
+ }
+ if (iter->type == NODE_IMPL_TRAIT) {
+ char *sname = iter->impl_trait.target_type;
+ if (!sname) {
+ iter = iter->next;
+ continue;
}
- int has_user_main = 0;
- ASTNode *chk = merged_funcs;
- while (chk)
- {
- if (chk->type == NODE_FUNCTION && strcmp(chk->func.name, "main") == 0)
- {
- has_user_main = 1;
- break;
+ char *mangled = replace_string_type(sname);
+ ASTNode *def = find_struct_def_codegen(ctx, mangled);
+ int skip = 0;
+ if (def) {
+ if (def->strct.is_template) {
+ skip = 1;
+ }
+ } else {
+ char *lt = strchr(sname, '<');
+ if (lt) {
+ int len = lt - sname;
+ char *buf = xmalloc(len + 1);
+ strncpy(buf, sname, len);
+ buf[len] = 0;
+ def = find_struct_def_codegen(ctx, buf);
+ if (def && def->strct.is_template) {
+ skip = 1;
}
- chk = chk->next;
+ free(buf);
+ }
}
-
- if (!has_user_main)
- {
- fprintf(out, "\nint main() { _z_run_tests(); return 0; }\n");
+ if (mangled) {
+ free(mangled);
+ }
+ if (skip) {
+ iter = iter->next;
+ continue;
}
+ }
+ codegen_node_single(ctx, iter, out);
+ iter = iter->next;
+ }
+
+ int has_user_main = 0;
+ ASTNode *chk = merged_funcs;
+ while (chk) {
+ if (chk->type == NODE_FUNCTION && strcmp(chk->func.name, "main") == 0) {
+ has_user_main = 1;
+ break;
+ }
+ chk = chk->next;
+ }
+
+ if (!has_user_main) {
+ fprintf(out, "\nint main() { _z_run_tests(); return 0; }\n");
}
+ }
}
diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c
index 2feb757..c10685b 100644
--- a/src/codegen/codegen_utils.c
+++ b/src/codegen/codegen_utils.c
@@ -17,505 +17,417 @@ int defer_count = 0;
ASTNode *g_current_lambda = NULL;
// 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;
-
- char *bracket = strchr(type_str, '[');
-
- if (bracket)
- {
- int base_len = bracket - type_str;
- fprintf(out, "%.*s %s%s", base_len, type_str, var_name, bracket);
- }
- else
- {
- fprintf(out, "%s %s", type_str, var_name);
- }
+void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str,
+ const char *var_name) {
+ (void)ctx;
+
+ char *bracket = strchr(type_str, '[');
+
+ if (bracket) {
+ int base_len = bracket - type_str;
+ fprintf(out, "%.*s %s%s", base_len, type_str, var_name, bracket);
+ } else {
+ fprintf(out, "%s %s", type_str, var_name);
+ }
}
// Find struct definition
-ASTNode *find_struct_def_codegen(ParserContext *ctx, const char *name)
-{
- if (!name)
- {
- return NULL;
- }
- ASTNode *s = global_user_structs;
- while (s)
- {
- if (s->type == NODE_STRUCT && strcmp(s->strct.name, name) == 0 && !s->strct.is_incomplete)
- {
- return s;
- }
- s = s->next;
+ASTNode *find_struct_def_codegen(ParserContext *ctx, const char *name) {
+ if (!name) {
+ return NULL;
+ }
+ ASTNode *s = global_user_structs;
+ while (s) {
+ if (s->type == NODE_STRUCT && strcmp(s->strct.name, name) == 0 &&
+ !s->strct.is_incomplete) {
+ return s;
}
-
- // Check parsed structs list (imports)-
- StructRef *sr = ctx->parsed_structs_list;
- while (sr)
- {
- if (sr->node && sr->node->type == NODE_STRUCT && strcmp(sr->node->strct.name, name) == 0 &&
- !sr->node->strct.is_incomplete)
- {
- return sr->node;
- }
- sr = sr->next;
+ s = s->next;
+ }
+
+ // Check parsed structs list (imports)-
+ StructRef *sr = ctx->parsed_structs_list;
+ while (sr) {
+ if (sr->node && sr->node->type == NODE_STRUCT &&
+ strcmp(sr->node->strct.name, name) == 0 &&
+ !sr->node->strct.is_incomplete) {
+ return sr->node;
}
- s = ctx->instantiated_structs;
- while (s)
- {
- if (s->type == NODE_STRUCT && strcmp(s->strct.name, name) == 0 && !s->strct.is_incomplete)
- {
- return s;
- }
- s = s->next;
+ sr = sr->next;
+ }
+ s = ctx->instantiated_structs;
+ while (s) {
+ if (s->type == NODE_STRUCT && strcmp(s->strct.name, name) == 0 &&
+ !s->strct.is_incomplete) {
+ return s;
}
- return NULL;
+ s = s->next;
+ }
+ return NULL;
}
// Get field type from struct.
-char *get_field_type_str(ParserContext *ctx, const char *struct_name, const char *field_name)
-{
- char clean_name[256];
- strncpy(clean_name, struct_name, sizeof(clean_name) - 1);
- clean_name[sizeof(clean_name) - 1] = 0;
-
- char *ptr = strchr(clean_name, '<');
- if (ptr)
- {
- *ptr = 0;
- }
-
- ASTNode *def = find_struct_def_codegen(ctx, clean_name);
- if (!def)
- {
- return NULL;
- }
+char *get_field_type_str(ParserContext *ctx, const char *struct_name,
+ const char *field_name) {
+ char clean_name[256];
+ strncpy(clean_name, struct_name, sizeof(clean_name) - 1);
+ clean_name[sizeof(clean_name) - 1] = 0;
+
+ char *ptr = strchr(clean_name, '<');
+ if (ptr) {
+ *ptr = 0;
+ }
+
+ ASTNode *def = find_struct_def_codegen(ctx, clean_name);
+ if (!def) {
+ return NULL;
+ }
- ASTNode *f = def->strct.fields;
- while (f)
- {
- if (strcmp(f->field.name, field_name) == 0)
- {
- return f->field.type;
- }
- f = f->next;
+ ASTNode *f = def->strct.fields;
+ while (f) {
+ if (strcmp(f->field.name, field_name) == 0) {
+ return f->field.type;
}
- return NULL;
+ f = f->next;
+ }
+ return NULL;
}
// Type inference.
-char *infer_type(ParserContext *ctx, ASTNode *node)
-{
- if (!node)
- {
- return NULL;
+char *infer_type(ParserContext *ctx, ASTNode *node) {
+ if (!node) {
+ return NULL;
+ }
+ if (node->resolved_type && strcmp(node->resolved_type, "unknown") != 0) {
+ return node->resolved_type;
+ }
+
+ if (node->type == NODE_EXPR_LITERAL) {
+ if (node->type_info) {
+ return type_to_string(node->type_info);
}
- if (node->resolved_type && strcmp(node->resolved_type, "unknown") != 0)
- {
- return node->resolved_type;
+ return NULL;
+ }
+
+ if (node->type == NODE_EXPR_VAR) {
+ Symbol *sym = find_symbol_entry(ctx, node->var_ref.name);
+ if (sym) {
+ if (sym->type_name) {
+ return sym->type_name;
+ }
+ if (sym->type_info) {
+ return type_to_string(sym->type_info);
+ }
}
+ }
- if (node->type == NODE_EXPR_LITERAL)
- {
- if (node->type_info)
- {
- return type_to_string(node->type_info);
+ if (node->type == NODE_EXPR_CALL) {
+ if (node->call.callee->type == NODE_EXPR_VAR) {
+ FuncSig *sig = find_func(ctx, node->call.callee->var_ref.name);
+ if (sig) {
+ if (sig->is_async) {
+ return "Async";
}
- return NULL;
- }
-
- if (node->type == NODE_EXPR_VAR)
- {
- Symbol *sym = find_symbol_entry(ctx, node->var_ref.name);
- if (sym)
- {
- if (sym->type_name)
- {
- return sym->type_name;
- }
- if (sym->type_info)
- {
- return type_to_string(sym->type_info);
- }
+ if (sig->ret_type) {
+ return type_to_string(sig->ret_type);
}
- }
+ }
- if (node->type == NODE_EXPR_CALL)
- {
- if (node->call.callee->type == NODE_EXPR_VAR)
- {
- FuncSig *sig = find_func(ctx, node->call.callee->var_ref.name);
- if (sig)
- {
- if (sig->is_async)
- {
- return "Async";
- }
- if (sig->ret_type)
- {
- return type_to_string(sig->ret_type);
- }
- }
-
- // Fallback for known stdlib memory functions.
- if (strcmp(node->call.callee->var_ref.name, "malloc") == 0 ||
- strcmp(node->call.callee->var_ref.name, "calloc") == 0 ||
- strcmp(node->call.callee->var_ref.name, "realloc") == 0)
- {
- return "void*";
- }
- ASTNode *sdef = find_struct_def_codegen(ctx, node->call.callee->var_ref.name);
- if (sdef)
- {
- return node->call.callee->var_ref.name;
- }
- }
- // Method call: target.method() - look up Type_method signature.
- if (node->call.callee->type == NODE_EXPR_MEMBER)
- {
- char *target_type = infer_type(ctx, node->call.callee->member.target);
- if (target_type)
- {
- char clean_type[256];
- strcpy(clean_type, target_type);
- char *ptr = strchr(clean_type, '*');
- if (ptr)
- {
- *ptr = 0;
- }
-
- char *base = clean_type;
- if (strncmp(base, "struct ", 7) == 0)
- {
- base += 7;
- }
-
- char func_name[512];
- sprintf(func_name, "%s_%s", base, node->call.callee->member.field);
-
- FuncSig *sig = find_func(ctx, func_name);
- if (sig && sig->ret_type)
- {
- return type_to_string(sig->ret_type);
- }
- }
+ // Fallback for known stdlib memory functions.
+ if (strcmp(node->call.callee->var_ref.name, "malloc") == 0 ||
+ strcmp(node->call.callee->var_ref.name, "calloc") == 0 ||
+ strcmp(node->call.callee->var_ref.name, "realloc") == 0) {
+ return "void*";
+ }
+ ASTNode *sdef =
+ find_struct_def_codegen(ctx, node->call.callee->var_ref.name);
+ if (sdef) {
+ return node->call.callee->var_ref.name;
+ }
+ }
+ // Method call: target.method() - look up Type_method signature.
+ if (node->call.callee->type == NODE_EXPR_MEMBER) {
+ char *target_type = infer_type(ctx, node->call.callee->member.target);
+ if (target_type) {
+ char clean_type[256];
+ strcpy(clean_type, target_type);
+ char *ptr = strchr(clean_type, '*');
+ if (ptr) {
+ *ptr = 0;
}
- if (node->call.callee->type == NODE_EXPR_VAR)
- {
- Symbol *sym = find_symbol_entry(ctx, node->call.callee->var_ref.name);
- if (sym && sym->type_info && sym->type_info->kind == TYPE_FUNCTION &&
- sym->type_info->inner)
- {
- return type_to_string(sym->type_info->inner);
- }
+ char *base = clean_type;
+ if (strncmp(base, "struct ", 7) == 0) {
+ base += 7;
}
- }
- if (node->type == NODE_TRY)
- {
- char *inner_type = infer_type(ctx, node->try_stmt.expr);
- if (inner_type)
- {
- // Extract T from Result<T> or Option<T>
- char *start = strchr(inner_type, '<');
- if (start)
- {
- start++; // Skip <
- char *end = strrchr(inner_type, '>');
- if (end && end > start)
- {
- int len = end - start;
- char *extracted = xmalloc(len + 1);
- strncpy(extracted, start, len);
- extracted[len] = 0;
- return extracted;
- }
- }
- }
- }
+ char func_name[512];
+ sprintf(func_name, "%s_%s", base, node->call.callee->member.field);
- if (node->type == NODE_EXPR_MEMBER)
- {
- char *parent_type = infer_type(ctx, node->member.target);
- if (!parent_type)
- {
- return NULL;
+ FuncSig *sig = find_func(ctx, func_name);
+ if (sig && sig->ret_type) {
+ return type_to_string(sig->ret_type);
}
+ }
+ }
- char clean_name[256];
- strcpy(clean_name, parent_type);
- char *ptr = strchr(clean_name, '*');
- if (ptr)
- {
- *ptr = 0;
- }
+ if (node->call.callee->type == NODE_EXPR_VAR) {
+ Symbol *sym = find_symbol_entry(ctx, node->call.callee->var_ref.name);
+ if (sym && sym->type_info && sym->type_info->kind == TYPE_FUNCTION &&
+ sym->type_info->inner) {
+ return type_to_string(sym->type_info->inner);
+ }
+ }
+ }
+
+ if (node->type == NODE_TRY) {
+ char *inner_type = infer_type(ctx, node->try_stmt.expr);
+ if (inner_type) {
+ // Extract T from Result<T> or Option<T>
+ char *start = strchr(inner_type, '<');
+ if (start) {
+ start++; // Skip <
+ char *end = strrchr(inner_type, '>');
+ if (end && end > start) {
+ int len = end - start;
+ char *extracted = xmalloc(len + 1);
+ strncpy(extracted, start, len);
+ extracted[len] = 0;
+ return extracted;
+ }
+ }
+ }
+ }
- return get_field_type_str(ctx, clean_name, node->member.field);
+ if (node->type == NODE_EXPR_MEMBER) {
+ char *parent_type = infer_type(ctx, node->member.target);
+ if (!parent_type) {
+ return NULL;
}
- if (node->type == NODE_EXPR_BINARY)
- {
- if (strcmp(node->binary.op, "??") == 0)
- {
- return infer_type(ctx, node->binary.left);
- }
+ char clean_name[256];
+ strcpy(clean_name, parent_type);
+ char *ptr = strchr(clean_name, '*');
+ if (ptr) {
+ *ptr = 0;
+ }
- const char *op = node->binary.op;
- char *left_type = infer_type(ctx, node->binary.left);
- char *right_type = infer_type(ctx, node->binary.right);
+ return get_field_type_str(ctx, clean_name, node->member.field);
+ }
- int is_logical = (strcmp(op, "&&") == 0 || strcmp(op, "||") == 0 || strcmp(op, "==") == 0 ||
- strcmp(op, "!=") == 0 || strcmp(op, "<") == 0 || strcmp(op, ">") == 0 ||
- strcmp(op, "<=") == 0 || strcmp(op, ">=") == 0);
+ if (node->type == NODE_EXPR_BINARY) {
+ if (strcmp(node->binary.op, "??") == 0) {
+ return infer_type(ctx, node->binary.left);
+ }
- if (is_logical)
- {
- return xstrdup("int");
- }
+ const char *op = node->binary.op;
+ char *left_type = infer_type(ctx, node->binary.left);
+ char *right_type = infer_type(ctx, node->binary.right);
- if (left_type && strcmp(left_type, "usize") == 0)
- {
- return "usize";
- }
- if (right_type && strcmp(right_type, "usize") == 0)
- {
- return "usize";
- }
- if (left_type && strcmp(left_type, "double") == 0)
- {
- return "double";
- }
+ int is_logical = (strcmp(op, "&&") == 0 || strcmp(op, "||") == 0 ||
+ strcmp(op, "==") == 0 || strcmp(op, "!=") == 0 ||
+ strcmp(op, "<") == 0 || strcmp(op, ">") == 0 ||
+ strcmp(op, "<=") == 0 || strcmp(op, ">=") == 0);
- return left_type ? left_type : right_type;
+ if (is_logical) {
+ return xstrdup("int");
}
- if (node->type == NODE_MATCH)
- {
- ASTNode *case_node = node->match_stmt.cases;
- while (case_node)
- {
- char *type = infer_type(ctx, case_node->match_case.body);
- if (type && strcmp(type, "void") != 0 && strcmp(type, "unknown") != 0)
- {
- return type;
- }
- case_node = case_node->next;
- }
- return NULL;
+ if (left_type && strcmp(left_type, "usize") == 0) {
+ return "usize";
}
-
- if (node->type == NODE_EXPR_INDEX)
- {
- char *array_type = infer_type(ctx, node->index.array);
- if (array_type)
- {
- // If T*, returns T. If T[], returns T.
- char *ptr = strrchr(array_type, '*');
- if (ptr)
- {
- int len = ptr - array_type;
- char *buf = xmalloc(len + 1);
- strncpy(buf, array_type, len);
- buf[len] = 0;
- return buf;
- }
- }
- return "int";
+ if (right_type && strcmp(right_type, "usize") == 0) {
+ return "usize";
+ }
+ if (left_type && strcmp(left_type, "double") == 0) {
+ return "double";
}
- if (node->type == NODE_EXPR_UNARY)
- {
- if (strcmp(node->unary.op, "&") == 0)
- {
- char *inner = infer_type(ctx, node->unary.operand);
- if (inner)
- {
- char *buf = xmalloc(strlen(inner) + 2);
- sprintf(buf, "%s*", inner);
- return buf;
- }
- }
- if (strcmp(node->unary.op, "*") == 0)
- {
- char *inner = infer_type(ctx, node->unary.operand);
- if (inner)
- {
- char *ptr = strchr(inner, '*');
- if (ptr)
- {
- // Return base type (naive)
- int len = ptr - inner;
- char *dup = xmalloc(len + 1);
- strncpy(dup, inner, len);
- dup[len] = 0;
- return dup;
- }
- }
- }
- return infer_type(ctx, node->unary.operand);
+ return left_type ? left_type : right_type;
+ }
+
+ if (node->type == NODE_MATCH) {
+ ASTNode *case_node = node->match_stmt.cases;
+ while (case_node) {
+ char *type = infer_type(ctx, case_node->match_case.body);
+ if (type && strcmp(type, "void") != 0 && strcmp(type, "unknown") != 0) {
+ return type;
+ }
+ case_node = case_node->next;
+ }
+ return NULL;
+ }
+
+ if (node->type == NODE_EXPR_INDEX) {
+ char *array_type = infer_type(ctx, node->index.array);
+ if (array_type) {
+ // If T*, returns T. If T[], returns T.
+ char *ptr = strrchr(array_type, '*');
+ if (ptr) {
+ int len = ptr - array_type;
+ char *buf = xmalloc(len + 1);
+ strncpy(buf, array_type, len);
+ buf[len] = 0;
+ return buf;
+ }
+ }
+ return "int";
+ }
+
+ if (node->type == NODE_EXPR_UNARY) {
+ if (strcmp(node->unary.op, "&") == 0) {
+ char *inner = infer_type(ctx, node->unary.operand);
+ if (inner) {
+ char *buf = xmalloc(strlen(inner) + 2);
+ sprintf(buf, "%s*", inner);
+ return buf;
+ }
+ }
+ if (strcmp(node->unary.op, "*") == 0) {
+ char *inner = infer_type(ctx, node->unary.operand);
+ if (inner) {
+ char *ptr = strchr(inner, '*');
+ if (ptr) {
+ // Return base type (naive)
+ int len = ptr - inner;
+ char *dup = xmalloc(len + 1);
+ strncpy(dup, inner, len);
+ dup[len] = 0;
+ return dup;
+ }
+ }
+ }
+ return infer_type(ctx, node->unary.operand);
+ }
+
+ if (node->type == NODE_AWAIT) {
+ // Infer underlying type T from await Async<T>
+ // If it's a direct call await foo(), we know T from foo's signature.
+ if (node->unary.operand->type == NODE_EXPR_CALL &&
+ node->unary.operand->call.callee->type == NODE_EXPR_VAR) {
+ FuncSig *sig =
+ find_func(ctx, node->unary.operand->call.callee->var_ref.name);
+ if (sig && sig->ret_type) {
+ return type_to_string(sig->ret_type);
+ }
}
- if (node->type == NODE_AWAIT)
- {
- // Infer underlying type T from await Async<T>
- // If it's a direct call await foo(), we know T from foo's signature.
- if (node->unary.operand->type == NODE_EXPR_CALL &&
- node->unary.operand->call.callee->type == NODE_EXPR_VAR)
- {
- FuncSig *sig = find_func(ctx, node->unary.operand->call.callee->var_ref.name);
- if (sig && sig->ret_type)
- {
- return type_to_string(sig->ret_type);
- }
- }
+ return "void*";
+ }
- return "void*";
- }
+ if (node->type == NODE_EXPR_CAST) {
+ return node->cast.target_type;
+ }
- if (node->type == NODE_EXPR_CAST)
- {
- return node->cast.target_type;
- }
+ if (node->type == NODE_EXPR_STRUCT_INIT) {
+ return node->struct_init.struct_name;
+ }
- if (node->type == NODE_EXPR_STRUCT_INIT)
- {
- return node->struct_init.struct_name;
+ if (node->type == NODE_EXPR_LITERAL) {
+ if (node->literal.type_kind == TOK_STRING) {
+ return "string";
}
-
- if (node->type == NODE_EXPR_LITERAL)
- {
- if (node->literal.type_kind == TOK_STRING)
- {
- return "string";
- }
- if (node->literal.type_kind == TOK_CHAR)
- {
- return "char";
- }
- if (node->literal.type_kind == 1)
- {
- return "double";
- }
- return "int";
+ if (node->literal.type_kind == TOK_CHAR) {
+ return "char";
+ }
+ if (node->literal.type_kind == 1) {
+ return "double";
}
+ return "int";
+ }
- return NULL;
+ return NULL;
}
// Extract variable names from argument string.
-char *extract_call_args(const char *args)
-{
- if (!args || strlen(args) == 0)
- {
- return xstrdup("");
+char *extract_call_args(const char *args) {
+ if (!args || strlen(args) == 0) {
+ return xstrdup("");
+ }
+ char *out = xmalloc(strlen(args) + 1);
+ out[0] = 0;
+
+ char *dup = xstrdup(args);
+ char *p = strtok(dup, ",");
+ while (p) {
+ while (*p == ' ') {
+ p++;
}
- char *out = xmalloc(strlen(args) + 1);
- out[0] = 0;
-
- char *dup = xstrdup(args);
- char *p = strtok(dup, ",");
- while (p)
- {
- while (*p == ' ')
- {
- p++;
- }
- char *last_space = strrchr(p, ' ');
- char *ptr_star = strrchr(p, '*');
-
- char *name = p;
- if (last_space)
- {
- name = last_space + 1;
- }
- if (ptr_star && ptr_star > last_space)
- {
- name = ptr_star + 1;
- }
+ char *last_space = strrchr(p, ' ');
+ char *ptr_star = strrchr(p, '*');
- if (strlen(out) > 0)
- {
- strcat(out, ", ");
- }
- strcat(out, name);
+ char *name = p;
+ if (last_space) {
+ name = last_space + 1;
+ }
+ if (ptr_star && ptr_star > last_space) {
+ name = ptr_star + 1;
+ }
- p = strtok(NULL, ",");
+ if (strlen(out) > 0) {
+ strcat(out, ", ");
}
- free(dup);
- return out;
+ strcat(out, name);
+
+ p = strtok(NULL, ",");
+ }
+ free(dup);
+ return out;
}
// Parse original method name from mangled name.
-const char *parse_original_method_name(const char *mangled)
-{
- const char *last = strrchr(mangled, '_');
- return last ? last + 1 : mangled;
+const char *parse_original_method_name(const char *mangled) {
+ const char *last = strrchr(mangled, '_');
+ return last ? last + 1 : mangled;
}
// Replace string type in arguments.
-char *replace_string_type(const char *args)
-{
- if (!args)
- {
- return NULL;
- }
- char *res = xmalloc(strlen(args) * 2 + 1);
- res[0] = 0;
- const char *p = args;
- while (*p)
- {
- const char *match = strstr(p, "string ");
- if (match)
- {
- if (match > args && (isalnum(*(match - 1)) || *(match - 1) == '_'))
- {
- strncat(res, p, match - p + 6);
- p = match + 6;
- }
- else
- {
- strncat(res, p, match - p);
- strcat(res, "const char* ");
- p = match + 7;
- }
- }
- else
- {
- strcat(res, p);
- break;
- }
+char *replace_string_type(const char *args) {
+ if (!args) {
+ return NULL;
+ }
+ char *res = xmalloc(strlen(args) * 2 + 1);
+ res[0] = 0;
+ const char *p = args;
+ while (*p) {
+ const char *match = strstr(p, "string ");
+ if (match) {
+ if (match > args && (isalnum(*(match - 1)) || *(match - 1) == '_')) {
+ strncat(res, p, match - p + 6);
+ p = match + 6;
+ } else {
+ strncat(res, p, match - p);
+ strcat(res, "const char* ");
+ p = match + 7;
+ }
+ } else {
+ strcat(res, p);
+ break;
}
- return res;
+ }
+ return res;
}
// Helper to emit auto type or fallback.
-void emit_auto_type(ParserContext *ctx, ASTNode *init_expr, Token t, FILE *out)
-{
- char *inferred = NULL;
- if (init_expr)
- {
- inferred = infer_type(ctx, init_expr);
- }
-
- if (inferred && strcmp(inferred, "__auto_type") != 0 && strcmp(inferred, "unknown") != 0)
- {
- fprintf(out, "%s", inferred);
- }
- else
- {
- if (strstr(g_config.cc, "tcc"))
- {
- zpanic_with_suggestion(t,
- "Type inference failed for variable initialization and TCC does "
- "not support __auto_type",
- "Please specify the type explicitly: 'var x: Type = ...'");
- }
- else
- {
- fprintf(out, "__auto_type");
- }
+void emit_auto_type(ParserContext *ctx, ASTNode *init_expr, Token t,
+ FILE *out) {
+ char *inferred = NULL;
+ if (init_expr) {
+ inferred = infer_type(ctx, init_expr);
+ }
+
+ if (inferred && strcmp(inferred, "__auto_type") != 0 &&
+ strcmp(inferred, "unknown") != 0) {
+ fprintf(out, "%s", inferred);
+ } else {
+ if (strstr(g_config.cc, "tcc")) {
+ zpanic_with_suggestion(
+ t,
+ "Type inference failed for variable initialization and TCC does "
+ "not support __auto_type",
+ "Please specify the type explicitly: 'var x: Type = ...'");
+ } else {
+ fprintf(out, "__auto_type");
}
+ }
}
diff --git a/src/lexer/token.c b/src/lexer/token.c
index cd662d9..01e414f 100644
--- a/src/lexer/token.c
+++ b/src/lexer/token.c
@@ -1,453 +1,353 @@
#include "zprep.h"
-void lexer_init(Lexer *l, const char *src)
-{
- l->src = src;
- l->pos = 0;
- l->line = 1;
- l->col = 1;
+void lexer_init(Lexer *l, const char *src) {
+ l->src = src;
+ l->pos = 0;
+ l->line = 1;
+ l->col = 1;
}
-static int is_ident_start(char c)
-{
- return isalpha(c) || c == '_';
-}
+static int is_ident_start(char c) { return isalpha(c) || c == '_'; }
-static int is_ident_char(char c)
-{
- return isalnum(c) || c == '_';
-}
+static int is_ident_char(char c) { return isalnum(c) || c == '_'; }
-Token lexer_next(Lexer *l)
-{
- const char *s = l->src + l->pos;
- int start_line = l->line;
- int start_col = l->col;
-
- while (isspace(*s))
- {
- if (*s == '\n')
- {
- l->line++;
- l->col = 1;
- }
- else
- {
- l->col++;
- }
- l->pos++;
- s++;
- start_line = l->line;
- start_col = l->col;
- }
-
- // Check for EOF.
- if (!*s)
- {
- return (Token){TOK_EOF, s, 0, start_line, start_col};
- }
-
- // C preprocessor directives.
- if (*s == '#')
- {
- int len = 0;
- while (s[len] && s[len] != '\n')
- {
- if (s[len] == '\\' && s[len + 1] == '\n')
- {
- len += 2;
- l->line++;
- }
- else
- {
- len++;
- }
- }
- l->pos += len;
+Token lexer_next(Lexer *l) {
+ const char *s = l->src + l->pos;
+ int start_line = l->line;
+ int start_col = l->col;
- return (Token){TOK_PREPROC, s, len, start_line, start_col};
+ while (isspace(*s)) {
+ if (*s == '\n') {
+ l->line++;
+ l->col = 1;
+ } else {
+ l->col++;
}
-
- // Comments.
- if (s[0] == '/' && s[1] == '/')
- {
- int len = 2;
- while (s[len] && s[len] != '\n')
- {
- len++;
- }
- l->pos += len;
- l->col += len;
- return lexer_next(l);
+ l->pos++;
+ s++;
+ start_line = l->line;
+ start_col = l->col;
+ }
+
+ // Check for EOF.
+ if (!*s) {
+ return (Token){TOK_EOF, s, 0, start_line, start_col};
+ }
+
+ // C preprocessor directives.
+ if (*s == '#') {
+ int len = 0;
+ while (s[len] && s[len] != '\n') {
+ if (s[len] == '\\' && s[len + 1] == '\n') {
+ len += 2;
+ l->line++;
+ } else {
+ len++;
+ }
}
+ l->pos += len;
- // Block Comments.
- if (s[0] == '/' && s[1] == '*')
- {
- // skip two start chars
+ return (Token){TOK_PREPROC, s, len, start_line, start_col};
+ }
+
+ // Comments.
+ if (s[0] == '/' && s[1] == '/') {
+ int len = 2;
+ while (s[len] && s[len] != '\n') {
+ len++;
+ }
+ l->pos += len;
+ l->col += len;
+ return lexer_next(l);
+ }
+
+ // Block Comments.
+ if (s[0] == '/' && s[1] == '*') {
+ // skip two start chars
+ l->pos += 2;
+ s += 2;
+
+ while (s[0]) {
+ // s[len+1] can be at most the null terminator
+ if (s[0] == '*' && s[1] == '/') {
+ // go over */
l->pos += 2;
s += 2;
-
- while (s[0])
- {
- // s[len+1] can be at most the null terminator
- if (s[0] == '*' && s[1] == '/')
- {
- // go over */
- l->pos += 2;
- s += 2;
- break;
- }
-
- if (s[0] == '\n')
- {
- l->line++;
- l->col = 1;
- }
- else
- {
- l->col++;
- }
-
- l->pos++;
- s++;
- }
-
- return lexer_next(l);
+ break;
+ }
+
+ if (s[0] == '\n') {
+ l->line++;
+ l->col = 1;
+ } else {
+ l->col++;
+ }
+
+ l->pos++;
+ s++;
}
- // Identifiers.
- if (is_ident_start(*s))
- {
- int len = 0;
- while (is_ident_char(s[len]))
- {
- len++;
- }
+ return lexer_next(l);
+ }
- l->pos += len;
- l->col += len;
+ // Identifiers.
+ if (is_ident_start(*s)) {
+ int len = 0;
+ while (is_ident_char(s[len])) {
+ len++;
+ }
- if (len == 4 && strncmp(s, "test", 4) == 0)
- {
- return (Token){TOK_TEST, s, 4, start_line, start_col};
- }
- if (len == 6 && strncmp(s, "assert", 6) == 0)
- {
- return (Token){TOK_ASSERT, s, 6, start_line, start_col};
- }
- if (len == 6 && strncmp(s, "sizeof", 6) == 0)
- {
- return (Token){TOK_SIZEOF, s, 6, start_line, start_col};
- }
- if (len == 5 && strncmp(s, "defer", 5) == 0)
- {
- return (Token){TOK_DEFER, s, 5, start_line, start_col};
- }
- if (len == 8 && strncmp(s, "autofree", 8) == 0)
- {
- return (Token){TOK_AUTOFREE, s, 8, start_line, start_col};
- }
- if (len == 3 && strncmp(s, "use", 3) == 0)
- {
- return (Token){TOK_USE, s, 3, start_line, start_col};
- }
- if (len == 8 && strncmp(s, "comptime", 8) == 0)
- {
- return (Token){TOK_COMPTIME, s, 8, start_line, start_col};
- }
- if (len == 5 && strncmp(s, "union", 5) == 0)
- {
- return (Token){TOK_UNION, s, 5, start_line, start_col};
- }
- if (len == 3 && strncmp(s, "asm", 3) == 0)
- {
- return (Token){TOK_ASM, s, 3, start_line, start_col};
- }
- if (len == 8 && strncmp(s, "volatile", 8) == 0)
- {
- return (Token){TOK_VOLATILE, s, 8, start_line, start_col};
- }
- if (len == 3 && strncmp(s, "mut", 3) == 0)
- {
- return (Token){TOK_MUT, s, 3, start_line, start_col};
- }
- if (len == 5 && strncmp(s, "async", 5) == 0)
- {
- return (Token){TOK_ASYNC, s, 5, start_line, start_col};
- }
- if (len == 5 && strncmp(s, "await", 5) == 0)
- {
- return (Token){TOK_AWAIT, s, 5, start_line, start_col};
- }
- if (len == 3 && strncmp(s, "and", 3) == 0)
- {
- return (Token){TOK_AND, s, 3, start_line, start_col};
- }
- if (len == 2 && strncmp(s, "or", 2) == 0)
- {
- return (Token){TOK_OR, s, 2, start_line, start_col};
- }
+ l->pos += len;
+ l->col += len;
- // F-Strings
- if (len == 1 && s[0] == 'f' && s[1] == '"')
- {
- // Reset pos/col because we want to parse string
- l->pos -= len;
- l->col -= len;
- }
- else
- {
- return (Token){TOK_IDENT, s, len, start_line, start_col};
- }
+ if (len == 4 && strncmp(s, "test", 4) == 0) {
+ return (Token){TOK_TEST, s, 4, start_line, start_col};
}
-
- if (s[0] == 'f' && s[1] == '"')
- {
- int len = 2;
- while (s[len] && s[len] != '"')
- {
- if (s[len] == '\\')
- {
- len++;
- }
- len++;
- }
- if (s[len] == '"')
- {
- len++;
- }
- l->pos += len;
- l->col += len;
- return (Token){TOK_FSTRING, s, len, start_line, start_col};
- }
-
- // Numbers
- if (isdigit(*s))
- {
- int len = 0;
- if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
- {
- len = 2;
- while (isxdigit(s[len]))
- {
- len++;
- }
- }
- else if (s[0] == '0' && (s[1] == 'b' || s[1] == 'B'))
- {
- len = 2;
- while (s[len] == '0' || s[len] == '1')
- {
- len++;
- }
- }
- else
- {
- while (isdigit(s[len]))
- {
- len++;
- }
- if (s[len] == '.')
- {
- if (s[len + 1] != '.')
- {
- len++;
- while (isdigit(s[len]))
- {
- len++;
- }
- l->pos += len;
- l->col += len;
- return (Token){TOK_FLOAT, s, len, start_line, start_col};
- }
- }
- }
- l->pos += len;
- l->col += len;
- return (Token){TOK_INT, s, len, start_line, start_col};
- }
-
- // Strings
- if (*s == '"')
- {
- int len = 1;
- while (s[len] && s[len] != '"')
- {
- if (s[len] == '\\')
- {
- len++;
- }
- len++;
- }
- if (s[len] == '"')
- {
- len++;
- }
- l->pos += len;
- l->col += len;
- return (Token){TOK_STRING, s, len, start_line, start_col};
+ if (len == 6 && strncmp(s, "assert", 6) == 0) {
+ return (Token){TOK_ASSERT, s, 6, start_line, start_col};
+ }
+ if (len == 6 && strncmp(s, "sizeof", 6) == 0) {
+ return (Token){TOK_SIZEOF, s, 6, start_line, start_col};
+ }
+ if (len == 5 && strncmp(s, "defer", 5) == 0) {
+ return (Token){TOK_DEFER, s, 5, start_line, start_col};
+ }
+ if (len == 8 && strncmp(s, "autofree", 8) == 0) {
+ return (Token){TOK_AUTOFREE, s, 8, start_line, start_col};
+ }
+ if (len == 3 && strncmp(s, "use", 3) == 0) {
+ return (Token){TOK_USE, s, 3, start_line, start_col};
+ }
+ if (len == 8 && strncmp(s, "comptime", 8) == 0) {
+ return (Token){TOK_COMPTIME, s, 8, start_line, start_col};
+ }
+ if (len == 5 && strncmp(s, "union", 5) == 0) {
+ return (Token){TOK_UNION, s, 5, start_line, start_col};
+ }
+ if (len == 3 && strncmp(s, "asm", 3) == 0) {
+ return (Token){TOK_ASM, s, 3, start_line, start_col};
+ }
+ if (len == 8 && strncmp(s, "volatile", 8) == 0) {
+ return (Token){TOK_VOLATILE, s, 8, start_line, start_col};
+ }
+ if (len == 3 && strncmp(s, "mut", 3) == 0) {
+ return (Token){TOK_MUT, s, 3, start_line, start_col};
+ }
+ if (len == 5 && strncmp(s, "async", 5) == 0) {
+ return (Token){TOK_ASYNC, s, 5, start_line, start_col};
+ }
+ if (len == 5 && strncmp(s, "await", 5) == 0) {
+ return (Token){TOK_AWAIT, s, 5, start_line, start_col};
+ }
+ if (len == 3 && strncmp(s, "and", 3) == 0) {
+ return (Token){TOK_AND, s, 3, start_line, start_col};
+ }
+ if (len == 2 && strncmp(s, "or", 2) == 0) {
+ return (Token){TOK_OR, s, 2, start_line, start_col};
}
- if (*s == '\'')
- {
- int len = 1;
- // Handle escapes like '\n' or regular 'a'
- if (s[len] == '\\')
- {
- len++;
- len++;
- }
- else
- {
- len++;
- }
- if (s[len] == '\'')
- {
+ // F-Strings
+ if (len == 1 && s[0] == 'f' && s[1] == '"') {
+ // Reset pos/col because we want to parse string
+ l->pos -= len;
+ l->col -= len;
+ } else {
+ return (Token){TOK_IDENT, s, len, start_line, start_col};
+ }
+ }
+
+ if (s[0] == 'f' && s[1] == '"') {
+ int len = 2;
+ while (s[len] && s[len] != '"') {
+ if (s[len] == '\\') {
+ len++;
+ }
+ len++;
+ }
+ if (s[len] == '"') {
+ len++;
+ }
+ l->pos += len;
+ l->col += len;
+ return (Token){TOK_FSTRING, s, len, start_line, start_col};
+ }
+
+ // Numbers
+ if (isdigit(*s)) {
+ int len = 0;
+ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+ len = 2;
+ while (isxdigit(s[len])) {
+ len++;
+ }
+ } else if (s[0] == '0' && (s[1] == 'b' || s[1] == 'B')) {
+ len = 2;
+ while (s[len] == '0' || s[len] == '1') {
+ len++;
+ }
+ } else {
+ while (isdigit(s[len])) {
+ len++;
+ }
+ if (s[len] == '.') {
+ if (s[len + 1] != '.') {
+ len++;
+ while (isdigit(s[len])) {
len++;
+ }
+ l->pos += len;
+ l->col += len;
+ return (Token){TOK_FLOAT, s, len, start_line, start_col};
}
-
- l->pos += len;
- l->col += len;
- return (Token){TOK_CHAR, s, len, start_line, start_col};
+ }
}
+ l->pos += len;
+ l->col += len;
+ return (Token){TOK_INT, s, len, start_line, start_col};
+ }
- // Operators.
+ // Strings
+ if (*s == '"') {
int len = 1;
- TokenType type = TOK_OP;
-
- if (s[0] == '?' && s[1] == '.')
- {
- len = 2;
- type = TOK_Q_DOT;
- }
- else if (s[0] == '?' && s[1] == '?')
- {
- if (s[2] == '=')
- {
- len = 3;
- type = TOK_QQ_EQ;
- }
- else
- {
- len = 2;
- type = TOK_QQ;
- }
- }
- else if (*s == '?')
- {
- type = TOK_QUESTION;
- }
- else if (s[0] == '|' && s[1] == '>')
- {
- len = 2;
- type = TOK_PIPE;
- }
- else if (s[0] == ':' && s[1] == ':')
- {
- len = 2;
- type = TOK_DCOLON;
- }
- else if (s[0] == '.' && s[1] == '.' && s[2] == '.')
- {
- len = 3;
- type = TOK_ELLIPSIS;
- }
- else if (s[0] == '.' && s[1] == '.')
- {
- len = 2;
- type = TOK_DOTDOT;
- }
- else if ((s[0] == '-' && s[1] == '>') || (s[0] == '=' && s[1] == '>'))
- {
- len = 2;
- type = TOK_ARROW;
- }
-
- else if ((s[0] == '<' && s[1] == '<') || (s[0] == '>' && s[1] == '>'))
- {
- len = 2;
- if (s[2] == '=')
- {
- len = 3; // Handle <<= and >>=
- }
+ while (s[len] && s[len] != '"') {
+ if (s[len] == '\\') {
+ len++;
+ }
+ len++;
}
- else if ((s[0] == '&' && s[1] == '&') || (s[0] == '|' && s[1] == '|') ||
- (s[0] == '+' && s[1] == '+') || (s[0] == '-' && s[1] == '-'))
- {
- len = 2;
- }
- else if (s[1] == '=')
- {
- // This catches: == != <= >= += -= *= /= %= |= &= ^=
- if (strchr("=!<>+-*/%|&^", s[0]))
- {
- len = 2;
- }
+ if (s[len] == '"') {
+ len++;
}
+ l->pos += len;
+ l->col += len;
+ return (Token){TOK_STRING, s, len, start_line, start_col};
+ }
- else
- {
- switch (*s)
- {
-
- case '(':
- type = TOK_LPAREN;
- break;
- case ')':
- type = TOK_RPAREN;
- break;
- case '{':
- type = TOK_LBRACE;
- break;
- case '}':
- type = TOK_RBRACE;
- break;
- case '[':
- type = TOK_LBRACKET;
- break;
- case ']':
- type = TOK_RBRACKET;
- break;
- case '<':
- type = TOK_LANGLE;
- break;
- case '>':
- type = TOK_RANGLE;
- break;
- case ',':
- type = TOK_COMMA;
- break;
- case ':':
- type = TOK_COLON;
- break;
- case ';':
- type = TOK_SEMICOLON;
- break;
- case '@':
- type = TOK_AT;
- break;
- default:
- type = TOK_OP;
- break;
- }
+ if (*s == '\'') {
+ int len = 1;
+ // Handle escapes like '\n' or regular 'a'
+ if (s[len] == '\\') {
+ len++;
+ len++;
+ } else {
+ len++;
+ }
+ if (s[len] == '\'') {
+ len++;
}
l->pos += len;
l->col += len;
- return (Token){type, s, len, start_line, start_col};
+ return (Token){TOK_CHAR, s, len, start_line, start_col};
+ }
+
+ // Operators.
+ int len = 1;
+ TokenType type = TOK_OP;
+
+ if (s[0] == '?' && s[1] == '.') {
+ len = 2;
+ type = TOK_Q_DOT;
+ } else if (s[0] == '?' && s[1] == '?') {
+ if (s[2] == '=') {
+ len = 3;
+ type = TOK_QQ_EQ;
+ } else {
+ len = 2;
+ type = TOK_QQ;
+ }
+ } else if (*s == '?') {
+ type = TOK_QUESTION;
+ } else if (s[0] == '|' && s[1] == '>') {
+ len = 2;
+ type = TOK_PIPE;
+ } else if (s[0] == ':' && s[1] == ':') {
+ len = 2;
+ type = TOK_DCOLON;
+ } else if (s[0] == '.' && s[1] == '.' && s[2] == '.') {
+ len = 3;
+ type = TOK_ELLIPSIS;
+ } else if (s[0] == '.' && s[1] == '.') {
+ len = 2;
+ type = TOK_DOTDOT;
+ } else if ((s[0] == '-' && s[1] == '>') || (s[0] == '=' && s[1] == '>')) {
+ len = 2;
+ type = TOK_ARROW;
+ }
+
+ else if ((s[0] == '<' && s[1] == '<') || (s[0] == '>' && s[1] == '>')) {
+ len = 2;
+ if (s[2] == '=') {
+ len = 3; // Handle <<= and >>=
+ }
+ } else if ((s[0] == '&' && s[1] == '&') || (s[0] == '|' && s[1] == '|') ||
+ (s[0] == '+' && s[1] == '+') || (s[0] == '-' && s[1] == '-')) {
+ len = 2;
+ } else if (s[1] == '=') {
+ // This catches: == != <= >= += -= *= /= %= |= &= ^=
+ if (strchr("=!<>+-*/%|&^", s[0])) {
+ len = 2;
+ }
+ }
+
+ else {
+ switch (*s) {
+
+ case '(':
+ type = TOK_LPAREN;
+ break;
+ case ')':
+ type = TOK_RPAREN;
+ break;
+ case '{':
+ type = TOK_LBRACE;
+ break;
+ case '}':
+ type = TOK_RBRACE;
+ break;
+ case '[':
+ type = TOK_LBRACKET;
+ break;
+ case ']':
+ type = TOK_RBRACKET;
+ break;
+ case '<':
+ type = TOK_LANGLE;
+ break;
+ case '>':
+ type = TOK_RANGLE;
+ break;
+ case ',':
+ type = TOK_COMMA;
+ break;
+ case ':':
+ type = TOK_COLON;
+ break;
+ case ';':
+ type = TOK_SEMICOLON;
+ break;
+ case '@':
+ type = TOK_AT;
+ break;
+ default:
+ type = TOK_OP;
+ break;
+ }
+ }
+
+ l->pos += len;
+ l->col += len;
+ return (Token){type, s, len, start_line, start_col};
}
-Token lexer_peek(Lexer *l)
-{
- Lexer saved = *l;
- return lexer_next(&saved);
+Token lexer_peek(Lexer *l) {
+ Lexer saved = *l;
+ return lexer_next(&saved);
}
-Token lexer_peek2(Lexer *l)
-{
- Lexer saved = *l;
- lexer_next(&saved);
- return lexer_next(&saved);
+Token lexer_peek2(Lexer *l) {
+ Lexer saved = *l;
+ lexer_next(&saved);
+ return lexer_next(&saved);
}
diff --git a/src/lsp/json_rpc.c b/src/lsp/json_rpc.c
index 903da71..c75af95 100644
--- a/src/lsp/json_rpc.c
+++ b/src/lsp/json_rpc.c
@@ -5,107 +5,81 @@
#include <string.h>
// Basic JSON parsing helpers
-char *get_json_string(const char *json, const char *key)
-{
- char search[256];
- sprintf(search, "\"%s\":\"", key);
- char *p = strstr(json, search);
- if (!p)
- {
- return NULL;
- }
- p += strlen(search);
- char *end = strchr(p, '"');
- if (!end)
- {
- return NULL;
- }
- int len = end - p;
- char *res = malloc(len + 1);
- strncpy(res, p, len);
- res[len] = 0;
- return res;
+char *get_json_string(const char *json, const char *key) {
+ char search[256];
+ sprintf(search, "\"%s\":\"", key);
+ char *p = strstr(json, search);
+ if (!p) {
+ return NULL;
+ }
+ p += strlen(search);
+ char *end = strchr(p, '"');
+ if (!end) {
+ return NULL;
+ }
+ int len = end - p;
+ char *res = malloc(len + 1);
+ strncpy(res, p, len);
+ res[len] = 0;
+ return res;
}
// Extract nested "text" from params/contentChanges/0/text or
// params/textDocument/text This is very hacky for MVP. proper JSON library
// needed.
-char *get_text_content(const char *json)
-{
- char *p = strstr(json, "\"text\":\"");
- if (!p)
- {
- return NULL;
- }
- p += 8;
-
- size_t cap = strlen(p);
- char *res = malloc(cap + 1);
- char *dst = res;
-
- while (*p)
- {
- if (*p == '\\')
- {
- p++;
- if (*p == 'n')
- {
- *dst++ = '\n';
- }
- else if (*p == 'r')
- {
- *dst++ = '\r';
- }
- else if (*p == 't')
- {
- *dst++ = '\t';
- }
- else if (*p == '"')
- {
- *dst++ = '"';
- }
- else if (*p == '\\')
- {
- *dst++ = '\\';
- }
- else
- {
- *dst++ = *p; // preserve others
- }
- p++;
- }
- else if (*p == '"')
- {
- break; // End of string.
- }
- else
- {
- *dst++ = *p++;
- }
+char *get_text_content(const char *json) {
+ char *p = strstr(json, "\"text\":\"");
+ if (!p) {
+ return NULL;
+ }
+ p += 8;
+
+ size_t cap = strlen(p);
+ char *res = malloc(cap + 1);
+ char *dst = res;
+
+ while (*p) {
+ if (*p == '\\') {
+ p++;
+ if (*p == 'n') {
+ *dst++ = '\n';
+ } else if (*p == 'r') {
+ *dst++ = '\r';
+ } else if (*p == 't') {
+ *dst++ = '\t';
+ } else if (*p == '"') {
+ *dst++ = '"';
+ } else if (*p == '\\') {
+ *dst++ = '\\';
+ } else {
+ *dst++ = *p; // preserve others
+ }
+ p++;
+ } else if (*p == '"') {
+ break; // End of string.
+ } else {
+ *dst++ = *p++;
}
- *dst = 0;
- return res;
+ }
+ *dst = 0;
+ return res;
}
// Helper to get line/char
-void get_json_position(const char *json, int *line, int *col)
-{
- char *pos = strstr(json, "\"position\":");
- if (!pos)
- {
- return;
- }
- char *l = strstr(pos, "\"line\":");
- if (l)
- {
- *line = atoi(l + 7);
- }
- char *c = strstr(pos, "\"character\":");
- if (c)
- {
- *col = atoi(c + 12);
- }
+void get_json_position(const char *json, int *line, int *col) {
+ char *pos = strstr(json, "\"position\":");
+ if (!pos) {
+ return;
+ }
+ char *l = strstr(pos, "\"line\":");
+ if (l) {
+ *line = atoi(l + 7);
+ }
+ char *c = strstr(pos, "\"character\":");
+ if (c) {
+ *col = atoi(c + 12);
+ }
}
void lsp_check_file(const char *uri, const char *src);
@@ -113,82 +87,71 @@ void lsp_goto_definition(const char *uri, int line, int col);
void lsp_hover(const char *uri, int line, int col);
void lsp_completion(const char *uri, int line, int col);
-void handle_request(const char *json_str)
-{
- if (strstr(json_str, "\"method\":\"initialize\""))
- {
- const char *response = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{"
- "\"capabilities\":{\"textDocumentSync\":1,"
- "\"definitionProvider\":true,\"hoverProvider\":true,"
- "\"completionProvider\":{"
- "\"triggerCharacters\":[\".\"]}}}}";
- fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(response), response);
- fflush(stdout);
- return;
+void handle_request(const char *json_str) {
+ if (strstr(json_str, "\"method\":\"initialize\"")) {
+ const char *response = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{"
+ "\"capabilities\":{\"textDocumentSync\":1,"
+ "\"definitionProvider\":true,\"hoverProvider\":true,"
+ "\"completionProvider\":{"
+ "\"triggerCharacters\":[\".\"]}}}}";
+ fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(response),
+ response);
+ fflush(stdout);
+ return;
+ }
+
+ if (strstr(json_str, "\"method\":\"textDocument/didOpen\"") ||
+ strstr(json_str, "\"method\":\"textDocument/didChange\"")) {
+
+ char *uri = get_json_string(json_str, "uri");
+ char *text = get_text_content(json_str);
+
+ if (uri && text) {
+ fprintf(stderr, "zls: Checking %s\n", uri);
+ lsp_check_file(uri, text);
}
- if (strstr(json_str, "\"method\":\"textDocument/didOpen\"") ||
- strstr(json_str, "\"method\":\"textDocument/didChange\""))
- {
-
- char *uri = get_json_string(json_str, "uri");
- char *text = get_text_content(json_str);
-
- if (uri && text)
- {
- fprintf(stderr, "zls: Checking %s\n", uri);
- lsp_check_file(uri, text);
- }
-
- if (uri)
- {
- free(uri);
- }
- if (text)
- {
- free(text);
- }
+ if (uri) {
+ free(uri);
+ }
+ if (text) {
+ free(text);
}
+ }
+
+ if (strstr(json_str, "\"method\":\"textDocument/definition\"")) {
+ char *uri = get_json_string(json_str, "uri");
+ int line = 0, col = 0;
+ get_json_position(json_str, &line, &col);
- if (strstr(json_str, "\"method\":\"textDocument/definition\""))
- {
- char *uri = get_json_string(json_str, "uri");
- int line = 0, col = 0;
- get_json_position(json_str, &line, &col);
-
- if (uri)
- {
- fprintf(stderr, "zls: Definition request at %d:%d\n", line, col);
- lsp_goto_definition(uri, line, col);
- free(uri);
- }
+ if (uri) {
+ fprintf(stderr, "zls: Definition request at %d:%d\n", line, col);
+ lsp_goto_definition(uri, line, col);
+ free(uri);
}
+ }
- if (strstr(json_str, "\"method\":\"textDocument/hover\""))
- {
- char *uri = get_json_string(json_str, "uri");
- int line = 0, col = 0;
- get_json_position(json_str, &line, &col);
-
- if (uri)
- {
- fprintf(stderr, "zls: Hover request at %d:%d\n", line, col);
- lsp_hover(uri, line, col);
- free(uri);
- }
+ if (strstr(json_str, "\"method\":\"textDocument/hover\"")) {
+ char *uri = get_json_string(json_str, "uri");
+ int line = 0, col = 0;
+ get_json_position(json_str, &line, &col);
+
+ if (uri) {
+ fprintf(stderr, "zls: Hover request at %d:%d\n", line, col);
+ lsp_hover(uri, line, col);
+ free(uri);
}
+ }
+
+ if (strstr(json_str, "\"method\":\"textDocument/completion\"")) {
+ char *uri = get_json_string(json_str, "uri");
+ int line = 0, col = 0;
+ get_json_position(json_str, &line, &col);
- if (strstr(json_str, "\"method\":\"textDocument/completion\""))
- {
- char *uri = get_json_string(json_str, "uri");
- int line = 0, col = 0;
- get_json_position(json_str, &line, &col);
-
- if (uri)
- {
- fprintf(stderr, "zls: Completion request at %d:%d\n", line, col);
- lsp_completion(uri, line, col);
- free(uri);
- }
+ if (uri) {
+ fprintf(stderr, "zls: Completion request at %d:%d\n", line, col);
+ lsp_completion(uri, line, col);
+ free(uri);
}
+ }
}
diff --git a/src/lsp/lsp_analysis.c b/src/lsp/lsp_analysis.c
index d455894..73ba2c5 100644
--- a/src/lsp/lsp_analysis.c
+++ b/src/lsp/lsp_analysis.c
@@ -9,387 +9,335 @@
static LSPIndex *g_index = NULL;
-typedef struct Diagnostic
-{
- int line;
- int col;
- char *message;
- struct Diagnostic *next;
+typedef struct Diagnostic {
+ int line;
+ int col;
+ char *message;
+ struct Diagnostic *next;
} Diagnostic;
-typedef struct
-{
- Diagnostic *head;
- Diagnostic *tail;
+typedef struct {
+ Diagnostic *head;
+ Diagnostic *tail;
} DiagnosticList;
static ParserContext *g_ctx = NULL;
static char *g_last_src = NULL;
// Callback for parser errors.
-void lsp_on_error(void *data, Token t, const char *msg)
-{
- DiagnosticList *list = (DiagnosticList *)data;
- Diagnostic *d = xmalloc(sizeof(Diagnostic));
- d->line = t.line > 0 ? t.line - 1 : 0;
- d->col = t.col > 0 ? t.col - 1 : 0;
- d->message = xstrdup(msg);
- d->next = NULL;
-
- if (!list->head)
- {
- list->head = d;
- list->tail = d;
- }
- else
- {
- list->tail->next = d;
- list->tail = d;
- }
+void lsp_on_error(void *data, Token t, const char *msg) {
+ DiagnosticList *list = (DiagnosticList *)data;
+ Diagnostic *d = xmalloc(sizeof(Diagnostic));
+ d->line = t.line > 0 ? t.line - 1 : 0;
+ d->col = t.col > 0 ? t.col - 1 : 0;
+ d->message = xstrdup(msg);
+ d->next = NULL;
+
+ if (!list->head) {
+ list->head = d;
+ list->tail = d;
+ } else {
+ list->tail->next = d;
+ list->tail = d;
+ }
}
-void lsp_check_file(const char *uri, const char *json_src)
-{
- // Prepare ParserContext (persistent).
- if (g_ctx)
- {
+void lsp_check_file(const char *uri, const char *json_src) {
+ // Prepare ParserContext (persistent).
+ if (g_ctx) {
- free(g_ctx);
- }
- g_ctx = calloc(1, sizeof(ParserContext));
- g_ctx->is_fault_tolerant = 1;
-
- DiagnosticList diagnostics = {0};
- g_ctx->error_callback_data = &diagnostics;
- g_ctx->on_error = lsp_on_error;
-
- // Prepare Lexer.
- // Cache source.
- if (g_last_src)
- {
- free(g_last_src);
- }
- g_last_src = strdup(json_src);
+ free(g_ctx);
+ }
+ g_ctx = calloc(1, sizeof(ParserContext));
+ g_ctx->is_fault_tolerant = 1;
- Lexer l;
- lexer_init(&l, json_src);
+ DiagnosticList diagnostics = {0};
+ g_ctx->error_callback_data = &diagnostics;
+ g_ctx->on_error = lsp_on_error;
- ASTNode *root = parse_program(g_ctx, &l);
+ // Prepare Lexer.
+ // Cache source.
+ if (g_last_src) {
+ free(g_last_src);
+ }
+ g_last_src = strdup(json_src);
- if (g_index)
- {
- lsp_index_free(g_index);
- }
- g_index = lsp_index_new();
- if (root)
- {
- lsp_build_index(g_index, root);
- }
+ Lexer l;
+ lexer_init(&l, json_src);
+
+ ASTNode *root = parse_program(g_ctx, &l);
- // Construct JSON Response (notification)
+ if (g_index) {
+ lsp_index_free(g_index);
+ }
+ g_index = lsp_index_new();
+ if (root) {
+ lsp_build_index(g_index, root);
+ }
+
+ // Construct JSON Response (notification)
+
+ char *notification = malloc(128 * 1024);
+ char *p = notification;
+ p += sprintf(
+ p,
+ "{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/"
+ "publishDiagnostics\",\"params\":{\"uri\":\"%s\",\"diagnostics\":[",
+ uri);
+
+ Diagnostic *d = diagnostics.head;
+ while (d) {
- char *notification = malloc(128 * 1024);
- char *p = notification;
p += sprintf(p,
- "{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/"
- "publishDiagnostics\",\"params\":{\"uri\":\"%s\",\"diagnostics\":[",
- uri);
-
- Diagnostic *d = diagnostics.head;
- while (d)
- {
-
- p += sprintf(p,
- "{\"range\":{\"start\":{\"line\":%d,\"character\":%d},\"end\":"
- "{\"line\":%d,"
- "\"character\":%d}},\"severity\":1,\"message\":\"%s\"}",
- d->line, d->col, d->line, d->col + 1, d->message);
-
- if (d->next)
- {
- p += sprintf(p, ",");
- }
+ "{\"range\":{\"start\":{\"line\":%d,\"character\":%d},\"end\":"
+ "{\"line\":%d,"
+ "\"character\":%d}},\"severity\":1,\"message\":\"%s\"}",
+ d->line, d->col, d->line, d->col + 1, d->message);
- d = d->next;
+ if (d->next) {
+ p += sprintf(p, ",");
}
- p += sprintf(p, "]}}");
+ d = d->next;
+ }
- // Send notification.
- long len = strlen(notification);
- fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", len, notification);
- fflush(stdout);
+ p += sprintf(p, "]}}");
- free(notification);
+ // Send notification.
+ long len = strlen(notification);
+ fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", len, notification);
+ fflush(stdout);
- // Cleanup.
- Diagnostic *cur = diagnostics.head;
- while (cur)
- {
- Diagnostic *next = cur->next;
- free(cur->message);
- free(cur);
- cur = next;
- }
-}
+ free(notification);
-void lsp_goto_definition(const char *uri, int line, int col)
-{
- if (!g_index)
- {
- return;
- }
+ // Cleanup.
+ Diagnostic *cur = diagnostics.head;
+ while (cur) {
+ Diagnostic *next = cur->next;
+ free(cur->message);
+ free(cur);
+ cur = next;
+ }
+}
- LSPRange *r = lsp_find_at(g_index, line, col);
- if (r && r->type == RANGE_REFERENCE)
- {
- // Found reference, return definition
- char resp[1024];
- sprintf(resp,
- "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"uri\":\"%s\","
- "\"range\":{\"start\":{"
- "\"line\":%d,\"character\":%d},\"end\":{\"line\":%d,\"character\":%"
- "d}}}}",
- uri, r->def_line, r->def_col, r->def_line, r->def_col);
-
- fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(resp), resp);
- fflush(stdout);
- }
- else if (r && r->type == RANGE_DEFINITION)
- {
- // Already at definition? Return itself.
- char resp[1024];
- sprintf(resp,
- "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"uri\":\"%s\","
- "\"range\":{\"start\":{"
- "\"line\":%d,\"character\":%d},\"end\":{\"line\":%d,\"character\":%"
- "d}}}}",
- uri, r->start_line, r->start_col, r->end_line, r->end_col);
-
- fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(resp), resp);
- fflush(stdout);
- }
- else
- {
- // Null result
- const char *null_resp = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":null}";
- fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(null_resp), null_resp);
- fflush(stdout);
- }
+void lsp_goto_definition(const char *uri, int line, int col) {
+ if (!g_index) {
+ return;
+ }
+
+ LSPRange *r = lsp_find_at(g_index, line, col);
+ if (r && r->type == RANGE_REFERENCE) {
+ // Found reference, return definition
+ char resp[1024];
+ sprintf(resp,
+ "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"uri\":\"%s\","
+ "\"range\":{\"start\":{"
+ "\"line\":%d,\"character\":%d},\"end\":{\"line\":%d,\"character\":%"
+ "d}}}}",
+ uri, r->def_line, r->def_col, r->def_line, r->def_col);
+
+ fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(resp), resp);
+ fflush(stdout);
+ } else if (r && r->type == RANGE_DEFINITION) {
+ // Already at definition? Return itself.
+ char resp[1024];
+ sprintf(resp,
+ "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"uri\":\"%s\","
+ "\"range\":{\"start\":{"
+ "\"line\":%d,\"character\":%d},\"end\":{\"line\":%d,\"character\":%"
+ "d}}}}",
+ uri, r->start_line, r->start_col, r->end_line, r->end_col);
+
+ fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(resp), resp);
+ fflush(stdout);
+ } else {
+ // Null result
+ const char *null_resp = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":null}";
+ fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(null_resp),
+ null_resp);
+ fflush(stdout);
+ }
}
-void lsp_hover(const char *uri, int line, int col)
-{
- (void)uri;
- if (!g_index)
- {
- return;
+void lsp_hover(const char *uri, int line, int col) {
+ (void)uri;
+ if (!g_index) {
+ return;
+ }
+
+ LSPRange *r = lsp_find_at(g_index, line, col);
+ char *text = NULL;
+
+ if (r) {
+ if (r->type == RANGE_DEFINITION) {
+ text = r->hover_text;
+ } else if (r->type == RANGE_REFERENCE) {
+ LSPRange *def = lsp_find_at(g_index, r->def_line, r->def_col);
+ if (def && def->type == RANGE_DEFINITION) {
+ text = def->hover_text;
+ }
}
+ }
- LSPRange *r = lsp_find_at(g_index, line, col);
- char *text = NULL;
+ if (text) {
+ char *json = malloc(16384);
+ // content: { kind: markdown, value: text }
+ sprintf(json,
+ "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"contents\":{\"kind\":"
+ "\"markdown\","
+ "\"value\":\"```c\\n%s\\n```\"}}}",
+ text);
- if (r)
- {
- if (r->type == RANGE_DEFINITION)
- {
- text = r->hover_text;
- }
- else if (r->type == RANGE_REFERENCE)
- {
- LSPRange *def = lsp_find_at(g_index, r->def_line, r->def_col);
- if (def && def->type == RANGE_DEFINITION)
- {
- text = def->hover_text;
- }
- }
- }
-
- if (text)
- {
- char *json = malloc(16384);
- // content: { kind: markdown, value: text }
- sprintf(json,
- "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"contents\":{\"kind\":"
- "\"markdown\","
- "\"value\":\"```c\\n%s\\n```\"}}}",
- text);
-
- fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(json), json);
- fflush(stdout);
- free(json);
- }
- else
- {
- const char *null_resp = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":null}";
- fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(null_resp), null_resp);
- fflush(stdout);
- }
+ fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(json), json);
+ fflush(stdout);
+ free(json);
+ } else {
+ const char *null_resp = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":null}";
+ fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(null_resp),
+ null_resp);
+ fflush(stdout);
+ }
}
-void lsp_completion(const char *uri, int line, int col)
-{
- (void)uri;
- if (!g_ctx)
- {
- return;
+void lsp_completion(const char *uri, int line, int col) {
+ (void)uri;
+ if (!g_ctx) {
+ return;
+ }
+
+ // Context-aware completion (Dot access)
+ if (g_last_src) {
+ // Simple line access
+ int cur_line = 0;
+
+ char *ptr = g_last_src;
+ // Fast forward to line
+ while (*ptr && cur_line < line) {
+ if (*ptr == '\n') {
+ cur_line++;
+ }
+ ptr++;
}
- // Context-aware completion (Dot access)
- if (g_last_src)
- {
- // Simple line access
- int cur_line = 0;
-
- char *ptr = g_last_src;
- // Fast forward to line
- while (*ptr && cur_line < line)
- {
- if (*ptr == '\n')
- {
- cur_line++;
- }
- ptr++;
+ if (col > 0 && ptr[col - 1] == '.') {
+ // Found dot!
+ // Scan backwards for identifier: [whitespace] [ident] .
+ int i = col - 2;
+ while (i >= 0 && (ptr[i] == ' ' || ptr[i] == '\t')) {
+ i--;
+ }
+
+ if (i >= 0) {
+ int end_ident = i;
+ while (i >= 0 && (isalnum(ptr[i]) || ptr[i] == '_')) {
+ i--;
}
-
- if (col > 0 && ptr[col - 1] == '.')
- {
- // Found dot!
- // Scan backwards for identifier: [whitespace] [ident] .
- int i = col - 2;
- while (i >= 0 && (ptr[i] == ' ' || ptr[i] == '\t'))
- {
- i--;
+ int start_ident = i + 1;
+
+ if (start_ident <= end_ident) {
+ int len = end_ident - start_ident + 1;
+ char var_name[256];
+ strncpy(var_name, ptr + start_ident, len);
+ var_name[len] = 0;
+
+ char *type_name = NULL;
+ Symbol *sym = find_symbol_in_all(g_ctx, var_name);
+
+ if (sym) {
+ if (sym->type_info) {
+ type_name = type_to_string(sym->type_info);
+ } else if (sym->type_name) {
+ type_name = sym->type_name;
}
+ }
- if (i >= 0)
- {
- int end_ident = i;
- while (i >= 0 && (isalnum(ptr[i]) || ptr[i] == '_'))
- {
- i--;
- }
- int start_ident = i + 1;
-
- if (start_ident <= end_ident)
- {
- int len = end_ident - start_ident + 1;
- char var_name[256];
- strncpy(var_name, ptr + start_ident, len);
- var_name[len] = 0;
-
- char *type_name = NULL;
- Symbol *sym = find_symbol_in_all(g_ctx, var_name);
-
- if (sym)
- {
- if (sym->type_info)
- {
- type_name = type_to_string(sym->type_info);
- }
- else if (sym->type_name)
- {
- type_name = sym->type_name;
- }
- }
-
- if (type_name)
- {
- char clean_name[256];
- char *src = type_name;
- if (0 == strncmp(src, "struct ", 7))
- {
- src += 7;
- }
- char *dst = clean_name;
- while (*src && *src != '*')
- {
- *dst++ = *src++;
- }
- *dst = 0;
-
- // Lookup struct.
- StructDef *sd = g_ctx->struct_defs;
- while (sd)
- {
- if (0 == strcmp(sd->name, clean_name))
- {
- char *json_fields = malloc(1024 * 1024);
- char *pj = json_fields;
- pj += sprintf(pj, "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":[");
-
- int ffirst = 1;
- if (sd->node && sd->node->strct.fields)
- {
- ASTNode *field = sd->node->strct.fields;
- while (field)
- {
- if (!ffirst)
- {
- pj += sprintf(pj, ",");
- }
- pj += sprintf(
- pj,
- "{\"label\":\"%s\",\"kind\":5,\"detail\":\"field %s\"}",
- field->field.name, field->field.type); // Kind 5 = Field
- ffirst = 0;
- field = field->next;
- }
- }
-
- pj += sprintf(pj, "]}");
- fprintf(stdout, "Content-Length: %ld\r\n\r\n%s",
- strlen(json_fields), json_fields);
- fflush(stdout);
- free(json_fields);
- free(json);
- return; // Done, yippee.
- }
- sd = sd->next;
- }
+ if (type_name) {
+ char clean_name[256];
+ char *src = type_name;
+ if (0 == strncmp(src, "struct ", 7)) {
+ src += 7;
+ }
+ char *dst = clean_name;
+ while (*src && *src != '*') {
+ *dst++ = *src++;
+ }
+ *dst = 0;
+
+ // Lookup struct.
+ StructDef *sd = g_ctx->struct_defs;
+ while (sd) {
+ if (0 == strcmp(sd->name, clean_name)) {
+ char *json_fields = malloc(1024 * 1024);
+ char *pj = json_fields;
+ pj += sprintf(pj, "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":[");
+
+ int ffirst = 1;
+ if (sd->node && sd->node->strct.fields) {
+ ASTNode *field = sd->node->strct.fields;
+ while (field) {
+ if (!ffirst) {
+ pj += sprintf(pj, ",");
}
+ pj += sprintf(
+ pj,
+ "{\"label\":\"%s\",\"kind\":5,\"detail\":\"field %s\"}",
+ field->field.name, field->field.type); // Kind 5 = Field
+ ffirst = 0;
+ field = field->next;
+ }
}
+
+ pj += sprintf(pj, "]}");
+ fprintf(stdout, "Content-Length: %ld\r\n\r\n%s",
+ strlen(json_fields), json_fields);
+ fflush(stdout);
+ free(json_fields);
+ free(json);
+ return; // Done, yippee.
+ }
+ sd = sd->next;
}
+ }
}
+ }
}
+ }
- char *json = xmalloc(1024 * 1024); // 1MB buffer.
- char *p = json;
- p += sprintf(p, "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":[");
+ char *json = xmalloc(1024 * 1024); // 1MB buffer.
+ char *p = json;
+ p += sprintf(p, "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":[");
- int first = 1;
+ int first = 1;
- // Functions
- FuncSig *f = g_ctx->func_registry;
- while (f)
- {
- if (!first)
- {
- p += sprintf(p, ",");
- }
- p += sprintf(p, "{\"label\":\"%s\",\"kind\":3,\"detail\":\"fn %s(...)\"}", f->name,
- f->name); // Kind 3 = Function
- first = 0;
- f = f->next;
+ // Functions
+ FuncSig *f = g_ctx->func_registry;
+ while (f) {
+ if (!first) {
+ p += sprintf(p, ",");
}
-
- // Structs
- StructDef *s = g_ctx->struct_defs;
- while (s)
- {
- if (!first)
- {
- p += sprintf(p, ",");
- }
- p += sprintf(p, "{\"label\":\"%s\",\"kind\":22,\"detail\":\"struct %s\"}", s->name,
- s->name); // Kind 22 = Struct
- first = 0;
- s = s->next;
+ p += sprintf(p, "{\"label\":\"%s\",\"kind\":3,\"detail\":\"fn %s(...)\"}",
+ f->name,
+ f->name); // Kind 3 = Function
+ first = 0;
+ f = f->next;
+ }
+
+ // Structs
+ StructDef *s = g_ctx->struct_defs;
+ while (s) {
+ if (!first) {
+ p += sprintf(p, ",");
}
-
- p += sprintf(p, "]}");
-
- fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(json), json);
- fflush(stdout);
- free(json);
+ p += sprintf(p, "{\"label\":\"%s\",\"kind\":22,\"detail\":\"struct %s\"}",
+ s->name,
+ s->name); // Kind 22 = Struct
+ first = 0;
+ s = s->next;
+ }
+
+ p += sprintf(p, "]}");
+
+ fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", strlen(json), json);
+ fflush(stdout);
+ free(json);
}
diff --git a/src/lsp/lsp_index.c b/src/lsp/lsp_index.c
index d952b77..fdd0cf8 100644
--- a/src/lsp/lsp_index.c
+++ b/src/lsp/lsp_index.c
@@ -4,201 +4,168 @@
#include <stdlib.h>
#include <string.h>
-LSPIndex *lsp_index_new()
-{
- return calloc(1, sizeof(LSPIndex));
-}
-
-void lsp_index_free(LSPIndex *idx)
-{
- if (!idx)
- {
- return;
- }
- LSPRange *c = idx->head;
- while (c)
- {
- LSPRange *n = c->next;
- if (c->hover_text)
- {
- free(c->hover_text);
- }
- free(c);
- c = n;
- }
- free(idx);
-}
-
-void lsp_index_add(LSPIndex *idx, LSPRange *r)
-{
- if (!idx->head)
- {
- idx->head = r;
- idx->tail = r;
- }
- else
- {
- idx->tail->next = r;
- idx->tail = r;
+LSPIndex *lsp_index_new() { return calloc(1, sizeof(LSPIndex)); }
+
+void lsp_index_free(LSPIndex *idx) {
+ if (!idx) {
+ return;
+ }
+ LSPRange *c = idx->head;
+ while (c) {
+ LSPRange *n = c->next;
+ if (c->hover_text) {
+ free(c->hover_text);
}
+ free(c);
+ c = n;
+ }
+ free(idx);
}
-void lsp_index_add_def(LSPIndex *idx, Token t, const char *hover, ASTNode *node)
-{
- if (t.line <= 0)
- {
- return;
- }
- LSPRange *r = calloc(1, sizeof(LSPRange));
- r->type = RANGE_DEFINITION;
- r->start_line = t.line - 1;
- r->start_col = t.col - 1;
- r->end_line = t.line - 1;
- r->end_col = t.col - 1 + t.len;
- if (hover)
- {
- r->hover_text = strdup(hover);
- }
- r->node = node;
-
- lsp_index_add(idx, r);
+void lsp_index_add(LSPIndex *idx, LSPRange *r) {
+ if (!idx->head) {
+ idx->head = r;
+ idx->tail = r;
+ } else {
+ idx->tail->next = r;
+ idx->tail = r;
+ }
}
-void lsp_index_add_ref(LSPIndex *idx, Token t, Token def_t, ASTNode *node)
-{
- if (t.line <= 0 || def_t.line <= 0)
- {
- return;
- }
- LSPRange *r = calloc(1, sizeof(LSPRange));
- r->type = RANGE_REFERENCE;
- r->start_line = t.line - 1;
- r->start_col = t.col - 1;
- r->end_line = t.line - 1;
- r->end_col = t.col - 1 + t.len;
-
- r->def_line = def_t.line - 1;
- r->def_col = def_t.col - 1;
- r->node = node;
-
- lsp_index_add(idx, r);
+void lsp_index_add_def(LSPIndex *idx, Token t, const char *hover,
+ ASTNode *node) {
+ if (t.line <= 0) {
+ return;
+ }
+ LSPRange *r = calloc(1, sizeof(LSPRange));
+ r->type = RANGE_DEFINITION;
+ r->start_line = t.line - 1;
+ r->start_col = t.col - 1;
+ r->end_line = t.line - 1;
+ r->end_col = t.col - 1 + t.len;
+ if (hover) {
+ r->hover_text = strdup(hover);
+ }
+ r->node = node;
+
+ lsp_index_add(idx, r);
}
-LSPRange *lsp_find_at(LSPIndex *idx, int line, int col)
-{
- LSPRange *curr = idx->head;
- LSPRange *best = NULL;
-
- while (curr)
- {
- if (line >= curr->start_line && line <= curr->end_line)
- {
- if (line == curr->start_line && col < curr->start_col)
- {
- curr = curr->next;
- continue;
- }
-
- if (line == curr->end_line && col > curr->end_col)
- {
- curr = curr->next;
- continue;
- }
-
- best = curr;
- }
- curr = curr->next;
- }
- return best;
+void lsp_index_add_ref(LSPIndex *idx, Token t, Token def_t, ASTNode *node) {
+ if (t.line <= 0 || def_t.line <= 0) {
+ return;
+ }
+ LSPRange *r = calloc(1, sizeof(LSPRange));
+ r->type = RANGE_REFERENCE;
+ r->start_line = t.line - 1;
+ r->start_col = t.col - 1;
+ r->end_line = t.line - 1;
+ r->end_col = t.col - 1 + t.len;
+
+ r->def_line = def_t.line - 1;
+ r->def_col = def_t.col - 1;
+ r->node = node;
+
+ lsp_index_add(idx, r);
}
-// Walker.
-
-void lsp_walk_node(LSPIndex *idx, ASTNode *node)
-{
- if (!node)
- {
- return;
- }
-
- // Definition logic.
- if (node->type == NODE_FUNCTION)
- {
- char hover[256];
- sprintf(hover, "fn %s(...) -> %s", node->func.name,
- node->func.ret_type ? node->func.ret_type : "void");
- lsp_index_add_def(idx, node->token, hover, node);
+LSPRange *lsp_find_at(LSPIndex *idx, int line, int col) {
+ LSPRange *curr = idx->head;
+ LSPRange *best = NULL;
- // Recurse body.
- lsp_walk_node(idx, node->func.body);
- }
- else if (node->type == NODE_VAR_DECL)
- {
- char hover[256];
- sprintf(hover, "var %s", node->var_decl.name);
- lsp_index_add_def(idx, node->token, hover, node);
-
- lsp_walk_node(idx, node->var_decl.init_expr);
- }
- else if (node->type == NODE_CONST)
- {
- char hover[256];
- sprintf(hover, "const %s", node->var_decl.name);
- lsp_index_add_def(idx, node->token, hover, node);
+ while (curr) {
+ if (line >= curr->start_line && line <= curr->end_line) {
+ if (line == curr->start_line && col < curr->start_col) {
+ curr = curr->next;
+ continue;
+ }
- lsp_walk_node(idx, node->var_decl.init_expr);
- }
+ if (line == curr->end_line && col > curr->end_col) {
+ curr = curr->next;
+ continue;
+ }
- // Reference logic.
- if (node->definition_token.line > 0 && node->definition_token.line != node->token.line)
- {
- // It has a definition!
- lsp_index_add_ref(idx, node->token, node->definition_token, node);
- }
- else if (node->definition_token.line > 0)
- {
- lsp_index_add_ref(idx, node->token, node->definition_token, node);
+ best = curr;
}
+ curr = curr->next;
+ }
+ return best;
+}
- // General recursion.
-
- switch (node->type)
- {
- case NODE_ROOT:
- lsp_walk_node(idx, node->root.children);
- break;
- case NODE_BLOCK:
- lsp_walk_node(idx, node->block.statements);
- break;
- case NODE_IF:
- lsp_walk_node(idx, node->if_stmt.condition);
- lsp_walk_node(idx, node->if_stmt.then_body);
- lsp_walk_node(idx, node->if_stmt.else_body);
- break;
- case NODE_WHILE:
- lsp_walk_node(idx, node->while_stmt.condition);
- lsp_walk_node(idx, node->while_stmt.body);
- break;
- case NODE_RETURN:
- lsp_walk_node(idx, node->ret.value);
- break;
- case NODE_EXPR_BINARY:
- lsp_walk_node(idx, node->binary.left);
- lsp_walk_node(idx, node->binary.right);
- break;
- case NODE_EXPR_CALL:
- lsp_walk_node(idx, node->call.callee);
- lsp_walk_node(idx, node->call.args);
- break;
- default:
- break;
- }
+// Walker.
- // Walk next sibling.
- lsp_walk_node(idx, node->next);
+void lsp_walk_node(LSPIndex *idx, ASTNode *node) {
+ if (!node) {
+ return;
+ }
+
+ // Definition logic.
+ if (node->type == NODE_FUNCTION) {
+ char hover[256];
+ sprintf(hover, "fn %s(...) -> %s", node->func.name,
+ node->func.ret_type ? node->func.ret_type : "void");
+ lsp_index_add_def(idx, node->token, hover, node);
+
+ // Recurse body.
+ lsp_walk_node(idx, node->func.body);
+ } else if (node->type == NODE_VAR_DECL) {
+ char hover[256];
+ sprintf(hover, "var %s", node->var_decl.name);
+ lsp_index_add_def(idx, node->token, hover, node);
+
+ lsp_walk_node(idx, node->var_decl.init_expr);
+ } else if (node->type == NODE_CONST) {
+ char hover[256];
+ sprintf(hover, "const %s", node->var_decl.name);
+ lsp_index_add_def(idx, node->token, hover, node);
+
+ lsp_walk_node(idx, node->var_decl.init_expr);
+ }
+
+ // Reference logic.
+ if (node->definition_token.line > 0 &&
+ node->definition_token.line != node->token.line) {
+ // It has a definition!
+ lsp_index_add_ref(idx, node->token, node->definition_token, node);
+ } else if (node->definition_token.line > 0) {
+ lsp_index_add_ref(idx, node->token, node->definition_token, node);
+ }
+
+ // General recursion.
+
+ switch (node->type) {
+ case NODE_ROOT:
+ lsp_walk_node(idx, node->root.children);
+ break;
+ case NODE_BLOCK:
+ lsp_walk_node(idx, node->block.statements);
+ break;
+ case NODE_IF:
+ lsp_walk_node(idx, node->if_stmt.condition);
+ lsp_walk_node(idx, node->if_stmt.then_body);
+ lsp_walk_node(idx, node->if_stmt.else_body);
+ break;
+ case NODE_WHILE:
+ lsp_walk_node(idx, node->while_stmt.condition);
+ lsp_walk_node(idx, node->while_stmt.body);
+ break;
+ case NODE_RETURN:
+ lsp_walk_node(idx, node->ret.value);
+ break;
+ case NODE_EXPR_BINARY:
+ lsp_walk_node(idx, node->binary.left);
+ lsp_walk_node(idx, node->binary.right);
+ break;
+ case NODE_EXPR_CALL:
+ lsp_walk_node(idx, node->call.callee);
+ lsp_walk_node(idx, node->call.args);
+ break;
+ default:
+ break;
+ }
+
+ // Walk next sibling.
+ lsp_walk_node(idx, node->next);
}
-void lsp_build_index(LSPIndex *idx, ASTNode *root)
-{
- lsp_walk_node(idx, root);
-}
+void lsp_build_index(LSPIndex *idx, ASTNode *root) { lsp_walk_node(idx, root); }
diff --git a/src/lsp/lsp_index.h b/src/lsp/lsp_index.h
index f4aaf96..8b8207b 100644
--- a/src/lsp/lsp_index.h
+++ b/src/lsp/lsp_index.h
@@ -4,36 +4,31 @@
#include "parser.h"
-typedef enum
-{
- RANGE_DEFINITION,
- RANGE_REFERENCE
-} RangeType;
-
-typedef struct LSPRange
-{
- int start_line;
- int start_col;
- int end_line;
- int end_col; // Approximation.
- RangeType type;
- int def_line;
- int def_col;
- char *hover_text;
- ASTNode *node;
- struct LSPRange *next;
+typedef enum { RANGE_DEFINITION, RANGE_REFERENCE } RangeType;
+
+typedef struct LSPRange {
+ int start_line;
+ int start_col;
+ int end_line;
+ int end_col; // Approximation.
+ RangeType type;
+ int def_line;
+ int def_col;
+ char *hover_text;
+ ASTNode *node;
+ struct LSPRange *next;
} LSPRange;
-typedef struct LSPIndex
-{
- LSPRange *head;
- LSPRange *tail;
+typedef struct LSPIndex {
+ LSPRange *head;
+ LSPRange *tail;
} LSPIndex;
// API.
LSPIndex *lsp_index_new();
void lsp_index_free(LSPIndex *idx);
-void lsp_index_add_def(LSPIndex *idx, Token t, const char *hover, ASTNode *node);
+void lsp_index_add_def(LSPIndex *idx, Token t, const char *hover,
+ ASTNode *node);
void lsp_index_add_ref(LSPIndex *idx, Token t, Token def_t, ASTNode *node);
LSPRange *lsp_find_at(LSPIndex *idx, int line, int col);
diff --git a/src/lsp/lsp_main.c b/src/lsp/lsp_main.c
index fbe5312..a9a9f06 100644
--- a/src/lsp/lsp_main.c
+++ b/src/lsp/lsp_main.c
@@ -6,55 +6,47 @@
#include <unistd.h>
// Simple Main Loop for LSP.
-int lsp_main(int argc, char **argv)
-{
- (void)argc;
- (void)argv;
- fprintf(stderr, "zls: Zen Language Server starting...\n");
-
- while (1)
- {
- // Read headers
- int content_len = 0;
- char line[512];
- while (fgets(line, sizeof(line), stdin))
- {
- if (0 == strcmp(line, "\r\n"))
- {
- break; // End of headers
- }
- if (0 == strncmp(line, "Content-Length: ", 16))
- {
- content_len = atoi(line + 16);
- }
- }
-
- if (content_len <= 0)
- {
- // Maybe EOF or error?
- if (feof(stdin))
- {
- break;
- }
- continue; // Wait for more (yeah we gotta work on this).
- }
-
- // Read body.
- char *body = malloc(content_len + 1);
- if (fread(body, 1, content_len, stdin) != (size_t)content_len)
- {
- fprintf(stderr, "zls: Error reading body\n");
- free(body);
- break;
- }
- body[content_len] = 0;
-
- // Process JSON-RPC.
- fprintf(stderr, "zls: Received: %s\n", body);
- handle_request(body);
-
- free(body);
+int lsp_main(int argc, char **argv) {
+ (void)argc;
+ (void)argv;
+ fprintf(stderr, "zls: Zen Language Server starting...\n");
+
+ while (1) {
+ // Read headers
+ int content_len = 0;
+ char line[512];
+ while (fgets(line, sizeof(line), stdin)) {
+ if (0 == strcmp(line, "\r\n")) {
+ break; // End of headers
+ }
+ if (0 == strncmp(line, "Content-Length: ", 16)) {
+ content_len = atoi(line + 16);
+ }
}
- return 0;
+ if (content_len <= 0) {
+ // Maybe EOF or error?
+ if (feof(stdin)) {
+ break;
+ }
+ continue; // Wait for more (yeah we gotta work on this).
+ }
+
+ // Read body.
+ char *body = malloc(content_len + 1);
+ if (fread(body, 1, content_len, stdin) != (size_t)content_len) {
+ fprintf(stderr, "zls: Error reading body\n");
+ free(body);
+ break;
+ }
+ body[content_len] = 0;
+
+ // Process JSON-RPC.
+ fprintf(stderr, "zls: Received: %s\n", body);
+ handle_request(body);
+
+ free(body);
+ }
+
+ return 0;
}
diff --git a/src/main.c b/src/main.c
index 2c33a6b..ab4c39f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -12,334 +12,258 @@
// Forward decl for LSP
int lsp_main(int argc, char **argv);
-void print_search_paths()
-{
- printf("Search paths:\n");
- printf(" ./\n");
- printf(" ./std/\n");
- printf(" /usr/local/share/zenc\n");
- printf(" /usr/share/zenc\n");
+void print_search_paths() {
+ printf("Search paths:\n");
+ printf(" ./\n");
+ printf(" ./std/\n");
+ printf(" /usr/local/share/zenc\n");
+ printf(" /usr/share/zenc\n");
}
-void print_version()
-{
- printf("Zen C version %s\n", ZEN_VERSION);
+void print_version() { printf("Zen C version %s\n", ZEN_VERSION); }
+
+void print_usage() {
+ printf("Usage: zc [command] [options] <file.zc>\n");
+ printf("Commands:\n");
+ printf(" run Compile and run the program\n");
+ printf(" build Compile to executable\n");
+ printf(" check Check for errors only\n");
+ printf(" repl Start Interactive REPL\n");
+ printf(" transpile Transpile to C code only (no compilation)\n");
+ printf(" lsp Start Language Server\n");
+ printf("Options:\n");
+ printf(" --version Print version information");
+ printf(" -o <file> Output executable name\n");
+ printf(" --emit-c Keep generated C file (out.c)\n");
+ printf(" --freestanding Freestanding mode (no stdlib)\n");
+ printf(" --cc <compiler> C compiler to use (gcc, clang, tcc, zig)\n");
+ printf(" -O<level> Optimization level\n");
+ printf(" -g Debug info\n");
+ printf(" -v, --verbose Verbose output\n");
+ printf(" -q, --quiet Quiet output\n");
+ printf(" -c Compile only (produce .o)\n");
}
-void print_usage()
-{
- printf("Usage: zc [command] [options] <file.zc>\n");
- printf("Commands:\n");
- printf(" run Compile and run the program\n");
- printf(" build Compile to executable\n");
- printf(" check Check for errors only\n");
- printf(" repl Start Interactive REPL\n");
- printf(" transpile Transpile to C code only (no compilation)\n");
- printf(" lsp Start Language Server\n");
- printf("Options:\n");
- printf(" --version Print version information");
- printf(" -o <file> Output executable name\n");
- printf(" --emit-c Keep generated C file (out.c)\n");
- printf(" --freestanding Freestanding mode (no stdlib)\n");
- printf(" --cc <compiler> C compiler to use (gcc, clang, tcc, zig)\n");
- printf(" -O<level> Optimization level\n");
- printf(" -g Debug info\n");
- printf(" -v, --verbose Verbose output\n");
- printf(" -q, --quiet Quiet output\n");
- printf(" -c Compile only (produce .o)\n");
-}
-
-int main(int argc, char **argv)
-{
- // Defaults
- memset(&g_config, 0, sizeof(g_config));
- strcpy(g_config.cc, "gcc");
+int main(int argc, char **argv) {
+ // Defaults
+ memset(&g_config, 0, sizeof(g_config));
+ strcpy(g_config.cc, "gcc");
- if (argc < 2)
- {
- print_usage();
- return 1;
- }
+ if (argc < 2) {
+ print_usage();
+ return 1;
+ }
- // Parse command
- char *command = argv[1];
- int arg_start = 2;
+ // Parse command
+ char *command = argv[1];
+ int arg_start = 2;
- if (strcmp(command, "lsp") == 0)
- {
- return lsp_main(argc, argv);
- }
- else if (strcmp(command, "repl") == 0)
- {
- run_repl(argv[0]); // Pass self path for recursive calls
- return 0;
- }
- else if (strcmp(command, "transpile") == 0)
- {
- g_config.mode_transpile = 1;
- g_config.emit_c = 1; // Transpile implies emitting C
- }
- else if (strcmp(command, "run") == 0)
- {
- g_config.mode_run = 1;
- }
- else if (strcmp(command, "check") == 0)
- {
- g_config.mode_check = 1;
- }
- else if (strcmp(command, "build") == 0)
- {
- // default mode
- }
- else if (command[0] == '-')
- {
- // implicit build or run? assume build if starts with flag, but usually
- // command first If file provided directly: "zc file.zc" -> build
- if (strchr(command, '.'))
- {
- // treat as filename
- g_config.input_file = command;
- arg_start = 2; // already consumed
- }
- else
- {
- // Flags
- arg_start = 1;
- }
+ if (strcmp(command, "lsp") == 0) {
+ return lsp_main(argc, argv);
+ } else if (strcmp(command, "repl") == 0) {
+ run_repl(argv[0]); // Pass self path for recursive calls
+ return 0;
+ } else if (strcmp(command, "transpile") == 0) {
+ g_config.mode_transpile = 1;
+ g_config.emit_c = 1; // Transpile implies emitting C
+ } else if (strcmp(command, "run") == 0) {
+ g_config.mode_run = 1;
+ } else if (strcmp(command, "check") == 0) {
+ g_config.mode_check = 1;
+ } else if (strcmp(command, "build") == 0) {
+ // default mode
+ } else if (command[0] == '-') {
+ // implicit build or run? assume build if starts with flag, but usually
+ // command first If file provided directly: "zc file.zc" -> build
+ if (strchr(command, '.')) {
+ // treat as filename
+ g_config.input_file = command;
+ arg_start = 2; // already consumed
+ } else {
+ // Flags
+ arg_start = 1;
}
- else
- {
- // Check if file
- if (strchr(command, '.'))
- {
- g_config.input_file = command;
- arg_start = 2;
- }
+ } else {
+ // Check if file
+ if (strchr(command, '.')) {
+ g_config.input_file = command;
+ arg_start = 2;
}
-
- // Parse args
- for (int i = arg_start; i < argc; i++)
- {
- char *arg = argv[i];
- if (strcmp(arg, "--emit-c") == 0)
- {
- g_config.emit_c = 1;
- }
- else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-V") == 0)
- {
- print_version();
- return 0;
+ }
+
+ // Parse args
+ for (int i = arg_start; i < argc; i++) {
+ char *arg = argv[i];
+ if (strcmp(arg, "--emit-c") == 0) {
+ g_config.emit_c = 1;
+ } else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-V") == 0) {
+ print_version();
+ return 0;
+ } else if (strcmp(arg, "--verbose") == 0 || strcmp(arg, "-v") == 0) {
+ g_config.verbose = 1;
+ } else if (strcmp(arg, "--quiet") == 0 || strcmp(arg, "-q") == 0) {
+ g_config.quiet = 1;
+ } else if (strcmp(arg, "--freestanding") == 0) {
+ g_config.is_freestanding = 1;
+ } else if (strcmp(arg, "--check") == 0) {
+ g_config.mode_check = 1;
+ } else if (strcmp(arg, "--cc") == 0) {
+ if (i + 1 < argc) {
+ char *cc_arg = argv[++i];
+ // Handle "zig" shorthand for "zig cc"
+ if (strcmp(cc_arg, "zig") == 0) {
+ strcpy(g_config.cc, "zig cc");
+ } else {
+ strcpy(g_config.cc, cc_arg);
}
- else if (strcmp(arg, "--verbose") == 0 || strcmp(arg, "-v") == 0)
- {
- g_config.verbose = 1;
- }
- else if (strcmp(arg, "--quiet") == 0 || strcmp(arg, "-q") == 0)
- {
- g_config.quiet = 1;
- }
- else if (strcmp(arg, "--freestanding") == 0)
- {
- g_config.is_freestanding = 1;
- }
- else if (strcmp(arg, "--check") == 0)
- {
- g_config.mode_check = 1;
- }
- else if (strcmp(arg, "--cc") == 0)
- {
- if (i + 1 < argc)
- {
- char *cc_arg = argv[++i];
- // Handle "zig" shorthand for "zig cc"
- if (strcmp(cc_arg, "zig") == 0)
- {
- strcpy(g_config.cc, "zig cc");
- }
- else
- {
- strcpy(g_config.cc, cc_arg);
- }
- }
- }
- else if (strcmp(arg, "-o") == 0)
- {
- if (i + 1 < argc)
- {
- g_config.output_file = argv[++i];
- }
- }
- else if (strncmp(arg, "-O", 2) == 0)
- {
- // Add to CFLAGS
- strcat(g_config.gcc_flags, " ");
- strcat(g_config.gcc_flags, arg);
- }
- else if (strcmp(arg, "-g") == 0)
- {
- strcat(g_config.gcc_flags, " -g");
- }
- else if (arg[0] == '-')
- {
- // Unknown flag or C flag
- strcat(g_config.gcc_flags, " ");
- strcat(g_config.gcc_flags, arg);
- }
- else
- {
- if (!g_config.input_file)
- {
- g_config.input_file = arg;
- }
- else
- {
- printf("Multiple input files not supported yet.\n");
- return 1;
- }
- }
- }
-
- if (!g_config.input_file)
- {
- printf("Error: No input file specified.\n");
- return 1;
- }
-
- g_current_filename = g_config.input_file;
-
- // Load file
- char *src = load_file(g_config.input_file);
- if (!src)
- {
- printf("Error: Could not read file %s\n", g_config.input_file);
+ }
+ } else if (strcmp(arg, "-o") == 0) {
+ if (i + 1 < argc) {
+ g_config.output_file = argv[++i];
+ }
+ } else if (strncmp(arg, "-O", 2) == 0) {
+ // Add to CFLAGS
+ strcat(g_config.gcc_flags, " ");
+ strcat(g_config.gcc_flags, arg);
+ } else if (strcmp(arg, "-g") == 0) {
+ strcat(g_config.gcc_flags, " -g");
+ } else if (arg[0] == '-') {
+ // Unknown flag or C flag
+ strcat(g_config.gcc_flags, " ");
+ strcat(g_config.gcc_flags, arg);
+ } else {
+ if (!g_config.input_file) {
+ g_config.input_file = arg;
+ } else {
+ printf("Multiple input files not supported yet.\n");
return 1;
+ }
}
-
- init_builtins();
- zen_init();
-
- // Initialize Plugin Manager
- zptr_plugin_mgr_init();
-
- // Parse context init
- ParserContext ctx;
- memset(&ctx, 0, sizeof(ctx));
-
- // Scan for build directives (e.g. //> link: -lm)
- scan_build_directives(&ctx, src);
-
- Lexer l;
- lexer_init(&l, src);
-
- ctx.hoist_out = tmpfile(); // Temp file for plugin hoisting
- if (!ctx.hoist_out)
- {
- perror("tmpfile for hoisting");
- return 1;
- }
- g_parser_ctx = &ctx;
-
- if (!g_config.quiet)
- {
- printf("[zc] Compiling %s...\n", g_config.input_file);
- }
-
- ASTNode *root = parse_program(&ctx, &l);
- if (!root)
- {
- // Parse failed
- return 1;
- }
-
- if (g_config.mode_check)
- {
- // Just verify
- printf("Check passed.\n");
- return 0;
- }
-
- // Codegen to C
- FILE *out = fopen("out.c", "w");
- if (!out)
- {
- perror("fopen out.c");
+ }
+
+ if (!g_config.input_file) {
+ printf("Error: No input file specified.\n");
+ return 1;
+ }
+
+ g_current_filename = g_config.input_file;
+
+ // Load file
+ char *src = load_file(g_config.input_file);
+ if (!src) {
+ printf("Error: Could not read file %s\n", g_config.input_file);
+ return 1;
+ }
+
+ init_builtins();
+ zen_init();
+
+ // Initialize Plugin Manager
+ zptr_plugin_mgr_init();
+
+ // Parse context init
+ ParserContext ctx;
+ memset(&ctx, 0, sizeof(ctx));
+
+ // Scan for build directives (e.g. //> link: -lm)
+ scan_build_directives(&ctx, src);
+
+ Lexer l;
+ lexer_init(&l, src);
+
+ ctx.hoist_out = tmpfile(); // Temp file for plugin hoisting
+ if (!ctx.hoist_out) {
+ perror("tmpfile for hoisting");
+ return 1;
+ }
+ g_parser_ctx = &ctx;
+
+ if (!g_config.quiet) {
+ printf("[zc] Compiling %s...\n", g_config.input_file);
+ }
+
+ ASTNode *root = parse_program(&ctx, &l);
+ if (!root) {
+ // Parse failed
+ return 1;
+ }
+
+ if (g_config.mode_check) {
+ // Just verify
+ printf("Check passed.\n");
+ return 0;
+ }
+
+ // Codegen to C
+ FILE *out = fopen("out.c", "w");
+ if (!out) {
+ perror("fopen out.c");
+ return 1;
+ }
+
+ codegen_node(&ctx, root, out);
+ fclose(out);
+
+ if (g_config.mode_transpile) {
+ if (g_config.output_file) {
+ // If user specified -o, rename out.c to that
+ if (rename("out.c", g_config.output_file) != 0) {
+ perror("rename out.c");
return 1;
+ }
+ if (!g_config.quiet) {
+ printf("[zc] Transpiled to %s\n", g_config.output_file);
+ }
+ } else {
+ if (!g_config.quiet) {
+ printf("[zc] Transpiled to out.c\n");
+ }
}
+ // Done, no C compilation
+ return 0;
+ }
- codegen_node(&ctx, root, out);
- fclose(out);
-
- if (g_config.mode_transpile)
- {
- if (g_config.output_file)
- {
- // If user specified -o, rename out.c to that
- if (rename("out.c", g_config.output_file) != 0)
- {
- perror("rename out.c");
- return 1;
- }
- if (!g_config.quiet)
- {
- printf("[zc] Transpiled to %s\n", g_config.output_file);
- }
- }
- else
- {
- if (!g_config.quiet)
- {
- printf("[zc] Transpiled to out.c\n");
- }
- }
- // Done, no C compilation
- return 0;
- }
-
- // Compile C
- char cmd[8192];
- char *outfile = g_config.output_file ? g_config.output_file : "a.out";
-
- // TCC-specific adjustments?
- // Already handled by user passing --cc tcc
-
- snprintf(cmd, sizeof(cmd), "%s %s %s %s %s -o %s out.c -lm %s -I./src %s", g_config.cc,
- g_config.gcc_flags, g_cflags, g_config.is_freestanding ? "-ffreestanding" : "", "",
- outfile, g_parser_ctx->has_async ? "-lpthread" : "", g_link_flags);
+ // Compile C
+ char cmd[8192];
+ char *outfile = g_config.output_file ? g_config.output_file : "a.out";
- if (g_config.verbose)
- {
- printf("[CMD] %s\n", cmd);
- }
+ // TCC-specific adjustments?
+ // Already handled by user passing --cc tcc
- int ret = system(cmd);
- if (ret != 0)
- {
- printf("C compilation failed.\n");
- if (!g_config.emit_c)
- {
- remove("out.c");
- }
- return 1;
- }
+ snprintf(cmd, sizeof(cmd), "%s %s %s %s %s -o %s out.c -lm %s -I./src %s",
+ g_config.cc, g_config.gcc_flags, g_cflags,
+ g_config.is_freestanding ? "-ffreestanding" : "", "", outfile,
+ g_parser_ctx->has_async ? "-lpthread" : "", g_link_flags);
- if (!g_config.emit_c)
- {
- // remove("out.c"); // Keep it for debugging for now or follow flag
- remove("out.c");
- }
+ if (g_config.verbose) {
+ printf("[CMD] %s\n", cmd);
+ }
- if (g_config.mode_run)
- {
- char run_cmd[2048];
- sprintf(run_cmd, "./%s", outfile);
- ret = system(run_cmd);
- remove(outfile);
- zptr_plugin_mgr_cleanup();
- zen_trigger_global();
- return ret;
+ int ret = system(cmd);
+ if (ret != 0) {
+ printf("C compilation failed.\n");
+ if (!g_config.emit_c) {
+ remove("out.c");
}
-
+ return 1;
+ }
+
+ if (!g_config.emit_c) {
+ // remove("out.c"); // Keep it for debugging for now or follow flag
+ remove("out.c");
+ }
+
+ if (g_config.mode_run) {
+ char run_cmd[2048];
+ sprintf(run_cmd, "./%s", outfile);
+ ret = system(run_cmd);
+ remove(outfile);
zptr_plugin_mgr_cleanup();
zen_trigger_global();
- return 0;
+ return ret;
+ }
+
+ zptr_plugin_mgr_cleanup();
+ zen_trigger_global();
+ return 0;
}
diff --git a/src/parser/parser.h b/src/parser/parser.h
index 4d105cf..815164c 100644
--- a/src/parser/parser.h
+++ b/src/parser/parser.h
@@ -6,20 +6,19 @@
#include "zprep.h"
// Operator precedence for expression parsing
-typedef enum
-{
- PREC_NONE,
- PREC_ASSIGNMENT,
- PREC_TERNARY,
- PREC_OR,
- PREC_AND,
- PREC_EQUALITY,
- PREC_COMPARISON,
- PREC_TERM,
- PREC_FACTOR,
- PREC_UNARY,
- PREC_CALL,
- PREC_PRIMARY
+typedef enum {
+ PREC_NONE,
+ PREC_ASSIGNMENT,
+ PREC_TERNARY,
+ PREC_OR,
+ PREC_AND,
+ PREC_EQUALITY,
+ PREC_COMPARISON,
+ PREC_TERM,
+ PREC_FACTOR,
+ PREC_UNARY,
+ PREC_CALL,
+ PREC_PRIMARY
} Precedence;
// Main entry points
@@ -32,240 +31,219 @@ ASTNode *parse_program(ParserContext *ctx, Lexer *l);
extern ParserContext *g_parser_ctx;
// Symbol table
-typedef struct Symbol
-{
- char *name;
- char *type_name;
- Type *type_info;
- int is_mutable;
- int is_used;
- int is_autofree;
- Token decl_token;
- int is_const_value;
- int const_int_val;
- struct Symbol *next;
+typedef struct Symbol {
+ char *name;
+ char *type_name;
+ Type *type_info;
+ int is_mutable;
+ int is_used;
+ int is_autofree;
+ Token decl_token;
+ int is_const_value;
+ int const_int_val;
+ struct Symbol *next;
} Symbol;
-typedef struct Scope
-{
- Symbol *symbols;
- struct Scope *parent;
+typedef struct Scope {
+ Symbol *symbols;
+ struct Scope *parent;
} Scope;
// Function registry
-typedef struct FuncSig
-{
- char *name;
- Token decl_token; // For LSP
- int total_args;
- char **defaults;
- Type **arg_types;
- Type *ret_type;
- int is_varargs;
- int is_async; // Async function flag
- int must_use; // Attribute: warn if return value discarded
- struct FuncSig *next;
+typedef struct FuncSig {
+ char *name;
+ Token decl_token; // For LSP
+ int total_args;
+ char **defaults;
+ Type **arg_types;
+ Type *ret_type;
+ int is_varargs;
+ int is_async; // Async function flag
+ int must_use; // Attribute: warn if return value discarded
+ struct FuncSig *next;
} FuncSig;
// Lambda tracking
-typedef struct LambdaRef
-{
- ASTNode *node;
- struct LambdaRef *next;
+typedef struct LambdaRef {
+ ASTNode *node;
+ struct LambdaRef *next;
} LambdaRef;
-typedef struct GenericTemplate
-{
- char *name;
- ASTNode *struct_node;
- struct GenericTemplate *next;
+typedef struct GenericTemplate {
+ char *name;
+ ASTNode *struct_node;
+ struct GenericTemplate *next;
} GenericTemplate;
-typedef struct GenericFuncTemplate
-{
- char *name;
- char *generic_param;
- ASTNode *func_node;
- struct GenericFuncTemplate *next;
+typedef struct GenericFuncTemplate {
+ char *name;
+ char *generic_param;
+ ASTNode *func_node;
+ struct GenericFuncTemplate *next;
} GenericFuncTemplate;
-typedef struct GenericImplTemplate
-{
- char *struct_name;
- char *generic_param;
- ASTNode *impl_node;
- struct GenericImplTemplate *next;
+typedef struct GenericImplTemplate {
+ char *struct_name;
+ char *generic_param;
+ ASTNode *impl_node;
+ struct GenericImplTemplate *next;
} GenericImplTemplate;
-typedef struct ImportedFile
-{
- char *path;
- struct ImportedFile *next;
+typedef struct ImportedFile {
+ char *path;
+ struct ImportedFile *next;
} ImportedFile;
-typedef struct VarMutability
-{
- char *name;
- int is_mutable;
- struct VarMutability *next;
+typedef struct VarMutability {
+ char *name;
+ int is_mutable;
+ struct VarMutability *next;
} VarMutability;
// Instantiation tracking
-typedef struct Instantiation
-{
- char *name;
- char *template_name;
- char *concrete_arg;
- ASTNode *struct_node;
- struct Instantiation *next;
+typedef struct Instantiation {
+ char *name;
+ char *template_name;
+ char *concrete_arg;
+ ASTNode *struct_node;
+ struct Instantiation *next;
} Instantiation;
-typedef struct StructRef
-{
- ASTNode *node;
- struct StructRef *next;
+typedef struct StructRef {
+ ASTNode *node;
+ struct StructRef *next;
} StructRef;
-typedef struct StructDef
-{
- char *name;
- ASTNode *node;
- struct StructDef *next;
+typedef struct StructDef {
+ char *name;
+ ASTNode *node;
+ struct StructDef *next;
} StructDef;
// Type tracking
-typedef struct SliceType
-{
- char *name;
- struct SliceType *next;
+typedef struct SliceType {
+ char *name;
+ struct SliceType *next;
} SliceType;
-typedef struct TupleType
-{
- char *sig;
- struct TupleType *next;
+typedef struct TupleType {
+ char *sig;
+ struct TupleType *next;
} TupleType;
// Enum tracking
-typedef struct EnumVariantReg
-{
- char *enum_name;
- char *variant_name;
- int tag_id;
- struct EnumVariantReg *next;
+typedef struct EnumVariantReg {
+ char *enum_name;
+ char *variant_name;
+ int tag_id;
+ struct EnumVariantReg *next;
} EnumVariantReg;
// Deprecated function tracking
-typedef struct DeprecatedFunc
-{
- char *name;
- char *reason; // Optional reason message
- struct DeprecatedFunc *next;
+typedef struct DeprecatedFunc {
+ char *name;
+ char *reason; // Optional reason message
+ struct DeprecatedFunc *next;
} DeprecatedFunc;
// Module system
-typedef struct Module
-{
- char *alias;
- char *path;
- char *base_name;
- int is_c_header;
- struct Module *next;
+typedef struct Module {
+ char *alias;
+ char *path;
+ char *base_name;
+ int is_c_header;
+ struct Module *next;
} Module;
-typedef struct SelectiveImport
-{
- char *symbol;
- char *alias;
- char *source_module;
- struct SelectiveImport *next;
+typedef struct SelectiveImport {
+ char *symbol;
+ char *alias;
+ char *source_module;
+ struct SelectiveImport *next;
} SelectiveImport;
// Impl cache
-typedef struct ImplReg
-{
- char *trait;
- char *strct;
- struct ImplReg *next;
+typedef struct ImplReg {
+ char *trait;
+ char *strct;
+ struct ImplReg *next;
} ImplReg;
// Plugin tracking
-typedef struct ImportedPlugin
-{
- char *name; // Original plugin name (for example, "brainfuck")
- char *alias; // Optional alias (for example, "bf"), NULL if no alias
- struct ImportedPlugin *next;
+typedef struct ImportedPlugin {
+ char *name; // Original plugin name (for example, "brainfuck")
+ char *alias; // Optional alias (for example, "bf"), NULL if no alias
+ struct ImportedPlugin *next;
} ImportedPlugin;
-struct ParserContext
-{
- Scope *current_scope;
- FuncSig *func_registry;
+struct ParserContext {
+ Scope *current_scope;
+ FuncSig *func_registry;
- // Lambdas
- LambdaRef *global_lambdas;
- int lambda_counter;
+ // Lambdas
+ LambdaRef *global_lambdas;
+ int lambda_counter;
// Generics
#define MAX_KNOWN_GENERICS 1024
- char *known_generics[MAX_KNOWN_GENERICS];
- int known_generics_count;
- GenericTemplate *templates;
- GenericFuncTemplate *func_templates;
- GenericImplTemplate *impl_templates;
-
- // Instantiations
- Instantiation *instantiations;
- ASTNode *instantiated_structs;
- ASTNode *instantiated_funcs;
-
- // Structs/Enums
- StructRef *parsed_structs_list;
- StructRef *parsed_enums_list;
- StructRef *parsed_funcs_list;
- StructRef *parsed_impls_list;
- StructRef *parsed_globals_list;
- StructDef *struct_defs;
- EnumVariantReg *enum_variants;
- ImplReg *registered_impls;
-
- // Types
- SliceType *used_slices;
- TupleType *used_tuples;
-
- // Modules/Imports
- Module *modules;
- SelectiveImport *selective_imports;
- char *current_module_prefix;
- ImportedFile *imported_files;
- ImportedPlugin *imported_plugins; // Plugin imports
-
- // Config/State
- int immutable_by_default;
- char *current_impl_struct;
-
- // Internal tracking
- VarMutability *var_mutability_table;
- DeprecatedFunc *deprecated_funcs;
-
- // LSP / Fault Tolerance
- int is_fault_tolerant;
- void *error_callback_data;
- void (*on_error)(void *data, Token t, const char *msg);
-
- // LSP: Flat symbol list (persists after parsing for LSP queries)
- Symbol *all_symbols;
-
- // External C interop: suppress undefined warnings for external symbols
- int has_external_includes; // Set when include <...> is used
- char **extern_symbols; // Explicitly declared extern symbols
- int extern_symbol_count;
-
- // Codegen state:
- FILE *hoist_out; // For plugins to hoist code to file scope
- int skip_preamble; // If 1, codegen_node(NODE_ROOT) won't emit preamble
- int is_repl; // REPL mode flag
- int has_async; // Track if async features are used
+ char *known_generics[MAX_KNOWN_GENERICS];
+ int known_generics_count;
+ GenericTemplate *templates;
+ GenericFuncTemplate *func_templates;
+ GenericImplTemplate *impl_templates;
+
+ // Instantiations
+ Instantiation *instantiations;
+ ASTNode *instantiated_structs;
+ ASTNode *instantiated_funcs;
+
+ // Structs/Enums
+ StructRef *parsed_structs_list;
+ StructRef *parsed_enums_list;
+ StructRef *parsed_funcs_list;
+ StructRef *parsed_impls_list;
+ StructRef *parsed_globals_list;
+ StructDef *struct_defs;
+ EnumVariantReg *enum_variants;
+ ImplReg *registered_impls;
+
+ // Types
+ SliceType *used_slices;
+ TupleType *used_tuples;
+
+ // Modules/Imports
+ Module *modules;
+ SelectiveImport *selective_imports;
+ char *current_module_prefix;
+ ImportedFile *imported_files;
+ ImportedPlugin *imported_plugins; // Plugin imports
+
+ // Config/State
+ int immutable_by_default;
+ char *current_impl_struct;
+
+ // Internal tracking
+ VarMutability *var_mutability_table;
+ DeprecatedFunc *deprecated_funcs;
+
+ // LSP / Fault Tolerance
+ int is_fault_tolerant;
+ void *error_callback_data;
+ void (*on_error)(void *data, Token t, const char *msg);
+
+ // LSP: Flat symbol list (persists after parsing for LSP queries)
+ Symbol *all_symbols;
+
+ // External C interop: suppress undefined warnings for external symbols
+ int has_external_includes; // Set when include <...> is used
+ char **extern_symbols; // Explicitly declared extern symbols
+ int extern_symbol_count;
+
+ // Codegen state:
+ FILE *hoist_out; // For plugins to hoist code to file scope
+ int skip_preamble; // If 1, codegen_node(NODE_ROOT) won't emit preamble
+ int is_repl; // REPL mode flag
+ int has_async; // Track if async features are used
};
// Token helpers
@@ -283,9 +261,10 @@ void warn_c_reserved_word(Token t, const char *name);
// Symbol table
void enter_scope(ParserContext *ctx);
void exit_scope(ParserContext *ctx);
-void add_symbol(ParserContext *ctx, const char *n, const char *t, Type *type_info);
-void add_symbol_with_token(ParserContext *ctx, const char *n, const char *t, Type *type_info,
- Token tok);
+void add_symbol(ParserContext *ctx, const char *n, const char *t,
+ Type *type_info);
+void add_symbol_with_token(ParserContext *ctx, const char *n, const char *t,
+ Type *type_info, Token tok);
Type *find_symbol_type_info(ParserContext *ctx, const char *n);
char *find_symbol_type(ParserContext *ctx, const char *n);
Symbol *find_symbol_entry(ParserContext *ctx, const char *n);
@@ -294,17 +273,18 @@ Symbol *find_symbol_in_all(ParserContext *ctx,
char *find_similar_symbol(ParserContext *ctx, const char *name);
// Function registry
-void register_func(ParserContext *ctx, const char *name, int count, char **defaults,
- Type **arg_types, Type *ret_type, int is_varargs, int is_async,
- Token decl_token);
-void register_func_template(ParserContext *ctx, const char *name, const char *param, ASTNode *node);
+void register_func(ParserContext *ctx, const char *name, int count,
+ char **defaults, Type **arg_types, Type *ret_type,
+ int is_varargs, int is_async, Token decl_token);
+void register_func_template(ParserContext *ctx, const char *name,
+ const char *param, ASTNode *node);
GenericFuncTemplate *find_func_template(ParserContext *ctx, const char *name);
// Generic/template helpers
void register_generic(ParserContext *ctx, char *name);
int is_known_generic(ParserContext *ctx, char *name);
-void register_impl_template(ParserContext *ctx, const char *sname, const char *param,
- ASTNode *node);
+void register_impl_template(ParserContext *ctx, const char *sname,
+ const char *param, ASTNode *node);
void add_to_struct_list(ParserContext *ctx, ASTNode *node);
void add_to_enum_list(ParserContext *ctx, ASTNode *node);
void add_to_func_list(ParserContext *ctx, ASTNode *node);
@@ -312,19 +292,24 @@ void add_to_impl_list(ParserContext *ctx, ASTNode *node);
void add_to_global_list(ParserContext *ctx, ASTNode *node);
void register_builtins(ParserContext *ctx);
void add_instantiated_func(ParserContext *ctx, ASTNode *fn);
-void instantiate_generic(ParserContext *ctx, const char *name, const char *concrete_type, Token t);
+void instantiate_generic(ParserContext *ctx, const char *name,
+ const char *concrete_type, Token t);
char *sanitize_mangled_name(const char *s);
void register_impl(ParserContext *ctx, const char *trait, const char *strct);
int check_impl(ParserContext *ctx, const char *trait, const char *strct);
void register_template(ParserContext *ctx, const char *name, ASTNode *node);
-void register_deprecated_func(ParserContext *ctx, const char *name, const char *reason);
+void register_deprecated_func(ParserContext *ctx, const char *name,
+ const char *reason);
DeprecatedFunc *find_deprecated_func(ParserContext *ctx, const char *name);
-ASTNode *parse_arrow_lambda_single(ParserContext *ctx, Lexer *l, char *param_name);
-ASTNode *parse_arrow_lambda_multi(ParserContext *ctx, Lexer *l, char **param_names, int num_params);
+ASTNode *parse_arrow_lambda_single(ParserContext *ctx, Lexer *l,
+ char *param_name);
+ASTNode *parse_arrow_lambda_multi(ParserContext *ctx, Lexer *l,
+ char **param_names, int num_params);
// Utils
-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);
+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);
int is_file_imported(ParserContext *ctx, const char *path);
void mark_file_imported(ParserContext *ctx, const char *path);
void register_plugin(ParserContext *ctx, const char *name, const char *alias);
@@ -335,13 +320,15 @@ void print_type_defs(ParserContext *ctx, FILE *out, ASTNode *nodes);
char *replace_in_string(const char *src, const char *old_w, const char *new_w);
char *replace_type_str(const char *src, const char *param, const char *concrete,
const char *old_struct, const char *new_struct);
-Type *replace_type_formal(Type *t, const char *p, const char *c, const char *os, const char *ns);
-ASTNode *copy_ast_replacing(ASTNode *n, const char *p, const char *c, const char *os,
- const char *ns);
+Type *replace_type_formal(Type *t, const char *p, const char *c, const char *os,
+ const char *ns);
+ASTNode *copy_ast_replacing(ASTNode *n, const char *p, const char *c,
+ const char *os, const char *ns);
char *extract_module_name(const char *path);
// Enum helpers
-void register_enum_variant(ParserContext *ctx, const char *ename, const char *vname, int tag);
+void register_enum_variant(ParserContext *ctx, const char *ename,
+ const char *vname, int tag);
EnumVariantReg *find_enum_variant(ParserContext *ctx, const char *vname);
// Lambda helpers
@@ -359,12 +346,13 @@ void register_struct_def(ParserContext *ctx, const char *name, ASTNode *node);
// Module system
Module *find_module(ParserContext *ctx, const char *alias);
void register_module(ParserContext *ctx, const char *alias, const char *path);
-void register_selective_import(ParserContext *ctx, const char *symbol, const char *alias,
- const char *source_module);
+void register_selective_import(ParserContext *ctx, const char *symbol,
+ const char *alias, const char *source_module);
SelectiveImport *find_selective_import(ParserContext *ctx, const char *name);
// Mutability tracking
-void register_var_mutability(ParserContext *ctx, const char *name, int is_mutable);
+void register_var_mutability(ParserContext *ctx, const char *name,
+ int is_mutable);
int is_var_mutable(ParserContext *ctx, const char *name);
// External symbol tracking (C interop)
@@ -377,7 +365,8 @@ void init_builtins();
// Expression rewriting
char *rewrite_expr_methods(ParserContext *ctx, char *raw);
-char *process_fstring(ParserContext *ctx, const char *content, char ***used_syms, int *count);
+char *process_fstring(ParserContext *ctx, const char *content,
+ char ***used_syms, int *count);
char *instantiate_function_template(ParserContext *ctx, const char *name,
const char *concrete_type);
FuncSig *find_func(ParserContext *ctx, const char *name);
@@ -409,8 +398,8 @@ ASTNode *parse_guard(ParserContext *ctx, Lexer *l);
ASTNode *parse_match(ParserContext *ctx, Lexer *l);
ASTNode *parse_return(ParserContext *ctx, Lexer *l);
-char *process_printf_sugar(ParserContext *ctx, const char *content, int newline, const char *target,
- char ***used_syms, int *count);
+char *process_printf_sugar(ParserContext *ctx, const char *content, int newline,
+ const char *target, char ***used_syms, int *count);
ASTNode *parse_assert(ParserContext *ctx, Lexer *l);
ASTNode *parse_defer(ParserContext *ctx, Lexer *l);
ASTNode *parse_asm(ParserContext *ctx, Lexer *l);
diff --git a/src/parser/parser_core.c b/src/parser/parser_core.c
index 7d5bc32..777fc46 100644
--- a/src/parser/parser_core.c
+++ b/src/parser/parser_core.c
@@ -5,614 +5,453 @@
#include <stdlib.h>
#include <string.h>
-static ASTNode *generate_derive_impls(ParserContext *ctx, ASTNode *strct, char **traits, int count);
+static ASTNode *generate_derive_impls(ParserContext *ctx, ASTNode *strct,
+ char **traits, int count);
// Main parsing entry point
-ASTNode *parse_program_nodes(ParserContext *ctx, Lexer *l)
-{
- ASTNode *h = 0, *tl = 0;
- while (1)
- {
- skip_comments(l);
- Token t = lexer_peek(l);
-
- if (t.type == TOK_EOF)
- {
- break;
- }
+ASTNode *parse_program_nodes(ParserContext *ctx, Lexer *l) {
+ ASTNode *h = 0, *tl = 0;
+ while (1) {
+ skip_comments(l);
+ Token t = lexer_peek(l);
+
+ if (t.type == TOK_EOF) {
+ break;
+ }
- if (t.type == TOK_COMPTIME)
- {
- ASTNode *gen = parse_comptime(ctx, l);
- if (gen)
- {
- if (!h)
- {
- h = gen;
- }
- else
- {
- tl->next = gen;
- }
- if (!tl)
- {
- tl = gen;
- }
- while (tl->next)
- {
- tl = tl->next;
- }
- }
- continue;
+ if (t.type == TOK_COMPTIME) {
+ ASTNode *gen = parse_comptime(ctx, l);
+ if (gen) {
+ if (!h) {
+ h = gen;
+ } else {
+ tl->next = gen;
+ }
+ if (!tl) {
+ tl = gen;
+ }
+ while (tl->next) {
+ tl = tl->next;
}
+ }
+ continue;
+ }
- ASTNode *s = 0;
-
- int attr_must_use = 0;
- int attr_deprecated = 0;
- int attr_inline = 0;
- int attr_pure = 0;
- int attr_noreturn = 0;
- int attr_cold = 0;
- int attr_hot = 0;
- int attr_packed = 0;
- int attr_align = 0;
- int attr_noinline = 0;
- int attr_constructor = 0;
- int attr_destructor = 0;
- int attr_unused = 0;
- int attr_weak = 0;
- int attr_export = 0;
- int attr_comptime = 0;
- char *deprecated_msg = NULL;
- char *attr_section = NULL;
-
- char *derived_traits[32];
- int derived_count = 0;
-
- while (t.type == TOK_AT)
- {
- lexer_next(l);
- Token attr = lexer_next(l);
- if (attr.type != TOK_IDENT && attr.type != TOK_COMPTIME)
- {
- zpanic_at(attr, "Expected attribute name after @");
- }
+ ASTNode *s = 0;
+
+ int attr_must_use = 0;
+ int attr_deprecated = 0;
+ int attr_inline = 0;
+ int attr_pure = 0;
+ int attr_noreturn = 0;
+ int attr_cold = 0;
+ int attr_hot = 0;
+ int attr_packed = 0;
+ int attr_align = 0;
+ int attr_noinline = 0;
+ int attr_constructor = 0;
+ int attr_destructor = 0;
+ int attr_unused = 0;
+ int attr_weak = 0;
+ int attr_export = 0;
+ int attr_comptime = 0;
+ char *deprecated_msg = NULL;
+ char *attr_section = NULL;
+
+ char *derived_traits[32];
+ int derived_count = 0;
+
+ while (t.type == TOK_AT) {
+ lexer_next(l);
+ Token attr = lexer_next(l);
+ if (attr.type != TOK_IDENT && attr.type != TOK_COMPTIME) {
+ zpanic_at(attr, "Expected attribute name after @");
+ }
+
+ if (0 == strncmp(attr.start, "must_use", 8) && 8 == attr.len) {
+ attr_must_use = 1;
+ } else if (0 == strncmp(attr.start, "deprecated", 10) && 10 == attr.len) {
+ attr_deprecated = 1;
+ if (lexer_peek(l).type == TOK_LPAREN) {
+ lexer_next(l);
+ Token msg = lexer_next(l);
+ if (msg.type == TOK_STRING) {
+ deprecated_msg = xmalloc(msg.len - 1);
+ strncpy(deprecated_msg, msg.start + 1, msg.len - 2);
+ deprecated_msg[msg.len - 2] = 0;
+ }
+ if (lexer_next(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected ) after deprecated message");
+ }
+ }
+ } else if (0 == strncmp(attr.start, "inline", 6) && 6 == attr.len) {
+ attr_inline = 1;
+ } else if (0 == strncmp(attr.start, "noinline", 8) && 8 == attr.len) {
+ attr_noinline = 1;
+ } else if (0 == strncmp(attr.start, "pure", 4) && 4 == attr.len) {
+ attr_pure = 1;
+ } else if (0 == strncmp(attr.start, "noreturn", 8) && 8 == attr.len) {
+ attr_noreturn = 1;
+ } else if (0 == strncmp(attr.start, "cold", 4) && 4 == attr.len) {
+ attr_cold = 1;
+ } else if (0 == strncmp(attr.start, "hot", 3) && 3 == attr.len) {
+ attr_hot = 1;
+ } else if (0 == strncmp(attr.start, "constructor", 11) &&
+ 11 == attr.len) {
+ attr_constructor = 1;
+ } else if (0 == strncmp(attr.start, "destructor", 10) && 10 == attr.len) {
+ attr_destructor = 1;
+ } else if (0 == strncmp(attr.start, "unused", 6) && 6 == attr.len) {
+ attr_unused = 1;
+ } else if (0 == strncmp(attr.start, "weak", 4) && 4 == attr.len) {
+ attr_weak = 1;
+ } else if (0 == strncmp(attr.start, "export", 6) && 6 == attr.len) {
+ attr_export = 1;
+ } else if (0 == strncmp(attr.start, "comptime", 8) && 8 == attr.len) {
+ attr_comptime = 1;
+ } else if (0 == strncmp(attr.start, "section", 7) && 7 == attr.len) {
+ if (lexer_peek(l).type == TOK_LPAREN) {
+ lexer_next(l);
+ Token sec = lexer_next(l);
+ if (sec.type == TOK_STRING) {
+ attr_section = xmalloc(sec.len - 1);
+ strncpy(attr_section, sec.start + 1, sec.len - 2);
+ attr_section[sec.len - 2] = 0;
+ }
+ if (lexer_next(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected ) after section name");
+ }
+ } else {
+ zpanic_at(lexer_peek(l),
+ "@section requires a name: @section(\"name\")");
+ }
+ } else if (0 == strncmp(attr.start, "packed", 6) && 6 == attr.len) {
+ attr_packed = 1;
+ } else if (0 == strncmp(attr.start, "align", 5) && 5 == attr.len) {
+ if (lexer_peek(l).type == TOK_LPAREN) {
+ lexer_next(l);
+ Token num = lexer_next(l);
+ if (num.type == TOK_INT) {
+ attr_align = atoi(num.start);
+ }
+ if (lexer_next(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected ) after align value");
+ }
+ } else {
+ zpanic_at(lexer_peek(l), "@align requires a value: @align(N)");
+ }
+ } else if (0 == strncmp(attr.start, "derive", 6) && 6 == attr.len) {
+ if (lexer_peek(l).type == TOK_LPAREN) {
+ lexer_next(l);
+ while (1) {
+ Token t = lexer_next(l);
+ if (t.type != TOK_IDENT) {
+ zpanic_at(t, "Expected trait name in @derive");
+ }
+ if (derived_count < 32) {
+ derived_traits[derived_count++] = token_strdup(t);
+ }
+ if (lexer_peek(l).type == TOK_COMMA) {
+ lexer_next(l);
+ } else {
+ break;
+ }
+ }
+ if (lexer_next(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected ) after derive traits");
+ }
+ } else {
+ zpanic_at(lexer_peek(l),
+ "@derive requires traits: @derive(Debug, Clone)");
+ }
+ } else {
+ zwarn_at(attr, "Unknown attribute: %.*s", attr.len, attr.start);
+ }
- if (0 == strncmp(attr.start, "must_use", 8) && 8 == attr.len)
- {
- attr_must_use = 1;
- }
- else if (0 == strncmp(attr.start, "deprecated", 10) && 10 == attr.len)
- {
- attr_deprecated = 1;
- if (lexer_peek(l).type == TOK_LPAREN)
- {
- lexer_next(l);
- Token msg = lexer_next(l);
- if (msg.type == TOK_STRING)
- {
- deprecated_msg = xmalloc(msg.len - 1);
- strncpy(deprecated_msg, msg.start + 1, msg.len - 2);
- deprecated_msg[msg.len - 2] = 0;
- }
- if (lexer_next(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected ) after deprecated message");
- }
- }
- }
- else if (0 == strncmp(attr.start, "inline", 6) && 6 == attr.len)
- {
- attr_inline = 1;
- }
- else if (0 == strncmp(attr.start, "noinline", 8) && 8 == attr.len)
- {
- attr_noinline = 1;
- }
- else if (0 == strncmp(attr.start, "pure", 4) && 4 == attr.len)
- {
- attr_pure = 1;
- }
- else if (0 == strncmp(attr.start, "noreturn", 8) && 8 == attr.len)
- {
- attr_noreturn = 1;
- }
- else if (0 == strncmp(attr.start, "cold", 4) && 4 == attr.len)
- {
- attr_cold = 1;
- }
- else if (0 == strncmp(attr.start, "hot", 3) && 3 == attr.len)
- {
- attr_hot = 1;
- }
- else if (0 == strncmp(attr.start, "constructor", 11) && 11 == attr.len)
- {
- attr_constructor = 1;
- }
- else if (0 == strncmp(attr.start, "destructor", 10) && 10 == attr.len)
- {
- attr_destructor = 1;
- }
- else if (0 == strncmp(attr.start, "unused", 6) && 6 == attr.len)
- {
- attr_unused = 1;
- }
- else if (0 == strncmp(attr.start, "weak", 4) && 4 == attr.len)
- {
- attr_weak = 1;
- }
- else if (0 == strncmp(attr.start, "export", 6) && 6 == attr.len)
- {
- attr_export = 1;
- }
- else if (0 == strncmp(attr.start, "comptime", 8) && 8 == attr.len)
- {
- attr_comptime = 1;
- }
- else if (0 == strncmp(attr.start, "section", 7) && 7 == attr.len)
- {
- if (lexer_peek(l).type == TOK_LPAREN)
- {
- lexer_next(l);
- Token sec = lexer_next(l);
- if (sec.type == TOK_STRING)
- {
- attr_section = xmalloc(sec.len - 1);
- strncpy(attr_section, sec.start + 1, sec.len - 2);
- attr_section[sec.len - 2] = 0;
- }
- if (lexer_next(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected ) after section name");
- }
- }
- else
- {
- zpanic_at(lexer_peek(l), "@section requires a name: @section(\"name\")");
- }
- }
- else if (0 == strncmp(attr.start, "packed", 6) && 6 == attr.len)
- {
- attr_packed = 1;
- }
- else if (0 == strncmp(attr.start, "align", 5) && 5 == attr.len)
- {
- if (lexer_peek(l).type == TOK_LPAREN)
- {
- lexer_next(l);
- Token num = lexer_next(l);
- if (num.type == TOK_INT)
- {
- attr_align = atoi(num.start);
- }
- if (lexer_next(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected ) after align value");
- }
- }
- else
- {
- zpanic_at(lexer_peek(l), "@align requires a value: @align(N)");
- }
- }
- else if (0 == strncmp(attr.start, "derive", 6) && 6 == attr.len)
- {
- if (lexer_peek(l).type == TOK_LPAREN)
- {
- lexer_next(l);
- while (1)
- {
- Token t = lexer_next(l);
- if (t.type != TOK_IDENT)
- {
- zpanic_at(t, "Expected trait name in @derive");
- }
- if (derived_count < 32)
- {
- derived_traits[derived_count++] = token_strdup(t);
- }
- if (lexer_peek(l).type == TOK_COMMA)
- {
- lexer_next(l);
- }
- else
- {
- break;
- }
- }
- if (lexer_next(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected ) after derive traits");
- }
- }
- else
- {
- zpanic_at(lexer_peek(l), "@derive requires traits: @derive(Debug, Clone)");
- }
- }
- else
- {
- zwarn_at(attr, "Unknown attribute: %.*s", attr.len, attr.start);
- }
+ t = lexer_peek(l);
+ }
- t = lexer_peek(l);
+ if (t.type == TOK_PREPROC) {
+ lexer_next(l);
+ char *content = xmalloc(t.len + 2);
+ strncpy(content, t.start, t.len);
+ content[t.len] = '\n';
+ content[t.len + 1] = 0;
+ s = ast_create(NODE_RAW_STMT);
+ s->raw_stmt.content = content;
+ } else if (t.type == TOK_IDENT) {
+ // Inline function: inline fn name(...) { }
+ if (0 == strncmp(t.start, "inline", 6) && 6 == t.len) {
+ lexer_next(l);
+ Token next = lexer_peek(l);
+ if (next.type == TOK_IDENT && 2 == next.len &&
+ 0 == strncmp(next.start, "fn", 2)) {
+ s = parse_function(ctx, l, 0);
+ attr_inline = 1;
+ } else {
+ zpanic_at(next, "Expected 'fn' after 'inline'");
}
-
- if (t.type == TOK_PREPROC)
- {
- lexer_next(l);
- char *content = xmalloc(t.len + 2);
- strncpy(content, t.start, t.len);
- content[t.len] = '\n';
- content[t.len + 1] = 0;
- s = ast_create(NODE_RAW_STMT);
- s->raw_stmt.content = content;
+ } else if (0 == strncmp(t.start, "fn", 2) && 2 == t.len) {
+ s = parse_function(ctx, l, 0);
+ } else if (0 == strncmp(t.start, "struct", 6) && 6 == t.len) {
+ s = parse_struct(ctx, l, 0);
+ if (s && s->type == NODE_STRUCT) {
+ s->strct.is_packed = attr_packed;
+ s->strct.align = attr_align;
+
+ if (derived_count > 0) {
+ ASTNode *impls =
+ generate_derive_impls(ctx, s, derived_traits, derived_count);
+ s->next = impls;
+ }
}
- else if (t.type == TOK_IDENT)
- {
- // Inline function: inline fn name(...) { }
- if (0 == strncmp(t.start, "inline", 6) && 6 == t.len)
- {
- lexer_next(l);
- Token next = lexer_peek(l);
- if (next.type == TOK_IDENT && 2 == next.len && 0 == strncmp(next.start, "fn", 2))
- {
- s = parse_function(ctx, l, 0);
- attr_inline = 1;
- }
- else
- {
- zpanic_at(next, "Expected 'fn' after 'inline'");
- }
- }
- else if (0 == strncmp(t.start, "fn", 2) && 2 == t.len)
- {
- s = parse_function(ctx, l, 0);
- }
- else if (0 == strncmp(t.start, "struct", 6) && 6 == t.len)
- {
- s = parse_struct(ctx, l, 0);
- if (s && s->type == NODE_STRUCT)
- {
- s->strct.is_packed = attr_packed;
- s->strct.align = attr_align;
-
- if (derived_count > 0)
- {
- ASTNode *impls =
- generate_derive_impls(ctx, s, derived_traits, derived_count);
- s->next = impls;
- }
- }
- }
- else if (0 == strncmp(t.start, "enum", 4) && 4 == t.len)
- {
- s = parse_enum(ctx, l);
- if (s && s->type == NODE_ENUM)
- {
- if (derived_count > 0)
- {
- ASTNode *impls =
- generate_derive_impls(ctx, s, derived_traits, derived_count);
- s->next = impls;
- }
- }
- }
- else if (t.len == 4 && strncmp(t.start, "impl", 4) == 0)
- {
- s = parse_impl(ctx, l);
- }
- else if (t.len == 5 && strncmp(t.start, "trait", 5) == 0)
- {
- s = parse_trait(ctx, l);
- }
- else if (t.len == 7 && strncmp(t.start, "include", 7) == 0)
- {
- s = parse_include(ctx, l);
- }
- else if (t.len == 6 && strncmp(t.start, "import", 6) == 0)
- {
- s = parse_import(ctx, l);
- }
- else if (t.len == 3 && strncmp(t.start, "var", 3) == 0)
- {
- s = parse_var_decl(ctx, l);
- }
- else if (t.len == 5 && strncmp(t.start, "const", 5) == 0)
- {
- s = parse_const(ctx, l);
- }
- else if (t.len == 6 && strncmp(t.start, "extern", 6) == 0)
- {
- lexer_next(l);
-
- Token peek = lexer_peek(l);
- if (peek.type == TOK_IDENT && peek.len == 2 && strncmp(peek.start, "fn", 2) == 0)
- {
- s = parse_function(ctx, l, 0);
- }
- else
- {
- while (1)
- {
- Token sym = lexer_next(l);
- if (sym.type != TOK_IDENT)
- {
- break;
- }
-
- char *name = token_strdup(sym);
- register_extern_symbol(ctx, name);
-
- Token next = lexer_peek(l);
- if (next.type == TOK_COMMA)
- {
- lexer_next(l);
- }
- else
- {
- break;
- }
- }
-
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- }
- continue;
- }
- }
- else if (0 == strncmp(t.start, "type", 4) && 4 == t.len)
- {
- s = parse_type_alias(ctx, l);
- }
- else if (0 == strncmp(t.start, "raw", 3) && 3 == t.len)
- {
- lexer_next(l);
- if (lexer_peek(l).type != TOK_LBRACE)
- {
- zpanic_at(lexer_peek(l), "Expected { after raw");
- }
- lexer_next(l);
-
- const char *start = l->src + l->pos;
-
- int depth = 1;
- while (depth > 0)
- {
- Token t = lexer_next(l);
- if (t.type == TOK_EOF)
- {
- zpanic_at(t, "Unexpected EOF in raw block");
- }
- if (t.type == TOK_LBRACE)
- {
- depth++;
- }
- if (t.type == TOK_RBRACE)
- {
- depth--;
- }
- }
-
- const char *end = l->src + l->pos - 1;
- size_t len = end - start;
-
- char *content = xmalloc(len + 1);
- memcpy(content, start, len);
- content[len] = 0;
-
- s = ast_create(NODE_RAW_STMT);
- s->raw_stmt.content = content;
- }
- else
- {
- lexer_next(l);
- }
+ } else if (0 == strncmp(t.start, "enum", 4) && 4 == t.len) {
+ s = parse_enum(ctx, l);
+ if (s && s->type == NODE_ENUM) {
+ if (derived_count > 0) {
+ ASTNode *impls =
+ generate_derive_impls(ctx, s, derived_traits, derived_count);
+ s->next = impls;
+ }
}
- else if (t.type == TOK_ASYNC)
- {
- lexer_next(l);
+ } else if (t.len == 4 && strncmp(t.start, "impl", 4) == 0) {
+ s = parse_impl(ctx, l);
+ } else if (t.len == 5 && strncmp(t.start, "trait", 5) == 0) {
+ s = parse_trait(ctx, l);
+ } else if (t.len == 7 && strncmp(t.start, "include", 7) == 0) {
+ s = parse_include(ctx, l);
+ } else if (t.len == 6 && strncmp(t.start, "import", 6) == 0) {
+ s = parse_import(ctx, l);
+ } else if (t.len == 3 && strncmp(t.start, "var", 3) == 0) {
+ s = parse_var_decl(ctx, l);
+ } else if (t.len == 5 && strncmp(t.start, "const", 5) == 0) {
+ s = parse_const(ctx, l);
+ } else if (t.len == 6 && strncmp(t.start, "extern", 6) == 0) {
+ lexer_next(l);
+
+ Token peek = lexer_peek(l);
+ if (peek.type == TOK_IDENT && peek.len == 2 &&
+ strncmp(peek.start, "fn", 2) == 0) {
+ s = parse_function(ctx, l, 0);
+ } else {
+ while (1) {
+ Token sym = lexer_next(l);
+ if (sym.type != TOK_IDENT) {
+ break;
+ }
+
+ char *name = token_strdup(sym);
+ register_extern_symbol(ctx, name);
+
Token next = lexer_peek(l);
- if (0 == strncmp(next.start, "fn", 2) && 2 == next.len)
- {
- s = parse_function(ctx, l, 1);
- if (s)
- {
- s->func.is_async = 1;
- }
+ if (next.type == TOK_COMMA) {
+ lexer_next(l);
+ } else {
+ break;
}
- else
- {
- zpanic_at(next, "Expected 'fn' after 'async'");
- }
- }
+ }
- else if (t.type == TOK_UNION)
- {
- s = parse_struct(ctx, l, 1);
- }
- else if (t.type == TOK_TRAIT)
- {
- s = parse_trait(ctx, l);
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
+ }
+ continue;
}
- else if (t.type == TOK_IMPL)
- {
- s = parse_impl(ctx, l);
+ } else if (0 == strncmp(t.start, "type", 4) && 4 == t.len) {
+ s = parse_type_alias(ctx, l);
+ } else if (0 == strncmp(t.start, "raw", 3) && 3 == t.len) {
+ lexer_next(l);
+ if (lexer_peek(l).type != TOK_LBRACE) {
+ zpanic_at(lexer_peek(l), "Expected { after raw");
}
- else if (t.type == TOK_TEST)
- {
- s = parse_test(ctx, l);
+ lexer_next(l);
+
+ const char *start = l->src + l->pos;
+
+ int depth = 1;
+ while (depth > 0) {
+ Token t = lexer_next(l);
+ if (t.type == TOK_EOF) {
+ zpanic_at(t, "Unexpected EOF in raw block");
+ }
+ if (t.type == TOK_LBRACE) {
+ depth++;
+ }
+ if (t.type == TOK_RBRACE) {
+ depth--;
+ }
}
- else
- {
- lexer_next(l);
+
+ const char *end = l->src + l->pos - 1;
+ size_t len = end - start;
+
+ char *content = xmalloc(len + 1);
+ memcpy(content, start, len);
+ content[len] = 0;
+
+ s = ast_create(NODE_RAW_STMT);
+ s->raw_stmt.content = content;
+ } else {
+ lexer_next(l);
+ }
+ } else if (t.type == TOK_ASYNC) {
+ lexer_next(l);
+ Token next = lexer_peek(l);
+ if (0 == strncmp(next.start, "fn", 2) && 2 == next.len) {
+ s = parse_function(ctx, l, 1);
+ if (s) {
+ s->func.is_async = 1;
}
+ } else {
+ zpanic_at(next, "Expected 'fn' after 'async'");
+ }
+ }
- if (s && s->type == NODE_FUNCTION)
- {
- s->func.must_use = attr_must_use;
- s->func.is_inline = attr_inline || s->func.is_inline;
- s->func.noinline = attr_noinline;
- s->func.constructor = attr_constructor;
- s->func.destructor = attr_destructor;
- s->func.unused = attr_unused;
- s->func.weak = attr_weak;
- s->func.is_export = attr_export;
- s->func.cold = attr_cold;
- s->func.hot = attr_hot;
- s->func.noreturn = attr_noreturn;
- s->func.pure = attr_pure;
- s->func.section = attr_section;
- s->func.is_comptime = attr_comptime;
-
- if (attr_deprecated && s->func.name)
- {
- register_deprecated_func(ctx, s->func.name, deprecated_msg);
- }
+ else if (t.type == TOK_UNION) {
+ s = parse_struct(ctx, l, 1);
+ } else if (t.type == TOK_TRAIT) {
+ s = parse_trait(ctx, l);
+ } else if (t.type == TOK_IMPL) {
+ s = parse_impl(ctx, l);
+ } else if (t.type == TOK_TEST) {
+ s = parse_test(ctx, l);
+ } else {
+ lexer_next(l);
+ }
- if (attr_must_use && s->func.name)
- {
- FuncSig *sig = find_func(ctx, s->func.name);
- if (sig)
- {
- sig->must_use = 1;
- }
- }
+ if (s && s->type == NODE_FUNCTION) {
+ s->func.must_use = attr_must_use;
+ s->func.is_inline = attr_inline || s->func.is_inline;
+ s->func.noinline = attr_noinline;
+ s->func.constructor = attr_constructor;
+ s->func.destructor = attr_destructor;
+ s->func.unused = attr_unused;
+ s->func.weak = attr_weak;
+ s->func.is_export = attr_export;
+ s->func.cold = attr_cold;
+ s->func.hot = attr_hot;
+ s->func.noreturn = attr_noreturn;
+ s->func.pure = attr_pure;
+ s->func.section = attr_section;
+ s->func.is_comptime = attr_comptime;
+
+ if (attr_deprecated && s->func.name) {
+ register_deprecated_func(ctx, s->func.name, deprecated_msg);
+ }
+
+ if (attr_must_use && s->func.name) {
+ FuncSig *sig = find_func(ctx, s->func.name);
+ if (sig) {
+ sig->must_use = 1;
}
+ }
+ }
- if (s)
- {
- if (!h)
- {
- h = s;
- }
- else
- {
- tl->next = s;
- }
- tl = s;
- while (tl->next)
- {
- tl = tl->next;
- }
- }
+ if (s) {
+ if (!h) {
+ h = s;
+ } else {
+ tl->next = s;
+ }
+ tl = s;
+ while (tl->next) {
+ tl = tl->next;
+ }
}
- return h;
+ }
+ return h;
}
-ASTNode *parse_program(ParserContext *ctx, Lexer *l)
-{
- g_parser_ctx = ctx;
- enter_scope(ctx);
- register_builtins(ctx);
+ASTNode *parse_program(ParserContext *ctx, Lexer *l) {
+ g_parser_ctx = ctx;
+ enter_scope(ctx);
+ register_builtins(ctx);
- ASTNode *r = ast_create(NODE_ROOT);
- r->root.children = parse_program_nodes(ctx, l);
- return r;
+ ASTNode *r = ast_create(NODE_ROOT);
+ r->root.children = parse_program_nodes(ctx, l);
+ return r;
}
-static ASTNode *generate_derive_impls(ParserContext *ctx, ASTNode *strct, char **traits, int count)
-{
- ASTNode *head = NULL, *tail = NULL;
- char *name = strct->strct.name;
-
- for (int i = 0; i < count; i++)
- {
- char *trait = traits[i];
- char *code = NULL;
-
- if (0 == strcmp(trait, "Clone"))
- {
- code = xmalloc(1024);
- sprintf(code, "impl %s { fn clone(self) -> %s { return *self; } }", name, name);
+static ASTNode *generate_derive_impls(ParserContext *ctx, ASTNode *strct,
+ char **traits, int count) {
+ ASTNode *head = NULL, *tail = NULL;
+ char *name = strct->strct.name;
+
+ for (int i = 0; i < count; i++) {
+ char *trait = traits[i];
+ char *code = NULL;
+
+ if (0 == strcmp(trait, "Clone")) {
+ code = xmalloc(1024);
+ sprintf(code, "impl %s { fn clone(self) -> %s { return *self; } }", name,
+ name);
+ } else if (0 == strcmp(trait, "Eq")) {
+ char body[4096];
+ body[0] = 0;
+
+ if (strct->type == NODE_ENUM) {
+ // Simple Enum equality (tag comparison)
+ // Generate Eq impl for Enum
+
+ sprintf(body, "return self.tag == other.tag;");
+ } else {
+ ASTNode *f = strct->strct.fields;
+ int first = 1;
+ strcat(body, "return ");
+ while (f) {
+ if (f->type == NODE_FIELD) {
+ char *fn = f->field.name;
+ char *ft = f->field.type;
+ if (!first) {
+ strcat(body, " && ");
+ }
+ char cmp[256];
+
+ ASTNode *fdef = find_struct_def(ctx, ft);
+ if (fdef && fdef->type == NODE_ENUM) {
+ // Enum field: compare tags
+ sprintf(cmp, "self.%s.tag == other.%s.tag", fn, fn);
+ } else if (fdef && fdef->type == NODE_STRUCT) {
+ // Struct field: use _eq function
+ sprintf(cmp, "%s_eq(&self.%s, other.%s)", ft, fn, fn);
+ } else {
+ // Primitive or unknown: use ==
+ sprintf(cmp, "self.%s == other.%s", fn, fn);
+ }
+ strcat(body, cmp);
+ first = 0;
+ }
+ f = f->next;
}
- else if (0 == strcmp(trait, "Eq"))
- {
- char body[4096];
- body[0] = 0;
-
- if (strct->type == NODE_ENUM)
- {
- // Simple Enum equality (tag comparison)
- // Generate Eq impl for Enum
-
- sprintf(body, "return self.tag == other.tag;");
- }
- else
- {
- ASTNode *f = strct->strct.fields;
- int first = 1;
- strcat(body, "return ");
- while (f)
- {
- if (f->type == NODE_FIELD)
- {
- char *fn = f->field.name;
- char *ft = f->field.type;
- if (!first)
- {
- strcat(body, " && ");
- }
- char cmp[256];
-
- ASTNode *fdef = find_struct_def(ctx, ft);
- if (fdef && fdef->type == NODE_ENUM)
- {
- // Enum field: compare tags
- sprintf(cmp, "self.%s.tag == other.%s.tag", fn, fn);
- }
- else if (fdef && fdef->type == NODE_STRUCT)
- {
- // Struct field: use _eq function
- sprintf(cmp, "%s_eq(&self.%s, other.%s)", ft, fn, fn);
- }
- else
- {
- // Primitive or unknown: use ==
- sprintf(cmp, "self.%s == other.%s", fn, fn);
- }
- strcat(body, cmp);
- first = 0;
- }
- f = f->next;
- }
- if (first)
- {
- strcat(body, "true");
- }
- strcat(body, ";");
- }
- code = xmalloc(4096 + 1024);
- sprintf(code, "impl %s { fn eq(self, other: %s) -> bool { %s } }", name, name, body);
- }
- else if (0 == strcmp(trait, "Debug"))
- {
- // Simplistic Debug for now, I know.
- code = xmalloc(1024);
- sprintf(code, "impl %s { fn to_string(self) -> char* { return \"%s { ... }\"; } }",
- name, name);
+ if (first) {
+ strcat(body, "true");
}
+ strcat(body, ";");
+ }
+ code = xmalloc(4096 + 1024);
+ sprintf(code, "impl %s { fn eq(self, other: %s) -> bool { %s } }", name,
+ name, body);
+ } else if (0 == strcmp(trait, "Debug")) {
+ // Simplistic Debug for now, I know.
+ code = xmalloc(1024);
+ sprintf(
+ code,
+ "impl %s { fn to_string(self) -> char* { return \"%s { ... }\"; } }",
+ name, name);
+ }
- if (code)
- {
- Lexer tmp;
- lexer_init(&tmp, code);
- ASTNode *impl = parse_impl(ctx, &tmp);
- if (impl)
- {
- if (!head)
- {
- head = impl;
- }
- else
- {
- tail->next = impl;
- }
- tail = impl;
- }
+ if (code) {
+ Lexer tmp;
+ lexer_init(&tmp, code);
+ ASTNode *impl = parse_impl(ctx, &tmp);
+ if (impl) {
+ if (!head) {
+ head = impl;
+ } else {
+ tail->next = impl;
}
+ tail = impl;
+ }
}
- return head;
+ }
+ return head;
}
diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c
index 40d6ae0..4c360c0 100644
--- a/src/parser/parser_expr.c
+++ b/src/parser/parser_expr.c
@@ -6,3675 +6,3080 @@
#include <stdlib.h>
#include <string.h>
-Type *get_field_type(ParserContext *ctx, Type *struct_type, const char *field_name);
+Type *get_field_type(ParserContext *ctx, Type *struct_type,
+ const char *field_name);
-static int type_is_unsigned(Type *t)
-{
- if (!t)
- {
- return 0;
- }
-
- return (t->kind == TYPE_U8 || t->kind == TYPE_U16 || t->kind == TYPE_U32 ||
- t->kind == TYPE_U64 || t->kind == TYPE_USIZE || t->kind == TYPE_BYTE ||
- t->kind == TYPE_U128 || t->kind == TYPE_UINT ||
- (t->kind == TYPE_STRUCT && t->name &&
- (0 == strcmp(t->name, "uint8_t") || 0 == strcmp(t->name, "uint16_t") ||
- 0 == strcmp(t->name, "uint32_t") || 0 == strcmp(t->name, "uint64_t") ||
- 0 == strcmp(t->name, "size_t"))));
+static int type_is_unsigned(Type *t) {
+ if (!t) {
+ return 0;
+ }
+
+ return (
+ t->kind == TYPE_U8 || t->kind == TYPE_U16 || t->kind == TYPE_U32 ||
+ t->kind == TYPE_U64 || t->kind == TYPE_USIZE || t->kind == TYPE_BYTE ||
+ t->kind == TYPE_U128 || t->kind == TYPE_UINT ||
+ (t->kind == TYPE_STRUCT && t->name &&
+ (0 == strcmp(t->name, "uint8_t") || 0 == strcmp(t->name, "uint16_t") ||
+ 0 == strcmp(t->name, "uint32_t") || 0 == strcmp(t->name, "uint64_t") ||
+ 0 == strcmp(t->name, "size_t"))));
}
-static void check_format_string(ASTNode *call, Token t)
-{
- if (call->type != NODE_EXPR_CALL)
- {
- return;
- }
- ASTNode *callee = call->call.callee;
- if (callee->type != NODE_EXPR_VAR)
- {
- return;
- }
+static void check_format_string(ASTNode *call, Token t) {
+ if (call->type != NODE_EXPR_CALL) {
+ return;
+ }
+ ASTNode *callee = call->call.callee;
+ if (callee->type != NODE_EXPR_VAR) {
+ return;
+ }
+
+ char *fname = callee->var_ref.name;
+ if (!fname) {
+ return;
+ }
+
+ if (strcmp(fname, "printf") != 0 && strcmp(fname, "sprintf") != 0 &&
+ strcmp(fname, "fprintf") != 0 && strcmp(fname, "dprintf") != 0) {
+ return;
+ }
+
+ int fmt_idx = 0;
+ if (strcmp(fname, "fprintf") == 0 || strcmp(fname, "sprintf") == 0 ||
+ strcmp(fname, "dprintf") == 0) {
+ fmt_idx = 1;
+ }
+
+ ASTNode *args = call->call.args;
+ ASTNode *fmt_arg = args;
+ for (int i = 0; i < fmt_idx; i++) {
+ if (!fmt_arg) {
+ return;
+ }
+ fmt_arg = fmt_arg->next;
+ }
+ if (!fmt_arg) {
+ return;
+ }
+
+ if (fmt_arg->type != NODE_EXPR_LITERAL ||
+ fmt_arg->literal.type_kind != TOK_STRING) {
+ return;
+ }
+
+ const char *fmt = fmt_arg->literal.string_val;
+
+ ASTNode *curr_arg = fmt_arg->next;
+ int arg_num = fmt_idx + 2;
+
+ for (int i = 0; fmt[i]; i++) {
+ if (fmt[i] == '%') {
+ i++;
+ if (fmt[i] == 0) {
+ break;
+ }
+ if (fmt[i] == '%') {
+ continue;
+ }
+
+ // Flags.
+ while (fmt[i] == '-' || fmt[i] == '+' || fmt[i] == ' ' || fmt[i] == '#' ||
+ fmt[i] == '0') {
+ i++;
+ }
+
+ // Width.
+ while (isdigit(fmt[i])) {
+ i++;
+ }
+
+ if (fmt[i] == '*') {
+ i++;
+ if (!curr_arg) {
+ warn_format_string(t, arg_num, "width(int)", "missing");
+ } else {
+ /* check int */
+ curr_arg = curr_arg->next;
+ arg_num++;
+ }
+ }
+
+ // Precision.
+ if (fmt[i] == '.') {
+ i++;
+ while (isdigit(fmt[i])) {
+ i++;
+ }
+
+ if (fmt[i] == '*') {
+ i++;
+ if (!curr_arg) {
+ warn_format_string(t, arg_num, "precision(int)", "missing");
+ } else {
+ /* check int */
+ curr_arg = curr_arg->next;
+ arg_num++;
+ }
+ }
+ }
- char *fname = callee->var_ref.name;
- if (!fname)
- {
- return;
- }
+ // Length.
+ if (fmt[i] == 'h' || fmt[i] == 'l' || fmt[i] == 'L' || fmt[i] == 'z' ||
+ fmt[i] == 'j' || fmt[i] == 't') {
+ if (fmt[i] == 'h' && fmt[i + 1] == 'h') {
+ i++;
+ } else if (fmt[i] == 'l' && fmt[i + 1] == 'l') {
+ i++;
+ }
+ i++;
+ }
- if (strcmp(fname, "printf") != 0 && strcmp(fname, "sprintf") != 0 &&
- strcmp(fname, "fprintf") != 0 && strcmp(fname, "dprintf") != 0)
- {
- return;
- }
+ char spec = fmt[i];
- int fmt_idx = 0;
- if (strcmp(fname, "fprintf") == 0 || strcmp(fname, "sprintf") == 0 ||
- strcmp(fname, "dprintf") == 0)
- {
- fmt_idx = 1;
- }
+ if (!curr_arg) {
+ warn_format_string(t, arg_num, "argument", "missing");
+ continue;
+ }
- ASTNode *args = call->call.args;
- ASTNode *fmt_arg = args;
- for (int i = 0; i < fmt_idx; i++)
- {
- if (!fmt_arg)
- {
- return;
+ Type *vt = curr_arg->type_info;
+ char *got_type = vt ? type_to_string(vt) : "?";
+
+ if (spec == 'd' || spec == 'i' || spec == 'u' || spec == 'x' ||
+ spec == 'X' || spec == 'o') {
+ if (vt && vt->kind != TYPE_INT && vt->kind != TYPE_I64 &&
+ !type_is_unsigned(vt) && vt->kind != TYPE_CHAR) {
+ warn_format_string(t, arg_num, "integer", got_type);
}
- fmt_arg = fmt_arg->next;
- }
- if (!fmt_arg)
- {
- return;
- }
+ } else if (spec == 's') {
+ if (vt && vt->kind != TYPE_STRING && vt->kind != TYPE_POINTER &&
+ vt->kind != TYPE_ARRAY) {
+ warn_format_string(t, arg_num, "string", got_type);
+ }
+ } else if (spec == 'f' || spec == 'F' || spec == 'e' || spec == 'E' ||
+ spec == 'g' || spec == 'G') {
+ if (vt && vt->kind != TYPE_FLOAT && vt->kind != TYPE_F64) {
+ warn_format_string(t, arg_num, "float", got_type);
+ }
+ } else if (spec == 'p') {
+ if (vt && vt->kind != TYPE_POINTER && vt->kind != TYPE_ARRAY) {
+ warn_format_string(t, arg_num, "pointer", got_type);
+ }
+ }
- if (fmt_arg->type != NODE_EXPR_LITERAL || fmt_arg->literal.type_kind != TOK_STRING)
- {
- return;
+ curr_arg = curr_arg->next;
+ arg_num++;
}
+ }
+}
- const char *fmt = fmt_arg->literal.string_val;
+ASTNode *parse_expression(ParserContext *ctx, Lexer *l) {
+ return parse_expr_prec(ctx, l, PREC_NONE);
+}
- ASTNode *curr_arg = fmt_arg->next;
- int arg_num = fmt_idx + 2;
+Precedence get_token_precedence(Token t) {
+ if (t.type == TOK_INT || t.type == TOK_FLOAT || t.type == TOK_STRING ||
+ t.type == TOK_IDENT || t.type == TOK_FSTRING) {
+ return PREC_NONE;
+ }
- for (int i = 0; fmt[i]; i++)
- {
- if (fmt[i] == '%')
- {
- i++;
- if (fmt[i] == 0)
- {
- break;
- }
- if (fmt[i] == '%')
- {
- continue;
- }
+ if (t.type == TOK_QUESTION) {
+ return PREC_CALL;
+ }
- // Flags.
- while (fmt[i] == '-' || fmt[i] == '+' || fmt[i] == ' ' || fmt[i] == '#' ||
- fmt[i] == '0')
- {
- i++;
- }
+ if (t.type == TOK_ARROW && t.start[0] == '-') {
+ return PREC_CALL;
+ }
- // Width.
- while (isdigit(fmt[i]))
- {
- i++;
- }
+ if (t.type == TOK_Q_DOT) {
+ return PREC_CALL;
+ }
- if (fmt[i] == '*')
- {
- i++;
- if (!curr_arg)
- {
- warn_format_string(t, arg_num, "width(int)", "missing");
- }
- else
- {
- /* check int */
- curr_arg = curr_arg->next;
- arg_num++;
- }
- }
+ if (t.type == TOK_QQ) {
+ return PREC_OR;
+ }
- // Precision.
- if (fmt[i] == '.')
- {
- i++;
- while (isdigit(fmt[i]))
- {
- i++;
- }
-
- if (fmt[i] == '*')
- {
- i++;
- if (!curr_arg)
- {
- warn_format_string(t, arg_num, "precision(int)", "missing");
- }
- else
- {
- /* check int */
- curr_arg = curr_arg->next;
- arg_num++;
- }
- }
- }
+ if (t.type == TOK_OR) {
+ return PREC_OR;
+ }
- // Length.
- if (fmt[i] == 'h' || fmt[i] == 'l' || fmt[i] == 'L' || fmt[i] == 'z' || fmt[i] == 'j' ||
- fmt[i] == 't')
- {
- if (fmt[i] == 'h' && fmt[i + 1] == 'h')
- {
- i++;
- }
- else if (fmt[i] == 'l' && fmt[i + 1] == 'l')
- {
- i++;
- }
- i++;
- }
+ if (t.type == TOK_AND) {
+ return PREC_AND;
+ }
- char spec = fmt[i];
+ if (t.type == TOK_QQ_EQ) {
+ return PREC_ASSIGNMENT;
+ }
- if (!curr_arg)
- {
- warn_format_string(t, arg_num, "argument", "missing");
- continue;
- }
+ if (t.type == TOK_PIPE) {
+ return PREC_TERM;
+ }
- Type *vt = curr_arg->type_info;
- char *got_type = vt ? type_to_string(vt) : "?";
-
- if (spec == 'd' || spec == 'i' || spec == 'u' || spec == 'x' || spec == 'X' ||
- spec == 'o')
- {
- if (vt && vt->kind != TYPE_INT && vt->kind != TYPE_I64 && !type_is_unsigned(vt) &&
- vt->kind != TYPE_CHAR)
- {
- warn_format_string(t, arg_num, "integer", got_type);
- }
- }
- else if (spec == 's')
- {
- if (vt && vt->kind != TYPE_STRING && vt->kind != TYPE_POINTER &&
- vt->kind != TYPE_ARRAY)
- {
- warn_format_string(t, arg_num, "string", got_type);
- }
- }
- else if (spec == 'f' || spec == 'F' || spec == 'e' || spec == 'E' || spec == 'g' ||
- spec == 'G')
- {
- if (vt && vt->kind != TYPE_FLOAT && vt->kind != TYPE_F64)
- {
- warn_format_string(t, arg_num, "float", got_type);
- }
- }
- else if (spec == 'p')
- {
- if (vt && vt->kind != TYPE_POINTER && vt->kind != TYPE_ARRAY)
- {
- warn_format_string(t, arg_num, "pointer", got_type);
- }
- }
+ if (t.type == TOK_LANGLE || t.type == TOK_RANGLE) {
+ return PREC_COMPARISON;
+ }
- curr_arg = curr_arg->next;
- arg_num++;
- }
+ if (t.type == TOK_OP) {
+ if (is_token(t, "=") || is_token(t, "+=") || is_token(t, "-=") ||
+ is_token(t, "*=") || is_token(t, "/=") || is_token(t, "%=") ||
+ is_token(t, "|=") || is_token(t, "&=") || is_token(t, "^=") ||
+ is_token(t, "<<=") || is_token(t, ">>=")) {
+ return PREC_ASSIGNMENT;
}
-}
-ASTNode *parse_expression(ParserContext *ctx, Lexer *l)
-{
- return parse_expr_prec(ctx, l, PREC_NONE);
-}
-
-Precedence get_token_precedence(Token t)
-{
- if (t.type == TOK_INT || t.type == TOK_FLOAT || t.type == TOK_STRING || t.type == TOK_IDENT ||
- t.type == TOK_FSTRING)
- {
- return PREC_NONE;
+ if (is_token(t, "||") || is_token(t, "or")) {
+ return PREC_OR;
}
- if (t.type == TOK_QUESTION)
- {
- return PREC_CALL;
+ if (is_token(t, "&&") || is_token(t, "and")) {
+ return PREC_AND;
}
- if (t.type == TOK_ARROW && t.start[0] == '-')
- {
- return PREC_CALL;
+ if (is_token(t, "|")) {
+ return PREC_TERM;
}
- if (t.type == TOK_Q_DOT)
- {
- return PREC_CALL;
+ if (is_token(t, "^")) {
+ return PREC_TERM;
}
- if (t.type == TOK_QQ)
- {
- return PREC_OR;
+ if (is_token(t, "&")) {
+ return PREC_TERM;
}
- if (t.type == TOK_OR)
- {
- return PREC_OR;
+ if (is_token(t, "<<") || is_token(t, ">>")) {
+ return PREC_TERM;
}
- if (t.type == TOK_AND)
- {
- return PREC_AND;
+ if (is_token(t, "==") || is_token(t, "!=")) {
+ return PREC_EQUALITY;
}
- if (t.type == TOK_QQ_EQ)
- {
- return PREC_ASSIGNMENT;
+ if (is_token(t, "<") || is_token(t, ">") || is_token(t, "<=") ||
+ is_token(t, ">=")) {
+ return PREC_COMPARISON;
}
- if (t.type == TOK_PIPE)
- {
- return PREC_TERM;
+ if (is_token(t, "+") || is_token(t, "-")) {
+ return PREC_TERM;
}
- if (t.type == TOK_LANGLE || t.type == TOK_RANGLE)
- {
- return PREC_COMPARISON;
+ if (is_token(t, "*") || is_token(t, "/") || is_token(t, "%")) {
+ return PREC_FACTOR;
}
- if (t.type == TOK_OP)
- {
- if (is_token(t, "=") || is_token(t, "+=") || is_token(t, "-=") || is_token(t, "*=") ||
- is_token(t, "/=") || is_token(t, "%=") || is_token(t, "|=") || is_token(t, "&=") ||
- is_token(t, "^=") || is_token(t, "<<=") || is_token(t, ">>="))
- {
- return PREC_ASSIGNMENT;
- }
-
- if (is_token(t, "||") || is_token(t, "or"))
- {
- return PREC_OR;
- }
+ if (is_token(t, ".")) {
+ return PREC_CALL;
+ }
- if (is_token(t, "&&") || is_token(t, "and"))
- {
- return PREC_AND;
- }
+ if (is_token(t, "|>")) {
+ return PREC_TERM;
+ }
+ }
- if (is_token(t, "|"))
- {
- return PREC_TERM;
- }
+ if (t.type == TOK_LBRACKET || t.type == TOK_LPAREN) {
+ return PREC_CALL;
+ }
- if (is_token(t, "^"))
- {
- return PREC_TERM;
- }
+ if (is_token(t, "??")) {
+ return PREC_OR;
+ }
- if (is_token(t, "&"))
- {
- return PREC_TERM;
- }
+ if (is_token(t, "\?\?=")) {
+ return PREC_ASSIGNMENT;
+ }
- if (is_token(t, "<<") || is_token(t, ">>"))
- {
- return PREC_TERM;
- }
+ return PREC_NONE;
+}
- if (is_token(t, "==") || is_token(t, "!="))
- {
- return PREC_EQUALITY;
- }
+// Helper to check if a variable name is in a list.
+static int is_in_list(const char *name, char **list, int count) {
+ for (int i = 0; i < count; i++) {
+ if (0 == strcmp(name, list[i])) {
+ return 1;
+ }
+ }
+ return 0;
+}
- if (is_token(t, "<") || is_token(t, ">") || is_token(t, "<=") || is_token(t, ">="))
- {
- return PREC_COMPARISON;
- }
+// Recursively find all variable references in an expression/statement.
+static void find_var_refs(ASTNode *node, char ***refs, int *ref_count) {
+ if (!node) {
+ return;
+ }
+
+ if (node->type == NODE_EXPR_VAR) {
+ *refs = xrealloc(*refs, sizeof(char *) * (*ref_count + 1));
+ (*refs)[*ref_count] = xstrdup(node->var_ref.name);
+ (*ref_count)++;
+ }
+
+ switch (node->type) {
+ case NODE_EXPR_BINARY:
+ find_var_refs(node->binary.left, refs, ref_count);
+ find_var_refs(node->binary.right, refs, ref_count);
+ break;
+ case NODE_EXPR_UNARY:
+ find_var_refs(node->unary.operand, refs, ref_count);
+ break;
+ case NODE_EXPR_CALL:
+ find_var_refs(node->call.callee, refs, ref_count);
+ for (ASTNode *arg = node->call.args; arg; arg = arg->next) {
+ find_var_refs(arg, refs, ref_count);
+ }
+ break;
+ case NODE_EXPR_MEMBER:
+ find_var_refs(node->member.target, refs, ref_count);
+ break;
+ case NODE_EXPR_INDEX:
+ find_var_refs(node->index.array, refs, ref_count);
+ find_var_refs(node->index.index, refs, ref_count);
+ break;
+ case NODE_EXPR_SLICE:
+ find_var_refs(node->slice.array, refs, ref_count);
+ find_var_refs(node->slice.start, refs, ref_count);
+ find_var_refs(node->slice.end, refs, ref_count);
+ break;
+ case NODE_BLOCK:
+ for (ASTNode *stmt = node->block.statements; stmt; stmt = stmt->next) {
+ find_var_refs(stmt, refs, ref_count);
+ }
+ break;
+ case NODE_RETURN:
+ find_var_refs(node->ret.value, refs, ref_count);
+ break;
+ case NODE_VAR_DECL:
+ case NODE_CONST:
+ find_var_refs(node->var_decl.init_expr, refs, ref_count);
+ break;
+ case NODE_IF:
+ find_var_refs(node->if_stmt.condition, refs, ref_count);
+ find_var_refs(node->if_stmt.then_body, refs, ref_count);
+ if (node->if_stmt.else_body) {
+ find_var_refs(node->if_stmt.else_body, refs, ref_count);
+ }
+ break;
+ case NODE_WHILE:
+ find_var_refs(node->while_stmt.condition, refs, ref_count);
+ find_var_refs(node->while_stmt.body, refs, ref_count);
+ break;
+ case NODE_FOR:
+ find_var_refs(node->for_stmt.init, refs, ref_count);
+ find_var_refs(node->for_stmt.condition, refs, ref_count);
+ find_var_refs(node->for_stmt.step, refs, ref_count);
+ find_var_refs(node->for_stmt.body, refs, ref_count);
+ break;
+ case NODE_MATCH:
+ find_var_refs(node->match_stmt.expr, refs, ref_count);
+ for (ASTNode *c = node->match_stmt.cases; c; c = c->next) {
+ find_var_refs(c->match_case.body, refs, ref_count);
+ }
+ break;
+ default:
+ break;
+ }
+}
- if (is_token(t, "+") || is_token(t, "-"))
- {
- return PREC_TERM;
- }
+// Helper to find variable declarations in a subtree
+static void find_declared_vars(ASTNode *node, char ***decls, int *count) {
+ if (!node) {
+ return;
+ }
+
+ if (node->type == NODE_VAR_DECL) {
+ *decls = xrealloc(*decls, sizeof(char *) * (*count + 1));
+ (*decls)[*count] = xstrdup(node->var_decl.name);
+ (*count)++;
+ }
+
+ if (node->type == NODE_MATCH_CASE && node->match_case.binding_name) {
+ *decls = xrealloc(*decls, sizeof(char *) * (*count + 1));
+ (*decls)[*count] = xstrdup(node->match_case.binding_name);
+ (*count)++;
+ }
+
+ switch (node->type) {
+ case NODE_BLOCK:
+ for (ASTNode *stmt = node->block.statements; stmt; stmt = stmt->next) {
+ find_declared_vars(stmt, decls, count);
+ }
+ break;
+ case NODE_IF:
+ find_declared_vars(node->if_stmt.then_body, decls, count);
+ find_declared_vars(node->if_stmt.else_body, decls, count);
+ break;
+ case NODE_WHILE:
+ find_declared_vars(node->while_stmt.body, decls, count);
+ break;
+ case NODE_FOR:
+ find_declared_vars(node->for_stmt.init, decls, count);
+ find_declared_vars(node->for_stmt.body, decls, count);
+ break;
+ case NODE_MATCH:
+ for (ASTNode *c = node->match_stmt.cases; c; c = c->next) {
+ find_declared_vars(c, decls, count);
+ find_declared_vars(c->match_case.body, decls, count);
+ }
+ break;
+ default:
+ break;
+ }
+}
- if (is_token(t, "*") || is_token(t, "/") || is_token(t, "%"))
- {
- return PREC_FACTOR;
- }
+// Analyze lambda body to find captured variables.
+void analyze_lambda_captures(ParserContext *ctx, ASTNode *lambda) {
+ if (!lambda || lambda->type != NODE_LAMBDA) {
+ return;
+ }
- if (is_token(t, "."))
- {
- return PREC_CALL;
- }
+ char **all_refs = NULL;
+ int num_refs = 0;
+ find_var_refs(lambda->lambda.body, &all_refs, &num_refs);
- if (is_token(t, "|>"))
- {
- return PREC_TERM;
- }
- }
+ char **local_decls = NULL;
+ int num_local_decls = 0;
+ find_declared_vars(lambda->lambda.body, &local_decls, &num_local_decls);
- if (t.type == TOK_LBRACKET || t.type == TOK_LPAREN)
- {
- return PREC_CALL;
- }
+ char **captures = xmalloc(sizeof(char *) * 16);
+ char **capture_types = xmalloc(sizeof(char *) * 16);
+ int num_captures = 0;
- if (is_token(t, "??"))
- {
- return PREC_OR;
- }
+ for (int i = 0; i < num_refs; i++) {
+ const char *var_name = all_refs[i];
- if (is_token(t, "\?\?="))
- {
- return PREC_ASSIGNMENT;
+ if (is_in_list(var_name, lambda->lambda.param_names,
+ lambda->lambda.num_params)) {
+ continue;
}
- return PREC_NONE;
-}
-
-// Helper to check if a variable name is in a list.
-static int is_in_list(const char *name, char **list, int count)
-{
- for (int i = 0; i < count; i++)
- {
- if (0 == strcmp(name, list[i]))
- {
- return 1;
- }
+ if (is_in_list(var_name, local_decls, num_local_decls)) {
+ continue;
}
- return 0;
-}
-// Recursively find all variable references in an expression/statement.
-static void find_var_refs(ASTNode *node, char ***refs, int *ref_count)
-{
- if (!node)
- {
- return;
+ if (is_in_list(var_name, captures, num_captures)) {
+ continue;
}
- if (node->type == NODE_EXPR_VAR)
- {
- *refs = xrealloc(*refs, sizeof(char *) * (*ref_count + 1));
- (*refs)[*ref_count] = xstrdup(node->var_ref.name);
- (*ref_count)++;
+ if (strcmp(var_name, "printf") == 0 || strcmp(var_name, "malloc") == 0 ||
+ strcmp(var_name, "strcmp") == 0 || strcmp(var_name, "free") == 0 ||
+ strcmp(var_name, "Vec_new") == 0 || strcmp(var_name, "Vec_push") == 0) {
+ continue;
}
- switch (node->type)
- {
- case NODE_EXPR_BINARY:
- find_var_refs(node->binary.left, refs, ref_count);
- find_var_refs(node->binary.right, refs, ref_count);
- break;
- case NODE_EXPR_UNARY:
- find_var_refs(node->unary.operand, refs, ref_count);
- break;
- case NODE_EXPR_CALL:
- find_var_refs(node->call.callee, refs, ref_count);
- for (ASTNode *arg = node->call.args; arg; arg = arg->next)
- {
- find_var_refs(arg, refs, ref_count);
- }
- break;
- case NODE_EXPR_MEMBER:
- find_var_refs(node->member.target, refs, ref_count);
- break;
- case NODE_EXPR_INDEX:
- find_var_refs(node->index.array, refs, ref_count);
- find_var_refs(node->index.index, refs, ref_count);
- break;
- case NODE_EXPR_SLICE:
- find_var_refs(node->slice.array, refs, ref_count);
- find_var_refs(node->slice.start, refs, ref_count);
- find_var_refs(node->slice.end, refs, ref_count);
- break;
- case NODE_BLOCK:
- for (ASTNode *stmt = node->block.statements; stmt; stmt = stmt->next)
- {
- find_var_refs(stmt, refs, ref_count);
- }
- break;
- case NODE_RETURN:
- find_var_refs(node->ret.value, refs, ref_count);
- break;
- case NODE_VAR_DECL:
- case NODE_CONST:
- find_var_refs(node->var_decl.init_expr, refs, ref_count);
- break;
- case NODE_IF:
- find_var_refs(node->if_stmt.condition, refs, ref_count);
- find_var_refs(node->if_stmt.then_body, refs, ref_count);
- if (node->if_stmt.else_body)
- {
- find_var_refs(node->if_stmt.else_body, refs, ref_count);
- }
+ FuncSig *fs = ctx->func_registry;
+ int is_func = 0;
+ while (fs) {
+ if (0 == strcmp(fs->name, var_name)) {
+ is_func = 1;
break;
- case NODE_WHILE:
- find_var_refs(node->while_stmt.condition, refs, ref_count);
- find_var_refs(node->while_stmt.body, refs, ref_count);
- break;
- case NODE_FOR:
- find_var_refs(node->for_stmt.init, refs, ref_count);
- find_var_refs(node->for_stmt.condition, refs, ref_count);
- find_var_refs(node->for_stmt.step, refs, ref_count);
- find_var_refs(node->for_stmt.body, refs, ref_count);
- break;
- case NODE_MATCH:
- find_var_refs(node->match_stmt.expr, refs, ref_count);
- for (ASTNode *c = node->match_stmt.cases; c; c = c->next)
- {
- find_var_refs(c->match_case.body, refs, ref_count);
- }
- break;
- default:
+ }
+ fs = fs->next;
+ }
+ if (is_func) {
+ continue;
+ }
+
+ Scope *s = ctx->current_scope;
+ int is_local = 0;
+ int is_found = 0;
+ while (s) {
+ Symbol *cur = s->symbols;
+ while (cur) {
+ if (0 == strcmp(cur->name, var_name)) {
+ is_found = 1;
+ if (s->parent != NULL) {
+ is_local = 1;
+ }
+ break;
+ }
+ cur = cur->next;
+ }
+ if (is_found) {
break;
+ }
+ s = s->parent;
}
-}
-// Helper to find variable declarations in a subtree
-static void find_declared_vars(ASTNode *node, char ***decls, int *count)
-{
- if (!node)
- {
- return;
+ if (is_found && !is_local) {
+ continue;
}
- if (node->type == NODE_VAR_DECL)
- {
- *decls = xrealloc(*decls, sizeof(char *) * (*count + 1));
- (*decls)[*count] = xstrdup(node->var_decl.name);
- (*count)++;
- }
+ captures[num_captures] = xstrdup(var_name);
- if (node->type == NODE_MATCH_CASE && node->match_case.binding_name)
- {
- *decls = xrealloc(*decls, sizeof(char *) * (*count + 1));
- (*decls)[*count] = xstrdup(node->match_case.binding_name);
- (*count)++;
+ Type *t = find_symbol_type_info(ctx, var_name);
+ if (t) {
+ capture_types[num_captures] = type_to_string(t);
+ } else {
+ // Fallback for global/unknown
+ // If looks like a function, use "void*" (for closure ctx)
+ // else default to "int" or "void*"
+ capture_types[num_captures] = xstrdup("int");
}
+ num_captures++;
+ }
- switch (node->type)
- {
- case NODE_BLOCK:
- for (ASTNode *stmt = node->block.statements; stmt; stmt = stmt->next)
- {
- find_declared_vars(stmt, decls, count);
- }
- break;
- case NODE_IF:
- find_declared_vars(node->if_stmt.then_body, decls, count);
- find_declared_vars(node->if_stmt.else_body, decls, count);
- break;
- case NODE_WHILE:
- find_declared_vars(node->while_stmt.body, decls, count);
- break;
- case NODE_FOR:
- find_declared_vars(node->for_stmt.init, decls, count);
- find_declared_vars(node->for_stmt.body, decls, count);
- break;
- case NODE_MATCH:
- for (ASTNode *c = node->match_stmt.cases; c; c = c->next)
- {
- find_declared_vars(c, decls, count);
- find_declared_vars(c->match_case.body, decls, count);
- }
- break;
- default:
- break;
- }
-}
+ lambda->lambda.captured_vars = captures;
+ lambda->lambda.captured_types = capture_types;
+ lambda->lambda.num_captures = num_captures;
-// Analyze lambda body to find captured variables.
-void analyze_lambda_captures(ParserContext *ctx, ASTNode *lambda)
-{
- if (!lambda || lambda->type != NODE_LAMBDA)
- {
- return;
+ if (local_decls) {
+ for (int i = 0; i < num_local_decls; i++) {
+ free(local_decls[i]);
}
+ free(local_decls);
+ }
+ for (int i = 0; i < num_refs; i++) {
+ free(all_refs[i]);
+ }
+ if (all_refs) {
+ free(all_refs);
+ }
+}
- char **all_refs = NULL;
- int num_refs = 0;
- find_var_refs(lambda->lambda.body, &all_refs, &num_refs);
-
- char **local_decls = NULL;
- int num_local_decls = 0;
- find_declared_vars(lambda->lambda.body, &local_decls, &num_local_decls);
-
- char **captures = xmalloc(sizeof(char *) * 16);
- char **capture_types = xmalloc(sizeof(char *) * 16);
- int num_captures = 0;
-
- for (int i = 0; i < num_refs; i++)
- {
- const char *var_name = all_refs[i];
-
- if (is_in_list(var_name, lambda->lambda.param_names, lambda->lambda.num_params))
- {
- continue;
- }
-
- if (is_in_list(var_name, local_decls, num_local_decls))
- {
- continue;
- }
+ASTNode *parse_lambda(ParserContext *ctx, Lexer *l) {
+ lexer_next(l);
- if (is_in_list(var_name, captures, num_captures))
- {
- continue;
- }
+ if (lexer_peek(l).type != TOK_LPAREN) {
+ zpanic_at(lexer_peek(l), "Expected '(' after 'fn' in lambda");
+ }
- if (strcmp(var_name, "printf") == 0 || strcmp(var_name, "malloc") == 0 ||
- strcmp(var_name, "strcmp") == 0 || strcmp(var_name, "free") == 0 ||
- strcmp(var_name, "Vec_new") == 0 || strcmp(var_name, "Vec_push") == 0)
- {
- continue;
- }
+ lexer_next(l);
- FuncSig *fs = ctx->func_registry;
- int is_func = 0;
- while (fs)
- {
- if (0 == strcmp(fs->name, var_name))
- {
- is_func = 1;
- break;
- }
- fs = fs->next;
- }
- if (is_func)
- {
- continue;
- }
+ char **param_names = xmalloc(sizeof(char *) * 16);
+ char **param_types = xmalloc(sizeof(char *) * 16);
+ int num_params = 0;
- Scope *s = ctx->current_scope;
- int is_local = 0;
- int is_found = 0;
- while (s)
- {
- Symbol *cur = s->symbols;
- while (cur)
- {
- if (0 == strcmp(cur->name, var_name))
- {
- is_found = 1;
- if (s->parent != NULL)
- {
- is_local = 1;
- }
- break;
- }
- cur = cur->next;
- }
- if (is_found)
- {
- break;
- }
- s = s->parent;
- }
+ while (lexer_peek(l).type != TOK_RPAREN) {
+ if (num_params > 0) {
+ if (lexer_peek(l).type != TOK_COMMA) {
+ zpanic_at(lexer_peek(l), "Expected ',' between parameters");
+ }
- if (is_found && !is_local)
- {
- continue;
- }
-
- captures[num_captures] = xstrdup(var_name);
-
- Type *t = find_symbol_type_info(ctx, var_name);
- if (t)
- {
- capture_types[num_captures] = type_to_string(t);
- }
- else
- {
- // Fallback for global/unknown
- // If looks like a function, use "void*" (for closure ctx)
- // else default to "int" or "void*"
- capture_types[num_captures] = xstrdup("int");
- }
- num_captures++;
+ lexer_next(l);
}
- lambda->lambda.captured_vars = captures;
- lambda->lambda.captured_types = capture_types;
- lambda->lambda.num_captures = num_captures;
-
- if (local_decls)
- {
- for (int i = 0; i < num_local_decls; i++)
- {
- free(local_decls[i]);
- }
- free(local_decls);
- }
- for (int i = 0; i < num_refs; i++)
- {
- free(all_refs[i]);
- }
- if (all_refs)
- {
- free(all_refs);
+ Token name_tok = lexer_next(l);
+ if (name_tok.type != TOK_IDENT) {
+ zpanic_at(name_tok, "Expected parameter name");
}
-}
-ASTNode *parse_lambda(ParserContext *ctx, Lexer *l)
-{
- lexer_next(l);
+ param_names[num_params] = token_strdup(name_tok);
- if (lexer_peek(l).type != TOK_LPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected '(' after 'fn' in lambda");
+ if (lexer_peek(l).type != TOK_COLON) {
+ zpanic_at(lexer_peek(l), "Expected ':' after parameter name");
}
lexer_next(l);
- char **param_names = xmalloc(sizeof(char *) * 16);
- char **param_types = xmalloc(sizeof(char *) * 16);
- int num_params = 0;
-
- while (lexer_peek(l).type != TOK_RPAREN)
- {
- if (num_params > 0)
- {
- if (lexer_peek(l).type != TOK_COMMA)
- {
- zpanic_at(lexer_peek(l), "Expected ',' between parameters");
- }
-
- lexer_next(l);
- }
-
- Token name_tok = lexer_next(l);
- if (name_tok.type != TOK_IDENT)
- {
- zpanic_at(name_tok, "Expected parameter name");
- }
-
- param_names[num_params] = token_strdup(name_tok);
+ char *param_type_str = parse_type(ctx, l);
+ param_types[num_params] = param_type_str;
+ num_params++;
+ }
+ lexer_next(l);
- if (lexer_peek(l).type != TOK_COLON)
- {
- zpanic_at(lexer_peek(l), "Expected ':' after parameter name");
- }
+ char *return_type = xstrdup("void");
+ if (lexer_peek(l).type == TOK_ARROW) {
+ lexer_next(l);
+ return_type = parse_type(ctx, l);
+ }
+
+ ASTNode *body = NULL;
+ if (lexer_peek(l).type == TOK_LBRACE) {
+ body = parse_block(ctx, l);
+ } else {
+ zpanic_at(lexer_peek(l), "Expected '{' for lambda body");
+ }
+
+ ASTNode *lambda = ast_create(NODE_LAMBDA);
+ lambda->lambda.param_names = param_names;
+ lambda->lambda.param_types = param_types;
+ lambda->lambda.return_type = return_type;
+ lambda->lambda.body = body;
+ lambda->lambda.num_params = num_params;
+ lambda->lambda.lambda_id = ctx->lambda_counter++;
+ lambda->lambda.is_expression = 0;
+ register_lambda(ctx, lambda);
+ analyze_lambda_captures(ctx, lambda);
+
+ return lambda;
+}
- lexer_next(l);
+// Helper to create AST for f-string content.
+static ASTNode *create_fstring_block(ParserContext *ctx, const char *content) {
+ ASTNode *block = ast_create(NODE_BLOCK);
+ block->type_info = type_new(TYPE_STRING);
+ block->resolved_type = xstrdup("string");
+
+ ASTNode *head = NULL, *tail = NULL;
+
+ ASTNode *decl_b = ast_create(NODE_RAW_STMT);
+ decl_b->raw_stmt.content = xstrdup("static char _b[4096]; _b[0]=0;");
+ if (!head) {
+ head = decl_b;
+ } else {
+ tail->next = decl_b;
+ }
+ tail = decl_b;
+
+ ASTNode *decl_t = ast_create(NODE_RAW_STMT);
+ decl_t->raw_stmt.content = xstrdup("char _t[128];");
+ tail->next = decl_t;
+ tail = decl_t;
+
+ const char *cur = content;
+ while (*cur) {
+ char *brace = strchr(cur, '{');
+ if (!brace) {
+ if (strlen(cur) > 0) {
+ ASTNode *cat = ast_create(NODE_RAW_STMT);
+ cat->raw_stmt.content = xmalloc(strlen(cur) + 20);
+ sprintf(cat->raw_stmt.content, "strcat(_b, \"%s\");", cur);
+ tail->next = cat;
+ tail = cat;
+ }
+ break;
+ }
+
+ if (brace > cur) {
+ int len = brace - cur;
+ char *txt = xmalloc(len + 1);
+ strncpy(txt, cur, len);
+ txt[len] = 0;
+ ASTNode *cat = ast_create(NODE_RAW_STMT);
+ cat->raw_stmt.content = xmalloc(len + 20);
+ sprintf(cat->raw_stmt.content, "strcat(_b, \"%s\");", txt);
+ tail->next = cat;
+ tail = cat;
+ free(txt);
+ }
+
+ char *end_brace = strchr(brace, '}');
+ if (!end_brace) {
+ break;
+ }
+
+ char *colon = NULL;
+ char *p = brace + 1;
+ int depth = 1;
+ while (p < end_brace) {
+ if (*p == '{') {
+ depth++;
+ }
+ if (*p == '}') {
+ depth--;
+ }
+ if (depth == 1 && *p == ':' && !colon) {
+ colon = p;
+ }
+ p++;
+ }
+
+ char *expr_str;
+ char *fmt = NULL;
+
+ if (colon && colon < end_brace) {
+ int expr_len = colon - (brace + 1);
+ expr_str = xmalloc(expr_len + 1);
+ strncpy(expr_str, brace + 1, expr_len);
+ expr_str[expr_len] = 0;
+
+ int fmt_len = end_brace - (colon + 1);
+ fmt = xmalloc(fmt_len + 1);
+ strncpy(fmt, colon + 1, fmt_len);
+ fmt[fmt_len] = 0;
+ } else {
+ int expr_len = end_brace - (brace + 1);
+ expr_str = xmalloc(expr_len + 1);
+ strncpy(expr_str, brace + 1, expr_len);
+ expr_str[expr_len] = 0;
+ }
+
+ Lexer sub_l;
+ lexer_init(&sub_l, expr_str);
+ ASTNode *expr_node = parse_expression(ctx, &sub_l);
+
+ if (expr_node && expr_node->type == NODE_EXPR_VAR) {
+ Symbol *sym = find_symbol_entry(ctx, expr_node->var_ref.name);
+ if (sym) {
+ sym->is_used = 1;
+ }
+ }
+
+ ASTNode *call_sprintf = ast_create(NODE_EXPR_CALL);
+ ASTNode *callee = ast_create(NODE_EXPR_VAR);
+ callee->var_ref.name = xstrdup("sprintf");
+ call_sprintf->call.callee = callee;
+
+ ASTNode *arg_t = ast_create(NODE_EXPR_VAR);
+ arg_t->var_ref.name = xstrdup("_t");
+
+ ASTNode *arg_fmt = NULL;
+ if (fmt) {
+ char *fmt_str = xmalloc(strlen(fmt) + 3);
+ sprintf(fmt_str, "%%%s", fmt);
+ arg_fmt = ast_create(NODE_EXPR_LITERAL);
+ arg_fmt->literal.type_kind = 2;
+ arg_fmt->literal.string_val = fmt_str;
+ arg_fmt->type_info = type_new(TYPE_STRING);
+ } else {
+ // _z_str(expr)
+ ASTNode *call_macro = ast_create(NODE_EXPR_CALL);
+ ASTNode *macro_callee = ast_create(NODE_EXPR_VAR);
+ macro_callee->var_ref.name = xstrdup("_z_str");
+ call_macro->call.callee = macro_callee;
+ Lexer l2;
+ lexer_init(&l2, expr_str);
+ ASTNode *expr_copy = parse_expression(ctx, &l2);
+
+ call_macro->call.args = expr_copy;
+ arg_fmt = call_macro;
+ }
+
+ call_sprintf->call.args = arg_t;
+ arg_t->next = arg_fmt;
+ arg_fmt->next = expr_node;
+
+ tail->next = call_sprintf;
+ tail = call_sprintf;
+
+ // strcat(_b, _t)
+ ASTNode *cat_t = ast_create(NODE_RAW_STMT);
+ cat_t->raw_stmt.content = xstrdup("strcat(_b, _t);");
+ tail->next = cat_t;
+ tail = cat_t;
+
+ cur = end_brace + 1;
+ free(expr_str);
+ if (fmt) {
+ free(fmt);
+ }
+ }
+
+ // Return _b
+ ASTNode *ret_b = ast_create(NODE_RAW_STMT);
+ ret_b->raw_stmt.content = xstrdup("_b;");
+ tail->next = ret_b;
+ tail = ret_b;
+
+ block->block.statements = head;
+ return block;
+}
- char *param_type_str = parse_type(ctx, l);
- param_types[num_params] = param_type_str;
- num_params++;
+ASTNode *parse_primary(ParserContext *ctx, Lexer *l) {
+ ASTNode *node = NULL;
+ Token t = lexer_next(l);
+
+ // ** Prefixes **
+
+ // Literals
+ if (t.type == TOK_INT) {
+ node = ast_create(NODE_EXPR_LITERAL);
+ node->literal.type_kind = 0;
+ node->type_info = type_new(TYPE_INT);
+ char *s = token_strdup(t);
+ unsigned long long val;
+ if (t.len > 2 && s[0] == '0' && s[1] == 'b') {
+ val = strtoull(s + 2, NULL, 2);
+ } else {
+ val = strtoull(s, NULL, 0);
+ }
+ node->literal.int_val = (unsigned long long)val;
+ free(s);
+ } else if (t.type == TOK_FLOAT) {
+ node = ast_create(NODE_EXPR_LITERAL);
+ node->literal.type_kind = 1;
+ node->literal.float_val = atof(t.start);
+ node->type_info = type_new(TYPE_F64);
+ } else if (t.type == TOK_STRING) {
+ node = ast_create(NODE_EXPR_LITERAL);
+ node->literal.type_kind = TOK_STRING;
+ node->literal.string_val = xmalloc(t.len);
+ strncpy(node->literal.string_val, t.start + 1, t.len - 2);
+ node->literal.string_val[t.len - 2] = 0;
+ node->type_info = type_new(TYPE_STRING);
+ } else if (t.type == TOK_FSTRING) {
+ char *inner = xmalloc(t.len);
+ strncpy(inner, t.start + 2, t.len - 3);
+ inner[t.len - 3] = 0;
+ node = create_fstring_block(ctx, inner);
+ free(inner);
+ } else if (t.type == TOK_CHAR) {
+ node = ast_create(NODE_EXPR_LITERAL);
+ node->literal.type_kind = TOK_CHAR;
+ node->literal.string_val = token_strdup(t);
+ node->type_info = type_new(TYPE_I8);
+ }
+
+ else if (t.type == TOK_SIZEOF) {
+ if (lexer_peek(l).type != TOK_LPAREN) {
+ zpanic_at(lexer_peek(l), "Expected ( after sizeof");
}
lexer_next(l);
- char *return_type = xstrdup("void");
- if (lexer_peek(l).type == TOK_ARROW)
- {
- lexer_next(l);
- return_type = parse_type(ctx, l);
- }
-
- ASTNode *body = NULL;
- if (lexer_peek(l).type == TOK_LBRACE)
- {
- body = parse_block(ctx, l);
+ int pos = l->pos;
+ int col = l->col;
+ int line = l->line;
+ Type *ty = parse_type_formal(ctx, l);
+
+ if (ty->kind != TYPE_UNKNOWN && lexer_peek(l).type == TOK_RPAREN) {
+ lexer_next(l);
+ char *ts = type_to_string(ty);
+ node = ast_create(NODE_EXPR_SIZEOF);
+ node->size_of.target_type = ts;
+ node->size_of.expr = NULL;
+ node->type_info = type_new(TYPE_USIZE);
+ } else {
+ l->pos = pos;
+ l->col = col;
+ l->line = line;
+ ASTNode *ex = parse_expression(ctx, l);
+ if (lexer_next(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected ) after sizeof identifier");
+ }
+ node = ast_create(NODE_EXPR_SIZEOF);
+ node->size_of.target_type = NULL;
+ node->size_of.expr = ex;
+ node->type_info = type_new(TYPE_USIZE);
+ }
+ }
+
+ else if (t.type == TOK_IDENT && strncmp(t.start, "typeof", 6) == 0 &&
+ t.len == 6) {
+ if (lexer_peek(l).type != TOK_LPAREN) {
+ zpanic_at(lexer_peek(l), "Expected ( after typeof");
}
- else
- {
- zpanic_at(lexer_peek(l), "Expected '{' for lambda body");
- }
-
- ASTNode *lambda = ast_create(NODE_LAMBDA);
- lambda->lambda.param_names = param_names;
- lambda->lambda.param_types = param_types;
- lambda->lambda.return_type = return_type;
- lambda->lambda.body = body;
- lambda->lambda.num_params = num_params;
- lambda->lambda.lambda_id = ctx->lambda_counter++;
- lambda->lambda.is_expression = 0;
- register_lambda(ctx, lambda);
- analyze_lambda_captures(ctx, lambda);
+ lexer_next(l);
- return lambda;
-}
+ int pos = l->pos;
+ int col = l->col;
+ int line = l->line;
+ Type *ty = parse_type_formal(ctx, l);
-// Helper to create AST for f-string content.
-static ASTNode *create_fstring_block(ParserContext *ctx, const char *content)
-{
- ASTNode *block = ast_create(NODE_BLOCK);
- block->type_info = type_new(TYPE_STRING);
- block->resolved_type = xstrdup("string");
+ if (ty->kind != TYPE_UNKNOWN && lexer_peek(l).type == TOK_RPAREN) {
+ lexer_next(l);
+ char *ts = type_to_string(ty);
+ node = ast_create(NODE_TYPEOF);
+ node->size_of.target_type = ts;
+ node->size_of.expr = NULL;
+ } else {
+ l->pos = pos;
+ l->col = col;
+ l->line = line;
+ ASTNode *ex = parse_expression(ctx, l);
+ if (lexer_next(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected ) after typeof expression");
+ }
+ node = ast_create(NODE_TYPEOF);
+ node->size_of.target_type = NULL;
+ node->size_of.expr = ex;
+ }
+ }
- ASTNode *head = NULL, *tail = NULL;
+ else if (t.type == TOK_AT) {
+ Token ident = lexer_next(l);
+ if (ident.type != TOK_IDENT) {
+ zpanic_at(ident, "Expected intrinsic name after @");
+ }
- ASTNode *decl_b = ast_create(NODE_RAW_STMT);
- decl_b->raw_stmt.content = xstrdup("static char _b[4096]; _b[0]=0;");
- if (!head)
- {
- head = decl_b;
+ int kind = -1;
+ if (strncmp(ident.start, "type_name", 9) == 0 && ident.len == 9) {
+ kind = 0;
+ } else if (strncmp(ident.start, "fields", 6) == 0 && ident.len == 6) {
+ kind = 1;
+ } else {
+ zpanic_at(ident, "Unknown intrinsic @%.*s", ident.len, ident.start);
}
- else
+
{
- tail->next = decl_b;
+ Token t = lexer_next(l);
+ if (t.type != TOK_LPAREN) {
+ zpanic_at(t, "Expected ( after intrinsic");
+ }
}
- tail = decl_b;
- ASTNode *decl_t = ast_create(NODE_RAW_STMT);
- decl_t->raw_stmt.content = xstrdup("char _t[128];");
- tail->next = decl_t;
- tail = decl_t;
+ Type *target = parse_type_formal(ctx, l);
- const char *cur = content;
- while (*cur)
{
- char *brace = strchr(cur, '{');
- if (!brace)
- {
- if (strlen(cur) > 0)
- {
- ASTNode *cat = ast_create(NODE_RAW_STMT);
- cat->raw_stmt.content = xmalloc(strlen(cur) + 20);
- sprintf(cat->raw_stmt.content, "strcat(_b, \"%s\");", cur);
- tail->next = cat;
- tail = cat;
- }
- break;
- }
-
- if (brace > cur)
- {
- int len = brace - cur;
- char *txt = xmalloc(len + 1);
- strncpy(txt, cur, len);
- txt[len] = 0;
- ASTNode *cat = ast_create(NODE_RAW_STMT);
- cat->raw_stmt.content = xmalloc(len + 20);
- sprintf(cat->raw_stmt.content, "strcat(_b, \"%s\");", txt);
- tail->next = cat;
- tail = cat;
- free(txt);
- }
-
- char *end_brace = strchr(brace, '}');
- if (!end_brace)
- {
- break;
- }
-
- char *colon = NULL;
- char *p = brace + 1;
- int depth = 1;
- while (p < end_brace)
- {
- if (*p == '{')
- {
- depth++;
- }
- if (*p == '}')
- {
- depth--;
- }
- if (depth == 1 && *p == ':' && !colon)
- {
- colon = p;
- }
- p++;
- }
-
- char *expr_str;
- char *fmt = NULL;
-
- if (colon && colon < end_brace)
- {
- int expr_len = colon - (brace + 1);
- expr_str = xmalloc(expr_len + 1);
- strncpy(expr_str, brace + 1, expr_len);
- expr_str[expr_len] = 0;
-
- int fmt_len = end_brace - (colon + 1);
- fmt = xmalloc(fmt_len + 1);
- strncpy(fmt, colon + 1, fmt_len);
- fmt[fmt_len] = 0;
- }
- else
- {
- int expr_len = end_brace - (brace + 1);
- expr_str = xmalloc(expr_len + 1);
- strncpy(expr_str, brace + 1, expr_len);
- expr_str[expr_len] = 0;
- }
-
- Lexer sub_l;
- lexer_init(&sub_l, expr_str);
- ASTNode *expr_node = parse_expression(ctx, &sub_l);
-
- if (expr_node && expr_node->type == NODE_EXPR_VAR)
- {
- Symbol *sym = find_symbol_entry(ctx, expr_node->var_ref.name);
- if (sym)
- {
- sym->is_used = 1;
- }
- }
-
- ASTNode *call_sprintf = ast_create(NODE_EXPR_CALL);
- ASTNode *callee = ast_create(NODE_EXPR_VAR);
- callee->var_ref.name = xstrdup("sprintf");
- call_sprintf->call.callee = callee;
-
- ASTNode *arg_t = ast_create(NODE_EXPR_VAR);
- arg_t->var_ref.name = xstrdup("_t");
-
- ASTNode *arg_fmt = NULL;
- if (fmt)
- {
- char *fmt_str = xmalloc(strlen(fmt) + 3);
- sprintf(fmt_str, "%%%s", fmt);
- arg_fmt = ast_create(NODE_EXPR_LITERAL);
- arg_fmt->literal.type_kind = 2;
- arg_fmt->literal.string_val = fmt_str;
- arg_fmt->type_info = type_new(TYPE_STRING);
- }
- else
- {
- // _z_str(expr)
- ASTNode *call_macro = ast_create(NODE_EXPR_CALL);
- ASTNode *macro_callee = ast_create(NODE_EXPR_VAR);
- macro_callee->var_ref.name = xstrdup("_z_str");
- call_macro->call.callee = macro_callee;
- Lexer l2;
- lexer_init(&l2, expr_str);
- ASTNode *expr_copy = parse_expression(ctx, &l2);
-
- call_macro->call.args = expr_copy;
- arg_fmt = call_macro;
- }
-
- call_sprintf->call.args = arg_t;
- arg_t->next = arg_fmt;
- arg_fmt->next = expr_node;
-
- tail->next = call_sprintf;
- tail = call_sprintf;
-
- // strcat(_b, _t)
- ASTNode *cat_t = ast_create(NODE_RAW_STMT);
- cat_t->raw_stmt.content = xstrdup("strcat(_b, _t);");
- tail->next = cat_t;
- tail = cat_t;
-
- cur = end_brace + 1;
- free(expr_str);
- if (fmt)
- {
- free(fmt);
- }
- }
-
- // Return _b
- ASTNode *ret_b = ast_create(NODE_RAW_STMT);
- ret_b->raw_stmt.content = xstrdup("_b;");
- tail->next = ret_b;
- tail = ret_b;
-
- block->block.statements = head;
- return block;
-}
-
-ASTNode *parse_primary(ParserContext *ctx, Lexer *l)
-{
- ASTNode *node = NULL;
- Token t = lexer_next(l);
+ Token t = lexer_next(l);
+ if (t.type != TOK_RPAREN) {
+ zpanic_at(t, "Expected ) after intrinsic type");
+ }
+ }
- // ** Prefixes **
+ node = ast_create(NODE_REFLECTION);
+ node->reflection.kind = kind;
+ node->reflection.target_type = target;
+ node->type_info =
+ (kind == 0) ? type_new(TYPE_STRING) : type_new_ptr(type_new(TYPE_VOID));
+ }
- // Literals
- if (t.type == TOK_INT)
- {
- node = ast_create(NODE_EXPR_LITERAL);
- node->literal.type_kind = 0;
- node->type_info = type_new(TYPE_INT);
- char *s = token_strdup(t);
- unsigned long long val;
- if (t.len > 2 && s[0] == '0' && s[1] == 'b')
- {
- val = strtoull(s + 2, NULL, 2);
- }
- else
- {
- val = strtoull(s, NULL, 0);
- }
- node->literal.int_val = (unsigned long long)val;
- free(s);
- }
- else if (t.type == TOK_FLOAT)
- {
- node = ast_create(NODE_EXPR_LITERAL);
- node->literal.type_kind = 1;
- node->literal.float_val = atof(t.start);
- node->type_info = type_new(TYPE_F64);
- }
- else if (t.type == TOK_STRING)
+ else if (t.type == TOK_IDENT && strncmp(t.start, "match", 5) == 0 &&
+ t.len == 5) {
+ ASTNode *expr = parse_expression(ctx, l);
+ skip_comments(l);
{
- node = ast_create(NODE_EXPR_LITERAL);
- node->literal.type_kind = TOK_STRING;
- node->literal.string_val = xmalloc(t.len);
- strncpy(node->literal.string_val, t.start + 1, t.len - 2);
- node->literal.string_val[t.len - 2] = 0;
- node->type_info = type_new(TYPE_STRING);
- }
- else if (t.type == TOK_FSTRING)
- {
- char *inner = xmalloc(t.len);
- strncpy(inner, t.start + 2, t.len - 3);
- inner[t.len - 3] = 0;
- node = create_fstring_block(ctx, inner);
- free(inner);
- }
- else if (t.type == TOK_CHAR)
- {
- node = ast_create(NODE_EXPR_LITERAL);
- node->literal.type_kind = TOK_CHAR;
- node->literal.string_val = token_strdup(t);
- node->type_info = type_new(TYPE_I8);
+ Token t = lexer_next(l);
+ if (t.type != TOK_LBRACE) {
+ zpanic_at(t, "Expected { after match expression");
+ }
}
- else if (t.type == TOK_SIZEOF)
- {
- if (lexer_peek(l).type != TOK_LPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected ( after sizeof");
- }
+ ASTNode *h = 0, *tl = 0;
+ while (1) {
+ skip_comments(l);
+ if (lexer_peek(l).type == TOK_RBRACE) {
+ break;
+ }
+ if (lexer_peek(l).type == TOK_COMMA) {
lexer_next(l);
+ }
- int pos = l->pos;
- int col = l->col;
- int line = l->line;
- Type *ty = parse_type_formal(ctx, l);
+ skip_comments(l);
+ if (lexer_peek(l).type == TOK_RBRACE) {
+ break;
+ }
- if (ty->kind != TYPE_UNKNOWN && lexer_peek(l).type == TOK_RPAREN)
- {
- lexer_next(l);
- char *ts = type_to_string(ty);
- node = ast_create(NODE_EXPR_SIZEOF);
- node->size_of.target_type = ts;
- node->size_of.expr = NULL;
- node->type_info = type_new(TYPE_USIZE);
- }
- else
- {
- l->pos = pos;
- l->col = col;
- l->line = line;
- ASTNode *ex = parse_expression(ctx, l);
- if (lexer_next(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected ) after sizeof identifier");
- }
- node = ast_create(NODE_EXPR_SIZEOF);
- node->size_of.target_type = NULL;
- node->size_of.expr = ex;
- node->type_info = type_new(TYPE_USIZE);
- }
- }
+ Token p = lexer_next(l);
+ char *pattern = token_strdup(p);
+ int is_default = (strcmp(pattern, "_") == 0);
- else if (t.type == TOK_IDENT && strncmp(t.start, "typeof", 6) == 0 && t.len == 6)
- {
- if (lexer_peek(l).type != TOK_LPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected ( after typeof");
- }
+ char *binding = NULL;
+ int is_destructure = 0;
+ skip_comments(l);
+ if (!is_default && lexer_peek(l).type == TOK_LPAREN) {
lexer_next(l);
-
- int pos = l->pos;
- int col = l->col;
- int line = l->line;
- Type *ty = parse_type_formal(ctx, l);
-
- if (ty->kind != TYPE_UNKNOWN && lexer_peek(l).type == TOK_RPAREN)
- {
- lexer_next(l);
- char *ts = type_to_string(ty);
- node = ast_create(NODE_TYPEOF);
- node->size_of.target_type = ts;
- node->size_of.expr = NULL;
- }
- else
- {
- l->pos = pos;
- l->col = col;
- l->line = line;
- ASTNode *ex = parse_expression(ctx, l);
- if (lexer_next(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected ) after typeof expression");
- }
- node = ast_create(NODE_TYPEOF);
- node->size_of.target_type = NULL;
- node->size_of.expr = ex;
+ Token b = lexer_next(l);
+ if (b.type != TOK_IDENT) {
+ zpanic_at(b, "Expected binding name");
}
- }
-
- else if (t.type == TOK_AT)
- {
- Token ident = lexer_next(l);
- if (ident.type != TOK_IDENT)
- {
- zpanic_at(ident, "Expected intrinsic name after @");
+ binding = token_strdup(b);
+ if (lexer_next(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected )");
}
+ is_destructure = 1;
+ }
- int kind = -1;
- if (strncmp(ident.start, "type_name", 9) == 0 && ident.len == 9)
- {
- kind = 0;
- }
- else if (strncmp(ident.start, "fields", 6) == 0 && ident.len == 6)
- {
- kind = 1;
- }
- else
- {
- zpanic_at(ident, "Unknown intrinsic @%.*s", ident.len, ident.start);
- }
+ ASTNode *guard = NULL;
+ skip_comments(l);
+ if (lexer_peek(l).type == TOK_IDENT &&
+ strncmp(lexer_peek(l).start, "if", 2) == 0) {
+ lexer_next(l);
+ guard = parse_expression(ctx, l);
+ }
+
+ skip_comments(l);
+ if (lexer_next(l).type != TOK_ARROW) {
+ zpanic_at(lexer_peek(l), "Expected '=>'");
+ }
+
+ ASTNode *body;
+ skip_comments(l);
+ Token pk = lexer_peek(l);
+ if (pk.type == TOK_LBRACE) {
+ body = parse_block(ctx, l);
+ } else if (pk.type == TOK_ASSERT ||
+ (pk.type == TOK_IDENT &&
+ strncmp(pk.start, "assert", 6) == 0)) {
+ body = parse_assert(ctx, l);
+ } else if (pk.type == TOK_IDENT && strncmp(pk.start, "return", 6) == 0) {
+ body = parse_return(ctx, l);
+ } else {
+ body = parse_expression(ctx, l);
+ }
+
+ ASTNode *c = ast_create(NODE_MATCH_CASE);
+ c->match_case.pattern = pattern;
+ c->match_case.binding_name = binding;
+ c->match_case.is_destructuring = is_destructure;
+ c->match_case.guard = guard;
+ c->match_case.body = body;
+ c->match_case.is_default = is_default;
+ if (!h) {
+ h = c;
+ } else {
+ tl->next = c;
+ }
+ tl = c;
+ }
+ lexer_next(l);
+ node = ast_create(NODE_MATCH);
+ node->match_stmt.expr = expr;
+ node->match_stmt.cases = h;
+ }
- {
- Token t = lexer_next(l);
- if (t.type != TOK_LPAREN)
- {
- zpanic_at(t, "Expected ( after intrinsic");
- }
- }
+ else if (t.type == TOK_IDENT) {
+ if (t.len == 2 && strncmp(t.start, "fn", 2) == 0 &&
+ lexer_peek(l).type == TOK_LPAREN) {
+ l->pos -= t.len;
+ l->col -= t.len;
+ return parse_lambda(ctx, l);
+ }
- Type *target = parse_type_formal(ctx, l);
+ char *ident = token_strdup(t);
- {
- Token t = lexer_next(l);
- if (t.type != TOK_RPAREN)
- {
- zpanic_at(t, "Expected ) after intrinsic type");
- }
- }
+ if (lexer_peek(l).type == TOK_OP && lexer_peek(l).start[0] == '!' &&
+ lexer_peek(l).len == 1) {
+ node = parse_macro_call(ctx, l, ident);
+ if (node) {
+ free(ident);
+ return node;
+ }
+ }
- node = ast_create(NODE_REFLECTION);
- node->reflection.kind = kind;
- node->reflection.target_type = target;
- node->type_info = (kind == 0) ? type_new(TYPE_STRING) : type_new_ptr(type_new(TYPE_VOID));
+ if (lexer_peek(l).type == TOK_ARROW) {
+ lexer_next(l);
+ return parse_arrow_lambda_single(ctx, l, ident);
}
- else if (t.type == TOK_IDENT && strncmp(t.start, "match", 5) == 0 && t.len == 5)
- {
- ASTNode *expr = parse_expression(ctx, l);
- skip_comments(l);
- {
- Token t = lexer_next(l);
- if (t.type != TOK_LBRACE)
- {
- zpanic_at(t, "Expected { after match expression");
- }
+ char *acc = ident;
+ while (1) {
+ int changed = 0;
+ if (lexer_peek(l).type == TOK_DCOLON) {
+ lexer_next(l);
+ Token suffix = lexer_next(l);
+ if (suffix.type != TOK_IDENT) {
+ zpanic_at(suffix, "Expected identifier after ::");
+ }
+
+ SelectiveImport *si = (!ctx->current_module_prefix)
+ ? find_selective_import(ctx, acc)
+ : NULL;
+ if (si) {
+ char *tmp = xmalloc(strlen(si->source_module) + strlen(si->symbol) +
+ suffix.len + 3);
+ sprintf(tmp, "%s_%s_", si->source_module, si->symbol);
+ strncat(tmp, suffix.start, suffix.len);
+ free(acc);
+ acc = tmp;
+ } else {
+ Module *mod = find_module(ctx, acc);
+ if (mod) {
+ if (mod->is_c_header) {
+ char *tmp = xmalloc(suffix.len + 1);
+ strncpy(tmp, suffix.start, suffix.len);
+ tmp[suffix.len] = 0;
+ free(acc);
+ acc = tmp;
+ }
+
+ else {
+ char *tmp = xmalloc(strlen(mod->base_name) + suffix.len + 2);
+ sprintf(tmp, "%s_", mod->base_name);
+ strncat(tmp, suffix.start, suffix.len);
+ free(acc);
+ acc = tmp;
+ }
+ } else {
+ char *tmp = xmalloc(strlen(acc) + suffix.len + 2);
+ sprintf(tmp, "%s_", acc);
+ strncat(tmp, suffix.start, suffix.len);
+ free(acc);
+ acc = tmp;
+ }
}
+ changed = 1;
+ }
- ASTNode *h = 0, *tl = 0;
- while (1)
- {
- skip_comments(l);
- if (lexer_peek(l).type == TOK_RBRACE)
- {
- break;
- }
- if (lexer_peek(l).type == TOK_COMMA)
- {
- lexer_next(l);
- }
-
- skip_comments(l);
- if (lexer_peek(l).type == TOK_RBRACE)
- {
- break;
- }
-
- Token p = lexer_next(l);
- char *pattern = token_strdup(p);
- int is_default = (strcmp(pattern, "_") == 0);
-
- char *binding = NULL;
- int is_destructure = 0;
- skip_comments(l);
- if (!is_default && lexer_peek(l).type == TOK_LPAREN)
- {
- lexer_next(l);
- Token b = lexer_next(l);
- if (b.type != TOK_IDENT)
- {
- zpanic_at(b, "Expected binding name");
- }
- binding = token_strdup(b);
- if (lexer_next(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected )");
- }
- is_destructure = 1;
- }
-
- ASTNode *guard = NULL;
- skip_comments(l);
- if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "if", 2) == 0)
- {
- lexer_next(l);
- guard = parse_expression(ctx, l);
- }
-
- skip_comments(l);
- if (lexer_next(l).type != TOK_ARROW)
- {
- zpanic_at(lexer_peek(l), "Expected '=>'");
- }
-
- ASTNode *body;
- skip_comments(l);
- Token pk = lexer_peek(l);
- if (pk.type == TOK_LBRACE)
- {
- body = parse_block(ctx, l);
- }
- else if (pk.type == TOK_ASSERT ||
- (pk.type == TOK_IDENT && strncmp(pk.start, "assert", 6) == 0))
- {
- body = parse_assert(ctx, l);
- }
- else if (pk.type == TOK_IDENT && strncmp(pk.start, "return", 6) == 0)
- {
- body = parse_return(ctx, l);
- }
- else
- {
- body = parse_expression(ctx, l);
- }
+ if (lexer_peek(l).type == TOK_LANGLE) {
+ Lexer lookahead = *l;
+ lexer_next(&lookahead);
+ parse_type(ctx, &lookahead);
+ if (lexer_peek(&lookahead).type == TOK_RANGLE) {
+ lexer_next(l);
+ char *concrete_type = parse_type(ctx, l);
+ lexer_next(l);
+
+ int is_struct = 0;
+ GenericTemplate *st = ctx->templates;
+ while (st) {
+ if (strcmp(st->name, acc) == 0) {
+ is_struct = 1;
+ break;
+ }
+ st = st->next;
+ }
+ if (!is_struct &&
+ (strcmp(acc, "Result") == 0 || strcmp(acc, "Option") == 0)) {
+ is_struct = 1;
+ }
+
+ if (is_struct) {
+ instantiate_generic(ctx, acc, concrete_type, t);
+
+ char *clean_type = sanitize_mangled_name(concrete_type);
+
+ char *m = xmalloc(strlen(acc) + strlen(clean_type) + 2);
+ sprintf(m, "%s_%s", acc, clean_type);
+ free(clean_type);
- ASTNode *c = ast_create(NODE_MATCH_CASE);
- c->match_case.pattern = pattern;
- c->match_case.binding_name = binding;
- c->match_case.is_destructuring = is_destructure;
- c->match_case.guard = guard;
- c->match_case.body = body;
- c->match_case.is_default = is_default;
- if (!h)
- {
- h = c;
- }
- else
- {
- tl->next = c;
- }
- tl = c;
+ free(acc);
+ acc = m;
+ } else {
+ char *m = instantiate_function_template(ctx, acc, concrete_type);
+ if (m) {
+ free(acc);
+ acc = m;
+ } else {
+ zpanic_at(t, "Unknown generic %s", acc);
+ }
+ }
+ changed = 1;
+ }
+ }
+ if (!changed) {
+ break;
+ }
+ }
+
+ if (lexer_peek(l).type == TOK_LBRACE) {
+ int is_struct_init = 0;
+ Lexer pl = *l;
+ lexer_next(&pl);
+ Token fi = lexer_peek(&pl);
+
+ if (fi.type == TOK_RBRACE) {
+ is_struct_init = 1;
+ } else if (fi.type == TOK_IDENT) {
+ lexer_next(&pl);
+ if (lexer_peek(&pl).type == TOK_COLON) {
+ is_struct_init = 1;
+ }
+ }
+ if (is_struct_init) {
+ char *struct_name = acc;
+ if (!ctx->current_module_prefix) {
+ SelectiveImport *si = find_selective_import(ctx, acc);
+ if (si) {
+ struct_name =
+ xmalloc(strlen(si->source_module) + strlen(si->symbol) + 2);
+ sprintf(struct_name, "%s_%s", si->source_module, si->symbol);
+ }
+ }
+ if (struct_name == acc && ctx->current_module_prefix &&
+ !is_known_generic(ctx, acc)) {
+ char *prefixed =
+ xmalloc(strlen(ctx->current_module_prefix) + strlen(acc) + 2);
+ sprintf(prefixed, "%s_%s", ctx->current_module_prefix, acc);
+ struct_name = prefixed;
}
lexer_next(l);
- node = ast_create(NODE_MATCH);
- node->match_stmt.expr = expr;
- node->match_stmt.cases = h;
- }
-
- else if (t.type == TOK_IDENT)
- {
- if (t.len == 2 && strncmp(t.start, "fn", 2) == 0 && lexer_peek(l).type == TOK_LPAREN)
- {
- l->pos -= t.len;
- l->col -= t.len;
- return parse_lambda(ctx, l);
- }
-
- char *ident = token_strdup(t);
-
- if (lexer_peek(l).type == TOK_OP && lexer_peek(l).start[0] == '!' && lexer_peek(l).len == 1)
- {
- node = parse_macro_call(ctx, l, ident);
- if (node)
- {
- free(ident);
- return node;
- }
- }
+ node = ast_create(NODE_EXPR_STRUCT_INIT);
+ node->struct_init.struct_name = struct_name;
+ Type *init_type = type_new(TYPE_STRUCT);
+ init_type->name = xstrdup(struct_name);
+ node->type_info = init_type;
- if (lexer_peek(l).type == TOK_ARROW)
- {
+ ASTNode *head = NULL, *tail = NULL;
+ int first = 1;
+ while (lexer_peek(l).type != TOK_RBRACE) {
+ if (!first && lexer_peek(l).type == TOK_COMMA) {
lexer_next(l);
- return parse_arrow_lambda_single(ctx, l, ident);
- }
-
- char *acc = ident;
- while (1)
- {
- int changed = 0;
- if (lexer_peek(l).type == TOK_DCOLON)
- {
- lexer_next(l);
- Token suffix = lexer_next(l);
- if (suffix.type != TOK_IDENT)
- {
- zpanic_at(suffix, "Expected identifier after ::");
- }
-
- SelectiveImport *si =
- (!ctx->current_module_prefix) ? find_selective_import(ctx, acc) : NULL;
- if (si)
- {
- char *tmp =
- xmalloc(strlen(si->source_module) + strlen(si->symbol) + suffix.len + 3);
- sprintf(tmp, "%s_%s_", si->source_module, si->symbol);
- strncat(tmp, suffix.start, suffix.len);
- free(acc);
- acc = tmp;
- }
- else
- {
- Module *mod = find_module(ctx, acc);
- if (mod)
- {
- if (mod->is_c_header)
- {
- char *tmp = xmalloc(suffix.len + 1);
- strncpy(tmp, suffix.start, suffix.len);
- tmp[suffix.len] = 0;
- free(acc);
- acc = tmp;
- }
-
- else
- {
- char *tmp = xmalloc(strlen(mod->base_name) + suffix.len + 2);
- sprintf(tmp, "%s_", mod->base_name);
- strncat(tmp, suffix.start, suffix.len);
- free(acc);
- acc = tmp;
- }
- }
- else
- {
- char *tmp = xmalloc(strlen(acc) + suffix.len + 2);
- sprintf(tmp, "%s_", acc);
- strncat(tmp, suffix.start, suffix.len);
- free(acc);
- acc = tmp;
- }
- }
- changed = 1;
- }
-
- if (lexer_peek(l).type == TOK_LANGLE)
- {
- Lexer lookahead = *l;
- lexer_next(&lookahead);
- parse_type(ctx, &lookahead);
- if (lexer_peek(&lookahead).type == TOK_RANGLE)
- {
- lexer_next(l);
- char *concrete_type = parse_type(ctx, l);
- lexer_next(l);
-
- int is_struct = 0;
- GenericTemplate *st = ctx->templates;
- while (st)
- {
- if (strcmp(st->name, acc) == 0)
- {
- is_struct = 1;
- break;
- }
- st = st->next;
- }
- if (!is_struct && (strcmp(acc, "Result") == 0 || strcmp(acc, "Option") == 0))
- {
- is_struct = 1;
- }
-
- if (is_struct)
- {
- instantiate_generic(ctx, acc, concrete_type, t);
-
- char *clean_type = sanitize_mangled_name(concrete_type);
-
- char *m = xmalloc(strlen(acc) + strlen(clean_type) + 2);
- sprintf(m, "%s_%s", acc, clean_type);
- free(clean_type);
-
- free(acc);
- acc = m;
- }
- else
- {
- char *m = instantiate_function_template(ctx, acc, concrete_type);
- if (m)
- {
- free(acc);
- acc = m;
- }
- else
- {
- zpanic_at(t, "Unknown generic %s", acc);
- }
- }
- changed = 1;
- }
- }
- if (!changed)
- {
- break;
- }
- }
-
- if (lexer_peek(l).type == TOK_LBRACE)
- {
- int is_struct_init = 0;
- Lexer pl = *l;
- lexer_next(&pl);
- Token fi = lexer_peek(&pl);
-
- if (fi.type == TOK_RBRACE)
- {
- is_struct_init = 1;
- }
- else if (fi.type == TOK_IDENT)
- {
- lexer_next(&pl);
- if (lexer_peek(&pl).type == TOK_COLON)
- {
- is_struct_init = 1;
- }
- }
- if (is_struct_init)
- {
- char *struct_name = acc;
- if (!ctx->current_module_prefix)
- {
- SelectiveImport *si = find_selective_import(ctx, acc);
- if (si)
- {
- struct_name = xmalloc(strlen(si->source_module) + strlen(si->symbol) + 2);
- sprintf(struct_name, "%s_%s", si->source_module, si->symbol);
- }
- }
- if (struct_name == acc && ctx->current_module_prefix && !is_known_generic(ctx, acc))
- {
- char *prefixed = xmalloc(strlen(ctx->current_module_prefix) + strlen(acc) + 2);
- sprintf(prefixed, "%s_%s", ctx->current_module_prefix, acc);
- struct_name = prefixed;
- }
- lexer_next(l);
- node = ast_create(NODE_EXPR_STRUCT_INIT);
- node->struct_init.struct_name = struct_name;
- Type *init_type = type_new(TYPE_STRUCT);
- init_type->name = xstrdup(struct_name);
- node->type_info = init_type;
-
- ASTNode *head = NULL, *tail = NULL;
- int first = 1;
- while (lexer_peek(l).type != TOK_RBRACE)
- {
- if (!first && lexer_peek(l).type == TOK_COMMA)
- {
- lexer_next(l);
- }
- if (lexer_peek(l).type == TOK_RBRACE)
- {
- break;
- }
- Token fn = lexer_next(l);
- if (lexer_next(l).type != TOK_COLON)
- {
- zpanic_at(lexer_peek(l), "Expected :");
- }
- ASTNode *val = parse_expression(ctx, l);
- ASTNode *assign = ast_create(NODE_VAR_DECL);
- assign->var_decl.name = token_strdup(fn);
- assign->var_decl.init_expr = val;
- if (!head)
- {
- head = assign;
- }
- else
- {
- tail->next = assign;
- }
- tail = assign;
- first = 0;
- }
- lexer_next(l);
- node->struct_init.fields = head;
- Type *st = type_new(TYPE_STRUCT);
- st->name = xstrdup(struct_name);
- node->type_info = st;
- return node; // Struct init cannot be called/indexed usually, return
- // early
- }
- }
-
- // E. readln(args...) Magic
- FuncSig *sig = find_func(ctx, acc);
- if (strcmp(acc, "readln") == 0 && lexer_peek(l).type == TOK_LPAREN)
- {
- lexer_next(l); // eat (
-
- // Parse args
- ASTNode *args[16];
- int ac = 0;
- if (lexer_peek(l).type != TOK_RPAREN)
- {
- while (1)
- {
- args[ac++] = parse_expression(ctx, l);
- if (lexer_peek(l).type == TOK_COMMA)
- {
- lexer_next(l);
- }
- else
- {
- break;
- }
- }
- }
- if (lexer_next(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected )");
- }
-
- if (ac == 0)
- {
- // readln() -> _z_readln_raw()
- node = ast_create(NODE_EXPR_CALL);
- ASTNode *callee = ast_create(NODE_EXPR_VAR);
- callee->var_ref.name = xstrdup("_z_readln_raw");
- node->call.callee = callee;
- node->type_info = type_new(TYPE_STRING);
- }
- else
- {
- // readln(vars...) -> _z_scan_helper("fmt", &vars...)
- char fmt[256];
- fmt[0] = 0;
- for (int i = 0; i < ac; i++)
- {
- Type *t = args[i]->type_info;
- if (!t && args[i]->type == NODE_EXPR_VAR)
- {
- t = find_symbol_type_info(ctx, args[i]->var_ref.name);
- }
-
- if (!t)
- {
- strcat(fmt, "%d"); // Fallback
- }
- else
- {
- if (t->kind == TYPE_INT || t->kind == TYPE_I32 || t->kind == TYPE_BOOL)
- {
- strcat(fmt, "%d");
- }
- else if (t->kind == TYPE_F64)
- {
- strcat(fmt, "%lf");
- }
- else if (t->kind == TYPE_F32 || t->kind == TYPE_FLOAT)
- {
- strcat(fmt, "%f");
- }
- else if (t->kind == TYPE_STRING)
- {
- strcat(fmt, "%s");
- }
- else if (t->kind == TYPE_CHAR || t->kind == TYPE_I8 || t->kind == TYPE_U8 ||
- t->kind == TYPE_BYTE)
- {
- strcat(fmt, " %c"); // Space skip whitespace
- }
- else
- {
- strcat(fmt, "%d");
- }
- }
- if (i < ac - 1)
- {
- strcat(fmt, " ");
- }
- }
-
- node = ast_create(NODE_EXPR_CALL);
- ASTNode *callee = ast_create(NODE_EXPR_VAR);
- callee->var_ref.name = xstrdup("_z_scan_helper");
- node->call.callee = callee;
- node->type_info = type_new(TYPE_INT); // Returns count
-
- ASTNode *fmt_node = ast_create(NODE_EXPR_LITERAL);
- fmt_node->literal.type_kind = 2; // string
- fmt_node->literal.string_val = xstrdup(fmt);
-
- ASTNode *head = fmt_node, *tail = fmt_node;
-
- for (int i = 0; i < ac; i++)
- {
- // Create Unary & (AddressOf) node wrapping the arg
- ASTNode *addr = ast_create(NODE_EXPR_UNARY);
- addr->unary.op = xstrdup("&");
- addr->unary.operand = args[i];
- // Link
- tail->next = addr;
- tail = addr;
- }
- node->call.args = head;
- }
- free(acc);
+ }
+ if (lexer_peek(l).type == TOK_RBRACE) {
+ break;
+ }
+ Token fn = lexer_next(l);
+ if (lexer_next(l).type != TOK_COLON) {
+ zpanic_at(lexer_peek(l), "Expected :");
+ }
+ ASTNode *val = parse_expression(ctx, l);
+ ASTNode *assign = ast_create(NODE_VAR_DECL);
+ assign->var_decl.name = token_strdup(fn);
+ assign->var_decl.init_expr = val;
+ if (!head) {
+ head = assign;
+ } else {
+ tail->next = assign;
+ }
+ tail = assign;
+ first = 0;
}
- else if (sig && lexer_peek(l).type == TOK_LPAREN)
- {
+ lexer_next(l);
+ node->struct_init.fields = head;
+ Type *st = type_new(TYPE_STRUCT);
+ st->name = xstrdup(struct_name);
+ node->type_info = st;
+ return node; // Struct init cannot be called/indexed usually, return
+ // early
+ }
+ }
+
+ // E. readln(args...) Magic
+ FuncSig *sig = find_func(ctx, acc);
+ if (strcmp(acc, "readln") == 0 && lexer_peek(l).type == TOK_LPAREN) {
+ lexer_next(l); // eat (
+
+ // Parse args
+ ASTNode *args[16];
+ int ac = 0;
+ if (lexer_peek(l).type != TOK_RPAREN) {
+ while (1) {
+ args[ac++] = parse_expression(ctx, l);
+ if (lexer_peek(l).type == TOK_COMMA) {
lexer_next(l);
- ASTNode *head = NULL, *tail = NULL;
- int args_provided = 0;
- char **arg_names = NULL;
- int has_named = 0;
-
- if (lexer_peek(l).type != TOK_RPAREN)
- {
- while (1)
- {
- char *arg_name = NULL;
-
- Token t1 = lexer_peek(l);
- if (t1.type == TOK_IDENT)
- {
- Token t2 = lexer_peek2(l);
- if (t2.type == TOK_COLON)
- {
- arg_name = token_strdup(t1);
- has_named = 1;
- lexer_next(l);
- lexer_next(l);
- }
- }
-
- ASTNode *arg = parse_expression(ctx, l);
- if (!head)
- {
- head = arg;
- }
- else
- {
- tail->next = arg;
- }
- tail = arg;
- args_provided++;
-
- arg_names = xrealloc(arg_names, args_provided * sizeof(char *));
- arg_names[args_provided - 1] = arg_name;
-
- arg_names = xrealloc(arg_names, args_provided * sizeof(char *));
- arg_names[args_provided - 1] = arg_name;
-
- if (lexer_peek(l).type == TOK_COMMA)
- {
- lexer_next(l);
- }
- else
- {
- break;
- }
- }
- }
- if (lexer_next(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected )");
- }
- for (int i = args_provided; i < sig->total_args; i++)
- {
- if (sig->defaults[i])
- {
- ASTNode *def = ast_create(NODE_RAW_STMT);
- def->raw_stmt.content = xstrdup(sig->defaults[i]);
- if (!head)
- {
- head = def;
- }
- else
- {
- tail->next = def;
- }
- tail = def;
- }
- }
- node = ast_create(NODE_EXPR_CALL);
- node->token = t; // Set source token
- ASTNode *callee = ast_create(NODE_EXPR_VAR);
- callee->var_ref.name = acc;
- node->call.callee = callee;
- node->call.args = head;
- node->call.arg_names = has_named ? arg_names : NULL;
- node->call.arg_count = args_provided;
- if (sig)
- {
- node->definition_token = sig->decl_token;
- }
- if (sig->is_async)
- {
- Type *async_type = type_new(TYPE_STRUCT);
- async_type->name = xstrdup("Async");
- node->type_info = async_type;
- node->resolved_type = xstrdup("Async");
- }
- else if (sig->ret_type)
- {
- node->type_info = sig->ret_type;
- node->resolved_type = type_to_string(sig->ret_type);
- }
- else
- {
- node->resolved_type = xstrdup("void");
- }
- }
- else if (!sig && !find_symbol_entry(ctx, acc) && lexer_peek(l).type == TOK_LPAREN)
- {
- lexer_next(l); // eat (
- ASTNode *head = NULL, *tail = NULL;
- char **arg_names = NULL;
- int args_provided = 0;
- int has_named = 0;
-
- if (lexer_peek(l).type != TOK_RPAREN)
- {
- while (1)
- {
- char *arg_name = NULL;
-
- // Check for named argument: name: value
- Token t1 = lexer_peek(l);
- if (t1.type == TOK_IDENT)
- {
- Token t2 = lexer_peek2(l);
- if (t2.type == TOK_COLON)
- {
- arg_name = token_strdup(t1);
- has_named = 1;
- lexer_next(l);
- lexer_next(l);
- }
- }
-
- ASTNode *arg = parse_expression(ctx, l);
- if (!head)
- {
- head = arg;
- }
- else
- {
- tail->next = arg;
- }
- tail = arg;
- args_provided++;
-
- arg_names = xrealloc(arg_names, args_provided * sizeof(char *));
- arg_names[args_provided - 1] = arg_name;
-
- if (lexer_peek(l).type == TOK_COMMA)
- {
- lexer_next(l);
- }
- else
- {
- break;
- }
- }
- }
- if (lexer_next(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected )");
- }
-
- node = ast_create(NODE_EXPR_CALL);
- node->token = t;
- ASTNode *callee = ast_create(NODE_EXPR_VAR);
- callee->var_ref.name = acc;
- node->call.callee = callee;
- node->call.args = head;
- node->call.arg_names = has_named ? arg_names : NULL;
- node->call.arg_count = args_provided;
- // Unknown return type - let codegen infer it
- node->resolved_type = xstrdup("unknown");
- // Fall through to Postfix
- }
- else
- {
- node = ast_create(NODE_EXPR_VAR);
- node->token = t; // Set source token
- node->var_ref.name = acc;
- node->type_info = find_symbol_type_info(ctx, acc);
-
- Symbol *sym = find_symbol_entry(ctx, acc);
- if (sym)
- {
- sym->is_used = 1;
- node->definition_token = sym->decl_token;
- }
-
- char *type_str = find_symbol_type(ctx, acc);
-
- if (type_str)
- {
- node->resolved_type = type_str;
- node->var_ref.suggestion = NULL;
- }
- else
- {
- node->resolved_type = xstrdup("unknown");
- if (should_suppress_undef_warning(ctx, acc))
- {
- node->var_ref.suggestion = NULL;
- }
- else
- {
- node->var_ref.suggestion = find_similar_symbol(ctx, acc);
- }
- }
+ } else {
+ break;
+ }
}
- }
-
- else if (t.type == TOK_LPAREN)
- {
+ }
+ if (lexer_next(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected )");
+ }
- Lexer lookahead = *l;
- int is_lambda = 0;
- char **params = xmalloc(sizeof(char *) * 16);
- int nparams = 0;
-
- while (1)
- {
- if (lexer_peek(&lookahead).type != TOK_IDENT)
- {
- break;
- }
- params[nparams++] = token_strdup(lexer_next(&lookahead));
- Token sep = lexer_peek(&lookahead);
- if (sep.type == TOK_COMMA)
- {
- lexer_next(&lookahead);
- continue;
- }
- else if (sep.type == TOK_RPAREN)
- {
- lexer_next(&lookahead);
- if (lexer_peek(&lookahead).type == TOK_ARROW)
- {
- lexer_next(&lookahead);
- is_lambda = 1;
- }
- break;
- }
- else
- {
- break;
- }
+ if (ac == 0) {
+ // readln() -> _z_readln_raw()
+ node = ast_create(NODE_EXPR_CALL);
+ ASTNode *callee = ast_create(NODE_EXPR_VAR);
+ callee->var_ref.name = xstrdup("_z_readln_raw");
+ node->call.callee = callee;
+ node->type_info = type_new(TYPE_STRING);
+ } else {
+ // readln(vars...) -> _z_scan_helper("fmt", &vars...)
+ char fmt[256];
+ fmt[0] = 0;
+ for (int i = 0; i < ac; i++) {
+ Type *t = args[i]->type_info;
+ if (!t && args[i]->type == NODE_EXPR_VAR) {
+ t = find_symbol_type_info(ctx, args[i]->var_ref.name);
+ }
+
+ if (!t) {
+ strcat(fmt, "%d"); // Fallback
+ } else {
+ if (t->kind == TYPE_INT || t->kind == TYPE_I32 ||
+ t->kind == TYPE_BOOL) {
+ strcat(fmt, "%d");
+ } else if (t->kind == TYPE_F64) {
+ strcat(fmt, "%lf");
+ } else if (t->kind == TYPE_F32 || t->kind == TYPE_FLOAT) {
+ strcat(fmt, "%f");
+ } else if (t->kind == TYPE_STRING) {
+ strcat(fmt, "%s");
+ } else if (t->kind == TYPE_CHAR || t->kind == TYPE_I8 ||
+ t->kind == TYPE_U8 || t->kind == TYPE_BYTE) {
+ strcat(fmt, " %c"); // Space skip whitespace
+ } else {
+ strcat(fmt, "%d");
+ }
+ }
+ if (i < ac - 1) {
+ strcat(fmt, " ");
+ }
+ }
+
+ node = ast_create(NODE_EXPR_CALL);
+ ASTNode *callee = ast_create(NODE_EXPR_VAR);
+ callee->var_ref.name = xstrdup("_z_scan_helper");
+ node->call.callee = callee;
+ node->type_info = type_new(TYPE_INT); // Returns count
+
+ ASTNode *fmt_node = ast_create(NODE_EXPR_LITERAL);
+ fmt_node->literal.type_kind = 2; // string
+ fmt_node->literal.string_val = xstrdup(fmt);
+
+ ASTNode *head = fmt_node, *tail = fmt_node;
+
+ for (int i = 0; i < ac; i++) {
+ // Create Unary & (AddressOf) node wrapping the arg
+ ASTNode *addr = ast_create(NODE_EXPR_UNARY);
+ addr->unary.op = xstrdup("&");
+ addr->unary.operand = args[i];
+ // Link
+ tail->next = addr;
+ tail = addr;
+ }
+ node->call.args = head;
+ }
+ free(acc);
+ } else if (sig && lexer_peek(l).type == TOK_LPAREN) {
+ lexer_next(l);
+ ASTNode *head = NULL, *tail = NULL;
+ int args_provided = 0;
+ char **arg_names = NULL;
+ int has_named = 0;
+
+ if (lexer_peek(l).type != TOK_RPAREN) {
+ while (1) {
+ char *arg_name = NULL;
+
+ Token t1 = lexer_peek(l);
+ if (t1.type == TOK_IDENT) {
+ Token t2 = lexer_peek2(l);
+ if (t2.type == TOK_COLON) {
+ arg_name = token_strdup(t1);
+ has_named = 1;
+ lexer_next(l);
+ lexer_next(l);
+ }
+ }
+
+ ASTNode *arg = parse_expression(ctx, l);
+ if (!head) {
+ head = arg;
+ } else {
+ tail->next = arg;
+ }
+ tail = arg;
+ args_provided++;
+
+ arg_names = xrealloc(arg_names, args_provided * sizeof(char *));
+ arg_names[args_provided - 1] = arg_name;
+
+ arg_names = xrealloc(arg_names, args_provided * sizeof(char *));
+ arg_names[args_provided - 1] = arg_name;
+
+ if (lexer_peek(l).type == TOK_COMMA) {
+ lexer_next(l);
+ } else {
+ break;
+ }
+ }
+ }
+ if (lexer_next(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected )");
+ }
+ for (int i = args_provided; i < sig->total_args; i++) {
+ if (sig->defaults[i]) {
+ ASTNode *def = ast_create(NODE_RAW_STMT);
+ def->raw_stmt.content = xstrdup(sig->defaults[i]);
+ if (!head) {
+ head = def;
+ } else {
+ tail->next = def;
+ }
+ tail = def;
+ }
+ }
+ node = ast_create(NODE_EXPR_CALL);
+ node->token = t; // Set source token
+ ASTNode *callee = ast_create(NODE_EXPR_VAR);
+ callee->var_ref.name = acc;
+ node->call.callee = callee;
+ node->call.args = head;
+ node->call.arg_names = has_named ? arg_names : NULL;
+ node->call.arg_count = args_provided;
+ if (sig) {
+ node->definition_token = sig->decl_token;
+ }
+ if (sig->is_async) {
+ Type *async_type = type_new(TYPE_STRUCT);
+ async_type->name = xstrdup("Async");
+ node->type_info = async_type;
+ node->resolved_type = xstrdup("Async");
+ } else if (sig->ret_type) {
+ node->type_info = sig->ret_type;
+ node->resolved_type = type_to_string(sig->ret_type);
+ } else {
+ node->resolved_type = xstrdup("void");
+ }
+ } else if (!sig && !find_symbol_entry(ctx, acc) &&
+ lexer_peek(l).type == TOK_LPAREN) {
+ lexer_next(l); // eat (
+ ASTNode *head = NULL, *tail = NULL;
+ char **arg_names = NULL;
+ int args_provided = 0;
+ int has_named = 0;
+
+ if (lexer_peek(l).type != TOK_RPAREN) {
+ while (1) {
+ char *arg_name = NULL;
+
+ // Check for named argument: name: value
+ Token t1 = lexer_peek(l);
+ if (t1.type == TOK_IDENT) {
+ Token t2 = lexer_peek2(l);
+ if (t2.type == TOK_COLON) {
+ arg_name = token_strdup(t1);
+ has_named = 1;
+ lexer_next(l);
+ lexer_next(l);
+ }
+ }
+
+ ASTNode *arg = parse_expression(ctx, l);
+ if (!head) {
+ head = arg;
+ } else {
+ tail->next = arg;
+ }
+ tail = arg;
+ args_provided++;
+
+ arg_names = xrealloc(arg_names, args_provided * sizeof(char *));
+ arg_names[args_provided - 1] = arg_name;
+
+ if (lexer_peek(l).type == TOK_COMMA) {
+ lexer_next(l);
+ } else {
+ break;
+ }
+ }
+ }
+ if (lexer_next(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected )");
+ }
+
+ node = ast_create(NODE_EXPR_CALL);
+ node->token = t;
+ ASTNode *callee = ast_create(NODE_EXPR_VAR);
+ callee->var_ref.name = acc;
+ node->call.callee = callee;
+ node->call.args = head;
+ node->call.arg_names = has_named ? arg_names : NULL;
+ node->call.arg_count = args_provided;
+ // Unknown return type - let codegen infer it
+ node->resolved_type = xstrdup("unknown");
+ // Fall through to Postfix
+ } else {
+ node = ast_create(NODE_EXPR_VAR);
+ node->token = t; // Set source token
+ node->var_ref.name = acc;
+ node->type_info = find_symbol_type_info(ctx, acc);
+
+ Symbol *sym = find_symbol_entry(ctx, acc);
+ if (sym) {
+ sym->is_used = 1;
+ node->definition_token = sym->decl_token;
+ }
+
+ char *type_str = find_symbol_type(ctx, acc);
+
+ if (type_str) {
+ node->resolved_type = type_str;
+ node->var_ref.suggestion = NULL;
+ } else {
+ node->resolved_type = xstrdup("unknown");
+ if (should_suppress_undef_warning(ctx, acc)) {
+ node->var_ref.suggestion = NULL;
+ } else {
+ node->var_ref.suggestion = find_similar_symbol(ctx, acc);
+ }
+ }
+ }
+ }
+
+ else if (t.type == TOK_LPAREN) {
+
+ Lexer lookahead = *l;
+ int is_lambda = 0;
+ char **params = xmalloc(sizeof(char *) * 16);
+ int nparams = 0;
+
+ while (1) {
+ if (lexer_peek(&lookahead).type != TOK_IDENT) {
+ break;
+ }
+ params[nparams++] = token_strdup(lexer_next(&lookahead));
+ Token sep = lexer_peek(&lookahead);
+ if (sep.type == TOK_COMMA) {
+ lexer_next(&lookahead);
+ continue;
+ } else if (sep.type == TOK_RPAREN) {
+ lexer_next(&lookahead);
+ if (lexer_peek(&lookahead).type == TOK_ARROW) {
+ lexer_next(&lookahead);
+ is_lambda = 1;
}
-
- if (is_lambda && nparams > 0)
- {
- *l = lookahead; // Commit
- return parse_arrow_lambda_multi(ctx, l, params, nparams);
- }
-
- int saved = l->pos;
- if (lexer_peek(l).type == TOK_IDENT)
- {
- Lexer cast_look = *l;
- lexer_next(&cast_look); // eat ident
- while (lexer_peek(&cast_look).type == TOK_DCOLON)
- { // handle A::B
- lexer_next(&cast_look);
- if (lexer_peek(&cast_look).type == TOK_IDENT)
- {
- lexer_next(&cast_look);
- }
- else
- {
- break;
- }
- }
- while (lexer_peek(&cast_look).type == TOK_OP && is_token(lexer_peek(&cast_look), "*"))
- {
- lexer_next(&cast_look);
- }
-
- if (lexer_peek(&cast_look).type == TOK_RPAREN)
- {
- lexer_next(&cast_look); // eat )
- Token next = lexer_peek(&cast_look);
- // Heuristic: It's a cast if followed by literal, ident, paren, or &/*
- if (next.type == TOK_STRING || next.type == TOK_INT || next.type == TOK_FLOAT ||
- (next.type == TOK_OP &&
- (is_token(next, "&") || is_token(next, "*") || is_token(next, "!"))) ||
- next.type == TOK_IDENT || next.type == TOK_LPAREN)
- {
-
- Type *cast_type_obj = parse_type_formal(ctx, l);
- char *cast_type = type_to_string(cast_type_obj);
- {
- Token t = lexer_next(l);
- if (t.type != TOK_RPAREN)
- {
- zpanic_at(t, "Expected ) after cast");
- }
- }
- ASTNode *target = parse_expr_prec(ctx, l, PREC_UNARY);
-
- node = ast_create(NODE_EXPR_CAST);
- node->cast.target_type = cast_type;
- node->cast.expr = target;
- node->type_info = cast_type_obj;
- return node; // Casts are usually unary, handled here.
- }
+ break;
+ } else {
+ break;
+ }
+ }
+
+ if (is_lambda && nparams > 0) {
+ *l = lookahead; // Commit
+ return parse_arrow_lambda_multi(ctx, l, params, nparams);
+ }
+
+ int saved = l->pos;
+ if (lexer_peek(l).type == TOK_IDENT) {
+ Lexer cast_look = *l;
+ lexer_next(&cast_look); // eat ident
+ while (lexer_peek(&cast_look).type == TOK_DCOLON) { // handle A::B
+ lexer_next(&cast_look);
+ if (lexer_peek(&cast_look).type == TOK_IDENT) {
+ lexer_next(&cast_look);
+ } else {
+ break;
+ }
+ }
+ while (lexer_peek(&cast_look).type == TOK_OP &&
+ is_token(lexer_peek(&cast_look), "*")) {
+ lexer_next(&cast_look);
+ }
+
+ if (lexer_peek(&cast_look).type == TOK_RPAREN) {
+ lexer_next(&cast_look); // eat )
+ Token next = lexer_peek(&cast_look);
+ // Heuristic: It's a cast if followed by literal, ident, paren, or &/*
+ if (next.type == TOK_STRING || next.type == TOK_INT ||
+ next.type == TOK_FLOAT ||
+ (next.type == TOK_OP &&
+ (is_token(next, "&") || is_token(next, "*") ||
+ is_token(next, "!"))) ||
+ next.type == TOK_IDENT || next.type == TOK_LPAREN) {
+
+ Type *cast_type_obj = parse_type_formal(ctx, l);
+ char *cast_type = type_to_string(cast_type_obj);
+ {
+ Token t = lexer_next(l);
+ if (t.type != TOK_RPAREN) {
+ zpanic_at(t, "Expected ) after cast");
}
- }
- l->pos = saved; // Reset if not a cast
+ }
+ ASTNode *target = parse_expr_prec(ctx, l, PREC_UNARY);
- ASTNode *expr = parse_expression(ctx, l);
- if (lexer_next(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected )");
+ node = ast_create(NODE_EXPR_CAST);
+ node->cast.target_type = cast_type;
+ node->cast.expr = target;
+ node->type_info = cast_type_obj;
+ return node; // Casts are usually unary, handled here.
}
- node = expr;
+ }
}
+ l->pos = saved; // Reset if not a cast
- else if (t.type == TOK_LBRACKET)
- {
- ASTNode *head = NULL, *tail = NULL;
- int count = 0;
- while (lexer_peek(l).type != TOK_RBRACKET)
- {
- ASTNode *elem = parse_expression(ctx, l);
- count++;
- if (!head)
- {
- head = elem;
- tail = elem;
- }
- else
- {
- tail->next = elem;
- tail = elem;
- }
- if (lexer_peek(l).type == TOK_COMMA)
- {
- lexer_next(l);
- }
- else
- {
- break;
- }
- }
- if (lexer_next(l).type != TOK_RBRACKET)
- {
- zpanic_at(lexer_peek(l), "Expected ] after array literal");
- }
- node = ast_create(NODE_EXPR_ARRAY_LITERAL);
- node->array_literal.elements = head;
- node->array_literal.count = count;
- if (head && head->type_info)
- {
- Type *elem_type = head->type_info;
- Type *arr_type = type_new(TYPE_ARRAY);
- arr_type->inner = elem_type;
- arr_type->array_size = count;
- node->type_info = arr_type;
- }
+ ASTNode *expr = parse_expression(ctx, l);
+ if (lexer_next(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected )");
}
- else
- {
- zpanic_at(t, "Unexpected token in parse_primary: %.*s", t.len, t.start);
- }
-
- while (1)
- {
- if (lexer_peek(l).type == TOK_LPAREN)
- {
- Token op = lexer_next(l); // consume '('
- ASTNode *head = NULL, *tail = NULL;
- char **arg_names = NULL;
- int arg_count = 0;
- int has_named = 0;
-
- if (lexer_peek(l).type != TOK_RPAREN)
- {
- while (1)
- {
- char *arg_name = NULL;
-
- // Check for named argument: IDENT : expr
- Token t1 = lexer_peek(l);
- if (t1.type == TOK_IDENT)
- {
- Token t2 = lexer_peek2(l);
- if (t2.type == TOK_COLON)
- {
- arg_name = token_strdup(t1);
- has_named = 1;
- lexer_next(l); // eat IDENT
- lexer_next(l); // eat :
- }
- }
-
- ASTNode *arg = parse_expression(ctx, l);
- if (!head)
- {
- head = arg;
- }
- else
- {
- tail->next = arg;
- }
- tail = arg;
-
- arg_names = xrealloc(arg_names, (arg_count + 1) * sizeof(char *));
- arg_names[arg_count] = arg_name;
- arg_count++;
-
- if (lexer_peek(l).type == TOK_COMMA)
- {
- lexer_next(l);
- }
- else
- {
- break;
- }
- }
- }
- {
- Token t = lexer_next(l);
- if (t.type != TOK_RPAREN)
- {
- zpanic_at(t, "Expected ) after call arguments");
- }
- }
-
- ASTNode *call = ast_create(NODE_EXPR_CALL);
- call->call.callee = node;
- call->call.args = head;
- call->call.arg_names = has_named ? arg_names : NULL;
- call->call.arg_count = arg_count;
- check_format_string(call, op);
-
- // Try to infer type if callee has function type info
- call->resolved_type = xstrdup("unknown"); // Default (was int)
- if (node->type_info && node->type_info->kind == TYPE_FUNCTION && node->type_info->inner)
- {
- call->type_info = node->type_info->inner;
-
- // Update resolved_type based on real return
- // (Optional: type_to_string(call->type_info))
- }
- node = call;
- }
-
- else if (lexer_peek(l).type == TOK_LBRACKET)
- {
- Token bracket = lexer_next(l); // consume '['
- ASTNode *index = parse_expression(ctx, l);
- {
- Token t = lexer_next(l);
- if (t.type != TOK_RBRACKET)
- {
- zpanic_at(t, "Expected ] after index");
- }
- }
-
- // Static Array Bounds Check
- if (node->type_info && node->type_info->kind == TYPE_ARRAY &&
- node->type_info->array_size > 0)
- {
- if (index->type == NODE_EXPR_LITERAL && index->literal.type_kind == 0)
- {
- int idx = index->literal.int_val;
- if (idx < 0 || idx >= node->type_info->array_size)
- {
- warn_array_bounds(bracket, idx, node->type_info->array_size);
- }
- }
- }
-
- int overloaded_get = 0;
- if (node->type_info && node->type_info->kind != TYPE_ARRAY &&
- (node->type_info->kind == TYPE_STRUCT ||
- (node->type_info->kind == TYPE_POINTER && node->type_info->inner &&
- node->type_info->inner->kind == TYPE_STRUCT)))
- {
- Type *st = node->type_info;
- char *struct_name = (st->kind == TYPE_STRUCT) ? st->name : st->inner->name;
- int is_ptr = (st->kind == TYPE_POINTER);
-
- char mangled[256];
- sprintf(mangled, "%s_get", struct_name);
- FuncSig *sig = find_func(ctx, mangled);
- if (sig)
- {
- // Rewrite to Call: node.get(index)
- ASTNode *call = ast_create(NODE_EXPR_CALL);
- ASTNode *callee = ast_create(NODE_EXPR_VAR);
- callee->var_ref.name = xstrdup(mangled);
- call->call.callee = callee;
-
- // Arg 1: Self
- ASTNode *arg1 = node;
- if (sig->total_args > 0 && sig->arg_types[0]->kind == TYPE_POINTER && !is_ptr)
- {
- // Needs ptr, have value -> &node
- ASTNode *addr = ast_create(NODE_EXPR_UNARY);
- addr->unary.op = xstrdup("&");
- addr->unary.operand = node;
- addr->type_info = type_new_ptr(st);
- arg1 = addr;
- }
- else if (is_ptr && sig->arg_types[0]->kind != TYPE_POINTER)
- {
- // Needs value, have ptr -> *node
- ASTNode *deref = ast_create(NODE_EXPR_UNARY);
- deref->unary.op = xstrdup("*");
- deref->unary.operand = node;
- arg1 = deref;
- }
-
- // Arg 2: Index
- arg1->next = index;
- index->next = NULL;
- call->call.args = arg1;
-
- call->type_info = sig->ret_type;
- call->resolved_type = type_to_string(sig->ret_type);
-
- node = call;
- overloaded_get = 1;
- }
- }
+ node = expr;
+ }
- if (!overloaded_get)
- {
- ASTNode *idx_node = ast_create(NODE_EXPR_INDEX);
- idx_node->index.array = node;
- idx_node->index.index = index;
- idx_node->type_info = (node->type_info && node->type_info->inner)
- ? node->type_info->inner
- : type_new(TYPE_INT);
- node = idx_node;
- }
- }
-
- else
- {
+ else if (t.type == TOK_LBRACKET) {
+ ASTNode *head = NULL, *tail = NULL;
+ int count = 0;
+ while (lexer_peek(l).type != TOK_RBRACKET) {
+ ASTNode *elem = parse_expression(ctx, l);
+ count++;
+ if (!head) {
+ head = elem;
+ tail = elem;
+ } else {
+ tail->next = elem;
+ tail = elem;
+ }
+ if (lexer_peek(l).type == TOK_COMMA) {
+ lexer_next(l);
+ } else {
+ break;
+ }
+ }
+ if (lexer_next(l).type != TOK_RBRACKET) {
+ zpanic_at(lexer_peek(l), "Expected ] after array literal");
+ }
+ node = ast_create(NODE_EXPR_ARRAY_LITERAL);
+ node->array_literal.elements = head;
+ node->array_literal.count = count;
+ if (head && head->type_info) {
+ Type *elem_type = head->type_info;
+ Type *arr_type = type_new(TYPE_ARRAY);
+ arr_type->inner = elem_type;
+ arr_type->array_size = count;
+ node->type_info = arr_type;
+ }
+ } else {
+ zpanic_at(t, "Unexpected token in parse_primary: %.*s", t.len, t.start);
+ }
+
+ while (1) {
+ if (lexer_peek(l).type == TOK_LPAREN) {
+ Token op = lexer_next(l); // consume '('
+ ASTNode *head = NULL, *tail = NULL;
+ char **arg_names = NULL;
+ int arg_count = 0;
+ int has_named = 0;
+
+ if (lexer_peek(l).type != TOK_RPAREN) {
+ while (1) {
+ char *arg_name = NULL;
+
+ // Check for named argument: IDENT : expr
+ Token t1 = lexer_peek(l);
+ if (t1.type == TOK_IDENT) {
+ Token t2 = lexer_peek2(l);
+ if (t2.type == TOK_COLON) {
+ arg_name = token_strdup(t1);
+ has_named = 1;
+ lexer_next(l); // eat IDENT
+ lexer_next(l); // eat :
+ }
+ }
+
+ ASTNode *arg = parse_expression(ctx, l);
+ if (!head) {
+ head = arg;
+ } else {
+ tail->next = arg;
+ }
+ tail = arg;
+
+ arg_names = xrealloc(arg_names, (arg_count + 1) * sizeof(char *));
+ arg_names[arg_count] = arg_name;
+ arg_count++;
+
+ if (lexer_peek(l).type == TOK_COMMA) {
+ lexer_next(l);
+ } else {
break;
- }
- }
-
- return node;
+ }
+ }
+ }
+ {
+ Token t = lexer_next(l);
+ if (t.type != TOK_RPAREN) {
+ zpanic_at(t, "Expected ) after call arguments");
+ }
+ }
+
+ ASTNode *call = ast_create(NODE_EXPR_CALL);
+ call->call.callee = node;
+ call->call.args = head;
+ call->call.arg_names = has_named ? arg_names : NULL;
+ call->call.arg_count = arg_count;
+ check_format_string(call, op);
+
+ // Try to infer type if callee has function type info
+ call->resolved_type = xstrdup("unknown"); // Default (was int)
+ if (node->type_info && node->type_info->kind == TYPE_FUNCTION &&
+ node->type_info->inner) {
+ call->type_info = node->type_info->inner;
+
+ // Update resolved_type based on real return
+ // (Optional: type_to_string(call->type_info))
+ }
+ node = call;
+ }
+
+ else if (lexer_peek(l).type == TOK_LBRACKET) {
+ Token bracket = lexer_next(l); // consume '['
+ ASTNode *index = parse_expression(ctx, l);
+ {
+ Token t = lexer_next(l);
+ if (t.type != TOK_RBRACKET) {
+ zpanic_at(t, "Expected ] after index");
+ }
+ }
+
+ // Static Array Bounds Check
+ if (node->type_info && node->type_info->kind == TYPE_ARRAY &&
+ node->type_info->array_size > 0) {
+ if (index->type == NODE_EXPR_LITERAL && index->literal.type_kind == 0) {
+ int idx = index->literal.int_val;
+ if (idx < 0 || idx >= node->type_info->array_size) {
+ warn_array_bounds(bracket, idx, node->type_info->array_size);
+ }
+ }
+ }
+
+ int overloaded_get = 0;
+ if (node->type_info && node->type_info->kind != TYPE_ARRAY &&
+ (node->type_info->kind == TYPE_STRUCT ||
+ (node->type_info->kind == TYPE_POINTER && node->type_info->inner &&
+ node->type_info->inner->kind == TYPE_STRUCT))) {
+ Type *st = node->type_info;
+ char *struct_name =
+ (st->kind == TYPE_STRUCT) ? st->name : st->inner->name;
+ int is_ptr = (st->kind == TYPE_POINTER);
+
+ char mangled[256];
+ sprintf(mangled, "%s_get", struct_name);
+ FuncSig *sig = find_func(ctx, mangled);
+ if (sig) {
+ // Rewrite to Call: node.get(index)
+ ASTNode *call = ast_create(NODE_EXPR_CALL);
+ ASTNode *callee = ast_create(NODE_EXPR_VAR);
+ callee->var_ref.name = xstrdup(mangled);
+ call->call.callee = callee;
+
+ // Arg 1: Self
+ ASTNode *arg1 = node;
+ if (sig->total_args > 0 && sig->arg_types[0]->kind == TYPE_POINTER &&
+ !is_ptr) {
+ // Needs ptr, have value -> &node
+ ASTNode *addr = ast_create(NODE_EXPR_UNARY);
+ addr->unary.op = xstrdup("&");
+ addr->unary.operand = node;
+ addr->type_info = type_new_ptr(st);
+ arg1 = addr;
+ } else if (is_ptr && sig->arg_types[0]->kind != TYPE_POINTER) {
+ // Needs value, have ptr -> *node
+ ASTNode *deref = ast_create(NODE_EXPR_UNARY);
+ deref->unary.op = xstrdup("*");
+ deref->unary.operand = node;
+ arg1 = deref;
+ }
+
+ // Arg 2: Index
+ arg1->next = index;
+ index->next = NULL;
+ call->call.args = arg1;
+
+ call->type_info = sig->ret_type;
+ call->resolved_type = type_to_string(sig->ret_type);
+
+ node = call;
+ overloaded_get = 1;
+ }
+ }
+
+ if (!overloaded_get) {
+ ASTNode *idx_node = ast_create(NODE_EXPR_INDEX);
+ idx_node->index.array = node;
+ idx_node->index.index = index;
+ idx_node->type_info = (node->type_info && node->type_info->inner)
+ ? node->type_info->inner
+ : type_new(TYPE_INT);
+ node = idx_node;
+ }
+ }
+
+ else {
+ break;
+ }
+ }
+
+ return node;
}
-int is_comparison_op(const char *op)
-{
- return (strcmp(op, "==") == 0 || strcmp(op, "!=") == 0 || strcmp(op, "<") == 0 ||
- strcmp(op, ">") == 0 || strcmp(op, "<=") == 0 || strcmp(op, ">=") == 0);
+int is_comparison_op(const char *op) {
+ return (strcmp(op, "==") == 0 || strcmp(op, "!=") == 0 ||
+ strcmp(op, "<") == 0 || strcmp(op, ">") == 0 ||
+ strcmp(op, "<=") == 0 || strcmp(op, ">=") == 0);
}
-Type *get_field_type(ParserContext *ctx, Type *struct_type, const char *field_name)
-{
- if (!struct_type)
- {
- return NULL;
- }
-
- // Built-in Fields for Arrays/Slices
- if (struct_type->kind == TYPE_ARRAY)
- {
- if (strcmp(field_name, "len") == 0)
- {
- return type_new(TYPE_INT);
- }
- if (struct_type->array_size == 0)
- { // Slice
- if (strcmp(field_name, "cap") == 0)
- {
- return type_new(TYPE_INT);
- }
- if (strcmp(field_name, "data") == 0)
- {
- return type_new_ptr(struct_type->inner);
- }
- }
- }
-
- char *sname = struct_type->name;
- // Handle Pointers (User* -> User)
- if (struct_type->kind == TYPE_POINTER && struct_type->inner)
- {
- sname = struct_type->inner->name;
- }
- if (!sname)
- {
- return NULL;
- }
-
- ASTNode *def = find_struct_def(ctx, sname);
- if (!def)
- {
- return NULL;
- }
-
- ASTNode *f = def->strct.fields;
- while (f)
- {
- if (strcmp(f->field.name, field_name) == 0)
- {
- return f->type_info;
- }
- f = f->next;
- }
+Type *get_field_type(ParserContext *ctx, Type *struct_type,
+ const char *field_name) {
+ if (!struct_type) {
return NULL;
-}
-
-const char *get_operator_method(const char *op)
-{
- // Arithmetic
- if (strcmp(op, "+") == 0)
- {
- return "add";
- }
- if (strcmp(op, "-") == 0)
- {
- return "sub";
- }
- if (strcmp(op, "*") == 0)
- {
- return "mul";
- }
- if (strcmp(op, "/") == 0)
- {
- return "div";
- }
- if (strcmp(op, "%") == 0)
- {
- return "rem";
- }
-
- // Comparison
- if (strcmp(op, "==") == 0)
- {
- return "eq";
- }
- if (strcmp(op, "!=") == 0)
- {
- return "neq";
- }
- if (strcmp(op, "<") == 0)
- {
- return "lt";
- }
- if (strcmp(op, ">") == 0)
- {
- return "gt";
- }
- if (strcmp(op, "<=") == 0)
- {
- return "le";
- }
- if (strcmp(op, ">=") == 0)
- {
- return "ge";
- }
-
- // --- NEW: Bitwise ---
- if (strcmp(op, "&") == 0)
- {
- return "bitand";
- }
- if (strcmp(op, "|") == 0)
- {
- return "bitor";
- }
- if (strcmp(op, "^") == 0)
- {
- return "bitxor";
- }
- if (strcmp(op, "<<") == 0)
- {
- return "shl";
- }
- if (strcmp(op, ">>") == 0)
- {
- return "shr";
- }
-
+ }
+
+ // Built-in Fields for Arrays/Slices
+ if (struct_type->kind == TYPE_ARRAY) {
+ if (strcmp(field_name, "len") == 0) {
+ return type_new(TYPE_INT);
+ }
+ if (struct_type->array_size == 0) { // Slice
+ if (strcmp(field_name, "cap") == 0) {
+ return type_new(TYPE_INT);
+ }
+ if (strcmp(field_name, "data") == 0) {
+ return type_new_ptr(struct_type->inner);
+ }
+ }
+ }
+
+ char *sname = struct_type->name;
+ // Handle Pointers (User* -> User)
+ if (struct_type->kind == TYPE_POINTER && struct_type->inner) {
+ sname = struct_type->inner->name;
+ }
+ if (!sname) {
return NULL;
-}
-
-ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
-{
- Token t = lexer_peek(l);
- ASTNode *lhs = NULL;
-
- if (t.type == TOK_QUESTION)
- {
- Lexer lookahead = *l;
- lexer_next(&lookahead);
- Token next = lexer_peek(&lookahead);
-
- if (next.type == TOK_STRING || next.type == TOK_FSTRING)
- {
- lexer_next(l); // consume '?'
- Token t_str = lexer_next(l);
-
- char *inner = xmalloc(t_str.len);
- if (t_str.type == TOK_FSTRING)
- {
- strncpy(inner, t_str.start + 2, t_str.len - 3);
- inner[t_str.len - 3] = 0;
- }
- else
- {
- strncpy(inner, t_str.start + 1, t_str.len - 2);
- inner[t_str.len - 2] = 0;
- }
-
- // Reuse printf sugar to generate the prompt print
- char *print_code = process_printf_sugar(ctx, inner, 0, "stdout", NULL, NULL);
- free(inner);
-
- // Checks for (args...) suffix for SCAN mode
- if (lexer_peek(l).type == TOK_LPAREN)
- {
- lexer_next(l); // consume (
-
- // Parse args
- ASTNode *args[16];
- int ac = 0;
- if (lexer_peek(l).type != TOK_RPAREN)
- {
- while (1)
- {
- args[ac++] = parse_expression(ctx, l);
- if (lexer_peek(l).type == TOK_COMMA)
- {
- lexer_next(l);
- }
- else
- {
- break;
- }
- }
- }
- if (lexer_next(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected )");
- }
-
- char fmt[256];
- fmt[0] = 0;
- for (int i = 0; i < ac; i++)
- {
- Type *t = args[i]->type_info;
- if (!t && args[i]->type == NODE_EXPR_VAR)
- {
- t = find_symbol_type_info(ctx, args[i]->var_ref.name);
- }
-
- if (!t)
- {
- strcat(fmt, "%d");
- }
- else
- {
- if (t->kind == TYPE_INT || t->kind == TYPE_I32 || t->kind == TYPE_BOOL)
- {
- strcat(fmt, "%d");
- }
- else if (t->kind == TYPE_F64)
- {
- strcat(fmt, "%lf");
- }
- else if (t->kind == TYPE_F32 || t->kind == TYPE_FLOAT)
- {
- strcat(fmt, "%f");
- }
- else if (t->kind == TYPE_STRING)
- {
- strcat(fmt, "%s");
- }
- else if (t->kind == TYPE_CHAR || t->kind == TYPE_I8 || t->kind == TYPE_U8 ||
- t->kind == TYPE_BYTE)
- {
- strcat(fmt, " %c");
- }
- else
- {
- strcat(fmt, "%d");
- }
- }
- if (i < ac - 1)
- {
- strcat(fmt, " ");
- }
- }
-
- ASTNode *block = ast_create(NODE_BLOCK);
-
- ASTNode *s1 = ast_create(NODE_RAW_STMT);
- // Append semicolon to ensure it's a valid statement
- char *s1_code = xmalloc(strlen(print_code) + 2);
- sprintf(s1_code, "%s;", print_code);
- s1->raw_stmt.content = s1_code;
- free(print_code);
-
- ASTNode *call = ast_create(NODE_EXPR_CALL);
- ASTNode *callee = ast_create(NODE_EXPR_VAR);
- callee->var_ref.name = xstrdup("_z_scan_helper");
- call->call.callee = callee;
- call->type_info = type_new(TYPE_INT);
-
- ASTNode *fmt_node = ast_create(NODE_EXPR_LITERAL);
- fmt_node->literal.type_kind = TOK_STRING;
- fmt_node->literal.string_val = xstrdup(fmt);
- ASTNode *head = fmt_node, *tail = fmt_node;
-
- for (int i = 0; i < ac; i++)
- {
- ASTNode *addr = ast_create(NODE_EXPR_UNARY);
- addr->unary.op = xstrdup("&");
- addr->unary.operand = args[i];
- tail->next = addr;
- tail = addr;
- }
- call->call.args = head;
-
- // Link Statements
- s1->next = call;
- block->block.statements = s1;
-
- return block;
- }
- else
- {
- // String Mode (Original)
- size_t len = strlen(print_code);
- if (len > 5)
- {
- print_code[len - 5] = 0; // Strip "0; })"
- }
-
- char *final_code = xmalloc(strlen(print_code) + 64);
- sprintf(final_code, "%s readln(); })", print_code);
- free(print_code);
-
- ASTNode *n = ast_create(NODE_RAW_STMT);
- n->raw_stmt.content = final_code;
- return n;
- }
- }
- }
- if (t.type == TOK_OP && is_token(t, "!"))
- {
- Lexer lookahead = *l;
- lexer_next(&lookahead);
- Token next = lexer_peek(&lookahead);
-
- if (next.type == TOK_STRING || next.type == TOK_FSTRING)
- {
- lexer_next(l); // consume '!'
- Token t_str = lexer_next(l);
-
- char *inner = xmalloc(t_str.len);
- if (t_str.type == TOK_FSTRING)
- {
- strncpy(inner, t_str.start + 2, t_str.len - 3);
- inner[t_str.len - 3] = 0;
- }
- else
- {
- strncpy(inner, t_str.start + 1, t_str.len - 2);
- inner[t_str.len - 2] = 0;
- }
-
- // Check for .. suffix (.. suppresses newline)
- int newline = 1;
- if (lexer_peek(l).type == TOK_DOTDOT)
- {
- lexer_next(l); // consume ..
- newline = 0;
- }
-
- char *code = process_printf_sugar(ctx, inner, newline, "stderr", NULL, NULL);
- free(inner);
-
- ASTNode *n = ast_create(NODE_RAW_STMT);
- n->raw_stmt.content = code;
- return n;
- }
- }
+ }
- if (t.type == TOK_AWAIT)
- {
- lexer_next(l); // consume await
- ASTNode *operand = parse_expr_prec(ctx, l, PREC_UNARY);
-
- lhs = ast_create(NODE_AWAIT);
- lhs->unary.operand = operand;
- // Type inference: await Async<T> yields T
- // If operand is a call to an async function, look up its ret_type (not
- // Async)
- if (operand->type == NODE_EXPR_CALL && operand->call.callee->type == NODE_EXPR_VAR)
- {
- FuncSig *sig = find_func(ctx, operand->call.callee->var_ref.name);
- if (sig && sig->is_async && sig->ret_type)
- {
- lhs->type_info = sig->ret_type;
- lhs->resolved_type = type_to_string(sig->ret_type);
- }
- else if (sig && !sig->is_async)
- {
- // Not an async function - shouldn't await it
- lhs->type_info = type_new(TYPE_VOID);
- lhs->resolved_type = xstrdup("void");
- }
- else
- {
- lhs->type_info = type_new_ptr(type_new(TYPE_VOID));
- lhs->resolved_type = xstrdup("void*");
- }
- }
- else
- {
- // Awaiting a variable - harder to determine underlying type
- // Fallback to void* for now (could be improved with metadata)
- lhs->type_info = type_new_ptr(type_new(TYPE_VOID));
- lhs->resolved_type = xstrdup("void*");
- }
+ ASTNode *def = find_struct_def(ctx, sname);
+ if (!def) {
+ return NULL;
+ }
- goto after_unary;
+ ASTNode *f = def->strct.fields;
+ while (f) {
+ if (strcmp(f->field.name, field_name) == 0) {
+ return f->type_info;
}
+ f = f->next;
+ }
+ return NULL;
+}
- if (t.type == TOK_OP &&
- (is_token(t, "-") || is_token(t, "!") || is_token(t, "*") || is_token(t, "&") ||
- is_token(t, "~") || is_token(t, "&&") || is_token(t, "++") || is_token(t, "--")))
- {
- lexer_next(l); // consume op
- ASTNode *operand = parse_expr_prec(ctx, l, PREC_UNARY);
-
- char *method = NULL;
- if (is_token(t, "-"))
- {
- method = "neg";
- }
- if (is_token(t, "!"))
- {
- method = "not";
- }
- if (is_token(t, "~"))
- {
- method = "bitnot";
- }
-
- if (method && operand->type_info)
- {
- Type *ot = operand->type_info;
- char *struct_name = NULL;
- int is_ptr = 0;
- // Unwrap pointer if needed (Struct* -> Struct) to find the method
- if (ot->kind == TYPE_STRUCT)
- {
- struct_name = ot->name;
- is_ptr = 0;
- }
- else if (ot->kind == TYPE_POINTER && ot->inner->kind == TYPE_STRUCT)
- {
- struct_name = ot->inner->name;
- is_ptr = 1;
- }
+const char *get_operator_method(const char *op) {
+ // Arithmetic
+ if (strcmp(op, "+") == 0) {
+ return "add";
+ }
+ if (strcmp(op, "-") == 0) {
+ return "sub";
+ }
+ if (strcmp(op, "*") == 0) {
+ return "mul";
+ }
+ if (strcmp(op, "/") == 0) {
+ return "div";
+ }
+ if (strcmp(op, "%") == 0) {
+ return "rem";
+ }
+
+ // Comparison
+ if (strcmp(op, "==") == 0) {
+ return "eq";
+ }
+ if (strcmp(op, "!=") == 0) {
+ return "neq";
+ }
+ if (strcmp(op, "<") == 0) {
+ return "lt";
+ }
+ if (strcmp(op, ">") == 0) {
+ return "gt";
+ }
+ if (strcmp(op, "<=") == 0) {
+ return "le";
+ }
+ if (strcmp(op, ">=") == 0) {
+ return "ge";
+ }
+
+ // --- NEW: Bitwise ---
+ if (strcmp(op, "&") == 0) {
+ return "bitand";
+ }
+ if (strcmp(op, "|") == 0) {
+ return "bitor";
+ }
+ if (strcmp(op, "^") == 0) {
+ return "bitxor";
+ }
+ if (strcmp(op, "<<") == 0) {
+ return "shl";
+ }
+ if (strcmp(op, ">>") == 0) {
+ return "shr";
+ }
+
+ return NULL;
+}
- if (struct_name)
- {
- char mangled[256];
- sprintf(mangled, "%s_%s", struct_name, method);
-
- if (find_func(ctx, mangled))
- {
- // Rewrite: ~x -> Struct_bitnot(x)
- ASTNode *call = ast_create(NODE_EXPR_CALL);
- ASTNode *callee = ast_create(NODE_EXPR_VAR);
- callee->var_ref.name = xstrdup(mangled);
- call->call.callee = callee;
-
- // Handle 'self' argument adjustment (Pointer vs Value)
- ASTNode *arg = operand;
- FuncSig *sig = find_func(ctx, mangled);
-
- if (sig->total_args > 0 && sig->arg_types[0]->kind == TYPE_POINTER && !is_ptr)
- {
- int is_rvalue =
- (operand->type == NODE_EXPR_CALL || operand->type == NODE_EXPR_BINARY ||
+ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) {
+ Token t = lexer_peek(l);
+ ASTNode *lhs = NULL;
+
+ if (t.type == TOK_QUESTION) {
+ Lexer lookahead = *l;
+ lexer_next(&lookahead);
+ Token next = lexer_peek(&lookahead);
+
+ if (next.type == TOK_STRING || next.type == TOK_FSTRING) {
+ lexer_next(l); // consume '?'
+ Token t_str = lexer_next(l);
+
+ char *inner = xmalloc(t_str.len);
+ if (t_str.type == TOK_FSTRING) {
+ strncpy(inner, t_str.start + 2, t_str.len - 3);
+ inner[t_str.len - 3] = 0;
+ } else {
+ strncpy(inner, t_str.start + 1, t_str.len - 2);
+ inner[t_str.len - 2] = 0;
+ }
+
+ // Reuse printf sugar to generate the prompt print
+ char *print_code =
+ process_printf_sugar(ctx, inner, 0, "stdout", NULL, NULL);
+ free(inner);
+
+ // Checks for (args...) suffix for SCAN mode
+ if (lexer_peek(l).type == TOK_LPAREN) {
+ lexer_next(l); // consume (
+
+ // Parse args
+ ASTNode *args[16];
+ int ac = 0;
+ if (lexer_peek(l).type != TOK_RPAREN) {
+ while (1) {
+ args[ac++] = parse_expression(ctx, l);
+ if (lexer_peek(l).type == TOK_COMMA) {
+ lexer_next(l);
+ } else {
+ break;
+ }
+ }
+ }
+ if (lexer_next(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected )");
+ }
+
+ char fmt[256];
+ fmt[0] = 0;
+ for (int i = 0; i < ac; i++) {
+ Type *t = args[i]->type_info;
+ if (!t && args[i]->type == NODE_EXPR_VAR) {
+ t = find_symbol_type_info(ctx, args[i]->var_ref.name);
+ }
+
+ if (!t) {
+ strcat(fmt, "%d");
+ } else {
+ if (t->kind == TYPE_INT || t->kind == TYPE_I32 ||
+ t->kind == TYPE_BOOL) {
+ strcat(fmt, "%d");
+ } else if (t->kind == TYPE_F64) {
+ strcat(fmt, "%lf");
+ } else if (t->kind == TYPE_F32 || t->kind == TYPE_FLOAT) {
+ strcat(fmt, "%f");
+ } else if (t->kind == TYPE_STRING) {
+ strcat(fmt, "%s");
+ } else if (t->kind == TYPE_CHAR || t->kind == TYPE_I8 ||
+ t->kind == TYPE_U8 || t->kind == TYPE_BYTE) {
+ strcat(fmt, " %c");
+ } else {
+ strcat(fmt, "%d");
+ }
+ }
+ if (i < ac - 1) {
+ strcat(fmt, " ");
+ }
+ }
+
+ ASTNode *block = ast_create(NODE_BLOCK);
+
+ ASTNode *s1 = ast_create(NODE_RAW_STMT);
+ // Append semicolon to ensure it's a valid statement
+ char *s1_code = xmalloc(strlen(print_code) + 2);
+ sprintf(s1_code, "%s;", print_code);
+ s1->raw_stmt.content = s1_code;
+ free(print_code);
+
+ ASTNode *call = ast_create(NODE_EXPR_CALL);
+ ASTNode *callee = ast_create(NODE_EXPR_VAR);
+ callee->var_ref.name = xstrdup("_z_scan_helper");
+ call->call.callee = callee;
+ call->type_info = type_new(TYPE_INT);
+
+ ASTNode *fmt_node = ast_create(NODE_EXPR_LITERAL);
+ fmt_node->literal.type_kind = TOK_STRING;
+ fmt_node->literal.string_val = xstrdup(fmt);
+ ASTNode *head = fmt_node, *tail = fmt_node;
+
+ for (int i = 0; i < ac; i++) {
+ ASTNode *addr = ast_create(NODE_EXPR_UNARY);
+ addr->unary.op = xstrdup("&");
+ addr->unary.operand = args[i];
+ tail->next = addr;
+ tail = addr;
+ }
+ call->call.args = head;
+
+ // Link Statements
+ s1->next = call;
+ block->block.statements = s1;
+
+ return block;
+ } else {
+ // String Mode (Original)
+ size_t len = strlen(print_code);
+ if (len > 5) {
+ print_code[len - 5] = 0; // Strip "0; })"
+ }
+
+ char *final_code = xmalloc(strlen(print_code) + 64);
+ sprintf(final_code, "%s readln(); })", print_code);
+ free(print_code);
+
+ ASTNode *n = ast_create(NODE_RAW_STMT);
+ n->raw_stmt.content = final_code;
+ return n;
+ }
+ }
+ }
+ if (t.type == TOK_OP && is_token(t, "!")) {
+ Lexer lookahead = *l;
+ lexer_next(&lookahead);
+ Token next = lexer_peek(&lookahead);
+
+ if (next.type == TOK_STRING || next.type == TOK_FSTRING) {
+ lexer_next(l); // consume '!'
+ Token t_str = lexer_next(l);
+
+ char *inner = xmalloc(t_str.len);
+ if (t_str.type == TOK_FSTRING) {
+ strncpy(inner, t_str.start + 2, t_str.len - 3);
+ inner[t_str.len - 3] = 0;
+ } else {
+ strncpy(inner, t_str.start + 1, t_str.len - 2);
+ inner[t_str.len - 2] = 0;
+ }
+
+ // Check for .. suffix (.. suppresses newline)
+ int newline = 1;
+ if (lexer_peek(l).type == TOK_DOTDOT) {
+ lexer_next(l); // consume ..
+ newline = 0;
+ }
+
+ char *code =
+ process_printf_sugar(ctx, inner, newline, "stderr", NULL, NULL);
+ free(inner);
+
+ ASTNode *n = ast_create(NODE_RAW_STMT);
+ n->raw_stmt.content = code;
+ return n;
+ }
+ }
+
+ if (t.type == TOK_AWAIT) {
+ lexer_next(l); // consume await
+ ASTNode *operand = parse_expr_prec(ctx, l, PREC_UNARY);
+
+ lhs = ast_create(NODE_AWAIT);
+ lhs->unary.operand = operand;
+ // Type inference: await Async<T> yields T
+ // If operand is a call to an async function, look up its ret_type (not
+ // Async)
+ if (operand->type == NODE_EXPR_CALL &&
+ operand->call.callee->type == NODE_EXPR_VAR) {
+ FuncSig *sig = find_func(ctx, operand->call.callee->var_ref.name);
+ if (sig && sig->is_async && sig->ret_type) {
+ lhs->type_info = sig->ret_type;
+ lhs->resolved_type = type_to_string(sig->ret_type);
+ } else if (sig && !sig->is_async) {
+ // Not an async function - shouldn't await it
+ lhs->type_info = type_new(TYPE_VOID);
+ lhs->resolved_type = xstrdup("void");
+ } else {
+ lhs->type_info = type_new_ptr(type_new(TYPE_VOID));
+ lhs->resolved_type = xstrdup("void*");
+ }
+ } else {
+ // Awaiting a variable - harder to determine underlying type
+ // Fallback to void* for now (could be improved with metadata)
+ lhs->type_info = type_new_ptr(type_new(TYPE_VOID));
+ lhs->resolved_type = xstrdup("void*");
+ }
+
+ goto after_unary;
+ }
+
+ if (t.type == TOK_OP &&
+ (is_token(t, "-") || is_token(t, "!") || is_token(t, "*") ||
+ is_token(t, "&") || is_token(t, "~") || is_token(t, "&&") ||
+ is_token(t, "++") || is_token(t, "--"))) {
+ lexer_next(l); // consume op
+ ASTNode *operand = parse_expr_prec(ctx, l, PREC_UNARY);
+
+ char *method = NULL;
+ if (is_token(t, "-")) {
+ method = "neg";
+ }
+ if (is_token(t, "!")) {
+ method = "not";
+ }
+ if (is_token(t, "~")) {
+ method = "bitnot";
+ }
+
+ if (method && operand->type_info) {
+ Type *ot = operand->type_info;
+ char *struct_name = NULL;
+ int is_ptr = 0;
+ // Unwrap pointer if needed (Struct* -> Struct) to find the method
+ if (ot->kind == TYPE_STRUCT) {
+ struct_name = ot->name;
+ is_ptr = 0;
+ } else if (ot->kind == TYPE_POINTER && ot->inner->kind == TYPE_STRUCT) {
+ struct_name = ot->inner->name;
+ is_ptr = 1;
+ }
+
+ if (struct_name) {
+ char mangled[256];
+ sprintf(mangled, "%s_%s", struct_name, method);
+
+ if (find_func(ctx, mangled)) {
+ // Rewrite: ~x -> Struct_bitnot(x)
+ ASTNode *call = ast_create(NODE_EXPR_CALL);
+ ASTNode *callee = ast_create(NODE_EXPR_VAR);
+ callee->var_ref.name = xstrdup(mangled);
+ call->call.callee = callee;
+
+ // Handle 'self' argument adjustment (Pointer vs Value)
+ ASTNode *arg = operand;
+ FuncSig *sig = find_func(ctx, mangled);
+
+ if (sig->total_args > 0 && sig->arg_types[0]->kind == TYPE_POINTER &&
+ !is_ptr) {
+ int is_rvalue = (operand->type == NODE_EXPR_CALL ||
+ operand->type == NODE_EXPR_BINARY ||
operand->type == NODE_MATCH);
- ASTNode *addr = ast_create(NODE_EXPR_UNARY);
- addr->unary.op = is_rvalue ? xstrdup("&_rval") : xstrdup("&");
- addr->unary.operand = operand;
- addr->type_info = type_new_ptr(ot);
- arg = addr;
- }
- else if (is_ptr && sig->arg_types[0]->kind != TYPE_POINTER)
- {
- // Function wants Value, we have Pointer -> Dereference (*)
- ASTNode *deref = ast_create(NODE_EXPR_UNARY);
- deref->unary.op = xstrdup("*");
- deref->unary.operand = operand;
- deref->type_info = ot->inner;
- arg = deref;
- }
-
- call->call.args = arg;
- call->type_info = sig->ret_type;
- call->resolved_type = type_to_string(sig->ret_type);
- lhs = call;
-
- // Skip standard unary node creation
- goto after_unary;
- }
- }
- }
-
- // Standard Unary Node (for primitives or if no overload found)
- lhs = ast_create(NODE_EXPR_UNARY);
- lhs->unary.op = token_strdup(t);
- lhs->unary.operand = operand;
-
- if (operand->type_info)
- {
- if (is_token(t, "&"))
- {
- lhs->type_info = type_new_ptr(operand->type_info);
- }
- else if (is_token(t, "*"))
- {
- if (operand->type_info->kind == TYPE_POINTER)
- {
- lhs->type_info = operand->type_info->inner;
- }
- }
- else
- {
- lhs->type_info = operand->type_info;
- }
- }
-
- after_unary:; // Label to skip standard creation if overloaded
- }
-
- else if (is_token(t, "sizeof"))
- {
- lexer_next(l);
- if (lexer_peek(l).type == TOK_LPAREN)
- {
- const char *start = l->src + l->pos;
- int depth = 0;
- while (1)
- {
- Token tk = lexer_peek(l);
- if (tk.type == TOK_EOF)
- {
- zpanic_at(tk, "Unterminated sizeof");
- }
- if (tk.type == TOK_LPAREN)
- {
- depth++;
- }
- if (tk.type == TOK_RPAREN)
- {
- depth--;
- if (depth == 0)
- {
- lexer_next(l);
- break;
- }
- }
- lexer_next(l);
- }
- int len = (l->src + l->pos) - start;
- char *content = xmalloc(len + 8);
- sprintf(content, "sizeof%.*s", len, start);
- lhs = ast_create(NODE_RAW_STMT);
- lhs->raw_stmt.content = content;
- lhs->type_info = type_new(TYPE_INT);
- }
- else
- {
- zpanic_at(lexer_peek(l), "sizeof must be followed by (");
- }
- }
- else
- {
- lhs = parse_primary(ctx, l);
- }
-
- while (1)
- {
- Token op = lexer_peek(l);
- Precedence prec = get_token_precedence(op);
-
- // Handle postfix ++ and -- (highest postfix precedence)
- if (op.type == TOK_OP && op.len == 2 &&
- ((op.start[0] == '+' && op.start[1] == '+') ||
- (op.start[0] == '-' && op.start[1] == '-')))
- {
- lexer_next(l); // consume ++ or --
- ASTNode *node = ast_create(NODE_EXPR_UNARY);
- node->unary.op = (op.start[0] == '+') ? xstrdup("_post++") : xstrdup("_post--");
- node->unary.operand = lhs;
- node->type_info = lhs->type_info;
- lhs = node;
- continue;
- }
-
- if (prec == PREC_NONE || prec < min_prec)
- {
- break;
- }
-
- // Pointer access: ->
- if (op.type == TOK_ARROW && op.start[0] == '-')
- {
- lexer_next(l);
- Token field = lexer_next(l);
- if (field.type != TOK_IDENT)
- {
- zpanic_at(field, "Expected field name after ->");
- break;
- }
- ASTNode *node = ast_create(NODE_EXPR_MEMBER);
- node->member.target = lhs;
- node->member.field = token_strdup(field);
- node->member.is_pointer_access = 1;
-
- node->type_info = get_field_type(ctx, lhs->type_info, node->member.field);
- if (node->type_info)
- {
- node->resolved_type = type_to_string(node->type_info);
- }
- else
- {
- node->resolved_type = xstrdup("unknown");
- }
-
- lhs = node;
- continue;
- }
-
- // Null-safe access: ?.
- if (op.type == TOK_Q_DOT)
- {
- lexer_next(l);
- Token field = lexer_next(l);
- if (field.type != TOK_IDENT)
- {
- zpanic_at(field, "Expected field name after ?.");
- break;
- }
- ASTNode *node = ast_create(NODE_EXPR_MEMBER);
- node->member.target = lhs;
- node->member.field = token_strdup(field);
- node->member.is_pointer_access = 2;
-
- node->type_info = get_field_type(ctx, lhs->type_info, node->member.field);
- if (node->type_info)
- {
- node->resolved_type = type_to_string(node->type_info);
- }
-
- lhs = node;
- continue;
- }
-
- // Postfix ? (Result Unwrap OR Ternary)
- if (op.type == TOK_QUESTION)
- {
- // Disambiguate
- Lexer lookahead = *l;
- lexer_next(&lookahead); // skip ?
- Token next = lexer_peek(&lookahead);
-
- // Heuristic: If next token starts an expression => Ternary
- // (Ident, Number, String, (, {, -, !, *, etc)
- int is_ternary = 0;
- if (next.type == TOK_INT || next.type == TOK_FLOAT || next.type == TOK_STRING ||
- next.type == TOK_IDENT || next.type == TOK_LPAREN || next.type == TOK_LBRACE ||
- next.type == TOK_SIZEOF || next.type == TOK_DEFER || next.type == TOK_AUTOFREE ||
- next.type == TOK_FSTRING || next.type == TOK_CHAR)
- {
- is_ternary = 1;
- }
- // Check unary ops
- if (next.type == TOK_OP)
- {
- if (is_token(next, "-") || is_token(next, "!") || is_token(next, "*") ||
- is_token(next, "&") || is_token(next, "~"))
- {
- is_ternary = 1;
- }
- }
-
- if (is_ternary)
- {
- if (PREC_TERNARY < min_prec)
- {
- break; // Return to caller to handle precedence
- }
-
- lexer_next(l); // consume ?
- ASTNode *true_expr = parse_expression(ctx, l);
- expect(l, TOK_COLON, "Expected : in ternary");
- ASTNode *false_expr = parse_expr_prec(ctx, l, PREC_TERNARY); // Right associative
-
- ASTNode *tern = ast_create(NODE_TERNARY);
- zen_trigger_at(TRIGGER_TERNARY, lhs->token);
-
- tern->ternary.cond = lhs;
- tern->ternary.true_expr = true_expr;
- tern->ternary.false_expr = false_expr;
-
- // Type inference hint: Both branches should match?
- // Logic later in codegen/semant.
- lhs = tern;
- continue;
- }
-
- // Otherwise: Unwrap (High Precedence)
- if (PREC_CALL < min_prec)
- {
- break;
- }
-
+ ASTNode *addr = ast_create(NODE_EXPR_UNARY);
+ addr->unary.op = is_rvalue ? xstrdup("&_rval") : xstrdup("&");
+ addr->unary.operand = operand;
+ addr->type_info = type_new_ptr(ot);
+ arg = addr;
+ } else if (is_ptr && sig->arg_types[0]->kind != TYPE_POINTER) {
+ // Function wants Value, we have Pointer -> Dereference (*)
+ ASTNode *deref = ast_create(NODE_EXPR_UNARY);
+ deref->unary.op = xstrdup("*");
+ deref->unary.operand = operand;
+ deref->type_info = ot->inner;
+ arg = deref;
+ }
+
+ call->call.args = arg;
+ call->type_info = sig->ret_type;
+ call->resolved_type = type_to_string(sig->ret_type);
+ lhs = call;
+
+ // Skip standard unary node creation
+ goto after_unary;
+ }
+ }
+ }
+
+ // Standard Unary Node (for primitives or if no overload found)
+ lhs = ast_create(NODE_EXPR_UNARY);
+ lhs->unary.op = token_strdup(t);
+ lhs->unary.operand = operand;
+
+ if (operand->type_info) {
+ if (is_token(t, "&")) {
+ lhs->type_info = type_new_ptr(operand->type_info);
+ } else if (is_token(t, "*")) {
+ if (operand->type_info->kind == TYPE_POINTER) {
+ lhs->type_info = operand->type_info->inner;
+ }
+ } else {
+ lhs->type_info = operand->type_info;
+ }
+ }
+
+ after_unary:; // Label to skip standard creation if overloaded
+ }
+
+ else if (is_token(t, "sizeof")) {
+ lexer_next(l);
+ if (lexer_peek(l).type == TOK_LPAREN) {
+ const char *start = l->src + l->pos;
+ int depth = 0;
+ while (1) {
+ Token tk = lexer_peek(l);
+ if (tk.type == TOK_EOF) {
+ zpanic_at(tk, "Unterminated sizeof");
+ }
+ if (tk.type == TOK_LPAREN) {
+ depth++;
+ }
+ if (tk.type == TOK_RPAREN) {
+ depth--;
+ if (depth == 0) {
lexer_next(l);
- ASTNode *n = ast_create(NODE_TRY);
- n->try_stmt.expr = lhs;
- lhs = n;
- continue;
+ break;
+ }
}
-
- // Pipe: |>
- if (op.type == TOK_PIPE || (op.type == TOK_OP && is_token(op, "|>")))
- {
+ lexer_next(l);
+ }
+ int len = (l->src + l->pos) - start;
+ char *content = xmalloc(len + 8);
+ sprintf(content, "sizeof%.*s", len, start);
+ lhs = ast_create(NODE_RAW_STMT);
+ lhs->raw_stmt.content = content;
+ lhs->type_info = type_new(TYPE_INT);
+ } else {
+ zpanic_at(lexer_peek(l), "sizeof must be followed by (");
+ }
+ } else {
+ lhs = parse_primary(ctx, l);
+ }
+
+ while (1) {
+ Token op = lexer_peek(l);
+ Precedence prec = get_token_precedence(op);
+
+ // Handle postfix ++ and -- (highest postfix precedence)
+ if (op.type == TOK_OP && op.len == 2 &&
+ ((op.start[0] == '+' && op.start[1] == '+') ||
+ (op.start[0] == '-' && op.start[1] == '-'))) {
+ lexer_next(l); // consume ++ or --
+ ASTNode *node = ast_create(NODE_EXPR_UNARY);
+ node->unary.op =
+ (op.start[0] == '+') ? xstrdup("_post++") : xstrdup("_post--");
+ node->unary.operand = lhs;
+ node->type_info = lhs->type_info;
+ lhs = node;
+ continue;
+ }
+
+ if (prec == PREC_NONE || prec < min_prec) {
+ break;
+ }
+
+ // Pointer access: ->
+ if (op.type == TOK_ARROW && op.start[0] == '-') {
+ lexer_next(l);
+ Token field = lexer_next(l);
+ if (field.type != TOK_IDENT) {
+ zpanic_at(field, "Expected field name after ->");
+ break;
+ }
+ ASTNode *node = ast_create(NODE_EXPR_MEMBER);
+ node->member.target = lhs;
+ node->member.field = token_strdup(field);
+ node->member.is_pointer_access = 1;
+
+ node->type_info = get_field_type(ctx, lhs->type_info, node->member.field);
+ if (node->type_info) {
+ node->resolved_type = type_to_string(node->type_info);
+ } else {
+ node->resolved_type = xstrdup("unknown");
+ }
+
+ lhs = node;
+ continue;
+ }
+
+ // Null-safe access: ?.
+ if (op.type == TOK_Q_DOT) {
+ lexer_next(l);
+ Token field = lexer_next(l);
+ if (field.type != TOK_IDENT) {
+ zpanic_at(field, "Expected field name after ?.");
+ break;
+ }
+ ASTNode *node = ast_create(NODE_EXPR_MEMBER);
+ node->member.target = lhs;
+ node->member.field = token_strdup(field);
+ node->member.is_pointer_access = 2;
+
+ node->type_info = get_field_type(ctx, lhs->type_info, node->member.field);
+ if (node->type_info) {
+ node->resolved_type = type_to_string(node->type_info);
+ }
+
+ lhs = node;
+ continue;
+ }
+
+ // Postfix ? (Result Unwrap OR Ternary)
+ if (op.type == TOK_QUESTION) {
+ // Disambiguate
+ Lexer lookahead = *l;
+ lexer_next(&lookahead); // skip ?
+ Token next = lexer_peek(&lookahead);
+
+ // Heuristic: If next token starts an expression => Ternary
+ // (Ident, Number, String, (, {, -, !, *, etc)
+ int is_ternary = 0;
+ if (next.type == TOK_INT || next.type == TOK_FLOAT ||
+ next.type == TOK_STRING || next.type == TOK_IDENT ||
+ next.type == TOK_LPAREN || next.type == TOK_LBRACE ||
+ next.type == TOK_SIZEOF || next.type == TOK_DEFER ||
+ next.type == TOK_AUTOFREE || next.type == TOK_FSTRING ||
+ next.type == TOK_CHAR) {
+ is_ternary = 1;
+ }
+ // Check unary ops
+ if (next.type == TOK_OP) {
+ if (is_token(next, "-") || is_token(next, "!") || is_token(next, "*") ||
+ is_token(next, "&") || is_token(next, "~")) {
+ is_ternary = 1;
+ }
+ }
+
+ if (is_ternary) {
+ if (PREC_TERNARY < min_prec) {
+ break; // Return to caller to handle precedence
+ }
+
+ lexer_next(l); // consume ?
+ ASTNode *true_expr = parse_expression(ctx, l);
+ expect(l, TOK_COLON, "Expected : in ternary");
+ ASTNode *false_expr =
+ parse_expr_prec(ctx, l, PREC_TERNARY); // Right associative
+
+ ASTNode *tern = ast_create(NODE_TERNARY);
+ zen_trigger_at(TRIGGER_TERNARY, lhs->token);
+
+ tern->ternary.cond = lhs;
+ tern->ternary.true_expr = true_expr;
+ tern->ternary.false_expr = false_expr;
+
+ // Type inference hint: Both branches should match?
+ // Logic later in codegen/semant.
+ lhs = tern;
+ continue;
+ }
+
+ // Otherwise: Unwrap (High Precedence)
+ if (PREC_CALL < min_prec) {
+ break;
+ }
+
+ lexer_next(l);
+ ASTNode *n = ast_create(NODE_TRY);
+ n->try_stmt.expr = lhs;
+ lhs = n;
+ continue;
+ }
+
+ // Pipe: |>
+ if (op.type == TOK_PIPE || (op.type == TOK_OP && is_token(op, "|>"))) {
+ lexer_next(l);
+ ASTNode *rhs = parse_expr_prec(ctx, l, prec + 1);
+ if (rhs->type == NODE_EXPR_CALL) {
+ ASTNode *old_args = rhs->call.args;
+ lhs->next = old_args;
+ rhs->call.args = lhs;
+ lhs = rhs;
+ } else {
+ ASTNode *call = ast_create(NODE_EXPR_CALL);
+ call->call.callee = rhs;
+ call->call.args = lhs;
+ lhs->next = NULL;
+ lhs = call;
+ }
+ continue;
+ }
+
+ lexer_next(l); // Consume operator/paren/bracket
+
+ // Call: (...)
+ if (op.type == TOK_LPAREN) {
+ ASTNode *call = ast_create(NODE_EXPR_CALL);
+ call->call.callee = lhs;
+ ASTNode *head = NULL, *tail = NULL;
+ char **arg_names = NULL;
+ int arg_count = 0;
+ int has_named = 0;
+
+ if (lexer_peek(l).type != TOK_RPAREN) {
+ while (1) {
+ char *arg_name = NULL;
+
+ // Check for named argument: IDENT : expr
+ Token t1 = lexer_peek(l);
+ if (t1.type == TOK_IDENT) {
+ // Lookahead for colon
+ Token t2 = lexer_peek2(l);
+ if (t2.type == TOK_COLON) {
+ arg_name = token_strdup(t1);
+ has_named = 1;
+ lexer_next(l); // eat IDENT
+ lexer_next(l); // eat :
+ }
+ }
+
+ ASTNode *arg = parse_expression(ctx, l);
+ if (!head) {
+ head = arg;
+ } else {
+ tail->next = arg;
+ }
+ tail = arg;
+
+ // Store arg name
+ arg_names = xrealloc(arg_names, (arg_count + 1) * sizeof(char *));
+ arg_names[arg_count] = arg_name;
+ arg_count++;
+
+ if (lexer_peek(l).type == TOK_COMMA) {
lexer_next(l);
- ASTNode *rhs = parse_expr_prec(ctx, l, prec + 1);
- if (rhs->type == NODE_EXPR_CALL)
- {
- ASTNode *old_args = rhs->call.args;
- lhs->next = old_args;
- rhs->call.args = lhs;
- lhs = rhs;
- }
- else
- {
- ASTNode *call = ast_create(NODE_EXPR_CALL);
- call->call.callee = rhs;
- call->call.args = lhs;
- lhs->next = NULL;
- lhs = call;
- }
+ } else {
+ break;
+ }
+ }
+ }
+ if (lexer_next(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected )");
+ }
+ call->call.args = head;
+ call->call.arg_names = has_named ? arg_names : NULL;
+ call->call.arg_count = arg_count;
+
+ call->resolved_type = xstrdup("unknown");
+ if (lhs->type_info && lhs->type_info->kind == TYPE_FUNCTION &&
+ lhs->type_info->inner) {
+ call->type_info = lhs->type_info->inner;
+ }
+
+ lhs = call;
+ continue;
+ }
+
+ // Index: [...] or Slice: [start..end]
+ if (op.type == TOK_LBRACKET) {
+ ASTNode *start = NULL;
+ ASTNode *end = NULL;
+ int is_slice = 0;
+
+ // Case: [..] or [..end]
+ if (lexer_peek(l).type == TOK_DOTDOT) {
+ is_slice = 1;
+ lexer_next(l); // consume ..
+ if (lexer_peek(l).type != TOK_RBRACKET) {
+ end = parse_expression(ctx, l);
+ }
+ } else {
+ // Case: [start] or [start..] or [start..end]
+ start = parse_expression(ctx, l);
+ if (lexer_peek(l).type == TOK_DOTDOT) {
+ is_slice = 1;
+ lexer_next(l); // consume ..
+ if (lexer_peek(l).type != TOK_RBRACKET) {
+ end = parse_expression(ctx, l);
+ }
+ }
+ }
+
+ if (lexer_next(l).type != TOK_RBRACKET) {
+ zpanic_at(lexer_peek(l), "Expected ]");
+ }
+
+ if (is_slice) {
+ ASTNode *node = ast_create(NODE_EXPR_SLICE);
+ node->slice.array = lhs;
+ node->slice.start = start;
+ node->slice.end = end;
+
+ // Type Inference & Registration
+ if (lhs->type_info) {
+ Type *inner = NULL;
+ if (lhs->type_info->kind == TYPE_ARRAY) {
+ inner = lhs->type_info->inner;
+ } else if (lhs->type_info->kind == TYPE_POINTER) {
+ inner = lhs->type_info->inner;
+ }
+
+ if (inner) {
+ node->type_info = type_new(TYPE_ARRAY);
+ node->type_info->inner = inner;
+ node->type_info->array_size = 0; // Slice
+
+ // Clean up string for registration (e.g. "int" from "int*")
+ char *inner_str = type_to_string(inner);
+
+ // Strip * if it somehow managed to keep one, though
+ // parse_type_formal should handle it For now assume type_to_string
+ // gives base type
+ register_slice(ctx, inner_str);
+ }
+ }
+
+ lhs = node;
+ } else {
+ ASTNode *node = ast_create(NODE_EXPR_INDEX);
+ node->index.array = lhs;
+ node->index.index = start;
+
+ // Static Array Bounds Check
+ if (lhs->type_info && lhs->type_info->kind == TYPE_ARRAY &&
+ lhs->type_info->array_size > 0) {
+ if (start->type == NODE_EXPR_LITERAL &&
+ start->literal.type_kind == 0) {
+ int idx = start->literal.int_val;
+ if (idx < 0 || idx >= lhs->type_info->array_size) {
+ warn_array_bounds(op, idx, lhs->type_info->array_size);
+ }
+ }
+ }
+
+ lhs = node;
+ }
+ continue;
+ }
+
+ // Member: .
+ if (op.type == TOK_OP && is_token(op, ".")) {
+ Token field = lexer_next(l);
+ if (field.type != TOK_IDENT) {
+ zpanic_at(field, "Expected field name after .");
+ break;
+ }
+ ASTNode *node = ast_create(NODE_EXPR_MEMBER);
+ node->member.target = lhs;
+ node->member.field = token_strdup(field);
+ node->member.is_pointer_access = 0;
+
+ if (lhs->type_info && lhs->type_info->kind == TYPE_POINTER) {
+ node->member.is_pointer_access = 1;
+
+ // Special case: .val() on pointer = dereference
+ if (strcmp(node->member.field, "val") == 0 &&
+ lexer_peek(l).type == TOK_LPAREN) {
+ lexer_next(l); // consume (
+ if (lexer_peek(l).type == TOK_RPAREN) {
+ lexer_next(l); // consume )
+ // Rewrite to dereference: *ptr
+ ASTNode *deref = ast_create(NODE_EXPR_UNARY);
+ deref->unary.op = xstrdup("*");
+ deref->unary.operand = lhs;
+ deref->type_info = lhs->type_info->inner;
+ lhs = deref;
continue;
- }
-
- lexer_next(l); // Consume operator/paren/bracket
-
- // Call: (...)
- if (op.type == TOK_LPAREN)
- {
+ }
+ }
+ } else if (lhs->type == NODE_EXPR_VAR) {
+ char *type = find_symbol_type(ctx, lhs->var_ref.name);
+ if (type && strchr(type, '*')) {
+ node->member.is_pointer_access = 1;
+
+ // Special case: .val() on pointer = dereference
+ if (strcmp(node->member.field, "val") == 0 &&
+ lexer_peek(l).type == TOK_LPAREN) {
+ lexer_next(l); // consume (
+ if (lexer_peek(l).type == TOK_RPAREN) {
+ lexer_next(l); // consume )
+ // Rewrite to dereference: *ptr
+ ASTNode *deref = ast_create(NODE_EXPR_UNARY);
+ deref->unary.op = xstrdup("*");
+ deref->unary.operand = lhs;
+ // Try to get inner type
+ if (lhs->type_info && lhs->type_info->kind == TYPE_POINTER) {
+ deref->type_info = lhs->type_info->inner;
+ }
+ lhs = deref;
+ continue;
+ }
+ }
+ }
+ if (strcmp(lhs->var_ref.name, "self") == 0 &&
+ !node->member.is_pointer_access) {
+ node->member.is_pointer_access = 1;
+ }
+ }
+
+ node->type_info = get_field_type(ctx, lhs->type_info, node->member.field);
+
+ if (!node->type_info && lhs->type_info) {
+ char *struct_name = NULL;
+ Type *st = lhs->type_info;
+ if (st->kind == TYPE_STRUCT) {
+ struct_name = st->name;
+ } else if (st->kind == TYPE_POINTER && st->inner &&
+ st->inner->kind == TYPE_STRUCT) {
+ struct_name = st->inner->name;
+ }
+
+ if (struct_name) {
+ char mangled[256];
+ sprintf(mangled, "%s_%s", struct_name, node->member.field);
+
+ FuncSig *sig = find_func(ctx, mangled);
+ if (sig) {
+ // It is a method! Create a Function Type Info to carry the return
+ // type
+ Type *ft = type_new(TYPE_FUNCTION);
+ ft->name = xstrdup(mangled);
+ ft->inner = sig->ret_type; // Return type
+ node->type_info = ft;
+ }
+ }
+ }
+
+ if (node->type_info) {
+ node->resolved_type = type_to_string(node->type_info);
+ } else {
+ node->resolved_type = xstrdup("unknown");
+ }
+
+ lhs = node;
+ continue;
+ }
+
+ ASTNode *rhs = parse_expr_prec(ctx, l, prec + 1);
+ ASTNode *bin = ast_create(NODE_EXPR_BINARY);
+ bin->token = op;
+ if (op.type == TOK_OP) {
+ if (is_token(op, "&") || is_token(op, "|") || is_token(op, "^")) {
+ zen_trigger_at(TRIGGER_BITWISE, op);
+ } else if (is_token(op, "<<") || is_token(op, ">>")) {
+ zen_trigger_at(TRIGGER_BITWISE, op);
+ }
+ }
+ bin->binary.left = lhs;
+ bin->binary.right = rhs;
+
+ if (op.type == TOK_LANGLE) {
+ bin->binary.op = xstrdup("<");
+ } else if (op.type == TOK_RANGLE) {
+ bin->binary.op = xstrdup(">");
+ } else if (op.type == TOK_AND) {
+ bin->binary.op = xstrdup("&&");
+ } else if (op.type == TOK_OR) {
+ bin->binary.op = xstrdup("||");
+ } else {
+ bin->binary.op = token_strdup(op);
+ }
+
+ if (strcmp(bin->binary.op, "/") == 0 || strcmp(bin->binary.op, "%") == 0) {
+ if (rhs->type == NODE_EXPR_LITERAL && rhs->literal.type_kind == 0 &&
+ rhs->literal.int_val == 0) {
+ warn_division_by_zero(op);
+ }
+ }
+
+ if (is_comparison_op(bin->binary.op)) {
+ // Check for identical operands (x == x)
+ if (lhs->type == NODE_EXPR_VAR && rhs->type == NODE_EXPR_VAR) {
+ if (strcmp(lhs->var_ref.name, rhs->var_ref.name) == 0) {
+ if (strcmp(bin->binary.op, "==") == 0 ||
+ strcmp(bin->binary.op, ">=") == 0 ||
+ strcmp(bin->binary.op, "<=") == 0) {
+ warn_comparison_always_true(op, "Comparing a variable to itself");
+ } else if (strcmp(bin->binary.op, "!=") == 0 ||
+ strcmp(bin->binary.op, ">") == 0 ||
+ strcmp(bin->binary.op, "<") == 0) {
+ warn_comparison_always_false(op, "Comparing a variable to itself");
+ }
+ }
+ } else if (lhs->type == NODE_EXPR_LITERAL &&
+ lhs->literal.type_kind == 0 &&
+ rhs->type == NODE_EXPR_LITERAL &&
+ rhs->literal.type_kind == 0) {
+ // Check if literals make sense (e.g. 5 > 5)
+ if (lhs->literal.int_val == rhs->literal.int_val) {
+ if (strcmp(bin->binary.op, "==") == 0 ||
+ strcmp(bin->binary.op, ">=") == 0 ||
+ strcmp(bin->binary.op, "<=") == 0) {
+ warn_comparison_always_true(op, "Comparing identical literals");
+ } else {
+ warn_comparison_always_false(op, "Comparing identical literals");
+ }
+ }
+ }
+
+ if (lhs->type_info && type_is_unsigned(lhs->type_info)) {
+ if (rhs->type == NODE_EXPR_LITERAL && rhs->literal.type_kind == 0 &&
+ rhs->literal.int_val == 0) {
+ if (strcmp(bin->binary.op, ">=") == 0) {
+ warn_comparison_always_true(op, "Unsigned value is always >= 0");
+ } else if (strcmp(bin->binary.op, "<") == 0) {
+ warn_comparison_always_false(op, "Unsigned value is never < 0");
+ }
+ }
+ }
+ }
+
+ if (strcmp(bin->binary.op, "=") == 0 || strcmp(bin->binary.op, "+=") == 0 ||
+ strcmp(bin->binary.op, "-=") == 0 ||
+ strcmp(bin->binary.op, "*=") == 0 ||
+ strcmp(bin->binary.op, "/=") == 0) {
+
+ if (lhs->type == NODE_EXPR_VAR) {
+ // Check if the variable is const
+ Type *t = find_symbol_type_info(ctx, lhs->var_ref.name);
+ if (t && t->is_const) {
+ zpanic_at(op, "Cannot assign to const variable '%s'",
+ lhs->var_ref.name);
+ }
+
+ // Check if the variable is immutable
+ if (!is_var_mutable(ctx, lhs->var_ref.name)) {
+ zpanic_at(op,
+ "Cannot assign to immutable variable '%s' (use 'var mut' "
+ "to make it "
+ "mutable)",
+ lhs->var_ref.name);
+ }
+ }
+ }
+
+ int is_compound = 0;
+ size_t op_len = strlen(bin->binary.op);
+
+ // Check if operator ends with '=' but is not ==, !=, <=, >=
+ if (op_len > 1 && bin->binary.op[op_len - 1] == '=') {
+ char c = bin->binary.op[0];
+ if (c != '=' && c != '!' && c != '<' && c != '>') {
+ is_compound = 1;
+ }
+ // Special handle for <<= and >>=
+ if (strcmp(bin->binary.op, "<<=") == 0 ||
+ strcmp(bin->binary.op, ">>=") == 0) {
+ is_compound = 1;
+ }
+ }
+
+ if (is_compound) {
+ ASTNode *op_node = ast_create(NODE_EXPR_BINARY);
+ op_node->binary.left = lhs;
+ op_node->binary.right = rhs;
+
+ // Extract the base operator (remove last char '=')
+ char *inner_op = xmalloc(op_len);
+ strncpy(inner_op, bin->binary.op, op_len - 1);
+ inner_op[op_len - 1] = '\0';
+ op_node->binary.op = inner_op;
+
+ // Inherit type info temporarily
+ if (lhs->type_info && rhs->type_info &&
+ type_eq(lhs->type_info, rhs->type_info)) {
+ op_node->type_info = lhs->type_info;
+ }
+
+ const char *inner_method = get_operator_method(inner_op);
+ if (inner_method) {
+ Type *lt = lhs->type_info;
+ char *struct_name = NULL;
+ int is_lhs_ptr = 0;
+
+ if (lt) {
+ if (lt->kind == TYPE_STRUCT) {
+ struct_name = lt->name;
+ is_lhs_ptr = 0;
+ } else if (lt->kind == TYPE_POINTER &&
+ lt->inner->kind == TYPE_STRUCT) {
+ struct_name = lt->inner->name;
+ is_lhs_ptr = 1;
+ }
+ }
+
+ if (struct_name) {
+ char mangled[256];
+ sprintf(mangled, "%s_%s", struct_name, inner_method);
+ FuncSig *sig = find_func(ctx, mangled);
+ if (sig) {
+ // Rewrite op_node from BINARY -> CALL
ASTNode *call = ast_create(NODE_EXPR_CALL);
- call->call.callee = lhs;
- ASTNode *head = NULL, *tail = NULL;
- char **arg_names = NULL;
- int arg_count = 0;
- int has_named = 0;
-
- if (lexer_peek(l).type != TOK_RPAREN)
- {
- while (1)
- {
- char *arg_name = NULL;
-
- // Check for named argument: IDENT : expr
- Token t1 = lexer_peek(l);
- if (t1.type == TOK_IDENT)
- {
- // Lookahead for colon
- Token t2 = lexer_peek2(l);
- if (t2.type == TOK_COLON)
- {
- arg_name = token_strdup(t1);
- has_named = 1;
- lexer_next(l); // eat IDENT
- lexer_next(l); // eat :
- }
- }
-
- ASTNode *arg = parse_expression(ctx, l);
- if (!head)
- {
- head = arg;
- }
- else
- {
- tail->next = arg;
- }
- tail = arg;
-
- // Store arg name
- arg_names = xrealloc(arg_names, (arg_count + 1) * sizeof(char *));
- arg_names[arg_count] = arg_name;
- arg_count++;
-
- if (lexer_peek(l).type == TOK_COMMA)
- {
- lexer_next(l);
- }
- else
- {
- break;
- }
- }
- }
- if (lexer_next(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected )");
- }
- call->call.args = head;
- call->call.arg_names = has_named ? arg_names : NULL;
- call->call.arg_count = arg_count;
-
- call->resolved_type = xstrdup("unknown");
- if (lhs->type_info && lhs->type_info->kind == TYPE_FUNCTION && lhs->type_info->inner)
- {
- call->type_info = lhs->type_info->inner;
- }
-
- lhs = call;
- continue;
- }
-
- // Index: [...] or Slice: [start..end]
- if (op.type == TOK_LBRACKET)
- {
- ASTNode *start = NULL;
- ASTNode *end = NULL;
- int is_slice = 0;
-
- // Case: [..] or [..end]
- if (lexer_peek(l).type == TOK_DOTDOT)
- {
- is_slice = 1;
- lexer_next(l); // consume ..
- if (lexer_peek(l).type != TOK_RBRACKET)
- {
- end = parse_expression(ctx, l);
- }
- }
- else
- {
- // Case: [start] or [start..] or [start..end]
- start = parse_expression(ctx, l);
- if (lexer_peek(l).type == TOK_DOTDOT)
- {
- is_slice = 1;
- lexer_next(l); // consume ..
- if (lexer_peek(l).type != TOK_RBRACKET)
- {
- end = parse_expression(ctx, l);
- }
- }
- }
-
- if (lexer_next(l).type != TOK_RBRACKET)
- {
- zpanic_at(lexer_peek(l), "Expected ]");
- }
-
- if (is_slice)
- {
- ASTNode *node = ast_create(NODE_EXPR_SLICE);
- node->slice.array = lhs;
- node->slice.start = start;
- node->slice.end = end;
-
- // Type Inference & Registration
- if (lhs->type_info)
- {
- Type *inner = NULL;
- if (lhs->type_info->kind == TYPE_ARRAY)
- {
- inner = lhs->type_info->inner;
- }
- else if (lhs->type_info->kind == TYPE_POINTER)
- {
- inner = lhs->type_info->inner;
- }
-
- if (inner)
- {
- node->type_info = type_new(TYPE_ARRAY);
- node->type_info->inner = inner;
- node->type_info->array_size = 0; // Slice
-
- // Clean up string for registration (e.g. "int" from "int*")
- char *inner_str = type_to_string(inner);
-
- // Strip * if it somehow managed to keep one, though
- // parse_type_formal should handle it For now assume type_to_string
- // gives base type
- register_slice(ctx, inner_str);
- }
- }
-
- lhs = node;
- }
- else
- {
- ASTNode *node = ast_create(NODE_EXPR_INDEX);
- node->index.array = lhs;
- node->index.index = start;
-
- // Static Array Bounds Check
- if (lhs->type_info && lhs->type_info->kind == TYPE_ARRAY &&
- lhs->type_info->array_size > 0)
- {
- if (start->type == NODE_EXPR_LITERAL && start->literal.type_kind == 0)
- {
- int idx = start->literal.int_val;
- if (idx < 0 || idx >= lhs->type_info->array_size)
- {
- warn_array_bounds(op, idx, lhs->type_info->array_size);
- }
- }
- }
-
- lhs = node;
- }
- continue;
- }
-
- // Member: .
- if (op.type == TOK_OP && is_token(op, "."))
- {
- Token field = lexer_next(l);
- if (field.type != TOK_IDENT)
- {
- zpanic_at(field, "Expected field name after .");
- break;
- }
- ASTNode *node = ast_create(NODE_EXPR_MEMBER);
- node->member.target = lhs;
- node->member.field = token_strdup(field);
- node->member.is_pointer_access = 0;
-
- if (lhs->type_info && lhs->type_info->kind == TYPE_POINTER)
- {
- node->member.is_pointer_access = 1;
-
- // Special case: .val() on pointer = dereference
- if (strcmp(node->member.field, "val") == 0 && lexer_peek(l).type == TOK_LPAREN)
- {
- lexer_next(l); // consume (
- if (lexer_peek(l).type == TOK_RPAREN)
- {
- lexer_next(l); // consume )
- // Rewrite to dereference: *ptr
- ASTNode *deref = ast_create(NODE_EXPR_UNARY);
- deref->unary.op = xstrdup("*");
- deref->unary.operand = lhs;
- deref->type_info = lhs->type_info->inner;
- lhs = deref;
- continue;
- }
- }
- }
- else if (lhs->type == NODE_EXPR_VAR)
- {
- char *type = find_symbol_type(ctx, lhs->var_ref.name);
- if (type && strchr(type, '*'))
- {
- node->member.is_pointer_access = 1;
-
- // Special case: .val() on pointer = dereference
- if (strcmp(node->member.field, "val") == 0 && lexer_peek(l).type == TOK_LPAREN)
- {
- lexer_next(l); // consume (
- if (lexer_peek(l).type == TOK_RPAREN)
- {
- lexer_next(l); // consume )
- // Rewrite to dereference: *ptr
- ASTNode *deref = ast_create(NODE_EXPR_UNARY);
- deref->unary.op = xstrdup("*");
- deref->unary.operand = lhs;
- // Try to get inner type
- if (lhs->type_info && lhs->type_info->kind == TYPE_POINTER)
- {
- deref->type_info = lhs->type_info->inner;
- }
- lhs = deref;
- continue;
- }
- }
- }
- if (strcmp(lhs->var_ref.name, "self") == 0 && !node->member.is_pointer_access)
- {
- node->member.is_pointer_access = 1;
- }
- }
-
- node->type_info = get_field_type(ctx, lhs->type_info, node->member.field);
-
- if (!node->type_info && lhs->type_info)
- {
- char *struct_name = NULL;
- Type *st = lhs->type_info;
- if (st->kind == TYPE_STRUCT)
- {
- struct_name = st->name;
- }
- else if (st->kind == TYPE_POINTER && st->inner && st->inner->kind == TYPE_STRUCT)
- {
- struct_name = st->inner->name;
- }
-
- if (struct_name)
- {
- char mangled[256];
- sprintf(mangled, "%s_%s", struct_name, node->member.field);
-
- FuncSig *sig = find_func(ctx, mangled);
- if (sig)
- {
- // It is a method! Create a Function Type Info to carry the return
- // type
- Type *ft = type_new(TYPE_FUNCTION);
- ft->name = xstrdup(mangled);
- ft->inner = sig->ret_type; // Return type
- node->type_info = ft;
- }
- }
- }
-
- if (node->type_info)
- {
- node->resolved_type = type_to_string(node->type_info);
- }
- else
- {
- node->resolved_type = xstrdup("unknown");
- }
-
- lhs = node;
+ ASTNode *callee = ast_create(NODE_EXPR_VAR);
+ callee->var_ref.name = xstrdup(mangled);
+ call->call.callee = callee;
+
+ // Handle 'self' argument
+ ASTNode *arg1 = lhs;
+ if (sig->total_args > 0 &&
+ sig->arg_types[0]->kind == TYPE_POINTER && !is_lhs_ptr) {
+ ASTNode *addr = ast_create(NODE_EXPR_UNARY);
+ addr->unary.op = xstrdup("&");
+ addr->unary.operand = lhs;
+ addr->type_info = type_new_ptr(lt);
+ arg1 = addr;
+ } else if (is_lhs_ptr && sig->arg_types[0]->kind != TYPE_POINTER) {
+ ASTNode *deref = ast_create(NODE_EXPR_UNARY);
+ deref->unary.op = xstrdup("*");
+ deref->unary.operand = lhs;
+ arg1 = deref;
+ }
+
+ call->call.args = arg1;
+ arg1->next = rhs;
+ rhs->next = NULL;
+ call->type_info = sig->ret_type;
+
+ // Replace op_node with the call
+ op_node = call;
+ }
+ }
+ }
+
+ free(bin->binary.op);
+ bin->binary.op = xstrdup("=");
+ bin->binary.right = op_node;
+ }
+
+ // Index Set Overload: Call(get, idx) = val --> Call(set, idx, val)
+ if (strcmp(bin->binary.op, "=") == 0 && lhs->type == NODE_EXPR_CALL) {
+ if (lhs->call.callee->type == NODE_EXPR_VAR) {
+ char *name = lhs->call.callee->var_ref.name;
+ // Check if it ends in "_get"
+ size_t len = strlen(name);
+ if (len > 4 && strcmp(name + len - 4, "_get") == 0) {
+ char *set_name = xstrdup(name);
+ set_name[len - 3] = 's'; // Replace 'g' with 's' -> _set
+ set_name[len - 2] = 'e';
+ set_name[len - 1] = 't';
+
+ if (find_func(ctx, set_name)) {
+ // Create NEW Call Node for Set
+ ASTNode *set_call = ast_create(NODE_EXPR_CALL);
+ ASTNode *set_callee = ast_create(NODE_EXPR_VAR);
+ set_callee->var_ref.name = set_name;
+ set_call->call.callee = set_callee;
+
+ // Clone argument list (Shallow copy of arg nodes to preserve chain
+ // for get)
+ ASTNode *lhs_args = lhs->call.args;
+ ASTNode *new_head = NULL;
+ ASTNode *new_tail = NULL;
+
+ while (lhs_args) {
+ ASTNode *arg_copy = xmalloc(sizeof(ASTNode));
+ memcpy(arg_copy, lhs_args, sizeof(ASTNode));
+ arg_copy->next = NULL;
+
+ if (!new_head) {
+ new_head = arg_copy;
+ } else {
+ new_tail->next = arg_copy;
+ }
+ new_tail = arg_copy;
+
+ lhs_args = lhs_args->next;
+ }
+
+ // Append RHS to new args
+ ASTNode *val_expr = bin->binary.right;
+ if (new_tail) {
+ new_tail->next = val_expr;
+ } else {
+ new_head = val_expr;
+ }
+
+ set_call->call.args = new_head;
+ set_call->type_info = type_new(TYPE_VOID);
+
+ lhs = set_call; // Use the new Set call as the result
continue;
- }
-
- ASTNode *rhs = parse_expr_prec(ctx, l, prec + 1);
- ASTNode *bin = ast_create(NODE_EXPR_BINARY);
- bin->token = op;
- if (op.type == TOK_OP)
- {
- if (is_token(op, "&") || is_token(op, "|") || is_token(op, "^"))
- {
- zen_trigger_at(TRIGGER_BITWISE, op);
- }
- else if (is_token(op, "<<") || is_token(op, ">>"))
- {
- zen_trigger_at(TRIGGER_BITWISE, op);
- }
- }
- bin->binary.left = lhs;
- bin->binary.right = rhs;
-
- if (op.type == TOK_LANGLE)
- {
- bin->binary.op = xstrdup("<");
- }
- else if (op.type == TOK_RANGLE)
- {
- bin->binary.op = xstrdup(">");
- }
- else if (op.type == TOK_AND)
- {
- bin->binary.op = xstrdup("&&");
- }
- else if (op.type == TOK_OR)
- {
- bin->binary.op = xstrdup("||");
- }
- else
- {
- bin->binary.op = token_strdup(op);
- }
-
- if (strcmp(bin->binary.op, "/") == 0 || strcmp(bin->binary.op, "%") == 0)
- {
- if (rhs->type == NODE_EXPR_LITERAL && rhs->literal.type_kind == 0 &&
- rhs->literal.int_val == 0)
- {
- warn_division_by_zero(op);
- }
- }
-
- if (is_comparison_op(bin->binary.op))
- {
- // Check for identical operands (x == x)
- if (lhs->type == NODE_EXPR_VAR && rhs->type == NODE_EXPR_VAR)
- {
- if (strcmp(lhs->var_ref.name, rhs->var_ref.name) == 0)
- {
- if (strcmp(bin->binary.op, "==") == 0 || strcmp(bin->binary.op, ">=") == 0 ||
- strcmp(bin->binary.op, "<=") == 0)
- {
- warn_comparison_always_true(op, "Comparing a variable to itself");
- }
- else if (strcmp(bin->binary.op, "!=") == 0 ||
- strcmp(bin->binary.op, ">") == 0 || strcmp(bin->binary.op, "<") == 0)
- {
- warn_comparison_always_false(op, "Comparing a variable to itself");
- }
- }
- }
- else if (lhs->type == NODE_EXPR_LITERAL && lhs->literal.type_kind == 0 &&
- rhs->type == NODE_EXPR_LITERAL && rhs->literal.type_kind == 0)
- {
- // Check if literals make sense (e.g. 5 > 5)
- if (lhs->literal.int_val == rhs->literal.int_val)
- {
- if (strcmp(bin->binary.op, "==") == 0 || strcmp(bin->binary.op, ">=") == 0 ||
- strcmp(bin->binary.op, "<=") == 0)
- {
- warn_comparison_always_true(op, "Comparing identical literals");
- }
- else
- {
- warn_comparison_always_false(op, "Comparing identical literals");
- }
- }
- }
-
- if (lhs->type_info && type_is_unsigned(lhs->type_info))
- {
- if (rhs->type == NODE_EXPR_LITERAL && rhs->literal.type_kind == 0 &&
- rhs->literal.int_val == 0)
- {
- if (strcmp(bin->binary.op, ">=") == 0)
- {
- warn_comparison_always_true(op, "Unsigned value is always >= 0");
- }
- else if (strcmp(bin->binary.op, "<") == 0)
- {
- warn_comparison_always_false(op, "Unsigned value is never < 0");
- }
- }
- }
- }
-
- if (strcmp(bin->binary.op, "=") == 0 || strcmp(bin->binary.op, "+=") == 0 ||
- strcmp(bin->binary.op, "-=") == 0 || strcmp(bin->binary.op, "*=") == 0 ||
- strcmp(bin->binary.op, "/=") == 0)
- {
-
- if (lhs->type == NODE_EXPR_VAR)
- {
- // Check if the variable is const
- Type *t = find_symbol_type_info(ctx, lhs->var_ref.name);
- if (t && t->is_const)
- {
- zpanic_at(op, "Cannot assign to const variable '%s'", lhs->var_ref.name);
- }
-
- // Check if the variable is immutable
- if (!is_var_mutable(ctx, lhs->var_ref.name))
- {
- zpanic_at(op,
- "Cannot assign to immutable variable '%s' (use 'var mut' "
- "to make it "
- "mutable)",
- lhs->var_ref.name);
- }
- }
- }
-
- int is_compound = 0;
- size_t op_len = strlen(bin->binary.op);
-
- // Check if operator ends with '=' but is not ==, !=, <=, >=
- if (op_len > 1 && bin->binary.op[op_len - 1] == '=')
- {
- char c = bin->binary.op[0];
- if (c != '=' && c != '!' && c != '<' && c != '>')
- {
- is_compound = 1;
- }
- // Special handle for <<= and >>=
- if (strcmp(bin->binary.op, "<<=") == 0 || strcmp(bin->binary.op, ">>=") == 0)
- {
- is_compound = 1;
- }
- }
-
- if (is_compound)
- {
- ASTNode *op_node = ast_create(NODE_EXPR_BINARY);
- op_node->binary.left = lhs;
- op_node->binary.right = rhs;
-
- // Extract the base operator (remove last char '=')
- char *inner_op = xmalloc(op_len);
- strncpy(inner_op, bin->binary.op, op_len - 1);
- inner_op[op_len - 1] = '\0';
- op_node->binary.op = inner_op;
-
- // Inherit type info temporarily
- if (lhs->type_info && rhs->type_info && type_eq(lhs->type_info, rhs->type_info))
- {
- op_node->type_info = lhs->type_info;
- }
-
- const char *inner_method = get_operator_method(inner_op);
- if (inner_method)
- {
- Type *lt = lhs->type_info;
- char *struct_name = NULL;
- int is_lhs_ptr = 0;
-
- if (lt)
- {
- if (lt->kind == TYPE_STRUCT)
- {
- struct_name = lt->name;
- is_lhs_ptr = 0;
- }
- else if (lt->kind == TYPE_POINTER && lt->inner->kind == TYPE_STRUCT)
- {
- struct_name = lt->inner->name;
- is_lhs_ptr = 1;
- }
- }
-
- if (struct_name)
- {
- char mangled[256];
- sprintf(mangled, "%s_%s", struct_name, inner_method);
- FuncSig *sig = find_func(ctx, mangled);
- if (sig)
- {
- // Rewrite op_node from BINARY -> CALL
- ASTNode *call = ast_create(NODE_EXPR_CALL);
- ASTNode *callee = ast_create(NODE_EXPR_VAR);
- callee->var_ref.name = xstrdup(mangled);
- call->call.callee = callee;
-
- // Handle 'self' argument
- ASTNode *arg1 = lhs;
- if (sig->total_args > 0 && sig->arg_types[0]->kind == TYPE_POINTER &&
- !is_lhs_ptr)
- {
- ASTNode *addr = ast_create(NODE_EXPR_UNARY);
- addr->unary.op = xstrdup("&");
- addr->unary.operand = lhs;
- addr->type_info = type_new_ptr(lt);
- arg1 = addr;
- }
- else if (is_lhs_ptr && sig->arg_types[0]->kind != TYPE_POINTER)
- {
- ASTNode *deref = ast_create(NODE_EXPR_UNARY);
- deref->unary.op = xstrdup("*");
- deref->unary.operand = lhs;
- arg1 = deref;
- }
-
- call->call.args = arg1;
- arg1->next = rhs;
- rhs->next = NULL;
- call->type_info = sig->ret_type;
-
- // Replace op_node with the call
- op_node = call;
- }
- }
- }
-
- free(bin->binary.op);
- bin->binary.op = xstrdup("=");
- bin->binary.right = op_node;
- }
-
- // Index Set Overload: Call(get, idx) = val --> Call(set, idx, val)
- if (strcmp(bin->binary.op, "=") == 0 && lhs->type == NODE_EXPR_CALL)
- {
- if (lhs->call.callee->type == NODE_EXPR_VAR)
- {
- char *name = lhs->call.callee->var_ref.name;
- // Check if it ends in "_get"
- size_t len = strlen(name);
- if (len > 4 && strcmp(name + len - 4, "_get") == 0)
- {
- char *set_name = xstrdup(name);
- set_name[len - 3] = 's'; // Replace 'g' with 's' -> _set
- set_name[len - 2] = 'e';
- set_name[len - 1] = 't';
-
- if (find_func(ctx, set_name))
- {
- // Create NEW Call Node for Set
- ASTNode *set_call = ast_create(NODE_EXPR_CALL);
- ASTNode *set_callee = ast_create(NODE_EXPR_VAR);
- set_callee->var_ref.name = set_name;
- set_call->call.callee = set_callee;
-
- // Clone argument list (Shallow copy of arg nodes to preserve chain
- // for get)
- ASTNode *lhs_args = lhs->call.args;
- ASTNode *new_head = NULL;
- ASTNode *new_tail = NULL;
-
- while (lhs_args)
- {
- ASTNode *arg_copy = xmalloc(sizeof(ASTNode));
- memcpy(arg_copy, lhs_args, sizeof(ASTNode));
- arg_copy->next = NULL;
-
- if (!new_head)
- {
- new_head = arg_copy;
- }
- else
- {
- new_tail->next = arg_copy;
- }
- new_tail = arg_copy;
-
- lhs_args = lhs_args->next;
- }
-
- // Append RHS to new args
- ASTNode *val_expr = bin->binary.right;
- if (new_tail)
- {
- new_tail->next = val_expr;
- }
- else
- {
- new_head = val_expr;
- }
-
- set_call->call.args = new_head;
- set_call->type_info = type_new(TYPE_VOID);
-
- lhs = set_call; // Use the new Set call as the result
- continue;
- }
- else
- {
- free(set_name);
- }
- }
- }
- }
-
- const char *method = get_operator_method(bin->binary.op);
-
- if (method)
- {
- Type *lt = lhs->type_info;
- char *struct_name = NULL;
- int is_lhs_ptr = 0;
-
- if (lt)
- {
- if (lt->kind == TYPE_STRUCT)
- {
- struct_name = lt->name;
- is_lhs_ptr = 0;
- }
- else if (lt->kind == TYPE_POINTER && lt->inner->kind == TYPE_STRUCT)
- {
- struct_name = lt->inner->name;
- is_lhs_ptr = 1;
- }
- }
-
- if (struct_name)
- {
- char mangled[256];
- sprintf(mangled, "%s_%s", struct_name, method);
-
- FuncSig *sig = find_func(ctx, mangled);
-
- if (sig)
- {
- ASTNode *call = ast_create(NODE_EXPR_CALL);
- ASTNode *callee = ast_create(NODE_EXPR_VAR);
- callee->var_ref.name = xstrdup(mangled);
- call->call.callee = callee;
-
- ASTNode *arg1 = lhs;
-
- // Check if function expects a pointer for 'self'
- if (sig->total_args > 0 && sig->arg_types[0] &&
- sig->arg_types[0]->kind == TYPE_POINTER)
- {
- if (!is_lhs_ptr)
- {
- // Value -> Pointer.
- int is_rvalue =
- (lhs->type == NODE_EXPR_CALL || lhs->type == NODE_EXPR_BINARY ||
- lhs->type == NODE_EXPR_STRUCT_INIT ||
- lhs->type == NODE_EXPR_CAST || lhs->type == NODE_MATCH);
-
- ASTNode *addr = ast_create(NODE_EXPR_UNARY);
- addr->unary.op = is_rvalue ? xstrdup("&_rval") : xstrdup("&");
- addr->unary.operand = lhs;
- addr->type_info = type_new_ptr(lt);
- arg1 = addr;
- }
- }
- else
- {
- // Function expects value
- if (is_lhs_ptr)
- {
- // Have pointer, need value -> *lhs
- ASTNode *deref = ast_create(NODE_EXPR_UNARY);
- deref->unary.op = xstrdup("*");
- deref->unary.operand = lhs;
- if (lt && lt->kind == TYPE_POINTER)
- {
- deref->type_info = lt->inner;
- }
- arg1 = deref;
- }
- }
-
- call->call.args = arg1;
- arg1->next = rhs;
- rhs->next = NULL;
-
- call->type_info = sig->ret_type;
- call->resolved_type = type_to_string(sig->ret_type);
-
- lhs = call;
- continue; // Loop again with result as new lhs
- }
- }
- }
-
- // Standard Type Checking (if no overload found)
- if (lhs->type_info && rhs->type_info)
- {
- if (is_comparison_op(bin->binary.op))
- {
- bin->type_info = type_new(TYPE_INT); // bool
- 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)
- int skip_check = (strcmp(t1, "void*") == 0 || strcmp(t2, "void*") == 0);
-
- // Allow comparing pointers/strings with integer literal 0 (NULL)
- if (!skip_check)
- {
- int lhs_is_ptr =
- (lhs->type_info->kind == TYPE_POINTER ||
- lhs->type_info->kind == TYPE_STRING || (t1 && strstr(t1, "*")));
- int rhs_is_ptr =
- (rhs->type_info->kind == TYPE_POINTER ||
- rhs->type_info->kind == TYPE_STRING || (t2 && strstr(t2, "*")));
-
- if (lhs_is_ptr && rhs->type == NODE_EXPR_LITERAL && rhs->literal.int_val == 0)
- {
- skip_check = 1;
- }
- if (rhs_is_ptr && lhs->type == NODE_EXPR_LITERAL && lhs->literal.int_val == 0)
- {
- skip_check = 1;
- }
- }
-
- if (!skip_check && !type_eq(lhs->type_info, rhs->type_info))
- {
- char msg[256];
- sprintf(msg, "Type mismatch in comparison: cannot compare '%s' and '%s'", t1,
- t2);
-
- char suggestion[256];
- sprintf(suggestion, "Both operands must have compatible types for comparison");
-
- zpanic_with_suggestion(op, msg, suggestion);
- }
- }
- else
- {
- if (type_eq(lhs->type_info, rhs->type_info))
- {
- bin->type_info = lhs->type_info;
- }
- else
- {
- char *t1 = type_to_string(lhs->type_info);
- char *t2 = type_to_string(rhs->type_info);
-
- // Allow pointer arithmetic: ptr + int, ptr - int, int + ptr
- int is_ptr_arith = 0;
- if (strcmp(bin->binary.op, "+") == 0 || strcmp(bin->binary.op, "-") == 0)
- {
- int lhs_is_ptr = (lhs->type_info->kind == TYPE_POINTER ||
- lhs->type_info->kind == TYPE_STRING ||
- (t1 && strstr(t1, "*") != NULL));
- int rhs_is_ptr = (rhs->type_info->kind == TYPE_POINTER ||
- rhs->type_info->kind == TYPE_STRING ||
- (t2 && strstr(t2, "*") != NULL));
- int lhs_is_int =
- (lhs->type_info->kind == TYPE_INT || lhs->type_info->kind == TYPE_I32 ||
- lhs->type_info->kind == TYPE_I64 ||
- lhs->type_info->kind == TYPE_ISIZE ||
- lhs->type_info->kind == TYPE_USIZE ||
- (t1 && (strcmp(t1, "int") == 0 || strcmp(t1, "isize") == 0 ||
- strcmp(t1, "usize") == 0 || strcmp(t1, "size_t") == 0 ||
- strcmp(t1, "ptrdiff_t") == 0)));
- int rhs_is_int =
- (rhs->type_info->kind == TYPE_INT || rhs->type_info->kind == TYPE_I32 ||
- rhs->type_info->kind == TYPE_I64 ||
- rhs->type_info->kind == TYPE_ISIZE ||
- rhs->type_info->kind == TYPE_USIZE ||
- (t2 && (strcmp(t2, "int") == 0 || strcmp(t2, "isize") == 0 ||
- strcmp(t2, "usize") == 0 || strcmp(t2, "size_t") == 0 ||
- strcmp(t2, "ptrdiff_t") == 0)));
-
- if ((lhs_is_ptr && rhs_is_int) || (lhs_is_int && rhs_is_ptr))
- {
- is_ptr_arith = 1;
- bin->type_info = lhs_is_ptr ? lhs->type_info : rhs->type_info;
- }
- }
-
- if (!is_ptr_arith)
- {
- char msg[256];
- sprintf(msg, "Type mismatch in binary operation '%s'", bin->binary.op);
-
- char suggestion[512];
- sprintf(suggestion,
- "Left operand has type '%s', right operand has type '%s'\n = "
- "note: Consider casting one operand to match the other",
- t1, t2);
-
- zpanic_with_suggestion(op, msg, suggestion);
- }
- }
- }
- }
-
- lhs = bin;
- }
- return lhs;
+ } else {
+ free(set_name);
+ }
+ }
+ }
+ }
+
+ const char *method = get_operator_method(bin->binary.op);
+
+ if (method) {
+ Type *lt = lhs->type_info;
+ char *struct_name = NULL;
+ int is_lhs_ptr = 0;
+
+ if (lt) {
+ if (lt->kind == TYPE_STRUCT) {
+ struct_name = lt->name;
+ is_lhs_ptr = 0;
+ } else if (lt->kind == TYPE_POINTER && lt->inner->kind == TYPE_STRUCT) {
+ struct_name = lt->inner->name;
+ is_lhs_ptr = 1;
+ }
+ }
+
+ if (struct_name) {
+ char mangled[256];
+ sprintf(mangled, "%s_%s", struct_name, method);
+
+ FuncSig *sig = find_func(ctx, mangled);
+
+ if (sig) {
+ ASTNode *call = ast_create(NODE_EXPR_CALL);
+ ASTNode *callee = ast_create(NODE_EXPR_VAR);
+ callee->var_ref.name = xstrdup(mangled);
+ call->call.callee = callee;
+
+ ASTNode *arg1 = lhs;
+
+ // Check if function expects a pointer for 'self'
+ if (sig->total_args > 0 && sig->arg_types[0] &&
+ sig->arg_types[0]->kind == TYPE_POINTER) {
+ if (!is_lhs_ptr) {
+ // Value -> Pointer.
+ int is_rvalue =
+ (lhs->type == NODE_EXPR_CALL ||
+ lhs->type == NODE_EXPR_BINARY ||
+ lhs->type == NODE_EXPR_STRUCT_INIT ||
+ lhs->type == NODE_EXPR_CAST || lhs->type == NODE_MATCH);
+
+ ASTNode *addr = ast_create(NODE_EXPR_UNARY);
+ addr->unary.op = is_rvalue ? xstrdup("&_rval") : xstrdup("&");
+ addr->unary.operand = lhs;
+ addr->type_info = type_new_ptr(lt);
+ arg1 = addr;
+ }
+ } else {
+ // Function expects value
+ if (is_lhs_ptr) {
+ // Have pointer, need value -> *lhs
+ ASTNode *deref = ast_create(NODE_EXPR_UNARY);
+ deref->unary.op = xstrdup("*");
+ deref->unary.operand = lhs;
+ if (lt && lt->kind == TYPE_POINTER) {
+ deref->type_info = lt->inner;
+ }
+ arg1 = deref;
+ }
+ }
+
+ call->call.args = arg1;
+ arg1->next = rhs;
+ rhs->next = NULL;
+
+ call->type_info = sig->ret_type;
+ call->resolved_type = type_to_string(sig->ret_type);
+
+ lhs = call;
+ continue; // Loop again with result as new lhs
+ }
+ }
+ }
+
+ // Standard Type Checking (if no overload found)
+ if (lhs->type_info && rhs->type_info) {
+ if (is_comparison_op(bin->binary.op)) {
+ bin->type_info = type_new(TYPE_INT); // bool
+ 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)
+ int skip_check = (strcmp(t1, "void*") == 0 || strcmp(t2, "void*") == 0);
+
+ // Allow comparing pointers/strings with integer literal 0 (NULL)
+ if (!skip_check) {
+ int lhs_is_ptr =
+ (lhs->type_info->kind == TYPE_POINTER ||
+ lhs->type_info->kind == TYPE_STRING || (t1 && strstr(t1, "*")));
+ int rhs_is_ptr =
+ (rhs->type_info->kind == TYPE_POINTER ||
+ rhs->type_info->kind == TYPE_STRING || (t2 && strstr(t2, "*")));
+
+ if (lhs_is_ptr && rhs->type == NODE_EXPR_LITERAL &&
+ rhs->literal.int_val == 0) {
+ skip_check = 1;
+ }
+ if (rhs_is_ptr && lhs->type == NODE_EXPR_LITERAL &&
+ lhs->literal.int_val == 0) {
+ skip_check = 1;
+ }
+ }
+
+ if (!skip_check && !type_eq(lhs->type_info, rhs->type_info)) {
+ char msg[256];
+ sprintf(msg,
+ "Type mismatch in comparison: cannot compare '%s' and '%s'",
+ t1, t2);
+
+ char suggestion[256];
+ sprintf(suggestion,
+ "Both operands must have compatible types for comparison");
+
+ zpanic_with_suggestion(op, msg, suggestion);
+ }
+ } else {
+ if (type_eq(lhs->type_info, rhs->type_info)) {
+ bin->type_info = lhs->type_info;
+ } else {
+ char *t1 = type_to_string(lhs->type_info);
+ char *t2 = type_to_string(rhs->type_info);
+
+ // Allow pointer arithmetic: ptr + int, ptr - int, int + ptr
+ int is_ptr_arith = 0;
+ if (strcmp(bin->binary.op, "+") == 0 ||
+ strcmp(bin->binary.op, "-") == 0) {
+ int lhs_is_ptr = (lhs->type_info->kind == TYPE_POINTER ||
+ lhs->type_info->kind == TYPE_STRING ||
+ (t1 && strstr(t1, "*") != NULL));
+ int rhs_is_ptr = (rhs->type_info->kind == TYPE_POINTER ||
+ rhs->type_info->kind == TYPE_STRING ||
+ (t2 && strstr(t2, "*") != NULL));
+ int lhs_is_int =
+ (lhs->type_info->kind == TYPE_INT ||
+ lhs->type_info->kind == TYPE_I32 ||
+ lhs->type_info->kind == TYPE_I64 ||
+ lhs->type_info->kind == TYPE_ISIZE ||
+ lhs->type_info->kind == TYPE_USIZE ||
+ (t1 &&
+ (strcmp(t1, "int") == 0 || strcmp(t1, "isize") == 0 ||
+ strcmp(t1, "usize") == 0 || strcmp(t1, "size_t") == 0 ||
+ strcmp(t1, "ptrdiff_t") == 0)));
+ int rhs_is_int =
+ (rhs->type_info->kind == TYPE_INT ||
+ rhs->type_info->kind == TYPE_I32 ||
+ rhs->type_info->kind == TYPE_I64 ||
+ rhs->type_info->kind == TYPE_ISIZE ||
+ rhs->type_info->kind == TYPE_USIZE ||
+ (t2 &&
+ (strcmp(t2, "int") == 0 || strcmp(t2, "isize") == 0 ||
+ strcmp(t2, "usize") == 0 || strcmp(t2, "size_t") == 0 ||
+ strcmp(t2, "ptrdiff_t") == 0)));
+
+ if ((lhs_is_ptr && rhs_is_int) || (lhs_is_int && rhs_is_ptr)) {
+ is_ptr_arith = 1;
+ bin->type_info = lhs_is_ptr ? lhs->type_info : rhs->type_info;
+ }
+ }
+
+ if (!is_ptr_arith) {
+ char msg[256];
+ sprintf(msg, "Type mismatch in binary operation '%s'",
+ bin->binary.op);
+
+ char suggestion[512];
+ sprintf(
+ suggestion,
+ "Left operand has type '%s', right operand has type '%s'\n = "
+ "note: Consider casting one operand to match the other",
+ t1, t2);
+
+ zpanic_with_suggestion(op, msg, suggestion);
+ }
+ }
+ }
+ }
+
+ lhs = bin;
+ }
+ return lhs;
}
-ASTNode *parse_arrow_lambda_single(ParserContext *ctx, Lexer *l, char *param_name)
-{
- ASTNode *lambda = ast_create(NODE_LAMBDA);
- lambda->lambda.param_names = xmalloc(sizeof(char *));
- lambda->lambda.param_names[0] = param_name;
- lambda->lambda.num_params = 1;
-
- // Default param type: int
- lambda->lambda.param_types = xmalloc(sizeof(char *));
- lambda->lambda.param_types[0] = xstrdup("int");
-
- // Create Type Info: int -> int
- Type *t = type_new(TYPE_FUNCTION);
- t->inner = type_new(TYPE_INT); // Return
- t->args = xmalloc(sizeof(Type *));
- t->args[0] = type_new(TYPE_INT); // Arg
- t->arg_count = 1;
- lambda->type_info = t;
-
- // Body parsing...
- ASTNode *body_block = NULL;
- if (lexer_peek(l).type == TOK_LBRACE)
- {
- body_block = parse_block(ctx, l);
- }
- else
- {
- ASTNode *expr = parse_expression(ctx, l);
- ASTNode *ret = ast_create(NODE_RETURN);
- ret->ret.value = expr;
- body_block = ast_create(NODE_BLOCK);
- body_block->block.statements = ret;
- }
- lambda->lambda.body = body_block;
- lambda->lambda.return_type = xstrdup("int");
- lambda->lambda.lambda_id = ctx->lambda_counter++;
- lambda->lambda.is_expression = 1;
- register_lambda(ctx, lambda);
- analyze_lambda_captures(ctx, lambda);
- return lambda;
+ASTNode *parse_arrow_lambda_single(ParserContext *ctx, Lexer *l,
+ char *param_name) {
+ ASTNode *lambda = ast_create(NODE_LAMBDA);
+ lambda->lambda.param_names = xmalloc(sizeof(char *));
+ lambda->lambda.param_names[0] = param_name;
+ lambda->lambda.num_params = 1;
+
+ // Default param type: int
+ lambda->lambda.param_types = xmalloc(sizeof(char *));
+ lambda->lambda.param_types[0] = xstrdup("int");
+
+ // Create Type Info: int -> int
+ Type *t = type_new(TYPE_FUNCTION);
+ t->inner = type_new(TYPE_INT); // Return
+ t->args = xmalloc(sizeof(Type *));
+ t->args[0] = type_new(TYPE_INT); // Arg
+ t->arg_count = 1;
+ lambda->type_info = t;
+
+ // Body parsing...
+ ASTNode *body_block = NULL;
+ if (lexer_peek(l).type == TOK_LBRACE) {
+ body_block = parse_block(ctx, l);
+ } else {
+ ASTNode *expr = parse_expression(ctx, l);
+ ASTNode *ret = ast_create(NODE_RETURN);
+ ret->ret.value = expr;
+ body_block = ast_create(NODE_BLOCK);
+ body_block->block.statements = ret;
+ }
+ lambda->lambda.body = body_block;
+ lambda->lambda.return_type = xstrdup("int");
+ lambda->lambda.lambda_id = ctx->lambda_counter++;
+ lambda->lambda.is_expression = 1;
+ register_lambda(ctx, lambda);
+ analyze_lambda_captures(ctx, lambda);
+ return lambda;
}
-ASTNode *parse_arrow_lambda_multi(ParserContext *ctx, Lexer *l, char **param_names, int num_params)
-{
- ASTNode *lambda = ast_create(NODE_LAMBDA);
- lambda->lambda.param_names = param_names;
- lambda->lambda.num_params = num_params;
-
- // Type Info construction
- Type *t = type_new(TYPE_FUNCTION);
- t->inner = type_new(TYPE_INT);
- t->args = xmalloc(sizeof(Type *) * num_params);
- t->arg_count = num_params;
-
- lambda->lambda.param_types = xmalloc(sizeof(char *) * num_params);
- for (int i = 0; i < num_params; i++)
- {
- lambda->lambda.param_types[i] = xstrdup("int");
- t->args[i] = type_new(TYPE_INT);
- }
- lambda->type_info = t;
-
- // Body parsing...
- ASTNode *body_block = NULL;
- if (lexer_peek(l).type == TOK_LBRACE)
- {
- body_block = parse_block(ctx, l);
- }
- else
- {
- ASTNode *expr = parse_expression(ctx, l);
- ASTNode *ret = ast_create(NODE_RETURN);
- ret->ret.value = expr;
- body_block = ast_create(NODE_BLOCK);
- body_block->block.statements = ret;
- }
- lambda->lambda.body = body_block;
- lambda->lambda.return_type = xstrdup("int");
- lambda->lambda.lambda_id = ctx->lambda_counter++;
- lambda->lambda.is_expression = 1;
- register_lambda(ctx, lambda);
- analyze_lambda_captures(ctx, lambda);
- return lambda;
+ASTNode *parse_arrow_lambda_multi(ParserContext *ctx, Lexer *l,
+ char **param_names, int num_params) {
+ ASTNode *lambda = ast_create(NODE_LAMBDA);
+ lambda->lambda.param_names = param_names;
+ lambda->lambda.num_params = num_params;
+
+ // Type Info construction
+ Type *t = type_new(TYPE_FUNCTION);
+ t->inner = type_new(TYPE_INT);
+ t->args = xmalloc(sizeof(Type *) * num_params);
+ t->arg_count = num_params;
+
+ lambda->lambda.param_types = xmalloc(sizeof(char *) * num_params);
+ for (int i = 0; i < num_params; i++) {
+ lambda->lambda.param_types[i] = xstrdup("int");
+ t->args[i] = type_new(TYPE_INT);
+ }
+ lambda->type_info = t;
+
+ // Body parsing...
+ ASTNode *body_block = NULL;
+ if (lexer_peek(l).type == TOK_LBRACE) {
+ body_block = parse_block(ctx, l);
+ } else {
+ ASTNode *expr = parse_expression(ctx, l);
+ ASTNode *ret = ast_create(NODE_RETURN);
+ ret->ret.value = expr;
+ body_block = ast_create(NODE_BLOCK);
+ body_block->block.statements = ret;
+ }
+ lambda->lambda.body = body_block;
+ lambda->lambda.return_type = xstrdup("int");
+ lambda->lambda.lambda_id = ctx->lambda_counter++;
+ lambda->lambda.is_expression = 1;
+ register_lambda(ctx, lambda);
+ analyze_lambda_captures(ctx, lambda);
+ return lambda;
}
diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c
index 6f93dbf..ea87947 100644
--- a/src/parser/parser_stmt.c
+++ b/src/parser/parser_stmt.c
@@ -7,4098 +7,3473 @@
#include <unistd.h>
#include "../ast/ast.h"
+#include "../codegen/codegen.h"
#include "../plugins/plugin_manager.h"
#include "../zen/zen_facts.h"
#include "zprep_plugin.h"
-#include "../codegen/codegen.h"
static char *curr_func_ret = NULL;
char *run_comptime_block(ParserContext *ctx, Lexer *l);
-static void check_assignment_condition(ASTNode *cond)
-{
- if (!cond)
- {
- return;
- }
- if (cond->type == NODE_EXPR_BINARY)
- {
- if (cond->binary.op && strcmp(cond->binary.op, "=") == 0)
- {
- zwarn_at(cond->token, "Assignment in condition");
- fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "Did you mean '=='?\n");
- }
- }
+static void check_assignment_condition(ASTNode *cond) {
+ if (!cond) {
+ return;
+ }
+ if (cond->type == NODE_EXPR_BINARY) {
+ if (cond->binary.op && strcmp(cond->binary.op, "=") == 0) {
+ zwarn_at(cond->token, "Assignment in condition");
+ fprintf(stderr,
+ COLOR_CYAN " = note: " COLOR_RESET "Did you mean '=='?\n");
+ }
+ }
}
-ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async)
-{
- lexer_next(l); // eat 'fn'
- Token name_tok = lexer_next(l);
- char *name = token_strdup(name_tok);
+ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async) {
+ lexer_next(l); // eat 'fn'
+ Token name_tok = lexer_next(l);
+ char *name = token_strdup(name_tok);
- if (is_async)
- {
- ctx->has_async = 1;
- }
+ if (is_async) {
+ ctx->has_async = 1;
+ }
- // Check for C reserved word conflict
- if (is_c_reserved_word(name))
- {
- warn_c_reserved_word(name_tok, name);
- }
+ // Check for C reserved word conflict
+ if (is_c_reserved_word(name)) {
+ warn_c_reserved_word(name_tok, name);
+ }
- char *gen_param = NULL;
- if (lexer_peek(l).type == TOK_LANGLE)
- {
- lexer_next(l);
- Token gt = lexer_next(l);
- gen_param = token_strdup(gt);
- if (lexer_next(l).type != TOK_RANGLE)
- {
- zpanic_at(lexer_peek(l), "Expected >");
- }
+ char *gen_param = NULL;
+ if (lexer_peek(l).type == TOK_LANGLE) {
+ lexer_next(l);
+ Token gt = lexer_next(l);
+ gen_param = token_strdup(gt);
+ if (lexer_next(l).type != TOK_RANGLE) {
+ zpanic_at(lexer_peek(l), "Expected >");
}
+ }
- enter_scope(ctx);
- char **defaults;
- int count;
- Type **arg_types;
- char **param_names;
- int is_varargs = 0;
+ enter_scope(ctx);
+ char **defaults;
+ int count;
+ Type **arg_types;
+ char **param_names;
+ int is_varargs = 0;
- char *args =
- parse_and_convert_args(ctx, l, &defaults, &count, &arg_types, &param_names, &is_varargs);
+ char *args = parse_and_convert_args(ctx, l, &defaults, &count, &arg_types,
+ &param_names, &is_varargs);
- char *ret = "void";
- Type *ret_type_obj = type_new(TYPE_VOID);
+ char *ret = "void";
+ Type *ret_type_obj = type_new(TYPE_VOID);
- if (strcmp(name, "main") == 0)
- {
- ret = "int";
- ret_type_obj = type_new(TYPE_INT);
- }
+ if (strcmp(name, "main") == 0) {
+ ret = "int";
+ ret_type_obj = type_new(TYPE_INT);
+ }
- if (lexer_peek(l).type == TOK_ARROW)
- {
- lexer_next(l);
- ret_type_obj = parse_type_formal(ctx, l);
- ret = type_to_string(ret_type_obj);
- }
+ if (lexer_peek(l).type == TOK_ARROW) {
+ lexer_next(l);
+ ret_type_obj = parse_type_formal(ctx, l);
+ ret = type_to_string(ret_type_obj);
+ }
+
+ extern char *curr_func_ret;
+ curr_func_ret = ret;
+
+ // Auto-prefix function name if in module context
+ // Don't prefix generic templates or functions inside impl blocks (already
+ // mangled)
+ if (ctx->current_module_prefix && !gen_param && !ctx->current_impl_struct) {
+ char *prefixed_name =
+ xmalloc(strlen(ctx->current_module_prefix) + strlen(name) + 2);
+ sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name);
+ free(name);
+ name = prefixed_name;
+ }
+
+ // Register if concrete (Global functions only)
+ if (!gen_param && !ctx->current_impl_struct) {
+ register_func(ctx, name, count, defaults, arg_types, ret_type_obj,
+ is_varargs, is_async, name_tok);
+ // Note: must_use is set after return by caller (parser_core.c)
+ }
+
+ ASTNode *body = NULL;
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l); // consume ;
+ } else {
+ body = parse_block(ctx, l);
+ }
+
+ // Check for unused parameters
+ // The current scope contains arguments (since parse_block creates a new child
+ // scope for body) Only check if we parsed a body (not a prototype) function
+ if (body && ctx->current_scope) {
+ Symbol *sym = ctx->current_scope->symbols;
+ while (sym) {
+ // Check if unused and not prefixed with '_' (conventional ignore)
+ // also ignore 'self' as it is often mandated by traits
+ if (!sym->is_used && sym->name[0] != '_' &&
+ strcmp(sym->name, "self") != 0 && strcmp(name, "main") != 0) {
+ warn_unused_parameter(sym->decl_token, sym->name, name);
+ }
+ sym = sym->next;
+ }
+ }
+
+ exit_scope(ctx);
+ curr_func_ret = NULL;
+
+ ASTNode *node = ast_create(NODE_FUNCTION);
+ node->token = name_tok; // Save definition location
+ node->func.name = name;
+ node->func.args = args;
+ node->func.ret_type = ret;
+ node->func.body = body;
+
+ node->func.arg_types = arg_types;
+ node->func.param_names = param_names;
+ node->func.arg_count = count;
+ node->func.defaults = defaults;
+ node->func.ret_type_info = ret_type_obj;
+ node->func.is_varargs = is_varargs;
+
+ if (gen_param) {
+ register_func_template(ctx, name, gen_param, node);
+ return NULL;
+ }
+ if (!ctx->current_impl_struct) {
+ add_to_func_list(ctx, node);
+ }
+ return node;
+}
- extern char *curr_func_ret;
- curr_func_ret = ret;
+char *patch_self_args(const char *args, const char *struct_name) {
+ if (!args) {
+ return NULL;
+ }
+ char *new_args = xmalloc(strlen(args) + strlen(struct_name) + 10);
+
+ // Check if it starts with "void* self"
+ if (strncmp(args, "void* self", 10) == 0) {
+ sprintf(new_args, "%s* self%s", struct_name, args + 10);
+ } else {
+ strcpy(new_args, args);
+ }
+ return new_args;
+}
- // Auto-prefix function name if in module context
- // Don't prefix generic templates or functions inside impl blocks (already
- // mangled)
- if (ctx->current_module_prefix && !gen_param && !ctx->current_impl_struct)
- {
- char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(name) + 2);
- sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name);
- free(name);
- name = prefixed_name;
- }
+ASTNode *parse_match(ParserContext *ctx, Lexer *l) {
+ init_builtins();
+ Token start_token = lexer_peek(l);
+ lexer_next(l); // eat 'match'
+ ASTNode *expr = parse_expression(ctx, l);
- // Register if concrete (Global functions only)
- if (!gen_param && !ctx->current_impl_struct)
- {
- register_func(ctx, name, count, defaults, arg_types, ret_type_obj, is_varargs, is_async,
- name_tok);
- // Note: must_use is set after return by caller (parser_core.c)
- }
+ Token t_brace = lexer_next(l);
+ if (t_brace.type != TOK_LBRACE) {
+ zpanic_at(t_brace, "Expected { in match");
+ }
- ASTNode *body = NULL;
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l); // consume ;
+ ASTNode *h = 0, *tl = 0;
+ while (lexer_peek(l).type != TOK_RBRACE) {
+ skip_comments(l);
+ if (lexer_peek(l).type == TOK_RBRACE) {
+ break;
}
- else
- {
- body = parse_block(ctx, l);
+ if (lexer_peek(l).type == TOK_COMMA) {
+ lexer_next(l);
}
-
- // Check for unused parameters
- // The current scope contains arguments (since parse_block creates a new child
- // scope for body) Only check if we parsed a body (not a prototype) function
- if (body && ctx->current_scope)
- {
- Symbol *sym = ctx->current_scope->symbols;
- while (sym)
- {
- // Check if unused and not prefixed with '_' (conventional ignore)
- // also ignore 'self' as it is often mandated by traits
- if (!sym->is_used && sym->name[0] != '_' && strcmp(sym->name, "self") != 0 &&
- strcmp(name, "main") != 0)
- {
- warn_unused_parameter(sym->decl_token, sym->name, name);
- }
- sym = sym->next;
- }
+ skip_comments(l);
+ if (lexer_peek(l).type == TOK_RBRACE) {
+ break;
+ }
+
+ // --- 1. Parse Comma-Separated Patterns ---
+ char patterns_buf[1024];
+ patterns_buf[0] = 0;
+ int pattern_count = 0;
+
+ while (1) {
+ Token p = lexer_next(l);
+ char *p_str = token_strdup(p);
+
+ while (lexer_peek(l).type == TOK_DCOLON) {
+ lexer_next(l); // eat ::
+ Token suffix = lexer_next(l);
+ char *tmp = xmalloc(strlen(p_str) + suffix.len + 2);
+ // Join with underscore: Result::Ok -> Result_Ok
+ sprintf(tmp, "%s_%.*s", p_str, suffix.len, suffix.start);
+ free(p_str);
+ p_str = tmp;
+ }
+
+ if (pattern_count > 0) {
+ strcat(patterns_buf, ",");
+ }
+ strcat(patterns_buf, p_str);
+ free(p_str);
+ pattern_count++;
+
+ Lexer lookahead = *l;
+ skip_comments(&lookahead);
+ if (lexer_peek(&lookahead).type == TOK_COMMA) {
+ lexer_next(l); // eat comma
+ skip_comments(l);
+ } else {
+ break;
+ }
}
- exit_scope(ctx);
- curr_func_ret = NULL;
-
- ASTNode *node = ast_create(NODE_FUNCTION);
- node->token = name_tok; // Save definition location
- node->func.name = name;
- node->func.args = args;
- node->func.ret_type = ret;
- node->func.body = body;
-
- node->func.arg_types = arg_types;
- node->func.param_names = param_names;
- node->func.arg_count = count;
- node->func.defaults = defaults;
- node->func.ret_type_info = ret_type_obj;
- node->func.is_varargs = is_varargs;
-
- if (gen_param)
- {
- register_func_template(ctx, name, gen_param, node);
- return NULL;
- }
- if (!ctx->current_impl_struct)
- {
- add_to_func_list(ctx, node);
- }
- return node;
-}
+ char *pattern = xstrdup(patterns_buf);
+ int is_default = (strcmp(pattern, "_") == 0);
-char *patch_self_args(const char *args, const char *struct_name)
-{
- if (!args)
- {
- return NULL;
- }
- char *new_args = xmalloc(strlen(args) + strlen(struct_name) + 10);
+ char *binding = NULL;
+ int is_destructure = 0;
- // Check if it starts with "void* self"
- if (strncmp(args, "void* self", 10) == 0)
- {
- sprintf(new_args, "%s* self%s", struct_name, args + 10);
- }
- else
- {
- strcpy(new_args, args);
+ // --- 2. Handle Destructuring: Ok(v) ---
+ // (Only allowed if we matched a single pattern, e.g. "Result::Ok(val)")
+ if (!is_default && pattern_count == 1 && lexer_peek(l).type == TOK_LPAREN) {
+ lexer_next(l); // eat (
+ Token b = lexer_next(l);
+ if (b.type != TOK_IDENT) {
+ zpanic_at(b, "Expected variable name in pattern");
+ }
+ binding = token_strdup(b);
+ if (lexer_next(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected )");
+ }
+ is_destructure = 1;
}
- return new_args;
-}
-
-ASTNode *parse_match(ParserContext *ctx, Lexer *l)
-{
- init_builtins();
- Token start_token = lexer_peek(l);
- lexer_next(l); // eat 'match'
- ASTNode *expr = parse_expression(ctx, l);
- Token t_brace = lexer_next(l);
- if (t_brace.type != TOK_LBRACE)
- {
- zpanic_at(t_brace, "Expected { in match");
+ // --- 3. Parse Guard (if condition) ---
+ ASTNode *guard = NULL;
+ if (lexer_peek(l).type == TOK_IDENT &&
+ strncmp(lexer_peek(l).start, "if", 2) == 0) {
+ lexer_next(l);
+ guard = parse_expression(ctx, l);
+ check_assignment_condition(guard);
}
- ASTNode *h = 0, *tl = 0;
- while (lexer_peek(l).type != TOK_RBRACE)
- {
- skip_comments(l);
- if (lexer_peek(l).type == TOK_RBRACE)
- {
- break;
- }
- if (lexer_peek(l).type == TOK_COMMA)
- {
- lexer_next(l);
- }
- skip_comments(l);
- if (lexer_peek(l).type == TOK_RBRACE)
- {
- break;
- }
-
- // --- 1. Parse Comma-Separated Patterns ---
- char patterns_buf[1024];
- patterns_buf[0] = 0;
- int pattern_count = 0;
-
- while (1)
- {
- Token p = lexer_next(l);
- char *p_str = token_strdup(p);
-
- while (lexer_peek(l).type == TOK_DCOLON)
- {
- lexer_next(l); // eat ::
- Token suffix = lexer_next(l);
- char *tmp = xmalloc(strlen(p_str) + suffix.len + 2);
- // Join with underscore: Result::Ok -> Result_Ok
- sprintf(tmp, "%s_%.*s", p_str, suffix.len, suffix.start);
- free(p_str);
- p_str = tmp;
- }
-
- if (pattern_count > 0)
- {
- strcat(patterns_buf, ",");
- }
- strcat(patterns_buf, p_str);
- free(p_str);
- pattern_count++;
-
- Lexer lookahead = *l;
- skip_comments(&lookahead);
- if (lexer_peek(&lookahead).type == TOK_COMMA)
- {
- lexer_next(l); // eat comma
- skip_comments(l);
- }
- else
- {
- break;
- }
- }
-
- char *pattern = xstrdup(patterns_buf);
- int is_default = (strcmp(pattern, "_") == 0);
-
- char *binding = NULL;
- int is_destructure = 0;
-
- // --- 2. Handle Destructuring: Ok(v) ---
- // (Only allowed if we matched a single pattern, e.g. "Result::Ok(val)")
- if (!is_default && pattern_count == 1 && lexer_peek(l).type == TOK_LPAREN)
- {
- lexer_next(l); // eat (
- Token b = lexer_next(l);
- if (b.type != TOK_IDENT)
- {
- zpanic_at(b, "Expected variable name in pattern");
- }
- binding = token_strdup(b);
- if (lexer_next(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected )");
- }
- is_destructure = 1;
- }
-
- // --- 3. Parse Guard (if condition) ---
- ASTNode *guard = NULL;
- if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "if", 2) == 0)
- {
- lexer_next(l);
- guard = parse_expression(ctx, l);
- check_assignment_condition(guard);
- }
-
- if (lexer_next(l).type != TOK_ARROW)
- {
- zpanic_at(lexer_peek(l), "Expected =>");
- }
-
- ASTNode *body;
- Token pk = lexer_peek(l);
- if (pk.type == TOK_LBRACE)
- {
- body = parse_block(ctx, l);
- }
- else if (pk.type == TOK_ASSERT ||
- (pk.type == TOK_IDENT && strncmp(pk.start, "assert", 6) == 0))
- {
- body = parse_assert(ctx, l);
- }
- else if (pk.type == TOK_IDENT && strncmp(pk.start, "return", 6) == 0)
- {
- body = parse_return(ctx, l);
- }
- else
- {
- body = parse_expression(ctx, l);
- }
-
- ASTNode *c = ast_create(NODE_MATCH_CASE);
- c->match_case.pattern = pattern;
- c->match_case.binding_name = binding;
- c->match_case.is_destructuring = is_destructure;
- c->match_case.guard = guard;
- c->match_case.body = body;
- c->match_case.is_default = is_default;
-
- if (!h)
- {
- h = c;
- }
- else
- {
- tl->next = c;
- }
- tl = c;
+ if (lexer_next(l).type != TOK_ARROW) {
+ zpanic_at(lexer_peek(l), "Expected =>");
}
- lexer_next(l); // eat }
- ASTNode *n = ast_create(NODE_MATCH);
- n->line = start_token.line;
- n->token = start_token; // Capture token for rich warning
- n->match_stmt.expr = expr;
- n->match_stmt.cases = h;
- return n;
+ ASTNode *body;
+ Token pk = lexer_peek(l);
+ if (pk.type == TOK_LBRACE) {
+ body = parse_block(ctx, l);
+ } else if (pk.type == TOK_ASSERT ||
+ (pk.type == TOK_IDENT && strncmp(pk.start, "assert", 6) == 0)) {
+ body = parse_assert(ctx, l);
+ } else if (pk.type == TOK_IDENT && strncmp(pk.start, "return", 6) == 0) {
+ body = parse_return(ctx, l);
+ } else {
+ body = parse_expression(ctx, l);
+ }
+
+ ASTNode *c = ast_create(NODE_MATCH_CASE);
+ c->match_case.pattern = pattern;
+ c->match_case.binding_name = binding;
+ c->match_case.is_destructuring = is_destructure;
+ c->match_case.guard = guard;
+ c->match_case.body = body;
+ c->match_case.is_default = is_default;
+
+ if (!h) {
+ h = c;
+ } else {
+ tl->next = c;
+ }
+ tl = c;
+ }
+ lexer_next(l); // eat }
+
+ ASTNode *n = ast_create(NODE_MATCH);
+ n->line = start_token.line;
+ n->token = start_token; // Capture token for rich warning
+ n->match_stmt.expr = expr;
+ n->match_stmt.cases = h;
+ return n;
}
-ASTNode *parse_loop(ParserContext *ctx, Lexer *l)
-{
- lexer_next(l);
- ASTNode *b = parse_block(ctx, l);
- ASTNode *n = ast_create(NODE_LOOP);
- n->loop_stmt.body = b;
- return n;
+ASTNode *parse_loop(ParserContext *ctx, Lexer *l) {
+ lexer_next(l);
+ ASTNode *b = parse_block(ctx, l);
+ ASTNode *n = ast_create(NODE_LOOP);
+ n->loop_stmt.body = b;
+ return n;
}
-ASTNode *parse_repeat(ParserContext *ctx, Lexer *l)
-{
- lexer_next(l);
- char *c = rewrite_expr_methods(ctx, parse_condition_raw(ctx, l));
- ASTNode *b = parse_block(ctx, l);
- ASTNode *n = ast_create(NODE_REPEAT);
- n->repeat_stmt.count = c;
- n->repeat_stmt.body = b;
- return n;
+ASTNode *parse_repeat(ParserContext *ctx, Lexer *l) {
+ lexer_next(l);
+ char *c = rewrite_expr_methods(ctx, parse_condition_raw(ctx, l));
+ ASTNode *b = parse_block(ctx, l);
+ ASTNode *n = ast_create(NODE_REPEAT);
+ n->repeat_stmt.count = c;
+ n->repeat_stmt.body = b;
+ return n;
}
-ASTNode *parse_unless(ParserContext *ctx, Lexer *l)
-{
- lexer_next(l);
- ASTNode *cond = parse_expression(ctx, l);
- ASTNode *body = parse_block(ctx, l);
- ASTNode *n = ast_create(NODE_UNLESS);
- n->unless_stmt.condition = cond;
- n->unless_stmt.body = body;
- return n;
+ASTNode *parse_unless(ParserContext *ctx, Lexer *l) {
+ lexer_next(l);
+ ASTNode *cond = parse_expression(ctx, l);
+ ASTNode *body = parse_block(ctx, l);
+ ASTNode *n = ast_create(NODE_UNLESS);
+ n->unless_stmt.condition = cond;
+ n->unless_stmt.body = body;
+ return n;
}
-ASTNode *parse_guard(ParserContext *ctx, Lexer *l)
-{
- lexer_next(l); // consume 'guard'
-
- // Parse the condition as an AST
- ASTNode *cond = parse_expression(ctx, l);
-
- // Check for 'else'
- Token t = lexer_peek(l);
- if (t.type != TOK_IDENT || strncmp(t.start, "else", 4) != 0)
- {
- zpanic_at(t, "Expected 'else' after guard condition");
- }
- lexer_next(l); // consume 'else'
-
- // Parse the body - either a block or a single statement
- ASTNode *body;
- if (lexer_peek(l).type == TOK_LBRACE)
- {
- body = parse_block(ctx, l);
- }
- else
- {
- // Single statement (e.g., guard x != NULL else return;)
- body = parse_statement(ctx, l);
- }
-
- // Create the node
- ASTNode *n = ast_create(NODE_GUARD);
- n->guard_stmt.condition = cond;
- n->guard_stmt.body = body;
- return n;
+ASTNode *parse_guard(ParserContext *ctx, Lexer *l) {
+ lexer_next(l); // consume 'guard'
+
+ // Parse the condition as an AST
+ ASTNode *cond = parse_expression(ctx, l);
+
+ // Check for 'else'
+ Token t = lexer_peek(l);
+ if (t.type != TOK_IDENT || strncmp(t.start, "else", 4) != 0) {
+ zpanic_at(t, "Expected 'else' after guard condition");
+ }
+ lexer_next(l); // consume 'else'
+
+ // Parse the body - either a block or a single statement
+ ASTNode *body;
+ if (lexer_peek(l).type == TOK_LBRACE) {
+ body = parse_block(ctx, l);
+ } else {
+ // Single statement (e.g., guard x != NULL else return;)
+ body = parse_statement(ctx, l);
+ }
+
+ // Create the node
+ ASTNode *n = ast_create(NODE_GUARD);
+ n->guard_stmt.condition = cond;
+ n->guard_stmt.body = body;
+ return n;
}
-ASTNode *parse_defer(ParserContext *ctx, Lexer *l)
-{
- lexer_next(l); // defer
- ASTNode *s;
- if (lexer_peek(l).type == TOK_LBRACE)
- {
- s = parse_block(ctx, l);
- }
- else
- {
- s = ast_create(NODE_RAW_STMT);
- s->raw_stmt.content = consume_and_rewrite(ctx, l);
- }
- ASTNode *n = ast_create(NODE_DEFER);
- n->defer_stmt.stmt = s;
- return n;
+ASTNode *parse_defer(ParserContext *ctx, Lexer *l) {
+ lexer_next(l); // defer
+ ASTNode *s;
+ if (lexer_peek(l).type == TOK_LBRACE) {
+ s = parse_block(ctx, l);
+ } else {
+ s = ast_create(NODE_RAW_STMT);
+ s->raw_stmt.content = consume_and_rewrite(ctx, l);
+ }
+ ASTNode *n = ast_create(NODE_DEFER);
+ n->defer_stmt.stmt = s;
+ return n;
}
-ASTNode *parse_asm(ParserContext *ctx, Lexer *l)
-{
- (void)ctx; // suppress unused parameter warning
- Token t = lexer_peek(l);
- zen_trigger_at(TRIGGER_ASM, t);
- lexer_next(l); // eat 'asm'
+ASTNode *parse_asm(ParserContext *ctx, Lexer *l) {
+ (void)ctx; // suppress unused parameter warning
+ Token t = lexer_peek(l);
+ zen_trigger_at(TRIGGER_ASM, t);
+ lexer_next(l); // eat 'asm'
- // Check for 'volatile'
- int is_volatile = 0;
- if (lexer_peek(l).type == TOK_VOLATILE)
- {
- is_volatile = 1;
- lexer_next(l);
- }
-
- // Expect {
- if (lexer_peek(l).type != TOK_LBRACE)
- {
- zpanic_at(lexer_peek(l), "Expected { after asm");
- }
+ // Check for 'volatile'
+ int is_volatile = 0;
+ if (lexer_peek(l).type == TOK_VOLATILE) {
+ is_volatile = 1;
lexer_next(l);
+ }
- // Parse assembly template strings
- char *code = xmalloc(4096); // Buffer for assembly code
- code[0] = 0;
+ // Expect {
+ if (lexer_peek(l).type != TOK_LBRACE) {
+ zpanic_at(lexer_peek(l), "Expected { after asm");
+ }
+ lexer_next(l);
- while (1)
- {
- Token t = lexer_peek(l);
+ // Parse assembly template strings
+ char *code = xmalloc(4096); // Buffer for assembly code
+ code[0] = 0;
- // Check for end of asm block or start of operands
- if (t.type == TOK_RBRACE)
- {
- break;
- }
- if (t.type == TOK_COLON)
- {
- break;
- }
+ while (1) {
+ Token t = lexer_peek(l);
- // Support string literals for assembly instructions
- if (t.type == TOK_STRING)
- {
- lexer_next(l);
- // Extract string content (strip quotes)
- int str_len = t.len - 2;
- if (strlen(code) > 0)
- {
- strcat(code, "\n");
- }
- strncat(code, t.start + 1, str_len);
- }
- // Also support bare identifiers for simple instructions like 'nop', 'pause'
- else if (t.type == TOK_IDENT)
- {
+ // Check for end of asm block or start of operands
+ if (t.type == TOK_RBRACE) {
+ break;
+ }
+ if (t.type == TOK_COLON) {
+ break;
+ }
+
+ // Support string literals for assembly instructions
+ if (t.type == TOK_STRING) {
+ lexer_next(l);
+ // Extract string content (strip quotes)
+ int str_len = t.len - 2;
+ if (strlen(code) > 0) {
+ strcat(code, "\n");
+ }
+ strncat(code, t.start + 1, str_len);
+ }
+ // Also support bare identifiers for simple instructions like 'nop', 'pause'
+ else if (t.type == TOK_IDENT) {
+ lexer_next(l);
+ if (strlen(code) > 0) {
+ strcat(code, "\n");
+ }
+ strncat(code, t.start, t.len);
+
+ // Check for instruction arguments
+ while (lexer_peek(l).type != TOK_RBRACE &&
+ lexer_peek(l).type != TOK_COLON) {
+ Token arg = lexer_peek(l);
+
+ if (arg.type == TOK_SEMICOLON) {
+ lexer_next(l);
+ break;
+ }
+
+ // Handle substitution {var}
+ if (arg.type == TOK_LBRACE) {
+ lexer_next(l);
+ strcat(code, "{");
+ // Consume until }
+ while (lexer_peek(l).type != TOK_RBRACE &&
+ lexer_peek(l).type != TOK_EOF) {
+ Token sub = lexer_next(l);
+ strncat(code, sub.start, sub.len);
+ }
+ if (lexer_peek(l).type == TOK_RBRACE) {
lexer_next(l);
- if (strlen(code) > 0)
- {
- strcat(code, "\n");
- }
- strncat(code, t.start, t.len);
-
- // Check for instruction arguments
- while (lexer_peek(l).type != TOK_RBRACE && lexer_peek(l).type != TOK_COLON)
- {
- Token arg = lexer_peek(l);
-
- if (arg.type == TOK_SEMICOLON)
- {
- lexer_next(l);
- break;
- }
-
- // Handle substitution {var}
- if (arg.type == TOK_LBRACE)
- {
- lexer_next(l);
- strcat(code, "{");
- // Consume until }
- while (lexer_peek(l).type != TOK_RBRACE && lexer_peek(l).type != TOK_EOF)
- {
- Token sub = lexer_next(l);
- strncat(code, sub.start, sub.len);
- }
- if (lexer_peek(l).type == TOK_RBRACE)
- {
- lexer_next(l);
- strcat(code, "}");
- }
- continue;
- }
-
- if (arg.type == TOK_IDENT)
- {
- // Check prev char for % or $
- char last_char = 0;
- size_t clen = strlen(code);
- if (clen > 0)
- {
- if (code[clen - 1] == ' ' && clen > 1)
- {
- last_char = code[clen - 2];
- }
- else
- {
- last_char = code[clen - 1];
- }
- }
- if (last_char != '%' && last_char != '$' && last_char != ',')
- {
- break;
- }
- }
-
- lexer_next(l);
-
- // No space logic
- int no_space = 0;
- size_t clen = strlen(code);
- if (clen > 0)
- {
- char lc = code[clen - 1];
- if (lc == '%' || lc == '$')
- {
- no_space = 1;
- }
- }
-
- if (!no_space)
- {
- strcat(code, " ");
- }
- strncat(code, arg.start, arg.len);
- }
- }
- else
- {
- zpanic_at(t, "Expected assembly string, instruction, or ':' in asm block");
+ strcat(code, "}");
+ }
+ continue;
+ }
+
+ if (arg.type == TOK_IDENT) {
+ // Check prev char for % or $
+ char last_char = 0;
+ size_t clen = strlen(code);
+ if (clen > 0) {
+ if (code[clen - 1] == ' ' && clen > 1) {
+ last_char = code[clen - 2];
+ } else {
+ last_char = code[clen - 1];
+ }
+ }
+ if (last_char != '%' && last_char != '$' && last_char != ',') {
+ break;
+ }
}
- }
-
- // Parse outputs (: out(x), inout(y))
- char **outputs = NULL;
- char **output_modes = NULL;
- int num_outputs = 0;
- if (lexer_peek(l).type == TOK_COLON)
- {
- lexer_next(l); // eat :
-
- outputs = xmalloc(sizeof(char *) * 16);
- output_modes = xmalloc(sizeof(char *) * 16);
-
- while (1)
- {
- Token t = lexer_peek(l);
- if (t.type == TOK_COLON || t.type == TOK_RBRACE)
- {
- break;
- }
- if (t.type == TOK_COMMA)
- {
- lexer_next(l);
- continue;
- }
+ lexer_next(l);
- // Parse out(var) or inout(var)
- if (t.type == TOK_IDENT)
- {
- char *mode = token_strdup(t);
- lexer_next(l);
-
- if (lexer_peek(l).type != TOK_LPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected ( after output mode");
- }
- lexer_next(l);
-
- Token var = lexer_next(l);
- if (var.type != TOK_IDENT)
- {
- zpanic_at(var, "Expected variable name");
- }
-
- if (lexer_peek(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected ) after variable");
- }
- lexer_next(l);
-
- outputs[num_outputs] = token_strdup(var);
- output_modes[num_outputs] = mode;
- num_outputs++;
- }
- else
- {
- break;
- }
+ // No space logic
+ int no_space = 0;
+ size_t clen = strlen(code);
+ if (clen > 0) {
+ char lc = code[clen - 1];
+ if (lc == '%' || lc == '$') {
+ no_space = 1;
+ }
}
- }
-
- // Parse inputs (: in(a), in(b))
- char **inputs = NULL;
- int num_inputs = 0;
-
- if (lexer_peek(l).type == TOK_COLON)
- {
- lexer_next(l); // eat :
-
- inputs = xmalloc(sizeof(char *) * 16);
- while (1)
- {
- Token t = lexer_peek(l);
- if (t.type == TOK_COLON || t.type == TOK_RBRACE)
- {
- break;
- }
- if (t.type == TOK_COMMA)
- {
- lexer_next(l);
- continue;
- }
-
- // Parse in(var)
- if (t.type == TOK_IDENT && strncmp(t.start, "in", 2) == 0)
- {
- lexer_next(l);
-
- if (lexer_peek(l).type != TOK_LPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected ( after in");
- }
- lexer_next(l);
-
- Token var = lexer_next(l);
- if (var.type != TOK_IDENT)
- {
- zpanic_at(var, "Expected variable name");
- }
-
- if (lexer_peek(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected ) after variable");
- }
- lexer_next(l);
-
- inputs[num_inputs] = token_strdup(var);
- num_inputs++;
- }
- else
- {
- break;
- }
+ if (!no_space) {
+ strcat(code, " ");
}
+ strncat(code, arg.start, arg.len);
+ }
+ } else {
+ zpanic_at(t,
+ "Expected assembly string, instruction, or ':' in asm block");
}
+ }
- // Parse clobbers (: "eax", "memory")
- char **clobbers = NULL;
- int num_clobbers = 0;
+ // Parse outputs (: out(x), inout(y))
+ char **outputs = NULL;
+ char **output_modes = NULL;
+ int num_outputs = 0;
- if (lexer_peek(l).type == TOK_COLON)
- {
- lexer_next(l); // eat :
+ if (lexer_peek(l).type == TOK_COLON) {
+ lexer_next(l); // eat :
- clobbers = xmalloc(sizeof(char *) * 16);
+ outputs = xmalloc(sizeof(char *) * 16);
+ output_modes = xmalloc(sizeof(char *) * 16);
- while (1)
- {
- Token t = lexer_peek(l);
- if (t.type == TOK_RBRACE)
- {
- break;
- }
- if (t.type == TOK_COMMA)
- {
- lexer_next(l);
- continue;
- }
+ while (1) {
+ Token t = lexer_peek(l);
+ if (t.type == TOK_COLON || t.type == TOK_RBRACE) {
+ break;
+ }
+ if (t.type == TOK_COMMA) {
+ lexer_next(l);
+ continue;
+ }
- if (t.type == TOK_STRING)
- {
- lexer_next(l);
- // Extract string content
- char *clob = xmalloc(t.len);
- strncpy(clob, t.start + 1, t.len - 2);
- clob[t.len - 2] = 0;
- clobbers[num_clobbers++] = clob;
- }
- else
- {
- break;
- }
- }
- }
+ // Parse out(var) or inout(var)
+ if (t.type == TOK_IDENT) {
+ char *mode = token_strdup(t);
+ lexer_next(l);
- // Expect closing }
- if (lexer_peek(l).type != TOK_RBRACE)
- {
- zpanic_at(lexer_peek(l), "Expected } at end of asm block");
- }
- lexer_next(l);
+ if (lexer_peek(l).type != TOK_LPAREN) {
+ zpanic_at(lexer_peek(l), "Expected ( after output mode");
+ }
+ lexer_next(l);
- // Create AST node
- ASTNode *n = ast_create(NODE_ASM);
- n->asm_stmt.code = code;
- n->asm_stmt.is_volatile = is_volatile;
- n->asm_stmt.outputs = outputs;
- n->asm_stmt.output_modes = output_modes;
- n->asm_stmt.inputs = inputs;
- n->asm_stmt.clobbers = clobbers;
- n->asm_stmt.num_outputs = num_outputs;
- n->asm_stmt.num_inputs = num_inputs;
- n->asm_stmt.num_clobbers = num_clobbers;
+ Token var = lexer_next(l);
+ if (var.type != TOK_IDENT) {
+ zpanic_at(var, "Expected variable name");
+ }
- return n;
-}
+ if (lexer_peek(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected ) after variable");
+ }
+ lexer_next(l);
-ASTNode *parse_test(ParserContext *ctx, Lexer *l)
-{
- lexer_next(l); // eat 'test'
- Token t = lexer_next(l);
- if (t.type != TOK_STRING)
- {
- zpanic_at(t, "Test name must be a string literal");
+ outputs[num_outputs] = token_strdup(var);
+ output_modes[num_outputs] = mode;
+ num_outputs++;
+ } else {
+ break;
+ }
}
+ }
- // Strip quotes for AST storage
- char *name = xmalloc(t.len);
- strncpy(name, t.start + 1, t.len - 2);
- name[t.len - 2] = 0;
+ // Parse inputs (: in(a), in(b))
+ char **inputs = NULL;
+ int num_inputs = 0;
- ASTNode *body = parse_block(ctx, l);
+ if (lexer_peek(l).type == TOK_COLON) {
+ lexer_next(l); // eat :
- ASTNode *n = ast_create(NODE_TEST);
- n->test_stmt.name = name;
- n->test_stmt.body = body;
- return n;
-}
+ inputs = xmalloc(sizeof(char *) * 16);
-ASTNode *parse_assert(ParserContext *ctx, Lexer *l)
-{
- lexer_next(l); // assert
- if (lexer_peek(l).type == TOK_LPAREN)
- {
- lexer_next(l); // optional paren? usually yes
- }
-
- ASTNode *cond = parse_expression(ctx, l);
-
- char *msg = NULL;
- if (lexer_peek(l).type == TOK_COMMA)
- {
+ while (1) {
+ Token t = lexer_peek(l);
+ if (t.type == TOK_COLON || t.type == TOK_RBRACE) {
+ break;
+ }
+ if (t.type == TOK_COMMA) {
lexer_next(l);
- Token st = lexer_next(l);
- if (st.type != TOK_STRING)
- {
- zpanic_at(st, "Expected message string");
- }
- msg = xmalloc(st.len + 1);
- strncpy(msg, st.start, st.len);
- msg[st.len] = 0;
- }
+ continue;
+ }
- if (lexer_peek(l).type == TOK_RPAREN)
- {
+ // Parse in(var)
+ if (t.type == TOK_IDENT && strncmp(t.start, "in", 2) == 0) {
lexer_next(l);
- }
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- }
-
- ASTNode *n = ast_create(NODE_ASSERT);
- n->assert_stmt.condition = cond;
- n->assert_stmt.message = msg;
- return n;
-}
-// Helper for Value-Returning Defer
-static void replace_it_with_var(ASTNode *node, char *var_name)
-{
- if (!node)
- {
- return;
- }
- if (node->type == NODE_EXPR_VAR)
- {
- if (strcmp(node->var_ref.name, "it") == 0)
- {
- // Replace 'it' with var_name
- node->var_ref.name = xstrdup(var_name);
- }
- }
- else if (node->type == NODE_EXPR_CALL)
- {
- replace_it_with_var(node->call.callee, var_name);
- ASTNode *arg = node->call.args;
- while (arg)
- {
- replace_it_with_var(arg, var_name);
- arg = arg->next;
- }
- }
- else if (node->type == NODE_EXPR_MEMBER)
- {
- replace_it_with_var(node->member.target, var_name);
- }
- else if (node->type == NODE_EXPR_BINARY)
- {
- replace_it_with_var(node->binary.left, var_name);
- replace_it_with_var(node->binary.right, var_name);
- }
- else if (node->type == NODE_EXPR_UNARY)
- {
- replace_it_with_var(node->unary.operand, var_name);
- }
- else if (node->type == NODE_BLOCK)
- {
- ASTNode *s = node->block.statements;
- while (s)
- {
- replace_it_with_var(s, var_name);
- s = s->next;
+ if (lexer_peek(l).type != TOK_LPAREN) {
+ zpanic_at(lexer_peek(l), "Expected ( after in");
}
- }
-}
-
-ASTNode *parse_var_decl(ParserContext *ctx, Lexer *l)
-{
- lexer_next(l); // eat 'var'
-
- // Check for 'mut' keyword
- int is_mutable = 0;
- if (lexer_peek(l).type == TOK_MUT)
- {
- is_mutable = 1;
lexer_next(l);
- }
- else
- {
- // Default mutability depends on directive
- is_mutable = !ctx->immutable_by_default;
- }
- // Destructuring: var {x, y} = ...
- if (lexer_peek(l).type == TOK_LBRACE || lexer_peek(l).type == TOK_LPAREN)
- {
- int is_struct = (lexer_peek(l).type == TOK_LBRACE);
- lexer_next(l);
- char **names = xmalloc(16 * sizeof(char *));
- int count = 0;
- while (1)
- {
- Token t = lexer_next(l);
- char *nm = token_strdup(t);
- // UPDATE: Pass NULL to add_symbol
- names[count++] = nm;
- add_symbol(ctx, nm, "unknown", NULL);
- // Register mutability for each destructured variable
- register_var_mutability(ctx, nm, is_mutable);
- Token next = lexer_next(l);
- if (next.type == (is_struct ? TOK_RBRACE : TOK_RPAREN))
- {
- break;
- }
- if (next.type != TOK_COMMA)
- {
- zpanic_at(next, "Expected comma");
- }
- }
- if (lexer_next(l).type != TOK_OP)
- {
- zpanic_at(lexer_peek(l), "Expected =");
+ Token var = lexer_next(l);
+ if (var.type != TOK_IDENT) {
+ zpanic_at(var, "Expected variable name");
}
- ASTNode *init = parse_expression(ctx, l);
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
+
+ if (lexer_peek(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected ) after variable");
}
- ASTNode *n = ast_create(NODE_DESTRUCT_VAR);
- n->destruct.names = names;
- n->destruct.count = count;
- n->destruct.init_expr = init;
- n->destruct.is_struct_destruct = is_struct;
- return n;
+ lexer_next(l);
+
+ inputs[num_inputs] = token_strdup(var);
+ num_inputs++;
+ } else {
+ break;
+ }
}
+ }
- // Normal Declaration OR Named Struct Destructuring
- Token name_tok = lexer_next(l);
- char *name = token_strdup(name_tok);
+ // Parse clobbers (: "eax", "memory")
+ char **clobbers = NULL;
+ int num_clobbers = 0;
- // Check for Struct Destructuring: var Point { x, y }
- if (lexer_peek(l).type == TOK_LBRACE)
- {
- lexer_next(l); // eat {
- char **names = xmalloc(16 * sizeof(char *));
- char **fields = xmalloc(16 * sizeof(char *));
- int count = 0;
-
- while (1)
- {
- // Parse field:name or just name
- Token t = lexer_next(l);
- char *ident = token_strdup(t);
-
- if (lexer_peek(l).type == TOK_COLON)
- {
- // field: var_name
- lexer_next(l); // eat :
- Token v = lexer_next(l);
- fields[count] = ident;
- names[count] = token_strdup(v);
- }
- else
- {
- // Shorthand: field (implies var name = field)
- fields[count] = ident;
- names[count] = ident; // Share pointer or duplicate? duplicate safer if we free
- }
- // Register symbol for variable
- add_symbol(ctx, names[count], "unknown", NULL);
- register_var_mutability(ctx, names[count], is_mutable);
+ if (lexer_peek(l).type == TOK_COLON) {
+ lexer_next(l); // eat :
- count++;
+ clobbers = xmalloc(sizeof(char *) * 16);
- Token next = lexer_next(l);
- if (next.type == TOK_RBRACE)
- {
- break;
- }
- if (next.type != TOK_COMMA)
- {
- zpanic_at(next, "Expected comma in struct pattern");
- }
- }
+ while (1) {
+ Token t = lexer_peek(l);
+ if (t.type == TOK_RBRACE) {
+ break;
+ }
+ if (t.type == TOK_COMMA) {
+ lexer_next(l);
+ continue;
+ }
- if (lexer_next(l).type != TOK_OP)
- {
- zpanic_at(lexer_peek(l), "Expected =");
- }
- ASTNode *init = parse_expression(ctx, l);
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- }
+ if (t.type == TOK_STRING) {
+ lexer_next(l);
+ // Extract string content
+ char *clob = xmalloc(t.len);
+ strncpy(clob, t.start + 1, t.len - 2);
+ clob[t.len - 2] = 0;
+ clobbers[num_clobbers++] = clob;
+ } else {
+ break;
+ }
+ }
+ }
+
+ // Expect closing }
+ if (lexer_peek(l).type != TOK_RBRACE) {
+ zpanic_at(lexer_peek(l), "Expected } at end of asm block");
+ }
+ lexer_next(l);
+
+ // Create AST node
+ ASTNode *n = ast_create(NODE_ASM);
+ n->asm_stmt.code = code;
+ n->asm_stmt.is_volatile = is_volatile;
+ n->asm_stmt.outputs = outputs;
+ n->asm_stmt.output_modes = output_modes;
+ n->asm_stmt.inputs = inputs;
+ n->asm_stmt.clobbers = clobbers;
+ n->asm_stmt.num_outputs = num_outputs;
+ n->asm_stmt.num_inputs = num_inputs;
+ n->asm_stmt.num_clobbers = num_clobbers;
+
+ return n;
+}
- ASTNode *n = ast_create(NODE_DESTRUCT_VAR);
- n->destruct.names = names;
- n->destruct.field_names = fields;
- n->destruct.count = count;
- n->destruct.init_expr = init;
- n->destruct.is_struct_destruct = 1;
- n->destruct.struct_name = name; // "Point"
- return n;
- }
+ASTNode *parse_test(ParserContext *ctx, Lexer *l) {
+ lexer_next(l); // eat 'test'
+ Token t = lexer_next(l);
+ if (t.type != TOK_STRING) {
+ zpanic_at(t, "Test name must be a string literal");
+ }
- // Check for Guard Pattern: var Some(val) = opt else { ... }
- if (lexer_peek(l).type == TOK_LPAREN)
- {
- lexer_next(l); // eat (
- Token val_tok = lexer_next(l);
- char *val_name = token_strdup(val_tok);
+ // Strip quotes for AST storage
+ char *name = xmalloc(t.len);
+ strncpy(name, t.start + 1, t.len - 2);
+ name[t.len - 2] = 0;
- if (lexer_next(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected ')' in guard pattern");
- }
+ ASTNode *body = parse_block(ctx, l);
- if (lexer_next(l).type != TOK_OP)
- {
- zpanic_at(lexer_peek(l), "Expected '=' after guard pattern");
- }
+ ASTNode *n = ast_create(NODE_TEST);
+ n->test_stmt.name = name;
+ n->test_stmt.body = body;
+ return n;
+}
- ASTNode *init = parse_expression(ctx, l);
+ASTNode *parse_assert(ParserContext *ctx, Lexer *l) {
+ lexer_next(l); // assert
+ if (lexer_peek(l).type == TOK_LPAREN) {
+ lexer_next(l); // optional paren? usually yes
+ }
- Token t = lexer_next(l);
- if (t.type != TOK_IDENT || strncmp(t.start, "else", 4) != 0)
- {
- zpanic_at(t, "Expected 'else' in guard statement");
- }
+ ASTNode *cond = parse_expression(ctx, l);
- ASTNode *else_blk;
- if (lexer_peek(l).type == TOK_LBRACE)
- {
- else_blk = parse_block(ctx, l);
- }
- else
- {
- else_blk = ast_create(NODE_BLOCK);
- else_blk->block.statements = parse_statement(ctx, l);
- }
+ char *msg = NULL;
+ if (lexer_peek(l).type == TOK_COMMA) {
+ lexer_next(l);
+ Token st = lexer_next(l);
+ if (st.type != TOK_STRING) {
+ zpanic_at(st, "Expected message string");
+ }
+ msg = xmalloc(st.len + 1);
+ strncpy(msg, st.start, st.len);
+ msg[st.len] = 0;
+ }
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- }
+ if (lexer_peek(l).type == TOK_RPAREN) {
+ lexer_next(l);
+ }
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
+ }
- ASTNode *n = ast_create(NODE_DESTRUCT_VAR);
- n->destruct.names = xmalloc(sizeof(char *));
- n->destruct.names[0] = val_name;
- n->destruct.count = 1;
- n->destruct.init_expr = init;
- n->destruct.is_guard = 1;
- n->destruct.guard_variant = name;
- n->destruct.else_block = else_blk;
+ ASTNode *n = ast_create(NODE_ASSERT);
+ n->assert_stmt.condition = cond;
+ n->assert_stmt.message = msg;
+ return n;
+}
- add_symbol(ctx, val_name, "unknown", NULL);
- register_var_mutability(ctx, val_name, is_mutable);
+// Helper for Value-Returning Defer
+static void replace_it_with_var(ASTNode *node, char *var_name) {
+ if (!node) {
+ return;
+ }
+ if (node->type == NODE_EXPR_VAR) {
+ if (strcmp(node->var_ref.name, "it") == 0) {
+ // Replace 'it' with var_name
+ node->var_ref.name = xstrdup(var_name);
+ }
+ } else if (node->type == NODE_EXPR_CALL) {
+ replace_it_with_var(node->call.callee, var_name);
+ ASTNode *arg = node->call.args;
+ while (arg) {
+ replace_it_with_var(arg, var_name);
+ arg = arg->next;
+ }
+ } else if (node->type == NODE_EXPR_MEMBER) {
+ replace_it_with_var(node->member.target, var_name);
+ } else if (node->type == NODE_EXPR_BINARY) {
+ replace_it_with_var(node->binary.left, var_name);
+ replace_it_with_var(node->binary.right, var_name);
+ } else if (node->type == NODE_EXPR_UNARY) {
+ replace_it_with_var(node->unary.operand, var_name);
+ } else if (node->type == NODE_BLOCK) {
+ ASTNode *s = node->block.statements;
+ while (s) {
+ replace_it_with_var(s, var_name);
+ s = s->next;
+ }
+ }
+}
- return n;
- }
+ASTNode *parse_var_decl(ParserContext *ctx, Lexer *l) {
+ lexer_next(l); // eat 'var'
- char *type = NULL;
- Type *type_obj = NULL; // --- NEW: Formal Type Object ---
+ // Check for 'mut' keyword
+ int is_mutable = 0;
+ if (lexer_peek(l).type == TOK_MUT) {
+ is_mutable = 1;
+ lexer_next(l);
+ } else {
+ // Default mutability depends on directive
+ is_mutable = !ctx->immutable_by_default;
+ }
+
+ // Destructuring: var {x, y} = ...
+ if (lexer_peek(l).type == TOK_LBRACE || lexer_peek(l).type == TOK_LPAREN) {
+ int is_struct = (lexer_peek(l).type == TOK_LBRACE);
+ lexer_next(l);
+ char **names = xmalloc(16 * sizeof(char *));
+ int count = 0;
+ while (1) {
+ Token t = lexer_next(l);
+ char *nm = token_strdup(t);
+ // UPDATE: Pass NULL to add_symbol
+ names[count++] = nm;
+ add_symbol(ctx, nm, "unknown", NULL);
+ // Register mutability for each destructured variable
+ register_var_mutability(ctx, nm, is_mutable);
+ Token next = lexer_next(l);
+ if (next.type == (is_struct ? TOK_RBRACE : TOK_RPAREN)) {
+ break;
+ }
+ if (next.type != TOK_COMMA) {
+ zpanic_at(next, "Expected comma");
+ }
+ }
+ if (lexer_next(l).type != TOK_OP) {
+ zpanic_at(lexer_peek(l), "Expected =");
+ }
+ ASTNode *init = parse_expression(ctx, l);
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
+ }
+ ASTNode *n = ast_create(NODE_DESTRUCT_VAR);
+ n->destruct.names = names;
+ n->destruct.count = count;
+ n->destruct.init_expr = init;
+ n->destruct.is_struct_destruct = is_struct;
+ return n;
+ }
- if (lexer_peek(l).type == TOK_COLON)
- {
- lexer_next(l);
- // Hybrid Parse: Get Object AND String
- type_obj = parse_type_formal(ctx, l);
- type = type_to_string(type_obj);
- }
+ // Normal Declaration OR Named Struct Destructuring
+ Token name_tok = lexer_next(l);
+ char *name = token_strdup(name_tok);
- ASTNode *init = NULL;
- if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "="))
- {
- lexer_next(l);
+ // Check for Struct Destructuring: var Point { x, y }
+ if (lexer_peek(l).type == TOK_LBRACE) {
+ lexer_next(l); // eat {
+ char **names = xmalloc(16 * sizeof(char *));
+ char **fields = xmalloc(16 * sizeof(char *));
+ int count = 0;
- // Peek for special initializers
- Token next = lexer_peek(l);
- if (next.type == TOK_IDENT && strncmp(next.start, "embed", 5) == 0)
- {
- char *e = parse_embed(ctx, l);
- init = ast_create(NODE_RAW_STMT);
- init->raw_stmt.content = e;
- if (!type)
- {
- register_slice(ctx, "char");
- type = xstrdup("Slice_char");
- }
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- }
- }
- else if (next.type == TOK_LBRACKET && type && strncmp(type, "Slice_", 6) == 0)
- {
- char *code = parse_array_literal(ctx, l, type);
- init = ast_create(NODE_RAW_STMT);
- init->raw_stmt.content = code;
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- }
- }
- else if (next.type == TOK_LPAREN && type && strncmp(type, "Tuple_", 6) == 0)
- {
- char *code = parse_tuple_literal(ctx, l, type);
- init = ast_create(NODE_RAW_STMT);
- init->raw_stmt.content = code;
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- }
- }
- else
- {
- init = parse_expression(ctx, l);
- }
+ while (1) {
+ // Parse field:name or just name
+ Token t = lexer_next(l);
+ char *ident = token_strdup(t);
- if (init && type)
- {
- char *rhs_type = init->resolved_type;
- if (!rhs_type && init->type_info)
- {
- rhs_type = type_to_string(init->type_info);
- }
+ if (lexer_peek(l).type == TOK_COLON) {
+ // field: var_name
+ lexer_next(l); // eat :
+ Token v = lexer_next(l);
+ fields[count] = ident;
+ names[count] = token_strdup(v);
+ } else {
+ // Shorthand: field (implies var name = field)
+ fields[count] = ident;
+ names[count] =
+ ident; // Share pointer or duplicate? duplicate safer if we free
+ }
+ // Register symbol for variable
+ add_symbol(ctx, names[count], "unknown", NULL);
+ register_var_mutability(ctx, names[count], is_mutable);
+
+ count++;
+
+ Token next = lexer_next(l);
+ if (next.type == TOK_RBRACE) {
+ break;
+ }
+ if (next.type != TOK_COMMA) {
+ zpanic_at(next, "Expected comma in struct pattern");
+ }
+ }
+
+ if (lexer_next(l).type != TOK_OP) {
+ zpanic_at(lexer_peek(l), "Expected =");
+ }
+ ASTNode *init = parse_expression(ctx, l);
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
+ }
+
+ ASTNode *n = ast_create(NODE_DESTRUCT_VAR);
+ n->destruct.names = names;
+ n->destruct.field_names = fields;
+ n->destruct.count = count;
+ n->destruct.init_expr = init;
+ n->destruct.is_struct_destruct = 1;
+ n->destruct.struct_name = name; // "Point"
+ return n;
+ }
- if (rhs_type && strchr(type, '*') && strchr(rhs_type, '*'))
- {
- // Strip stars to get struct names
- char target_struct[256];
- strcpy(target_struct, type);
- target_struct[strlen(target_struct) - 1] = 0;
- char source_struct[256];
- strcpy(source_struct, rhs_type);
- source_struct[strlen(source_struct) - 1] = 0;
-
- ASTNode *def = find_struct_def(ctx, source_struct);
-
- if (def && def->strct.parent && strcmp(def->strct.parent, target_struct) == 0)
- {
- // Create Cast Node
- ASTNode *cast = ast_create(NODE_EXPR_CAST);
- cast->cast.target_type = xstrdup(type);
- cast->cast.expr = init;
- cast->type_info = type_obj; // Inherit formal type
-
- init = cast; // Replace init with cast
- }
- }
- }
+ // Check for Guard Pattern: var Some(val) = opt else { ... }
+ if (lexer_peek(l).type == TOK_LPAREN) {
+ lexer_next(l); // eat (
+ Token val_tok = lexer_next(l);
+ char *val_name = token_strdup(val_tok);
- // ** Type Inference Logic **
- if (!type && init)
- {
- if (init->type_info)
- {
- type_obj = init->type_info;
- type = type_to_string(type_obj);
- }
- else if (init->type == NODE_EXPR_SLICE)
- {
- zpanic_at(init->token, "Slice Node has NO Type Info!");
- }
- // Fallbacks for literals
- else if (init->type == NODE_EXPR_LITERAL)
- {
- if (init->literal.type_kind == 0)
- {
- type = xstrdup("int");
- type_obj = type_new(TYPE_INT);
- }
- else if (init->literal.type_kind == 1)
- {
- type = xstrdup("float");
- type_obj = type_new(TYPE_FLOAT);
- }
- else if (init->literal.type_kind == 2)
- {
- type = xstrdup("string");
- type_obj = type_new(TYPE_STRING);
- }
- }
- else if (init->type == NODE_EXPR_STRUCT_INIT)
- {
- type = xstrdup(init->struct_init.struct_name);
- type_obj = type_new(TYPE_STRUCT);
- type_obj->name = xstrdup(type);
- }
- }
+ if (lexer_next(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected ')' in guard pattern");
}
- if (!type && !init)
- {
- zpanic_at(name_tok, "Variable '%s' requires a type or initializer", name);
+ if (lexer_next(l).type != TOK_OP) {
+ zpanic_at(lexer_peek(l), "Expected '=' after guard pattern");
}
- // Register in symbol table with actual token
- add_symbol_with_token(ctx, name, type, type_obj, name_tok);
- register_var_mutability(ctx, name, is_mutable);
+ ASTNode *init = parse_expression(ctx, l);
- // NEW: Capture Const Integer Values
- if (!is_mutable && init && init->type == NODE_EXPR_LITERAL && init->literal.type_kind == 0)
- {
- Symbol *s = find_symbol_entry(ctx, name); // Helper to find the struct
- if (s)
- {
- s->is_const_value = 1;
- s->const_int_val = init->literal.int_val;
- }
+ Token t = lexer_next(l);
+ if (t.type != TOK_IDENT || strncmp(t.start, "else", 4) != 0) {
+ zpanic_at(t, "Expected 'else' in guard statement");
}
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
+ ASTNode *else_blk;
+ if (lexer_peek(l).type == TOK_LBRACE) {
+ else_blk = parse_block(ctx, l);
+ } else {
+ else_blk = ast_create(NODE_BLOCK);
+ else_blk->block.statements = parse_statement(ctx, l);
}
- ASTNode *n = ast_create(NODE_VAR_DECL);
- n->token = name_tok; // Save location
- n->var_decl.name = name;
- n->var_decl.type_str = type;
- n->var_decl.is_mutable = is_mutable;
- n->type_info = type_obj;
-
- // Auto-construct Trait Object
- if (type && is_trait(type) && init && init->type == NODE_EXPR_UNARY &&
- strcmp(init->unary.op, "&") == 0 && init->unary.operand->type == NODE_EXPR_VAR)
- {
- char *var_ref_name = init->unary.operand->var_ref.name;
- char *struct_type = find_symbol_type(ctx, var_ref_name);
- if (struct_type)
- {
- char *code = xmalloc(512);
- sprintf(code, "(%s){.self=&%s, .vtable=&%s_%s_VTable}", type, var_ref_name, struct_type,
- type);
- ASTNode *wrapper = ast_create(NODE_RAW_STMT);
- wrapper->raw_stmt.content = code;
- init = wrapper;
- }
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
}
- n->var_decl.init_expr = init;
+ ASTNode *n = ast_create(NODE_DESTRUCT_VAR);
+ n->destruct.names = xmalloc(sizeof(char *));
+ n->destruct.names[0] = val_name;
+ n->destruct.count = 1;
+ n->destruct.init_expr = init;
+ n->destruct.is_guard = 1;
+ n->destruct.guard_variant = name;
+ n->destruct.else_block = else_blk;
- // Global detection: Either no scope (yet) OR root scope (no parent)
- if (!ctx->current_scope || !ctx->current_scope->parent)
- {
- add_to_global_list(ctx, n);
- }
+ add_symbol(ctx, val_name, "unknown", NULL);
+ register_var_mutability(ctx, val_name, is_mutable);
- // Check for 'defer' (Value-Returning Defer)
- if (lexer_peek(l).type == TOK_DEFER)
- {
- lexer_next(l); // eat defer
- // Parse the defer expression/statement
- // Usually defer close(it);
- // We parse expression.
- ASTNode *expr = parse_expression(ctx, l);
+ return n;
+ }
- // Handle "it" substitution
- replace_it_with_var(expr, name);
+ char *type = NULL;
+ Type *type_obj = NULL; // --- NEW: Formal Type Object ---
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- }
+ if (lexer_peek(l).type == TOK_COLON) {
+ lexer_next(l);
+ // Hybrid Parse: Get Object AND String
+ type_obj = parse_type_formal(ctx, l);
+ type = type_to_string(type_obj);
+ }
- ASTNode *d = ast_create(NODE_DEFER);
- d->defer_stmt.stmt = expr;
+ ASTNode *init = NULL;
+ if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) {
+ lexer_next(l);
- // Chain it: var_decl -> defer
- n->next = d;
+ // Peek for special initializers
+ Token next = lexer_peek(l);
+ if (next.type == TOK_IDENT && strncmp(next.start, "embed", 5) == 0) {
+ char *e = parse_embed(ctx, l);
+ init = ast_create(NODE_RAW_STMT);
+ init->raw_stmt.content = e;
+ if (!type) {
+ register_slice(ctx, "char");
+ type = xstrdup("Slice_char");
+ }
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
+ }
+ } else if (next.type == TOK_LBRACKET && type &&
+ strncmp(type, "Slice_", 6) == 0) {
+ char *code = parse_array_literal(ctx, l, type);
+ init = ast_create(NODE_RAW_STMT);
+ init->raw_stmt.content = code;
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
+ }
+ } else if (next.type == TOK_LPAREN && type &&
+ strncmp(type, "Tuple_", 6) == 0) {
+ char *code = parse_tuple_literal(ctx, l, type);
+ init = ast_create(NODE_RAW_STMT);
+ init->raw_stmt.content = code;
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
+ }
+ } else {
+ init = parse_expression(ctx, l);
}
- return n;
-}
+ if (init && type) {
+ char *rhs_type = init->resolved_type;
+ if (!rhs_type && init->type_info) {
+ rhs_type = type_to_string(init->type_info);
+ }
-ASTNode *parse_const(ParserContext *ctx, Lexer *l)
-{
- lexer_next(l); // eat const
- Token n = lexer_next(l);
+ if (rhs_type && strchr(type, '*') && strchr(rhs_type, '*')) {
+ // Strip stars to get struct names
+ char target_struct[256];
+ strcpy(target_struct, type);
+ target_struct[strlen(target_struct) - 1] = 0;
+ char source_struct[256];
+ strcpy(source_struct, rhs_type);
+ source_struct[strlen(source_struct) - 1] = 0;
- char *type_str = NULL;
- Type *type_obj = NULL;
+ ASTNode *def = find_struct_def(ctx, source_struct);
- if (lexer_peek(l).type == TOK_COLON)
- {
- lexer_next(l);
- // Hybrid Parse
- type_obj = parse_type_formal(ctx, l);
- type_str = type_to_string(type_obj);
- }
+ if (def && def->strct.parent &&
+ strcmp(def->strct.parent, target_struct) == 0) {
+ // Create Cast Node
+ ASTNode *cast = ast_create(NODE_EXPR_CAST);
+ cast->cast.target_type = xstrdup(type);
+ cast->cast.expr = init;
+ cast->type_info = type_obj; // Inherit formal type
- char *ns = token_strdup(n);
- if (!type_obj)
- {
- type_obj = type_new(TYPE_UNKNOWN); // Ensure we have an object
+ init = cast; // Replace init with cast
+ }
+ }
}
- type_obj->is_const = 1;
- add_symbol(ctx, ns, type_str ? type_str : "unknown", type_obj);
-
- ASTNode *i = 0;
- if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "="))
- {
- lexer_next(l);
- // Check for constant integer literal
- if (lexer_peek(l).type == TOK_INT)
- {
- Token val_tok = lexer_peek(l);
- int val = atoi(token_strdup(val_tok)); // quick check
-
- Symbol *s = find_symbol_entry(ctx, ns);
- if (s)
- {
- s->is_const_value = 1;
- s->const_int_val = val;
-
- if (!s->type_name || strcmp(s->type_name, "unknown") == 0)
- {
- if (s->type_name)
- {
- free(s->type_name);
- }
- s->type_name = xstrdup("int");
- if (s->type_info)
- {
- free(s->type_info);
- }
- s->type_info = type_new(TYPE_INT);
- }
- }
- }
+ // ** Type Inference Logic **
+ if (!type && init) {
+ if (init->type_info) {
+ type_obj = init->type_info;
+ type = type_to_string(type_obj);
+ } else if (init->type == NODE_EXPR_SLICE) {
+ zpanic_at(init->token, "Slice Node has NO Type Info!");
+ }
+ // Fallbacks for literals
+ else if (init->type == NODE_EXPR_LITERAL) {
+ if (init->literal.type_kind == 0) {
+ type = xstrdup("int");
+ type_obj = type_new(TYPE_INT);
+ } else if (init->literal.type_kind == 1) {
+ type = xstrdup("float");
+ type_obj = type_new(TYPE_FLOAT);
+ } else if (init->literal.type_kind == 2) {
+ type = xstrdup("string");
+ type_obj = type_new(TYPE_STRING);
+ }
+ } else if (init->type == NODE_EXPR_STRUCT_INIT) {
+ type = xstrdup(init->struct_init.struct_name);
+ type_obj = type_new(TYPE_STRUCT);
+ type_obj->name = xstrdup(type);
+ }
+ }
+ }
+
+ if (!type && !init) {
+ zpanic_at(name_tok, "Variable '%s' requires a type or initializer", name);
+ }
+
+ // Register in symbol table with actual token
+ add_symbol_with_token(ctx, name, type, type_obj, name_tok);
+ register_var_mutability(ctx, name, is_mutable);
+
+ // NEW: Capture Const Integer Values
+ if (!is_mutable && init && init->type == NODE_EXPR_LITERAL &&
+ init->literal.type_kind == 0) {
+ Symbol *s = find_symbol_entry(ctx, name); // Helper to find the struct
+ if (s) {
+ s->is_const_value = 1;
+ s->const_int_val = init->literal.int_val;
+ }
+ }
+
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
+ }
+
+ ASTNode *n = ast_create(NODE_VAR_DECL);
+ n->token = name_tok; // Save location
+ n->var_decl.name = name;
+ n->var_decl.type_str = type;
+ n->var_decl.is_mutable = is_mutable;
+ n->type_info = type_obj;
+
+ // Auto-construct Trait Object
+ if (type && is_trait(type) && init && init->type == NODE_EXPR_UNARY &&
+ strcmp(init->unary.op, "&") == 0 &&
+ init->unary.operand->type == NODE_EXPR_VAR) {
+ char *var_ref_name = init->unary.operand->var_ref.name;
+ char *struct_type = find_symbol_type(ctx, var_ref_name);
+ if (struct_type) {
+ char *code = xmalloc(512);
+ sprintf(code, "(%s){.self=&%s, .vtable=&%s_%s_VTable}", type,
+ var_ref_name, struct_type, type);
+ ASTNode *wrapper = ast_create(NODE_RAW_STMT);
+ wrapper->raw_stmt.content = code;
+ init = wrapper;
+ }
+ }
+
+ n->var_decl.init_expr = init;
+
+ // Global detection: Either no scope (yet) OR root scope (no parent)
+ if (!ctx->current_scope || !ctx->current_scope->parent) {
+ add_to_global_list(ctx, n);
+ }
+
+ // Check for 'defer' (Value-Returning Defer)
+ if (lexer_peek(l).type == TOK_DEFER) {
+ lexer_next(l); // eat defer
+ // Parse the defer expression/statement
+ // Usually defer close(it);
+ // We parse expression.
+ ASTNode *expr = parse_expression(ctx, l);
- if (lexer_peek(l).type == TOK_LPAREN && type_str && strncmp(type_str, "Tuple_", 6) == 0)
- {
- char *code = parse_tuple_literal(ctx, l, type_str);
- i = ast_create(NODE_RAW_STMT);
- i->raw_stmt.content = code;
- }
- else
- {
- i = parse_expression(ctx, l);
- }
- }
- else
- {
- lexer_next(l);
- }
+ // Handle "it" substitution
+ replace_it_with_var(expr, name);
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
}
- ASTNode *o = ast_create(NODE_CONST);
- o->var_decl.name = ns;
- o->var_decl.type_str = type_str;
- o->var_decl.init_expr = i;
+ ASTNode *d = ast_create(NODE_DEFER);
+ d->defer_stmt.stmt = expr;
- if (!ctx->current_scope || !ctx->current_scope->parent)
- {
- add_to_global_list(ctx, o);
- }
+ // Chain it: var_decl -> defer
+ n->next = d;
+ }
- return o;
+ return n;
}
-ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l)
-{
- lexer_next(l);
- Token n = lexer_next(l);
- lexer_next(l);
- char *o = parse_type(ctx, l);
- lexer_next(l);
- ASTNode *node = ast_create(NODE_TYPE_ALIAS);
- node->type_alias.alias = xmalloc(n.len + 1);
- strncpy(node->type_alias.alias, n.start, n.len);
- node->type_alias.alias[n.len] = 0;
- node->type_alias.original_type = o;
- return node;
-}
+ASTNode *parse_const(ParserContext *ctx, Lexer *l) {
+ lexer_next(l); // eat const
+ Token n = lexer_next(l);
-ASTNode *parse_return(ParserContext *ctx, Lexer *l)
-{
- lexer_next(l); // eat 'return'
- ASTNode *n = ast_create(NODE_RETURN);
+ char *type_str = NULL;
+ Type *type_obj = NULL;
- int handled = 0;
+ if (lexer_peek(l).type == TOK_COLON) {
+ lexer_next(l);
+ // Hybrid Parse
+ type_obj = parse_type_formal(ctx, l);
+ type_str = type_to_string(type_obj);
+ }
+
+ char *ns = token_strdup(n);
+ if (!type_obj) {
+ type_obj = type_new(TYPE_UNKNOWN); // Ensure we have an object
+ }
+ type_obj->is_const = 1;
+ add_symbol(ctx, ns, type_str ? type_str : "unknown", type_obj);
+
+ ASTNode *i = 0;
+ if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) {
+ lexer_next(l);
- // 1. Check for Tuple Literal Return: return (a, b);
- // Condition: Function returns Tuple_..., starts with '(', and contains ',' at
- // top level
- if (curr_func_ret && strncmp(curr_func_ret, "Tuple_", 6) == 0 &&
- lexer_peek(l).type == TOK_LPAREN)
- {
+ // Check for constant integer literal
+ if (lexer_peek(l).type == TOK_INT) {
+ Token val_tok = lexer_peek(l);
+ int val = atoi(token_strdup(val_tok)); // quick check
+
+ Symbol *s = find_symbol_entry(ctx, ns);
+ if (s) {
+ s->is_const_value = 1;
+ s->const_int_val = val;
+
+ if (!s->type_name || strcmp(s->type_name, "unknown") == 0) {
+ if (s->type_name) {
+ free(s->type_name);
+ }
+ s->type_name = xstrdup("int");
+ if (s->type_info) {
+ free(s->type_info);
+ }
+ s->type_info = type_new(TYPE_INT);
+ }
+ }
+ }
+
+ if (lexer_peek(l).type == TOK_LPAREN && type_str &&
+ strncmp(type_str, "Tuple_", 6) == 0) {
+ char *code = parse_tuple_literal(ctx, l, type_str);
+ i = ast_create(NODE_RAW_STMT);
+ i->raw_stmt.content = code;
+ } else {
+ i = parse_expression(ctx, l);
+ }
+ } else {
+ lexer_next(l);
+ }
- // Peek ahead to distinguish "(expr)" from "(a, b)"
- int is_tuple_lit = 0;
- int depth = 0;
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
+ }
- // Just scan tokens manually using a temp lexer to be safe
- Lexer temp_l = *l;
+ ASTNode *o = ast_create(NODE_CONST);
+ o->var_decl.name = ns;
+ o->var_decl.type_str = type_str;
+ o->var_decl.init_expr = i;
- while (1)
- {
- Token t = lexer_next(&temp_l);
- if (t.type == TOK_EOF)
- {
- break;
- }
- if (t.type == TOK_SEMICOLON)
- {
- break; // Safety break
- }
+ if (!ctx->current_scope || !ctx->current_scope->parent) {
+ add_to_global_list(ctx, o);
+ }
- if (t.type == TOK_LPAREN)
- {
- depth++;
- }
- if (t.type == TOK_RPAREN)
- {
- depth--;
- if (depth == 0)
- {
- break; // End of potential tuple
- }
- }
+ return o;
+}
- // If we find a comma at depth 1 (inside the first parens), it's a tuple
- // literal!
- if (depth == 1 && t.type == TOK_COMMA)
- {
- is_tuple_lit = 1;
- break;
- }
- }
+ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l) {
+ lexer_next(l);
+ Token n = lexer_next(l);
+ lexer_next(l);
+ char *o = parse_type(ctx, l);
+ lexer_next(l);
+ ASTNode *node = ast_create(NODE_TYPE_ALIAS);
+ node->type_alias.alias = xmalloc(n.len + 1);
+ strncpy(node->type_alias.alias, n.start, n.len);
+ node->type_alias.alias[n.len] = 0;
+ node->type_alias.original_type = o;
+ return node;
+}
- if (is_tuple_lit)
- {
- char *code = parse_tuple_literal(ctx, l, curr_func_ret);
- ASTNode *raw = ast_create(NODE_RAW_STMT);
- raw->raw_stmt.content = code;
- n->ret.value = raw;
- handled = 1;
- }
- }
- // 2. Check for Array Literal Return: return [a, b];
- else if (curr_func_ret && strncmp(curr_func_ret, "Slice_", 6) == 0 &&
- lexer_peek(l).type == TOK_LBRACKET)
- {
- char *code = parse_array_literal(ctx, l, curr_func_ret);
- ASTNode *raw = ast_create(NODE_RAW_STMT);
- raw->raw_stmt.content = code;
- n->ret.value = raw;
- handled = 1;
- }
+ASTNode *parse_return(ParserContext *ctx, Lexer *l) {
+ lexer_next(l); // eat 'return'
+ ASTNode *n = ast_create(NODE_RETURN);
+
+ int handled = 0;
+
+ // 1. Check for Tuple Literal Return: return (a, b);
+ // Condition: Function returns Tuple_..., starts with '(', and contains ',' at
+ // top level
+ if (curr_func_ret && strncmp(curr_func_ret, "Tuple_", 6) == 0 &&
+ lexer_peek(l).type == TOK_LPAREN) {
+
+ // Peek ahead to distinguish "(expr)" from "(a, b)"
+ int is_tuple_lit = 0;
+ int depth = 0;
+
+ // Just scan tokens manually using a temp lexer to be safe
+ Lexer temp_l = *l;
+
+ while (1) {
+ Token t = lexer_next(&temp_l);
+ if (t.type == TOK_EOF) {
+ break;
+ }
+ if (t.type == TOK_SEMICOLON) {
+ break; // Safety break
+ }
+
+ if (t.type == TOK_LPAREN) {
+ depth++;
+ }
+ if (t.type == TOK_RPAREN) {
+ depth--;
+ if (depth == 0) {
+ break; // End of potential tuple
+ }
+ }
+
+ // If we find a comma at depth 1 (inside the first parens), it's a tuple
+ // literal!
+ if (depth == 1 && t.type == TOK_COMMA) {
+ is_tuple_lit = 1;
+ break;
+ }
+ }
+
+ if (is_tuple_lit) {
+ char *code = parse_tuple_literal(ctx, l, curr_func_ret);
+ ASTNode *raw = ast_create(NODE_RAW_STMT);
+ raw->raw_stmt.content = code;
+ n->ret.value = raw;
+ handled = 1;
+ }
+ }
+ // 2. Check for Array Literal Return: return [a, b];
+ else if (curr_func_ret && strncmp(curr_func_ret, "Slice_", 6) == 0 &&
+ lexer_peek(l).type == TOK_LBRACKET) {
+ char *code = parse_array_literal(ctx, l, curr_func_ret);
+ ASTNode *raw = ast_create(NODE_RAW_STMT);
+ raw->raw_stmt.content = code;
+ n->ret.value = raw;
+ handled = 1;
+ }
+
+ // 3. Standard Expression Return
+ if (!handled) {
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ n->ret.value = NULL;
+ } else {
+ n->ret.value = parse_expression(ctx, l);
+ }
+ }
+
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
+ }
+ return n;
+}
- // 3. Standard Expression Return
- if (!handled)
- {
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- n->ret.value = NULL;
- }
- else
- {
- n->ret.value = parse_expression(ctx, l);
- }
- }
+ASTNode *parse_if(ParserContext *ctx, Lexer *l) {
+ lexer_next(l); // eat if
+ ASTNode *cond = parse_expression(ctx, l);
+ check_assignment_condition(cond);
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- }
- return n;
+ ASTNode *then_b = NULL;
+ if (lexer_peek(l).type == TOK_LBRACE) {
+ then_b = parse_block(ctx, l);
+ } else {
+ // Single statement: Wrap in scope + block
+ enter_scope(ctx);
+ ASTNode *s = parse_statement(ctx, l);
+ exit_scope(ctx);
+ then_b = ast_create(NODE_BLOCK);
+ then_b->block.statements = s;
+ }
+
+ ASTNode *else_b = NULL;
+ skip_comments(l);
+ if (lexer_peek(l).type == TOK_IDENT &&
+ strncmp(lexer_peek(l).start, "else", 4) == 0) {
+ lexer_next(l);
+ if (lexer_peek(l).type == TOK_IDENT &&
+ strncmp(lexer_peek(l).start, "if", 2) == 0) {
+ else_b = parse_if(ctx, l);
+ } else if (lexer_peek(l).type == TOK_LBRACE) {
+ else_b = parse_block(ctx, l);
+ } else {
+ // Single statement else
+ enter_scope(ctx);
+ ASTNode *s = parse_statement(ctx, l);
+ exit_scope(ctx);
+ else_b = ast_create(NODE_BLOCK);
+ else_b->block.statements = s;
+ }
+ }
+ ASTNode *n = ast_create(NODE_IF);
+ n->if_stmt.condition = cond;
+ n->if_stmt.then_body = then_b;
+ n->if_stmt.else_body = else_b;
+ return n;
}
-ASTNode *parse_if(ParserContext *ctx, Lexer *l)
-{
- lexer_next(l); // eat if
- ASTNode *cond = parse_expression(ctx, l);
- check_assignment_condition(cond);
+ASTNode *parse_while(ParserContext *ctx, Lexer *l) {
+ lexer_next(l);
+ ASTNode *cond = parse_expression(ctx, l);
+ check_assignment_condition(cond);
+
+ // Zen: While(true)
+ if ((cond->type == NODE_EXPR_LITERAL && cond->literal.type_kind == TOK_INT &&
+ strcmp(cond->literal.string_val, "1") == 0) ||
+ (cond->type == NODE_EXPR_VAR &&
+ strcmp(cond->var_ref.name, "true") == 0)) {
+ zen_trigger_at(TRIGGER_WHILE_TRUE, cond->token);
+ }
+ ASTNode *body;
+ if (lexer_peek(l).type == TOK_LBRACE) {
+ body = parse_block(ctx, l);
+ } else {
+ body = parse_statement(ctx, l);
+ }
+ ASTNode *n = ast_create(NODE_WHILE);
+ n->while_stmt.condition = cond;
+ n->while_stmt.body = body;
+ return n;
+}
- ASTNode *then_b = NULL;
- if (lexer_peek(l).type == TOK_LBRACE)
- {
- then_b = parse_block(ctx, l);
- }
- else
- {
- // Single statement: Wrap in scope + block
+ASTNode *parse_for(ParserContext *ctx, Lexer *l) {
+ lexer_next(l);
+
+ // Range Loop: for i in 0..10
+ if (lexer_peek(l).type == TOK_IDENT) {
+ int saved_pos = l->pos;
+ Token var = lexer_next(l);
+ Token in_tok = lexer_next(l);
+
+ if (in_tok.type == TOK_IDENT && strncmp(in_tok.start, "in", 2) == 0) {
+ ASTNode *start_expr = parse_expression(ctx, l);
+ if (lexer_peek(l).type == TOK_DOTDOT) {
+ lexer_next(l); // consume ..
+ ASTNode *end_expr = parse_expression(ctx, l);
+
+ ASTNode *n = ast_create(NODE_FOR_RANGE);
+ n->for_range.var_name = xmalloc(var.len + 1);
+ strncpy(n->for_range.var_name, var.start, var.len);
+ n->for_range.var_name[var.len] = 0;
+ n->for_range.start = start_expr;
+ n->for_range.end = end_expr;
+
+ if (lexer_peek(l).type == TOK_IDENT &&
+ strncmp(lexer_peek(l).start, "step", 4) == 0) {
+ lexer_next(l);
+ Token s_tok = lexer_next(l);
+ char *sval = xmalloc(s_tok.len + 1);
+ strncpy(sval, s_tok.start, s_tok.len);
+ sval[s_tok.len] = 0;
+ n->for_range.step = sval;
+ } else {
+ n->for_range.step = NULL;
+ }
+
+ // Fix: Enter scope to register loop variable
enter_scope(ctx);
- ASTNode *s = parse_statement(ctx, l);
+ // Register loop variable so body can see it
+ add_symbol(ctx, n->for_range.var_name, "int", type_new(TYPE_INT));
+
+ // Handle body (brace or single stmt)
+ if (lexer_peek(l).type == TOK_LBRACE) {
+ n->for_range.body = parse_block(ctx, l);
+ } else {
+ n->for_range.body = parse_statement(ctx, l);
+ }
exit_scope(ctx);
- then_b = ast_create(NODE_BLOCK);
- then_b->block.statements = s;
+
+ return n;
+ }
}
+ l->pos = saved_pos; // Restore
+ }
- ASTNode *else_b = NULL;
- skip_comments(l);
- if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "else", 4) == 0)
- {
+ // C-Style For Loop
+ enter_scope(ctx);
+ if (lexer_peek(l).type == TOK_LPAREN) {
+ lexer_next(l);
+ }
+
+ ASTNode *init = NULL;
+ if (lexer_peek(l).type != TOK_SEMICOLON) {
+ if (lexer_peek(l).type == TOK_IDENT &&
+ strncmp(lexer_peek(l).start, "var", 3) == 0) {
+ init = parse_var_decl(ctx, l);
+ } else {
+ init = parse_expression(ctx, l);
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
lexer_next(l);
- if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "if", 2) == 0)
- {
- else_b = parse_if(ctx, l);
- }
- else if (lexer_peek(l).type == TOK_LBRACE)
- {
- else_b = parse_block(ctx, l);
- }
- else
- {
- // Single statement else
- enter_scope(ctx);
- ASTNode *s = parse_statement(ctx, l);
- exit_scope(ctx);
- else_b = ast_create(NODE_BLOCK);
- else_b->block.statements = s;
- }
+ }
}
- ASTNode *n = ast_create(NODE_IF);
- n->if_stmt.condition = cond;
- n->if_stmt.then_body = then_b;
- n->if_stmt.else_body = else_b;
- return n;
-}
-
-ASTNode *parse_while(ParserContext *ctx, Lexer *l)
-{
+ } else {
+ lexer_next(l);
+ }
+
+ ASTNode *cond = NULL;
+ if (lexer_peek(l).type != TOK_SEMICOLON) {
+ cond = parse_expression(ctx, l);
+ } else {
+ // Empty condition = true
+ ASTNode *true_lit = ast_create(NODE_EXPR_LITERAL);
+ true_lit->literal.type_kind = 0;
+ true_lit->literal.int_val = 1;
+ cond = true_lit;
+ }
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
lexer_next(l);
- ASTNode *cond = parse_expression(ctx, l);
- check_assignment_condition(cond);
+ }
- // Zen: While(true)
- if ((cond->type == NODE_EXPR_LITERAL && cond->literal.type_kind == TOK_INT &&
- strcmp(cond->literal.string_val, "1") == 0) ||
- (cond->type == NODE_EXPR_VAR && strcmp(cond->var_ref.name, "true") == 0))
- {
- zen_trigger_at(TRIGGER_WHILE_TRUE, cond->token);
- }
- ASTNode *body;
- if (lexer_peek(l).type == TOK_LBRACE)
- {
- body = parse_block(ctx, l);
- }
- else
- {
- body = parse_statement(ctx, l);
- }
- ASTNode *n = ast_create(NODE_WHILE);
- n->while_stmt.condition = cond;
- n->while_stmt.body = body;
- return n;
-}
+ ASTNode *step = NULL;
+ if (lexer_peek(l).type != TOK_RPAREN && lexer_peek(l).type != TOK_LBRACE) {
+ step = parse_expression(ctx, l);
+ }
-ASTNode *parse_for(ParserContext *ctx, Lexer *l)
-{
+ if (lexer_peek(l).type == TOK_RPAREN) {
lexer_next(l);
+ }
+
+ ASTNode *body;
+ if (lexer_peek(l).type == TOK_LBRACE) {
+ body = parse_block(ctx, l);
+ } else {
+ body = parse_statement(ctx, l);
+ }
+ exit_scope(ctx);
+
+ ASTNode *n = ast_create(NODE_FOR);
+ n->for_stmt.init = init;
+ n->for_stmt.condition = cond;
+ n->for_stmt.step = step;
+ n->for_stmt.body = body;
+ return n;
+}
- // Range Loop: for i in 0..10
- if (lexer_peek(l).type == TOK_IDENT)
- {
- int saved_pos = l->pos;
- Token var = lexer_next(l);
- Token in_tok = lexer_next(l);
-
- if (in_tok.type == TOK_IDENT && strncmp(in_tok.start, "in", 2) == 0)
- {
- ASTNode *start_expr = parse_expression(ctx, l);
- if (lexer_peek(l).type == TOK_DOTDOT)
- {
- lexer_next(l); // consume ..
- ASTNode *end_expr = parse_expression(ctx, l);
-
- ASTNode *n = ast_create(NODE_FOR_RANGE);
- n->for_range.var_name = xmalloc(var.len + 1);
- strncpy(n->for_range.var_name, var.start, var.len);
- n->for_range.var_name[var.len] = 0;
- n->for_range.start = start_expr;
- n->for_range.end = end_expr;
-
- if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "step", 4) == 0)
- {
- lexer_next(l);
- Token s_tok = lexer_next(l);
- char *sval = xmalloc(s_tok.len + 1);
- strncpy(sval, s_tok.start, s_tok.len);
- sval[s_tok.len] = 0;
- n->for_range.step = sval;
- }
- else
- {
- n->for_range.step = NULL;
- }
-
- // Fix: Enter scope to register loop variable
- enter_scope(ctx);
- // Register loop variable so body can see it
- add_symbol(ctx, n->for_range.var_name, "int", type_new(TYPE_INT));
-
- // Handle body (brace or single stmt)
- if (lexer_peek(l).type == TOK_LBRACE)
- {
- n->for_range.body = parse_block(ctx, l);
- }
- else
- {
- n->for_range.body = parse_statement(ctx, l);
- }
- exit_scope(ctx);
-
- return n;
- }
- }
- l->pos = saved_pos; // Restore
- }
+char *process_printf_sugar(ParserContext *ctx, const char *content, int newline,
+ const char *target, char ***used_syms, int *count) {
+ char *gen = xmalloc(8192);
+ strcpy(gen, "({ ");
- // C-Style For Loop
- enter_scope(ctx);
- if (lexer_peek(l).type == TOK_LPAREN)
- {
- lexer_next(l);
- }
+ char *s = xstrdup(content);
+ char *cur = s;
- ASTNode *init = NULL;
- if (lexer_peek(l).type != TOK_SEMICOLON)
- {
- if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "var", 3) == 0)
- {
- init = parse_var_decl(ctx, l);
- }
- else
- {
- init = parse_expression(ctx, l);
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- }
- }
- }
- else
- {
- lexer_next(l);
+ while (*cur) {
+ // 1. Find text before the next '{'
+ char *brace = cur;
+ while (*brace && *brace != '{') {
+ brace++;
}
- ASTNode *cond = NULL;
- if (lexer_peek(l).type != TOK_SEMICOLON)
- {
- cond = parse_expression(ctx, l);
- }
- else
- {
- // Empty condition = true
- ASTNode *true_lit = ast_create(NODE_EXPR_LITERAL);
- true_lit->literal.type_kind = 0;
- true_lit->literal.int_val = 1;
- cond = true_lit;
- }
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
+ if (brace > cur) {
+ // Append text literal
+ char buf[256];
+ sprintf(buf, "fprintf(%s, \"%%s\", \"", target);
+ strcat(gen, buf);
+ strncat(gen, cur, brace - cur);
+ strcat(gen, "\"); ");
}
- ASTNode *step = NULL;
- if (lexer_peek(l).type != TOK_RPAREN && lexer_peek(l).type != TOK_LBRACE)
- {
- step = parse_expression(ctx, l);
+ if (*brace == 0) {
+ break;
}
- if (lexer_peek(l).type == TOK_RPAREN)
- {
- lexer_next(l);
+ // 2. Handle {expression}
+ char *p = brace + 1;
+ char *colon = NULL;
+ int depth = 1;
+ while (*p && depth > 0) {
+ if (*p == '{') {
+ depth++;
+ }
+ if (*p == '}') {
+ depth--;
+ }
+ if (depth == 1 && *p == ':' && !colon) {
+ colon = p;
+ }
+ if (depth == 0) {
+ break;
+ }
+ p++;
+ }
+
+ *p = 0; // Terminate expression
+ char *expr = brace + 1;
+
+ // Unescape \" to " in the expression code to ensure correct parsing
+ char *read = expr;
+ char *write = expr;
+ while (*read) {
+ if (*read == '\\' && *(read + 1) == '"') {
+ *write = '"';
+ read += 2;
+ write++;
+ } else {
+ *write = *read;
+ read++;
+ write++;
+ }
+ }
+ *write = 0;
+ char *fmt = NULL;
+ if (colon) {
+ *colon = 0;
+ fmt = colon + 1;
+ }
+
+ char *clean_expr = expr;
+ while (*clean_expr == ' ') {
+ clean_expr++; // Skip leading spaces
+ }
+
+ // Analyze usage
+ {
+ Lexer lex;
+ lexer_init(&lex, clean_expr);
+ Token t;
+ while ((t = lexer_next(&lex)).type != TOK_EOF) {
+ if (t.type == TOK_IDENT) {
+ char *name = token_strdup(t);
+ Symbol *sym = find_symbol_entry(ctx, name);
+ if (sym) {
+ sym->is_used = 1;
+ }
+
+ if (used_syms && count) {
+ *used_syms = xrealloc(*used_syms, sizeof(char *) * (*count + 1));
+ (*used_syms)[*count] = name;
+ (*count)++;
+ } else {
+ free(name);
+ }
+ }
+ }
+ }
+
+ char *type = find_symbol_type(ctx, clean_expr);
+ char *allocated_expr = NULL;
+
+ if (type) {
+ char func_name[512];
+ snprintf(func_name, sizeof(func_name), "%s_to_string", type);
+
+ // If type is a pointer (Struct*), check if base Struct has to_string
+ if (!find_func(ctx, func_name) && strchr(type, '*')) {
+ char base[256];
+ strcpy(base, type);
+ base[strlen(base) - 1] = 0; // remove '*'
+ snprintf(func_name, sizeof(func_name), "%s_to_string", base);
+ }
+
+ // If a _to_string method exists, rewrite 'x' -> 'x.to_string()'
+ if (find_func(ctx, func_name)) {
+ allocated_expr = xmalloc(strlen(clean_expr) + 20);
+ sprintf(allocated_expr, "%s.to_string()", clean_expr);
+ expr = allocated_expr;
+ }
+ }
+
+ // Rewrite the expression to handle pointer access (header_ptr.magic ->
+ // header_ptr->magic)
+ char *wrapped_expr = xmalloc(strlen(expr) + 5);
+ sprintf(wrapped_expr, "#{%s}", expr);
+ char *rw_expr = rewrite_expr_methods(ctx, wrapped_expr);
+ free(wrapped_expr);
+
+ if (fmt) {
+ // Explicit format: {x:%.2f}
+ char buf[128];
+ sprintf(buf, "fprintf(%s, \"%%", target);
+ strcat(gen, buf);
+ strcat(gen, fmt);
+ strcat(gen, "\", ");
+ strcat(gen, rw_expr); // Use rewritten expr
+ strcat(gen, "); ");
+ } else {
+ // Auto-detect format based on type if possible
+ const char *format_spec = NULL;
+ char *inferred_type =
+ find_symbol_type(ctx, clean_expr); // Simple variable lookup
+
+ // Basic Type Mappings
+ if (inferred_type) {
+ if (strcmp(inferred_type, "int") == 0 ||
+ strcmp(inferred_type, "i32") == 0 ||
+ strcmp(inferred_type, "bool") == 0) {
+ format_spec = "%d";
+ } else if (strcmp(inferred_type, "long") == 0 ||
+ strcmp(inferred_type, "i64") == 0 ||
+ strcmp(inferred_type, "isize") == 0) {
+ format_spec = "%ld";
+ } else if (strcmp(inferred_type, "usize") == 0 ||
+ strcmp(inferred_type, "u64") == 0) {
+ format_spec = "%lu";
+ } else if (strcmp(inferred_type, "float") == 0 ||
+ strcmp(inferred_type, "f32") == 0 ||
+ strcmp(inferred_type, "double") == 0) {
+ format_spec = "%f";
+ } else if (strcmp(inferred_type, "char") == 0 ||
+ strcmp(inferred_type, "byte") == 0) {
+ format_spec = "%c";
+ } else if (strcmp(inferred_type, "string") == 0 ||
+ strcmp(inferred_type, "str") == 0 ||
+ (inferred_type[strlen(inferred_type) - 1] == '*' &&
+ strstr(inferred_type, "char"))) {
+ format_spec = "%s";
+ } else if (strstr(inferred_type, "*")) {
+ format_spec = "%p"; // Pointer
+ }
+ }
+
+ // Check for Literals if variable lookup failed
+ if (!format_spec) {
+ if (isdigit(clean_expr[0]) || clean_expr[0] == '-') {
+ format_spec = "%d"; // Naive integer guess (could be float)
+ } else if (clean_expr[0] == '"') {
+ format_spec = "%s";
+ } else if (clean_expr[0] == '\'') {
+ format_spec = "%c";
+ }
+ }
+
+ if (format_spec) {
+ char buf[128];
+ sprintf(buf, "fprintf(%s, \"", target);
+ strcat(gen, buf);
+ strcat(gen, format_spec);
+ strcat(gen, "\", ");
+ strcat(gen, rw_expr);
+ strcat(gen, "); ");
+ } else {
+ // Fallback to runtime macro
+ char buf[128];
+ sprintf(buf, "fprintf(%s, _z_str(", target);
+ strcat(gen, buf);
+ strcat(gen, rw_expr);
+ strcat(gen, "), ");
+ strcat(gen, rw_expr);
+ strcat(gen, "); ");
+ }
}
- ASTNode *body;
- if (lexer_peek(l).type == TOK_LBRACE)
- {
- body = parse_block(ctx, l);
- }
- else
- {
- body = parse_statement(ctx, l);
+ free(rw_expr); // Don't forget to free!
+ if (allocated_expr) {
+ free(allocated_expr); // Don't forget to free the auto-generated call!
}
- exit_scope(ctx);
-
- ASTNode *n = ast_create(NODE_FOR);
- n->for_stmt.init = init;
- n->for_stmt.condition = cond;
- n->for_stmt.step = step;
- n->for_stmt.body = body;
- return n;
-}
-char *process_printf_sugar(ParserContext *ctx, const char *content, int newline, const char *target,
- char ***used_syms, int *count)
-{
- char *gen = xmalloc(8192);
- strcpy(gen, "({ ");
-
- char *s = xstrdup(content);
- char *cur = s;
-
- while (*cur)
- {
- // 1. Find text before the next '{'
- char *brace = cur;
- while (*brace && *brace != '{')
- {
- brace++;
- }
-
- if (brace > cur)
- {
- // Append text literal
- char buf[256];
- sprintf(buf, "fprintf(%s, \"%%s\", \"", target);
- strcat(gen, buf);
- strncat(gen, cur, brace - cur);
- strcat(gen, "\"); ");
- }
-
- if (*brace == 0)
- {
- break;
- }
-
- // 2. Handle {expression}
- char *p = brace + 1;
- char *colon = NULL;
- int depth = 1;
- while (*p && depth > 0)
- {
- if (*p == '{')
- {
- depth++;
- }
- if (*p == '}')
- {
- depth--;
- }
- if (depth == 1 && *p == ':' && !colon)
- {
- colon = p;
- }
- if (depth == 0)
- {
- break;
- }
- p++;
- }
+ cur = p + 1;
+ }
- *p = 0; // Terminate expression
- char *expr = brace + 1;
-
- // Unescape \" to " in the expression code to ensure correct parsing
- char *read = expr;
- char *write = expr;
- while (*read)
- {
- if (*read == '\\' && *(read + 1) == '"')
- {
- *write = '"';
- read += 2;
- write++;
- }
- else
- {
- *write = *read;
- read++;
- write++;
- }
- }
- *write = 0;
- char *fmt = NULL;
- if (colon)
- {
- *colon = 0;
- fmt = colon + 1;
- }
+ if (newline) {
+ char buf[128];
+ sprintf(buf, "fprintf(%s, \"\\n\"); ", target);
+ strcat(gen, buf);
+ } else {
+ strcat(gen, "fflush(stdout); ");
+ }
- char *clean_expr = expr;
- while (*clean_expr == ' ')
- {
- clean_expr++; // Skip leading spaces
- }
+ strcat(gen, "0; })");
- // Analyze usage
- {
- Lexer lex;
- lexer_init(&lex, clean_expr);
- Token t;
- while ((t = lexer_next(&lex)).type != TOK_EOF)
- {
- if (t.type == TOK_IDENT)
- {
- char *name = token_strdup(t);
- Symbol *sym = find_symbol_entry(ctx, name);
- if (sym)
- {
- sym->is_used = 1;
- }
-
- if (used_syms && count)
- {
- *used_syms = xrealloc(*used_syms, sizeof(char *) * (*count + 1));
- (*used_syms)[*count] = name;
- (*count)++;
- }
- else
- {
- free(name);
- }
- }
- }
- }
+ free(s);
+ return gen;
+}
- char *type = find_symbol_type(ctx, clean_expr);
- char *allocated_expr = NULL;
-
- if (type)
- {
- char func_name[512];
- snprintf(func_name, sizeof(func_name), "%s_to_string", type);
-
- // If type is a pointer (Struct*), check if base Struct has to_string
- if (!find_func(ctx, func_name) && strchr(type, '*'))
- {
- char base[256];
- strcpy(base, type);
- base[strlen(base) - 1] = 0; // remove '*'
- snprintf(func_name, sizeof(func_name), "%s_to_string", base);
- }
+ASTNode *parse_macro_call(ParserContext *ctx, Lexer *l, char *macro_name) {
+ Token start_tok = lexer_peek(l);
+ if (lexer_peek(l).type != TOK_OP || lexer_peek(l).start[0] != '!') {
+ return NULL;
+ }
+ lexer_next(l); // consume !
+
+ // Expect {
+ if (lexer_peek(l).type != TOK_LBRACE) {
+ zpanic_at(lexer_peek(l), "Expected { after macro invocation");
+ }
+ lexer_next(l); // consume {
+
+ // Collect body until }
+ char *body = xmalloc(8192);
+ body[0] = '\0';
+ int body_len = 0;
+ int depth = 1;
+ int last_line = start_tok.line;
+
+ while (depth > 0) {
+ Token t = lexer_peek(l);
+ if (t.type == TOK_EOF) {
+ zpanic_at(t, "Unexpected EOF in macro block");
+ }
- // If a _to_string method exists, rewrite 'x' -> 'x.to_string()'
- if (find_func(ctx, func_name))
- {
- allocated_expr = xmalloc(strlen(clean_expr) + 20);
- sprintf(allocated_expr, "%s.to_string()", clean_expr);
- expr = allocated_expr;
- }
- }
+ if (t.type == TOK_LBRACE) {
+ depth++;
+ }
+ if (t.type == TOK_RBRACE) {
+ depth--;
+ }
- // Rewrite the expression to handle pointer access (header_ptr.magic ->
- // header_ptr->magic)
- char *wrapped_expr = xmalloc(strlen(expr) + 5);
- sprintf(wrapped_expr, "#{%s}", expr);
- char *rw_expr = rewrite_expr_methods(ctx, wrapped_expr);
- free(wrapped_expr);
-
- if (fmt)
- {
- // Explicit format: {x:%.2f}
- char buf[128];
- sprintf(buf, "fprintf(%s, \"%%", target);
- strcat(gen, buf);
- strcat(gen, fmt);
- strcat(gen, "\", ");
- strcat(gen, rw_expr); // Use rewritten expr
- strcat(gen, "); ");
+ if (depth > 0) {
+ if (body_len + t.len + 2 < 8192) {
+ // Preserve newlines
+ if (t.line > last_line) {
+ body[body_len] = '\n';
+ body[body_len + 1] = 0;
+ body_len++;
+ } else {
+ body[body_len] = ' ';
+ body[body_len + 1] = 0;
+ body_len++;
}
- else
- {
- // Auto-detect format based on type if possible
- const char *format_spec = NULL;
- char *inferred_type = find_symbol_type(ctx, clean_expr); // Simple variable lookup
-
- // Basic Type Mappings
- if (inferred_type)
- {
- if (strcmp(inferred_type, "int") == 0 || strcmp(inferred_type, "i32") == 0 ||
- strcmp(inferred_type, "bool") == 0)
- {
- format_spec = "%d";
- }
- else if (strcmp(inferred_type, "long") == 0 || strcmp(inferred_type, "i64") == 0 ||
- strcmp(inferred_type, "isize") == 0)
- {
- format_spec = "%ld";
- }
- else if (strcmp(inferred_type, "usize") == 0 || strcmp(inferred_type, "u64") == 0)
- {
- format_spec = "%lu";
- }
- else if (strcmp(inferred_type, "float") == 0 || strcmp(inferred_type, "f32") == 0 ||
- strcmp(inferred_type, "double") == 0)
- {
- format_spec = "%f";
- }
- else if (strcmp(inferred_type, "char") == 0 || strcmp(inferred_type, "byte") == 0)
- {
- format_spec = "%c";
- }
- else if (strcmp(inferred_type, "string") == 0 ||
- strcmp(inferred_type, "str") == 0 ||
- (inferred_type[strlen(inferred_type) - 1] == '*' &&
- strstr(inferred_type, "char")))
- {
- format_spec = "%s";
- }
- else if (strstr(inferred_type, "*"))
- {
- format_spec = "%p"; // Pointer
- }
- }
-
- // Check for Literals if variable lookup failed
- if (!format_spec)
- {
- if (isdigit(clean_expr[0]) || clean_expr[0] == '-')
- {
- format_spec = "%d"; // Naive integer guess (could be float)
- }
- else if (clean_expr[0] == '"')
- {
- format_spec = "%s";
- }
- else if (clean_expr[0] == '\'')
- {
- format_spec = "%c";
- }
- }
- if (format_spec)
- {
- char buf[128];
- sprintf(buf, "fprintf(%s, \"", target);
- strcat(gen, buf);
- strcat(gen, format_spec);
- strcat(gen, "\", ");
- strcat(gen, rw_expr);
- strcat(gen, "); ");
- }
- else
- {
- // Fallback to runtime macro
- char buf[128];
- sprintf(buf, "fprintf(%s, _z_str(", target);
- strcat(gen, buf);
- strcat(gen, rw_expr);
- strcat(gen, "), ");
- strcat(gen, rw_expr);
- strcat(gen, "); ");
- }
- }
+ strncat(body, t.start, t.len);
+ body_len += t.len;
+ }
+ }
- free(rw_expr); // Don't forget to free!
- if (allocated_expr)
- {
- free(allocated_expr); // Don't forget to free the auto-generated call!
- }
+ last_line = t.line;
+ lexer_next(l);
+ }
+
+ // Resolve plugin name
+ const char *plugin_name = resolve_plugin(ctx, macro_name);
+ if (!plugin_name) {
+ char err[256];
+ snprintf(err, sizeof(err),
+ "Unknown plugin: %s (did you forget 'import plugin \"%s\"'?)",
+ macro_name, macro_name);
+ zpanic_at(start_tok, err);
+ }
+
+ // Find Plugin Definition
+ // Verify plugin exists
+ ZPlugin *found = zptr_find_plugin(plugin_name);
+
+ if (!found) {
+ char err[256];
+ snprintf(err, sizeof(err), "Plugin implementation not found: %s",
+ plugin_name);
+ zpanic_at(start_tok, err);
+ }
+
+ // Execute Plugin Immediately (Expansion)
+ FILE *capture = tmpfile();
+ if (!capture) {
+ zpanic_at(start_tok,
+ "Failed to create capture buffer for plugin expansion");
+ }
+
+ ZApi api = {.filename = g_current_filename ? g_current_filename : "input.zc",
+ .current_line = start_tok.line,
+ .out = capture,
+ .hoist_out = ctx->hoist_out};
+
+ found->fn(body, &api);
+
+ // Read captured output
+ long len = ftell(capture);
+ rewind(capture);
+ char *expanded_code = xmalloc(len + 1);
+ fread(expanded_code, 1, len, capture);
+ expanded_code[len] = 0;
+ fclose(capture);
+ free(body);
+
+ // Create Raw Statement/Expression Node
+ ASTNode *n = ast_create(NODE_RAW_STMT);
+ n->line = start_tok.line;
+ n->raw_stmt.content = expanded_code;
+
+ return n;
+}
- cur = p + 1;
- }
+ASTNode *parse_statement(ParserContext *ctx, Lexer *l) {
+ Token tk = lexer_peek(l);
+ ASTNode *s = NULL;
- if (newline)
- {
- char buf[128];
- sprintf(buf, "fprintf(%s, \"\\n\"); ", target);
- strcat(gen, buf);
+ if (tk.type == TOK_SEMICOLON) {
+ lexer_next(l);
+ ASTNode *nop = ast_create(NODE_BLOCK); // Empty block as NOP
+ nop->block.statements = NULL;
+ return nop;
+ }
+
+ if (tk.type == TOK_PREPROC) {
+ lexer_next(l); // consume token
+ char *content = xmalloc(tk.len + 2);
+ strncpy(content, tk.start, tk.len);
+ content[tk.len] = '\n'; // Ensure newline
+ content[tk.len + 1] = 0;
+ ASTNode *s = ast_create(NODE_RAW_STMT);
+ s->raw_stmt.content = content;
+ return s;
+ }
+
+ if (tk.type == TOK_STRING || tk.type == TOK_FSTRING) {
+ Lexer lookahead = *l;
+ lexer_next(&lookahead);
+ TokenType next_type = lexer_peek(&lookahead).type;
+
+ if (next_type == TOK_SEMICOLON || next_type == TOK_DOTDOT) {
+ Token t = lexer_next(l); // consume string
+
+ char *inner = xmalloc(t.len);
+ // Strip quotes
+ if (t.type == TOK_FSTRING) {
+ strncpy(inner, t.start + 2, t.len - 3);
+ inner[t.len - 3] = 0;
+ } else {
+ strncpy(inner, t.start + 1, t.len - 2);
+ inner[t.len - 2] = 0;
+ }
+
+ // ; means println (end of line), .. means print (continuation)
+ int is_ln = (next_type == TOK_SEMICOLON);
+ char **used_syms = NULL;
+ int used_count = 0;
+ char *code = process_printf_sugar(ctx, inner, is_ln, "stdout", &used_syms,
+ &used_count);
+
+ if (next_type == TOK_SEMICOLON) {
+ lexer_next(l); // consume ;
+ } else if (next_type == TOK_DOTDOT) {
+ lexer_next(l); // consume ..
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l); // consume optional ;
+ }
+ }
+
+ ASTNode *n = ast_create(NODE_RAW_STMT);
+ n->raw_stmt.content = code;
+ n->raw_stmt.used_symbols = used_syms;
+ n->raw_stmt.used_symbol_count = used_count;
+ free(inner);
+ return n;
+ }
+ }
+
+ // Block
+ if (tk.type == TOK_LBRACE) {
+ return parse_block(ctx, l);
+ }
+
+ // Keywords / Special
+ if (tk.type == TOK_TRAIT) {
+ return parse_trait(ctx, l);
+ }
+ if (tk.type == TOK_IMPL) {
+ return parse_impl(ctx, l);
+ }
+ if (tk.type == TOK_AUTOFREE) {
+ lexer_next(l);
+ if (lexer_peek(l).type != TOK_IDENT ||
+ strncmp(lexer_peek(l).start, "var", 3) != 0) {
+ zpanic_at(lexer_peek(l), "Expected 'var' after autofree");
}
- else
- {
- strcat(gen, "fflush(stdout); ");
+ s = parse_var_decl(ctx, l);
+ s->var_decl.is_autofree = 1;
+ // Mark symbol as autofree to suppress unused variable warning
+ Symbol *sym = find_symbol_entry(ctx, s->var_decl.name);
+ if (sym) {
+ sym->is_autofree = 1;
}
+ return s;
+ }
+ if (tk.type == TOK_TEST) {
+ return parse_test(ctx, l);
+ }
+ if (tk.type == TOK_COMPTIME) {
+ char *src = run_comptime_block(ctx, l);
+ Lexer new_l;
+ lexer_init(&new_l, src);
+ ASTNode *head = NULL, *tail = NULL;
- strcat(gen, "0; })");
-
- free(s);
- return gen;
-}
-
-ASTNode *parse_macro_call(ParserContext *ctx, Lexer *l, char *macro_name)
-{
- Token start_tok = lexer_peek(l);
- if (lexer_peek(l).type != TOK_OP || lexer_peek(l).start[0] != '!')
- {
- return NULL;
+ while (lexer_peek(&new_l).type != TOK_EOF) {
+ ASTNode *s = parse_statement(ctx, &new_l);
+ if (!s) {
+ break;
+ }
+ if (!head) {
+ head = s;
+ } else {
+ tail->next = s;
+ }
+ tail = s;
+ while (tail->next) {
+ tail = tail->next;
+ }
}
- lexer_next(l); // consume !
- // Expect {
- if (lexer_peek(l).type != TOK_LBRACE)
- {
- zpanic_at(lexer_peek(l), "Expected { after macro invocation");
+ if (head && !head->next) {
+ return head;
}
- lexer_next(l); // consume {
-
- // Collect body until }
- char *body = xmalloc(8192);
- body[0] = '\0';
- int body_len = 0;
- int depth = 1;
- int last_line = start_tok.line;
- while (depth > 0)
- {
- Token t = lexer_peek(l);
- if (t.type == TOK_EOF)
- {
- zpanic_at(t, "Unexpected EOF in macro block");
+ ASTNode *b = ast_create(NODE_BLOCK);
+ b->block.statements = head;
+ return b;
+ }
+ if (tk.type == TOK_ASSERT) {
+ return parse_assert(ctx, l);
+ }
+ if (tk.type == TOK_DEFER) {
+ return parse_defer(ctx, l);
+ }
+ if (tk.type == TOK_ASM) {
+ return parse_asm(ctx, l);
+ }
+
+ // Identifiers (Keywords or Expressions)
+ if (tk.type == TOK_IDENT) {
+ // Check for macro invocation: identifier! { code }
+ Lexer lookahead = *l;
+ lexer_next(&lookahead);
+ Token exclaim = lexer_peek(&lookahead);
+ lexer_next(&lookahead);
+ Token lbrace = lexer_peek(&lookahead);
+ if (exclaim.type == TOK_OP && exclaim.len == 1 && exclaim.start[0] == '!' &&
+ lbrace.type == TOK_LBRACE) {
+ // This is a macro invocation
+ char *macro_name = token_strdup(tk);
+ lexer_next(l); // consume identifier
+
+ ASTNode *n = parse_macro_call(ctx, l, macro_name);
+ free(macro_name);
+ return n;
+ }
+
+ // Check for raw blocks
+ if (strncmp(tk.start, "raw", 3) == 0 && tk.len == 3) {
+ lexer_next(l); // eat raw
+ if (lexer_peek(l).type != TOK_LBRACE) {
+ zpanic_at(lexer_peek(l), "Expected { after raw");
+ }
+ lexer_next(l); // eat {
+
+ const char *start = l->src + l->pos;
+ int depth = 1;
+ while (depth > 0) {
+ Token t = lexer_next(l);
+ if (t.type == TOK_EOF) {
+ zpanic_at(t, "Unexpected EOF in raw block");
}
-
- if (t.type == TOK_LBRACE)
- {
- depth++;
+ if (t.type == TOK_LBRACE) {
+ depth++;
}
- if (t.type == TOK_RBRACE)
- {
- depth--;
+ if (t.type == TOK_RBRACE) {
+ depth--;
}
+ }
+ const char *end = l->src + l->pos - 1;
+ size_t len = end - start;
- if (depth > 0)
- {
- if (body_len + t.len + 2 < 8192)
- {
- // Preserve newlines
- if (t.line > last_line)
- {
- body[body_len] = '\n';
- body[body_len + 1] = 0;
- body_len++;
- }
- else
- {
- body[body_len] = ' ';
- body[body_len + 1] = 0;
- body_len++;
- }
-
- strncat(body, t.start, t.len);
- body_len += t.len;
- }
- }
+ char *content = xmalloc(len + 1);
+ memcpy(content, start, len);
+ content[len] = 0;
- last_line = t.line;
- lexer_next(l);
+ ASTNode *s = ast_create(NODE_RAW_STMT);
+ s->raw_stmt.content = content;
+ return s;
}
- // Resolve plugin name
- const char *plugin_name = resolve_plugin(ctx, macro_name);
- if (!plugin_name)
- {
- char err[256];
- snprintf(err, sizeof(err), "Unknown plugin: %s (did you forget 'import plugin \"%s\"'?)",
- macro_name, macro_name);
- zpanic_at(start_tok, err);
+ // Check for plugin blocks
+ if (strncmp(tk.start, "plugin", 6) == 0 && tk.len == 6) {
+ lexer_next(l); // consume 'plugin'
+ return parse_plugin(ctx, l);
}
- // Find Plugin Definition
- // Verify plugin exists
- ZPlugin *found = zptr_find_plugin(plugin_name);
-
- if (!found)
- {
- char err[256];
- snprintf(err, sizeof(err), "Plugin implementation not found: %s", plugin_name);
- zpanic_at(start_tok, err);
+ if (strncmp(tk.start, "var", 3) == 0 && tk.len == 3) {
+ return parse_var_decl(ctx, l);
}
- // Execute Plugin Immediately (Expansion)
- FILE *capture = tmpfile();
- if (!capture)
- {
- zpanic_at(start_tok, "Failed to create capture buffer for plugin expansion");
+ // Static local variable: static var x = 0;
+ if (strncmp(tk.start, "static", 6) == 0 && tk.len == 6) {
+ lexer_next(l); // eat 'static'
+ Token next = lexer_peek(l);
+ if (strncmp(next.start, "var", 3) == 0 && next.len == 3) {
+ ASTNode *v = parse_var_decl(ctx, l);
+ v->var_decl.is_static = 1;
+ return v;
+ }
+ zpanic_at(next, "Expected 'var' after 'static'");
}
-
- ZApi api = {.filename = g_current_filename ? g_current_filename : "input.zc",
- .current_line = start_tok.line,
- .out = capture,
- .hoist_out = ctx->hoist_out};
-
- found->fn(body, &api);
-
- // Read captured output
- long len = ftell(capture);
- rewind(capture);
- char *expanded_code = xmalloc(len + 1);
- fread(expanded_code, 1, len, capture);
- expanded_code[len] = 0;
- fclose(capture);
- free(body);
-
- // Create Raw Statement/Expression Node
- ASTNode *n = ast_create(NODE_RAW_STMT);
- n->line = start_tok.line;
- n->raw_stmt.content = expanded_code;
-
- return n;
-}
-
-ASTNode *parse_statement(ParserContext *ctx, Lexer *l)
-{
- Token tk = lexer_peek(l);
- ASTNode *s = NULL;
-
- if (tk.type == TOK_SEMICOLON)
- {
- lexer_next(l);
- ASTNode *nop = ast_create(NODE_BLOCK); // Empty block as NOP
- nop->block.statements = NULL;
- return nop;
+ if (strncmp(tk.start, "const", 5) == 0 && tk.len == 5) {
+ return parse_const(ctx, l);
}
-
- if (tk.type == TOK_PREPROC)
- {
- lexer_next(l); // consume token
- char *content = xmalloc(tk.len + 2);
- strncpy(content, tk.start, tk.len);
- content[tk.len] = '\n'; // Ensure newline
- content[tk.len + 1] = 0;
- ASTNode *s = ast_create(NODE_RAW_STMT);
- s->raw_stmt.content = content;
- return s;
+ if (strncmp(tk.start, "return", 6) == 0 && tk.len == 6) {
+ return parse_return(ctx, l);
}
-
- if (tk.type == TOK_STRING || tk.type == TOK_FSTRING)
- {
- Lexer lookahead = *l;
- lexer_next(&lookahead);
- TokenType next_type = lexer_peek(&lookahead).type;
-
- if (next_type == TOK_SEMICOLON || next_type == TOK_DOTDOT)
- {
- Token t = lexer_next(l); // consume string
-
- char *inner = xmalloc(t.len);
- // Strip quotes
- if (t.type == TOK_FSTRING)
- {
- strncpy(inner, t.start + 2, t.len - 3);
- inner[t.len - 3] = 0;
- }
- else
- {
- strncpy(inner, t.start + 1, t.len - 2);
- inner[t.len - 2] = 0;
- }
-
- // ; means println (end of line), .. means print (continuation)
- int is_ln = (next_type == TOK_SEMICOLON);
- char **used_syms = NULL;
- int used_count = 0;
- char *code = process_printf_sugar(ctx, inner, is_ln, "stdout", &used_syms, &used_count);
-
- if (next_type == TOK_SEMICOLON)
- {
- lexer_next(l); // consume ;
- }
- else if (next_type == TOK_DOTDOT)
- {
- lexer_next(l); // consume ..
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l); // consume optional ;
- }
- }
-
- ASTNode *n = ast_create(NODE_RAW_STMT);
- n->raw_stmt.content = code;
- n->raw_stmt.used_symbols = used_syms;
- n->raw_stmt.used_symbol_count = used_count;
- free(inner);
- return n;
- }
+ if (strncmp(tk.start, "if", 2) == 0 && tk.len == 2) {
+ return parse_if(ctx, l);
}
-
- // Block
- if (tk.type == TOK_LBRACE)
- {
- return parse_block(ctx, l);
+ if (strncmp(tk.start, "while", 5) == 0 && tk.len == 5) {
+ return parse_while(ctx, l);
}
-
- // Keywords / Special
- if (tk.type == TOK_TRAIT)
- {
- return parse_trait(ctx, l);
+ if (strncmp(tk.start, "for", 3) == 0 && tk.len == 3) {
+ return parse_for(ctx, l);
}
- if (tk.type == TOK_IMPL)
- {
- return parse_impl(ctx, l);
+ if (strncmp(tk.start, "match", 5) == 0 && tk.len == 5) {
+ return parse_match(ctx, l);
}
- if (tk.type == TOK_AUTOFREE)
- {
+
+ // Break with optional label: break; or break 'outer;
+ if (strncmp(tk.start, "break", 5) == 0 && tk.len == 5) {
+ lexer_next(l);
+ ASTNode *n = ast_create(NODE_BREAK);
+ n->break_stmt.target_label = NULL;
+ // Check for 'label
+ if (lexer_peek(l).type == TOK_CHAR) {
+ Token label_tok = lexer_next(l);
+ // Extract label name (strip quotes)
+ char *label = xmalloc(label_tok.len);
+ strncpy(label, label_tok.start + 1, label_tok.len - 2);
+ label[label_tok.len - 2] = 0;
+ n->break_stmt.target_label = label;
+ }
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
lexer_next(l);
- if (lexer_peek(l).type != TOK_IDENT || strncmp(lexer_peek(l).start, "var", 3) != 0)
- {
- zpanic_at(lexer_peek(l), "Expected 'var' after autofree");
- }
- s = parse_var_decl(ctx, l);
- s->var_decl.is_autofree = 1;
- // Mark symbol as autofree to suppress unused variable warning
- Symbol *sym = find_symbol_entry(ctx, s->var_decl.name);
- if (sym)
- {
- sym->is_autofree = 1;
- }
- return s;
- }
- if (tk.type == TOK_TEST)
- {
- return parse_test(ctx, l);
+ }
+ return n;
+ }
+
+ // Continue with optional label
+ if (strncmp(tk.start, "continue", 8) == 0 && tk.len == 8) {
+ lexer_next(l);
+ ASTNode *n = ast_create(NODE_CONTINUE);
+ n->continue_stmt.target_label = NULL;
+ if (lexer_peek(l).type == TOK_CHAR) {
+ Token label_tok = lexer_next(l);
+ char *label = xmalloc(label_tok.len);
+ strncpy(label, label_tok.start + 1, label_tok.len - 2);
+ label[label_tok.len - 2] = 0;
+ n->continue_stmt.target_label = label;
+ }
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
+ }
+ return n;
}
- if (tk.type == TOK_COMPTIME)
- {
- char *src = run_comptime_block(ctx, l);
- Lexer new_l;
- lexer_init(&new_l, src);
- ASTNode *head = NULL, *tail = NULL;
-
- while (lexer_peek(&new_l).type != TOK_EOF)
- {
- ASTNode *s = parse_statement(ctx, &new_l);
- if (!s)
- {
- break;
- }
- if (!head)
- {
- head = s;
- }
- else
- {
- tail->next = s;
- }
- tail = s;
- while (tail->next)
- {
- tail = tail->next;
- }
- }
- if (head && !head->next)
- {
- return head;
- }
-
- ASTNode *b = ast_create(NODE_BLOCK);
- b->block.statements = head;
- return b;
+ if (strncmp(tk.start, "loop", 4) == 0 && tk.len == 4) {
+ return parse_loop(ctx, l);
}
- if (tk.type == TOK_ASSERT)
- {
- return parse_assert(ctx, l);
+ if (strncmp(tk.start, "repeat", 6) == 0 && tk.len == 6) {
+ return parse_repeat(ctx, l);
}
- if (tk.type == TOK_DEFER)
- {
- return parse_defer(ctx, l);
+ if (strncmp(tk.start, "unless", 6) == 0 && tk.len == 6) {
+ return parse_unless(ctx, l);
}
- if (tk.type == TOK_ASM)
- {
- return parse_asm(ctx, l);
+ if (strncmp(tk.start, "guard", 5) == 0 && tk.len == 5) {
+ return parse_guard(ctx, l);
}
- // Identifiers (Keywords or Expressions)
- if (tk.type == TOK_IDENT)
- {
- // Check for macro invocation: identifier! { code }
- Lexer lookahead = *l;
- lexer_next(&lookahead);
- Token exclaim = lexer_peek(&lookahead);
- lexer_next(&lookahead);
- Token lbrace = lexer_peek(&lookahead);
- if (exclaim.type == TOK_OP && exclaim.len == 1 && exclaim.start[0] == '!' &&
- lbrace.type == TOK_LBRACE)
- {
- // This is a macro invocation
- char *macro_name = token_strdup(tk);
- lexer_next(l); // consume identifier
-
- ASTNode *n = parse_macro_call(ctx, l, macro_name);
- free(macro_name);
- return n;
- }
-
- // Check for raw blocks
- if (strncmp(tk.start, "raw", 3) == 0 && tk.len == 3)
- {
- lexer_next(l); // eat raw
- if (lexer_peek(l).type != TOK_LBRACE)
- {
- zpanic_at(lexer_peek(l), "Expected { after raw");
- }
- lexer_next(l); // eat {
-
- const char *start = l->src + l->pos;
- int depth = 1;
- while (depth > 0)
- {
- Token t = lexer_next(l);
- if (t.type == TOK_EOF)
- {
- zpanic_at(t, "Unexpected EOF in raw block");
- }
- if (t.type == TOK_LBRACE)
- {
- depth++;
- }
- if (t.type == TOK_RBRACE)
- {
- depth--;
- }
- }
- const char *end = l->src + l->pos - 1;
- size_t len = end - start;
+ // Do-while loop: do { body } while condition;
+ if (strncmp(tk.start, "do", 2) == 0 && tk.len == 2) {
+ lexer_next(l); // eat 'do'
+ ASTNode *body = parse_block(ctx, l);
- char *content = xmalloc(len + 1);
- memcpy(content, start, len);
- content[len] = 0;
-
- ASTNode *s = ast_create(NODE_RAW_STMT);
- s->raw_stmt.content = content;
- return s;
- }
-
- // Check for plugin blocks
- if (strncmp(tk.start, "plugin", 6) == 0 && tk.len == 6)
- {
- lexer_next(l); // consume 'plugin'
- return parse_plugin(ctx, l);
- }
+ // Expect 'while'
+ Token while_tok = lexer_peek(l);
+ if (while_tok.type != TOK_IDENT ||
+ strncmp(while_tok.start, "while", 5) != 0 || while_tok.len != 5) {
+ zpanic_at(while_tok, "Expected 'while' after do block");
+ }
+ lexer_next(l); // eat 'while'
- if (strncmp(tk.start, "var", 3) == 0 && tk.len == 3)
- {
- return parse_var_decl(ctx, l);
- }
-
- // Static local variable: static var x = 0;
- if (strncmp(tk.start, "static", 6) == 0 && tk.len == 6)
- {
- lexer_next(l); // eat 'static'
- Token next = lexer_peek(l);
- if (strncmp(next.start, "var", 3) == 0 && next.len == 3)
- {
- ASTNode *v = parse_var_decl(ctx, l);
- v->var_decl.is_static = 1;
- return v;
- }
- zpanic_at(next, "Expected 'var' after 'static'");
- }
- if (strncmp(tk.start, "const", 5) == 0 && tk.len == 5)
- {
- return parse_const(ctx, l);
- }
- if (strncmp(tk.start, "return", 6) == 0 && tk.len == 6)
- {
- return parse_return(ctx, l);
- }
- if (strncmp(tk.start, "if", 2) == 0 && tk.len == 2)
- {
- return parse_if(ctx, l);
- }
- if (strncmp(tk.start, "while", 5) == 0 && tk.len == 5)
- {
- return parse_while(ctx, l);
- }
- if (strncmp(tk.start, "for", 3) == 0 && tk.len == 3)
- {
- return parse_for(ctx, l);
- }
- if (strncmp(tk.start, "match", 5) == 0 && tk.len == 5)
- {
- return parse_match(ctx, l);
- }
-
- // Break with optional label: break; or break 'outer;
- if (strncmp(tk.start, "break", 5) == 0 && tk.len == 5)
- {
- lexer_next(l);
- ASTNode *n = ast_create(NODE_BREAK);
- n->break_stmt.target_label = NULL;
- // Check for 'label
- if (lexer_peek(l).type == TOK_CHAR)
- {
- Token label_tok = lexer_next(l);
- // Extract label name (strip quotes)
- char *label = xmalloc(label_tok.len);
- strncpy(label, label_tok.start + 1, label_tok.len - 2);
- label[label_tok.len - 2] = 0;
- n->break_stmt.target_label = label;
- }
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- }
- return n;
- }
-
- // Continue with optional label
- if (strncmp(tk.start, "continue", 8) == 0 && tk.len == 8)
- {
- lexer_next(l);
- ASTNode *n = ast_create(NODE_CONTINUE);
- n->continue_stmt.target_label = NULL;
- if (lexer_peek(l).type == TOK_CHAR)
- {
- Token label_tok = lexer_next(l);
- char *label = xmalloc(label_tok.len);
- strncpy(label, label_tok.start + 1, label_tok.len - 2);
- label[label_tok.len - 2] = 0;
- n->continue_stmt.target_label = label;
- }
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- }
- return n;
- }
-
- if (strncmp(tk.start, "loop", 4) == 0 && tk.len == 4)
- {
- return parse_loop(ctx, l);
- }
- if (strncmp(tk.start, "repeat", 6) == 0 && tk.len == 6)
- {
- return parse_repeat(ctx, l);
- }
- if (strncmp(tk.start, "unless", 6) == 0 && tk.len == 6)
- {
- return parse_unless(ctx, l);
- }
- if (strncmp(tk.start, "guard", 5) == 0 && tk.len == 5)
- {
- return parse_guard(ctx, l);
- }
-
- // Do-while loop: do { body } while condition;
- if (strncmp(tk.start, "do", 2) == 0 && tk.len == 2)
- {
- lexer_next(l); // eat 'do'
- ASTNode *body = parse_block(ctx, l);
-
- // Expect 'while'
- Token while_tok = lexer_peek(l);
- if (while_tok.type != TOK_IDENT || strncmp(while_tok.start, "while", 5) != 0 ||
- while_tok.len != 5)
- {
- zpanic_at(while_tok, "Expected 'while' after do block");
- }
- lexer_next(l); // eat 'while'
-
- ASTNode *cond = parse_expression(ctx, l);
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- }
-
- ASTNode *n = ast_create(NODE_DO_WHILE);
- n->do_while_stmt.body = body;
- n->do_while_stmt.condition = cond;
- n->do_while_stmt.loop_label = NULL;
- return n;
- }
-
- if (strncmp(tk.start, "defer", 5) == 0 && tk.len == 5)
- {
- return parse_defer(ctx, l);
- }
-
- // Goto statement: goto label_name; OR goto *expr; (computed goto)
- if (strncmp(tk.start, "goto", 4) == 0 && tk.len == 4)
- {
- Token goto_tok = lexer_next(l); // eat 'goto'
- Token next = lexer_peek(l);
-
- // Computed goto: goto *ptr;
- if (next.type == TOK_OP && next.start[0] == '*')
- {
- lexer_next(l); // eat '*'
- ASTNode *target = parse_expression(ctx, l);
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- }
-
- ASTNode *n = ast_create(NODE_GOTO);
- n->goto_stmt.label_name = NULL;
- n->goto_stmt.goto_expr = target;
- n->token = goto_tok;
- return n;
- }
-
- // Regular goto
- Token label = lexer_next(l);
- if (label.type != TOK_IDENT)
- {
- zpanic_at(label, "Expected label name after goto");
- }
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- }
- ASTNode *n = ast_create(NODE_GOTO);
- n->goto_stmt.label_name = token_strdup(label);
- n->token = goto_tok;
- zen_trigger_at(TRIGGER_GOTO, goto_tok);
- return n;
- }
-
- // Label detection: identifier followed by : (but not ::)
- {
- Lexer lookahead = *l;
- Token ident = lexer_next(&lookahead);
- Token maybe_colon = lexer_peek(&lookahead);
- if (maybe_colon.type == TOK_COLON)
- {
- // Check it's not :: (double colon for namespaces)
- lexer_next(&lookahead);
- Token after_colon = lexer_peek(&lookahead);
- if (after_colon.type != TOK_COLON)
- {
- // This is a label!
- lexer_next(l); // eat identifier
- lexer_next(l); // eat :
- ASTNode *n = ast_create(NODE_LABEL);
- n->label_stmt.label_name = token_strdup(ident);
- n->token = ident;
- return n;
- }
- }
- }
-
- if ((strncmp(tk.start, "print", 5) == 0 && tk.len == 5) ||
- (strncmp(tk.start, "println", 7) == 0 && tk.len == 7) ||
- (strncmp(tk.start, "eprint", 6) == 0 && tk.len == 6) ||
- (strncmp(tk.start, "eprintln", 8) == 0 && tk.len == 8))
- {
-
- // Revert: User requested print without newline
- int is_ln = (tk.len == 7 || tk.len == 8);
- // int is_ln = (tk.len == 7 || tk.len == 8);
- int is_err = (tk.start[0] == 'e');
- char *target = is_err ? "stderr" : "stdout";
-
- lexer_next(l); // eat keyword
-
- Token t = lexer_next(l);
- if (t.type != TOK_STRING && t.type != TOK_FSTRING)
- {
- zpanic_at(t, "Expected string literal after print/eprint");
- }
+ ASTNode *cond = parse_expression(ctx, l);
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
+ }
- char *inner = xmalloc(t.len);
- if (t.type == TOK_FSTRING)
- {
- strncpy(inner, t.start + 2, t.len - 3);
- inner[t.len - 3] = 0;
- }
- else
- {
- strncpy(inner, t.start + 1, t.len - 2);
- inner[t.len - 2] = 0;
- }
+ ASTNode *n = ast_create(NODE_DO_WHILE);
+ n->do_while_stmt.body = body;
+ n->do_while_stmt.condition = cond;
+ n->do_while_stmt.loop_label = NULL;
+ return n;
+ }
- char **used_syms = NULL;
- int used_count = 0;
- char *code = process_printf_sugar(ctx, inner, is_ln, target, &used_syms, &used_count);
- free(inner);
+ if (strncmp(tk.start, "defer", 5) == 0 && tk.len == 5) {
+ return parse_defer(ctx, l);
+ }
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- }
+ // Goto statement: goto label_name; OR goto *expr; (computed goto)
+ if (strncmp(tk.start, "goto", 4) == 0 && tk.len == 4) {
+ Token goto_tok = lexer_next(l); // eat 'goto'
+ Token next = lexer_peek(l);
- ASTNode *n = ast_create(NODE_RAW_STMT);
- n->raw_stmt.content = code;
- n->raw_stmt.used_symbols = used_syms;
- n->raw_stmt.used_symbol_count = used_count;
- return n;
+ // Computed goto: goto *ptr;
+ if (next.type == TOK_OP && next.start[0] == '*') {
+ lexer_next(l); // eat '*'
+ ASTNode *target = parse_expression(ctx, l);
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
}
- }
-
- // Default: Expression Statement
- s = parse_expression(ctx, l);
- int has_semi = 0;
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
+ ASTNode *n = ast_create(NODE_GOTO);
+ n->goto_stmt.label_name = NULL;
+ n->goto_stmt.goto_expr = target;
+ n->token = goto_tok;
+ return n;
+ }
+
+ // Regular goto
+ Token label = lexer_next(l);
+ if (label.type != TOK_IDENT) {
+ zpanic_at(label, "Expected label name after goto");
+ }
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
lexer_next(l);
- has_semi = 1;
+ }
+ ASTNode *n = ast_create(NODE_GOTO);
+ n->goto_stmt.label_name = token_strdup(label);
+ n->token = goto_tok;
+ zen_trigger_at(TRIGGER_GOTO, goto_tok);
+ return n;
}
- // Auto-print in REPL: If no semicolon (implicit expr at block end)
- // and not an assignment, print it.
- if (ctx->is_repl && s && !has_semi)
+ // Label detection: identifier followed by : (but not ::)
{
- int is_assign = 0;
- if (s->type == NODE_EXPR_BINARY)
- {
- char *op = s->binary.op;
- if (strcmp(op, "=") == 0 ||
- (strlen(op) > 1 && op[strlen(op) - 1] == '=' && strcmp(op, "==") != 0 &&
- strcmp(op, "!=") != 0 && strcmp(op, "<=") != 0 && strcmp(op, ">=") != 0))
- {
- is_assign = 1;
- }
- }
+ Lexer lookahead = *l;
+ Token ident = lexer_next(&lookahead);
+ Token maybe_colon = lexer_peek(&lookahead);
+ if (maybe_colon.type == TOK_COLON) {
+ // Check it's not :: (double colon for namespaces)
+ lexer_next(&lookahead);
+ Token after_colon = lexer_peek(&lookahead);
+ if (after_colon.type != TOK_COLON) {
+ // This is a label!
+ lexer_next(l); // eat identifier
+ lexer_next(l); // eat :
+ ASTNode *n = ast_create(NODE_LABEL);
+ n->label_stmt.label_name = token_strdup(ident);
+ n->token = ident;
+ return n;
+ }
+ }
+ }
+
+ if ((strncmp(tk.start, "print", 5) == 0 && tk.len == 5) ||
+ (strncmp(tk.start, "println", 7) == 0 && tk.len == 7) ||
+ (strncmp(tk.start, "eprint", 6) == 0 && tk.len == 6) ||
+ (strncmp(tk.start, "eprintln", 8) == 0 && tk.len == 8)) {
+
+ // Revert: User requested print without newline
+ int is_ln = (tk.len == 7 || tk.len == 8);
+ // int is_ln = (tk.len == 7 || tk.len == 8);
+ int is_err = (tk.start[0] == 'e');
+ char *target = is_err ? "stderr" : "stdout";
+
+ lexer_next(l); // eat keyword
+
+ Token t = lexer_next(l);
+ if (t.type != TOK_STRING && t.type != TOK_FSTRING) {
+ zpanic_at(t, "Expected string literal after print/eprint");
+ }
+
+ char *inner = xmalloc(t.len);
+ if (t.type == TOK_FSTRING) {
+ strncpy(inner, t.start + 2, t.len - 3);
+ inner[t.len - 3] = 0;
+ } else {
+ strncpy(inner, t.start + 1, t.len - 2);
+ inner[t.len - 2] = 0;
+ }
+
+ char **used_syms = NULL;
+ int used_count = 0;
+ char *code = process_printf_sugar(ctx, inner, is_ln, target, &used_syms,
+ &used_count);
+ free(inner);
+
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
+ }
- if (!is_assign)
- {
- ASTNode *print_node = ast_create(NODE_REPL_PRINT);
- print_node->repl_print.expr = s;
- // Preserve line info
- print_node->line = s->line;
- print_node->token = s->token;
- return print_node;
- }
+ ASTNode *n = ast_create(NODE_RAW_STMT);
+ n->raw_stmt.content = code;
+ n->raw_stmt.used_symbols = used_syms;
+ n->raw_stmt.used_symbol_count = used_count;
+ return n;
}
+ }
- if (s)
- {
- s->line = tk.line;
- }
+ // Default: Expression Statement
+ s = parse_expression(ctx, l);
- // Check for discarded must_use result
- if (s && s->type == NODE_EXPR_CALL)
- {
- ASTNode *callee = s->call.callee;
- if (callee && callee->type == NODE_EXPR_VAR)
- {
- FuncSig *sig = find_func(ctx, callee->var_ref.name);
- if (sig && sig->must_use)
- {
- zwarn_at(tk, "Ignoring return value of function marked @must_use");
- fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET
- "Use the result or explicitly discard with `_ = ...`\n");
- }
- }
- }
-
- return s;
+ int has_semi = 0;
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
+ has_semi = 1;
+ }
+
+ // Auto-print in REPL: If no semicolon (implicit expr at block end)
+ // and not an assignment, print it.
+ if (ctx->is_repl && s && !has_semi) {
+ int is_assign = 0;
+ if (s->type == NODE_EXPR_BINARY) {
+ char *op = s->binary.op;
+ if (strcmp(op, "=") == 0 ||
+ (strlen(op) > 1 && op[strlen(op) - 1] == '=' &&
+ strcmp(op, "==") != 0 && strcmp(op, "!=") != 0 &&
+ strcmp(op, "<=") != 0 && strcmp(op, ">=") != 0)) {
+ is_assign = 1;
+ }
+ }
+
+ if (!is_assign) {
+ ASTNode *print_node = ast_create(NODE_REPL_PRINT);
+ print_node->repl_print.expr = s;
+ // Preserve line info
+ print_node->line = s->line;
+ print_node->token = s->token;
+ return print_node;
+ }
+ }
+
+ if (s) {
+ s->line = tk.line;
+ }
+
+ // Check for discarded must_use result
+ if (s && s->type == NODE_EXPR_CALL) {
+ ASTNode *callee = s->call.callee;
+ if (callee && callee->type == NODE_EXPR_VAR) {
+ FuncSig *sig = find_func(ctx, callee->var_ref.name);
+ if (sig && sig->must_use) {
+ zwarn_at(tk, "Ignoring return value of function marked @must_use");
+ fprintf(stderr, COLOR_CYAN
+ " = note: " COLOR_RESET
+ "Use the result or explicitly discard with `_ = ...`\n");
+ }
+ }
+ }
+
+ return s;
}
-ASTNode *parse_block(ParserContext *ctx, Lexer *l)
-{
- lexer_next(l); // eat '{'
- enter_scope(ctx);
- ASTNode *head = 0, *tail = 0;
-
- int unreachable = 0;
-
- while (1)
- {
- skip_comments(l);
- Token tk = lexer_peek(l);
- if (tk.type == TOK_RBRACE)
- {
- lexer_next(l);
- break;
- }
- if (tk.type == TOK_EOF)
- {
- break;
- }
-
- if (unreachable == 1)
- {
- warn_unreachable_code(tk);
- unreachable = 2; // Warned once, don't spam
- }
-
- if (tk.type == TOK_COMPTIME)
- {
- // lexer_next(l); // don't eat here, run_comptime_block expects it
- char *src = run_comptime_block(ctx, l);
- Lexer new_l;
- lexer_init(&new_l, src);
- // Parse statements from the generated source
- while (lexer_peek(&new_l).type != TOK_EOF)
- {
- ASTNode *s = parse_statement(ctx, &new_l);
- if (!s)
- {
- break; // EOF or error handling dependency
- }
-
- // Link
- if (!head)
- {
- head = s;
- }
- else
- {
- tail->next = s;
- }
- tail = s;
- while (tail->next)
- {
- tail = tail->next;
- }
- }
- continue;
- }
-
- ASTNode *s = parse_statement(ctx, l);
- if (s)
- {
- if (!head)
- {
- head = s;
- }
- else
- {
- tail->next = s;
- }
- tail = s;
- while (tail->next)
- {
- tail = tail->next; // Handle chains (e.g. var decl + defer)
- }
+ASTNode *parse_block(ParserContext *ctx, Lexer *l) {
+ lexer_next(l); // eat '{'
+ enter_scope(ctx);
+ ASTNode *head = 0, *tail = 0;
- // Check for control flow interruption
- if (s->type == NODE_RETURN || s->type == NODE_BREAK || s->type == NODE_CONTINUE)
- {
- if (unreachable == 0)
- {
- unreachable = 1;
- }
- }
- }
- }
-
- // Check for unused variables in this block scope
- if (ctx->current_scope && !ctx->is_repl)
- {
- Symbol *sym = ctx->current_scope->symbols;
- while (sym)
- {
- // Skip special names and already warned
- if (!sym->is_used && sym->name[0] != '_' && strcmp(sym->name, "it") != 0 &&
- strcmp(sym->name, "self") != 0)
- {
- // Skip autofree variables (used implicitly for cleanup)
- if (sym->is_autofree)
- {
- sym = sym->next;
- continue;
- }
-
- // RAII: Don't warn if type implements Drop (it is used implicitly)
- int has_drop = (sym->type_info && sym->type_info->has_drop);
- if (!has_drop && sym->type_info && sym->type_info->name)
- {
- ASTNode *def = find_struct_def(ctx, sym->type_info->name);
- if (def && def->type_info && def->type_info->has_drop)
- {
- has_drop = 1;
- }
- }
-
- if (!has_drop)
- {
- warn_unused_variable(sym->decl_token, sym->name);
- }
- }
- sym = sym->next;
- }
- }
+ int unreachable = 0;
- exit_scope(ctx);
- ASTNode *b = ast_create(NODE_BLOCK);
- b->block.statements = head;
- return b;
+ while (1) {
+ skip_comments(l);
+ Token tk = lexer_peek(l);
+ if (tk.type == TOK_RBRACE) {
+ lexer_next(l);
+ break;
+ }
+ if (tk.type == TOK_EOF) {
+ break;
+ }
+
+ if (unreachable == 1) {
+ warn_unreachable_code(tk);
+ unreachable = 2; // Warned once, don't spam
+ }
+
+ if (tk.type == TOK_COMPTIME) {
+ // lexer_next(l); // don't eat here, run_comptime_block expects it
+ char *src = run_comptime_block(ctx, l);
+ Lexer new_l;
+ lexer_init(&new_l, src);
+ // Parse statements from the generated source
+ while (lexer_peek(&new_l).type != TOK_EOF) {
+ ASTNode *s = parse_statement(ctx, &new_l);
+ if (!s) {
+ break; // EOF or error handling dependency
+ }
+
+ // Link
+ if (!head) {
+ head = s;
+ } else {
+ tail->next = s;
+ }
+ tail = s;
+ while (tail->next) {
+ tail = tail->next;
+ }
+ }
+ continue;
+ }
+
+ ASTNode *s = parse_statement(ctx, l);
+ if (s) {
+ if (!head) {
+ head = s;
+ } else {
+ tail->next = s;
+ }
+ tail = s;
+ while (tail->next) {
+ tail = tail->next; // Handle chains (e.g. var decl + defer)
+ }
+
+ // Check for control flow interruption
+ if (s->type == NODE_RETURN || s->type == NODE_BREAK ||
+ s->type == NODE_CONTINUE) {
+ if (unreachable == 0) {
+ unreachable = 1;
+ }
+ }
+ }
+ }
+
+ // Check for unused variables in this block scope
+ if (ctx->current_scope && !ctx->is_repl) {
+ Symbol *sym = ctx->current_scope->symbols;
+ while (sym) {
+ // Skip special names and already warned
+ if (!sym->is_used && sym->name[0] != '_' &&
+ strcmp(sym->name, "it") != 0 && strcmp(sym->name, "self") != 0) {
+ // Skip autofree variables (used implicitly for cleanup)
+ if (sym->is_autofree) {
+ sym = sym->next;
+ continue;
+ }
+
+ // RAII: Don't warn if type implements Drop (it is used implicitly)
+ int has_drop = (sym->type_info && sym->type_info->has_drop);
+ if (!has_drop && sym->type_info && sym->type_info->name) {
+ ASTNode *def = find_struct_def(ctx, sym->type_info->name);
+ if (def && def->type_info && def->type_info->has_drop) {
+ has_drop = 1;
+ }
+ }
+
+ if (!has_drop) {
+ warn_unused_variable(sym->decl_token, sym->name);
+ }
+ }
+ sym = sym->next;
+ }
+ }
+
+ exit_scope(ctx);
+ ASTNode *b = ast_create(NODE_BLOCK);
+ b->block.statements = head;
+ return b;
}
// Trait Parsing
-ASTNode *parse_trait(ParserContext *ctx, Lexer *l)
-{
- lexer_next(l); // eat trait
- Token n = lexer_next(l);
- if (n.type != TOK_IDENT)
- {
- zpanic_at(n, "Expected trait name");
+ASTNode *parse_trait(ParserContext *ctx, Lexer *l) {
+ lexer_next(l); // eat trait
+ Token n = lexer_next(l);
+ if (n.type != TOK_IDENT) {
+ zpanic_at(n, "Expected trait name");
+ }
+ char *name = xmalloc(n.len + 1);
+ strncpy(name, n.start, n.len);
+ name[n.len] = 0;
+
+ lexer_next(l); // eat {
+
+ ASTNode *methods = NULL, *tail = NULL;
+ while (1) {
+ skip_comments(l);
+ if (lexer_peek(l).type == TOK_RBRACE) {
+ lexer_next(l);
+ break;
}
- char *name = xmalloc(n.len + 1);
- strncpy(name, n.start, n.len);
- name[n.len] = 0;
-
- lexer_next(l); // eat {
-
- ASTNode *methods = NULL, *tail = NULL;
- while (1)
- {
- skip_comments(l);
- if (lexer_peek(l).type == TOK_RBRACE)
- {
- lexer_next(l);
- break;
- }
-
- // Parse method signature: fn name(args...) -> ret;
- // Re-use parse_function but stop at semicolon?
- // Actually trait methods might have default impls later, but for now just
- // signatures. Let's parse full function but body might be empty/null? Or
- // simpler: just parse signature manually.
-
- Token ft = lexer_next(l);
- if (ft.type != TOK_IDENT || strncmp(ft.start, "fn", 2) != 0)
- {
- zpanic_at(ft, "Expected fn in trait");
- }
- Token mn = lexer_next(l);
- char *mname = xmalloc(mn.len + 1);
- strncpy(mname, mn.start, mn.len);
- mname[mn.len] = 0;
-
- char **defaults = NULL;
- int arg_count = 0;
- Type **arg_types = NULL;
- char **param_names = NULL;
- int is_varargs = 0;
- char *args = parse_and_convert_args(ctx, l, &defaults, &arg_count, &arg_types, &param_names,
- &is_varargs);
-
- char *ret = xstrdup("void");
- if (lexer_peek(l).type == TOK_ARROW)
- {
- lexer_next(l);
- char *rt = parse_type(ctx, l);
- free(ret);
- ret = rt;
- }
+ // Parse method signature: fn name(args...) -> ret;
+ // Re-use parse_function but stop at semicolon?
+ // Actually trait methods might have default impls later, but for now just
+ // signatures. Let's parse full function but body might be empty/null? Or
+ // simpler: just parse signature manually.
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- ASTNode *m = ast_create(NODE_FUNCTION);
- m->func.param_names = param_names;
- m->func.name = mname;
- m->func.args = args;
- m->func.ret_type = ret;
- m->func.body = NULL;
- if (!methods)
- {
- methods = m;
- }
- else
- {
- tail->next = m;
- }
- tail = m;
- }
- else
- {
- // Default implementation? Not supported yet.
- zpanic_at(lexer_peek(l), "Trait methods must end with ; for now");
- }
+ Token ft = lexer_next(l);
+ if (ft.type != TOK_IDENT || strncmp(ft.start, "fn", 2) != 0) {
+ zpanic_at(ft, "Expected fn in trait");
}
- ASTNode *n_node = ast_create(NODE_TRAIT);
- n_node->trait.name = name;
- n_node->trait.methods = methods;
- register_trait(name);
- return n_node;
+ Token mn = lexer_next(l);
+ char *mname = xmalloc(mn.len + 1);
+ strncpy(mname, mn.start, mn.len);
+ mname[mn.len] = 0;
+
+ char **defaults = NULL;
+ int arg_count = 0;
+ Type **arg_types = NULL;
+ char **param_names = NULL;
+ int is_varargs = 0;
+ char *args = parse_and_convert_args(ctx, l, &defaults, &arg_count,
+ &arg_types, &param_names, &is_varargs);
+
+ char *ret = xstrdup("void");
+ if (lexer_peek(l).type == TOK_ARROW) {
+ lexer_next(l);
+ char *rt = parse_type(ctx, l);
+ free(ret);
+ ret = rt;
+ }
+
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
+ ASTNode *m = ast_create(NODE_FUNCTION);
+ m->func.param_names = param_names;
+ m->func.name = mname;
+ m->func.args = args;
+ m->func.ret_type = ret;
+ m->func.body = NULL;
+ if (!methods) {
+ methods = m;
+ } else {
+ tail->next = m;
+ }
+ tail = m;
+ } else {
+ // Default implementation? Not supported yet.
+ zpanic_at(lexer_peek(l), "Trait methods must end with ; for now");
+ }
+ }
+
+ ASTNode *n_node = ast_create(NODE_TRAIT);
+ n_node->trait.name = name;
+ n_node->trait.methods = methods;
+ register_trait(name);
+ return n_node;
}
-ASTNode *parse_impl(ParserContext *ctx, Lexer *l)
-{
+ASTNode *parse_impl(ParserContext *ctx, Lexer *l) {
- lexer_next(l); // eat impl
- Token t1 = lexer_next(l);
- char *name1 = token_strdup(t1);
+ lexer_next(l); // eat impl
+ Token t1 = lexer_next(l);
+ char *name1 = token_strdup(t1);
- char *gen_param = NULL;
- // Check for <T> on the struct name
- if (lexer_peek(l).type == TOK_LANGLE)
- {
- lexer_next(l); // eat <
- Token gt = lexer_next(l);
- gen_param = token_strdup(gt);
- if (lexer_next(l).type != TOK_RANGLE)
- {
- zpanic_at(lexer_peek(l), "Expected >");
- }
+ char *gen_param = NULL;
+ // Check for <T> on the struct name
+ if (lexer_peek(l).type == TOK_LANGLE) {
+ lexer_next(l); // eat <
+ Token gt = lexer_next(l);
+ gen_param = token_strdup(gt);
+ if (lexer_next(l).type != TOK_RANGLE) {
+ zpanic_at(lexer_peek(l), "Expected >");
}
+ }
- // Check for "for" (Trait impl)
- Token pk = lexer_peek(l);
- if (pk.type == TOK_FOR ||
- (pk.type == TOK_IDENT && strncmp(pk.start, "for", 3) == 0 && pk.len == 3))
- {
- if (pk.type != TOK_FOR)
- {
- lexer_next(l);
- }
- else
- {
- lexer_next(l); // eat for
- }
- Token t2 = lexer_next(l);
- char *name2 = token_strdup(t2);
-
- register_impl(ctx, name1, name2);
-
- // RAII: Check for "Drop" trait implementation
- if (strcmp(name1, "Drop") == 0)
- {
- Symbol *s = find_symbol_entry(ctx, name2);
- if (s && s->type_info)
- {
- s->type_info->has_drop = 1;
- }
- else
- {
- // Try finding struct definition
- ASTNode *def = find_struct_def(ctx, name2);
- if (def && def->type_info)
- {
- def->type_info->has_drop = 1;
- }
- }
- }
-
- ctx->current_impl_struct = name2; // Set context to prevent duplicate emission and prefixing
-
- lexer_next(l); // eat {
- ASTNode *h = 0, *tl = 0;
- while (1)
- {
- skip_comments(l);
- if (lexer_peek(l).type == TOK_RBRACE)
- {
- lexer_next(l);
- break;
- }
- if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0)
- {
- ASTNode *f = parse_function(ctx, l, 0);
- // Mangle: Type_Trait_Method
- char *mangled = xmalloc(strlen(name2) + strlen(name1) + strlen(f->func.name) + 4);
- sprintf(mangled, "%s_%s_%s", name2, name1, f->func.name);
- free(f->func.name);
- f->func.name = mangled;
- char *na = patch_self_args(f->func.args, name2);
- free(f->func.args);
- f->func.args = na;
- if (!h)
- {
- h = f;
- }
- else
- {
- tl->next = f;
- }
- tl = f;
- }
- else if (lexer_peek(l).type == TOK_ASYNC)
- {
- lexer_next(l); // eat async
- if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0)
- {
- ASTNode *f = parse_function(ctx, l, 1);
- f->func.is_async = 1;
- // Mangle: Type_Trait_Method
- char *mangled =
- xmalloc(strlen(name2) + strlen(name1) + strlen(f->func.name) + 4);
- sprintf(mangled, "%s_%s_%s", name2, name1, f->func.name);
- free(f->func.name);
- f->func.name = mangled;
- char *na = patch_self_args(f->func.args, name2);
- free(f->func.args);
- f->func.args = na;
- if (!h)
- {
- h = f;
- }
- else
- {
- tl->next = f;
- }
- tl = f;
- }
- else
- {
- zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'");
- }
- }
- else
- {
- lexer_next(l);
- }
- }
- ctx->current_impl_struct = NULL; // Restore context
- ASTNode *n = ast_create(NODE_IMPL_TRAIT);
- n->impl_trait.trait_name = name1;
- n->impl_trait.target_type = name2;
- n->impl_trait.methods = h;
- add_to_impl_list(ctx, n);
- return n;
+ // Check for "for" (Trait impl)
+ Token pk = lexer_peek(l);
+ if (pk.type == TOK_FOR || (pk.type == TOK_IDENT &&
+ strncmp(pk.start, "for", 3) == 0 && pk.len == 3)) {
+ if (pk.type != TOK_FOR) {
+ lexer_next(l);
+ } else {
+ lexer_next(l); // eat for
}
- else
- {
- // Regular impl Struct (impl Box or impl Box<T>)
-
- // Auto-prefix struct name if in module context
- if (ctx->current_module_prefix && !gen_param)
- {
- char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(name1) + 2);
- sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name1);
- free(name1);
- name1 = prefixed_name;
- }
+ Token t2 = lexer_next(l);
+ char *name2 = token_strdup(t2);
- ctx->current_impl_struct = name1; // For patch_self_args inside parse_function
+ register_impl(ctx, name1, name2);
- if (gen_param)
- {
- // GENERIC IMPL TEMPLATE: impl Box<T>
- if (lexer_next(l).type != TOK_LBRACE)
- {
- zpanic_at(lexer_peek(l), "Expected {");
- }
- ASTNode *h = 0, *tl = 0;
- while (1)
- {
- skip_comments(l);
- if (lexer_peek(l).type == TOK_RBRACE)
- {
- lexer_next(l);
- break;
- }
- if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0)
- {
- ASTNode *f = parse_function(ctx, l, 0);
- // Standard Mangle for template: Box_method
- char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 2);
- sprintf(mangled, "%s_%s", name1, f->func.name);
- free(f->func.name);
- f->func.name = mangled;
-
- char *na = patch_self_args(f->func.args, name1);
- free(f->func.args);
- f->func.args = na;
-
- if (!h)
- {
- h = f;
- }
- else
- {
- tl->next = f;
- }
- tl = f;
- }
- else if (lexer_peek(l).type == TOK_ASYNC)
- {
- lexer_next(l); // eat async
- if (lexer_peek(l).type == TOK_IDENT &&
- strncmp(lexer_peek(l).start, "fn", 2) == 0)
- {
- ASTNode *f = parse_function(ctx, l, 1);
- f->func.is_async = 1;
- char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 2);
- sprintf(mangled, "%s_%s", name1, f->func.name);
- free(f->func.name);
- f->func.name = mangled;
- char *na = patch_self_args(f->func.args, name1);
- free(f->func.args);
- f->func.args = na;
- if (!h)
- {
- h = f;
- }
- else
- {
- tl->next = f;
- }
- tl = f;
- }
- else
- {
- zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'");
- }
- }
- else
- {
- lexer_next(l);
- }
- }
- // Register Template
- ASTNode *n = ast_create(NODE_IMPL);
- n->impl.struct_name = name1;
- n->impl.methods = h;
- register_impl_template(ctx, name1, gen_param, n);
- ctx->current_impl_struct = NULL;
- return NULL; // Do not emit generic template
- }
- else
- {
- // REGULAR IMPL
- lexer_next(l); // eat {
- ASTNode *h = 0, *tl = 0;
- while (1)
- {
- skip_comments(l);
- if (lexer_peek(l).type == TOK_RBRACE)
- {
- lexer_next(l);
- break;
- }
- if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0)
- {
- ASTNode *f = parse_function(ctx, l, 0);
-
- // Standard Mangle: Struct_method
- char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 2);
- sprintf(mangled, "%s_%s", name1, f->func.name);
- free(f->func.name);
- f->func.name = mangled;
-
- char *na = patch_self_args(f->func.args, name1);
- free(f->func.args);
- f->func.args = na;
-
- register_func(ctx, mangled, f->func.arg_count, f->func.defaults,
- f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, 0,
- f->token);
-
- if (!h)
- {
- h = f;
- }
- else
- {
- tl->next = f;
- }
- tl = f;
- }
- else if (lexer_peek(l).type == TOK_ASYNC)
- {
- lexer_next(l);
- if (lexer_peek(l).type == TOK_IDENT &&
- strncmp(lexer_peek(l).start, "fn", 2) == 0)
- {
- ASTNode *f = parse_function(ctx, l, 1);
- f->func.is_async = 1;
- char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 2);
- sprintf(mangled, "%s_%s", name1, f->func.name);
- free(f->func.name);
- f->func.name = mangled;
- char *na = patch_self_args(f->func.args, name1);
- free(f->func.args);
- f->func.args = na;
- register_func(ctx, mangled, f->func.arg_count, f->func.defaults,
- f->func.arg_types, f->func.ret_type_info, f->func.is_varargs,
- 1, f->token);
- if (!h)
- {
- h = f;
- }
- else
- {
- tl->next = f;
- }
- tl = f;
- }
- else
- {
- zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'");
- }
- }
- else
- {
- lexer_next(l);
- }
- }
- ctx->current_impl_struct = NULL;
- ASTNode *n = ast_create(NODE_IMPL);
- n->impl.struct_name = name1;
- n->impl.methods = h;
- add_to_impl_list(ctx, n);
- return n;
+ // RAII: Check for "Drop" trait implementation
+ if (strcmp(name1, "Drop") == 0) {
+ Symbol *s = find_symbol_entry(ctx, name2);
+ if (s && s->type_info) {
+ s->type_info->has_drop = 1;
+ } else {
+ // Try finding struct definition
+ ASTNode *def = find_struct_def(ctx, name2);
+ if (def && def->type_info) {
+ def->type_info->has_drop = 1;
}
+ }
}
-}
-
-ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union)
-{
- lexer_next(l); // eat struct or union
- Token n = lexer_next(l);
- char *name = token_strdup(n);
-
- // Generic Param <T>
- char *gp = NULL;
- if (lexer_peek(l).type == TOK_LANGLE)
- {
- lexer_next(l);
- Token g = lexer_next(l);
- gp = token_strdup(g);
- lexer_next(l);
- register_generic(ctx, name);
- }
-
- // Check for prototype (forward declaration)
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- ASTNode *n = ast_create(NODE_STRUCT);
- n->strct.name = name;
- n->strct.is_template = (gp != NULL);
- n->strct.generic_param = gp;
- n->strct.is_union = is_union;
- n->strct.fields = NULL;
- n->strct.is_incomplete = 1;
-
- if (!gp)
- {
- add_to_struct_list(ctx, n);
- }
- return n;
- }
+ ctx->current_impl_struct =
+ name2; // Set context to prevent duplicate emission and prefixing
lexer_next(l); // eat {
ASTNode *h = 0, *tl = 0;
+ while (1) {
+ skip_comments(l);
+ if (lexer_peek(l).type == TOK_RBRACE) {
+ lexer_next(l);
+ break;
+ }
+ if (lexer_peek(l).type == TOK_IDENT &&
+ strncmp(lexer_peek(l).start, "fn", 2) == 0) {
+ ASTNode *f = parse_function(ctx, l, 0);
+ // Mangle: Type_Trait_Method
+ char *mangled =
+ xmalloc(strlen(name2) + strlen(name1) + strlen(f->func.name) + 4);
+ sprintf(mangled, "%s_%s_%s", name2, name1, f->func.name);
+ free(f->func.name);
+ f->func.name = mangled;
+ char *na = patch_self_args(f->func.args, name2);
+ free(f->func.args);
+ f->func.args = na;
+ if (!h) {
+ h = f;
+ } else {
+ tl->next = f;
+ }
+ tl = f;
+ } else if (lexer_peek(l).type == TOK_ASYNC) {
+ lexer_next(l); // eat async
+ if (lexer_peek(l).type == TOK_IDENT &&
+ strncmp(lexer_peek(l).start, "fn", 2) == 0) {
+ ASTNode *f = parse_function(ctx, l, 1);
+ f->func.is_async = 1;
+ // Mangle: Type_Trait_Method
+ char *mangled =
+ xmalloc(strlen(name2) + strlen(name1) + strlen(f->func.name) + 4);
+ sprintf(mangled, "%s_%s_%s", name2, name1, f->func.name);
+ free(f->func.name);
+ f->func.name = mangled;
+ char *na = patch_self_args(f->func.args, name2);
+ free(f->func.args);
+ f->func.args = na;
+ if (!h) {
+ h = f;
+ } else {
+ tl->next = f;
+ }
+ tl = f;
+ } else {
+ zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'");
+ }
+ } else {
+ lexer_next(l);
+ }
+ }
+ ctx->current_impl_struct = NULL; // Restore context
+ ASTNode *n = ast_create(NODE_IMPL_TRAIT);
+ n->impl_trait.trait_name = name1;
+ n->impl_trait.target_type = name2;
+ n->impl_trait.methods = h;
+ add_to_impl_list(ctx, n);
+ return n;
+ } else {
+ // Regular impl Struct (impl Box or impl Box<T>)
- while (1)
- {
+ // Auto-prefix struct name if in module context
+ if (ctx->current_module_prefix && !gen_param) {
+ char *prefixed_name =
+ xmalloc(strlen(ctx->current_module_prefix) + strlen(name1) + 2);
+ sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name1);
+ free(name1);
+ name1 = prefixed_name;
+ }
+
+ ctx->current_impl_struct =
+ name1; // For patch_self_args inside parse_function
+
+ if (gen_param) {
+ // GENERIC IMPL TEMPLATE: impl Box<T>
+ if (lexer_next(l).type != TOK_LBRACE) {
+ zpanic_at(lexer_peek(l), "Expected {");
+ }
+ ASTNode *h = 0, *tl = 0;
+ while (1) {
skip_comments(l);
- Token t = lexer_peek(l);
- if (t.type == TOK_RBRACE)
- {
- lexer_next(l);
- break;
- }
- if (t.type == TOK_SEMICOLON || t.type == TOK_COMMA)
- {
- lexer_next(l);
- continue;
- }
-
- // --- HANDLE 'use' (Struct Embedding) ---
- if (t.type == TOK_USE)
- {
- lexer_next(l); // eat use
- // Parse the type (e.g. Header<I32>)
- Type *use_type = parse_type_formal(ctx, l);
- char *use_name = type_to_string(use_type);
-
- expect(l, TOK_SEMICOLON, "Expected ; after use");
-
- // Find the definition and COPY fields
- ASTNode *def = find_struct_def(ctx, use_name);
- if (!def && is_known_generic(ctx, use_type->name))
- {
- // Try to force instantiation if not found?
- // For now, rely on parse_type having triggered instantiation.
- char *mangled =
- type_to_string(use_type); // This works if type_to_string returns mangled name
- def = find_struct_def(ctx, mangled);
- free(mangled);
- }
-
- if (def && def->type == NODE_STRUCT)
- {
- ASTNode *f = def->strct.fields;
- while (f)
- {
- ASTNode *nf = ast_create(NODE_FIELD);
- nf->field.name = xstrdup(f->field.name);
- nf->field.type = xstrdup(f->field.type);
- if (!h)
- {
- h = nf;
- }
- else
- {
- tl->next = nf;
- }
- tl = nf;
- f = f->next;
- }
- }
- else
- {
- // If definition not found (e.g. user struct defined later), we can't
- // embed fields yet. Compiler limitation: 'use' requires struct to be
- // defined before. Fallback: Emit a placeholder field so compilation
- // doesn't crash, but layout will be wrong. printf("Warning: Could not
- // find struct '%s' for embedding.\n", use_name);
- }
- free(use_name);
- continue;
- }
- // ---------------------------------------
-
- if (t.type == TOK_IDENT)
- {
- Token f_name = lexer_next(l);
- expect(l, TOK_COLON, "Expected :");
- char *f_type = parse_type(ctx, l);
-
- ASTNode *f = ast_create(NODE_FIELD);
- f->field.name = token_strdup(f_name);
- f->field.type = f_type;
- f->field.bit_width = 0;
-
- // Optional bit width: name: type : 3
- if (lexer_peek(l).type == TOK_COLON)
- {
- lexer_next(l); // eat :
- Token width_tok = lexer_next(l);
- if (width_tok.type != TOK_INT)
- {
- zpanic_at(width_tok, "Expected bit width integer");
- }
- f->field.bit_width = atoi(token_strdup(width_tok));
- }
-
- if (!h)
- {
- h = f;
- }
- else
- {
- tl->next = f;
+ if (lexer_peek(l).type == TOK_RBRACE) {
+ lexer_next(l);
+ break;
+ }
+ if (lexer_peek(l).type == TOK_IDENT &&
+ strncmp(lexer_peek(l).start, "fn", 2) == 0) {
+ ASTNode *f = parse_function(ctx, l, 0);
+ // Standard Mangle for template: Box_method
+ char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 2);
+ sprintf(mangled, "%s_%s", name1, f->func.name);
+ free(f->func.name);
+ f->func.name = mangled;
+
+ char *na = patch_self_args(f->func.args, name1);
+ free(f->func.args);
+ f->func.args = na;
+
+ if (!h) {
+ h = f;
+ } else {
+ tl->next = f;
+ }
+ tl = f;
+ } else if (lexer_peek(l).type == TOK_ASYNC) {
+ lexer_next(l); // eat async
+ if (lexer_peek(l).type == TOK_IDENT &&
+ strncmp(lexer_peek(l).start, "fn", 2) == 0) {
+ ASTNode *f = parse_function(ctx, l, 1);
+ f->func.is_async = 1;
+ char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 2);
+ sprintf(mangled, "%s_%s", name1, f->func.name);
+ free(f->func.name);
+ f->func.name = mangled;
+ char *na = patch_self_args(f->func.args, name1);
+ free(f->func.args);
+ f->func.args = na;
+ if (!h) {
+ h = f;
+ } else {
+ tl->next = f;
}
tl = f;
-
- if (lexer_peek(l).type == TOK_SEMICOLON || lexer_peek(l).type == TOK_COMMA)
- {
- lexer_next(l);
+ } else {
+ zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'");
+ }
+ } else {
+ lexer_next(l);
+ }
+ }
+ // Register Template
+ ASTNode *n = ast_create(NODE_IMPL);
+ n->impl.struct_name = name1;
+ n->impl.methods = h;
+ register_impl_template(ctx, name1, gen_param, n);
+ ctx->current_impl_struct = NULL;
+ return NULL; // Do not emit generic template
+ } else {
+ // REGULAR IMPL
+ lexer_next(l); // eat {
+ ASTNode *h = 0, *tl = 0;
+ while (1) {
+ skip_comments(l);
+ if (lexer_peek(l).type == TOK_RBRACE) {
+ lexer_next(l);
+ break;
+ }
+ if (lexer_peek(l).type == TOK_IDENT &&
+ strncmp(lexer_peek(l).start, "fn", 2) == 0) {
+ ASTNode *f = parse_function(ctx, l, 0);
+
+ // Standard Mangle: Struct_method
+ char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 2);
+ sprintf(mangled, "%s_%s", name1, f->func.name);
+ free(f->func.name);
+ f->func.name = mangled;
+
+ char *na = patch_self_args(f->func.args, name1);
+ free(f->func.args);
+ f->func.args = na;
+
+ register_func(ctx, mangled, f->func.arg_count, f->func.defaults,
+ f->func.arg_types, f->func.ret_type_info,
+ f->func.is_varargs, 0, f->token);
+
+ if (!h) {
+ h = f;
+ } else {
+ tl->next = f;
+ }
+ tl = f;
+ } else if (lexer_peek(l).type == TOK_ASYNC) {
+ lexer_next(l);
+ if (lexer_peek(l).type == TOK_IDENT &&
+ strncmp(lexer_peek(l).start, "fn", 2) == 0) {
+ ASTNode *f = parse_function(ctx, l, 1);
+ f->func.is_async = 1;
+ char *mangled = xmalloc(strlen(name1) + strlen(f->func.name) + 2);
+ sprintf(mangled, "%s_%s", name1, f->func.name);
+ free(f->func.name);
+ f->func.name = mangled;
+ char *na = patch_self_args(f->func.args, name1);
+ free(f->func.args);
+ f->func.args = na;
+ register_func(ctx, mangled, f->func.arg_count, f->func.defaults,
+ f->func.arg_types, f->func.ret_type_info,
+ f->func.is_varargs, 1, f->token);
+ if (!h) {
+ h = f;
+ } else {
+ tl->next = f;
}
- }
- else
- {
- lexer_next(l);
- }
- }
-
- ASTNode *node = ast_create(NODE_STRUCT);
- add_to_struct_list(ctx, node);
-
- // Auto-prefix struct name if in module context
- if (ctx->current_module_prefix && !gp)
- { // Don't prefix generic templates
- char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(name) + 2);
- sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name);
- free(name);
- name = prefixed_name;
- }
-
- node->strct.name = name;
-
- // Initialize Type Info so we can track traits (like Drop)
- node->type_info = type_new(TYPE_STRUCT);
- node->type_info->name = xstrdup(name);
- if (gp)
- {
- node->type_info->kind = TYPE_GENERIC;
- // TODO: track generic params
- }
-
- node->strct.fields = h;
- node->strct.generic_param = gp;
- node->strct.is_union = is_union;
-
- if (gp)
- {
- node->strct.is_template = 1;
- register_template(ctx, name, node);
- }
-
- // Register definition for 'use' lookups and LSP
- if (!gp)
- {
- register_struct_def(ctx, name, node);
- }
-
- return node;
+ tl = f;
+ } else {
+ zpanic_at(lexer_peek(l), "Expected 'fn' after 'async'");
+ }
+ } else {
+ lexer_next(l);
+ }
+ }
+ ctx->current_impl_struct = NULL;
+ ASTNode *n = ast_create(NODE_IMPL);
+ n->impl.struct_name = name1;
+ n->impl.methods = h;
+ add_to_impl_list(ctx, n);
+ return n;
+ }
+ }
}
-Type *parse_type_obj(ParserContext *ctx, Lexer *l)
-{
- // 1. Parse the base type (int, U32, MyStruct, etc.)
- Type *t = parse_type_base(ctx, l);
+ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) {
- // 2. Handle Pointers (e.g. int***)
- while (lexer_peek(l).type == TOK_OP && lexer_peek(l).start[0] == '*')
- {
- lexer_next(l); // eat *
- // Wrap the current type in a Pointer type
- Type *ptr = type_new(TYPE_POINTER);
- ptr->inner = t;
- t = ptr;
- }
-
- // (Optional: You can add array parsing here later if needed)
-
- return t;
-}
+ lexer_next(l); // eat struct or union
+ Token n = lexer_next(l);
+ char *name = token_strdup(n);
-ASTNode *parse_enum(ParserContext *ctx, Lexer *l)
-{
+ // Generic Param <T>
+ char *gp = NULL;
+ if (lexer_peek(l).type == TOK_LANGLE) {
lexer_next(l);
- Token n = lexer_next(l);
-
- // 1. Check for Generic <T>
- char *gp = NULL;
- if (lexer_peek(l).type == TOK_LANGLE)
- {
- lexer_next(l); // eat <
- Token g = lexer_next(l);
- gp = token_strdup(g);
- lexer_next(l); // eat >
- register_generic(ctx, n.start ? token_strdup(n) : "anon");
- }
-
- lexer_next(l); // eat {
-
- ASTNode *h = 0, *tl = 0;
- int v = 0;
- char *ename = token_strdup(n); // Store enum name
-
- while (1)
- {
- skip_comments(l);
- Token t = lexer_peek(l);
- if (t.type == TOK_RBRACE)
- {
- lexer_next(l);
- break;
- }
- if (t.type == TOK_COMMA)
- {
- lexer_next(l);
- continue;
- }
-
- if (t.type == TOK_IDENT)
- {
- Token vt = lexer_next(l);
- char *vname = token_strdup(vt);
-
- // 2. Parse Payload Type (Ok(int))
- Type *payload = NULL;
- if (lexer_peek(l).type == TOK_LPAREN)
- {
- lexer_next(l);
- payload = parse_type_obj(ctx, l);
- if (lexer_next(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected )");
- }
- }
-
- ASTNode *va = ast_create(NODE_ENUM_VARIANT);
- va->variant.name = vname;
- va->variant.tag_id = v++; // Use tag_id instead of value
- va->variant.payload = payload; // Store Type*
-
- // Register Variant (Mangled name to avoid collisions: Result_Ok)
- char mangled[256];
- sprintf(mangled, "%s_%s", ename, vname);
- register_enum_variant(ctx, ename, mangled, va->variant.tag_id);
-
- // Handle explicit assignment: Ok = 5
- if (lexer_peek(l).type == TOK_OP && *lexer_peek(l).start == '=')
- {
- lexer_next(l);
- va->variant.tag_id = atoi(lexer_next(l).start);
- v = va->variant.tag_id + 1;
- }
-
- if (!h)
- {
- h = va;
- }
- else
- {
- tl->next = va;
- }
- tl = va;
- }
- else
- {
- lexer_next(l);
- }
- }
-
- // Auto-prefix enum name if in module context
- if (ctx->current_module_prefix && !gp)
- { // Don't prefix generic templates
- char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(ename) + 2);
- sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, ename);
- free(ename);
- ename = prefixed_name;
- }
-
- ASTNode *node = ast_create(NODE_ENUM);
- node->enm.name = ename;
-
- node->enm.variants = h;
- node->enm.generic_param = gp; // 3. Store generic param
-
- if (gp)
- {
- node->enm.is_template = 1;
- register_template(ctx, node->enm.name, node);
- }
-
- add_to_enum_list(ctx, node); // Register globally
-
- return node;
-}
-ASTNode *parse_include(ParserContext *ctx, Lexer *l)
-{
- lexer_next(l); // eat 'include'
- Token t = lexer_next(l);
- char *path = NULL;
- int is_system = 0;
+ Token g = lexer_next(l);
+ gp = token_strdup(g);
+ lexer_next(l);
+ register_generic(ctx, name);
+ }
- if (t.type == TOK_LANGLE)
- {
- // System include: include <raylib.h>
- is_system = 1;
- char buf[256];
- buf[0] = 0;
- while (1)
- {
- Token i = lexer_next(l);
- if (i.type == TOK_RANGLE)
- {
- break;
- }
- strncat(buf, i.start, i.len);
- }
- path = xstrdup(buf);
+ // Check for prototype (forward declaration)
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
+ ASTNode *n = ast_create(NODE_STRUCT);
+ n->strct.name = name;
+ n->strct.is_template = (gp != NULL);
+ n->strct.generic_param = gp;
+ n->strct.is_union = is_union;
+ n->strct.fields = NULL;
+ n->strct.is_incomplete = 1;
- // Mark that this file has external includes (suppress undefined warnings)
- ctx->has_external_includes = 1;
- }
- else
- {
- // Local include: include "file.h"
- is_system = 0;
- int len = t.len - 2;
- path = xmalloc(len + 1);
- strncpy(path, t.start + 1, len);
- path[len] = 0;
+ if (!gp) {
+ add_to_struct_list(ctx, n);
}
-
- ASTNode *n = ast_create(NODE_INCLUDE);
- n->include.path = path;
- n->include.is_system = is_system;
return n;
-}
-ASTNode *parse_import(ParserContext *ctx, Lexer *l)
-{
- lexer_next(l); // eat 'import'
-
- // Check for 'plugin' keyword
- Token next = lexer_peek(l);
- if (next.type == TOK_IDENT && next.len == 6 && strncmp(next.start, "plugin", 6) == 0)
- {
- lexer_next(l); // consume "plugin"
-
- // Expect string literal with plugin name
- Token plugin_tok = lexer_next(l);
- if (plugin_tok.type != TOK_STRING)
- {
- zpanic_at(plugin_tok, "Expected string literal after 'import plugin'");
- }
-
- // Extract plugin name (strip quotes)
- int name_len = plugin_tok.len - 2;
- char *plugin_name = xmalloc(name_len + 1);
- strncpy(plugin_name, plugin_tok.start + 1, name_len);
- plugin_name[name_len] = '\0';
-
- if (plugin_name[0] == '.' &&
- (plugin_name[1] == '/' || (plugin_name[1] == '.' && plugin_name[2] == '/')))
- {
- char *current_dir = xstrdup(g_current_filename);
- char *last_slash = strrchr(current_dir, '/');
- if (last_slash)
- {
- *last_slash = 0;
- char resolved_path[1024];
- snprintf(resolved_path, sizeof(resolved_path), "%s/%s", current_dir, plugin_name);
- free(plugin_name);
- plugin_name = xstrdup(resolved_path);
- }
- free(current_dir);
- }
+ }
- // Check for optional "as alias"
- char *alias = NULL;
- Token as_tok = lexer_peek(l);
- if (as_tok.type == TOK_IDENT && as_tok.len == 2 && strncmp(as_tok.start, "as", 2) == 0)
- {
- lexer_next(l); // consume "as"
- Token alias_tok = lexer_next(l);
- if (alias_tok.type != TOK_IDENT)
- {
- zpanic_at(alias_tok, "Expected identifier after 'as'");
- }
- alias = token_strdup(alias_tok);
- }
+ lexer_next(l); // eat {
+ ASTNode *h = 0, *tl = 0;
- // Register the plugin
- register_plugin(ctx, plugin_name, alias);
+ while (1) {
+ skip_comments(l);
+ Token t = lexer_peek(l);
+ if (t.type == TOK_RBRACE) {
+ lexer_next(l);
+ break;
+ }
+ if (t.type == TOK_SEMICOLON || t.type == TOK_COMMA) {
+ lexer_next(l);
+ continue;
+ }
+
+ // --- HANDLE 'use' (Struct Embedding) ---
+ if (t.type == TOK_USE) {
+ lexer_next(l); // eat use
+ // Parse the type (e.g. Header<I32>)
+ Type *use_type = parse_type_formal(ctx, l);
+ char *use_name = type_to_string(use_type);
+
+ expect(l, TOK_SEMICOLON, "Expected ; after use");
+
+ // Find the definition and COPY fields
+ ASTNode *def = find_struct_def(ctx, use_name);
+ if (!def && is_known_generic(ctx, use_type->name)) {
+ // Try to force instantiation if not found?
+ // For now, rely on parse_type having triggered instantiation.
+ char *mangled = type_to_string(
+ use_type); // This works if type_to_string returns mangled name
+ def = find_struct_def(ctx, mangled);
+ free(mangled);
+ }
+
+ if (def && def->type == NODE_STRUCT) {
+ ASTNode *f = def->strct.fields;
+ while (f) {
+ ASTNode *nf = ast_create(NODE_FIELD);
+ nf->field.name = xstrdup(f->field.name);
+ nf->field.type = xstrdup(f->field.type);
+ if (!h) {
+ h = nf;
+ } else {
+ tl->next = nf;
+ }
+ tl = nf;
+ f = f->next;
+ }
+ } else {
+ // If definition not found (e.g. user struct defined later), we can't
+ // embed fields yet. Compiler limitation: 'use' requires struct to be
+ // defined before. Fallback: Emit a placeholder field so compilation
+ // doesn't crash, but layout will be wrong. printf("Warning: Could not
+ // find struct '%s' for embedding.\n", use_name);
+ }
+ free(use_name);
+ continue;
+ }
+ // ---------------------------------------
+
+ if (t.type == TOK_IDENT) {
+ Token f_name = lexer_next(l);
+ expect(l, TOK_COLON, "Expected :");
+ char *f_type = parse_type(ctx, l);
+
+ ASTNode *f = ast_create(NODE_FIELD);
+ f->field.name = token_strdup(f_name);
+ f->field.type = f_type;
+ f->field.bit_width = 0;
+
+ // Optional bit width: name: type : 3
+ if (lexer_peek(l).type == TOK_COLON) {
+ lexer_next(l); // eat :
+ Token width_tok = lexer_next(l);
+ if (width_tok.type != TOK_INT) {
+ zpanic_at(width_tok, "Expected bit width integer");
+ }
+ f->field.bit_width = atoi(token_strdup(width_tok));
+ }
+
+ if (!h) {
+ h = f;
+ } else {
+ tl->next = f;
+ }
+ tl = f;
+
+ if (lexer_peek(l).type == TOK_SEMICOLON ||
+ lexer_peek(l).type == TOK_COMMA) {
+ lexer_next(l);
+ }
+ } else {
+ lexer_next(l);
+ }
+ }
+
+ ASTNode *node = ast_create(NODE_STRUCT);
+ add_to_struct_list(ctx, node);
+
+ // Auto-prefix struct name if in module context
+ if (ctx->current_module_prefix && !gp) { // Don't prefix generic templates
+ char *prefixed_name =
+ xmalloc(strlen(ctx->current_module_prefix) + strlen(name) + 2);
+ sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name);
+ free(name);
+ name = prefixed_name;
+ }
+
+ node->strct.name = name;
+
+ // Initialize Type Info so we can track traits (like Drop)
+ node->type_info = type_new(TYPE_STRUCT);
+ node->type_info->name = xstrdup(name);
+ if (gp) {
+ node->type_info->kind = TYPE_GENERIC;
+ // TODO: track generic params
+ }
+
+ node->strct.fields = h;
+ node->strct.generic_param = gp;
+ node->strct.is_union = is_union;
+
+ if (gp) {
+ node->strct.is_template = 1;
+ register_template(ctx, name, node);
+ }
+
+ // Register definition for 'use' lookups and LSP
+ if (!gp) {
+ register_struct_def(ctx, name, node);
+ }
+
+ return node;
+}
- // Consume optional semicolon
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- }
+Type *parse_type_obj(ParserContext *ctx, Lexer *l) {
+ // 1. Parse the base type (int, U32, MyStruct, etc.)
+ Type *t = parse_type_base(ctx, l);
- // Return NULL - no AST node needed for imports
- return NULL;
- }
+ // 2. Handle Pointers (e.g. int***)
+ while (lexer_peek(l).type == TOK_OP && lexer_peek(l).start[0] == '*') {
+ lexer_next(l); // eat *
+ // Wrap the current type in a Pointer type
+ Type *ptr = type_new(TYPE_POINTER);
+ ptr->inner = t;
+ t = ptr;
+ }
- // Regular module import handling follows...
- // Check if this is selective import: import { ... } from "file"
- int is_selective = 0;
- char *symbols[32]; // Max 32 selective imports
- char *aliases[32];
- int symbol_count = 0;
+ // (Optional: You can add array parsing here later if needed)
- if (lexer_peek(l).type == TOK_LBRACE)
- {
- is_selective = 1;
- lexer_next(l); // eat {
-
- // Parse symbol list
- while (lexer_peek(l).type != TOK_RBRACE)
- {
- if (symbol_count > 0 && lexer_peek(l).type == TOK_COMMA)
- {
- lexer_next(l); // eat comma
- }
+ return t;
+}
- Token sym_tok = lexer_next(l);
- if (sym_tok.type != TOK_IDENT)
- {
- zpanic_at(sym_tok, "Expected identifier in selective import");
- }
+ASTNode *parse_enum(ParserContext *ctx, Lexer *l) {
+ lexer_next(l);
+ Token n = lexer_next(l);
- symbols[symbol_count] = xmalloc(sym_tok.len + 1);
- strncpy(symbols[symbol_count], sym_tok.start, sym_tok.len);
- symbols[symbol_count][sym_tok.len] = 0;
-
- // Check for 'as alias'
- Token next = lexer_peek(l);
- if (next.type == TOK_IDENT && next.len == 2 && strncmp(next.start, "as", 2) == 0)
- {
- lexer_next(l); // eat 'as'
- Token alias_tok = lexer_next(l);
- if (alias_tok.type != TOK_IDENT)
- {
- zpanic_at(alias_tok, "Expected identifier after 'as'");
- }
-
- aliases[symbol_count] = xmalloc(alias_tok.len + 1);
- strncpy(aliases[symbol_count], alias_tok.start, alias_tok.len);
- aliases[symbol_count][alias_tok.len] = 0;
- }
- else
- {
- aliases[symbol_count] = NULL; // No alias
- }
+ // 1. Check for Generic <T>
+ char *gp = NULL;
+ if (lexer_peek(l).type == TOK_LANGLE) {
+ lexer_next(l); // eat <
+ Token g = lexer_next(l);
+ gp = token_strdup(g);
+ lexer_next(l); // eat >
+ register_generic(ctx, n.start ? token_strdup(n) : "anon");
+ }
- symbol_count++;
- }
+ lexer_next(l); // eat {
- lexer_next(l); // eat }
+ ASTNode *h = 0, *tl = 0;
+ int v = 0;
+ char *ename = token_strdup(n); // Store enum name
- // Expect 'from'
- Token from_tok = lexer_next(l);
- if (from_tok.type != TOK_IDENT || from_tok.len != 4 ||
- strncmp(from_tok.start, "from", 4) != 0)
- {
- zpanic_at(from_tok, "Expected 'from' after selective import list, got type=%d",
- from_tok.type);
- }
+ while (1) {
+ skip_comments(l);
+ Token t = lexer_peek(l);
+ if (t.type == TOK_RBRACE) {
+ lexer_next(l);
+ break;
}
-
- // Parse filename
- Token t = lexer_next(l);
- if (t.type != TOK_STRING)
- {
- zpanic_at(t,
- "Expected string (filename) after 'from' in selective import, got "
- "type %d",
- t.type);
- }
- int ln = t.len - 2; // Remove quotes
- char *fn = xmalloc(ln + 1);
- strncpy(fn, t.start + 1, ln);
- fn[ln] = 0;
-
- // Resolve relative paths (if starts with ./ or ../
- char resolved_path[1024];
- if (fn[0] == '.' && (fn[1] == '/' || (fn[1] == '.' && fn[2] == '/')))
- {
- // Relative import - resolve relative to current file
- char *current_dir = xstrdup(g_current_filename);
- char *last_slash = strrchr(current_dir, '/');
- if (last_slash)
- {
- *last_slash = 0; // Truncate to directory
- const char *leaf = fn;
- if (leaf[0] == '.' && leaf[1] == '/')
- {
- leaf += 2;
- }
- snprintf(resolved_path, sizeof(resolved_path), "%s/%s", current_dir, leaf);
- }
- else
- {
- snprintf(resolved_path, sizeof(resolved_path), "%s", fn);
- }
- free(current_dir);
- free(fn);
- fn = xstrdup(resolved_path);
+ if (t.type == TOK_COMMA) {
+ lexer_next(l);
+ continue;
}
- // Check if file exists, if not try system-wide paths
- if (access(fn, R_OK) != 0)
- {
- // Try system-wide standard library location
- static const char *system_paths[] = {"/usr/local/share/zenc", "/usr/share/zenc", NULL};
-
- char system_path[1024];
- int found = 0;
-
- for (int i = 0; system_paths[i] && !found; i++)
- {
- snprintf(system_path, sizeof(system_path), "%s/%s", system_paths[i], fn);
- if (access(system_path, R_OK) == 0)
- {
- free(fn);
- fn = xstrdup(system_path);
- found = 1;
- }
- }
+ if (t.type == TOK_IDENT) {
+ Token vt = lexer_next(l);
+ char *vname = token_strdup(vt);
- if (!found)
- {
- // File not found anywhere - will error later when trying to open
+ // 2. Parse Payload Type (Ok(int))
+ Type *payload = NULL;
+ if (lexer_peek(l).type == TOK_LPAREN) {
+ lexer_next(l);
+ payload = parse_type_obj(ctx, l);
+ if (lexer_next(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected )");
}
- }
+ }
- // Canonicalize path to avoid duplicates (for example: "./std/io.zc" vs "std/io.zc")
- char *real_fn = realpath(fn, NULL);
- if (real_fn)
- {
- free(fn);
- fn = real_fn;
- }
+ ASTNode *va = ast_create(NODE_ENUM_VARIANT);
+ va->variant.name = vname;
+ va->variant.tag_id = v++; // Use tag_id instead of value
+ va->variant.payload = payload; // Store Type*
- // Check if file already imported
- if (is_file_imported(ctx, fn))
- {
- free(fn);
- return NULL;
- }
- mark_file_imported(ctx, fn);
+ // Register Variant (Mangled name to avoid collisions: Result_Ok)
+ char mangled[256];
+ sprintf(mangled, "%s_%s", ename, vname);
+ register_enum_variant(ctx, ename, mangled, va->variant.tag_id);
- // For selective imports, register them BEFORE parsing the file
- char *module_base_name = NULL;
- if (is_selective)
- {
- module_base_name = extract_module_name(fn);
- for (int i = 0; i < symbol_count; i++)
- {
- register_selective_import(ctx, symbols[i], aliases[i], module_base_name);
- }
- }
-
- // Check for 'as alias' syntax (for namespaced imports)
+ // Handle explicit assignment: Ok = 5
+ if (lexer_peek(l).type == TOK_OP && *lexer_peek(l).start == '=') {
+ lexer_next(l);
+ va->variant.tag_id = atoi(lexer_next(l).start);
+ v = va->variant.tag_id + 1;
+ }
+
+ if (!h) {
+ h = va;
+ } else {
+ tl->next = va;
+ }
+ tl = va;
+ } else {
+ lexer_next(l);
+ }
+ }
+
+ // Auto-prefix enum name if in module context
+ if (ctx->current_module_prefix && !gp) { // Don't prefix generic templates
+ char *prefixed_name =
+ xmalloc(strlen(ctx->current_module_prefix) + strlen(ename) + 2);
+ sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, ename);
+ free(ename);
+ ename = prefixed_name;
+ }
+
+ ASTNode *node = ast_create(NODE_ENUM);
+ node->enm.name = ename;
+
+ node->enm.variants = h;
+ node->enm.generic_param = gp; // 3. Store generic param
+
+ if (gp) {
+ node->enm.is_template = 1;
+ register_template(ctx, node->enm.name, node);
+ }
+
+ add_to_enum_list(ctx, node); // Register globally
+
+ return node;
+}
+ASTNode *parse_include(ParserContext *ctx, Lexer *l) {
+ lexer_next(l); // eat 'include'
+ Token t = lexer_next(l);
+ char *path = NULL;
+ int is_system = 0;
+
+ if (t.type == TOK_LANGLE) {
+ // System include: include <raylib.h>
+ is_system = 1;
+ char buf[256];
+ buf[0] = 0;
+ while (1) {
+ Token i = lexer_next(l);
+ if (i.type == TOK_RANGLE) {
+ break;
+ }
+ strncat(buf, i.start, i.len);
+ }
+ path = xstrdup(buf);
+
+ // Mark that this file has external includes (suppress undefined warnings)
+ ctx->has_external_includes = 1;
+ } else {
+ // Local include: include "file.h"
+ is_system = 0;
+ int len = t.len - 2;
+ path = xmalloc(len + 1);
+ strncpy(path, t.start + 1, len);
+ path[len] = 0;
+ }
+
+ ASTNode *n = ast_create(NODE_INCLUDE);
+ n->include.path = path;
+ n->include.is_system = is_system;
+ return n;
+}
+ASTNode *parse_import(ParserContext *ctx, Lexer *l) {
+ lexer_next(l); // eat 'import'
+
+ // Check for 'plugin' keyword
+ Token next = lexer_peek(l);
+ if (next.type == TOK_IDENT && next.len == 6 &&
+ strncmp(next.start, "plugin", 6) == 0) {
+ lexer_next(l); // consume "plugin"
+
+ // Expect string literal with plugin name
+ Token plugin_tok = lexer_next(l);
+ if (plugin_tok.type != TOK_STRING) {
+ zpanic_at(plugin_tok, "Expected string literal after 'import plugin'");
+ }
+
+ // Extract plugin name (strip quotes)
+ int name_len = plugin_tok.len - 2;
+ char *plugin_name = xmalloc(name_len + 1);
+ strncpy(plugin_name, plugin_tok.start + 1, name_len);
+ plugin_name[name_len] = '\0';
+
+ if (plugin_name[0] == '.' &&
+ (plugin_name[1] == '/' ||
+ (plugin_name[1] == '.' && plugin_name[2] == '/'))) {
+ char *current_dir = xstrdup(g_current_filename);
+ char *last_slash = strrchr(current_dir, '/');
+ if (last_slash) {
+ *last_slash = 0;
+ char resolved_path[1024];
+ snprintf(resolved_path, sizeof(resolved_path), "%s/%s", current_dir,
+ plugin_name);
+ free(plugin_name);
+ plugin_name = xstrdup(resolved_path);
+ }
+ free(current_dir);
+ }
+
+ // Check for optional "as alias"
char *alias = NULL;
- if (!is_selective)
- {
- Token next_tok = lexer_peek(l);
- if (next_tok.type == TOK_IDENT && next_tok.len == 2 &&
- strncmp(next_tok.start, "as", 2) == 0)
- {
- lexer_next(l); // eat 'as'
- Token alias_tok = lexer_next(l);
- if (alias_tok.type != TOK_IDENT)
- {
- zpanic_at(alias_tok, "Expected identifier after 'as'");
- }
+ Token as_tok = lexer_peek(l);
+ if (as_tok.type == TOK_IDENT && as_tok.len == 2 &&
+ strncmp(as_tok.start, "as", 2) == 0) {
+ lexer_next(l); // consume "as"
+ Token alias_tok = lexer_next(l);
+ if (alias_tok.type != TOK_IDENT) {
+ zpanic_at(alias_tok, "Expected identifier after 'as'");
+ }
+ alias = token_strdup(alias_tok);
+ }
+
+ // Register the plugin
+ register_plugin(ctx, plugin_name, alias);
+
+ // Consume optional semicolon
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
+ }
+
+ // Return NULL - no AST node needed for imports
+ return NULL;
+ }
+
+ // Regular module import handling follows...
+ // Check if this is selective import: import { ... } from "file"
+ int is_selective = 0;
+ char *symbols[32]; // Max 32 selective imports
+ char *aliases[32];
+ int symbol_count = 0;
+
+ if (lexer_peek(l).type == TOK_LBRACE) {
+ is_selective = 1;
+ lexer_next(l); // eat {
- alias = xmalloc(alias_tok.len + 1);
- strncpy(alias, alias_tok.start, alias_tok.len);
- alias[alias_tok.len] = 0;
+ // Parse symbol list
+ while (lexer_peek(l).type != TOK_RBRACE) {
+ if (symbol_count > 0 && lexer_peek(l).type == TOK_COMMA) {
+ lexer_next(l); // eat comma
+ }
- // Register the module
+ Token sym_tok = lexer_next(l);
+ if (sym_tok.type != TOK_IDENT) {
+ zpanic_at(sym_tok, "Expected identifier in selective import");
+ }
- // Check if C header
- int is_header = 0;
- if (strlen(fn) > 2 && strcmp(fn + strlen(fn) - 2, ".h") == 0)
- {
- is_header = 1;
- }
+ symbols[symbol_count] = xmalloc(sym_tok.len + 1);
+ strncpy(symbols[symbol_count], sym_tok.start, sym_tok.len);
+ symbols[symbol_count][sym_tok.len] = 0;
- // Register the module
- Module *m = xmalloc(sizeof(Module));
- m->alias = xstrdup(alias);
- m->path = xstrdup(fn);
- m->base_name = extract_module_name(fn);
- m->is_c_header = is_header;
- m->next = ctx->modules;
- ctx->modules = m;
+ // Check for 'as alias'
+ Token next = lexer_peek(l);
+ if (next.type == TOK_IDENT && next.len == 2 &&
+ strncmp(next.start, "as", 2) == 0) {
+ lexer_next(l); // eat 'as'
+ Token alias_tok = lexer_next(l);
+ if (alias_tok.type != TOK_IDENT) {
+ zpanic_at(alias_tok, "Expected identifier after 'as'");
}
- }
- // C Header: Emit include and return (don't parse)
- if (strlen(fn) > 2 && strcmp(fn + strlen(fn) - 2, ".h") == 0)
- {
- ASTNode *n = ast_create(NODE_INCLUDE);
- n->include.path = xstrdup(fn); // Store exact path
- n->include.is_system = 0; // Double quotes
- return n;
- }
+ aliases[symbol_count] = xmalloc(alias_tok.len + 1);
+ strncpy(aliases[symbol_count], alias_tok.start, alias_tok.len);
+ aliases[symbol_count][alias_tok.len] = 0;
+ } else {
+ aliases[symbol_count] = NULL; // No alias
+ }
- // Load and parse the file
- char *src = load_file(fn);
- if (!src)
- {
- zpanic_at(t, "Not found: %s", fn);
+ symbol_count++;
}
- Lexer i;
- lexer_init(&i, src);
-
- // If this is a namespaced import or selective import, set the module prefix
- char *prev_module_prefix = ctx->current_module_prefix;
- char *temp_module_prefix = NULL;
-
- if (alias)
- { // For 'import "file" as alias'
- temp_module_prefix = extract_module_name(fn);
- ctx->current_module_prefix = temp_module_prefix;
- }
- else if (is_selective)
- { // For 'import {sym} from "file"'
- temp_module_prefix = extract_module_name(fn);
- ctx->current_module_prefix = temp_module_prefix;
- }
-
- // Update global filename context for relative imports inside the new file
- const char *saved_fn = g_current_filename;
- g_current_filename = fn;
+ lexer_next(l); // eat }
- ASTNode *r = parse_program_nodes(ctx, &i);
+ // Expect 'from'
+ Token from_tok = lexer_next(l);
+ if (from_tok.type != TOK_IDENT || from_tok.len != 4 ||
+ strncmp(from_tok.start, "from", 4) != 0) {
+ zpanic_at(from_tok,
+ "Expected 'from' after selective import list, got type=%d",
+ from_tok.type);
+ }
+ }
+
+ // Parse filename
+ Token t = lexer_next(l);
+ if (t.type != TOK_STRING) {
+ zpanic_at(
+ t,
+ "Expected string (filename) after 'from' in selective import, got "
+ "type %d",
+ t.type);
+ }
+ int ln = t.len - 2; // Remove quotes
+ char *fn = xmalloc(ln + 1);
+ strncpy(fn, t.start + 1, ln);
+ fn[ln] = 0;
+
+ // Resolve relative paths (if starts with ./ or ../
+ char resolved_path[1024];
+ if (fn[0] == '.' && (fn[1] == '/' || (fn[1] == '.' && fn[2] == '/'))) {
+ // Relative import - resolve relative to current file
+ char *current_dir = xstrdup(g_current_filename);
+ char *last_slash = strrchr(current_dir, '/');
+ if (last_slash) {
+ *last_slash = 0; // Truncate to directory
+ const char *leaf = fn;
+ if (leaf[0] == '.' && leaf[1] == '/') {
+ leaf += 2;
+ }
+ snprintf(resolved_path, sizeof(resolved_path), "%s/%s", current_dir,
+ leaf);
+ } else {
+ snprintf(resolved_path, sizeof(resolved_path), "%s", fn);
+ }
+ free(current_dir);
+ free(fn);
+ fn = xstrdup(resolved_path);
+ }
- // Restore filename context
- g_current_filename = (char *)saved_fn;
+ // Check if file exists, if not try system-wide paths
+ if (access(fn, R_OK) != 0) {
+ // Try system-wide standard library location
+ static const char *system_paths[] = {"/usr/local/share/zenc",
+ "/usr/share/zenc", NULL};
- // Restore previous module context
- if (temp_module_prefix)
- {
- free(temp_module_prefix);
- ctx->current_module_prefix = prev_module_prefix;
- }
+ char system_path[1024];
+ int found = 0;
- // Free selective import symbols and aliases
- if (is_selective)
- {
- for (int k = 0; k < symbol_count; k++)
- {
- free(symbols[k]);
- if (aliases[k])
- {
- free(aliases[k]);
- }
- }
+ for (int i = 0; system_paths[i] && !found; i++) {
+ snprintf(system_path, sizeof(system_path), "%s/%s", system_paths[i], fn);
+ if (access(system_path, R_OK) == 0) {
+ free(fn);
+ fn = xstrdup(system_path);
+ found = 1;
+ }
}
- if (alias)
- {
- free(alias);
+ if (!found) {
+ // File not found anywhere - will error later when trying to open
}
+ }
- if (module_base_name)
- { // This was only used for selective import
- // registration, not for ctx->current_module_prefix
- free(module_base_name);
- }
+ // Canonicalize path to avoid duplicates (for example: "./std/io.zc" vs
+ // "std/io.zc")
+ char *real_fn = realpath(fn, NULL);
+ if (real_fn) {
+ free(fn);
+ fn = real_fn;
+ }
+ // Check if file already imported
+ if (is_file_imported(ctx, fn)) {
free(fn);
- return r;
+ return NULL;
+ }
+ mark_file_imported(ctx, fn);
+
+ // For selective imports, register them BEFORE parsing the file
+ char *module_base_name = NULL;
+ if (is_selective) {
+ module_base_name = extract_module_name(fn);
+ for (int i = 0; i < symbol_count; i++) {
+ register_selective_import(ctx, symbols[i], aliases[i], module_base_name);
+ }
+ }
+
+ // Check for 'as alias' syntax (for namespaced imports)
+ char *alias = NULL;
+ if (!is_selective) {
+ Token next_tok = lexer_peek(l);
+ if (next_tok.type == TOK_IDENT && next_tok.len == 2 &&
+ strncmp(next_tok.start, "as", 2) == 0) {
+ lexer_next(l); // eat 'as'
+ Token alias_tok = lexer_next(l);
+ if (alias_tok.type != TOK_IDENT) {
+ zpanic_at(alias_tok, "Expected identifier after 'as'");
+ }
+
+ alias = xmalloc(alias_tok.len + 1);
+ strncpy(alias, alias_tok.start, alias_tok.len);
+ alias[alias_tok.len] = 0;
+
+ // Register the module
+
+ // Check if C header
+ int is_header = 0;
+ if (strlen(fn) > 2 && strcmp(fn + strlen(fn) - 2, ".h") == 0) {
+ is_header = 1;
+ }
+
+ // Register the module
+ Module *m = xmalloc(sizeof(Module));
+ m->alias = xstrdup(alias);
+ m->path = xstrdup(fn);
+ m->base_name = extract_module_name(fn);
+ m->is_c_header = is_header;
+ m->next = ctx->modules;
+ ctx->modules = m;
+ }
+ }
+
+ // C Header: Emit include and return (don't parse)
+ if (strlen(fn) > 2 && strcmp(fn + strlen(fn) - 2, ".h") == 0) {
+ ASTNode *n = ast_create(NODE_INCLUDE);
+ n->include.path = xstrdup(fn); // Store exact path
+ n->include.is_system = 0; // Double quotes
+ return n;
+ }
+
+ // Load and parse the file
+ char *src = load_file(fn);
+ if (!src) {
+ zpanic_at(t, "Not found: %s", fn);
+ }
+
+ Lexer i;
+ lexer_init(&i, src);
+
+ // If this is a namespaced import or selective import, set the module prefix
+ char *prev_module_prefix = ctx->current_module_prefix;
+ char *temp_module_prefix = NULL;
+
+ if (alias) { // For 'import "file" as alias'
+ temp_module_prefix = extract_module_name(fn);
+ ctx->current_module_prefix = temp_module_prefix;
+ } else if (is_selective) { // For 'import {sym} from "file"'
+ temp_module_prefix = extract_module_name(fn);
+ ctx->current_module_prefix = temp_module_prefix;
+ }
+
+ // Update global filename context for relative imports inside the new file
+ const char *saved_fn = g_current_filename;
+ g_current_filename = fn;
+
+ ASTNode *r = parse_program_nodes(ctx, &i);
+
+ // Restore filename context
+ g_current_filename = (char *)saved_fn;
+
+ // Restore previous module context
+ if (temp_module_prefix) {
+ free(temp_module_prefix);
+ ctx->current_module_prefix = prev_module_prefix;
+ }
+
+ // Free selective import symbols and aliases
+ if (is_selective) {
+ for (int k = 0; k < symbol_count; k++) {
+ free(symbols[k]);
+ if (aliases[k]) {
+ free(aliases[k]);
+ }
+ }
+ }
+
+ if (alias) {
+ free(alias);
+ }
+
+ if (module_base_name) { // This was only used for selective import
+ // registration, not for ctx->current_module_prefix
+ free(module_base_name);
+ }
+
+ free(fn);
+ return r;
}
// Helper: Execute comptime block and return generated source
-char *run_comptime_block(ParserContext *ctx, Lexer *l)
-{
- (void)ctx;
- expect(l, TOK_COMPTIME, "comptime");
- expect(l, TOK_LBRACE, "expected { after comptime");
-
- const char *start = l->src + l->pos;
- int depth = 1;
- while (depth > 0)
- {
- Token t = lexer_next(l);
- if (t.type == TOK_EOF)
- {
- zpanic_at(t, "Unexpected EOF in comptime block");
- }
- if (t.type == TOK_LBRACE)
- {
- depth++;
- }
- if (t.type == TOK_RBRACE)
- {
- depth--;
- }
- }
- // End is passed the closing brace, so pos points after it.
- // The code block is between start and (current pos - 1)
- int len = (l->src + l->pos - 1) - start;
- char *code = xmalloc(len + 1);
- strncpy(code, start, len);
- code[len] = 0;
-
- // Wrap in block to parse mixed statements/declarations
- int wrapped_len = len + 4; // "{ " + code + " }"
- char *wrapped_code = xmalloc(wrapped_len + 1);
- sprintf(wrapped_code, "{ %s }", code);
-
- Lexer cl;
- lexer_init(&cl, wrapped_code);
- ParserContext cctx;
- memset(&cctx, 0, sizeof(cctx));
- enter_scope(&cctx); // Global scope
- register_builtins(&cctx);
-
- ASTNode *block = parse_block(&cctx, &cl);
- ASTNode *nodes = block ? block->block.statements : NULL;
-
- free(wrapped_code);
-
- char filename[64];
- sprintf(filename, "_tmp_comptime_%d.c", rand());
- FILE *f = fopen(filename, "w");
- if (!f)
- {
- zpanic_at(lexer_peek(l), "Could not create temp file %s", filename);
- }
-
- emit_preamble(ctx, f);
- fprintf(
- f,
- "size_t _z_check_bounds(size_t index, size_t size) { if (index >= size) { fprintf(stderr, "
- "\"Index out of bounds: %%zu >= %%zu\\n\", index, size); exit(1); } return index; }\n");
-
- ASTNode *curr = nodes;
- ASTNode *stmts = NULL;
- ASTNode *stmts_tail = NULL;
-
- while (curr)
- {
- ASTNode *next = curr->next;
- curr->next = NULL;
-
- if (curr->type == NODE_INCLUDE)
- {
- emit_includes_and_aliases(curr, f);
- }
- else if (curr->type == NODE_STRUCT)
- {
- emit_struct_defs(&cctx, curr, f);
- }
- else if (curr->type == NODE_ENUM)
- {
- emit_enum_protos(curr, f);
- }
- else if (curr->type == NODE_CONST)
- {
- emit_globals(&cctx, curr, f);
- }
- else if (curr->type == NODE_FUNCTION)
- {
- codegen_node_single(&cctx, curr, f);
- }
- else if (curr->type == NODE_IMPL)
- {
- // Impl support pending
- }
- else
- {
- // Statement or expression -> main
- if (!stmts)
- {
- stmts = curr;
- }
- else
- {
- stmts_tail->next = curr;
- }
- stmts_tail = curr;
- }
- curr = next;
- }
-
- fprintf(f, "int main() {\n");
- curr = stmts;
- while (curr)
- {
- if (curr->type >= NODE_EXPR_BINARY && curr->type <= NODE_EXPR_SLICE)
- {
- codegen_expression(&cctx, curr, f);
- fprintf(f, ";\n");
- }
- else
- {
- codegen_node_single(&cctx, curr, f);
- }
- curr = curr->next;
- }
- fprintf(f, "return 0;\n}\n");
- fclose(f);
-
- char cmd[4096];
- char bin[1024];
- sprintf(bin, "%s.bin", filename);
- sprintf(cmd, "gcc %s -o %s > /dev/null 2>&1", filename, bin);
- int res = system(cmd);
- if (res != 0)
- {
- zpanic_at(lexer_peek(l), "Comptime compilation failed for:\n%s", code);
- }
-
- char out_file[1024];
- sprintf(out_file, "%s.out", filename);
- sprintf(cmd, "./%s > %s", bin, out_file);
- if (system(cmd) != 0)
- {
- zpanic_at(lexer_peek(l), "Comptime execution failed");
- }
-
- char *output_src = load_file(out_file);
- if (!output_src)
- {
- output_src = xstrdup(""); // Empty output is valid
- }
-
- // Cleanup
- remove(filename);
- remove(bin);
- remove(out_file);
- free(code);
-
- return output_src;
+char *run_comptime_block(ParserContext *ctx, Lexer *l) {
+ (void)ctx;
+ expect(l, TOK_COMPTIME, "comptime");
+ expect(l, TOK_LBRACE, "expected { after comptime");
+
+ const char *start = l->src + l->pos;
+ int depth = 1;
+ while (depth > 0) {
+ Token t = lexer_next(l);
+ if (t.type == TOK_EOF) {
+ zpanic_at(t, "Unexpected EOF in comptime block");
+ }
+ if (t.type == TOK_LBRACE) {
+ depth++;
+ }
+ if (t.type == TOK_RBRACE) {
+ depth--;
+ }
+ }
+ // End is passed the closing brace, so pos points after it.
+ // The code block is between start and (current pos - 1)
+ int len = (l->src + l->pos - 1) - start;
+ char *code = xmalloc(len + 1);
+ strncpy(code, start, len);
+ code[len] = 0;
+
+ // Wrap in block to parse mixed statements/declarations
+ int wrapped_len = len + 4; // "{ " + code + " }"
+ char *wrapped_code = xmalloc(wrapped_len + 1);
+ sprintf(wrapped_code, "{ %s }", code);
+
+ Lexer cl;
+ lexer_init(&cl, wrapped_code);
+ ParserContext cctx;
+ memset(&cctx, 0, sizeof(cctx));
+ enter_scope(&cctx); // Global scope
+ register_builtins(&cctx);
+
+ ASTNode *block = parse_block(&cctx, &cl);
+ ASTNode *nodes = block ? block->block.statements : NULL;
+
+ free(wrapped_code);
+
+ char filename[64];
+ sprintf(filename, "_tmp_comptime_%d.c", rand());
+ FILE *f = fopen(filename, "w");
+ if (!f) {
+ zpanic_at(lexer_peek(l), "Could not create temp file %s", filename);
+ }
+
+ emit_preamble(ctx, f);
+ fprintf(f, "size_t _z_check_bounds(size_t index, size_t size) { if (index >= "
+ "size) { fprintf(stderr, "
+ "\"Index out of bounds: %%zu >= %%zu\\n\", index, size); exit(1); "
+ "} return index; }\n");
+
+ ASTNode *curr = nodes;
+ ASTNode *stmts = NULL;
+ ASTNode *stmts_tail = NULL;
+
+ while (curr) {
+ ASTNode *next = curr->next;
+ curr->next = NULL;
+
+ if (curr->type == NODE_INCLUDE) {
+ emit_includes_and_aliases(curr, f);
+ } else if (curr->type == NODE_STRUCT) {
+ emit_struct_defs(&cctx, curr, f);
+ } else if (curr->type == NODE_ENUM) {
+ emit_enum_protos(curr, f);
+ } else if (curr->type == NODE_CONST) {
+ emit_globals(&cctx, curr, f);
+ } else if (curr->type == NODE_FUNCTION) {
+ codegen_node_single(&cctx, curr, f);
+ } else if (curr->type == NODE_IMPL) {
+ // Impl support pending
+ } else {
+ // Statement or expression -> main
+ if (!stmts) {
+ stmts = curr;
+ } else {
+ stmts_tail->next = curr;
+ }
+ stmts_tail = curr;
+ }
+ curr = next;
+ }
+
+ fprintf(f, "int main() {\n");
+ curr = stmts;
+ while (curr) {
+ if (curr->type >= NODE_EXPR_BINARY && curr->type <= NODE_EXPR_SLICE) {
+ codegen_expression(&cctx, curr, f);
+ fprintf(f, ";\n");
+ } else {
+ codegen_node_single(&cctx, curr, f);
+ }
+ curr = curr->next;
+ }
+ fprintf(f, "return 0;\n}\n");
+ fclose(f);
+
+ char cmd[4096];
+ char bin[1024];
+ sprintf(bin, "%s.bin", filename);
+ sprintf(cmd, "gcc %s -o %s > /dev/null 2>&1", filename, bin);
+ int res = system(cmd);
+ if (res != 0) {
+ zpanic_at(lexer_peek(l), "Comptime compilation failed for:\n%s", code);
+ }
+
+ char out_file[1024];
+ sprintf(out_file, "%s.out", filename);
+ sprintf(cmd, "./%s > %s", bin, out_file);
+ if (system(cmd) != 0) {
+ zpanic_at(lexer_peek(l), "Comptime execution failed");
+ }
+
+ char *output_src = load_file(out_file);
+ if (!output_src) {
+ output_src = xstrdup(""); // Empty output is valid
+ }
+
+ // Cleanup
+ remove(filename);
+ remove(bin);
+ remove(out_file);
+ free(code);
+
+ return output_src;
}
-ASTNode *parse_comptime(ParserContext *ctx, Lexer *l)
-{
- char *output_src = run_comptime_block(ctx, l);
+ASTNode *parse_comptime(ParserContext *ctx, Lexer *l) {
+ char *output_src = run_comptime_block(ctx, l);
- Lexer new_l;
- lexer_init(&new_l, output_src);
- return parse_program_nodes(ctx, &new_l);
+ Lexer new_l;
+ lexer_init(&new_l, output_src);
+ return parse_program_nodes(ctx, &new_l);
}
// Parse plugin block: plugin name ... end
-ASTNode *parse_plugin(ParserContext *ctx, Lexer *l)
-{
- (void)ctx;
-
- // Expect 'plugin' keyword (already consumed by caller)
- // Next should be plugin name
- Token tk = lexer_next(l);
- if (tk.type != TOK_IDENT)
- {
- zpanic_at(tk, "Expected plugin name after 'plugin' keyword");
+ASTNode *parse_plugin(ParserContext *ctx, Lexer *l) {
+ (void)ctx;
+
+ // Expect 'plugin' keyword (already consumed by caller)
+ // Next should be plugin name
+ Token tk = lexer_next(l);
+ if (tk.type != TOK_IDENT) {
+ zpanic_at(tk, "Expected plugin name after 'plugin' keyword");
+ }
+
+ // Extract plugin name
+ char *plugin_name = xmalloc(tk.len + 1);
+ strncpy(plugin_name, tk.start, tk.len);
+ plugin_name[tk.len] = '\0';
+
+ // Collect everything until 'end'
+ char *body = xmalloc(8192);
+ body[0] = '\0';
+ int body_len = 0;
+
+ while (1) {
+ Token t = lexer_peek(l);
+ if (t.type == TOK_EOF) {
+ zpanic_at(t, "Unexpected EOF in plugin block, expected 'end'");
}
- // Extract plugin name
- char *plugin_name = xmalloc(tk.len + 1);
- strncpy(plugin_name, tk.start, tk.len);
- plugin_name[tk.len] = '\0';
-
- // Collect everything until 'end'
- char *body = xmalloc(8192);
- body[0] = '\0';
- int body_len = 0;
-
- while (1)
- {
- Token t = lexer_peek(l);
- if (t.type == TOK_EOF)
- {
- zpanic_at(t, "Unexpected EOF in plugin block, expected 'end'");
- }
-
- // Check for 'end'
- if (t.type == TOK_IDENT && t.len == 3 && strncmp(t.start, "end", 3) == 0)
- {
- lexer_next(l); // consume 'end'
- break;
- }
-
- // Append token to body
- if (body_len + t.len + 2 < 8192)
- {
- strncat(body, t.start, t.len);
- body[body_len + t.len] = ' ';
- body[body_len + t.len + 1] = '\0';
- body_len += t.len + 1;
- }
+ // Check for 'end'
+ if (t.type == TOK_IDENT && t.len == 3 && strncmp(t.start, "end", 3) == 0) {
+ lexer_next(l); // consume 'end'
+ break;
+ }
- lexer_next(l);
+ // Append token to body
+ if (body_len + t.len + 2 < 8192) {
+ strncat(body, t.start, t.len);
+ body[body_len + t.len] = ' ';
+ body[body_len + t.len + 1] = '\0';
+ body_len += t.len + 1;
}
- // Create plugin node
- ASTNode *n = ast_create(NODE_PLUGIN);
- n->plugin_stmt.plugin_name = plugin_name;
- n->plugin_stmt.body = body;
+ lexer_next(l);
+ }
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l);
- }
- return n;
+ // Create plugin node
+ ASTNode *n = ast_create(NODE_PLUGIN);
+ n->plugin_stmt.plugin_name = plugin_name;
+ n->plugin_stmt.body = body;
+
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l);
+ }
+ return n;
}
diff --git a/src/parser/parser_type.c b/src/parser/parser_type.c
index d39e498..e8d9c27 100644
--- a/src/parser/parser_type.c
+++ b/src/parser/parser_type.c
@@ -6,801 +6,648 @@
#include <stdlib.h>
#include <string.h>
-Type *parse_type_base(ParserContext *ctx, Lexer *l)
-{
- Token t = lexer_peek(l);
+Type *parse_type_base(ParserContext *ctx, Lexer *l) {
+ Token t = lexer_peek(l);
+
+ if (t.type == TOK_IDENT) {
+ // Handle "struct Name" or "enum Name"
+ if ((t.len == 6 && strncmp(t.start, "struct", 6) == 0) ||
+ (t.len == 4 && strncmp(t.start, "enum", 4) == 0)) {
+ lexer_next(l); // consume keyword
+ t = lexer_peek(l);
+ if (t.type != TOK_IDENT) {
+ zpanic_at(t, "Expected identifier after struct/enum");
+ }
+ }
- if (t.type == TOK_IDENT)
- {
- // Handle "struct Name" or "enum Name"
- if ((t.len == 6 && strncmp(t.start, "struct", 6) == 0) ||
- (t.len == 4 && strncmp(t.start, "enum", 4) == 0))
- {
- lexer_next(l); // consume keyword
- t = lexer_peek(l);
- if (t.type != TOK_IDENT)
- {
- zpanic_at(t, "Expected identifier after struct/enum");
- }
- }
+ lexer_next(l);
+ char *name = token_strdup(t);
- lexer_next(l);
- char *name = token_strdup(t);
+ // Self type alias: Replace "Self" with current impl struct type
+ if (strcmp(name, "Self") == 0 && ctx->current_impl_struct) {
+ name = xstrdup(ctx->current_impl_struct);
+ }
- // Self type alias: Replace "Self" with current impl struct type
- if (strcmp(name, "Self") == 0 && ctx->current_impl_struct)
- {
- name = xstrdup(ctx->current_impl_struct);
- }
+ // Handle Namespace :: (A::B -> A_B)
+ while (lexer_peek(l).type == TOK_DCOLON) {
+ lexer_next(l); // eat ::
+ Token next = lexer_next(l);
+ if (next.type != TOK_IDENT) {
+ zpanic_at(t, "Expected identifier after ::");
+ }
+
+ char *suffix = token_strdup(next);
+ char *resolved_suffix = suffix;
+
+ // Map Zen Primitive suffixes to C types to match Generic Instantiation
+ if (strcmp(suffix, "I32") == 0) {
+ resolved_suffix = "int32_t";
+ } else if (strcmp(suffix, "U32") == 0) {
+ resolved_suffix = "uint32_t";
+ } else if (strcmp(suffix, "I8") == 0) {
+ resolved_suffix = "int8_t";
+ } else if (strcmp(suffix, "U8") == 0) {
+ resolved_suffix = "uint8_t";
+ } else if (strcmp(suffix, "I16") == 0) {
+ resolved_suffix = "int16_t";
+ } else if (strcmp(suffix, "U16") == 0) {
+ resolved_suffix = "uint16_t";
+ } else if (strcmp(suffix, "I64") == 0) {
+ resolved_suffix = "int64_t";
+ } else if (strcmp(suffix, "U64") == 0) {
+ resolved_suffix = "uint64_t";
+ }
+ // Lowercase aliases
+ else if (strcmp(suffix, "i8") == 0) {
+ resolved_suffix = "int8_t";
+ } else if (strcmp(suffix, "u8") == 0) {
+ resolved_suffix = "uint8_t";
+ } else if (strcmp(suffix, "i16") == 0) {
+ resolved_suffix = "int16_t";
+ } else if (strcmp(suffix, "u16") == 0) {
+ resolved_suffix = "uint16_t";
+ } else if (strcmp(suffix, "i32") == 0) {
+ resolved_suffix = "int32_t";
+ } else if (strcmp(suffix, "u32") == 0) {
+ resolved_suffix = "uint32_t";
+ } else if (strcmp(suffix, "i64") == 0) {
+ resolved_suffix = "int64_t";
+ } else if (strcmp(suffix, "u64") == 0) {
+ resolved_suffix = "uint64_t";
+ } else if (strcmp(suffix, "usize") == 0) {
+ resolved_suffix = "size_t";
+ } else if (strcmp(suffix, "string") == 0) {
+ resolved_suffix = "char*";
+ }
+
+ // Check if 'name' is a module alias (e.g., m::Vector)
+ Module *mod = find_module(ctx, name);
+ char *merged;
+ if (mod) {
+ // Module-qualified type: Use module base name
+ merged = xmalloc(strlen(mod->base_name) + strlen(resolved_suffix) + 2);
+ sprintf(merged, "%s_%s", mod->base_name, resolved_suffix);
+ } else {
+ // Regular namespace or enum variant
+ merged = xmalloc(strlen(name) + strlen(resolved_suffix) + 2);
+ sprintf(merged, "%s_%s", name, resolved_suffix);
+ }
+
+ free(name);
+ if (suffix != resolved_suffix) {
+ free(suffix); // Only free if we didn't remap
+ } else {
+ free(suffix);
+ }
+
+ name = merged;
+ }
- // Handle Namespace :: (A::B -> A_B)
- while (lexer_peek(l).type == TOK_DCOLON)
- {
- lexer_next(l); // eat ::
- Token next = lexer_next(l);
- if (next.type != TOK_IDENT)
- {
- zpanic_at(t, "Expected identifier after ::");
- }
-
- char *suffix = token_strdup(next);
- char *resolved_suffix = suffix;
-
- // Map Zen Primitive suffixes to C types to match Generic Instantiation
- if (strcmp(suffix, "I32") == 0)
- {
- resolved_suffix = "int32_t";
- }
- else if (strcmp(suffix, "U32") == 0)
- {
- resolved_suffix = "uint32_t";
- }
- else if (strcmp(suffix, "I8") == 0)
- {
- resolved_suffix = "int8_t";
- }
- else if (strcmp(suffix, "U8") == 0)
- {
- resolved_suffix = "uint8_t";
- }
- else if (strcmp(suffix, "I16") == 0)
- {
- resolved_suffix = "int16_t";
- }
- else if (strcmp(suffix, "U16") == 0)
- {
- resolved_suffix = "uint16_t";
- }
- else if (strcmp(suffix, "I64") == 0)
- {
- resolved_suffix = "int64_t";
- }
- else if (strcmp(suffix, "U64") == 0)
- {
- resolved_suffix = "uint64_t";
- }
- // Lowercase aliases
- else if (strcmp(suffix, "i8") == 0)
- {
- resolved_suffix = "int8_t";
- }
- else if (strcmp(suffix, "u8") == 0)
- {
- resolved_suffix = "uint8_t";
- }
- else if (strcmp(suffix, "i16") == 0)
- {
- resolved_suffix = "int16_t";
- }
- else if (strcmp(suffix, "u16") == 0)
- {
- resolved_suffix = "uint16_t";
- }
- else if (strcmp(suffix, "i32") == 0)
- {
- resolved_suffix = "int32_t";
- }
- else if (strcmp(suffix, "u32") == 0)
- {
- resolved_suffix = "uint32_t";
- }
- else if (strcmp(suffix, "i64") == 0)
- {
- resolved_suffix = "int64_t";
- }
- else if (strcmp(suffix, "u64") == 0)
- {
- resolved_suffix = "uint64_t";
- }
- else if (strcmp(suffix, "usize") == 0)
- {
- resolved_suffix = "size_t";
- }
- else if (strcmp(suffix, "string") == 0)
- {
- resolved_suffix = "char*";
- }
-
- // Check if 'name' is a module alias (e.g., m::Vector)
- Module *mod = find_module(ctx, name);
- char *merged;
- if (mod)
- {
- // Module-qualified type: Use module base name
- merged = xmalloc(strlen(mod->base_name) + strlen(resolved_suffix) + 2);
- sprintf(merged, "%s_%s", mod->base_name, resolved_suffix);
- }
- else
- {
- // Regular namespace or enum variant
- merged = xmalloc(strlen(name) + strlen(resolved_suffix) + 2);
- sprintf(merged, "%s_%s", name, resolved_suffix);
- }
-
- free(name);
- if (suffix != resolved_suffix)
- {
- free(suffix); // Only free if we didn't remap
- }
- else
- {
- free(suffix);
- }
-
- name = merged;
- }
+ // Check for Primitives (Base types)
+ if (strcmp(name, "U0") == 0) {
+ free(name);
+ return type_new(TYPE_VOID);
+ }
+ if (strcmp(name, "u0") == 0) {
+ free(name);
+ return type_new(TYPE_VOID);
+ }
+ if (strcmp(name, "I8") == 0) {
+ free(name);
+ return type_new(TYPE_I8);
+ }
+ if (strcmp(name, "U8") == 0) {
+ free(name);
+ return type_new(TYPE_U8);
+ }
+ if (strcmp(name, "I16") == 0) {
+ free(name);
+ return type_new(TYPE_I16);
+ }
+ if (strcmp(name, "U16") == 0) {
+ free(name);
+ return type_new(TYPE_U16);
+ }
+ if (strcmp(name, "I32") == 0) {
+ free(name);
+ return type_new(TYPE_I32);
+ }
+ if (strcmp(name, "U32") == 0) {
+ free(name);
+ return type_new(TYPE_U32);
+ }
+ if (strcmp(name, "I64") == 0) {
+ free(name);
+ return type_new(TYPE_I64);
+ }
+ if (strcmp(name, "U64") == 0) {
+ free(name);
+ return type_new(TYPE_U64);
+ }
+ if (strcmp(name, "F32") == 0) {
+ free(name);
+ return type_new(TYPE_F32);
+ }
+ if (strcmp(name, "f32") == 0) {
+ free(name);
+ return type_new(TYPE_F32);
+ }
+ if (strcmp(name, "F64") == 0) {
+ free(name);
+ return type_new(TYPE_F64);
+ }
+ if (strcmp(name, "f64") == 0) {
+ free(name);
+ return type_new(TYPE_F64);
+ }
+ if (strcmp(name, "usize") == 0) {
+ free(name);
+ return type_new(TYPE_USIZE);
+ }
+ if (strcmp(name, "isize") == 0) {
+ free(name);
+ return type_new(TYPE_ISIZE);
+ }
+ if (strcmp(name, "byte") == 0) {
+ free(name);
+ return type_new(TYPE_BYTE);
+ }
+ if (strcmp(name, "I128") == 0) {
+ free(name);
+ return type_new(TYPE_I128);
+ }
+ if (strcmp(name, "U128") == 0) {
+ free(name);
+ return type_new(TYPE_U128);
+ }
+ if (strcmp(name, "i8") == 0) {
+ free(name);
+ return type_new(TYPE_I8);
+ }
+ if (strcmp(name, "u8") == 0) {
+ free(name);
+ return type_new(TYPE_U8);
+ }
+ if (strcmp(name, "i16") == 0) {
+ free(name);
+ return type_new(TYPE_I16);
+ }
+ if (strcmp(name, "u16") == 0) {
+ free(name);
+ return type_new(TYPE_U16);
+ }
+ if (strcmp(name, "i32") == 0) {
+ free(name);
+ return type_new(TYPE_I32);
+ }
+ if (strcmp(name, "u32") == 0) {
+ free(name);
+ return type_new(TYPE_U32);
+ }
+ if (strcmp(name, "i64") == 0) {
+ free(name);
+ return type_new(TYPE_I64);
+ }
+ if (strcmp(name, "u64") == 0) {
+ free(name);
+ return type_new(TYPE_U64);
+ }
+ if (strcmp(name, "i128") == 0) {
+ free(name);
+ return type_new(TYPE_I128);
+ }
+ if (strcmp(name, "u128") == 0) {
+ free(name);
+ return type_new(TYPE_U128);
+ }
+ if (strcmp(name, "rune") == 0) {
+ free(name);
+ return type_new(TYPE_RUNE);
+ }
+ if (strcmp(name, "uint") == 0) {
+ free(name);
+ return type_new(TYPE_UINT);
+ }
- // Check for Primitives (Base types)
- if (strcmp(name, "U0") == 0)
- {
- free(name);
- return type_new(TYPE_VOID);
- }
- if (strcmp(name, "u0") == 0)
- {
- free(name);
- return type_new(TYPE_VOID);
- }
- if (strcmp(name, "I8") == 0)
- {
- free(name);
- return type_new(TYPE_I8);
- }
- if (strcmp(name, "U8") == 0)
- {
- free(name);
- return type_new(TYPE_U8);
- }
- if (strcmp(name, "I16") == 0)
- {
- free(name);
- return type_new(TYPE_I16);
- }
- if (strcmp(name, "U16") == 0)
- {
- free(name);
- return type_new(TYPE_U16);
- }
- if (strcmp(name, "I32") == 0)
- {
- free(name);
- return type_new(TYPE_I32);
- }
- if (strcmp(name, "U32") == 0)
- {
- free(name);
- return type_new(TYPE_U32);
- }
- if (strcmp(name, "I64") == 0)
- {
- free(name);
- return type_new(TYPE_I64);
- }
- if (strcmp(name, "U64") == 0)
- {
- free(name);
- return type_new(TYPE_U64);
- }
- if (strcmp(name, "F32") == 0)
- {
- free(name);
- return type_new(TYPE_F32);
- }
- if (strcmp(name, "f32") == 0)
- {
- free(name);
- return type_new(TYPE_F32);
- }
- if (strcmp(name, "F64") == 0)
- {
- free(name);
- return type_new(TYPE_F64);
- }
- if (strcmp(name, "f64") == 0)
- {
- free(name);
- return type_new(TYPE_F64);
- }
- if (strcmp(name, "usize") == 0)
- {
- free(name);
- return type_new(TYPE_USIZE);
- }
- if (strcmp(name, "isize") == 0)
- {
- free(name);
- return type_new(TYPE_ISIZE);
- }
- if (strcmp(name, "byte") == 0)
- {
- free(name);
- return type_new(TYPE_BYTE);
- }
- if (strcmp(name, "I128") == 0)
- {
- free(name);
- return type_new(TYPE_I128);
- }
- if (strcmp(name, "U128") == 0)
- {
- free(name);
- return type_new(TYPE_U128);
- }
- if (strcmp(name, "i8") == 0)
- {
- free(name);
- return type_new(TYPE_I8);
- }
- if (strcmp(name, "u8") == 0)
- {
- free(name);
- return type_new(TYPE_U8);
- }
- if (strcmp(name, "i16") == 0)
- {
- free(name);
- return type_new(TYPE_I16);
- }
- if (strcmp(name, "u16") == 0)
- {
- free(name);
- return type_new(TYPE_U16);
- }
- if (strcmp(name, "i32") == 0)
- {
- free(name);
- return type_new(TYPE_I32);
- }
- if (strcmp(name, "u32") == 0)
- {
- free(name);
- return type_new(TYPE_U32);
- }
- if (strcmp(name, "i64") == 0)
- {
- free(name);
- return type_new(TYPE_I64);
- }
- if (strcmp(name, "u64") == 0)
- {
- free(name);
- return type_new(TYPE_U64);
- }
- if (strcmp(name, "i128") == 0)
- {
- free(name);
- return type_new(TYPE_I128);
- }
- if (strcmp(name, "u128") == 0)
- {
- free(name);
- return type_new(TYPE_U128);
- }
- if (strcmp(name, "rune") == 0)
- {
- free(name);
- return type_new(TYPE_RUNE);
- }
- if (strcmp(name, "uint") == 0)
- {
- free(name);
- return type_new(TYPE_UINT);
- }
+ if (strcmp(name, "int") == 0) {
+ free(name);
+ return type_new(TYPE_INT);
+ }
+ if (strcmp(name, "float") == 0) {
+ free(name);
+ return type_new(TYPE_F32);
+ }
+ if (strcmp(name, "double") == 0) {
+ free(name);
+ return type_new(TYPE_F64);
+ }
+ if (strcmp(name, "void") == 0) {
+ free(name);
+ return type_new(TYPE_VOID);
+ }
+ if (strcmp(name, "string") == 0) {
+ free(name);
+ return type_new(TYPE_STRING);
+ }
+ if (strcmp(name, "bool") == 0) {
+ free(name);
+ return type_new(TYPE_BOOL);
+ }
+ if (strcmp(name, "char") == 0) {
+ free(name);
+ return type_new(TYPE_CHAR);
+ }
- if (strcmp(name, "int") == 0)
- {
- free(name);
- return type_new(TYPE_INT);
- }
- if (strcmp(name, "float") == 0)
- {
- free(name);
- return type_new(TYPE_F32);
- }
- if (strcmp(name, "double") == 0)
- {
- free(name);
- return type_new(TYPE_F64);
- }
- if (strcmp(name, "void") == 0)
- {
- free(name);
- return type_new(TYPE_VOID);
- }
- if (strcmp(name, "string") == 0)
- {
- free(name);
- return type_new(TYPE_STRING);
- }
- if (strcmp(name, "bool") == 0)
- {
- free(name);
- return type_new(TYPE_BOOL);
- }
- if (strcmp(name, "char") == 0)
- {
- free(name);
- return type_new(TYPE_CHAR);
- }
+ // Selective imports ONLY apply when we're NOT in a module context
+ if (!ctx->current_module_prefix) {
+ SelectiveImport *si = find_selective_import(ctx, name);
+ if (si) {
+ // This is a selectively imported symbol
+ // Resolve to the actual struct name which was prefixed during module
+ // parsing
+ free(name);
+ name = xmalloc(strlen(si->source_module) + strlen(si->symbol) + 2);
+ sprintf(name, "%s_%s", si->source_module, si->symbol);
+ }
+ }
- // Selective imports ONLY apply when we're NOT in a module context
- if (!ctx->current_module_prefix)
- {
- SelectiveImport *si = find_selective_import(ctx, name);
- if (si)
- {
- // This is a selectively imported symbol
- // Resolve to the actual struct name which was prefixed during module
- // parsing
- free(name);
- name = xmalloc(strlen(si->source_module) + strlen(si->symbol) + 2);
- sprintf(name, "%s_%s", si->source_module, si->symbol);
- }
- }
+ // If we're IN a module and no selective import matched, apply module prefix
+ if (ctx->current_module_prefix && !is_known_generic(ctx, name)) {
+ // Auto-prefix struct name if in module context (unless it's a known
+ // primitive/generic)
+ char *prefixed_name =
+ xmalloc(strlen(ctx->current_module_prefix) + strlen(name) + 2);
+ sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name);
+ free(name);
+ name = prefixed_name;
+ }
- // If we're IN a module and no selective import matched, apply module prefix
- if (ctx->current_module_prefix && !is_known_generic(ctx, name))
- {
- // Auto-prefix struct name if in module context (unless it's a known
- // primitive/generic)
- char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(name) + 2);
- sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name);
- free(name);
- name = prefixed_name;
- }
+ Type *ty = type_new(TYPE_STRUCT);
+ ty->name = name;
+
+ // Handle Generics <T>
+ if (lexer_peek(l).type == TOK_LANGLE) {
+ lexer_next(l); // eat <
+ Type *arg = parse_type_formal(ctx, l);
+
+ // Handle nested generics like Vec<Vec<int>> where >> is tokenized as one
+ // op
+ Token next_tok = lexer_peek(l);
+ if (next_tok.type == TOK_RANGLE) {
+ lexer_next(l); // Consume >
+ } else if (next_tok.type == TOK_OP && next_tok.len == 2 &&
+ strncmp(next_tok.start, ">>", 2) == 0) {
+ // Split >> into two > tokens
+ // Consume the first > by advancing lexer manually
+ l->pos += 1;
+ l->col += 1;
+ } else {
+ zpanic_at(t, "Expected > after generic");
+ }
+
+ char *arg_str = type_to_string(arg);
+ instantiate_generic(ctx, name, arg_str, t);
+
+ char *clean_arg = sanitize_mangled_name(arg_str);
+ char mangled[256];
+ sprintf(mangled, "%s_%s", name, clean_arg);
+ free(clean_arg);
+
+ free(ty->name);
+ ty->name = xstrdup(mangled);
+ free(arg_str);
+
+ ty->kind = TYPE_STRUCT;
+ ty->args = NULL;
+ ty->arg_count = 0;
+ }
+ return ty;
+ }
+
+ if (t.type == TOK_LBRACKET) {
+ lexer_next(l); // eat [
+ Type *inner = parse_type_formal(ctx, l);
+
+ // Check for fixed-size array [T; N]
+ if (lexer_peek(l).type == TOK_SEMICOLON) {
+ lexer_next(l); // eat ;
+ Token size_tok = lexer_next(l);
+ int size = 0;
+ if (size_tok.type == TOK_INT) {
+ size = atoi(size_tok.start);
+ } else if (size_tok.type == TOK_IDENT) {
+ // Look up in symbol table for constant propagation
+ char *name = token_strdup(size_tok);
+ Symbol *sym = find_symbol_entry(ctx, name);
+ if (sym && sym->is_const_value) {
+ size = sym->const_int_val;
+ sym->is_used = 1; // MARK AS USED
+ } else {
+ zpanic_at(
+ size_tok,
+ "Array size must be a compile-time constant or integer literal");
+ }
+ free(name);
+ } else {
+ zpanic_at(size_tok, "Expected integer for array size");
+ }
+ if (lexer_next(l).type != TOK_RBRACKET) {
+ zpanic_at(lexer_peek(l), "Expected ] after array size");
+ }
+
+ Type *arr = type_new(TYPE_ARRAY);
+ arr->inner = inner;
+ arr->array_size = size;
+ return arr;
+ }
- Type *ty = type_new(TYPE_STRUCT);
- ty->name = name;
-
- // Handle Generics <T>
- if (lexer_peek(l).type == TOK_LANGLE)
- {
- lexer_next(l); // eat <
- Type *arg = parse_type_formal(ctx, l);
-
- // Handle nested generics like Vec<Vec<int>> where >> is tokenized as one
- // op
- Token next_tok = lexer_peek(l);
- if (next_tok.type == TOK_RANGLE)
- {
- lexer_next(l); // Consume >
- }
- else if (next_tok.type == TOK_OP && next_tok.len == 2 &&
- strncmp(next_tok.start, ">>", 2) == 0)
- {
- // Split >> into two > tokens
- // Consume the first > by advancing lexer manually
- l->pos += 1;
- l->col += 1;
- }
- else
- {
- zpanic_at(t, "Expected > after generic");
- }
-
- char *arg_str = type_to_string(arg);
- instantiate_generic(ctx, name, arg_str, t);
-
- char *clean_arg = sanitize_mangled_name(arg_str);
- char mangled[256];
- sprintf(mangled, "%s_%s", name, clean_arg);
- free(clean_arg);
-
- free(ty->name);
- ty->name = xstrdup(mangled);
- free(arg_str);
-
- ty->kind = TYPE_STRUCT;
- ty->args = NULL;
- ty->arg_count = 0;
- }
- return ty;
- }
-
- if (t.type == TOK_LBRACKET)
- {
- lexer_next(l); // eat [
- Type *inner = parse_type_formal(ctx, l);
-
- // Check for fixed-size array [T; N]
- if (lexer_peek(l).type == TOK_SEMICOLON)
- {
- lexer_next(l); // eat ;
- Token size_tok = lexer_next(l);
- int size = 0;
- if (size_tok.type == TOK_INT)
- {
- size = atoi(size_tok.start);
- }
- else if (size_tok.type == TOK_IDENT)
- {
- // Look up in symbol table for constant propagation
- char *name = token_strdup(size_tok);
- Symbol *sym = find_symbol_entry(ctx, name);
- if (sym && sym->is_const_value)
- {
- size = sym->const_int_val;
- sym->is_used = 1; // MARK AS USED
- }
- else
- {
- zpanic_at(size_tok,
- "Array size must be a compile-time constant or integer literal");
- }
- free(name);
- }
- else
- {
- zpanic_at(size_tok, "Expected integer for array size");
- }
- if (lexer_next(l).type != TOK_RBRACKET)
- {
- zpanic_at(lexer_peek(l), "Expected ] after array size");
- }
-
- Type *arr = type_new(TYPE_ARRAY);
- arr->inner = inner;
- arr->array_size = size;
- return arr;
- }
+ // Otherwise it's a slice [T]
+ if (lexer_next(l).type != TOK_RBRACKET) {
+ zpanic_at(lexer_peek(l), "Expected ] in type");
+ }
- // Otherwise it's a slice [T]
- if (lexer_next(l).type != TOK_RBRACKET)
- {
- zpanic_at(lexer_peek(l), "Expected ] in type");
- }
+ // Register Slice
+ char *inner_str = type_to_string(inner);
+ register_slice(ctx, inner_str);
- // Register Slice
- char *inner_str = type_to_string(inner);
- register_slice(ctx, inner_str);
-
- Type *arr = type_new(TYPE_ARRAY);
- arr->inner = inner;
- arr->array_size = 0; // 0 means slice, not fixed-size
- return arr;
- }
-
- if (t.type == TOK_LPAREN)
- {
- lexer_next(l); // eat (
- char sig[256];
- sig[0] = 0;
-
- while (1)
- {
- Type *sub = parse_type_formal(ctx, l);
- char *s = type_to_string(sub);
- strcat(sig, s);
- free(s);
-
- if (lexer_peek(l).type == TOK_COMMA)
- {
- lexer_next(l);
- strcat(sig, "_");
- }
- else
- {
- break;
- }
- }
- if (lexer_next(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected ) in tuple");
- }
+ Type *arr = type_new(TYPE_ARRAY);
+ arr->inner = inner;
+ arr->array_size = 0; // 0 means slice, not fixed-size
+ return arr;
+ }
- register_tuple(ctx, sig);
+ if (t.type == TOK_LPAREN) {
+ lexer_next(l); // eat (
+ char sig[256];
+ sig[0] = 0;
- char *tuple_name = xmalloc(strlen(sig) + 7);
- sprintf(tuple_name, "Tuple_%s", sig);
+ while (1) {
+ Type *sub = parse_type_formal(ctx, l);
+ char *s = type_to_string(sub);
+ strcat(sig, s);
+ free(s);
- Type *ty = type_new(TYPE_STRUCT);
- ty->name = tuple_name;
- return ty;
+ if (lexer_peek(l).type == TOK_COMMA) {
+ lexer_next(l);
+ strcat(sig, "_");
+ } else {
+ break;
+ }
+ }
+ if (lexer_next(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected ) in tuple");
}
- return type_new(TYPE_UNKNOWN);
-}
+ register_tuple(ctx, sig);
-Type *parse_type_formal(ParserContext *ctx, Lexer *l)
-{
- int is_restrict = 0;
- if (lexer_peek(l).type == TOK_IDENT && lexer_peek(l).len == 8 &&
- strncmp(lexer_peek(l).start, "restrict", 8) == 0)
- {
- lexer_next(l); // eat restrict
- is_restrict = 1;
- }
-
- // Example: fn(int, int) -> int
- if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "fn", 2) == 0 &&
- lexer_peek(l).len == 2)
- {
-
- lexer_next(l); // eat 'fn'
- Type *fn_type = type_new(TYPE_FUNCTION);
- fn_type->is_varargs = 0;
-
- expect(l, TOK_LPAREN, "Expected '(' for function type");
-
- // Parse Arguments
- fn_type->arg_count = 0;
- fn_type->args = NULL;
-
- while (lexer_peek(l).type != TOK_RPAREN)
- {
- Type *arg = parse_type_formal(ctx, l);
- fn_type->arg_count++;
- fn_type->args = xrealloc(fn_type->args, sizeof(Type *) * fn_type->arg_count);
- fn_type->args[fn_type->arg_count - 1] = arg;
-
- if (lexer_peek(l).type == TOK_COMMA)
- {
- lexer_next(l);
- }
- else
- {
- break;
- }
- }
- expect(l, TOK_RPAREN, "Expected ')' after function args");
+ char *tuple_name = xmalloc(strlen(sig) + 7);
+ sprintf(tuple_name, "Tuple_%s", sig);
- // Parse Return Type (-> Type)
- if (lexer_peek(l).type == TOK_ARROW)
- {
- lexer_next(l); // eat ->
- fn_type->inner = parse_type_formal(ctx, l); // Return type stored in inner
- }
- else
- {
- fn_type->inner = type_new(TYPE_VOID);
- }
+ Type *ty = type_new(TYPE_STRUCT);
+ ty->name = tuple_name;
+ return ty;
+ }
- return fn_type;
- }
+ return type_new(TYPE_UNKNOWN);
+}
- // Handles: int, Struct, Generic<T>, [Slice], (Tuple)
- Type *t = parse_type_base(ctx, l);
+Type *parse_type_formal(ParserContext *ctx, Lexer *l) {
+ int is_restrict = 0;
+ if (lexer_peek(l).type == TOK_IDENT && lexer_peek(l).len == 8 &&
+ strncmp(lexer_peek(l).start, "restrict", 8) == 0) {
+ lexer_next(l); // eat restrict
+ is_restrict = 1;
+ }
- // Handles: T*, T**, etc.
- while (lexer_peek(l).type == TOK_OP && *lexer_peek(l).start == '*')
- {
- lexer_next(l); // consume '*'
- t = type_new_ptr(t);
- }
+ // Example: fn(int, int) -> int
+ if (lexer_peek(l).type == TOK_IDENT &&
+ strncmp(lexer_peek(l).start, "fn", 2) == 0 && lexer_peek(l).len == 2) {
- // 4. Handle Array Suffixes (e.g. int[10])
- while (lexer_peek(l).type == TOK_LBRACKET)
- {
- lexer_next(l); // consume '['
+ lexer_next(l); // eat 'fn'
+ Type *fn_type = type_new(TYPE_FUNCTION);
+ fn_type->is_varargs = 0;
- int size = 0;
- if (lexer_peek(l).type == TOK_INT)
- {
- Token t = lexer_peek(l);
- char buffer[64];
- int len = t.len < 63 ? t.len : 63;
- strncpy(buffer, t.start, len);
- buffer[len] = 0;
- size = atoi(buffer);
- lexer_next(l);
- }
+ expect(l, TOK_LPAREN, "Expected '(' for function type");
- expect(l, TOK_RBRACKET, "Expected ']' in array type");
+ // Parse Arguments
+ fn_type->arg_count = 0;
+ fn_type->args = NULL;
- Type *arr = type_new(TYPE_ARRAY);
- arr->inner = t;
- arr->array_size = size;
- t = arr;
+ while (lexer_peek(l).type != TOK_RPAREN) {
+ Type *arg = parse_type_formal(ctx, l);
+ fn_type->arg_count++;
+ fn_type->args =
+ xrealloc(fn_type->args, sizeof(Type *) * fn_type->arg_count);
+ fn_type->args[fn_type->arg_count - 1] = arg;
+
+ if (lexer_peek(l).type == TOK_COMMA) {
+ lexer_next(l);
+ } else {
+ break;
+ }
+ }
+ expect(l, TOK_RPAREN, "Expected ')' after function args");
+
+ // Parse Return Type (-> Type)
+ if (lexer_peek(l).type == TOK_ARROW) {
+ lexer_next(l); // eat ->
+ fn_type->inner = parse_type_formal(ctx, l); // Return type stored in inner
+ } else {
+ fn_type->inner = type_new(TYPE_VOID);
}
- if (is_restrict)
- {
- t->is_restrict = 1;
+ return fn_type;
+ }
+
+ // Handles: int, Struct, Generic<T>, [Slice], (Tuple)
+ Type *t = parse_type_base(ctx, l);
+
+ // Handles: T*, T**, etc.
+ while (lexer_peek(l).type == TOK_OP && *lexer_peek(l).start == '*') {
+ lexer_next(l); // consume '*'
+ t = type_new_ptr(t);
+ }
+
+ // 4. Handle Array Suffixes (e.g. int[10])
+ while (lexer_peek(l).type == TOK_LBRACKET) {
+ lexer_next(l); // consume '['
+
+ int size = 0;
+ if (lexer_peek(l).type == TOK_INT) {
+ Token t = lexer_peek(l);
+ char buffer[64];
+ int len = t.len < 63 ? t.len : 63;
+ strncpy(buffer, t.start, len);
+ buffer[len] = 0;
+ size = atoi(buffer);
+ lexer_next(l);
}
- return t;
+
+ expect(l, TOK_RBRACKET, "Expected ']' in array type");
+
+ Type *arr = type_new(TYPE_ARRAY);
+ arr->inner = t;
+ arr->array_size = size;
+ t = arr;
+ }
+
+ if (is_restrict) {
+ t->is_restrict = 1;
+ }
+ return t;
}
-char *parse_type(ParserContext *ctx, Lexer *l)
-{
- Type *t = parse_type_formal(ctx, l);
+char *parse_type(ParserContext *ctx, Lexer *l) {
+ Type *t = parse_type_formal(ctx, l);
- return type_to_string(t);
+ return type_to_string(t);
}
-char *parse_array_literal(ParserContext *ctx, Lexer *l, const char *st)
-{
- (void)ctx; // suppress unused parameter warning
- lexer_next(l);
- size_t cap = 128;
- char *c = xmalloc(cap);
- c[0] = 0;
- int n = 0;
-
- while (1)
- {
- Token t = lexer_peek(l);
- if (t.type == TOK_RBRACKET)
- {
- lexer_next(l);
- break;
- }
- if (t.type == TOK_COMMA)
- {
- lexer_next(l);
- continue;
- }
+char *parse_array_literal(ParserContext *ctx, Lexer *l, const char *st) {
+ (void)ctx; // suppress unused parameter warning
+ lexer_next(l);
+ size_t cap = 128;
+ char *c = xmalloc(cap);
+ c[0] = 0;
+ int n = 0;
- const char *s = l->src + l->pos;
- int d = 0;
- while (1)
- {
- Token it = lexer_peek(l);
- if (it.type == TOK_EOF)
- {
- break;
- }
- if (d == 0 && (it.type == TOK_COMMA || it.type == TOK_RBRACKET))
- {
- break;
- }
- if (it.type == TOK_LBRACKET || it.type == TOK_LPAREN)
- {
- d++;
- }
- if (it.type == TOK_RBRACKET || it.type == TOK_RPAREN)
- {
- d--;
- }
- lexer_next(l);
- }
+ while (1) {
+ Token t = lexer_peek(l);
+ if (t.type == TOK_RBRACKET) {
+ lexer_next(l);
+ break;
+ }
+ if (t.type == TOK_COMMA) {
+ lexer_next(l);
+ continue;
+ }
- int len = (l->src + l->pos) - s;
- if (strlen(c) + len + 5 > cap)
- {
- cap *= 2;
- c = xrealloc(c, cap);
- }
- if (n > 0)
- {
- strcat(c, ", ");
- }
- strncat(c, s, len);
- n++;
+ const char *s = l->src + l->pos;
+ int d = 0;
+ while (1) {
+ Token it = lexer_peek(l);
+ if (it.type == TOK_EOF) {
+ break;
+ }
+ if (d == 0 && (it.type == TOK_COMMA || it.type == TOK_RBRACKET)) {
+ break;
+ }
+ if (it.type == TOK_LBRACKET || it.type == TOK_LPAREN) {
+ d++;
+ }
+ if (it.type == TOK_RBRACKET || it.type == TOK_RPAREN) {
+ d--;
+ }
+ lexer_next(l);
}
- char rt[64];
- if (strncmp(st, "Slice_", 6) == 0)
- {
- strcpy(rt, st + 6);
+ int len = (l->src + l->pos) - s;
+ if (strlen(c) + len + 5 > cap) {
+ cap *= 2;
+ c = xrealloc(c, cap);
}
- else
- {
- strcpy(rt, "int");
+ if (n > 0) {
+ strcat(c, ", ");
}
-
- char *o = xmalloc(strlen(c) + 128);
- sprintf(o, "(%s){.data=(%s[]){%s},.len=%d,.cap=%d}", st, rt, c, n, n);
- free(c);
- return o;
+ strncat(c, s, len);
+ n++;
+ }
+
+ char rt[64];
+ if (strncmp(st, "Slice_", 6) == 0) {
+ strcpy(rt, st + 6);
+ } else {
+ strcpy(rt, "int");
+ }
+
+ char *o = xmalloc(strlen(c) + 128);
+ sprintf(o, "(%s){.data=(%s[]){%s},.len=%d,.cap=%d}", st, rt, c, n, n);
+ free(c);
+ return o;
}
-char *parse_tuple_literal(ParserContext *ctx, Lexer *l, const char *tn)
-{
- (void)ctx; // suppress unused parameter warning
- lexer_next(l);
- size_t cap = 128;
- char *c = xmalloc(cap);
- c[0] = 0;
-
- while (1)
- {
- Token t = lexer_peek(l);
- if (t.type == TOK_RPAREN)
- {
- lexer_next(l);
- break;
- }
- if (t.type == TOK_COMMA)
- {
- lexer_next(l);
- continue;
- }
+char *parse_tuple_literal(ParserContext *ctx, Lexer *l, const char *tn) {
+ (void)ctx; // suppress unused parameter warning
+ lexer_next(l);
+ size_t cap = 128;
+ char *c = xmalloc(cap);
+ c[0] = 0;
+
+ while (1) {
+ Token t = lexer_peek(l);
+ if (t.type == TOK_RPAREN) {
+ lexer_next(l);
+ break;
+ }
+ if (t.type == TOK_COMMA) {
+ lexer_next(l);
+ continue;
+ }
- const char *s = l->src + l->pos;
- int d = 0;
- while (1)
- {
- Token it = lexer_peek(l);
- if (it.type == TOK_EOF)
- {
- break;
- }
- if (d == 0 && (it.type == TOK_COMMA || it.type == TOK_RPAREN))
- {
- break;
- }
- if (it.type == TOK_LPAREN)
- {
- d++;
- }
- if (it.type == TOK_RPAREN)
- {
- d--;
- }
- lexer_next(l);
- }
+ const char *s = l->src + l->pos;
+ int d = 0;
+ while (1) {
+ Token it = lexer_peek(l);
+ if (it.type == TOK_EOF) {
+ break;
+ }
+ if (d == 0 && (it.type == TOK_COMMA || it.type == TOK_RPAREN)) {
+ break;
+ }
+ if (it.type == TOK_LPAREN) {
+ d++;
+ }
+ if (it.type == TOK_RPAREN) {
+ d--;
+ }
+ lexer_next(l);
+ }
- int len = (l->src + l->pos) - s;
- if (strlen(c) + len + 5 > cap)
- {
- cap *= 2;
- c = xrealloc(c, cap);
- }
- if (strlen(c) > 0)
- {
- strcat(c, ", ");
- }
- strncat(c, s, len);
+ int len = (l->src + l->pos) - s;
+ if (strlen(c) + len + 5 > cap) {
+ cap *= 2;
+ c = xrealloc(c, cap);
}
+ if (strlen(c) > 0) {
+ strcat(c, ", ");
+ }
+ strncat(c, s, len);
+ }
- char *o = xmalloc(strlen(c) + 128);
- sprintf(o, "(%s){%s}", tn, c);
- free(c);
- return o;
+ char *o = xmalloc(strlen(c) + 128);
+ sprintf(o, "(%s){%s}", tn, c);
+ free(c);
+ return o;
}
-char *parse_embed(ParserContext *ctx, Lexer *l)
-{
- lexer_next(l);
- Token t = lexer_next(l);
- if (t.type != TOK_STRING)
- {
- zpanic_at(t, "String required");
- }
- char fn[256];
- strncpy(fn, t.start + 1, t.len - 2);
- fn[t.len - 2] = 0;
-
- FILE *f = fopen(fn, "rb");
- if (!f)
- {
- zpanic_at(t, "404: %s", fn);
- }
- fseek(f, 0, SEEK_END);
- long len = ftell(f);
- rewind(f);
- unsigned char *b = xmalloc(len);
- fread(b, 1, len, f);
- fclose(f);
-
- register_slice(ctx, "char");
- size_t oc = len * 6 + 128;
- char *o = xmalloc(oc);
- sprintf(o, "(Slice_char){.data=(char[]){");
- char *p = o + strlen(o);
- for (int i = 0; i < len; i++)
- {
- p += sprintf(p, "0x%02X,", b[i]);
- }
- sprintf(p, "},.len=%ld,.cap=%ld}", len, len);
- free(b);
- return o;
+char *parse_embed(ParserContext *ctx, Lexer *l) {
+ lexer_next(l);
+ Token t = lexer_next(l);
+ if (t.type != TOK_STRING) {
+ zpanic_at(t, "String required");
+ }
+ char fn[256];
+ strncpy(fn, t.start + 1, t.len - 2);
+ fn[t.len - 2] = 0;
+
+ FILE *f = fopen(fn, "rb");
+ if (!f) {
+ zpanic_at(t, "404: %s", fn);
+ }
+ fseek(f, 0, SEEK_END);
+ long len = ftell(f);
+ rewind(f);
+ unsigned char *b = xmalloc(len);
+ fread(b, 1, len, f);
+ fclose(f);
+
+ register_slice(ctx, "char");
+ size_t oc = len * 6 + 128;
+ char *o = xmalloc(oc);
+ sprintf(o, "(Slice_char){.data=(char[]){");
+ char *p = o + strlen(o);
+ for (int i = 0; i < len; i++) {
+ p += sprintf(p, "0x%02X,", b[i]);
+ }
+ sprintf(p, "},.len=%ld,.cap=%ld}", len, len);
+ free(b);
+ return o;
}
diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c
index 2e2fb5b..d616056 100644
--- a/src/parser/parser_utils.c
+++ b/src/parser/parser_utils.c
@@ -10,2631 +10,2237 @@
void instantiate_methods(ParserContext *ctx, GenericImplTemplate *it,
const char *mangled_struct_name, const char *arg);
-Token expect(Lexer *l, TokenType type, const char *msg)
-{
- Token t = lexer_next(l);
- if (t.type != type)
- {
- zpanic_at(t, "Expected %s, but got '%.*s'", msg, t.len, t.start);
- return (Token){type, t.start, 0, t.line, t.col};
- }
- return t;
+Token expect(Lexer *l, TokenType type, const char *msg) {
+ Token t = lexer_next(l);
+ if (t.type != type) {
+ zpanic_at(t, "Expected %s, but got '%.*s'", msg, t.len, t.start);
+ return (Token){type, t.start, 0, t.line, t.col};
+ }
+ return t;
}
-int is_token(Token t, const char *s)
-{
- int len = strlen(s);
- return (t.len == len && strncmp(t.start, s, len) == 0);
+int is_token(Token t, const char *s) {
+ int len = strlen(s);
+ return (t.len == len && strncmp(t.start, s, len) == 0);
}
-char *token_strdup(Token t)
-{
- char *s = xmalloc(t.len + 1);
- strncpy(s, t.start, t.len);
- s[t.len] = 0;
- return s;
+char *token_strdup(Token t) {
+ char *s = xmalloc(t.len + 1);
+ strncpy(s, t.start, t.len);
+ s[t.len] = 0;
+ return s;
}
-void skip_comments(Lexer *l)
-{
- while (lexer_peek(l).type == TOK_COMMENT)
- {
- lexer_next(l);
- }
+void skip_comments(Lexer *l) {
+ while (lexer_peek(l).type == TOK_COMMENT) {
+ lexer_next(l);
+ }
}
// C reserved words that conflict with C when used as identifiers.
// TODO: We gotta work on these.
static const char *C_RESERVED_WORDS[] = {
// C types that could be used as names
- "double", "float", "signed", "unsigned", "short", "long", "auto", "register",
+ "double", "float", "signed", "unsigned", "short", "long", "auto",
+ "register",
// C keywords
- "switch", "case", "default", "do", "goto", "typedef", "static", "extern", "volatile", "inline",
- "restrict", "sizeof", "const",
+ "switch", "case", "default", "do", "goto", "typedef", "static", "extern",
+ "volatile", "inline", "restrict", "sizeof", "const",
// C11+ keywords
- "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary", "_Noreturn",
- "_Static_assert", "_Thread_local", NULL};
+ "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic",
+ "_Imaginary", "_Noreturn", "_Static_assert", "_Thread_local", NULL};
-int is_c_reserved_word(const char *name)
-{
- for (int i = 0; C_RESERVED_WORDS[i] != NULL; i++)
- {
- if (strcmp(name, C_RESERVED_WORDS[i]) == 0)
- {
- return 1;
- }
+int is_c_reserved_word(const char *name) {
+ for (int i = 0; C_RESERVED_WORDS[i] != NULL; i++) {
+ if (strcmp(name, C_RESERVED_WORDS[i]) == 0) {
+ return 1;
}
- return 0;
+ }
+ return 0;
}
-void warn_c_reserved_word(Token t, const char *name)
-{
- zwarn_at(t, "Identifier '%s' conflicts with C reserved word", name);
- fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET
- "This will cause compilation errors in the generated C code\n");
+void warn_c_reserved_word(Token t, const char *name) {
+ zwarn_at(t, "Identifier '%s' conflicts with C reserved word", name);
+ fprintf(stderr, COLOR_CYAN
+ " = note: " COLOR_RESET
+ "This will cause compilation errors in the generated C code\n");
}
-char *consume_until_semicolon(Lexer *l)
-{
- const char *s = l->src + l->pos;
- int d = 0;
- while (1)
- {
- Token t = lexer_peek(l);
- if (t.type == TOK_EOF)
- {
- break;
- }
- if (t.type == TOK_LBRACE || t.type == TOK_LPAREN || t.type == TOK_LBRACKET)
- {
- d++;
- }
- if (t.type == TOK_RBRACE || t.type == TOK_RPAREN || t.type == TOK_RBRACKET)
- {
- d--;
- }
-
- if (d == 0 && t.type == TOK_SEMICOLON)
- {
- int len = t.start - s;
- char *r = xmalloc(len + 1);
- strncpy(r, s, len);
- r[len] = 0;
- lexer_next(l);
- return r;
- }
- lexer_next(l);
+char *consume_until_semicolon(Lexer *l) {
+ const char *s = l->src + l->pos;
+ int d = 0;
+ while (1) {
+ Token t = lexer_peek(l);
+ if (t.type == TOK_EOF) {
+ break;
+ }
+ if (t.type == TOK_LBRACE || t.type == TOK_LPAREN ||
+ t.type == TOK_LBRACKET) {
+ d++;
+ }
+ if (t.type == TOK_RBRACE || t.type == TOK_RPAREN ||
+ t.type == TOK_RBRACKET) {
+ d--;
+ }
+
+ if (d == 0 && t.type == TOK_SEMICOLON) {
+ int len = t.start - s;
+ char *r = xmalloc(len + 1);
+ strncpy(r, s, len);
+ r[len] = 0;
+ lexer_next(l);
+ return r;
+ }
+ lexer_next(l);
+ }
+ return xstrdup("");
+}
+
+void enter_scope(ParserContext *ctx) {
+ Scope *s = xmalloc(sizeof(Scope));
+ s->symbols = 0;
+ s->parent = ctx->current_scope;
+ ctx->current_scope = s;
+}
+
+void exit_scope(ParserContext *ctx) {
+ if (!ctx->current_scope) {
+ return;
+ }
+
+ // Check for unused variables
+ Symbol *sym = ctx->current_scope->symbols;
+ while (sym) {
+ if (!sym->is_used && strcmp(sym->name, "self") != 0 &&
+ sym->name[0] != '_') {
+ // Could emit warning here
+ }
+ sym = sym->next;
+ }
+
+ ctx->current_scope = ctx->current_scope->parent;
+}
+
+void add_symbol(ParserContext *ctx, const char *n, const char *t,
+ Type *type_info) {
+ add_symbol_with_token(ctx, n, t, type_info, (Token){0});
+}
+
+void add_symbol_with_token(ParserContext *ctx, const char *n, const char *t,
+ Type *type_info, Token tok) {
+ if (!ctx->current_scope) {
+ enter_scope(ctx);
+ }
+
+ if (n[0] != '_' && ctx->current_scope->parent && strcmp(n, "it") != 0 &&
+ strcmp(n, "self") != 0) {
+ Scope *p = ctx->current_scope->parent;
+ while (p) {
+ Symbol *sh = p->symbols;
+ while (sh) {
+ if (strcmp(sh->name, n) == 0) {
+ warn_shadowing(tok, n);
+ break;
+ }
+ sh = sh->next;
+ }
+ if (sh) {
+ break; // found it
+ }
+ p = p->parent;
+ }
+ }
+ Symbol *s = xmalloc(sizeof(Symbol));
+ s->name = xstrdup(n);
+ s->type_name = t ? xstrdup(t) : NULL;
+ s->type_info = type_info;
+ s->is_mutable = 1;
+ s->is_used = 0;
+ s->decl_token = tok;
+ s->is_const_value = 0;
+ s->next = ctx->current_scope->symbols;
+ ctx->current_scope->symbols = s;
+
+ // LSP: Also add to flat list (for persistent access after scope exit)
+ Symbol *lsp_copy = xmalloc(sizeof(Symbol));
+ *lsp_copy = *s;
+ lsp_copy->next = ctx->all_symbols;
+ ctx->all_symbols = lsp_copy;
+}
+
+Type *find_symbol_type_info(ParserContext *ctx, const char *n) {
+ if (!ctx->current_scope) {
+ return NULL;
+ }
+ Scope *s = ctx->current_scope;
+ while (s) {
+ Symbol *sym = s->symbols;
+ while (sym) {
+ if (strcmp(sym->name, n) == 0) {
+ return sym->type_info;
+ }
+ sym = sym->next;
+ }
+ s = s->parent;
+ }
+ return NULL;
+}
+
+char *find_symbol_type(ParserContext *ctx, const char *n) {
+ if (!ctx->current_scope) {
+ return NULL;
+ }
+ Scope *s = ctx->current_scope;
+ while (s) {
+ Symbol *sym = s->symbols;
+ while (sym) {
+ if (strcmp(sym->name, n) == 0) {
+ return sym->type_name;
+ }
+ sym = sym->next;
+ }
+ s = s->parent;
+ }
+ return NULL;
+}
+
+Symbol *find_symbol_entry(ParserContext *ctx, const char *n) {
+ if (!ctx->current_scope) {
+ return NULL;
+ }
+ Scope *s = ctx->current_scope;
+ while (s) {
+ Symbol *sym = s->symbols;
+ while (sym) {
+ if (strcmp(sym->name, n) == 0) {
+ return sym;
+ }
+ sym = sym->next;
}
- return xstrdup("");
+ s = s->parent;
+ }
+ return NULL;
}
-void enter_scope(ParserContext *ctx)
-{
- Scope *s = xmalloc(sizeof(Scope));
- s->symbols = 0;
- s->parent = ctx->current_scope;
- ctx->current_scope = s;
+// LSP: Search flat symbol list (works after scopes are destroyed).
+Symbol *find_symbol_in_all(ParserContext *ctx, const char *n) {
+ Symbol *sym = ctx->all_symbols;
+ while (sym) {
+ if (strcmp(sym->name, n) == 0) {
+ return sym;
+ }
+ sym = sym->next;
+ }
+ return NULL;
+}
+
+void init_builtins() {
+ static int init = 0;
+ if (init) {
+ return;
+ }
+ init = 1;
+}
+
+void register_func(ParserContext *ctx, const char *name, int count,
+ char **defaults, Type **arg_types, Type *ret_type,
+ int is_varargs, int is_async, Token decl_token) {
+ FuncSig *f = xmalloc(sizeof(FuncSig));
+ f->name = xstrdup(name);
+ f->decl_token = decl_token;
+ f->total_args = count;
+ f->defaults = defaults;
+ f->arg_types = arg_types;
+ f->ret_type = ret_type;
+ f->is_varargs = is_varargs;
+ f->is_async = is_async;
+ f->must_use = 0; // Default: can discard result
+ f->next = ctx->func_registry;
+ ctx->func_registry = f;
+}
+
+void register_func_template(ParserContext *ctx, const char *name,
+ const char *param, ASTNode *node) {
+ GenericFuncTemplate *t = xmalloc(sizeof(GenericFuncTemplate));
+ t->name = xstrdup(name);
+ t->generic_param = xstrdup(param);
+ t->func_node = node;
+ t->next = ctx->func_templates;
+ ctx->func_templates = t;
+}
+
+void register_deprecated_func(ParserContext *ctx, const char *name,
+ const char *reason) {
+ DeprecatedFunc *d = xmalloc(sizeof(DeprecatedFunc));
+ d->name = xstrdup(name);
+ d->reason = reason ? xstrdup(reason) : NULL;
+ d->next = ctx->deprecated_funcs;
+ ctx->deprecated_funcs = d;
+}
+
+DeprecatedFunc *find_deprecated_func(ParserContext *ctx, const char *name) {
+ DeprecatedFunc *d = ctx->deprecated_funcs;
+ while (d) {
+ if (strcmp(d->name, name) == 0) {
+ return d;
+ }
+ d = d->next;
+ }
+ return NULL;
+}
+
+GenericFuncTemplate *find_func_template(ParserContext *ctx, const char *name) {
+ GenericFuncTemplate *t = ctx->func_templates;
+ while (t) {
+ if (strcmp(t->name, name) == 0) {
+ return t;
+ }
+ t = t->next;
+ }
+ return NULL;
+}
+
+void register_generic(ParserContext *ctx, char *name) {
+ for (int i = 0; i < ctx->known_generics_count; i++) {
+ if (strcmp(ctx->known_generics[i], name) == 0) {
+ return;
+ }
+ }
+ ctx->known_generics[ctx->known_generics_count++] = strdup(name);
+}
+
+int is_known_generic(ParserContext *ctx, char *name) {
+ for (int i = 0; i < ctx->known_generics_count; i++) {
+ if (strcmp(ctx->known_generics[i], name) == 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void register_impl_template(ParserContext *ctx, const char *sname,
+ const char *param, ASTNode *node) {
+ GenericImplTemplate *t = xmalloc(sizeof(GenericImplTemplate));
+ t->struct_name = xstrdup(sname);
+ t->generic_param = xstrdup(param);
+ t->impl_node = node;
+ t->next = ctx->impl_templates;
+ ctx->impl_templates = t;
+
+ // Late binding: Check if any existing instantiations match this new impl
+ // template
+ Instantiation *inst = ctx->instantiations;
+ while (inst) {
+ if (inst->template_name && strcmp(inst->template_name, sname) == 0) {
+ instantiate_methods(ctx, t, inst->name, inst->concrete_arg);
+ }
+ inst = inst->next;
+ }
+}
+
+void add_to_struct_list(ParserContext *ctx, ASTNode *node) {
+ StructRef *r = xmalloc(sizeof(StructRef));
+ r->node = node;
+ r->next = ctx->parsed_structs_list;
+ ctx->parsed_structs_list = r;
+}
+
+void add_to_enum_list(ParserContext *ctx, ASTNode *node) {
+ StructRef *r = xmalloc(sizeof(StructRef));
+ r->node = node;
+ r->next = ctx->parsed_enums_list;
+ ctx->parsed_enums_list = r;
+}
+
+void add_to_func_list(ParserContext *ctx, ASTNode *node) {
+ StructRef *r = xmalloc(sizeof(StructRef));
+ r->node = node;
+ r->next = ctx->parsed_funcs_list;
+ ctx->parsed_funcs_list = r;
+}
+
+void add_to_impl_list(ParserContext *ctx, ASTNode *node) {
+ StructRef *r = xmalloc(sizeof(StructRef));
+ r->node = node;
+ r->next = ctx->parsed_impls_list;
+ ctx->parsed_impls_list = r;
}
-void exit_scope(ParserContext *ctx)
-{
- if (!ctx->current_scope)
- {
- return;
- }
+void add_to_global_list(ParserContext *ctx, ASTNode *node) {
+ StructRef *r = xmalloc(sizeof(StructRef));
+ r->node = node;
+ r->next = ctx->parsed_globals_list;
+ ctx->parsed_globals_list = r;
+}
- // Check for unused variables
- Symbol *sym = ctx->current_scope->symbols;
- while (sym)
- {
- if (!sym->is_used && strcmp(sym->name, "self") != 0 && sym->name[0] != '_')
- {
- // Could emit warning here
- }
- sym = sym->next;
- }
+void register_builtins(ParserContext *ctx) {
+ Type *t = type_new(TYPE_BOOL);
+ t->is_const = 1;
+ add_symbol(ctx, "true", "bool", t);
+
+ t = type_new(TYPE_BOOL);
+ t->is_const = 1;
+ add_symbol(ctx, "false", "bool", t);
+
+ // Register 'free'
+ Type *void_t = type_new(TYPE_VOID);
+ add_symbol(ctx, "free", "void", void_t);
- ctx->current_scope = ctx->current_scope->parent;
+ // Register common libc functions to avoid warnings
+ add_symbol(ctx, "strdup", "string", type_new(TYPE_STRING));
+ add_symbol(ctx, "malloc", "void*", type_new_ptr(void_t));
+ add_symbol(ctx, "realloc", "void*", type_new_ptr(void_t));
+ add_symbol(ctx, "calloc", "void*", type_new_ptr(void_t));
+ add_symbol(ctx, "puts", "int", type_new(TYPE_INT));
+ add_symbol(ctx, "printf", "int", type_new(TYPE_INT));
+ add_symbol(ctx, "strcmp", "int", type_new(TYPE_INT));
+ add_symbol(ctx, "strlen", "int", type_new(TYPE_INT));
+ add_symbol(ctx, "strcpy", "string", type_new(TYPE_STRING));
+ add_symbol(ctx, "strcat", "string", type_new(TYPE_STRING));
+ add_symbol(ctx, "exit", "void", void_t);
+
+ // File I/O
+ add_symbol(ctx, "fopen", "void*", type_new_ptr(void_t));
+ add_symbol(ctx, "fclose", "int", type_new(TYPE_INT));
+ add_symbol(ctx, "fread", "usize", type_new(TYPE_USIZE));
+ add_symbol(ctx, "fwrite", "usize", type_new(TYPE_USIZE));
+ add_symbol(ctx, "fseek", "int", type_new(TYPE_INT));
+ add_symbol(ctx, "ftell", "long", type_new(TYPE_I64));
+ add_symbol(ctx, "rewind", "void", void_t);
+ add_symbol(ctx, "fprintf", "int", type_new(TYPE_INT));
+ add_symbol(ctx, "sprintf", "int", type_new(TYPE_INT));
+ add_symbol(ctx, "feof", "int", type_new(TYPE_INT));
+ add_symbol(ctx, "ferror", "int", type_new(TYPE_INT));
+ add_symbol(ctx, "usleep", "int", type_new(TYPE_INT));
}
-void add_symbol(ParserContext *ctx, const char *n, const char *t, Type *type_info)
-{
- add_symbol_with_token(ctx, n, t, type_info, (Token){0});
+void add_instantiated_func(ParserContext *ctx, ASTNode *fn) {
+ fn->next = ctx->instantiated_funcs;
+ ctx->instantiated_funcs = fn;
+}
+
+void register_enum_variant(ParserContext *ctx, const char *ename,
+ const char *vname, int tag) {
+ EnumVariantReg *r = xmalloc(sizeof(EnumVariantReg));
+ r->enum_name = xstrdup(ename);
+ r->variant_name = xstrdup(vname);
+ r->tag_id = tag;
+ r->next = ctx->enum_variants;
+ ctx->enum_variants = r;
}
-void add_symbol_with_token(ParserContext *ctx, const char *n, const char *t, Type *type_info,
- Token tok)
-{
- if (!ctx->current_scope)
- {
- enter_scope(ctx);
+EnumVariantReg *find_enum_variant(ParserContext *ctx, const char *vname) {
+ EnumVariantReg *r = ctx->enum_variants;
+ while (r) {
+ if (strcmp(r->variant_name, vname) == 0) {
+ return r;
}
+ r = r->next;
+ }
+ return NULL;
+}
- if (n[0] != '_' && ctx->current_scope->parent && strcmp(n, "it") != 0 && strcmp(n, "self") != 0)
- {
- Scope *p = ctx->current_scope->parent;
- while (p)
- {
- Symbol *sh = p->symbols;
- while (sh)
- {
- if (strcmp(sh->name, n) == 0)
- {
- warn_shadowing(tok, n);
- break;
- }
- sh = sh->next;
- }
- if (sh)
- {
- break; // found it
- }
- p = p->parent;
- }
- }
- Symbol *s = xmalloc(sizeof(Symbol));
- s->name = xstrdup(n);
- s->type_name = t ? xstrdup(t) : NULL;
- s->type_info = type_info;
- s->is_mutable = 1;
- s->is_used = 0;
- s->decl_token = tok;
- s->is_const_value = 0;
- s->next = ctx->current_scope->symbols;
- ctx->current_scope->symbols = s;
-
- // LSP: Also add to flat list (for persistent access after scope exit)
- Symbol *lsp_copy = xmalloc(sizeof(Symbol));
- *lsp_copy = *s;
- lsp_copy->next = ctx->all_symbols;
- ctx->all_symbols = lsp_copy;
-}
-
-Type *find_symbol_type_info(ParserContext *ctx, const char *n)
-{
- if (!ctx->current_scope)
- {
- return NULL;
- }
- Scope *s = ctx->current_scope;
- while (s)
- {
- Symbol *sym = s->symbols;
- while (sym)
- {
- if (strcmp(sym->name, n) == 0)
- {
- return sym->type_info;
- }
- sym = sym->next;
- }
- s = s->parent;
- }
- return NULL;
+void register_lambda(ParserContext *ctx, ASTNode *node) {
+ LambdaRef *ref = xmalloc(sizeof(LambdaRef));
+ ref->node = node;
+ ref->next = ctx->global_lambdas;
+ ctx->global_lambdas = ref;
+}
+
+void register_var_mutability(ParserContext *ctx, const char *name,
+ int is_mutable) {
+ VarMutability *v = xmalloc(sizeof(VarMutability));
+ v->name = xstrdup(name);
+ v->is_mutable = is_mutable;
+ v->next = ctx->var_mutability_table;
+ ctx->var_mutability_table = v;
}
-char *find_symbol_type(ParserContext *ctx, const char *n)
-{
- if (!ctx->current_scope)
- {
- return NULL;
- }
- Scope *s = ctx->current_scope;
- while (s)
- {
- Symbol *sym = s->symbols;
- while (sym)
- {
- if (strcmp(sym->name, n) == 0)
- {
- return sym->type_name;
- }
- sym = sym->next;
- }
- s = s->parent;
- }
- return NULL;
+int is_var_mutable(ParserContext *ctx, const char *name) {
+ for (VarMutability *v = ctx->var_mutability_table; v; v = v->next) {
+ if (strcmp(v->name, name) == 0) {
+ return v->is_mutable;
+ }
+ }
+ return 1;
}
-Symbol *find_symbol_entry(ParserContext *ctx, const char *n)
-{
- if (!ctx->current_scope)
- {
- return NULL;
+void register_extern_symbol(ParserContext *ctx, const char *name) {
+ // Check for duplicates
+ for (int i = 0; i < ctx->extern_symbol_count; i++) {
+ if (strcmp(ctx->extern_symbols[i], name) == 0) {
+ return;
}
- Scope *s = ctx->current_scope;
- while (s)
- {
- Symbol *sym = s->symbols;
- while (sym)
- {
- if (strcmp(sym->name, n) == 0)
- {
- return sym;
- }
- sym = sym->next;
- }
- s = s->parent;
- }
- return NULL;
+ }
+
+ // Grow array if needed
+ if (ctx->extern_symbol_count == 0) {
+ ctx->extern_symbols = xmalloc(sizeof(char *) * 64);
+ } else if (ctx->extern_symbol_count % 64 == 0) {
+ ctx->extern_symbols = xrealloc(
+ ctx->extern_symbols, sizeof(char *) * (ctx->extern_symbol_count + 64));
+ }
+
+ ctx->extern_symbols[ctx->extern_symbol_count++] = xstrdup(name);
+}
+
+int is_extern_symbol(ParserContext *ctx, const char *name) {
+ for (int i = 0; i < ctx->extern_symbol_count; i++) {
+ if (strcmp(ctx->extern_symbols[i], name) == 0) {
+ return 1;
+ }
+ }
+ return 0;
}
-// LSP: Search flat symbol list (works after scopes are destroyed).
-Symbol *find_symbol_in_all(ParserContext *ctx, const char *n)
-{
- Symbol *sym = ctx->all_symbols;
- while (sym)
- {
- if (strcmp(sym->name, n) == 0)
- {
- return sym;
- }
- sym = sym->next;
+// Unified check: should we suppress "undefined variable" warning for this name?
+int should_suppress_undef_warning(ParserContext *ctx, const char *name) {
+ if (strcmp(name, "struct") == 0 || strcmp(name, "tv") == 0) {
+ return 1;
+ }
+
+ if (is_extern_symbol(ctx, name)) {
+ return 1;
+ }
+
+ int is_all_caps = 1;
+ for (const char *p = name; *p; p++) {
+ if (islower((unsigned char)*p)) {
+ is_all_caps = 0;
+ break;
}
- return NULL;
+ }
+ if (is_all_caps && name[0] != '\0') {
+ return 1;
+ }
+
+ if (ctx->has_external_includes) {
+ return 1;
+ }
+
+ return 0;
+}
+
+void register_slice(ParserContext *ctx, const char *type) {
+ SliceType *c = ctx->used_slices;
+ while (c) {
+ if (strcmp(c->name, type) == 0) {
+ return;
+ }
+ c = c->next;
+ }
+ SliceType *n = xmalloc(sizeof(SliceType));
+ n->name = xstrdup(type);
+ n->next = ctx->used_slices;
+ ctx->used_slices = n;
+
+ // Register Struct Def for Reflection
+ char slice_name[256];
+ sprintf(slice_name, "Slice_%s", type);
+
+ ASTNode *len_f = ast_create(NODE_FIELD);
+ len_f->field.name = xstrdup("len");
+ len_f->field.type = xstrdup("int");
+ ASTNode *cap_f = ast_create(NODE_FIELD);
+ cap_f->field.name = xstrdup("cap");
+ cap_f->field.type = xstrdup("int");
+ ASTNode *data_f = ast_create(NODE_FIELD);
+ data_f->field.name = xstrdup("data");
+ char ptr_type[256];
+ sprintf(ptr_type, "%s*", type);
+ data_f->field.type = xstrdup(ptr_type);
+
+ data_f->next = len_f;
+ len_f->next = cap_f;
+
+ ASTNode *def = ast_create(NODE_STRUCT);
+ def->strct.name = xstrdup(slice_name);
+ def->strct.fields = data_f;
+
+ register_struct_def(ctx, slice_name, def);
}
-void init_builtins()
-{
- static int init = 0;
- if (init)
- {
- return;
- }
- init = 1;
-}
-
-void register_func(ParserContext *ctx, const char *name, int count, char **defaults,
- Type **arg_types, Type *ret_type, int is_varargs, int is_async, Token decl_token)
-{
- FuncSig *f = xmalloc(sizeof(FuncSig));
- f->name = xstrdup(name);
- f->decl_token = decl_token;
- f->total_args = count;
- f->defaults = defaults;
- f->arg_types = arg_types;
- f->ret_type = ret_type;
- f->is_varargs = is_varargs;
- f->is_async = is_async;
- f->must_use = 0; // Default: can discard result
- f->next = ctx->func_registry;
- ctx->func_registry = f;
-}
-
-void register_func_template(ParserContext *ctx, const char *name, const char *param, ASTNode *node)
-{
- GenericFuncTemplate *t = xmalloc(sizeof(GenericFuncTemplate));
- t->name = xstrdup(name);
- t->generic_param = xstrdup(param);
- t->func_node = node;
- t->next = ctx->func_templates;
- ctx->func_templates = t;
-}
-
-void register_deprecated_func(ParserContext *ctx, const char *name, const char *reason)
-{
- DeprecatedFunc *d = xmalloc(sizeof(DeprecatedFunc));
- d->name = xstrdup(name);
- d->reason = reason ? xstrdup(reason) : NULL;
- d->next = ctx->deprecated_funcs;
- ctx->deprecated_funcs = d;
-}
-
-DeprecatedFunc *find_deprecated_func(ParserContext *ctx, const char *name)
-{
- DeprecatedFunc *d = ctx->deprecated_funcs;
- while (d)
- {
- if (strcmp(d->name, name) == 0)
- {
- return d;
- }
- d = d->next;
+void register_tuple(ParserContext *ctx, const char *sig) {
+ TupleType *c = ctx->used_tuples;
+ while (c) {
+ if (strcmp(c->sig, sig) == 0) {
+ return;
+ }
+ c = c->next;
+ }
+ TupleType *n = xmalloc(sizeof(TupleType));
+ n->sig = xstrdup(sig);
+ n->next = ctx->used_tuples;
+ ctx->used_tuples = n;
+
+ char struct_name[1024];
+ sprintf(struct_name, "Tuple_%s", sig);
+
+ ASTNode *s_def = ast_create(NODE_STRUCT);
+ s_def->strct.name = xstrdup(struct_name);
+
+ char *s_sig = xstrdup(sig);
+ char *tok = strtok(s_sig, "_");
+ ASTNode *head = NULL, *tail = NULL;
+ int i = 0;
+ while (tok) {
+ ASTNode *f = ast_create(NODE_FIELD);
+ char fname[32];
+ sprintf(fname, "v%d", i++);
+ f->field.name = xstrdup(fname);
+ f->field.type = xstrdup(tok);
+
+ if (!head) {
+ head = f;
+ } else {
+ tail->next = f;
+ }
+ tail = f;
+
+ tok = strtok(NULL, "_");
+ }
+ free(s_sig);
+ s_def->strct.fields = head;
+
+ register_struct_def(ctx, struct_name, s_def);
+}
+
+void register_struct_def(ParserContext *ctx, const char *name, ASTNode *node) {
+ StructDef *d = xmalloc(sizeof(StructDef));
+ d->name = xstrdup(name);
+ d->node = node;
+ d->next = ctx->struct_defs;
+ ctx->struct_defs = d;
+}
+
+ASTNode *find_struct_def(ParserContext *ctx, const char *name) {
+ Instantiation *i = ctx->instantiations;
+ while (i) {
+ if (strcmp(i->name, name) == 0) {
+ return i->struct_node;
+ }
+ i = i->next;
+ }
+
+ ASTNode *s = ctx->instantiated_structs;
+ while (s) {
+ if (s->type == NODE_STRUCT && strcmp(s->strct.name, name) == 0) {
+ return s;
+ }
+ s = s->next;
+ }
+
+ StructRef *r = ctx->parsed_structs_list;
+ while (r) {
+ if (strcmp(r->node->strct.name, name) == 0) {
+ return r->node;
+ }
+ r = r->next;
+ }
+
+ // Check manually registered definitions (e.g. Slices)
+ StructDef *d = ctx->struct_defs;
+ while (d) {
+ if (strcmp(d->name, name) == 0) {
+ return d->node;
}
- return NULL;
-}
+ d = d->next;
+ }
-GenericFuncTemplate *find_func_template(ParserContext *ctx, const char *name)
-{
- GenericFuncTemplate *t = ctx->func_templates;
- while (t)
- {
- if (strcmp(t->name, name) == 0)
- {
- return t;
- }
- t = t->next;
+ // Check enums list (for @derive(Eq) and field type lookups)
+ StructRef *e = ctx->parsed_enums_list;
+ while (e) {
+ if (e->node->type == NODE_ENUM && strcmp(e->node->enm.name, name) == 0) {
+ return e->node;
}
- return NULL;
+ e = e->next;
+ }
+
+ return NULL;
}
-void register_generic(ParserContext *ctx, char *name)
-{
- for (int i = 0; i < ctx->known_generics_count; i++)
- {
- if (strcmp(ctx->known_generics[i], name) == 0)
- {
- return;
- }
+Module *find_module(ParserContext *ctx, const char *alias) {
+ Module *m = ctx->modules;
+ while (m) {
+ if (strcmp(m->alias, alias) == 0) {
+ return m;
}
- ctx->known_generics[ctx->known_generics_count++] = strdup(name);
+ m = m->next;
+ }
+ return NULL;
}
-int is_known_generic(ParserContext *ctx, char *name)
-{
- for (int i = 0; i < ctx->known_generics_count; i++)
- {
- if (strcmp(ctx->known_generics[i], name) == 0)
- {
- return 1;
- }
- }
- return 0;
+void register_module(ParserContext *ctx, const char *alias, const char *path) {
+ Module *m = xmalloc(sizeof(Module));
+ m->alias = xstrdup(alias);
+ m->path = xstrdup(path);
+ m->base_name = extract_module_name(path);
+ m->next = ctx->modules;
+ ctx->modules = m;
}
-void register_impl_template(ParserContext *ctx, const char *sname, const char *param, ASTNode *node)
-{
- GenericImplTemplate *t = xmalloc(sizeof(GenericImplTemplate));
- t->struct_name = xstrdup(sname);
- t->generic_param = xstrdup(param);
- t->impl_node = node;
- t->next = ctx->impl_templates;
- ctx->impl_templates = t;
+void register_selective_import(ParserContext *ctx, const char *symbol,
+ const char *alias, const char *source_module) {
+ SelectiveImport *si = xmalloc(sizeof(SelectiveImport));
+ si->symbol = xstrdup(symbol);
+ si->alias = alias ? xstrdup(alias) : NULL;
+ si->source_module = xstrdup(source_module);
+ si->next = ctx->selective_imports;
+ ctx->selective_imports = si;
+}
- // Late binding: Check if any existing instantiations match this new impl
- // template
- Instantiation *inst = ctx->instantiations;
- while (inst)
- {
- if (inst->template_name && strcmp(inst->template_name, sname) == 0)
- {
- instantiate_methods(ctx, t, inst->name, inst->concrete_arg);
- }
- inst = inst->next;
- }
-}
-
-void add_to_struct_list(ParserContext *ctx, ASTNode *node)
-{
- StructRef *r = xmalloc(sizeof(StructRef));
- r->node = node;
- r->next = ctx->parsed_structs_list;
- ctx->parsed_structs_list = r;
-}
-
-void add_to_enum_list(ParserContext *ctx, ASTNode *node)
-{
- StructRef *r = xmalloc(sizeof(StructRef));
- r->node = node;
- r->next = ctx->parsed_enums_list;
- ctx->parsed_enums_list = r;
-}
-
-void add_to_func_list(ParserContext *ctx, ASTNode *node)
-{
- StructRef *r = xmalloc(sizeof(StructRef));
- r->node = node;
- r->next = ctx->parsed_funcs_list;
- ctx->parsed_funcs_list = r;
-}
-
-void add_to_impl_list(ParserContext *ctx, ASTNode *node)
-{
- StructRef *r = xmalloc(sizeof(StructRef));
- r->node = node;
- r->next = ctx->parsed_impls_list;
- ctx->parsed_impls_list = r;
-}
-
-void add_to_global_list(ParserContext *ctx, ASTNode *node)
-{
- StructRef *r = xmalloc(sizeof(StructRef));
- r->node = node;
- r->next = ctx->parsed_globals_list;
- ctx->parsed_globals_list = r;
-}
-
-void register_builtins(ParserContext *ctx)
-{
- Type *t = type_new(TYPE_BOOL);
- t->is_const = 1;
- add_symbol(ctx, "true", "bool", t);
-
- t = type_new(TYPE_BOOL);
- t->is_const = 1;
- add_symbol(ctx, "false", "bool", t);
-
- // Register 'free'
- Type *void_t = type_new(TYPE_VOID);
- add_symbol(ctx, "free", "void", void_t);
-
- // Register common libc functions to avoid warnings
- add_symbol(ctx, "strdup", "string", type_new(TYPE_STRING));
- add_symbol(ctx, "malloc", "void*", type_new_ptr(void_t));
- add_symbol(ctx, "realloc", "void*", type_new_ptr(void_t));
- add_symbol(ctx, "calloc", "void*", type_new_ptr(void_t));
- add_symbol(ctx, "puts", "int", type_new(TYPE_INT));
- add_symbol(ctx, "printf", "int", type_new(TYPE_INT));
- add_symbol(ctx, "strcmp", "int", type_new(TYPE_INT));
- add_symbol(ctx, "strlen", "int", type_new(TYPE_INT));
- add_symbol(ctx, "strcpy", "string", type_new(TYPE_STRING));
- add_symbol(ctx, "strcat", "string", type_new(TYPE_STRING));
- add_symbol(ctx, "exit", "void", void_t);
-
- // File I/O
- add_symbol(ctx, "fopen", "void*", type_new_ptr(void_t));
- add_symbol(ctx, "fclose", "int", type_new(TYPE_INT));
- add_symbol(ctx, "fread", "usize", type_new(TYPE_USIZE));
- add_symbol(ctx, "fwrite", "usize", type_new(TYPE_USIZE));
- add_symbol(ctx, "fseek", "int", type_new(TYPE_INT));
- add_symbol(ctx, "ftell", "long", type_new(TYPE_I64));
- add_symbol(ctx, "rewind", "void", void_t);
- add_symbol(ctx, "fprintf", "int", type_new(TYPE_INT));
- add_symbol(ctx, "sprintf", "int", type_new(TYPE_INT));
- add_symbol(ctx, "feof", "int", type_new(TYPE_INT));
- add_symbol(ctx, "ferror", "int", type_new(TYPE_INT));
- add_symbol(ctx, "usleep", "int", type_new(TYPE_INT));
-}
-
-void add_instantiated_func(ParserContext *ctx, ASTNode *fn)
-{
- fn->next = ctx->instantiated_funcs;
- ctx->instantiated_funcs = fn;
-}
-
-void register_enum_variant(ParserContext *ctx, const char *ename, const char *vname, int tag)
-{
- EnumVariantReg *r = xmalloc(sizeof(EnumVariantReg));
- r->enum_name = xstrdup(ename);
- r->variant_name = xstrdup(vname);
- r->tag_id = tag;
- r->next = ctx->enum_variants;
- ctx->enum_variants = r;
-}
-
-EnumVariantReg *find_enum_variant(ParserContext *ctx, const char *vname)
-{
- EnumVariantReg *r = ctx->enum_variants;
- while (r)
- {
- if (strcmp(r->variant_name, vname) == 0)
- {
- return r;
- }
- r = r->next;
+SelectiveImport *find_selective_import(ParserContext *ctx, const char *name) {
+ SelectiveImport *si = ctx->selective_imports;
+ while (si) {
+ if (si->alias && strcmp(si->alias, name) == 0) {
+ return si;
}
- return NULL;
+ if (!si->alias && strcmp(si->symbol, name) == 0) {
+ return si;
+ }
+ si = si->next;
+ }
+ return NULL;
}
-void register_lambda(ParserContext *ctx, ASTNode *node)
-{
- LambdaRef *ref = xmalloc(sizeof(LambdaRef));
- ref->node = node;
- ref->next = ctx->global_lambdas;
- ctx->global_lambdas = ref;
+char *extract_module_name(const char *path) {
+ const char *slash = strrchr(path, '/');
+ const char *base = slash ? slash + 1 : path;
+ const char *dot = strrchr(base, '.');
+ int len = dot ? (int)(dot - base) : (int)strlen(base);
+ char *name = xmalloc(len + 1);
+ strncpy(name, base, len);
+ name[len] = 0;
+ return name;
}
-void register_var_mutability(ParserContext *ctx, const char *name, int is_mutable)
-{
- VarMutability *v = xmalloc(sizeof(VarMutability));
- v->name = xstrdup(name);
- v->is_mutable = is_mutable;
- v->next = ctx->var_mutability_table;
- ctx->var_mutability_table = v;
-}
+int is_ident_char(char c) { return isalnum(c) || c == '_'; }
-int is_var_mutable(ParserContext *ctx, const char *name)
-{
- for (VarMutability *v = ctx->var_mutability_table; v; v = v->next)
- {
- if (strcmp(v->name, name) == 0)
- {
- return v->is_mutable;
- }
- }
- return 1;
+ASTNode *copy_fields(ASTNode *fields) {
+ if (!fields) {
+ return NULL;
+ }
+ ASTNode *n = ast_create(NODE_FIELD);
+ n->field.name = xstrdup(fields->field.name);
+ n->field.type = xstrdup(fields->field.type);
+ n->next = copy_fields(fields->next);
+ return n;
+}
+char *replace_in_string(const char *src, const char *old_w, const char *new_w) {
+ if (!src || !old_w || !new_w) {
+ return src ? xstrdup(src) : NULL;
+ }
+
+ char *result;
+ int i, cnt = 0;
+ int newWlen = strlen(new_w);
+ int oldWlen = strlen(old_w);
+
+ for (i = 0; src[i] != '\0'; i++) {
+ if (strstr(&src[i], old_w) == &src[i]) {
+ // Check boundaries to ensure we match whole words only
+ int valid = 1;
+
+ // Check preceding character
+ if (i > 0 && is_ident_char(src[i - 1])) {
+ valid = 0;
+ }
+
+ // Check following character
+ if (valid && is_ident_char(src[i + oldWlen])) {
+ valid = 0;
+ }
+
+ if (valid) {
+ cnt++;
+ i += oldWlen - 1;
+ }
+ }
+ }
+
+ // Allocate result buffer
+ result = (char *)xmalloc(i + cnt * (newWlen - oldWlen) + 1);
+
+ i = 0;
+ while (*src) {
+ if (strstr(src, old_w) == src) {
+ int valid = 1;
+
+ // Check boundary relative to the *new* result buffer built so far
+ if (i > 0 && is_ident_char(result[i - 1])) {
+ valid = 0;
+ }
+
+ // Check boundary relative to the *original* source string
+ if (valid && is_ident_char(src[oldWlen])) {
+ valid = 0;
+ }
+
+ if (valid) {
+ strcpy(&result[i], new_w);
+ i += newWlen;
+ src += oldWlen;
+ } else {
+ result[i++] = *src++;
+ }
+ } else {
+ result[i++] = *src++;
+ }
+ }
+ result[i] = '\0';
+ return result;
}
-void register_extern_symbol(ParserContext *ctx, const char *name)
-{
- // Check for duplicates
- for (int i = 0; i < ctx->extern_symbol_count; i++)
- {
- if (strcmp(ctx->extern_symbols[i], name) == 0)
- {
- return;
- }
- }
+char *replace_type_str(const char *src, const char *param, const char *concrete,
+ const char *old_struct, const char *new_struct) {
+ if (!src) {
+ return NULL;
+ }
+
+ if (strcmp(src, param) == 0) {
+ return xstrdup(concrete);
+ }
+
+ if (old_struct && new_struct && strcmp(src, old_struct) == 0) {
+ return xstrdup(new_struct);
+ }
+
+ if (old_struct && new_struct && param) {
+ char *mangled = xmalloc(strlen(old_struct) + strlen(param) + 2);
+ sprintf(mangled, "%s_%s", old_struct, param);
+ if (strcmp(src, mangled) == 0) {
+ free(mangled);
+ return xstrdup(new_struct);
+ }
+ free(mangled);
+ }
+
+ if (param && concrete && src) {
+ char suffix[256];
+ sprintf(suffix, "_%s", param);
+ size_t slen = strlen(src);
+ size_t plen = strlen(suffix);
+ if (slen > plen && strcmp(src + slen - plen, suffix) == 0) {
+ // Ends with _T -> Replace suffix with _int (sanitize for pointers like
+ // JsonValue*)
+ char *clean_concrete = sanitize_mangled_name(concrete);
+ char *ret = xmalloc(slen - plen + strlen(clean_concrete) + 2);
+ strncpy(ret, src, slen - plen);
+ ret[slen - plen] = 0;
+ strcat(ret, "_");
+ strcat(ret, clean_concrete);
+ free(clean_concrete);
+ return ret;
+ }
+ }
+
+ size_t len = strlen(src);
+ if (len > 1 && src[len - 1] == '*') {
+ char *base = xmalloc(len);
+ strncpy(base, src, len - 1);
+ base[len - 1] = 0;
+
+ char *new_base =
+ replace_type_str(base, param, concrete, old_struct, new_struct);
+ free(base);
+
+ if (strcmp(new_base, base) != 0) {
+ char *ret = xmalloc(strlen(new_base) + 2);
+ sprintf(ret, "%s*", new_base);
+ free(new_base);
+ return ret;
+ }
+ free(new_base);
+ }
+
+ if (strncmp(src, "Slice_", 6) == 0) {
+ char *base = xstrdup(src + 6);
+ char *new_base =
+ replace_type_str(base, param, concrete, old_struct, new_struct);
+ free(base);
+
+ if (strcmp(new_base, base) != 0) {
+ char *ret = xmalloc(strlen(new_base) + 7);
+ sprintf(ret, "Slice_%s", new_base);
+ free(new_base);
+ return ret;
+ }
+ free(new_base);
+ }
+
+ return xstrdup(src);
+}
+
+ASTNode *copy_ast_replacing(ASTNode *n, const char *p, const char *c,
+ const char *os, const char *ns);
+
+Type *replace_type_formal(Type *t, const char *p, const char *c, const char *os,
+ const char *ns) {
+ if (!t) {
+ return NULL;
+ }
- // Grow array if needed
- if (ctx->extern_symbol_count == 0)
- {
- ctx->extern_symbols = xmalloc(sizeof(char *) * 64);
+ if ((t->kind == TYPE_STRUCT || t->kind == TYPE_GENERIC) && t->name &&
+ strcmp(t->name, p) == 0) {
+ if (strcmp(c, "int") == 0) {
+ return type_new(TYPE_INT);
}
- else if (ctx->extern_symbol_count % 64 == 0)
- {
- ctx->extern_symbols =
- xrealloc(ctx->extern_symbols, sizeof(char *) * (ctx->extern_symbol_count + 64));
+ if (strcmp(c, "float") == 0) {
+ return type_new(TYPE_FLOAT);
}
-
- ctx->extern_symbols[ctx->extern_symbol_count++] = xstrdup(name);
-}
-
-int is_extern_symbol(ParserContext *ctx, const char *name)
-{
- for (int i = 0; i < ctx->extern_symbol_count; i++)
- {
- if (strcmp(ctx->extern_symbols[i], name) == 0)
- {
- return 1;
- }
+ if (strcmp(c, "void") == 0) {
+ return type_new(TYPE_VOID);
}
- return 0;
-}
-
-// Unified check: should we suppress "undefined variable" warning for this name?
-int should_suppress_undef_warning(ParserContext *ctx, const char *name)
-{
- if (strcmp(name, "struct") == 0 || strcmp(name, "tv") == 0)
- {
- return 1;
+ if (strcmp(c, "string") == 0) {
+ return type_new(TYPE_STRING);
}
-
- if (is_extern_symbol(ctx, name))
- {
- return 1;
+ if (strcmp(c, "bool") == 0) {
+ return type_new(TYPE_BOOL);
+ }
+ if (strcmp(c, "char") == 0) {
+ return type_new(TYPE_CHAR);
}
- int is_all_caps = 1;
- for (const char *p = name; *p; p++)
- {
- if (islower((unsigned char)*p))
- {
- is_all_caps = 0;
- break;
- }
+ if (strcmp(c, "I8") == 0) {
+ return type_new(TYPE_I8);
}
- if (is_all_caps && name[0] != '\0')
- {
- return 1;
+ if (strcmp(c, "U8") == 0) {
+ return type_new(TYPE_U8);
}
-
- if (ctx->has_external_includes)
- {
- return 1;
+ if (strcmp(c, "I16") == 0) {
+ return type_new(TYPE_I16);
}
-
- return 0;
-}
-
-void register_slice(ParserContext *ctx, const char *type)
-{
- SliceType *c = ctx->used_slices;
- while (c)
- {
- if (strcmp(c->name, type) == 0)
- {
- return;
- }
- c = c->next;
- }
- SliceType *n = xmalloc(sizeof(SliceType));
- n->name = xstrdup(type);
- n->next = ctx->used_slices;
- ctx->used_slices = n;
-
- // Register Struct Def for Reflection
- char slice_name[256];
- sprintf(slice_name, "Slice_%s", type);
-
- ASTNode *len_f = ast_create(NODE_FIELD);
- len_f->field.name = xstrdup("len");
- len_f->field.type = xstrdup("int");
- ASTNode *cap_f = ast_create(NODE_FIELD);
- cap_f->field.name = xstrdup("cap");
- cap_f->field.type = xstrdup("int");
- ASTNode *data_f = ast_create(NODE_FIELD);
- data_f->field.name = xstrdup("data");
- char ptr_type[256];
- sprintf(ptr_type, "%s*", type);
- data_f->field.type = xstrdup(ptr_type);
-
- data_f->next = len_f;
- len_f->next = cap_f;
-
- ASTNode *def = ast_create(NODE_STRUCT);
- def->strct.name = xstrdup(slice_name);
- def->strct.fields = data_f;
-
- register_struct_def(ctx, slice_name, def);
-}
-
-void register_tuple(ParserContext *ctx, const char *sig)
-{
- TupleType *c = ctx->used_tuples;
- while (c)
- {
- if (strcmp(c->sig, sig) == 0)
- {
- return;
- }
- c = c->next;
+ if (strcmp(c, "U16") == 0) {
+ return type_new(TYPE_U16);
}
- TupleType *n = xmalloc(sizeof(TupleType));
- n->sig = xstrdup(sig);
- n->next = ctx->used_tuples;
- ctx->used_tuples = n;
-
- char struct_name[1024];
- sprintf(struct_name, "Tuple_%s", sig);
-
- ASTNode *s_def = ast_create(NODE_STRUCT);
- s_def->strct.name = xstrdup(struct_name);
-
- char *s_sig = xstrdup(sig);
- char *tok = strtok(s_sig, "_");
- ASTNode *head = NULL, *tail = NULL;
- int i = 0;
- while (tok)
- {
- ASTNode *f = ast_create(NODE_FIELD);
- char fname[32];
- sprintf(fname, "v%d", i++);
- f->field.name = xstrdup(fname);
- f->field.type = xstrdup(tok);
-
- if (!head)
- {
- head = f;
- }
- else
- {
- tail->next = f;
- }
- tail = f;
-
- tok = strtok(NULL, "_");
+ if (strcmp(c, "I32") == 0) {
+ return type_new(TYPE_I32);
}
- free(s_sig);
- s_def->strct.fields = head;
-
- register_struct_def(ctx, struct_name, s_def);
-}
-
-void register_struct_def(ParserContext *ctx, const char *name, ASTNode *node)
-{
- StructDef *d = xmalloc(sizeof(StructDef));
- d->name = xstrdup(name);
- d->node = node;
- d->next = ctx->struct_defs;
- ctx->struct_defs = d;
-}
-
-ASTNode *find_struct_def(ParserContext *ctx, const char *name)
-{
- Instantiation *i = ctx->instantiations;
- while (i)
- {
- if (strcmp(i->name, name) == 0)
- {
- return i->struct_node;
- }
- i = i->next;
+ if (strcmp(c, "U32") == 0) {
+ return type_new(TYPE_U32);
}
-
- ASTNode *s = ctx->instantiated_structs;
- while (s)
- {
- if (s->type == NODE_STRUCT && strcmp(s->strct.name, name) == 0)
- {
- return s;
- }
- s = s->next;
+ if (strcmp(c, "I64") == 0) {
+ return type_new(TYPE_I64);
}
-
- StructRef *r = ctx->parsed_structs_list;
- while (r)
- {
- if (strcmp(r->node->strct.name, name) == 0)
- {
- return r->node;
- }
- r = r->next;
+ if (strcmp(c, "U64") == 0) {
+ return type_new(TYPE_U64);
}
-
- // Check manually registered definitions (e.g. Slices)
- StructDef *d = ctx->struct_defs;
- while (d)
- {
- if (strcmp(d->name, name) == 0)
- {
- return d->node;
- }
- d = d->next;
+ if (strcmp(c, "F32") == 0) {
+ return type_new(TYPE_F32);
}
-
- // Check enums list (for @derive(Eq) and field type lookups)
- StructRef *e = ctx->parsed_enums_list;
- while (e)
- {
- if (e->node->type == NODE_ENUM && strcmp(e->node->enm.name, name) == 0)
- {
- return e->node;
- }
- e = e->next;
+ if (strcmp(c, "f32") == 0) {
+ return type_new(TYPE_F32);
}
-
- return NULL;
-}
-
-Module *find_module(ParserContext *ctx, const char *alias)
-{
- Module *m = ctx->modules;
- while (m)
- {
- if (strcmp(m->alias, alias) == 0)
- {
- return m;
- }
- m = m->next;
+ if (strcmp(c, "F64") == 0) {
+ return type_new(TYPE_F64);
}
- return NULL;
-}
-
-void register_module(ParserContext *ctx, const char *alias, const char *path)
-{
- Module *m = xmalloc(sizeof(Module));
- m->alias = xstrdup(alias);
- m->path = xstrdup(path);
- m->base_name = extract_module_name(path);
- m->next = ctx->modules;
- ctx->modules = m;
-}
-
-void register_selective_import(ParserContext *ctx, const char *symbol, const char *alias,
- const char *source_module)
-{
- SelectiveImport *si = xmalloc(sizeof(SelectiveImport));
- si->symbol = xstrdup(symbol);
- si->alias = alias ? xstrdup(alias) : NULL;
- si->source_module = xstrdup(source_module);
- si->next = ctx->selective_imports;
- ctx->selective_imports = si;
-}
-
-SelectiveImport *find_selective_import(ParserContext *ctx, const char *name)
-{
- SelectiveImport *si = ctx->selective_imports;
- while (si)
- {
- if (si->alias && strcmp(si->alias, name) == 0)
- {
- return si;
- }
- if (!si->alias && strcmp(si->symbol, name) == 0)
- {
- return si;
- }
- si = si->next;
+ if (strcmp(c, "f64") == 0) {
+ return type_new(TYPE_F64);
}
- return NULL;
-}
-
-char *extract_module_name(const char *path)
-{
- const char *slash = strrchr(path, '/');
- const char *base = slash ? slash + 1 : path;
- const char *dot = strrchr(base, '.');
- int len = dot ? (int)(dot - base) : (int)strlen(base);
- char *name = xmalloc(len + 1);
- strncpy(name, base, len);
- name[len] = 0;
- return name;
-}
-int is_ident_char(char c)
-{
- return isalnum(c) || c == '_';
-}
-
-ASTNode *copy_fields(ASTNode *fields)
-{
- if (!fields)
- {
- return NULL;
+ if (strcmp(c, "usize") == 0) {
+ return type_new(TYPE_USIZE);
}
- ASTNode *n = ast_create(NODE_FIELD);
- n->field.name = xstrdup(fields->field.name);
- n->field.type = xstrdup(fields->field.type);
- n->next = copy_fields(fields->next);
- return n;
-}
-char *replace_in_string(const char *src, const char *old_w, const char *new_w)
-{
- if (!src || !old_w || !new_w)
- {
- return src ? xstrdup(src) : NULL;
+ if (strcmp(c, "isize") == 0) {
+ return type_new(TYPE_ISIZE);
}
-
- char *result;
- int i, cnt = 0;
- int newWlen = strlen(new_w);
- int oldWlen = strlen(old_w);
-
- for (i = 0; src[i] != '\0'; i++)
- {
- if (strstr(&src[i], old_w) == &src[i])
- {
- // Check boundaries to ensure we match whole words only
- int valid = 1;
-
- // Check preceding character
- if (i > 0 && is_ident_char(src[i - 1]))
- {
- valid = 0;
- }
-
- // Check following character
- if (valid && is_ident_char(src[i + oldWlen]))
- {
- valid = 0;
- }
-
- if (valid)
- {
- cnt++;
- i += oldWlen - 1;
- }
- }
+ if (strcmp(c, "byte") == 0) {
+ return type_new(TYPE_BYTE);
}
-
- // Allocate result buffer
- result = (char *)xmalloc(i + cnt * (newWlen - oldWlen) + 1);
-
- i = 0;
- while (*src)
- {
- if (strstr(src, old_w) == src)
- {
- int valid = 1;
-
- // Check boundary relative to the *new* result buffer built so far
- if (i > 0 && is_ident_char(result[i - 1]))
- {
- valid = 0;
- }
-
- // Check boundary relative to the *original* source string
- if (valid && is_ident_char(src[oldWlen]))
- {
- valid = 0;
- }
-
- if (valid)
- {
- strcpy(&result[i], new_w);
- i += newWlen;
- src += oldWlen;
- }
- else
- {
- result[i++] = *src++;
- }
- }
- else
- {
- result[i++] = *src++;
- }
+ if (strcmp(c, "I128") == 0) {
+ return type_new(TYPE_I128);
}
- result[i] = '\0';
- return result;
-}
-
-char *replace_type_str(const char *src, const char *param, const char *concrete,
- const char *old_struct, const char *new_struct)
-{
- if (!src)
- {
- return NULL;
+ if (strcmp(c, "U128") == 0) {
+ return type_new(TYPE_U128);
}
-
- if (strcmp(src, param) == 0)
- {
- return xstrdup(concrete);
+ if (strcmp(c, "i8") == 0) {
+ return type_new(TYPE_I8);
}
-
- if (old_struct && new_struct && strcmp(src, old_struct) == 0)
- {
- return xstrdup(new_struct);
+ if (strcmp(c, "u8") == 0) {
+ return type_new(TYPE_U8);
}
-
- if (old_struct && new_struct && param)
- {
- char *mangled = xmalloc(strlen(old_struct) + strlen(param) + 2);
- sprintf(mangled, "%s_%s", old_struct, param);
- if (strcmp(src, mangled) == 0)
- {
- free(mangled);
- return xstrdup(new_struct);
- }
- free(mangled);
+ if (strcmp(c, "i16") == 0) {
+ return type_new(TYPE_I16);
}
-
- if (param && concrete && src)
- {
- char suffix[256];
- sprintf(suffix, "_%s", param);
- size_t slen = strlen(src);
- size_t plen = strlen(suffix);
- if (slen > plen && strcmp(src + slen - plen, suffix) == 0)
- {
- // Ends with _T -> Replace suffix with _int (sanitize for pointers like
- // JsonValue*)
- char *clean_concrete = sanitize_mangled_name(concrete);
- char *ret = xmalloc(slen - plen + strlen(clean_concrete) + 2);
- strncpy(ret, src, slen - plen);
- ret[slen - plen] = 0;
- strcat(ret, "_");
- strcat(ret, clean_concrete);
- free(clean_concrete);
- return ret;
- }
+ if (strcmp(c, "u16") == 0) {
+ return type_new(TYPE_U16);
}
-
- size_t len = strlen(src);
- if (len > 1 && src[len - 1] == '*')
- {
- char *base = xmalloc(len);
- strncpy(base, src, len - 1);
- base[len - 1] = 0;
-
- char *new_base = replace_type_str(base, param, concrete, old_struct, new_struct);
- free(base);
-
- if (strcmp(new_base, base) != 0)
- {
- char *ret = xmalloc(strlen(new_base) + 2);
- sprintf(ret, "%s*", new_base);
- free(new_base);
- return ret;
- }
- free(new_base);
+ if (strcmp(c, "i32") == 0) {
+ return type_new(TYPE_I32);
}
-
- if (strncmp(src, "Slice_", 6) == 0)
- {
- char *base = xstrdup(src + 6);
- char *new_base = replace_type_str(base, param, concrete, old_struct, new_struct);
- free(base);
-
- if (strcmp(new_base, base) != 0)
- {
- char *ret = xmalloc(strlen(new_base) + 7);
- sprintf(ret, "Slice_%s", new_base);
- free(new_base);
- return ret;
- }
- free(new_base);
+ if (strcmp(c, "u32") == 0) {
+ return type_new(TYPE_U32);
}
-
- return xstrdup(src);
-}
-
-ASTNode *copy_ast_replacing(ASTNode *n, const char *p, const char *c, const char *os,
- const char *ns);
-
-Type *replace_type_formal(Type *t, const char *p, const char *c, const char *os, const char *ns)
-{
- if (!t)
- {
- return NULL;
+ if (strcmp(c, "i64") == 0) {
+ return type_new(TYPE_I64);
}
-
- if ((t->kind == TYPE_STRUCT || t->kind == TYPE_GENERIC) && t->name && strcmp(t->name, p) == 0)
- {
- if (strcmp(c, "int") == 0)
- {
- return type_new(TYPE_INT);
- }
- if (strcmp(c, "float") == 0)
- {
- return type_new(TYPE_FLOAT);
- }
- if (strcmp(c, "void") == 0)
- {
- return type_new(TYPE_VOID);
- }
- if (strcmp(c, "string") == 0)
- {
- return type_new(TYPE_STRING);
- }
- if (strcmp(c, "bool") == 0)
- {
- return type_new(TYPE_BOOL);
- }
- if (strcmp(c, "char") == 0)
- {
- return type_new(TYPE_CHAR);
- }
-
- if (strcmp(c, "I8") == 0)
- {
- return type_new(TYPE_I8);
- }
- if (strcmp(c, "U8") == 0)
- {
- return type_new(TYPE_U8);
- }
- if (strcmp(c, "I16") == 0)
- {
- return type_new(TYPE_I16);
- }
- if (strcmp(c, "U16") == 0)
- {
- return type_new(TYPE_U16);
- }
- if (strcmp(c, "I32") == 0)
- {
- return type_new(TYPE_I32);
- }
- if (strcmp(c, "U32") == 0)
- {
- return type_new(TYPE_U32);
- }
- if (strcmp(c, "I64") == 0)
- {
- return type_new(TYPE_I64);
- }
- if (strcmp(c, "U64") == 0)
- {
- return type_new(TYPE_U64);
- }
- if (strcmp(c, "F32") == 0)
- {
- return type_new(TYPE_F32);
- }
- if (strcmp(c, "f32") == 0)
- {
- return type_new(TYPE_F32);
- }
- if (strcmp(c, "F64") == 0)
- {
- return type_new(TYPE_F64);
- }
- if (strcmp(c, "f64") == 0)
- {
- return type_new(TYPE_F64);
- }
-
- if (strcmp(c, "usize") == 0)
- {
- return type_new(TYPE_USIZE);
- }
- if (strcmp(c, "isize") == 0)
- {
- return type_new(TYPE_ISIZE);
- }
- if (strcmp(c, "byte") == 0)
- {
- return type_new(TYPE_BYTE);
- }
- if (strcmp(c, "I128") == 0)
- {
- return type_new(TYPE_I128);
- }
- if (strcmp(c, "U128") == 0)
- {
- return type_new(TYPE_U128);
- }
- if (strcmp(c, "i8") == 0)
- {
- return type_new(TYPE_I8);
- }
- if (strcmp(c, "u8") == 0)
- {
- return type_new(TYPE_U8);
- }
- if (strcmp(c, "i16") == 0)
- {
- return type_new(TYPE_I16);
- }
- if (strcmp(c, "u16") == 0)
- {
- return type_new(TYPE_U16);
- }
- if (strcmp(c, "i32") == 0)
- {
- return type_new(TYPE_I32);
- }
- if (strcmp(c, "u32") == 0)
- {
- return type_new(TYPE_U32);
- }
- if (strcmp(c, "i64") == 0)
- {
- return type_new(TYPE_I64);
- }
- if (strcmp(c, "u64") == 0)
- {
- return type_new(TYPE_U64);
- }
- if (strcmp(c, "i128") == 0)
- {
- return type_new(TYPE_I128);
- }
- if (strcmp(c, "u128") == 0)
- {
- return type_new(TYPE_U128);
- }
-
- if (strcmp(c, "rune") == 0)
- {
- return type_new(TYPE_RUNE);
- }
- if (strcmp(c, "uint") == 0)
- {
- return type_new(TYPE_UINT);
- }
-
- Type *n = type_new(TYPE_STRUCT);
- n->name = sanitize_mangled_name(c);
- return n;
+ if (strcmp(c, "u64") == 0) {
+ return type_new(TYPE_U64);
}
-
- Type *n = xmalloc(sizeof(Type));
- *n = *t;
-
- if (t->name)
- {
- if (os && ns && strcmp(t->name, os) == 0)
- {
- n->name = xstrdup(ns);
- n->kind = TYPE_STRUCT;
- n->arg_count = 0;
- n->args = NULL;
- }
-
- else if (p && c)
- {
- char suffix[256];
- sprintf(suffix, "_%s", p); // e.g. "_T"
- size_t nlen = strlen(t->name);
- size_t slen = strlen(suffix);
-
- if (nlen > slen && strcmp(t->name + nlen - slen, suffix) == 0)
- {
- // It ends in _T. Replace with _int (c), sanitizing for pointers
- char *clean_c = sanitize_mangled_name(c);
- char *new_name = xmalloc(nlen - slen + strlen(clean_c) + 2);
- strncpy(new_name, t->name, nlen - slen);
- new_name[nlen - slen] = 0;
- strcat(new_name, "_");
- strcat(new_name, clean_c);
- free(clean_c);
- n->name = new_name;
- // Ensure it's concrete to prevent double mangling later
- n->kind = TYPE_STRUCT;
- n->arg_count = 0;
- n->args = NULL;
- }
- else
- {
- n->name = xstrdup(t->name);
- }
- }
- else
- {
- n->name = xstrdup(t->name);
- }
+ if (strcmp(c, "i128") == 0) {
+ return type_new(TYPE_I128);
}
-
- if (t->kind == TYPE_POINTER || t->kind == TYPE_ARRAY)
- {
- n->inner = replace_type_formal(t->inner, p, c, os, ns);
+ if (strcmp(c, "u128") == 0) {
+ return type_new(TYPE_U128);
}
- if (n->arg_count > 0 && t->args)
- {
- n->args = xmalloc(sizeof(Type *) * t->arg_count);
- for (int i = 0; i < t->arg_count; i++)
- {
- n->args[i] = replace_type_formal(t->args[i], p, c, os, ns);
- }
+ if (strcmp(c, "rune") == 0) {
+ return type_new(TYPE_RUNE);
+ }
+ if (strcmp(c, "uint") == 0) {
+ return type_new(TYPE_UINT);
}
+ Type *n = type_new(TYPE_STRUCT);
+ n->name = sanitize_mangled_name(c);
return n;
+ }
+
+ Type *n = xmalloc(sizeof(Type));
+ *n = *t;
+
+ if (t->name) {
+ if (os && ns && strcmp(t->name, os) == 0) {
+ n->name = xstrdup(ns);
+ n->kind = TYPE_STRUCT;
+ n->arg_count = 0;
+ n->args = NULL;
+ }
+
+ else if (p && c) {
+ char suffix[256];
+ sprintf(suffix, "_%s", p); // e.g. "_T"
+ size_t nlen = strlen(t->name);
+ size_t slen = strlen(suffix);
+
+ if (nlen > slen && strcmp(t->name + nlen - slen, suffix) == 0) {
+ // It ends in _T. Replace with _int (c), sanitizing for pointers
+ char *clean_c = sanitize_mangled_name(c);
+ char *new_name = xmalloc(nlen - slen + strlen(clean_c) + 2);
+ strncpy(new_name, t->name, nlen - slen);
+ new_name[nlen - slen] = 0;
+ strcat(new_name, "_");
+ strcat(new_name, clean_c);
+ free(clean_c);
+ n->name = new_name;
+ // Ensure it's concrete to prevent double mangling later
+ n->kind = TYPE_STRUCT;
+ n->arg_count = 0;
+ n->args = NULL;
+ } else {
+ n->name = xstrdup(t->name);
+ }
+ } else {
+ n->name = xstrdup(t->name);
+ }
+ }
+
+ if (t->kind == TYPE_POINTER || t->kind == TYPE_ARRAY) {
+ n->inner = replace_type_formal(t->inner, p, c, os, ns);
+ }
+
+ if (n->arg_count > 0 && t->args) {
+ n->args = xmalloc(sizeof(Type *) * t->arg_count);
+ for (int i = 0; i < t->arg_count; i++) {
+ n->args[i] = replace_type_formal(t->args[i], p, c, os, ns);
+ }
+ }
+
+ return n;
}
// Helper to replace generic params in mangled names (e.g. Option_V_None ->
// Option_int_None)
-char *replace_mangled_part(const char *src, const char *param, const char *concrete)
-{
- if (!src || !param || !concrete)
- {
- return src ? xstrdup(src) : NULL;
- }
+char *replace_mangled_part(const char *src, const char *param,
+ const char *concrete) {
+ if (!src || !param || !concrete) {
+ return src ? xstrdup(src) : NULL;
+ }
+
+ char *result = xmalloc(4096); // Basic buffer for simplicity
+ result[0] = 0;
+
+ const char *curr = src;
+ char *out = result;
+ int plen = strlen(param);
+
+ while (*curr) {
+ // Check if param matches here
+ if (strncmp(curr, param, plen) == 0) {
+ // Check boundaries: Must be delimited by quoted boundaries, OR
+ // underscores, OR string ends
+ int valid = 1;
+
+ // Check Prev: Start of string OR Underscore
+ if (curr > src) {
+ if (*(curr - 1) != '_' && is_ident_char(*(curr - 1))) {
+ valid = 0;
+ }
+ }
+
+ // Check Next: End of string OR Underscore
+ if (valid && curr[plen] != 0 && curr[plen] != '_' &&
+ is_ident_char(curr[plen])) {
+ valid = 0;
+ }
+
+ if (valid) {
+ strcpy(out, concrete);
+ out += strlen(concrete);
+ curr += plen;
+ continue;
+ }
+ }
+ *out++ = *curr++;
+ }
+ *out = 0;
+ return xstrdup(result);
+}
+
+ASTNode *copy_ast_replacing(ASTNode *n, const char *p, const char *c,
+ const char *os, const char *ns) {
+ if (!n) {
+ return NULL;
+ }
- char *result = xmalloc(4096); // Basic buffer for simplicity
- result[0] = 0;
+ ASTNode *new_node = xmalloc(sizeof(ASTNode));
+ *new_node = *n;
- const char *curr = src;
- char *out = result;
- int plen = strlen(param);
+ if (n->resolved_type) {
+ new_node->resolved_type = replace_type_str(n->resolved_type, p, c, os, ns);
+ }
+ new_node->type_info = replace_type_formal(n->type_info, p, c, os, ns);
- while (*curr)
- {
- // Check if param matches here
- if (strncmp(curr, param, plen) == 0)
- {
- // Check boundaries: Must be delimited by quoted boundaries, OR
- // underscores, OR string ends
- int valid = 1;
-
- // Check Prev: Start of string OR Underscore
- if (curr > src)
- {
- if (*(curr - 1) != '_' && is_ident_char(*(curr - 1)))
- {
- valid = 0;
- }
- }
+ new_node->next = copy_ast_replacing(n->next, p, c, os, ns);
- // Check Next: End of string OR Underscore
- if (valid && curr[plen] != 0 && curr[plen] != '_' && is_ident_char(curr[plen]))
- {
- valid = 0;
- }
+ switch (n->type) {
+ case NODE_FUNCTION:
+ new_node->func.name = xstrdup(n->func.name);
+ new_node->func.ret_type = replace_type_str(n->func.ret_type, p, c, os, ns);
- if (valid)
- {
- strcpy(out, concrete);
- out += strlen(concrete);
- curr += plen;
- continue;
- }
- }
- *out++ = *curr++;
+ char *tmp_args = replace_in_string(n->func.args, p, c);
+ if (os && ns) {
+ char *tmp2 = replace_in_string(tmp_args, os, ns);
+ free(tmp_args);
+ tmp_args = tmp2;
}
- *out = 0;
- return xstrdup(result);
-}
-
-ASTNode *copy_ast_replacing(ASTNode *n, const char *p, const char *c, const char *os,
- const char *ns)
-{
- if (!n)
- {
- return NULL;
+ if (p && c) {
+ char *clean_c = sanitize_mangled_name(c);
+ char *tmp3 = replace_mangled_part(tmp_args, p, clean_c);
+ free(clean_c);
+ free(tmp_args);
+ tmp_args = tmp3;
}
+ new_node->func.args = tmp_args;
- ASTNode *new_node = xmalloc(sizeof(ASTNode));
- *new_node = *n;
-
- if (n->resolved_type)
- {
- new_node->resolved_type = replace_type_str(n->resolved_type, p, c, os, ns);
+ new_node->func.ret_type_info =
+ replace_type_formal(n->func.ret_type_info, p, c, os, ns);
+ if (n->func.arg_types) {
+ new_node->func.arg_types = xmalloc(sizeof(Type *) * n->func.arg_count);
+ for (int i = 0; i < n->func.arg_count; i++) {
+ new_node->func.arg_types[i] =
+ replace_type_formal(n->func.arg_types[i], p, c, os, ns);
+ }
}
- new_node->type_info = replace_type_formal(n->type_info, p, c, os, ns);
-
- new_node->next = copy_ast_replacing(n->next, p, c, os, ns);
-
- switch (n->type)
- {
- case NODE_FUNCTION:
- new_node->func.name = xstrdup(n->func.name);
- new_node->func.ret_type = replace_type_str(n->func.ret_type, p, c, os, ns);
-
- char *tmp_args = replace_in_string(n->func.args, p, c);
- if (os && ns)
- {
- char *tmp2 = replace_in_string(tmp_args, os, ns);
- free(tmp_args);
- tmp_args = tmp2;
- }
- if (p && c)
- {
- char *clean_c = sanitize_mangled_name(c);
- char *tmp3 = replace_mangled_part(tmp_args, p, clean_c);
- free(clean_c);
- free(tmp_args);
- tmp_args = tmp3;
- }
- new_node->func.args = tmp_args;
-
- new_node->func.ret_type_info = replace_type_formal(n->func.ret_type_info, p, c, os, ns);
- if (n->func.arg_types)
- {
- new_node->func.arg_types = xmalloc(sizeof(Type *) * n->func.arg_count);
- for (int i = 0; i < n->func.arg_count; i++)
- {
- new_node->func.arg_types[i] =
- replace_type_formal(n->func.arg_types[i], p, c, os, ns);
- }
- }
- new_node->func.body = copy_ast_replacing(n->func.body, p, c, os, ns);
- break;
- case NODE_BLOCK:
- new_node->block.statements = copy_ast_replacing(n->block.statements, p, c, os, ns);
- break;
- case NODE_RAW_STMT:
- {
- char *s1 = replace_in_string(n->raw_stmt.content, p, c);
- if (os && ns)
- {
- char *s2 = replace_in_string(s1, os, ns);
- free(s1);
- s1 = s2;
- }
-
- if (p && c)
- {
- char *clean_c = sanitize_mangled_name(c);
- char *s3 = replace_mangled_part(s1, p, clean_c);
- free(clean_c);
- free(s1);
- s1 = s3;
- }
-
- new_node->raw_stmt.content = s1;
- }
+ new_node->func.body = copy_ast_replacing(n->func.body, p, c, os, ns);
break;
- case NODE_VAR_DECL:
- new_node->var_decl.name = xstrdup(n->var_decl.name);
- new_node->var_decl.type_str = replace_type_str(n->var_decl.type_str, p, c, os, ns);
- new_node->var_decl.init_expr = copy_ast_replacing(n->var_decl.init_expr, p, c, os, ns);
- break;
- case NODE_RETURN:
- new_node->ret.value = copy_ast_replacing(n->ret.value, p, c, os, ns);
- break;
- case NODE_EXPR_BINARY:
- new_node->binary.left = copy_ast_replacing(n->binary.left, p, c, os, ns);
- new_node->binary.right = copy_ast_replacing(n->binary.right, p, c, os, ns);
- new_node->binary.op = xstrdup(n->binary.op);
- break;
- case NODE_EXPR_UNARY:
- new_node->unary.op = xstrdup(n->unary.op);
- new_node->unary.operand = copy_ast_replacing(n->unary.operand, p, c, os, ns);
- break;
- case NODE_EXPR_CALL:
- new_node->call.callee = copy_ast_replacing(n->call.callee, p, c, os, ns);
- new_node->call.args = copy_ast_replacing(n->call.args, p, c, os, ns);
- new_node->call.arg_names = n->call.arg_names; // Share pointer (shallow copy)
- new_node->call.arg_count = n->call.arg_count;
- break;
- case NODE_EXPR_VAR:
- {
- char *n1 = xstrdup(n->var_ref.name);
- if (p && c)
- {
- char *clean_c = sanitize_mangled_name(c);
- char *n2 = replace_mangled_part(n1, p, clean_c);
- free(clean_c);
- free(n1);
- n1 = n2;
- }
- new_node->var_ref.name = n1;
+ case NODE_BLOCK:
+ new_node->block.statements =
+ copy_ast_replacing(n->block.statements, p, c, os, ns);
+ break;
+ case NODE_RAW_STMT: {
+ char *s1 = replace_in_string(n->raw_stmt.content, p, c);
+ if (os && ns) {
+ char *s2 = replace_in_string(s1, os, ns);
+ free(s1);
+ s1 = s2;
+ }
+
+ if (p && c) {
+ char *clean_c = sanitize_mangled_name(c);
+ char *s3 = replace_mangled_part(s1, p, clean_c);
+ free(clean_c);
+ free(s1);
+ s1 = s3;
+ }
+
+ new_node->raw_stmt.content = s1;
+ } break;
+ case NODE_VAR_DECL:
+ new_node->var_decl.name = xstrdup(n->var_decl.name);
+ new_node->var_decl.type_str =
+ replace_type_str(n->var_decl.type_str, p, c, os, ns);
+ new_node->var_decl.init_expr =
+ copy_ast_replacing(n->var_decl.init_expr, p, c, os, ns);
+ break;
+ case NODE_RETURN:
+ new_node->ret.value = copy_ast_replacing(n->ret.value, p, c, os, ns);
+ break;
+ case NODE_EXPR_BINARY:
+ new_node->binary.left = copy_ast_replacing(n->binary.left, p, c, os, ns);
+ new_node->binary.right = copy_ast_replacing(n->binary.right, p, c, os, ns);
+ new_node->binary.op = xstrdup(n->binary.op);
+ break;
+ case NODE_EXPR_UNARY:
+ new_node->unary.op = xstrdup(n->unary.op);
+ new_node->unary.operand =
+ copy_ast_replacing(n->unary.operand, p, c, os, ns);
+ break;
+ case NODE_EXPR_CALL:
+ new_node->call.callee = copy_ast_replacing(n->call.callee, p, c, os, ns);
+ new_node->call.args = copy_ast_replacing(n->call.args, p, c, os, ns);
+ new_node->call.arg_names =
+ n->call.arg_names; // Share pointer (shallow copy)
+ new_node->call.arg_count = n->call.arg_count;
+ break;
+ case NODE_EXPR_VAR: {
+ char *n1 = xstrdup(n->var_ref.name);
+ if (p && c) {
+ char *clean_c = sanitize_mangled_name(c);
+ char *n2 = replace_mangled_part(n1, p, clean_c);
+ free(clean_c);
+ free(n1);
+ n1 = n2;
+ }
+ new_node->var_ref.name = n1;
+ } break;
+ case NODE_FIELD:
+ new_node->field.name = xstrdup(n->field.name);
+ new_node->field.type = replace_type_str(n->field.type, p, c, os, ns);
+ break;
+ case NODE_EXPR_LITERAL:
+ if (n->literal.type_kind == 2) {
+ new_node->literal.string_val = xstrdup(n->literal.string_val);
}
break;
- case NODE_FIELD:
- new_node->field.name = xstrdup(n->field.name);
- new_node->field.type = replace_type_str(n->field.type, p, c, os, ns);
- break;
- case NODE_EXPR_LITERAL:
- if (n->literal.type_kind == 2)
- {
- new_node->literal.string_val = xstrdup(n->literal.string_val);
- }
- break;
- case NODE_EXPR_MEMBER:
- new_node->member.target = copy_ast_replacing(n->member.target, p, c, os, ns);
- new_node->member.field = xstrdup(n->member.field);
- break;
- case NODE_EXPR_INDEX:
- new_node->index.array = copy_ast_replacing(n->index.array, p, c, os, ns);
- new_node->index.index = copy_ast_replacing(n->index.index, p, c, os, ns);
- break;
- case NODE_EXPR_CAST:
- new_node->cast.target_type = replace_type_str(n->cast.target_type, p, c, os, ns);
- new_node->cast.expr = copy_ast_replacing(n->cast.expr, p, c, os, ns);
- break;
- case NODE_EXPR_STRUCT_INIT:
- new_node->struct_init.struct_name =
- replace_type_str(n->struct_init.struct_name, p, c, os, ns);
- ASTNode *h = NULL, *t = NULL, *curr = n->struct_init.fields;
- while (curr)
- {
- ASTNode *cp = copy_ast_replacing(curr, p, c, os, ns);
- cp->next = NULL;
- if (!h)
- {
- h = cp;
- }
- else
- {
- t->next = cp;
- }
- t = cp;
- curr = curr->next;
- }
- new_node->struct_init.fields = h;
- break;
- case NODE_IF:
- new_node->if_stmt.condition = copy_ast_replacing(n->if_stmt.condition, p, c, os, ns);
- new_node->if_stmt.then_body = copy_ast_replacing(n->if_stmt.then_body, p, c, os, ns);
- new_node->if_stmt.else_body = copy_ast_replacing(n->if_stmt.else_body, p, c, os, ns);
- break;
- case NODE_WHILE:
- new_node->while_stmt.condition = copy_ast_replacing(n->while_stmt.condition, p, c, os, ns);
- new_node->while_stmt.body = copy_ast_replacing(n->while_stmt.body, p, c, os, ns);
- break;
- case NODE_FOR:
- new_node->for_stmt.init = copy_ast_replacing(n->for_stmt.init, p, c, os, ns);
- new_node->for_stmt.condition = copy_ast_replacing(n->for_stmt.condition, p, c, os, ns);
- new_node->for_stmt.step = copy_ast_replacing(n->for_stmt.step, p, c, os, ns);
- new_node->for_stmt.body = copy_ast_replacing(n->for_stmt.body, p, c, os, ns);
- break;
-
- case NODE_MATCH_CASE:
- if (n->match_case.pattern)
- {
- char *s1 = replace_in_string(n->match_case.pattern, p, c);
- if (os && ns)
- {
- char *s2 = replace_in_string(s1, os, ns);
- free(s1);
- s1 = s2;
- char *colons = strstr(s1, "::");
- if (colons)
- {
- colons[0] = '_';
- memmove(colons + 1, colons + 2, strlen(colons + 2) + 1);
- }
- }
- new_node->match_case.pattern = s1;
- }
- new_node->match_case.body = copy_ast_replacing(n->match_case.body, p, c, os, ns);
- if (n->match_case.guard)
- {
- new_node->match_case.guard = copy_ast_replacing(n->match_case.guard, p, c, os, ns);
- }
- break;
+ case NODE_EXPR_MEMBER:
+ new_node->member.target =
+ copy_ast_replacing(n->member.target, p, c, os, ns);
+ new_node->member.field = xstrdup(n->member.field);
+ break;
+ case NODE_EXPR_INDEX:
+ new_node->index.array = copy_ast_replacing(n->index.array, p, c, os, ns);
+ new_node->index.index = copy_ast_replacing(n->index.index, p, c, os, ns);
+ break;
+ case NODE_EXPR_CAST:
+ new_node->cast.target_type =
+ replace_type_str(n->cast.target_type, p, c, os, ns);
+ new_node->cast.expr = copy_ast_replacing(n->cast.expr, p, c, os, ns);
+ break;
+ case NODE_EXPR_STRUCT_INIT:
+ new_node->struct_init.struct_name =
+ replace_type_str(n->struct_init.struct_name, p, c, os, ns);
+ ASTNode *h = NULL, *t = NULL, *curr = n->struct_init.fields;
+ while (curr) {
+ ASTNode *cp = copy_ast_replacing(curr, p, c, os, ns);
+ cp->next = NULL;
+ if (!h) {
+ h = cp;
+ } else {
+ t->next = cp;
+ }
+ t = cp;
+ curr = curr->next;
+ }
+ new_node->struct_init.fields = h;
+ break;
+ case NODE_IF:
+ new_node->if_stmt.condition =
+ copy_ast_replacing(n->if_stmt.condition, p, c, os, ns);
+ new_node->if_stmt.then_body =
+ copy_ast_replacing(n->if_stmt.then_body, p, c, os, ns);
+ new_node->if_stmt.else_body =
+ copy_ast_replacing(n->if_stmt.else_body, p, c, os, ns);
+ break;
+ case NODE_WHILE:
+ new_node->while_stmt.condition =
+ copy_ast_replacing(n->while_stmt.condition, p, c, os, ns);
+ new_node->while_stmt.body =
+ copy_ast_replacing(n->while_stmt.body, p, c, os, ns);
+ break;
+ case NODE_FOR:
+ new_node->for_stmt.init =
+ copy_ast_replacing(n->for_stmt.init, p, c, os, ns);
+ new_node->for_stmt.condition =
+ copy_ast_replacing(n->for_stmt.condition, p, c, os, ns);
+ new_node->for_stmt.step =
+ copy_ast_replacing(n->for_stmt.step, p, c, os, ns);
+ new_node->for_stmt.body =
+ copy_ast_replacing(n->for_stmt.body, p, c, os, ns);
+ break;
- case NODE_IMPL:
- new_node->impl.struct_name = replace_type_str(n->impl.struct_name, p, c, os, ns);
- new_node->impl.methods = copy_ast_replacing(n->impl.methods, p, c, os, ns);
- break;
- default:
- break;
+ case NODE_MATCH_CASE:
+ if (n->match_case.pattern) {
+ char *s1 = replace_in_string(n->match_case.pattern, p, c);
+ if (os && ns) {
+ char *s2 = replace_in_string(s1, os, ns);
+ free(s1);
+ s1 = s2;
+ char *colons = strstr(s1, "::");
+ if (colons) {
+ colons[0] = '_';
+ memmove(colons + 1, colons + 2, strlen(colons + 2) + 1);
+ }
+ }
+ new_node->match_case.pattern = s1;
+ }
+ new_node->match_case.body =
+ copy_ast_replacing(n->match_case.body, p, c, os, ns);
+ if (n->match_case.guard) {
+ new_node->match_case.guard =
+ copy_ast_replacing(n->match_case.guard, p, c, os, ns);
}
- return new_node;
-}
+ break;
-// Helper to sanitize type names for mangling (e.g. "int*" -> "intPtr")
-char *sanitize_mangled_name(const char *s)
-{
- char *buf = xmalloc(strlen(s) * 4 + 1);
- char *p = buf;
- while (*s)
- {
- if (*s == '*')
- {
- strcpy(p, "Ptr");
- p += 3;
- }
- else if (*s == ' ')
- {
- *p++ = '_';
- }
- else if ((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || (*s >= '0' && *s <= '9') ||
- *s == '_')
- {
- *p++ = *s;
- }
- else
- {
- *p++ = '_';
- }
- s++;
- }
- *p = 0;
- return buf;
+ case NODE_IMPL:
+ new_node->impl.struct_name =
+ replace_type_str(n->impl.struct_name, p, c, os, ns);
+ new_node->impl.methods = copy_ast_replacing(n->impl.methods, p, c, os, ns);
+ break;
+ default:
+ break;
+ }
+ return new_node;
}
-FuncSig *find_func(ParserContext *ctx, const char *name)
-{
- FuncSig *c = ctx->func_registry;
- while (c)
- {
- if (strcmp(c->name, name) == 0)
- {
- return c;
- }
- c = c->next;
- }
+// Helper to sanitize type names for mangling (e.g. "int*" -> "intPtr")
+char *sanitize_mangled_name(const char *s) {
+ char *buf = xmalloc(strlen(s) * 4 + 1);
+ char *p = buf;
+ while (*s) {
+ if (*s == '*') {
+ strcpy(p, "Ptr");
+ p += 3;
+ } else if (*s == ' ') {
+ *p++ = '_';
+ } else if ((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
+ (*s >= '0' && *s <= '9') || *s == '_') {
+ *p++ = *s;
+ } else {
+ *p++ = '_';
+ }
+ s++;
+ }
+ *p = 0;
+ return buf;
+}
+
+FuncSig *find_func(ParserContext *ctx, const char *name) {
+ FuncSig *c = ctx->func_registry;
+ while (c) {
+ if (strcmp(c->name, name) == 0) {
+ return c;
+ }
+ c = c->next;
+ }
+ return NULL;
+}
+
+char *instantiate_function_template(ParserContext *ctx, const char *name,
+ const char *concrete_type) {
+ GenericFuncTemplate *tpl = find_func_template(ctx, name);
+ if (!tpl) {
return NULL;
-}
-
-char *instantiate_function_template(ParserContext *ctx, const char *name, const char *concrete_type)
-{
- GenericFuncTemplate *tpl = find_func_template(ctx, name);
- if (!tpl)
- {
- return NULL;
- }
-
- char *clean_type = sanitize_mangled_name(concrete_type);
- char *mangled = xmalloc(strlen(name) + strlen(clean_type) + 2);
- sprintf(mangled, "%s_%s", name, clean_type);
- free(clean_type);
-
- if (find_func(ctx, mangled))
- {
- return mangled;
- }
+ }
- ASTNode *new_fn =
- copy_ast_replacing(tpl->func_node, tpl->generic_param, concrete_type, NULL, NULL);
- if (!new_fn || new_fn->type != NODE_FUNCTION)
- {
- return NULL;
- }
- free(new_fn->func.name);
- new_fn->func.name = xstrdup(mangled);
-
- register_func(ctx, mangled, new_fn->func.arg_count, new_fn->func.defaults,
- new_fn->func.arg_types, new_fn->func.ret_type_info, new_fn->func.is_varargs, 0,
- new_fn->token);
+ char *clean_type = sanitize_mangled_name(concrete_type);
+ char *mangled = xmalloc(strlen(name) + strlen(clean_type) + 2);
+ sprintf(mangled, "%s_%s", name, clean_type);
+ free(clean_type);
- add_instantiated_func(ctx, new_fn);
+ if (find_func(ctx, mangled)) {
return mangled;
-}
-
-char *process_fstring(ParserContext *ctx, const char *content, char ***used_syms, int *count)
-{
- (void)ctx; // suppress unused parameter warning
- char *gen = xmalloc(4096);
-
- strcpy(gen, "({ static char _b[1024]; _b[0]=0; char _t[128]; ");
-
- char *s = xstrdup(content);
- char *cur = s;
-
- while (*cur)
- {
- char *brace = cur;
- while (*brace && *brace != '{')
- {
- brace++;
- }
-
- if (brace > cur)
- {
- char tmp = *brace;
- *brace = 0;
- strcat(gen, "strcat(_b, \"");
- strcat(gen, cur);
- strcat(gen, "\"); ");
- *brace = tmp;
- }
-
- if (*brace == 0)
- {
- break;
- }
-
- char *p = brace + 1;
- char *colon = NULL;
- int depth = 1;
-
- while (*p && depth > 0)
- {
- if (*p == '{')
- {
- depth++;
- }
- if (*p == '}')
- {
- depth--;
- }
- if (depth == 1 && *p == ':' && !colon)
- {
- colon = p;
- }
- if (depth == 0)
- {
- break;
- }
- p++;
- }
-
- *p = 0;
- char *expr = brace + 1;
- char *fmt = NULL;
- if (colon)
- {
- *colon = 0;
- fmt = colon + 1;
- }
-
- // Analyze usage in expression
- {
- Lexer lex;
- lexer_init(&lex, expr);
- Token t;
- while ((t = lexer_next(&lex)).type != TOK_EOF)
- {
- if (t.type == TOK_IDENT)
- {
- char *name = token_strdup(t);
- Symbol *sym = find_symbol_entry(ctx, name);
- if (sym)
- {
- sym->is_used = 1;
- }
-
- if (used_syms && count)
- {
- *used_syms = xrealloc(*used_syms, sizeof(char *) * (*count + 1));
- (*used_syms)[*count] = name;
- (*count)++;
- }
- else
- {
- free(name);
- }
- }
- }
- }
+ }
- if (fmt)
- {
- strcat(gen, "sprintf(_t, \"%");
- strcat(gen, fmt);
- strcat(gen, "\", ");
- strcat(gen, expr);
- strcat(gen, "); strcat(_b, _t); ");
- }
- else
- {
- strcat(gen, "sprintf(_t, _z_str(");
- strcat(gen, expr);
- strcat(gen, "), ");
- strcat(gen, expr);
- strcat(gen, "); strcat(_b, _t); ");
- }
+ ASTNode *new_fn = copy_ast_replacing(tpl->func_node, tpl->generic_param,
+ concrete_type, NULL, NULL);
+ if (!new_fn || new_fn->type != NODE_FUNCTION) {
+ return NULL;
+ }
+ free(new_fn->func.name);
+ new_fn->func.name = xstrdup(mangled);
- cur = p + 1;
- }
+ register_func(ctx, mangled, new_fn->func.arg_count, new_fn->func.defaults,
+ new_fn->func.arg_types, new_fn->func.ret_type_info,
+ new_fn->func.is_varargs, 0, new_fn->token);
- strcat(gen, "_b; })");
- free(s);
- return gen;
+ add_instantiated_func(ctx, new_fn);
+ return mangled;
}
-void register_impl(ParserContext *ctx, const char *trait, const char *strct)
-{
- ImplReg *r = xmalloc(sizeof(ImplReg));
- r->trait = xstrdup(trait);
- r->strct = xstrdup(strct);
- r->next = ctx->registered_impls;
- ctx->registered_impls = r;
-}
+char *process_fstring(ParserContext *ctx, const char *content,
+ char ***used_syms, int *count) {
+ (void)ctx; // suppress unused parameter warning
+ char *gen = xmalloc(4096);
-int check_impl(ParserContext *ctx, const char *trait, const char *strct)
-{
- ImplReg *r = ctx->registered_impls;
- while (r)
- {
- if (strcmp(r->trait, trait) == 0 && strcmp(r->strct, strct) == 0)
- {
- return 1;
- }
- r = r->next;
- }
- return 0;
-}
+ strcpy(gen, "({ static char _b[1024]; _b[0]=0; char _t[128]; ");
-void register_template(ParserContext *ctx, const char *name, ASTNode *node)
-{
- GenericTemplate *t = xmalloc(sizeof(GenericTemplate));
- t->name = xstrdup(name);
- t->struct_node = node;
- t->next = ctx->templates;
- ctx->templates = t;
-}
+ char *s = xstrdup(content);
+ char *cur = s;
-ASTNode *copy_fields_replacing(ParserContext *ctx, ASTNode *fields, const char *param,
- const char *concrete)
-{
- if (!fields)
- {
- return NULL;
+ while (*cur) {
+ char *brace = cur;
+ while (*brace && *brace != '{') {
+ brace++;
}
- ASTNode *n = ast_create(NODE_FIELD);
- n->field.name = xstrdup(fields->field.name);
-
- // Replace strings
- n->field.type = replace_type_str(fields->field.type, param, concrete, NULL, NULL);
- // Replace formal types (Deep Copy)
- n->type_info = replace_type_formal(fields->type_info, param, concrete, NULL, NULL);
-
- if (n->field.type && strchr(n->field.type, '_'))
- {
- // Parse potential generic: e.g. "MapEntry_int" -> instantiate("MapEntry",
- // "int")
- char *underscore = strrchr(n->field.type, '_');
- if (underscore && underscore > n->field.type)
- {
- // Remove trailing '*' if present
- char *type_copy = xstrdup(n->field.type);
- char *star = strchr(type_copy, '*');
- if (star)
- {
- *star = '\0';
- }
-
- underscore = strrchr(type_copy, '_');
- if (underscore)
- {
- *underscore = '\0';
- char *template_name = type_copy;
- char *concrete_arg = underscore + 1;
-
- // Check if this is actually a known generic template
- GenericTemplate *gt = ctx->templates;
- int found = 0;
- while (gt)
- {
- if (strcmp(gt->name, template_name) == 0)
- {
- found = 1;
- break;
- }
- gt = gt->next;
- }
-
- if (found)
- {
- instantiate_generic(ctx, template_name, concrete_arg, fields->token);
- }
- }
- free(type_copy);
- }
+ if (brace > cur) {
+ char tmp = *brace;
+ *brace = 0;
+ strcat(gen, "strcat(_b, \"");
+ strcat(gen, cur);
+ strcat(gen, "\"); ");
+ *brace = tmp;
}
- n->next = copy_fields_replacing(ctx, fields->next, param, concrete);
- return n;
-}
-
-void instantiate_methods(ParserContext *ctx, GenericImplTemplate *it,
- const char *mangled_struct_name, const char *arg)
-{
- if (check_impl(ctx, "Methods", mangled_struct_name))
- {
- return; // Simple dedupe check
+ if (*brace == 0) {
+ break;
}
- ASTNode *backup_next = it->impl_node->next;
- it->impl_node->next = NULL; // Break link to isolate node
- ASTNode *new_impl = copy_ast_replacing(it->impl_node, it->generic_param, arg, it->struct_name,
- mangled_struct_name);
- it->impl_node->next = backup_next; // Restore
-
- new_impl->impl.struct_name = xstrdup(mangled_struct_name);
- ASTNode *meth = new_impl->impl.methods;
- while (meth)
- {
- char *suffix = strchr(meth->func.name, '_');
- if (suffix)
- {
- char *new_name = xmalloc(strlen(mangled_struct_name) + strlen(suffix) + 1);
- sprintf(new_name, "%s%s", mangled_struct_name, suffix);
- free(meth->func.name);
- meth->func.name = new_name;
- register_func(ctx, new_name, meth->func.arg_count, meth->func.defaults,
- meth->func.arg_types, meth->func.ret_type_info, meth->func.is_varargs, 0,
- meth->token);
- }
-
- // Handle generic return types in methods (e.g., Option<T> -> Option_int)
- if (meth->func.ret_type && strchr(meth->func.ret_type, '_'))
- {
- char *ret_copy = xstrdup(meth->func.ret_type);
- char *underscore = strrchr(ret_copy, '_');
- if (underscore && underscore > ret_copy)
- {
- *underscore = '\0';
- char *template_name = ret_copy;
-
- // Check if this looks like a generic (e.g., "Option_V" or "Result_V")
- GenericTemplate *gt = ctx->templates;
- while (gt)
- {
- if (strcmp(gt->name, template_name) == 0)
- {
- // Found matching template, instantiate it
- instantiate_generic(ctx, template_name, arg, meth->token);
- break;
- }
- gt = gt->next;
- }
- }
- free(ret_copy);
- }
-
- meth = meth->next;
- }
- add_instantiated_func(ctx, new_impl);
-}
+ char *p = brace + 1;
+ char *colon = NULL;
+ int depth = 1;
-void instantiate_generic(ParserContext *ctx, const char *tpl, const char *arg, Token token)
-{
- // Ignore generic placeholders
- if (strlen(arg) == 1 && isupper(arg[0]))
- {
- return;
- }
- if (strcmp(arg, "T") == 0)
- {
- return;
- }
-
- char *clean_arg = sanitize_mangled_name(arg);
- char m[256];
- sprintf(m, "%s_%s", tpl, clean_arg);
- free(clean_arg);
-
- Instantiation *c = ctx->instantiations;
- while (c)
- {
- if (strcmp(c->name, m) == 0)
- {
- return; // Already instantiated, DO NOTHING.
- }
- c = c->next;
+ while (*p && depth > 0) {
+ if (*p == '{') {
+ depth++;
+ }
+ if (*p == '}') {
+ depth--;
+ }
+ if (depth == 1 && *p == ':' && !colon) {
+ colon = p;
+ }
+ if (depth == 0) {
+ break;
+ }
+ p++;
}
- GenericTemplate *t = ctx->templates;
- while (t)
- {
- if (strcmp(t->name, tpl) == 0)
- {
+ *p = 0;
+ char *expr = brace + 1;
+ char *fmt = NULL;
+ if (colon) {
+ *colon = 0;
+ fmt = colon + 1;
+ }
+
+ // Analyze usage in expression
+ {
+ Lexer lex;
+ lexer_init(&lex, expr);
+ Token t;
+ while ((t = lexer_next(&lex)).type != TOK_EOF) {
+ if (t.type == TOK_IDENT) {
+ char *name = token_strdup(t);
+ Symbol *sym = find_symbol_entry(ctx, name);
+ if (sym) {
+ sym->is_used = 1;
+ }
+
+ if (used_syms && count) {
+ *used_syms = xrealloc(*used_syms, sizeof(char *) * (*count + 1));
+ (*used_syms)[*count] = name;
+ (*count)++;
+ } else {
+ free(name);
+ }
+ }
+ }
+ }
+
+ if (fmt) {
+ strcat(gen, "sprintf(_t, \"%");
+ strcat(gen, fmt);
+ strcat(gen, "\", ");
+ strcat(gen, expr);
+ strcat(gen, "); strcat(_b, _t); ");
+ } else {
+ strcat(gen, "sprintf(_t, _z_str(");
+ strcat(gen, expr);
+ strcat(gen, "), ");
+ strcat(gen, expr);
+ strcat(gen, "); strcat(_b, _t); ");
+ }
+
+ cur = p + 1;
+ }
+
+ strcat(gen, "_b; })");
+ free(s);
+ return gen;
+}
+
+void register_impl(ParserContext *ctx, const char *trait, const char *strct) {
+ ImplReg *r = xmalloc(sizeof(ImplReg));
+ r->trait = xstrdup(trait);
+ r->strct = xstrdup(strct);
+ r->next = ctx->registered_impls;
+ ctx->registered_impls = r;
+}
+
+int check_impl(ParserContext *ctx, const char *trait, const char *strct) {
+ ImplReg *r = ctx->registered_impls;
+ while (r) {
+ if (strcmp(r->trait, trait) == 0 && strcmp(r->strct, strct) == 0) {
+ return 1;
+ }
+ r = r->next;
+ }
+ return 0;
+}
+
+void register_template(ParserContext *ctx, const char *name, ASTNode *node) {
+ GenericTemplate *t = xmalloc(sizeof(GenericTemplate));
+ t->name = xstrdup(name);
+ t->struct_node = node;
+ t->next = ctx->templates;
+ ctx->templates = t;
+}
+
+ASTNode *copy_fields_replacing(ParserContext *ctx, ASTNode *fields,
+ const char *param, const char *concrete) {
+ if (!fields) {
+ return NULL;
+ }
+ ASTNode *n = ast_create(NODE_FIELD);
+ n->field.name = xstrdup(fields->field.name);
+
+ // Replace strings
+ n->field.type =
+ replace_type_str(fields->field.type, param, concrete, NULL, NULL);
+
+ // Replace formal types (Deep Copy)
+ n->type_info =
+ replace_type_formal(fields->type_info, param, concrete, NULL, NULL);
+
+ if (n->field.type && strchr(n->field.type, '_')) {
+ // Parse potential generic: e.g. "MapEntry_int" -> instantiate("MapEntry",
+ // "int")
+ char *underscore = strrchr(n->field.type, '_');
+ if (underscore && underscore > n->field.type) {
+ // Remove trailing '*' if present
+ char *type_copy = xstrdup(n->field.type);
+ char *star = strchr(type_copy, '*');
+ if (star) {
+ *star = '\0';
+ }
+
+ underscore = strrchr(type_copy, '_');
+ if (underscore) {
+ *underscore = '\0';
+ char *template_name = type_copy;
+ char *concrete_arg = underscore + 1;
+
+ // Check if this is actually a known generic template
+ GenericTemplate *gt = ctx->templates;
+ int found = 0;
+ while (gt) {
+ if (strcmp(gt->name, template_name) == 0) {
+ found = 1;
break;
+ }
+ gt = gt->next;
}
- t = t->next;
- }
- if (!t)
- {
- zpanic_at(token, "Unknown generic: %s", tpl);
- }
-
- Instantiation *ni = xmalloc(sizeof(Instantiation));
- ni->name = xstrdup(m);
- ni->template_name = xstrdup(tpl);
- ni->concrete_arg = xstrdup(arg);
- ni->struct_node = NULL; // Placeholder to break cycles
- ni->next = ctx->instantiations;
- ctx->instantiations = ni;
- ASTNode *struct_node_copy = NULL;
-
- if (t->struct_node->type == NODE_STRUCT)
- {
- ASTNode *i = ast_create(NODE_STRUCT);
- i->strct.name = xstrdup(m);
- i->strct.is_template = 0;
- i->strct.fields = copy_fields_replacing(ctx, t->struct_node->strct.fields,
- t->struct_node->strct.generic_param, arg);
- struct_node_copy = i;
- register_struct_def(ctx, m, i);
- }
- else if (t->struct_node->type == NODE_ENUM)
- {
- ASTNode *i = ast_create(NODE_ENUM);
- i->enm.name = xstrdup(m);
- i->enm.is_template = 0;
- ASTNode *h = 0, *tl = 0;
- ASTNode *v = t->struct_node->enm.variants;
- while (v)
- {
- ASTNode *nv = ast_create(NODE_ENUM_VARIANT);
- nv->variant.name = xstrdup(v->variant.name);
- nv->variant.tag_id = v->variant.tag_id;
- nv->variant.payload = replace_type_formal(
- v->variant.payload, t->struct_node->enm.generic_param, arg, NULL, NULL);
- char mangled_var[512];
- sprintf(mangled_var, "%s_%s", m, nv->variant.name);
- register_enum_variant(ctx, m, mangled_var, nv->variant.tag_id);
- if (!h)
- {
- h = nv;
- }
- else
- {
- tl->next = nv;
- }
- tl = nv;
- v = v->next;
+ if (found) {
+ instantiate_generic(ctx, template_name, concrete_arg, fields->token);
}
- i->enm.variants = h;
- struct_node_copy = i;
- }
-
- ni->struct_node = struct_node_copy;
-
- if (struct_node_copy)
- {
- struct_node_copy->next = ctx->instantiated_structs;
- ctx->instantiated_structs = struct_node_copy;
+ }
+ free(type_copy);
}
+ }
- GenericImplTemplate *it = ctx->impl_templates;
- while (it)
- {
- if (strcmp(it->struct_name, tpl) == 0)
- {
- instantiate_methods(ctx, it, m, arg);
- }
- it = it->next;
- }
+ n->next = copy_fields_replacing(ctx, fields->next, param, concrete);
+ return n;
}
-int is_file_imported(ParserContext *ctx, const char *p)
-{
- ImportedFile *c = ctx->imported_files;
- while (c)
- {
- if (strcmp(c->path, p) == 0)
- {
- return 1;
- }
- c = c->next;
+void instantiate_methods(ParserContext *ctx, GenericImplTemplate *it,
+ const char *mangled_struct_name, const char *arg) {
+ if (check_impl(ctx, "Methods", mangled_struct_name)) {
+ return; // Simple dedupe check
+ }
+
+ ASTNode *backup_next = it->impl_node->next;
+ it->impl_node->next = NULL; // Break link to isolate node
+ ASTNode *new_impl = copy_ast_replacing(it->impl_node, it->generic_param, arg,
+ it->struct_name, mangled_struct_name);
+ it->impl_node->next = backup_next; // Restore
+
+ new_impl->impl.struct_name = xstrdup(mangled_struct_name);
+ ASTNode *meth = new_impl->impl.methods;
+ while (meth) {
+ char *suffix = strchr(meth->func.name, '_');
+ if (suffix) {
+ char *new_name =
+ xmalloc(strlen(mangled_struct_name) + strlen(suffix) + 1);
+ sprintf(new_name, "%s%s", mangled_struct_name, suffix);
+ free(meth->func.name);
+ meth->func.name = new_name;
+ register_func(ctx, new_name, meth->func.arg_count, meth->func.defaults,
+ meth->func.arg_types, meth->func.ret_type_info,
+ meth->func.is_varargs, 0, meth->token);
+ }
+
+ // Handle generic return types in methods (e.g., Option<T> -> Option_int)
+ if (meth->func.ret_type && strchr(meth->func.ret_type, '_')) {
+ char *ret_copy = xstrdup(meth->func.ret_type);
+ char *underscore = strrchr(ret_copy, '_');
+ if (underscore && underscore > ret_copy) {
+ *underscore = '\0';
+ char *template_name = ret_copy;
+
+ // Check if this looks like a generic (e.g., "Option_V" or "Result_V")
+ GenericTemplate *gt = ctx->templates;
+ while (gt) {
+ if (strcmp(gt->name, template_name) == 0) {
+ // Found matching template, instantiate it
+ instantiate_generic(ctx, template_name, arg, meth->token);
+ break;
+ }
+ gt = gt->next;
+ }
+ }
+ free(ret_copy);
+ }
+
+ meth = meth->next;
+ }
+ add_instantiated_func(ctx, new_impl);
+}
+
+void instantiate_generic(ParserContext *ctx, const char *tpl, const char *arg,
+ Token token) {
+ // Ignore generic placeholders
+ if (strlen(arg) == 1 && isupper(arg[0])) {
+ return;
+ }
+ if (strcmp(arg, "T") == 0) {
+ return;
+ }
+
+ char *clean_arg = sanitize_mangled_name(arg);
+ char m[256];
+ sprintf(m, "%s_%s", tpl, clean_arg);
+ free(clean_arg);
+
+ Instantiation *c = ctx->instantiations;
+ while (c) {
+ if (strcmp(c->name, m) == 0) {
+ return; // Already instantiated, DO NOTHING.
+ }
+ c = c->next;
+ }
+
+ GenericTemplate *t = ctx->templates;
+ while (t) {
+ if (strcmp(t->name, tpl) == 0) {
+ break;
+ }
+ t = t->next;
+ }
+ if (!t) {
+ zpanic_at(token, "Unknown generic: %s", tpl);
+ }
+
+ Instantiation *ni = xmalloc(sizeof(Instantiation));
+ ni->name = xstrdup(m);
+ ni->template_name = xstrdup(tpl);
+ ni->concrete_arg = xstrdup(arg);
+ ni->struct_node = NULL; // Placeholder to break cycles
+ ni->next = ctx->instantiations;
+ ctx->instantiations = ni;
+
+ ASTNode *struct_node_copy = NULL;
+
+ if (t->struct_node->type == NODE_STRUCT) {
+ ASTNode *i = ast_create(NODE_STRUCT);
+ i->strct.name = xstrdup(m);
+ i->strct.is_template = 0;
+ i->strct.fields =
+ copy_fields_replacing(ctx, t->struct_node->strct.fields,
+ t->struct_node->strct.generic_param, arg);
+ struct_node_copy = i;
+ register_struct_def(ctx, m, i);
+ } else if (t->struct_node->type == NODE_ENUM) {
+ ASTNode *i = ast_create(NODE_ENUM);
+ i->enm.name = xstrdup(m);
+ i->enm.is_template = 0;
+ ASTNode *h = 0, *tl = 0;
+ ASTNode *v = t->struct_node->enm.variants;
+ while (v) {
+ ASTNode *nv = ast_create(NODE_ENUM_VARIANT);
+ nv->variant.name = xstrdup(v->variant.name);
+ nv->variant.tag_id = v->variant.tag_id;
+ nv->variant.payload = replace_type_formal(
+ v->variant.payload, t->struct_node->enm.generic_param, arg, NULL,
+ NULL);
+ char mangled_var[512];
+ sprintf(mangled_var, "%s_%s", m, nv->variant.name);
+ register_enum_variant(ctx, m, mangled_var, nv->variant.tag_id);
+ if (!h) {
+ h = nv;
+ } else {
+ tl->next = nv;
+ }
+ tl = nv;
+ v = v->next;
+ }
+ i->enm.variants = h;
+ struct_node_copy = i;
+ }
+
+ ni->struct_node = struct_node_copy;
+
+ if (struct_node_copy) {
+ struct_node_copy->next = ctx->instantiated_structs;
+ ctx->instantiated_structs = struct_node_copy;
+ }
+
+ GenericImplTemplate *it = ctx->impl_templates;
+ while (it) {
+ if (strcmp(it->struct_name, tpl) == 0) {
+ instantiate_methods(ctx, it, m, arg);
+ }
+ it = it->next;
+ }
+}
+
+int is_file_imported(ParserContext *ctx, const char *p) {
+ ImportedFile *c = ctx->imported_files;
+ while (c) {
+ if (strcmp(c->path, p) == 0) {
+ return 1;
+ }
+ c = c->next;
+ }
+ return 0;
+}
+
+void mark_file_imported(ParserContext *ctx, const char *p) {
+ ImportedFile *f = xmalloc(sizeof(ImportedFile));
+ f->path = xstrdup(p);
+ f->next = ctx->imported_files;
+ ctx->imported_files = f;
+}
+
+char *parse_condition_raw(ParserContext *ctx, Lexer *l) {
+ (void)ctx; // suppress unused parameter warning
+ Token t = lexer_peek(l);
+ if (t.type == TOK_LPAREN) {
+ Token op = lexer_next(l);
+ const char *s = op.start;
+ int d = 1;
+ while (d > 0) {
+ t = lexer_next(l);
+ if (t.type == TOK_EOF) {
+ zpanic_at(t, "Unterminated condition");
+ }
+ if (t.type == TOK_LPAREN) {
+ d++;
+ }
+ if (t.type == TOK_RPAREN) {
+ d--;
+ }
+ }
+ const char *cs = s + 1;
+ int len = t.start - cs;
+ char *c = xmalloc(len + 1);
+ strncpy(c, cs, len);
+ c[len] = 0;
+ return c;
+ } else {
+ const char *start = l->src + l->pos;
+ while (1) {
+ t = lexer_peek(l);
+ if (t.type == TOK_LBRACE || t.type == TOK_EOF) {
+ break;
+ }
+ lexer_next(l);
}
- return 0;
-}
-
-void mark_file_imported(ParserContext *ctx, const char *p)
-{
- ImportedFile *f = xmalloc(sizeof(ImportedFile));
- f->path = xstrdup(p);
- f->next = ctx->imported_files;
- ctx->imported_files = f;
-}
-
-char *parse_condition_raw(ParserContext *ctx, Lexer *l)
-{
- (void)ctx; // suppress unused parameter warning
- Token t = lexer_peek(l);
- if (t.type == TOK_LPAREN)
- {
- Token op = lexer_next(l);
- const char *s = op.start;
- int d = 1;
- while (d > 0)
- {
- t = lexer_next(l);
- if (t.type == TOK_EOF)
- {
- zpanic_at(t, "Unterminated condition");
- }
- if (t.type == TOK_LPAREN)
- {
- d++;
- }
- if (t.type == TOK_RPAREN)
- {
- d--;
- }
- }
- const char *cs = s + 1;
- int len = t.start - cs;
- char *c = xmalloc(len + 1);
- strncpy(c, cs, len);
- c[len] = 0;
- return c;
- }
- else
- {
- const char *start = l->src + l->pos;
- while (1)
- {
- t = lexer_peek(l);
- if (t.type == TOK_LBRACE || t.type == TOK_EOF)
- {
- break;
- }
- lexer_next(l);
- }
- int len = (l->src + l->pos) - start;
- if (len == 0)
- {
- zpanic_at(lexer_peek(l), "Empty condition or missing body");
- }
- char *c = xmalloc(len + 1);
- strncpy(c, start, len);
- c[len] = 0;
- return c;
+ int len = (l->src + l->pos) - start;
+ if (len == 0) {
+ zpanic_at(lexer_peek(l), "Empty condition or missing body");
}
+ char *c = xmalloc(len + 1);
+ strncpy(c, start, len);
+ c[len] = 0;
+ return c;
+ }
}
-char *rewrite_expr_methods(ParserContext *ctx, char *raw)
-{
- if (!raw)
- {
- return NULL;
- }
-
- int in_expr = 0;
- char *result = xmalloc(strlen(raw) * 4 + 100);
- char *dest = result;
- char *src = raw;
-
- while (*src)
- {
- if (strncmp(src, "#{", 2) == 0)
- {
- in_expr = 1;
- src += 2;
- *dest++ = '(';
- continue;
- }
-
- if (in_expr && *src == '}')
- {
- in_expr = 0;
- *dest++ = ')';
+char *rewrite_expr_methods(ParserContext *ctx, char *raw) {
+ if (!raw) {
+ return NULL;
+ }
+
+ int in_expr = 0;
+ char *result = xmalloc(strlen(raw) * 4 + 100);
+ char *dest = result;
+ char *src = raw;
+
+ while (*src) {
+ if (strncmp(src, "#{", 2) == 0) {
+ in_expr = 1;
+ src += 2;
+ *dest++ = '(';
+ continue;
+ }
+
+ if (in_expr && *src == '}') {
+ in_expr = 0;
+ *dest++ = ')';
+ src++;
+ continue;
+ }
+
+ if (in_expr && *src == '.') {
+ char acc[64];
+ int i = 0;
+ char *back = src - 1;
+ while (back >= raw && (isalnum(*back) || *back == '_')) {
+ back--;
+ }
+ back++;
+ while (back < src && i < 63) {
+ acc[i++] = *back++;
+ }
+ acc[i] = 0;
+
+ char *vtype = find_symbol_type(ctx, acc);
+ if (!vtype) {
+ *dest++ = *src++;
+ continue;
+ }
+
+ char method[64];
+ i = 0;
+ src++;
+ while (isalnum(*src) || *src == '_') {
+ method[i++] = *src++;
+ }
+ method[i] = 0;
+
+ // Check for field access
+ char *base_t = xstrdup(vtype);
+ char *pc = strchr(base_t, '*');
+ int is_ptr_type = (pc != NULL);
+ if (pc) {
+ *pc = 0;
+ }
+
+ ASTNode *def = find_struct_def(ctx, base_t);
+ int is_field = 0;
+ if (def && (def->type == NODE_STRUCT)) {
+ ASTNode *f = def->strct.fields;
+ while (f) {
+ if (strcmp(f->field.name, method) == 0) {
+ is_field = 1;
+ break;
+ }
+ f = f->next;
+ }
+ }
+ free(base_t);
+
+ if (is_field) {
+ dest -= strlen(acc);
+ if (is_ptr_type) {
+ dest += sprintf(dest, "(%s)->%s", acc, method);
+ } else {
+ dest += sprintf(dest, "(%s).%s", acc, method);
+ }
+ continue;
+ }
+
+ if (*src == '(') {
+ dest -= strlen(acc);
+ int paren_depth = 0;
+ src++;
+ paren_depth++;
+
+ char ptr_check[64];
+ strcpy(ptr_check, vtype);
+ int is_ptr = (strchr(ptr_check, '*') != NULL);
+ if (is_ptr) {
+ char *p = strchr(ptr_check, '*');
+ if (p) {
+ *p = 0;
+ }
+ }
+
+ dest += sprintf(dest, "%s_%s(%s%s", ptr_check, method,
+ is_ptr ? "" : "&", acc);
+
+ int has_args = 0;
+ while (*src && paren_depth > 0) {
+ if (!isspace(*src)) {
+ has_args = 1;
+ }
+ if (*src == '(') {
+ paren_depth++;
+ }
+ if (*src == ')') {
+ paren_depth--;
+ }
+ if (paren_depth == 0) {
+ break;
+ }
+ *dest++ = *src++;
+ }
+
+ if (has_args) {
+ *dest++ = ')';
+ } else {
+ *dest++ = ')';
+ }
+
+ src++;
+ continue;
+ } else {
+ dest -= strlen(acc);
+ char ptr_check[64];
+ strcpy(ptr_check, vtype);
+ int is_ptr = (strchr(ptr_check, '*') != NULL);
+ if (is_ptr) {
+ char *p = strchr(ptr_check, '*');
+ if (p) {
+ *p = 0;
+ }
+ }
+ dest += sprintf(dest, "%s_%s(%s%s)", ptr_check, method,
+ is_ptr ? "" : "&", acc);
+ continue;
+ }
+ }
+
+ if (!in_expr && strncmp(src, "::", 2) == 0) {
+ char acc[64];
+ int i = 0;
+ char *back = src - 1;
+ while (back >= raw && (isalnum(*back) || *back == '_')) {
+ back--;
+ }
+ back++;
+ while (back < src && i < 63) {
+ acc[i++] = *back++;
+ }
+ acc[i] = 0;
+
+ src += 2;
+ char field[64];
+ i = 0;
+ while (isalnum(*src) || *src == '_') {
+ field[i++] = *src++;
+ }
+ field[i] = 0;
+
+ dest -= strlen(acc);
+
+ Module *mod = find_module(ctx, acc);
+ if (mod && mod->is_c_header) {
+ dest += sprintf(dest, "%s", field);
+ } else {
+ dest += sprintf(dest, "%s_%s", acc, field);
+ }
+ continue;
+ }
+
+ if (in_expr && isalpha(*src)) {
+ char tok[128];
+ int i = 0;
+ while ((isalnum(*src) || *src == '_') && i < 127) {
+ tok[i++] = *src++;
+ }
+ tok[i] = 0;
+
+ while (*src == ' ' || *src == '\t') {
+ src++;
+ }
+
+ if (strncmp(src, "::", 2) == 0) {
+ src += 2;
+ char func_name[128];
+ snprintf(func_name, sizeof(func_name), "%s", tok);
+ char method[64];
+ i = 0;
+ while (isalnum(*src) || *src == '_') {
+ method[i++] = *src++;
+ }
+ method[i] = 0;
+
+ while (*src == ' ' || *src == '\t') {
+ src++;
+ }
+
+ if (*src == '(') {
+ src++;
+
+ char mangled[256];
+ snprintf(mangled, sizeof(mangled), "%s_%s", func_name, method);
+
+ if (*src == ')') {
+ dest += sprintf(dest, "%s()", mangled);
src++;
- continue;
- }
-
- if (in_expr && *src == '.')
- {
- char acc[64];
- int i = 0;
- char *back = src - 1;
- while (back >= raw && (isalnum(*back) || *back == '_'))
- {
- back--;
- }
- back++;
- while (back < src && i < 63)
- {
- acc[i++] = *back++;
- }
- acc[i] = 0;
-
- char *vtype = find_symbol_type(ctx, acc);
- if (!vtype)
- {
+ } else {
+ FuncSig *sig = find_func(ctx, func_name);
+ if (sig) {
+ dest += sprintf(dest, "%s(&(%s){0}", mangled, func_name);
+ while (*src && *src != ')') {
*dest++ = *src++;
- continue;
- }
-
- char method[64];
- i = 0;
- src++;
- while (isalnum(*src) || *src == '_')
- {
- method[i++] = *src++;
- }
- method[i] = 0;
-
- // Check for field access
- char *base_t = xstrdup(vtype);
- char *pc = strchr(base_t, '*');
- int is_ptr_type = (pc != NULL);
- if (pc)
- {
- *pc = 0;
- }
-
- ASTNode *def = find_struct_def(ctx, base_t);
- int is_field = 0;
- if (def && (def->type == NODE_STRUCT))
- {
- ASTNode *f = def->strct.fields;
- while (f)
- {
- if (strcmp(f->field.name, method) == 0)
- {
- is_field = 1;
- break;
- }
- f = f->next;
- }
- }
- free(base_t);
-
- if (is_field)
- {
- dest -= strlen(acc);
- if (is_ptr_type)
- {
- dest += sprintf(dest, "(%s)->%s", acc, method);
- }
- else
- {
- dest += sprintf(dest, "(%s).%s", acc, method);
- }
- continue;
- }
-
- if (*src == '(')
- {
- dest -= strlen(acc);
- int paren_depth = 0;
+ }
+ *dest++ = ')';
+ if (*src == ')') {
src++;
- paren_depth++;
-
- char ptr_check[64];
- strcpy(ptr_check, vtype);
- int is_ptr = (strchr(ptr_check, '*') != NULL);
- if (is_ptr)
- {
- char *p = strchr(ptr_check, '*');
- if (p)
- {
- *p = 0;
- }
- }
-
- dest += sprintf(dest, "%s_%s(%s%s", ptr_check, method, is_ptr ? "" : "&", acc);
-
- int has_args = 0;
- while (*src && paren_depth > 0)
- {
- if (!isspace(*src))
- {
- has_args = 1;
- }
- if (*src == '(')
- {
- paren_depth++;
- }
- if (*src == ')')
- {
- paren_depth--;
- }
- if (paren_depth == 0)
- {
- break;
- }
- *dest++ = *src++;
- }
-
- if (has_args)
- {
- *dest++ = ')';
- }
- else
- {
- *dest++ = ')';
- }
-
- src++;
- continue;
- }
- else
- {
- dest -= strlen(acc);
- char ptr_check[64];
- strcpy(ptr_check, vtype);
- int is_ptr = (strchr(ptr_check, '*') != NULL);
- if (is_ptr)
- {
- char *p = strchr(ptr_check, '*');
- if (p)
- {
- *p = 0;
- }
- }
- dest += sprintf(dest, "%s_%s(%s%s)", ptr_check, method, is_ptr ? "" : "&", acc);
- continue;
- }
- }
-
- if (!in_expr && strncmp(src, "::", 2) == 0)
- {
- char acc[64];
- int i = 0;
- char *back = src - 1;
- while (back >= raw && (isalnum(*back) || *back == '_'))
- {
- back--;
- }
- back++;
- while (back < src && i < 63)
- {
- acc[i++] = *back++;
- }
- acc[i] = 0;
-
- src += 2;
- char field[64];
- i = 0;
- while (isalnum(*src) || *src == '_')
- {
- field[i++] = *src++;
- }
- field[i] = 0;
-
- dest -= strlen(acc);
-
- Module *mod = find_module(ctx, acc);
- if (mod && mod->is_c_header)
- {
- dest += sprintf(dest, "%s", field);
- }
- else
- {
- dest += sprintf(dest, "%s_%s", acc, field);
- }
- continue;
- }
-
- if (in_expr && isalpha(*src))
- {
- char tok[128];
- int i = 0;
- while ((isalnum(*src) || *src == '_') && i < 127)
- {
- tok[i++] = *src++;
- }
- tok[i] = 0;
-
- while (*src == ' ' || *src == '\t')
- {
+ }
+ } else {
+ dest += sprintf(dest, "%s(", mangled);
+ while (*src && *src != ')') {
+ *dest++ = *src++;
+ }
+ *dest++ = ')';
+ if (*src == ')') {
src++;
+ }
}
-
- if (strncmp(src, "::", 2) == 0)
- {
- src += 2;
- char func_name[128];
- snprintf(func_name, sizeof(func_name), "%s", tok);
- char method[64];
- i = 0;
- while (isalnum(*src) || *src == '_')
- {
- method[i++] = *src++;
- }
- method[i] = 0;
-
- while (*src == ' ' || *src == '\t')
- {
- src++;
- }
-
- if (*src == '(')
- {
- src++;
-
- char mangled[256];
- snprintf(mangled, sizeof(mangled), "%s_%s", func_name, method);
-
- if (*src == ')')
- {
- dest += sprintf(dest, "%s()", mangled);
- src++;
- }
- else
- {
- FuncSig *sig = find_func(ctx, func_name);
- if (sig)
- {
- dest += sprintf(dest, "%s(&(%s){0}", mangled, func_name);
- while (*src && *src != ')')
- {
- *dest++ = *src++;
- }
- *dest++ = ')';
- if (*src == ')')
- {
- src++;
- }
- }
- else
- {
- dest += sprintf(dest, "%s(", mangled);
- while (*src && *src != ')')
- {
- *dest++ = *src++;
- }
- *dest++ = ')';
- if (*src == ')')
- {
- src++;
- }
- }
- }
- continue;
- }
- }
-
- strcpy(dest, tok);
- dest += strlen(tok);
- continue;
- }
-
- *dest++ = *src++;
- }
-
- *dest = 0;
- return result;
-}
-
-char *consume_and_rewrite(ParserContext *ctx, Lexer *l)
-{
- char *r = consume_until_semicolon(l);
- char *rw = rewrite_expr_methods(ctx, r);
- free(r);
- return rw;
-}
-
-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)
-{
- Token t = lexer_next(l);
- if (t.type != TOK_LPAREN)
- {
- zpanic_at(t, "Expected '(' in function args");
- }
-
- char *buf = xmalloc(1024);
- buf[0] = 0;
- int count = 0;
- char **defaults = xmalloc(sizeof(char *) * 16);
- Type **types = xmalloc(sizeof(Type *) * 16);
- char **names = xmalloc(sizeof(char *) * 16);
-
- for (int i = 0; i < 16; i++)
- {
- defaults[i] = NULL;
- types[i] = NULL;
- names[i] = NULL;
- }
-
- if (lexer_peek(l).type != TOK_RPAREN)
- {
- while (1)
- {
- Token t = lexer_next(l);
- // Handle 'self'
- if (t.type == TOK_IDENT && strncmp(t.start, "self", 4) == 0 && t.len == 4)
- {
- names[count] = xstrdup("self");
- if (ctx->current_impl_struct)
- {
- Type *st = NULL;
- // Check for primitives to avoid creating struct int*
- if (strcmp(ctx->current_impl_struct, "int") == 0)
- {
- st = type_new(TYPE_INT);
- }
- else if (strcmp(ctx->current_impl_struct, "float") == 0)
- {
- st = type_new(TYPE_F32);
- }
- else if (strcmp(ctx->current_impl_struct, "char") == 0)
- {
- st = type_new(TYPE_CHAR);
- }
- else if (strcmp(ctx->current_impl_struct, "bool") == 0)
- {
- st = type_new(TYPE_BOOL);
- }
- else if (strcmp(ctx->current_impl_struct, "string") == 0)
- {
- st = type_new(TYPE_STRING);
- }
- // Add other primitives as needed
- else
- {
- st = type_new(TYPE_STRUCT);
- st->name = xstrdup(ctx->current_impl_struct);
- }
- Type *pt = type_new_ptr(st);
-
- char buf_type[256];
- sprintf(buf_type, "%s*", ctx->current_impl_struct);
- // Register 'self' with actual type in symbol table
- add_symbol(ctx, "self", buf_type, pt);
-
- types[count] = pt;
-
- strcat(buf, "void* self");
- }
- else
- {
- strcat(buf, "void* self");
- types[count] = type_new_ptr(type_new(TYPE_VOID));
- add_symbol(ctx, "self", "void*", types[count]);
- }
- count++;
- }
- else
- {
- if (t.type != TOK_IDENT)
- {
- zpanic_at(lexer_peek(l), "Expected arg name");
- }
- char *name = token_strdup(t);
- names[count] = name; // Store name
- if (lexer_next(l).type != TOK_COLON)
- {
- zpanic_at(lexer_peek(l), "Expected ':'");
- }
-
- Type *arg_type = parse_type_formal(ctx, l);
- char *type_str = type_to_string(arg_type);
-
- add_symbol(ctx, name, type_str, arg_type);
- types[count] = arg_type;
-
- if (strlen(buf) > 0)
- {
- strcat(buf, ", ");
- }
-
- char *fn_ptr = strstr(type_str, "(*)");
- if (arg_type->kind == TYPE_FUNCTION)
- {
- strcat(buf, "z_closure_T ");
- strcat(buf, name);
- }
- else if (fn_ptr)
- {
- // Inject name into function pointer: int (*)(int) -> int (*name)(int)
- int prefix_len = fn_ptr - type_str;
- strncat(buf, type_str, prefix_len);
- strcat(buf, " (*");
- strcat(buf, name);
- strcat(buf, ")");
- strcat(buf, fn_ptr + 3); // Skip "(*)"
- }
- else
- {
- strcat(buf, type_str);
- strcat(buf, " ");
- strcat(buf, name);
- }
-
- count++;
-
- if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "="))
- {
- lexer_next(l);
- Token val = lexer_next(l);
- defaults[count - 1] = token_strdup(val);
- }
- }
- if (lexer_peek(l).type == TOK_COMMA)
- {
- lexer_next(l);
- // Check if next is ...
- if (lexer_peek(l).type == TOK_ELLIPSIS)
- {
- lexer_next(l);
- if (is_varargs_out)
- {
- *is_varargs_out = 1;
- }
- if (strlen(buf) > 0)
- {
- strcat(buf, ", ");
- }
- strcat(buf, "...");
- break; // Must be last
- }
- }
- else
- {
- break;
- }
- }
- }
- if (lexer_next(l).type != TOK_RPAREN)
- {
- zpanic_at(lexer_peek(l), "Expected ')' after args");
+ }
+ continue;
+ }
+ }
+
+ strcpy(dest, tok);
+ dest += strlen(tok);
+ continue;
+ }
+
+ *dest++ = *src++;
+ }
+
+ *dest = 0;
+ return result;
+}
+
+char *consume_and_rewrite(ParserContext *ctx, Lexer *l) {
+ char *r = consume_until_semicolon(l);
+ char *rw = rewrite_expr_methods(ctx, r);
+ free(r);
+ return rw;
+}
+
+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) {
+ Token t = lexer_next(l);
+ if (t.type != TOK_LPAREN) {
+ zpanic_at(t, "Expected '(' in function args");
+ }
+
+ char *buf = xmalloc(1024);
+ buf[0] = 0;
+ int count = 0;
+ char **defaults = xmalloc(sizeof(char *) * 16);
+ Type **types = xmalloc(sizeof(Type *) * 16);
+ char **names = xmalloc(sizeof(char *) * 16);
+
+ for (int i = 0; i < 16; i++) {
+ defaults[i] = NULL;
+ types[i] = NULL;
+ names[i] = NULL;
+ }
+
+ if (lexer_peek(l).type != TOK_RPAREN) {
+ while (1) {
+ Token t = lexer_next(l);
+ // Handle 'self'
+ if (t.type == TOK_IDENT && strncmp(t.start, "self", 4) == 0 &&
+ t.len == 4) {
+ names[count] = xstrdup("self");
+ if (ctx->current_impl_struct) {
+ Type *st = NULL;
+ // Check for primitives to avoid creating struct int*
+ if (strcmp(ctx->current_impl_struct, "int") == 0) {
+ st = type_new(TYPE_INT);
+ } else if (strcmp(ctx->current_impl_struct, "float") == 0) {
+ st = type_new(TYPE_F32);
+ } else if (strcmp(ctx->current_impl_struct, "char") == 0) {
+ st = type_new(TYPE_CHAR);
+ } else if (strcmp(ctx->current_impl_struct, "bool") == 0) {
+ st = type_new(TYPE_BOOL);
+ } else if (strcmp(ctx->current_impl_struct, "string") == 0) {
+ st = type_new(TYPE_STRING);
+ }
+ // Add other primitives as needed
+ else {
+ st = type_new(TYPE_STRUCT);
+ st->name = xstrdup(ctx->current_impl_struct);
+ }
+ Type *pt = type_new_ptr(st);
+
+ char buf_type[256];
+ sprintf(buf_type, "%s*", ctx->current_impl_struct);
+ // Register 'self' with actual type in symbol table
+ add_symbol(ctx, "self", buf_type, pt);
+
+ types[count] = pt;
+
+ strcat(buf, "void* self");
+ } else {
+ strcat(buf, "void* self");
+ types[count] = type_new_ptr(type_new(TYPE_VOID));
+ add_symbol(ctx, "self", "void*", types[count]);
+ }
+ count++;
+ } else {
+ if (t.type != TOK_IDENT) {
+ zpanic_at(lexer_peek(l), "Expected arg name");
+ }
+ char *name = token_strdup(t);
+ names[count] = name; // Store name
+ if (lexer_next(l).type != TOK_COLON) {
+ zpanic_at(lexer_peek(l), "Expected ':'");
+ }
+
+ Type *arg_type = parse_type_formal(ctx, l);
+ char *type_str = type_to_string(arg_type);
+
+ add_symbol(ctx, name, type_str, arg_type);
+ types[count] = arg_type;
+
+ if (strlen(buf) > 0) {
+ strcat(buf, ", ");
+ }
+
+ char *fn_ptr = strstr(type_str, "(*)");
+ if (arg_type->kind == TYPE_FUNCTION) {
+ strcat(buf, "z_closure_T ");
+ strcat(buf, name);
+ } else if (fn_ptr) {
+ // Inject name into function pointer: int (*)(int) -> int (*name)(int)
+ int prefix_len = fn_ptr - type_str;
+ strncat(buf, type_str, prefix_len);
+ strcat(buf, " (*");
+ strcat(buf, name);
+ strcat(buf, ")");
+ strcat(buf, fn_ptr + 3); // Skip "(*)"
+ } else {
+ strcat(buf, type_str);
+ strcat(buf, " ");
+ strcat(buf, name);
+ }
+
+ count++;
+
+ if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) {
+ lexer_next(l);
+ Token val = lexer_next(l);
+ defaults[count - 1] = token_strdup(val);
+ }
+ }
+ if (lexer_peek(l).type == TOK_COMMA) {
+ lexer_next(l);
+ // Check if next is ...
+ if (lexer_peek(l).type == TOK_ELLIPSIS) {
+ lexer_next(l);
+ if (is_varargs_out) {
+ *is_varargs_out = 1;
+ }
+ if (strlen(buf) > 0) {
+ strcat(buf, ", ");
+ }
+ strcat(buf, "...");
+ break; // Must be last
+ }
+ } else {
+ break;
+ }
}
+ }
+ if (lexer_next(l).type != TOK_RPAREN) {
+ zpanic_at(lexer_peek(l), "Expected ')' after args");
+ }
- *defaults_out = defaults;
- *count_out = count;
- *types_out = types;
- *names_out = names;
- return buf;
+ *defaults_out = defaults;
+ *count_out = count;
+ *types_out = types;
+ *names_out = names;
+ return buf;
}
// Helper to find similar symbol name in current scope
-char *find_similar_symbol(ParserContext *ctx, const char *name)
-{
- if (!ctx->current_scope)
- {
- return NULL;
- }
-
- const char *best_match = NULL;
- int best_dist = 999;
-
- // Check local scopes
- Scope *s = ctx->current_scope;
- while (s)
- {
- Symbol *sym = s->symbols;
- while (sym)
- {
- int dist = levenshtein(name, sym->name);
- if (dist < best_dist && dist <= 3)
- {
- best_dist = dist;
- best_match = sym->name;
- }
- sym = sym->next;
- }
- s = s->parent;
- }
-
- // Check builtins/globals if any (simplified)
- return best_match ? xstrdup(best_match) : NULL;
-}
-
-void register_plugin(ParserContext *ctx, const char *name, const char *alias)
-{
- // Try to find existing (built-in) or already loaded plugin
- ZPlugin *plugin = zptr_find_plugin(name);
-
- // If not found, try to load it dynamically
- if (!plugin)
- {
- plugin = zptr_load_plugin(name);
-
- if (!plugin)
- {
- char path[1024];
- snprintf(path, sizeof(path), "%s.so", name);
- plugin = zptr_load_plugin(path);
- }
-
- if (!plugin && !strchr(name, '/'))
- {
- char path[1024];
- snprintf(path, sizeof(path), "./%s.so", name);
- plugin = zptr_load_plugin(path);
- }
- }
-
- if (!plugin)
- {
- fprintf(stderr,
- COLOR_RED "Error:" COLOR_RESET " Could not load plugin '%s'\n"
- " Tried built-ins and dynamic loading (.so)\n",
- name);
- exit(1);
- }
-
- ImportedPlugin *p = xmalloc(sizeof(ImportedPlugin));
- p->name = xstrdup(plugin->name); // Use the plugin's internal name
- p->alias = alias ? xstrdup(alias) : NULL;
- p->next = ctx->imported_plugins;
- ctx->imported_plugins = p;
-}
-
-const char *resolve_plugin(ParserContext *ctx, const char *name_or_alias)
-{
- for (ImportedPlugin *p = ctx->imported_plugins; p; p = p->next)
- {
- // Check if it matches the alias
- if (p->alias && strcmp(p->alias, name_or_alias) == 0)
- {
- return p->name;
- }
- // Check if it matches the name
- if (strcmp(p->name, name_or_alias) == 0)
- {
- return p->name;
- }
- }
- return NULL; // Plugin not found
+char *find_similar_symbol(ParserContext *ctx, const char *name) {
+ if (!ctx->current_scope) {
+ return NULL;
+ }
+
+ const char *best_match = NULL;
+ int best_dist = 999;
+
+ // Check local scopes
+ Scope *s = ctx->current_scope;
+ while (s) {
+ Symbol *sym = s->symbols;
+ while (sym) {
+ int dist = levenshtein(name, sym->name);
+ if (dist < best_dist && dist <= 3) {
+ best_dist = dist;
+ best_match = sym->name;
+ }
+ sym = sym->next;
+ }
+ s = s->parent;
+ }
+
+ // Check builtins/globals if any (simplified)
+ return best_match ? xstrdup(best_match) : NULL;
+}
+
+void register_plugin(ParserContext *ctx, const char *name, const char *alias) {
+ // Try to find existing (built-in) or already loaded plugin
+ ZPlugin *plugin = zptr_find_plugin(name);
+
+ // If not found, try to load it dynamically
+ if (!plugin) {
+ plugin = zptr_load_plugin(name);
+
+ if (!plugin) {
+ char path[1024];
+ snprintf(path, sizeof(path), "%s.so", name);
+ plugin = zptr_load_plugin(path);
+ }
+
+ if (!plugin && !strchr(name, '/')) {
+ char path[1024];
+ snprintf(path, sizeof(path), "./%s.so", name);
+ plugin = zptr_load_plugin(path);
+ }
+ }
+
+ if (!plugin) {
+ fprintf(stderr,
+ COLOR_RED "Error:" COLOR_RESET " Could not load plugin '%s'\n"
+ " Tried built-ins and dynamic loading (.so)\n",
+ name);
+ exit(1);
+ }
+
+ ImportedPlugin *p = xmalloc(sizeof(ImportedPlugin));
+ p->name = xstrdup(plugin->name); // Use the plugin's internal name
+ p->alias = alias ? xstrdup(alias) : NULL;
+ p->next = ctx->imported_plugins;
+ ctx->imported_plugins = p;
+}
+
+const char *resolve_plugin(ParserContext *ctx, const char *name_or_alias) {
+ for (ImportedPlugin *p = ctx->imported_plugins; p; p = p->next) {
+ // Check if it matches the alias
+ if (p->alias && strcmp(p->alias, name_or_alias) == 0) {
+ return p->name;
+ }
+ // Check if it matches the name
+ if (strcmp(p->name, name_or_alias) == 0) {
+ return p->name;
+ }
+ }
+ return NULL; // Plugin not found
}
diff --git a/src/plugins/plugin_manager.c b/src/plugins/plugin_manager.c
index afca55e..aab98c8 100644
--- a/src/plugins/plugin_manager.c
+++ b/src/plugins/plugin_manager.c
@@ -6,99 +6,82 @@
#include <string.h>
// Linked list node for plugins.
-typedef struct PluginNode
-{
- ZPlugin *plugin;
- void *handle; // dlopen handle (NULL for built-ins).
- struct PluginNode *next;
+typedef struct PluginNode {
+ ZPlugin *plugin;
+ void *handle; // dlopen handle (NULL for built-ins).
+ struct PluginNode *next;
} PluginNode;
static PluginNode *head = NULL;
-void zptr_plugin_mgr_init(void)
-{
- head = NULL;
-}
+void zptr_plugin_mgr_init(void) { head = NULL; }
-void zptr_register_plugin(ZPlugin *plugin)
-{
- if (!plugin)
- {
- return;
- }
+void zptr_register_plugin(ZPlugin *plugin) {
+ if (!plugin) {
+ return;
+ }
- if (zptr_find_plugin(plugin->name))
- {
- return;
- }
+ if (zptr_find_plugin(plugin->name)) {
+ return;
+ }
- PluginNode *node = malloc(sizeof(PluginNode));
- node->plugin = plugin;
- node->handle = NULL;
- node->next = head;
- head = node;
+ PluginNode *node = malloc(sizeof(PluginNode));
+ node->plugin = plugin;
+ node->handle = NULL;
+ node->next = head;
+ head = node;
}
-ZPlugin *zptr_load_plugin(const char *path)
-{
- void *handle = dlopen(path, RTLD_LAZY);
- if (!handle)
- {
- return NULL;
- }
+ZPlugin *zptr_load_plugin(const char *path) {
+ void *handle = dlopen(path, RTLD_LAZY);
+ if (!handle) {
+ return NULL;
+ }
- ZPluginInitFn init_fn = (ZPluginInitFn)dlsym(handle, "z_plugin_init");
- if (!init_fn)
- {
- fprintf(stderr, "Plugin '%s' missing 'z_plugin_init' symbol\n", path);
- dlclose(handle);
- return NULL;
- }
+ ZPluginInitFn init_fn = (ZPluginInitFn)dlsym(handle, "z_plugin_init");
+ if (!init_fn) {
+ fprintf(stderr, "Plugin '%s' missing 'z_plugin_init' symbol\n", path);
+ dlclose(handle);
+ return NULL;
+ }
- ZPlugin *plugin = init_fn();
- if (!plugin)
- {
- fprintf(stderr, "Plugin '%s' init returned NULL\n", path);
- dlclose(handle);
- return NULL;
- }
+ ZPlugin *plugin = init_fn();
+ if (!plugin) {
+ fprintf(stderr, "Plugin '%s' init returned NULL\n", path);
+ dlclose(handle);
+ return NULL;
+ }
- // Register
- PluginNode *node = malloc(sizeof(PluginNode));
- node->plugin = plugin;
- node->handle = handle;
- node->next = head;
- head = node;
+ // Register
+ PluginNode *node = malloc(sizeof(PluginNode));
+ node->plugin = plugin;
+ node->handle = handle;
+ node->next = head;
+ head = node;
- return plugin;
+ return plugin;
}
-ZPlugin *zptr_find_plugin(const char *name)
-{
- PluginNode *curr = head;
- while (curr)
- {
- if (strcmp(curr->plugin->name, name) == 0)
- {
- return curr->plugin;
- }
- curr = curr->next;
+ZPlugin *zptr_find_plugin(const char *name) {
+ PluginNode *curr = head;
+ while (curr) {
+ if (strcmp(curr->plugin->name, name) == 0) {
+ return curr->plugin;
}
- return NULL;
+ curr = curr->next;
+ }
+ return NULL;
}
-void zptr_plugin_mgr_cleanup(void)
-{
- PluginNode *curr = head;
- while (curr)
- {
- PluginNode *next = curr->next;
- if (curr->handle)
- {
- dlclose(curr->handle);
- }
- free(curr);
- curr = next;
+void zptr_plugin_mgr_cleanup(void) {
+ PluginNode *curr = head;
+ while (curr) {
+ PluginNode *next = curr->next;
+ if (curr->handle) {
+ dlclose(curr->handle);
}
- head = NULL;
+ free(curr);
+ curr = next;
+ }
+ head = NULL;
}
diff --git a/src/repl/repl.c b/src/repl/repl.c
index 22fe95d..5c82038 100644
--- a/src/repl/repl.c
+++ b/src/repl/repl.c
@@ -9,1368 +9,1177 @@
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);
+static int is_header_line(const char *line) {
+ return (strncmp(line, "import ", 7) == 0 ||
+ strncmp(line, "include ", 8) == 0 ||
+ strncmp(line, "#include", 8) == 0);
}
-void run_repl(const char *self_path)
-{
- printf("\033[1;36mZen C REPL (v0.1)\033[0m\n");
- 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 *));
-
- char history_path[512];
- const char *home = getenv("HOME");
- if (home)
- {
- snprintf(history_path, sizeof(history_path), "%s/.zprep_history", home);
- FILE *hf = fopen(history_path, "r");
- if (hf)
- {
- char buf[1024];
- while (fgets(buf, sizeof(buf), hf))
- {
- size_t l = strlen(buf);
- if (l > 0 && buf[l - 1] == '\n')
- {
- buf[--l] = 0;
- }
- if (l == 0)
- {
- continue;
- }
- if (history_len >= history_cap)
- {
- history_cap *= 2;
- history = realloc(history, history_cap * sizeof(char *));
- }
- history[history_len++] = strdup(buf);
- }
- fclose(hf);
- if (history_len > 0)
- {
- printf("Loaded %d entries from history.\n", history_len);
- }
+void run_repl(const char *self_path) {
+ printf("\033[1;36mZen C REPL (v0.1)\033[0m\n");
+ 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 *));
+
+ char history_path[512];
+ const char *home = getenv("HOME");
+ if (home) {
+ snprintf(history_path, sizeof(history_path), "%s/.zprep_history", home);
+ FILE *hf = fopen(history_path, "r");
+ if (hf) {
+ char buf[1024];
+ while (fgets(buf, sizeof(buf), hf)) {
+ size_t l = strlen(buf);
+ if (l > 0 && buf[l - 1] == '\n') {
+ buf[--l] = 0;
}
- }
- else
- {
- history_path[0] = 0;
- }
-
- // Watch list.
- char *watches[16];
- int watches_len = 0;
- for (int i = 0; i < 16; i++)
- {
- watches[i] = NULL;
- }
-
- // Load startup file (~/.zprep_init.zc) if exists
- if (home)
- {
- char init_path[512];
- snprintf(init_path, sizeof(init_path), "%s/.zprep_init.zc", home);
- FILE *init_f = fopen(init_path, "r");
- if (init_f)
- {
- char buf[1024];
- int init_count = 0;
- while (fgets(buf, sizeof(buf), init_f))
- {
- size_t l = strlen(buf);
- if (l > 0 && buf[l - 1] == '\n')
- {
- buf[--l] = 0;
- }
- char *p = buf;
- while (*p == ' ' || *p == '\t')
- {
- p++;
- }
- if (*p == 0 || *p == '/' || *p == '#')
- {
- continue;
- }
- if (history_len >= history_cap)
- {
- history_cap *= 2;
- history = realloc(history, history_cap * sizeof(char *));
- }
- history[history_len++] = strdup(p);
- init_count++;
- }
- fclose(init_f);
- if (init_count > 0)
- {
- printf("Loaded %d lines from ~/.zprep_init.zc\n", init_count);
- }
+ if (l == 0) {
+ continue;
+ }
+ if (history_len >= history_cap) {
+ history_cap *= 2;
+ history = realloc(history, history_cap * sizeof(char *));
}
+ history[history_len++] = strdup(buf);
+ }
+ fclose(hf);
+ if (history_len > 0) {
+ printf("Loaded %d entries from history.\n", history_len);
+ }
}
-
- char line_buf[1024];
-
- char *input_buffer = NULL;
- size_t input_len = 0;
- int brace_depth = 0;
- int paren_depth = 0;
-
- while (1)
- {
- if (brace_depth > 0 || paren_depth > 0)
- {
- printf("... ");
+ } else {
+ history_path[0] = 0;
+ }
+
+ // Watch list.
+ char *watches[16];
+ int watches_len = 0;
+ for (int i = 0; i < 16; i++) {
+ watches[i] = NULL;
+ }
+
+ // Load startup file (~/.zprep_init.zc) if exists
+ if (home) {
+ char init_path[512];
+ snprintf(init_path, sizeof(init_path), "%s/.zprep_init.zc", home);
+ FILE *init_f = fopen(init_path, "r");
+ if (init_f) {
+ char buf[1024];
+ int init_count = 0;
+ while (fgets(buf, sizeof(buf), init_f)) {
+ size_t l = strlen(buf);
+ if (l > 0 && buf[l - 1] == '\n') {
+ buf[--l] = 0;
}
- else
- {
- printf("\033[1;32m>>>\033[0m ");
+ char *p = buf;
+ while (*p == ' ' || *p == '\t') {
+ p++;
}
-
- if (!fgets(line_buf, sizeof(line_buf), stdin))
- {
- break;
+ if (*p == 0 || *p == '/' || *p == '#') {
+ continue;
+ }
+ if (history_len >= history_cap) {
+ history_cap *= 2;
+ history = realloc(history, history_cap * sizeof(char *));
}
+ history[history_len++] = strdup(p);
+ init_count++;
+ }
+ fclose(init_f);
+ if (init_count > 0) {
+ printf("Loaded %d lines from ~/.zprep_init.zc\n", init_count);
+ }
+ }
+ }
- // Handle commands (only on fresh line).
- if (NULL == input_buffer)
- {
- size_t len = strlen(line_buf);
- char cmd_buf[1024];
- strcpy(cmd_buf, line_buf);
- if (len > 0 && cmd_buf[len - 1] == '\n')
- {
- cmd_buf[--len] = 0;
- }
- while (len > 0 && (cmd_buf[len - 1] == ' ' || cmd_buf[len - 1] == '\t'))
- {
- cmd_buf[--len] = 0;
- }
+ char line_buf[1024];
- if (0 == strcmp(cmd_buf, "exit") || 0 == strcmp(cmd_buf, "quit"))
- {
- break;
- }
+ char *input_buffer = NULL;
+ size_t input_len = 0;
+ int brace_depth = 0;
+ int paren_depth = 0;
- // Commands
- if (cmd_buf[0] == ':' || cmd_buf[0] == '!')
- {
- if (0 == strcmp(cmd_buf, ":help"))
- {
- printf("REPL Commands:\n");
- printf(" :help Show this help\n");
- printf(" :reset Clear history\n");
- printf(" :imports Show active imports\n");
- printf(" :vars Show active variables\n");
- printf(" :funcs Show user functions\n");
- printf(" :structs Show user structs\n");
- printf(" :history Show command history\n");
- printf(" :type <x> Show type of expression\n");
- printf(" :time <x> Benchmark expression (1000 iters)\n");
- printf(" :c <x> Show generated C code\n");
- printf(" :doc <x> Show documentation for symbol\n");
- printf(" :run Execute full session\n");
- 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(" :clear Clear screen\n");
- printf(" ! <cmd> Run shell command\n");
- printf(" :quit Exit REPL\n");
- continue;
- }
- else if (0 == strcmp(cmd_buf, ":reset"))
- {
- for (int i = 0; i < history_len; i++)
- {
- free(history[i]);
- }
- history_len = 0;
- printf("History cleared.\n");
- continue;
- }
- else if (0 == strcmp(cmd_buf, ":quit"))
- {
- break;
- }
- else if (0 == strcmp(cmd_buf, ":clear"))
- {
- printf("\033[2J\033[H"); // ANSI clear screen
- continue;
- }
- else if (0 == strcmp(cmd_buf, ":undo"))
- {
- if (history_len > 0)
- {
- history_len = history_len - 1;
- free(history[history_len]);
- printf("Removed last entry.\n");
- }
- else
- {
- printf("History is empty.\n");
- }
- continue;
- }
- else if (0 == strncmp(cmd_buf, ":delete ", 8))
- {
- int idx = atoi(cmd_buf + 8) - 1;
- if (idx >= 0 && idx < history_len)
- {
- free(history[idx]);
- for (int i = idx; i < history_len - 1; i++)
- {
- history[i] = history[i + 1];
- }
- history_len = history_len - 1;
- printf("Deleted entry %d.\n", idx + 1);
- }
- else
- {
- printf("Invalid index. Use :history to see valid indices.\n");
- }
- continue;
- }
- else if (0 == strncmp(cmd_buf, ":edit", 5))
- {
- int idx = history_len - 1;
- if (strlen(cmd_buf) > 6)
- {
- idx = atoi(cmd_buf + 6) - 1;
- }
+ while (1) {
+ if (brace_depth > 0 || paren_depth > 0) {
+ printf("... ");
+ } else {
+ printf("\033[1;32m>>>\033[0m ");
+ }
- if (history_len == 0)
- {
- printf("History is empty.\n");
- continue;
- }
+ if (!fgets(line_buf, sizeof(line_buf), stdin)) {
+ break;
+ }
- if (idx < 0 || idx >= history_len)
- {
- printf("Invalid index.\n");
- continue;
- }
+ // Handle commands (only on fresh line).
+ if (NULL == input_buffer) {
+ size_t len = strlen(line_buf);
+ char cmd_buf[1024];
+ strcpy(cmd_buf, line_buf);
+ if (len > 0 && cmd_buf[len - 1] == '\n') {
+ cmd_buf[--len] = 0;
+ }
+ while (len > 0 && (cmd_buf[len - 1] == ' ' || cmd_buf[len - 1] == '\t')) {
+ cmd_buf[--len] = 0;
+ }
+
+ if (0 == strcmp(cmd_buf, "exit") || 0 == strcmp(cmd_buf, "quit")) {
+ break;
+ }
+
+ // Commands
+ if (cmd_buf[0] == ':' || cmd_buf[0] == '!') {
+ if (0 == strcmp(cmd_buf, ":help")) {
+ printf("REPL Commands:\n");
+ printf(" :help Show this help\n");
+ printf(" :reset Clear history\n");
+ printf(" :imports Show active imports\n");
+ printf(" :vars Show active variables\n");
+ printf(" :funcs Show user functions\n");
+ printf(" :structs Show user structs\n");
+ printf(" :history Show command history\n");
+ printf(" :type <x> Show type of expression\n");
+ printf(" :time <x> Benchmark expression (1000 iters)\n");
+ printf(" :c <x> Show generated C code\n");
+ printf(" :doc <x> Show documentation for symbol\n");
+ printf(" :run Execute full session\n");
+ 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(" :clear Clear screen\n");
+ printf(" ! <cmd> Run shell command\n");
+ printf(" :quit Exit REPL\n");
+ continue;
+ } else if (0 == strcmp(cmd_buf, ":reset")) {
+ for (int i = 0; i < history_len; i++) {
+ free(history[i]);
+ }
+ history_len = 0;
+ printf("History cleared.\n");
+ continue;
+ } else if (0 == strcmp(cmd_buf, ":quit")) {
+ break;
+ } else if (0 == strcmp(cmd_buf, ":clear")) {
+ printf("\033[2J\033[H"); // ANSI clear screen
+ continue;
+ } else if (0 == strcmp(cmd_buf, ":undo")) {
+ if (history_len > 0) {
+ history_len = history_len - 1;
+ free(history[history_len]);
+ printf("Removed last entry.\n");
+ } else {
+ printf("History is empty.\n");
+ }
+ continue;
+ } else if (0 == strncmp(cmd_buf, ":delete ", 8)) {
+ int idx = atoi(cmd_buf + 8) - 1;
+ if (idx >= 0 && idx < history_len) {
+ free(history[idx]);
+ for (int i = idx; i < history_len - 1; i++) {
+ history[i] = history[i + 1];
+ }
+ history_len = history_len - 1;
+ printf("Deleted entry %d.\n", idx + 1);
+ } else {
+ printf("Invalid index. Use :history to see valid indices.\n");
+ }
+ continue;
+ } else if (0 == strncmp(cmd_buf, ":edit", 5)) {
+ int idx = history_len - 1;
+ if (strlen(cmd_buf) > 6) {
+ idx = atoi(cmd_buf + 6) - 1;
+ }
+
+ if (history_len == 0) {
+ printf("History is empty.\n");
+ continue;
+ }
- char edit_path[256];
- sprintf(edit_path, "/tmp/zprep_edit_%d.zc", rand());
- FILE *f = fopen(edit_path, "w");
- if (f)
- {
- fprintf(f, "%s", history[idx]);
- fclose(f);
-
- const char *editor = getenv("EDITOR");
- if (!editor)
- {
- editor = "nano"; // Default fallback,
- // 'cause I know some of you
- // don't know how to exit Vim.
- }
-
- char cmd[1024];
- sprintf(cmd, "%s %s", editor, edit_path);
- int status = system(cmd);
-
- if (0 == status)
- {
- // Read back file.
- FILE *fr = fopen(edit_path, "r");
- if (fr)
- {
- fseek(fr, 0, SEEK_END);
- long length = ftell(fr);
- fseek(fr, 0, SEEK_SET);
- char *buffer = malloc(length + 1);
- if (buffer)
- {
- fread(buffer, 1, length, fr);
- buffer[length] = 0;
-
- while (length > 0 && buffer[length - 1] == '\n')
- {
- buffer[--length] = 0;
- }
-
- if (strlen(buffer) > 0)
- {
- printf("Running: %s\n", buffer);
- if (history_len >= history_cap)
- {
- history_cap *= 2;
- history =
- realloc(history, history_cap * sizeof(char *));
- }
- history[history_len++] = strdup(buffer);
- }
- else
- {
- free(buffer);
- }
- }
- fclose(fr);
- }
- }
- }
- continue;
- }
- else if (0 == strncmp(cmd_buf, ":watch ", 7))
- {
- char *expr = cmd_buf + 7;
- while (*expr == ' ')
- {
- expr++;
- }
- size_t l = strlen(expr);
- while (l > 0 && expr[l - 1] == ' ')
- {
- expr[--l] = 0;
- }
+ if (idx < 0 || idx >= history_len) {
+ printf("Invalid index.\n");
+ continue;
+ }
+
+ char edit_path[256];
+ sprintf(edit_path, "/tmp/zprep_edit_%d.zc", rand());
+ FILE *f = fopen(edit_path, "w");
+ if (f) {
+ fprintf(f, "%s", history[idx]);
+ fclose(f);
+
+ const char *editor = getenv("EDITOR");
+ if (!editor) {
+ editor = "nano"; // Default fallback,
+ // 'cause I know some of you
+ // don't know how to exit Vim.
+ }
- if (l > 0)
- {
- if (watches_len < 16)
- {
- watches[watches_len++] = strdup(expr);
- printf("Watching: %s\n", expr);
- }
- else
- {
- printf("Watch list full (max 16).\n");
- }
- }
- else
- {
- if (watches_len == 0)
- {
- printf("No active watches.\n");
- }
- else
- {
- for (int i = 0; i < watches_len; i++)
- {
- printf("%d: %s\n", i + 1, watches[i]);
- }
- }
- }
- continue;
- }
- else if (0 == strncmp(cmd_buf, ":unwatch ", 9))
- {
- // Remove watch.
- int idx = atoi(cmd_buf + 9) - 1;
- if (idx >= 0 && idx < watches_len)
- {
- free(watches[idx]);
- for (int i = idx; i < watches_len - 1; i++)
- {
- watches[i] = watches[i + 1];
- }
-
- watches_len--;
-
- printf("Removed watch %d.\n", idx + 1);
- }
- else
- {
- printf("Invalid index.\n");
- }
- continue;
- }
- else if (cmd_buf[0] == '!')
- {
- // Shell escape.
- system(cmd_buf + 1);
- continue;
- }
- else if (0 == strncmp(cmd_buf, ":save ", 6))
- {
- char *filename = cmd_buf + 6;
- 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");
- fclose(f);
- printf("Session saved to %s\n", filename);
- }
- else
- {
- printf("Error: Cannot write to %s\n", filename);
- }
- continue;
- }
- else if (0 == strncmp(cmd_buf, ":load ", 6))
- {
- char *filename = cmd_buf + 6;
- FILE *f = fopen(filename, "r");
- if (f)
- {
- char buf[1024];
- int count = 0;
- while (fgets(buf, sizeof(buf), f))
- {
- size_t l = strlen(buf);
- if (l > 0 && buf[l - 1] == '\n')
- {
- buf[--l] = 0;
- }
- if (l == 0)
- {
- continue;
- }
- if (history_len >= history_cap)
- {
- history_cap *= 2;
- history = realloc(history, history_cap * sizeof(char *));
- }
- history[history_len++] = strdup(buf);
- count++;
- }
- fclose(f);
- printf("Loaded %d lines from %s\n", count, filename);
- }
- else
- {
- printf("Error: Cannot read %s\n", filename);
- }
- continue;
- }
- else if (0 == strcmp(cmd_buf, ":imports"))
- {
- printf("Active Imports:\n");
- for (int i = 0; i < history_len; i++)
- {
- if (is_header_line(history[i]))
- {
- printf(" %s\n", history[i]);
- }
+ char cmd[1024];
+ sprintf(cmd, "%s %s", editor, edit_path);
+ int status = system(cmd);
+
+ if (0 == status) {
+ // Read back file.
+ FILE *fr = fopen(edit_path, "r");
+ if (fr) {
+ fseek(fr, 0, SEEK_END);
+ long length = ftell(fr);
+ fseek(fr, 0, SEEK_SET);
+ char *buffer = malloc(length + 1);
+ if (buffer) {
+ fread(buffer, 1, length, fr);
+ buffer[length] = 0;
+
+ while (length > 0 && buffer[length - 1] == '\n') {
+ buffer[--length] = 0;
+ }
+
+ if (strlen(buffer) > 0) {
+ printf("Running: %s\n", buffer);
+ if (history_len >= history_cap) {
+ history_cap *= 2;
+ history = realloc(history, history_cap * sizeof(char *));
}
- continue;
+ history[history_len++] = strdup(buffer);
+ } else {
+ free(buffer);
+ }
}
- else if (0 == strcmp(cmd_buf, ":history"))
- {
- printf("Session History:\n");
- for (int i = 0; i < history_len; i++)
- {
- printf("%4d %s\n", i + 1, history[i]);
- }
- continue;
- }
- 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, "");
-
- 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, " }");
-
- ParserContext ctx = {0};
- ctx.is_repl = 1;
- ctx.skip_preamble = 1;
-
- Lexer l;
- lexer_init(&l, code);
- ASTNode *nodes = parse_program(&ctx, &l);
-
- ASTNode *search = nodes;
- if (search && search->type == NODE_ROOT)
- {
- search = search->root.children;
- }
-
- if (0 == strcmp(cmd_buf, ":vars"))
- {
- ASTNode *main_func = NULL;
- for (ASTNode *n = search; n; n = n->next)
- {
- if (n->type == NODE_FUNCTION && 0 == strcmp(n->func.name, "main"))
- {
- main_func = n;
- break;
- }
- }
- printf("Variables:\n");
- 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)
- {
- if (s->type == NODE_VAR_DECL)
- {
- char *t =
- s->var_decl.type_str ? s->var_decl.type_str : "Inferred";
- printf(" %s: %s\n", s->var_decl.name, t);
- found = 1;
- }
- }
- if (!found)
- {
- printf(" (none)\n");
- }
- }
- else
- {
- printf(" (none)\n");
- }
- }
- else if (0 == strcmp(cmd_buf, ":funcs"))
- {
- printf("Functions:\n");
- int found = 0;
- for (ASTNode *n = search; n; n = n->next)
- {
- if (n->type == NODE_FUNCTION && 0 != strcmp(n->func.name, "main"))
- {
- printf(" fn %s()\n", n->func.name);
- found = 1;
- }
- }
- if (!found)
- {
- printf(" (none)\n");
- }
- }
- else if (0 == strcmp(cmd_buf, ":structs"))
- {
- printf("Structs:\n");
- int found = 0;
- for (ASTNode *n = search; n; n = n->next)
- {
- if (n->type == NODE_STRUCT)
- {
- printf(" struct %s\n", n->strct.name);
- found = 1;
- }
- }
- if (!found)
- {
- printf(" (none)\n");
- }
- }
+ fclose(fr);
+ }
+ }
+ }
+ continue;
+ } else if (0 == strncmp(cmd_buf, ":watch ", 7)) {
+ char *expr = cmd_buf + 7;
+ while (*expr == ' ') {
+ expr++;
+ }
+ size_t l = strlen(expr);
+ while (l > 0 && expr[l - 1] == ' ') {
+ expr[--l] = 0;
+ }
+
+ if (l > 0) {
+ if (watches_len < 16) {
+ watches[watches_len++] = strdup(expr);
+ printf("Watching: %s\n", expr);
+ } else {
+ printf("Watch list full (max 16).\n");
+ }
+ } else {
+ if (watches_len == 0) {
+ printf("No active watches.\n");
+ } else {
+ for (int i = 0; i < watches_len; i++) {
+ printf("%d: %s\n", i + 1, watches[i]);
+ }
+ }
+ }
+ continue;
+ } else if (0 == strncmp(cmd_buf, ":unwatch ", 9)) {
+ // Remove watch.
+ int idx = atoi(cmd_buf + 9) - 1;
+ if (idx >= 0 && idx < watches_len) {
+ free(watches[idx]);
+ for (int i = idx; i < watches_len - 1; i++) {
+ watches[i] = watches[i + 1];
+ }
- free(code);
- continue;
+ watches_len--;
+
+ printf("Removed watch %d.\n", idx + 1);
+ } else {
+ printf("Invalid index.\n");
+ }
+ continue;
+ } else if (cmd_buf[0] == '!') {
+ // Shell escape.
+ system(cmd_buf + 1);
+ continue;
+ } else if (0 == strncmp(cmd_buf, ":save ", 6)) {
+ char *filename = cmd_buf + 6;
+ 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");
+ fclose(f);
+ printf("Session saved to %s\n", filename);
+ } else {
+ printf("Error: Cannot write to %s\n", filename);
+ }
+ continue;
+ } else if (0 == strncmp(cmd_buf, ":load ", 6)) {
+ char *filename = cmd_buf + 6;
+ FILE *f = fopen(filename, "r");
+ if (f) {
+ char buf[1024];
+ int count = 0;
+ while (fgets(buf, sizeof(buf), f)) {
+ size_t l = strlen(buf);
+ if (l > 0 && buf[l - 1] == '\n') {
+ buf[--l] = 0;
+ }
+ if (l == 0) {
+ continue;
+ }
+ if (history_len >= history_cap) {
+ history_cap *= 2;
+ history = realloc(history, history_cap * sizeof(char *));
+ }
+ history[history_len++] = strdup(buf);
+ count++;
+ }
+ fclose(f);
+ printf("Loaded %d lines from %s\n", count, filename);
+ } else {
+ printf("Error: Cannot read %s\n", filename);
+ }
+ continue;
+ } else if (0 == strcmp(cmd_buf, ":imports")) {
+ printf("Active Imports:\n");
+ for (int i = 0; i < history_len; i++) {
+ if (is_header_line(history[i])) {
+ printf(" %s\n", history[i]);
+ }
+ }
+ continue;
+ } else if (0 == strcmp(cmd_buf, ":history")) {
+ printf("Session History:\n");
+ for (int i = 0; i < history_len; i++) {
+ printf("%4d %s\n", i + 1, history[i]);
+ }
+ continue;
+ } 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, "");
+
+ 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, " }");
+
+ ParserContext ctx = {0};
+ ctx.is_repl = 1;
+ ctx.skip_preamble = 1;
+
+ Lexer l;
+ lexer_init(&l, code);
+ ASTNode *nodes = parse_program(&ctx, &l);
+
+ ASTNode *search = nodes;
+ if (search && search->type == NODE_ROOT) {
+ search = search->root.children;
+ }
+
+ if (0 == strcmp(cmd_buf, ":vars")) {
+ ASTNode *main_func = NULL;
+ for (ASTNode *n = search; n; n = n->next) {
+ if (n->type == NODE_FUNCTION &&
+ 0 == strcmp(n->func.name, "main")) {
+ main_func = n;
+ break;
+ }
+ }
+ printf("Variables:\n");
+ 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) {
+ if (s->type == NODE_VAR_DECL) {
+ char *t =
+ s->var_decl.type_str ? s->var_decl.type_str : "Inferred";
+ printf(" %s: %s\n", s->var_decl.name, t);
+ found = 1;
}
- else if (0 == strncmp(cmd_buf, ":type ", 6))
- {
- 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 *probe_code = malloc(probe_size + strlen(expr) + 256);
- strcpy(probe_code, "");
-
- for (int i = 0; i < history_len; i++)
- {
- if (is_header_line(history[i]))
- {
- strcat(probe_code, history[i]);
- strcat(probe_code, "\n");
- }
- }
+ }
+ if (!found) {
+ printf(" (none)\n");
+ }
+ } else {
+ printf(" (none)\n");
+ }
+ } else if (0 == strcmp(cmd_buf, ":funcs")) {
+ printf("Functions:\n");
+ int found = 0;
+ for (ASTNode *n = search; n; n = n->next) {
+ if (n->type == NODE_FUNCTION &&
+ 0 != strcmp(n->func.name, "main")) {
+ printf(" fn %s()\n", n->func.name);
+ found = 1;
+ }
+ }
+ if (!found) {
+ printf(" (none)\n");
+ }
+ } else if (0 == strcmp(cmd_buf, ":structs")) {
+ printf("Structs:\n");
+ int found = 0;
+ for (ASTNode *n = search; n; n = n->next) {
+ if (n->type == NODE_STRUCT) {
+ printf(" struct %s\n", n->strct.name);
+ found = 1;
+ }
+ }
+ if (!found) {
+ printf(" (none)\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, " ");
- }
- }
+ free(code);
+ continue;
+ } else if (0 == strncmp(cmd_buf, ":type ", 6)) {
+ char *expr = cmd_buf + 6;
- 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, expr);
- strcat(probe_code, "); }");
-
- char tmp_path[256];
- sprintf(tmp_path, "/tmp/zprep_repl_type_%d.zc", rand());
- FILE *f = fopen(tmp_path, "w");
- if (f)
- {
- fprintf(f, "%s", probe_code);
- fclose(f);
-
- char cmd[2048];
- sprintf(cmd, "%s run -q %s 2>&1", self_path, tmp_path);
-
- FILE *p = popen(cmd, "r");
- if (p)
- {
- char buf[1024];
- int found = 0;
- while (fgets(buf, sizeof(buf), p))
- {
- char *marker = "right operand has type '";
- char *start = strstr(buf, marker);
- if (start)
- {
- start += strlen(marker);
- char *end = strchr(start, '\'');
- if (end)
- {
- *end = 0;
- printf("\033[1;36mType: %s\033[0m\n", start);
- found = 1;
- break;
- }
- }
- }
- pclose(p);
- if (!found)
- {
- printf("Type: <unknown>\n");
- }
- }
- }
- free(probe_code);
- continue;
- }
- else if (0 == strncmp(cmd_buf, ":time ", 6))
- {
- // 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, "");
-
- 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);
- strcat(code, "; }\n");
- strcat(code, "raw { clock_t _end = clock(); double _elapsed = (double)(_end - "
- "_start) / CLOCKS_PER_SEC; printf(\"1000 iterations: %.4fs "
- "(%.6fs/iter)\\n\", _elapsed, _elapsed/1000); }\n");
- strcat(code, "}");
-
- char tmp_path[256];
- sprintf(tmp_path, "/tmp/zprep_repl_time_%d.zc", rand());
- FILE *f = fopen(tmp_path, "w");
- if (f)
- {
- fprintf(f, "%s", code);
- fclose(f);
- char cmd[2048];
- sprintf(cmd, "%s run -q %s", self_path, tmp_path);
- system(cmd);
- }
- free(code);
- continue;
- }
- else if (0 == strncmp(cmd_buf, ":c ", 3))
- {
- char *expr_buf = malloc(8192);
- strcpy(expr_buf, cmd_buf + 3);
-
- int brace_depth = 0;
- for (char *p = expr_buf; *p; p++)
- {
- if (*p == '{')
- {
- brace_depth++;
- }
- else if (*p == '}')
- {
- brace_depth--;
- }
- }
+ size_t probe_size = 4096;
+ for (int i = 0; i < history_len; i++) {
+ probe_size += strlen(history[i]) + 2;
+ }
- while (brace_depth > 0)
- {
- printf("... ");
- char more[1024];
- if (!fgets(more, sizeof(more), stdin))
- {
- 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++)
- {
- if (*p == '{')
- {
- brace_depth++;
- }
- else if (*p == '}')
- {
- brace_depth--;
- }
- }
- }
+ char *probe_code = malloc(probe_size + strlen(expr) + 256);
+ strcpy(probe_code, "");
- 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, "");
-
- 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}");
- free(expr_buf);
-
- char tmp_path[256];
- sprintf(tmp_path, "/tmp/zprep_repl_c_%d.zc", rand());
- FILE *f = fopen(tmp_path, "w");
- if (f)
- {
- fprintf(f, "%s", code);
- fclose(f);
- char cmd[2048];
- sprintf(cmd,
- "%s build -q --emit-c -o /tmp/zprep_repl_out %s "
- "2>/dev/null; sed "
- "-n '/^int main() {$/,/^}$/p' /tmp/zprep_repl_out.c "
- "2>/dev/null | "
- "tail -n +3 | head -n -2 | sed 's/^ //'",
- self_path, tmp_path);
- system(cmd);
- }
- free(code);
- continue;
- }
- 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 *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");
-
- char tmp_path[256];
- sprintf(tmp_path, "/tmp/zprep_repl_run_%d.zc", rand());
- FILE *f = fopen(tmp_path, "w");
- if (f)
- {
- fprintf(f, "%s", code);
- fclose(f);
- char cmd[2048];
- sprintf(cmd, "%s run %s", self_path, tmp_path);
- system(cmd);
- }
- free(code);
- continue;
- }
- else if (0 == strncmp(cmd_buf, ":doc ", 5))
- {
- char *sym = cmd_buf + 5;
- while (*sym == ' ')
- {
- sym++;
- }
- size_t symlen = strlen(sym);
- while (symlen > 0 && sym[symlen - 1] == ' ')
- {
- sym[--symlen] = 0;
- }
+ for (int i = 0; i < history_len; i++) {
+ if (is_header_line(history[i])) {
+ strcat(probe_code, history[i]);
+ strcat(probe_code, "\n");
+ }
+ }
- // Documentation database
-
- struct
- {
- const char *name;
- const char *doc;
- } docs[] = {
- {"Vec", "Vec<T> - Dynamic array (generic)\n Fields: data: T*, "
- "len: usize, cap: "
- "usize\n Methods: new, push, pop, get, set, insert, "
- "remove, contains, "
- "clear, free, clone, reverse, first, last, length, "
- "is_empty, eq"},
- {"Vec.new", "fn Vec<T>::new() -> Vec<T>\n Creates an empty vector."},
- {"Vec.push", "fn push(self, item: T)\n Appends item to the end. "
- "Auto-grows capacity."},
- {"Vec.pop", "fn pop(self) -> T\n Removes and returns the last element. "
- "Panics if empty."},
- {"Vec.get", "fn get(self, idx: usize) -> T\n Returns element at index. "
- "Panics if out of bounds."},
- {"Vec.set", "fn set(self, idx: usize, item: T)\n Sets element at index. "
- "Panics if out of bounds."},
- {"Vec.insert", "fn insert(self, idx: usize, item: T)\n Inserts item at "
- "index, shifting elements right."},
- {"Vec.remove", "fn remove(self, idx: usize) -> T\n Removes and returns "
- "element at index, shifting elements left."},
- {"Vec.contains", "fn contains(self, item: T) -> bool\n Returns true if "
- "item is in the vector."},
- {"Vec.clear", "fn clear(self)\n Removes all elements but keeps capacity."},
- {"Vec.free", "fn free(self)\n Frees memory. Sets data to null."},
- {"Vec.clone", "fn clone(self) -> Vec<T>\n Returns a shallow copy."},
- {"Vec.reverse", "fn reverse(self)\n Reverses elements in place."},
- {"Vec.first", "fn first(self) -> T\n Returns first element. "
- "Panics if empty."},
- {"Vec.last",
- "fn last(self) -> T\n Returns last element. Panics if empty."},
- {"Vec.length", "fn length(self) -> usize\n Returns number of elements."},
- {"Vec.is_empty",
- "fn is_empty(self) -> bool\n Returns true if length is 0."},
- {"String", "String - Mutable string (alias for char*)\n "
- "Methods: len, split, trim, "
- "contains, starts_with, ends_with, to_upper, "
- "to_lower, substring, find"},
- {"String.len", "fn len(self) -> usize\n Returns string length."},
- {"String.contains", "fn contains(self, substr: string) -> bool\n Returns "
- "true if string contains substr."},
- {"String.starts_with", "fn starts_with(self, prefix: string) -> bool\n "
- "Returns true if string starts with prefix."},
- {"String.ends_with", "fn ends_with(self, suffix: string) -> bool\n "
- "Returns true if string ends with suffix."},
- {"String.substring", "fn substring(self, start: usize, len: usize) -> "
- "string\n Returns a substring. Caller must free."},
- {"String.find", "fn find(self, substr: string) -> int\n Returns index of "
- "substr, or -1 if not found."},
- {"println", "println \"format string {expr}\"\n Prints to stdout with "
- "newline. Auto-formats {expr} values."},
- {"print", "print \"format string {expr}\"\n Prints to stdout "
- "without newline."},
- {"eprintln",
- "eprintln \"format string\"\n Prints to stderr with newline."},
- {"eprint", "eprint \"format string\"\n Prints to stderr without newline."},
- {"guard", "guard condition else action\n Early exit pattern. "
- "Executes action if "
- "condition is false.\n Example: guard ptr != NULL "
- "else return;"},
- {"defer", "defer statement\n Executes statement at end of scope.\n "
- "Example: defer free(ptr);"},
- {"sizeof", "sizeof(type) or sizeof(expr)\n Returns size in bytes."},
- {"typeof", "typeof(expr)\n Returns the type of expression "
- "(compile-time)."},
- {"malloc", "void *malloc(size_t size)\n Allocates size bytes. Returns "
- "pointer or NULL. Free with free()."},
- {"free", "void free(void *ptr)\n Frees memory allocated by "
- "malloc/calloc/realloc."},
- {"calloc", "void *calloc(size_t n, size_t size)\n Allocates n*size bytes, "
- "zeroed. Returns pointer or NULL."},
- {"realloc", "void *realloc(void *ptr, size_t size)\n Resizes allocation "
- "to size bytes. May move memory."},
- {"memcpy", "void *memcpy(void *dest, const void *src, size_t n)\n Copies "
- "n bytes from src to dest. Returns dest. No overlap."},
- {"memmove", "void *memmove(void *dest, const void *src, size_t n)\n "
- "Copies n bytes, handles overlapping regions."},
- {"memset", "void *memset(void *s, int c, size_t n)\n Sets n "
- "bytes of s to value c."},
- {"strlen", "size_t strlen(const char *s)\n Returns length of string (not "
- "including null terminator)."},
- {"strcpy", "char *strcpy(char *dest, const char *src)\n Copies src to "
- "dest including null terminator. No bounds check."},
- {"strncpy", "char *strncpy(char *dest, const char *src, size_t n)\n "
- "Copies up to n chars. May not null-terminate."},
- {"strcat", "char *strcat(char *dest, const char *src)\n Appends "
- "src to dest."},
- {"strcmp", "int strcmp(const char *s1, const char *s2)\n Compares "
- "strings. Returns 0 if equal, <0 or >0 otherwise."},
- {"strncmp", "int strncmp(const char *s1, const char *s2, size_t n)\n "
- "Compares up to n characters."},
- {"strstr", "char *strstr(const char *haystack, const char *needle)\n "
- "Finds first occurrence of needle. Returns pointer or NULL."},
- {"strchr", "char *strchr(const char *s, int c)\n Finds first occurrence "
- "of char c. Returns pointer or NULL."},
- {"strdup", "char *strdup(const char *s)\n Duplicates string. Caller must "
- "free the result."},
- {"printf", "int printf(const char *fmt, ...)\n Prints formatted output to "
- "stdout. Returns chars written."},
- {"sprintf", "int sprintf(char *str, const char *fmt, ...)\n Prints "
- "formatted output to string buffer."},
- {"snprintf", "int snprintf(char *str, size_t n, const char *fmt, ...)\n "
- "Safe sprintf with size limit."},
- {"fprintf", "int fprintf(FILE *f, const char *fmt, ...)\n Prints "
- "formatted output to file stream."},
- {"scanf", "int scanf(const char *fmt, ...)\n Reads formatted "
- "input from stdin."},
- {"fopen", "FILE *fopen(const char *path, const char *mode)\n Opens file. "
- "Modes: "
- "\"r\", \"w\", \"a\", \"rb\", \"wb\". Returns NULL on error."},
- {"fclose", "int fclose(FILE *f)\n Closes file. Returns 0 on success."},
- {"fread", "size_t fread(void *ptr, size_t size, size_t n, FILE *f)\n "
- "Reads n items of size bytes. Returns items read."},
- {"fwrite", "size_t fwrite(const void *ptr, size_t size, size_t n, FILE "
- "*f)\n Writes n items of size bytes. Returns items written."},
- {"fgets", "char *fgets(char *s, int n, FILE *f)\n Reads line up to n-1 "
- "chars. Includes newline. Returns s or NULL."},
- {"fputs", "int fputs(const char *s, FILE *f)\n Writes string to file. "
- "Returns non-negative or EOF."},
- {"exit", "void exit(int status)\n Terminates program with "
- "status code. 0 "
- "= success."},
- {"atoi", "int atoi(const char *s)\n Converts string to int. "
- "Returns 0 on error."},
- {"atof", "double atof(const char *s)\n Converts string to double."},
- {"abs", "int abs(int n)\n Returns absolute value."},
- {"rand", "int rand(void)\n Returns pseudo-random int in [0, RAND_MAX]."},
- {"srand", "void srand(unsigned seed)\n Seeds the random number "
- "generator."},
- {"qsort", "void qsort(void *base, size_t n, size_t size, int(*cmp)(const "
- "void*, const void*))\n Quicksorts array in-place."},
- {NULL, NULL}};
-
- int found = 0;
- for (int i = 0; docs[i].name != NULL; i++)
- {
- if (0 == strcmp(sym, docs[i].name))
- {
- printf("\033[1;36m%s\033[0m\n%s\n", docs[i].name, docs[i].doc);
- found = 1;
- break;
- }
- }
- 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' | "
- "head -10",
- sym);
- FILE *mp = popen(man_cmd, "r");
- if (mp)
- {
- char buf[256];
- int lines = 0;
- while (fgets(buf, sizeof(buf), mp) && lines < 8)
- {
- printf("%s", buf);
- lines++;
- }
- int status = pclose(mp);
- if (0 == status && lines > 0)
- {
- found = 1;
- printf("\033[90m(man 3 %s)\033[0m\n", sym);
- }
- }
- if (!found)
- {
- printf("No documentation for '%s'.\n", sym);
- }
- }
- continue;
- }
- else
- {
- printf("Unknown command: %s\n", cmd_buf);
- continue;
+ 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, " ");
+ }
+ }
+
+ 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, expr);
+ strcat(probe_code, "); }");
+
+ char tmp_path[256];
+ sprintf(tmp_path, "/tmp/zprep_repl_type_%d.zc", rand());
+ FILE *f = fopen(tmp_path, "w");
+ if (f) {
+ fprintf(f, "%s", probe_code);
+ fclose(f);
+
+ char cmd[2048];
+ sprintf(cmd, "%s run -q %s 2>&1", self_path, tmp_path);
+
+ FILE *p = popen(cmd, "r");
+ if (p) {
+ char buf[1024];
+ int found = 0;
+ while (fgets(buf, sizeof(buf), p)) {
+ char *marker = "right operand has type '";
+ char *start = strstr(buf, marker);
+ if (start) {
+ start += strlen(marker);
+ char *end = strchr(start, '\'');
+ if (end) {
+ *end = 0;
+ printf("\033[1;36mType: %s\033[0m\n", start);
+ found = 1;
+ break;
+ }
}
+ }
+ pclose(p);
+ if (!found) {
+ printf("Type: <unknown>\n");
+ }
}
- }
-
- int in_quote = 0;
- int escaped = 0;
- for (int i = 0; line_buf[i]; i++)
- {
- char c = line_buf[i];
- if (escaped)
- {
- escaped = 0;
- continue;
+ }
+ free(probe_code);
+ continue;
+ } else if (0 == strncmp(cmd_buf, ":time ", 6)) {
+ // 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, "");
+
+ for (int i = 0; i < history_len; i++) {
+ if (is_header_line(history[i])) {
+ strcat(code, history[i]);
+ strcat(code, "\n");
}
- if (c == '\\')
- {
- escaped = 1;
- continue;
+ }
+ 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, " ");
}
- if (c == '"')
- {
- in_quote = !in_quote;
- continue;
+ }
+ strcat(code, "_z_restore_stdout();\n");
+ strcat(code, "raw { clock_t _start = clock(); }\n");
+ strcat(code, "for _i in 0..1000 { ");
+ strcat(code, expr);
+ strcat(code, "; }\n");
+ strcat(
+ code,
+ "raw { clock_t _end = clock(); double _elapsed = (double)(_end - "
+ "_start) / CLOCKS_PER_SEC; printf(\"1000 iterations: %.4fs "
+ "(%.6fs/iter)\\n\", _elapsed, _elapsed/1000); }\n");
+ strcat(code, "}");
+
+ char tmp_path[256];
+ sprintf(tmp_path, "/tmp/zprep_repl_time_%d.zc", rand());
+ FILE *f = fopen(tmp_path, "w");
+ if (f) {
+ fprintf(f, "%s", code);
+ fclose(f);
+ char cmd[2048];
+ sprintf(cmd, "%s run -q %s", self_path, tmp_path);
+ system(cmd);
+ }
+ free(code);
+ continue;
+ } else if (0 == strncmp(cmd_buf, ":c ", 3)) {
+ char *expr_buf = malloc(8192);
+ strcpy(expr_buf, cmd_buf + 3);
+
+ int brace_depth = 0;
+ for (char *p = expr_buf; *p; p++) {
+ if (*p == '{') {
+ brace_depth++;
+ } else if (*p == '}') {
+ brace_depth--;
}
+ }
- if (!in_quote)
- {
- if (c == '{')
- {
- brace_depth++;
- }
- if (c == '}')
- {
- brace_depth--;
- }
- if (c == '(')
- {
- paren_depth++;
- }
- if (c == ')')
- {
- paren_depth--;
- }
+ while (brace_depth > 0) {
+ printf("... ");
+ char more[1024];
+ if (!fgets(more, sizeof(more), stdin)) {
+ 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++) {
+ if (*p == '{') {
+ brace_depth++;
+ } else if (*p == '}') {
+ brace_depth--;
+ }
+ }
+ }
+
+ 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, "");
+
+ 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}");
+ free(expr_buf);
+
+ char tmp_path[256];
+ sprintf(tmp_path, "/tmp/zprep_repl_c_%d.zc", rand());
+ FILE *f = fopen(tmp_path, "w");
+ if (f) {
+ fprintf(f, "%s", code);
+ fclose(f);
+ char cmd[2048];
+ sprintf(cmd,
+ "%s build -q --emit-c -o /tmp/zprep_repl_out %s "
+ "2>/dev/null; sed "
+ "-n '/^int main() {$/,/^}$/p' /tmp/zprep_repl_out.c "
+ "2>/dev/null | "
+ "tail -n +3 | head -n -2 | sed 's/^ //'",
+ self_path, tmp_path);
+ system(cmd);
+ }
+ free(code);
+ continue;
+ } 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 *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");
+
+ char tmp_path[256];
+ sprintf(tmp_path, "/tmp/zprep_repl_run_%d.zc", rand());
+ FILE *f = fopen(tmp_path, "w");
+ if (f) {
+ fprintf(f, "%s", code);
+ fclose(f);
+ char cmd[2048];
+ sprintf(cmd, "%s run %s", self_path, tmp_path);
+ system(cmd);
+ }
+ free(code);
+ continue;
+ } else if (0 == strncmp(cmd_buf, ":doc ", 5)) {
+ char *sym = cmd_buf + 5;
+ while (*sym == ' ') {
+ sym++;
+ }
+ size_t symlen = strlen(sym);
+ while (symlen > 0 && sym[symlen - 1] == ' ') {
+ sym[--symlen] = 0;
+ }
+
+ // Documentation database
+
+ struct {
+ const char *name;
+ const char *doc;
+ } docs[] = {
+ {"Vec", "Vec<T> - Dynamic array (generic)\n Fields: data: T*, "
+ "len: usize, cap: "
+ "usize\n Methods: new, push, pop, get, set, insert, "
+ "remove, contains, "
+ "clear, free, clone, reverse, first, last, length, "
+ "is_empty, eq"},
+ {"Vec.new",
+ "fn Vec<T>::new() -> Vec<T>\n Creates an empty vector."},
+ {"Vec.push", "fn push(self, item: T)\n Appends item to the end. "
+ "Auto-grows capacity."},
+ {"Vec.pop",
+ "fn pop(self) -> T\n Removes and returns the last element. "
+ "Panics if empty."},
+ {"Vec.get",
+ "fn get(self, idx: usize) -> T\n Returns element at index. "
+ "Panics if out of bounds."},
+ {"Vec.set",
+ "fn set(self, idx: usize, item: T)\n Sets element at index. "
+ "Panics if out of bounds."},
+ {"Vec.insert",
+ "fn insert(self, idx: usize, item: T)\n Inserts item at "
+ "index, shifting elements right."},
+ {"Vec.remove",
+ "fn remove(self, idx: usize) -> T\n Removes and returns "
+ "element at index, shifting elements left."},
+ {"Vec.contains",
+ "fn contains(self, item: T) -> bool\n Returns true if "
+ "item is in the vector."},
+ {"Vec.clear",
+ "fn clear(self)\n Removes all elements but keeps capacity."},
+ {"Vec.free", "fn free(self)\n Frees memory. Sets data to null."},
+ {"Vec.clone",
+ "fn clone(self) -> Vec<T>\n Returns a shallow copy."},
+ {"Vec.reverse",
+ "fn reverse(self)\n Reverses elements in place."},
+ {"Vec.first", "fn first(self) -> T\n Returns first element. "
+ "Panics if empty."},
+ {"Vec.last",
+ "fn last(self) -> T\n Returns last element. Panics if empty."},
+ {"Vec.length",
+ "fn length(self) -> usize\n Returns number of elements."},
+ {"Vec.is_empty",
+ "fn is_empty(self) -> bool\n Returns true if length is 0."},
+ {"String", "String - Mutable string (alias for char*)\n "
+ "Methods: len, split, trim, "
+ "contains, starts_with, ends_with, to_upper, "
+ "to_lower, substring, find"},
+ {"String.len", "fn len(self) -> usize\n Returns string length."},
+ {"String.contains",
+ "fn contains(self, substr: string) -> bool\n Returns "
+ "true if string contains substr."},
+ {"String.starts_with",
+ "fn starts_with(self, prefix: string) -> bool\n "
+ "Returns true if string starts with prefix."},
+ {"String.ends_with",
+ "fn ends_with(self, suffix: string) -> bool\n "
+ "Returns true if string ends with suffix."},
+ {"String.substring",
+ "fn substring(self, start: usize, len: usize) -> "
+ "string\n Returns a substring. Caller must free."},
+ {"String.find",
+ "fn find(self, substr: string) -> int\n Returns index of "
+ "substr, or -1 if not found."},
+ {"println",
+ "println \"format string {expr}\"\n Prints to stdout with "
+ "newline. Auto-formats {expr} values."},
+ {"print", "print \"format string {expr}\"\n Prints to stdout "
+ "without newline."},
+ {"eprintln",
+ "eprintln \"format string\"\n Prints to stderr with newline."},
+ {"eprint",
+ "eprint \"format string\"\n Prints to stderr without newline."},
+ {"guard", "guard condition else action\n Early exit pattern. "
+ "Executes action if "
+ "condition is false.\n Example: guard ptr != NULL "
+ "else return;"},
+ {"defer",
+ "defer statement\n Executes statement at end of scope.\n "
+ "Example: defer free(ptr);"},
+ {"sizeof",
+ "sizeof(type) or sizeof(expr)\n Returns size in bytes."},
+ {"typeof", "typeof(expr)\n Returns the type of expression "
+ "(compile-time)."},
+ {"malloc",
+ "void *malloc(size_t size)\n Allocates size bytes. Returns "
+ "pointer or NULL. Free with free()."},
+ {"free", "void free(void *ptr)\n Frees memory allocated by "
+ "malloc/calloc/realloc."},
+ {"calloc",
+ "void *calloc(size_t n, size_t size)\n Allocates n*size bytes, "
+ "zeroed. Returns pointer or NULL."},
+ {"realloc",
+ "void *realloc(void *ptr, size_t size)\n Resizes allocation "
+ "to size bytes. May move memory."},
+ {"memcpy",
+ "void *memcpy(void *dest, const void *src, size_t n)\n Copies "
+ "n bytes from src to dest. Returns dest. No overlap."},
+ {"memmove",
+ "void *memmove(void *dest, const void *src, size_t n)\n "
+ "Copies n bytes, handles overlapping regions."},
+ {"memset", "void *memset(void *s, int c, size_t n)\n Sets n "
+ "bytes of s to value c."},
+ {"strlen",
+ "size_t strlen(const char *s)\n Returns length of string (not "
+ "including null terminator)."},
+ {"strcpy",
+ "char *strcpy(char *dest, const char *src)\n Copies src to "
+ "dest including null terminator. No bounds check."},
+ {"strncpy",
+ "char *strncpy(char *dest, const char *src, size_t n)\n "
+ "Copies up to n chars. May not null-terminate."},
+ {"strcat", "char *strcat(char *dest, const char *src)\n Appends "
+ "src to dest."},
+ {"strcmp",
+ "int strcmp(const char *s1, const char *s2)\n Compares "
+ "strings. Returns 0 if equal, <0 or >0 otherwise."},
+ {"strncmp",
+ "int strncmp(const char *s1, const char *s2, size_t n)\n "
+ "Compares up to n characters."},
+ {"strstr",
+ "char *strstr(const char *haystack, const char *needle)\n "
+ "Finds first occurrence of needle. Returns pointer or NULL."},
+ {"strchr",
+ "char *strchr(const char *s, int c)\n Finds first occurrence "
+ "of char c. Returns pointer or NULL."},
+ {"strdup",
+ "char *strdup(const char *s)\n Duplicates string. Caller must "
+ "free the result."},
+ {"printf",
+ "int printf(const char *fmt, ...)\n Prints formatted output to "
+ "stdout. Returns chars written."},
+ {"sprintf",
+ "int sprintf(char *str, const char *fmt, ...)\n Prints "
+ "formatted output to string buffer."},
+ {"snprintf",
+ "int snprintf(char *str, size_t n, const char *fmt, ...)\n "
+ "Safe sprintf with size limit."},
+ {"fprintf",
+ "int fprintf(FILE *f, const char *fmt, ...)\n Prints "
+ "formatted output to file stream."},
+ {"scanf", "int scanf(const char *fmt, ...)\n Reads formatted "
+ "input from stdin."},
+ {"fopen",
+ "FILE *fopen(const char *path, const char *mode)\n Opens file. "
+ "Modes: "
+ "\"r\", \"w\", \"a\", \"rb\", \"wb\". Returns NULL on error."},
+ {"fclose",
+ "int fclose(FILE *f)\n Closes file. Returns 0 on success."},
+ {"fread",
+ "size_t fread(void *ptr, size_t size, size_t n, FILE *f)\n "
+ "Reads n items of size bytes. Returns items read."},
+ {"fwrite",
+ "size_t fwrite(const void *ptr, size_t size, size_t n, FILE "
+ "*f)\n Writes n items of size bytes. Returns items written."},
+ {"fgets",
+ "char *fgets(char *s, int n, FILE *f)\n Reads line up to n-1 "
+ "chars. Includes newline. Returns s or NULL."},
+ {"fputs",
+ "int fputs(const char *s, FILE *f)\n Writes string to file. "
+ "Returns non-negative or EOF."},
+ {"exit", "void exit(int status)\n Terminates program with "
+ "status code. 0 "
+ "= success."},
+ {"atoi", "int atoi(const char *s)\n Converts string to int. "
+ "Returns 0 on error."},
+ {"atof",
+ "double atof(const char *s)\n Converts string to double."},
+ {"abs", "int abs(int n)\n Returns absolute value."},
+ {"rand",
+ "int rand(void)\n Returns pseudo-random int in [0, RAND_MAX]."},
+ {"srand", "void srand(unsigned seed)\n Seeds the random number "
+ "generator."},
+ {"qsort",
+ "void qsort(void *base, size_t n, size_t size, int(*cmp)(const "
+ "void*, const void*))\n Quicksorts array in-place."},
+ {NULL, NULL}};
+
+ int found = 0;
+ for (int i = 0; docs[i].name != NULL; i++) {
+ if (0 == strcmp(sym, docs[i].name)) {
+ printf("\033[1;36m%s\033[0m\n%s\n", docs[i].name, docs[i].doc);
+ found = 1;
+ break;
+ }
+ }
+ 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' | "
+ "head -10",
+ sym);
+ FILE *mp = popen(man_cmd, "r");
+ if (mp) {
+ char buf[256];
+ int lines = 0;
+ while (fgets(buf, sizeof(buf), mp) && lines < 8) {
+ printf("%s", buf);
+ lines++;
+ }
+ int status = pclose(mp);
+ if (0 == status && lines > 0) {
+ found = 1;
+ printf("\033[90m(man 3 %s)\033[0m\n", sym);
+ }
+ }
+ if (!found) {
+ printf("No documentation for '%s'.\n", sym);
+ }
+ }
+ continue;
+ } else {
+ printf("Unknown command: %s\n", cmd_buf);
+ continue;
}
+ }
+ }
- size_t len = strlen(line_buf);
- input_buffer = realloc(input_buffer, input_len + len + 1);
- strcpy(input_buffer + input_len, line_buf);
- input_len += len;
-
- if (brace_depth > 0 || paren_depth > 0)
- {
- continue;
- }
-
- if (input_len > 0 && input_buffer[input_len - 1] == '\n')
- {
- input_buffer[--input_len] = 0;
- }
-
- if (input_len == 0)
- {
- free(input_buffer);
- input_buffer = NULL;
- input_len = 0;
- brace_depth = 0;
- paren_depth = 0;
- continue;
+ int in_quote = 0;
+ int escaped = 0;
+ for (int i = 0; line_buf[i]; i++) {
+ char c = line_buf[i];
+ if (escaped) {
+ escaped = 0;
+ continue;
+ }
+ if (c == '\\') {
+ escaped = 1;
+ continue;
+ }
+ if (c == '"') {
+ in_quote = !in_quote;
+ continue;
+ }
+
+ if (!in_quote) {
+ if (c == '{') {
+ brace_depth++;
}
-
- // Add to history.
- if (history_len >= history_cap)
- {
- history_cap *= 2;
- history = realloc(history, history_cap * sizeof(char *));
+ if (c == '}') {
+ brace_depth--;
}
- history[history_len++] = strdup(input_buffer);
-
- free(input_buffer);
- input_buffer = NULL;
- input_len = 0;
- 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;
+ if (c == '(') {
+ paren_depth++;
}
- if (watches_len > 0)
- {
- total_size += 16 * 1024; // Plenty of space for watches. Yeah static ik.
+ if (c == ')') {
+ paren_depth--;
}
+ }
+ }
- 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");
- }
- }
+ size_t len = strlen(line_buf);
+ input_buffer = realloc(input_buffer, input_len + len + 1);
+ strcpy(input_buffer + input_len, line_buf);
+ input_len += len;
- strcat(full_code, "fn main() { _z_suppress_stdout(); ");
+ if (brace_depth > 0 || paren_depth > 0) {
+ continue;
+ }
- for (int i = 0; i < history_len - 1; i++)
- {
- if (is_header_line(history[i]))
- {
- continue;
- }
- strcat(full_code, history[i]);
- strcat(full_code, " ");
- }
+ if (input_len > 0 && input_buffer[input_len - 1] == '\n') {
+ input_buffer[--input_len] = 0;
+ }
- strcat(full_code, "_z_restore_stdout(); ");
-
- if (history_len > 0 && !is_header_line(history[history_len - 1]))
- {
- char *last_line = history[history_len - 1];
-
- char *check_buf = malloc(strlen(last_line) + 2);
- strcpy(check_buf, last_line);
- strcat(check_buf, ";");
-
- ParserContext ctx = {0};
- ctx.is_repl = 1;
- ctx.skip_preamble = 1;
- Lexer l;
- lexer_init(&l, check_buf);
- ASTNode *node = parse_statement(&ctx, &l);
- free(check_buf);
-
- int is_expr = 0;
- if (node)
- {
- ASTNode *child = node;
- if (child->type == NODE_EXPR_BINARY || child->type == NODE_EXPR_UNARY ||
- child->type == NODE_EXPR_LITERAL || child->type == NODE_EXPR_VAR ||
- child->type == NODE_EXPR_CALL || child->type == NODE_EXPR_MEMBER ||
- child->type == NODE_EXPR_INDEX || child->type == NODE_EXPR_CAST ||
- child->type == NODE_EXPR_SIZEOF || child->type == NODE_EXPR_STRUCT_INIT ||
- child->type == NODE_EXPR_ARRAY_LITERAL || child->type == NODE_EXPR_SLICE ||
- child->type == NODE_TERNARY || child->type == NODE_MATCH)
- {
- is_expr = 1;
- }
- }
+ if (input_len == 0) {
+ free(input_buffer);
+ input_buffer = NULL;
+ input_len = 0;
+ brace_depth = 0;
+ paren_depth = 0;
+ continue;
+ }
- 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, "");
-
- for (int i = 0; i < history_len - 1; i++)
- {
- if (is_header_line(history[i]))
- {
- strcat(probe_code, history[i]);
- strcat(probe_code, "\n");
- }
- }
+ // Add to history.
+ if (history_len >= history_cap) {
+ history_cap *= 2;
+ history = realloc(history, history_cap * sizeof(char *));
+ }
+ history[history_len++] = strdup(input_buffer);
- strcat(probe_code, "fn main() { _z_suppress_stdout(); ");
+ free(input_buffer);
+ input_buffer = NULL;
+ input_len = 0;
+ brace_depth = 0;
+ paren_depth = 0;
- for (int i = 0; i < history_len - 1; i++)
- {
- if (!is_header_line(history[i]))
- {
- strcat(probe_code, history[i]);
- strcat(probe_code, " ");
- }
- }
+ size_t total_size = 4096;
+ for (int i = 0; i < history_len; i++) {
+ total_size += strlen(history[i]) + 2;
+ }
+ if (watches_len > 0) {
+ total_size += 16 * 1024; // Plenty of space for watches. Yeah static ik.
+ }
- 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, last_line);
- strcat(probe_code, "); }");
-
- char p_path[256];
- sprintf(p_path, "/tmp/zprep_repl_probe_%d.zc", rand());
- FILE *pf = fopen(p_path, "w");
- if (pf)
- {
- fprintf(pf, "%s", probe_code);
- fclose(pf);
-
- char p_cmd[2048];
- sprintf(p_cmd, "%s run -q %s 2>&1", self_path, p_path);
-
- FILE *pp = popen(p_cmd, "r");
- int is_void = 0;
- if (pp)
- {
- char buf[1024];
- while (fgets(buf, sizeof(buf), pp))
- {
- if (strstr(buf, "void") && strstr(buf, "expression"))
- {
- is_void = 1;
- }
- }
- pclose(pp);
- }
+ char *full_code = malloc(total_size);
+ strcpy(full_code, "");
- if (!is_void)
- {
- strcat(full_code, "println \"{");
- strcat(full_code, last_line);
- strcat(full_code, "}\";");
- }
- else
- {
- strcat(full_code, last_line);
- }
- }
- else
- {
- strcat(full_code, last_line);
- }
- free(probe_code);
- }
- else
- {
- strcat(full_code, last_line);
- }
- }
+ // 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");
+ }
+ }
- if (watches_len > 0)
- {
- strcat(full_code, "; "); // separator.
- 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}\"; "
- "printf(\"\\n\"); ",
- watches[i], watches[i]);
- strcat(full_code, wbuf);
- }
- }
+ strcat(full_code, "fn main() { _z_suppress_stdout(); ");
- strcat(full_code, " }");
+ for (int i = 0; i < history_len - 1; i++) {
+ if (is_header_line(history[i])) {
+ continue;
+ }
+ strcat(full_code, history[i]);
+ strcat(full_code, " ");
+ }
- char tmp_path[256];
- sprintf(tmp_path, "/tmp/zprep_repl_%d.zc", rand());
- FILE *f = fopen(tmp_path, "w");
- if (!f)
- {
- printf("Error: Cannot write temp file\n");
- free(full_code);
- break;
+ strcat(full_code, "_z_restore_stdout(); ");
+
+ if (history_len > 0 && !is_header_line(history[history_len - 1])) {
+ char *last_line = history[history_len - 1];
+
+ char *check_buf = malloc(strlen(last_line) + 2);
+ strcpy(check_buf, last_line);
+ strcat(check_buf, ";");
+
+ ParserContext ctx = {0};
+ ctx.is_repl = 1;
+ ctx.skip_preamble = 1;
+ Lexer l;
+ lexer_init(&l, check_buf);
+ ASTNode *node = parse_statement(&ctx, &l);
+ free(check_buf);
+
+ int is_expr = 0;
+ if (node) {
+ ASTNode *child = node;
+ if (child->type == NODE_EXPR_BINARY || child->type == NODE_EXPR_UNARY ||
+ child->type == NODE_EXPR_LITERAL || child->type == NODE_EXPR_VAR ||
+ child->type == NODE_EXPR_CALL || child->type == NODE_EXPR_MEMBER ||
+ child->type == NODE_EXPR_INDEX || child->type == NODE_EXPR_CAST ||
+ child->type == NODE_EXPR_SIZEOF ||
+ child->type == NODE_EXPR_STRUCT_INIT ||
+ child->type == NODE_EXPR_ARRAY_LITERAL ||
+ child->type == NODE_EXPR_SLICE || child->type == NODE_TERNARY ||
+ child->type == NODE_MATCH) {
+ is_expr = 1;
}
- fprintf(f, "%s", full_code);
- fclose(f);
- free(full_code);
+ }
- char cmd[2048];
- sprintf(cmd, "%s run -q %s", self_path, tmp_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, "");
+
+ for (int i = 0; i < history_len - 1; i++) {
+ if (is_header_line(history[i])) {
+ strcat(probe_code, history[i]);
+ strcat(probe_code, "\n");
+ }
+ }
- int ret = system(cmd);
- printf("\n");
+ strcat(probe_code, "fn main() { _z_suppress_stdout(); ");
- if (0 != ret)
- {
- free(history[--history_len]);
+ for (int i = 0; i < history_len - 1; i++) {
+ if (!is_header_line(history[i])) {
+ strcat(probe_code, history[i]);
+ strcat(probe_code, " ");
+ }
}
- }
- if (history_path[0])
- {
- FILE *hf = fopen(history_path, "w");
- if (hf)
- {
- for (int i = 0; i < history_len; i++)
- {
- fprintf(hf, "%s\n", history[i]);
+ 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, last_line);
+ strcat(probe_code, "); }");
+
+ char p_path[256];
+ sprintf(p_path, "/tmp/zprep_repl_probe_%d.zc", rand());
+ FILE *pf = fopen(p_path, "w");
+ if (pf) {
+ fprintf(pf, "%s", probe_code);
+ fclose(pf);
+
+ char p_cmd[2048];
+ sprintf(p_cmd, "%s run -q %s 2>&1", self_path, p_path);
+
+ FILE *pp = popen(p_cmd, "r");
+ int is_void = 0;
+ if (pp) {
+ char buf[1024];
+ while (fgets(buf, sizeof(buf), pp)) {
+ if (strstr(buf, "void") && strstr(buf, "expression")) {
+ is_void = 1;
+ }
}
- fclose(hf);
+ pclose(pp);
+ }
+
+ if (!is_void) {
+ strcat(full_code, "println \"{");
+ strcat(full_code, last_line);
+ strcat(full_code, "}\";");
+ } else {
+ strcat(full_code, last_line);
+ }
+ } else {
+ strcat(full_code, last_line);
}
+ free(probe_code);
+ } else {
+ strcat(full_code, last_line);
+ }
+ }
+
+ if (watches_len > 0) {
+ strcat(full_code, "; "); // separator.
+ 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}\"; "
+ "printf(\"\\n\"); ",
+ watches[i], watches[i]);
+ strcat(full_code, wbuf);
+ }
+ }
+
+ strcat(full_code, " }");
+
+ char tmp_path[256];
+ sprintf(tmp_path, "/tmp/zprep_repl_%d.zc", rand());
+ FILE *f = fopen(tmp_path, "w");
+ if (!f) {
+ printf("Error: Cannot write temp file\n");
+ free(full_code);
+ break;
}
+ fprintf(f, "%s", full_code);
+ fclose(f);
+ free(full_code);
+
+ char cmd[2048];
+ sprintf(cmd, "%s run -q %s", self_path, tmp_path);
+
+ int ret = system(cmd);
+ printf("\n");
- for (int i = 0; i < history_len; i++)
- {
- free(history[i]);
+ if (0 != ret) {
+ free(history[--history_len]);
}
- free(history);
- if (input_buffer)
- {
- free(input_buffer);
+ }
+
+ if (history_path[0]) {
+ FILE *hf = fopen(history_path, "w");
+ if (hf) {
+ for (int i = 0; i < history_len; i++) {
+ fprintf(hf, "%s\n", history[i]);
+ }
+ fclose(hf);
}
+ }
+
+ for (int i = 0; i < history_len; i++) {
+ free(history[i]);
+ }
+ free(history);
+ if (input_buffer) {
+ free(input_buffer);
+ }
}
diff --git a/src/utils/utils.c b/src/utils/utils.c
index 66a0a22..cee71d8 100644
--- a/src/utils/utils.c
+++ b/src/utils/utils.c
@@ -8,523 +8,476 @@ ParserContext *g_parser_ctx = NULL;
// ** Arena Implementation **
#define ARENA_BLOCK_SIZE (1024 * 1024)
-typedef struct ArenaBlock
-{
- struct ArenaBlock *next;
- size_t used;
- size_t cap;
- char data[];
+typedef struct ArenaBlock {
+ struct ArenaBlock *next;
+ size_t used;
+ size_t cap;
+ char data[];
} ArenaBlock;
static ArenaBlock *current_block = NULL;
-static void *arena_alloc_raw(size_t size)
-{
- size_t actual_size = size + sizeof(size_t);
- actual_size = (actual_size + 7) & ~7;
+static void *arena_alloc_raw(size_t size) {
+ size_t actual_size = size + sizeof(size_t);
+ actual_size = (actual_size + 7) & ~7;
- if (!current_block || (current_block->used + actual_size > current_block->cap))
- {
- size_t block_size = actual_size > ARENA_BLOCK_SIZE ? actual_size : ARENA_BLOCK_SIZE;
+ if (!current_block ||
+ (current_block->used + actual_size > current_block->cap)) {
+ size_t block_size =
+ actual_size > ARENA_BLOCK_SIZE ? actual_size : ARENA_BLOCK_SIZE;
#undef malloc
- ArenaBlock *new_block = malloc(sizeof(ArenaBlock) + block_size);
- if (!new_block)
- {
- fprintf(stderr, "Fatal: Out of memory\n");
- exit(1);
- }
-
- new_block->cap = block_size;
- new_block->used = 0;
- new_block->next = current_block;
- current_block = new_block;
- }
-
- void *ptr = current_block->data + current_block->used;
- current_block->used += actual_size;
- *(size_t *)ptr = size;
- return (char *)ptr + sizeof(size_t);
-}
-
-void *xmalloc(size_t size)
-{
- return arena_alloc_raw(size);
-}
-
-void *xcalloc(size_t n, size_t size)
-{
- size_t total = n * size;
- void *p = arena_alloc_raw(total);
- memset(p, 0, total);
- return p;
-}
-
-void *xrealloc(void *ptr, size_t new_size)
-{
- if (!ptr)
- {
- return xmalloc(new_size);
- }
- size_t *header = (size_t *)((char *)ptr - sizeof(size_t));
- size_t old_size = *header;
- if (new_size <= old_size)
- {
- return ptr;
- }
- void *new_ptr = xmalloc(new_size);
- memcpy(new_ptr, ptr, old_size);
- return new_ptr;
-}
-
-char *xstrdup(const char *s)
-{
- if (!s)
- {
- return NULL;
- }
- size_t len = strlen(s);
- char *d = xmalloc(len + 1);
- memcpy(d, s, len);
- d[len] = 0;
- return d;
-}
-
-void zpanic(const char *fmt, ...)
-{
- va_list a;
- va_start(a, fmt);
- fprintf(stderr, COLOR_RED "error: " COLOR_RESET COLOR_BOLD);
- vfprintf(stderr, fmt, a);
- fprintf(stderr, COLOR_RESET "\n");
- va_end(a);
- exit(1);
+ ArenaBlock *new_block = malloc(sizeof(ArenaBlock) + block_size);
+ if (!new_block) {
+ fprintf(stderr, "Fatal: Out of memory\n");
+ exit(1);
+ }
+
+ new_block->cap = block_size;
+ new_block->used = 0;
+ new_block->next = current_block;
+ current_block = new_block;
+ }
+
+ void *ptr = current_block->data + current_block->used;
+ current_block->used += actual_size;
+ *(size_t *)ptr = size;
+ return (char *)ptr + sizeof(size_t);
+}
+
+void *xmalloc(size_t size) { return arena_alloc_raw(size); }
+
+void *xcalloc(size_t n, size_t size) {
+ size_t total = n * size;
+ void *p = arena_alloc_raw(total);
+ memset(p, 0, total);
+ return p;
+}
+
+void *xrealloc(void *ptr, size_t new_size) {
+ if (!ptr) {
+ return xmalloc(new_size);
+ }
+ size_t *header = (size_t *)((char *)ptr - sizeof(size_t));
+ size_t old_size = *header;
+ if (new_size <= old_size) {
+ return ptr;
+ }
+ void *new_ptr = xmalloc(new_size);
+ memcpy(new_ptr, ptr, old_size);
+ return new_ptr;
+}
+
+char *xstrdup(const char *s) {
+ if (!s) {
+ return NULL;
+ }
+ size_t len = strlen(s);
+ char *d = xmalloc(len + 1);
+ memcpy(d, s, len);
+ d[len] = 0;
+ return d;
+}
+
+void zpanic(const char *fmt, ...) {
+ va_list a;
+ va_start(a, fmt);
+ fprintf(stderr, COLOR_RED "error: " COLOR_RESET COLOR_BOLD);
+ vfprintf(stderr, fmt, a);
+ fprintf(stderr, COLOR_RESET "\n");
+ va_end(a);
+ exit(1);
}
// Warning system (non-fatal).
-void zwarn(const char *fmt, ...)
-{
- if (g_config.quiet)
- {
- return;
- }
- g_warning_count++;
- va_list a;
- va_start(a, fmt);
- fprintf(stderr, COLOR_YELLOW "warning: " COLOR_RESET COLOR_BOLD);
- vfprintf(stderr, fmt, a);
- fprintf(stderr, COLOR_RESET "\n");
- va_end(a);
-}
-
-void zwarn_at(Token t, const char *fmt, ...)
-{
- if (g_config.quiet)
- {
- return;
- }
- // Header: 'warning: message'.
- g_warning_count++;
- va_list a;
- va_start(a, fmt);
- fprintf(stderr, COLOR_YELLOW "warning: " COLOR_RESET COLOR_BOLD);
- vfprintf(stderr, fmt, a);
- fprintf(stderr, COLOR_RESET "\n");
- va_end(a);
-
- // Location.
- fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n", g_current_filename, t.line,
- t.col);
-
- // Context. Only if token has valid data.
- if (t.start)
- {
- const char *line_start = t.start - (t.col - 1);
- const char *line_end = t.start;
- while (*line_end && *line_end != '\n')
- {
- line_end++;
- }
- int line_len = line_end - line_start;
-
- fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET);
- fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len, line_start);
- fprintf(stderr, COLOR_BLUE " | " COLOR_RESET);
-
- // Caret.
- for (int i = 0; i < t.col - 1; i++)
- {
- fprintf(stderr, " ");
- }
- fprintf(stderr, COLOR_YELLOW "^ here" COLOR_RESET "\n");
- fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET);
- }
-}
-
-void zpanic_at(Token t, const char *fmt, ...)
-{
- // Header: 'error: message'.
- va_list a;
- va_start(a, fmt);
- fprintf(stderr, COLOR_RED "error: " COLOR_RESET COLOR_BOLD);
- vfprintf(stderr, fmt, a);
- fprintf(stderr, COLOR_RESET "\n");
- va_end(a);
-
- // Location: '--> file:line:col'.
- fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n", g_current_filename, t.line,
- t.col);
-
- // Context line.
+void zwarn(const char *fmt, ...) {
+ if (g_config.quiet) {
+ return;
+ }
+ g_warning_count++;
+ va_list a;
+ va_start(a, fmt);
+ fprintf(stderr, COLOR_YELLOW "warning: " COLOR_RESET COLOR_BOLD);
+ vfprintf(stderr, fmt, a);
+ fprintf(stderr, COLOR_RESET "\n");
+ va_end(a);
+}
+
+void zwarn_at(Token t, const char *fmt, ...) {
+ if (g_config.quiet) {
+ return;
+ }
+ // Header: 'warning: message'.
+ g_warning_count++;
+ va_list a;
+ va_start(a, fmt);
+ fprintf(stderr, COLOR_YELLOW "warning: " COLOR_RESET COLOR_BOLD);
+ vfprintf(stderr, fmt, a);
+ fprintf(stderr, COLOR_RESET "\n");
+ va_end(a);
+
+ // Location.
+ fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n",
+ g_current_filename, t.line, t.col);
+
+ // Context. Only if token has valid data.
+ if (t.start) {
const char *line_start = t.start - (t.col - 1);
const char *line_end = t.start;
- while (*line_end && *line_end != '\n')
- {
- line_end++;
+ while (*line_end && *line_end != '\n') {
+ line_end++;
}
int line_len = line_end - line_start;
- // Visual bar.
fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET);
- fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len, line_start);
+ fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len,
+ line_start);
fprintf(stderr, COLOR_BLUE " | " COLOR_RESET);
- // caret
- for (int i = 0; i < t.col - 1; i++)
- {
- fprintf(stderr, " ");
+ // Caret.
+ for (int i = 0; i < t.col - 1; i++) {
+ fprintf(stderr, " ");
}
- fprintf(stderr, COLOR_RED "^ here" COLOR_RESET "\n");
+ fprintf(stderr, COLOR_YELLOW "^ here" COLOR_RESET "\n");
fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET);
-
- if (g_parser_ctx && g_parser_ctx->is_fault_tolerant && g_parser_ctx->on_error)
- {
- // Construct error message buffer
- char msg[1024];
- va_list args2;
- va_start(args2, fmt);
- vsnprintf(msg, sizeof(msg), fmt, args2);
- va_end(args2);
-
- g_parser_ctx->on_error(g_parser_ctx->error_callback_data, t, msg);
- return; // Recover!
- }
-
- exit(1);
+ }
+}
+
+void zpanic_at(Token t, const char *fmt, ...) {
+ // Header: 'error: message'.
+ va_list a;
+ va_start(a, fmt);
+ fprintf(stderr, COLOR_RED "error: " COLOR_RESET COLOR_BOLD);
+ vfprintf(stderr, fmt, a);
+ fprintf(stderr, COLOR_RESET "\n");
+ va_end(a);
+
+ // Location: '--> file:line:col'.
+ fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n",
+ g_current_filename, t.line, t.col);
+
+ // Context line.
+ const char *line_start = t.start - (t.col - 1);
+ const char *line_end = t.start;
+ while (*line_end && *line_end != '\n') {
+ line_end++;
+ }
+ int line_len = line_end - line_start;
+
+ // Visual bar.
+ fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET);
+ fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len,
+ line_start);
+ fprintf(stderr, COLOR_BLUE " | " COLOR_RESET);
+
+ // caret
+ for (int i = 0; i < t.col - 1; i++) {
+ fprintf(stderr, " ");
+ }
+ fprintf(stderr, COLOR_RED "^ here" COLOR_RESET "\n");
+ fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET);
+
+ if (g_parser_ctx && g_parser_ctx->is_fault_tolerant &&
+ g_parser_ctx->on_error) {
+ // Construct error message buffer
+ char msg[1024];
+ va_list args2;
+ va_start(args2, fmt);
+ vsnprintf(msg, sizeof(msg), fmt, args2);
+ va_end(args2);
+
+ g_parser_ctx->on_error(g_parser_ctx->error_callback_data, t, msg);
+ return; // Recover!
+ }
+
+ exit(1);
}
// Enhanced error with suggestion.
-void zpanic_with_suggestion(Token t, const char *msg, const char *suggestion)
-{
- // Header.
- fprintf(stderr, COLOR_RED "error: " COLOR_RESET COLOR_BOLD "%s" COLOR_RESET "\n", msg);
-
- // Location.
- fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n", g_current_filename, t.line,
- t.col);
-
- // Context.
- const char *line_start = t.start - (t.col - 1);
- const char *line_end = t.start;
- while (*line_end && *line_end != '\n')
- {
- line_end++;
- }
- int line_len = line_end - line_start;
-
+void zpanic_with_suggestion(Token t, const char *msg, const char *suggestion) {
+ // Header.
+ fprintf(stderr,
+ COLOR_RED "error: " COLOR_RESET COLOR_BOLD "%s" COLOR_RESET "\n",
+ msg);
+
+ // Location.
+ fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n",
+ g_current_filename, t.line, t.col);
+
+ // Context.
+ const char *line_start = t.start - (t.col - 1);
+ const char *line_end = t.start;
+ while (*line_end && *line_end != '\n') {
+ line_end++;
+ }
+ int line_len = line_end - line_start;
+
+ fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET);
+ fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len,
+ line_start);
+ fprintf(stderr, COLOR_BLUE " | " COLOR_RESET);
+ for (int i = 0; i < t.col - 1; i++) {
+ fprintf(stderr, " ");
+ }
+ fprintf(stderr, COLOR_RED "^ here" COLOR_RESET "\n");
+
+ // Suggestion.
+ if (suggestion) {
fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET);
- fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len, line_start);
- fprintf(stderr, COLOR_BLUE " | " COLOR_RESET);
- for (int i = 0; i < t.col - 1; i++)
- {
- fprintf(stderr, " ");
- }
- fprintf(stderr, COLOR_RED "^ here" COLOR_RESET "\n");
-
- // Suggestion.
- if (suggestion)
- {
- fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET);
- fprintf(stderr, COLOR_CYAN " = help: " COLOR_RESET "%s\n", suggestion);
- }
+ fprintf(stderr, COLOR_CYAN " = help: " COLOR_RESET "%s\n", suggestion);
+ }
- exit(1);
+ exit(1);
}
// Specific error types with helpful messages.
-void error_undefined_function(Token t, const char *func_name, const char *suggestion)
-{
- char msg[256];
- sprintf(msg, "Undefined function '%s'", func_name);
-
- if (suggestion)
- {
- char help[512];
- sprintf(help, "Did you mean '%s'?", suggestion);
- zpanic_with_suggestion(t, msg, help);
- }
- else
- {
- zpanic_with_suggestion(t, msg, "Check if the function is defined or imported");
- }
-}
-
-void error_wrong_arg_count(Token t, const char *func_name, int expected, int got)
-{
- char msg[256];
- sprintf(msg, "Wrong number of arguments to function '%s'", func_name);
-
- char help[256];
- sprintf(help, "Expected %d argument%s, but got %d", expected, expected == 1 ? "" : "s", got);
-
- zpanic_with_suggestion(t, msg, help);
-}
-
-void error_undefined_field(Token t, const char *struct_name, const char *field_name,
- const char *suggestion)
-{
- char msg[256];
- sprintf(msg, "Struct '%s' has no field '%s'", struct_name, field_name);
-
- if (suggestion)
- {
- char help[256];
- sprintf(help, "Did you mean '%s'?", suggestion);
- zpanic_with_suggestion(t, msg, help);
- }
- else
- {
- zpanic_with_suggestion(t, msg, "Check the struct definition");
- }
-}
-
-void error_type_expected(Token t, const char *expected, const char *got)
-{
- char msg[256];
- sprintf(msg, "Type mismatch");
+void error_undefined_function(Token t, const char *func_name,
+ const char *suggestion) {
+ char msg[256];
+ sprintf(msg, "Undefined function '%s'", func_name);
+ if (suggestion) {
char help[512];
- sprintf(help, "Expected type '%s', but found '%s'", expected, got);
-
+ sprintf(help, "Did you mean '%s'?", suggestion);
zpanic_with_suggestion(t, msg, help);
+ } else {
+ zpanic_with_suggestion(t, msg,
+ "Check if the function is defined or imported");
+ }
}
-void error_cannot_index(Token t, const char *type_name)
-{
- char msg[256];
- sprintf(msg, "Cannot index into type '%s'", type_name);
+void error_wrong_arg_count(Token t, const char *func_name, int expected,
+ int got) {
+ char msg[256];
+ sprintf(msg, "Wrong number of arguments to function '%s'", func_name);
- zpanic_with_suggestion(t, msg, "Only arrays and slices can be indexed");
-}
-
-void warn_unused_variable(Token t, const char *var_name)
-{
- if (g_config.quiet)
- {
- return;
- }
- char msg[256];
- sprintf(msg, "Unused variable '%s'", var_name);
- zwarn_at(t, "%s", msg);
- fprintf(stderr,
- COLOR_CYAN " = note: " COLOR_RESET "Consider removing it or prefixing with '_'\n");
-}
+ char help[256];
+ sprintf(help, "Expected %d argument%s, but got %d", expected,
+ expected == 1 ? "" : "s", got);
-void warn_shadowing(Token t, const char *var_name)
-{
- if (g_config.quiet)
- {
- return;
- }
- char msg[256];
- sprintf(msg, "Variable '%s' shadows a previous declaration", var_name);
- zwarn_at(t, "%s", msg);
- fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "This can lead to confusion\n");
+ zpanic_with_suggestion(t, msg, help);
}
-void warn_unreachable_code(Token t)
-{
- if (g_config.quiet)
- {
- return;
- }
- zwarn_at(t, "Unreachable code detected");
- fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "This code will never execute\n");
-}
+void error_undefined_field(Token t, const char *struct_name,
+ const char *field_name, const char *suggestion) {
+ char msg[256];
+ sprintf(msg, "Struct '%s' has no field '%s'", struct_name, field_name);
-void warn_implicit_conversion(Token t, const char *from_type, const char *to_type)
-{
- if (g_config.quiet)
- {
- return;
- }
- char msg[256];
- sprintf(msg, "Implicit conversion from '%s' to '%s'", from_type, to_type);
- zwarn_at(t, "%s", msg);
- fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "Consider using an explicit cast\n");
-}
-
-void warn_missing_return(Token t, const char *func_name)
-{
- if (g_config.quiet)
- {
- return;
- }
- char msg[256];
- sprintf(msg, "Function '%s' may not return a value in all paths", func_name);
- zwarn_at(t, "%s", msg);
- fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET
- "Add a return statement or make the function return 'void'\n");
-}
-
-void warn_comparison_always_true(Token t, const char *reason)
-{
- if (g_config.quiet)
- {
- return;
- }
- zwarn_at(t, "Comparison is always true");
- if (reason)
- {
- fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "%s\n", reason);
- }
-}
-
-void warn_comparison_always_false(Token t, const char *reason)
-{
- if (g_config.quiet)
- {
- return;
- }
- zwarn_at(t, "Comparison is always false");
- if (reason)
- {
- fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "%s\n", reason);
- }
-}
-
-void warn_unused_parameter(Token t, const char *param_name, const char *func_name)
-{
- if (g_config.quiet)
- {
- return;
- }
- char msg[256];
- sprintf(msg, "Unused parameter '%s' in function '%s'", param_name, func_name);
- zwarn_at(t, "%s", msg);
- fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET
- "Consider prefixing with '_' if intentionally unused\n");
-}
-
-void warn_narrowing_conversion(Token t, const char *from_type, const char *to_type)
-{
- if (g_config.quiet)
- {
- return;
- }
- char msg[256];
- sprintf(msg, "Narrowing conversion from '%s' to '%s'", from_type, to_type);
- zwarn_at(t, "%s", msg);
- fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "This may cause data loss\n");
-}
-
-void warn_division_by_zero(Token t)
-{
- if (g_config.quiet)
- {
- return;
- }
- zwarn_at(t, "Division by zero");
- fprintf(stderr,
- COLOR_CYAN " = note: " COLOR_RESET "This will cause undefined behavior at runtime\n");
-}
-
-void warn_integer_overflow(Token t, const char *type_name, long long value)
-{
- if (g_config.quiet)
- {
- return;
- }
- char msg[256];
- sprintf(msg, "Integer literal %lld overflows type '%s'", value, type_name);
- zwarn_at(t, "%s", msg);
- fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "Value will be truncated\n");
-}
-
-void warn_array_bounds(Token t, int index, int size)
-{
- if (g_config.quiet)
- {
- return;
- }
- char msg[256];
- sprintf(msg, "Array index %d is out of bounds for array of size %d", index, size);
- zwarn_at(t, "%s", msg);
- fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "Valid indices are 0 to %d\n", size - 1);
-}
-
-void warn_format_string(Token t, int arg_num, const char *expected, const char *got)
-{
- if (g_config.quiet)
- {
- return;
- }
- char msg[256];
- sprintf(msg, "Format argument %d: expected '%s', got '%s'", arg_num, expected, got);
- zwarn_at(t, "%s", msg);
- fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET
- "Mismatched format specifier may cause undefined behavior\n");
-}
-
-void warn_null_pointer(Token t, const char *expr)
-{
- if (g_config.quiet)
- {
- return;
- }
- char msg[256];
- sprintf(msg, "Potential null pointer access in '%s'", expr);
- zwarn_at(t, "%s", msg);
- fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "Add a null check before accessing\n");
-}
-
-char *load_file(const char *fn)
-{
- FILE *f = fopen(fn, "rb");
- if (!f)
- {
- char *root = getenv("ZC_ROOT");
- if (root)
- {
- char path[1024];
- snprintf(path, sizeof(path), "%s/%s", root, fn);
- f = fopen(path, "rb");
- }
- }
- if (!f)
- {
- char path[1024];
- snprintf(path, sizeof(path), "/usr/local/share/zenc/%s", fn);
- f = fopen(path, "rb");
- }
- if (!f)
- {
- char path[1024];
- snprintf(path, sizeof(path), "/usr/share/zenc/%s", fn);
- f = fopen(path, "rb");
- }
-
- if (!f)
- {
- return 0;
- }
- fseek(f, 0, SEEK_END);
- long l = ftell(f);
- rewind(f);
- char *b = xmalloc(l + 1);
- fread(b, 1, l, f);
- b[l] = 0;
- fclose(f);
- return b;
+ if (suggestion) {
+ char help[256];
+ sprintf(help, "Did you mean '%s'?", suggestion);
+ zpanic_with_suggestion(t, msg, help);
+ } else {
+ zpanic_with_suggestion(t, msg, "Check the struct definition");
+ }
+}
+
+void error_type_expected(Token t, const char *expected, const char *got) {
+ char msg[256];
+ sprintf(msg, "Type mismatch");
+
+ char help[512];
+ sprintf(help, "Expected type '%s', but found '%s'", expected, got);
+
+ zpanic_with_suggestion(t, msg, help);
+}
+
+void error_cannot_index(Token t, const char *type_name) {
+ char msg[256];
+ sprintf(msg, "Cannot index into type '%s'", type_name);
+
+ zpanic_with_suggestion(t, msg, "Only arrays and slices can be indexed");
+}
+
+void warn_unused_variable(Token t, const char *var_name) {
+ if (g_config.quiet) {
+ return;
+ }
+ char msg[256];
+ sprintf(msg, "Unused variable '%s'", var_name);
+ zwarn_at(t, "%s", msg);
+ fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET
+ "Consider removing it or prefixing with '_'\n");
+}
+
+void warn_shadowing(Token t, const char *var_name) {
+ if (g_config.quiet) {
+ return;
+ }
+ char msg[256];
+ sprintf(msg, "Variable '%s' shadows a previous declaration", var_name);
+ zwarn_at(t, "%s", msg);
+ fprintf(stderr,
+ COLOR_CYAN " = note: " COLOR_RESET "This can lead to confusion\n");
+}
+
+void warn_unreachable_code(Token t) {
+ if (g_config.quiet) {
+ return;
+ }
+ zwarn_at(t, "Unreachable code detected");
+ fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET
+ "This code will never execute\n");
+}
+
+void warn_implicit_conversion(Token t, const char *from_type,
+ const char *to_type) {
+ if (g_config.quiet) {
+ return;
+ }
+ char msg[256];
+ sprintf(msg, "Implicit conversion from '%s' to '%s'", from_type, to_type);
+ zwarn_at(t, "%s", msg);
+ fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET
+ "Consider using an explicit cast\n");
+}
+
+void warn_missing_return(Token t, const char *func_name) {
+ if (g_config.quiet) {
+ return;
+ }
+ char msg[256];
+ sprintf(msg, "Function '%s' may not return a value in all paths", func_name);
+ zwarn_at(t, "%s", msg);
+ fprintf(stderr, COLOR_CYAN
+ " = note: " COLOR_RESET
+ "Add a return statement or make the function return 'void'\n");
+}
+
+void warn_comparison_always_true(Token t, const char *reason) {
+ if (g_config.quiet) {
+ return;
+ }
+ zwarn_at(t, "Comparison is always true");
+ if (reason) {
+ fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "%s\n", reason);
+ }
+}
+
+void warn_comparison_always_false(Token t, const char *reason) {
+ if (g_config.quiet) {
+ return;
+ }
+ zwarn_at(t, "Comparison is always false");
+ if (reason) {
+ fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "%s\n", reason);
+ }
+}
+
+void warn_unused_parameter(Token t, const char *param_name,
+ const char *func_name) {
+ if (g_config.quiet) {
+ return;
+ }
+ char msg[256];
+ sprintf(msg, "Unused parameter '%s' in function '%s'", param_name, func_name);
+ zwarn_at(t, "%s", msg);
+ fprintf(stderr,
+ COLOR_CYAN " = note: " COLOR_RESET
+ "Consider prefixing with '_' if intentionally unused\n");
+}
+
+void warn_narrowing_conversion(Token t, const char *from_type,
+ const char *to_type) {
+ if (g_config.quiet) {
+ return;
+ }
+ char msg[256];
+ sprintf(msg, "Narrowing conversion from '%s' to '%s'", from_type, to_type);
+ zwarn_at(t, "%s", msg);
+ fprintf(stderr,
+ COLOR_CYAN " = note: " COLOR_RESET "This may cause data loss\n");
+}
+
+void warn_division_by_zero(Token t) {
+ if (g_config.quiet) {
+ return;
+ }
+ zwarn_at(t, "Division by zero");
+ fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET
+ "This will cause undefined behavior at runtime\n");
+}
+
+void warn_integer_overflow(Token t, const char *type_name, long long value) {
+ if (g_config.quiet) {
+ return;
+ }
+ char msg[256];
+ sprintf(msg, "Integer literal %lld overflows type '%s'", value, type_name);
+ zwarn_at(t, "%s", msg);
+ fprintf(stderr,
+ COLOR_CYAN " = note: " COLOR_RESET "Value will be truncated\n");
+}
+
+void warn_array_bounds(Token t, int index, int size) {
+ if (g_config.quiet) {
+ return;
+ }
+ char msg[256];
+ sprintf(msg, "Array index %d is out of bounds for array of size %d", index,
+ size);
+ zwarn_at(t, "%s", msg);
+ fprintf(stderr,
+ COLOR_CYAN " = note: " COLOR_RESET "Valid indices are 0 to %d\n",
+ size - 1);
+}
+
+void warn_format_string(Token t, int arg_num, const char *expected,
+ const char *got) {
+ if (g_config.quiet) {
+ return;
+ }
+ char msg[256];
+ sprintf(msg, "Format argument %d: expected '%s', got '%s'", arg_num, expected,
+ got);
+ zwarn_at(t, "%s", msg);
+ fprintf(stderr, COLOR_CYAN
+ " = note: " COLOR_RESET
+ "Mismatched format specifier may cause undefined behavior\n");
+}
+
+void warn_null_pointer(Token t, const char *expr) {
+ if (g_config.quiet) {
+ return;
+ }
+ char msg[256];
+ sprintf(msg, "Potential null pointer access in '%s'", expr);
+ zwarn_at(t, "%s", msg);
+ fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET
+ "Add a null check before accessing\n");
+}
+
+char *load_file(const char *fn) {
+ FILE *f = fopen(fn, "rb");
+ if (!f) {
+ char *root = getenv("ZC_ROOT");
+ if (root) {
+ char path[1024];
+ snprintf(path, sizeof(path), "%s/%s", root, fn);
+ f = fopen(path, "rb");
+ }
+ }
+ if (!f) {
+ char path[1024];
+ snprintf(path, sizeof(path), "/usr/local/share/zenc/%s", fn);
+ f = fopen(path, "rb");
+ }
+ if (!f) {
+ char path[1024];
+ snprintf(path, sizeof(path), "/usr/share/zenc/%s", fn);
+ f = fopen(path, "rb");
+ }
+
+ if (!f) {
+ return 0;
+ }
+ fseek(f, 0, SEEK_END);
+ long l = ftell(f);
+ rewind(f);
+ char *b = xmalloc(l + 1);
+ fread(b, 1, l, f);
+ b[l] = 0;
+ fclose(f);
+ return b;
}
// ** Build Directives **
@@ -533,259 +486,202 @@ char g_cflags[MAX_FLAGS_SIZE] = "";
int g_warning_count = 0;
CompilerConfig g_config = {0};
-void scan_build_directives(ParserContext *ctx, const char *src)
-{
- const char *p = src;
- while (*p)
- {
- if (p[0] == '/' && p[1] == '/' && p[2] == '>')
- {
- p += 3;
- while (*p == ' ')
- {
- p++;
- }
-
- const char *start = p;
- int len = 0;
- while (p[len] && p[len] != '\n')
- {
- len++;
- }
-
- char line[2048];
- if (len >= 2047)
- {
- len = 2047;
- }
- strncpy(line, start, len);
- line[len] = 0;
-
- if (0 == strncmp(line, "link:", 5))
- {
- char *val = line + 5;
- while (*val == ' ')
- {
- val++;
- }
- if (strlen(g_link_flags) > 0)
- {
- strcat(g_link_flags, " ");
- }
- strcat(g_link_flags, val);
- }
- else if (0 == strncmp(line, "cflags:", 7))
- {
- char *val = line + 7;
- while (*val == ' ')
- {
- val++;
- }
- if (strlen(g_cflags) > 0)
- {
- strcat(g_cflags, " ");
- }
- strcat(g_cflags, val);
- }
- else if (0 == strncmp(line, "include:", 8))
- {
- char *val = line + 8;
- while (*val == ' ')
- {
- val++;
- }
- char flags[2048];
- sprintf(flags, "-I%s", val);
- if (strlen(g_cflags) > 0)
- {
- strcat(g_cflags, " ");
- }
- strcat(g_cflags, flags);
- }
- else if (strncmp(line, "lib:", 4) == 0)
- {
- char *val = line + 4;
- while (*val == ' ')
- {
- val++;
- }
- char flags[2048];
- sprintf(flags, "-L%s", val);
- if (strlen(g_link_flags) > 0)
- {
- strcat(g_link_flags, " ");
- }
- strcat(g_link_flags, flags);
- }
- else if (strncmp(line, "define:", 7) == 0)
- {
- char *val = line + 7;
- while (*val == ' ')
- {
- val++;
- }
- char flags[2048];
- sprintf(flags, "-D%s", val);
- if (strlen(g_cflags) > 0)
- {
- strcat(g_cflags, " ");
- }
- strcat(g_cflags, flags);
- }
- else if (0 == strncmp(line, "shell:", 6))
- {
- char *cmd = line + 6;
- // printf("[zprep] Running shell: %s\n", cmd);
- int res = system(cmd);
- if (res != 0)
- {
- zpanic("Shell directive failed: %s", cmd);
- }
- }
- else if (strncmp(line, "get:", 4) == 0)
- {
- char *url = line + 4;
- while (*url == ' ')
- {
- url++;
- }
-
- char *filename = strrchr(url, '/');
- if (!filename)
- {
- filename = "downloaded_file";
- }
- else
- {
- filename++;
- }
-
- // Check if file exists to avoid redownloading.
- FILE *f = fopen(filename, "r");
- if (f)
- {
- fclose(f);
- }
- else
- {
- printf("[zprep] Downloading %s...\n", filename);
- char cmd[8192];
- // Try wget, then curl.
- sprintf(cmd, "wget -q \"%s\" -O \"%s\" || curl -s -L \"%s\" -o \"%s\"", url,
- filename, url, filename);
- if (system(cmd) != 0)
- {
- zpanic("Failed to download %s", url);
- }
- }
- }
- else if (strncmp(line, "pkg-config:", 11) == 0)
- {
- char *libs = line + 11;
- while (*libs == ' ')
- {
- libs++;
- }
-
- char cmd[4096];
- sprintf(cmd, "pkg-config --cflags %s", libs);
- FILE *fp = popen(cmd, "r");
- if (fp)
- {
- char flags[4096];
- flags[0] = 0;
- fgets(flags, sizeof(flags), fp);
- pclose(fp);
- int len = strlen(flags);
- if (len > 0 && flags[len - 1] == '\n')
- {
- flags[len - 1] = 0;
- }
- if (strlen(g_cflags) > 0)
- {
- strcat(g_cflags, " ");
- }
- strcat(g_cflags, flags);
- }
-
- sprintf(cmd, "pkg-config --libs %s", libs);
- fp = popen(cmd, "r");
- if (fp)
- {
- char flags[4096];
- flags[0] = 0;
- fgets(flags, sizeof(flags), fp);
- pclose(fp);
- int len = strlen(flags);
- if (len > 0 && flags[len - 1] == '\n')
- {
- flags[len - 1] = 0;
- }
- if (strlen(g_link_flags) > 0)
- {
- strcat(g_link_flags, " ");
- }
- strcat(g_link_flags, flags);
- }
- }
- else if (strncmp(line, "immutable-by-default", 20) == 0)
- {
- ctx->immutable_by_default = 1;
- }
-
- p += len;
+void scan_build_directives(ParserContext *ctx, const char *src) {
+ const char *p = src;
+ while (*p) {
+ if (p[0] == '/' && p[1] == '/' && p[2] == '>') {
+ p += 3;
+ while (*p == ' ') {
+ p++;
+ }
+
+ const char *start = p;
+ int len = 0;
+ while (p[len] && p[len] != '\n') {
+ len++;
+ }
+
+ char line[2048];
+ if (len >= 2047) {
+ len = 2047;
+ }
+ strncpy(line, start, len);
+ line[len] = 0;
+
+ if (0 == strncmp(line, "link:", 5)) {
+ char *val = line + 5;
+ while (*val == ' ') {
+ val++;
+ }
+ if (strlen(g_link_flags) > 0) {
+ strcat(g_link_flags, " ");
+ }
+ strcat(g_link_flags, val);
+ } else if (0 == strncmp(line, "cflags:", 7)) {
+ char *val = line + 7;
+ while (*val == ' ') {
+ val++;
+ }
+ if (strlen(g_cflags) > 0) {
+ strcat(g_cflags, " ");
+ }
+ strcat(g_cflags, val);
+ } else if (0 == strncmp(line, "include:", 8)) {
+ char *val = line + 8;
+ while (*val == ' ') {
+ val++;
+ }
+ char flags[2048];
+ sprintf(flags, "-I%s", val);
+ if (strlen(g_cflags) > 0) {
+ strcat(g_cflags, " ");
+ }
+ strcat(g_cflags, flags);
+ } else if (strncmp(line, "lib:", 4) == 0) {
+ char *val = line + 4;
+ while (*val == ' ') {
+ val++;
}
- while (*p && *p != '\n')
- {
- p++;
+ char flags[2048];
+ sprintf(flags, "-L%s", val);
+ if (strlen(g_link_flags) > 0) {
+ strcat(g_link_flags, " ");
+ }
+ strcat(g_link_flags, flags);
+ } else if (strncmp(line, "define:", 7) == 0) {
+ char *val = line + 7;
+ while (*val == ' ') {
+ val++;
+ }
+ char flags[2048];
+ sprintf(flags, "-D%s", val);
+ if (strlen(g_cflags) > 0) {
+ strcat(g_cflags, " ");
+ }
+ strcat(g_cflags, flags);
+ } else if (0 == strncmp(line, "shell:", 6)) {
+ char *cmd = line + 6;
+ // printf("[zprep] Running shell: %s\n", cmd);
+ int res = system(cmd);
+ if (res != 0) {
+ zpanic("Shell directive failed: %s", cmd);
+ }
+ } else if (strncmp(line, "get:", 4) == 0) {
+ char *url = line + 4;
+ while (*url == ' ') {
+ url++;
}
- if (*p == '\n')
- {
- p++;
+ char *filename = strrchr(url, '/');
+ if (!filename) {
+ filename = "downloaded_file";
+ } else {
+ filename++;
}
- }
-}
-// Levenshtein distance for "did you mean?" suggestions.
-int levenshtein(const char *s1, const char *s2)
-{
- int len1 = strlen(s1);
- int len2 = strlen(s2);
-
- // Quick optimization.
- if (abs(len1 - len2) > 3)
- {
- return 999;
- }
+ // Check if file exists to avoid redownloading.
+ FILE *f = fopen(filename, "r");
+ if (f) {
+ fclose(f);
+ } else {
+ printf("[zprep] Downloading %s...\n", filename);
+ char cmd[8192];
+ // Try wget, then curl.
+ sprintf(cmd,
+ "wget -q \"%s\" -O \"%s\" || curl -s -L \"%s\" -o \"%s\"",
+ url, filename, url, filename);
+ if (system(cmd) != 0) {
+ zpanic("Failed to download %s", url);
+ }
+ }
+ } else if (strncmp(line, "pkg-config:", 11) == 0) {
+ char *libs = line + 11;
+ while (*libs == ' ') {
+ libs++;
+ }
+
+ char cmd[4096];
+ sprintf(cmd, "pkg-config --cflags %s", libs);
+ FILE *fp = popen(cmd, "r");
+ if (fp) {
+ char flags[4096];
+ flags[0] = 0;
+ fgets(flags, sizeof(flags), fp);
+ pclose(fp);
+ int len = strlen(flags);
+ if (len > 0 && flags[len - 1] == '\n') {
+ flags[len - 1] = 0;
+ }
+ if (strlen(g_cflags) > 0) {
+ strcat(g_cflags, " ");
+ }
+ strcat(g_cflags, flags);
+ }
- int matrix[len1 + 1][len2 + 1];
+ sprintf(cmd, "pkg-config --libs %s", libs);
+ fp = popen(cmd, "r");
+ if (fp) {
+ char flags[4096];
+ flags[0] = 0;
+ fgets(flags, sizeof(flags), fp);
+ pclose(fp);
+ int len = strlen(flags);
+ if (len > 0 && flags[len - 1] == '\n') {
+ flags[len - 1] = 0;
+ }
+ if (strlen(g_link_flags) > 0) {
+ strcat(g_link_flags, " ");
+ }
+ strcat(g_link_flags, flags);
+ }
+ } else if (strncmp(line, "immutable-by-default", 20) == 0) {
+ ctx->immutable_by_default = 1;
+ }
- for (int i = 0; i <= len1; i++)
- {
- matrix[i][0] = i;
+ p += len;
}
- for (int j = 0; j <= len2; j++)
- {
- matrix[0][j] = j;
+ while (*p && *p != '\n') {
+ p++;
}
- for (int i = 1; i <= len1; i++)
- {
- for (int j = 1; j <= len2; j++)
- {
- int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1;
- int del = matrix[i - 1][j] + 1;
- int ins = matrix[i][j - 1] + 1;
- int sub = matrix[i - 1][j - 1] + cost;
-
- matrix[i][j] = (del < ins) ? del : ins;
- if (sub < matrix[i][j])
- {
- matrix[i][j] = sub;
- }
- }
+ if (*p == '\n') {
+ p++;
}
+ }
+}
- return matrix[len1][len2];
+// Levenshtein distance for "did you mean?" suggestions.
+int levenshtein(const char *s1, const char *s2) {
+ int len1 = strlen(s1);
+ int len2 = strlen(s2);
+
+ // Quick optimization.
+ if (abs(len1 - len2) > 3) {
+ return 999;
+ }
+
+ int matrix[len1 + 1][len2 + 1];
+
+ for (int i = 0; i <= len1; i++) {
+ matrix[i][0] = i;
+ }
+ for (int j = 0; j <= len2; j++) {
+ matrix[0][j] = j;
+ }
+
+ for (int i = 1; i <= len1; i++) {
+ for (int j = 1; j <= len2; j++) {
+ int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1;
+ int del = matrix[i - 1][j] + 1;
+ int ins = matrix[i][j - 1] + 1;
+ int sub = matrix[i - 1][j - 1] + cost;
+
+ matrix[i][j] = (del < ins) ? del : ins;
+ if (sub < matrix[i][j]) {
+ matrix[i][j] = sub;
+ }
+ }
+ }
+
+ return matrix[len1][len2];
}
diff --git a/src/zen/zen_facts.c b/src/zen/zen_facts.c
index a86e0cb..f012d8e 100644
--- a/src/zen/zen_facts.c
+++ b/src/zen/zen_facts.c
@@ -8,11 +8,10 @@
// We keep it low by default.
#define ZEN_PROBABILITY 10
-typedef struct
-{
- ZenTrigger trigger;
- const char *message;
- const char *url;
+typedef struct {
+ ZenTrigger trigger;
+ const char *message;
+ const char *url;
} ZenFact;
static const ZenFact facts[] = {
@@ -37,7 +36,8 @@ static const ZenFact facts[] = {
"`sizeof(*ptr)` bytes.",
NULL},
- {TRIGGER_BITWISE, "Use `(x & (x - 1)) == 0` to check if an integer is a power of two.",
+ {TRIGGER_BITWISE,
+ "Use `(x & (x - 1)) == 0` to check if an integer is a power of two.",
"https://graphics.stanford.edu/~seander/bithacks.html"},
{TRIGGER_BITWISE,
"XOR swap algorithm: `x ^= y; y ^= x; x ^= y;` swaps variables without a "
@@ -45,7 +45,8 @@ static const ZenFact facts[] = {
"optimized code is usually faster).",
NULL},
- {TRIGGER_RECURSION, "To understand recursion, you must first understand recursion.", NULL},
+ {TRIGGER_RECURSION,
+ "To understand recursion, you must first understand recursion.", NULL},
{TRIGGER_RECURSION,
"Tail Call Optimization (TCO) allows some recursive calls to consume no "
"additional stack "
@@ -194,7 +195,8 @@ static const ZenFact facts[] = {
"The `#` stringification operator in macros turns arguments into string "
"literals.",
NULL},
- {TRIGGER_GLOBAL, "The `##` token-pasting operator concatenates tokens in macro expansions.",
+ {TRIGGER_GLOBAL,
+ "The `##` token-pasting operator concatenates tokens in macro expansions.",
NULL},
{TRIGGER_GLOBAL,
"Flexible array members: `int data[];` at struct end allows variable-size "
@@ -276,7 +278,9 @@ static const ZenFact facts[] = {
"B, which was "
"typeless.",
NULL},
- {TRIGGER_GLOBAL, "The name 'C' simply comes from being the successor to the B language.", NULL},
+ {TRIGGER_GLOBAL,
+ "The name 'C' simply comes from being the successor to the B language.",
+ NULL},
{TRIGGER_GLOBAL,
"Plan 9 C allows `structure.member` notation even if `member` is inside "
"an anonymous inner "
@@ -359,152 +363,129 @@ static const ZenFact facts[] = {
static int fact_count = sizeof(facts) / sizeof(ZenFact);
static int has_triggered = 0;
-void zen_init(void)
-{
- struct timespec ts;
- clock_gettime(CLOCK_REALTIME, &ts);
- srand(ts.tv_nsec ^ getpid());
+void zen_init(void) {
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ srand(ts.tv_nsec ^ getpid());
}
// Global helper to print.
-void zzen_at(Token t, const char *msg, const char *url)
-{
- fprintf(stderr, COLOR_GREEN "zen: " COLOR_RESET COLOR_BOLD "%s" COLOR_RESET "\n", msg);
-
- extern char *g_current_filename;
- if (t.line > 0)
- {
- fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n",
- g_current_filename ? g_current_filename : "unknown", t.line, t.col);
+void zzen_at(Token t, const char *msg, const char *url) {
+ fprintf(stderr,
+ COLOR_GREEN "zen: " COLOR_RESET COLOR_BOLD "%s" COLOR_RESET "\n",
+ msg);
+
+ extern char *g_current_filename;
+ if (t.line > 0) {
+ fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n",
+ g_current_filename ? g_current_filename : "unknown", t.line, t.col);
+ }
+
+ if (t.start) {
+ const char *line_start = t.start - (t.col - 1);
+ const char *line_end = t.start;
+ while (*line_end && '\n' != *line_end) {
+ line_end++;
}
-
- if (t.start)
- {
- const char *line_start = t.start - (t.col - 1);
- const char *line_end = t.start;
- while (*line_end && '\n' != *line_end)
- {
- line_end++;
- }
- int line_len = line_end - line_start;
-
- fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET);
- fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len, line_start);
- fprintf(stderr, COLOR_BLUE " | " COLOR_RESET);
- for (int i = 0; i < t.col - 1; i++)
- {
- fprintf(stderr, " ");
- }
- fprintf(stderr, COLOR_GREEN "^ zen tip" COLOR_RESET "\n");
+ int line_len = line_end - line_start;
+
+ fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET);
+ fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len,
+ line_start);
+ fprintf(stderr, COLOR_BLUE " | " COLOR_RESET);
+ for (int i = 0; i < t.col - 1; i++) {
+ fprintf(stderr, " ");
}
+ fprintf(stderr, COLOR_GREEN "^ zen tip" COLOR_RESET "\n");
+ }
- if (url)
- {
- fprintf(stderr, COLOR_CYAN " = read more: %s" COLOR_RESET "\n", url);
- }
+ if (url) {
+ fprintf(stderr, COLOR_CYAN " = read more: %s" COLOR_RESET "\n", url);
+ }
}
-int zen_trigger_at(ZenTrigger t, Token location)
-{
- if (g_config.quiet)
- {
- return 0;
+int zen_trigger_at(ZenTrigger t, Token location) {
+ if (g_config.quiet) {
+ return 0;
+ }
+
+ if (has_triggered) {
+ return 0;
+ }
+
+ extern int g_warning_count;
+ if (g_warning_count > 0) {
+ return 0;
+ }
+
+ if ((rand() % 100) >= ZEN_PROBABILITY) {
+ return 0;
+ }
+
+ int matches[10];
+ int match_count = 0;
+
+ for (int i = 0; i < fact_count; i++) {
+ if (facts[i].trigger == t) {
+ matches[match_count++] = i;
+ if (match_count >= 10) {
+ break;
+ }
}
+ }
- if (has_triggered)
- {
- return 0;
- }
-
- extern int g_warning_count;
- if (g_warning_count > 0)
- {
- return 0;
- }
+ if (0 == match_count) {
+ return 0;
+ }
- if ((rand() % 100) >= ZEN_PROBABILITY)
- {
- return 0;
- }
-
- int matches[10];
- int match_count = 0;
-
- for (int i = 0; i < fact_count; i++)
- {
- if (facts[i].trigger == t)
- {
- matches[match_count++] = i;
- if (match_count >= 10)
- {
- break;
- }
- }
- }
+ int pick = matches[rand() % match_count];
+ const ZenFact *f = &facts[pick];
- if (0 == match_count)
- {
- return 0;
- }
-
- int pick = matches[rand() % match_count];
- const ZenFact *f = &facts[pick];
-
- zzen_at(location, f->message, f->url);
- has_triggered = 1;
- return 1;
+ zzen_at(location, f->message, f->url);
+ has_triggered = 1;
+ return 1;
}
-void zen_trigger_global(void)
-{
- if (g_config.quiet)
- {
- return;
- }
- if (!isatty(STDERR_FILENO))
- {
- return;
- }
- if (has_triggered)
- {
- return;
- }
-
- extern int g_warning_count;
- if (g_warning_count > 0)
- {
- return;
- }
-
- if ((rand() % 100) >= ZEN_PROBABILITY)
- {
- return;
+void zen_trigger_global(void) {
+ if (g_config.quiet) {
+ return;
+ }
+ if (!isatty(STDERR_FILENO)) {
+ return;
+ }
+ if (has_triggered) {
+ return;
+ }
+
+ extern int g_warning_count;
+ if (g_warning_count > 0) {
+ return;
+ }
+
+ if ((rand() % 100) >= ZEN_PROBABILITY) {
+ return;
+ }
+
+ int matches[10];
+ int match_count = 0;
+
+ for (int i = 0; i < fact_count; i++) {
+ if (TRIGGER_GLOBAL == facts[i].trigger) {
+ matches[match_count++] = i;
+ if (match_count >= 10) {
+ break;
+ }
}
+ }
- int matches[10];
- int match_count = 0;
-
- for (int i = 0; i < fact_count; i++)
- {
- if (TRIGGER_GLOBAL == facts[i].trigger)
- {
- matches[match_count++] = i;
- if (match_count >= 10)
- {
- break;
- }
- }
- }
-
- if (0 == match_count)
- {
- return;
- }
+ if (0 == match_count) {
+ return;
+ }
- int pick = matches[rand() % match_count];
- const ZenFact *f = &facts[pick];
+ int pick = matches[rand() % match_count];
+ const ZenFact *f = &facts[pick];
- Token empty = {0};
- zzen_at(empty, f->message, f->url);
- has_triggered = 1;
+ Token empty = {0};
+ zzen_at(empty, f->message, f->url);
+ has_triggered = 1;
}
diff --git a/src/zen/zen_facts.h b/src/zen/zen_facts.h
index ce5b952..18a810d 100644
--- a/src/zen/zen_facts.h
+++ b/src/zen/zen_facts.h
@@ -4,21 +4,20 @@
#include "../zprep.h"
-typedef enum
-{
- TRIGGER_GOTO,
- TRIGGER_POINTER_ARITH,
- TRIGGER_BITWISE,
- TRIGGER_RECURSION,
- TRIGGER_TERNARY,
- TRIGGER_ASM,
- TRIGGER_WHILE_TRUE,
- TRIGGER_MACRO,
- TRIGGER_VOID_PTR,
- TRIGGER_MAIN,
- TRIGGER_FORMAT_STRING,
- TRIGGER_STRUCT_PADDING,
- TRIGGER_GLOBAL
+typedef enum {
+ TRIGGER_GOTO,
+ TRIGGER_POINTER_ARITH,
+ TRIGGER_BITWISE,
+ TRIGGER_RECURSION,
+ TRIGGER_TERNARY,
+ TRIGGER_ASM,
+ TRIGGER_WHILE_TRUE,
+ TRIGGER_MACRO,
+ TRIGGER_VOID_PTR,
+ TRIGGER_MAIN,
+ TRIGGER_FORMAT_STRING,
+ TRIGGER_STRUCT_PADDING,
+ TRIGGER_GLOBAL
} ZenTrigger;
void zen_init(void);
diff --git a/src/zprep.h b/src/zprep.h
index 75d9cac..5808ac4 100644
--- a/src/zprep.h
+++ b/src/zprep.h
@@ -29,75 +29,72 @@
// ** GLOBAL STATE **
extern char *g_current_filename;
-typedef enum
-{
- TOK_EOF = 0,
- TOK_IDENT,
- TOK_INT,
- TOK_FLOAT,
- TOK_STRING,
- TOK_FSTRING,
- TOK_CHAR,
- TOK_LPAREN,
- TOK_RPAREN,
- TOK_LBRACE,
- TOK_RBRACE,
- TOK_LBRACKET,
- TOK_RBRACKET,
- TOK_LANGLE,
- TOK_RANGLE,
- TOK_COMMA,
- TOK_COLON,
- TOK_SEMICOLON,
- TOK_OP,
- TOK_AT,
- TOK_DOTDOT,
- TOK_ARROW,
- TOK_PIPE,
- TOK_TEST,
- TOK_ASSERT,
- TOK_SIZEOF,
- TOK_DEFER,
- TOK_AUTOFREE,
- TOK_QUESTION,
- TOK_USE,
- TOK_QQ,
- TOK_QQ_EQ,
- TOK_Q_DOT,
- TOK_DCOLON,
- TOK_TRAIT,
- TOK_IMPL,
- TOK_AND,
- TOK_OR,
- TOK_FOR,
- TOK_COMPTIME,
- TOK_ELLIPSIS,
- TOK_UNION,
- TOK_ASM,
- TOK_VOLATILE,
- TOK_MUT,
- TOK_ASYNC,
- TOK_AWAIT,
- TOK_PREPROC,
- TOK_COMMENT,
- TOK_UNKNOWN
+typedef enum {
+ TOK_EOF = 0,
+ TOK_IDENT,
+ TOK_INT,
+ TOK_FLOAT,
+ TOK_STRING,
+ TOK_FSTRING,
+ TOK_CHAR,
+ TOK_LPAREN,
+ TOK_RPAREN,
+ TOK_LBRACE,
+ TOK_RBRACE,
+ TOK_LBRACKET,
+ TOK_RBRACKET,
+ TOK_LANGLE,
+ TOK_RANGLE,
+ TOK_COMMA,
+ TOK_COLON,
+ TOK_SEMICOLON,
+ TOK_OP,
+ TOK_AT,
+ TOK_DOTDOT,
+ TOK_ARROW,
+ TOK_PIPE,
+ TOK_TEST,
+ TOK_ASSERT,
+ TOK_SIZEOF,
+ TOK_DEFER,
+ TOK_AUTOFREE,
+ TOK_QUESTION,
+ TOK_USE,
+ TOK_QQ,
+ TOK_QQ_EQ,
+ TOK_Q_DOT,
+ TOK_DCOLON,
+ TOK_TRAIT,
+ TOK_IMPL,
+ TOK_AND,
+ TOK_OR,
+ TOK_FOR,
+ TOK_COMPTIME,
+ TOK_ELLIPSIS,
+ TOK_UNION,
+ TOK_ASM,
+ TOK_VOLATILE,
+ TOK_MUT,
+ TOK_ASYNC,
+ TOK_AWAIT,
+ TOK_PREPROC,
+ TOK_COMMENT,
+ TOK_UNKNOWN
} TokenType;
-typedef struct
-{
- TokenType type;
- const char *start;
- int len;
- int line;
- int col;
+typedef struct {
+ TokenType type;
+ const char *start;
+ int len;
+ int line;
+ int col;
} Token;
-typedef struct
-{
- const char *src;
- int pos;
- int line;
- int col;
+typedef struct {
+ const char *src;
+ int pos;
+ int line;
+ int col;
} Lexer;
void lexer_init(Lexer *l, const char *src);
@@ -137,10 +134,12 @@ int levenshtein(const char *s1, const char *s2);
void zpanic_with_suggestion(Token t, const char *msg, const char *suggestion);
// Specific error types.
-void error_undefined_function(Token t, const char *func_name, const char *suggestion);
-void error_wrong_arg_count(Token t, const char *func_name, int expected, int got);
-void error_undefined_field(Token t, const char *struct_name, const char *field_name,
- const char *suggestion);
+void error_undefined_function(Token t, const char *func_name,
+ const char *suggestion);
+void error_wrong_arg_count(Token t, const char *func_name, int expected,
+ int got);
+void error_undefined_field(Token t, const char *struct_name,
+ const char *field_name, const char *suggestion);
void error_type_expected(Token t, const char *expected, const char *got);
void error_cannot_index(Token t, const char *type_name);
@@ -150,41 +149,44 @@ void zwarn_at(Token t, const char *fmt, ...);
// Specific warnings.
void warn_unused_variable(Token t, const char *var_name);
-void warn_unused_parameter(Token t, const char *param_name, const char *func_name);
+void warn_unused_parameter(Token t, const char *param_name,
+ const char *func_name);
void warn_shadowing(Token t, const char *var_name);
void warn_unreachable_code(Token t);
-void warn_implicit_conversion(Token t, const char *from_type, const char *to_type);
-void warn_narrowing_conversion(Token t, const char *from_type, const char *to_type);
+void warn_implicit_conversion(Token t, const char *from_type,
+ const char *to_type);
+void warn_narrowing_conversion(Token t, const char *from_type,
+ const char *to_type);
void warn_missing_return(Token t, const char *func_name);
void warn_comparison_always_true(Token t, const char *reason);
void warn_comparison_always_false(Token t, const char *reason);
void warn_division_by_zero(Token t);
void warn_integer_overflow(Token t, const char *type_name, long long value);
void warn_array_bounds(Token t, int index, int size);
-void warn_format_string(Token t, int arg_num, const char *expected, const char *got);
+void warn_format_string(Token t, int arg_num, const char *expected,
+ const char *got);
void warn_null_pointer(Token t, const char *expr);
// ** Compiler Config **
-typedef struct
-{
- char *input_file;
- char *output_file;
-
- // Modes.
- int mode_run; // 1 if 'run' command.
- int mode_check; // 1 if 'check' command or --check.
- int emit_c; // 1 if --emit-c (keep C file).
- int verbose; // 1 if --verbose.
- int quiet; // 1 if --quiet.
- int repl_mode; // 1 if --repl (internal flag for REPL usage).
- int is_freestanding; // 1 if --freestanding.
- int mode_transpile; // 1 if 'transpile' command.
-
- // GCC Flags accumulator.
- char gcc_flags[4096];
-
- // C Compiler selection (default: gcc)
- char cc[64];
+typedef struct {
+ char *input_file;
+ char *output_file;
+
+ // Modes.
+ int mode_run; // 1 if 'run' command.
+ int mode_check; // 1 if 'check' command or --check.
+ int emit_c; // 1 if --emit-c (keep C file).
+ int verbose; // 1 if --verbose.
+ int quiet; // 1 if --quiet.
+ int repl_mode; // 1 if --repl (internal flag for REPL usage).
+ int is_freestanding; // 1 if --freestanding.
+ int mode_transpile; // 1 if 'transpile' command.
+
+ // GCC Flags accumulator.
+ char gcc_flags[4096];
+
+ // C Compiler selection (default: gcc)
+ char cc[64];
} CompilerConfig;
extern CompilerConfig g_config;