summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorczjstmax <jstmaxlol@disroot.org>2026-01-31 15:10:20 +0100
committerGitHub <noreply@github.com>2026-01-31 15:10:20 +0100
commitd2e2617dec584884b92eb452f377b20c0bf8f321 (patch)
treed7cdc28d1a83f16a0fc7e945963aa070bfa9d3e4
parent0427d254207a69e394499d1abaea768f484f1cb5 (diff)
parent051400c70a4d5384923113cfbcbc69e8e58d27a0 (diff)
Merge pull request #1 from z-libs/main
Merge newer updates
-rw-r--r--.github/copilot-instructions.md87
-rw-r--r--Makefile2
-rw-r--r--README.md16
-rw-r--r--README_ES.md15
-rw-r--r--README_ZH_CN.md30
-rw-r--r--README_ZH_TW.md14
-rw-r--r--docs/std/json.md26
-rw-r--r--docs/std/slice.md90
-rw-r--r--docs/std/string.md4
-rw-r--r--src/ast/ast.h2
-rw-r--r--src/codegen/codegen.c88
-rw-r--r--src/codegen/codegen.h4
-rw-r--r--src/codegen/codegen_decl.c6
-rw-r--r--src/codegen/codegen_main.c2
-rw-r--r--src/codegen/codegen_stmt.c2
-rw-r--r--src/codegen/codegen_utils.c48
-rw-r--r--src/parser/parser.h3
-rw-r--r--src/parser/parser_decl.c6
-rw-r--r--src/parser/parser_expr.c1
-rw-r--r--src/parser/parser_stmt.c154
-rw-r--r--src/parser/parser_struct.c2
-rw-r--r--src/parser/parser_utils.c53
-rw-r--r--std/core.zc6
-rw-r--r--std/cuda.zc2
-rw-r--r--std/env.zc29
-rw-r--r--std/fs.zc62
-rw-r--r--std/io.zc49
-rw-r--r--std/json.zc65
-rw-r--r--std/map.zc22
-rw-r--r--std/net.zc25
-rw-r--r--std/process.zc21
-rw-r--r--std/set.zc20
-rw-r--r--std/slice.zc35
-rw-r--r--std/string.zc22
-rw-r--r--std/thread.zc5
-rw-r--r--std/time.zc3
-rw-r--r--tests/std/test_direct_array_iteration.zc37
-rw-r--r--tests/std/test_json_serialization.zc149
-rw-r--r--tests/std/test_slice_iteration.zc29
39 files changed, 1059 insertions, 177 deletions
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<int>`) 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<T>` 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<T>` 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<T>` is a view. `int[N]` auto-converts to slice in loops.
+* **Generics**:
+ * Syntax: `struct Box<T> { val: T; }`.
+* **Concurrency**:
+ * Use `async fn` and `await` keyword.
+ * `Async<T>` 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/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..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
@@ -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 { ... }
@@ -607,7 +614,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.";
@@ -939,6 +946,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. |
| `@<custom>` | 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..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
@@ -606,7 +614,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.";
@@ -938,6 +946,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. |
| `@<custom>` | 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..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 { ... }
```
@@ -606,7 +613,7 @@ Zen C 支持使用 `?` 前缀进行用户输入提示的简写。
- `? "输入年龄: " (age)`: 打印提示并扫描输入到变量 `age` 中。
- 格式说明符会根据变量类型自动推断。
-```c
+```zc
let age: int;
? "你多大了? " (age);
println "你 {age} 岁了。";
@@ -938,6 +945,7 @@ let re = regex! { ^[a-z]+$ };
| `@host` | 函数 | CUDA: 主机函数 (`__host__`)。 |
| `@comptime` | 函数 | 用于编译时执行的辅助函数。 |
| `@derive(...)` | 结构体 | 自动实现 Trait。支持 `Debug`, `Eq` (智能派生), `Copy`, `Clone`。 |
+| `@ctype("type")` | 函数参数 | 覆盖参数生成的 C 类型。 |
| `@<custom>` | 任意 | 将泛型属性传递给 C (例如 `@flatten`, `@alias("name")`)。 |
#### 自定义属性
diff --git a/README_ZH_TW.md b/README_ZH_TW.md
index 8618540..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 { ... }
@@ -606,7 +613,7 @@ Zen C 支持使用 `?` 前綴進行用戶輸入提示的簡寫。
- `? "輸入年齡: " (age)`: 打印提示並掃描輸入到變量 `age` 中。
- 格式說明符會根據變量類型自動推斷。
-```c
+```zc
let age: int;
? "你多大了? " (age);
println "你 {age} 歲了。";
@@ -938,6 +945,7 @@ let re = regex! { ^[a-z]+$ };
| `@host` | 函數 | CUDA: 主機函數 (`__host__`)。 |
| `@comptime` | 函數 | 用於編譯時執行的輔助函數。 |
| `@derive(...)` | 結構體 | 自動實現 Trait。支持 `Debug`, `Eq` (智能派生), `Copy`, `Clone`。 |
+| `@ctype("type")` | 函數參數 | 覆蓋參數生成的 C 類型。 |
| `@<custom>` | 任意 | 將泛型屬性傳遞給 C (例如 `@flatten`, `@alias("name")`)。 |
#### 自定義屬性
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<JsonValue*>`**
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/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<T>` 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<int>::from_array((int*)(&arr), 5);
+ for val in slice {
+ println "{val}";
+ }
+}
+```
+
+## Structure
+
+```zc
+struct Slice<T> {
+ data: T*;
+ len: usize;
+}
+```
+
+## Methods
+
+### Construction
+
+| Method | Signature | Description |
+| :--- | :--- | :--- |
+| **from_array** | `Slice<T>::from_array(arr: T*, len: usize) -> Slice<T>` | Creates a slice view over an array. |
+
+### Iteration
+
+| Method | Signature | Description |
+| :--- | :--- | :--- |
+| **iterator** | `iterator(self) -> SliceIter<T>` | Returns an iterator for `for-in` loops. |
+
+`SliceIter<T>` implements the iterator protocol with a `next() -> Option<T>` 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<T>` | Returns the element at index, or None if out of bounds. |
+| **at** | `at(self, idx: usize) -> Option<T>` | Alias for `get`. |
+
+## Examples
+
+### Iterating over fixed-size arrays
+
+```zc
+let numbers: int[3] = [10, 20, 30];
+let slice = Slice<int>::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<int>::from_array((int*)(&arr), 3);
+
+let opt = slice.get(1);
+if (!opt.is_none()) {
+ println "Value: {opt.unwrap()}"; // Prints: Value: 2
+}
+```
+
+## Notes
+
+- `Slice<T>` does not own its data - it's just a view
+- No memory management needed (no `free()` method)
+- Must specify the generic type explicitly: `Slice<int>`, `Slice<String>`, etc.
+- The array pointer cast `(T*)(&arr)` is required for fixed-size arrays
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/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.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<T>::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<int> -> 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<int> -> Slice_int, Vec<Point> -> 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.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 391ebd3..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,9 +64,34 @@ 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 mangled_candidate[256];
+ char *gt = strchr(generic, '>');
+ int success = 0;
+
+ if (gt)
+ {
+ int base_len = generic - type_str;
+ int arg_len = gt - generic - 1;
+
+ // Limit check
+ if (base_len + arg_len + 2 < 256)
+ {
+ 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;
+ }
+ }
+ }
+
+ if (!success)
+ {
+ int base_len = generic - type_str;
+ fprintf(out, "%.*s %s", base_len, type_str, name);
+ }
if (bracket)
{
@@ -87,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
@@ -644,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)
{
@@ -714,7 +738,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,13 +753,14 @@ 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];
}
// 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)
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, &param_names, &is_varargs);
+ char *args = parse_and_convert_args(ctx, l, &defaults, &count, &arg_types, &param_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_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);
diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c
index a471fe6..7758ae3 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<T>::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<elem_type>
+ 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<T>
+ // 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 <user_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;
}
@@ -3179,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);
}
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, &param_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 <stdarg.h>
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 <stdlib.h>
-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<string> {
- let value: string = _z_env_get(name);
+ let value: string = getenv(name);
if (value == NULL) {
return Option<string>::None();
}
@@ -39,7 +28,7 @@ impl Env {
}
fn get_dup(name: string) -> Option<String> {
- let value: string = _z_env_get(name);
+ let value: string = getenv(name);
if (value == NULL) {
return Option<String>::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 <unistd.h>
include <stdlib.h>
include <stdio.h>
-// 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<Metadata> {
@@ -230,14 +218,14 @@ impl File {
}
fn remove_file(path: char*) -> Result<bool> {
- if (_z_fs_unlink(path) != 0) {
+ if (unlink(path) != 0) {
return Result<bool>::Err("Failed to remove file");
}
return Result<bool>::Ok(true);
}
fn remove_dir(path: char*) -> Result<bool> {
- if (_z_fs_rmdir(path) != 0) {
+ if (rmdir(path) != 0) {
return Result<bool>::Err("Failed to remove directory");
}
return Result<bool>::Ok(true);
diff --git a/std/io.zc b/std/io.zc
index 2793ecf..a5a7359 100644
--- a/std/io.zc
+++ b/std/io.zc
@@ -2,37 +2,51 @@
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 <stdio.h>
+include <stdarg.h>
- 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;
+
+// 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;
+
+// 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); }
+ int _z_vsnprintf(char* str, size_t size, const char* fmt, va_list ap) {
+ return vsnprintf(str, size, fmt, ap);
+ }
}
-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);
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 = _z_vsnprintf(buffer, size, fmt, ap);
va_end(ap);
+
return ret;
}
@@ -42,15 +56,17 @@ fn format_new(fmt: char*, ...) -> char* {
let ap: va_list;
va_start(ap, fmt);
+
_z_vsnprintf(buffer, 1024, fmt, ap);
va_end(ap);
+
return buffer;
}
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 +74,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 +88,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 +108,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/json.zc b/std/json.zc
index d373ab9..9f5cf73 100644
--- a/std/json.zc
+++ b/std/json.zc
@@ -457,3 +457,68 @@ impl Drop for JsonValue {
self.free();
}
}
+
+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/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<V> {
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 <stdio.h>
+include <stdlib.h>
+
+// 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<T> {
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<T> {
data: T*;
len: usize;
}
+struct SliceIter<T> {
+ data: T*;
+ count: usize;
+ idx: usize;
+}
+
+impl SliceIter<T> {
+ fn next(self) -> Option<T> {
+ if (self.idx < self.count) {
+ let item = self.data[self.idx];
+ self.idx = self.idx + 1;
+ return Option<T>::Some(item);
+ }
+ return Option<T>::None();
+ }
+
+ fn iterator(self) -> SliceIter<T> {
+ return *self;
+ }
+}
+
impl Slice<T> {
+ fn from_array(arr: T*, len: usize) -> Slice<T> {
+ return Slice<T> { data: arr, len: len };
+ }
+
+ fn iterator(self) -> SliceIter<T> {
+ return SliceIter<T> {
+ data: self.data,
+ count: self.len,
+ idx: 0
+ };
+ }
+
fn length(self) -> usize {
return self.len;
}
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/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 <unistd.h>
include <sys/time.h>
include <stdlib.h>
+// 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;
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_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
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<int>::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<int>::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");
+}