diff options
| -rw-r--r-- | plugins/befunge.c | 11 | ||||
| -rw-r--r-- | plugins/brainfuck.c | 4 | ||||
| -rw-r--r-- | plugins/forth.c | 5 | ||||
| -rw-r--r-- | plugins/lisp.c | 36 | ||||
| -rw-r--r-- | plugins/regex.c | 5 | ||||
| -rw-r--r-- | plugins/sql.c | 4 | ||||
| -rw-r--r-- | src/analysis/typecheck.c | 2 | ||||
| -rw-r--r-- | src/ast/ast.c | 6 | ||||
| -rw-r--r-- | src/ast/ast.h | 16 | ||||
| -rw-r--r-- | src/codegen/codegen.c | 26 | ||||
| -rw-r--r-- | src/codegen/codegen.h | 4 | ||||
| -rw-r--r-- | src/codegen/codegen_decl.c | 98 | ||||
| -rw-r--r-- | src/codegen/codegen_main.c | 11 | ||||
| -rw-r--r-- | src/codegen/codegen_utils.c | 19 | ||||
| -rw-r--r-- | src/lsp/json_rpc.c | 16 | ||||
| -rw-r--r-- | src/lsp/lsp_analysis.c | 27 | ||||
| -rw-r--r-- | src/lsp/lsp_index.c | 2 | ||||
| -rw-r--r-- | src/lsp/lsp_main.c | 2 | ||||
| -rw-r--r-- | src/main.c | 17 | ||||
| -rw-r--r-- | src/parser/parser.h | 5 | ||||
| -rw-r--r-- | src/parser/parser_core.c | 4 | ||||
| -rw-r--r-- | src/parser/parser_expr.c | 375 | ||||
| -rw-r--r-- | src/parser/parser_stmt.c | 101 | ||||
| -rw-r--r-- | src/parser/parser_type.c | 15 | ||||
| -rw-r--r-- | src/parser/parser_utils.c | 147 | ||||
| -rw-r--r-- | src/plugins/plugin_manager.c | 2 | ||||
| -rw-r--r-- | src/repl/repl.c | 94 | ||||
| -rw-r--r-- | src/utils/utils.c | 2 | ||||
| -rw-r--r-- | src/zen/zen_facts.c | 222 | ||||
| -rw-r--r-- | src/zprep.h | 4 |
30 files changed, 747 insertions, 535 deletions
diff --git a/plugins/befunge.c b/plugins/befunge.c index 844e27f..92165f4 100644 --- a/plugins/befunge.c +++ b/plugins/befunge.c @@ -80,7 +80,8 @@ void befunge_transpile(const char *input_body, const ZApi *api) if (op >= '0' && op <= '9') { fprintf(out, - " if(string_mode) { stack[sp++] = '%c'; } else { stack[sp++] = %c; }\n", + " if(string_mode) { stack[sp++] = '%c'; } else { " + "stack[sp++] = %c; }\n", op, op); } else if (op == '"') @@ -119,13 +120,13 @@ void befunge_transpile(const char *input_body, const ZApi *api) } else if (op == '/') { - fprintf(out, - " { long a=stack[--sp]; stack[sp-1]= (a!=0)?stack[sp-1]/a:0; }\n"); + fprintf(out, " { long a=stack[--sp]; stack[sp-1]= " + "(a!=0)?stack[sp-1]/a:0; }\n"); } else if (op == '%') { - fprintf(out, - " { long a=stack[--sp]; stack[sp-1]= (a!=0)?stack[sp-1]%%a:0; }\n"); + fprintf(out, " { long a=stack[--sp]; stack[sp-1]= " + "(a!=0)?stack[sp-1]%%a:0; }\n"); } else if (op == '!') { diff --git a/plugins/brainfuck.c b/plugins/brainfuck.c index e800da7..576029b 100644 --- a/plugins/brainfuck.c +++ b/plugins/brainfuck.c @@ -4,8 +4,8 @@ void bf_transpile(const char *input_body, const ZApi *api) { FILE *out = api->out; - fprintf(out, - "{\n static unsigned char tape[30000] = {0};\n unsigned char *ptr = tape;\n"); + fprintf(out, "{\n static unsigned char tape[30000] = {0};\n unsigned " + "char *ptr = tape;\n"); const char *c = input_body; while (*c) { diff --git a/plugins/forth.c b/plugins/forth.c index 9d39c6d..90e05c6 100644 --- a/plugins/forth.c +++ b/plugins/forth.c @@ -1,9 +1,9 @@ #include "zprep_plugin.h" +#include <math.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <math.h> static long stack[256]; static int sp = 0; @@ -598,7 +598,8 @@ void process_forth_source(char *src, char **out_ptr) void load_prelude() { - char prelude[] = ": squared fdup f* ; : hypot squared fswap squared f+ fsqrt ; : over swap dup " + char prelude[] = ": squared fdup f* ; : hypot squared fswap squared f+ fsqrt " + "; : over swap dup " "rot rot ; : panic 0 1 - exit ; "; char dummy_buf[1024]; dummy_buf[0] = '\0'; diff --git a/plugins/lisp.c b/plugins/lisp.c index 7147928..439dd16 100644 --- a/plugins/lisp.c +++ b/plugins/lisp.c @@ -1,9 +1,9 @@ #include "zprep_plugin.h" +#include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <ctype.h> static void skip_whitespace(const char **p) { @@ -421,36 +421,42 @@ void lisp_transpile(const char *input_body, const ZApi *api) FILE *h = api->hoist_out; fprintf(h, "/* Lisp Runtime */\n"); fprintf(h, "typedef enum { L_NUM, L_PAIR, L_NIL } LType;\n"); - fprintf(h, "typedef struct LVal { LType type; union { long num; struct { struct LVal *car; " + fprintf(h, "typedef struct LVal { LType type; union { long num; struct { " + "struct LVal *car; " "struct LVal *cdr; } pair; }; } *LVal;\n"); fprintf(h, "static struct LVal _nil = { L_NIL }; static LVal LNIL = &_nil;\n"); fprintf(h, "static LVal nil = &_nil;\n"); // Use static for file scope fprintf(h, "static LVal l_num(long n) { LVal v = malloc(sizeof(struct LVal)); " "v->type=L_NUM; v->num=n; return v; }\n"); fprintf(h, "static LVal l_nil() { return LNIL; }\n"); - fprintf(h, "static LVal l_cons(LVal a, LVal b) { LVal v = malloc(sizeof(struct LVal)); " + fprintf(h, "static LVal l_cons(LVal a, LVal b) { LVal v = " + "malloc(sizeof(struct LVal)); " "v->type=L_PAIR; v->pair.car=a; v->pair.cdr=b; return v; }\n"); - fprintf( - h, - "static LVal l_car(LVal v) { return (v && v->type==L_PAIR) ? v->pair.car : LNIL; }\n"); - fprintf( - h, - "static LVal l_cdr(LVal v) { return (v && v->type==L_PAIR) ? v->pair.cdr : LNIL; }\n"); + fprintf(h, "static LVal l_car(LVal v) { return (v && v->type==L_PAIR) ? " + "v->pair.car : LNIL; }\n"); + fprintf(h, "static LVal l_cdr(LVal v) { return (v && v->type==L_PAIR) ? " + "v->pair.cdr : LNIL; }\n"); fprintf(h, "static int l_truthy(LVal v) { return (v && v->type!=L_NIL); }\n"); - fprintf(h, "static LVal l_add(LVal a, LVal b) { long x=(a&&a->type==L_NUM)?a->num:0; long " + fprintf(h, "static LVal l_add(LVal a, LVal b) { long " + "x=(a&&a->type==L_NUM)?a->num:0; long " "y=(b&&b->type==L_NUM)?b->num:0; return l_num(x+y); }\n"); - fprintf(h, "static LVal l_sub(LVal a, LVal b) { long x=(a&&a->type==L_NUM)?a->num:0; long " + fprintf(h, "static LVal l_sub(LVal a, LVal b) { long " + "x=(a&&a->type==L_NUM)?a->num:0; long " "y=(b&&b->type==L_NUM)?b->num:0; return l_num(x-y); }\n"); - fprintf(h, "static LVal l_mul(LVal a, LVal b) { long x=(a&&a->type==L_NUM)?a->num:0; long " + fprintf(h, "static LVal l_mul(LVal a, LVal b) { long " + "x=(a&&a->type==L_NUM)?a->num:0; long " "y=(b&&b->type==L_NUM)?b->num:0; return l_num(x*y); }\n"); - fprintf(h, "static LVal l_div(LVal a, LVal b) { long x=(a&&a->type==L_NUM)?a->num:0; long " + fprintf(h, "static LVal l_div(LVal a, LVal b) { long " + "x=(a&&a->type==L_NUM)?a->num:0; long " "y=(b&&b->type==L_NUM)?b->num:0; return l_num(y?x/y:0); }\n"); - fprintf(h, "static LVal l_lt(LVal a, LVal b) { long x=(a&&a->type==L_NUM)?a->num:0; long " + fprintf(h, "static LVal l_lt(LVal a, LVal b) { long " + "x=(a&&a->type==L_NUM)?a->num:0; long " "y=(b&&b->type==L_NUM)?b->num:0; return (x<y)?l_num(1):LNIL; }\n"); fprintf(h, "static void l_print(LVal v) { \n"); fprintf(h, " if(!v || v->type==L_NIL) printf(\"nil\");\n"); fprintf(h, " else if(v->type==L_NUM) printf(\"%%ld\", v->num);\n"); - fprintf(h, " else if(v->type==L_PAIR) { printf(\"(\"); l_print(v->pair.car); printf(\" . " + fprintf(h, " else if(v->type==L_PAIR) { printf(\"(\"); " + "l_print(v->pair.car); printf(\" . " "\"); l_print(v->pair.cdr); printf(\")\"); }\n"); fprintf(h, "}\n"); diff --git a/plugins/regex.c b/plugins/regex.c index d7a8a97..b56515c 100644 --- a/plugins/regex.c +++ b/plugins/regex.c @@ -1,9 +1,9 @@ #include "zprep_plugin.h" +#include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <ctype.h> static void emit_match_logic(const char *pattern, FILE *out, int *label_counter); @@ -111,7 +111,8 @@ static void emit_match_logic(const char *pattern, FILE *out, int * /*label_count } else { - fprintf(out, " if (*c == 0) return 0;\n"); // End of input for single char + fprintf(out, + " if (*c == 0) return 0;\n"); // End of input for single char } fprintf(out, " int match = 0;\n"); diff --git a/plugins/sql.c b/plugins/sql.c index 0c563e2..130317f 100644 --- a/plugins/sql.c +++ b/plugins/sql.c @@ -1,14 +1,14 @@ #include "zprep_plugin.h" +#include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <ctype.h> typedef struct Col { char name[32]; - char type[32]; + char type[32]; struct Col *next; } Col; diff --git a/src/analysis/typecheck.c b/src/analysis/typecheck.c index 4a172d8..903cb75 100644 --- a/src/analysis/typecheck.c +++ b/src/analysis/typecheck.c @@ -1,8 +1,8 @@ +#include "typecheck.h" #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "typecheck.h" // ** Internal Helpers ** diff --git a/src/ast/ast.c b/src/ast/ast.c index 4688bf7..f34370e 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -1,9 +1,9 @@ -#include <stdlib.h> -#include <string.h> #include "ast.h" -#include "zprep.h" #include "../parser/parser.h" +#include "zprep.h" +#include <stdlib.h> +#include <string.h> typedef struct TraitReg { diff --git a/src/ast/ast.h b/src/ast/ast.h index 67dc2f3..0da2c0d 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -2,8 +2,8 @@ #ifndef AST_H #define AST_H -#include <stdlib.h> #include "zprep.h" +#include <stdlib.h> // Forward declarations. struct ASTNode; @@ -129,12 +129,13 @@ struct ASTNode 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). + 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. + Token definition_token; // For LSP: Location where the symbol used in this + // node was defined. union { @@ -375,8 +376,9 @@ struct ASTNode char *generic_param; char *parent; int is_union; - int is_packed; // @packed attribute. - int align; // @align(N) attribute, 0 = default. + int is_packed; // @packed attribute. + int align; // @align(N) attribute, 0 = default. + int is_incomplete; // Forward declaration (prototype) } strct; struct diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index abffe84..1592806 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -1,10 +1,10 @@ -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <ctype.h> #include "codegen.h" #include "zprep.h" +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include "../plugins/plugin_manager.h" #include "ast.h" @@ -436,7 +436,9 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) case NODE_LAMBDA: if (node->lambda.num_captures > 0) { - fprintf(out, "({ struct Lambda_%d_Ctx *ctx = malloc(sizeof(struct Lambda_%d_Ctx));\n", + fprintf(out, + "({ struct Lambda_%d_Ctx *ctx = malloc(sizeof(struct " + "Lambda_%d_Ctx));\n", node->lambda.lambda_id, node->lambda.lambda_id); for (int i = 0; i < node->lambda.num_captures; i++) { @@ -818,7 +820,9 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) if (is_slice_struct) { - fprintf(out, "(Slice_%s){ .data = _arr.data + _start, .len = _len, .cap = _len }; })", + fprintf(out, + "(Slice_%s){ .data = _arr.data + _start, .len = _len, .cap = " + "_len }; })", tname); } else @@ -863,7 +867,8 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) fprintf(out, " _try = "); codegen_expression(ctx, node->try_stmt.expr, out); fprintf(out, - "; if (_try.tag == %s_Err_Tag) return (%s_Err(_try.data.Err)); _try.data.Ok; })", + "; if (_try.tag == %s_Err_Tag) return (%s_Err(_try.data.Err)); " + "_try.data.Ok; })", type_name, type_name); break; } @@ -1901,7 +1906,8 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) emit_auto_type(ctx, node->repl_print.expr, node->token, out); fprintf(out, " _zval = ("); codegen_expression(ctx, node->repl_print.expr, out); - fprintf(out, "); fprintf(stdout, _z_str(_zval), _zval); fprintf(stdout, \"\\n\"); }\n"); + fprintf(out, "); fprintf(stdout, _z_str(_zval), _zval); fprintf(stdout, " + "\"\\n\"); }\n"); break; } case NODE_AWAIT: @@ -1922,8 +1928,8 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) ret_type = node->resolved_type; } - // Fallback: If type is still Async/void* (likely from Future type, not Result type), try to - // infer + // Fallback: If type is still Async/void* (likely from Future type, not + // Result type), try to infer if (strcmp(ret_type, "Async") == 0 || strcmp(ret_type, "void*") == 0) { char *inf = infer_type(ctx, node); diff --git a/src/codegen/codegen.h b/src/codegen/codegen.h index a7f3df3..d489fb3 100644 --- a/src/codegen/codegen.h +++ b/src/codegen/codegen.h @@ -2,10 +2,10 @@ #ifndef CODEGEN_H #define CODEGEN_H -#include <stdio.h> -#include "../zprep.h" #include "../ast/ast.h" #include "../parser/parser.h" +#include "../zprep.h" +#include <stdio.h> // Main codegen entry points. void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out); diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c index 8c8af97..b009a64 100644 --- a/src/codegen/codegen_decl.c +++ b/src/codegen/codegen_decl.c @@ -1,11 +1,11 @@ -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include "codegen.h" -#include "../zprep.h" #include "../ast/ast.h" #include "../parser/parser.h" +#include "../zprep.h" +#include "codegen.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> // Emit C preamble with standard includes and type definitions. void emit_preamble(FILE *out) @@ -14,15 +14,16 @@ void emit_preamble(FILE *out) { // Freestanding preamble. // It actually needs more work, but yk. - fputs( - "#include <stddef.h>\n#include <stdint.h>\n#include <stdbool.h>\n#include <stdarg.h>\n", - out); + fputs("#include <stddef.h>\n#include <stdint.h>\n#include " + "<stdbool.h>\n#include <stdarg.h>\n", + out); fputs("#ifdef __TINYC__\n#define __auto_type __typeof__\n#endif\n", out); fputs("typedef size_t usize;\ntypedef char* string;\n", out); fputs("#define U0 void\n#define I8 int8_t\n#define U8 uint8_t\n#define I16 " "int16_t\n#define U16 uint16_t\n", out); - fputs("#define I32 int32_t\n#define U32 uint32_t\n#define I64 int64_t\n#define U64 " + fputs("#define I32 int32_t\n#define U32 uint32_t\n#define I64 " + "int64_t\n#define U64 " "uint64_t\n", out); fputs("#define F32 float\n#define F64 double\n", out); @@ -36,7 +37,9 @@ void emit_preamble(FILE *out) fputs("typedef struct { void *func; void *ctx; } z_closure_T;\n", out); fputs("__attribute__((weak)) void* z_malloc(usize sz) { return NULL; }\n", out); - fputs("__attribute__((weak)) void* z_realloc(void* ptr, usize sz) { return NULL; }\n", out); + fputs("__attribute__((weak)) void* z_realloc(void* ptr, usize sz) { return " + "NULL; }\n", + out); fputs("__attribute__((weak)) void z_free(void* ptr) { }\n", out); fputs("__attribute__((weak)) void z_print(const char* fmt, ...) { }\n", out); fputs("__attribute__((weak)) void z_panic(const char* msg) { while(1); }\n", out); @@ -44,7 +47,8 @@ void emit_preamble(FILE *out) else { // Standard hosted preamble. - fputs("#include <stdio.h>\n#include <stdlib.h>\n#include <stddef.h>\n#include <string.h>\n", + fputs("#include <stdio.h>\n#include <stdlib.h>\n#include " + "<stddef.h>\n#include <string.h>\n", out); fputs("#include <stdarg.h>\n#include <stdint.h>\n#include <stdbool.h>\n", out); fputs("#include <unistd.h>\n#include <fcntl.h>\n", out); // POSIX functions @@ -56,7 +60,8 @@ void emit_preamble(FILE *out) fputs("#define U0 void\n#define I8 int8_t\n#define U8 uint8_t\n#define I16 " "int16_t\n#define U16 uint16_t\n", out); - fputs("#define I32 int32_t\n#define U32 uint32_t\n#define I64 int64_t\n#define U64 " + fputs("#define I32 int32_t\n#define U32 uint32_t\n#define I64 " + "int64_t\n#define U64 " "uint64_t\n", out); fputs("#define F32 float\n#define F64 double\n", out); @@ -69,25 +74,32 @@ void emit_preamble(FILE *out) out); // Memory Mapping. - fputs("#define z_malloc malloc\n#define z_realloc realloc\n#define z_free free\n#define " + fputs("#define z_malloc malloc\n#define z_realloc realloc\n#define z_free " + "free\n#define " "z_print printf\n", out); - fputs( - "void z_panic(const char* msg) { fprintf(stderr, \"Panic: %s\\n\", msg); exit(1); }\n", - out); + fputs("void z_panic(const char* msg) { fprintf(stderr, \"Panic: %s\\n\", " + "msg); exit(1); }\n", + out); - fputs("void _z_autofree_impl(void *p) { void **pp = (void**)p; if(*pp) { z_free(*pp); *pp " + fputs("void _z_autofree_impl(void *p) { void **pp = (void**)p; if(*pp) { " + "z_free(*pp); *pp " "= NULL; } }\n", out); - fputs("#define assert(cond, ...) if (!(cond)) { fprintf(stderr, \"Assertion failed: \" " + fputs("#define assert(cond, ...) if (!(cond)) { fprintf(stderr, " + "\"Assertion failed: \" " "__VA_ARGS__); exit(1); }\n", out); - fputs("string _z_readln_raw() { char *line = NULL; size_t len = 0; if(getline(&line, &len, " - "stdin) == -1) return NULL; if(strlen(line) > 0 && line[strlen(line)-1] == '\\n') " + fputs("string _z_readln_raw() { char *line = NULL; size_t len = 0; " + "if(getline(&line, &len, " + "stdin) == -1) return NULL; if(strlen(line) > 0 && " + "line[strlen(line)-1] == '\\n') " "line[strlen(line)-1] = 0; return line; }\n", out); - fputs("int _z_scan_helper(const char *fmt, ...) { char *l = _z_readln_raw(); if(!l) return " - "0; va_list ap; va_start(ap, fmt); int r = vsscanf(l, fmt, ap); va_end(ap); " + fputs("int _z_scan_helper(const char *fmt, ...) { char *l = " + "_z_readln_raw(); if(!l) return " + "0; va_list ap; va_start(ap, fmt); int r = vsscanf(l, fmt, ap); " + "va_end(ap); " "z_free(l); return r; }\n", out); @@ -235,6 +247,13 @@ void emit_struct_defs(ParserContext *ctx, ASTNode *node, FILE *out) } if (node->type == NODE_STRUCT) { + if (node->strct.is_incomplete) + { + // Forward declaration - no body needed (typedef handles it) + node = node->next; + continue; + } + if (node->strct.is_union) { fprintf(out, "union %s {", node->strct.name); @@ -763,35 +782,40 @@ void print_type_defs(ParserContext *ctx, FILE *out, ASTNode *nodes) fprintf(out, "typedef struct { void **data; int len; int cap; } Vec;\n"); fprintf(out, "#define Vec_new() (Vec){.data=0, .len=0, .cap=0}\n"); - fprintf( - out, - "void _z_vec_push(Vec *v, void *item) { if(v->len >= v->cap) { v->cap = v->cap?v->cap*2:8; " - "v->data = z_realloc(v->data, v->cap * sizeof(void*)); } v->data[v->len++] = item; }\n"); + fprintf(out, "void _z_vec_push(Vec *v, void *item) { if(v->len >= v->cap) { " + "v->cap = v->cap?v->cap*2:8; " + "v->data = z_realloc(v->data, v->cap * sizeof(void*)); } " + "v->data[v->len++] = item; }\n"); fprintf(out, "#define Vec_push(v, i) _z_vec_push(&(v), (void*)(long)(i))\n"); - fprintf(out, "static inline Vec _z_make_vec(int count, ...) { Vec v = {0}; v.cap = count > 8 ? " - "count : 8; v.data = z_malloc(v.cap * sizeof(void*)); v.len = 0; va_list args; " + fprintf(out, "static inline Vec _z_make_vec(int count, ...) { Vec v = {0}; v.cap = " + "count > 8 ? " + "count : 8; v.data = z_malloc(v.cap * sizeof(void*)); v.len = 0; va_list " + "args; " "va_start(args, count); for(int i=0; i<count; i++) { v.data[v.len++] = " "va_arg(args, void*); } va_end(args); return v; }\n"); if (g_config.is_freestanding) { - fprintf(out, "#define _z_check_bounds(index, limit) ({ __auto_type _i = (index); if(_i < 0 " + fprintf(out, "#define _z_check_bounds(index, limit) ({ __auto_type _i = " + "(index); if(_i < 0 " "|| _i >= (limit)) { z_panic(\"index out of bounds\"); } _i; })\n"); } else { - fprintf(out, "#define _z_check_bounds(index, limit) ({ __auto_type _i = (index); if(_i < 0 " - "|| _i >= (limit)) { fprintf(stderr, \"Index out of bounds: %%ld (limit " + fprintf(out, "#define _z_check_bounds(index, limit) ({ __auto_type _i = " + "(index); if(_i < 0 " + "|| _i >= (limit)) { fprintf(stderr, \"Index out of bounds: " + "%%ld (limit " "%%d)\\n\", (long)_i, (int)(limit)); exit(1); } _i; })\n"); } SliceType *c = ctx->used_slices; while (c) { - fprintf( - out, - "typedef struct Slice_%s Slice_%s;\nstruct Slice_%s { %s *data; int len; int cap; };\n", - c->name, c->name, c->name, c->name); + fprintf(out, + "typedef struct Slice_%s Slice_%s;\nstruct Slice_%s { %s *data; " + "int len; int cap; };\n", + c->name, c->name, c->name, c->name); c = c->next; } @@ -814,8 +838,8 @@ void print_type_defs(ParserContext *ctx, FILE *out, ASTNode *nodes) } fprintf(out, "\n"); - // FIRST: Emit typedefs for ALL structs and enums in the current compilation unit (local - // definitions) + // FIRST: Emit typedefs for ALL structs and enums in the current compilation + // unit (local definitions) ASTNode *local = nodes; while (local) { diff --git a/src/codegen/codegen_main.c b/src/codegen/codegen_main.c index 0e10869..c9c69f6 100644 --- a/src/codegen/codegen_main.c +++ b/src/codegen/codegen_main.c @@ -1,10 +1,10 @@ +#include "../ast/ast.h" +#include "../zprep.h" +#include "codegen.h" #include <stdio.h> -#include <string.h> #include <stdlib.h> -#include "codegen.h" -#include "../zprep.h" -#include "../ast/ast.h" +#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) @@ -191,7 +191,8 @@ 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). + // Recursive Unwrap of Nested Roots (if accidentally wrapped multiple + // times). while (kids && kids->type == NODE_ROOT) { kids = kids->root.children; diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c index fac6c6d..5dcbf19 100644 --- a/src/codegen/codegen_utils.c +++ b/src/codegen/codegen_utils.c @@ -1,12 +1,12 @@ -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <ctype.h> -#include "codegen.h" -#include "../zprep.h" #include "../ast/ast.h" #include "../parser/parser.h" +#include "../zprep.h" +#include "codegen.h" +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> // Global state ASTNode *global_user_structs = NULL; @@ -44,7 +44,7 @@ ASTNode *find_struct_def_codegen(ParserContext *ctx, const char *name) ASTNode *s = global_user_structs; while (s) { - if (s->type == NODE_STRUCT && strcmp(s->strct.name, name) == 0) + if (s->type == NODE_STRUCT && strcmp(s->strct.name, name) == 0 && !s->strct.is_incomplete) { return s; } @@ -55,7 +55,8 @@ ASTNode *find_struct_def_codegen(ParserContext *ctx, const char *name) StructRef *sr = ctx->parsed_structs_list; while (sr) { - if (sr->node && sr->node->type == NODE_STRUCT && strcmp(sr->node->strct.name, name) == 0) + if (sr->node && sr->node->type == NODE_STRUCT && strcmp(sr->node->strct.name, name) == 0 && + !sr->node->strct.is_incomplete) { return sr->node; } @@ -64,7 +65,7 @@ ASTNode *find_struct_def_codegen(ParserContext *ctx, const char *name) s = ctx->instantiated_structs; while (s) { - if (s->type == NODE_STRUCT && strcmp(s->strct.name, name) == 0) + if (s->type == NODE_STRUCT && strcmp(s->strct.name, name) == 0 && !s->strct.is_incomplete) { return s; } diff --git a/src/lsp/json_rpc.c b/src/lsp/json_rpc.c index 008a147..903da71 100644 --- a/src/lsp/json_rpc.c +++ b/src/lsp/json_rpc.c @@ -1,8 +1,8 @@ +#include "json_rpc.h" #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "json_rpc.h" // Basic JSON parsing helpers char *get_json_string(const char *json, const char *key) @@ -27,8 +27,9 @@ char *get_json_string(const char *json, const char *key) return res; } -// Extract nested "text" from params/contentChanges/0/text or params/textDocument/text -// This is very hacky for MVP. proper JSON library needed. +// 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) { @@ -116,10 +117,11 @@ 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\":[\".\"]}}}}"; + 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; diff --git a/src/lsp/lsp_analysis.c b/src/lsp/lsp_analysis.c index 7a22906..d455894 100644 --- a/src/lsp/lsp_analysis.c +++ b/src/lsp/lsp_analysis.c @@ -1,12 +1,11 @@ +#include "json_rpc.h" +#include "lsp_index.h" +#include "parser.h" +#include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <ctype.h> -#include "parser.h" -#include "parser.h" -#include "json_rpc.h" -#include "lsp_index.h" static LSPIndex *g_index = NULL; @@ -101,7 +100,8 @@ void lsp_check_file(const char *uri, const char *json_src) { p += sprintf(p, - "{\"range\":{\"start\":{\"line\":%d,\"character\":%d},\"end\":{\"line\":%d," + "{\"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); @@ -146,8 +146,10 @@ void lsp_goto_definition(const char *uri, int line, int col) // 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}}}}", + "{\"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); @@ -158,8 +160,10 @@ void lsp_goto_definition(const char *uri, int line, int col) // 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}}}}", + "{\"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); @@ -206,7 +210,8 @@ void lsp_hover(const char *uri, int line, int col) char *json = malloc(16384); // content: { kind: markdown, value: text } sprintf(json, - "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"contents\":{\"kind\":\"markdown\"," + "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"contents\":{\"kind\":" + "\"markdown\"," "\"value\":\"```c\\n%s\\n```\"}}}", text); diff --git a/src/lsp/lsp_index.c b/src/lsp/lsp_index.c index 23d81ac..d952b77 100644 --- a/src/lsp/lsp_index.c +++ b/src/lsp/lsp_index.c @@ -1,8 +1,8 @@ +#include "lsp_index.h" #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "lsp_index.h" LSPIndex *lsp_index_new() { diff --git a/src/lsp/lsp_main.c b/src/lsp/lsp_main.c index 9a3489b..fbe5312 100644 --- a/src/lsp/lsp_main.c +++ b/src/lsp/lsp_main.c @@ -1,9 +1,9 @@ +#include "json_rpc.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include "json_rpc.h" // Simple Main Loop for LSP. int lsp_main(int argc, char **argv) @@ -1,12 +1,12 @@ +#include "codegen/codegen.h" +#include "parser/parser.h" +#include "plugins/plugin_manager.h" +#include "repl/repl.h" +#include "zprep.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include "zprep.h" -#include "parser/parser.h" -#include "codegen/codegen.h" -#include "repl/repl.h" -#include "plugins/plugin_manager.h" // Forward decl for LSP int lsp_main(int argc, char **argv); @@ -79,8 +79,8 @@ int main(int argc, char **argv) } 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 + // 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 @@ -234,7 +234,8 @@ int main(int argc, char **argv) } // Checking mode? - // analyze(root); // Implicit in parsing or separate step? Assuming separate if check_mode + // analyze(root); // Implicit in parsing or separate step? Assuming separate + // if check_mode if (g_config.mode_check) { diff --git a/src/parser/parser.h b/src/parser/parser.h index b3213c9..4aeb2c8 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -114,6 +114,8 @@ typedef struct VarMutability typedef struct Instantiation { char *name; + char *template_name; + char *concrete_arg; ASTNode *struct_node; struct Instantiation *next; } Instantiation; @@ -286,7 +288,8 @@ void add_symbol_with_token(ParserContext *ctx, const char *n, const char *t, Typ 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); -Symbol *find_symbol_in_all(ParserContext *ctx, const char *n); // LSP flat lookup +Symbol *find_symbol_in_all(ParserContext *ctx, + const char *n); // LSP flat lookup char *find_similar_symbol(ParserContext *ctx, const char *name); // Function registry diff --git a/src/parser/parser_core.c b/src/parser/parser_core.c index 1b40cf4..1a47275 100644 --- a/src/parser/parser_core.c +++ b/src/parser/parser_core.c @@ -1,9 +1,9 @@ +#include "parser.h" +#include "zprep.h" #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "parser.h" -#include "zprep.h" static ASTNode *generate_derive_impls(ParserContext *ctx, ASTNode *strct, char **traits, int count); diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c index e12c837..469d623 100644 --- a/src/parser/parser_expr.c +++ b/src/parser/parser_expr.c @@ -1,10 +1,10 @@ +#include "../zen/zen_facts.h" +#include "parser.h" +#include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <ctype.h> -#include "parser.h" -#include "../zen/zen_facts.h" Type *get_field_type(ParserContext *ctx, Type *struct_type, const char *field_name); @@ -1411,7 +1411,8 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l) Type *st = type_new(TYPE_STRUCT); st->name = xstrdup(struct_name); node->type_info = st; - return node; // Struct init cannot be called/indexed usually, return early + return node; // Struct init cannot be called/indexed usually, return + // early } } @@ -1456,7 +1457,6 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l) else { // readln(vars...) -> _z_scan_helper("fmt", &vars...) - // 1. Build Format String char fmt[256]; fmt[0] = 0; for (int i = 0; i < ac; i++) @@ -1505,14 +1505,12 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l) } } - // 2. Build Call Node node = ast_create(NODE_EXPR_CALL); ASTNode *callee = ast_create(NODE_EXPR_VAR); callee->var_ref.name = xstrdup("_z_scan_helper"); node->call.callee = callee; node->type_info = type_new(TYPE_INT); // Returns count - // 3. Build Args List: "fmt" then &arg1, &arg2... ASTNode *fmt_node = ast_create(NODE_EXPR_LITERAL); fmt_node->literal.type_kind = 2; // string fmt_node->literal.string_val = xstrdup(fmt); @@ -1532,218 +1530,216 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l) node->call.args = head; } free(acc); - } - else - if (sig && lexer_peek(l).type == TOK_LPAREN) - { - lexer_next(l); - ASTNode *head = NULL, *tail = NULL; - int args_provided = 0; - char **arg_names = NULL; - int has_named = 0; + else if (sig && lexer_peek(l).type == TOK_LPAREN) + { + lexer_next(l); + ASTNode *head = NULL, *tail = NULL; + int args_provided = 0; + char **arg_names = NULL; + int has_named = 0; - if (lexer_peek(l).type != TOK_RPAREN) + if (lexer_peek(l).type != TOK_RPAREN) + { + while (1) { - while (1) - { - char *arg_name = NULL; + char *arg_name = NULL; - Token t1 = lexer_peek(l); - if (t1.type == TOK_IDENT) + Token t1 = lexer_peek(l); + if (t1.type == TOK_IDENT) + { + Token t2 = lexer_peek2(l); + if (t2.type == TOK_COLON) { - Token t2 = lexer_peek2(l); - if (t2.type == TOK_COLON) - { - arg_name = token_strdup(t1); - has_named = 1; - lexer_next(l); - lexer_next(l); - } + arg_name = token_strdup(t1); + has_named = 1; + lexer_next(l); + lexer_next(l); } + } - ASTNode *arg = parse_expression(ctx, l); - if (!head) - { - head = arg; - } - else - { - tail->next = arg; - } - tail = arg; - args_provided++; + ASTNode *arg = parse_expression(ctx, l); + if (!head) + { + head = arg; + } + else + { + tail->next = arg; + } + tail = arg; + args_provided++; - arg_names = xrealloc(arg_names, args_provided * sizeof(char *)); - arg_names[args_provided - 1] = arg_name; + arg_names = xrealloc(arg_names, args_provided * sizeof(char *)); + arg_names[args_provided - 1] = arg_name; - arg_names = xrealloc(arg_names, args_provided * sizeof(char *)); - arg_names[args_provided - 1] = arg_name; + arg_names = xrealloc(arg_names, args_provided * sizeof(char *)); + arg_names[args_provided - 1] = arg_name; - if (lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - } - else - { - break; - } + if (lexer_peek(l).type == TOK_COMMA) + { + lexer_next(l); } - } - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic("Expected )"); - } - for (int i = args_provided; i < sig->total_args; i++) - { - if (sig->defaults[i]) + else { - ASTNode *def = ast_create(NODE_RAW_STMT); - def->raw_stmt.content = xstrdup(sig->defaults[i]); - if (!head) - { - head = def; - } - else - { - tail->next = def; - } - tail = def; + break; } } - node = ast_create(NODE_EXPR_CALL); - node->token = t; // Set source token - ASTNode *callee = ast_create(NODE_EXPR_VAR); - callee->var_ref.name = acc; - node->call.callee = callee; - node->call.args = head; - node->call.arg_names = has_named ? arg_names : NULL; - node->call.arg_count = args_provided; - if (sig) - { - node->definition_token = sig->decl_token; - } - if (sig->is_async) - { - Type *async_type = type_new(TYPE_STRUCT); - async_type->name = xstrdup("Async"); - node->type_info = async_type; - node->resolved_type = xstrdup("Async"); - } - else if (sig->ret_type) - { - node->type_info = sig->ret_type; - node->resolved_type = type_to_string(sig->ret_type); - } - else + } + if (lexer_next(l).type != TOK_RPAREN) + { + zpanic("Expected )"); + } + for (int i = args_provided; i < sig->total_args; i++) + { + if (sig->defaults[i]) { - node->resolved_type = xstrdup("void"); + ASTNode *def = ast_create(NODE_RAW_STMT); + def->raw_stmt.content = xstrdup(sig->defaults[i]); + if (!head) + { + head = def; + } + else + { + tail->next = def; + } + tail = def; } } - else if (!sig && !find_symbol_entry(ctx, acc) && lexer_peek(l).type == TOK_LPAREN) + node = ast_create(NODE_EXPR_CALL); + node->token = t; // Set source token + ASTNode *callee = ast_create(NODE_EXPR_VAR); + callee->var_ref.name = acc; + node->call.callee = callee; + node->call.args = head; + node->call.arg_names = has_named ? arg_names : NULL; + node->call.arg_count = args_provided; + if (sig) { - lexer_next(l); // eat ( - ASTNode *head = NULL, *tail = NULL; - char **arg_names = NULL; - int args_provided = 0; - int has_named = 0; + node->definition_token = sig->decl_token; + } + if (sig->is_async) + { + Type *async_type = type_new(TYPE_STRUCT); + async_type->name = xstrdup("Async"); + node->type_info = async_type; + node->resolved_type = xstrdup("Async"); + } + else if (sig->ret_type) + { + node->type_info = sig->ret_type; + node->resolved_type = type_to_string(sig->ret_type); + } + else + { + node->resolved_type = xstrdup("void"); + } + } + else if (!sig && !find_symbol_entry(ctx, acc) && lexer_peek(l).type == TOK_LPAREN) + { + lexer_next(l); // eat ( + ASTNode *head = NULL, *tail = NULL; + char **arg_names = NULL; + int args_provided = 0; + int has_named = 0; - if (lexer_peek(l).type != TOK_RPAREN) + if (lexer_peek(l).type != TOK_RPAREN) + { + while (1) { - while (1) - { - char *arg_name = NULL; + char *arg_name = NULL; - // Check for named argument: name: value - Token t1 = lexer_peek(l); - if (t1.type == TOK_IDENT) + // Check for named argument: name: value + Token t1 = lexer_peek(l); + if (t1.type == TOK_IDENT) + { + Token t2 = lexer_peek2(l); + if (t2.type == TOK_COLON) { - Token t2 = lexer_peek2(l); - if (t2.type == TOK_COLON) - { - arg_name = token_strdup(t1); - has_named = 1; - lexer_next(l); - lexer_next(l); - } + arg_name = token_strdup(t1); + has_named = 1; + lexer_next(l); + lexer_next(l); } + } - ASTNode *arg = parse_expression(ctx, l); - if (!head) - { - head = arg; - } - else - { - tail->next = arg; - } - tail = arg; - args_provided++; + ASTNode *arg = parse_expression(ctx, l); + if (!head) + { + head = arg; + } + else + { + tail->next = arg; + } + tail = arg; + args_provided++; - arg_names = xrealloc(arg_names, args_provided * sizeof(char *)); - arg_names[args_provided - 1] = arg_name; + arg_names = xrealloc(arg_names, args_provided * sizeof(char *)); + arg_names[args_provided - 1] = arg_name; - if (lexer_peek(l).type == TOK_COMMA) - { - lexer_next(l); - } - else - { - break; - } + if (lexer_peek(l).type == TOK_COMMA) + { + lexer_next(l); + } + else + { + break; } } - if (lexer_next(l).type != TOK_RPAREN) - { - zpanic("Expected )"); - } - - node = ast_create(NODE_EXPR_CALL); - node->token = t; - ASTNode *callee = ast_create(NODE_EXPR_VAR); - callee->var_ref.name = acc; - node->call.callee = callee; - node->call.args = head; - node->call.arg_names = has_named ? arg_names : NULL; - node->call.arg_count = args_provided; - // Unknown return type - let codegen infer it - node->resolved_type = xstrdup("unknown"); - // Fall through to Postfix } - else + if (lexer_next(l).type != TOK_RPAREN) { - node = ast_create(NODE_EXPR_VAR); - node->token = t; // Set source token - node->var_ref.name = acc; - node->type_info = find_symbol_type_info(ctx, acc); + zpanic("Expected )"); + } - Symbol *sym = find_symbol_entry(ctx, acc); - if (sym) - { - sym->is_used = 1; - node->definition_token = sym->decl_token; - } + node = ast_create(NODE_EXPR_CALL); + node->token = t; + ASTNode *callee = ast_create(NODE_EXPR_VAR); + callee->var_ref.name = acc; + node->call.callee = callee; + node->call.args = head; + node->call.arg_names = has_named ? arg_names : NULL; + node->call.arg_count = args_provided; + // Unknown return type - let codegen infer it + node->resolved_type = xstrdup("unknown"); + // Fall through to Postfix + } + else + { + node = ast_create(NODE_EXPR_VAR); + node->token = t; // Set source token + node->var_ref.name = acc; + node->type_info = find_symbol_type_info(ctx, acc); - char *type_str = find_symbol_type(ctx, acc); + Symbol *sym = find_symbol_entry(ctx, acc); + if (sym) + { + sym->is_used = 1; + node->definition_token = sym->decl_token; + } + + char *type_str = find_symbol_type(ctx, acc); - if (type_str) + if (type_str) + { + node->resolved_type = type_str; + node->var_ref.suggestion = NULL; + } + else + { + node->resolved_type = xstrdup("unknown"); + if (should_suppress_undef_warning(ctx, acc)) { - node->resolved_type = type_str; node->var_ref.suggestion = NULL; } else { - node->resolved_type = xstrdup("unknown"); - if (should_suppress_undef_warning(ctx, acc)) - { - node->var_ref.suggestion = NULL; - } - else - { - node->var_ref.suggestion = find_similar_symbol(ctx, acc); - } + node->var_ref.suggestion = find_similar_symbol(ctx, acc); } } + } } else if (t.type == TOK_LPAREN) @@ -2421,7 +2417,8 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) lhs = ast_create(NODE_AWAIT); lhs->unary.operand = operand; // Type inference: await Async<T> yields T - // If operand is a call to an async function, look up its ret_type (not Async) + // If operand is a call to an async function, look up its ret_type (not + // Async) if (operand->type == NODE_EXPR_CALL && operand->call.callee->type == NODE_EXPR_VAR) { FuncSig *sig = find_func(ctx, operand->call.callee->var_ref.name); @@ -2924,8 +2921,9 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) // Clean up string for registration (e.g. "int" from "int*") char *inner_str = type_to_string(inner); - // Strip * if it somehow managed to keep one, though parse_type_formal - // should handle it For now assume type_to_string gives base type + // Strip * if it somehow managed to keep one, though + // parse_type_formal should handle it For now assume type_to_string + // gives base type register_slice(ctx, inner_str); } } @@ -3050,7 +3048,8 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) FuncSig *sig = find_func(ctx, mangled); if (sig) { - // It is a method! Create a Function Type Info to carry the return type + // It is a method! Create a Function Type Info to carry the return + // type Type *ft = type_new(TYPE_FUNCTION); ft->name = xstrdup(mangled); ft->inner = sig->ret_type; // Return type @@ -3164,7 +3163,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) } } } - + if (strcmp(bin->binary.op, "=") == 0 || strcmp(bin->binary.op, "+=") == 0 || strcmp(bin->binary.op, "-=") == 0 || strcmp(bin->binary.op, "*=") == 0 || strcmp(bin->binary.op, "/=") == 0) @@ -3183,13 +3182,14 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) if (!is_var_mutable(ctx, lhs->var_ref.name)) { zpanic_at(op, - "Cannot assign to immutable variable '%s' (use 'var mut' to make it " + "Cannot assign to immutable variable '%s' (use 'var mut' " + "to make it " "mutable)", lhs->var_ref.name); } } } - + int is_compound = 0; size_t op_len = strlen(bin->binary.op); @@ -3318,7 +3318,8 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) set_callee->var_ref.name = set_name; set_call->call.callee = set_callee; - // Clone argument list (Shallow copy of arg nodes to preserve chain for get) + // Clone argument list (Shallow copy of arg nodes to preserve chain + // for get) ASTNode *lhs_args = lhs->call.args; ASTNode *new_head = NULL; ASTNode *new_tail = NULL; diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index 619caaf..30c9e90 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -1,13 +1,13 @@ -// parser_stmt.c - Statement and Declaration parsing + +#include "parser.h" +#include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <ctype.h> #include <unistd.h> -#include "parser.h" -#include "../plugins/plugin_manager.h" #include "../ast/ast.h" +#include "../plugins/plugin_manager.h" #include "../zen/zen_facts.h" #include "zprep_plugin.h" @@ -83,7 +83,8 @@ ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async) curr_func_ret = ret; // Auto-prefix function name if in module context - // Don't prefix generic templates or functions inside impl blocks (already mangled) + // Don't prefix generic templates or functions inside impl blocks (already + // mangled) if (ctx->current_module_prefix && !gen_param && !ctx->current_impl_struct) { char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(name) + 2); @@ -111,8 +112,8 @@ ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async) } // Check for unused parameters - // The current scope contains arguments (since parse_block creates a new child scope for body) - // Only check if we parsed a body (not a prototype) function + // The current scope contains arguments (since parse_block creates a new child + // scope for body) Only check if we parsed a body (not a prototype) function if (body && ctx->current_scope) { Symbol *sym = ctx->current_scope->symbols; @@ -1168,8 +1169,9 @@ ASTNode *parse_var_decl(ParserContext *ctx, Lexer *l) type_obj->name = xstrdup(type); } - // fprintf(stderr, "DEBUG PVarDecl: Var '%s' inferred type '%s' (init->type_info - // present: %d)\n", name, type, init && init->type_info ? 1 : 0); + // fprintf(stderr, "DEBUG PVarDecl: Var '%s' inferred type '%s' + // (init->type_info present: %d)\n", name, type, init && init->type_info ? + // 1 : 0); } } @@ -1372,7 +1374,8 @@ ASTNode *parse_return(ParserContext *ctx, Lexer *l) int handled = 0; // 1. Check for Tuple Literal Return: return (a, b); - // Condition: Function returns Tuple_..., starts with '(', and contains ',' at top level + // Condition: Function returns Tuple_..., starts with '(', and contains ',' at + // top level if (curr_func_ret && strncmp(curr_func_ret, "Tuple_", 6) == 0 && lexer_peek(l).type == TOK_LPAREN) { @@ -1409,7 +1412,8 @@ ASTNode *parse_return(ParserContext *ctx, Lexer *l) } } - // If we find a comma at depth 1 (inside the first parens), it's a tuple literal! + // If we find a comma at depth 1 (inside the first parens), it's a tuple + // literal! if (depth == 1 && t.type == TOK_COMMA) { is_tuple_lit = 1; @@ -1772,7 +1776,8 @@ char *process_printf_sugar(ParserContext *ctx, const char *content, int newline, clean_expr++; // Skip leading spaces } - // Mark the variable as used (fixes "Unused variable" warnings for f-string interpolation) + // Mark the variable as used (fixes "Unused variable" warnings for f-string + // interpolation) Symbol *sym = find_symbol_entry(ctx, clean_expr); if (sym) { @@ -1806,7 +1811,8 @@ char *process_printf_sugar(ParserContext *ctx, const char *content, int newline, } // ============================================================ - // Rewrite the expression to handle pointer access (header_ptr.magic -> header_ptr->magic) + // Rewrite the expression to handle pointer access (header_ptr.magic -> + // header_ptr->magic) char *wrapped_expr = xmalloc(strlen(expr) + 5); sprintf(wrapped_expr, "#{%s}", expr); char *rw_expr = rewrite_expr_methods(ctx, wrapped_expr); @@ -2672,9 +2678,9 @@ ASTNode *parse_trait(ParserContext *ctx, Lexer *l) // Parse method signature: fn name(args...) -> ret; // Re-use parse_function but stop at semicolon? - // Actually trait methods might have default impls later, but for now just signatures. - // Let's parse full function but body might be empty/null? - // Or simpler: just parse signature manually. + // Actually trait methods might have default impls later, but for now just + // signatures. Let's parse full function but body might be empty/null? Or + // simpler: just parse signature manually. Token ft = lexer_next(l); if (ft.type != TOK_IDENT || strncmp(ft.start, "fn", 2) != 0) @@ -2995,8 +3001,8 @@ ASTNode *parse_impl(ParserContext *ctx, Lexer *l) free(f->func.args); f->func.args = na; - // FIX: Register the MANGLED name so calls like String_from(...) find the return - // type + // FIX: Register the MANGLED name so calls like String_from(...) find + // the return type register_func(ctx, mangled, f->func.arg_count, f->func.defaults, f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, 0, f->token); @@ -3077,6 +3083,25 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) register_generic(ctx, name); } + // Check for prototype (forward declaration) + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); + ASTNode *n = ast_create(NODE_STRUCT); + n->strct.name = name; + n->strct.is_template = (gp != NULL); + n->strct.generic_param = gp; + n->strct.is_union = is_union; + n->strct.fields = NULL; + n->strct.is_incomplete = 1; + + if (!gp) + { + add_to_struct_list(ctx, n); + } + return n; + } + lexer_next(l); // eat { ASTNode *h = 0, *tl = 0; @@ -3139,10 +3164,11 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union) } else { - // If definition not found (e.g. user struct defined later), we can't embed fields - // yet. Compiler limitation: 'use' requires struct to be defined before. Fallback: - // Emit a placeholder field so compilation doesn't crash, but layout will be wrong. - // printf("Warning: Could not find struct '%s' for embedding.\n", use_name); + // If definition not found (e.g. user struct defined later), we can't + // embed fields yet. Compiler limitation: 'use' requires struct to be + // defined before. Fallback: Emit a placeholder field so compilation + // doesn't crash, but layout will be wrong. printf("Warning: Could not + // find struct '%s' for embedding.\n", use_name); } free(use_name); continue; @@ -3528,7 +3554,9 @@ ASTNode *parse_import(ParserContext *ctx, Lexer *l) Token t = lexer_next(l); if (t.type != TOK_STRING) { - zpanic("Expected string (filename) after 'from' in selective import, got type %d", t.type); + zpanic("Expected string (filename) after 'from' in selective import, got " + "type %d", + t.type); } int ln = t.len - 2; // Remove quotes char *fn = xmalloc(ln + 1); @@ -3648,9 +3676,10 @@ ASTNode *parse_import(ParserContext *ctx, Lexer *l) // C Header: Emit include and return (don't parse) if (strlen(fn) > 2 && strcmp(fn + strlen(fn) - 2, ".h") == 0) { - // We can iterate over registered modules to check if we missed setting is_c_header? - // But we handled 'as' above. If no 'as', we need to check if we should register it? - // Usually 'import "foo.h" as f' is required for namespacing. + // We can iterate over registered modules to check if we missed setting + // is_c_header? But we handled 'as' above. If no 'as', we need to check if + // we should register it? Usually 'import "foo.h" as f' is required for + // namespacing. // Emit #include // TODO: Where to emit? parser doesn't emit code usually. @@ -3726,7 +3755,8 @@ ASTNode *parse_import(ParserContext *ctx, Lexer *l) } if (module_base_name) - { // This was only used for selective import registration, not for ctx->current_module_prefix + { // This was only used for selective import + // registration, not for ctx->current_module_prefix free(module_base_name); } @@ -3776,12 +3806,13 @@ ASTNode *parse_comptime(ParserContext *ctx, Lexer *l) // Stdout capture wrapper // We assume the user writes C code that fits in main(), or includes headers. - // For simplicity V1: We provide standard headers and wrap content in main if it doesn't look - // like definitions? Actually failure mode: User defines functions. Better: User provides body - // of main() or definitions. Heuristic: If we wrap in main, we can't define functions inside - // main (standard C). Proposal: User code runs AS IS. User must provide main(). Wait, user - // example: printf("..."); If I just paste printf("..."), it needs a main. Let's wrap in main() - // by default. + // For simplicity V1: We provide standard headers and wrap content in main if + // it doesn't look like definitions? Actually failure mode: User defines + // functions. Better: User provides body of main() or definitions. Heuristic: + // If we wrap in main, we can't define functions inside main (standard C). + // Proposal: User code runs AS IS. User must provide main(). Wait, user + // example: printf("..."); If I just paste printf("..."), it needs a main. + // Let's wrap in main() by default. fprintf(f, "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n"); fprintf(f, "int main() {\n%s\nreturn 0;\n}\n", code); fclose(f); @@ -3832,8 +3863,8 @@ ASTNode *parse_comptime(ParserContext *ctx, Lexer *l) // 6. Parse Output (Recursively) Lexer new_l; lexer_init(&new_l, output_src); - // Note: Recursive call. We leak output_src intentionally so AST tokens remain valid. - // In a long running process we would manage this in an Arena. + // Note: Recursive call. We leak output_src intentionally so AST tokens remain + // valid. In a long running process we would manage this in an Arena. return parse_program_nodes(ctx, &new_l); } diff --git a/src/parser/parser_type.c b/src/parser/parser_type.c index a5f8d86..04a7de9 100644 --- a/src/parser/parser_type.c +++ b/src/parser/parser_type.c @@ -1,10 +1,10 @@ +#include "../ast/ast.h" +#include "parser.h" +#include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <ctype.h> -#include "parser.h" -#include "../ast/ast.h" Type *parse_type_base(ParserContext *ctx, Lexer *l) { @@ -252,7 +252,8 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l) if (si) { // This is a selectively imported symbol - // Resolve to the actual struct name which was prefixed during module parsing + // Resolve to the actual struct name which was prefixed during module + // parsing free(name); name = xmalloc(strlen(si->source_module) + strlen(si->symbol) + 2); sprintf(name, "%s_%s", si->source_module, si->symbol); @@ -262,7 +263,8 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l) // If we're IN a module and no selective import matched, apply module prefix if (ctx->current_module_prefix && !is_known_generic(ctx, name)) { - // Auto-prefix struct name if in module context (unless it's a known primitive/generic) + // Auto-prefix struct name if in module context (unless it's a known + // primitive/generic) char *prefixed_name = xmalloc(strlen(ctx->current_module_prefix) + strlen(name) + 2); sprintf(prefixed_name, "%s_%s", ctx->current_module_prefix, name); free(name); @@ -278,7 +280,8 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l) lexer_next(l); // eat < Type *arg = parse_type_formal(ctx, l); - // Handle nested generics like Vec<Vec<int>> where >> is tokenized as one op + // Handle nested generics like Vec<Vec<int>> where >> is tokenized as one + // op Token next_tok = lexer_peek(l); if (next_tok.type == TOK_RANGLE) { diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c index 88823de..fcaab7f 100644 --- a/src/parser/parser_utils.c +++ b/src/parser/parser_utils.c @@ -1,10 +1,13 @@ +#include "../codegen/codegen.h" +#include "parser.h" +#include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <ctype.h> -#include "parser.h" -#include "../codegen/codegen.h" + +void instantiate_methods(ParserContext *ctx, GenericImplTemplate *it, + const char *mangled_struct_name, const char *arg); Token expect(Lexer *l, TokenType type, const char *msg) { @@ -376,6 +379,18 @@ void register_impl_template(ParserContext *ctx, const char *sname, const char *p t->impl_node = node; t->next = ctx->impl_templates; ctx->impl_templates = t; + + // Late binding: Check if any existing instantiations match this new impl + // template + Instantiation *inst = ctx->instantiations; + while (inst) + { + if (inst->template_name && strcmp(inst->template_name, sname) == 0) + { + instantiate_methods(ctx, t, inst->name, inst->concrete_arg); + } + inst = inst->next; + } } void add_to_struct_list(ParserContext *ctx, ASTNode *node) @@ -917,7 +932,8 @@ char *replace_type_str(const char *src, const char *param, const char *concrete, size_t plen = strlen(suffix); if (slen > plen && strcmp(src + slen - plen, suffix) == 0) { - // Ends with _T -> Replace suffix with _int (sanitize for pointers like JsonValue*) + // Ends with _T -> Replace suffix with _int (sanitize for pointers like + // JsonValue*) char *clean_concrete = sanitize_mangled_name(concrete); char *ret = xmalloc(slen - plen + strlen(clean_concrete) + 2); strncpy(ret, src, slen - plen); @@ -1145,7 +1161,8 @@ Type *replace_type_formal(Type *t, const char *p, const char *c, const char *os, return n; } -// Helper to replace generic params in mangled names (e.g. Option_V_None -> Option_int_None) +// Helper to replace generic params in mangled names (e.g. Option_V_None -> +// Option_int_None) char *replace_mangled_part(const char *src, const char *param, const char *concrete) { if (!src || !param || !concrete) @@ -1165,8 +1182,8 @@ char *replace_mangled_part(const char *src, const char *param, const char *concr // Check if param matches here if (strncmp(curr, param, plen) == 0) { - // Check boundaries: Must be delimited by quoted boundaries, OR underscores, OR string - // ends + // Check boundaries: Must be delimited by quoted boundaries, OR + // underscores, OR string ends int valid = 1; // Check Prev: Start of string OR Underscore @@ -1631,7 +1648,8 @@ ASTNode *copy_fields_replacing(ParserContext *ctx, ASTNode *fields, const char * if (n->field.type && strchr(n->field.type, '_')) { - // Parse potential generic: e.g. "MapEntry_int" -> instantiate("MapEntry", "int") + // Parse potential generic: e.g. "MapEntry_int" -> instantiate("MapEntry", + // "int") char *underscore = strrchr(n->field.type, '_'); if (underscore && underscore > n->field.type) { @@ -1676,6 +1694,67 @@ ASTNode *copy_fields_replacing(ParserContext *ctx, ASTNode *fields, const char * return n; } +void instantiate_methods(ParserContext *ctx, GenericImplTemplate *it, + const char *mangled_struct_name, const char *arg) +{ + if (check_impl(ctx, "Methods", mangled_struct_name)) + { + return; // Simple dedupe check + } + + ASTNode *backup_next = it->impl_node->next; + it->impl_node->next = NULL; // Break link to isolate node + ASTNode *new_impl = copy_ast_replacing(it->impl_node, it->generic_param, arg, it->struct_name, + mangled_struct_name); + it->impl_node->next = backup_next; // Restore + + new_impl->impl.struct_name = xstrdup(mangled_struct_name); + ASTNode *meth = new_impl->impl.methods; + while (meth) + { + char *suffix = strchr(meth->func.name, '_'); + if (suffix) + { + char *new_name = xmalloc(strlen(mangled_struct_name) + strlen(suffix) + 1); + sprintf(new_name, "%s%s", mangled_struct_name, suffix); + free(meth->func.name); + meth->func.name = new_name; + register_func(ctx, new_name, meth->func.arg_count, meth->func.defaults, + meth->func.arg_types, meth->func.ret_type_info, meth->func.is_varargs, 0, + meth->token); + } + + // Handle generic return types in methods (e.g., Option<T> -> Option_int) + if (meth->func.ret_type && strchr(meth->func.ret_type, '_')) + { + char *ret_copy = xstrdup(meth->func.ret_type); + char *underscore = strrchr(ret_copy, '_'); + if (underscore && underscore > ret_copy) + { + *underscore = '\0'; + char *template_name = ret_copy; + + // Check if this looks like a generic (e.g., "Option_V" or "Result_V") + GenericTemplate *gt = ctx->templates; + while (gt) + { + if (strcmp(gt->name, template_name) == 0) + { + // Found matching template, instantiate it + instantiate_generic(ctx, template_name, arg); + break; + } + gt = gt->next; + } + } + free(ret_copy); + } + + meth = meth->next; + } + add_instantiated_func(ctx, new_impl); +} + void instantiate_generic(ParserContext *ctx, const char *tpl, const char *arg) { // Ignore generic placeholders @@ -1719,6 +1798,8 @@ void instantiate_generic(ParserContext *ctx, const char *tpl, const char *arg) Instantiation *ni = xmalloc(sizeof(Instantiation)); ni->name = xstrdup(m); + ni->template_name = xstrdup(tpl); + ni->concrete_arg = xstrdup(arg); ni->struct_node = NULL; // Placeholder to break cycles ni->next = ctx->instantiations; ctx->instantiations = ni; @@ -1780,55 +1861,7 @@ void instantiate_generic(ParserContext *ctx, const char *tpl, const char *arg) { if (strcmp(it->struct_name, tpl) == 0) { - ASTNode *backup_next = it->impl_node->next; - it->impl_node->next = NULL; // Break link to isolate node - ASTNode *new_impl = copy_ast_replacing(it->impl_node, it->generic_param, arg, tpl, m); - it->impl_node->next = backup_next; // Restore - - new_impl->impl.struct_name = xstrdup(m); - ASTNode *meth = new_impl->impl.methods; - while (meth) - { - char *suffix = strchr(meth->func.name, '_'); - if (suffix) - { - char *new_name = xmalloc(strlen(m) + strlen(suffix) + 1); - sprintf(new_name, "%s%s", m, suffix); - free(meth->func.name); - meth->func.name = new_name; - register_func(ctx, new_name, meth->func.arg_count, meth->func.defaults, - meth->func.arg_types, meth->func.ret_type_info, - meth->func.is_varargs, 0, meth->token); - } - - if (meth->func.ret_type && strchr(meth->func.ret_type, '_')) - { - char *ret_copy = xstrdup(meth->func.ret_type); - char *underscore = strrchr(ret_copy, '_'); - if (underscore && underscore > ret_copy) - { - *underscore = '\0'; - char *template_name = ret_copy; - - // Check if this looks like a generic (e.g., "Option_V" or "Result_V") - GenericTemplate *gt = ctx->templates; - while (gt) - { - if (strcmp(gt->name, template_name) == 0) - { - // Found matching template, instantiate it - instantiate_generic(ctx, template_name, arg); - break; - } - gt = gt->next; - } - } - free(ret_copy); - } - - meth = meth->next; - } - add_instantiated_func(ctx, new_impl); + instantiate_methods(ctx, it, m, arg); } it = it->next; } diff --git a/src/plugins/plugin_manager.c b/src/plugins/plugin_manager.c index a135e06..12c85cd 100644 --- a/src/plugins/plugin_manager.c +++ b/src/plugins/plugin_manager.c @@ -1,9 +1,9 @@ #include "plugin_manager.h" +#include <dlfcn.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <dlfcn.h> // Linked list node for plugins. typedef struct PluginNode diff --git a/src/repl/repl.c b/src/repl/repl.c index e57c5b3..9027efd 100644 --- a/src/repl/repl.c +++ b/src/repl/repl.c @@ -1,11 +1,11 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> #include "repl.h" -#include "zprep.h" #include "ast.h" #include "parser/parser.h" +#include "zprep.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> ASTNode *parse_program(ParserContext *ctx, Lexer *l); @@ -819,8 +819,10 @@ void run_repl(const char *self_path) 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 | " + "%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); @@ -892,13 +894,15 @@ void run_repl(const char *self_path) 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", "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.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. " @@ -915,16 +919,17 @@ void run_repl(const char *self_path) {"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.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", "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."}, @@ -938,19 +943,20 @@ void run_repl(const char *self_path) "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."}, + {"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;"}, + {"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)."}, + {"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 " @@ -963,16 +969,16 @@ void run_repl(const char *self_path) "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."}, + {"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."}, + {"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 " @@ -991,11 +997,11 @@ void run_repl(const char *self_path) "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."}, + {"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."}, @@ -1005,15 +1011,16 @@ void run_repl(const char *self_path) "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 " + {"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."}, + {"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."}, + {"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}}; @@ -1033,7 +1040,8 @@ void run_repl(const char *self_path) // 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", + "man 3 %s 2>/dev/null | sed -n '/^SYNOPSIS/,/^[A-Z]/p' | " + "head -10", sym); FILE *mp = popen(man_cmd, "r"); if (mp) @@ -1197,10 +1205,10 @@ void run_repl(const char *self_path) { // 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]); + sprintf(wbuf, + "printf(\"\\033[90mwatch:%s = \\033[0m\"); print \"{%s}\"; " + "printf(\"\\n\"); ", + watches[i], watches[i]); strcat(full_code, wbuf); } } diff --git a/src/utils/utils.c b/src/utils/utils.c index 08ae799..4af811f 100644 --- a/src/utils/utils.c +++ b/src/utils/utils.c @@ -1,6 +1,6 @@ -#include "zprep.h" #include "parser.h" +#include "zprep.h" char *g_current_filename = "unknown"; ParserContext *g_parser_ctx = NULL; diff --git a/src/zen/zen_facts.c b/src/zen/zen_facts.c index 441a180..6120e71 100644 --- a/src/zen/zen_facts.c +++ b/src/zen/zen_facts.c @@ -1,8 +1,8 @@ +#include "zen_facts.h" #include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> -#include "zen_facts.h" // We keep it low by default. #define ZEN_PROBABILITY 10 @@ -16,257 +16,339 @@ typedef struct static const ZenFact facts[] = { {TRIGGER_GOTO, - "Edsger W. Dijkstra considered 'Go To Statement Harmful' in 1968, advocating for structured " + "Edsger W. Dijkstra considered 'Go To Statement Harmful' in 1968, " + "advocating for structured " "programming.", "https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf"}, {TRIGGER_GOTO, - "Goto can be useful for error cleanup patterns in C (and Zen C), mimicking 'defer' or " + "Goto can be useful for error cleanup patterns in C (and Zen C), " + "mimicking 'defer' or " "'finally' blocks.", NULL}, {TRIGGER_POINTER_ARITH, - "In C, `arr[i]` is just `*(arr + i)`. Fun fact: `i[arr]` is also valid syntax!", + "In C, `arr[i]` is just `*(arr + i)`. Fun fact: `i[arr]` is also valid " + "syntax!", "https://c-faq.com/aryptr/aryptr2.html"}, {TRIGGER_POINTER_ARITH, - "Pointer arithmetic scales by the size of the type. `ptr + 1` increases the address by " + "Pointer arithmetic scales by the size of the type. `ptr + 1` increases " + "the address by " "`sizeof(*ptr)` bytes.", NULL}, {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 temporary (but " + "XOR swap algorithm: `x ^= y; y ^= x; x ^= y;` swaps variables without a " + "temporary (but " "optimized code is usually faster).", 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 " + "Tail Call Optimization (TCO) allows some recursive calls to consume no " + "additional stack " "space.", "https://en.wikipedia.org/wiki/Tail_call"}, {TRIGGER_TERNARY, - "The ternary operator `?:` is the only operator in C that takes three operands.", NULL}, + "The ternary operator `?:` is the only operator in C that takes three " + "operands.", + NULL}, {TRIGGER_ASM, - "With great power comes great responsibility. Inline assembly is compiler-specific and " + "With great power comes great responsibility. Inline assembly is " + "compiler-specific and " "fragile.", NULL}, {TRIGGER_WHILE_TRUE, - "The Halting Problem proves it is impossible to determine if an arbitrary program will " + "The Halting Problem proves it is impossible to determine if an arbitrary " + "program will " "eventually stop.", "https://en.wikipedia.org/wiki/Halting_problem"}, {TRIGGER_MACRO, - "Macros are handled by the preprocessor, doing simple text replacement before compilation " + "Macros are handled by the preprocessor, doing simple text replacement " + "before compilation " "begins.", NULL}, {TRIGGER_VOID_PTR, - "A `void*` is a generic pointer type. You cannot dereference it directly without casting.", + "A `void*` is a generic pointer type. You cannot dereference it directly " + "without casting.", NULL}, {TRIGGER_POINTER_ARITH, - "Subtracting two pointers returns a `ptrdiff_t`, a signed integer type capable of holding the " + "Subtracting two pointers returns a `ptrdiff_t`, a signed integer type " + "capable of holding the " "difference.", NULL}, {TRIGGER_MAIN, - "In C, `main` implicitly returns 0 if no return statement is found (C99+). Zen C follows " + "In C, `main` implicitly returns 0 if no return statement is found " + "(C99+). Zen C follows " "suit.", NULL}, {TRIGGER_FORMAT_STRING, - "printf format strings are a mini-language interpreted at runtime. Be careful with user " + "printf format strings are a mini-language interpreted at runtime. Be " + "careful with user " "input!", NULL}, {TRIGGER_BITWISE, - "The `!!` idiom (double negation) is a standard way to normalize any non-zero value to " + "The `!!` idiom (double negation) is a standard way to normalize any " + "non-zero value to " "exactly 1.", NULL}, {TRIGGER_GLOBAL, - "Duff's Device interleaves a switch statement with a do-while loop to unroll copy loops.", + "Duff's Device interleaves a switch statement with a do-while loop to " + "unroll copy loops.", "https://en.wikipedia.org/wiki/Duff%27s_device"}, {TRIGGER_GLOBAL, - "In C, `sizeof` is an operator, not a function. Parentheses are only required for type names.", + "In C, `sizeof` is an operator, not a function. Parentheses are only " + "required for type names.", NULL}, {TRIGGER_GLOBAL, - "Digraphs and Trigraphs were added for keyboards missing symbols. `\\?\\?=` is `#`, and `<:` " + "Digraphs and Trigraphs were added for keyboards missing symbols. " + "`\\?\\?=` is `#`, and `<:` " "is `[`.", "https://en.wikipedia.org/wiki/Digraphs_and_trigraphs"}, {TRIGGER_GLOBAL, - "Function designators convert to pointers automatically. `foo`, `&foo`, and `*foo` all " + "Function designators convert to pointers automatically. `foo`, `&foo`, " + "and `*foo` all " "resolve to the same address.", NULL}, {TRIGGER_GLOBAL, - "The comma operator evaluates operands left-to-right and returns the last. `x = (1, 2, 3)` " + "The comma operator evaluates operands left-to-right and returns the " + "last. `x = (1, 2, 3)` " "sets x to 3.", NULL}, {TRIGGER_GLOBAL, - "Multi-character constants like 'ABCD' are valid C. Their integer value is " + "Multi-character constants like 'ABCD' are valid C. Their integer value " + "is " "implementation-defined.", NULL}, {TRIGGER_GLOBAL, - "Bit-fields allow packing struct members into specific bit widths. `int x : 3;` uses only 3 " + "Bit-fields allow packing struct members into specific bit widths. `int x " + ": 3;` uses only 3 " "bits.", NULL}, {TRIGGER_GLOBAL, - "Array indexing is commutative: `5[arr]` is semantically identical to `arr[5]`.", NULL}, + "Array indexing is commutative: `5[arr]` is semantically identical to " + "`arr[5]`.", + NULL}, {TRIGGER_GLOBAL, - "The `restrict` keyword promises the compiler that a pointer is the only access to that " + "The `restrict` keyword promises the compiler that a pointer is the only " + "access to that " "memory.", NULL}, {TRIGGER_GLOBAL, - "A struct's size can be larger than the sum of its members due to alignment padding.", NULL}, + "A struct's size can be larger than the sum of its members due to " + "alignment padding.", + NULL}, {TRIGGER_GLOBAL, - "In C, `void*` arithmetic is a GCC extension (treating size as 1). Standard C forbids it.", + "In C, `void*` arithmetic is a GCC extension (treating size as 1). " + "Standard C forbids it.", NULL}, {TRIGGER_GLOBAL, - "The C standard guarantees that `NULL` equals `(void*)0`, but the bit pattern may not be all " + "The C standard guarantees that `NULL` equals `(void*)0`, but the bit " + "pattern may not be all " "zeros.", NULL}, {TRIGGER_GLOBAL, - "`sizeof('a')` is 4 in C (int) but 1 in C++ (char). A subtle difference between the " + "`sizeof('a')` is 4 in C (int) but 1 in C++ (char). A subtle difference " + "between the " "languages.", NULL}, {TRIGGER_GLOBAL, - "Static local variables are initialized only once, even if the function is called multiple " + "Static local variables are initialized only once, even if the function " + "is called multiple " "times.", NULL}, {TRIGGER_GLOBAL, - "The `volatile` keyword prevents compiler optimizations on a variable, useful for hardware " + "The `volatile` keyword prevents compiler optimizations on a variable, " + "useful for hardware " "registers.", NULL}, {TRIGGER_GLOBAL, - "In C99+, Variable Length Arrays (VLAs) can have runtime-determined sizes. Use with caution!", + "In C99+, Variable Length Arrays (VLAs) can have runtime-determined " + "sizes. Use with caution!", NULL}, - {TRIGGER_GLOBAL, "The `_Alignof` operator (C11) returns the alignment requirement of a type.", + {TRIGGER_GLOBAL, + "The `_Alignof` operator (C11) returns the alignment requirement of a " + "type.", NULL}, {TRIGGER_GLOBAL, - "Compound literals like `(int[]){1, 2, 3}` create anonymous arrays with automatic storage.", + "Compound literals like `(int[]){1, 2, 3}` create anonymous arrays with " + "automatic storage.", NULL}, {TRIGGER_GLOBAL, - "Designated initializers: `.field = val` lets you initialize struct fields out of order.", + "Designated initializers: `.field = val` lets you initialize struct " + "fields out of order.", NULL}, {TRIGGER_GLOBAL, - "The `#` stringification operator in macros turns arguments into string literals.", NULL}, + "The `#` stringification operator in macros turns arguments into string " + "literals.", + NULL}, {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 structs.", NULL}, + "Flexible array members: `int data[];` at struct end allows variable-size " + "structs.", + NULL}, {TRIGGER_GLOBAL, - "Anonymous structs/unions (C11) allow direct member access without field names.", NULL}, + "Anonymous structs/unions (C11) allow direct member access without field " + "names.", + NULL}, + {TRIGGER_GLOBAL, + "`_Generic` (C11) provides compile-time type dispatch, like a simpler " + "form of overloading.", + NULL}, {TRIGGER_GLOBAL, - "`_Generic` (C11) provides compile-time type dispatch, like a simpler form of overloading.", + "The `register` keyword is a hint to the compiler, but modern compilers " + "ignore it.", NULL}, {TRIGGER_GLOBAL, - "The `register` keyword is a hint to the compiler, but modern compilers ignore it.", NULL}, - {TRIGGER_GLOBAL, "Integer promotion: `char` and `short` are promoted to `int` in expressions.", + "Integer promotion: `char` and `short` are promoted to `int` in " + "expressions.", NULL}, {TRIGGER_GLOBAL, - "Signed integer overflow is undefined behavior in C. Use unsigned for wrap-around arithmetic.", + "Signed integer overflow is undefined behavior in C. Use unsigned for " + "wrap-around arithmetic.", NULL}, {TRIGGER_GLOBAL, - "The order of evaluation for function arguments is unspecified in C. Never rely on it!", NULL}, - {TRIGGER_GLOBAL, "In C, you can take the address of a label with `&&label` (GCC extension).", + "The order of evaluation for function arguments is unspecified in C. " + "Never rely on it!", NULL}, {TRIGGER_GLOBAL, - "The `inline` keyword is only a suggestion. Compilers decide whether to actually inline.", + "In C, you can take the address of a label with `&&label` (GCC " + "extension).", NULL}, {TRIGGER_GLOBAL, - "`setjmp`/`longjmp` provide non-local jumps, but are dangerous and rarely needed.", NULL}, + "The `inline` keyword is only a suggestion. Compilers decide whether to " + "actually inline.", + NULL}, {TRIGGER_GLOBAL, - "The `#pragma once` directive is non-standard but widely supported for include guards.", NULL}, + "`setjmp`/`longjmp` provide non-local jumps, but are dangerous and rarely " + "needed.", + NULL}, + {TRIGGER_GLOBAL, + "The `#pragma once` directive is non-standard but widely supported for " + "include guards.", + NULL}, {TRIGGER_GLOBAL, - "A `char` can be signed or unsigned depending on the platform. Use `signed char` or `unsigned " + "A `char` can be signed or unsigned depending on the platform. Use " + "`signed char` or `unsigned " "char` to be explicit.", NULL}, {TRIGGER_GLOBAL, - "The `const` keyword doesn't make data immutable - you can cast it away (but shouldn't).", + "The `const` keyword doesn't make data immutable - you can cast it away " + "(but shouldn't).", NULL}, {TRIGGER_GLOBAL, - "C has no native boolean type before C99. `_Bool` was added in C99, `bool` via stdbool.h.", + "C has no native boolean type before C99. `_Bool` was added in C99, " + "`bool` via stdbool.h.", NULL}, {TRIGGER_GLOBAL, - "Empty parameter lists in C mean 'unspecified arguments', not 'no arguments'. Use `(void)` " + "Empty parameter lists in C mean 'unspecified arguments', not 'no " + "arguments'. Use `(void)` " "for none.", NULL}, {TRIGGER_GLOBAL, - "The `extern` keyword declares a variable without defining it, linking to another translation " + "The `extern` keyword declares a variable without defining it, linking to " + "another translation " "unit.", NULL}, {TRIGGER_GLOBAL, - "K&R style function definitions predate ANSI C and put parameter types after the parentheses.", + "K&R style function definitions predate ANSI C and put parameter types " + "after the parentheses.", NULL}, {TRIGGER_GLOBAL, - "The IOCCC (Obfuscated C Code Contest) showcases creative abuse of C syntax since 1984.", + "The IOCCC (Obfuscated C Code Contest) showcases creative abuse of C " + "syntax since 1984.", "https://www.ioccc.org"}, {TRIGGER_GLOBAL, - "Dennis Ritchie developed C at Bell Labs between 1969-1973. It replaced B, which was " + "Dennis Ritchie developed C at Bell Labs between 1969-1973. It replaced " + "B, which was " "typeless.", 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 " + "Plan 9 C allows `structure.member` notation even if `member` is inside " + "an anonymous inner " "struct. C11 finally adopted this!", "https://9p.io/sys/doc/comp.html"}, {TRIGGER_GLOBAL, - "In Plan 9 C, arrays of zero length `int data[0]` were valid long before C99 flexible array " + "In Plan 9 C, arrays of zero length `int data[0]` were valid long before " + "C99 flexible array " "members.", NULL}, {TRIGGER_GLOBAL, - "Plan 9 C headers are 'idempotent' — they contain their own include guards, so you never see " + "Plan 9 C headers are 'idempotent' — they contain their own include " + "guards, so you never see " "`#ifndef HEADER_H` boilerplate.", NULL}, {TRIGGER_GLOBAL, - "In Plan 9, the `nil` pointer is distinct from 0. Accessing `nil` causes a hardware trap, not " + "In Plan 9, the `nil` pointer is distinct from 0. Accessing `nil` causes " + "a hardware trap, not " "just undefined behavior.", NULL}, {TRIGGER_GLOBAL, - "Ken Thompson, creator of B and C, also designed UTF-8 encoding on a placemat in a New Jersey " + "Ken Thompson, creator of B and C, also designed UTF-8 encoding on a " + "placemat in a New Jersey " "diner.", "https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt"}, {TRIGGER_GLOBAL, - "C11 introduced `_Noreturn` to tell the compiler a function (like `exit`) will never return " + "C11 introduced `_Noreturn` to tell the compiler a function (like `exit`) " + "will never return " "control to the caller.", NULL}, {TRIGGER_GLOBAL, - "A 'Sequence Point' is a juncture where all side effects of previous evaluations must be " + "A 'Sequence Point' is a juncture where all side effects of previous " + "evaluations must be " "complete. Violation = UB.", "https://en.wikipedia.org/wiki/Sequence_point"}, {TRIGGER_GLOBAL, - "VLAs were mandatory in C99, but made optional in C11 because they are hard to implement " + "VLAs were mandatory in C99, but made optional in C11 because they are " + "hard to implement " "safely.", NULL}, {TRIGGER_GLOBAL, - "Pre-ANSI C (K&R) didn't have `void`. Functions returning nothing actually returned an " + "Pre-ANSI C (K&R) didn't have `void`. Functions returning nothing " + "actually returned an " "undefined `int`.", NULL}, {TRIGGER_GLOBAL, - "Adjacent string literals are concatenated automatically. `\"Hello \" \"World\"` becomes " + "Adjacent string literals are concatenated automatically. `\"Hello \" " + "\"World\"` becomes " "`\"Hello World\"`.", NULL}, {TRIGGER_GLOBAL, - "The `auto` keyword exists in C! It declares automatic storage duration, but is almost never " + "The `auto` keyword exists in C! It declares automatic storage duration, " + "but is almost never " "used since it's the default.", NULL}, {TRIGGER_GLOBAL, - "Bitwise operators have lower precedence than comparisons! `val & MASK == 0` is `val & (MASK " + "Bitwise operators have lower precedence than comparisons! `val & MASK == " + "0` is `val & (MASK " "== 0)`. Always parenthesize!", NULL}, {TRIGGER_GLOBAL, - "The 'as-if' rule allows compilers to transform code however they want, as long as observable " + "The 'as-if' rule allows compilers to transform code however they want, " + "as long as observable " "behavior remains the same.", "https://en.cppreference.com/w/c/language/as_if"}, {TRIGGER_GLOBAL, - "Tail Recursion Elimination (TRE) isn't guaranteed by the C standard, but most compilers do " + "Tail Recursion Elimination (TRE) isn't guaranteed by the C standard, but " + "most compilers do " "it at -O2.", NULL}, {TRIGGER_GLOBAL, - "GCC's `__builtin_expect` allows you to tell the branch predictor which path is more likely " + "GCC's `__builtin_expect` allows you to tell the branch predictor which " + "path is more likely " "(the `likely()` macro).", NULL}, {TRIGGER_GLOBAL, "The actual inspiration for this project was this video.", diff --git a/src/zprep.h b/src/zprep.h index 8a85c9f..299ec13 100644 --- a/src/zprep.h +++ b/src/zprep.h @@ -2,11 +2,11 @@ #ifndef ZPREP_H #define ZPREP_H +#include <ctype.h> +#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <ctype.h> -#include <stdarg.h> // ** ANSI COLORS ** #define COLOR_RESET "\033[0m" |
