From 329aa22c4fe9097967263b8d9cd1dee0a94e0464 Mon Sep 17 00:00:00 2001 From: Squirrel Date: Tue, 27 Jan 2026 10:59:39 -0800 Subject: dddd --- tests/std/test_regex.zc | 187 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 tests/std/test_regex.zc (limited to 'tests') diff --git a/tests/std/test_regex.zc b/tests/std/test_regex.zc new file mode 100644 index 0000000..f1b840a --- /dev/null +++ b/tests/std/test_regex.zc @@ -0,0 +1,187 @@ +import "std/regex.zc" + +fn test_basic_matching() { + "testing: basic matching"; + let re = Regex::compile("abc"); + + if (re.match("abc")) { "literal match works"; } + if (re.match("abcdef")) { "substring match works"; } + if (!re.match("xyz")) { "not matching correctly returns false"; } + + re.destroy(); + ""; +} + +fn test_anchors() { + "testing: anchors"; + let re = Regex::compile("^start"); + + if (re.match("start here")) { " ^ anchor works for start"; } + if (!re.match("no start")) { " ^ anchor rejects non-start"; } + + re.destroy(); + + let re2 = Regex::compile("end$"); + if (re2.match("the end")) { " $ anchor works for end"; } + if (!re2.match("end here")) { " $ anchor rejects non-end"; } + + re2.destroy(); + ""; +} + +fn test_wildcards() { + "testing: wild cards"; + let re = Regex::compile("a.c"); + + if (re.match("abc")) { " . matches single char"; } + if (re.match("axc")) { " . matches different char"; } + if (!re.match("ac")) { " . requires exactly one char"; } + + re.destroy(); + ""; +} + +fn test_quantifiers() { + "testing: quantifiers"; + let re1 = Regex::compile("a*b"); + if (re1.match("b")) { " * matches zero occurrences"; } + if (re1.match("ab")) { " * matches one occurrence"; } + if (re1.match("aaab")) { " * matches multiple occurrences"; } + re1.destroy(); + + let re2 = Regex::compile("a+b"); + if (!re2.match("b")) { " + requires at least one"; } + if (re2.match("ab")) { " + matches one occurrence"; } + if (re2.match("aaab")) { " + matches multiple occurrences"; } + re2.destroy(); + + let re3 = Regex::compile("colou?r"); + if (re3.match("color")) { " ? matches with char"; } + if (re3.match("colour")) { " ? matches without char"; } + re3.destroy(); + ""; +} + +fn test_character_classes() { + "testing: character class stuff" + let re = Regex::compile("[0-9]+"); + + if (re.match("123")) { " [0-9] matches digits"; } + if (re.match("abc123")) { " [0-9] finds digits in string"; } + if (!re.match("abc")) { " [0-9] rejects non-digits"; } + + re.destroy(); + ""; +} + +fn test_alternation() { + "test: alternation"; + let re = Regex::compile("cat|dog"); + + if (re.match("cat")) { " | matches first alternative"; } + if (re.match("dog")) { " | matches second alternative"; } + if (!re.match("bird")) { " | rejects non-matching"; } + + re.destroy(); + ""; +} + +fn test_word_boundaries() { + "testing: word matching"; + let re = Regex::compile("[a-zA-Z]+"); + + if (re.match("hello")) { " letter class matches words"; } + if (re.match("hello123")) { " letter class finds word part"; } + if (!re.match("123")) { " letter class rejects non-letters"; } + + re.destroy(); + ""; +} + +fn test_is_valid() { + "testing: patern validation" + + if (Regex::is_valid_pattern("^[a-z]+$")) { " valid pattern accepted"; } + if (Regex::is_valid_pattern("(hello|world)")) { " complex pattern accepted"; } + + ""; +} + +fn test_find() { + "testing: find functionality"; + let re = Regex::compile("[0-9]+"); + let m = re.find("abc123def456"); + + if (m.is_some) { " find locates match"; } + + re.destroy(); + ""; +} + +fn test_count() { + "testing: count"; + let re = Regex::compile("[0-9]+"); + let count = re.count("123 456 789"); + + if (count >= 1) { " count finds matches"; } + + re.destroy(); + ""; +} + +fn test_convenience_functions() { + "testing: just some other functions and stuff"; + + if (regex_match("^test", "testing")) { " regex_match works"; } + if (regex_count("a", "banana") >= 1) { " regex_count works"; } + + let m = regex_find("[0-9]+", "id: 42"); + if (m.is_some) { " regex_find works"; } + + ""; +} + +fn test_email_pattern() { + "test: email pattern stuff" + let email_re = Regex::compile("^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"); + + if (email_re.match("swag@swag.com")) { " valid email accepted"; } + if (email_re.match("swag.swag@swag.swag.swag")) { " complex email accepted"; } + if (!email_re.match("invalid.email")) { " invalid email rejected"; } + + email_re.destroy(); + ""; +} + +fn test_url_pattern() { + "testing: url pattern stuff" + let url_re = Regex::compile("https?://[a-zA-Z0-9.-]+"); + + if (url_re.match("http://example.com")) { " http url matched matched"; } + if (url_re.match("https://secure.example.com")) { " https url matched"; } + if (!url_re.match("ftp://something.com")) { " ftp url rejected"; } + + url_re.destroy(); + ""; +} + +fn main() { + "testing...."; + + test_basic_matching(); + test_anchors(); + test_wildcards(); + test_quantifiers(); + test_character_classes(); + test_alternation(); + test_word_boundaries(); + test_is_valid(); + test_find(); + test_count(); + test_convenience_functions(); + test_email_pattern(); + test_url_pattern(); + + "all tests worked..."; + ""; +} -- cgit v1.2.3 From 536b1d1400d6c39aea2177d54693272e21e42643 Mon Sep 17 00:00:00 2001 From: Squirrel Date: Tue, 27 Jan 2026 12:19:24 -0800 Subject: forgot () --- tests/std/test_regex.zc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/std/test_regex.zc b/tests/std/test_regex.zc index f1b840a..8459fc6 100644 --- a/tests/std/test_regex.zc +++ b/tests/std/test_regex.zc @@ -112,7 +112,7 @@ fn test_find() { let re = Regex::compile("[0-9]+"); let m = re.find("abc123def456"); - if (m.is_some) { " find locates match"; } + if (m.is_some() { " find locates match"; } re.destroy(); ""; @@ -136,7 +136,7 @@ fn test_convenience_functions() { if (regex_count("a", "banana") >= 1) { " regex_count works"; } let m = regex_find("[0-9]+", "id: 42"); - if (m.is_some) { " regex_find works"; } + if (m.is_some() { " regex_find works"; } ""; } -- cgit v1.2.3 From 76118d8ea2369e4d89495bebeec52b98aaad56e4 Mon Sep 17 00:00:00 2001 From: Squirrel Date: Tue, 27 Jan 2026 13:41:16 -0800 Subject: sorry ok there fixed i think --- tests/std/test_regex.zc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/std/test_regex.zc b/tests/std/test_regex.zc index 8459fc6..91c5d70 100644 --- a/tests/std/test_regex.zc +++ b/tests/std/test_regex.zc @@ -112,7 +112,7 @@ fn test_find() { let re = Regex::compile("[0-9]+"); let m = re.find("abc123def456"); - if (m.is_some() { " find locates match"; } + if (m.is_some()) { " find locates match"; } re.destroy(); ""; @@ -136,7 +136,7 @@ fn test_convenience_functions() { if (regex_count("a", "banana") >= 1) { " regex_count works"; } let m = regex_find("[0-9]+", "id: 42"); - if (m.is_some() { " regex_find works"; } + if (m.is_some()) { " regex_find works"; } ""; } -- cgit v1.2.3 From e521ee7d175393ef37579ebd61ccb7e8d56a397f Mon Sep 17 00:00:00 2001 From: Squirrel Date: Tue, 27 Jan 2026 18:56:53 -0800 Subject: add else statements and stuff so you can tell and all dat --- tests/std/test_regex.zc | 146 ++++++++++++++++++++++++------------------------ 1 file changed, 73 insertions(+), 73 deletions(-) (limited to 'tests') diff --git a/tests/std/test_regex.zc b/tests/std/test_regex.zc index 91c5d70..4fe176c 100644 --- a/tests/std/test_regex.zc +++ b/tests/std/test_regex.zc @@ -3,11 +3,11 @@ import "std/regex.zc" fn test_basic_matching() { "testing: basic matching"; let re = Regex::compile("abc"); - - if (re.match("abc")) { "literal match works"; } - if (re.match("abcdef")) { "substring match works"; } - if (!re.match("xyz")) { "not matching correctly returns false"; } - + + if (re.match("abc")) { "literal match works"; } else { "FAILED: literal match"; } + if (re.match("abcdef")) { "substring match works"; } else { "FAILED: substring match"; } + if (!re.match("xyz")) { "not matching correctly returns false"; } else { "FAILED: mismatching"; } + re.destroy(); ""; } @@ -15,16 +15,16 @@ fn test_basic_matching() { fn test_anchors() { "testing: anchors"; let re = Regex::compile("^start"); - - if (re.match("start here")) { " ^ anchor works for start"; } - if (!re.match("no start")) { " ^ anchor rejects non-start"; } - + + if (re.match("start here")) { " ^ anchor works for start"; } else { "FAILED: ^ anchor start"; } + if (!re.match("no start")) { " ^ anchor rejects non-start"; } else { "FAILED: ^ anchor reject"; } + re.destroy(); - + let re2 = Regex::compile("end$"); - if (re2.match("the end")) { " $ anchor works for end"; } - if (!re2.match("end here")) { " $ anchor rejects non-end"; } - + if (re2.match("the end")) { " $ anchor works for end"; } else { "FAILED: $ anchor end"; } + if (!re2.match("end here")) { " $ anchor rejects non-end"; } else { "FAILED: $ anchor reject"; } + re2.destroy(); ""; } @@ -32,11 +32,11 @@ fn test_anchors() { fn test_wildcards() { "testing: wild cards"; let re = Regex::compile("a.c"); - - if (re.match("abc")) { " . matches single char"; } - if (re.match("axc")) { " . matches different char"; } - if (!re.match("ac")) { " . requires exactly one char"; } - + + if (re.match("abc")) { " . matches single char"; } else { "FAILED: . match 1"; } + if (re.match("axc")) { " . matches different char"; } else { "FAILED: . match 2"; } + if (!re.match("ac")) { " . requires exactly one char"; } else { "FAILED: . match 3"; } + re.destroy(); ""; } @@ -44,20 +44,20 @@ fn test_wildcards() { fn test_quantifiers() { "testing: quantifiers"; let re1 = Regex::compile("a*b"); - if (re1.match("b")) { " * matches zero occurrences"; } - if (re1.match("ab")) { " * matches one occurrence"; } - if (re1.match("aaab")) { " * matches multiple occurrences"; } + if (re1.match("b")) { " * matches zero occurrences"; } else { "FAILED: * 0"; } + if (re1.match("ab")) { " * matches one occurrence"; } else { "FAILED: * 1"; } + if (re1.match("aaab")) { " * matches multiple occurrences"; } else { "FAILED: * many"; } re1.destroy(); - + let re2 = Regex::compile("a+b"); - if (!re2.match("b")) { " + requires at least one"; } - if (re2.match("ab")) { " + matches one occurrence"; } - if (re2.match("aaab")) { " + matches multiple occurrences"; } + if (!re2.match("b")) { " + requires at least one"; } else { "FAILED: + 0"; } + if (re2.match("ab")) { " + matches one occurrence"; } else { "FAILED: + 1"; } + if (re2.match("aaab")) { " + matches multiple occurrences"; } else { "FAILED: + many"; } re2.destroy(); - + let re3 = Regex::compile("colou?r"); - if (re3.match("color")) { " ? matches with char"; } - if (re3.match("colour")) { " ? matches without char"; } + if (re3.match("color")) { " ? matches with char"; } else { "FAILED: ? with"; } + if (re3.match("colour")) { " ? matches without char"; } else { "FAILED: ? without"; } re3.destroy(); ""; } @@ -65,11 +65,11 @@ fn test_quantifiers() { fn test_character_classes() { "testing: character class stuff" let re = Regex::compile("[0-9]+"); - - if (re.match("123")) { " [0-9] matches digits"; } - if (re.match("abc123")) { " [0-9] finds digits in string"; } - if (!re.match("abc")) { " [0-9] rejects non-digits"; } - + + if (re.match("123")) { " [0-9] matches digits"; } else { "FAILED: [0-9] match"; } + if (re.match("abc123")) { " [0-9] finds digits in string"; } else { "FAILED: [0-9] find"; } + if (!re.match("abc")) { " [0-9] rejects non-digits"; } else { "FAILED: [0-9] reject"; } + re.destroy(); ""; } @@ -77,11 +77,11 @@ fn test_character_classes() { fn test_alternation() { "test: alternation"; let re = Regex::compile("cat|dog"); - - if (re.match("cat")) { " | matches first alternative"; } - if (re.match("dog")) { " | matches second alternative"; } - if (!re.match("bird")) { " | rejects non-matching"; } - + + if (re.match("cat")) { " | matches first alternative"; } else { "FAILED: | match 1"; } + if (re.match("dog")) { " | matches second alternative"; } else { "FAILED: | match 2"; } + if (!re.match("bird")) { " | rejects non-matching"; } else { "FAILED: | reject"; } + re.destroy(); ""; } @@ -89,21 +89,21 @@ fn test_alternation() { fn test_word_boundaries() { "testing: word matching"; let re = Regex::compile("[a-zA-Z]+"); - - if (re.match("hello")) { " letter class matches words"; } - if (re.match("hello123")) { " letter class finds word part"; } - if (!re.match("123")) { " letter class rejects non-letters"; } - + + if (re.match("hello")) { " letter class matches words"; } else { "FAILED: letter match"; } + if (re.match("hello123")) { " letter class finds word part"; } else { "FAILED: letter part"; } + if (!re.match("123")) { " letter class rejects non-letters"; } else { "FAILED: letter reject"; } + re.destroy(); ""; } fn test_is_valid() { "testing: patern validation" - - if (Regex::is_valid_pattern("^[a-z]+$")) { " valid pattern accepted"; } - if (Regex::is_valid_pattern("(hello|world)")) { " complex pattern accepted"; } - + + if (Regex::is_valid_pattern("^[a-z]+$")) { " valid pattern accepted"; } else { "FAILED: pattern validation 1"; } + if (Regex::is_valid_pattern("(hello|world)")) { " complex pattern accepted"; } else { "FAILED: pattern validation 2"; } + ""; } @@ -111,9 +111,9 @@ fn test_find() { "testing: find functionality"; let re = Regex::compile("[0-9]+"); let m = re.find("abc123def456"); - - if (m.is_some()) { " find locates match"; } - + + if (m.is_some()) { " find locates match"; } else { "FAILED: find match"; } + re.destroy(); ""; } @@ -122,33 +122,33 @@ fn test_count() { "testing: count"; let re = Regex::compile("[0-9]+"); let count = re.count("123 456 789"); - - if (count >= 1) { " count finds matches"; } - + + if (count >= 1) { " count finds matches"; } else { "FAILED: count matches"; } + re.destroy(); ""; } fn test_convenience_functions() { "testing: just some other functions and stuff"; - - if (regex_match("^test", "testing")) { " regex_match works"; } - if (regex_count("a", "banana") >= 1) { " regex_count works"; } - + + if (regex_match("^test", "testing")) { " regex_match works"; } else { "FAILED: regex_match"; } + if (regex_count("a", "banana") >= 1) { " regex_count works"; } else { "FAILED: regex_count"; } + let m = regex_find("[0-9]+", "id: 42"); - if (m.is_some()) { " regex_find works"; } - + if (m.is_some()) { " regex_find works"; } else { "FAILED: regex_find"; } + ""; } fn test_email_pattern() { "test: email pattern stuff" - let email_re = Regex::compile("^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"); - - if (email_re.match("swag@swag.com")) { " valid email accepted"; } - if (email_re.match("swag.swag@swag.swag.swag")) { " complex email accepted"; } - if (!email_re.match("invalid.email")) { " invalid email rejected"; } - + let email_re = Regex::compile("^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z][a-zA-Z]+$"); + + if (email_re.match("swag@swag.com")) { " valid email accepted"; } else { "FAILED: valid email"; } + if (email_re.match("swag.swag@swag.swag.swag")) { " complex email accepted"; } else { "FAILED: complex email"; } + if (!email_re.match("invalid.email")) { " invalid email rejected"; } else { "FAILED: invalid email reject"; } + email_re.destroy(); ""; } @@ -156,18 +156,18 @@ fn test_email_pattern() { fn test_url_pattern() { "testing: url pattern stuff" let url_re = Regex::compile("https?://[a-zA-Z0-9.-]+"); - - if (url_re.match("http://example.com")) { " http url matched matched"; } - if (url_re.match("https://secure.example.com")) { " https url matched"; } - if (!url_re.match("ftp://something.com")) { " ftp url rejected"; } - + + if (url_re.match("http://example.com")) { " http url matched matched"; } else { "FAILED: http url"; } + if (url_re.match("https://secure.example.com")) { " https url matched"; } else { "FAILED: https url"; } + if (!url_re.match("ftp://something.com")) { " ftp url rejected"; } else { "FAILED: ftp url reject"; } + url_re.destroy(); ""; } fn main() { "testing...."; - + test_basic_matching(); test_anchors(); test_wildcards(); @@ -181,7 +181,7 @@ fn main() { test_convenience_functions(); test_email_pattern(); test_url_pattern(); - - "all tests worked..."; + + "all tests worked... (hopefully.. look around for \"FAILED\" messages)"; ""; } -- cgit v1.2.3 From ccc53b11a0e273f46cb40e5f0eb32a74ab6750bf Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Sat, 31 Jan 2026 15:31:41 +0000 Subject: Fix for #159 --- docs/std/slice.md | 15 +++-- src/codegen/codegen_main.c | 33 +++++++++++ src/parser/parser_stmt.c | 114 ++++++++++++++++++++++++++++++++++++ src/parser/parser_struct.c | 115 +++++++++++++++++++++++++++++++++++++ std/mem.zc | 23 +------- std/slice.zc | 5 ++ tests/memory/test_memory_safety.zc | 9 ++- 7 files changed, 283 insertions(+), 31 deletions(-) (limited to 'tests') diff --git a/docs/std/slice.md b/docs/std/slice.md index b70c5fe..f029995 100644 --- a/docs/std/slice.md +++ b/docs/std/slice.md @@ -10,12 +10,12 @@ import "std/slice.zc" fn main() { let arr: int[5] = [1, 2, 3, 4, 5]; - // Direct iteration (Recommended) + // Direct iteration (auto-imports std/slice.zc) for val in arr { println "{val}"; } - // Manual slice creation (for partial views or specific needs) + // Manual slice creation let slice = Slice::from_array((int*)(&arr), 5); for val in slice { println "{val}"; @@ -39,6 +39,7 @@ struct Slice { | Method | Signature | Description | | :--- | :--- | :--- | | **from_array** | `Slice::from_array(arr: T*, len: usize) -> Slice` | Creates a slice view over an array. | +| **new** | `Slice::new(data: T*, len: usize) -> Slice` | Alias for `from_array` (backwards compat). | ### Iteration @@ -62,10 +63,10 @@ struct Slice { ### Iterating over fixed-size arrays ```zc +// std/slice.zc is auto-imported when using for-in on arrays let numbers: int[3] = [10, 20, 30]; -let slice = Slice::from_array((int*)(&numbers), 3); -for n in slice { +for n in numbers { println "Number: {n}"; } ``` @@ -73,6 +74,8 @@ for n in slice { ### Safe indexed access ```zc +import "std/slice.zc" + let arr: int[3] = [1, 2, 3]; let slice = Slice::from_array((int*)(&arr), 3); @@ -84,7 +87,7 @@ if (!opt.is_none()) { ## Notes -- `Slice` does not own its data - it's just a view +- `Slice` does not own its data — it's just a view - No memory management needed (no `free()` method) -- Must specify the generic type explicitly: `Slice`, `Slice`, etc. +- **Auto-import**: `std/slice.zc` is automatically imported when using `for val in arr` on a fixed-size array - The array pointer cast `(T*)(&arr)` is required for fixed-size arrays diff --git a/src/codegen/codegen_main.c b/src/codegen/codegen_main.c index b298700..82fc3ce 100644 --- a/src/codegen/codegen_main.c +++ b/src/codegen/codegen_main.c @@ -448,6 +448,39 @@ void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out) emit_type_aliases(kids, out); // Emit local aliases (redundant but safe) emit_trait_defs(kids, out); + // Also emit traits from parsed_globals_list (from auto-imported files like std/mem.zc) + // but only if they weren't already emitted from kids + StructRef *trait_ref = ctx->parsed_globals_list; + while (trait_ref) + { + if (trait_ref->node && trait_ref->node->type == NODE_TRAIT) + { + // Check if this trait was already in kids (explicitly imported) + int already_in_kids = 0; + ASTNode *k = kids; + while (k) + { + if (k->type == NODE_TRAIT && k->trait.name && trait_ref->node->trait.name && + strcmp(k->trait.name, trait_ref->node->trait.name) == 0) + { + already_in_kids = 1; + break; + } + k = k->next; + } + + if (!already_in_kids) + { + // Create a temporary single-node list for emit_trait_defs + ASTNode *saved_next = trait_ref->node->next; + trait_ref->node->next = NULL; + emit_trait_defs(trait_ref->node, out); + trait_ref->node->next = saved_next; + } + } + trait_ref = trait_ref->next; + } + // Track emitted raw statements to prevent duplicates EmittedContent *emitted_raw = NULL; diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index 7758ae3..0677cf5 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -14,6 +14,118 @@ char *curr_func_ret = NULL; char *run_comptime_block(ParserContext *ctx, Lexer *l); +extern char *g_current_filename; + +/** + * @brief Auto-imports std/slice.zc if not already imported. + * + * This is called when array iteration is detected in for-in loops, + * to ensure the Slice, SliceIter, and Option templates are available. + */ +static void auto_import_std_slice(ParserContext *ctx) +{ + // Check if already imported via templates + GenericTemplate *t = ctx->templates; + while (t) + { + if (strcmp(t->name, "Slice") == 0) + { + return; // Already have the Slice template + } + t = t->next; + } + + // Try to find and import std/slice.zc + static const char *std_paths[] = {"std/slice.zc", "./std/slice.zc", NULL}; + static const char *system_paths[] = {"/usr/local/share/zenc", "/usr/share/zenc", NULL}; + + char resolved_path[1024]; + int found = 0; + + // First, try relative to current file + if (g_current_filename) + { + char *current_dir = xstrdup(g_current_filename); + char *last_slash = strrchr(current_dir, '/'); + if (last_slash) + { + *last_slash = 0; + snprintf(resolved_path, sizeof(resolved_path), "%s/std/slice.zc", current_dir); + if (access(resolved_path, R_OK) == 0) + { + found = 1; + } + } + free(current_dir); + } + + // Try relative paths + if (!found) + { + for (int i = 0; std_paths[i] && !found; i++) + { + if (access(std_paths[i], R_OK) == 0) + { + strncpy(resolved_path, std_paths[i], sizeof(resolved_path) - 1); + resolved_path[sizeof(resolved_path) - 1] = '\0'; + found = 1; + } + } + } + + // Try system paths + if (!found) + { + for (int i = 0; system_paths[i] && !found; i++) + { + snprintf(resolved_path, sizeof(resolved_path), "%s/std/slice.zc", system_paths[i]); + if (access(resolved_path, R_OK) == 0) + { + found = 1; + } + } + } + + if (!found) + { + return; // Could not find std/slice.zc, instantiate_generic will error + } + + // Canonicalize path + char *real_fn = realpath(resolved_path, NULL); + if (real_fn) + { + strncpy(resolved_path, real_fn, sizeof(resolved_path) - 1); + resolved_path[sizeof(resolved_path) - 1] = '\0'; + free(real_fn); + } + + // Check if already imported + if (is_file_imported(ctx, resolved_path)) + { + return; + } + mark_file_imported(ctx, resolved_path); + + // Load and parse the file + char *src = load_file(resolved_path); + if (!src) + { + return; // Could not load file + } + + Lexer i; + lexer_init(&i, src); + + // Save and restore filename context + char *saved_fn = g_current_filename; + g_current_filename = resolved_path; + + // Parse the slice module contents + parse_program_nodes(ctx, &i); + + g_current_filename = saved_fn; +} static void check_assignment_condition(ASTNode *cond) { @@ -1193,6 +1305,8 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l) // Manually trigger generic instantiation for Slice // This ensures that Slice_int, Slice_float, etc. structures are generated + // First, ensure std/slice.zc is imported (auto-import if needed) + auto_import_std_slice(ctx); Token dummy_tok = {0}; instantiate_generic(ctx, "Slice", elem_type_str, elem_type_str, dummy_tok); diff --git a/src/parser/parser_struct.c b/src/parser/parser_struct.c index 109eeee..e53b56c 100644 --- a/src/parser/parser_struct.c +++ b/src/parser/parser_struct.c @@ -12,6 +12,114 @@ #include "zprep_plugin.h" #include "../codegen/codegen.h" +extern char *g_current_filename; + +/** + * @brief Auto-imports std/mem.zc if not already imported. + * + * This is called when the Drop trait is used (impl Drop for X). + */ +static void auto_import_std_mem(ParserContext *ctx) +{ + // Check if Drop trait is already registered (means mem.zc was imported) + if (check_impl(ctx, "Drop", "__trait_marker__")) + { + // Check_impl returns 0 if not found, but we need a different check + // Let's check if we can find any indicator that mem.zc was loaded + } + + // Try to find and import std/mem.zc + static const char *std_paths[] = {"std/mem.zc", "./std/mem.zc", NULL}; + static const char *system_paths[] = {"/usr/local/share/zenc", "/usr/share/zenc", NULL}; + + char resolved_path[1024]; + int found = 0; + + // First, try relative to current file + if (g_current_filename) + { + char *current_dir = xstrdup(g_current_filename); + char *last_slash = strrchr(current_dir, '/'); + if (last_slash) + { + *last_slash = 0; + snprintf(resolved_path, sizeof(resolved_path), "%s/std/mem.zc", current_dir); + if (access(resolved_path, R_OK) == 0) + { + found = 1; + } + } + free(current_dir); + } + + // Try relative paths + if (!found) + { + for (int i = 0; std_paths[i] && !found; i++) + { + if (access(std_paths[i], R_OK) == 0) + { + strncpy(resolved_path, std_paths[i], sizeof(resolved_path) - 1); + resolved_path[sizeof(resolved_path) - 1] = '\0'; + found = 1; + } + } + } + + // Try system paths + if (!found) + { + for (int i = 0; system_paths[i] && !found; i++) + { + snprintf(resolved_path, sizeof(resolved_path), "%s/std/mem.zc", system_paths[i]); + if (access(resolved_path, R_OK) == 0) + { + found = 1; + } + } + } + + if (!found) + { + return; // Could not find std/mem.zc + } + + // Canonicalize path + char *real_fn = realpath(resolved_path, NULL); + if (real_fn) + { + strncpy(resolved_path, real_fn, sizeof(resolved_path) - 1); + resolved_path[sizeof(resolved_path) - 1] = '\0'; + free(real_fn); + } + + // Check if already imported + if (is_file_imported(ctx, resolved_path)) + { + return; + } + mark_file_imported(ctx, resolved_path); + + // Load and parse the file + char *src = load_file(resolved_path); + if (!src) + { + return; // Could not load file + } + + Lexer i; + lexer_init(&i, src); + + // Save and restore filename context + char *saved_fn = g_current_filename; + g_current_filename = resolved_path; + + // Parse the mem module contents + parse_program_nodes(ctx, &i); + + g_current_filename = saved_fn; +} + // Trait Parsing ASTNode *parse_trait(ParserContext *ctx, Lexer *l) { @@ -149,6 +257,7 @@ ASTNode *parse_trait(ParserContext *ctx, Lexer *l) } register_trait(name); + add_to_global_list(ctx, n_node); // Track for codegen (VTable emission) return n_node; } @@ -206,6 +315,12 @@ ASTNode *parse_impl(ParserContext *ctx, Lexer *l) register_generic(ctx, target_gen_param); } + // Auto-import std/mem.zc if implementing Drop, Copy, or Clone traits + if (strcmp(name1, "Drop") == 0 || strcmp(name1, "Copy") == 0 || strcmp(name1, "Clone") == 0) + { + auto_import_std_mem(ctx); + } + register_impl(ctx, name1, name2); // RAII: Check for "Drop" trait implementation diff --git a/std/mem.zc b/std/mem.zc index 6ee96e8..f1a5f5a 100644 --- a/std/mem.zc +++ b/std/mem.zc @@ -49,28 +49,7 @@ impl Box { } } -struct Slice { - data: T*; - len: usize; -} - -impl Slice { - fn new(data: T*, len: usize) -> Self { - return Self { data: data, len: len }; - } - - fn get(self, i: usize) -> T { - return self.data[i]; - } - - fn set(self, i: usize, val: T) { - self.data[i] = val; - } - - fn is_empty(self) -> bool { - return self.len == 0; - } -} +// Note: Slice is defined in std/slice.zc with iteration support fn mem_zero(ptr: T*, count: usize) { memset(ptr, 0, sizeof(T) * count); diff --git a/std/slice.zc b/std/slice.zc index 7ace396..3c317ca 100644 --- a/std/slice.zc +++ b/std/slice.zc @@ -32,6 +32,11 @@ impl Slice { return Slice { data: arr, len: len }; } + // Alias for backwards compatibility with std/mem.zc + fn new(data: T*, len: usize) -> Slice { + return Slice { data: data, len: len }; + } + fn iterator(self) -> SliceIter { return SliceIter { data: self.data, diff --git a/tests/memory/test_memory_safety.zc b/tests/memory/test_memory_safety.zc index a5cc960..b672cc9 100644 --- a/tests/memory/test_memory_safety.zc +++ b/tests/memory/test_memory_safety.zc @@ -1,5 +1,6 @@ import "std/mem.zc" +import "std/slice.zc" // ** Globals ** let DROP_COUNT = 0; @@ -127,11 +128,13 @@ test "test_slice" { let data: int[5] = [1, 2, 3, 4, 5]; let s = Slice::new(&data[0], 5); f" Slice len: {(int)s.len}"; - let v2 = s.get(2); + let opt_v2 = s.get(2); + let v2 = opt_v2.unwrap(); f" Slice[2]: {v2}"; assert(v2 == 3, "Slice get failed"); - s.set(0, 99); - let v0 = s.get(0); + s.data[0] = 99; + let opt_v0 = s.get(0); + let v0 = opt_v0.unwrap(); f" After set: Slice[0] = {v0}"; assert(v0 == 99, "Slice set failed"); " ✓ Slice works!"; -- cgit v1.2.3