diff options
| author | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-16 12:43:51 +0000 |
|---|---|---|
| committer | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-16 12:43:51 +0000 |
| commit | e77725b55190b8ec6dcab46aa137c32652ea004b (patch) | |
| tree | a80e237f1f65873f908f5819488c1c32683aff74 | |
| parent | 2e1aa3d8853f3b49e93b1d50b1b6e60e8238d79c (diff) | |
Support for 'alias' + test. Improved formatting and '.gitignore'.
| -rw-r--r-- | .clang-format | 12 | ||||
| -rw-r--r-- | .gitignore | 14 | ||||
| -rw-r--r-- | src/analysis/typecheck.h | 14 | ||||
| -rw-r--r-- | src/ast/ast.h | 975 | ||||
| -rw-r--r-- | src/codegen/codegen.h | 6 | ||||
| -rw-r--r-- | src/codegen/codegen_main.c | 902 | ||||
| -rw-r--r-- | src/lexer/token.c | 734 | ||||
| -rw-r--r-- | src/lsp/json_rpc.c | 283 | ||||
| -rw-r--r-- | src/lsp/lsp_analysis.c | 620 | ||||
| -rw-r--r-- | src/lsp/lsp_index.c | 329 | ||||
| -rw-r--r-- | src/lsp/lsp_index.h | 41 | ||||
| -rw-r--r-- | src/lsp/lsp_main.c | 90 | ||||
| -rw-r--r-- | src/main.c | 540 | ||||
| -rw-r--r-- | src/parser/parser.h | 441 | ||||
| -rw-r--r-- | src/parser/parser_core.c | 5 | ||||
| -rw-r--r-- | src/parser/parser_stmt.c | 22 | ||||
| -rw-r--r-- | src/parser/parser_type.c | 11 | ||||
| -rw-r--r-- | src/parser/parser_utils.c | 23 | ||||
| -rw-r--r-- | src/plugins/plugin_manager.c | 135 | ||||
| -rw-r--r-- | src/repl/repl.c | 2415 | ||||
| -rw-r--r-- | src/utils/utils.c | 1342 | ||||
| -rw-r--r-- | src/zen/zen_facts.c | 259 | ||||
| -rw-r--r-- | src/zen/zen_facts.h | 29 | ||||
| -rw-r--r-- | src/zprep.h | 189 | ||||
| -rw-r--r-- | tests/features/test_alias.zc | 41 |
25 files changed, 5210 insertions, 4262 deletions
diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..5422ab6 --- /dev/null +++ b/.clang-format @@ -0,0 +1,12 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +BreakBeforeBraces: Allman +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +ColumnLimit: 100 +AccessModifierOffset: -4 +SortIncludes: false +InsertBraces: true @@ -1,5 +1,19 @@ obj/ zc result +out.c +a.out +*.o *.so *.dll +*.dylib +*.a +.vscode/ +.idea/ +*.swp +*~ +.DS_Store +compile_commands.json +vgcore.* +tests/**/*.o +plugins/*.so diff --git a/src/analysis/typecheck.h b/src/analysis/typecheck.h index a680f3a..fe51c4d 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.h b/src/ast/ast.h index 50b926e..e38453c 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -11,472 +11,531 @@ 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.h b/src/codegen/codegen.h index 4d13d65..4b66f99 100644 --- a/src/codegen/codegen.h +++ b/src/codegen/codegen.h @@ -16,11 +16,9 @@ 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_main.c b/src/codegen/codegen_main.c index 3b0e4dc..3634af3 100644 --- a/src/codegen/codegen_main.c +++ b/src/codegen/codegen_main.c @@ -7,457 +7,563 @@ #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; - } +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; - } + // Only structs have dependencies that matter for ordering. + if (s1->type != NODE_STRUCT) + { + return 0; + } - // Check if this field's type matches target (struct or enum). - if (strcmp(type_str, target_name) == 0) { - return 1; - } + 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; + } + } + field = field->next; } - field = field->next; - } - return 0; + return 0; } // Topologically sort a list of struct/enum nodes. -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; +static ASTNode *topo_sort_structs(ASTNode *head) +{ + if (!head) + { + return NULL; } - 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; - } - // 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; + // 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; + } - if (dep_name && struct_depends_on(nodes[i], dep_name)) { - can_emit = 0; - break; + // 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 (can_emit) { - order[order_idx++] = i; - emitted[i] = 1; - changed = 1; - } + n = n->next; } - } - // 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; -} + // Build order array (indices in emission order). + int *order = malloc(count * sizeof(int)); + int order_idx = 0; -// 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; - } + int changed = 1; + int max_iterations = count * count; + int iterations = 0; - global_user_structs = kids; + while (changed && iterations < max_iterations) + { + changed = 0; + iterations++; - 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 (int i = 0; i < count; i++) + { + if (emitted[i]) + { + continue; + } - 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; - } + // Enums have no dependencies, emit first. + if (nodes[i]->type == NODE_ENUM) + { + order[order_idx++] = i; + emitted[i] = 1; + changed = 1; + continue; + } - 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; + // 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; + } + } + + if (can_emit) + { + order[order_idx++] = i; + emitted[i] = 1; + changed = 1; + } } - } - sr = sr->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; + // Add any remaining nodes (cycles). + for (int i = 0; i < count; i++) + { + if (!emitted[i]) + { + order[order_idx++] = i; } - } - er = er->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; + // 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; } - - 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; - } + else + { + result_tail->next = node; + result_tail = node; } - } - k = k->next; - } - - // Topologically sort. - ASTNode *sorted = topo_sort_structs(merged); - - print_type_defs(ctx, out, sorted); - emit_enum_protos(sorted, 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; + if (result_tail) + { + result_tail->next = NULL; } - ASTNode *merged_globals = NULL; // Head + free(nodes); + free(emitted); + free(order); + return result; +} - 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; +// 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; + } - s = s->next; - } - } + global_user_structs = kids; - 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; + 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); } - s = s->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; + 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; } - s = s->next; - } - } - 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; + 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; - } - } - emit_protos(merged_funcs, out); + 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_impl_vtables(ctx, out); + 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; + } - emit_lambda_defs(ctx, out); + // Topologically sort. + ASTNode *sorted = topo_sort_structs(merged); - emit_tests_and_runner(ctx, kids, out); + print_type_defs(ctx, out, sorted); + emit_enum_protos(sorted, out); - ASTNode *iter = merged_funcs; - while (iter) { - if (iter->type == NODE_IMPL) { - char *sname = iter->impl.struct_name; - if (!sname) { - iter = iter->next; - continue; + if (sorted) + { + emit_struct_defs(ctx, sorted, out); } - - 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; + 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); } - free(buf); - } + raw_iter = raw_iter->next; } - if (mangled) { - free(mangled); - } - if (skip) { - iter = iter->next; - continue; + + ASTNode *merged_globals = NULL; // Head + + 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; + + s = s->next; + } } - } - if (iter->type == NODE_IMPL_TRAIT) { - char *sname = iter->impl_trait.target_type; - 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->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; + 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; } - free(buf); - } } - 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; } - } - 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; - } + 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"); + if (!has_user_main) + { + fprintf(out, "\nint main() { _z_run_tests(); return 0; }\n"); + } } - } } diff --git a/src/lexer/token.c b/src/lexer/token.c index 01e414f..54fa5d3 100644 --- a/src/lexer/token.c +++ b/src/lexer/token.c @@ -1,353 +1,457 @@ #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; +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; - 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++; - } + return (Token){TOK_PREPROC, s, len, start_line, start_col}; } - l->pos += len; - - 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++; + // 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 += 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 */ + + // Block Comments. + if (s[0] == '/' && s[1] == '*') + { + // skip two start chars 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); - } + 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++; + } - // 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 (s[0] == 'f' && s[1] == '"') { - int len = 2; - while (s[len] && s[len] != '"') { - if (s[len] == '\\') { - 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 == 5 && strncmp(s, "alias", 5) == 0) + { + return (Token){TOK_ALIAS, s, 5, 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}; + } + + // 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[len] == '"') { - len++; + + 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}; } - 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])) { + + if (*s == '\'') + { + int len = 1; + // Handle escapes like '\n' or regular 'a' + if (s[len] == '\\') + { + len++; + len++; + } + else + { len++; - } - l->pos += len; - l->col += len; - return (Token){TOK_FLOAT, s, len, start_line, start_col}; } - } + if (s[len] == '\'') + { + len++; + } + + 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}; - } - // Strings - if (*s == '"') { + // Operators. int len = 1; - while (s[len] && s[len] != '"') { - if (s[len] == '\\') { - len++; - } - len++; + 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; + } } - if (s[len] == '"') { - len++; + 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 >>= + } } - l->pos += len; - l->col += len; - return (Token){TOK_STRING, s, len, start_line, start_col}; - } - - if (*s == '\'') { - int len = 1; - // Handle escapes like '\n' or regular 'a' - if (s[len] == '\\') { - len++; - len++; - } else { - 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++; + + 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){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}; + 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 c75af95..903da71 100644 --- a/src/lsp/json_rpc.c +++ b/src/lsp/json_rpc.c @@ -5,81 +5,107 @@ #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); @@ -87,71 +113,82 @@ 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; - } - - 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); +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 (uri) { - free(uri); - } - if (text) { - free(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 (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 (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 (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 (uri) { - fprintf(stderr, "zls: Completion request at %d:%d\n", line, col); - lsp_completion(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 (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 73ba2c5..d455894 100644 --- a/src/lsp/lsp_analysis.c +++ b/src/lsp/lsp_analysis.c @@ -9,335 +9,387 @@ 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) { - - 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; +void lsp_check_file(const char *uri, const char *json_src) +{ + // Prepare ParserContext (persistent). + if (g_ctx) + { - // Prepare Lexer. - // Cache source. - if (g_last_src) { - free(g_last_src); - } - g_last_src = strdup(json_src); - - Lexer l; - lexer_init(&l, json_src); - - ASTNode *root = parse_program(g_ctx, &l); + 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); - 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); - // Construct JSON Response (notification) + ASTNode *root = parse_program(g_ctx, &l); - char *notification = malloc(128 * 1024); - char *p = notification; - p += sprintf( - p, - "{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/" - "publishDiagnostics\",\"params\":{\"uri\":\"%s\",\"diagnostics\":[", - uri); + if (g_index) + { + lsp_index_free(g_index); + } + g_index = lsp_index_new(); + if (root) + { + lsp_build_index(g_index, root); + } - Diagnostic *d = diagnostics.head; - while (d) { + // Construct JSON Response (notification) + char *notification = malloc(128 * 1024); + char *p = notification; 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); + "{\"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, ","); + } - if (d->next) { - p += sprintf(p, ","); + d = d->next; } - d = d->next; - } - - p += sprintf(p, "]}}"); + p += sprintf(p, "]}}"); - // Send notification. - long len = strlen(notification); - fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", len, notification); - fflush(stdout); + // Send notification. + long len = strlen(notification); + fprintf(stdout, "Content-Length: %ld\r\n\r\n%s", len, notification); + fflush(stdout); - free(notification); + free(notification); - // Cleanup. - Diagnostic *cur = diagnostics.head; - while (cur) { - Diagnostic *next = cur->next; - free(cur->message); - free(cur); - cur = next; - } + // Cleanup. + Diagnostic *cur = diagnostics.head; + while (cur) + { + Diagnostic *next = cur->next; + free(cur->message); + free(cur); + cur = next; + } } -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_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; - } - - 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; - } +void lsp_hover(const char *uri, int line, int col) +{ + (void)uri; + if (!g_index) + { + return; } - } - 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); + LSPRange *r = lsp_find_at(g_index, line, col); + char *text = NULL; - 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); - } + 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); + } } -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++; +void lsp_completion(const char *uri, int line, int col) +{ + (void)uri; + if (!g_ctx) + { + return; } - 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--; - } - 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; + // 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 (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++; + 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--; } - *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; - } + + 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; + } + } - 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, ","); + // 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; } - 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, ","); + + // 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\":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); + + 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 fdd0cf8..d952b77 100644 --- a/src/lsp/lsp_index.c +++ b/src/lsp/lsp_index.c @@ -4,168 +4,201 @@ #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); +LSPIndex *lsp_index_new() +{ + return calloc(1, sizeof(LSPIndex)); } -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_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; -LSPRange *lsp_find_at(LSPIndex *idx, int line, int col) { - LSPRange *curr = idx->head; - LSPRange *best = NULL; + lsp_index_add(idx, r); +} - while (curr) { - if (line >= curr->start_line && line <= curr->end_line) { - if (line == curr->start_line && col < curr->start_col) { - curr = curr->next; - continue; - } +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); +} - if (line == curr->end_line && col > curr->end_col) { +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; - continue; - } - - best = curr; } - curr = curr->next; - } - return best; + return best; } // 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); - - // 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_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 8b8207b..f4aaf96 100644 --- a/src/lsp/lsp_index.h +++ b/src/lsp/lsp_index.h @@ -4,31 +4,36 @@ #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 a9a9f06..fbe5312 100644 --- a/src/lsp/lsp_main.c +++ b/src/lsp/lsp_main.c @@ -6,47 +6,55 @@ #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); - } +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); } - 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; + return 0; } @@ -12,258 +12,334 @@ // 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_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_version() +{ + printf("Zen C version %s\n", ZEN_VERSION); } -int main(int argc, char **argv) { - // Defaults - memset(&g_config, 0, sizeof(g_config)); - strcpy(g_config.cc, "gcc"); +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"); +} - if (argc < 2) { - print_usage(); - return 1; - } +int main(int argc, char **argv) +{ + // Defaults + memset(&g_config, 0, sizeof(g_config)); + strcpy(g_config.cc, "gcc"); - // Parse command - char *command = argv[1]; - int arg_start = 2; + if (argc < 2) + { + print_usage(); + return 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; + // 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 { - // Check if file - if (strchr(command, '.')) { - g_config.input_file = command; - arg_start = 2; + else if (strcmp(command, "check") == 0) + { + g_config.mode_check = 1; } - } - - // 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(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; + } + } + + // 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, "-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"); + 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; - } } - } - - 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"); + + 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; - } - 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"; + init_builtins(); + zen_init(); - // TCC-specific adjustments? - // Already handled by user passing --cc tcc + // Initialize Plugin Manager + zptr_plugin_mgr_init(); - 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); + // Parse context init + ParserContext ctx; + memset(&ctx, 0, sizeof(ctx)); - if (g_config.verbose) { - printf("[CMD] %s\n", cmd); - } + // Scan for build directives (e.g. //> link: -lm) + scan_build_directives(&ctx, src); - int ret = system(cmd); - if (ret != 0) { - printf("C compilation failed.\n"); - if (!g_config.emit_c) { - remove("out.c"); + 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; } - 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); + + // 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); + + if (g_config.verbose) + { + printf("[CMD] %s\n", cmd); + } + + 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 ret; + } + zptr_plugin_mgr_cleanup(); zen_trigger_global(); - return ret; - } - - zptr_plugin_mgr_cleanup(); - zen_trigger_global(); - return 0; + return 0; } diff --git a/src/parser/parser.h b/src/parser/parser.h index 815164c..1e047b4 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -6,19 +6,20 @@ #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 @@ -31,219 +32,248 @@ 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; +typedef struct TypeAlias +{ + char *alias; + char *original_type; + struct TypeAlias *next; +} TypeAlias; - // Lambdas - LambdaRef *global_lambdas; - int lambda_counter; +struct ParserContext +{ + Scope *current_scope; + FuncSig *func_registry; + + // 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; + TypeAlias *type_aliases; + + // 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 @@ -261,10 +291,9 @@ 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); @@ -273,18 +302,17 @@ 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); @@ -292,24 +320,21 @@ 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); -char *sanitize_mangled_name(const char *s); +void instantiate_generic(ParserContext *ctx, const char *name, const char *concrete_type, Token t); +char *sanitize_mangled_name(const char *name); +void register_type_alias(ParserContext *ctx, const char *alias, const char *original); +const char *find_type_alias(ParserContext *ctx, const char *alias); 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); @@ -320,15 +345,13 @@ 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 @@ -346,13 +369,12 @@ 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) @@ -365,8 +387,7 @@ 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); @@ -398,8 +419,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 c72e87a..3e683fb 100644 --- a/src/parser/parser_core.c +++ b/src/parser/parser_core.c @@ -15,7 +15,6 @@ ASTNode *parse_program_nodes(ParserContext *ctx, Lexer *l) { skip_comments(l); Token t = lexer_peek(l); - if (t.type == TOK_EOF) { break; @@ -411,6 +410,10 @@ ASTNode *parse_program_nodes(ParserContext *ctx, Lexer *l) lexer_next(l); } } + else if (t.type == TOK_ALIAS) + { + s = parse_type_alias(ctx, l); + } else if (t.type == TOK_ASYNC) { lexer_next(l); diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index 6f019b7..f391f97 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -1358,16 +1358,31 @@ ASTNode *parse_const(ParserContext *ctx, Lexer *l) ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l) { - lexer_next(l); + lexer_next(l); // consume 'type' or 'alias' Token n = lexer_next(l); - lexer_next(l); + if (n.type != TOK_IDENT) + { + zpanic_at(n, "Expected identifier for type alias"); + } + + lexer_next(l); // consume '=' + char *o = parse_type(ctx, l); - lexer_next(l); + // printf("DEBUG: parse_type returned '%s'\n", o); + + lexer_next(l); // consume ';' (parse_type doesn't consume it? parse_type calls parse_type_formal + // which doesn't consume ;?) + // Note: parse_type_stmt usually expects ; but parse_type just parses type expression. + // Check previous implementation: it had lexer_next(l) at end. This assumes ;. + 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; + + register_type_alias(ctx, node->type_alias.alias, o); + return node; } @@ -3260,6 +3275,7 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) { skip_comments(l); Token t = lexer_peek(l); + // printf("DEBUG: parse_struct loop seeing '%.*s'\n", t.len, t.start); if (t.type == TOK_RBRACE) { lexer_next(l); diff --git a/src/parser/parser_type.c b/src/parser/parser_type.c index 6e884f6..3b4abf3 100644 --- a/src/parser/parser_type.c +++ b/src/parser/parser_type.c @@ -27,6 +27,16 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l) lexer_next(l); char *name = token_strdup(t); + // Check for alias + const char *aliased = find_type_alias(ctx, name); + if (aliased) + { + free(name); + Lexer tmp; + lexer_init(&tmp, aliased); + return parse_type_formal(ctx, &tmp); + } + // Self type alias: Replace "Self" with current impl struct type if (strcmp(name, "Self") == 0 && ctx->current_impl_struct) { @@ -535,7 +545,6 @@ Type *parse_type_formal(ParserContext *ctx, Lexer *l) 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; diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c index e013a40..6129990 100644 --- a/src/parser/parser_utils.c +++ b/src/parser/parser_utils.c @@ -402,6 +402,29 @@ void add_to_struct_list(ParserContext *ctx, ASTNode *node) ctx->parsed_structs_list = r; } +void register_type_alias(ParserContext *ctx, const char *alias, const char *original) +{ + TypeAlias *ta = xmalloc(sizeof(TypeAlias)); + ta->alias = xstrdup(alias); + ta->original_type = xstrdup(original); + ta->next = ctx->type_aliases; + ctx->type_aliases = ta; +} + +const char *find_type_alias(ParserContext *ctx, const char *alias) +{ + TypeAlias *ta = ctx->type_aliases; + while (ta) + { + if (strcmp(ta->alias, alias) == 0) + { + return ta->original_type; + } + ta = ta->next; + } + return NULL; +} + void add_to_enum_list(ParserContext *ctx, ASTNode *node) { StructRef *r = xmalloc(sizeof(StructRef)); diff --git a/src/plugins/plugin_manager.c b/src/plugins/plugin_manager.c index aab98c8..afca55e 100644 --- a/src/plugins/plugin_manager.c +++ b/src/plugins/plugin_manager.c @@ -6,82 +6,99 @@ #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; +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; } - curr = curr->next; - } - return NULL; + return NULL; } -void zptr_plugin_mgr_cleanup(void) { - PluginNode *curr = head; - while (curr) { - PluginNode *next = curr->next; - if (curr->handle) { - dlclose(curr->handle); +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; } - free(curr); - curr = next; - } - head = NULL; + head = NULL; } diff --git a/src/repl/repl.c b/src/repl/repl.c index 5c82038..22fe95d 100644 --- a/src/repl/repl.c +++ b/src/repl/repl.c @@ -9,1177 +9,1368 @@ 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 *)); +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); + } } - history[history_len++] = strdup(buf); - } - fclose(hf); - if (history_len > 0) { - printf("Loaded %d entries from history.\n", history_len); - } } - } 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 *)); + 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); + } } - 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); - } } - } - char line_buf[1024]; + char line_buf[1024]; - char *input_buffer = NULL; - size_t input_len = 0; - int brace_depth = 0; - int paren_depth = 0; + 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 { - printf("\033[1;32m>>>\033[0m "); - } + while (1) + { + if (brace_depth > 0 || paren_depth > 0) + { + printf("... "); + } + else + { + printf("\033[1;32m>>>\033[0m "); + } - if (!fgets(line_buf, sizeof(line_buf), stdin)) { - break; - } + if (!fgets(line_buf, sizeof(line_buf), stdin)) + { + break; + } - // 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]; + // 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; } - 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; - } - 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 (0 == strcmp(cmd_buf, "exit") || 0 == strcmp(cmd_buf, "quit")) + { + break; } - 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 *)); + // 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[history_len++] = strdup(buffer); - } else { - free(buffer); - } + history_len = 0; + printf("History cleared.\n"); + continue; } - 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]; - } + 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; + } - 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; + if (history_len == 0) + { + printf("History is empty.\n"); + continue; + } + + 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. + } + + 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; } - } - 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"); - } - } + 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; + } - free(code); - continue; - } else if (0 == strncmp(cmd_buf, ":type ", 6)) { - char *expr = cmd_buf + 6; + 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]); + } + } + 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, " }"); - size_t probe_size = 4096; - for (int i = 0; i < history_len; i++) { - probe_size += strlen(history[i]) + 2; - } + ParserContext ctx = {0}; + ctx.is_repl = 1; + ctx.skip_preamble = 1; - char *probe_code = malloc(probe_size + strlen(expr) + 256); - strcpy(probe_code, ""); + Lexer l; + lexer_init(&l, code); + ASTNode *nodes = parse_program(&ctx, &l); - for (int i = 0; i < history_len; i++) { - if (is_header_line(history[i])) { - strcat(probe_code, history[i]); - strcat(probe_code, "\n"); - } - } + ASTNode *search = nodes; + if (search && search->type == NODE_ROOT) + { + search = search->root.children; + } - 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; - } + 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"); + } + } + + free(code); + continue; } - } - 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--; - } - } + 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; + } - 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"); + 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"); + } + } + + 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"); + } + } + } + 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--; + } + } + + 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; + } } - } - 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"); + } + + 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; } - } - 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 (c == '\\') + { + escaped = 1; + continue; } - } - 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 (c == '"') + { + in_quote = !in_quote; + continue; } - if (!found) { - printf("No documentation for '%s'.\n", sym); + + if (!in_quote) + { + if (c == '{') + { + brace_depth++; + } + if (c == '}') + { + brace_depth--; + } + if (c == '(') + { + paren_depth++; + } + if (c == ')') + { + paren_depth--; + } } - } - continue; - } else { - printf("Unknown command: %s\n", cmd_buf); - 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++; - } - if (c == '}') { - brace_depth--; - } - if (c == '(') { - paren_depth++; + 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 (c == ')') { - paren_depth--; + + if (input_len > 0 && input_buffer[input_len - 1] == '\n') + { + input_buffer[--input_len] = 0; } - } - } - 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 (input_len == 0) + { + free(input_buffer); + input_buffer = NULL; + input_len = 0; + brace_depth = 0; + paren_depth = 0; + continue; + } - if (brace_depth > 0 || paren_depth > 0) { - continue; - } + // Add to history. + if (history_len >= history_cap) + { + history_cap *= 2; + history = realloc(history, history_cap * sizeof(char *)); + } + 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 (watches_len > 0) + { + total_size += 16 * 1024; // Plenty of space for watches. Yeah static ik. + } - if (input_len > 0 && input_buffer[input_len - 1] == '\n') { - input_buffer[--input_len] = 0; - } + char *full_code = malloc(total_size); + strcpy(full_code, ""); - if (input_len == 0) { - free(input_buffer); - input_buffer = NULL; - input_len = 0; - brace_depth = 0; - paren_depth = 0; - continue; - } + // 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"); + } + } - // 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(full_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])) + { + continue; + } + strcat(full_code, history[i]); + strcat(full_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(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; + } + } - char *full_code = malloc(total_size); - strcpy(full_code, ""); + 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"); + } + } - // Hoisting pass. - for (int i = 0; i < history_len; i++) { - if (is_header_line(history[i])) { - strcat(full_code, history[i]); - strcat(full_code, "\n"); - } - } + strcat(probe_code, "fn main() { _z_suppress_stdout(); "); - strcat(full_code, "fn main() { _z_suppress_stdout(); "); + for (int i = 0; i < history_len - 1; i++) + { + if (!is_header_line(history[i])) + { + strcat(probe_code, history[i]); + strcat(probe_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, " "); - } + 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); + } - 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 (!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 (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"); - } + 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(probe_code, "fn main() { _z_suppress_stdout(); "); - - for (int i = 0; i < history_len - 1; i++) { - if (!is_header_line(history[i])) { - strcat(probe_code, history[i]); - strcat(probe_code, " "); - } - } + strcat(full_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, 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); - } - - 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); + 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; } - free(probe_code); - } else { - strcat(full_code, last_line); - } - } + fprintf(f, "%s", full_code); + fclose(f); + free(full_code); - 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); - } - } + char cmd[2048]; + sprintf(cmd, "%s run -q %s", self_path, tmp_path); - strcat(full_code, " }"); + int ret = system(cmd); + printf("\n"); - 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; + if (0 != ret) + { + free(history[--history_len]); + } } - 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"); + 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); + } + } - if (0 != ret) { - free(history[--history_len]); + for (int i = 0; i < history_len; i++) + { + free(history[i]); } - } - - 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); + free(history); + if (input_buffer) + { + free(input_buffer); } - } - - 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 cee71d8..66a0a22 100644 --- a/src/utils/utils.c +++ b/src/utils/utils.c @@ -8,476 +8,523 @@ 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) { +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. 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_YELLOW "^ here" COLOR_RESET "\n"); + fprintf(stderr, COLOR_RED "^ 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. - 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); + + 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; - - 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) { +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_CYAN " = help: " COLOR_RESET "%s\n", suggestion); - } + 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); + } - 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); +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"); - if (suggestion) { char help[512]; - sprintf(help, "Did you mean '%s'?", suggestion); + sprintf(help, "Expected type '%s', but found '%s'", expected, got); + 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); +void error_cannot_index(Token t, const char *type_name) +{ + char msg[256]; + sprintf(msg, "Cannot index into type '%s'", type_name); - char help[256]; - sprintf(help, "Expected %d argument%s, but got %d", expected, - expected == 1 ? "" : "s", got); + zpanic_with_suggestion(t, msg, "Only arrays and slices can be indexed"); +} - zpanic_with_suggestion(t, msg, help); +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 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_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"); +} - 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; +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 ** @@ -486,202 +533,259 @@ 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++; +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; } - 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++; + while (*p && *p != '\n') + { + p++; } - 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++; + if (*p == '\n') + { + p++; } + } +} - 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); - } +// 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; + } - 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; - } + int matrix[len1 + 1][len2 + 1]; - p += len; + for (int i = 0; i <= len1; i++) + { + matrix[i][0] = i; } - while (*p && *p != '\n') { - p++; + for (int j = 0; j <= len2; j++) + { + matrix[0][j] = j; } - if (*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; + } + } } - } -} -// 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]; + return matrix[len1][len2]; } diff --git a/src/zen/zen_facts.c b/src/zen/zen_facts.c index f012d8e..a86e0cb 100644 --- a/src/zen/zen_facts.c +++ b/src/zen/zen_facts.c @@ -8,10 +8,11 @@ // 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[] = { @@ -36,8 +37,7 @@ 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,8 +45,7 @@ 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 " @@ -195,8 +194,7 @@ 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 " @@ -278,9 +276,7 @@ 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 " @@ -363,129 +359,152 @@ 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); - } - - 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++; +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); } - 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, " "); + + 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"); } - 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; - } - - 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; - } +int zen_trigger_at(ZenTrigger t, Token location) +{ + if (g_config.quiet) + { + return 0; } - } - if (0 == match_count) { - return 0; - } + if (has_triggered) + { + return 0; + } + + extern int g_warning_count; + if (g_warning_count > 0) + { + return 0; + } - int pick = matches[rand() % match_count]; - const ZenFact *f = &facts[pick]; + 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; + } + } + } - zzen_at(location, f->message, f->url); - has_triggered = 1; - return 1; + 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; } -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; - } +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; } - } - if (0 == match_count) { - 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; + } + } + } + + 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 18a810d..ce5b952 100644 --- a/src/zen/zen_facts.h +++ b/src/zen/zen_facts.h @@ -4,20 +4,21 @@ #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 5808ac4..4508f1f 100644 --- a/src/zprep.h +++ b/src/zprep.h @@ -29,72 +29,76 @@ // ** 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_ALIAS, + 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); @@ -134,12 +138,10 @@ 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); @@ -149,44 +151,41 @@ 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; diff --git a/tests/features/test_alias.zc b/tests/features/test_alias.zc new file mode 100644 index 0000000..f7bc42a --- /dev/null +++ b/tests/features/test_alias.zc @@ -0,0 +1,41 @@ + +struct Point { + x: int; + y: int; +} + +alias MyIntPtr = int*; +alias MyInt = int; + +fn process(val: MyInt) -> MyInt { + return val + 1; +} + +alias BinOp = fn(int, int) -> int; + +fn add(a: int, b: int) -> int { + return a + b; +} + +test "alias basic" { + var x: MyInt = 10; + var ptr: MyIntPtr = &x; + + assert(x == 10, "Basic alias failed"); + assert(*ptr == 10, "Pointer alias failed"); + + var res = process(x); + assert(res == 11, "Function with alias arg failed"); +} + + p.x = 100; + p.y = 200; + + assert(p.x == 100, "Struct alias access failed"); +} + +test "alias function pointer" { + // var op: BinOp; + // Assignment currently not supported for function types without casting op = add; + // assert(op(1, 2) == 3, "Function alias"); +} |
