summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-16 12:43:51 +0000
committerZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-16 12:43:51 +0000
commite77725b55190b8ec6dcab46aa137c32652ea004b (patch)
treea80e237f1f65873f908f5819488c1c32683aff74
parent2e1aa3d8853f3b49e93b1d50b1b6e60e8238d79c (diff)
Support for 'alias' + test. Improved formatting and '.gitignore'.
-rw-r--r--.clang-format12
-rw-r--r--.gitignore14
-rw-r--r--src/analysis/typecheck.h14
-rw-r--r--src/ast/ast.h975
-rw-r--r--src/codegen/codegen.h6
-rw-r--r--src/codegen/codegen_main.c902
-rw-r--r--src/lexer/token.c734
-rw-r--r--src/lsp/json_rpc.c283
-rw-r--r--src/lsp/lsp_analysis.c620
-rw-r--r--src/lsp/lsp_index.c329
-rw-r--r--src/lsp/lsp_index.h41
-rw-r--r--src/lsp/lsp_main.c90
-rw-r--r--src/main.c540
-rw-r--r--src/parser/parser.h441
-rw-r--r--src/parser/parser_core.c5
-rw-r--r--src/parser/parser_stmt.c22
-rw-r--r--src/parser/parser_type.c11
-rw-r--r--src/parser/parser_utils.c23
-rw-r--r--src/plugins/plugin_manager.c135
-rw-r--r--src/repl/repl.c2415
-rw-r--r--src/utils/utils.c1342
-rw-r--r--src/zen/zen_facts.c259
-rw-r--r--src/zen/zen_facts.h29
-rw-r--r--src/zprep.h189
-rw-r--r--tests/features/test_alias.zc41
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
diff --git a/.gitignore b/.gitignore
index 666ce03..2af6452 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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;
}
diff --git a/src/main.c b/src/main.c
index ab4c39f..2c33a6b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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");
+}