diff options
52 files changed, 4272 insertions, 484 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. @@ -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 @@ -1,7 +1,7 @@ <div align="center"> -[English](README.md) • [简体中文](README_ZH_CN.md) • [繁體中文](README_ZH_TW.md) • [Español](README_ES.md) +[English](README.md) • [简体中文](README_ZH_CN.md) • [繁體中文](README_ZH_TW.md) • [Español](README_ES.md) • [Italiano](README_IT.md) </div> @@ -13,7 +13,7 @@ []() []() -[]() +[]() []() *Write like a high-level language, run like C.* @@ -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) @@ -165,7 +166,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 @@ -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 @@ -494,8 +505,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 +625,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 +957,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 @@ -989,12 +1008,13 @@ Zen C simplifies the complex GCC constraint syntax with named bindings. // Syntax: : out(variable) : in(variable) : clobber(reg) // Uses {variable} placeholder syntax for readability -fn add(a: int, b: int) -> int { +fn add_five(x: int) -> int { let result: int; asm { - "add {result}, {a}, {b}" + "mov {x}, {result}" + "add $5, {result}" : out(result) - : in(a), in(b) + : in(x) : clobber("cc") } return result; @@ -1083,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 9040ea8..07234f0 100644 --- a/README_ES.md +++ b/README_ES.md @@ -1,7 +1,7 @@ <div align="center"> -[English](README.md) • [简体中文](README_ZH_CN.md) • [繁體中文](README_ZH_TW.md) • [Español](README_ES.md) +[English](README.md) • [简体中文](README_ZH_CN.md) • [繁體中文](README_ZH_TW.md) • [Español](README_ES.md) • [Italiano](README_IT.md) </div> @@ -13,7 +13,7 @@ []() []() -[]() +[]() []() *Escribe como un lenguaje de alto nivel, ejecuta como C.* @@ -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 @@ -493,8 +504,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 +526,7 @@ externo: loop { for _ in 0..5 { ... } ``` + #### Control Avanzado ```zc // Guard: Ejecuta else y retorna si la condición es falsa @@ -606,7 +625,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 +957,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 @@ -988,12 +1008,13 @@ Zen C simplifica la compleja sintaxis de restricciones de GCC con vinculaciones // Sintaxis: : out(variable) : in(variable) : clobber(reg) // Usa la sintaxis de marcador de posición {variable} para legibilidad -fn sumar(a: int, b: int) -> int { +fn sumar(x: int) -> int { let resultado: int; asm { - "add {resultado}, {a}, {b}" + "mov {x}, {resultado}" + "add $5, {resultado}" : out(resultado) - : in(a), in(b) + : in(x) : clobber("cc") } return resultado; @@ -1082,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 new file mode 100644 index 0000000..be48cda --- /dev/null +++ b/README_IT.md @@ -0,0 +1,1488 @@ + +<div align="center"> + +[English](README.md) • [简体中文](README_ZH_CN.md) • [繁體中文](README_ZH_TW.md) • [Español](README_ES.md) • [Italiano](README_IT.md) + +</div> + +<div align="center"> + +# Zen C + +**Ergonomia Moderna. Zero Overhead. C puro.** + +[]() +[]() +[]() +[]() + +*Comodità di un linguaggio ad alto livello, veloce come il C* + +</div> + +--- + +## Panoramica + +**Zen C** è un linguaggio di programmazione di sistemi moderno che genera codice `GNU C`/`C11`. Fornisce allo sviluppatore un ricco set di funzionalità, tra cui inferenza di tipo, pattern matching, generici, tratti, async/await, e gestione manuale della memoria con funzionalità RAII, mantenendo al contempo una compatibilità al 100% con l'ABI C + +## Community + +Unisciti alla conversazione, condividi delle demo, fai domande o segnala dei bug nel server ufficiale Discord Zen C + +- Discord: [Unisciti qui](https://discord.com/invite/q6wEsCmkJP) + +--- + +## Indice + +- [Panoramica](#panoramica) +- [Community](#community) +- [Guida Rapida](#guida-rapida) + - [Installazione](#installazione) + - [Utilizzo](#utilizzo) + - [Variabili d'ambiente](#variabili-d-ambiente) +- [Riferimenti Del Linguaggio](#riferimenti-del-linguaggio) + - [1. Variabili e Costanti](#1-variabili-e-costanti) + - [2. Tipi Primitivi](#2-tipi-primitivi) + - [3. Tipi Aggregati](#3-tipi-aggregati) + - [Array](#array) + - [Tuple](#tuple) + - [Struct](#struct) + - [Struct Opachi](#struct-opachi) + - [Enum](#enum) + - [Unioni](#unioni) + - [Alias del tipo](#alias-del-tipo) + - [Alias del tipo opachi](#alias-del-tipo-opachi) + - [4. Funzioni e Lambda](#4-funzioni-e-lambda) + - [Funzioni](#funzioni) + - [Argomenti Costanti](#argomenti-costanti) + - [Argomenti di default](#argomenti-di-default) + - [Lambda (Closure)](#lambda-closure) + - [Puntatori-Funzione Grezzi](#puntatori-funzione-grezzi) + - [Argomenti Variadici](#argomenti-variadici) + - [5. Controllo di Flusso](#5-controllo-di-flusso) + - [Condizionali](#condizionali) + - [Pattern Matching](#pattern-matching) + - [Loop](#loop) + - [Controllo Avanzato](#controllo-avanzato) + - [6. Operatori](#6-operatori) + - [Operatori Overload-abili](#operatori-overload-abili) + - [Zucchero Sintattico](#zucchero-sintattico) + - [7. Stampaggio e Interpolazione delle Stringhe](#7-stampaggio-e-interpolazione-delle-stringhe) + - [Keyword](#keyword) + - [Scorciatoie](#scorciatoie) + - [Interpolazione delle Stringhe (F-strings)](#interpolazione-delle-stringhe-f-strings) + - [Prompt di Input (`?`)](#prompt-di-input) + - [8. Gestione della memoria](#8-gestione-della-memoria) + - [Rimando](#rimando) + - [Liberazione-automatica](#liberazione-automatica) + - [Semantiche delle risorse (Muovi di default)](#semantiche-delle-risorse-muovi-di-default) + - [RAII / Rilascio Tratti](#raii--rilascio-tratti) + - [9. Programmazione Orientata a Oggetti](#9-programmazione-orientata-a-oggetti) + - [Metodi](#metodi) + - [Tratti](#tratti) + - [Tratti Standard](#tratti-standard) + - [Composizione](#composizione) + - [10. Generici](#10-generici) + - [11. Concorrenza Asincrona (Async/Await)](#11-concorrenza-asincrona-asyncawait) + - [12. Metaprogramming](#12-metaprogramming) + - [Comptime](#comptime) + - [Incorporati](#incorporati) + - [Plugin](#plugin) + - [Macro C Generiche](#macro-c-generiche) + - [13. Attributi](#13-attributi) + - [Attributi Personalizzati](#attributi-personalizzati) + - [Mappatura della Sintassi](#mappatura-della-sintassi) + - [Derivazioni Intelligenti](#derivazioni-intelligenti) + - [14. Assembly Inline](#14-assembly-inline) + - [Utilizzo Base](#utilizzo-base) + - [Volatile](#volatile) + - [Vincoli Nominati](#vincoli-nominati) + - [15. Direttive della Buil](#15-direttive-della-build) + - [16. Keyword](#16-keyword) + - [17. Interoperabilità C](#17-interoperabilità-c) +- [Libreria Standard](#liberia-standard) +- [Tooling](#tooling) + - [Language Server (LSP)](#language-server-lsp) + - [REPL](#repl) +- [Supporto del Compilatore e Compatibilità](#supporto-del-compilatore-e-compatibilità) + - [Stato della suite di test](#stato-della-suite-di-test) + - [Buildare con Zig](#buildare-con-zig) + - [Interop C++](#interop-c) + - [Interop CUDA](#interop-cuda) + - [Interop Objective-C](#interop-objective-c) + - [Supporto ISO C23](#supporto-iso-c23) +- [Contribuisci](#contribuisci) +- [Attribuzioni](#attribuzioni) + +--- + +## Guida Rapida + +### Installazione + +```bash +git clone https://github.com/z-libs/Zen-C.git +cd Zen-C +make +sudo make install +``` + +### Build Portatile (APE) + +Il codice Zen C può come un **Actually Portable Executable (APE)** (lett. _Eseguibile Effetivamente Portatile_) utilizzando la [Cosmopolitan Libc](https://github.com/jart/cosmopolitan). Ciò produrrà un singolo eseguibile (`.com`) che potrà essere eseguito nativamente su Linux, macOS, Windows, FreeBSD, OpenBSD e NetBSD sia sulle architetture x86_64 e aarch64. + +**Prerequisiti:** +- Strumenti `cosmocc` (deve trovarsi nella tua PATH) + +**Builda e Installa:** +```bash +make ape +sudo env "PATH=$PATH" make install-ape +``` + +**Artefatti:** +- `out/bin/zc.com`: Il compilatore Zen-C portatile. Inlude la libreria standard, incorporata nell'eseguibile. +- `out/bin/zc-boot.com`: Un installer bootstrap auto-contenuto per configurare nuovi progetti Zen-C rapidamente. + +**Utilizzo:** +```bash +# Eseguibile su qualunque OS supportato +./out/bin/zc.com build hello.zc -o hello +``` + +### Utilizzo + +```bash +# Compila e avvia +zc run hello.zc + +# Builda eseguibile +zc build hello.zc -o hello + +# Shell interattiva +zc repl +``` + +### Variabili d'ambiente + +Puoi impostare `ZC_ROOT` per specificare la posizione della Libreria Standard (per inclusioni standard come `import "std/vector.zc"`). Ciò ti permetterà di eseguire il comando `zc` da qualsiasi directory. + +```bash +export ZC_ROOT=/path/to/Zen-C +``` + +--- + +## Riferimenti Del Linguaggio + +### 1. Variabili e Costanti + +Zen C differenzia le costanti al tempo di compilazione e le variabili di esecuzione. + +#### Costanti Manifesto (`def`) +Valori che esistono solo durante la compilazione (integrate nel codice). Utilizzale per le grandezze degli array, configurazioni fisse, e numeri magici. + +```zc +def MAX_SIZE = 1024; +let buffer: char[MAX_SIZE]; // Grandezza valida per l'array +``` + +#### Variabili (`let`) +Locazioni di memoria. Possono essere mutabili o di sola lettura (`const`). + +```zc +let x = 10; // Mutabile +x = 20; // OK + +let y: const int = 10; // Sola lettura (Tipo qualificato) +// y = 20; // Errore: impossibile assegnare un valore ad una variabile costante +``` + +> **Inferenza di tipo**: Zen C inferisce automaticamente il tipo per le variabili inizializzate. Compilando ciò alla keyword `auto` dello standard C23 nei compilatori supportati, oppure alla estensione GCC `__auto_type`. + +### 2. Tipi Primitivi + +| Tipo | C Equivalent | Descrizione | +|:---|:---|:---| +| `int`, `uint` | `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 | +| `byte` | `uint8_t` | Alias per U8 | +| `F32`, `F64` or `f32`, `f64` | `float`, `double` | Numeri con parte decimale | +| `bool` | `bool` | `true` (lett. _vero_) o `false` (lett. _falso_) | +| `char` | `char` | Carattere singolo | +| `string` | `char*` | Stringhe C terminate da NULL | +| `U0`, `u0`, `void` | `void` | Tipo vuoto | +| `iN` (Per esempio, `i256`) | `_BitInt(N)` | Intero con segno a larghezza arbitraria di bit (C23) | +| `uN` (Per esempio, `u42`) | `unsigned _BitInt(N)` | Intero senza segno a larghezza arbitraria di bit (C23) | + +> **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 +Array a lunghezza fissa con valori arbitrari. +```zc +def GRANDEZZA = 5; +let interi: int[GRANDEZZA] = [1, 2, 3, 4, 5]; +let zeri: [int; GRANDEZZA]; // Inizializzato a zero +``` + +#### Tuple +Valori molteplici raggruppati assieme, accesso agli elementi indicizzato. +```zc +let paio = (1, "Ciao!"); +let x = paio.0; // 1 +let s = paio.1; // "Ciao!" +``` + +**Molteplici Valori di Ritorno** + +Le funzioni posso restituire delle tuple per fornire diversi risultati: +```zc +fn somma_e_differenza(a: int, b: int) -> (int, int) { + return (a + b, a - b); +} + +let risultato = somma_e_differenza(3, 2); +let somma = risultato.0; // 5 +let differenza = risultato.1; // 1 +``` + +**Separazione** + +Le tuple possono essere separate direttamente in variabili singole. +```zc +let (somma, differenza) = somma_e_differenza(3, 2); +// somma = 5, differenza = 1 +``` + +#### Structs +Strutture dati con campi di bit opzionali. +```zc +struct Punto { + x: int; + y: int; +} + +// Inizializzazione struct +let p = Punto { x: 10, y: 20 }; + +// Campi di bit +struct Flags { + valido: U8 : 1; + modalità: U8 : 3; +} +``` + +> **Nota**: Gli struct usano le [Semantiche di Spostamento](#semantiche-di-movimento--copia-sicura) di default. I campi di uno struct possono essere acceduti via `.` anche sui puntatori (Dereferenza-Automatica). + +#### Struct Opachi +Puoi definire uno struct come `opaque` (lett. _opaco_) per restringere l'accesso ai suoi campi al modulo che lo ha definito, permettendo comunque l'allocazione sullo stack dello struct (la grandezza è data). + +```zc +// In utente.zc +opaque struct Utente { + id: int; + nome: string; +} + +fn nuovo_utente(nome: string) -> Utente { + return Utente{id: 1, nome: nome}; // OK: Dentro il modulo +} + +// In main.zc +import "utente.zc"; + +fn main() { + let u = nuovo_utente("Alice"); + // let id = u.id; // Error: Impossibile accedere al campo privato 'id' +} +``` + +#### Enum +Unioni taggate (tipi somma) capaci di contenere dati. +```zc +enum Forma { + Cerchio(float), // Contiene il raggio + Rettangolo(float, float), // Contiene la larghezza e l'altezza + Punto // Non contiene dati +} +``` + +#### Unioni +Unioni standard C (accesso non sicuro). +```zc +union Dati { + i: int; + f: float; +} +``` + +#### Alias del tipo +Crea un alias per un tipo già esistente. +```zc +alias ID = int; +alias PuntoDellaMappa = Mappa<string, Punto>; +``` + +#### Alias del tipo opachi +Puoi definire un alias del tipo come `opaque` (lett. _opaco_) per creare un nuovo tipo che si distingue dal suo tipo sottostante al di fuori del modulo che l'ha definito. Questo permette una forte incapsulamento e sicurezza dei tipi senza overhead extra durante l'esecuzione di un wrapper struct. + +```zc +// In libreria.zc +opaque alias Handle = int; + +fn crea_handle(v: int) -> Handle { + return v; // Conversione implicita consentita all'interno del modulo +} + +// In main.zc +import "libreria.zc"; + +fn main() { + let h: Handle = crea_handle(42); + // let i: int = h; // Errore: Validazione del tipo fallita + // let h2: Handle = 10; // Errore: Validazione del tipo fallita +} +``` + +### 4. Funzioni e Lambda + +#### Funzioni +```zc +fn somma(a: int, b: int) -> int { + return a + b; +} + +// Supporto per argomenti nominati nelle chiamate +somma(a: 10, b: 20); +``` + +> **Nota**: Gli argomenti nominati devono seguire rigorosamente l'ordine predefinito dei parametri. `somma(b: 20, a: 10)` è errato. + +#### Argomenti Costanti +Gli argomenti di una funzione possono essere marcati come `const` (lett. _costanti_) per reinforzare semantiche di sola lettura. Questo è un qualificatore del tipo, non una costante esplicita. + +```zc +fn stampa_valore(v: const int) { + // v = 10; // Errore: Impossibile assegnare un valore ad una variabile costante + println "{v}"; +} +``` + +#### Argomenti di default +Le funzioni posso definire dei valori default per gli argomenti in caso che questi non vengano specificati durante la chiamata. Questi valori possono essere letterali, espressioni, o codice Zen C valido (come il costruttore di uno struct). +```zc +// Valore default semplice +fn incrementa(val: int, quantità: int = 1) -> int { + return val + quantità; +} + +// Espressione come valore default (calcolato) +fn offset(val: int, pad: int = 10 * 2) -> int { + return val + pad; +} + +// Struct come valore default +struct Config { debug: bool; } +fn init(cfg: Config = Config { debug: true }) { + if cfg.debug { println "Modalità Debug"; } +} + +fn main() { + incrementa(10); // 11 + offset(5); // 25 + init(); // Stampa "Modalità Debug" +} +``` + +#### Lambda (Closure) +Funzioni anonime che possono catturare il loro ambiente. +```zc +let fattore = 2; +let double = x -> x * fattore; // Sintassi con freccia +let pieno = fn(x: int) -> int { return x * fattore; }; // Sintassi a blocco +``` + +#### Puntatori-Funzione grezzi +Zen C supporta i puntatori-funzione grezzi utilizzando la sintassi `fn*`. Questo permette un'interoperabilità fluida con le librerie C che si aspettano puntatori-funzione senza overhead di closure. + +```zc +// Funzione che prende un puntatore-funzione grezzo +fn imposta_callback(cb: fn*(int)) { + cb(42); +} + +// Funzione che restituisce un puntatore-funzione grezzo +fn ottieni_callback() -> fn*(int) { + return il_mio_handler; +} + +// I puntatori a puntatori-funzione sono supportati (fn**) +let pptr: fn**(int) = &ptr; +``` + +#### Argomenti Variadici +Le funzioni possono accettare un numero variabile di argomenti utilizzando la sintassi `...` e il tipo `va_list`. +```zc +fn log(lvl: int, fmt: char*, ...) { + let ap: va_list; + va_start(ap, fmt); + vprintf(fmt, ap); // Usa lo stdio C + va_end(ap); +} +``` + +### 5. Controllo di Flusso + +#### Condizionali +```zc +if x > 10 { + print("Grande"); +} else if x > 5 { + print("Medio"); +} else { + print("Piccolo"); +} + +// Operatore ternario +let y = x > 10 ? 1 : 0; // Se x è maggiore di 10 y sarà uguale a 1, in ogni altro caso, y sarà uguale a 0 +``` + +#### Pattern Matching +Alternativa potente agli `switch`. +```zc +match val { + 1 => { print "Uno" }, + 2 || 3 => { print "Due o Tre" }, // OR logico con || + 4 or 5 => { print "Quattro or Cinque" }, // OR logico con 'or' + 6, 7, 8 => { print "Da Sei a Otto" }, // OR logico con la virgola (,) + 10 .. 15 => { print "Da 10 a 14" }, // Range Esclusivo (Legacy) + 10 ..< 15 => { print "Da 10 a 14" }, // Range Esclusivo (Esplicito) + 20 ..= 25 => { print "Da 20 a 25" }, // Range Inclusivo + _ => { print "Altro" }, +} + +// Destrutturazione degli Enums +match forma { + Forma::Cerchio(r) => println "Raggio: {r}", + Forma::Rettangolo(w, h) => println "Area: {w*h}", + Forma::Punto => println "Punto" +} +``` + +#### Associaione di riferiemnto +Per ispezionare un valore senza assumerne la proprietà (spostarlo) puoi usare la keyword `ref` nel pattern. Questo è essenziale per i tipi che implementano Semantiche di Movimento (come `Option`, `Result`, struct non-copiabile). + +```zc +let opt = Qualche(ValoreNonCopiable{...}); +match opt { + Some(ref x) => { + // 'x' è un puntatore che punta al valore contenuto in 'opt' + // 'opt' NON viene né mosso né consumato qui + println "{x.field}"; + }, + None => {} +} +``` + +#### Loops +```zc +// Range +for i in 0..10 { ... } // Esclusivo (Da 0 a 9) +for i in 0..<10 { ... } // Esclusivo (Esplicito) +for i in 0..=10 { ... } // Inclusivo (Da 0 a 10) +for i in 0..10 step 2 { ... } + +// Iteratore (Vec, Array, oppure un Iteratore personalizzato) +for item in collection { ... } + +// While (lett. mentre) +while x < 10 { ... } + +// Infinito con etichetta +esterno: loop { + if done { break esterno; } +} + +// Ripeti N volte +for _ in 0..5 { ... } +``` + +#### Controllo Avanzato +```zc +// Guard (lett. 'guardia'): Esegue il caso 'else' e ritorna se la condizione è falsa +guard ptr != NULL else { return; } + +// Unless (lett. 'a meno che'): Se non vero +unless è_valido { return; } +``` + +### 6. Operatori + +Zen C supporta l'overloading di operatori per gli struct definiti dall'utente per implementare nomi specifici di metodi. + +#### Operatori Overload-abili + +| Categoria | Operatore | Nome del Metodo | +|:---|:---|:---| +| **Aritmetico** | `+`, `-`, `*`, `/`, `%` | `add`, `sub`, `mul`, `div`, `rem` | +| **Paragone** | `==`, `!=` | `eq`, `neq` | +| | `<`, `>`, `<=`, `>=` | `lt`, `gt`, `le`, `ge` | +| **Bitwise** | `&`, `\|`, `^` | `bitand`, `bitor`, `bitxor` | +| | `<<`, `>>` | `shl`, `shr` | +| **Unari** | `-` | `neg` | +| | `!` | `not` | +| | `~` | `bitnot` | +| **Indice** | `a[i]` | `get(a, i)` | +| | `a[i] = v` | `set(a, i, v)` | + +> **Nota sull'uguaglianza delle stringhe**: +> - `string == string` performa un controllo del **valore** (equivalente a `strcmp`). +> - `char* == char*` performa un controllo dei **puntatori** (controlla gli indirizzi di memoria). +> - Paragoni misti (e.g. `string == char*`) defaulta al controllo dei **pointer**. + +**Esempio:** +```zc +impl Punto { + fn add(self, altro: Punto) -> Punto { + return Punto{x: self.x + altro.x, y: self.y + altro.y}; + } +} + +let p3 = p1 + p2; // Chiama p1.somma(p2) +``` + +#### Zucchero Sintattico + +Questi operatori sono funzionalità integrate del linguaggio e non è possibile overloadarli. + +| Operatore | Nome | Descrizione | +|:---|:---|:---| +| `\|>` | Pipeline | `x \|> f(y)` viene dezuccherato a `f(x, y)` | +| `??` | Coalescenza nulla | `val ?? default` restituisce `default` se `val` è NULL (puntatori) | +| `??=` | Assegnazione nulla | `val ??= init` assegna se `val` è NULL | +| `?.` | Safe Navigation | `ptr?.campo` accede a 'campo' solo se `ptr` non è NULL | +| `?` | Try Operator | `res?` restituisce un errore se presente (tipi Result/Option) | + +**Dereferenza Automatica**: +Pointer field access (`ptr.field`) and method calls (`ptr.method()`) automatically dereference the pointer, equivalent to `(*ptr).field`. +Accesso ai campi da un puntatore (`puntatore.campo`) e chiamate ai metodi (`puntatore.metodo()`) dereferenzano automaticamente il puntatore, ciò è equivalente a `(*puntatore).campo` + +### 7. Stampaggio e Interpolazione delle Stringhe + +Zen C fornisce opzioni versatili per stampare alla console, includendo keyword e scorciatoie coincise. + +#### Keyword + +- `print "testo"`: Stampa a `stdout` senza aggiunzione di una newline automatica. +- `println "testo"`: Stampa a `stdout` aggiungendo una newline automatica. +- `eprint "testo"`: Stampa a `stderr` senza aggiunzione di una newline automatica. +- `eprintln "testo"`: Stampa a `stderr` aggiungendo una newline automatica. + +#### Scorciatoie + +Zen C ti permette di utilizzare stringhe letterali direttamente come istruzione di stampaggio veloce: + +- `"Ciao Mondo!"`: Equivalente a `println "Ciao Mondo!"`. (Aggiunge una newline implicitamente) +- `"Ciao Mondo!"..`: Equivalente a `print "Ciao Mondo!"`. (Non aggiunge una newline) +- `!"Errore"`: Equivalente a `eprintln "Errore"`. (Output a stderr) +- `!"Errore"..`: Equivalente a `eprint "Errore"`. (Output a stderr, senza newline) + +#### Interpolazione delle Stringhe (F-strings) + +Puoi incorporare espressioni direttamente all'interno di stringhe letterali utilizzando la sintassi `{}`. Questo funziona con tutti i metodi di stampaggio, incluse le scorciatoie. + +```zc +let x = 42; +let nome = "Max"; +println "Valore: {x}, Nome: {name}"; +"Valore: {x}, Nome: {name}"; // scorciatoia per println +``` + +#### Prompt di Input (`?`) + +Zen C supporta una scorciatoia per richiedere input dall'utente utilizzando il prefisso `?`. + +- `? "Inserisci il tuo nome"`: Stampa il prompt (senza newline) e aspetta per dell'input (legge una linea). +- `? "Inserisci la tua età: " (età)`: Stampa il prompt e memorizza l'input nella variabile `età`. + - Gli specificatori del formato vengono automaticamente inferiti in base al tipo della variabile. + +```zc +let età: int; +? "Inserisci la tua età: " (età); +println "Hai {età} anni."; +``` + +### 8. Gestione della memoria + +Zen C permette una gestione manuale della memoria con aiuti ergonomici. + +#### Rimando +Esegui il codice quando l’ambito corrente termina. Le istruzioni defer vengono eseguite in ordine LIFO (last-in, first-out). +```zc +let f = fopen("file.txt", "r"); +defer fclose(f); +``` + +> Per prevenire comportamenti indefiniti, le istruzioni del controllo di flusso (`return`, `break`, `continue`, `goto`) **non sono ammesse** dentro un blocco `defer`. + +#### Liberazione automatica +Libera automaticamente la memoria occupata dalla variabile quando l'ambito corrente termina. +```zc +autofree let tipi = malloc(1024); +``` + +#### Semantiche delle risorse (Muovi di Default) +Zen C tratta i tipi con distruttori (come `File`, `Vec`, o puntatori allocati manualmente con `malloc`) come **Risorse**. Per prevenire errori di doppia-liberazione, le risorse non possono essere implicitamente duplicate. + +- **Muovi di Default**: Assegnare una risorsa variabile ne trasferisce il proprietario. La variabile originale diventa invalida (Spostata). +- **Tipi di Copia**: Tipi senza distruttori possono opzionalmente avere un comportamento `Copy`, rendendo l'assegnazione una duplicazione. + +**Diagnostica & Filosofia**: +Se vedi un errore "Utilizzo di una variabile spostata", il compilatore ti sta dicendo: *"Questo tipo è proprietario di una risorsa (come memoria o un handle) e copiarlo ciecamente non è sicuro."* + +> **Contrasto:** Al contrario di come fanno C/C++, Zen C non duplica implicitamente i valori che posseggono risorse. + +**Argomento di una funzione**: +Passare un valore ad una funzione segue le stesse regole dell'assegnazione: le risorse vengono spostate se non passate per referenza. + +```zc +fn processo(r: Risorsa) { ... } // 'r' viene spostato nella funzione +fn peek(r: Risorsa*) { ... } // 'r' viene preso in prestito (referenza) +``` + +**Clonazione Esplicita**: +Se *vuoi* avere più copie di una risorsa, rendilo esplicito: + +```zc +let b = a.clona(); // Chiama il metodo `clona` dal tratto `Clone` +``` + +**Duplicazione opt-in (Tipi dei valori)**: +Per tipi piccoli senza distruttore: + +```zc +struct Punto { x: int; y: int; } +impl Copy for Punto {} // Opt-in per la duplicazione implicita + +fn main() { + let p1 = Point { x: 1, y: 2 }; + let p2 = p1; // Copiato. p1 rimane valido. +} +``` + +#### RAII / Rilascio Tratti +Implementa `Drop` per una logica di pulizia automatica. +```zc +impl Drop for MioStruct { + fn drop(self) { + self.free(); + } +} +``` + +### 9. Programmazione Orientata a Oggetti + +#### Metodi +Definisci metodi sui tipi utilizziando `impl`. +```zc +impl Punto { + // Metodo statico (convenzione del costruttore) + fn nuovo(x: int, y: int) -> Self { + return Point{x: x, y: y}; + } + + // Metodo d'instanza + fn dist(self) -> float { + return sqrt(self.x * self.x + self.y * self.y); + } +} +``` + +#### Tratti +Definisci un comportamento condiviso. +```zc +struct Cerchio { raggio: f32; } + +trait Disegnabile { + fn disegna(self); +} + +impl Disegna for Cerchio { + fn disegna(self) { ... } +} + +let cerchio = Cerchio{}; +let disegnabile: Disegnabile = &cerchio; +``` + +#### Tratti Standard +Zen C include dei tratti standard che si integrano con la sintassi del linguaggio. + +**Iterable** (lett. _Iterabile_) + +Implementa `Iterable<T>` per abilitare loop `for-in` (lett. _per in_) nei tuoi tipi personalizzati. + +```zc +import "std/iter.zc" + +// Definisci un Iteratore +struct MioIteratore { + curr: int; + stop: int; +} + +impl MioIteratore { + fn next(self) -> Option<int> { + if self.curr < self.stop { + self.curr += 1; + return Option<int>::Some(self.curr - 1); + } + return Option<int>::None(); + } +} + +// Implementa Iterable +impl MioRange { + fn iterator(self) -> MioIteratore { + return MioIteratore{curr: self.start, stop: self.end}; + } +} + +// Usalo in un loop +for i in mio_range { + println "{i}"; +} +``` + +**Drop** (lett. _rilascia_) + +Implementa `Drop` per definire un distruttore che esegue quando l'oggetto va fuori ambito (RAII). + +```zc +import "std/mem.zc" + +struct Risorsa { + ptr: void*; +} + +impl Drop for Risorsa { + fn drop(self) { + if self.ptr != NULL { + free(self.ptr); + } + } +} +``` + +> **Nota:** Se una variabile viene spostata, `drop` NON verrà chiamato sulla variabile originale. Aderisce alle [Semantiche delle Risorse](#semantiche-delle-risorse) + +**Copy** (lett. _copia_) + +Tratto marcatore opt-in per il comportamento `Copy` (duplicazione implicita) al posto delle semantiche Move. Utilizzato tramite `@derive(Copy)` + +> **Regola:** I tipi che implementano `Copy` non dovrà definire un distruttore (`Drop`). + +```zc +@derive(Copy) +struct Punto { x: int; y: int; } + +fn main() { + let p1 = Punto{x: 1, y: 2}; + let p2 = p1; // Copiato! p1 rimane valido. +} +``` + +**Clone** (lett. _clona_) + +Implementa `Clone` per permettere la duplicazione esplicita di tipi che posseggono risorse. + +```zc +import "std/mem.zc" + +struct Scatola { val: int; } + +impl Clone for Scatola { + fn clone(self) -> Scatola { + return Scatola{val: self.val}; + } +} + +fn main() { + let b1 = Scatola{val: 42}; + let b2 = b1.clone(); // Explicit copy +} +``` + +#### Composizione +Usa `use` per incorporare altri struct. Puoi mischiarli (campi piatti) o nominarli (campi nidificato). + +```zc +struct Entità { id: int; } + +struct Giocatore { + // Mischiati (Non nominati): Campi piatti + use Entità; // Aggiunge 'id' a 'Giocatore' direttamente + nome: string; +} + +struct Partita { + // Composizione (Nominati): Campi nidificati + use p1: Giocatore; // Vi si accede tramite partita.p1 + use p2: Giocatore; // Vi si accede tramite partita.p2 +} +``` + +### 11. Generici + +Template type-safe per struct e funzioni. + +```zc +// Struct Generico +struct Scatola<T> { + oggetto: T; +} + +// Funzione Generica +fn identità<T>(valore: T) -> T { + return valore; +} + +// Generici Multi-parametro +struct Paio<K, V> { + chiavi: K; + valore: V; +} +``` + +### 11. Concorrenza Asincrona (Async/Await) + +Costruito sui pthreads. + +```zc +async fn ottieni_dati() -> string { + // Esegue in background + return "Dati"; +} + +fn main() { + let futuro = ottieni_dati(); + let risultato = await futuro; // (lett. 'aspetta') +} +``` + +### 12. Metaprogramming + +#### Comptime +Esegui codice al tempo di compilazione per generare sorgente o stampare messaggi. +```zc +comptime { + // Genera codice al tempo di compilazione (scritto a stdout) + println "let data_della_build = \"2024-01-01\";"; +} + +println "Data della build: {data_della_build}"; +``` + +#### Incorporati +Incorpora file come tipi specificati. +```zc +// Default (Slice_char) +let data = embed "assets/logo.png"; + +// Incorporazioni tipizzate +let testo = embed "shader.glsl" as string; // Incorpora come una stringa C +let rom = embed "bios.bin" as u8[1024]; // Incorpora come un array a dimensione fissa +let wav = embed "sound.wav" as u8[]; // Incorpora come Slice_u8 +``` + +#### Plugin +Importa plugin del compilatore per estendere la sintassi. +```zc +import plugin "regex" +let re = regex! { ^[a-z]+$ }; +``` + +#### Macro C Generiche +Passa delle macro del preprocessore C. + +> **Consiglio**: Per delle semplici costanti, utilizza `def`. Usa `#define` solo quanto ti servono macro del preprocessore C o flag di compilazione condizionale. + +```zc +#define BUFFER_MASSIMO 1024 +``` + +### 13. Attributi + +Decora le funzioni e gli struct per modificare il comportamento del compilatore. + +| Attributo | Ambito | Descrizione | +|:---|:---|:---| +| `@must_use` | Fn | Avvereti se il valore di ritorno viene ignorato. | +| `@deprecated("msg")` | Fn/Struct | Avverti all'uso con 'msg' | +| `@inline` | Fn | Suggerisci al compilatore di rendere il codice inline | +| `@noinline` | Fn | Previeni l'inline automatico | +| `@packed` | Struct | Rimuovi il padding (lett. _imbottitura_) automatico in mezzo ai campi. | +| `@align(N)` | Struct | Forza l'allineamento a N byte. | +| `@constructor` | Fn | Esegui prima di `main`. | +| `@destructor` | Fn | Esegue dopo la terminazione di `main`. | +| `@unused` | Fn/Var | Sopprimi gli errori di 'variabile inutilizzata' | +| `@weak` | Fn | Linking dei simboli _weak_ (lett. _debole_). | +| `@section("name")` | Fn | Inserisci il codice in una specifica sezione. | +| `@noreturn` | Fn | La funzione non restituisce valori. (e.g. `exit`). | +| `@pure` | Fn | La funzione non ha effetti collaterali (indizio per l'ottimizzazione). | +| `@cold` | Fn | La funzione è usata poco spesso (indizio per la branch prediction). | +| `@hot` | Fn | La funzione è usata molto spesso (indizio per l'ottimizzazione). | +| `@export` | Fn/Struct | Esporta simbolo (visibilità default). | +| `@global` | Fn | CUDA: Entry point del Kernel (`__global__`). | +| `@device` | Fn | CUDA: Funzione del Device (`__device__`). | +| `@host` | Fn | CUDA: Funzione dell'Host (`__host__`). | +| `@comptime` | Fn | Funzione di supporto disponibile per l'esecuzione al tempo di compilazione. | +| `@derive(...)` | Struct | Implementa automaticamente i tratti. Supporta `Debug`, `Eq` (Derivazione Intelligente), `Copy`, `Clone`. | +| `@<custom>` | Any | Passa gli attributi generici direttamente al C (e.g. `@flatten`, `@alias("nome")`) | + +#### Attributi Personalizzati + +Zen C supporta un potente sistema di **Attributi Personalizzati** che ti permettono di utilizzare ogni `__attributo__` GCC/Clang direttamente nel tuo codice Zen C. Qualsiasi attributo non riconosciuto dal compilatore Zen C viene trattato come un attributo generico e passato direttamente nel codice C generato. + +Ciò fornisce accesso a delle avanzate funzionalità, ottimizzazioni e direttive del linker senza necessitare di un supporto esplicito nel cuore del linguaggio. + +#### Mappatura della Sintassi +Zen C attributes are mapped directly to C attributes: +- `@name` → `__attribute__((name))` +- `@name(args)` → `__attribute__((name(args)))` +- `@name("string")` → `__attribute__((name("string")))` + +#### Derivazioni Intelligenti + +Zen C fornisce delle "derivazioni intelligenti" che rispettano le Semantiche di Movimento: + +- **`@derive(Eq)`**: Genera un metodo di uguaglianza che prende argomenti per referenza (`fn eq(self, other: T*)`). + - Quando si confrontano due struct non-Copy (`a == b`), il compilatore passa automaticamente `b` per referenza (`&b`) per non doverlo spostare. + - I controlli di uguaglianza ricorsivi preferiscono l'accesso da puntatore per prevenire il trasferimento del proprietario. + +### 14. Assembly Inline + +Zen C fornisce supporto di prima-classe per l'assembly _inline_, traspilando direttamente ad `asm` con estensioni in stile GCC. + +#### Utilizzo Base +Scrivi assembly grezzo all'interno di blocchi `asm`. Le stringhe vengono concatenate automaticamente. +```zc +asm { + "nop" + "mfence" +} +``` + +#### Volatile +Impedisci al compilatore di eliminare automaticamente istruzioni assembly (e.g. ottimizzazione) se ciò potrebbe avere ripercussioni. +```zc +asm volatile { + "rdtsc" +} +``` + +#### Vincoli Nominati +Zen C semplifica la sintassi complessa dei vincoli di GCC con dei binding nominati. + +**Nota per i lettori italiani**: Con 'clobber' si intende la *sovrascrizione*. + +```zc +// Sintassi: : out(variable) : in(variable) : clobber(reg) +// Usa una sintassi placeholder (`{variabile}`) per la leggibilità + +fn aggiungi_cinque(x: int) -> int { + let risultato: int; + asm { + "mov {x}, {risultato}" + "add $5, {risultato}" + : out(risultato) + : in(x) + : clobber("cc") + } + return risultato; +} +``` + +| Tipo | Sintassi | Equivalente GCC | +|:---|:---|:---| +| **Output** | `: out(variabile)` | `"=r"(variabile)` | +| **Input** | `: in(variabile)` | `"r"(variabile)` | +| **Clobber** | `: clobber("rax")` | `"rax"` | +| **Memory** | `: clobber("memoria")` | `"memoria"` | + +> **Nota:** Quando si usa la sintassi Intel (via `-masm=intel`), dovrai assicurarti che la tua build sia configurata correttamente (per esempio, `//> cflags: -masm=intel`). TCC non supporta la sintassi assembly Intel. + + +### 15. Direttive della Build + +Zen C supporta dei commenti speciali all'inizio del tuo file sorgente che ti permettono di configurare il processo di build senza necessitare di un sistema di build complesso o di un *Makefile*. + +| Direttiva | Argomenti | Descrizione | +|:---|:---|:---| +| `//> link:` | `-lfoo` oppure `path/to/lib.a` | Linka con una libreria o un file object. | +| `//> lib:` | `path/to/libs` | Aggiunge una directory dove cercare le librerie (`-L`). | +| `//> include:` | `path/to/headers` | Aggiunge una directory dove cercare i file include (`-I`). | +| `//> framework:` | `Cocoa` | Linka con un framework macOS. | +| `//> cflags:` | `-Wall -O3` | Passa flag arbitrare al compilatore C. | +| `//> define:` | `MACRO` or `KEY=VAL` | Definisci una macro del preprocessore (`-D`). | +| `//> pkg-config:` | `gtk+-3.0` | Esegui `pkg-config` e aggiungi `--cflags` e `--libs`. | +| `//> shell:` | `command` | Esegui un comando sulla shell durante il processo di build. | +| `//> get:` | `http://url/file` | Scarica un file se un file specifico non esiste. | + +#### Feature + +**1. OS Guarding** (lett. _Protezione OS_) +Prefissa delle direttive con il nome di un OS per applicarle solo su piattaforme specifiche. +Prefissi supportati: `linux:`, `windows:`, `macos:` (or `darwin:`). + +```zc +//> linux: link: -lm +//> windows: link: -lws2_32 +//> macos: framework: Cocoa +``` + +**2. Environment Variable Expansion** +Utilizza la sintassi `${VAR}` per espandare variabili d'ambiente nelle tue direttive. + +```zc +//> include: ${HOME}/MiaLibreria/include +//> lib: ${ZC_ROOT}/std +``` + +#### Esempi + +```zc +//> include: ./include +//> lib: ./librerie +//> link: -lraylib -lm +//> cflags: -Ofast +//> pkg-config: gtk+-3.0 + +import "raylib.h" + +fn main() { ... } +``` + +### 16. Keyword + +Le keyword che seguono sono riservate in Zen C. + +#### Dichiarazioni +`alias`, `def`, `enum`, `fn`, `impl`, `import`, `let`, `module`, `opaque`, `struct`, `trait`, `union`, `use` + +#### Controllo del Flusso +`async`, `await`, `break`, `catch`, `continue`, `defer`, `else`, `for`, `goto`, `guard`, `if`, `loop`, `match`, `return`, `try`, `unless`, `while` + +#### Speciali +`asm`, `assert`, `autofree`, `comptime`, `const`, `embed`, `launch`, `ref`, `sizeof`, `static`, `test`, `volatile` + +#### Costanti +`true`, `false`, `null` + +#### Riservate del C +Gli identifiers seguenti sono riservati poiché sono keyword nello standard C11: +`auto`, `case`, `char`, `default`, `do`, `double`, `extern`, `float`, `inline`, `int`, `long`, `register`, `restrict`, `short`, `signed`, `switch`, `typedef`, `unsigned`, `void`, `_Atomic`, `_Bool`, `_Complex`, `_Generic`, `_Imaginary`, `_Noreturn`, `_Static_assert`, `_Thread_local` + +#### Operatori +`and`, `or` + +### 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 + +Zen C include una libreria standard (`std`) che ricopre funzionalità essenziali. + +[Scopri la documentazione della Libreria Standard](docs/std/README.md) + +### Moduli Chiave + +| Modulo | Descrizione | Documentazione | +| :--- | :--- | :--- | +| **`std/vec.zc`** | Array dinamico espandibile `Vec<T>`. | [Docs](docs/std/vec.md) | +| **`std/string.zc`** | Tipo `String` allocato sull'Heap con supporto UTF-8. | [Docs](docs/std/string.md) | +| **`std/queue.zc`** | Coda FIFO (Buffer Circolare). | [Docs](docs/std/queue.md) | +| **`std/map.zc`** | Hash Map Generica `Map<V>`. | [Docs](docs/std/map.md) | +| **`std/fs.zc`** | Operazioni del File System. | [Docs](docs/std/fs.md) | +| **`std/io.zc`** | Standard Input/Output (`print`/`println`). | [Docs](docs/std/io.md) | +| **`std/option.zc`** | Valori opzionali (`Some`/`None`). | [Docs](docs/std/option.md) | +| **`std/result.zc`** | Gestione degli errori (`Ok`/`Err`). | [Docs](docs/std/result.md) | +| **`std/path.zc`** | Manipolazione dei percorsi Cross-platform. | [Docs](docs/std/path.md) | +| **`std/env.zc`** | Variabili d'ambiente del processo. | [Docs](docs/std/env.md) | +| **`std/net.zc`** | Networking TCP (Socket). | [Docs](docs/std/net.md) | +| **`std/thread.zc`** | Thread e Sincronizzazione. | [Docs](docs/std/thread.md) | +| **`std/time.zc`** | Misuramenti di tempo e `sleep`. | [Docs](docs/std/time.md) | +| **`std/json.zc`** | Parsing JSON e serializzazione. | [Docs](docs/std/json.md) | +| **`std/stack.zc`** | Stack LIFO `Stack<T>`. | [Docs](docs/std/stack.md) | +| **`std/set.zc`** | Hash Set Generico `Set<T>`. | [Docs](docs/std/set.md) | +| **`std/process.zc`** | Esecuzione e gestione di processi. | [Docs](docs/std/process.md) | + +--- + +## Tooling + +Zen C fornisce un Language Server (LSP) e un REPL per migliorare l'esperienza degli sviluppatori. + +### Language Server (LSP) + +Il server del linguaggio (LSP) di Zen C supporta le feature standard per l'integrazione con gli editor, esso fornisce: + +* **Vai alla definizione** +* **Trova riferimenti** +* **Informazioni sull'hover** +* **Completamenti automatici** (Nomi di funzioni/struct, Completamento dal punto per i methods/campi) +* **Simboli dei documenti** (Outline) +* **Aiuto con le signature delle funzioni** +* **Diagnostiche** (Errori sintattici/semantici) + +Per avviare il server del linguaggio (tipicamente configurato nelle impostazioni LSP del tuo editor): + +```bash +zc lsp +``` + +Il server comunica via lo Standard I/o (JSON-RPC 2.0). + +### REPL + +Il Read-Eval-Print-Loop (REPL, lett. _Leggi-Esegui-Stampa-Ripeti_) ti permette ti sperimentare con il codice Zen C in maniera interattiva. + +```bash +zc repl +``` + +#### Funzionalità + +* **Coding interattivo**: Scrivi espressioni o istruzioni per una esecuzione immediata. +* **Storia persistente**: I comandi vengono salvati in `~/.zprep_history`. +* **Script di avvio**: I comandi di avvio (auto-load) sono salvati in `~/.zprep_init.zc`. + +#### Comandi + +| Comande | Descrizione | +|:---|:---| +| `:help` | Mostra i comandi disponibili. | +| `:reset` | Cancella la storia della sessione corrente (variabili/funzioni). | +| `:vars` | Mostra le variabili attive. | +| `:funcs` | Mostra le funzioni definite dall'utente. | +| `:structs` | Mostra gli struct definiti dall'utente. | +| `:imports` | Mostra gli 'import' attivi. | +| `:history` | Mostra la storia dell'input della sessione. | +| `:type <expr>` | Mostra il tipo di un espressione. | +| `:c <stmt>` | Mostra il codice C generato per un istruzione. | +| `:time <expr>` | Esegui un benchmark per l'espressione data. (Esegue 1000 iterazioni). | +| `:edit [n]` | Modifica il comando `n` (default: l'ultimo comando) in `$EDITOR`. | +| `:save <file>` | Salva la sessione corrente in un file `.zc`. | +| `:load <file>` | Carica ed esegui un file `.zc` nella sessione corrente. | +| `:watch <expr>` | Watch (lett. _guarda_) un espressione (rieseguita dopo ogni entry). | +| `:unwatch <n>` | Rimuovi un watch. | +| `:undo` | Rimuovi l'ultimo comando dalla sessione. | +| `:delete <n>` | Rimuovi il comando all'indice `n`. | +| `:clear` | Pulisce lo schermo. | +| `:quit` | Esce dal REPL. | +| `! <cmd>` | Esegue un comando sulla shell (e.g. `!ls`). | + +--- + + +## Supporto del Compilatore e Compatibilità + +Zen C è stato creato in modo tale da poter funzionare con la maggior parte dei compilatori C11. Alcune funzionalità potrebbero affidarsi ad estensioni GNU C, ma spesso queste funzionano anche su altri compilatori. Utilizza la flag `--cc` per modificare il backend. + +```bash +zc run app.zc --cc clang +zc run app.zc --cc zig +``` + +### Stato della suite di test + +| Compilatore | Percentuale di Superamento | Funzionalità Supportate | Limitazioni Nota | +|:---|:---:|:---|:---| +| **GCC** | **100%** | Tutte le funzioni. | Nessuna. | +| **Clang** | **100%** | Tutte le funzioni. | Nessuna. | +| **Zig** | **100%** | Tutte le funzioni. | Nessuna. Usa `zig cc` come compilatore C _drop-in_. | +| **TCC** | **~70%** | Sintassi base, Generici, Tratti | Nessun `__auto_type`, Nessuna sintassi Intel per ASM, Nessuna funzione innestata. | + +> **Consiglio:** Usag **GCC**, **Clang**, o **Zig** per le build di produzione. TCC è eccellente per prototipazione rapida per via dei suoi tempi di compilazione molto brevi ma è carente di alcune estensioni C avanzate che servono a Zen C per un set completo di funzionalità. + +### Buildare con Zig + +Il comando `zig cc` di Zig fornisce un rimpiazzamento drop-in per GCC/Clang con eccellente supporto per la cross-compilation. Per usare Zig: + +```bash +# Compila ed esegui un programma Zen C con Zig +zc run app.zc --cc zig + +# Puoi compilare persino il compilatore Zen C stesso con Zig +make zig +``` + +### Interop C++ + +Zen C può generare codice compatibile con C++ utilizzando l'opzione `--cpp`, permettendo una integrazione fluida con le librerie C++. + +```bash +# Compilazione diretta con g++ +zc app.zc --cpp + +# O traspila per le build manuali +zc transpile app.zc --cpp +g++ out.c my_cpp_lib.o -o app +``` + +#### Usare C++ in Zen C + +Includi header C++ e usa blocchi grezzi per codice C++: + +```zc +include <vector> +include <iostream> + +raw { + std::vector<int> crea_vettore(int a, int b) { + return {a, b}; + } +} + +fn main() { + let v = crea_vettore(1, 2); + raw { std::cout << "Dimensione: " << v.size() << std::endl; } +} +``` + +> **Nota:** L'opzione `--cpp` rende il backend `g++` ed emette codice valido per C++ (utilizza `auto` al posto di `__auto_type`, overload delle funzioni al posto di `_Generic` e i cast espliciti per `void*`) + +#### Interop CUDA + +Zen C supporta la programmazione GPU traspilando a **CUDA C++**. Questo ti permette di utilizzare potenti funzionalità C++ (template, `constexpr`) all'interno dei tuoi kernel mantenendo la sintassi ergonomica di Zen C. + +```bash +# Compilazione diretta con nvcc +zc run app.zc --cuda + +# O traspila per le build manuali +zc transpile app.zc --cuda -o app.cu +nvcc app.cu -o app +``` + +#### Attributi specifici CUDA + +| Attributo | Equivalente CUDA | Descrizione | +|:---|:---|:---| +| `@global` | `__global__` | Function Kernel (esegue sulla GPU, chiamato dall'host) | +| `@device` | `__device__` | Funzione Device (esegue sulla GPU, chiamato dalla GPU) | +| `@host` | `__host__` | Funzione Host (Solo CPU esplicita) | + +#### Kernel Launch Syntax + +Zen C fornisce un'istruzione chiara `launch` per richiamare kernel CUDA: + +```zc +launch kernel_name(args) with { + grid: num_blocks, + block: threads_per_block, + shared_mem: 1024, // Opzionale + stream: my_stream // Opzionale +}; +``` + +Questo traspila a: `kernel_name<<<grid, block, shared, stream>>>(args);` + +#### Scrivere kernel CUDA + +Utilizza la sintassi delle funzioni Zen C con `@global` e l'istruzione `launch`: + +```zc +import "std/cuda.zc" + +@global +fn aggiungi_kernel(a: float*, b: float*, c: float*, n: int) { + let i = thread_id(); + if i < n { + c[i] = a[i] + b[i]; + } +} + +fn main() { + def N = 1024; + let d_a = cuda_alloc<float>(N); + let d_b = cuda_alloc<float>(N); + let d_c = cuda_alloc<float>(N); + defer cuda_free(d_a); + defer cuda_free(d_b); + defer cuda_free(d_c); + + // ... init data ... + + launch aggiungi_kernel(d_a, d_b, d_c, N) with { + grid: (N + 255) / 256, + block: 256 + }; + + cuda_sync(); +} +``` + +#### Libreria Standard (`std/cuda.zc`) +Zen C fornisce una libreria standard per delle operazioni comuni in CUDA per ridurre la mole di blocchi `raw` (grezzi): + +```zc +import "std/cuda.zc" + +// Gestione della memoria +let d_ptr = cuda_alloc<float>(1024); +cuda_copy_to_device(d_ptr, h_ptr, 1024 * sizeof(float)); +defer cuda_free(d_ptr); + +// Sincronizzazione +cuda_sync(); + +// Indicizzazione dei thread (usa all'interno del kernel) +let i = thread_id(); // Indice globale +let bid = block_id(); +let tid = local_id(); +``` + + +> **Nota:** La flag `--cuda` imposta `nvcc` come compilatore e implica la modalità `--cpp`. Richiede l'installazione dell'NVIDIA CUDA Toolkit. + +### Supporto C23 + +Zen C supporta le funzionalità moderne dello standard C23 quando si usa un backend compatibile (GCC 14+, Clang 14+, _TCC_ (_parziale_)). + +- **`auto`**: Zen C mappa automaticamente l'inferenza del tipo alla keyword `auto` di C23 (se `__STDC_VERSION__ >= 202300L`). +- **`_BitInt(N)`**: Usa i tipi `iN` e `uN` (e.g., `i256`, `u12`, `i24`) per accedere agli interi di lunghezza arbitraria di C23. + +### Interop Objective-C + +Zen C può compilare a Objective-C (`.m`) utilizzando la flag `--objc`, permettendoti di utilizzare i framework (come Cocoa/Foundation) e la sintassi Obj-C + +```bash +# Compila con clang (o gcc/gnustep) +zc app.zc --objc --cc clang +``` + +#### Usando l'Objective-C in Zen C + +Utilizza `include` per gli header e i blocchi `raw` per la sintassi Obj-C (`@interface`, `[...]`, `@""`). + +```zc +//> macos: framework: Foundation +//> linux: cflags: -fconstant-string-class=NSConstantString -D_NATIVE_OBJC_EXCEPTIONS +//> linux: link: -lgnustep-base -lobjc + +include <Foundation/Foundation.h> + +fn main() { + raw { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSLog(@"Ciao da Objective-C!"); + [pool drain]; + } + println "Funziona anche Zen C!"; +} +``` + +> **Nota:** L'interpolazione delle stringhe di Zen C funziona con gli oggetti dell'Objective-C (`id`) chiamando `debugDescription` oppure `description`. + +--- + +## Contribuisci + +Qui accogliamo tutti i contributori! Che siano fix di bug, miglioramenti alla documentazione, o la proposta di nuove funzionalità. + +### Come contribuire +1. **Forka la repository**: Workflow standard di GitHub. +2. **Crea un Branch per la funzionalità**: `git checkout -b feature/NewThing`. +3. **Guide Linea per il Codice**: + * Segui lo stile C esistente. + * Assicurati che passino tutti i test: `make test`. + * Aggiungi nuovi test per la tua nuova funzionalità in `tests/`. +4. **Crea una Pull Request**: Descrivi chiaramente le modifiche apportate. + +### Eseguire i test +La suite di test è il tuo miglior amico. + +```bash +# Esegui tutti i test (GCC) +make test + +# Esegui test specifici +./zc run tests/test_match.zc + +# Esegui test con un compilatore differente +./tests/run_tests.sh --cc clang +./tests/run_tests.sh --cc zig +./tests/run_tests.sh --cc tcc +``` + +### Estendere il Compilatore +* **Parser**: `src/parser/` - Parser a discesa ricorsiva. +* **Codegen**: `src/codegen/` - Logica di traspilazione (Zen C -> GNU C/C11). +* **Standard Library**: `std/` - Written in Zen C itself. + +--- + +## Attribuzioni + +Questo progetto utilizza librerie esterne. I testi di licenza completi possono essere trovati nella directory `LICENSES/`. + +* **[cJSON](https://github.com/DaveGamble/cJSON)** (Licenza MIT): Usato per il parsing e la generazione di JSON nel Language Server. +* **[zc-ape](https://github.com/OEvgeny/zc-ape)** (Licenza MIT): La versione originale di Actually Portable Executable di Zen-C, realizzata da [Eugene Olonov](https://github.com/OEvgeny). +* **[Cosmopolitan Libc](https://github.com/jart/cosmopolitan)** (Licenza ISC): La libreria fondamentale che rende possibile APE. + diff --git a/README_ZH_CN.md b/README_ZH_CN.md index 217e9ec..af83e8c 100644 --- a/README_ZH_CN.md +++ b/README_ZH_CN.md @@ -1,7 +1,7 @@ <div align="center"> -[English](README.md) • [简体中文](README_ZH_CN.md) • [繁體中文](README_ZH_TW.md) • [Español](README_ES.md) +[English](README.md) • [简体中文](README_ZH_CN.md) • [繁體中文](README_ZH_TW.md) • [Español](README_ES.md) • [Italiano](README_IT.md) </div> @@ -13,7 +13,7 @@ []() []() -[]() +[]() []() *像高级语言一样编写,像 C 一样运行。* @@ -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. 复合类型 #### 数组 @@ -485,26 +496,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 { ... } + +// 直接迭代固定大小数组 +let arr: int[5] = [1, 2, 3, 4, 5]; +for val in arr { + // val 是 int + println "{val}"; +} -// While 循环 +// While 循環 while x < 10 { ... } -// 带标签的无限循环 +// 帶標籤的無限循環 outer: loop { if done { break outer; } } -// 重复 N 次 +// 重複 N 次 for _ in 0..5 { ... } ``` @@ -606,7 +624,7 @@ Zen C 支持使用 `?` 前缀进行用户输入提示的简写。 - `? "输入年龄: " (age)`: 打印提示并扫描输入到变量 `age` 中。 - 格式说明符会根据变量类型自动推断。 -```c +```zc let age: int; ? "你多大了? " (age); println "你 {age} 岁了。"; @@ -938,6 +956,7 @@ let re = regex! { ^[a-z]+$ }; | `@host` | 函数 | CUDA: 主机函数 (`__host__`)。 | | `@comptime` | 函数 | 用于编译时执行的辅助函数。 | | `@derive(...)` | 结构体 | 自动实现 Trait。支持 `Debug`, `Eq` (智能派生), `Copy`, `Clone`。 | +| `@ctype("type")` | 函数参数 | 覆盖参数生成的 C 类型。 | | `@<custom>` | 任意 | 将泛型属性传递给 C (例如 `@flatten`, `@alias("name")`)。 | #### 自定义属性 @@ -988,12 +1007,13 @@ Zen C 通过命名绑定简化了复杂的 GCC 约束语法。 // 语法: : out(变量) : in(变量) : clobber(寄存器) // 使用 {变量} 占位符语法以提高可读性 -fn add(a: int, b: int) -> int { +fn add_five(x: int) -> int { let result: int; asm { - "add {result}, {a}, {b}" + "mov {x}, {result}" + "add $5, {result}" : out(result) - : in(a), in(b) + : in(x) : clobber("cc") } return result; @@ -1082,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 8618540..3a18a53 100644 --- a/README_ZH_TW.md +++ b/README_ZH_TW.md @@ -1,7 +1,7 @@ <div align="center"> -[English](README.md) • [简体中文](README_ZH_CN.md) • [繁體中文](README_ZH_TW.md) • [Español](README_ES.md) +[English](README.md) • [简体中文](README_ZH_CN.md) • [繁體中文](README_ZH_TW.md) • [Español](README_ES.md) • [Italiano](README_IT.md) </div> @@ -13,7 +13,7 @@ []() []() -[]() +[]() []() *像高級語言一樣編寫,像 C 一樣運行。* @@ -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. 複合類型 @@ -493,8 +504,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 +624,7 @@ Zen C 支持使用 `?` 前綴進行用戶輸入提示的簡寫。 - `? "輸入年齡: " (age)`: 打印提示並掃描輸入到變量 `age` 中。 - 格式說明符會根據變量類型自動推斷。 -```c +```zc let age: int; ? "你多大了? " (age); println "你 {age} 歲了。"; @@ -938,6 +956,7 @@ let re = regex! { ^[a-z]+$ }; | `@host` | 函數 | CUDA: 主機函數 (`__host__`)。 | | `@comptime` | 函數 | 用於編譯時執行的輔助函數。 | | `@derive(...)` | 結構體 | 自動實現 Trait。支持 `Debug`, `Eq` (智能派生), `Copy`, `Clone`。 | +| `@ctype("type")` | 函數參數 | 覆蓋參數生成的 C 類型。 | | `@<custom>` | 任意 | 將泛型屬性傳遞給 C (例如 `@flatten`, `@alias("name")`)。 | #### 自定義屬性 @@ -988,12 +1007,13 @@ Zen C 通過命名綁定簡化了複雜的 GCC 約束語法。 // 語法: : out(變量) : in(變量) : clobber(寄存器) // 使用 {變量} 佔位符語法以提高可讀性 -fn add(a: int, b: int) -> int { +fn add_five(x: int) -> int { let result: int; asm { - "add {result}, {a}, {b}" + "mov {x}, {result}" + "add $5, {result}" : out(result) - : in(a), in(b) + : in(x) : clobber("cc") } return result; @@ -1082,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/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..f029995 --- /dev/null +++ b/docs/std/slice.md @@ -0,0 +1,93 @@ +# 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 (auto-imports std/slice.zc) + for val in arr { + println "{val}"; + } + + // Manual slice creation + 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. | +| **new** | `Slice<T>::new(data: T*, len: usize) -> Slice<T>` | Alias for `from_array` (backwards compat). | + +### 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 +// std/slice.zc is auto-imported when using for-in on arrays +let numbers: int[3] = [10, 20, 30]; + +for n in numbers { + println "Number: {n}"; +} +``` + +### Safe indexed access + +```zc +import "std/slice.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) +- **Auto-import**: `std/slice.zc` is automatically imported when using `for val in arr` on a fixed-size array +- The array pointer cast `(T*)(&arr)` is required for fixed-size arrays diff --git a/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/examples/networking/echo_server.zc b/examples/networking/echo_server.zc index 2934923..82ecd82 100644 --- a/examples/networking/echo_server.zc +++ b/examples/networking/echo_server.zc @@ -4,6 +4,7 @@ import "std/net.zc" def SIZE = 1024; fn main() { + setbuf(stdout, NULL); "Starting Echo Server on 127.0.0.1:8080..."; let listener_res = TcpListener::bind("127.0.0.1", 8080); @@ -13,24 +14,37 @@ fn main() { } let listener = listener_res.unwrap(); - defer listener.close(); loop { let client_res = listener.accept(); if client_res.is_ok() { let stream = client_res.unwrap(); - defer stream.close(); - let buf = (char*)malloc(SIZE); - defer free(buf); - let read_res = stream.read(buf, SIZE); + "New Connection!"; - if read_res.is_ok() { + let buf: char[SIZE]; + + while true { + let read_res = stream.read(&buf[0], SIZE); + + if read_res.is_err() { + !"Read error: {read_res.err}"; + break; + } + let bytes = read_res.unwrap(); - if bytes > 0 { - stream.write(buf, bytes); - "Echoed {bytes} bytes."; + if bytes == 0 { + "Client disconnected."; + break; } + + let write_res = stream.write(&buf[0], bytes); + if write_res.is_err() { + !"Write error: {write_res.err}"; + break; + } + + "Echoed {bytes} bytes."; } } } diff --git a/src/ast/ast.c b/src/ast/ast.c index 439a9f5..28fe678 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -101,6 +101,9 @@ int is_integer_type(Type *t) t->kind == TYPE_ISIZE || t->kind == TYPE_BYTE || t->kind == TYPE_RUNE || t->kind == TYPE_UINT || t->kind == TYPE_I128 || t->kind == TYPE_U128 || t->kind == TYPE_BITINT || t->kind == TYPE_UBITINT || + t->kind == TYPE_C_INT || t->kind == TYPE_C_UINT || t->kind == TYPE_C_LONG || + t->kind == TYPE_C_ULONG || t->kind == TYPE_C_SHORT || t->kind == TYPE_C_USHORT || + t->kind == TYPE_C_CHAR || t->kind == TYPE_C_UCHAR || (t->kind == TYPE_STRUCT && t->name && (0 == strcmp(t->name, "int8_t") || 0 == strcmp(t->name, "uint8_t") || 0 == strcmp(t->name, "int16_t") || 0 == strcmp(t->name, "uint16_t") || @@ -259,6 +262,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 +483,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 +562,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 +607,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 71d9943..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 (*). @@ -229,6 +239,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..384820b 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -1,4 +1,3 @@ - #include "codegen.h" #include "zprep.h" #include "../constants.h" @@ -59,12 +58,58 @@ static void codegen_var_expr(ParserContext *ctx, ASTNode *node, FILE *out) if (node->var_ref.suggestion && !ctx->silent_warnings) { char msg[256]; - sprintf(msg, "Undefined variable '%s'", node->var_ref.name); char help[256]; - sprintf(help, "Did you mean '%s'?", node->var_ref.suggestion); + snprintf(msg, sizeof(msg), "Undefined variable '%s'", node->var_ref.name); + snprintf(help, sizeof(help), "Did you mean '%s'?", node->var_ref.suggestion); zwarn_at(node->token, "%s\n = help: %s", msg, help); } } + + // 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); } @@ -146,7 +191,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) else if ((strcmp(node->binary.op, "==") == 0 || strcmp(node->binary.op, "!=") == 0)) { char *t1 = infer_type(ctx, node->binary.left); - int is_ptr = 0; if (t1) { @@ -161,19 +205,16 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } int resolved = 0; ASTNode *alias = global_user_structs; - if (alias) + while (alias) { - while (alias) + if (alias->type == NODE_TYPE_ALIAS && + strcmp(check, alias->type_alias.alias) == 0) { - if (alias->type == NODE_TYPE_ALIAS && - strcmp(check, alias->type_alias.alias) == 0) - { - check = alias->type_alias.original_type; - resolved = 1; - break; - } - alias = alias->next; + check = alias->type_alias.original_type; + resolved = 1; + break; } + alias = alias->next; } if (!resolved) { @@ -183,8 +224,8 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } int is_basic = IS_BASIC_TYPE(t1); - ASTNode *def = t1 ? find_struct_def(ctx, t1) : NULL; + if (t1 && def && (def->type == NODE_STRUCT || def->type == NODE_ENUM) && !is_basic && !is_ptr) { @@ -239,8 +280,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) else if (t1 && (strcmp(t1, "string") == 0 || strcmp(t1, "char*") == 0 || strcmp(t1, "const char*") == 0)) { - // Check if comparing to NULL - don't use strcmp for NULL comparisons - char *t2 = infer_type(ctx, node->binary.right); int is_null_compare = 0; if (node->binary.right->type == NODE_EXPR_VAR && strcmp(node->binary.right->var_ref.name, "NULL") == 0) @@ -252,9 +291,28 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { is_null_compare = 1; } + else if (node->binary.right->type == NODE_EXPR_LITERAL && + node->binary.right->literal.type_kind == LITERAL_INT && + node->binary.right->literal.int_val == 0) + { + is_null_compare = 1; + } + else if (node->binary.left->type == NODE_EXPR_LITERAL && + node->binary.left->literal.type_kind == LITERAL_INT && + node->binary.left->literal.int_val == 0) + { + is_null_compare = 1; + } - if (!is_null_compare && strcmp(t1, "string") == 0 && t2 && - strcmp(t2, "string") == 0) + if (is_null_compare) + { + fprintf(out, "("); + codegen_expression(ctx, node->binary.left, out); + fprintf(out, " %s ", node->binary.op); + codegen_expression(ctx, node->binary.right, out); + fprintf(out, ")"); + } + else { fprintf(out, "(strcmp("); codegen_expression(ctx, node->binary.left, out); @@ -269,19 +327,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) fprintf(out, ") != 0)"); } } - else - { - // Direct pointer comparison - fprintf(out, "("); - codegen_expression(ctx, node->binary.left, out); - fprintf(out, " %s ", node->binary.op); - codegen_expression(ctx, node->binary.right, out); - fprintf(out, ")"); - } - if (t2) - { - free(t2); - } } else { @@ -291,6 +336,10 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) codegen_expression(ctx, node->binary.right, out); fprintf(out, ")"); } + if (t1) + { + free(t1); + } } else { @@ -354,7 +403,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) if (def && def->type == NODE_ENUM) { char mangled[256]; - sprintf(mangled, "%s_%s", target->var_ref.name, method); + snprintf(mangled, sizeof(mangled), "%s_%s", target->var_ref.name, method); FuncSig *sig = find_func(ctx, mangled); if (sig) { @@ -363,7 +412,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) int arg_idx = 0; while (arg) { - if (arg_idx > 0 && arg) + if (arg_idx > 0) { fprintf(out, ", "); } @@ -371,7 +420,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) Type *param_t = (arg_idx < sig->total_args) ? sig->arg_types[arg_idx] : NULL; - // Tuple Packing Logic if (param_t && param_t->kind == TYPE_STRUCT && strncmp(param_t->name, "Tuple_", 6) == 0 && sig->total_args == 1 && node->call.arg_count > 1) @@ -389,7 +437,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) arg = arg->next; } fprintf(out, "}"); - break; // All args consumed + break; } codegen_expression(ctx, arg, out); @@ -409,7 +457,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) char *ptr = strchr(clean, '*'); if (ptr) { - *ptr = 0; + *ptr = '\0'; } char *base = clean; @@ -418,11 +466,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,36 +515,34 @@ 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); + snprintf(mixin_func_name, sizeof(mixin_func_name), "%s__%s", call_base, method); char *resolved_method_suffix = NULL; if (!find_func(ctx, mixin_func_name)) { - // Try resolving as a trait method: Struct__Trait_Method StructRef *ref = ctx->parsed_impls_list; while (ref) { - if (ref->node && ref->node->type == NODE_IMPL_TRAIT) + if (ref->node && ref->node->type == NODE_IMPL_TRAIT && + strcmp(ref->node->impl_trait.target_type, base) == 0) { - if (strcmp(ref->node->impl_trait.target_type, base) == 0) + char trait_mangled[256]; + snprintf(trait_mangled, sizeof(trait_mangled), "%s__%s_%s", base, + ref->node->impl_trait.trait_name, method); + if (find_func(ctx, trait_mangled)) { - char trait_mangled[256]; - sprintf(trait_mangled, "%s__%s_%s", base, - ref->node->impl_trait.trait_name, method); - if (find_func(ctx, trait_mangled)) - { - char *suffix = - xmalloc(strlen(ref->node->impl_trait.trait_name) + - strlen(method) + 2); - sprintf(suffix, "%s_%s", ref->node->impl_trait.trait_name, - method); - resolved_method_suffix = suffix; - break; - } + size_t suffix_len = strlen(ref->node->impl_trait.trait_name) + + strlen(method) + 2; + char *suffix = xmalloc(suffix_len); + snprintf(suffix, suffix_len, "%s_%s", + ref->node->impl_trait.trait_name, method); + resolved_method_suffix = suffix; + break; } } ref = ref->next; @@ -479,15 +557,14 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) if (it->impl_node && it->impl_node->type == NODE_IMPL_TRAIT) { tname = it->impl_node->impl_trait.trait_name; - } - if (tname) - { char trait_mangled[512]; - sprintf(trait_mangled, "%s__%s_%s", base, tname, method); + snprintf(trait_mangled, sizeof(trait_mangled), "%s__%s_%s", + base, tname, method); if (find_func(ctx, trait_mangled)) { - char *suffix = xmalloc(strlen(tname) + strlen(method) + 2); - sprintf(suffix, "%s_%s", tname, method); + size_t suffix_len = strlen(tname) + strlen(method) + 2; + char *suffix = xmalloc(suffix_len); + snprintf(suffix, suffix_len, "%s_%s", tname, method); resolved_method_suffix = suffix; break; } @@ -502,15 +579,14 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } else { - // Method not found on primary struct, check mixins ASTNode *def = find_struct_def(ctx, base); if (def && def->type == NODE_STRUCT && def->strct.used_structs) { for (int k = 0; k < def->strct.used_struct_count; k++) { char mixin_check[128]; - sprintf(mixin_check, "%s__%s", def->strct.used_structs[k], - method); + snprintf(mixin_check, sizeof(mixin_check), "%s__%s", + def->strct.used_structs[k], method); if (find_func(ctx, mixin_check)) { call_base = def->strct.used_structs[k]; @@ -540,11 +616,22 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) arg = arg->next; } fprintf(out, ")"); + + if (resolved_method_suffix) + { + free(resolved_method_suffix); + } } free(clean); + free(type); return; } + if (type) + { + free(type); + } } + if (node->call.callee->type == NODE_EXPR_VAR) { ASTNode *def = find_struct_def(ctx, node->call.callee->var_ref.name); @@ -600,26 +687,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) if (node->call.arg_names && node->call.callee->type == NODE_EXPR_VAR) { - char *fn_name = node->call.callee->var_ref.name; - FuncSig *sig = find_func(ctx, fn_name); - - if (sig && sig->arg_types) - { - for (int p = 0; p < sig->total_args; p++) - { - ASTNode *arg = node->call.args; - - for (int i = 0; i < node->call.arg_count && arg; i++, arg = arg->next) - { - if (node->call.arg_names[i] && p < node->call.arg_count) - { - - // For now, emit in order provided... - } - } - } - } - ASTNode *arg = node->call.args; int first = 1; while (arg) @@ -666,11 +733,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) strncmp(param_t->name, "Tuple_", 6) == 0 && sig->total_args == 1 && node->call.arg_count > 1) { - // Implicit Tuple Packing: - // Function expects 1 Tuple argument, but call has multiple args -> Pack - // them fprintf(out, "(%s){", param_t->name); - ASTNode *curr = arg; int first_field = 1; while (curr) @@ -685,8 +748,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } fprintf(out, "}"); handled = 1; - - // Advance main loop iterator to end arg = NULL; } } @@ -695,7 +756,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { if (arg == NULL) { - break; // Tuple packed all args + break; } } else @@ -720,16 +781,12 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) case NODE_EXPR_MEMBER: if (strcmp(node->member.field, "len") == 0) { - if (node->member.target->type_info) + if (node->member.target->type_info && + node->member.target->type_info->kind == TYPE_ARRAY && + node->member.target->type_info->array_size > 0) { - if (node->member.target->type_info->kind == TYPE_ARRAY) - { - if (node->member.target->type_info->array_size > 0) - { - fprintf(out, "%d", node->member.target->type_info->array_size); - break; - } - } + fprintf(out, "%d", node->member.target->type_info->array_size); + break; } } @@ -751,16 +808,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } else { - if (node->member.target->type == NODE_EXPR_CAST) - { - fprintf(out, "("); - } codegen_expression(ctx, node->member.target, out); - if (node->member.target->type == NODE_EXPR_CAST) - { - fprintf(out, ")"); - } - // Verify actual type instead of trusting is_pointer_access flag char *lt = infer_type(ctx, node->member.target); int actually_ptr = 0; if (lt && (lt[strlen(lt) - 1] == '*' || strstr(lt, "*"))) @@ -771,6 +819,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { free(lt); } + char *field = node->member.field; if (field && field[0] >= '0' && field[0] <= '9') { @@ -793,7 +842,8 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) is_slice_struct = 1; } } - if (node->index.array->resolved_type) + + if (!is_slice_struct && node->index.array->resolved_type) { if (strncmp(node->index.array->resolved_type, "Slice_", 6) == 0) { @@ -910,7 +960,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) } else { - fprintf(out, "/* UNSAFE: Full Slice on unknown size */ 0; "); + fprintf(out, "0; "); } } @@ -926,6 +976,10 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) fprintf(out, "(Slice_%s){ .data = _arr + _start, .len = _len, .cap = _len }; })", tname); } + if (tname && strcmp(tname, "unknown") != 0) + { + free(tname); + } break; } case NODE_BLOCK: @@ -1016,9 +1070,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) break; case NODE_PLUGIN: { - // Plugin registry - declare external plugins ZPlugin *found = zptr_find_plugin(node->plugin_stmt.plugin_name); - if (found) { ZApi api = {.filename = g_current_filename ? g_current_filename : "input.zc", @@ -1098,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, ")"); + 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 { @@ -1131,20 +1271,19 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { Type *t = node->reflection.target_type; if (node->reflection.kind == 0) - { // @type_name + { char *s = codegen_type_to_string(t); fprintf(out, "\"%s\"", s); free(s); } else - { // @fields + { if (t->kind != TYPE_STRUCT || !t->name) { fprintf(out, "((void*)0)"); break; } char *sname = t->name; - // Find definition ASTNode *def = find_struct_def(ctx, sname); if (!def) { 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..1623ffc 100644 --- a/src/codegen/codegen_decl.c +++ b/src/codegen/codegen_decl.c @@ -50,6 +50,7 @@ void emit_preamble(ParserContext *ctx, FILE *out) else { // Standard hosted preamble. + fputs("#define _GNU_SOURCE\n", out); fputs("#include <stdio.h>\n#include <stdlib.h>\n#include " "<stddef.h>\n#include <string.h>\n", out); @@ -698,7 +699,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 +722,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 +800,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"); } @@ -1129,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/codegen/codegen_main.c b/src/codegen/codegen_main.c index a140070..82fc3ce 100644 --- a/src/codegen/codegen_main.c +++ b/src/codegen/codegen_main.c @@ -448,6 +448,39 @@ void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out) emit_type_aliases(kids, out); // Emit local aliases (redundant but safe) emit_trait_defs(kids, out); + // Also emit traits from parsed_globals_list (from auto-imported files like std/mem.zc) + // but only if they weren't already emitted from kids + StructRef *trait_ref = ctx->parsed_globals_list; + while (trait_ref) + { + if (trait_ref->node && trait_ref->node->type == NODE_TRAIT) + { + // Check if this trait was already in kids (explicitly imported) + int already_in_kids = 0; + ASTNode *k = kids; + while (k) + { + if (k->type == NODE_TRAIT && k->trait.name && trait_ref->node->trait.name && + strcmp(k->trait.name, trait_ref->node->trait.name) == 0) + { + already_in_kids = 1; + break; + } + k = k->next; + } + + if (!already_in_kids) + { + // Create a temporary single-node list for emit_trait_defs + ASTNode *saved_next = trait_ref->node->next; + trait_ref->node->next = NULL; + emit_trait_defs(trait_ref->node, out); + trait_ref->node->next = saved_next; + } + } + trait_ref = trait_ref->next; + } + // Track emitted raw statements to prevent duplicates EmittedContent *emitted_raw = NULL; @@ -616,7 +649,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..92c5395 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 @@ -169,6 +193,7 @@ char *infer_type(ParserContext *ctx, ASTNode *node) { return NULL; } + if (node->resolved_type && strcmp(node->resolved_type, "unknown") != 0 && strcmp(node->resolved_type, "void*") != 0) { @@ -302,6 +327,65 @@ char *infer_type(ParserContext *ctx, ASTNode *node) return extracted; } } + + // Find the struct/enum definition and look for "Ok" or "val" + char *search_name = inner_type; + if (strncmp(search_name, "struct ", 7) == 0) + { + search_name += 7; + } + + ASTNode *def = find_struct_def_codegen(ctx, search_name); + if (!def) + { + // check enums list explicitly if not found in instantiated list + StructRef *er = ctx->parsed_enums_list; + while (er) + { + if (er->node && er->node->type == NODE_ENUM && + strcmp(er->node->enm.name, search_name) == 0) + { + def = er->node; + break; + } + er = er->next; + } + } + + if (def) + { + if (def->type == NODE_ENUM) + { + // Look for "Ok" variant + ASTNode *var = def->enm.variants; + while (var) + { + if (var->variant.name && strcmp(var->variant.name, "Ok") == 0) + { + if (var->variant.payload) + { + return codegen_type_to_string(var->variant.payload); + } + // Ok with no payload? Then it's void/u0. + return "void"; + } + var = var->next; + } + } + else if (def->type == NODE_STRUCT) + { + // Look for "val" field + ASTNode *field = def->strct.fields; + while (field) + { + if (field->field.name && strcmp(field->field.name, "val") == 0) + { + return xstrdup(field->field.type); + } + field = field->next; + } + } + } } } @@ -644,7 +728,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 +798,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 +813,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, ¶m_names, &is_varargs); + char *args = parse_and_convert_args(ctx, l, &defaults, &count, &arg_types, ¶m_names, + &is_varargs, &ctype_overrides); char *ret = "void"; Type *ret_type_obj = type_new(TYPE_VOID); @@ -191,6 +192,7 @@ ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async) node->func.defaults = defaults; node->func.ret_type_info = ret_type_obj; node->func.is_varargs = is_varargs; + node->func.c_type_overrides = ctype_overrides; if (gen_param) { diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c index 6156cc0..f27e2c3 100644 --- a/src/parser/parser_expr.c +++ b/src/parser/parser_expr.c @@ -370,8 +370,7 @@ static void check_format_string(ASTNode *call, Token t) if (spec == 'd' || spec == 'i' || spec == 'u' || spec == 'x' || spec == 'X' || spec == 'o') { - if (vt && vt->kind != TYPE_INT && vt->kind != TYPE_I64 && !type_is_unsigned(vt) && - vt->kind != TYPE_CHAR) + if (vt && !is_integer_type(vt)) { warn_format_string(t, arg_num, "integer", got_type); } @@ -1908,7 +1907,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 +2943,7 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l) { if (i > 0) { - strcat(sig, "_"); + strcat(sig, "__"); } strcat(sig, type_strs[i]); } @@ -4116,6 +4115,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) break; } ASTNode *node = ast_create(NODE_EXPR_MEMBER); + node->token = field; node->member.target = lhs; node->member.field = token_strdup(field); node->member.is_pointer_access = 1; @@ -4169,6 +4169,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) break; } ASTNode *node = ast_create(NODE_EXPR_MEMBER); + node->token = field; node->member.target = lhs; node->member.field = token_strdup(field); node->member.is_pointer_access = 2; @@ -4350,6 +4351,52 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) if (sig) { + // Check if this is a static method being called with dot operator + // Static methods don't have 'self' as first parameter + int is_static_method = 0; + if (sig->total_args == 0) + { + // No arguments at all - definitely static + is_static_method = 1; + } + else if (sig->arg_types[0]) + { + // Check if first parameter is a pointer to the struct type + // Instance methods have: fn method(self) where self is StructType* + // Static methods have: fn method(x: int, y: int) etc. + Type *first_param = sig->arg_types[0]; + + // If first param is not a pointer, it's likely static + // OR if it's a pointer but not to this struct type + if (first_param->kind != TYPE_POINTER) + { + is_static_method = 1; + } + else if (first_param->inner) + { + // Check if the inner type matches the struct + char *inner_name = NULL; + if (first_param->inner->kind == TYPE_STRUCT) + { + inner_name = first_param->inner->name; + } + + if (!inner_name || strcmp(inner_name, struct_name) != 0) + { + is_static_method = 1; + } + } + } + + if (is_static_method) + { + zpanic_at(lhs->token, + "Cannot call static method '%s' with dot operator\n" + " = help: Use '%s::%s(...)' instead of instance.%s(...)", + lhs->member.field, struct_name, lhs->member.field, + lhs->member.field); + } + resolved_name = xstrdup(mangled); resolved_sig = sig; @@ -4772,6 +4819,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) break; } ASTNode *node = ast_create(NODE_EXPR_MEMBER); + node->token = field; node->member.target = lhs; node->member.field = token_strdup(field); node->member.is_pointer_access = 0; @@ -5421,6 +5469,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); @@ -5643,7 +5692,17 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) char *t1 = type_to_string(lhs->type_info); char *t2 = type_to_string(rhs->type_info); // Skip type check if either operand is void* (escape hatch type) + // or if either operand is a generic type parameter (T, K, V, etc.) int skip_check = (strcmp(t1, "void*") == 0 || strcmp(t2, "void*") == 0); + if (lhs->type_info->kind == TYPE_GENERIC || rhs->type_info->kind == TYPE_GENERIC) + { + skip_check = 1; + } + // Also check if type name is a single uppercase letter (common generic param) + if ((strlen(t1) == 1 && isupper(t1[0])) || (strlen(t2) == 1 && isupper(t2[0]))) + { + skip_check = 1; + } // Allow comparing pointers/strings with integer literal 0 (NULL) if (!skip_check) diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index a471fe6..ae16243 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -14,6 +14,118 @@ char *curr_func_ret = NULL; char *run_comptime_block(ParserContext *ctx, Lexer *l); +extern char *g_current_filename; + +/** + * @brief Auto-imports std/slice.zc if not already imported. + * + * This is called when array iteration is detected in for-in loops, + * to ensure the Slice<T>, SliceIter<T>, and Option<T> templates are available. + */ +static void auto_import_std_slice(ParserContext *ctx) +{ + // Check if already imported via templates + GenericTemplate *t = ctx->templates; + while (t) + { + if (strcmp(t->name, "Slice") == 0) + { + return; // Already have the Slice template + } + t = t->next; + } + + // Try to find and import std/slice.zc + static const char *std_paths[] = {"std/slice.zc", "./std/slice.zc", NULL}; + static const char *system_paths[] = {"/usr/local/share/zenc", "/usr/share/zenc", NULL}; + + char resolved_path[1024]; + int found = 0; + + // First, try relative to current file + if (g_current_filename) + { + char *current_dir = xstrdup(g_current_filename); + char *last_slash = strrchr(current_dir, '/'); + if (last_slash) + { + *last_slash = 0; + snprintf(resolved_path, sizeof(resolved_path), "%s/std/slice.zc", current_dir); + if (access(resolved_path, R_OK) == 0) + { + found = 1; + } + } + free(current_dir); + } + + // Try relative paths + if (!found) + { + for (int i = 0; std_paths[i] && !found; i++) + { + if (access(std_paths[i], R_OK) == 0) + { + strncpy(resolved_path, std_paths[i], sizeof(resolved_path) - 1); + resolved_path[sizeof(resolved_path) - 1] = '\0'; + found = 1; + } + } + } + + // Try system paths + if (!found) + { + for (int i = 0; system_paths[i] && !found; i++) + { + snprintf(resolved_path, sizeof(resolved_path), "%s/std/slice.zc", system_paths[i]); + if (access(resolved_path, R_OK) == 0) + { + found = 1; + } + } + } + + if (!found) + { + return; // Could not find std/slice.zc, instantiate_generic will error + } + + // Canonicalize path + char *real_fn = realpath(resolved_path, NULL); + if (real_fn) + { + strncpy(resolved_path, real_fn, sizeof(resolved_path) - 1); + resolved_path[sizeof(resolved_path) - 1] = '\0'; + free(real_fn); + } + + // Check if already imported + if (is_file_imported(ctx, resolved_path)) + { + return; + } + mark_file_imported(ctx, resolved_path); + + // Load and parse the file + char *src = load_file(resolved_path); + if (!src) + { + return; // Could not load file + } + + Lexer i; + lexer_init(&i, src); + + // Save and restore filename context + char *saved_fn = g_current_filename; + g_current_filename = resolved_path; + + // Parse the slice module contents + parse_program_nodes(ctx, &i); + + g_current_filename = saved_fn; +} static void check_assignment_condition(ASTNode *cond) { @@ -730,7 +842,7 @@ ASTNode *parse_asm(ParserContext *ctx, Lexer *l) } } - // Parse clobbers (: "eax", "memory") + // Parse clobbers (: "eax", "memory" OR : clobber("eax"), clobber("memory")) char **clobbers = NULL; int num_clobbers = 0; @@ -753,17 +865,36 @@ ASTNode *parse_asm(ParserContext *ctx, Lexer *l) continue; } - if (t.type == TOK_STRING) + // check for clobber("...") + if (t.type == TOK_IDENT && strncmp(t.start, "clobber", 7) == 0) { - lexer_next(l); - // Extract string content - char *clob = xmalloc(t.len); - strncpy(clob, t.start + 1, t.len - 2); - clob[t.len - 2] = 0; - clobbers[num_clobbers++] = clob; + lexer_next(l); // eat clobber + if (lexer_peek(l).type != TOK_LPAREN) + { + zpanic_at(lexer_peek(l), "Expected ( after clobber"); + } + lexer_next(l); // eat ( + + Token clob = lexer_next(l); + if (clob.type != TOK_STRING) + { + zpanic_at(clob, "Expected string literal for clobber"); + } + + if (lexer_peek(l).type != TOK_RPAREN) + { + zpanic_at(lexer_peek(l), "Expected ) after clobber string"); + } + lexer_next(l); // eat ) + + char *c = xmalloc(clob.len); + strncpy(c, clob.start + 1, clob.len - 2); + c[clob.len - 2] = 0; + clobbers[num_clobbers++] = c; } else { + zpanic_at(t, "Expected 'clobber(\"...\")' in clobber list"); break; } } @@ -1133,6 +1264,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 +1274,80 @@ 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 + // First, ensure std/slice.zc is imported (auto-import if needed) + auto_import_std_slice(ctx); + Token dummy_tok = {0}; + instantiate_generic(ctx, "Slice", elem_type_str, elem_type_str, dummy_tok); + + // 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 +1388,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 +1426,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 +1442,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 +1470,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) + // Body block + ASTNode *stmt = parse_statement(ctx, l); + ASTNode *user_body_node = stmt; + if (stmt && stmt->type != NODE_BLOCK) { - user_body_node = parse_block(ctx, l); - } - else - { - ASTNode *stmt = parse_statement(ctx, l); ASTNode *blk = ast_create(NODE_BLOCK); blk->block.statements = stmt; user_body_node = blk; @@ -1256,10 +1504,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 +3438,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..0c984a6 100644 --- a/src/parser/parser_struct.c +++ b/src/parser/parser_struct.c @@ -12,6 +12,114 @@ #include "zprep_plugin.h" #include "../codegen/codegen.h" +extern char *g_current_filename; + +/** + * @brief Auto-imports std/mem.zc if not already imported. + * + * This is called when the Drop trait is used (impl Drop for X). + */ +static void auto_import_std_mem(ParserContext *ctx) +{ + // Check if Drop trait is already registered (means mem.zc was imported) + if (check_impl(ctx, "Drop", "__trait_marker__")) + { + // Check_impl returns 0 if not found, but we need a different check + // Let's check if we can find any indicator that mem.zc was loaded + } + + // Try to find and import std/mem.zc + static const char *std_paths[] = {"std/mem.zc", "./std/mem.zc", NULL}; + static const char *system_paths[] = {"/usr/local/share/zenc", "/usr/share/zenc", NULL}; + + char resolved_path[1024]; + int found = 0; + + // First, try relative to current file + if (g_current_filename) + { + char *current_dir = xstrdup(g_current_filename); + char *last_slash = strrchr(current_dir, '/'); + if (last_slash) + { + *last_slash = 0; + snprintf(resolved_path, sizeof(resolved_path), "%s/std/mem.zc", current_dir); + if (access(resolved_path, R_OK) == 0) + { + found = 1; + } + } + free(current_dir); + } + + // Try relative paths + if (!found) + { + for (int i = 0; std_paths[i] && !found; i++) + { + if (access(std_paths[i], R_OK) == 0) + { + strncpy(resolved_path, std_paths[i], sizeof(resolved_path) - 1); + resolved_path[sizeof(resolved_path) - 1] = '\0'; + found = 1; + } + } + } + + // Try system paths + if (!found) + { + for (int i = 0; system_paths[i] && !found; i++) + { + snprintf(resolved_path, sizeof(resolved_path), "%s/std/mem.zc", system_paths[i]); + if (access(resolved_path, R_OK) == 0) + { + found = 1; + } + } + } + + if (!found) + { + return; // Could not find std/mem.zc + } + + // Canonicalize path + char *real_fn = realpath(resolved_path, NULL); + if (real_fn) + { + strncpy(resolved_path, real_fn, sizeof(resolved_path) - 1); + resolved_path[sizeof(resolved_path) - 1] = '\0'; + free(real_fn); + } + + // Check if already imported + if (is_file_imported(ctx, resolved_path)) + { + return; + } + mark_file_imported(ctx, resolved_path); + + // Load and parse the file + char *src = load_file(resolved_path); + if (!src) + { + return; // Could not load file + } + + Lexer i; + lexer_init(&i, src); + + // Save and restore filename context + char *saved_fn = g_current_filename; + g_current_filename = resolved_path; + + // Parse the mem module contents + parse_program_nodes(ctx, &i); + + g_current_filename = saved_fn; +} + // Trait Parsing ASTNode *parse_trait(ParserContext *ctx, Lexer *l) { @@ -100,7 +208,7 @@ ASTNode *parse_trait(ParserContext *ctx, Lexer *l) char **param_names = NULL; int is_varargs = 0; char *args = parse_and_convert_args(ctx, l, &defaults, &arg_count, &arg_types, ¶m_names, - &is_varargs); + &is_varargs, NULL); char *ret = xstrdup("void"); if (lexer_peek(l).type == TOK_ARROW) @@ -149,6 +257,7 @@ ASTNode *parse_trait(ParserContext *ctx, Lexer *l) } register_trait(name); + add_to_global_list(ctx, n_node); // Track for codegen (VTable emission) return n_node; } @@ -206,6 +315,22 @@ ASTNode *parse_impl(ParserContext *ctx, Lexer *l) register_generic(ctx, target_gen_param); } + // Check for common error: swapped Struct and Trait + // impl MyStruct for MyTrait (Wrong) vs impl MyTrait for MyStruct (Correct) + if (!is_trait(name1) && is_trait(name2)) + { + zpanic_at(t1, + "Incorrect usage of impl. Did you mean 'impl %s for %s'? Syntax is 'impl " + "<Trait> for <Struct>'", + name2, name1); + } + + // Auto-import std/mem.zc if implementing Drop, Copy, or Clone traits + if (strcmp(name1, "Drop") == 0 || strcmp(name1, "Copy") == 0 || strcmp(name1, "Clone") == 0) + { + auto_import_std_mem(ctx); + } + register_impl(ctx, name1, name2); // RAII: Check for "Drop" trait implementation @@ -748,7 +873,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); @@ -832,7 +957,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); @@ -1005,7 +1130,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 48418b6..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; @@ -3392,7 +3406,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 +3421,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 +3519,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 +3564,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 +3652,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; } @@ -18,5 +18,6 @@ import "./std/stack.zc" import "./std/queue.zc" import "./std/env.zc" import "./std/slice.zc" +import "./std/regex.zc" import "./std/process.zc" 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; @@ -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(); } @@ -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: 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*); + +// 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,25 +90,17 @@ 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_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_fclose(stream: void*) -> 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*) -> 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: 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; +extern fn _z_fs_opendir(name: const char*) -> void*; +extern fn _z_fs_closedir(dir: void*) -> c_int; struct File { @@ -203,41 +191,50 @@ impl File { } fn exists(path: char*) -> bool { - return _z_fs_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 (_z_fs_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 (_z_fs_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); @@ -257,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 @@ -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) -> 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) -> c_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*) -> c_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 { +fn format_into(buffer: char*, size: usize, fmt: char*, ...) -> c_int { let ap: va_list; va_start(ap, fmt); + let ret = _z_vsnprintf(buffer, size, fmt, ap); va_end(ap); + return ret; } @@ -42,23 +56,25 @@ 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 { +fn print(fmt: char*, ...) -> c_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; } -fn println(fmt: char*, ...) -> int { +fn println(fmt: char*, ...) -> c_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; @@ -70,14 +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(); - let eof_val = _z_get_eof(); while (true) { c = _z_fgetc(std_in); - if (c == eof_val) 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; @@ -93,7 +110,8 @@ fn readln() -> char* { len = len + 1; } - if (len == 0 && c == eof_val) { + let eof_final: c_int = Z_EOF; + if (len == 0 && c == eof_final) { 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("}"); + } + } +} @@ -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> { @@ -49,28 +49,7 @@ impl Box<T> { } } -struct Slice<T> { - data: T*; - len: usize; -} - -impl Slice<T> { - fn new(data: T*, len: usize) -> Self { - return Self { data: data, len: len }; - } - - fn get(self, i: usize) -> T { - return self.data[i]; - } - - fn set(self, i: usize, val: T) { - self.data[i] = val; - } - - fn is_empty(self) -> bool { - return self.len == 0; - } -} +// Note: Slice<T> is defined in std/slice.zc with iteration support fn mem_zero<T>(ptr: T*, count: usize) { memset(ptr, 0, sizeof(T) * count); @@ -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: 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 +// 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,54 +51,68 @@ 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_accept(fd: int) -> int; -extern fn _z_net_write(fd: int, buf: 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 { - fd: int; + handle: c_int; } +extern fn strerror(errnum: c_int) -> char*; + impl TcpStream { fn read(self, buf: char*, len: usize) -> Result<usize> { - let n = read(self.fd, (void*)buf, len); - if (n < 0) return Result<usize>::Err("Read failed"); + let n = read(self.handle - 1, (void*)buf, len); + 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.fd, 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.fd >= 0) { - close(self.fd); - self.fd = -1; + 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 { fd: fd }); + let one: c_int = 1; + return Result<TcpStream>::Ok(TcpStream { handle: fd + one }); } } @@ -100,32 +123,42 @@ impl Drop for TcpStream { } struct TcpListener { - fd: 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 { fd: fd }); + 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.fd); - if (client_fd < 0) return Result<TcpStream>::Err("Accept failed"); - return Result<TcpStream>::Ok(TcpStream { fd: client_fd }); + 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.fd >= 0) { - close(self.fd); - self.fd = -1; + 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 d0b09a9..9f432c0 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*) -> c_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_pclose(stream: void*) -> int; -extern fn _z_fgets(s: char*, size: int, stream: void*) -> char*; -extern fn _z_system(command: char*) -> int; +extern fn _z_popen(command: const char*, type: const char*) -> void*; +extern fn _z_pclose(stream: void*) -> c_int; +extern fn _z_fgets(s: char*, size: c_int, stream: void*) -> char*; struct Output { stdout: String; @@ -85,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); @@ -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/regex.zc b/std/regex.zc new file mode 100644 index 0000000..f64b36e --- /dev/null +++ b/std/regex.zc @@ -0,0 +1,198 @@ +include <regex.h> + +import "./core.zc" +import "./string.zc" +import "./vec.zc" +import "./option.zc" + +struct Match { + text: char*; + start: int; + len: int; +} + +impl Match { + fn new(text: char*, start: int, len: int) -> Match { + return Match { text: text, start: start, len: len }; + } + + fn as_string(self) -> char* { + return self.text; + } + + fn end(self) -> int { + return self.start + self.len; + } +} + +struct Regex { + preg: void*; + pattern: char*; + flags: int; +} + +impl Regex { + fn compile(pattern: char*) -> Regex { + return Regex::compile_with_flags(pattern, 1 | 2); + } + + fn compile_with_flags(pattern: char*, flags: int) -> Regex { + let preg = malloc(1024); + let status = regcomp(preg, pattern, flags); + if (status != 0) { + free(preg); + return Regex { preg: 0, pattern: 0, flags: flags }; + } + return Regex { preg: preg, pattern: pattern, flags: flags }; + } + + fn is_valid(self) -> bool { + return self.preg != 0; + } + + fn match(self, text: char*) -> bool { + if (self.preg == 0) { return false; } + return regexec(self.preg, text, 0, 0, 0) == 0; + } + + fn match_full(self, text: char*) -> bool { + return self.match(text); + } + + fn match_at(self, text: char*, offset: int) -> bool { + if (self.preg == 0) { return false; } + let len = strlen(text); + if (offset < 0 || offset > len) { return false; } + return regexec(self.preg, text + offset, 0, 0, 0) == 0; + } + + fn is_match(self, text: char*) -> bool { + return self.match(text); + } + + fn find(self, text: char*) -> Option<Match> { + if (self.preg == 0) { return Option<Match>::None(); } + let t_len = strlen(text); + for (let i = 0; i <= t_len; i = i + 1) { + let sub = text + i; + if (regexec(self.preg, sub, 0, 0, 0) == 0) { + let j = 0; + while (text[i + j] != 0 && regexec(self.preg, sub, 0, 0, 0) == 0) { + j = j + 1; + sub = text + i + j; + } + return Option<Match>::Some(Match::new(text + i, i, j)); + } + } + return Option<Match>::None(); + } + + fn find_at(self, text: char*, start: int) -> Option<Match> { + let len = strlen(text); + if (start < 0 || start >= len) { + return Option<Match>::None(); + } + return self.find(text + start); + } + + fn count(self, text: char*) -> int { + if (self.preg == 0) { return 0; } + let count = 0; + let pos = 0; + let t_len = strlen(text); + while (pos < t_len) { + let sub = text + pos; + if (regexec(self.preg, sub, 0, 0, 0) == 0) { + count = count + 1; + pos = pos + 1; + } else { + break; + } + } + return count; + } + + fn split(self, text: char*) -> Vec<String> { + let parts = Vec<String>::new(); + if (self.preg == 0) { + parts.push(String::from(text)); + return parts; + } + let t_len = strlen(text); + let last_pos = 0; + let pos = 0; + while (pos < t_len) { + let sub = text + pos; + if (regexec(self.preg, sub, 0, 0, 0) == 0) { + if (pos > last_pos) { + let before = text + last_pos; + let part_len = pos - last_pos; + let v = Vec<char>::new(); + for (let i = 0; i < part_len; i = i + 1) { + v.push(before[i]); + } + v.push(0); + parts.push(String { vec: v }); + } + last_pos = pos + 1; + pos = pos + 1; + } else { + pos = pos + 1; + } + } + if (last_pos < t_len) { + parts.push(String::from(text + last_pos)); + } + return parts; + } + + fn pattern(self) -> char* { + return self.pattern; + } + + fn flags(self) -> int { + return self.flags; + } + + fn is_valid_pattern(pattern: char*) -> bool { + let test_regex = Regex::compile(pattern); + let valid = test_regex.is_valid(); + test_regex.destroy(); + return valid; + } + + fn destroy(self) { + if (self.preg != 0) { + regfree(self.preg); + free(self.preg); + } + } +} + +fn regex_match(pattern: char*, text: char*) -> bool { + let re = Regex::compile(pattern); + let result = re.match(text); + re.destroy(); + return result; +} + +fn regex_find(pattern: char*, text: char*) -> Option<Match> { + let re = Regex::compile(pattern); + let result = re.find(text); + re.destroy(); + return result; +} + +fn regex_count(pattern: char*, text: char*) -> int { + let re = Regex::compile(pattern); + let count = re.count(text); + re.destroy(); + return count; +} + +fn regex_split(pattern: char*, text: char*) -> Vec<String> { + let re = Regex::compile(pattern); + let parts = re.split(text); + re.destroy(); + return parts; +} @@ -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..c757fbd 100644 --- a/std/slice.zc +++ b/std/slice.zc @@ -1,10 +1,50 @@ +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(ptr: T*, len: usize) -> Slice<T> { + return Slice<T> { data: ptr, len: len }; + } + + // Alias for backwards compatibility with std/mem.zc + fn new(data: T*, len: usize) -> Slice<T> { + return Slice<T> { data: data, 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..54f11b2 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); @@ -68,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 { @@ -124,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 { @@ -132,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 98f080e..0722b60 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*); @@ -20,9 +25,13 @@ raw { z_closure_T *closure = (z_closure_T*)c; void (*f)(void*) = (void(*)(void*))closure->func; f(closure->ctx); - free(c); + free(c); return NULL; } + + static int _z_thread_equal(void* handle1, void* handle2) { + return pthread_equal((pthread_t)handle1, (pthread_t)handle2); + } static int _z_thread_spawn(void *ctx_copy, size_t *out_handle) { pthread_t pt; @@ -30,11 +39,19 @@ 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 int _z_thread_detach(void* handle) { + return pthread_detach((pthread_t)handle); + } + + static int _z_thread_cancel(void* handle) { + return pthread_cancel((pthread_t)handle); } static void _z_mutex_init(void *ptr) { @@ -58,42 +75,70 @@ raw { } } -extern fn _z_thread_spawn(ctx: void*, out: usize*) -> int; -extern fn _z_thread_join(handle: void*) -> int; +extern fn _z_thread_equal(handle1: void*, handle2: void*) -> c_int; +extern fn _z_thread_spawn(ctx: void*, out: usize*) -> c_int; +extern fn _z_thread_join(handle: void*) -> c_int; +extern fn _z_thread_detach(handle: void*) -> c_int; +extern fn _z_thread_cancel(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); struct Thread { - handle: void*; + handle: void*; } impl Thread { + fn eq(self, other: const Thread) -> bool { + return _z_thread_equal(self.handle, other.handle); + } + fn neq(self, other: const Thread) -> bool { + return !(self == other); + } + fn spawn(func: fn()) -> Result<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); + let ret = _z_thread_spawn(ctx, &out_handle); - if (ret != 0) { - free(ctx_copy); + if ret != 0 { + 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 err = _z_thread_join(self.handle); + if err { + return Result<bool>::Err("Join failed"); + } + return Result<bool>::Ok(true); + } + + fn detach(self) -> Result<bool> { + let err = _z_thread_detach(self.handle); + if err { + return Result<bool>::Err("Detach failed"); + } + return Result<bool>::Ok(true); + } + + fn cancel(self) -> Result<bool> { + let err = _z_thread_cancel(self.handle); + if err { + return Result<bool>::Err("Cancel failed"); + } return Result<bool>::Ok(true); } } @@ -118,7 +163,7 @@ impl Mutex { } fn free(self) { - if (self.handle) { + if self.handle { _z_mutex_destroy(self.handle); free(self.handle); self.handle = NULL; @@ -133,5 +178,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/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/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_asm_clobber.zc b/tests/features/test_asm_clobber.zc new file mode 100644 index 0000000..2ba74da --- /dev/null +++ b/tests/features/test_asm_clobber.zc @@ -0,0 +1,20 @@ +fn add(a: int, b: int) -> int { + let result: int; + asm { + "mov {a}, {result}" + "add {b}, {result}" + : out(result) + : in(a), in(b) + : clobber("cc") + } + return result; +} + +test "asm_clobber" { + let res = add(10, 20); + if (res != 30) { + println "Failed: Expected 30, got {res}"; + exit(1); + } + println "Success: asm with clobber works properly"; +} 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; +} diff --git a/tests/memory/test_memory_safety.zc b/tests/memory/test_memory_safety.zc index a5cc960..b672cc9 100644 --- a/tests/memory/test_memory_safety.zc +++ b/tests/memory/test_memory_safety.zc @@ -1,5 +1,6 @@ import "std/mem.zc" +import "std/slice.zc" // ** Globals ** let DROP_COUNT = 0; @@ -127,11 +128,13 @@ test "test_slice" { let data: int[5] = [1, 2, 3, 4, 5]; let s = Slice<int>::new(&data[0], 5); f" Slice len: {(int)s.len}"; - let v2 = s.get(2); + let opt_v2 = s.get(2); + let v2 = opt_v2.unwrap(); f" Slice[2]: {v2}"; assert(v2 == 3, "Slice get failed"); - s.set(0, 99); - let v0 = s.get(0); + s.data[0] = 99; + let opt_v0 = s.get(0); + let v0 = opt_v0.unwrap(); f" After set: Slice[0] = {v0}"; assert(v0 == 99, "Slice set failed"); " ✓ Slice works!"; 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_regex.zc b/tests/std/test_regex.zc new file mode 100644 index 0000000..4fe176c --- /dev/null +++ b/tests/std/test_regex.zc @@ -0,0 +1,187 @@ +import "std/regex.zc" + +fn test_basic_matching() { + "testing: basic matching"; + let re = Regex::compile("abc"); + + if (re.match("abc")) { "literal match works"; } else { "FAILED: literal match"; } + if (re.match("abcdef")) { "substring match works"; } else { "FAILED: substring match"; } + if (!re.match("xyz")) { "not matching correctly returns false"; } else { "FAILED: mismatching"; } + + re.destroy(); + ""; +} + +fn test_anchors() { + "testing: anchors"; + let re = Regex::compile("^start"); + + if (re.match("start here")) { " ^ anchor works for start"; } else { "FAILED: ^ anchor start"; } + if (!re.match("no start")) { " ^ anchor rejects non-start"; } else { "FAILED: ^ anchor reject"; } + + re.destroy(); + + let re2 = Regex::compile("end$"); + if (re2.match("the end")) { " $ anchor works for end"; } else { "FAILED: $ anchor end"; } + if (!re2.match("end here")) { " $ anchor rejects non-end"; } else { "FAILED: $ anchor reject"; } + + re2.destroy(); + ""; +} + +fn test_wildcards() { + "testing: wild cards"; + let re = Regex::compile("a.c"); + + if (re.match("abc")) { " . matches single char"; } else { "FAILED: . match 1"; } + if (re.match("axc")) { " . matches different char"; } else { "FAILED: . match 2"; } + if (!re.match("ac")) { " . requires exactly one char"; } else { "FAILED: . match 3"; } + + re.destroy(); + ""; +} + +fn test_quantifiers() { + "testing: quantifiers"; + let re1 = Regex::compile("a*b"); + if (re1.match("b")) { " * matches zero occurrences"; } else { "FAILED: * 0"; } + if (re1.match("ab")) { " * matches one occurrence"; } else { "FAILED: * 1"; } + if (re1.match("aaab")) { " * matches multiple occurrences"; } else { "FAILED: * many"; } + re1.destroy(); + + let re2 = Regex::compile("a+b"); + if (!re2.match("b")) { " + requires at least one"; } else { "FAILED: + 0"; } + if (re2.match("ab")) { " + matches one occurrence"; } else { "FAILED: + 1"; } + if (re2.match("aaab")) { " + matches multiple occurrences"; } else { "FAILED: + many"; } + re2.destroy(); + + let re3 = Regex::compile("colou?r"); + if (re3.match("color")) { " ? matches with char"; } else { "FAILED: ? with"; } + if (re3.match("colour")) { " ? matches without char"; } else { "FAILED: ? without"; } + re3.destroy(); + ""; +} + +fn test_character_classes() { + "testing: character class stuff" + let re = Regex::compile("[0-9]+"); + + if (re.match("123")) { " [0-9] matches digits"; } else { "FAILED: [0-9] match"; } + if (re.match("abc123")) { " [0-9] finds digits in string"; } else { "FAILED: [0-9] find"; } + if (!re.match("abc")) { " [0-9] rejects non-digits"; } else { "FAILED: [0-9] reject"; } + + re.destroy(); + ""; +} + +fn test_alternation() { + "test: alternation"; + let re = Regex::compile("cat|dog"); + + if (re.match("cat")) { " | matches first alternative"; } else { "FAILED: | match 1"; } + if (re.match("dog")) { " | matches second alternative"; } else { "FAILED: | match 2"; } + if (!re.match("bird")) { " | rejects non-matching"; } else { "FAILED: | reject"; } + + re.destroy(); + ""; +} + +fn test_word_boundaries() { + "testing: word matching"; + let re = Regex::compile("[a-zA-Z]+"); + + if (re.match("hello")) { " letter class matches words"; } else { "FAILED: letter match"; } + if (re.match("hello123")) { " letter class finds word part"; } else { "FAILED: letter part"; } + if (!re.match("123")) { " letter class rejects non-letters"; } else { "FAILED: letter reject"; } + + re.destroy(); + ""; +} + +fn test_is_valid() { + "testing: patern validation" + + if (Regex::is_valid_pattern("^[a-z]+$")) { " valid pattern accepted"; } else { "FAILED: pattern validation 1"; } + if (Regex::is_valid_pattern("(hello|world)")) { " complex pattern accepted"; } else { "FAILED: pattern validation 2"; } + + ""; +} + +fn test_find() { + "testing: find functionality"; + let re = Regex::compile("[0-9]+"); + let m = re.find("abc123def456"); + + if (m.is_some()) { " find locates match"; } else { "FAILED: find match"; } + + re.destroy(); + ""; +} + +fn test_count() { + "testing: count"; + let re = Regex::compile("[0-9]+"); + let count = re.count("123 456 789"); + + if (count >= 1) { " count finds matches"; } else { "FAILED: count matches"; } + + re.destroy(); + ""; +} + +fn test_convenience_functions() { + "testing: just some other functions and stuff"; + + if (regex_match("^test", "testing")) { " regex_match works"; } else { "FAILED: regex_match"; } + if (regex_count("a", "banana") >= 1) { " regex_count works"; } else { "FAILED: regex_count"; } + + let m = regex_find("[0-9]+", "id: 42"); + if (m.is_some()) { " regex_find works"; } else { "FAILED: regex_find"; } + + ""; +} + +fn test_email_pattern() { + "test: email pattern stuff" + let email_re = Regex::compile("^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z][a-zA-Z]+$"); + + if (email_re.match("swag@swag.com")) { " valid email accepted"; } else { "FAILED: valid email"; } + if (email_re.match("swag.swag@swag.swag.swag")) { " complex email accepted"; } else { "FAILED: complex email"; } + if (!email_re.match("invalid.email")) { " invalid email rejected"; } else { "FAILED: invalid email reject"; } + + email_re.destroy(); + ""; +} + +fn test_url_pattern() { + "testing: url pattern stuff" + let url_re = Regex::compile("https?://[a-zA-Z0-9.-]+"); + + if (url_re.match("http://example.com")) { " http url matched matched"; } else { "FAILED: http url"; } + if (url_re.match("https://secure.example.com")) { " https url matched"; } else { "FAILED: https url"; } + if (!url_re.match("ftp://something.com")) { " ftp url rejected"; } else { "FAILED: ftp url reject"; } + + url_re.destroy(); + ""; +} + +fn main() { + "testing...."; + + test_basic_matching(); + test_anchors(); + test_wildcards(); + test_quantifiers(); + test_character_classes(); + test_alternation(); + test_word_boundaries(); + test_is_valid(); + test_find(); + test_count(); + test_convenience_functions(); + test_email_pattern(); + test_url_pattern(); + + "all tests worked... (hopefully.. look around for \"FAILED\" messages)"; + ""; +} 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"); +} diff --git a/tests/std/test_thread.zc b/tests/std/test_thread.zc new file mode 100644 index 0000000..42fa82e --- /dev/null +++ b/tests/std/test_thread.zc @@ -0,0 +1,42 @@ +import "std/thread.zc" + +test "Thread Spawn and Join" { + "Testing thread spawn and join"; + + let spawn_result = Thread::spawn(fn(){ + "Running on a separate thread"; + }); + assert(spawn_result.is_ok(), "Thread spawn has failed"); + + let thr = spawn_result.unwrap(); + let join_result = thr.join(); + assert(join_result.is_ok(), "Thread join has failed"); +} + +test "Thread Spawn and Detach" { + "Testing thread spawn and detach"; + + let spawn_result = Thread::spawn(fn(){ + "Detached thread, this line might print later."; + }); + assert(spawn_result.is_ok(), "Thread spawn has failed"); + + let thr = spawn_result.unwrap(); + let detach_result = thr.detach(); + assert(detach_result.is_ok(), "Thread detach has failed"); +} + +test "Thread Spawn and Cancel" { + "Testing thread spawn and cancel"; + + let spawn_result = Thread::spawn(fn(){ + "Thread running indefinitely..."; + while true { + } + }); + assert(spawn_result.is_ok(), "Thread spawn has failed"); + + let thr = spawn_result.unwrap(); + let cancel_result = thr.cancel(); + assert(cancel_result.is_ok(), "Thread cancel has failed"); +} |
