From a61c2e7b2afa685e76d259d06bec5cdee3739ae0 Mon Sep 17 00:00:00 2001 From: Squirrel Date: Tue, 27 Jan 2026 10:58:50 -0800 Subject: this kinda sucks but uhh it works --- std/regex.zc | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 std/regex.zc diff --git a/std/regex.zc b/std/regex.zc new file mode 100644 index 0000000..346985e --- /dev/null +++ b/std/regex.zc @@ -0,0 +1,194 @@ +include +include +import "./core.zc" +import "./string.zc" +import "./vec.zc" +import "./option.zc" + +struct Match { + text: char*; + start: int; + len: int; +} + +impl Match { + fn new(text: char*, start: int, len: int) -> Match { + return Match { text: text, start: start, len: len }; + } + + fn as_string(self) -> char* { + return self.text; + } + + fn end(self) -> int { + return self.start + self.len; + } +} + +struct Regex { + preg: void*; + pattern: char*; + flags: int; +} + +impl Regex { + fn compile(pattern: char*) -> Regex { + return Regex::compile_with_flags(pattern, 1); + } + + fn compile_with_flags(pattern: char*, flags: int) -> Regex { + let preg = malloc(128); + let status = regcomp(preg, pattern, flags); + if (status != 0) { + free(preg); + return Regex { preg: 0, pattern: 0, flags: flags }; + } + return Regex { preg: preg, pattern: pattern, flags: flags }; + } + + fn is_valid(self) -> bool { + return self.preg != 0; + } + + fn match(self, text: char*) -> bool { + if (self.preg == 0) { return false; } + return regexec(self.preg, text, 0, 0, 0) == 0; + } + + fn match_at(self, text: char*, offset: int) -> bool { + if (self.preg == 0) { return false; } + let len = strlen(text); + if (offset < 0 || offset > len) { return false; } + return regexec(self.preg, text + offset, 0, 0, 0) == 0; + } + + fn is_match(self, text: char*) -> bool { + return self.match(text); + } + + fn find(self, text: char*) -> Option { + if (self.preg == 0) { return Option::None(); } + let t_len = strlen(text); + for (let i = 0; i <= t_len; i = i + 1) { + let sub = text + i; + if (regexec(self.preg, sub, 0, 0, 0) == 0) { + let j = 0; + while (text[i + j] != 0 && regexec(self.preg, sub, 0, 0, 0) == 0) { + j = j + 1; + sub = text + i + j; + } + return Option::Some(Match::new(text + i, i, j)); + } + } + return Option::None(); + } + + fn find_at(self, text: char*, start: int) -> Option { + let len = strlen(text); + if (start < 0 || start >= len) { + return Option::None(); + } + return self.find(text + start); + } + + fn count(self, text: char*) -> int { + if (self.preg == 0) { return 0; } + let count = 0; + let pos = 0; + let t_len = strlen(text); + while (pos < t_len) { + let sub = text + pos; + if (regexec(self.preg, sub, 0, 0, 0) == 0) { + count = count + 1; + pos = pos + 1; + } else { + break; + } + } + return count; + } + + fn split(self, text: char*) -> Vec { + let parts = Vec::new(); + if (self.preg == 0) { + parts.push(String::from(text)); + return parts; + } + let t_len = strlen(text); + let last_pos = 0; + let pos = 0; + while (pos < t_len) { + let sub = text + pos; + if (regexec(self.preg, sub, 0, 0, 0) == 0) { + if (pos > last_pos) { + let before = text + last_pos; + let part_len = pos - last_pos; + let v = Vec::new(); + for (let i = 0; i < part_len; i = i + 1) { + v.push(before[i]); + } + v.push(0); + parts.push(String { vec: v }); + } + last_pos = pos + 1; + pos = pos + 1; + } else { + pos = pos + 1; + } + } + if (last_pos < t_len) { + parts.push(String::from(text + last_pos)); + } + return parts; + } + + fn pattern(self) -> char* { + return self.pattern; + } + + fn flags(self) -> int { + return self.flags; + } + + fn is_valid_pattern(pattern: char*) -> bool { + let test_regex = Regex::compile(pattern); + let valid = test_regex.is_valid(); + test_regex.destroy(); + return valid; + } + + fn destroy(self) { + if (self.preg != 0) { + regfree(self.preg); + free(self.preg); + } + } +} + +fn regex_match(pattern: char*, text: char*) -> bool { + let re = Regex::compile(pattern); + let result = re.match(text); + re.destroy(); + return result; +} + +fn regex_find(pattern: char*, text: char*) -> Option { + let re = Regex::compile(pattern); + let result = re.find(text); + re.destroy(); + return result; +} + +fn regex_count(pattern: char*, text: char*) -> int { + let re = Regex::compile(pattern); + let count = re.count(text); + re.destroy(); + return count; +} + +fn regex_split(pattern: char*, text: char*) -> Vec { + let re = Regex::compile(pattern); + let parts = re.split(text); + re.destroy(); + return parts; +} -- cgit v1.2.3 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 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 7b6eefac0e54a295f3a8b3f040f603295bb90156 Mon Sep 17 00:00:00 2001 From: Squirrel Date: Tue, 27 Jan 2026 11:27:38 -0800 Subject: oh yea this --- std.zc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std.zc b/std.zc index 03bcd45..f0b2cd1 100644 --- a/std.zc +++ b/std.zc @@ -18,4 +18,4 @@ import "./std/stack.zc" import "./std/queue.zc" import "./std/env.zc" import "./std/slice.zc" - +import "./std/regex.zc" -- 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(-) 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(-) 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 2bfa3286eda81375422d9dda38b110512749cef9 Mon Sep 17 00:00:00 2001 From: Squirrel Date: Tue, 27 Jan 2026 18:54:07 -0800 Subject: fix MAYBE..................... --- std/regex.zc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/std/regex.zc b/std/regex.zc index 346985e..92f1a54 100644 --- a/std/regex.zc +++ b/std/regex.zc @@ -1,5 +1,6 @@ include include + import "./core.zc" import "./string.zc" import "./vec.zc" @@ -33,11 +34,11 @@ struct Regex { impl Regex { fn compile(pattern: char*) -> Regex { - return Regex::compile_with_flags(pattern, 1); + return Regex::compile_with_flags(pattern, 1 | 2); } fn compile_with_flags(pattern: char*, flags: int) -> Regex { - let preg = malloc(128); + let preg = malloc(1024); let status = regcomp(preg, pattern, flags); if (status != 0) { free(preg); @@ -55,6 +56,10 @@ impl Regex { return regexec(self.preg, text, 0, 0, 0) == 0; } + fn match_full(self, text: char*) -> bool { + return self.match(text); + } + fn match_at(self, text: char*, offset: int) -> bool { if (self.preg == 0) { return false; } let len = strlen(text); -- cgit v1.2.3 From 18da1f89cc28c33d9621214158176b9531175f36 Mon Sep 17 00:00:00 2001 From: Squirrel Date: Tue, 27 Jan 2026 18:55:09 -0800 Subject: oh yea core.zc has string ok no need include string --- std/regex.zc | 1 - 1 file changed, 1 deletion(-) diff --git a/std/regex.zc b/std/regex.zc index 92f1a54..f64b36e 100644 --- a/std/regex.zc +++ b/std/regex.zc @@ -1,5 +1,4 @@ include -include import "./core.zc" import "./string.zc" -- 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(-) 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 472434301940015365f7ed303f52d71c505ac487 Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Fri, 30 Jan 2026 19:44:32 +0000 Subject: Improvements for the standard library + '@ctype'. --- Makefile | 2 +- README.md | 1 + README_ES.md | 1 + README_ZH_CN.md | 1 + README_ZH_TW.md | 1 + src/ast/ast.h | 2 ++ src/codegen/codegen_utils.c | 8 +++++- src/parser/parser.h | 3 ++- src/parser/parser_decl.c | 6 +++-- src/parser/parser_struct.c | 2 +- src/parser/parser_utils.c | 53 ++++++++++++++++++++++++++++++++++++-- std/core.zc | 6 ++--- std/cuda.zc | 2 ++ std/env.zc | 29 +++++++-------------- std/fs.zc | 62 ++++++++++++++++++--------------------------- std/io.zc | 43 ++++++++++++++++--------------- std/map.zc | 22 +++++++++------- std/net.zc | 25 ++++++++++-------- std/process.zc | 21 ++++++++------- std/set.zc | 20 +++++++-------- std/thread.zc | 5 ++++ std/time.zc | 3 +++ 22 files changed, 192 insertions(+), 126 deletions(-) diff --git a/Makefile b/Makefile index d15f556..b2d8e29 100644 --- a/Makefile +++ b/Makefile @@ -188,7 +188,7 @@ clean: @echo "=> Clean complete!" # Test -test: $(TARGET) +test: $(TARGET) $(PLUGINS) ./tests/run_tests.sh ./tests/run_codegen_tests.sh ./tests/run_example_transpile.sh diff --git a/README.md b/README.md index f03f605..001e590 100644 --- a/README.md +++ b/README.md @@ -939,6 +939,7 @@ Decorate functions and structs to modify compiler behavior. | `@host` | Fn | CUDA: Host function (`__host__`). | | `@comptime` | Fn | Helper function available for compile-time execution. | | `@derive(...)` | Struct | Auto-implement traits. Supports `Debug`, `Eq` (Smart Derive), `Copy`, `Clone`. | +| `@ctype("type")` | Fn Param | Overrides generated C type for a parameter. | | `@` | Any | Passes generic attributes to C (e.g. `@flatten`, `@alias("name")`). | #### Custom Attributes diff --git a/README_ES.md b/README_ES.md index 9040ea8..999a7b4 100644 --- a/README_ES.md +++ b/README_ES.md @@ -938,6 +938,7 @@ Decora funciones y structs para modificar el comportamiento del compilador. | `@host` | Fn | CUDA: Función de host (`__host__`). | | `@comptime` | Fn | Función auxiliar disponible para ejecución en tiempo de compilación. | | `@derive(...)` | Struct | Implementa traits automáticamente. Soporta `Debug`, `Eq` (Derivación Inteligente), `Copy`, `Clone`. | +| `@ctype("tipo")` | Parámetro Fn | Sobrescribe el tipo C generado para un parámetro. | | `@` | Cualquier | Pasa atributos genéricos a C (ej. `@flatten`, `@alias("nombre")`). | #### Atributos Personalizados diff --git a/README_ZH_CN.md b/README_ZH_CN.md index 217e9ec..daa8a3d 100644 --- a/README_ZH_CN.md +++ b/README_ZH_CN.md @@ -938,6 +938,7 @@ let re = regex! { ^[a-z]+$ }; | `@host` | 函数 | CUDA: 主机函数 (`__host__`)。 | | `@comptime` | 函数 | 用于编译时执行的辅助函数。 | | `@derive(...)` | 结构体 | 自动实现 Trait。支持 `Debug`, `Eq` (智能派生), `Copy`, `Clone`。 | +| `@ctype("type")` | 函数参数 | 覆盖参数生成的 C 类型。 | | `@` | 任意 | 将泛型属性传递给 C (例如 `@flatten`, `@alias("name")`)。 | #### 自定义属性 diff --git a/README_ZH_TW.md b/README_ZH_TW.md index 8618540..2707caa 100644 --- a/README_ZH_TW.md +++ b/README_ZH_TW.md @@ -938,6 +938,7 @@ let re = regex! { ^[a-z]+$ }; | `@host` | 函數 | CUDA: 主機函數 (`__host__`)。 | | `@comptime` | 函數 | 用於編譯時執行的輔助函數。 | | `@derive(...)` | 結構體 | 自動實現 Trait。支持 `Debug`, `Eq` (智能派生), `Copy`, `Clone`。 | +| `@ctype("type")` | 函數參數 | 覆蓋參數生成的 C 類型。 | | `@` | 任意 | 將泛型屬性傳遞給 C (例如 `@flatten`, `@alias("name")`)。 | #### 自定義屬性 diff --git a/src/ast/ast.h b/src/ast/ast.h index 71d9943..4498d7c 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -229,6 +229,8 @@ struct ASTNode int cuda_device; // @device -> __device__ int cuda_host; // @host -> __host__ + char **c_type_overrides; // @ctype("...") per parameter + Attribute *attributes; // Custom attributes } func; diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c index 391ebd3..0d03661 100644 --- a/src/codegen/codegen_utils.c +++ b/src/codegen/codegen_utils.c @@ -714,7 +714,12 @@ void emit_func_signature(FILE *out, ASTNode *func, const char *name_override) } char *type_str = NULL; - if (func->func.arg_types && func->func.arg_types[i]) + // Check for @ctype override first + if (func->func.c_type_overrides && func->func.c_type_overrides[i]) + { + type_str = xstrdup(func->func.c_type_overrides[i]); + } + else if (func->func.arg_types && func->func.arg_types[i]) { type_str = codegen_type_to_string(func->func.arg_types[i]); } @@ -724,6 +729,7 @@ void emit_func_signature(FILE *out, ASTNode *func, const char *name_override) } const char *name = ""; + if (func->func.param_names && func->func.param_names[i]) { name = func->func.param_names[i]; diff --git a/src/parser/parser.h b/src/parser/parser.h index 262c359..23c2920 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -561,7 +561,8 @@ ASTNode *parse_arrow_lambda_multi(ParserContext *ctx, Lexer *l, char **param_nam * @brief Parses and converts arguments. */ char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out, int *count_out, - Type ***types_out, char ***names_out, int *is_varargs_out); + Type ***types_out, char ***names_out, int *is_varargs_out, + char ***ctype_overrides_out); /** * @brief Checks if a file has been imported. diff --git a/src/parser/parser_decl.c b/src/parser/parser_decl.c index c96ca36..93a124d 100644 --- a/src/parser/parser_decl.c +++ b/src/parser/parser_decl.c @@ -99,10 +99,11 @@ ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async) int count; Type **arg_types; char **param_names; + char **ctype_overrides; int is_varargs = 0; - char *args = - parse_and_convert_args(ctx, l, &defaults, &count, &arg_types, ¶m_names, &is_varargs); + char *args = parse_and_convert_args(ctx, l, &defaults, &count, &arg_types, ¶m_names, + &is_varargs, &ctype_overrides); char *ret = "void"; Type *ret_type_obj = type_new(TYPE_VOID); @@ -191,6 +192,7 @@ ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async) node->func.defaults = defaults; node->func.ret_type_info = ret_type_obj; node->func.is_varargs = is_varargs; + node->func.c_type_overrides = ctype_overrides; if (gen_param) { diff --git a/src/parser/parser_struct.c b/src/parser/parser_struct.c index 82dd346..109eeee 100644 --- a/src/parser/parser_struct.c +++ b/src/parser/parser_struct.c @@ -100,7 +100,7 @@ ASTNode *parse_trait(ParserContext *ctx, Lexer *l) char **param_names = NULL; int is_varargs = 0; char *args = parse_and_convert_args(ctx, l, &defaults, &arg_count, &arg_types, ¶m_names, - &is_varargs); + &is_varargs, NULL); char *ret = xstrdup("void"); if (lexer_peek(l).type == TOK_ARROW) diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c index 48418b6..28d2c11 100644 --- a/src/parser/parser_utils.c +++ b/src/parser/parser_utils.c @@ -3392,7 +3392,8 @@ char *consume_and_rewrite(ParserContext *ctx, Lexer *l) } char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out, int *count_out, - Type ***types_out, char ***names_out, int *is_varargs_out) + Type ***types_out, char ***names_out, int *is_varargs_out, + char ***ctype_overrides_out) { Token t = lexer_next(l); if (t.type != TOK_LPAREN) @@ -3406,18 +3407,52 @@ char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out, char **defaults = xmalloc(sizeof(char *) * 16); Type **types = xmalloc(sizeof(Type *) * 16); char **names = xmalloc(sizeof(char *) * 16); + char **ctype_overrides = xmalloc(sizeof(char *) * 16); for (int i = 0; i < 16; i++) { defaults[i] = NULL; types[i] = NULL; names[i] = NULL; + ctype_overrides[i] = NULL; } if (lexer_peek(l).type != TOK_RPAREN) { while (1) { + // Check for @ctype("...") before parameter + char *ctype_override = NULL; + if (lexer_peek(l).type == TOK_AT) + { + lexer_next(l); // eat @ + Token attr = lexer_next(l); + if (attr.type == TOK_IDENT && attr.len == 5 && strncmp(attr.start, "ctype", 5) == 0) + { + if (lexer_next(l).type != TOK_LPAREN) + { + zpanic_at(lexer_peek(l), "Expected ( after @ctype"); + } + Token ctype_tok = lexer_next(l); + if (ctype_tok.type != TOK_STRING) + { + zpanic_at(ctype_tok, "@ctype requires a string argument"); + } + // Extract string content (strip quotes) + ctype_override = xmalloc(ctype_tok.len - 1); + strncpy(ctype_override, ctype_tok.start + 1, ctype_tok.len - 2); + ctype_override[ctype_tok.len - 2] = 0; + if (lexer_next(l).type != TOK_RPAREN) + { + zpanic_at(lexer_peek(l), "Expected ) after @ctype string"); + } + } + else + { + zpanic_at(attr, "Unknown parameter attribute @%.*s", attr.len, attr.start); + } + } + Token t = lexer_next(l); // Handle 'self' if (t.type == TOK_IDENT && strncmp(t.start, "self", 4) == 0 && t.len == 4) @@ -3470,6 +3505,7 @@ char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out, types[count] = type_new_ptr(type_new(TYPE_VOID)); add_symbol(ctx, "self", "void*", types[count]); } + ctype_overrides[count] = ctype_override; count++; } else @@ -3514,11 +3550,20 @@ char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out, } else { - strcat(buf, type_str); + // Use @ctype override if present + if (ctype_override) + { + strcat(buf, ctype_override); + } + else + { + strcat(buf, type_str); + } strcat(buf, " "); strcat(buf, name); } + ctype_overrides[count] = ctype_override; count++; if (lexer_peek(l).type == TOK_OP && is_token(lexer_peek(l), "=")) @@ -3593,6 +3638,10 @@ char *parse_and_convert_args(ParserContext *ctx, Lexer *l, char ***defaults_out, *count_out = count; *types_out = types; *names_out = names; + if (ctype_overrides_out) + { + *ctype_overrides_out = ctype_overrides; + } return buf; } diff --git a/std/core.zc b/std/core.zc index f450517..7379db4 100644 --- a/std/core.zc +++ b/std/core.zc @@ -7,11 +7,11 @@ include let __zen_hash_seed: usize = 14695981039346656037; -raw { -void _zen_panic(const char* file, int line, const char* func, const char* msg) { +extern fn exit(code: int); + +fn _zen_panic(file: const char*, line: int, func: const char*, msg: const char*) { fprintf(stderr, "%s:%d (%s): Panic: %s\n", file, line, func, msg); exit(1); } -} #define panic(msg) _zen_panic(__FILE__, __LINE__, __func__, msg) \ No newline at end of file diff --git a/std/cuda.zc b/std/cuda.zc index c6a9403..8fc6545 100644 --- a/std/cuda.zc +++ b/std/cuda.zc @@ -101,6 +101,8 @@ fn cuda_ok() -> bool { } +// Minimal raw block: required for cudaDeviceProp struct field access +// The cudaDeviceProp struct cannot be declared in Zen-C without type conflicts raw { void _z_cuda_get_props(int dev, char* name, size_t* total_mem, int* sm_count, int* major, int* minor, int* max_threads, int* warp_size) { struct cudaDeviceProp prop; diff --git a/std/env.zc b/std/env.zc index 959784f..c63fd3d 100644 --- a/std/env.zc +++ b/std/env.zc @@ -2,23 +2,12 @@ import "./core.zc" import "./option.zc" import "./string.zc" -raw { - char *_z_env_get(char *name) { - return getenv(name); - } - - int _z_env_set(char *name, char *value, int overwrite) { - return setenv(name, value, overwrite); - } - - int _z_env_unset(char *name) { - return unsetenv(name); - } -} +include -extern fn _z_env_get(name: char*) -> char*; -extern fn _z_env_set(name: char*, value: char*, overwrite: int) -> int; -extern fn _z_env_unset(name: char*) -> int; +// Direct externs with const char* to match C stdlib declarations +extern fn getenv(name: const char*) -> char*; +extern fn setenv(name: const char*, value: const char*, overwrite: int) -> int; +extern fn unsetenv(name: const char*) -> int; @derive(Eq) enum EnvRes { @@ -30,7 +19,7 @@ struct Env {} impl Env { fn get(name: string) -> Option { - let value: string = _z_env_get(name); + let value: string = getenv(name); if (value == NULL) { return Option::None(); } @@ -39,7 +28,7 @@ impl Env { } fn get_dup(name: string) -> Option { - let value: string = _z_env_get(name); + let value: string = getenv(name); if (value == NULL) { return Option::None(); } @@ -52,13 +41,13 @@ impl Env { } fn set(name: string, value: string) -> EnvRes { - let ret: int = _z_env_set(name, value, 1); + let ret: int = setenv(name, value, 1); return (ret == 0) ? EnvRes::OK() : EnvRes::ERR(); } fn unset(name: string) -> EnvRes { - let ret: int = _z_env_unset(name); + let ret: int = unsetenv(name); return (ret == 0) ? EnvRes::OK() : EnvRes::ERR(); } diff --git a/std/fs.zc b/std/fs.zc index 4547b30..a00993b 100644 --- a/std/fs.zc +++ b/std/fs.zc @@ -14,12 +14,20 @@ include include include -// TODO: restructure this tomorrow (lol). +// Direct externs for simple functions with const char* parameters +extern fn access(pathname: const char*, mode: int) -> int; +extern fn unlink(pathname: const char*) -> int; +extern fn rmdir(pathname: const char*) -> int; +extern fn malloc(size: usize) -> void*; +extern fn free(ptr: void*); + +// Minimal raw block: required for opaque FILE*/DIR* types and C struct access +// These cannot be expressed in Zen-C extern declarations without type conflicts raw { typedef struct DirEntry* DirEntryPtr; - // Wrappers for FILE* handling due to opaque pointer casting - void* _z_fs_fopen(char* path, char* mode) { + // FILE* wrappers - fopen/fclose/etc use FILE* which conflicts with void* + void* _z_fs_fopen(const char* path, const char* mode) { return fopen(path, mode); } @@ -43,22 +51,8 @@ raw { return (int64_t)ftell((FILE*)stream); } - // Wrappers needed because C headers declare these with 'const char*' - // but Zen C externs generate 'char*', leading to conflicting types. - int _z_fs_access(char* pathname, int mode) { - return access(pathname, mode); - } - - int _z_fs_unlink(char* pathname) { - return unlink(pathname); - } - - int _z_fs_rmdir(char* pathname) { - return rmdir(pathname); - } - - // Wrappers for DIR* handling - void* _z_fs_opendir(char* name) { + // DIR* wrappers - opendir/closedir/readdir use DIR* which conflicts with void* + void* _z_fs_opendir(const char* name) { return opendir(name); } @@ -66,8 +60,8 @@ raw { return closedir((DIR*)dir); } - // struct stat / struct dirent helpers - int _z_fs_get_metadata(char* path, uint64_t* size, int* is_dir, int* is_file) { + // struct stat access - cannot define matching Zen-C struct for stat + int _z_fs_get_metadata(const char* path, uint64_t* size, int* is_dir, int* is_file) { struct stat st; if (stat(path, &st) != 0) return -1; *size = st.st_size; @@ -76,6 +70,7 @@ raw { return 0; } + // struct dirent access - readdir returns struct dirent* int _z_fs_read_entry(void* dir, char* out_name, int buf_size, int* is_dir) { struct dirent* ent = readdir((DIR*)dir); if (!ent) return 0; @@ -85,7 +80,8 @@ raw { return 1; } - int _z_fs_mkdir(char* path) { + // mkdir has different signatures on Windows vs POSIX + int _z_fs_mkdir(const char* path) { #ifdef _WIN32 return mkdir(path); #else @@ -94,26 +90,18 @@ raw { } } -// Direct externs -extern fn malloc(size: usize) -> void*; -extern fn free(ptr: void*); - -extern fn _z_fs_mkdir(path: char*) -> int; -extern fn _z_fs_get_metadata(path: char*, size: U64*, is_dir: int*, is_file: int*) -> int; +extern fn _z_fs_mkdir(path: const char*) -> int; +extern fn _z_fs_get_metadata(path: const char*, size: U64*, is_dir: int*, is_file: int*) -> int; extern fn _z_fs_read_entry(dir: void*, out_name: char*, buf_size: int, is_dir: int*) -> int; -extern fn _z_fs_fopen(path: char*, mode: char*) -> void*; +extern fn _z_fs_fopen(path: const char*, mode: const char*) -> void*; extern fn _z_fs_fclose(stream: void*) -> int; extern fn _z_fs_fread(ptr: void*, size: usize, nmemb: usize, stream: void*) -> usize; extern fn _z_fs_fwrite(ptr: void*, size: usize, nmemb: usize, stream: void*) -> usize; extern fn _z_fs_fseek(stream: void*, offset: I64, whence: int) -> int; extern fn _z_fs_ftell(stream: void*) -> I64; -extern fn _z_fs_opendir(name: char*) -> void*; +extern fn _z_fs_opendir(name: const char*) -> void*; extern fn _z_fs_closedir(dir: void*) -> int; -extern fn _z_fs_access(pathname: char*, mode: int) -> int; -extern fn _z_fs_unlink(pathname: char*) -> int; -extern fn _z_fs_rmdir(pathname: char*) -> int; - struct File { handle: void*; @@ -203,7 +191,7 @@ impl File { } fn exists(path: char*) -> bool { - return _z_fs_access(path, Z_F_OK) == 0; + return access(path, Z_F_OK) == 0; } fn metadata(path: char*) -> Result { @@ -230,14 +218,14 @@ impl File { } fn remove_file(path: char*) -> Result { - if (_z_fs_unlink(path) != 0) { + if (unlink(path) != 0) { return Result::Err("Failed to remove file"); } return Result::Ok(true); } fn remove_dir(path: char*) -> Result { - if (_z_fs_rmdir(path) != 0) { + if (rmdir(path) != 0) { return Result::Err("Failed to remove directory"); } return Result::Ok(true); diff --git a/std/io.zc b/std/io.zc index 2793ecf..cfb9179 100644 --- a/std/io.zc +++ b/std/io.zc @@ -2,28 +2,32 @@ import "./core.zc" import "./string.zc" -raw { - int _z_vprintf(char* fmt, va_list ap) { - return vprintf((const char*)fmt, ap); - } - - int _z_vsnprintf(char* str, size_t size, char* fmt, va_list ap) { - return vsnprintf(str, size, (const char*)fmt, ap); - } +include +include - void* _z_get_stdin() { return stdin; } - int _z_get_eof() { return EOF; } +// These work directly with const char* in extern declarations +extern fn vprintf(fmt: const char*, ap: va_list) -> int; +extern fn vsnprintf(str: char*, size: usize, fmt: const char*, ap: va_list) -> int; + +// EOF is typically -1, but we define it for portability +def Z_EOF = -1; + +// Minimal raw block: only for truly opaque FILE* types that can't be +// represented in Zen-C extern declarations without type conflicts. +// These wrappers use void* to avoid FILE* declaration conflicts. +raw { + void* _z_get_stdin(void) { return stdin; } int _z_fgetc(void* stream) { return fgetc((FILE*)stream); } } -extern fn _z_vprintf(fmt: char*, ap: va_list) -> int; -extern fn _z_vsnprintf(str: char*, size: usize, fmt: char*, ap: va_list) -> int; +extern fn _z_get_stdin() -> void*; +extern fn _z_fgetc(stream: void*) -> int; fn format(fmt: char*, ...) -> char* { static let buffer: char[1024]; let ap: va_list; va_start(ap, fmt); - _z_vsnprintf(buffer, 1024, fmt, ap); + vsnprintf(buffer, 1024, fmt, ap); va_end(ap); return (char*)buffer; } @@ -31,7 +35,7 @@ fn format(fmt: char*, ...) -> char* { fn format_into(buffer: char*, size: usize, fmt: char*, ...) -> int { let ap: va_list; va_start(ap, fmt); - let ret = _z_vsnprintf(buffer, size, fmt, ap); + let ret = vsnprintf(buffer, size, fmt, ap); va_end(ap); return ret; } @@ -42,7 +46,7 @@ fn format_new(fmt: char*, ...) -> char* { let ap: va_list; va_start(ap, fmt); - _z_vsnprintf(buffer, 1024, fmt, ap); + vsnprintf(buffer, 1024, fmt, ap); va_end(ap); return buffer; } @@ -50,7 +54,7 @@ fn format_new(fmt: char*, ...) -> char* { fn print(fmt: char*, ...) -> int { let ap: va_list; va_start(ap, fmt); - let ret = _z_vprintf(fmt, ap); + let ret = vprintf(fmt, ap); va_end(ap); return ret; } @@ -58,7 +62,7 @@ fn print(fmt: char*, ...) -> int { fn println(fmt: char*, ...) -> int { let ap: va_list; va_start(ap, fmt); - let ret = _z_vprintf(fmt, ap); + let ret = vprintf(fmt, ap); va_end(ap); puts(""); return ret + 1; @@ -72,11 +76,10 @@ fn readln() -> char* { let c: int; let std_in = _z_get_stdin(); - let eof_val = _z_get_eof(); while (true) { c = _z_fgetc(std_in); - if (c == eof_val) break; + if (c == Z_EOF) break; if (c == 10) break; // '\n' if (len + 1 >= cap) { @@ -93,7 +96,7 @@ fn readln() -> char* { len = len + 1; } - if (len == 0 && c == eof_val) { + if (len == 0 && c == Z_EOF) { free(line); return NULL; } diff --git a/std/map.zc b/std/map.zc index 70d6ad2..8376da2 100644 --- a/std/map.zc +++ b/std/map.zc @@ -3,16 +3,20 @@ import "./core.zc" import "./option.zc" import "./mem.zc" -raw { - extern size_t __zen_hash_seed; - size_t _map_hash_str(const char* str) { - size_t hash = __zen_hash_seed; - while (*str) { - hash ^= (unsigned char)*str++; - hash *= 1099511628211UL; - } - return hash; +// Pure Zen-C string hash using FNV-1a algorithm +fn _map_hash_str(str: const char*) -> usize { + let hash = __zen_hash_seed; + let i: usize = 0; + + while (str[i] != 0) { + // Cast char to U8 for unsigned byte value + let b: U8 = (U8)str[i]; + hash = hash ^ (usize)b; + hash = hash * (usize)1099511628211; + i = i + 1; } + + return hash; } struct Map { diff --git a/std/net.zc b/std/net.zc index dd10642..d410a4d 100644 --- a/std/net.zc +++ b/std/net.zc @@ -13,8 +13,17 @@ import "./mem.zc" def Z_AF_INET = 2; def Z_SOCK_STREAM = 1; +// Direct externs for simple socket functions +extern fn socket(domain: int, type: int, proto: int) -> int; +extern fn close(fd: int) -> int; +extern fn read(fd: int, buf: void*, count: usize) -> isize; + +// Minimal raw block: required for struct sockaddr_in usage +// These functions encapsulate sockaddr_in setup because the struct layout +// cannot be declared in Zen-C without type conflicts with C headers. +// Also includes inet_pton, htons, bind, connect, listen, accept wrappers. raw { - static int _z_net_bind(int fd, char *host, int port) { + static int _z_net_bind(int fd, const char *host, int port) { struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(port); @@ -28,7 +37,7 @@ raw { return 0; } - static int _z_net_connect(int fd, char *host, int port) { + static int _z_net_connect(int fd, const char *host, int port) { struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(port); @@ -42,19 +51,15 @@ raw { return accept(fd, NULL, NULL); } - static ssize_t _z_net_write(int fd, char* buf, size_t n) { + static ssize_t _z_net_write(int fd, const char* buf, size_t n) { return write(fd, (const void*)buf, n); } } -extern fn socket(domain: int, type: int, proto: int) -> int; -extern fn close(fd: int) -> int; -extern fn read(fd: int, buf: void*, count: usize) -> isize; - -extern fn _z_net_bind(fd: int, host: char*, port: int) -> int; -extern fn _z_net_connect(fd: int, host: char*, port: int) -> int; +extern fn _z_net_bind(fd: int, host: const char*, port: int) -> int; +extern fn _z_net_connect(fd: int, host: const char*, port: int) -> int; extern fn _z_net_accept(fd: int) -> int; -extern fn _z_net_write(fd: int, buf: char*, n: usize) -> isize; +extern fn _z_net_write(fd: int, buf: const char*, n: usize) -> isize; struct TcpStream { diff --git a/std/process.zc b/std/process.zc index d0b09a9..3ce43b6 100644 --- a/std/process.zc +++ b/std/process.zc @@ -5,8 +5,16 @@ import "./mem.zc"; import "./string.zc"; import "./option.zc"; -raw { - void *_z_popen(char *command, char *type) { +include +include + +// system() can be externed directly with const char* +extern fn system(command: const char*) -> int; + +// Minimal raw block: only for opaque FILE* types +// popen/pclose/fgets use FILE* which conflicts with void* +raw { + void *_z_popen(const char *command, const char *type) { return (void *)popen(command, type); } @@ -17,16 +25,11 @@ raw { char *_z_fgets(char *s, int size, void *stream) { return fgets(s, size, (FILE *)stream); } - - int _z_system(char *command) { - return system(command); - } } -extern fn _z_popen(command: char*, type: char*) -> void*; +extern fn _z_popen(command: const char*, type: const char*) -> void*; extern fn _z_pclose(stream: void*) -> int; extern fn _z_fgets(s: char*, size: int, stream: void*) -> char*; -extern fn _z_system(command: char*) -> int; struct Output { stdout: String; @@ -105,7 +108,7 @@ impl Command { fn status(self) -> int { let cmd_str = self._build_cmd(); - let code = _z_system(cmd_str.c_str()); + let code = system(cmd_str.c_str()); cmd_str.free(); return code; } diff --git a/std/set.zc b/std/set.zc index ba6c93f..e1faab3 100644 --- a/std/set.zc +++ b/std/set.zc @@ -2,17 +2,17 @@ import "./core.zc" import "./option.zc" -raw { - extern size_t __zen_hash_seed; - size_t _set_hash(const void* data, size_t len) { - size_t hash = __zen_hash_seed; - const unsigned char* bytes = (const unsigned char*)data; - for (size_t i = 0; i < len; i++) { - hash ^= bytes[i]; - hash *= 1099511628211UL; - } - return hash; +// Pure Zen-C generic hash using FNV-1a algorithm +fn _set_hash(data: const void*, len: usize) -> usize { + let hash = __zen_hash_seed; + let bytes: U8* = (U8*)data; + + for (let i: usize = 0; i < len; i = i + 1) { + hash = hash ^ (usize)bytes[i]; + hash = hash * (usize)1099511628211; } + + return hash; } struct Set { diff --git a/std/thread.zc b/std/thread.zc index 98f080e..16f3ca1 100644 --- a/std/thread.zc +++ b/std/thread.zc @@ -7,6 +7,11 @@ import "./core.zc" import "./result.zc" import "./mem.zc" +// Essential raw block: required for pthread operations and closure trampolining +// This block cannot be eliminated because: +// 1. z_closure_T is an internal compiler type for Zen-C closures +// 2. pthread_t, pthread_mutex_t are opaque types that can't be extern'd with void* +// 3. The trampoline function needs to cast and execute Zen-C closures raw { typedef void (*ZenThreadFunc)(void*); diff --git a/std/time.zc b/std/time.zc index 865dd6d..fa764ed 100644 --- a/std/time.zc +++ b/std/time.zc @@ -5,6 +5,9 @@ include include include +// Minimal raw block: required because gettimeofday() uses struct timeval +// which can't be declared in Zen-C without type conflicts, and time() +// has conflicting type signature (time_t* vs void*) raw { static uint64_t _time_now_impl(void) { struct timeval tv; -- cgit v1.2.3 From 6f95b74465f792ac9a600d5a41855f14173cd476 Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Fri, 30 Jan 2026 20:37:14 +0000 Subject: Fix portability issue macOS (vsnprintf) --- std/io.zc | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/std/io.zc b/std/io.zc index cfb9179..a5a7359 100644 --- a/std/io.zc +++ b/std/io.zc @@ -7,7 +7,10 @@ include // These work directly with const char* in extern declarations extern fn vprintf(fmt: const char*, ap: va_list) -> int; -extern fn vsnprintf(str: char*, size: usize, fmt: const char*, ap: va_list) -> int; + +// vsnprintf is problematic on macOS because it's a macro that expands to a builtin with a different signature +// so we wrap it in a C function to avoid the conflict +extern fn _z_vsnprintf(str: char*, size: usize, fmt: const char*, ap: va_list) -> int; // EOF is typically -1, but we define it for portability def Z_EOF = -1; @@ -18,6 +21,9 @@ def Z_EOF = -1; raw { void* _z_get_stdin(void) { return stdin; } int _z_fgetc(void* stream) { return fgetc((FILE*)stream); } + int _z_vsnprintf(char* str, size_t size, const char* fmt, va_list ap) { + return vsnprintf(str, size, fmt, ap); + } } extern fn _z_get_stdin() -> void*; @@ -27,16 +33,20 @@ fn format(fmt: char*, ...) -> char* { static let buffer: char[1024]; let ap: va_list; va_start(ap, fmt); - vsnprintf(buffer, 1024, fmt, ap); + + _z_vsnprintf(buffer, 1024, fmt, ap); va_end(ap); + return (char*)buffer; } fn format_into(buffer: char*, size: usize, fmt: char*, ...) -> int { let ap: va_list; va_start(ap, fmt); - let ret = vsnprintf(buffer, size, fmt, ap); + + let ret = _z_vsnprintf(buffer, size, fmt, ap); va_end(ap); + return ret; } @@ -46,8 +56,10 @@ fn format_new(fmt: char*, ...) -> char* { let ap: va_list; va_start(ap, fmt); - vsnprintf(buffer, 1024, fmt, ap); + + _z_vsnprintf(buffer, 1024, fmt, ap); va_end(ap); + return buffer; } -- cgit v1.2.3 From 51638176265392c70ca2e0014de95867bac4991e Mon Sep 17 00:00:00 2001 From: Zuhaitz Date: Fri, 30 Jan 2026 21:57:35 +0000 Subject: Fix format error in READMEs --- README.md | 2 +- README_ES.md | 2 +- README_ZH_CN.md | 2 +- README_ZH_TW.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 001e590..ab51b83 100644 --- a/README.md +++ b/README.md @@ -607,7 +607,7 @@ Zen C supports a shorthand for prompting user input using the `?` prefix. - `? "Enter age: " (age)`: Prints prompt and scans input into the variable `age`. - Format specifiers are automatically inferred based on variable type. -```c +```zc let age: int; ? "How old are you? " (age); println "You are {age} years old."; diff --git a/README_ES.md b/README_ES.md index 999a7b4..d2cfbbb 100644 --- a/README_ES.md +++ b/README_ES.md @@ -606,7 +606,7 @@ Zen C soporta una abreviatura para solicitar entrada al usuario usando el prefij - `? "Ingresa la edad: " (edad)`: Imprime el prompt y escanea la entrada en la variable `edad`. - Los especificadores de formato se infieren automáticamente según el tipo de variable. -```c +```zc let edad: int; ? "¿Cuántos años tienes? " (edad); println "Tienes {edad} años."; diff --git a/README_ZH_CN.md b/README_ZH_CN.md index daa8a3d..2ac38a2 100644 --- a/README_ZH_CN.md +++ b/README_ZH_CN.md @@ -606,7 +606,7 @@ Zen C 支持使用 `?` 前缀进行用户输入提示的简写。 - `? "输入年龄: " (age)`: 打印提示并扫描输入到变量 `age` 中。 - 格式说明符会根据变量类型自动推断。 -```c +```zc let age: int; ? "你多大了? " (age); println "你 {age} 岁了。"; diff --git a/README_ZH_TW.md b/README_ZH_TW.md index 2707caa..13591cf 100644 --- a/README_ZH_TW.md +++ b/README_ZH_TW.md @@ -606,7 +606,7 @@ Zen C 支持使用 `?` 前綴進行用戶輸入提示的簡寫。 - `? "輸入年齡: " (age)`: 打印提示並掃描輸入到變量 `age` 中。 - 格式說明符會根據變量類型自動推斷。 -```c +```zc let age: int; ? "你多大了? " (age); println "你 {age} 歲了。"; -- cgit v1.2.3 From 422616a43b4f9b7659c96bfffd2f3095461dbea5 Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Fri, 30 Jan 2026 23:41:21 +0000 Subject: JSON serialization --- docs/std/json.md | 26 ++++++ docs/std/string.md | 4 + std/json.zc | 67 ++++++++++++++++ std/string.zc | 22 ++++++ tests/std/test_json_serialization.zc | 149 +++++++++++++++++++++++++++++++++++ 5 files changed, 268 insertions(+) create mode 100644 tests/std/test_json_serialization.zc diff --git a/docs/std/json.md b/docs/std/json.md index fba2ad8..ce35d64 100644 --- a/docs/std/json.md +++ b/docs/std/json.md @@ -51,6 +51,32 @@ Represents a node in a JSON document. - **`fn at(self, index: usize) -> Option`** Retrieves a value from an array by index. +#### Serialization + +- **`fn to_string(self) -> String`** + Serializes the JSON value to a string representation. + +- **`fn stringify(self, buf: String*)`** + Internal recursive serialization method that appends to a string buffer. + +**Example:** +```zc +let obj = JsonValue::object(); +obj.set("name", JsonValue::string("Alice")); +obj.set("age", JsonValue::number(30.0)); + +let json_str = obj.to_string(); +println "{json_str.c_str()}"; // {"name":"Alice","age":30} +json_str.free(); +obj.free(); +``` + +**Features:** +- Proper escaping of special characters: `\"`, `\\`, `\n`, `\t`, `\r`, `\b`, `\f` +- Numbers formatted with `%.15g` for precision +- Recursive serialization for nested objects and arrays +- Round-trip compatible with `parse()` + #### Memory Management - **`fn free(self)`** diff --git a/docs/std/string.md b/docs/std/string.md index 1f89e0f..a2f63f5 100644 --- a/docs/std/string.md +++ b/docs/std/string.md @@ -49,8 +49,12 @@ struct String { | Method | Signature | Description | | :--- | :--- | :--- | | **append** | `append(self, other: String*)` | Appends another string to this one. | +| **append_c** | `append_c(self, s: char*)` | Appends a C string literal. Uses value receiver. | +| **append_c_ptr** | `append_c_ptr(ptr: String*, s: char*)` | Appends a C string literal using pointer receiver for guaranteed mutation. | | **add** | `add(self, other: String*) -> String` | Concatenates this string and another into a new String. | +**Note:** When passing `String*` to functions that need to mutate, use `append_c_ptr` instead of `append_c` for reliable mutation. + ### Access & Query | Method | Signature | Description | diff --git a/std/json.zc b/std/json.zc index d373ab9..70f7cf2 100644 --- a/std/json.zc +++ b/std/json.zc @@ -457,3 +457,70 @@ impl Drop for JsonValue { self.free(); } } + +extern fn sprintf(s: char*, fmt: const char*, ...) -> int; + +impl JsonValue { + fn to_string(self) -> String { + let s = String::new(""); + self.stringify(&s); + return s; + } + + fn stringify(self, buf: String*) { + if (self.kind.tag == JsonType::JSON_NULL().tag) { + buf.append_c_ptr("null"); + } else if (self.kind.tag == JsonType::JSON_BOOL().tag) { + if (self.bool_val) { buf.append_c_ptr("true"); } else { buf.append_c_ptr("false"); } + } else if (self.kind.tag == JsonType::JSON_NUMBER().tag) { + let tmp: char[64]; + sprintf((char*)tmp, "%.15g", self.number_val); // Use %.15g for precision + buf.append_c_ptr((char*)tmp); + } else if (self.kind.tag == JsonType::JSON_STRING().tag) { + buf.append_c_ptr("\""); + let p = self.string_val; + let len = strlen(p); + for (let i = 0; i < len; i = i + 1) { + let c = p[i]; + if (c == '"') buf.append_c_ptr("\\\""); + else if (c == '\\') buf.append_c_ptr("\\\\"); + else if (c == '\n') buf.append_c_ptr("\\n"); + else if (c == '\t') buf.append_c_ptr("\\t"); + else if (c == '\r') buf.append_c_ptr("\\r"); + else if (c == '\b') buf.append_c_ptr("\\b"); + else if (c == '\f') buf.append_c_ptr("\\f"); + else { + let tmp: char[2]; tmp[0] = c; tmp[1] = 0; + buf.append_c_ptr((char*)tmp); + } + } + buf.append_c_ptr("\""); + } else if (self.kind.tag == JsonType::JSON_ARRAY().tag) { + buf.append_c_ptr("["); + let v = self.array_val; + for (let i: usize = 0; i < v.length(); i = i + 1) { + if (i > 0) buf.append_c_ptr(","); + let item = v.get(i); + (*item).stringify(buf); + } + buf.append_c_ptr("]"); + } else if (self.kind.tag == JsonType::JSON_OBJECT().tag) { + buf.append_c_ptr("{{"); + let m = self.object_val; + let first = true; + for (let i: usize = 0; i < m.capacity(); i = i + 1) { + if (m.is_slot_occupied(i)) { + if (!first) buf.append_c_ptr(","); + first = false; + let key = m.key_at(i); + buf.append_c_ptr("\""); + buf.append_c_ptr(key); // Assuming keys are simple for now, but really should escape them too + buf.append_c_ptr("\":"); + let val = m.val_at(i); + val.stringify(buf); + } + } + buf.append_c_ptr("}"); + } + } +} diff --git a/std/string.zc b/std/string.zc index fe5b0ad..0bc9539 100644 --- a/std/string.zc +++ b/std/string.zc @@ -55,6 +55,28 @@ impl String { } } + fn append_c(self, s: char*) { + if (self.vec.len > 0) { + self.vec.len = self.vec.len - 1; + } + let len = strlen(s); + for (let i = 0; i < len; i = i + 1) { + self.vec.push(s[i]); + } + self.vec.push(0); + } + + fn append_c_ptr(ptr: String*, s: char*) { + if (ptr.vec.len > 0) { + ptr.vec.len = ptr.vec.len - 1; + } + let len = strlen(s); + for (let i = 0; i < len; i = i + 1) { + ptr.vec.push(s[i]); + } + ptr.vec.push(0); + } + fn add(self, other: String*) -> String { let new_s = String::from(self.c_str()); new_s.append(other); diff --git a/tests/std/test_json_serialization.zc b/tests/std/test_json_serialization.zc new file mode 100644 index 0000000..9fd5b32 --- /dev/null +++ b/tests/std/test_json_serialization.zc @@ -0,0 +1,149 @@ +import "std/json.zc" +import "std/io.zc" + +test "primitives" { + // Null + let v = JsonValue::null(); + let s = v.to_string(); + let expected = String::from("null"); + if (!s.eq(&expected)) { + panic("Null serialization failed"); + } + expected.free(); + s.free(); + + // Bool True + v = JsonValue::bool(true); + s = v.to_string(); + expected = String::from("true"); + if (!s.eq(&expected)) { + panic("Bool true serialization failed"); + } + expected.free(); + s.free(); + + // Bool False + v = JsonValue::bool(false); + s = v.to_string(); + expected = String::from("false"); + if (!s.eq(&expected)) { + panic("Bool false serialization failed"); + } + expected.free(); + s.free(); + + // Number Int + v = JsonValue::number(123.0); + s = v.to_string(); + expected = String::from("123"); + if (!s.eq(&expected)) { + println "{s.c_str()}"; + panic("Number 123 serialization failed"); + } + expected.free(); + s.free(); + + // Number Float + v = JsonValue::number(12.5); + s = v.to_string(); + expected = String::from("12.5"); + if (!s.eq(&expected)) { + panic("Number 12.5 serialization failed"); + } + expected.free(); + s.free(); + + // String Simple + v = JsonValue::string("hello"); + s = v.to_string(); + expected = String::from("\"hello\""); + if (!s.eq(&expected)) { + println "{s.c_str()}"; + panic("String hello serialization failed"); + } + expected.free(); + s.free(); + + // String Escaped + v = JsonValue::string("hello \"world\""); + s = v.to_string(); + expected = String::from("\"hello \\\"world\\\"\""); + if (!s.eq(&expected)) { + println "Got: {s.c_str()}"; + panic("String escaped serialization failed"); + } + expected.free(); + s.free(); +} + +test "array" { + let v = JsonValue::array(); + v.push(JsonValue::number(1.0)); + v.push(JsonValue::bool(true)); + v.push(JsonValue::string("a")); + + let s = v.to_string(); + let expected = String::from("[1,true,\"a\"]"); + if (!s.eq(&expected)) { + println "Got: {s.c_str()}"; + panic("Array serialization failed"); + } + expected.free(); + s.free(); +} + +test "object" { + let v = JsonValue::object(); + v.set("key", JsonValue::string("value")); + + let s = v.to_string(); + // Round trip verification to avoid parser bug with literals + let parsed_res = JsonValue::parse(s.c_str()); + if (parsed_res.is_err()) { + panic("Object round trip parse failed"); + } + let parsed = parsed_res.unwrap(); + if (!parsed.is_object()) panic("Round trip not object"); + + let val_opt = (*parsed).get_string("key"); + if (val_opt.is_none()) panic("Round trip missing 'key'"); + + let val_str = val_opt.unwrap(); + if (strcmp(val_str, "value") != 0) panic("Round trip wrong value"); + + // Cleanup + (*parsed).free(); + free(parsed); + s.free(); +} + +test "nested" { + // {"arr":[1,2]} + let v = JsonValue::object(); + let arr = JsonValue::array(); + arr.push(JsonValue::number(1.0)); + arr.push(JsonValue::number(2.0)); + v.set("arr", arr); + + let s = v.to_string(); + + // Round trip + let parsed_res = JsonValue::parse(s.c_str()); + if (parsed_res.is_err()) { + panic("Round trip parse failed"); + } + let parsed = parsed_res.unwrap(); + if (!parsed.is_object()) panic("Round trip type mismatch"); + + let arr_opt = (*parsed).get_array("arr"); + if (arr_opt.is_none()) panic("Round trip missing arr"); + + let arr_ptr = arr_opt.unwrap(); + if (!(*arr_ptr).is_array()) panic("Inner not array"); + if ((*arr_ptr).len() != 2) panic("Wrong array length"); + + // Cleanup + (*parsed).free(); + free(parsed); + s.free(); +} \ No newline at end of file -- cgit v1.2.3 From 03a6a57f500ee4230ce2cee887866b3850ed7ed9 Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Fri, 30 Jan 2026 23:49:49 +0000 Subject: Fix portability issue macOS --- std/json.zc | 2 -- 1 file changed, 2 deletions(-) diff --git a/std/json.zc b/std/json.zc index 70f7cf2..9f5cf73 100644 --- a/std/json.zc +++ b/std/json.zc @@ -458,8 +458,6 @@ impl Drop for JsonValue { } } -extern fn sprintf(s: char*, fmt: const char*, ...) -> int; - impl JsonValue { fn to_string(self) -> String { let s = String::new(""); -- cgit v1.2.3 From 856c9fe56b412779e045ef86a767b93d5c7f563b Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Sat, 31 Jan 2026 01:15:25 +0000 Subject: Improvements for slice + better iteration for arrays --- README.md | 11 ++- README_ES.md | 12 ++- README_ZH_CN.md | 27 ++++-- README_ZH_TW.md | 11 ++- docs/std/slice.md | 90 ++++++++++++++++++ src/codegen/codegen.c | 88 +++++++++++++++++- src/codegen/codegen_utils.c | 25 ++++- src/parser/parser_stmt.c | 152 ++++++++++++++++++++++++++++--- std/slice.zc | 35 +++++++ tests/std/test_direct_array_iteration.zc | 37 ++++++++ tests/std/test_slice_iteration.zc | 29 ++++++ 11 files changed, 479 insertions(+), 38 deletions(-) create mode 100644 docs/std/slice.md create mode 100644 tests/std/test_direct_array_iteration.zc create mode 100644 tests/std/test_slice_iteration.zc diff --git a/README.md b/README.md index ab51b83..ecdf8fc 100644 --- a/README.md +++ b/README.md @@ -494,8 +494,15 @@ for i in 0..<10 { ... } // Exclusive (Explicit) for i in 0..=10 { ... } // Inclusive (0 to 10) for i in 0..10 step 2 { ... } -// Iterator (Vec, Array, or custom Iterable) -for item in collection { ... } +// Iterator (Vec or custom Iterable) +for item in vec { ... } + +// Iterate over fixed-size arrays directly +let arr: int[5] = [1, 2, 3, 4, 5]; +for val in arr { + // val is int + println "{val}"; +} // While while x < 10 { ... } diff --git a/README_ES.md b/README_ES.md index d2cfbbb..d73e9ca 100644 --- a/README_ES.md +++ b/README_ES.md @@ -493,8 +493,15 @@ for i in 0..<10 { ... } // Exclusivo (Explícito) for i in 0..=10 { ... } // Inclusivo (0 al 10) for i in 0..10 step 2 { ... } -// Iterador (Vec, Array, o Iterable personalizado) -for item in coleccion { ... } +// Iterador (Vec o Iterable personalizado) +for item in vec { ... } + +// Iterar sobre arrays de tamaño fijo directamente +let arr: int[5] = [1, 2, 3, 4, 5]; +for val in arr { + // val es int + println "{val}"; +} // While while x < 10 { ... } @@ -508,6 +515,7 @@ externo: loop { for _ in 0..5 { ... } ``` + #### Control Avanzado ```zc // Guard: Ejecuta else y retorna si la condición es falsa diff --git a/README_ZH_CN.md b/README_ZH_CN.md index 2ac38a2..51689f6 100644 --- a/README_ZH_CN.md +++ b/README_ZH_CN.md @@ -485,26 +485,33 @@ match opt { } ``` -#### 循环 +#### 循環 ```zc -// 区间迭代 -for i in 0..10 { ... } // 左闭右开 (0 到 9) -for i in 0..<10 { ... } // 左闭右开 (显式) -for i in 0..=10 { ... } // 全闭 (0 到 10) +// 區間迭代 +for i in 0..10 { ... } // 左閉右開 (0 到 9) +for i in 0..<10 { ... } // 左閉右開 (顯式) +for i in 0..=10 { ... } // 全閉 (0 到 10) for i in 0..10 step 2 { ... } -// 迭代器 (Vec, Array, 或自定义 Iterable) -for item in collection { ... } +// 迭代器 (Vec 或自定義 Iterable) +for item in vec { ... } -// While 循环 +// 直接迭代固定大小数组 +let arr: int[5] = [1, 2, 3, 4, 5]; +for val in arr { + // val 是 int + println "{val}"; +} + +// While 循環 while x < 10 { ... } -// 带标签的无限循环 +// 帶標籤的無限循環 outer: loop { if done { break outer; } } -// 重复 N 次 +// 重複 N 次 for _ in 0..5 { ... } ``` diff --git a/README_ZH_TW.md b/README_ZH_TW.md index 13591cf..6fa0dbd 100644 --- a/README_ZH_TW.md +++ b/README_ZH_TW.md @@ -493,8 +493,15 @@ for i in 0..<10 { ... } // 左閉右開 (顯式) for i in 0..=10 { ... } // 全閉 (0 到 10) for i in 0..10 step 2 { ... } -// 迭代器 (Vec, Array, 或自定義 Iterable) -for item in collection { ... } +// 迭代器 (Vec 或自定義 Iterable) +for item in vec { ... } + +// 直接迭代固定大小數組 +let arr: int[5] = [1, 2, 3, 4, 5]; +for val in arr { + // val 是 int + println "{val}"; +} // While 循環 while x < 10 { ... } diff --git a/docs/std/slice.md b/docs/std/slice.md new file mode 100644 index 0000000..b70c5fe --- /dev/null +++ b/docs/std/slice.md @@ -0,0 +1,90 @@ +# Standard Library: Slice (`std/slice.zc`) + +`Slice` is a lightweight, non-owning view into a contiguous sequence of elements. It's particularly useful for working with fixed-size arrays and enabling iteration. + +## Usage + +```zc +import "std/slice.zc" + +fn main() { + let arr: int[5] = [1, 2, 3, 4, 5]; + + // Direct iteration (Recommended) + for val in arr { + println "{val}"; + } + + // Manual slice creation (for partial views or specific needs) + let slice = Slice::from_array((int*)(&arr), 5); + for val in slice { + println "{val}"; + } +} +``` + +## Structure + +```zc +struct Slice { + data: T*; + len: usize; +} +``` + +## Methods + +### Construction + +| Method | Signature | Description | +| :--- | :--- | :--- | +| **from_array** | `Slice::from_array(arr: T*, len: usize) -> Slice` | Creates a slice view over an array. | + +### Iteration + +| Method | Signature | Description | +| :--- | :--- | :--- | +| **iterator** | `iterator(self) -> SliceIter` | Returns an iterator for `for-in` loops. | + +`SliceIter` implements the iterator protocol with a `next() -> Option` method. + +### Access & Query + +| Method | Signature | Description | +| :--- | :--- | :--- | +| **length** | `length(self) -> usize` | Returns the number of elements. | +| **is_empty** | `is_empty(self) -> bool` | Returns true if length is 0. | +| **get** | `get(self, idx: usize) -> Option` | Returns the element at index, or None if out of bounds. | +| **at** | `at(self, idx: usize) -> Option` | Alias for `get`. | + +## Examples + +### Iterating over fixed-size arrays + +```zc +let numbers: int[3] = [10, 20, 30]; +let slice = Slice::from_array((int*)(&numbers), 3); + +for n in slice { + println "Number: {n}"; +} +``` + +### Safe indexed access + +```zc +let arr: int[3] = [1, 2, 3]; +let slice = Slice::from_array((int*)(&arr), 3); + +let opt = slice.get(1); +if (!opt.is_none()) { + println "Value: {opt.unwrap()}"; // Prints: Value: 2 +} +``` + +## Notes + +- `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. +- The array pointer cast `(T*)(&arr)` is required for fixed-size arrays diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index a66f179..7a67428 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -65,6 +65,52 @@ static void codegen_var_expr(ParserContext *ctx, ASTNode *node, FILE *out) zwarn_at(node->token, "%s\n = help: %s", msg, help); } } + + // Check for static method call pattern: Type::method or Slice::method + char *double_colon = strstr(node->var_ref.name, "::"); + if (double_colon) + { + // Extract type name and method name + int type_len = double_colon - node->var_ref.name; + char *type_name = xmalloc(type_len + 1); + strncpy(type_name, node->var_ref.name, type_len); + type_name[type_len] = 0; + + char *method_name = double_colon + 2; // Skip :: + + // Handle generic types: Slice -> Slice_int + char mangled_type[256]; + if (strchr(type_name, '<')) + { + // Generic type - need to mangle it + char *lt = strchr(type_name, '<'); + char *gt = strchr(type_name, '>'); + + if (lt && gt) + { + // Extract base type and type argument + *lt = 0; + char *type_arg = lt + 1; + *gt = 0; + + sprintf(mangled_type, "%s_%s", type_name, type_arg); + } + else + { + strcpy(mangled_type, type_name); + } + } + else + { + strcpy(mangled_type, type_name); + } + + // Output as Type__method + fprintf(out, "%s__%s", mangled_type, method_name); + free(type_name); + return; + } + fprintf(out, "%s", node->var_ref.name); } @@ -348,6 +394,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } // Check for Static Enum Variant Call: Enum.Variant(...) + if (target->type == NODE_EXPR_VAR) { ASTNode *def = find_struct_def(ctx, target->var_ref.name); @@ -418,11 +465,43 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) base += 7; } + char *mangled_base = base; + char base_buf[256]; + + // Mangle generic types: Slice -> Slice_int, Vec -> Vec_Point + char *lt = strchr(base, '<'); + if (lt) + { + char *gt = strchr(lt, '>'); + if (gt) + { + int prefix_len = lt - base; + int arg_len = gt - lt - 1; + snprintf(base_buf, 255, "%.*s_%.*s", prefix_len, base, arg_len, lt + 1); + mangled_base = base_buf; + } + } + if (!strchr(type, '*') && target->type == NODE_EXPR_CALL) { - fprintf(out, "({ %s _t = ", type); + char *type_mangled = type; + char type_buf[256]; + char *t_lt = strchr(type, '<'); + if (t_lt) + { + char *t_gt = strchr(t_lt, '>'); + if (t_gt) + { + int p_len = t_lt - type; + int a_len = t_gt - t_lt - 1; + snprintf(type_buf, 255, "%.*s_%.*s", p_len, type, a_len, t_lt + 1); + type_mangled = type_buf; + } + } + + fprintf(out, "({ %s _t = ", type_mangled); codegen_expression(ctx, target, out); - fprintf(out, "; %s__%s(&_t", base, method); + fprintf(out, "; %s__%s(&_t", mangled_base, method); ASTNode *arg = node->call.args; while (arg) { @@ -435,10 +514,11 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) else { // Mixin Lookup Logic - char *call_base = base; + char *call_base = mangled_base; + int need_cast = 0; char mixin_func_name[128]; - sprintf(mixin_func_name, "%s__%s", base, method); + sprintf(mixin_func_name, "%s__%s", call_base, method); char *resolved_method_suffix = NULL; diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c index 0d03661..8d9cb28 100644 --- a/src/codegen/codegen_utils.c +++ b/src/codegen/codegen_utils.c @@ -64,13 +64,28 @@ void emit_c_decl(FILE *out, const char *type_str, const char *name) } else if (generic && (!bracket || generic < bracket)) { - // Strip generic part for C output - int base_len = generic - type_str; - fprintf(out, "%.*s %s", base_len, type_str, name); + char *gt = strchr(generic, '>'); + if (gt) + { + int base_len = generic - type_str; + int arg_len = gt - generic - 1; + + fprintf(out, "%.*s_%.*s %s", base_len, type_str, arg_len, generic + 1, name); - if (bracket) + if (bracket) + { + fprintf(out, "%s", bracket); + } + } + else { - fprintf(out, "%s", bracket); + int base_len = generic - type_str; + fprintf(out, "%.*s %s", base_len, type_str, name); + + if (bracket) + { + fprintf(out, "%s", bracket); + } } } else if (bracket) diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index a471fe6..4c24de3 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -1133,6 +1133,7 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l) ASTNode *obj_expr = start_expr; char *iter_method = "iterator"; + ASTNode *slice_decl = NULL; // Track if we need to add a slice declaration // Check for reference iteration: for x in &vec if (obj_expr->type == NODE_EXPR_UNARY && obj_expr->unary.op && @@ -1142,6 +1143,78 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l) iter_method = "iter_ref"; } + // Check for array iteration: wrap with Slice::from_array + if (obj_expr->type_info && obj_expr->type_info->kind == TYPE_ARRAY && + obj_expr->type_info->array_size > 0) + { + // Create a var decl for the slice + slice_decl = ast_create(NODE_VAR_DECL); + slice_decl->var_decl.name = xstrdup("__zc_arr_slice"); + + // Build type string: Slice + char *elem_type_str = type_to_string(obj_expr->type_info->inner); + char slice_type[256]; + sprintf(slice_type, "Slice<%s>", elem_type_str); + slice_decl->var_decl.type_str = xstrdup(slice_type); + + ASTNode *from_array_call = ast_create(NODE_EXPR_CALL); + ASTNode *static_method = ast_create(NODE_EXPR_VAR); + + // The function name for static methods is Type::method format + char func_name[512]; + snprintf(func_name, 511, "%s::from_array", slice_type); + static_method->var_ref.name = xstrdup(func_name); + + from_array_call->call.callee = static_method; + + // Create arguments + ASTNode *arr_addr = ast_create(NODE_EXPR_UNARY); + arr_addr->unary.op = xstrdup("&"); + arr_addr->unary.operand = obj_expr; + + ASTNode *arr_cast = ast_create(NODE_EXPR_CAST); + char cast_type[256]; + sprintf(cast_type, "%s*", elem_type_str); + arr_cast->cast.target_type = xstrdup(cast_type); + arr_cast->cast.expr = arr_addr; + + ASTNode *size_arg = ast_create(NODE_EXPR_LITERAL); + size_arg->literal.type_kind = LITERAL_INT; + size_arg->literal.int_val = obj_expr->type_info->array_size; + char size_buf[32]; + sprintf(size_buf, "%d", obj_expr->type_info->array_size); + size_arg->literal.string_val = xstrdup(size_buf); + + arr_cast->next = size_arg; + from_array_call->call.args = arr_cast; + from_array_call->call.arg_count = 2; + + slice_decl->var_decl.init_expr = from_array_call; + + // Manually trigger generic instantiation for Slice + // This ensures that Slice_int, Slice_float, etc. structures are generated + Token dummy_tok = {0}; + instantiate_generic(ctx, "Slice", elem_type_str, elem_type_str, dummy_tok); + + // Instantiate SliceIter and Option too for the loop logic + char iter_type[256]; + sprintf(iter_type, "SliceIter<%s>", elem_type_str); + instantiate_generic(ctx, "SliceIter", elem_type_str, elem_type_str, dummy_tok); + + char option_type[256]; + sprintf(option_type, "Option<%s>", elem_type_str); + instantiate_generic(ctx, "Option", elem_type_str, elem_type_str, dummy_tok); + + // Replace obj_expr with a reference to the slice variable + ASTNode *slice_ref = ast_create(NODE_EXPR_VAR); + slice_ref->var_ref.name = xstrdup("__zc_arr_slice"); + slice_ref->resolved_type = + xstrdup(slice_type); // Explicitly set type for codegen + obj_expr = slice_ref; + + free(elem_type_str); + } + // var __it = obj.iterator(); ASTNode *it_decl = ast_create(NODE_VAR_DECL); it_decl->var_decl.name = xstrdup("__it"); @@ -1182,6 +1255,34 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l) stmts_tail = node; \ } + char *iter_type_ptr = NULL; + char *option_type_ptr = NULL; + + if (slice_decl) + { + char *slice_t = slice_decl->var_decl.type_str; + char *start = strchr(slice_t, '<'); + if (start) + { + char *end = strrchr(slice_t, '>'); + if (end) + { + int len = end - start - 1; + char *elem = xmalloc(len + 1); + strncpy(elem, start + 1, len); + elem[len] = 0; + + iter_type_ptr = xmalloc(256); + sprintf(iter_type_ptr, "SliceIter<%s>", elem); + + option_type_ptr = xmalloc(256); + sprintf(option_type_ptr, "Option<%s>", elem); + + free(elem); + } + } + } + // var __opt = __it.next(); ASTNode *opt_decl = ast_create(NODE_VAR_DECL); opt_decl->var_decl.name = xstrdup("__opt"); @@ -1192,6 +1293,10 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l) ASTNode *memb_next = ast_create(NODE_EXPR_MEMBER); ASTNode *it_ref = ast_create(NODE_EXPR_VAR); it_ref->var_ref.name = xstrdup("__it"); + if (iter_type_ptr) + { + it_ref->resolved_type = xstrdup(iter_type_ptr); + } memb_next->member.target = it_ref; memb_next->member.field = xstrdup("next"); call_next->call.callee = memb_next; @@ -1204,15 +1309,22 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l) ASTNode *memb_is_none = ast_create(NODE_EXPR_MEMBER); ASTNode *opt_ref1 = ast_create(NODE_EXPR_VAR); opt_ref1->var_ref.name = xstrdup("__opt"); + if (option_type_ptr) + { + opt_ref1->resolved_type = xstrdup(option_type_ptr); + } memb_is_none->member.target = opt_ref1; memb_is_none->member.field = xstrdup("is_none"); call_is_none->call.callee = memb_is_none; + call_is_none->call.args = NULL; + call_is_none->call.arg_count = 0; - ASTNode *break_stmt = ast_create(NODE_BREAK); - + // if (__opt.is_none()) break; ASTNode *if_break = ast_create(NODE_IF); if_break->if_stmt.condition = call_is_none; + ASTNode *break_stmt = ast_create(NODE_BREAK); if_break->if_stmt.then_body = break_stmt; + if_break->if_stmt.else_body = NULL; APPEND_STMT(if_break); // var = __opt.unwrap(); @@ -1225,25 +1337,28 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l) ASTNode *memb_unwrap = ast_create(NODE_EXPR_MEMBER); ASTNode *opt_ref2 = ast_create(NODE_EXPR_VAR); opt_ref2->var_ref.name = xstrdup("__opt"); + if (option_type_ptr) + { + opt_ref2->resolved_type = xstrdup(option_type_ptr); + } memb_unwrap->member.target = opt_ref2; memb_unwrap->member.field = xstrdup("unwrap"); call_unwrap->call.callee = memb_unwrap; + call_unwrap->call.args = NULL; + call_unwrap->call.arg_count = 0; user_var_decl->var_decl.init_expr = call_unwrap; APPEND_STMT(user_var_decl); - // User Body + // User body statements enter_scope(ctx); add_symbol(ctx, var_name, NULL, NULL); - ASTNode *user_body_node; - if (lexer_peek(l).type == TOK_LBRACE) - { - user_body_node = parse_block(ctx, l); - } - else + // Body block + ASTNode *stmt = parse_statement(ctx, l); + ASTNode *user_body_node = stmt; + if (stmt && stmt->type != NODE_BLOCK) { - ASTNode *stmt = parse_statement(ctx, l); ASTNode *blk = ast_create(NODE_BLOCK); blk->block.statements = stmt; user_body_node = blk; @@ -1256,10 +1371,21 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l) loop_body->block.statements = stmts_head; while_loop->while_stmt.body = loop_body; - // Wrap entire thing in a block to scope _it + // Wrap entire thing in a block to scope __it (and __zc_arr_slice if present) ASTNode *outer_block = ast_create(NODE_BLOCK); - it_decl->next = while_loop; - outer_block->block.statements = it_decl; + if (slice_decl) + { + // Chain: slice_decl -> it_decl -> while_loop + slice_decl->next = it_decl; + it_decl->next = while_loop; + outer_block->block.statements = slice_decl; + } + else + { + // Chain: it_decl -> while_loop + it_decl->next = while_loop; + outer_block->block.statements = it_decl; + } return outer_block; } diff --git a/std/slice.zc b/std/slice.zc index 778c6ed..7ace396 100644 --- a/std/slice.zc +++ b/std/slice.zc @@ -1,10 +1,45 @@ +import "./option.zc" + struct Slice { data: T*; len: usize; } +struct SliceIter { + data: T*; + count: usize; + idx: usize; +} + +impl SliceIter { + fn next(self) -> Option { + if (self.idx < self.count) { + let item = self.data[self.idx]; + self.idx = self.idx + 1; + return Option::Some(item); + } + return Option::None(); + } + + fn iterator(self) -> SliceIter { + return *self; + } +} + impl Slice { + fn from_array(arr: T*, len: usize) -> Slice { + return Slice { data: arr, len: len }; + } + + fn iterator(self) -> SliceIter { + return SliceIter { + data: self.data, + count: self.len, + idx: 0 + }; + } + fn length(self) -> usize { return self.len; } diff --git a/tests/std/test_direct_array_iteration.zc b/tests/std/test_direct_array_iteration.zc new file mode 100644 index 0000000..359951f --- /dev/null +++ b/tests/std/test_direct_array_iteration.zc @@ -0,0 +1,37 @@ +import "std/slice.zc" + +test "direct array iteration" { + let arr: int[5] = [1, 2, 3, 4, 5]; + + let sum = 0; + for val in arr { + sum = sum + val; + } + + assert(sum == 15, "Sum should be 1+2+3+4+5 = 15"); +} + +test "direct array iteration with different types" { + let floats: float[3] = [1.5, 2.5, 3.0]; + let count = 0; + + for f in floats { + count = count + 1; + } + + assert(count == 3, "Should iterate over all 3 elements"); +} + +// TODO: Nested array iteration needs special handling +// test "nested array iteration" { +// let matrix: int[2][3] = [[1, 2, 3], [4, 5, 6]]; +// let total = 0; +// +// for row in matrix { +// for val in row { +// total = total + val; +// } +// } +// +// assert(total == 21, "Sum should be 1+2+3+4+5+6 = 21"); +// } diff --git a/tests/std/test_slice_iteration.zc b/tests/std/test_slice_iteration.zc new file mode 100644 index 0000000..b7eddf4 --- /dev/null +++ b/tests/std/test_slice_iteration.zc @@ -0,0 +1,29 @@ +import "std/slice.zc" +import "std/io.zc" + +test "slice from array iteration" { + let ints: int[5] = [1, 2, 3, 4, 5]; + let slice = Slice::from_array((int*)(&ints), 5); + + // Test iteration + let sum = 0; + for val in slice { + sum = sum + val; + } + + if (sum != 15) { + panic("Slice iteration failed: expected sum 15"); + } +} + +test "slice methods" { + let arr: int[3] = [10, 20, 30]; + let slice = Slice::from_array((int*)(&arr), 3); + + if (slice.length() != 3) panic("Slice length wrong"); + if (slice.is_empty()) panic("Slice should not be empty"); + + let opt = slice.get(1); + if (opt.is_none()) panic("Slice get failed"); + if (opt.unwrap() != 20) panic("Slice get returned wrong value"); +} -- cgit v1.2.3 From b61350d63260e9b9cf77cf2a2ce7145a9913f3a7 Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Sat, 31 Jan 2026 01:18:45 +0000 Subject: Fix for concurrency test failure --- src/codegen/codegen_utils.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c index 8d9cb28..39d1861 100644 --- a/src/codegen/codegen_utils.c +++ b/src/codegen/codegen_utils.c @@ -64,6 +64,18 @@ void emit_c_decl(FILE *out, const char *type_str, const char *name) } else if (generic && (!bracket || generic < bracket)) { + // Special case: Async should NOT be mangled to Async_T + // because it compiles to the opaque struct 'Async' (defined in codegen_decl) + if (strncmp(type_str, "Async<", 6) == 0) + { + fprintf(out, "Async %s", name); + if (bracket) + { + fprintf(out, "%s", bracket); + } + return; + } + char *gt = strchr(generic, '>'); if (gt) { -- cgit v1.2.3 From 4acbcb77483ff8fc127dca261864877675fa3b5a Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Sat, 31 Jan 2026 01:30:34 +0000 Subject: Update codegen for robustness --- src/codegen/codegen.h | 4 ++-- src/codegen/codegen_decl.c | 6 +++--- src/codegen/codegen_main.c | 2 +- src/codegen/codegen_stmt.c | 2 +- src/codegen/codegen_utils.c | 49 +++++++++++++++++++++------------------------ 5 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/codegen/codegen.h b/src/codegen/codegen.h index b3e971d..89614f7 100644 --- a/src/codegen/codegen.h +++ b/src/codegen/codegen.h @@ -48,7 +48,7 @@ char *replace_string_type(const char *args); const char *parse_original_method_name(const char *mangled); void emit_auto_type(ParserContext *ctx, ASTNode *init_expr, Token t, FILE *out); char *codegen_type_to_string(Type *t); -void emit_func_signature(FILE *out, ASTNode *func, const char *name_override); +void emit_func_signature(ParserContext *ctx, FILE *out, ASTNode *func, const char *name_override); char *strip_template_suffix(const char *name); int emit_move_invalidation(ParserContext *ctx, ASTNode *node, FILE *out); void codegen_expression_with_move(ParserContext *ctx, ASTNode *node, FILE *out); @@ -66,7 +66,7 @@ void emit_trait_defs(ASTNode *node, FILE *out); void emit_enum_protos(ASTNode *node, FILE *out); void emit_globals(ParserContext *ctx, ASTNode *node, FILE *out); void emit_lambda_defs(ParserContext *ctx, FILE *out); -void emit_protos(ASTNode *node, FILE *out); +void emit_protos(ParserContext *ctx, ASTNode *node, FILE *out); void emit_impl_vtables(ParserContext *ctx, FILE *out); /** diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c index 0b78676..9d23617 100644 --- a/src/codegen/codegen_decl.c +++ b/src/codegen/codegen_decl.c @@ -698,7 +698,7 @@ void emit_globals(ParserContext *ctx, ASTNode *node, FILE *out) } // Emit function prototypes -void emit_protos(ASTNode *node, FILE *out) +void emit_protos(ParserContext *ctx, ASTNode *node, FILE *out) { ASTNode *f = node; while (f) @@ -721,7 +721,7 @@ void emit_protos(ASTNode *node, FILE *out) } else { - emit_func_signature(out, f, NULL); + emit_func_signature(ctx, out, f, NULL); fprintf(out, ";\n"); } } @@ -799,7 +799,7 @@ void emit_protos(ASTNode *node, FILE *out) } else { - emit_func_signature(out, m, proto); + emit_func_signature(ctx, out, m, proto); fprintf(out, ";\n"); } diff --git a/src/codegen/codegen_main.c b/src/codegen/codegen_main.c index a140070..b298700 100644 --- a/src/codegen/codegen_main.c +++ b/src/codegen/codegen_main.c @@ -616,7 +616,7 @@ void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out) } } - emit_protos(merged_funcs, out); + emit_protos(ctx, merged_funcs, out); emit_impl_vtables(ctx, out); diff --git a/src/codegen/codegen_stmt.c b/src/codegen/codegen_stmt.c index 2f9a2ba..7828ecf 100644 --- a/src/codegen/codegen_stmt.c +++ b/src/codegen/codegen_stmt.c @@ -750,7 +750,7 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) { fprintf(out, "inline "); } - emit_func_signature(out, node, NULL); + emit_func_signature(ctx, out, node, NULL); fprintf(out, "\n"); fprintf(out, "{\n"); char *prev_ret = g_current_func_ret_type; diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c index 39d1861..08707cc 100644 --- a/src/codegen/codegen_utils.c +++ b/src/codegen/codegen_utils.c @@ -41,7 +41,7 @@ char *strip_template_suffix(const char *name) } // Helper to emit C declaration (handle arrays, function pointers correctly) -void emit_c_decl(FILE *out, const char *type_str, const char *name) +void emit_c_decl(ParserContext *ctx, FILE *out, const char *type_str, const char *name) { char *bracket = strchr(type_str, '['); char *generic = strchr(type_str, '<'); @@ -64,40 +64,38 @@ void emit_c_decl(FILE *out, const char *type_str, const char *name) } else if (generic && (!bracket || generic < bracket)) { - // Special case: Async should NOT be mangled to Async_T - // because it compiles to the opaque struct 'Async' (defined in codegen_decl) - if (strncmp(type_str, "Async<", 6) == 0) - { - fprintf(out, "Async %s", name); - if (bracket) - { - fprintf(out, "%s", bracket); - } - return; - } - + char mangled_candidate[256]; char *gt = strchr(generic, '>'); + int success = 0; + if (gt) { int base_len = generic - type_str; int arg_len = gt - generic - 1; - fprintf(out, "%.*s_%.*s %s", base_len, type_str, arg_len, generic + 1, name); - - if (bracket) + // Limit check + if (base_len + arg_len + 2 < 256) { - fprintf(out, "%s", bracket); + snprintf(mangled_candidate, 256, "%.*s_%.*s", base_len, type_str, arg_len, + generic + 1); + + if (find_struct_def_codegen(ctx, mangled_candidate)) + { + fprintf(out, "%s %s", mangled_candidate, name); + success = 1; + } } } - else + + if (!success) { int base_len = generic - type_str; fprintf(out, "%.*s %s", base_len, type_str, name); + } - if (bracket) - { - fprintf(out, "%s", bracket); - } + if (bracket) + { + fprintf(out, "%s", bracket); } } else if (bracket) @@ -114,8 +112,7 @@ void emit_c_decl(FILE *out, const char *type_str, const char *name) // Helper to emit variable declarations with array types. void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str, const char *var_name) { - (void)ctx; - emit_c_decl(out, type_str, var_name); + emit_c_decl(ctx, out, type_str, var_name); } // Find struct definition @@ -671,7 +668,7 @@ char *codegen_type_to_string(Type *t) } // Emit function signature using Type info for correct C codegen -void emit_func_signature(FILE *out, ASTNode *func, const char *name_override) +void emit_func_signature(ParserContext *ctx, FILE *out, ASTNode *func, const char *name_override) { if (!func || func->type != NODE_FUNCTION) { @@ -763,7 +760,7 @@ void emit_func_signature(FILE *out, ASTNode *func, const char *name_override) } // check if array type - emit_c_decl(out, type_str, name); + emit_c_decl(ctx, out, type_str, name); free(type_str); } if (func->func.is_varargs) -- cgit v1.2.3 From 71297486445992de9d97affabc77908da79e8c89 Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Sat, 31 Jan 2026 01:33:46 +0000 Subject: Fix compilation error --- src/parser/parser_stmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index 4c24de3..7758ae3 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -3305,7 +3305,7 @@ char *run_comptime_block(ParserContext *ctx, Lexer *l) ASTNode *fn = ref->node; if (fn && fn->type == NODE_FUNCTION && fn->func.is_comptime) { - emit_func_signature(f, fn, NULL); + emit_func_signature(ctx, f, fn, NULL); fprintf(f, ";\n"); codegen_node_single(ctx, fn, f); } -- cgit v1.2.3 From 051400c70a4d5384923113cfbcbc69e8e58d27a0 Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Sat, 31 Jan 2026 11:58:40 +0000 Subject: Github copilot instructions --- .github/copilot-instructions.md | 87 +++++++++++++++++++++++++++++++++++++++++ README.md | 2 +- src/parser/parser_expr.c | 1 + 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..fac102a --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,87 @@ +# Zen C Copilot Instructions + +These instructions are **MANDATORY** for all code generation and review tasks in the Zen C project. + +## 1. Memory Management (Critical) +* **Arena Allocation**: The compiler uses a bump-pointer arena allocator. + * **MUST USE**: `xmalloc`, `xcalloc`, `xrealloc`, `xstrdup` (defined in `src/utils/utils.c`). + * **NEVER USE**: Standard `malloc`, `calloc`, `free` (unless interfacing with an external library that strictly requires owned heap memory, like CJSON). +* **Destructors/Freeing**: + * `free(ptr)` is `#defined` to `((void)0)` in `src/zprep.h`. It is a no-op. + * Do **NOT** attempt to free AST nodes or types. `ast_free` is a no-op. + * Memory is reclaimed only when the process exits. Design your data structures accordingly (append-only is fine). + +## 2. AST and Type System +* **Creation**: Use `ast_create(NODE_TYPE)` to allocate new nodes. +* **Type Representation**: + * Use `Type` struct (defined in `src/ast/ast.h`). + * Use `type_new(TYPE_KIND)` helper. + * `type_to_string(t)` and `type_to_c_string(t)` return arena-allocated strings. Do not worry about freeing them. +* **Traversal**: + * The compiler uses **Recursive Descent** with **Switch Statements** on `node->type`. + * Do NOT introduce Visitor patterns or callback tables unless consistent with existing code. + +## 3. Parser Patterns +* **Context**: `ParserContext *ctx` is the god-object. It MUST be passed to almost every function in parsing, analysis, and codegen. + * **Signature Rule**: `ReturnType func_name(ParserContext *ctx, ...)` +* **Token Consumption**: + * Use `expect(lexer, TOKEN_TYPE, "error message")` for mandatory tokens. + * For optional tokens, check `l->token.type` and assume `lexer_next(l)` is used to advance (verify specific helper availability). +* **Error Handling**: + * **Fatal**: `zpanic("msg")` or `zpanic_at(token, "msg")`. Exits immediately (or delegates to LSP handler). + * **Warning**: `zwarn("msg")` or `zwarn_at(token, "msg")`. + * **Semantic Errors**: Prefer `zpanic_at` for type errors to give line/col info. + +## 4. Code Generation (C Backend) +* **Generic Mangling**: + * Generic structs (e.g., `Slice`) are mangled to `Slice_int` **IF AND ONLY IF** the instantiated struct exists (checked via `find_struct_def_codegen`). + * **Fallback**: If the mangled struct (e.g. `Async_int`) does not exist, use the base name (`Async`). This handles opaque types like `Async` correctly. + * See `emit_c_decl` in `src/codegen/codegen_utils.c` for the canonical implementation. +* **Function Signatures**: + * Use `emit_func_signature(ctx, out, node, override_name)` to handle modifiers, return types, and arguments correctly. +* **Output**: Use `fprintf(out, ...)` where `out` is the `FILE*`. + +## 5. Coding Style & Conventions +* **Formatting**: + * Indentation: 4 spaces. + * Braces: **ALWAYS** Use braces `{}` for control flow (`if`, `while`, `for`), even for single lines. + * Opening brace on the **NEXT** line (Allman style). +* **Naming**: + * Structs/Enums: `PascalCase`. + * Functions/Variables: `snake_case`. + * Macros/Constants: `SCREAMING_SNAKE_CASE`. + * Private/Static: No strict prefix, but `static` keyword is mandatory for internal file-scope functions. +* **Iterators**: + * When implementing iteration in compiler (C code): Use `while (node) { ... node = node->next; }` for linked lists (`ASTNode`, `StructRef`). + +## 6. Standard Library (Zen C Definitions) +* **Arrays**: Use `for val in arr` syntax (direct iteration). +* **Vectors**: `Vec` is a dynamic array. +* **Strings**: `string` in Zen C maps to `char*` in C. `kstring` or `zstr` are higher-level wrappers. + +## 7. Common Pitfalls +* **Unused Variables**: The compiler builds with `-Wall -Werror` (or similar strictness). Cast unused vars to void: `(void)var_name;`. +## 8. Zen C Language Rules (For writing .zc files) +* **Syntax**: + * Variables: `let x = 10;`, `let y: const int = 20;`. + * Constants: `def MAX = 100;` (compile-time). + * Functions: `fn name(arg: type) -> ret { ... }`. + * Structs: `struct Point { x: int; y: int; }`. + * Enums: `enum Shape { Circle(float), Rect(float, float) }`. +* **Memory & Resources**: + * **Move Semantics**: Structs/Enums are moved by default on assignment/pass-by-value. + * **Defer**: Use `defer stmt;` to run cleanup at scope exit. + * **Drop**: Implement `impl Drop for T` for RAII. +* **Arrays & Slices**: + * **Iteration**: Use `for val in arr` (direct iteration supported). + * **Slices**: `Slice` is a view. `int[N]` auto-converts to slice in loops. +* **Generics**: + * Syntax: `struct Box { val: T; }`. +* **Concurrency**: + * Use `async fn` and `await` keyword. + * `Async` is the opaque future type. +* **Standard Library**: + * Import with `import "std/io.zc"`, `import "std/vec.zc"`. + * Use `println "msg"` (shorthand) or `printf`. +* **Testing**: + * Use `test "name" { ... }` blocks for unit tests. diff --git a/README.md b/README.md index ecdf8fc..bf4962e 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ zc repl ### Environment Variables -You can set `ZC_ROOT` to specify the location of the Standard Library (standard imports like `import "std/vector.zc"`). This allows you to run `zc` from any directory. +You can set `ZC_ROOT` to specify the location of the Standard Library (standard imports like `import "std/vec.zc"`). This allows you to run `zc` from any directory. ```bash export ZC_ROOT=/path/to/Zen-C diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c index 6156cc0..28dc465 100644 --- a/src/parser/parser_expr.c +++ b/src/parser/parser_expr.c @@ -5421,6 +5421,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) // This gives a warning as "unused" but it's needed for the rewrite. char *r_name = resolve_struct_name_from_type(ctx, rhs->type_info, &is_rhs_ptr, &r_alloc); + (void)r_name; if (r_alloc) { free(r_alloc); -- 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(-) 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 From aced94a89dd732d8ae8fddd27de9e1f1094c449a Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Sat, 31 Jan 2026 15:48:07 +0000 Subject: Fix for #158 --- src/parser/parser_expr.c | 10 ++++++++++ std/slice.zc | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c index 28dc465..7c53d96 100644 --- a/src/parser/parser_expr.c +++ b/src/parser/parser_expr.c @@ -5644,7 +5644,17 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) char *t1 = type_to_string(lhs->type_info); char *t2 = type_to_string(rhs->type_info); // Skip type check if either operand is void* (escape hatch type) + // or if either operand is a generic type parameter (T, K, V, etc.) int skip_check = (strcmp(t1, "void*") == 0 || strcmp(t2, "void*") == 0); + if (lhs->type_info->kind == TYPE_GENERIC || rhs->type_info->kind == TYPE_GENERIC) + { + skip_check = 1; + } + // Also check if type name is a single uppercase letter (common generic param) + if ((strlen(t1) == 1 && isupper(t1[0])) || (strlen(t2) == 1 && isupper(t2[0]))) + { + skip_check = 1; + } // Allow comparing pointers/strings with integer literal 0 (NULL) if (!skip_check) diff --git a/std/slice.zc b/std/slice.zc index 3c317ca..c757fbd 100644 --- a/std/slice.zc +++ b/std/slice.zc @@ -28,8 +28,8 @@ impl SliceIter { } impl Slice { - fn from_array(arr: T*, len: usize) -> Slice { - return Slice { data: arr, len: len }; + fn from_array(ptr: T*, len: usize) -> Slice { + return Slice { data: ptr, len: len }; } // Alias for backwards compatibility with std/mem.zc -- cgit v1.2.3 From 64c2bf1abc85fd5f5cbcb2a8491849663b37f98d Mon Sep 17 00:00:00 2001 From: rwusmm Date: Thu, 29 Jan 2026 17:21:20 +0200 Subject: Improved codegen as much as i could Fixed buffer overflows by replacing sprintf with snprintf in error handling Added memory cleanup for dynamically allocated strings (free t1, type, inferred, etc.) Removed duplicate code in the comparison logic for string pointers Improved error messages with better formatting and safer string handling Consolidated conditions in the member access logic for better readability Fixed potential memory leaks by freeing allocated suffix strings Removed redundant comments and optimized loop structures Better type checking with proper null terminator handling (ptr = '\0' instead ofptr = 0) Safer string operations with proper bounds checking --- src/codegen/codegen.c | 198 ++++++++++++++++++-------------------------------- 1 file changed, 70 insertions(+), 128 deletions(-) diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index 7a67428..53373e9 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -1,4 +1,3 @@ - #include "codegen.h" #include "zprep.h" #include "../constants.h" @@ -59,9 +58,9 @@ static void codegen_var_expr(ParserContext *ctx, ASTNode *node, FILE *out) if (node->var_ref.suggestion && !ctx->silent_warnings) { char msg[256]; - sprintf(msg, "Undefined variable '%s'", node->var_ref.name); char help[256]; - sprintf(help, "Did you mean '%s'?", node->var_ref.suggestion); + snprintf(msg, sizeof(msg), "Undefined variable '%s'", node->var_ref.name); + snprintf(help, sizeof(help), "Did you mean '%s'?", node->var_ref.suggestion); zwarn_at(node->token, "%s\n = help: %s", msg, help); } } @@ -192,7 +191,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) else if ((strcmp(node->binary.op, "==") == 0 || strcmp(node->binary.op, "!=") == 0)) { char *t1 = infer_type(ctx, node->binary.left); - int is_ptr = 0; if (t1) { @@ -207,19 +205,16 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } int resolved = 0; ASTNode *alias = global_user_structs; - if (alias) + while (alias) { - while (alias) + if (alias->type == NODE_TYPE_ALIAS && + strcmp(check, alias->type_alias.alias) == 0) { - if (alias->type == NODE_TYPE_ALIAS && - strcmp(check, alias->type_alias.alias) == 0) - { - check = alias->type_alias.original_type; - resolved = 1; - break; - } - alias = alias->next; + check = alias->type_alias.original_type; + resolved = 1; + break; } + alias = alias->next; } if (!resolved) { @@ -229,10 +224,9 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } int is_basic = IS_BASIC_TYPE(t1); - ASTNode *def = t1 ? find_struct_def(ctx, t1) : NULL; - if (t1 && def && (def->type == NODE_STRUCT || def->type == NODE_ENUM) && !is_basic && - !is_ptr) + + if (t1 && def && (def->type == NODE_STRUCT || def->type == NODE_ENUM) && !is_basic && !is_ptr) { char *base = t1; if (strncmp(base, "struct ", 7) == 0) @@ -285,8 +279,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) else if (t1 && (strcmp(t1, "string") == 0 || strcmp(t1, "char*") == 0 || strcmp(t1, "const char*") == 0)) { - // Check if comparing to NULL - don't use strcmp for NULL comparisons - char *t2 = infer_type(ctx, node->binary.right); int is_null_compare = 0; if (node->binary.right->type == NODE_EXPR_VAR && strcmp(node->binary.right->var_ref.name, "NULL") == 0) @@ -299,8 +291,15 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) is_null_compare = 1; } - if (!is_null_compare && strcmp(t1, "string") == 0 && t2 && - strcmp(t2, "string") == 0) + if (is_null_compare) + { + fprintf(out, "("); + codegen_expression(ctx, node->binary.left, out); + fprintf(out, " %s ", node->binary.op); + codegen_expression(ctx, node->binary.right, out); + fprintf(out, ")"); + } + else { fprintf(out, "(strcmp("); codegen_expression(ctx, node->binary.left, out); @@ -315,19 +314,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) fprintf(out, ") != 0)"); } } - else - { - // Direct pointer comparison - fprintf(out, "("); - codegen_expression(ctx, node->binary.left, out); - fprintf(out, " %s ", node->binary.op); - codegen_expression(ctx, node->binary.right, out); - fprintf(out, ")"); - } - if (t2) - { - free(t2); - } } else { @@ -337,6 +323,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) codegen_expression(ctx, node->binary.right, out); fprintf(out, ")"); } + if (t1) free(t1); } else { @@ -394,14 +381,13 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } // Check for Static Enum Variant Call: Enum.Variant(...) - if (target->type == NODE_EXPR_VAR) { ASTNode *def = find_struct_def(ctx, target->var_ref.name); if (def && def->type == NODE_ENUM) { char mangled[256]; - sprintf(mangled, "%s_%s", target->var_ref.name, method); + snprintf(mangled, sizeof(mangled), "%s_%s", target->var_ref.name, method); FuncSig *sig = find_func(ctx, mangled); if (sig) { @@ -410,15 +396,13 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) int arg_idx = 0; while (arg) { - if (arg_idx > 0 && arg) + if (arg_idx > 0) { fprintf(out, ", "); } - Type *param_t = - (arg_idx < sig->total_args) ? sig->arg_types[arg_idx] : NULL; + Type *param_t = (arg_idx < sig->total_args) ? sig->arg_types[arg_idx] : NULL; - // Tuple Packing Logic if (param_t && param_t->kind == TYPE_STRUCT && strncmp(param_t->name, "Tuple_", 6) == 0 && sig->total_args == 1 && node->call.arg_count > 1) @@ -436,7 +420,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) arg = arg->next; } fprintf(out, "}"); - break; // All args consumed + break; } codegen_expression(ctx, arg, out); @@ -456,7 +440,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) char *ptr = strchr(clean, '*'); if (ptr) { - *ptr = 0; + *ptr = '\0'; } char *base = clean; @@ -518,33 +502,30 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) int need_cast = 0; char mixin_func_name[128]; - sprintf(mixin_func_name, "%s__%s", call_base, method); + snprintf(mixin_func_name, sizeof(mixin_func_name), "%s__%s", call_base, method); char *resolved_method_suffix = NULL; if (!find_func(ctx, mixin_func_name)) { - // Try resolving as a trait method: Struct__Trait_Method StructRef *ref = ctx->parsed_impls_list; while (ref) { - if (ref->node && ref->node->type == NODE_IMPL_TRAIT) + if (ref->node && ref->node->type == NODE_IMPL_TRAIT && + strcmp(ref->node->impl_trait.target_type, base) == 0) { - if (strcmp(ref->node->impl_trait.target_type, base) == 0) + char trait_mangled[256]; + snprintf(trait_mangled, sizeof(trait_mangled), "%s__%s_%s", base, + ref->node->impl_trait.trait_name, method); + if (find_func(ctx, trait_mangled)) { - char trait_mangled[256]; - sprintf(trait_mangled, "%s__%s_%s", base, - ref->node->impl_trait.trait_name, method); - if (find_func(ctx, trait_mangled)) - { - char *suffix = - xmalloc(strlen(ref->node->impl_trait.trait_name) + - strlen(method) + 2); - sprintf(suffix, "%s_%s", ref->node->impl_trait.trait_name, - method); - resolved_method_suffix = suffix; - break; - } + size_t suffix_len = strlen(ref->node->impl_trait.trait_name) + + strlen(method) + 2; + char *suffix = xmalloc(suffix_len); + snprintf(suffix, suffix_len, "%s_%s", + ref->node->impl_trait.trait_name, method); + resolved_method_suffix = suffix; + break; } } ref = ref->next; @@ -559,15 +540,14 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) if (it->impl_node && it->impl_node->type == NODE_IMPL_TRAIT) { tname = it->impl_node->impl_trait.trait_name; - } - if (tname) - { char trait_mangled[512]; - sprintf(trait_mangled, "%s__%s_%s", base, tname, method); + snprintf(trait_mangled, sizeof(trait_mangled), + "%s__%s_%s", base, tname, method); if (find_func(ctx, trait_mangled)) { - char *suffix = xmalloc(strlen(tname) + strlen(method) + 2); - sprintf(suffix, "%s_%s", tname, method); + size_t suffix_len = strlen(tname) + strlen(method) + 2; + char *suffix = xmalloc(suffix_len); + snprintf(suffix, suffix_len, "%s_%s", tname, method); resolved_method_suffix = suffix; break; } @@ -582,15 +562,14 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } else { - // Method not found on primary struct, check mixins ASTNode *def = find_struct_def(ctx, base); if (def && def->type == NODE_STRUCT && def->strct.used_structs) { for (int k = 0; k < def->strct.used_struct_count; k++) { char mixin_check[128]; - sprintf(mixin_check, "%s__%s", def->strct.used_structs[k], - method); + snprintf(mixin_check, sizeof(mixin_check), "%s__%s", + def->strct.used_structs[k], method); if (find_func(ctx, mixin_check)) { call_base = def->strct.used_structs[k]; @@ -620,11 +599,19 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) arg = arg->next; } fprintf(out, ")"); + + if (resolved_method_suffix) + { + free(resolved_method_suffix); + } } free(clean); + free(type); return; } + if (type) free(type); } + if (node->call.callee->type == NODE_EXPR_VAR) { ASTNode *def = find_struct_def(ctx, node->call.callee->var_ref.name); @@ -680,26 +667,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) if (node->call.arg_names && node->call.callee->type == NODE_EXPR_VAR) { - char *fn_name = node->call.callee->var_ref.name; - FuncSig *sig = find_func(ctx, fn_name); - - if (sig && sig->arg_types) - { - for (int p = 0; p < sig->total_args; p++) - { - ASTNode *arg = node->call.args; - - for (int i = 0; i < node->call.arg_count && arg; i++, arg = arg->next) - { - if (node->call.arg_names[i] && p < node->call.arg_count) - { - - // For now, emit in order provided... - } - } - } - } - ASTNode *arg = node->call.args; int first = 1; while (arg) @@ -746,11 +713,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) strncmp(param_t->name, "Tuple_", 6) == 0 && sig->total_args == 1 && node->call.arg_count > 1) { - // Implicit Tuple Packing: - // Function expects 1 Tuple argument, but call has multiple args -> Pack - // them fprintf(out, "(%s){", param_t->name); - ASTNode *curr = arg; int first_field = 1; while (curr) @@ -765,8 +728,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } fprintf(out, "}"); handled = 1; - - // Advance main loop iterator to end arg = NULL; } } @@ -775,7 +736,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { if (arg == NULL) { - break; // Tuple packed all args + break; } } else @@ -800,16 +761,12 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) case NODE_EXPR_MEMBER: if (strcmp(node->member.field, "len") == 0) { - if (node->member.target->type_info) + if (node->member.target->type_info && + node->member.target->type_info->kind == TYPE_ARRAY && + node->member.target->type_info->array_size > 0) { - if (node->member.target->type_info->kind == TYPE_ARRAY) - { - if (node->member.target->type_info->array_size > 0) - { - fprintf(out, "%d", node->member.target->type_info->array_size); - break; - } - } + fprintf(out, "%d", node->member.target->type_info->array_size); + break; } } @@ -831,26 +788,15 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } else { - if (node->member.target->type == NODE_EXPR_CAST) - { - fprintf(out, "("); - } codegen_expression(ctx, node->member.target, out); - if (node->member.target->type == NODE_EXPR_CAST) - { - fprintf(out, ")"); - } - // Verify actual type instead of trusting is_pointer_access flag char *lt = infer_type(ctx, node->member.target); int actually_ptr = 0; if (lt && (lt[strlen(lt) - 1] == '*' || strstr(lt, "*"))) { actually_ptr = 1; } - if (lt) - { - free(lt); - } + if (lt) free(lt); + char *field = node->member.field; if (field && field[0] >= '0' && field[0] <= '9') { @@ -873,7 +819,8 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) is_slice_struct = 1; } } - if (node->index.array->resolved_type) + + if (!is_slice_struct && node->index.array->resolved_type) { if (strncmp(node->index.array->resolved_type, "Slice_", 6) == 0) { @@ -888,10 +835,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { is_slice_struct = 1; } - if (inferred) - { - free(inferred); - } + if (inferred) free(inferred); } if (is_slice_struct) @@ -990,7 +934,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } else { - fprintf(out, "/* UNSAFE: Full Slice on unknown size */ 0; "); + fprintf(out, "0; "); } } @@ -1006,6 +950,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) fprintf(out, "(Slice_%s){ .data = _arr + _start, .len = _len, .cap = _len }; })", tname); } + if (tname && strcmp(tname, "unknown") != 0) free(tname); break; } case NODE_BLOCK: @@ -1096,9 +1041,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) break; case NODE_PLUGIN: { - // Plugin registry - declare external plugins ZPlugin *found = zptr_find_plugin(node->plugin_stmt.plugin_name); - if (found) { ZApi api = {.filename = g_current_filename ? g_current_filename : "input.zc", @@ -1211,20 +1154,19 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { Type *t = node->reflection.target_type; if (node->reflection.kind == 0) - { // @type_name + { char *s = codegen_type_to_string(t); fprintf(out, "\"%s\"", s); free(s); } else - { // @fields + { if (t->kind != TYPE_STRUCT || !t->name) { fprintf(out, "((void*)0)"); break; } char *sname = t->name; - // Find definition ASTNode *def = find_struct_def(ctx, sname); if (!def) { -- cgit v1.2.3 From 91ed9fdd65e09bd6cd32e44dd07c390f2cf79c22 Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Sat, 31 Jan 2026 17:06:50 +0000 Subject: Fix codegen regressions: casting precedence and process segfault --- src/codegen/codegen.c | 16 ++++++++++++++-- src/codegen/codegen_decl.c | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index 53373e9..37415c2 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -290,6 +290,18 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { is_null_compare = 1; } + else if (node->binary.right->type == NODE_EXPR_LITERAL && + node->binary.right->literal.type_kind == LITERAL_INT && + node->binary.right->literal.int_val == 0) + { + is_null_compare = 1; + } + else if (node->binary.left->type == NODE_EXPR_LITERAL && + node->binary.left->literal.type_kind == LITERAL_INT && + node->binary.left->literal.int_val == 0) + { + is_null_compare = 1; + } if (is_null_compare) { @@ -1121,9 +1133,9 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) break; } case NODE_EXPR_CAST: - fprintf(out, "(%s)(", node->cast.target_type); + fprintf(out, "((%s)(", node->cast.target_type); codegen_expression(ctx, node->cast.expr, out); - fprintf(out, ")"); + fprintf(out, "))"); break; case NODE_EXPR_SIZEOF: if (node->size_of.target_type) diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c index 9d23617..31bd2ee 100644 --- a/src/codegen/codegen_decl.c +++ b/src/codegen/codegen_decl.c @@ -50,6 +50,7 @@ void emit_preamble(ParserContext *ctx, FILE *out) else { // Standard hosted preamble. + fputs("#define _GNU_SOURCE\n", out); fputs("#include \n#include \n#include " "\n#include \n", out); -- cgit v1.2.3 From 705733a604a39e9f2b0e21ad77a3dbb97a2019a2 Mon Sep 17 00:00:00 2001 From: czjstmax Date: Sat, 31 Jan 2026 20:10:20 +0100 Subject: Italian translation - Added italian translation (`README_IT.md`) for the documentation. - Added links for the italian translation in the other documents. (`README.md` and `README_*.md`) Signed-off-by: czjstmax --- README.md | 2 +- README_ES.md | 2 +- README_IT.md | 1439 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ README_ZH_CN.md | 2 +- README_ZH_TW.md | 2 +- 5 files changed, 1443 insertions(+), 4 deletions(-) create mode 100644 README_IT.md diff --git a/README.md b/README.md index bf4962e..786e32c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@
-[English](README.md) • [简体中文](README_ZH_CN.md) • [繁體中文](README_ZH_TW.md) • [Español](README_ES.md) +[English](README.md) • [简体中文](README_ZH_CN.md) • [繁體中文](README_ZH_TW.md) • [Español](README_ES.md) • [Italiano](README_IT.md)
diff --git a/README_ES.md b/README_ES.md index d73e9ca..4f1e4be 100644 --- a/README_ES.md +++ b/README_ES.md @@ -1,7 +1,7 @@
-[English](README.md) • [简体中文](README_ZH_CN.md) • [繁體中文](README_ZH_TW.md) • [Español](README_ES.md) +[English](README.md) • [简体中文](README_ZH_CN.md) • [繁體中文](README_ZH_TW.md) • [Español](README_ES.md) • [Italiano](README_IT.md)
diff --git a/README_IT.md b/README_IT.md new file mode 100644 index 0000000..9b48e62 --- /dev/null +++ b/README_IT.md @@ -0,0 +1,1439 @@ + +
+ +[English](README.md) • [简体中文](README_ZH_CN.md) • [繁體中文](README_ZH_TW.md) • [Español](README_ES.md) • [Italiano](README_IT.md) + +
+ +
+ +# Zen C + +**Ergonomia Moderna. Zero Overhead. C puro.** + +[![Stato Build](https://img.shields.io/badge/build-passing-brightgreen)]() +[![Licenza](https://img.shields.io/badge/license-MIT-blue)]() +[![Versione](https://img.shields.io/badge/version-0.1.0-orange)]() +[![Piattaforma](https://img.shields.io/badge/platform-linux-lightgrey)]() + +*Comodità di un linguaggio ad alto livello, veloce come il C* + +
+ +--- + +## Panoramica + +**Zen C** è un linguaggio di programmazione di sistemi moderno che genera codice `GNU C`/`C11`. Fornisce allo sviluppatore un ricco set di funzionalità, tra cui inferenza di tipo, pattern matcing, generici, tratti, async/await, e gestione manuale della memoria con funzionalità RAII, mantenendo al contempo una compatibilità al 100% con l'ABI C + +## Community + +Unisciti alla conversazione, condividi delle demo, fai comande o segnala dei bug nel server ufficiale Discord Zen C + +- Discord: [Unisciti qui](https://discord.com/invite/q6wEsCmkJP) + +--- + +## Indice + +- [Panoramica](#panoramica) +- [Community](#community) +- [Guida Rapida](#guida-rapida) + - [Installazione](#installazione) + - [Utilizzo](#utilizzo) + - [Variabili d'ambiente](#variabili-d-ambiente) +- [Riferimenti Del Linguaggio](#riferimenti-del-linguaggio) + - [1. Variabili e Costanti](#1-variabili-e-costanti) + - [2. Tipi Primitivi](#2-tipi-primitivi) + - [3. Tipi Aggregati](#3-tipi-aggregati) + - [Array](#array) + - [Tuple](#tuple) + - [Struct](#struct) + - [Struct Opachi](#struct-opachi) + - [Enum](#enum) + - [Unioni](#unioni) + - [Alias del tipo](#alias-del-tipo) + - [Alias del tipo opachi](#alias-del-tipo-opachi) + - [4. Funzioni e Lambda](#4-funzioni-e-lambda) + - [Funzioni](#funzioni) + - [Argomenti Costanti](#argomenti-costanti) + - [Argomenti di default](#argomenti-di-default) + - [Lambda (Closure)](#lambda-closure) + - [Puntatori-Funzione Grezzi](#puntatori-funzione-grezzi) + - [Argomenti Variadici](#argomenti-variadici) + - [5. Controllo di Flusso](#5-controllo-di-flusso) + - [Condizionali](#condizionali) + - [Pattern Matching](#pattern-matching) + - [Loop](#loop) + - [Controllo Avanzato](#controllo-avanzato) + - [6. Operators](#6-operatori) + - [Operatori Overload-abili](#operatori-overload-abili) + - [Zucchero Sintattico](#zucchero-sintattico) + - [7. Stampaggio e Interpolazione delle Stringhe](#7-stampaggio-e-interpolazione-delle-stringhe) + - [Keyword](#keyword) + - [Scorciatoie](#scorciatoie) + - [Interpolazione delle Stringhe (F-strings)](#interpolazione-delle-stringhe-f-strings) + - [Prompt di Input (`?`)](#prompt-di-input) + - [8. Gestione della memoria](#8-gestione-della-memoria) + - [Rimando](#rimando) + - [Liberazione-automatica](#liberazione-automatica) + - [Semantiche delle risorse (Muovi di default)](#semantiche-delle-risorse-muovi-di-default) + - [RAII / Rilascio Tratti](#raii--rilascio-tratti) + - [9. Programmazione Orientata a Oggetti](#9-programmazione-orientata-a-oggetti) + - [Metodi](#metodi) + - [Tratti](#tratti) + - [Tratti Standard](#tratti-standard) + - [Composizione](#composizione) + - [10. Generici](#10-generici) + - [11. Concorrenza Asincrona (Async/Await)](#11-concorrenza-asincrona-asyncawait) + - [12. Metaprogramming](#12-metaprogramming) + - [Comptime](#comptime) + - [Incorporati](#incorporati) + - [Plugin](#plugin) + - [Macro C Generiche](#macro-c-generiche) + - [13. Attributi](#13-attributi) + - [Attributi Personalizzati](#attributi-personalizzati) + - [Mappatura della Sintassi](#mappatura-della-sintassi) + - [Derivazioni Intelligenti](#derivazioni-intelligenti) + - [14. Assembly Inline](#14-assembly-inline) + - [Utilizzo Base](#utilizzo-base) + - [Volatile](#volatile) + - [Vincoli Nominati](#vincoli-nominati) + - [15. Direttive della Buil](#15-direttive-della-build) + - [16. Keyword](#16-keyword) +- [Libreria Standard](#liberia-standard) +- [Tooling](#tooling) + - [Language Server (LSP)](#language-server-lsp) + - [REPL](#repl) +- [Supporto del Compilatore e Compatibilità](#supporto-del-compilatore-e-compatibilità) + - [Stato della suite di test](#stato-della-suite-di-test) + - [Buildare con Zig](#buildare-con-zig) + - [Interop C++](#interop-c) + - [Interop CUDA](#interop-cuda) + - [Interop Objective-C](#interop-objective-c) + - [Supporto ISO C23](#supporto-iso-c23) +- [Contribuisci](#contribuisci) +- [Attribuzioni](#attribuzioni) + +--- + +## Guida Rapida + +### Installazione + +```bash +git clone https://github.com/z-libs/Zen-C.git +cd Zen-C +make +sudo make install +``` + +### Build Portatile (APE) + +Il codice Zen C può come un **Actually Portable Executable (APE)** (lett. _Eseguibile Effetivamente Portatile_) utilizzando la [Cosmopolitan Libc](https://github.com/jart/cosmopolitan). Ciò produrrà un singolo eseguibile (`.com`) che potrà essere eseguito nativamente su Linux, macOS, Windows, FreeBSD, OpenBSD e NetBSD sia sulle architetture x86_64 e aarch64. + +**Prerequisiti:** +- Strumenti `cosmocc` (deve trovarsi nella tua PATH) + +**Builda e Installa:** +```bash +make ape +sudo env "PATH=$PATH" make install-ape +``` + +**Artefatti:** +- `out/bin/zc.com`: Il compilatore Zen-C portatile. Inlude la libreria standard, incorporata nell'eseguibile. +- `out/bin/zc-boot.com`: Un installer bootstrap auto-contenuto per configurare nuovi progetti Zen-C rapidamente. + +**Usage:** +```bash +# Eseguibile su qualunque OS supportato +./out/bin/zc.com build hello.zc -o hello +``` + +### Utilizzo + +```bash +# Compila e avvia +zc run hello.zc + +# Builda eseguibile +zc build hello.zc -o hello + +# Shell interattiva +zc repl +``` + +### Variabili d'ambiente + +Puoi impostare `ZC_ROOT` per specificare la posizione della Libreria Standard (per inclusioni standard come `import "std/vector.zc"`). Ciò ti permetterà di eseguire il comando `zc` da qualsiasi directory. + +```bash +export ZC_ROOT=/path/to/Zen-C +``` + +--- + +## Riferimenti Del Linguaggio + +### 1. Variabili e Costanti + +Zen C differenzia le costanti al tempo di compilazione e le varaibili di esecuzione. + +#### Manifest Constants (`def`) +#### Costanti Manifesto (`def`) +Valori che esistono solo durante la compilazione (integrate nel codice). Utilizzale per le grandezze degli array, configurazioni fisse, e numeri magici. + +```zc +def MAX_SIZE = 1024; +let buffer: char[MAX_SIZE]; // Grandezza valida per l'array +``` + +#### Variabili (`let`) +Locazioni di memoria. Possono essere mutabili o di sola lettura (`const`). + +```zc +let x = 10; // Mutabile +x = 20; // OK + +let y: const int = 10; // Sola lettura (Tipo qualificato) +// y = 20; // Errore: impossibile assegnare un valore ad una variabile costante +``` + +> **Inferenza di tipo**: Zen C inferisce automaticamente il tipo per le variabili inizializzate. Compilando ciò alla keyword `auto` dello standard C23 nei compilatori supportati, oppure alla estensione GCC `__auto_type`. + +### 2. Tipi Primitivi + +| Tipo | C Equivalent | Descrizione | +|:---|:---|:---| +| `int`, `uint` | `int`, `unsigned int` | Intero standard della piattaforma | +| `I8` .. `I128` or `i8` .. `i128` | `int8_t` .. `__int128_t` | Interi a grandezza fissa con segno | +| `U8` .. `U128` or `u8` .. `u128` | `uint8_t` .. `__uint128_t` | Interi a grandezza fissa senza segno | +| `isize`, `usize` | `ptrdiff_t`, `size_t` | Interi con grandezza di un puntatore | +| `byte` | `uint8_t` | Alias per U8 | +| `F32`, `F64` or `f32`, `f64` | `float`, `double` | Numeri con parte decimale | +| `bool` | `bool` | `true` (lett. _vero_) o `false` (lett. _falso_) | +| `char` | `char` | Carattere singolo | +| `string` | `char*` | Stringhe C terminate da NULL | +| `U0`, `u0`, `void` | `void` | Tipo vuoto | +| `iN` (Per esempio, `i256`) | `_BitInt(N)` | Intero con segno a larghezza arbitraria di bit (C23) | +| `uN` (Per esempio, `u42`) | `unsigned _BitInt(N)` | Intero senza segno a larghezza arbitraria di bit (C23) | + +### 3. Tipi Aggregati + +#### Array +Array a lunghezza fissa con valori arbitrari. +```zc +def GRANDEZZA = 5; +let interi: int[GRANDEZZA] = [1, 2, 3, 4, 5]; +let zeri: [int; GRANDEZZA]; // Inizializzato a zero +``` + +#### Tuple +Valori molteplici raggruppati assieme, accesso agli elementi indicizzato. +```zc +let paio = (1, "Ciao!"); +let x = paio.0; // 1 +let s = paio.1; // "Ciao!" +``` + +**Molteplici Valori di Ritorno** + +Le funzioni posso restituire delle tuple per fornire diversi risultati: +```zc +fn somma_e_differenza(a: int, b: int) -> (int, int) { + return (a + b, a - b); +} + +let risultato = somma_e_differenza(3, 2); +let somma = risultato.0; // 5 +let differenza = risultato.1; // 1 +``` + +**Separazione** + +Le tuple possono essere separate irettamente in variabili singole. +```zc +let (somma, differenza) = somma_e_differenza(3, 2); +// somma = 5, differenza = 1 +``` + +#### Structs +Strutture dati con campi di bit opzionali. +```zc +struct Punto { + x: int; + y: int; +} + +// Inizializzazione struct +let p = Punto { x: 10, y: 20 }; + +// Campi di bit +struct Flags { + valido: U8 : 1; + modalità: U8 : 3; +} +``` + +> **Nota**: Gli struct usano le [Semantiche di Spostatmento](#semantiche-di-movimento--copia-sicura) di default. I campi di uno struct possono essere acceduti via `.` anche sui puntatori (Dereferenza-Automatica). + +#### Struct Opachi +Puoi definire uno struct come `opaque` (lett. _opaco_) per restringere l'accesso ai suoi campi al modulo che lo ha definito, permettendo comunque l'allocazione sullo stack dello struct (la grandezza è data). + +```zc +// In utente.zc +opaque struct Utente { + id: int; + nome: string; +} + +fn nuovo_utente(nome: string) -> Utente { + return Utente{id: 1, nome: nome}; // OK: Dentro il modulo +} + +// In main.zc +import "utente.zc"; + +fn main() { + let u = nuovo_utente("Alice"); + // let id = u.id; // Error: Impossibile accedere al campo privato 'id' +} +``` + +#### Enum +Unioni taggate (tipi somma) capaci di contenere dati. +```zc +enum Forma { + Cerchio(float), // Contiene il raggio + Rettangolo(float, float), // Contiene la larghezza e l'altezza + Punto // Non contiene dati +} +``` + +#### Unioni +Unioni standard C (accesso non sicuro). +```zc +union Dati { + i: int; + f: float; +} +``` + +#### Alias del tipo +Crea un alias per un tipo già esistente. +```zc +alias ID = int; +alias PuntoDellaMappa = Mappa; +``` + +#### Opaque Type Aliases +#### Alias del tipo opachi +Puoi definire un alias del tipo come `opaque` (lett. _opaco_) per creare un nuovo tipo che si distingue dal suo tipo sottostante al di fuori del modulo che l'ha definito. Questo permette una forte incapsulamento e sicurezza dei tipi senza overhead extra durante l'esecuzione di un wrapper struct. + +```zc +// In libreria.zc +opaque alias Handle = int; + +fn crea_handle(v: int) -> Handle { + return v; // Conversione implicita consentita all'interno del modulo +} + +// In main.zc +import "libreria.zc"; + +fn main() { + let h: Handle = crea_handle(42); + // let i: int = h; // Errore: Validazione del tipo fallita + // let h2: Handle = 10; // Errore: Validazione del tipo fallita +} +``` + +### 4. Functions & Lambdas + +#### Funzioni +```zc +fn somma(a: int, b: int) -> int { + return a + b; +} + +// Supporto per argomenti nominati nelle chiamate +somma(a: 10, b: 20); +``` + +> **Nota**: Gli argomenti nominati devono seguire rigorosamente l'ordine predefinito dei parametri. `somma(b: 20, a: 10)` è errato. + +#### Argomenti Costanti +Gli argomenti di una funzione possono essere marcati come `const` (lett. _costanti_) per reinforzare semantiche di sola lettura. Questo è un qualificatore del tipo, non una costante esplicita. + +```zc +fn stampa_valore(v: const int) { + // v = 10; // Errore: Impossibile assegnare un valore ad una variabile costante + println "{v}"; +} +``` + +#### Argomenti di default +Le funzioni posso definire dei valori default per gli argomenti in caso che questi non vengano specificati durante la chiamata. Questi valori possono essere letterali, espressioni, o codice Zen C valido (come il costruttore di uno struct). +```zc +// Valore default semplice +fn incrementa(val: int, quantità: int = 1) -> int { + return val + quantità; +} + +// Expression default value (evaluated at call site) +// Espressione come valore default (calcolato) +fn offset(val: int, pad: int = 10 * 2) -> int { + return val + pad; +} + +// Struct come valore default +struct Config { debug: bool; } +fn init(cfg: Config = Config { debug: true }) { + if cfg.debug { println "Modalità Debug"; } +} + +fn main() { + incrementa(10); // 11 + offset(5); // 25 + init(); // Stampa "Modalità Debug" +} +``` + +#### Lambdas (Closures) +#### Lambda (Closure) +Funzioni anonime che possono catturare il loro ambiente. +```zc +let fattore = 2; +let double = x -> x * fattore; // Sintassi con freccia +let pieno = fn(x: int) -> int { return x * fattore; }; // Sintassi a blocco +``` + +#### Puntatori-Funzione grezzi +Zen C supporta i puntatori-funzione grezzi utilizzando la sintassi `fn*`. Questo permette un'interoperabilità fluida con le librerie C che si aspettano puntatori-funzione senza overhead di closure. + +```zc +// Funzione che prende un puntatore-funzione grezzo +fn imposta_callback(cb: fn*(int)) { + cb(42); +} + +// Funzione che restituisce un puntatore-funzione grezzo +fn ottieni_callback() -> fn*(int) { + return il_mio_handler; +} + +// I puntatori a puntatori-funzione sono supportati (fn**) +let pptr: fn**(int) = &ptr; +``` + +#### Argomenti Variadici +Le funzioni possono accettare un numero variabile di argomenti utilizzando la sintassi `...` e il tipo `va_list`. +```zc +fn log(lvl: int, fmt: char*, ...) { + let ap: va_list; + va_start(ap, fmt); + vprintf(fmt, ap); // Usa lo stdio C + va_end(ap); +} +``` + +### 5. Controllo di Flusso + +#### Condizionali +```zc +if x > 10 { + print("Grande"); +} else if x > 5 { + print("Medio"); +} else { + print("Piccolo"); +} + +// Operatore ternario +let y = x > 10 ? 1 : 0; // Se x è maggiore di 10 y sarà uguale a 1, in ogni altro caso, y sarà uguale a 0 +``` + +#### Pattern Matching +Alternativa potente agli `switch`. +```zc +match val { + 1 => { print "Uno" }, + 2 || 3 => { print "Due o Tre" }, // OR logico con || + 4 or 5 => { print "Quattro or Cinque" }, // OR logico con 'or' + 6, 7, 8 => { print "Da Sei a Otto" }, // OR logico con la virgola (,) + 10 .. 15 => { print "Da 10 a 14" }, // Range Esclusivo (Legacy) + 10 ..< 15 => { print "Da 10 a 14" }, // Range Esclusivo (Esplicito) + 20 ..= 25 => { print "Da 20 a 25" }, // Range Inclusivo + _ => { print "Altro" }, +} + +// Destrutturazione degli Enums +match forma { + Forma::Cerchio(r) => println "Raggio: {r}", + Forma::Rettangolo(w, h) => println "Area: {w*h}", + Forma::Punto => println "Punto" +} +``` + +#### Associaione di riferiemnto +Per ispezionare un valore senza assumerne la proprietà (spostarlo) puoi usare la keyword `ref` nel pattern. Questo è essenziale per i tipi che implementano Semantiche di Movimento (come `Option`, `Result`, struct non-copiabile). + +```zc +let opt = Qualche(ValoreNonCopiable{...}); +match opt { + Some(ref x) => { + // 'x' è un puntatore che punta al valore contenuto in 'opt' + // 'opt' NON viene né mosso né consumato qui + println "{x.field}"; + }, + None => {} +} +``` + +#### Loops +```zc +// Range +for i in 0..10 { ... } // Esclusivo (Da 0 a 9) +for i in 0..<10 { ... } // Esclusivo (Esplicito) +for i in 0..=10 { ... } // Inclusivo (Da 0 a 10) +for i in 0..10 step 2 { ... } + +// Iteratore (Vec, Array, oppure un Iteratore personalizzato) +for item in collection { ... } + +// While (lett. mentre) +while x < 10 { ... } + +// Infinito con etichetta +esterno: loop { + if done { break esterno; } +} + +// Ripeti N volte +for _ in 0..5 { ... } +``` + +#### Controllo Avanzato +```zc +// Guard (lett. 'guardia'): Esegue il caso 'else' e ritorna se la condizione è falsa +guard ptr != NULL else { return; } + +// Unless (lett. 'a meno che'): Se non vero +unless è_valido { return; } +``` + +### 6. Operatori + +Zen C supporta l'overloading di operatori per gli struct definiti dall'utente per implementare nomi specifici di metodi. + +#### Operatori Overload-abili + +| Categoria | Operatore | Nome del Metodo | +|:---|:---|:---| +| **Aritmetico** | `+`, `-`, `*`, `/`, `%` | `add`, `sub`, `mul`, `div`, `rem` | +| **Paragone** | `==`, `!=` | `eq`, `neq` | +| | `<`, `>`, `<=`, `>=` | `lt`, `gt`, `le`, `ge` | +| **Bitwise** | `&`, `\|`, `^` | `bitand`, `bitor`, `bitxor` | +| | `<<`, `>>` | `shl`, `shr` | +| **Unari** | `-` | `neg` | +| | `!` | `not` | +| | `~` | `bitnot` | +| **Indice** | `a[i]` | `get(a, i)` | +| | `a[i] = v` | `set(a, i, v)` | + +> **Nota sull'uguaglianza delle stringhe**: +> - `string == string` performa un controllo del **valore** (equivalente a `strcmp`). +> - `char* == char*` performa un controllo dei **puntatori** (controlla gli indirizzi di memoria). +> - Paragoni misti (e.g. `string == char*`) defaulta al controllo dei **pointer**. + +**Esempio:** +```zc +impl Punto { + fn add(self, altro: Punto) -> Punto { + return Punto{x: self.x + altro.x, y: self.y + altro.y}; + } +} + +let p3 = p1 + p2; // Chiama p1.somma(p2) +``` + +#### Zucchero Sintattico + +Questi operatori sono funzionalità integrate del linguaggio e non è possibile overloadarli. + +| Operatore | Nome | Descrizione | +|:---|:---|:---| +| `\|>` | Pipeline | `x \|> f(y)` viene dezuccherato a `f(x, y)` | +| `??` | Coalescenza nulla | `val ?? default` restituisce `default` se `val` è NULL (puntatori) | +| `??=` | Assegnazione nulla | `val ??= init` assegna se `val` è NULL | +| `?.` | Safe Navigation | `ptr?.campo` accede a 'campo' solo se `ptr` non è NULL | +| `?` | Try Operator | `res?` restituisce un errore se presente (tipi Result/Option) | + +**Dereferenza Automatica**: +Pointer field access (`ptr.field`) and method calls (`ptr.method()`) automatically dereference the pointer, equivalent to `(*ptr).field`. +Accesso ai campi da un puntatore (`puntatore.campo`) e chiamate ai metodi (`puntatore.metodo()`) dereferenzano automaticamente il puntatore, ciò è equivalente a `(*puntatore).campo` + +### 7. Stampaggio e Interpolazione delle Stringhe + +Zen C fornisce opzioni versatili per stampare alla console, includendo keyword e scorciatoie coincise. + +#### Keyword + +- `print "testo"`: Stampa a `stdout` senza aggiunzione di una newline automatica. +- `println "testo"`: Stampa a `stdout` aggiungendo una newline automatica. +- `eprint "testo"`: Stampa a `stderr` senza aggiunzione di una newline automatica. +- `eprintln "testo"`: Stampa a `stderr` aggiungendo una newline automatica. + +#### Scorciatoie + +Zen C ti permette di utilizzare stringhe letterali direttamente come istruzione di stampaggio veloce: + +- `"Ciao Mondo!"`: Equivalente a `println "Ciao Mondo!"`. (Aggiunge una newline implicitamente) +- `"Ciao Mondo!"..`: Equivalente a `print "Ciao Mondo!"`. (Non aggiunge una newline) +- `!"Errore"`: Equivalente a `eprintln "Errore"`. (Output a stderr) +- `!"Errore"..`: Equivalente a `eprint "Errore"`. (Output a stderr, senza newline) + +#### Interpolazione delle Stringhe (F-strings) + +Puoi incorporare espressioni direttamente all'interno di stringhe letterali utilizzando la sintassi `{}`. Questo funziona con tutti i metodi di stampaggio, incluse le scorciatoie. + +```zc +let x = 42; +let nome = "Max"; +println "Valore: {x}, Nome: {name}"; +"Valore: {x}, Nome: {name}"; // scorciatoia per println +``` + +#### Prompt di Input (`?`) + +Zen C supporta una scorciatoia per richiedere input dall'utente utilizzando il prefisso `?`. + +- `? "Inserisci il tuo nome"`: Stampa il prompt (senza newline) e aspetta per dell'input (legge una linea). +- `? "Inserisci la tua età: " (età)`: Stampa il prompt e memorizza l'input nella variabile `età`. + - Gli specificatori del formato vengono automaticamente inferiti in base al tipo della variabile. + +```zc +let età: int; +? "Inserisci la tua età: " (età); +println "Hai {età} anni."; +``` + +### 8. Gestione della memoria + +Zen C permette una gestione manuale della memoria con aiuti ergonomici. + +#### Rimando +Esegui il codice quando l’ambito corrente termina. Le istruzioni defer vengono eseguite in ordine LIFO (last-in, first-out). +```zc +let f = fopen("file.txt", "r"); +defer fclose(f); +``` + +> Per prevenire comportamenti indefiniti, le istruzioni del controllo di flusso (`return`, `break`, `continue`, `goto`) **non sono ammesse** dentro un blocco `defer`. + +#### Liberazione automatica +Libera automaticamente la memoria occupata dalla variabile quando l'ambito corrente termina. +```zc +autofree let tipi = malloc(1024); +``` + +#### Semantiche delle risorse (Muovi di Default) +Zen C tratta i tipi con distruttori (come `File`, `Vec`, o puntatori allocati manualmente con `malloc`) come **Risorse**. Per prevenire errori di doppia-liberazione, le risorse non possono essere implicitamente duplicate. + +- **Muovi di Default**: Assegnare una risorsa variabile ne trasferisce il proprietario. La variabile originale diventa invalida (Spostata). +- **Tipi di Copia**: Tipi senza distruttori possono opzionalmente avere un comportamento `Copy`, rendendo l'assegnazione una duplicazione. + +**Diagnostica & Filosofia**: +Se vedi un errore "Utilizzo di una variabile spostata", il compilatore ti sta dicendo: *"Questo tipo è proprietario di una risorsa (come memoria o un handle) e copiarlo ciecamente non è sicuro."* + +> **Contrasto:** Al contrario di come fanno C/C++, Zen C non duplica implicitamente i valori che posseggono risorse. + +**Argomento di una funzione**: +Passare un valore ad una funzione segue le stesse regole dell'assegnazione: le risorse vengono spostate se non passate per referenza. + +```zc +fn processo(r: Risorsa) { ... } // 'r' viene spostato nella funzione +fn peek(r: Risorsa*) { ... } // 'r' viene preso in prestito (referenza) +``` + +**Clonazione Esplicita**: +Se *vuoi* avere più copie di una risorsas, rendilo esplicito: + +```zc +let b = a.clona(); // Chiama il metodo `clona` dal tratto `Clone` +``` + +**Duplicazione opt-in (Tipi dei valori)**: +Per tipi piccoli senza distruttore: + +```zc +struct Punto { x: int; y: int; } +impl Copy for Punto {} // Opt-in per la duplicazione implicita + +fn main() { + let p1 = Point { x: 1, y: 2 }; + let p2 = p1; // Copiato. p1 rimane valido. +} +``` + +#### RAII / Rilascio Tratti +Implementa `Drop` per una logica di pulizia automatica. +```zc +impl Drop for MioStruct { + fn drop(self) { + self.free(); + } +} +``` + +### 9. Programmazione Orientata a Oggetti + +#### Metodi +Definisci metodi sui tipi utilizziando `impl`. +```zc +impl Punto { + // Metodo statico (convenzione del costruttore) + fn nuovo(x: int, y: int) -> Self { + return Point{x: x, y: y}; + } + + // Metodo d'instanza + fn dist(self) -> float { + return sqrt(self.x * self.x + self.y * self.y); + } +} +``` + +#### Tratti +Definisci un comportamento condiviso. +```zc +struct Cerchio { raggio: f32; } + +trait Disegnabile { + fn disegna(self); +} + +impl Disegna for Cerchio { + fn disegna(self) { ... } +} + +let cerchio = Cerchio{}; +let disegnabile: Disegnabile = &cerchio; +``` + +#### Tratti Standard +Zen C include dei tratti standard che si integrano con la sintassi del linguaggio. + +**Iterable** (lett. _Iterabile_) + +Implementa `Iterable` per abilitare loop `for-in` (lett. _per in_) nei tuoi tipi personalizzati. + +```zc +import "std/iter.zc" + +// Definisci un Iteratore +struct MioIteratore { + curr: int; + stop: int; +} + +impl MioIteratore { + fn next(self) -> Option { + if self.curr < self.stop { + self.curr += 1; + return Option::Some(self.curr - 1); + } + return Option::None(); + } +} + +// Implementa Iterable +impl MioRange { + fn iterator(self) -> MioIteratore { + return MioIteratore{curr: self.start, stop: self.end}; + } +} + +// Usalo in un loop +for i in mio_range { + println "{i}"; +} +``` + +**Drop** (lett. _rilascia_) + +Implementa `Drop` per definire un distruttore che esegue quando l'oggetto va fuori ambito (RAII). + +```zc +import "std/mem.zc" + +struct Risorsa { + ptr: void*; +} + +impl Drop for Risorsa { + fn drop(self) { + if self.ptr != NULL { + free(self.ptr); + } + } +} +``` + +> **Nota:** Se una variabile viene spostata, `drop` NON verrà chiamato sulla variabile originale. Aderisce alle [Semantiche delle Risorse](#semantiche-delle-risorse) + +**Copy** (lett. _copia_) + +Tratto marcatore opt-in per il comportamento `Copy` (duplicazione implicita) al posto delle semantiche Move. Utilizzato tramite `@derive(Copy)` + +> **Regola:** I tipi che implementano `Copy` non dovrà definire un distruttore (`Drop`). + +```zc +@derive(Copy) +struct Punto { x: int; y: int; } + +fn main() { + let p1 = Punto{x: 1, y: 2}; + let p2 = p1; // Copiato! p1 rimane valido. +} +``` + +**Clone** (lett. _clona_) + +Implementa `Clone` per permettere la duplicazione esplicita di tipi che posseggono risorse. + +```zc +import "std/mem.zc" + +struct Scatola { val: int; } + +impl Clone for Scatola { + fn clone(self) -> Scatola { + return Scatola{val: self.val}; + } +} + +fn main() { + let b1 = Scatola{val: 42}; + let b2 = b1.clone(); // Explicit copy +} +``` + +#### Composizione +Usa `use` per incorporare altri struct. Puoi mischiarli (campi piatti) o nominarli (campi nidificato). + +```zc +struct Entità { id: int; } + +struct Giocatore { + // Mischiati (Non nominati): Campi piatti + use Entità; // Aggiunge 'id' a 'Giocatore' direttamente + nome: string; +} + +struct Partita { + // Composizione (Nominati): Campi nidificati + use p1: Giocatore; // Vi si accede tramite partita.p1 + use p2: Giocatore; // Vi si accede tramite partita.p2 +} +``` + +### 11. Generici + +Template type-safe per struct e funzioni. + +```zc +// Struct Generico +struct Scatola { + oggetto: T; +} + +// Funzione Generica +fn identità(valore: T) -> T { + return valore; +} + +// Generici Multi-parametro +struct Paio { + chiavi: K; + valore: V; +} +``` + +### 11. Concorreza Asincrona (Async/Await) + +Costruito sui pthreads. + +```zc +async fn ottieni_dati() -> string { + // Esegue in background + return "Dati"; +} + +fn main() { + let futuro = ottieni_dati(); + let risultato = await futuro; // (lett. 'aspetta') +} +``` + +### 12. Metaprogramming + +#### Comptime +Esegui codice al tempo di compilazione per generare sorgente o stampare messaggi. +```zc +comptime { + // Genera codice al tempo di compilazione (scritto a stdout) + println "let data_della_build = \"2024-01-01\";"; +} + +println "Data della build: {data_della_build}"; +``` + +#### Incorporati +Incorpora file come tipi specificati. +```zc +// Default (Slice_char) +let data = embed "assets/logo.png"; + +// Incorporazioni tipizzate +let testo = embed "shader.glsl" as string; // Incorpora come una stringa C +let rom = embed "bios.bin" as u8[1024]; // Incorpora come un array a dimensione fissa +let wav = embed "sound.wav" as u8[]; // Incorpora come Slice_u8 +``` + +#### Plugin +Importa plugin del compilatore per estendere la sintassi. +```zc +import plugin "regex" +let re = regex! { ^[a-z]+$ }; +``` + +#### Macro C Generiche +Passa delle macro del preprocessore C. + +> **Consiglio**: Per delle semplici costanti, utilizza `def`. Usa `#define` solo quanto ti servono macro del preprocessore C o flag di compilazione condizionale. + +```zc +#define BUFFER_MASSIMO 1024 +``` + +### 13. Attributti + +Decora le funzioni e gli struct per modificare il comportamento del compilatore. + +| Attributo | Ambito | Descrizione | +|:---|:---|:---| +| `@must_use` | Fn | Avvereti se il valore di ritorno viene ignorato. | +| `@deprecated("msg")` | Fn/Struct | Avverti all'uso con 'msg' | +| `@inline` | Fn | Suggerisci al compilatore di rendere il codice inline | +| `@noinline` | Fn | Previeni l'inline automatico | +| `@packed` | Struct | Rimuovi il padding (lett. _imbottitura_) automatico in mezzo ai campi. | +| `@align(N)` | Struct | Forza l'allineamento a N byte. | +| `@constructor` | Fn | Esegui prima di `main`. | +| `@destructor` | Fn | Esegue dopo la terminazione di `main`. | +| `@unused` | Fn/Var | Sopprimi gli errori di 'variabile inutilizzata' | +| `@weak` | Fn | Linking dei simboli _weak_ (lett. _debole_). | +| `@section("name")` | Fn | Inserisci il codice in una specifica sezione. | +| `@noreturn` | Fn | La funzione non restituisce valori. (e.g. `exit`). | +| `@pure` | Fn | La funzione non ha effetti collaterali (indizio per l'ottimizzazione). | +| `@cold` | Fn | La funzione è usata poco spesso (indizio per la branch prediction). | +| `@hot` | Fn | La funzione è usata molto spesso (indizio per l'ottimizzazione). | +| `@export` | Fn/Struct | Export symbol (visibility default). | +| `@global` | Fn | CUDA: Entry point del Kernel (`__global__`). | +| `@device` | Fn | CUDA: Funzione del Device (`__device__`). | +| `@host` | Fn | CUDA: Funzione dell'Host (`__host__`). | +| `@comptime` | Fn | Funzione di supporto disponibile per l'esecuzione al tempo di compilazione. | +| `@derive(...)` | Struct | Implementa automaticamente i tratti. Supporta `Debug`, `Eq` (Derivazione Intelligente), `Copy`, `Clone`. | +| `@` | Any | Passa gli attributi generici direttamente al C (e.g. `@flatten`, `@alias("nome")`) | + +#### Attributi Personalizzati + +Zen C supporta un potente sistema di **Attributi Personalizzati** che ti permettono di utilizzare ogni `__attributo__` GCC/Clang direttamente nel tuo codice Zen C. Qualsiasi attributo non riconosciuto dal compilatore Zen C viene trattato come un attributo generico e passato direttamente nel codice C generato. + +Ciò fornisce accesso a delle avanzate funzionalità, ottimizzazioni e direttive del linker senza necessitare di un supporto esplicito nel cuore del linguaggio. + +#### Mappatura della Sintassi +Zen C attributes are mapped directly to C attributes: +- `@name` → `__attribute__((name))` +- `@name(args)` → `__attribute__((name(args)))` +- `@name("string")` → `__attribute__((name("string")))` + +#### Derivazioni Intelligenti + +Zen C fornisce delle "derivazioni intelligenti" che rispettano le Semantiche di Movimento: + +- **`@derive(Eq)`**: Genera un metodo di uguaglianza che prende argomenti per referenza (`fn eq(self, other: T*)`). + - Quando si confrontano due struct non-Copy (`a == b`), il compilatore passa automaticamente `b` per referenza (`&b`) per non doverlo spostare. + - I controlli di uguaglianza ricorsivi preferiscono l'accesso da puntatore per prevenire il trasferimento del proprietario. + +### 14. Assembly Inline + +Zen C fornisce supporto di prima-classe per l'assembly _inline_, traspilando direttamente ad `asm` con estensioni in stile GCC. + +#### Utilizzo Base +Scrivi assembly grezzo all'interno di blocchi `asm`. Le stringhe vengono concatenate automaticamente. +```zc +asm { + "nop" + "mfence" +} +``` + +#### Volatile +Impedisci al compilatore di eliminare automaticamente istruzioni assembly (e.g. ottimizzazione) se ciò potrebbe avere ripercussioni. +```zc +asm volatile { + "rdtsc" +} +``` + +#### Vincoli Nominati +Zen C semplifica la sintassi complessa dei vincoli di GCC con dei binding nominati. + +**Nota per i lettori italiani**: Con 'clobber' si intende la *sovrascrizione*. + +```zc +// Sintassi: : out(variable) : in(variable) : clobber(reg) +// Usa una sintassi placeholder (`{variabile}`) per la leggibilità + +fn somma(a: int, b: int) -> int { + let risultato: int; + asm { + "add {risultato}, {a}, {b}" + : out(risultato) + : in(a), in(b) + : clobber("cc") + } + return risultato; +} +``` + +| Tipo | Sintassi | Equivalente GCC | +|:---|:---|:---| +| **Output** | `: out(variabile)` | `"=r"(variabile)` | +| **Input** | `: in(variabile)` | `"r"(variabile)` | +| **Clobber** | `: clobber("rax")` | `"rax"` | +| **Memory** | `: clobber("memoria")` | `"memoria"` | + +> **Nota:** Quando si usa la sintassi Intel (via `-masm=intel`), dovrai assicurartic che la tua build sia configurata correttamente (per esempio, `//> cflags: -masm=intel`). TCC non supporta la sintassi assembly Intel. + + +### 15. Direttive della Build + +Zen C supporta dei commenti speciali all'inizio del tuo file sorgente che ti permettono di configurare il process di build senza necessitare di un sistema di build complesso o di un *Makefile*. + +| Direttiva | Argomenti | Descrizione | +|:---|:---|:---| +| `//> link:` | `-lfoo` oppure `path/to/lib.a` | Linka con una libreria o un file object. | +| `//> lib:` | `path/to/libs` | Aggiunge una directory dove cercare le librerie (`-L`). | +| `//> include:` | `path/to/headers` | Aggiunge una directory dove ricercare i file include (`-I`). | +| `//> framework:` | `Cocoa` | Linka con un framework macOS. | +| `//> cflags:` | `-Wall -O3` | Passa flag arbitrare al compilatore C. | +| `//> define:` | `MACRO` or `KEY=VAL` | Definisci una macro del preprocessore (`-D`). | +| `//> pkg-config:` | `gtk+-3.0` | Esegui `pkg-config` e aggiungi `--cflags` e `--libs`. | +| `//> shell:` | `command` | Esegui un comando sulla shell durante il processo di build. | +| `//> get:` | `http://url/file` | Scarica un file se un file spefico non esiste. | + +#### Feature + +**1. OS Guarding** (lett. _Protezione OS_) +Prefissa delle direttive con il nome di un OS per applicarle solo su piattaforme specifiche. +Prefissi supportati: `linux:`, `windows:`, `macos:` (or `darwin:`). + +```zc +//> linux: link: -lm +//> windows: link: -lws2_32 +//> macos: framework: Cocoa +``` + +**2. Environment Variable Expansion** +Utilizza la sintassi `${VAR}` per espandare variabili d'ambiente nelle tue direttive. + +```zc +//> include: ${HOME}/MiaLibreria/include +//> lib: ${ZC_ROOT}/std +``` + +#### Esempi + +```zc +//> include: ./include +//> lib: ./librerie +//> link: -lraylib -lm +//> cflags: -Ofast +//> pkg-config: gtk+-3.0 + +import "raylib.h" + +fn main() { ... } +``` + +### 16. Keyword + +Le keyword che seguono sono riservate in Zen C. + +#### Dichiarazioni +`alias`, `def`, `enum`, `fn`, `impl`, `import`, `let`, `module`, `opaque`, `struct`, `trait`, `union`, `use` + +#### Controllo del Flusso +`async`, `await`, `break`, `catch`, `continue`, `defer`, `else`, `for`, `goto`, `guard`, `if`, `loop`, `match`, `return`, `try`, `unless`, `while` + +#### Speciali +`asm`, `assert`, `autofree`, `comptime`, `const`, `embed`, `launch`, `ref`, `sizeof`, `static`, `test`, `volatile` + +#### Costanti +`true`, `false`, `null` + +#### Riservate del C +Gli identifiers seguenti sono riservati poiché sono keyword nello standard C11: +`auto`, `case`, `char`, `default`, `do`, `double`, `extern`, `float`, `inline`, `int`, `long`, `register`, `restrict`, `short`, `signed`, `switch`, `typedef`, `unsigned`, `void`, `_Atomic`, `_Bool`, `_Complex`, `_Generic`, `_Imaginary`, `_Noreturn`, `_Static_assert`, `_Thread_local` + +#### Operatori +`and`, `or` + +--- + +## Libreria Standard + +Zen C include una libreria standard (`std`) che ricopre funzionalità essenziali. + +[Scopri la documentazione della Libreria Standard](docs/std/README.md) + +### Moduli Chiave + +| Modulo | Descrizione | Documentazione | +| :--- | :--- | :--- | +| **`std/vec.zc`** | Array dinamico espandibile `Vec`. | [Docs](docs/std/vec.md) | +| **`std/string.zc`** | Tipo `String` allocato sull'Heap con supporto UTF-8. | [Docs](docs/std/string.md) | +| **`std/queue.zc`** | Coda FIFO (Buffer Circolare). | [Docs](docs/std/queue.md) | +| **`std/map.zc`** | Hash Map Generica `Map`. | [Docs](docs/std/map.md) | +| **`std/fs.zc`** | Operazioni del File System. | [Docs](docs/std/fs.md) | +| **`std/io.zc`** | Standard Input/Output (`print`/`println`). | [Docs](docs/std/io.md) | +| **`std/option.zc`** | Valori opzionali (`Some`/`None`). | [Docs](docs/std/option.md) | +| **`std/result.zc`** | Gestione degli errori (`Ok`/`Err`). | [Docs](docs/std/result.md) | +| **`std/path.zc`** | Manipolazione dei percorsi Cross-platform. | [Docs](docs/std/path.md) | +| **`std/env.zc`** | Variabili d'ambiente del processo. | [Docs](docs/std/env.md) | +| **`std/net.zc`** | Networking TCP (Socket). | [Docs](docs/std/net.md) | +| **`std/thread.zc`** | Threads and Synchronization. | [Docs](docs/std/thread.md) | +| **`std/time.zc`** | Misuramenti di tempo e `sleep`. | [Docs](docs/std/time.md) | +| **`std/json.zc`** | Parsing JSON e serializzazione. | [Docs](docs/std/json.md) | +| **`std/stack.zc`** | Stack LIFO `Stack`. | [Docs](docs/std/stack.md) | +| **`std/set.zc`** | Hash Set Generico `Set`. | [Docs](docs/std/set.md) | +| **`std/process.zc`** | Esecuzione e gestione di processi. | [Docs](docs/std/process.md) | + +--- + +## Tooling + +Zen C fornisce un Language Server (LSP) e un REPL per migliorare l'esperienza degli sviluppatori. + +### Language Server (LSP) + +Il server del linguaggio (LSP) di Zen C supporta le feature standard per l'integrazione con gli editor, esso fornisce: + +* **Vai alla definizione** +* **Trova riferimenti** +* **Informazioni sull'hover** +* **Completamenti automatici** (Nomi di funzioni/struct, Completamento dal punto per i methods/campi) +* **Simboli dei documenti** (Outline) +* **Aiuto con le signature delle funzioni** +* **Diagnostiche** (Errori sintattici/semantici) + +Per avviare il server del linguaggio (tipicamente configurato nelle impostazioni LSP del tuo editor): + +```bash +zc lsp +``` + +Il server comunica via lo Standard I/o (JSON-RPC 2.0). + +### REPL + +Il Read-Eval-Print-Loop (REPL, lett. _Leggi-Esegui-Stampa-Ripeti_) ti permette ti sperimentare con il codice Zen C in maniera interattiva. + +```bash +zc repl +``` + +#### Funzionalità + +* **Coding interattivo**: Scrivi espressioni o istruzioni per una esecuzione immediata. +* **Storia persistente**: I comandi vengono salvati in `~/.zprep_history`. +* **Script di avvio**: I comandi di avvio (auto-load) sono salvati in `~/.zprep_init.zc`. + +#### Comandi + +| Comande | Descrizione | +|:---|:---| +| `:help` | Mostra i comandi disponibili. | +| `:reset` | Cancella la storia della sessione corrente (variabili/funzioni). | +| `:vars` | Mostra le variabili attive. | +| `:funcs` | Mostra le funzioni definite dall'utente. | +| `:structs` | Mostra gli struct definiti dall'utente. | +| `:imports` | Mostra gli 'import' attivi. | +| `:history` | Mostra la storia dell'input della sessione. | +| `:type ` | Mostra il tipo di un espressione. | +| `:c ` | Mostra il codice C generato per un istruzione. | +| `:time ` | Esegui un benchmark per l'espressione data. (Esegue 1000 iterazioni). | +| `:edit [n]` | Modifica il comando `n` (default: l'ultimo comando) in `$EDITOR`. | +| `:save ` | Salva la sessione corrente in un file `.zc`. | +| `:load ` | Carica ed esegui un file `.zc` nella sessione corrente. | +| `:watch ` | Watch (lett. _guarda_) un espressione (rieseguita dopo ogni entry). | +| `:unwatch ` | Rimuovi un watch. | +| `:undo` | Rimuovi l'ultimo comando dalla sessione. | +| `:delete ` | Rimuovi il comando all'indice `n`. | +| `:clear` | Pulisce lo schermo. | +| `:quit` | Esce dal REPL. | +| `! ` | Esegue un comando sulla shell (e.g. `!ls`). | + +--- + + +## Supporto del Compilatore e Compatibilità + +Zen C è stato creato in modo tale da poter funzionare con la maggior parte dei compilatori C11. Alcune funzionalità potrebbero affidarsi ad estensioni GNU C, ma spesso queste funzionano anche su altri compilatori. Utilizza la flag `--cc` per modificare il backend. + +```bash +zc run app.zc --cc clang +zc run app.zc --cc zig +``` + +### Stato della suite di test + +| Compilatore | Percentuale di Superamento | Funzionalità Supportate | Limitazioni Nota | +|:---|:---:|:---|:---| +| **GCC** | **100%** | Tutte le funzioni. | Nessuna. | +| **Clang** | **100%** | Tutte le funzioni. | Nessuna. | +| **Zig** | **100%** | Tutte le funzioni. | Nessuna. Usa `zig cc` come compilatore C _drop-in_. | +| **TCC** | **~70%** | Sintassi base, Generici, Tratti | Nessun `__auto_type`, Nessuna sintassi Intel per ASM, Nessuna funzione innestata. | + +> **Consiglio:** Usag **GCC**, **Clang**, o **Zig** per le build di produzione. TCC è eccellente per prototipazione rapida per via dei suoi tempi di compilazione molto brevi ma è carente di alcune estensioni C avanzate che servono a Zen C per un set completo di funzionalità. + +### Buildare con Zig + +Il comando `zig cc` di Zig fornisce un rimpiazzamento drop-in per GCC/Clang con eccellente supporto per la cross-compilation. Per usare Zig: + +```bash +# Compila ed esegui un programma Zen C con Zig +zc run app.zc --cc zig + +# Puoi compilare persino il compilatore Zen C stesso con Zig +make zig +``` + +### Interop C++ + +Zen C può generare codice compatibile con C++ utilizzando l'opzione `--cpp`, permettendo una integrazione fluida con le librerie C++. + +```bash +# Compilazione diretta con g++ +zc app.zc --cpp + +# O traspila per le build manuali +zc transpile app.zc --cpp +g++ out.c my_cpp_lib.o -o app +``` + +#### Usare C++ in Zen C + +Includi header C++ e usa blocchi grezzi per codice C++: + +```zc +include +include + +raw { + std::vector crea_vettore(int a, int b) { + return {a, b}; + } +} + +fn main() { + let v = crea_vettore(1, 2); + raw { std::cout << "Dimensione: " << v.size() << std::endl; } +} +``` + +> **Nota:** L'opzione `--cpp` rende il backend `g++` ed emette codice valido per C++ (utilizza `auto` al posto di `__auto_type`, overload delle funzioni al posto di `_Generic` e i cast espliciti per `void*`) + +#### Interop CUDA + +Zen C supporta la programmazione GPU traspilando a **CUDA C++**. Questo ti permette di utilizzare potenti funzionalità C++ (template, `constexpr`) all'interno dei tuoi kernel mantenendo la sintassi ergonomica di Zen C. + +```bash +# Compilazione diretta con nvcc +zc run app.zc --cuda + +# O traspila per le build manuali +zc transpile app.zc --cuda -o app.cu +nvcc app.cu -o app +``` + +#### Attributi specifici CUDA + +| Attributo | Equivalente CUDA | Descrizione | +|:---|:---|:---| +| `@global` | `__global__` | Function Kernel (esegue sulla GPU, chiamato dall'host) | +| `@device` | `__device__` | Funzione Device (esegue sulla GPU, chiamato dalla GPU) | +| `@host` | `__host__` | Funzione Host (Solo CPU esplicita) | + +#### Kernel Launch Syntax + +Zen C fornisce un'istruzione chiara `launch` per richiamare kernel CUDA: + +```zc +launch kernel_name(args) with { + grid: num_blocks, + block: threads_per_block, + shared_mem: 1024, // Opzionale + stream: my_stream // Opzionale +}; +``` + +Questo traspila a: `kernel_name<<>>(args);` + +#### Scrivere kernel CUDA + +Utilizza la sintassi delle funzioni Zen C con `@global` e l'istruzione `launch`: + +```zc +import "std/cuda.zc" + +@global +fn aggiungi_kernel(a: float*, b: float*, c: float*, n: int) { + let i = thread_id(); + if i < n { + c[i] = a[i] + b[i]; + } +} + +fn main() { + def N = 1024; + let d_a = cuda_alloc(N); + let d_b = cuda_alloc(N); + let d_c = cuda_alloc(N); + defer cuda_free(d_a); + defer cuda_free(d_b); + defer cuda_free(d_c); + + // ... init data ... + + launch aggiungi_kernel(d_a, d_b, d_c, N) with { + grid: (N + 255) / 256, + block: 256 + }; + + cuda_sync(); +} +``` + +#### Libreria Standard (`std/cuda.zc`) +Zen C fornisce una libreria standard per delle operazioni comuni in CUDA per ridurre la mole di blocchi `raw` (grezzi): + +```zc +import "std/cuda.zc" + +// Gestione della memoria +let d_ptr = cuda_alloc(1024); +cuda_copy_to_device(d_ptr, h_ptr, 1024 * sizeof(float)); +defer cuda_free(d_ptr); + +// Sincronizzazione +cuda_sync(); + +// Thread Indexing (use inside kernels) +// Indicizzazione dei thread (usa all'interno del kernel) +let i = thread_id(); // Indice globale +let bid = block_id(); +let tid = local_id(); +``` + + +> **Nota:** La flag `--cuda` imposta `nvcc` come compilatore e implica la modalità `--cpp`. Richiede l'installazione dell'NVIDIA CUDA Toolkit. + +### Supporto C23 + +Zen C supporta le funzionalità moderne dello standard C23 quando si usa un backend compatibile (GCC 14+, Clang 14+, _TCC_ (_parziale_)). + +- **`auto`**: Zen C mappa automaticamente l'inferenza del tipo alla keyword `auto` di C23 (se `__STDC_VERSION__ >= 202300L`). +- **`_BitInt(N)`**: Usa i tipi `iN` e `uN` (e.g., `i256`, `u12`, `i24`) per accedere agli interi di lunghezza arbitraria di C23. + +### Interop Objective-C + +Zen C può compilare a Objective-C (`.m`) utilizzando la flag `--objc`, permettendoti di utilizzare i framework (come Cocoa/Foundation) e la sintassi Obj-C + +```bash +# Compila con clang (o gcc/gnustep) +zc app.zc --objc --cc clang +``` + +#### Usando l'Objective-C in Zen C + +Utilizza `include` per gli header e i blocchi `raw` per la sintassi Obj-C (`@interface`, `[...]`, `@""`). + +```zc +//> macos: framework: Foundation +//> linux: cflags: -fconstant-string-class=NSConstantString -D_NATIVE_OBJC_EXCEPTIONS +//> linux: link: -lgnustep-base -lobjc + +include + +fn main() { + raw { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSLog(@"Ciao da Objective-C!"); + [pool drain]; + } + println "Funziona anche Zen C!"; +} +``` + +> **Nota:** L'interpolazione delle stringhe di Zen C funziona con gli oggetti dell'Objective-C (`id`) chiamando `debugDescription` oppure `description`. + +--- + +## Contribuisci + +Qui accogliamo tutti i contributori! Che siano fix di bug, miglioramenti alla documentazione, o la proposta di nuove funzionalità. + +### Come contribuire +1. **Forka la repository**: Workflow standard di GitHub. +2. **Crea un Branch per la funzionalità**: `git checkout -b feature/NewThing`. +3. **Guide Linea per il Codice**: + * Segui lo stile C esistente. + * Assicurati che passino tutti i test: `make test`. + * Aggiungi nuovi test per la tua nuova funzionalità in `tests/`. +4. **Crea una Pull Request**: Descrivi chiaramente le modifiche apportate. + +### Eseguire i test +La suite di test è il tuo miglior amico. + +```bash +# Esegui tutti i test (GCC) +make test + +# Esegui test specifici +./zc run tests/test_match.zc + +# Esegui test con un compilatore differente +./tests/run_tests.sh --cc clang +./tests/run_tests.sh --cc zig +./tests/run_tests.sh --cc tcc +``` + +### Estendere il Compilatore +* **Parser**: `src/parser/` - Parser a discesa ricorsiva. +* **Codegen**: `src/codegen/` - Logica di traspilazione (Zen C -> GNU C/C11). +* **Standard Library**: `std/` - Written in Zen C itself. + +--- + +## Attribuzioni + +Questo progetto utilizza librerie esterne. I testi di licenza completi possono essere trovati nella directory `LICENSES/`. + +* **[cJSON](https://github.com/DaveGamble/cJSON)** (Licenza MIT): Usato per il parsing e la generazione di JSON nel Language Server. +* **[zc-ape](https://github.com/OEvgeny/zc-ape)** (Licenza MIT): La versione originale di Actually Portable Executable di Zen-C, realizzata da [Eugene Olonov](https://github.com/OEvgeny). +* **[Cosmopolitan Libc](https://github.com/jart/cosmopolitan)** (Licenza ISC): La libreria fondamentale che rende possibile APE. + diff --git a/README_ZH_CN.md b/README_ZH_CN.md index 51689f6..09b9bc3 100644 --- a/README_ZH_CN.md +++ b/README_ZH_CN.md @@ -1,7 +1,7 @@
-[English](README.md) • [简体中文](README_ZH_CN.md) • [繁體中文](README_ZH_TW.md) • [Español](README_ES.md) +[English](README.md) • [简体中文](README_ZH_CN.md) • [繁體中文](README_ZH_TW.md) • [Español](README_ES.md) • [Italiano](README_IT.md)
diff --git a/README_ZH_TW.md b/README_ZH_TW.md index 6fa0dbd..b0a98b8 100644 --- a/README_ZH_TW.md +++ b/README_ZH_TW.md @@ -1,7 +1,7 @@
-[English](README.md) • [简体中文](README_ZH_CN.md) • [繁體中文](README_ZH_TW.md) • [Español](README_ES.md) +[English](README.md) • [简体中文](README_ZH_CN.md) • [繁體中文](README_ZH_TW.md) • [Español](README_ES.md) • [Italiano](README_IT.md)
-- cgit v1.2.3 From 263be2cc48b900c22f9354bf4d61bdd767a0f025 Mon Sep 17 00:00:00 2001 From: czjstmax Date: Sat, 31 Jan 2026 20:50:36 +0100 Subject: Grammar fixes for italian documentation Signed-off-by: czjstmax --- README_IT.md | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/README_IT.md b/README_IT.md index 9b48e62..b67c7e8 100644 --- a/README_IT.md +++ b/README_IT.md @@ -24,11 +24,11 @@ ## Panoramica -**Zen C** è un linguaggio di programmazione di sistemi moderno che genera codice `GNU C`/`C11`. Fornisce allo sviluppatore un ricco set di funzionalità, tra cui inferenza di tipo, pattern matcing, generici, tratti, async/await, e gestione manuale della memoria con funzionalità RAII, mantenendo al contempo una compatibilità al 100% con l'ABI C +**Zen C** è un linguaggio di programmazione di sistemi moderno che genera codice `GNU C`/`C11`. Fornisce allo sviluppatore un ricco set di funzionalità, tra cui inferenza di tipo, pattern matching, generici, tratti, async/await, e gestione manuale della memoria con funzionalità RAII, mantenendo al contempo una compatibilità al 100% con l'ABI C ## Community -Unisciti alla conversazione, condividi delle demo, fai comande o segnala dei bug nel server ufficiale Discord Zen C +Unisciti alla conversazione, condividi delle demo, fai domande o segnala dei bug nel server ufficiale Discord Zen C - Discord: [Unisciti qui](https://discord.com/invite/q6wEsCmkJP) @@ -66,7 +66,7 @@ Unisciti alla conversazione, condividi delle demo, fai comande o segnala dei bug - [Pattern Matching](#pattern-matching) - [Loop](#loop) - [Controllo Avanzato](#controllo-avanzato) - - [6. Operators](#6-operatori) + - [6. Operatori](#6-operatori) - [Operatori Overload-abili](#operatori-overload-abili) - [Zucchero Sintattico](#zucchero-sintattico) - [7. Stampaggio e Interpolazione delle Stringhe](#7-stampaggio-e-interpolazione-delle-stringhe) @@ -178,7 +178,7 @@ export ZC_ROOT=/path/to/Zen-C ### 1. Variabili e Costanti -Zen C differenzia le costanti al tempo di compilazione e le varaibili di esecuzione. +Zen C differenzia le costanti al tempo di compilazione e le variabili di esecuzione. #### Manifest Constants (`def`) #### Costanti Manifesto (`def`) @@ -252,7 +252,7 @@ let differenza = risultato.1; // 1 **Separazione** -Le tuple possono essere separate irettamente in variabili singole. +Le tuple possono essere separate direttamente in variabili singole. ```zc let (somma, differenza) = somma_e_differenza(3, 2); // somma = 5, differenza = 1 @@ -276,7 +276,7 @@ struct Flags { } ``` -> **Nota**: Gli struct usano le [Semantiche di Spostatmento](#semantiche-di-movimento--copia-sicura) di default. I campi di uno struct possono essere acceduti via `.` anche sui puntatori (Dereferenza-Automatica). +> **Nota**: Gli struct usano le [Semantiche di Spostamento](#semantiche-di-movimento--copia-sicura) di default. I campi di uno struct possono essere acceduti via `.` anche sui puntatori (Dereferenza-Automatica). #### Struct Opachi Puoi definire uno struct come `opaque` (lett. _opaco_) per restringere l'accesso ai suoi campi al modulo che lo ha definito, permettendo comunque l'allocazione sullo stack dello struct (la grandezza è data). @@ -349,7 +349,7 @@ fn main() { } ``` -### 4. Functions & Lambdas +### 4. Funzioni e Lambda #### Funzioni ```zc @@ -381,7 +381,6 @@ fn incrementa(val: int, quantità: int = 1) -> int { return val + quantità; } -// Expression default value (evaluated at call site) // Espressione come valore default (calcolato) fn offset(val: int, pad: int = 10 * 2) -> int { return val + pad; @@ -658,7 +657,7 @@ fn peek(r: Risorsa*) { ... } // 'r' viene preso in prestito (referenza) ``` **Clonazione Esplicita**: -Se *vuoi* avere più copie di una risorsas, rendilo esplicito: +Se *vuoi* avere più copie di una risorsa, rendilo esplicito: ```zc let b = a.clona(); // Chiama il metodo `clona` dal tratto `Clone` @@ -861,7 +860,7 @@ struct Paio { } ``` -### 11. Concorreza Asincrona (Async/Await) +### 11. Concorrenza Asincrona (Async/Await) Costruito sui pthreads. @@ -918,7 +917,7 @@ Passa delle macro del preprocessore C. #define BUFFER_MASSIMO 1024 ``` -### 13. Attributti +### 13. Attributi Decora le funzioni e gli struct per modificare il comportamento del compilatore. @@ -1016,24 +1015,24 @@ fn somma(a: int, b: int) -> int { | **Clobber** | `: clobber("rax")` | `"rax"` | | **Memory** | `: clobber("memoria")` | `"memoria"` | -> **Nota:** Quando si usa la sintassi Intel (via `-masm=intel`), dovrai assicurartic che la tua build sia configurata correttamente (per esempio, `//> cflags: -masm=intel`). TCC non supporta la sintassi assembly Intel. +> **Nota:** Quando si usa la sintassi Intel (via `-masm=intel`), dovrai assicurarti che la tua build sia configurata correttamente (per esempio, `//> cflags: -masm=intel`). TCC non supporta la sintassi assembly Intel. ### 15. Direttive della Build -Zen C supporta dei commenti speciali all'inizio del tuo file sorgente che ti permettono di configurare il process di build senza necessitare di un sistema di build complesso o di un *Makefile*. +Zen C supporta dei commenti speciali all'inizio del tuo file sorgente che ti permettono di configurare il processo di build senza necessitare di un sistema di build complesso o di un *Makefile*. | Direttiva | Argomenti | Descrizione | |:---|:---|:---| | `//> link:` | `-lfoo` oppure `path/to/lib.a` | Linka con una libreria o un file object. | | `//> lib:` | `path/to/libs` | Aggiunge una directory dove cercare le librerie (`-L`). | -| `//> include:` | `path/to/headers` | Aggiunge una directory dove ricercare i file include (`-I`). | +| `//> include:` | `path/to/headers` | Aggiunge una directory dove cercare i file include (`-I`). | | `//> framework:` | `Cocoa` | Linka con un framework macOS. | | `//> cflags:` | `-Wall -O3` | Passa flag arbitrare al compilatore C. | | `//> define:` | `MACRO` or `KEY=VAL` | Definisci una macro del preprocessore (`-D`). | | `//> pkg-config:` | `gtk+-3.0` | Esegui `pkg-config` e aggiungi `--cflags` e `--libs`. | | `//> shell:` | `command` | Esegui un comando sulla shell durante il processo di build. | -| `//> get:` | `http://url/file` | Scarica un file se un file spefico non esiste. | +| `//> get:` | `http://url/file` | Scarica un file se un file specifico non esiste. | #### Feature @@ -1342,7 +1341,6 @@ defer cuda_free(d_ptr); // Sincronizzazione cuda_sync(); -// Thread Indexing (use inside kernels) // Indicizzazione dei thread (usa all'interno del kernel) let i = thread_id(); // Indice globale let bid = block_id(); -- cgit v1.2.3 From 78cecf150151de2f89abf4513a21078854365ad5 Mon Sep 17 00:00:00 2001 From: czjstmax Date: Sat, 31 Jan 2026 21:20:44 +0100 Subject: Ulterior fixes made to the italian translation Signed-off-by: czjstmax --- README_IT.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README_IT.md b/README_IT.md index b67c7e8..7d14165 100644 --- a/README_IT.md +++ b/README_IT.md @@ -145,7 +145,7 @@ sudo env "PATH=$PATH" make install-ape - `out/bin/zc.com`: Il compilatore Zen-C portatile. Inlude la libreria standard, incorporata nell'eseguibile. - `out/bin/zc-boot.com`: Un installer bootstrap auto-contenuto per configurare nuovi progetti Zen-C rapidamente. -**Usage:** +**Utilizzo:** ```bash # Eseguibile su qualunque OS supportato ./out/bin/zc.com build hello.zc -o hello @@ -180,7 +180,6 @@ export ZC_ROOT=/path/to/Zen-C Zen C differenzia le costanti al tempo di compilazione e le variabili di esecuzione. -#### Manifest Constants (`def`) #### Costanti Manifesto (`def`) Valori che esistono solo durante la compilazione (integrate nel codice). Utilizzale per le grandezze degli array, configurazioni fisse, e numeri magici. @@ -327,7 +326,6 @@ alias ID = int; alias PuntoDellaMappa = Mappa; ``` -#### Opaque Type Aliases #### Alias del tipo opachi Puoi definire un alias del tipo come `opaque` (lett. _opaco_) per creare un nuovo tipo che si distingue dal suo tipo sottostante al di fuori del modulo che l'ha definito. Questo permette una forte incapsulamento e sicurezza dei tipi senza overhead extra durante l'esecuzione di un wrapper struct. @@ -399,7 +397,6 @@ fn main() { } ``` -#### Lambdas (Closures) #### Lambda (Closure) Funzioni anonime che possono catturare il loro ambiente. ```zc @@ -938,7 +935,7 @@ Decora le funzioni e gli struct per modificare il comportamento del compilatore. | `@pure` | Fn | La funzione non ha effetti collaterali (indizio per l'ottimizzazione). | | `@cold` | Fn | La funzione è usata poco spesso (indizio per la branch prediction). | | `@hot` | Fn | La funzione è usata molto spesso (indizio per l'ottimizzazione). | -| `@export` | Fn/Struct | Export symbol (visibility default). | +| `@export` | Fn/Struct | Esporta simbolo (visibilità default). | | `@global` | Fn | CUDA: Entry point del Kernel (`__global__`). | | `@device` | Fn | CUDA: Funzione del Device (`__device__`). | | `@host` | Fn | CUDA: Funzione dell'Host (`__host__`). | @@ -1114,7 +1111,7 @@ Zen C include una libreria standard (`std`) che ricopre funzionalità essenziali | **`std/path.zc`** | Manipolazione dei percorsi Cross-platform. | [Docs](docs/std/path.md) | | **`std/env.zc`** | Variabili d'ambiente del processo. | [Docs](docs/std/env.md) | | **`std/net.zc`** | Networking TCP (Socket). | [Docs](docs/std/net.md) | -| **`std/thread.zc`** | Threads and Synchronization. | [Docs](docs/std/thread.md) | +| **`std/thread.zc`** | Thread e Sincronizzazione. | [Docs](docs/std/thread.md) | | **`std/time.zc`** | Misuramenti di tempo e `sleep`. | [Docs](docs/std/time.md) | | **`std/json.zc`** | Parsing JSON e serializzazione. | [Docs](docs/std/json.md) | | **`std/stack.zc`** | Stack LIFO `Stack`. | [Docs](docs/std/stack.md) | -- cgit v1.2.3 From 45165b0192325867771acc0e27a443100b700b3e Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Sat, 31 Jan 2026 22:35:26 +0000 Subject: Improved networking example --- examples/networking/echo_server.zc | 32 +++++++++++++++++++++++--------- std/net.zc | 32 +++++++++++++++++--------------- 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/examples/networking/echo_server.zc b/examples/networking/echo_server.zc index 2934923..64c60da 100644 --- a/examples/networking/echo_server.zc +++ b/examples/networking/echo_server.zc @@ -4,6 +4,7 @@ import "std/net.zc" def SIZE = 1024; fn main() { + raw { setbuf(stdout, NULL); } "Starting Echo Server on 127.0.0.1:8080..."; let listener_res = TcpListener::bind("127.0.0.1", 8080); @@ -13,24 +14,37 @@ fn main() { } let listener = listener_res.unwrap(); - defer listener.close(); loop { let client_res = listener.accept(); if client_res.is_ok() { let stream = client_res.unwrap(); - defer stream.close(); - let buf = (char*)malloc(SIZE); - defer free(buf); - let read_res = stream.read(buf, SIZE); + "New Connection!"; - if read_res.is_ok() { + let buf: char[SIZE]; + + while true { + let read_res = stream.read(&buf[0], SIZE); + + if read_res.is_err() { + !"Read error: {read_res.err}"; + break; + } + let bytes = read_res.unwrap(); - if bytes > 0 { - stream.write(buf, bytes); - "Echoed {bytes} bytes."; + if bytes == 0 { + "Client disconnected."; + break; } + + let write_res = stream.write(&buf[0], bytes); + if write_res.is_err() { + !"Write error: {write_res.err}"; + break; + } + + "Echoed {bytes} bytes."; } } } diff --git a/std/net.zc b/std/net.zc index d410a4d..826b795 100644 --- a/std/net.zc +++ b/std/net.zc @@ -63,26 +63,28 @@ extern fn _z_net_write(fd: int, buf: const char*, n: usize) -> isize; struct TcpStream { - fd: int; + handle: int; } +extern fn strerror(errnum: int) -> char*; + impl TcpStream { fn read(self, buf: char*, len: usize) -> Result { - let n = read(self.fd, (void*)buf, len); - if (n < 0) return Result::Err("Read failed"); + let n = read(self.handle - 1, (void*)buf, len); + if (n < 0) return Result::Err(strerror(errno)); return Result::Ok((usize)n); } fn write(self, buf: char*, len: usize) -> Result { - let n = _z_net_write(self.fd, buf, len); + let n = _z_net_write(self.handle - 1, buf, len); if (n < 0) return Result::Err("Write failed"); return Result::Ok((usize)n); } fn close(self) { - if (self.fd >= 0) { - close(self.fd); - self.fd = -1; + if (self.handle > 0) { + close(self.handle - 1); + self.handle = 0; } } @@ -94,7 +96,7 @@ impl TcpStream { if (res == -1) { close(fd); return Result::Err("Invalid address"); } if (res == -2) { close(fd); return Result::Err("Connection failed"); } - return Result::Ok(TcpStream { fd: fd }); + return Result::Ok(TcpStream { handle: fd + 1 }); } } @@ -105,7 +107,7 @@ impl Drop for TcpStream { } struct TcpListener { - fd: int; + handle: int; } impl TcpListener { @@ -118,19 +120,19 @@ impl TcpListener { if (res == -2) { close(fd); return Result::Err("Bind failed"); } if (res == -3) { close(fd); return Result::Err("Listen failed"); } - return Result::Ok(TcpListener { fd: fd }); + return Result::Ok(TcpListener { handle: fd + 1 }); } fn accept(self) -> Result { - let client_fd = _z_net_accept(self.fd); + let client_fd = _z_net_accept(self.handle - 1); if (client_fd < 0) return Result::Err("Accept failed"); - return Result::Ok(TcpStream { fd: client_fd }); + return Result::Ok(TcpStream { handle: client_fd + 1 }); } fn close(self) { - if (self.fd >= 0) { - close(self.fd); - self.fd = -1; + if (self.handle > 0) { + close(self.handle - 1); + self.handle = 0; } } } -- cgit v1.2.3 From aafd7e8739b14dd89b2e81148f2b07710f3c2c42 Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Sat, 31 Jan 2026 22:53:52 +0000 Subject: Automatic versioning --- README.md | 2 +- README_ES.md | 2 +- README_IT.md | 2 +- README_ZH_CN.md | 2 +- README_ZH_TW.md | 2 +- examples/networking/echo_server.zc | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 786e32c..2f0832f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ [![Build Status](https://img.shields.io/badge/build-passing-brightgreen)]() [![License](https://img.shields.io/badge/license-MIT-blue)]() -[![Version](https://img.shields.io/badge/version-0.1.0-orange)]() +[![Version](https://img.shields.io/github/v/release/z-libs/Zen-C?label=version&color=orange)]() [![Platform](https://img.shields.io/badge/platform-linux-lightgrey)]() *Write like a high-level language, run like C.* diff --git a/README_ES.md b/README_ES.md index 4f1e4be..2feb762 100644 --- a/README_ES.md +++ b/README_ES.md @@ -13,7 +13,7 @@ [![Estado de la Construcción](https://img.shields.io/badge/build-passing-brightgreen)]() [![Licencia](https://img.shields.io/badge/license-MIT-blue)]() -[![Versión](https://img.shields.io/badge/version-0.1.0-orange)]() +[![Versión](https://img.shields.io/github/v/release/z-libs/Zen-C?label=version&color=orange)]() [![Plataforma](https://img.shields.io/badge/platform-linux-lightgrey)]() *Escribe como un lenguaje de alto nivel, ejecuta como C.* diff --git a/README_IT.md b/README_IT.md index 7d14165..ba49e46 100644 --- a/README_IT.md +++ b/README_IT.md @@ -13,7 +13,7 @@ [![Stato Build](https://img.shields.io/badge/build-passing-brightgreen)]() [![Licenza](https://img.shields.io/badge/license-MIT-blue)]() -[![Versione](https://img.shields.io/badge/version-0.1.0-orange)]() +[![Versione](https://img.shields.io/github/v/release/z-libs/Zen-C?label=version&color=orange)]() [![Piattaforma](https://img.shields.io/badge/platform-linux-lightgrey)]() *Comodità di un linguaggio ad alto livello, veloce come il C* diff --git a/README_ZH_CN.md b/README_ZH_CN.md index 09b9bc3..796eb66 100644 --- a/README_ZH_CN.md +++ b/README_ZH_CN.md @@ -13,7 +13,7 @@ [![构建状态](https://img.shields.io/badge/build-passing-brightgreen)]() [![许可证](https://img.shields.io/badge/license-MIT-blue)]() -[![版本](https://img.shields.io/badge/version-0.1.0-orange)]() +[![版本](https://img.shields.io/github/v/release/z-libs/Zen-C?label=version&color=orange)]() [![平台](https://img.shields.io/badge/platform-linux-lightgrey)]() *像高级语言一样编写,像 C 一样运行。* diff --git a/README_ZH_TW.md b/README_ZH_TW.md index b0a98b8..5b4d484 100644 --- a/README_ZH_TW.md +++ b/README_ZH_TW.md @@ -13,7 +13,7 @@ [![構建狀態](https://img.shields.io/badge/build-passing-brightgreen)]() [![許可證](https://img.shields.io/badge/license-MIT-blue)]() -[![版本](https://img.shields.io/badge/version-0.1.0-orange)]() +[![版本](https://img.shields.io/github/v/release/z-libs/Zen-C?label=version&color=orange)]() [![平台](https://img.shields.io/badge/platform-linux-lightgrey)]() *像高級語言一樣編寫,像 C 一樣運行。* diff --git a/examples/networking/echo_server.zc b/examples/networking/echo_server.zc index 64c60da..82ecd82 100644 --- a/examples/networking/echo_server.zc +++ b/examples/networking/echo_server.zc @@ -4,7 +4,7 @@ import "std/net.zc" def SIZE = 1024; fn main() { - raw { setbuf(stdout, NULL); } + setbuf(stdout, NULL); "Starting Echo Server on 127.0.0.1:8080..."; let listener_res = TcpListener::bind("127.0.0.1", 8080); -- cgit v1.2.3 From fcc9210aa32d671e16b392cf48546c4e2001ff8f Mon Sep 17 00:00:00 2001 From: Lam Wei Lun Date: Sun, 1 Feb 2026 14:46:46 +0800 Subject: Added detach/cancel and equality check for std/thread --- std/thread.zc | 56 ++++++++++++++++++++++++++++++++++++++++++------ tests/std/test_thread.zc | 42 ++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 tests/std/test_thread.zc diff --git a/std/thread.zc b/std/thread.zc index 16f3ca1..0ab02e4 100644 --- a/std/thread.zc +++ b/std/thread.zc @@ -25,9 +25,13 @@ raw { z_closure_T *closure = (z_closure_T*)c; void (*f)(void*) = (void(*)(void*))closure->func; f(closure->ctx); - free(c); + free(c); return NULL; } + + static int _z_thread_equal(void* handle1, void* handle2) { + return pthread_equal((pthread_t)handle1, (pthread_t)handle2); + } static int _z_thread_spawn(void *ctx_copy, size_t *out_handle) { pthread_t pt; @@ -41,6 +45,14 @@ raw { static int _z_thread_join(void *handle) { return pthread_join((pthread_t)handle, NULL); } + + static int _z_thread_detach(void* handle) { + return pthread_detach((pthread_t)handle); + } + + static int _z_thread_cancel(void* handle) { + return pthread_cancel((pthread_t)handle); + } static void _z_mutex_init(void *ptr) { pthread_mutex_init((pthread_mutex_t*)ptr, NULL); @@ -63,8 +75,11 @@ raw { } } +extern fn _z_thread_equal(handle1: void*, handle2: void*) -> int; extern fn _z_thread_spawn(ctx: void*, out: usize*) -> int; extern fn _z_thread_join(handle: void*) -> int; +extern fn _z_thread_detach(handle: void*) -> int; +extern fn _z_thread_cancel(handle: void*) -> int; extern fn _z_mutex_init(ptr: void*); extern fn _z_mutex_lock(ptr: void*); extern fn _z_mutex_unlock(ptr: void*); @@ -74,21 +89,30 @@ extern fn _z_usleep(micros: int); struct Thread { - handle: void*; + handle: void*; } impl Thread { + fn eq(self, other: const Thread) -> bool { + return _z_thread_equal(self.handle, other.handle); + } + fn neq(self, other: const Thread) -> bool { + return !(self == other); + } + fn spawn(func: fn()) -> Result { let t: usize = 0; let ctx_copy = malloc(16); // z_closure_T is 16 bytes - if (ctx_copy == NULL) return Result::Err("OOM"); + if ctx_copy == NULL { + return Result::Err("OOM"); + } memcpy(ctx_copy, &func, 16); let ret = _z_thread_spawn(ctx_copy, &t); - if (ret != 0) { + if ret != 0 { free(ctx_copy); return Result::Err("Failed to create thread"); } @@ -97,8 +121,26 @@ impl Thread { } fn join(self) -> Result { - let ret = _z_thread_join(self.handle); - if (ret != 0) return Result::Err("Join failed"); + let err = _z_thread_join(self.handle); + if err { + return Result::Err("Join failed"); + } + return Result::Ok(true); + } + + fn detach(self) -> Result { + let err = _z_thread_detach(self.handle); + if err { + return Result::Err("Detach failed"); + } + return Result::Ok(true); + } + + fn cancel(self) -> Result { + let err = _z_thread_cancel(self.handle); + if err { + return Result::Err("Cancel failed"); + } return Result::Ok(true); } } @@ -123,7 +165,7 @@ impl Mutex { } fn free(self) { - if (self.handle) { + if self.handle { _z_mutex_destroy(self.handle); free(self.handle); self.handle = NULL; diff --git a/tests/std/test_thread.zc b/tests/std/test_thread.zc new file mode 100644 index 0000000..42fa82e --- /dev/null +++ b/tests/std/test_thread.zc @@ -0,0 +1,42 @@ +import "std/thread.zc" + +test "Thread Spawn and Join" { + "Testing thread spawn and join"; + + let spawn_result = Thread::spawn(fn(){ + "Running on a separate thread"; + }); + assert(spawn_result.is_ok(), "Thread spawn has failed"); + + let thr = spawn_result.unwrap(); + let join_result = thr.join(); + assert(join_result.is_ok(), "Thread join has failed"); +} + +test "Thread Spawn and Detach" { + "Testing thread spawn and detach"; + + let spawn_result = Thread::spawn(fn(){ + "Detached thread, this line might print later."; + }); + assert(spawn_result.is_ok(), "Thread spawn has failed"); + + let thr = spawn_result.unwrap(); + let detach_result = thr.detach(); + assert(detach_result.is_ok(), "Thread detach has failed"); +} + +test "Thread Spawn and Cancel" { + "Testing thread spawn and cancel"; + + let spawn_result = Thread::spawn(fn(){ + "Thread running indefinitely..."; + while true { + } + }); + assert(spawn_result.is_ok(), "Thread spawn has failed"); + + let thr = spawn_result.unwrap(); + let cancel_result = thr.cancel(); + assert(cancel_result.is_ok(), "Thread cancel has failed"); +} -- cgit v1.2.3 From f14c26996e2f69aaa25e284dd40320f9c00079e3 Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Sun, 1 Feb 2026 11:19:34 +0000 Subject: Fix for #167 --- README.md | 7 ++--- README_ES.md | 7 ++--- README_IT.md | 7 ++--- README_ZH_CN.md | 7 ++--- README_ZH_TW.md | 7 ++--- src/codegen/codegen.c | 55 +++++++++++++++++++++++++------------- src/parser/parser_stmt.c | 35 ++++++++++++++++++------ tests/features/test_asm_clobber.zc | 20 ++++++++++++++ 8 files changed, 103 insertions(+), 42 deletions(-) create mode 100644 tests/features/test_asm_clobber.zc diff --git a/README.md b/README.md index 2f0832f..87f95af 100644 --- a/README.md +++ b/README.md @@ -997,12 +997,13 @@ Zen C simplifies the complex GCC constraint syntax with named bindings. // Syntax: : out(variable) : in(variable) : clobber(reg) // Uses {variable} placeholder syntax for readability -fn add(a: int, b: int) -> int { +fn add_five(x: int) -> int { let result: int; asm { - "add {result}, {a}, {b}" + "mov {x}, {result}" + "add $5, {result}" : out(result) - : in(a), in(b) + : in(x) : clobber("cc") } return result; diff --git a/README_ES.md b/README_ES.md index 2feb762..11c4d89 100644 --- a/README_ES.md +++ b/README_ES.md @@ -997,12 +997,13 @@ Zen C simplifica la compleja sintaxis de restricciones de GCC con vinculaciones // Sintaxis: : out(variable) : in(variable) : clobber(reg) // Usa la sintaxis de marcador de posición {variable} para legibilidad -fn sumar(a: int, b: int) -> int { +fn sumar(x: int) -> int { let resultado: int; asm { - "add {resultado}, {a}, {b}" + "mov {x}, {resultado}" + "add $5, {resultado}" : out(resultado) - : in(a), in(b) + : in(x) : clobber("cc") } return resultado; diff --git a/README_IT.md b/README_IT.md index ba49e46..9bf804e 100644 --- a/README_IT.md +++ b/README_IT.md @@ -993,12 +993,13 @@ Zen C semplifica la sintassi complessa dei vincoli di GCC con dei binding nomina // Sintassi: : out(variable) : in(variable) : clobber(reg) // Usa una sintassi placeholder (`{variabile}`) per la leggibilità -fn somma(a: int, b: int) -> int { +fn aggiungi_cinque(x: int) -> int { let risultato: int; asm { - "add {risultato}, {a}, {b}" + "mov {x}, {risultato}" + "add $5, {risultato}" : out(risultato) - : in(a), in(b) + : in(x) : clobber("cc") } return risultato; diff --git a/README_ZH_CN.md b/README_ZH_CN.md index 796eb66..aa1130b 100644 --- a/README_ZH_CN.md +++ b/README_ZH_CN.md @@ -996,12 +996,13 @@ Zen C 通过命名绑定简化了复杂的 GCC 约束语法。 // 语法: : out(变量) : in(变量) : clobber(寄存器) // 使用 {变量} 占位符语法以提高可读性 -fn add(a: int, b: int) -> int { +fn add_five(x: int) -> int { let result: int; asm { - "add {result}, {a}, {b}" + "mov {x}, {result}" + "add $5, {result}" : out(result) - : in(a), in(b) + : in(x) : clobber("cc") } return result; diff --git a/README_ZH_TW.md b/README_ZH_TW.md index 5b4d484..5d85a76 100644 --- a/README_ZH_TW.md +++ b/README_ZH_TW.md @@ -996,12 +996,13 @@ Zen C 通過命名綁定簡化了複雜的 GCC 約束語法。 // 語法: : out(變量) : in(變量) : clobber(寄存器) // 使用 {變量} 佔位符語法以提高可讀性 -fn add(a: int, b: int) -> int { +fn add_five(x: int) -> int { let result: int; asm { - "add {result}, {a}, {b}" + "mov {x}, {result}" + "add $5, {result}" : out(result) - : in(a), in(b) + : in(x) : clobber("cc") } return result; diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index 37415c2..0496a46 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -225,8 +225,9 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) int is_basic = IS_BASIC_TYPE(t1); ASTNode *def = t1 ? find_struct_def(ctx, t1) : NULL; - - if (t1 && def && (def->type == NODE_STRUCT || def->type == NODE_ENUM) && !is_basic && !is_ptr) + + if (t1 && def && (def->type == NODE_STRUCT || def->type == NODE_ENUM) && !is_basic && + !is_ptr) { char *base = t1; if (strncmp(base, "struct ", 7) == 0) @@ -335,7 +336,10 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) codegen_expression(ctx, node->binary.right, out); fprintf(out, ")"); } - if (t1) free(t1); + if (t1) + { + free(t1); + } } else { @@ -413,7 +417,8 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) fprintf(out, ", "); } - Type *param_t = (arg_idx < sig->total_args) ? sig->arg_types[arg_idx] : NULL; + Type *param_t = + (arg_idx < sig->total_args) ? sig->arg_types[arg_idx] : NULL; if (param_t && param_t->kind == TYPE_STRUCT && strncmp(param_t->name, "Tuple_", 6) == 0 && sig->total_args == 1 && @@ -528,13 +533,13 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { char trait_mangled[256]; snprintf(trait_mangled, sizeof(trait_mangled), "%s__%s_%s", base, - ref->node->impl_trait.trait_name, method); + ref->node->impl_trait.trait_name, method); if (find_func(ctx, trait_mangled)) { - size_t suffix_len = strlen(ref->node->impl_trait.trait_name) + - strlen(method) + 2; + size_t suffix_len = strlen(ref->node->impl_trait.trait_name) + + strlen(method) + 2; char *suffix = xmalloc(suffix_len); - snprintf(suffix, suffix_len, "%s_%s", + snprintf(suffix, suffix_len, "%s_%s", ref->node->impl_trait.trait_name, method); resolved_method_suffix = suffix; break; @@ -553,8 +558,8 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { tname = it->impl_node->impl_trait.trait_name; char trait_mangled[512]; - snprintf(trait_mangled, sizeof(trait_mangled), - "%s__%s_%s", base, tname, method); + snprintf(trait_mangled, sizeof(trait_mangled), "%s__%s_%s", + base, tname, method); if (find_func(ctx, trait_mangled)) { size_t suffix_len = strlen(tname) + strlen(method) + 2; @@ -580,7 +585,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) for (int k = 0; k < def->strct.used_struct_count; k++) { char mixin_check[128]; - snprintf(mixin_check, sizeof(mixin_check), "%s__%s", + snprintf(mixin_check, sizeof(mixin_check), "%s__%s", def->strct.used_structs[k], method); if (find_func(ctx, mixin_check)) { @@ -611,7 +616,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) arg = arg->next; } fprintf(out, ")"); - + if (resolved_method_suffix) { free(resolved_method_suffix); @@ -621,9 +626,12 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) free(type); return; } - if (type) free(type); + if (type) + { + free(type); + } } - + if (node->call.callee->type == NODE_EXPR_VAR) { ASTNode *def = find_struct_def(ctx, node->call.callee->var_ref.name); @@ -807,8 +815,11 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { actually_ptr = 1; } - if (lt) free(lt); - + if (lt) + { + free(lt); + } + char *field = node->member.field; if (field && field[0] >= '0' && field[0] <= '9') { @@ -831,7 +842,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) is_slice_struct = 1; } } - + if (!is_slice_struct && node->index.array->resolved_type) { if (strncmp(node->index.array->resolved_type, "Slice_", 6) == 0) @@ -847,7 +858,10 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { is_slice_struct = 1; } - if (inferred) free(inferred); + if (inferred) + { + free(inferred); + } } if (is_slice_struct) @@ -962,7 +976,10 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) fprintf(out, "(Slice_%s){ .data = _arr + _start, .len = _len, .cap = _len }; })", tname); } - if (tname && strcmp(tname, "unknown") != 0) free(tname); + if (tname && strcmp(tname, "unknown") != 0) + { + free(tname); + } break; } case NODE_BLOCK: diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index 0677cf5..ae16243 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -842,7 +842,7 @@ ASTNode *parse_asm(ParserContext *ctx, Lexer *l) } } - // Parse clobbers (: "eax", "memory") + // Parse clobbers (: "eax", "memory" OR : clobber("eax"), clobber("memory")) char **clobbers = NULL; int num_clobbers = 0; @@ -865,17 +865,36 @@ ASTNode *parse_asm(ParserContext *ctx, Lexer *l) continue; } - if (t.type == TOK_STRING) + // check for clobber("...") + if (t.type == TOK_IDENT && strncmp(t.start, "clobber", 7) == 0) { - lexer_next(l); - // Extract string content - char *clob = xmalloc(t.len); - strncpy(clob, t.start + 1, t.len - 2); - clob[t.len - 2] = 0; - clobbers[num_clobbers++] = clob; + lexer_next(l); // eat clobber + if (lexer_peek(l).type != TOK_LPAREN) + { + zpanic_at(lexer_peek(l), "Expected ( after clobber"); + } + lexer_next(l); // eat ( + + Token clob = lexer_next(l); + if (clob.type != TOK_STRING) + { + zpanic_at(clob, "Expected string literal for clobber"); + } + + if (lexer_peek(l).type != TOK_RPAREN) + { + zpanic_at(lexer_peek(l), "Expected ) after clobber string"); + } + lexer_next(l); // eat ) + + char *c = xmalloc(clob.len); + strncpy(c, clob.start + 1, clob.len - 2); + c[clob.len - 2] = 0; + clobbers[num_clobbers++] = c; } else { + zpanic_at(t, "Expected 'clobber(\"...\")' in clobber list"); break; } } diff --git a/tests/features/test_asm_clobber.zc b/tests/features/test_asm_clobber.zc new file mode 100644 index 0000000..2ba74da --- /dev/null +++ b/tests/features/test_asm_clobber.zc @@ -0,0 +1,20 @@ +fn add(a: int, b: int) -> int { + let result: int; + asm { + "mov {a}, {result}" + "add {b}, {result}" + : out(result) + : in(a), in(b) + : clobber("cc") + } + return result; +} + +test "asm_clobber" { + let res = add(10, 20); + if (res != 30) { + println "Failed: Expected 30, got {res}"; + exit(1); + } + println "Success: asm with clobber works properly"; +} -- cgit v1.2.3 From eafd8c67012ea253436b79f703dc0702046703f8 Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Sun, 1 Feb 2026 11:54:22 +0000 Subject: Fix for #95 --- src/codegen/codegen_utils.c | 60 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c index 08707cc..92c5395 100644 --- a/src/codegen/codegen_utils.c +++ b/src/codegen/codegen_utils.c @@ -193,6 +193,7 @@ char *infer_type(ParserContext *ctx, ASTNode *node) { return NULL; } + if (node->resolved_type && strcmp(node->resolved_type, "unknown") != 0 && strcmp(node->resolved_type, "void*") != 0) { @@ -326,6 +327,65 @@ char *infer_type(ParserContext *ctx, ASTNode *node) return extracted; } } + + // Find the struct/enum definition and look for "Ok" or "val" + char *search_name = inner_type; + if (strncmp(search_name, "struct ", 7) == 0) + { + search_name += 7; + } + + ASTNode *def = find_struct_def_codegen(ctx, search_name); + if (!def) + { + // check enums list explicitly if not found in instantiated list + StructRef *er = ctx->parsed_enums_list; + while (er) + { + if (er->node && er->node->type == NODE_ENUM && + strcmp(er->node->enm.name, search_name) == 0) + { + def = er->node; + break; + } + er = er->next; + } + } + + if (def) + { + if (def->type == NODE_ENUM) + { + // Look for "Ok" variant + ASTNode *var = def->enm.variants; + while (var) + { + if (var->variant.name && strcmp(var->variant.name, "Ok") == 0) + { + if (var->variant.payload) + { + return codegen_type_to_string(var->variant.payload); + } + // Ok with no payload? Then it's void/u0. + return "void"; + } + var = var->next; + } + } + else if (def->type == NODE_STRUCT) + { + // Look for "val" field + ASTNode *field = def->strct.fields; + while (field) + { + if (field->field.name && strcmp(field->field.name, "val") == 0) + { + return xstrdup(field->field.type); + } + field = field->next; + } + } + } } } -- cgit v1.2.3 From fbfce63744882d48ea2fc514ab1594000254db80 Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Sun, 1 Feb 2026 14:01:51 +0000 Subject: Related to #138 --- README.md | 58 +++++++++++++++++++- README_ES.md | 57 +++++++++++++++++++- README_IT.md | 55 ++++++++++++++++++- README_ZH_CN.md | 58 +++++++++++++++++++- README_ZH_TW.md | 60 ++++++++++++++++++++- src/ast/ast.c | 56 +++++++++++++++++-- src/ast/ast.h | 58 +++++++++++--------- src/codegen/codegen.c | 92 ++++++++++++++++++++++++++++++- src/codegen/codegen_decl.c | 19 +++++-- src/parser/parser_expr.c | 4 +- src/parser/parser_struct.c | 6 +-- src/parser/parser_type.c | 62 ++++++++++++++++++--- src/parser/parser_utils.c | 22 ++++++-- std/fs.zc | 53 ++++++++++-------- std/io.zc | 23 ++++---- std/net.zc | 98 +++++++++++++++++++++------------- std/process.zc | 8 +-- std/string.zc | 9 ++-- std/thread.zc | 35 ++++++------ tests/collections/test_string_suite.zc | 4 +- tests/features/test_portable_types.zc | 46 ++++++++++++++++ 21 files changed, 737 insertions(+), 146 deletions(-) create mode 100644 tests/features/test_portable_types.zc diff --git a/README.md b/README.md index 87f95af..fc1aa27 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ Join the discussion, share demos, ask questions, or report bugs in the official - [Named Constraints](#named-constraints) - [15. Build Directives](#15-build-directives) - [16. Keywords](#16-keywords) + - [17. C Interoperability](#17-c-interoperability) - [Standard Library](#standard-library) - [Tooling](#tooling) - [Language Server (LSP)](#language-server-lsp) @@ -204,7 +205,11 @@ let y: const int = 10; // Read-only (Type qualified) | Type | C Equivalent | Description | |:---|:---|:---| -| `int`, `uint` | `int`, `unsigned int` | Platform standard integer | +| `int`, `uint` | `int32_t`, `uint32_t` | 32-bit signed/unsigned integer | +| `c_char`, `c_uchar` | `char`, `unsigned char` | C char / unsigned char (Interop) | +| `c_short`, `c_ushort` | `short`, `unsigned short` | C short / unsigned short (Interop) | +| `c_int`, `c_uint` | `int`, `unsigned int` | C int / unsigned int (Interop) | +| `c_long`, `c_ulong` | `long`, `unsigned long` | C long / unsigned long (Interop) | | `I8` .. `I128` or `i8` .. `i128` | `int8_t` .. `__int128_t` | Signed fixed-width integers | | `U8` .. `U128` or `u8` .. `u128` | `uint8_t` .. `__uint128_t` | Unsigned fixed-width integers | | `isize`, `usize` | `ptrdiff_t`, `size_t` | Pointer-sized integers | @@ -217,6 +222,12 @@ let y: const int = 10; // Read-only (Type qualified) | `iN` (for example, `i256`) | `_BitInt(N)` | Arbitrary bit-width signed integer (C23) | | `uN` (for example, `u42`) | `unsigned _BitInt(N)` | Arbitrary bit-width unsigned integer (C23) | +> **Best Practices for Portable Code** +> +> - Use **Portable Types** (`int`, `uint`, `i64`, `u8`, etc.) for all pure Zen C logic. `int` is guaranteed to be 32-bit signed on all architectures. +> - Use **C Interop Types** (`c_int`, `c_char`, `c_long`) **only** when interacting with C libraries (FFI). Their size varies by platform and C compiler (e.g. `c_long` size differs between Windows and Linux). +> - Use `isize` and `usize` for array indexing and memory pointer arithmetic. + ### 3. Aggregate Types #### Arrays @@ -1092,6 +1103,51 @@ The following identifiers are reserved because they are keywords in C11: #### Operators `and`, `or` +### 17. C Interoperability + +Zen C offers two ways to interact with C code: **Trusted Imports** (Convenient) and **Explicit FFI** (Safe/Precise). + +#### Method 1: Trusted Imports (Convenient) + +You can import a C header directly using the `import` keyword with the `.h` extension. This treats the header as a module and assumes all symbols accessed through it exist. + +```zc +//> link: -lm +import "math.h" as c_math; + +fn main() { + // Compiler trusts correctness; emits 'cos(...)' directly + let x = c_math::cos(3.14159); +} +``` + +> **Pros**: Zero boilerplate. Access everything in the header immediately. +> **Cons**: No type safety from Zen C (errors caught by C compiler later). + +#### Method 2: Explicit FFI (Safe) + +For strict type checking or when you don't want to include the text of a header, use `extern fn`. + +```zc +include // Emits #include in generated C + +// Define strict signature +extern fn printf(fmt: char*, ...) -> c_int; + +fn main() { + printf("Hello FFI: %d\n", 42); // Type checked by Zen C +} +``` + +> **Pros**: Zen C ensures types match. +> **Cons**: Requires manual declaration of functions. + +#### `import` vs `include` + +- **`import "file.h"`**: Registers the header as a named module. Enables implicit access to symbols (for example, `file::function()`). +- **`include `**: Purely emits `#include ` in the generated C code. Does not introduce any symbols to the Zen C compiler; you must use `extern fn` to access them. + + --- ## Standard Library diff --git a/README_ES.md b/README_ES.md index 11c4d89..07234f0 100644 --- a/README_ES.md +++ b/README_ES.md @@ -100,6 +100,7 @@ - [Restricciones con Nombre](#restricciones-con-nombre) - [15. Directivas de Construcción](#15-directivas-de-construcción) - [16. Palabras Clave](#16-palabras-clave) + - [17. Interoperabilidad C](#17-interoperabilidad-c) - [Biblioteca Estándar](#biblioteca-estándar) - [Herramientas](#herramientas) - [Servidor de Lenguaje (LSP)](#servidor-de-lenguaje-lsp) @@ -203,7 +204,11 @@ let y: const int = 10; // Solo lectura (Calificado por tipo) | Tipo | Equivalente en C | Descripción | |:---|:---|:---| -| `int`, `uint` | `int`, `unsigned int` | Entero estándar de la plataforma | +| `int`, `uint` | `int32_t`, `uint32_t` | Entero de 32 bits con signo/sin signo | +| `c_char`, `c_uchar` | `char`, `unsigned char` | C char (Interoperabilidad) | +| `c_short`, `c_ushort` | `short`, `unsigned short` | C short (Interoperabilidad) | +| `c_int`, `c_uint` | `int`, `unsigned int` | C int (Interoperabilidad) | +| `c_long`, `c_ulong` | `long`, `unsigned long` | C long (Interoperabilidad) | | `I8` .. `I128` o `i8` .. `i128` | `int8_t` .. `__int128_t` | Enteros con signo de ancho fijo | | `U8` .. `U128` o `u8` .. `u128` | `uint8_t` .. `__uint128_t` | Enteros sin signo de ancho fijo | | `isize`, `usize` | `ptrdiff_t`, `size_t` | Enteros del tamaño de un puntero | @@ -216,6 +221,12 @@ let y: const int = 10; // Solo lectura (Calificado por tipo) | `iN` (ej. `i256`) | `_BitInt(N)` | Entero con signo de ancho arbitrario (C23) | | `uN` (ej. `u42`) | `unsigned _BitInt(N)` | Entero sin signo de ancho arbitrario (C23) | +> **Mejores Prácticas para Código Portable** +> +> - Usa **Tipos Portables** (`int`, `uint`, `i64`, `u8`, etc.) para toda la lógica pura de Zen C. `int` garantiza ser 32-bits con signo en todas las arquitecturas. +> - Usa **Tipos de Interoperabilidad C** (`c_int`, `c_char`, `c_long`) **sólo** al interactuar con bibliotecas C (FFI). Su tamaño varía según la plataforma y el compilador C. +> - Usa `isize` y `usize` para indexado de arrays y aritmética de punteros. + ### 3. Tipos Agregados #### Arrays @@ -1092,6 +1103,50 @@ Los siguientes identificadores están reservados porque son palabras clave en C1 #### Operadores `and`, `or` +### 17. Interoperabilidad C +Zen C ofrece dos formas de interactuar con código C: **Importaciones de Confianza** (Conveniente) y **FFI Explícita** (Seguro/Preciso). + +#### Método 1: Importaciones de Confianza (Conveniente) + +Puedes importar una cabecera C directamente usando la palabra clave `import` con la extensión `.h`. Esto trata la cabecera como un módulo y asume que todos los símbolos accedidos existen. + +```zc +//> link: -lm +import "math.h" as c_math; + +fn main() { + // El compilador confía en la corrección; emite 'cos(...)' directamente + let x = c_math::cos(3.14159); +} +``` + +> **Pros**: Cero código repetitivo. Acceso a todo el contenido de la cabecera inmediato. +> **Cons**: Sin seguridad de tipos desde Zen C (errores capturados por el compilador C después). + +#### Método 2: FFI Explícita (Seguro) + +Para una comprobación estricta de tipos o cuando no quieres incluir el texto de una cabecera, usa `extern fn`. + +```zc +include // Emite #include en el C generado + +// Define firma estricta +extern fn printf(fmt: char*, ...) -> c_int; + +fn main() { + printf("Hola FFI: %d\n", 42); // Comprobado por tipos por Zen C +} +``` + +> **Pros**: Zen C asegura que los tipos coincidan. +> **Cons**: Requiere declaración manual de funciones. + +#### `import` vs `include` + +- **`import "file.h"`**: Registra la cabecera como un módulo con nombre. Habilita el acceso implícito a símbolos (ej. `file::function()`). +- **`include `**: Puramente emite `#include ` en el código C generado. No introduce ningún símbolo al compilador de Zen C; debes usar `extern fn` para acceder a ellos. + + --- ## Biblioteca Estándar diff --git a/README_IT.md b/README_IT.md index 9bf804e..be48cda 100644 --- a/README_IT.md +++ b/README_IT.md @@ -101,6 +101,7 @@ Unisciti alla conversazione, condividi delle demo, fai domande o segnala dei bug - [Vincoli Nominati](#vincoli-nominati) - [15. Direttive della Buil](#15-direttive-della-build) - [16. Keyword](#16-keyword) + - [17. Interoperabilità C](#17-interoperabilità-c) - [Libreria Standard](#liberia-standard) - [Tooling](#tooling) - [Language Server (LSP)](#language-server-lsp) @@ -205,7 +206,11 @@ let y: const int = 10; // Sola lettura (Tipo qualificato) | Tipo | C Equivalent | Descrizione | |:---|:---|:---| -| `int`, `uint` | `int`, `unsigned int` | Intero standard della piattaforma | +| `int`, `uint` | `int32_t`, `uint32_t` | Intero a 32 bit con segno/senza segno | +| `c_char`, `c_uchar` | `char`, `unsigned char` | C char (Interop) | +| `c_short`, `c_ushort` | `short`, `unsigned short` | C short (Interop) | +| `c_int`, `c_uint` | `int`, `unsigned int` | C int (Interop) | +| `c_long`, `c_ulong` | `long`, `unsigned long` | C long (Interop) | | `I8` .. `I128` or `i8` .. `i128` | `int8_t` .. `__int128_t` | Interi a grandezza fissa con segno | | `U8` .. `U128` or `u8` .. `u128` | `uint8_t` .. `__uint128_t` | Interi a grandezza fissa senza segno | | `isize`, `usize` | `ptrdiff_t`, `size_t` | Interi con grandezza di un puntatore | @@ -218,6 +223,12 @@ let y: const int = 10; // Sola lettura (Tipo qualificato) | `iN` (Per esempio, `i256`) | `_BitInt(N)` | Intero con segno a larghezza arbitraria di bit (C23) | | `uN` (Per esempio, `u42`) | `unsigned _BitInt(N)` | Intero senza segno a larghezza arbitraria di bit (C23) | +> **Best Practice per Codice Portabile** +> +> - Usa **Tipi Portabili** (`int`, `uint`, `i64`, `u8`, ecc.) per tutta la logica Zen C pura. `int` è garantito essere a 32-bit con segno su tutte le architetture. +> - Usa **Tipi di Interop C** (`c_int`, `c_char`, `c_long`) **solo** quando interagisci con librerie C (FFI). La loro dimensione varia in base alla piattaforma e al compilatore C. +> - Usa `isize` e `usize` per indicizzazione di array e aritmetica dei puntatori. + ### 3. Tipi Aggregati #### Array @@ -1089,6 +1100,48 @@ Gli identifiers seguenti sono riservati poiché sono keyword nello standard C11: #### Operatori `and`, `or` +### 17. Interoperabilità C +Zen C offre due modi per interagire con il codice C: **Import Trusted** (Conveniente) e **FFI Esplicita** (Sicuro/Preciso). + +#### Metodo 1: Import Trusted (Conveniente) +Puoi importare un header C direttamente usando la parola chiave `import` con l'estensione `.h`. Questo tratta l'header come un modulo e assume che tutti i simboli acceduti esistano. + +```zc +//> link: -lm +import "math.h" as c_math; + +fn main() { + // Il compilatore si fida della correttezza; emette 'cos(...)' direttamente + let x = c_math::cos(3.14159); +} +``` + +> **Pro**: Zero boilerplate. Accesso immediato a tutto nell'header. +> **Contro**: Nessuna sicurezza dei tipi da Zen C (errori catturati dal compilatore C dopo). + +#### Metodo 2: FFI Esplicita (Sicuro) +Per un controllo rigoroso dei tipi o quando non vuoi includere il testo di un header, usa `extern fn`. + +```zc +include // Emette #include nel C generato + +// Definisci firma rigorosa +extern fn printf(fmt: char*, ...) -> c_int; + +fn main() { + printf("Ciao FFI: %d\n", 42); // Controllato nei tipi da Zen C +} +``` + +> **Pro**: Zen C assicura che i tipi corrispondano. +> **Contro**: Richiede dichiarazione manuale delle funzioni. + +#### `import` vs `include` + +- **`import "file.h"`**: Registra l'header come un modulo con nome. Abilita l'accesso implicito ai simboli (es. `file::function()`). +- **`include `**: Emette puramente `#include ` nel codice C generato. Non introduce alcun simbolo nel compilatore Zen C; devi usare `extern fn` per accedervi. + + --- ## Libreria Standard diff --git a/README_ZH_CN.md b/README_ZH_CN.md index aa1130b..af83e8c 100644 --- a/README_ZH_CN.md +++ b/README_ZH_CN.md @@ -100,6 +100,7 @@ - [命名约束](#命名约束) - [15. 构建指令](#15-构建指令) - [16. 关键字](#16-关键字) + - [17. C 互操作性](#17-c-互操作性) - [标准库](#标准库) - [工具链](#工具链) - [语言服务器 (LSP)](#语言服务器-lsp) @@ -203,7 +204,11 @@ let y: const int = 10; // 只读 (类型修饰) | 类型 | C 等效类型 | 描述 | |:---|:---|:---| -| `int`, `uint` | `int`, `unsigned int` | 平台标准整数 | +| `int`, `uint` | `int32_t`, `uint32_t` | 32位有符号/无符号整数 | +| `c_char`, `c_uchar` | `char`, `unsigned char` | C char (互操作) | +| `c_short`, `c_ushort` | `short`, `unsigned short` | C short (互操作) | +| `c_int`, `c_uint` | `int`, `unsigned int` | C int (互操作) | +| `c_long`, `c_ulong` | `long`, `unsigned long` | C long (互操作) | | `I8` .. `I128` 或 `i8` .. `i128` | `int8_t` .. `__int128_t` | 有符号固定宽度整数 | | `U8` .. `U128` 或 `u8` .. `u128` | `uint8_t` .. `__uint128_t` | 无符号固定宽度整数 | | `isize`, `usize` | `ptrdiff_t`, `size_t` | 指针大小的整数 | @@ -216,6 +221,12 @@ let y: const int = 10; // 只读 (类型修饰) | `iN` (例 `i256`) | `_BitInt(N)` | 任意位宽有符号整数 (C23) | | `uN` (例 `u42`) | `unsigned _BitInt(N)` | 任意位宽无符号整数 (C23) | +> **可移植代码最佳实践** +> +> - 对于所有纯 Zen C 逻辑,请使用 **可移植类型** (`int`、`uint`、`i64`、`u8` 等)。`int` 保证在所有架构上都是 32 位有符号整数。 +> - 仅在与 C 库 (FFI) 交互时使用 **C 互操作类型** (`c_int`、`c_char`、`c_long`)。它们的大小因平台和 C 编译器而异。 +> - 使用 `isize` 和 `usize` 进行数组索引和内存指针运算。 + ### 3. 复合类型 #### 数组 @@ -1091,6 +1102,51 @@ fn main() { ... } #### 运算符 `and`, `or` +### 17. C 互操作性 + +Zen C 提供了两种与 C 代码交互的方式:**信任导入 (Trusted Imports)** (方便) 和 **显式 FFI** (安全/精确)。 + +#### 方法 1: 信任导入 (方便) + +你可以使用 `import` 关键字直接导入 `.h` 扩展名的 C 头文件。这会将头文件视为一个模块,并假设通过它访问的所有符号都存在。 + +```zc +//> link: -lm +import "math.h" as c_math; + +fn main() { + // 编译器信任不仅正确;直接生成 'cos(...)' + let x = c_math::cos(3.14159); +} +``` + +> **优点**: 零样板代码。立即访问头文件中的所有内容。 +> **缺点**: Zen C 不提供类型安全 (错误将在稍后由 C 编译器捕获)。 + +#### 方法 2: 显式 FFI (安全) + +对于严格的类型检查,或当你不想包含头文件文本时,请使用 `extern fn`. + +```zc +include // 在生成的 C 代码中发出 #include + +// 定义严格签名 +extern fn printf(fmt: char*, ...) -> c_int; + +fn main() { + printf("Hello FFI: %d\n", 42); // 由 Zen C 进行类型检查 +} +``` + +> **优点**: Zen C 确保类型匹配。 +> **缺点**: 需要手动声明函数。 + +#### `import` vs `include` + +- **`import "file.h"`**: 将头文件注册为命名模块。启用对符号的隐式访问 (例如 `file::function()`)。 +- **`include `**: 纯粹在生成的 C 代码中发出 `#include `。不向 Zen C 编译器引入任何符号;必须使用 `extern fn` 才能访问它们。 + + --- ## 标准库 diff --git a/README_ZH_TW.md b/README_ZH_TW.md index 5d85a76..3a18a53 100644 --- a/README_ZH_TW.md +++ b/README_ZH_TW.md @@ -100,6 +100,7 @@ - [命名約束](#命名約束) - [15. 構建指令](#15-構建指令) - [16. 關鍵字](#16-關鍵字) + - [17. C 互操作性](#17-c-互操作性) - [標準庫](#標準庫) - [工具鏈](#工具鏈) - [語言服務器 (LSP)](#語言服務器-lsp) @@ -203,7 +204,11 @@ let y: const int = 10; // 只讀 (類型修飾) | 類型 | C 等效類型 | 描述 | |:---|:---|:---| -| `int`, `uint` | `int`, `unsigned int` | 平台標準整數 | +| `int`, `uint` | `int32_t`, `uint32_t` | 32位元有號/無號整數 | +| `c_char`, `c_uchar` | `char`, `unsigned char` | C char (互操作) | +| `c_short`, `c_ushort` | `short`, `unsigned short` | C short (互操作) | +| `c_int`, `c_uint` | `int`, `unsigned int` | C int (互操作) | +| `c_long`, `c_ulong` | `long`, `unsigned long` | C long (互操作) | | `I8` .. `I128` 或 `i8` .. `i128` | `int8_t` .. `__int128_t` | 有符號固定寬度整數 | | `U8` .. `U128` 或 `u8` .. `u128` | `uint8_t` .. `__uint128_t` | 無符號固定寬度整數 | | `isize`, `usize` | `ptrdiff_t`, `size_t` | 指針大小的整數 | @@ -214,7 +219,13 @@ let y: const int = 10; // 只讀 (類型修飾) | `string` | `char*` | C-string (以 null 結尾) | | `U0`, `u0`, `void` | `void` | 空類型 | | `iN` (例 `i256`) | `_BitInt(N)` | 任意位元寬度有號整數 (C23) | -| `uN` (例 `u42`) | `unsigned _BitInt(N)` | 任意位元寬度無號整數 (C23) | +| `uN` (例 `u42`) | `unsigned _BitInt(N)` | 任意位寬無號整數 (C23) | + +> **可移植代碼最佳實踐** +> +> - 對於所有純 Zen C 邏輯,請使用 **可移植類型** (`int`、`uint`、`i64`、`u8` 等)。`int` 保證在所有架構上都是 32 位元有號整數。 +> - 僅在與 C 庫 (FFI) 交互時使用 **C 互操作類型** (`c_int`、`c_char`、`c_long`)。它們的大小因平台和 C 編譯器而異。 +> - 使用 `isize` 和 `usize` 進行數組索引和內存指針運算。 ### 3. 複合類型 @@ -1091,6 +1102,51 @@ fn main() { ... } #### 運算符 `and`, `or` +### 17. C 互操作性 + +Zen C 提供了兩種與 C 代碼交互的方式:**信任導入 (Trusted Imports)** (方便) 和 **顯式 FFI** (安全/精確)。 + +#### 方法 1: 信任導入 (方便) + +你可以使用 `import` 關鍵字直接導入 `.h` 擴展名的 C 頭文件。這會將頭文件視為一個模塊,並假設通過它訪問的所有符號都存在。 + +```zc +//> link: -lm +import "math.h" as c_math; + +fn main() { + // 編譯器信任不僅正確;直接生成 'cos(...)' + let x = c_math::cos(3.14159); +} +``` + +> **優點**: 零樣板代碼。立即訪問頭文件中的所有內容。 +> **缺點**: Zen C 不提供類型安全 (錯誤將在稍後由 C 編譯器捕獲)。 + +#### 方法 2: 顯式 FFI (安全) + +對於嚴格的類型檢查,或當你不想包含頭文件文本時,請使用 `extern fn`. + +```zc +include // 在生成的 C 代碼中發出 #include + +// 定義嚴格簽名 +extern fn printf(fmt: char*, ...) -> c_int; + +fn main() { + printf("Hello FFI: %d\n", 42); // 由 Zen C 進行類型檢查 +} +``` + +> **優點**: Zen C 確保類型匹配。 +> **缺點**: 需要手動聲明函數。 + +#### `import` vs `include` + +- **`import "file.h"`**: 將頭文件註冊為命名模塊。啟用對符號的隱式訪問 (例如 `file::function()`)。 +- **`include `**: 純粹在生成的 C 代碼中發出 `#include `。不向 Zen C 編譯器引入任何符號;必須使用 `extern fn` 才能訪問它們。 + + --- ## 標準庫 diff --git a/src/ast/ast.c b/src/ast/ast.c index 439a9f5..1b35500 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -259,6 +259,25 @@ static char *type_to_string_impl(Type *t) return xstrdup("int32_t"); case TYPE_UINT: return xstrdup("unsigned int"); + + // Portable C Types + case TYPE_C_INT: + return xstrdup("c_int"); + case TYPE_C_UINT: + return xstrdup("c_uint"); + case TYPE_C_LONG: + return xstrdup("c_long"); + case TYPE_C_ULONG: + return xstrdup("c_ulong"); + case TYPE_C_SHORT: + return xstrdup("c_short"); + case TYPE_C_USHORT: + return xstrdup("c_ushort"); + case TYPE_C_CHAR: + return xstrdup("c_char"); + case TYPE_C_UCHAR: + return xstrdup("c_uchar"); + case TYPE_INT: return xstrdup("int"); case TYPE_FLOAT: @@ -461,8 +480,29 @@ static char *type_to_c_string_impl(Type *t) return xstrdup("int32_t"); case TYPE_UINT: return xstrdup("unsigned int"); - case TYPE_INT: + + // Portable C Types (Map directly to C types) + case TYPE_C_INT: return xstrdup("int"); + case TYPE_C_UINT: + return xstrdup("unsigned int"); + case TYPE_C_LONG: + return xstrdup("long"); + case TYPE_C_ULONG: + return xstrdup("unsigned long"); + case TYPE_C_SHORT: + return xstrdup("short"); + case TYPE_C_USHORT: + return xstrdup("unsigned short"); + case TYPE_C_CHAR: + return xstrdup("char"); + case TYPE_C_UCHAR: + return xstrdup("unsigned char"); + + case TYPE_INT: + // 'int' in Zen C maps to 'i32' now for portability. + // FFI should use c_int. + return xstrdup("int32_t"); case TYPE_FLOAT: return xstrdup("float"); case TYPE_BITINT: @@ -519,8 +559,11 @@ static char *type_to_c_string_impl(Type *t) return res; } - char *res = xmalloc(strlen(inner) + 7); - sprintf(res, "Slice_%s", inner); + char *inner_zens = type_to_string(t->inner); + char *res = xmalloc(strlen(inner_zens) + 7); + sprintf(res, "Slice_%s", inner_zens); + free(inner_zens); + free(inner); return res; } @@ -561,7 +604,12 @@ static char *type_to_c_string_impl(Type *t) return xstrdup("z_closure_T"); case TYPE_GENERIC: - return xstrdup(t->name); + // Use type_to_string to get the mangled name (e.g. Option_int) instead of raw C string + // composition This ensures consistency with struct definitions. + { + char *s = type_to_string(t); + return s; + } case TYPE_ALIAS: return type_to_c_string(t->inner); diff --git a/src/ast/ast.h b/src/ast/ast.h index 4498d7c..fa67043 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -28,30 +28,40 @@ typedef enum */ typedef enum { - TYPE_VOID, ///< `void` type. - TYPE_BOOL, ///< `bool` type. - TYPE_CHAR, ///< `char` type. - TYPE_STRING, ///< `string` type. - TYPE_U0, ///< `u0` type. - TYPE_I8, ///< `i8` type. - TYPE_U8, ///< `u8` type. - TYPE_I16, ///< `i16` type. - TYPE_U16, ///< `u16` type. - TYPE_I32, ///< `i32` type. - TYPE_U32, ///< `u32` type. - TYPE_I64, ///< `i64` type. - TYPE_U64, ///< `u64` type. - TYPE_I128, ///< `i128` type. - TYPE_U128, ///< `u128` type. - TYPE_F32, ///< `f32` type. - TYPE_F64, ///< `f64` type. - TYPE_INT, ///< `int` (alias, usually i32). - TYPE_FLOAT, ///< `float` (alias). - TYPE_USIZE, ///< `usize` (pointer size unsigned). - TYPE_ISIZE, ///< `isize` (pointer size signed). - TYPE_BYTE, ///< `byte`. - TYPE_RUNE, ///< `rune`. - TYPE_UINT, ///< `uint` (alias). + TYPE_VOID, ///< `void` type. + TYPE_BOOL, ///< `bool` type. + TYPE_CHAR, ///< `char` type. + TYPE_STRING, ///< `string` type. + TYPE_U0, ///< `u0` type. + TYPE_I8, ///< `i8` type. + TYPE_U8, ///< `u8` type. + TYPE_I16, ///< `i16` type. + TYPE_U16, ///< `u16` type. + TYPE_I32, ///< `i32` type. + TYPE_U32, ///< `u32` type. + TYPE_I64, ///< `i64` type. + TYPE_U64, ///< `u64` type. + TYPE_I128, ///< `i128` type. + TYPE_U128, ///< `u128` type. + TYPE_F32, ///< `f32` type. + TYPE_F64, ///< `f64` type. + TYPE_INT, ///< `int` (alias, usually i32). + TYPE_FLOAT, ///< `float` (alias). + TYPE_USIZE, ///< `usize` (pointer size unsigned). + TYPE_ISIZE, ///< `isize` (pointer size signed). + TYPE_BYTE, ///< `byte`. + TYPE_RUNE, ///< `rune`. + TYPE_UINT, ///< `uint` (alias). + // Portable C Types (FFI) + TYPE_C_INT, ///< `c_int` (int). + TYPE_C_UINT, ///< `c_uint` (unsigned int). + TYPE_C_LONG, ///< `c_long` (long). + TYPE_C_ULONG, ///< `c_ulong` (unsigned long). + TYPE_C_SHORT, ///< `c_short` (short). + TYPE_C_USHORT, ///< `c_ushort` (unsigned short). + TYPE_C_CHAR, ///< `c_char` (char). + TYPE_C_UCHAR, ///< `c_uchar` (unsigned char). + TYPE_STRUCT, ///< Struct type. TYPE_ENUM, ///< Enum type. TYPE_POINTER, ///< Pointer type (*). diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index 0496a46..384820b 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -1150,14 +1150,102 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) break; } case NODE_EXPR_CAST: - fprintf(out, "((%s)(", node->cast.target_type); + { + const char *t = node->cast.target_type; + const char *mapped = t; + if (strcmp(t, "c_int") == 0) + { + mapped = "int"; + } + else if (strcmp(t, "c_uint") == 0) + { + mapped = "unsigned int"; + } + else if (strcmp(t, "c_long") == 0) + { + mapped = "long"; + } + else if (strcmp(t, "c_ulong") == 0) + { + mapped = "unsigned long"; + } + else if (strcmp(t, "c_short") == 0) + { + mapped = "short"; + } + else if (strcmp(t, "c_ushort") == 0) + { + mapped = "unsigned short"; + } + else if (strcmp(t, "c_char") == 0) + { + mapped = "char"; + } + else if (strcmp(t, "c_uchar") == 0) + { + mapped = "unsigned char"; + } + else if (strcmp(t, "int") == 0) + { + mapped = "int32_t"; + } + else if (strcmp(t, "uint") == 0) + { + mapped = "unsigned int"; + } + + fprintf(out, "((%s)(", mapped); codegen_expression(ctx, node->cast.expr, out); fprintf(out, "))"); break; + } case NODE_EXPR_SIZEOF: if (node->size_of.target_type) { - fprintf(out, "sizeof(%s)", node->size_of.target_type); + const char *t = node->size_of.target_type; + const char *mapped = t; + if (strcmp(t, "c_int") == 0) + { + mapped = "int"; + } + else if (strcmp(t, "c_uint") == 0) + { + mapped = "unsigned int"; + } + else if (strcmp(t, "c_long") == 0) + { + mapped = "long"; + } + else if (strcmp(t, "c_ulong") == 0) + { + mapped = "unsigned long"; + } + else if (strcmp(t, "c_short") == 0) + { + mapped = "short"; + } + else if (strcmp(t, "c_ushort") == 0) + { + mapped = "unsigned short"; + } + else if (strcmp(t, "c_char") == 0) + { + mapped = "char"; + } + else if (strcmp(t, "c_uchar") == 0) + { + mapped = "unsigned char"; + } + else if (strcmp(t, "int") == 0) + { + mapped = "int32_t"; // Strict mapping + } + else if (strcmp(t, "uint") == 0) + { + mapped = "unsigned int"; // uint alias + } + + fprintf(out, "sizeof(%s)", mapped); } else { diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c index 31bd2ee..1623ffc 100644 --- a/src/codegen/codegen_decl.c +++ b/src/codegen/codegen_decl.c @@ -1130,12 +1130,23 @@ void print_type_defs(ParserContext *ctx, FILE *out, ASTNode *nodes) fprintf(out, "typedef struct Tuple_%s Tuple_%s;\nstruct Tuple_%s { ", t->sig, t->sig, t->sig); char *s = xstrdup(t->sig); - char *p = strtok(s, "_"); + char *current = s; + char *next_sep = strstr(current, "__"); int i = 0; - while (p) + while (current) { - fprintf(out, "%s v%d; ", p, i++); - p = strtok(NULL, "_"); + if (next_sep) + { + *next_sep = 0; + fprintf(out, "%s v%d; ", current, i++); + current = next_sep + 2; + next_sep = strstr(current, "__"); + } + else + { + fprintf(out, "%s v%d; ", current, i++); + break; + } } free(s); fprintf(out, "};\n"); diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c index 7c53d96..5bf0089 100644 --- a/src/parser/parser_expr.c +++ b/src/parser/parser_expr.c @@ -1908,7 +1908,7 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l) { Type *formal_type = parse_type_formal(ctx, l); concrete_types[arg_count] = type_to_string(formal_type); - unmangled_types[arg_count] = type_to_c_string(formal_type); + unmangled_types[arg_count] = type_to_string(formal_type); arg_count++; if (lexer_peek(l).type == TOK_COMMA) @@ -2944,7 +2944,7 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l) { if (i > 0) { - strcat(sig, "_"); + strcat(sig, "__"); } strcat(sig, type_strs[i]); } diff --git a/src/parser/parser_struct.c b/src/parser/parser_struct.c index e53b56c..c89ad34 100644 --- a/src/parser/parser_struct.c +++ b/src/parser/parser_struct.c @@ -863,7 +863,7 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union, int is_opaque) Token field_name = lexer_next(l); lexer_next(l); // eat : Type *ft = parse_type_formal(ctx, l); - char *field_type_str = type_to_string(ft); + char *field_type_str = type_to_c_string(ft); expect(l, TOK_SEMICOLON, "Expected ;"); ASTNode *nf = ast_create(NODE_FIELD); @@ -947,7 +947,7 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union, int is_opaque) Token f_name = lexer_next(l); expect(l, TOK_COLON, "Expected :"); Type *ft = parse_type_formal(ctx, l); - char *f_type = type_to_string(ft); + char *f_type = type_to_c_string(ft); ASTNode *f = ast_create(NODE_FIELD); f->field.name = token_strdup(f_name); @@ -1120,7 +1120,7 @@ ASTNode *parse_enum(ParserContext *ctx, Lexer *l) while (lexer_peek(l).type == TOK_COMMA) { lexer_next(l); // eat , - strcat(sig, "_"); + strcat(sig, "__"); Type *next_t = parse_type_obj(ctx, l); char *ns = type_to_string(next_t); if (strlen(sig) + strlen(ns) + 2 > 510) diff --git a/src/parser/parser_type.c b/src/parser/parser_type.c index 49e961c..fcbe12d 100644 --- a/src/parser/parser_type.c +++ b/src/parser/parser_type.c @@ -427,13 +427,13 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l) if (strcmp(name, "uint") == 0) { free(name); - return type_new(TYPE_UINT); + return type_new(TYPE_U32); // Strict uint32_t } if (strcmp(name, "int") == 0) { free(name); - return type_new(TYPE_INT); + return type_new(TYPE_I32); // Strict int32_t } if (strcmp(name, "float") == 0) { @@ -467,23 +467,31 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l) } if (strcmp(name, "long") == 0) { + zwarn_at(t, "'long' is treated as portable 'int64_t' in Zen C. Use 'c_long' for " + "platform-dependent C long."); free(name); return type_new(TYPE_I64); } if (strcmp(name, "short") == 0) { + zwarn_at(t, "'short' is treated as portable 'int16_t' in Zen C. Use 'c_short' for " + "platform-dependent C short."); free(name); return type_new(TYPE_I16); } if (strcmp(name, "unsigned") == 0) { + zwarn_at(t, "'unsigned' is treated as portable 'uint32_t' in Zen C. Use 'c_uint' for " + "platform-dependent C unsigned int."); free(name); - return type_new(TYPE_UINT); + return type_new(TYPE_U32); } if (strcmp(name, "signed") == 0) { + zwarn_at(t, "'signed' is treated as portable 'int32_t' in Zen C. Use 'c_int' for " + "platform-dependent C int."); free(name); - return type_new(TYPE_INT); + return type_new(TYPE_I32); } if (strcmp(name, "int8_t") == 0) { @@ -536,6 +544,48 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l) return type_new(TYPE_ISIZE); } + // Portable C Types + if (strcmp(name, "c_int") == 0) + { + free(name); + return type_new(TYPE_C_INT); + } + if (strcmp(name, "c_uint") == 0) + { + free(name); + return type_new(TYPE_C_UINT); + } + if (strcmp(name, "c_long") == 0) + { + free(name); + return type_new(TYPE_C_LONG); + } + if (strcmp(name, "c_ulong") == 0) + { + free(name); + return type_new(TYPE_C_ULONG); + } + if (strcmp(name, "c_short") == 0) + { + free(name); + return type_new(TYPE_C_SHORT); + } + if (strcmp(name, "c_ushort") == 0) + { + free(name); + return type_new(TYPE_C_USHORT); + } + if (strcmp(name, "c_char") == 0) + { + free(name); + return type_new(TYPE_C_CHAR); + } + if (strcmp(name, "c_uchar") == 0) + { + free(name); + return type_new(TYPE_C_UCHAR); + } + // Relaxed Type Check: If explicit 'struct Name', trust the user. if (explicit_struct) { @@ -677,7 +727,7 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l) zpanic_at(t, "Expected > after generic"); } - char *unmangled_arg = type_to_c_string(first_arg); + char *unmangled_arg = type_to_string(first_arg); int is_single_dep = 0; for (int k = 0; k < ctx->known_generics_count; ++k) @@ -791,7 +841,7 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l) if (lexer_peek(l).type == TOK_COMMA) { lexer_next(l); - strcat(sig, "_"); + strcat(sig, "__"); } else { diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c index 28d2c11..8ea2934 100644 --- a/src/parser/parser_utils.c +++ b/src/parser/parser_utils.c @@ -691,16 +691,22 @@ void register_tuple(ParserContext *ctx, const char *sig) s_def->strct.name = xstrdup(struct_name); char *s_sig = xstrdup(sig); - char *tok = strtok(s_sig, "_"); + char *current = s_sig; + char *next_sep = strstr(current, "__"); ASTNode *head = NULL, *tail = NULL; int i = 0; - while (tok) + while (current) { + if (next_sep) + { + *next_sep = 0; + } + ASTNode *f = ast_create(NODE_FIELD); char fname[32]; sprintf(fname, "v%d", i++); f->field.name = xstrdup(fname); - f->field.type = xstrdup(tok); + f->field.type = xstrdup(current); if (!head) { @@ -712,7 +718,15 @@ void register_tuple(ParserContext *ctx, const char *sig) } tail = f; - tok = strtok(NULL, "_"); + if (next_sep) + { + current = next_sep + 2; + next_sep = strstr(current, "__"); + } + else + { + break; + } } free(s_sig); s_def->strct.fields = head; diff --git a/std/fs.zc b/std/fs.zc index a00993b..5b2cb21 100644 --- a/std/fs.zc +++ b/std/fs.zc @@ -15,9 +15,9 @@ include include // Direct externs for simple functions with const char* parameters -extern fn access(pathname: const char*, mode: int) -> int; -extern fn unlink(pathname: const char*) -> int; -extern fn rmdir(pathname: const char*) -> int; +extern fn access(pathname: const char*, mode: c_int) -> c_int; +extern fn unlink(pathname: const char*) -> c_int; +extern fn rmdir(pathname: const char*) -> c_int; extern fn malloc(size: usize) -> void*; extern fn free(ptr: void*); @@ -90,17 +90,17 @@ raw { } } -extern fn _z_fs_mkdir(path: const char*) -> int; -extern fn _z_fs_get_metadata(path: const char*, size: U64*, is_dir: int*, is_file: int*) -> int; -extern fn _z_fs_read_entry(dir: void*, out_name: char*, buf_size: int, is_dir: int*) -> int; +extern fn _z_fs_mkdir(path: const char*) -> c_int; +extern fn _z_fs_get_metadata(path: const char*, size: U64*, is_dir: c_int*, is_file: c_int*) -> c_int; +extern fn _z_fs_read_entry(dir: void*, out_name: char*, buf_size: c_int, is_dir: c_int*) -> c_int; extern fn _z_fs_fopen(path: const char*, mode: const char*) -> void*; -extern fn _z_fs_fclose(stream: void*) -> int; +extern fn _z_fs_fclose(stream: void*) -> c_int; extern fn _z_fs_fread(ptr: void*, size: usize, nmemb: usize, stream: void*) -> usize; extern fn _z_fs_fwrite(ptr: void*, size: usize, nmemb: usize, stream: void*) -> usize; -extern fn _z_fs_fseek(stream: void*, offset: I64, whence: int) -> int; +extern fn _z_fs_fseek(stream: void*, offset: I64, whence: c_int) -> c_int; extern fn _z_fs_ftell(stream: void*) -> I64; extern fn _z_fs_opendir(name: const char*) -> void*; -extern fn _z_fs_closedir(dir: void*) -> int; +extern fn _z_fs_closedir(dir: void*) -> c_int; struct File { @@ -191,41 +191,50 @@ impl File { } fn exists(path: char*) -> bool { - return access(path, Z_F_OK) == 0; + let zero: c_int = 0; + return access(path, Z_F_OK) == zero; } fn metadata(path: char*) -> Result { let size: uint64_t; - let is_d: int; - let is_f: int; + let is_d: c_int; + let is_f: c_int; - if (_z_fs_get_metadata(path, &size, &is_d, &is_f) != 0) { + let res = _z_fs_get_metadata(path, &size, &is_d, &is_f); + let non_zero: c_int = 0; + if (res != non_zero) { return Result::Err("Failed to get metadata"); } return Result::Ok(Metadata { size: (U64)size, - is_dir: is_d != 0, - is_file: is_f != 0 + is_dir: is_d != non_zero, + is_file: is_f != non_zero }); } fn create_dir(path: char*) -> Result { - if (_z_fs_mkdir(path) != 0) { + let res = _z_fs_mkdir(path); + let zero: c_int = 0; + if (res != zero) { return Result::Err("Failed to create directory"); } return Result::Ok(true); } fn remove_file(path: char*) -> Result { - if (unlink(path) != 0) { + let res = unlink(path); + let zero: c_int = 0; + if (res != zero) { return Result::Err("Failed to remove file"); } return Result::Ok(true); } fn remove_dir(path: char*) -> Result { - if (rmdir(path) != 0) { + let res = rmdir(path); + let zero: c_int = 0; + if (res != zero) { return Result::Err("Failed to remove directory"); } return Result::Ok(true); @@ -245,17 +254,19 @@ impl File { return Result< Vec >::Err("Out of memory"); } - let is_d: int = 0; + let is_d: c_int = 0; + let is_d_zero: c_int = 0; while (_z_fs_read_entry(dir, name_buf, 256, &is_d)) { - if (strcmp(name_buf, ".") == 0 || strcmp(name_buf, "..") == 0) { + let zero_cmp: c_int = 0; + if (strcmp(name_buf, ".") == zero_cmp || strcmp(name_buf, "..") == zero_cmp) { continue; } let s = String::new(name_buf); let ent = DirEntry { name: s, - is_dir: is_d != 0 + is_dir: is_d != is_d_zero }; // Transfer ownership: String -> DirEntry diff --git a/std/io.zc b/std/io.zc index a5a7359..d9829dd 100644 --- a/std/io.zc +++ b/std/io.zc @@ -6,11 +6,11 @@ include include // These work directly with const char* in extern declarations -extern fn vprintf(fmt: const char*, ap: va_list) -> int; +extern fn vprintf(fmt: const char*, ap: va_list) -> c_int; // vsnprintf is problematic on macOS because it's a macro that expands to a builtin with a different signature // so we wrap it in a C function to avoid the conflict -extern fn _z_vsnprintf(str: char*, size: usize, fmt: const char*, ap: va_list) -> int; +extern fn _z_vsnprintf(str: char*, size: usize, fmt: const char*, ap: va_list) -> c_int; // EOF is typically -1, but we define it for portability def Z_EOF = -1; @@ -27,7 +27,7 @@ raw { } extern fn _z_get_stdin() -> void*; -extern fn _z_fgetc(stream: void*) -> int; +extern fn _z_fgetc(stream: void*) -> c_int; fn format(fmt: char*, ...) -> char* { static let buffer: char[1024]; @@ -40,7 +40,7 @@ fn format(fmt: char*, ...) -> char* { return (char*)buffer; } -fn format_into(buffer: char*, size: usize, fmt: char*, ...) -> int { +fn format_into(buffer: char*, size: usize, fmt: char*, ...) -> c_int { let ap: va_list; va_start(ap, fmt); @@ -63,7 +63,7 @@ fn format_new(fmt: char*, ...) -> char* { return buffer; } -fn print(fmt: char*, ...) -> int { +fn print(fmt: char*, ...) -> c_int { let ap: va_list; va_start(ap, fmt); let ret = vprintf(fmt, ap); @@ -71,7 +71,7 @@ fn print(fmt: char*, ...) -> int { return ret; } -fn println(fmt: char*, ...) -> int { +fn println(fmt: char*, ...) -> c_int { let ap: va_list; va_start(ap, fmt); let ret = vprintf(fmt, ap); @@ -86,13 +86,15 @@ fn readln() -> char* { let line: char* = malloc(cap); if (line == NULL) return NULL; - let c: int; + let c: c_int; let std_in = _z_get_stdin(); while (true) { c = _z_fgetc(std_in); - if (c == Z_EOF) break; - if (c == 10) break; // '\n' + let eof_c: c_int = Z_EOF; + if (c == eof_c) break; + let nl_c: c_int = 10; + if (c == nl_c) break; // '\n' if (len + 1 >= cap) { cap = cap * 2; @@ -108,7 +110,8 @@ fn readln() -> char* { len = len + 1; } - if (len == 0 && c == Z_EOF) { + let eof_final: c_int = Z_EOF; + if (len == 0 && c == eof_final) { free(line); return NULL; } diff --git a/std/net.zc b/std/net.zc index 826b795..dce1a01 100644 --- a/std/net.zc +++ b/std/net.zc @@ -14,9 +14,9 @@ def Z_AF_INET = 2; def Z_SOCK_STREAM = 1; // Direct externs for simple socket functions -extern fn socket(domain: int, type: int, proto: int) -> int; -extern fn close(fd: int) -> int; -extern fn read(fd: int, buf: void*, count: usize) -> isize; +extern fn socket(domain: c_int, type: c_int, proto: c_int) -> c_int; +extern fn close(fd: c_int) -> c_int; +extern fn read(fd: c_int, buf: void*, count: usize) -> isize; // Minimal raw block: required for struct sockaddr_in usage // These functions encapsulate sockaddr_in setup because the struct layout @@ -56,47 +56,63 @@ raw { } } -extern fn _z_net_bind(fd: int, host: const char*, port: int) -> int; -extern fn _z_net_connect(fd: int, host: const char*, port: int) -> int; -extern fn _z_net_accept(fd: int) -> int; -extern fn _z_net_write(fd: int, buf: const char*, n: usize) -> isize; +extern fn _z_net_bind(fd: c_int, host: const char*, port: c_int) -> c_int; +extern fn _z_net_connect(fd: c_int, host: const char*, port: c_int) -> c_int; +extern fn _z_net_accept(fd: c_int) -> c_int; +extern fn _z_net_write(fd: c_int, buf: const char*, n: usize) -> isize; struct TcpStream { - handle: int; + handle: c_int; } -extern fn strerror(errnum: int) -> char*; +extern fn strerror(errnum: c_int) -> char*; impl TcpStream { fn read(self, buf: char*, len: usize) -> Result { let n = read(self.handle - 1, (void*)buf, len); - if (n < 0) return Result::Err(strerror(errno)); + let zero: c_int = 0; + if (n < (isize)zero) return Result::Err(strerror(errno)); return Result::Ok((usize)n); } - fn write(self, buf: char*, len: usize) -> Result { - let n = _z_net_write(self.handle - 1, buf, len); - if (n < 0) return Result::Err("Write failed"); + fn write(self, buf: u8*, len: usize) -> Result { + let one: c_int = 1; + let n: isize = _z_net_write(self.handle - one, buf, len); + let zero: isize = 0; + if (n < zero) return Result::Err("Write failed"); return Result::Ok((usize)n); } fn close(self) { - if (self.handle > 0) { - close(self.handle - 1); - self.handle = 0; + let zero: c_int = 0; + if (self.handle > zero) { + let one: c_int = 1; + close(self.handle - one); + self.handle = zero; } } - fn connect(host: char*, port: int) -> Result { - let fd = socket(Z_AF_INET, Z_SOCK_STREAM, 0); - if (fd < 0) return Result::Err("Failed to create socket"); + fn connect(host: char*, port: c_int) -> Result { + let zero: c_int = 0; + let fd = socket(Z_AF_INET, Z_SOCK_STREAM, zero); + if (fd < zero) return Result::Err("Failed to create socket"); + + // C constants like -1 + let neg_one: c_int = -1; let res = _z_net_connect(fd, host, port); - if (res == -1) { close(fd); return Result::Err("Invalid address"); } - if (res == -2) { close(fd); return Result::Err("Connection failed"); } + if (res == neg_one) { close(fd); return Result::Err("Invalid address"); } + // _z_net_connect might return -2? The original code had it. + // Assuming -2 is also possible... check implementation or just assume logic was correct. + // But wait, the original code had: + // if (res == -1) ... if (res == -2) ... + // I will keep it but cast strict. + let neg_two: c_int = -2; + if (res == neg_two) { close(fd); return Result::Err("Connection failed"); } - return Result::Ok(TcpStream { handle: fd + 1 }); + let one: c_int = 1; + return Result::Ok(TcpStream { handle: fd + one }); } } @@ -107,32 +123,42 @@ impl Drop for TcpStream { } struct TcpListener { - handle: int; + handle: c_int; } impl TcpListener { - fn bind(host: char*, port: int) -> Result { - let fd = socket(Z_AF_INET, Z_SOCK_STREAM, 0); - if (fd < 0) return Result::Err("Failed to create socket"); + fn bind(host: char*, port: c_int) -> Result { + let zero: c_int = 0; + let fd = socket(Z_AF_INET, Z_SOCK_STREAM, zero); + if (fd < zero) return Result::Err("Failed to create socket"); let res = _z_net_bind(fd, host, port); - if (res == -1) { close(fd); return Result::Err("Invalid address"); } - if (res == -2) { close(fd); return Result::Err("Bind failed"); } - if (res == -3) { close(fd); return Result::Err("Listen failed"); } + let neg_one: c_int = -1; + let neg_two: c_int = -2; + let neg_three: c_int = -3; + + if (res == neg_one) { close(fd); return Result::Err("Invalid address"); } + if (res == neg_two) { close(fd); return Result::Err("Bind failed"); } + if (res == neg_three) { close(fd); return Result::Err("Listen failed"); } - return Result::Ok(TcpListener { handle: fd + 1 }); + let one: c_int = 1; + return Result::Ok(TcpListener { handle: fd + one }); } fn accept(self) -> Result { - let client_fd = _z_net_accept(self.handle - 1); - if (client_fd < 0) return Result::Err("Accept failed"); - return Result::Ok(TcpStream { handle: client_fd + 1 }); + let one: c_int = 1; + let client_fd = _z_net_accept(self.handle - one); + let zero: c_int = 0; + if (client_fd < zero) return Result::Err("Accept failed"); + return Result::Ok(TcpStream { handle: client_fd + one }); } fn close(self) { - if (self.handle > 0) { - close(self.handle - 1); - self.handle = 0; + let zero: c_int = 0; + if (self.handle > zero) { + let one: c_int = 1; + close(self.handle - one); + self.handle = zero; } } } diff --git a/std/process.zc b/std/process.zc index 3ce43b6..9f432c0 100644 --- a/std/process.zc +++ b/std/process.zc @@ -9,7 +9,7 @@ include include // system() can be externed directly with const char* -extern fn system(command: const char*) -> int; +extern fn system(command: const char*) -> c_int; // Minimal raw block: only for opaque FILE* types // popen/pclose/fgets use FILE* which conflicts with void* @@ -28,8 +28,8 @@ raw { } extern fn _z_popen(command: const char*, type: const char*) -> void*; -extern fn _z_pclose(stream: void*) -> int; -extern fn _z_fgets(s: char*, size: int, stream: void*) -> char*; +extern fn _z_pclose(stream: void*) -> c_int; +extern fn _z_fgets(s: char*, size: c_int, stream: void*) -> char*; struct Output { stdout: String; @@ -88,7 +88,7 @@ impl Command { let buf = (char*)malloc(buf_size); while (true) { - let res = _z_fgets(buf, (int)buf_size, fp); + let res = _z_fgets(buf, (c_int)buf_size, fp); if (res == 0) break; let chunk = String::from(buf); diff --git a/std/string.zc b/std/string.zc index 0bc9539..54f11b2 100644 --- a/std/string.zc +++ b/std/string.zc @@ -90,7 +90,8 @@ impl String { } fn eq(self, other: String*) -> bool { - return strcmp(self.c_str(), (*other).c_str()) == 0; + let zero: c_int = 0; + return strcmp(self.c_str(), (*other).c_str()) == zero; } fn length(self) -> usize { @@ -146,7 +147,8 @@ impl String { fn starts_with(self, prefix: char*) -> bool { let plen = strlen(prefix); if plen > self.length() { return false; } - return strncmp(self.c_str(), prefix, plen) == 0; + let zero: c_int = 0; + return strncmp(self.c_str(), prefix, plen) == zero; } fn ends_with(self, suffix: char*) -> bool { @@ -154,7 +156,8 @@ impl String { let len = self.length(); if slen > len { return false; } let offset = (int)(len - slen); - return strcmp(self.c_str() + offset, suffix) == 0; + let zero: c_int = 0; + return strcmp(self.c_str() + offset, suffix) == zero; } fn free(self) { diff --git a/std/thread.zc b/std/thread.zc index 16f3ca1..78d2547 100644 --- a/std/thread.zc +++ b/std/thread.zc @@ -35,11 +35,11 @@ raw { if (ret == 0) { *out_handle = (size_t)pt; } - return ret; + return (int)ret; } static int _z_thread_join(void *handle) { - return pthread_join((pthread_t)handle, NULL); + return (int)pthread_join((pthread_t)handle, NULL); } static void _z_mutex_init(void *ptr) { @@ -63,13 +63,13 @@ raw { } } -extern fn _z_thread_spawn(ctx: void*, out: usize*) -> int; -extern fn _z_thread_join(handle: void*) -> int; +extern fn _z_thread_spawn(ctx: void*, out: usize*) -> c_int; +extern fn _z_thread_join(handle: void*) -> c_int; extern fn _z_mutex_init(ptr: void*); extern fn _z_mutex_lock(ptr: void*); extern fn _z_mutex_unlock(ptr: void*); extern fn _z_mutex_destroy(ptr: void*); -extern fn _z_usleep(micros: int); +extern fn _z_usleep(micros: c_int); @@ -79,26 +79,28 @@ struct Thread { impl Thread { fn spawn(func: fn()) -> Result { - let t: usize = 0; + let out_handle: usize = 0; - let ctx_copy = malloc(16); // z_closure_T is 16 bytes - if (ctx_copy == NULL) return Result::Err("OOM"); + let ctx = malloc(16); // z_closure_T is 16 bytes + if (ctx == NULL) return Result::Err("OOM"); - memcpy(ctx_copy, &func, 16); + memcpy(ctx, &func, 16); - let ret = _z_thread_spawn(ctx_copy, &t); - - if (ret != 0) { - free(ctx_copy); + let ret = _z_thread_spawn(ctx, &out_handle); + let zero: c_int = 0; + if (ret != zero) { + // Failed to spawn + free(ctx); return Result::Err("Failed to create thread"); } - return Result::Ok(Thread { handle: (void*)t }); + return Result::Ok(Thread { handle: (void*)out_handle }); } fn join(self) -> Result { let ret = _z_thread_join(self.handle); - if (ret != 0) return Result::Err("Join failed"); + let zero: c_int = 0; + if (ret != zero) return Result::Err("Join failed"); return Result::Ok(true); } } @@ -138,5 +140,6 @@ impl Drop for Mutex { } fn sleep_ms(ms: int) { - _z_usleep(ms * 1000); + let micros: c_int = (c_int)(ms * 1000); + _z_usleep(micros); } diff --git a/tests/collections/test_string_suite.zc b/tests/collections/test_string_suite.zc index afe08af..64ed9d8 100644 --- a/tests/collections/test_string_suite.zc +++ b/tests/collections/test_string_suite.zc @@ -91,7 +91,9 @@ test "test_fstrings_return" { let inner = f"Inner({x})"; let outer = f"Outer({inner})"; println "Composed: {outer}"; - assert(strcmp(outer, "Outer(Inner(100))") == 0, "Composed f-string failed"); + let outer_res = strcmp(outer, "Outer(Inner(100))"); + let zero: c_int = 0; + assert(outer_res == zero, "Composed f-string failed"); } test "test_string_std_ops" { diff --git a/tests/features/test_portable_types.zc b/tests/features/test_portable_types.zc new file mode 100644 index 0000000..8f54fcb --- /dev/null +++ b/tests/features/test_portable_types.zc @@ -0,0 +1,46 @@ + +import "std/io.zc"; + +// This test verifies the new portable integer types and C interop types. + +extern fn abs(x: c_int) -> c_int; +extern fn labs(x: c_long) -> c_long; + +fn main() -> int { + // Portable types + let a: i32 = -42; + let b: u32 = 42; + let c: i64 = -1000000; + let d: u64 = 1000000; + + if (a != -42) return 1; + if (b != 42) return 2; + if (c != -1000000) return 3; + if (d != 1000000) return 4; + + // C Types + let ca: c_int = -10; + let cb: c_long = -20; + let cc: c_short = -5; + let cd: c_char = 65; // 'A' + + // Test C interaction + let abs_val = abs(ca); + let expected_abs: c_int = 10; + if (abs_val != expected_abs) return 5; + + let labs_val = labs(cb); + let expected_labs: c_long = 20; + if (labs_val != expected_labs) return 6; + + // Size checks (these are platform dependent but we can check relations) + // sizeof(c_char) is always 1 + if (sizeof(c_char) != 1) return 7; + + // sizeof(c_short) <= sizeof(c_int) <= sizeof(c_long) + if (sizeof(c_short) > sizeof(c_int)) return 8; + if (sizeof(c_int) > sizeof(c_long)) return 9; + + printf("Portable types test passed.\n"); + return 0; +} -- cgit v1.2.3 From e2c4f3aebeb9aa49b68f26f92512759cbe43ee6b Mon Sep 17 00:00:00 2001 From: SAJJA EASWAR SAI Date: Mon, 2 Feb 2026 00:24:16 +0530 Subject: Fix: catch illegal static method calls with dot operator and fix NODE_EXPR_MEMBER token initialization --- src/parser/parser_expr.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c index 5bf0089..a732448 100644 --- a/src/parser/parser_expr.c +++ b/src/parser/parser_expr.c @@ -4116,6 +4116,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) break; } ASTNode *node = ast_create(NODE_EXPR_MEMBER); + node->token = field; node->member.target = lhs; node->member.field = token_strdup(field); node->member.is_pointer_access = 1; @@ -4169,6 +4170,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) break; } ASTNode *node = ast_create(NODE_EXPR_MEMBER); + node->token = field; node->member.target = lhs; node->member.field = token_strdup(field); node->member.is_pointer_access = 2; @@ -4350,6 +4352,52 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) if (sig) { + // Check if this is a static method being called with dot operator + // Static methods don't have 'self' as first parameter + int is_static_method = 0; + if (sig->total_args == 0) + { + // No arguments at all - definitely static + is_static_method = 1; + } + else if (sig->arg_types[0]) + { + // Check if first parameter is a pointer to the struct type + // Instance methods have: fn method(self) where self is StructType* + // Static methods have: fn method(x: int, y: int) etc. + Type *first_param = sig->arg_types[0]; + + // If first param is not a pointer, it's likely static + // OR if it's a pointer but not to this struct type + if (first_param->kind != TYPE_POINTER) + { + is_static_method = 1; + } + else if (first_param->inner) + { + // Check if the inner type matches the struct + char *inner_name = NULL; + if (first_param->inner->kind == TYPE_STRUCT) + { + inner_name = first_param->inner->name; + } + + if (!inner_name || strcmp(inner_name, struct_name) != 0) + { + is_static_method = 1; + } + } + } + + if (is_static_method) + { + zpanic_at(lhs->token, + "Cannot call static method '%s' with dot operator\n" + " = help: Use '%s::%s(...)' instead of instance.%s(...)", + lhs->member.field, struct_name, lhs->member.field, + lhs->member.field); + } + resolved_name = xstrdup(mangled); resolved_sig = sig; @@ -4772,6 +4820,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) break; } ASTNode *node = ast_create(NODE_EXPR_MEMBER); + node->token = field; node->member.target = lhs; node->member.field = token_strdup(field); node->member.is_pointer_access = 0; -- cgit v1.2.3 From d51c9d091951bd4e5d2d0ccd5f72dfcbbfb412ec Mon Sep 17 00:00:00 2001 From: SAJJA EASWAR SAI Date: Mon, 2 Feb 2026 02:44:34 +0530 Subject: Fix:#170 Detect incorrectly swapped struct and trait in impl block --- src/parser/parser_struct.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/parser/parser_struct.c b/src/parser/parser_struct.c index c89ad34..0c984a6 100644 --- a/src/parser/parser_struct.c +++ b/src/parser/parser_struct.c @@ -315,6 +315,16 @@ ASTNode *parse_impl(ParserContext *ctx, Lexer *l) register_generic(ctx, target_gen_param); } + // Check for common error: swapped Struct and Trait + // impl MyStruct for MyTrait (Wrong) vs impl MyTrait for MyStruct (Correct) + if (!is_trait(name1) && is_trait(name2)) + { + zpanic_at(t1, + "Incorrect usage of impl. Did you mean 'impl %s for %s'? Syntax is 'impl " + " for '", + name2, name1); + } + // 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) { -- cgit v1.2.3 From dadabf8b9d11d099777acc261068a3ed8ca06f24 Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Mon, 2 Feb 2026 00:33:12 +0000 Subject: Fix concurrency suite compilation errors involving C types --- src/ast/ast.c | 3 +++ src/parser/parser_expr.c | 3 +-- std/thread.zc | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ast/ast.c b/src/ast/ast.c index 1b35500..28fe678 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -101,6 +101,9 @@ int is_integer_type(Type *t) t->kind == TYPE_ISIZE || t->kind == TYPE_BYTE || t->kind == TYPE_RUNE || t->kind == TYPE_UINT || t->kind == TYPE_I128 || t->kind == TYPE_U128 || t->kind == TYPE_BITINT || t->kind == TYPE_UBITINT || + t->kind == TYPE_C_INT || t->kind == TYPE_C_UINT || t->kind == TYPE_C_LONG || + t->kind == TYPE_C_ULONG || t->kind == TYPE_C_SHORT || t->kind == TYPE_C_USHORT || + t->kind == TYPE_C_CHAR || t->kind == TYPE_C_UCHAR || (t->kind == TYPE_STRUCT && t->name && (0 == strcmp(t->name, "int8_t") || 0 == strcmp(t->name, "uint8_t") || 0 == strcmp(t->name, "int16_t") || 0 == strcmp(t->name, "uint16_t") || diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c index a732448..f27e2c3 100644 --- a/src/parser/parser_expr.c +++ b/src/parser/parser_expr.c @@ -370,8 +370,7 @@ static void check_format_string(ASTNode *call, Token t) if (spec == 'd' || spec == 'i' || spec == 'u' || spec == 'x' || spec == 'X' || spec == 'o') { - if (vt && vt->kind != TYPE_INT && vt->kind != TYPE_I64 && !type_is_unsigned(vt) && - vt->kind != TYPE_CHAR) + if (vt && !is_integer_type(vt)) { warn_format_string(t, arg_num, "integer", got_type); } diff --git a/std/thread.zc b/std/thread.zc index da21772..0722b60 100644 --- a/std/thread.zc +++ b/std/thread.zc @@ -108,10 +108,10 @@ impl Thread { memcpy(ctx, &func, 16); - let ret = _z_thread_spawn(ctx_copy, &t); + let ret = _z_thread_spawn(ctx, &out_handle); if ret != 0 { - free(ctx_copy); + free(ctx); return Result::Err("Failed to create thread"); } -- cgit v1.2.3