summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-02-01 14:01:51 +0000
committerZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-02-01 14:01:51 +0000
commitfbfce63744882d48ea2fc514ab1594000254db80 (patch)
treecc7949dab2149d829d3c04e3d542553ed883593f
parenteafd8c67012ea253436b79f703dc0702046703f8 (diff)
Related to #138
-rw-r--r--README.md58
-rw-r--r--README_ES.md57
-rw-r--r--README_IT.md55
-rw-r--r--README_ZH_CN.md58
-rw-r--r--README_ZH_TW.md60
-rw-r--r--src/ast/ast.c56
-rw-r--r--src/ast/ast.h58
-rw-r--r--src/codegen/codegen.c92
-rw-r--r--src/codegen/codegen_decl.c19
-rw-r--r--src/parser/parser_expr.c4
-rw-r--r--src/parser/parser_struct.c6
-rw-r--r--src/parser/parser_type.c62
-rw-r--r--src/parser/parser_utils.c22
-rw-r--r--std/fs.zc53
-rw-r--r--std/io.zc23
-rw-r--r--std/net.zc98
-rw-r--r--std/process.zc8
-rw-r--r--std/string.zc9
-rw-r--r--std/thread.zc35
-rw-r--r--tests/collections/test_string_suite.zc4
-rw-r--r--tests/features/test_portable_types.zc46
21 files changed, 737 insertions, 146 deletions
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 <stdio.h> // Emits #include <stdio.h> 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 <file.h>`**: Purely emits `#include <file.h>` 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 <stdio.h> // Emite #include <stdio.h> 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 <file.h>`**: Puramente emite `#include <file.h>` 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 <stdio.h> // Emette #include <stdio.h> 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 <file.h>`**: Emette puramente `#include <file.h>` 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 <stdio.h> // 在生成的 C 代码中发出 #include <stdio.h>
+
+// 定义严格签名
+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 <file.h>`**: 纯粹在生成的 C 代码中发出 `#include <file.h>`。不向 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 <stdio.h> // 在生成的 C 代碼中發出 #include <stdio.h>
+
+// 定義嚴格簽名
+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 <file.h>`**: 純粹在生成的 C 代碼中發出 `#include <file.h>`。不向 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 <stdlib.h>
include <stdio.h>
// 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<Metadata> {
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<Metadata>::Err("Failed to get metadata");
}
return Result<Metadata>::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<bool> {
- if (_z_fs_mkdir(path) != 0) {
+ let res = _z_fs_mkdir(path);
+ let zero: c_int = 0;
+ if (res != zero) {
return Result<bool>::Err("Failed to create directory");
}
return Result<bool>::Ok(true);
}
fn remove_file(path: char*) -> Result<bool> {
- if (unlink(path) != 0) {
+ let res = unlink(path);
+ let zero: c_int = 0;
+ if (res != zero) {
return Result<bool>::Err("Failed to remove file");
}
return Result<bool>::Ok(true);
}
fn remove_dir(path: char*) -> Result<bool> {
- if (rmdir(path) != 0) {
+ let res = rmdir(path);
+ let zero: c_int = 0;
+ if (res != zero) {
return Result<bool>::Err("Failed to remove directory");
}
return Result<bool>::Ok(true);
@@ -245,17 +254,19 @@ impl File {
return Result< Vec<DirEntry> >::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 <stdio.h>
include <stdarg.h>
// 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<usize> {
let n = read(self.handle - 1, (void*)buf, len);
- if (n < 0) return Result<usize>::Err(strerror(errno));
+ let zero: c_int = 0;
+ if (n < (isize)zero) return Result<usize>::Err(strerror(errno));
return Result<usize>::Ok((usize)n);
}
- fn write(self, buf: char*, len: usize) -> Result<usize> {
- let n = _z_net_write(self.handle - 1, buf, len);
- if (n < 0) return Result<usize>::Err("Write failed");
+ fn write(self, buf: u8*, len: usize) -> Result<usize> {
+ 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<usize>::Err("Write failed");
return Result<usize>::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<TcpStream> {
- let fd = socket(Z_AF_INET, Z_SOCK_STREAM, 0);
- if (fd < 0) return Result<TcpStream>::Err("Failed to create socket");
+ fn connect(host: char*, port: c_int) -> Result<TcpStream> {
+ let zero: c_int = 0;
+ let fd = socket(Z_AF_INET, Z_SOCK_STREAM, zero);
+ if (fd < zero) return Result<TcpStream>::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<TcpStream>::Err("Invalid address"); }
- if (res == -2) { close(fd); return Result<TcpStream>::Err("Connection failed"); }
+ if (res == neg_one) { close(fd); return Result<TcpStream>::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<TcpStream>::Err("Connection failed"); }
- return Result<TcpStream>::Ok(TcpStream { handle: fd + 1 });
+ let one: c_int = 1;
+ return Result<TcpStream>::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<TcpListener> {
- let fd = socket(Z_AF_INET, Z_SOCK_STREAM, 0);
- if (fd < 0) return Result<TcpListener>::Err("Failed to create socket");
+ fn bind(host: char*, port: c_int) -> Result<TcpListener> {
+ let zero: c_int = 0;
+ let fd = socket(Z_AF_INET, Z_SOCK_STREAM, zero);
+ if (fd < zero) return Result<TcpListener>::Err("Failed to create socket");
let res = _z_net_bind(fd, host, port);
- if (res == -1) { close(fd); return Result<TcpListener>::Err("Invalid address"); }
- if (res == -2) { close(fd); return Result<TcpListener>::Err("Bind failed"); }
- if (res == -3) { close(fd); return Result<TcpListener>::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<TcpListener>::Err("Invalid address"); }
+ if (res == neg_two) { close(fd); return Result<TcpListener>::Err("Bind failed"); }
+ if (res == neg_three) { close(fd); return Result<TcpListener>::Err("Listen failed"); }
- return Result<TcpListener>::Ok(TcpListener { handle: fd + 1 });
+ let one: c_int = 1;
+ return Result<TcpListener>::Ok(TcpListener { handle: fd + one });
}
fn accept(self) -> Result<TcpStream> {
- let client_fd = _z_net_accept(self.handle - 1);
- if (client_fd < 0) return Result<TcpStream>::Err("Accept failed");
- return Result<TcpStream>::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<TcpStream>::Err("Accept failed");
+ return Result<TcpStream>::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 <stdio.h>
include <stdlib.h>
// 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<Thread> {
- 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<Thread>::Err("OOM");
+ let ctx = malloc(16); // z_closure_T is 16 bytes
+ if (ctx == NULL) return Result<Thread>::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<Thread>::Err("Failed to create thread");
}
- return Result<Thread>::Ok(Thread { handle: (void*)t });
+ return Result<Thread>::Ok(Thread { handle: (void*)out_handle });
}
fn join(self) -> Result<bool> {
let ret = _z_thread_join(self.handle);
- if (ret != 0) return Result<bool>::Err("Join failed");
+ let zero: c_int = 0;
+ if (ret != zero) return Result<bool>::Err("Join failed");
return Result<bool>::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;
+}