From da9e8758e9d89dc7362be67f8e7573309efe170c Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Thu, 29 Jan 2026 03:03:56 +0000 Subject: Centralize TCC compat --- src/codegen/codegen_decl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/codegen/codegen_decl.c') diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c index 5fb9f54..11cdece 100644 --- a/src/codegen/codegen_decl.c +++ b/src/codegen/codegen_decl.c @@ -3,6 +3,7 @@ #include "../parser/parser.h" #include "../zprep.h" #include "codegen.h" +#include "compat.h" #include #include #include @@ -12,7 +13,7 @@ static void emit_freestanding_preamble(FILE *out) fputs("#include \n#include \n#include " "\n#include \n", out); - fputs("#ifdef __TINYC__\n#define __auto_type __typeof__\n#endif\n", out); + fputs(ZC_TCC_COMPAT_STR, out); fputs("typedef size_t usize;\ntypedef char* string;\n", out); fputs("#define U0 void\n#define I8 int8_t\n#define U8 uint8_t\n#define I16 " "int16_t\n#define U16 uint16_t\n", @@ -86,7 +87,7 @@ void emit_preamble(ParserContext *ctx, FILE *out) // C mode fputs("#define ZC_AUTO __auto_type\n", out); fputs("#define ZC_CAST(T, x) ((T)(x))\n", out); - fputs("#ifdef __TINYC__\n#define __auto_type __typeof__\n#endif\n", out); + fputs(ZC_TCC_COMPAT_STR, out); fputs("static inline const char* _z_bool_str(_Bool b) { return b ? \"true\" : " "\"false\"; }\n", out); -- cgit v1.2.3 From fc6ff10acb9d00ea1c8c5924869e0efbd38093c5 Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Thu, 29 Jan 2026 13:17:30 +0000 Subject: Objective-C interop + a few improvements --- README.md | 64 ++++++++- docs/lex.md | 130 +++++++++++++++++++ docs/std/README.md | 6 + docs/std/json.md | 57 ++++++++ docs/std/net.md | 44 +++++++ docs/std/set.md | 38 ++++++ docs/std/stack.md | 38 ++++++ docs/std/thread.md | 47 +++++++ docs/std/time.md | 38 ++++++ examples/objc_interop.zc | 42 ++++++ src/codegen/codegen_decl.c | 19 +-- src/codegen/compat.h | 47 +++++++ src/main.c | 8 ++ src/utils/utils.c | 223 +++++++++++++++++++++----------- src/zprep.h | 1 + std/fs.zc | 46 +++---- std/json.zc | 7 + std/map.zc | 7 + std/net.zc | 23 +++- std/thread.zc | 9 +- std/time.zc | 26 ++-- tests/features/test_build_directives.zc | 21 +-- 22 files changed, 793 insertions(+), 148 deletions(-) create mode 100644 docs/lex.md create mode 100644 docs/std/json.md create mode 100644 docs/std/net.md create mode 100644 docs/std/set.md create mode 100644 docs/std/stack.md create mode 100644 docs/std/thread.md create mode 100644 docs/std/time.md create mode 100644 examples/objc_interop.zc (limited to 'src/codegen/codegen_decl.c') diff --git a/README.md b/README.md index a35e0de..e295eb5 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,7 @@ Join the discussion, share demos, ask questions, or report bugs in the official - [Building with Zig](#building-with-zig) - [C++ Interop](#c-interop) - [CUDA Interop](#cuda-interop) + - [Objective-C Interop](#objective-c-interop) - [Contributing](#contributing) - [Attributions](#attributions) @@ -929,7 +930,7 @@ Decorate functions and structs to modify compiler behavior. | `@derive(...)` | Struct | Auto-implement traits. Supports `Debug`, `Eq` (Smart Derive), `Copy`, `Clone`. | | `@` | Any | Passes generic attributes to C (e.g. `@flatten`, `@alias("name")`). | -### Custom Attributes +#### Custom Attributes Zen C supports a powerful **Custom Attribute** system that allows you to use any GCC/Clang `__attribute__` directly in your code. Any attribute that is not explicitly recognized by the Zen C compiler is treated as a generic attribute and passed through to the generated C code. @@ -941,7 +942,7 @@ Zen C attributes are mapped directly to C attributes: - `@name(args)` → `__attribute__((name(args)))` - `@name("string")` → `__attribute__((name("string")))` -### Smart Derives +#### Smart Derives Zen C provides "Smart Derives" that respect Move Semantics: @@ -1007,12 +1008,33 @@ Zen C supports special comments at the top of your source file to configure the | `//> link:` | `-lfoo` or `path/to/lib.a` | Link against a library or object file. | | `//> lib:` | `path/to/libs` | Add a library search path (`-L`). | | `//> include:` | `path/to/headers` | Add an include search path (`-I`). | +| `//> framework:` | `Cocoa` | Link against a macOS framework. | | `//> cflags:` | `-Wall -O3` | Pass arbitrary flags to the C compiler. | | `//> define:` | `MACRO` or `KEY=VAL` | Define a preprocessor macro (`-D`). | | `//> pkg-config:` | `gtk+-3.0` | Run `pkg-config` and append `--cflags` and `--libs`. | | `//> shell:` | `command` | Execute a shell command during the build. | | `//> get:` | `http://url/file` | Download a file if specific file does not exist. | +#### Features + +**1. OS Guarding** +Prefix directives with an OS name to apply them only on specific platforms. +Supported prefixes: `linux:`, `windows:`, `macos:` (or `darwin:`). + +```zc +//> linux: link: -lm +//> windows: link: -lws2_32 +//> macos: framework: Cocoa +``` + +**2. Environment Variable Expansion** +Use `${VAR}` syntax to expand environment variables in your directives. + +```zc +//> include: ${HOME}/mylib/include +//> lib: ${ZC_ROOT}/std +``` + #### Examples ```zc @@ -1072,6 +1094,12 @@ Zen C includes a standard library (`std`) covering essential functionality. | **`std/result.zc`** | Error handling (`Ok`/`Err`). | [Docs](docs/std/result.md) | | **`std/path.zc`** | Cross-platform path manipulation. | [Docs](docs/std/path.md) | | **`std/env.zc`** | Process environment variables. | [Docs](docs/std/env.md) | +| **`std/net.zc`** | TCP networking (Sockets). | [Docs](docs/std/net.md) | +| **`std/thread.zc`** | Threads and Synchronization. | [Docs](docs/std/thread.md) | +| **`std/time.zc`** | Time measurement and sleep. | [Docs](docs/std/time.md) | +| **`std/json.zc`** | JSON parsing and serialization. | [Docs](docs/std/json.md) | +| **`std/stack.zc`** | LIFO Stack `Stack`. | [Docs](docs/std/stack.md) | +| **`std/set.zc`** | Generic Hash Set `Set`. | [Docs](docs/std/set.md) | --- @@ -1302,6 +1330,38 @@ let tid = local_id(); > **Note:** The `--cuda` flag sets `nvcc` as the compiler and implies `--cpp` mode. Requires the NVIDIA CUDA Toolkit. +### Objective-C Interop + +Zen C can compile to Objective-C (`.m`) using the `--objc` flag, allowing you to use Objective-C frameworks (like Cocoa/Foundation) and syntax. + +```bash +# Compile with clang (or gcc/gnustep) +zc app.zc --objc --cc clang +``` + +#### Using Objective-C in Zen C + +Use `include` for headers and `raw` blocks for Objective-C syntax (`@interface`, `[...]`, `@""`). + +```zc +//> macos: framework: Foundation +//> linux: cflags: -fconstant-string-class=NSConstantString -D_NATIVE_OBJC_EXCEPTIONS +//> linux: link: -lgnustep-base -lobjc + +include + +fn main() { + raw { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSLog(@"Hello from Objective-C!"); + [pool drain]; + } + println "Zen C works too!"; +} +``` + +> **Note:** Zen C string interpolation works with Objective-C objects (`id`) by calling `debugDescription` or `description`. + --- ## Contributing diff --git a/docs/lex.md b/docs/lex.md new file mode 100644 index 0000000..1cd70fd --- /dev/null +++ b/docs/lex.md @@ -0,0 +1,130 @@ +# Lexical Structure + +## Source Text + +Zen-C source code is encoded in UTF-8. + +## Grammar Notation + +The lexical grammar is defined using a notation similar to EBNF. +- `Rule ::= Production`: Defines a rule. +- `[ ... ]`: Character class. +- `*`: Zero or more repetitions. +- `+`: One or more repetitions. +- `?`: Zero or one occurrence. +- `|`: Alternation. +- `"..."` or `'...'`: Literal string/character. +- `~`: Negation (e.g., `~[\n]` means any character except newline). + +## Whitespace and Comments + +Whitespace separates tokens but is otherwise ignored. Comments are treated as whitespace. + +```text +Whitespace ::= [ \t\n\r]+ +Comment ::= LineComment | BlockComment + +LineComment ::= "//" ~[\n]* +BlockComment ::= "/*" (BlockComment | ~("*/"))* "*/" +``` + +## Identifiers + +Identifiers name entities such as variables, functions, and types. + +```text +Identifier ::= IdentifierStart IdentifierPart* +IdentifierStart ::= [a-zA-Z_] +IdentifierPart ::= [a-zA-Z0-9_] +``` + +## Literals + +### Integer Literals + +Integers can be decimal, hexadecimal, or binary. + +```text +IntegerLiteral ::= ( DecimalInt | HexInt | BinaryInt ) IntegerSuffix? + +DecimalInt ::= [0-9]+ +HexInt ::= "0x" [0-9a-fA-F]+ +BinaryInt ::= "0b" [01]+ + +IntegerSuffix ::= "u" | "L" | "u64" | ... +``` +*Note: The lexer technically consumes any alphanumeric sequence following a number as a suffix.* + +### Floating Point Literals + +```text +FloatLiteral ::= [0-9]+ "." [0-9]* FloatSuffix? + | [0-9]+ FloatSuffix + +FloatSuffix ::= "f" +``` + +### String Literals + +```text +StringLiteral ::= '"' StringChar* '"' +StringChar ::= ~["\\] | EscapeSequence +EscapeSequence ::= "\\" ( ["\\/bfnrt] | "u" HexDigit{4} ) +``` + +### F-Strings + +```text +FStringLiteral ::= 'f"' StringChar* '"' +``` + + +### Character Literals + +```text +CharLiteral ::= "'" ( ~['\\] | EscapeSequence ) "'" +``` + +## Keywords + +```text +Keyword ::= Declaration | Control | Special | BoolLiteral | NullLiteral | LogicOp + +Declaration ::= "let" | "def" | "fn" | "struct" | "enum" | "union" | "alias" + | "trait" | "impl" | "use" | "module" | "import" | "opaque" + +Control ::= "if" | "else" | "match" | "for" | "while" | "loop" + | "return" | "break" | "continue" | "guard" | "unless" + | "defer" | "async" | "await" | "try" | "catch" | "goto" + +Special ::= "asm" | "assert" | "test" | "sizeof" | "embed" | "comptime" + | "autofree" | "volatile" | "launch" | "ref" | "static" | "const" + +BoolLiteral ::= "true" | "false" +NullLiteral ::= "null" + +CReserved ::= "auto" | "case" | "char" | "default" | "do" | "double" + | "extern" | "float" | "inline" | "int" | "long" | "register" + | "restrict" | "short" | "signed" | "switch" | "typedef" + | "unsigned" | "void" | "_Atomic" | "_Bool" | "_Complex" + | "_Generic" | "_Imaginary" | "_lmaginary" | "_Noreturn" + | "_Static_assert" | "_Thread_local" + +LogicOp ::= "and" | "or" +``` + +## Operators and Punctuation + +```text +Operator ::= "+" | "-" | "*" | "/" | "%" + | "&&" | "||" | "!" | "++" | "--" + | "&" | "|" | "^" | "~" | "<<" | ">>" + | "==" | "!=" | "<" | ">" | "<=" | ">=" + | "=" | "+=" | "-=" | "*=" | "/=" | "%=" + | "&=" | "|=" | "^=" | "<<=" | ">>=" + | ".." | "..=" | "..<" | "..." + | "." | "?." | "??" | "??=" | "->" | "=>" + | "::" | "|>" | "?" + | "(" | ")" | "{" | "}" | "[" | "]" + | "," | ":" | ";" | "@" +``` diff --git a/docs/std/README.md b/docs/std/README.md index 6125a4e..16ffc74 100644 --- a/docs/std/README.md +++ b/docs/std/README.md @@ -3,10 +3,16 @@ - [Env (Environment)](./env.md) - Process environment variables. - [File System (FS)](./fs.md) - File I/O and directory operations. - [IO](./io.md) - Standard Input/Output. +- [JSON](./json.md) - JSON parsing and serialization. - [Map](./map.md) - Hash map implementation. +- [Networking (Net)](./net.md) - TCP networking. - [Option](./option.md) - Optional values (Some/None). - [Path](./path.md) - File path manipulation. - [Result](./result.md) - Error handling (Ok/Err). - [Queue](./queue.md) - FIFO queue (Ring Buffer). +- [Set](./set.md) - Hash set implementation. +- [Stack](./stack.md) - LIFO stack. - [String](./string.md) - Growable, heap-allocated string type. +- [Thread (Concurrency)](./thread.md) - Multithreading and synchronization. +- [Time](./time.md) - Time measurement and sleep. - [Vector (Vec)](./vec.md) - A growable dynamic array. diff --git a/docs/std/json.md b/docs/std/json.md new file mode 100644 index 0000000..fba2ad8 --- /dev/null +++ b/docs/std/json.md @@ -0,0 +1,57 @@ +# JSON (`std/json.zc`) + +The `std/json` module provides a DOM-style JSON parser and builder. + +## Usage + +```zc +import "std/json.zc" +``` + +## Types + +### Struct `JsonValue` + +Represents a node in a JSON document. + +#### Creation Methods + +- **`fn null() -> JsonValue`**, **`fn null_ptr() -> JsonValue*`** +- **`fn bool(b: bool) -> JsonValue`**, **`fn bool_ptr(b: bool) -> JsonValue*`** +- **`fn number(n: double) -> JsonValue`**, **`fn number_ptr(n: double) -> JsonValue*`** +- **`fn string(s: char*) -> JsonValue`**, **`fn string_ptr(s: char*) -> JsonValue*`** +- **`fn array() -> JsonValue`**, **`fn array_ptr() -> JsonValue*`** +- **`fn object() -> JsonValue`**, **`fn object_ptr() -> JsonValue*`** + +#### Parsing + +- **`fn parse(json: char*) -> Result`** + Parses a JSON string into a heap-allocated `JsonValue` tree. + +#### Accessors + +- **`fn is_null(self) -> bool`**, **`is_bool`**, **`is_number`**, **`is_string`**, **`is_array`**, **`is_object`** + Check the type of the value. + +- **`fn as_string(self) -> Option`** + Returns `Some(string)` if the value is a string, `None` otherwise. +- **`fn as_int(self) -> Option`** +- **`fn as_float(self) -> Option`** +- **`fn as_bool(self) -> Option`** + +#### Object/Array Operations + +- **`fn push(self, val: JsonValue)`** + Appends a value to an array. +- **`fn set(self, key: char*, val: JsonValue)`** + Sets a key-value pair in an object. + +- **`fn get(self, key: char*) -> Option`** + Retrieves a value from an object by key. +- **`fn at(self, index: usize) -> Option`** + Retrieves a value from an array by index. + +#### Memory Management + +- **`fn free(self)`** + Recursively frees the JSON value and all its children. diff --git a/docs/std/net.md b/docs/std/net.md new file mode 100644 index 0000000..392c901 --- /dev/null +++ b/docs/std/net.md @@ -0,0 +1,44 @@ +# Networking (`std/net.zc`) + +The `std/net` module provides basic TCP networking capabilities. + +## Usage + +```zc +import "std/net.zc" +``` + +## Types + +### Type `TcpListener` + +Represents a TCP socket listening for incoming connections. + +#### Methods + +- **`fn bind(host: char*, port: int) -> Result`** + Creates a new listener bound to the specified host and port. + +- **`fn accept(self) -> Result`** + Blocks waiting for a new connection. Returns a `TcpStream` for the connected client. + +- **`fn close(self)`** + Closes the listening socket. + +### Type `TcpStream` + +Represents a TCP connection stream. + +#### Methods + +- **`fn connect(host: char*, port: int) -> Result`** + Connects to a remote host. + +- **`fn read(self, buf: char*, len: usize) -> Result`** + Reads up to `len` bytes into `buf`. Returns the number of bytes read. + +- **`fn write(self, buf: char*, len: usize) -> Result`** + Writes `len` bytes from `buf` to the stream. Returns the number of bytes written. + +- **`fn close(self)`** + Closes the connection. diff --git a/docs/std/set.md b/docs/std/set.md new file mode 100644 index 0000000..0d62a66 --- /dev/null +++ b/docs/std/set.md @@ -0,0 +1,38 @@ +# Set (`std/set.zc`) + +The `std/set` module provides a Generic Hash Set `Set`. + +## Usage + +```zc +import "std/set.zc" +``` + +## Types + +### Struct `Set` + +A set of unique elements. + +#### Methods + +- **`fn new() -> Set`** + Creates a new empty set. + +- **`fn add(self, val: T) -> bool`** + Adds a value to the set. Returns `true` if the value was added, `false` if it was already present. + +- **`fn contains(self, val: T) -> bool`** + Returns `true` if the set contains the value. + +- **`fn remove(self, val: T) -> bool`** + Removes a value from the set. Returns `true` if present and removed. + +- **`fn length(self) -> usize`** + Returns the number of elements in the set. + +- **`fn is_empty(self) -> bool`** + Returns `true` if the set is empty. + +- **`fn clear(self)`** + Removes all elements from the set. diff --git a/docs/std/stack.md b/docs/std/stack.md new file mode 100644 index 0000000..6e5da84 --- /dev/null +++ b/docs/std/stack.md @@ -0,0 +1,38 @@ +# Stack (`std/stack.zc`) + +The `std/stack` module provides a LIFO (Last-In, First-Out) stack data structure. + +## Usage + +```zc +import "std/stack.zc" +``` + +## Types + +### Struct `Stack` + +A generic stack. + +#### Methods + +- **`fn new() -> Stack`** + Creates a new empty stack. + +- **`fn push(self, value: T)`** + Pushes a value onto the top of the stack. + +- **`fn pop(self) -> Option`** + Removes and returns the top element of the stack. Returns `None` if empty. + +- **`fn length(self) -> usize`** + Returns the number of elements in the stack. + +- **`fn is_empty(self) -> bool`** + Returns `true` if the stack contains no elements. + +- **`fn clear(self)`** + Removes all elements from the stack. + +- **`fn clone(self) -> Stack`** + Creates a deep copy of the stack. diff --git a/docs/std/thread.md b/docs/std/thread.md new file mode 100644 index 0000000..6ac7e29 --- /dev/null +++ b/docs/std/thread.md @@ -0,0 +1,47 @@ +# Concurrency (`std/thread.zc`) + +The `std/thread` module provides primitives for multithreading and synchronization. + +## Usage + +```zc +import "std/thread.zc" +``` + +## Functions + +- **`fn sleep_ms(ms: int)`** + Sleeps the current thread for the specified number of milliseconds. + +## Types + +### Type `Thread` + +Represents a handle to a spawned thread. + +#### Methods + +- **`fn spawn(func: fn()) -> Result`** + Spawns a new thread executing the provided function. + > Note: Currently supports void functions with no arguments. + +- **`fn join(self) -> Result`** + Blocks the current thread until the spawned thread finishes. + +### Type `Mutex` + +A mutual exclusion primitive for protecting shared data. + +#### Methods + +- **`fn new() -> Mutex`** + Creates a new mutex. + +- **`fn lock(self)`** + Acquires the lock. Blocks if the lock is already held. + +- **`fn unlock(self)`** + Releases the lock. + +- **`fn free(self)`** + Destroys the mutex and frees associated resources. diff --git a/docs/std/time.md b/docs/std/time.md new file mode 100644 index 0000000..97dd208 --- /dev/null +++ b/docs/std/time.md @@ -0,0 +1,38 @@ +# Time (`std/time.zc`) + +The `std/time` module provides functionality for measuring time and sleeping. + +## Usage + +```zc +import "std/time.zc" +``` + +## Structs + +### Struct `Duration` + +Represents a span of time in milliseconds. + +#### Methods + +- **`fn from_ms(ms: U64) -> Duration`** + Creates a duration from milliseconds. + +- **`fn from_secs(s: U64) -> Duration`** + Creates a duration from seconds. + +### Struct `Time` + +Utilities for time manipulation. + +#### Methods + +- **`fn now() -> U64`** + Returns the current system time in milliseconds since the epoch. + +- **`fn sleep(d: Duration)`** + Sleeps for the specified duration. + +- **`fn sleep_ms(ms: U64)`** + Sleeps for the specified number of milliseconds. diff --git a/examples/objc_interop.zc b/examples/objc_interop.zc new file mode 100644 index 0000000..c33fd0d --- /dev/null +++ b/examples/objc_interop.zc @@ -0,0 +1,42 @@ + +//> macos: framework: Foundation +//> linux: cflags: -fconstant-string-class=NSConstantString -D_NATIVE_OBJC_EXCEPTIONS -I/usr/include/x86_64-linux-gnu/GNUstep -std=gnu99 +//> linux: link: -lgnustep-base -lobjc + +include + +raw { + id make_nsstring(const char* s) { + return [NSString stringWithUTF8String:s]; + } + + void print_description(id obj) { + NSLog(@"[ObjC Log] Description: %@", obj); + } +} + +fn main() { + + raw { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + } + + "=> Zen C + Objective-C interop demo."; + "=> (Make sure to run with: zc run --objc examples/objc_interop.zc)"; + + // Call ObjC helper to create an NSString (returned as id) + let s = make_nsstring("Hello from Objective-C!"); + + // Pass it back to ObjC + print_description(s); + + "Zen C interpolated string: {s}"; + + raw { + // You can also raw ObjC syntax anywhere. + NSArray *arr = [NSArray arrayWithObjects: @"Zen", @"Bit", @"C", nil]; + NSLog(@"[ObjC Raw] Array: %@", arr); + + [pool drain]; + } +} diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c index 11cdece..31513ef 100644 --- a/src/codegen/codegen_decl.c +++ b/src/codegen/codegen_decl.c @@ -91,14 +91,8 @@ void emit_preamble(ParserContext *ctx, FILE *out) fputs("static inline const char* _z_bool_str(_Bool b) { return b ? \"true\" : " "\"false\"; }\n", out); - fputs("#define _z_str(x) _Generic((x), _Bool: \"%s\", char: \"%c\", " - "signed char: \"%c\", unsigned char: \"%u\", short: \"%d\", " - "unsigned short: \"%u\", int: \"%d\", unsigned int: \"%u\", " - "long: \"%ld\", unsigned long: \"%lu\", long long: \"%lld\", " - "unsigned long long: \"%llu\", float: \"%f\", double: \"%f\", " - "char*: \"%s\", void*: \"%p\")\n", - out); - fputs("#define _z_arg(x) _Generic((x), _Bool: _z_bool_str(x), default: (x))\n", out); + fputs(ZC_C_GENERIC_STR, out); + fputs(ZC_C_ARG_GENERIC_STR, out); } fputs("typedef size_t usize;\ntypedef char* string;\n", out); @@ -108,12 +102,11 @@ void emit_preamble(ParserContext *ctx, FILE *out) fputs("typedef struct { pthread_t thread; void *result; } Async;\n", out); } fputs("typedef struct { void *func; void *ctx; } z_closure_T;\n", out); - fputs("#define U0 void\n#define I8 int8_t\n#define U8 uint8_t\n#define I16 " - "int16_t\n#define U16 uint16_t\n", + fputs("typedef void U0;\ntypedef int8_t I8;\ntypedef uint8_t U8;\ntypedef " + "int16_t I16;\ntypedef uint16_t U16;\n", out); - fputs("#define I32 int32_t\n#define U32 uint32_t\n#define I64 " - "int64_t\n#define U64 " - "uint64_t\n", + fputs("typedef int32_t I32;\ntypedef uint32_t U32;\ntypedef int64_t I64;\ntypedef " + "uint64_t U64;\n", out); fputs("#define F32 float\n#define F64 double\n", out); diff --git a/src/codegen/compat.h b/src/codegen/compat.h index 26b0df5..63a5af5 100644 --- a/src/codegen/compat.h +++ b/src/codegen/compat.h @@ -53,6 +53,27 @@ "#endif\n" \ "#endif\n" +/* Generic selection string for C mode */ +#define ZC_C_GENERIC_STR \ + "#ifdef __OBJC__\n" \ + "#define _z_objc_map ,id: \"%s\", Class: \"%s\", SEL: \"%s\"\n" \ + "#define _z_objc_arg_map(x) ,id: [(id)(x) description].UTF8String, Class: " \ + "class_getName((Class)(x)), SEL: sel_getName((SEL)(x))\n" \ + "#else\n" \ + "#define _z_objc_map\n" \ + "#define _z_objc_arg_map(x)\n" \ + "#endif\n" \ + "\n" \ + "#define _z_str(x) _Generic((x), _Bool: \"%s\", char: \"%c\", " \ + "signed char: \"%c\", unsigned char: \"%u\", short: \"%d\", " \ + "unsigned short: \"%u\", int: \"%d\", unsigned int: \"%u\", " \ + "long: \"%ld\", unsigned long: \"%lu\", long long: \"%lld\", " \ + "unsigned long long: \"%llu\", float: \"%f\", double: \"%f\", " \ + "char*: \"%s\", void*: \"%p\" _z_objc_map)\n" + +#define ZC_C_ARG_GENERIC_STR \ + "#define _z_arg(x) _Generic((x), _Bool: _z_bool_str(x) _z_objc_arg_map(x), default: (x))\n" + #ifdef __cplusplus #include @@ -126,6 +147,32 @@ inline const char *_zc_fmt(void *) } #define _z_str(x) _zc_fmt(x) + +#ifdef __OBJC__ +#include +#include +#include // for direct calls if needed, but [x description] is fine + +inline const char *_zc_fmt(id x) +{ + return [[x description] UTF8String]; +} +inline const char *_zc_fmt(Class x) +{ + return class_getName(x); +} +inline const char *_zc_fmt(SEL x) +{ + return sel_getName(x); +} +// BOOL is signed char usually, already handled? +// "typedef signed char BOOL;" on standard apple headers. +// If it maps to signed char, `_zc_fmt(signed char)` handles it ("%c"). +// We might want "YES"/"NO" for BOOL. +// But we can't distinguish typedefs in C++ function overloads easily if underlying type is same. +// We'll leave BOOL as %c or %d for now to avoid ambiguity errors. +#endif + #endif #endif diff --git a/src/main.c b/src/main.c index b6392cc..f5d8c1b 100644 --- a/src/main.c +++ b/src/main.c @@ -172,6 +172,10 @@ int main(int argc, char **argv) g_config.use_cuda = 1; g_config.use_cpp = 1; // CUDA implies C++ mode. } + else if (strcmp(arg, "--objc") == 0) + { + g_config.use_objc = 1; + } else if (strcmp(arg, "--check") == 0) { g_config.mode_check = 1; @@ -304,6 +308,10 @@ int main(int argc, char **argv) { temp_source_file = "out.cpp"; } + else if (g_config.use_objc) + { + temp_source_file = "out.m"; + } // Codegen to C/C++/CUDA FILE *out = fopen(temp_source_file, "w"); diff --git a/src/utils/utils.c b/src/utils/utils.c index 56a7690..d6d9853 100644 --- a/src/utils/utils.c +++ b/src/utils/utils.c @@ -533,9 +533,81 @@ char g_cflags[MAX_FLAGS_SIZE] = ""; int g_warning_count = 0; CompilerConfig g_config = {0}; +// Helper for environment expansion +static void expand_env_vars(char *dest, size_t dest_size, const char *src) +{ + char *d = dest; + const char *s = src; + size_t remaining = dest_size - 1; + + while (*s && remaining > 0) + { + if (*s == '$' && *(s + 1) == '{') + { + const char *end = strchr(s + 2, '}'); + if (end) + { + char var_name[256]; + int len = end - (s + 2); + if (len < 255) + { + strncpy(var_name, s + 2, len); + var_name[len] = 0; + char *val = getenv(var_name); + if (val) + { + size_t val_len = strlen(val); + if (val_len < remaining) + { + strcpy(d, val); + d += val_len; + remaining -= val_len; + s = end + 1; + continue; + } + } + } + } + } + *d++ = *s++; + remaining--; + } + *d = 0; +} + +// Helper to determine active OS +static int is_os_active(const char *os_name) +{ + if (0 == strcmp(os_name, "linux")) + { +#ifdef __linux__ + return 1; +#else + return 0; +#endif + } + else if (0 == strcmp(os_name, "windows")) + { +#ifdef _WIN32 + return 1; +#else + return 0; +#endif + } + else if (0 == strcmp(os_name, "macos") || 0 == strcmp(os_name, "darwin")) + { +#ifdef __APPLE__ + return 1; +#else + return 0; +#endif + } + return 0; +} + void scan_build_directives(ParserContext *ctx, const char *src) { - (void)ctx; // Currently unused, reserved for future use + (void)ctx; const char *p = src; while (*p) { @@ -554,103 +626,113 @@ void scan_build_directives(ParserContext *ctx, const char *src) len++; } - char line[2048]; + char raw_line[2048]; if (len >= 2047) { len = 2047; } - strncpy(line, start, len); - line[len] = 0; + strncpy(raw_line, start, len); + raw_line[len] = 0; + + char line[2048]; + expand_env_vars(line, sizeof(line), raw_line); - if (0 == strncmp(line, "link:", 5)) + char *directive = line; + char *colon = strchr(line, ':'); + if (colon) { - char *val = line + 5; - while (*val == ' ') + *colon = 0; + if (is_os_active(line)) + { + directive = colon + 1; + while (*directive == ' ') + { + directive++; + } + } + else if (0 == strcmp(line, "linux") || 0 == strcmp(line, "windows") || + 0 == strcmp(line, "macos")) { - val++; + goto next_line; } - if (strlen(g_link_flags) > 0) + else { - strcat(g_link_flags, " "); + *colon = ':'; + directive = line; } - strcat(g_link_flags, val); } - else if (0 == strncmp(line, "cflags:", 7)) + + // Process Directive + if (0 == strncmp(directive, "link:", 5)) { - char *val = line + 7; - while (*val == ' ') + if (strlen(g_link_flags) > 0) { - val++; + strcat(g_link_flags, " "); } + strcat(g_link_flags, directive + 5); + } + else if (0 == strncmp(directive, "cflags:", 7)) + { if (strlen(g_cflags) > 0) { strcat(g_cflags, " "); } - strcat(g_cflags, val); + strcat(g_cflags, directive + 7); } - else if (0 == strncmp(line, "include:", 8)) + else if (0 == strncmp(directive, "include:", 8)) { - char *val = line + 8; - while (*val == ' ') - { - val++; - } char flags[2048]; - sprintf(flags, "-I%s", val); + sprintf(flags, "-I%s", directive + 8); if (strlen(g_cflags) > 0) { strcat(g_cflags, " "); } strcat(g_cflags, flags); } - else if (strncmp(line, "lib:", 4) == 0) + else if (strncmp(directive, "lib:", 4) == 0) { - char *val = line + 4; - while (*val == ' ') - { - val++; - } char flags[2048]; - sprintf(flags, "-L%s", val); + sprintf(flags, "-L%s", directive + 4); if (strlen(g_link_flags) > 0) { strcat(g_link_flags, " "); } strcat(g_link_flags, flags); } - else if (strncmp(line, "define:", 7) == 0) + else if (strncmp(directive, "framework:", 10) == 0) { - char *val = line + 7; - while (*val == ' ') + char flags[2048]; + sprintf(flags, "-framework %s", directive + 10); + if (strlen(g_link_flags) > 0) { - val++; + strcat(g_link_flags, " "); } + strcat(g_link_flags, flags); + } + else if (strncmp(directive, "define:", 7) == 0) + { char flags[2048]; - sprintf(flags, "-D%s", val); + sprintf(flags, "-D%s", directive + 7); if (strlen(g_cflags) > 0) { strcat(g_cflags, " "); } strcat(g_cflags, flags); } - else if (0 == strncmp(line, "shell:", 6)) + else if (0 == strncmp(directive, "shell:", 6)) { - char *cmd = line + 6; - // printf("[zprep] Running shell: %s\n", cmd); - int res = system(cmd); - if (res != 0) + if (system(directive + 6) != 0) { - zwarn("Shell directive failed: %s", cmd); + zwarn("Shell directive failed: %s", directive + 6); } } - else if (strncmp(line, "get:", 4) == 0) + else if (strncmp(directive, "get:", 4) == 0) { - char *url = line + 4; + char *url = directive + 4; while (*url == ' ') { url++; } - char *filename = strrchr(url, '/'); if (!filename) { @@ -660,8 +742,6 @@ void scan_build_directives(ParserContext *ctx, const char *src) { filename++; } - - // Check if file exists to avoid redownloading. FILE *f = fopen(filename, "r"); if (f) { @@ -669,16 +749,13 @@ void scan_build_directives(ParserContext *ctx, const char *src) } else { - printf("[zprep] Downloading %s...\n", filename); char cmd[8192]; if (z_is_windows()) { - // On Windows, try curl which is often built-in now sprintf(cmd, "curl -s -L \"%s\" -o \"%s\"", url, filename); } else { - // Try wget, then curl. sprintf(cmd, "wget -q \"%s\" -O \"%s\" || curl -s -L \"%s\" -o \"%s\"", url, filename, url, filename); } @@ -688,40 +765,27 @@ void scan_build_directives(ParserContext *ctx, const char *src) } } } - else if (strncmp(line, "pkg-config:", 11) == 0) + else if (strncmp(directive, "pkg-config:", 11) == 0) { - char *libs = line + 11; - while (*libs == ' ') - { - libs++; - } - - if (z_is_windows()) - { - zwarn("pkg-config is usually not available on Windows. Build directive " - "'pkg-config:%s' might fail.", - libs); - } - + char *libs = directive + 11; char cmd[4096]; sprintf(cmd, "pkg-config --cflags %s", libs); FILE *fp = popen(cmd, "r"); if (fp) { - char flags[4096]; - flags[0] = 0; - if (fgets(flags, sizeof(flags), fp)) + char buf[1024]; + if (fgets(buf, sizeof(buf), fp)) { - int len = strlen(flags); - if (len > 0 && flags[len - 1] == '\n') + size_t l = strlen(buf); + if (l > 0 && buf[l - 1] == '\n') { - flags[len - 1] = 0; + buf[l - 1] = 0; } if (strlen(g_cflags) > 0) { strcat(g_cflags, " "); } - strcat(g_cflags, flags); + strcat(g_cflags, buf); } pclose(fp); } @@ -730,32 +794,35 @@ void scan_build_directives(ParserContext *ctx, const char *src) fp = popen(cmd, "r"); if (fp) { - char flags[4096]; - flags[0] = 0; - if (fgets(flags, sizeof(flags), fp)) + char buf[1024]; + if (fgets(buf, sizeof(buf), fp)) { - int len = strlen(flags); - if (len > 0 && flags[len - 1] == '\n') + size_t l = strlen(buf); + if (l > 0 && buf[l - 1] == '\n') { - flags[len - 1] = 0; + buf[l - 1] = 0; } if (strlen(g_link_flags) > 0) { strcat(g_link_flags, " "); } - strcat(g_link_flags, flags); + strcat(g_link_flags, buf); } pclose(fp); } } + else + { + zwarn("Unknown build directive: '%s'", directive); + } p += len; } + next_line: while (*p && *p != '\n') { p++; } - if (*p == '\n') { p++; diff --git a/src/zprep.h b/src/zprep.h index a943f3f..84400b3 100644 --- a/src/zprep.h +++ b/src/zprep.h @@ -359,6 +359,7 @@ typedef struct int mode_transpile; ///< 1 if 'transpile' command (to C). int use_cpp; ///< 1 if --cpp (emit C++ compatible code). int use_cuda; ///< 1 if --cuda (emit CUDA-compatible code). + int use_objc; ///< 1 if --objc (emit Objective-C compatible code). // GCC Flags accumulator. char gcc_flags[4096]; ///< Flags passed to the backend compiler. diff --git a/std/fs.zc b/std/fs.zc index c19f9f1..7a54005 100644 --- a/std/fs.zc +++ b/std/fs.zc @@ -2,21 +2,23 @@ import "./core.zc" import "./result.zc" import "./string.zc" import "./vec.zc" +import "./mem.zc" def Z_SEEK_SET = 0; def Z_SEEK_END = 2; def Z_F_OK = 0; +include +include +include +include +include // TODO: restructure this tomorrow. raw { - #include - #include - #include - - // typedef needed for Vec generation if inferred typedef struct DirEntry* DirEntryPtr; + // Wrappers for FILE* handling due to opaque pointer casting void* _z_fs_fopen(char* path, char* mode) { return fopen(path, mode); } @@ -40,7 +42,9 @@ raw { int64_t _z_fs_ftell(void* stream) { 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); } @@ -53,6 +57,7 @@ raw { return rmdir(pathname); } + // Wrappers for DIR* handling void* _z_fs_opendir(char* name) { return opendir(name); } @@ -61,14 +66,7 @@ raw { return closedir((DIR*)dir); } - void* _z_fs_malloc(size_t size) { - return malloc(size); - } - - void _z_fs_free(void* ptr) { - free(ptr); - } - + // struct stat / struct dirent helpers int _z_fs_get_metadata(char* path, uint64_t* size, int* is_dir, int* is_file) { struct stat st; if (stat(path, &st) != 0) return -1; @@ -96,6 +94,10 @@ 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; @@ -105,13 +107,13 @@ extern fn _z_fs_fread(ptr: void*, size: usize, nmemb: usize, stream: void*) -> u extern fn _z_fs_fwrite(ptr: void*, size: usize, nmemb: usize, stream: void*) -> usize; extern fn _z_fs_fseek(stream: void*, offset: long, whence: int) -> int; extern fn _z_fs_ftell(stream: void*) -> long; +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: char*) -> void*; -extern fn _z_fs_closedir(dir: void*) -> int; -extern fn _z_fs_malloc(size: usize) -> void*; -extern fn _z_fs_free(ptr: void*); + struct File { handle: void*; @@ -153,7 +155,7 @@ impl File { let size = _z_fs_ftell(self.handle); _z_fs_fseek(self.handle, 0, Z_SEEK_SET); - let buffer: char* = _z_fs_malloc((usize)size + 1); + let buffer: char* = malloc((usize)size + 1); if (buffer == NULL) { return Result::Err("Out of memory"); } @@ -162,7 +164,7 @@ impl File { buffer[read] = 0; let s = String::new(buffer); - _z_fs_free(buffer); + free(buffer); let res = Result::Ok(s); s.forget(); @@ -248,7 +250,7 @@ impl File { } let entries = Vec::new(); - let name_buf: char* = _z_fs_malloc(256); + let name_buf: char* = malloc(256); if (name_buf == NULL) { _z_fs_closedir(dir); @@ -277,7 +279,7 @@ impl File { ent.name.forget(); } - _z_fs_free(name_buf); + free(name_buf); _z_fs_closedir(dir); let res = Result< Vec >::Ok(entries); diff --git a/std/json.zc b/std/json.zc index e6a15cd..d373ab9 100644 --- a/std/json.zc +++ b/std/json.zc @@ -5,6 +5,7 @@ import "./map.zc" import "./string.zc" import "./result.zc" import "./option.zc" +import "./mem.zc" @derive(Eq) enum JsonType { @@ -450,3 +451,9 @@ impl JsonValue { } } } + +impl Drop for JsonValue { + fn drop(self) { + self.free(); + } +} diff --git a/std/map.zc b/std/map.zc index 8e58cdc..70d6ad2 100644 --- a/std/map.zc +++ b/std/map.zc @@ -1,6 +1,7 @@ import "./core.zc" import "./option.zc" +import "./mem.zc" raw { extern size_t __zen_hash_seed; @@ -248,3 +249,9 @@ impl Map { }; } } + +impl Drop for Map { + fn drop(self) { + self.free(); + } +} diff --git a/std/net.zc b/std/net.zc index 72c3fcb..dd10642 100644 --- a/std/net.zc +++ b/std/net.zc @@ -8,6 +8,7 @@ include import "./core.zc" import "./result.zc" import "./string.zc" +import "./mem.zc" def Z_AF_INET = 2; def Z_SOCK_STREAM = 1; @@ -36,15 +37,11 @@ raw { if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) return -2; return 0; } - + static int _z_net_accept(int fd) { return accept(fd, NULL, NULL); } - static ssize_t _z_net_read(int fd, char* buf, size_t n) { - return read(fd, (void*)buf, n); - } - static ssize_t _z_net_write(int fd, char* buf, size_t n) { return write(fd, (const void*)buf, n); } @@ -52,11 +49,11 @@ raw { 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_read(fd: int, buf: char*, n: usize) -> isize; extern fn _z_net_write(fd: int, buf: char*, n: usize) -> isize; @@ -66,7 +63,7 @@ struct TcpStream { impl TcpStream { fn read(self, buf: char*, len: usize) -> Result { - let n = _z_net_read(self.fd, buf, len); + let n = read(self.fd, (void*)buf, len); if (n < 0) return Result::Err("Read failed"); return Result::Ok((usize)n); } @@ -96,6 +93,12 @@ impl TcpStream { } } +impl Drop for TcpStream { + fn drop(self) { + self.close(); + } +} + struct TcpListener { fd: int; } @@ -126,3 +129,9 @@ impl TcpListener { } } } + +impl Drop for TcpListener { + fn drop(self) { + self.close(); + } +} diff --git a/std/thread.zc b/std/thread.zc index e90943b..98f080e 100644 --- a/std/thread.zc +++ b/std/thread.zc @@ -5,6 +5,7 @@ include import "./core.zc" import "./result.zc" +import "./mem.zc" raw { typedef void (*ZenThreadFunc)(void*); @@ -120,11 +121,17 @@ impl Mutex { if (self.handle) { _z_mutex_destroy(self.handle); free(self.handle); + self.handle = NULL; } } } +impl Drop for Mutex { + fn drop(self) { + self.free(); + } +} + fn sleep_ms(ms: int) { _z_usleep(ms * 1000); } - diff --git a/std/time.zc b/std/time.zc index 1191821..8e7cc6c 100644 --- a/std/time.zc +++ b/std/time.zc @@ -1,18 +1,28 @@ - import "./core.zc" +include +include +include +include + raw { - #include - #include - #include - static uint64_t _time_now_impl(void) { struct timeval tv; gettimeofday(&tv, NULL); return (uint64_t)(tv.tv_sec) * 1000 + (uint64_t)(tv.tv_usec) / 1000; } + + static long _z_time_time(void) { + return (long)time(NULL); + } } +extern fn srand(seed: U32); +extern fn rand() -> int; +extern fn usleep(micros: U32) -> int; +extern fn _time_now_impl() -> U64; +extern fn _z_time_time() -> long; + struct Duration { millis: U64; } @@ -33,10 +43,8 @@ extern size_t __zen_hash_seed; impl Time { fn randomize_hash() { - raw { - srand(time(NULL)); - __zen_hash_seed ^= (size_t)rand(); - } + srand((U32)_z_time_time()); + __zen_hash_seed ^= (size_t)rand(); } fn now() -> U64 { diff --git a/tests/features/test_build_directives.zc b/tests/features/test_build_directives.zc index 7edd317..d3f1cba 100644 --- a/tests/features/test_build_directives.zc +++ b/tests/features/test_build_directives.zc @@ -1,19 +1,8 @@ -//> link: -lm -//> cflags: -O2 -// Declare C math function (since we don't have a math stdlib module yet) -extern fn sin(x: double) -> double; +//> shell: echo "Env Worked" > ${PWD}/build_dir_env.txt +//> linux: shell: echo "Linux Worked" > ${PWD}/build_dir_linux.txt +//> windows: shell: echo "Windows Worked" > ${PWD}/build_dir_windows.txt -test "test_build_directives" { - println "Running Build Directives Test..."; - let x = 3.14159 / 2.0; // PI/2 - let s = sin(x); - // sin(PI/2) should be 1.0 - println "sin(PI/2) = {s}"; - - if (s > 0.99 && s < 1.01) { - println "Math Link Success!"; - } else { - println "Math Link Failure (Value wrong)"; - } +fn main() { + return 0; } -- cgit v1.2.3 From 0427d254207a69e394499d1abaea768f484f1cb5 Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Fri, 30 Jan 2026 02:24:10 +0000 Subject: Improvements related to C23 (#112) --- README.md | 12 +++++++ README_ES.md | 11 ++++++ README_ZH_CN.md | 11 ++++++ README_ZH_TW.md | 11 ++++++ src/ast/ast.c | 25 ++++++++++++++ src/ast/ast.h | 4 ++- src/codegen/codegen_decl.c | 4 +++ src/codegen/compat.h | 6 +++- src/parser/parser_expr.c | 2 ++ src/parser/parser_type.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 168 insertions(+), 2 deletions(-) (limited to 'src/codegen/codegen_decl.c') diff --git a/README.md b/README.md index 75c4624..f03f605 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ Join the discussion, share demos, ask questions, or report bugs in the official - [C++ Interop](#c-interop) - [CUDA Interop](#cuda-interop) - [Objective-C Interop](#objective-c-interop) + - [C23 Support](#c23-support) - [Contributing](#contributing) - [Attributions](#attributions) @@ -197,6 +198,8 @@ let y: const int = 10; // Read-only (Type qualified) // y = 20; // Error: cannot assign to const ``` +> **Type Inference**: Zen C automatically infers types for initialized variables. It compiles to C23 `auto` on supported compilers, or GCC's `__auto_type` extension otherwise. + ### 2. Primitive Types | Type | C Equivalent | Description | @@ -211,6 +214,8 @@ let y: const int = 10; // Read-only (Type qualified) | `char` | `char` | Single character | | `string` | `char*` | C-string (null-terminated) | | `U0`, `u0`, `void` | `void` | Empty type | +| `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) | ### 3. Aggregate Types @@ -1337,6 +1342,13 @@ let tid = local_id(); > **Note:** The `--cuda` flag sets `nvcc` as the compiler and implies `--cpp` mode. Requires the NVIDIA CUDA Toolkit. +### C23 Support + +Zen C supports modern C23 features when using a compatible backend compiler (GCC 14+, Clang 14+, TCC (partial)). + +- **`auto`**: Zen C automatically maps type inference to standard C23 `auto` if `__STDC_VERSION__ >= 202300L`. +- **`_BitInt(N)`**: Use `iN` and `uN` types (e.g., `i256`, `u12`, `i24`) to access C23 arbitrary-width integers. + ### Objective-C Interop Zen C can compile to Objective-C (`.m`) using the `--objc` flag, allowing you to use Objective-C frameworks (like Cocoa/Foundation) and syntax. diff --git a/README_ES.md b/README_ES.md index c5655e3..9040ea8 100644 --- a/README_ES.md +++ b/README_ES.md @@ -197,6 +197,8 @@ let y: const int = 10; // Solo lectura (Calificado por tipo) // y = 20; // Error: no se puede asignar a una constante ``` +> **Inferencia de tipos**: Zen C infiere automáticamente los tipos para variables inicializadas. Se compila a `auto` de C23 en compiladores compatibles, o a la extensión `__auto_type` de GCC en otros casos. + ### 2. Tipos Primitivos | Tipo | Equivalente en C | Descripción | @@ -211,6 +213,8 @@ let y: const int = 10; // Solo lectura (Calificado por tipo) | `char` | `char` | Carácter único | | `string` | `char*` | Cadena de C (terminada en null) | | `U0`, `u0`, `void` | `void` | Tipo vacío | +| `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) | ### 3. Tipos Agregados @@ -1337,6 +1341,13 @@ let tid = local_id(); > **Nota:** La flag `--cuda` establece `nvcc` como el compilador e implica el modo `--cpp`. Requiere el NVIDIA CUDA Toolkit. +### Soporte C23 + +Zen C soporta características modernas de C23 cuando se utiliza un compilador backend compatible (GCC 14+, Clang 14+). + +- **`auto`**: Zen C mapea automáticamente la inferencia de tipos a `auto` estándar de C23 si `__STDC_VERSION__ >= 202300L`. +- **`_BitInt(N)`**: Use tipos `iN` y `uN` (ej. `i256`, `u12`, `i24`) para acceder a enteros de ancho arbitrario de C23. + ### Interop con Objective-C Zen C puede compilarse a Objective-C (`.m`) usando la flag `--objc`, permitiéndote usar frameworks de Objective-C (como Cocoa/Foundation) y su sintaxis. diff --git a/README_ZH_CN.md b/README_ZH_CN.md index 6fce2d2..217e9ec 100644 --- a/README_ZH_CN.md +++ b/README_ZH_CN.md @@ -197,6 +197,8 @@ let y: const int = 10; // 只读 (类型修饰) // y = 20; // 错误:无法赋值给 const 变量 ``` +> **类型推导**:Zen C 自动推导初始化变量的类型。在支持的编译器上编译为 C23 的 `auto`,否则使用 GCC 的 `__auto_type` 扩展。 + ### 2. 原始类型 | 类型 | C 等效类型 | 描述 | @@ -211,6 +213,8 @@ let y: const int = 10; // 只读 (类型修饰) | `char` | `char` | 单个字符 | | `string` | `char*` | C-string (以 null 结尾) | | `U0`, `u0`, `void` | `void` | 空类型 | +| `iN` (例 `i256`) | `_BitInt(N)` | 任意位宽有符号整数 (C23) | +| `uN` (例 `u42`) | `unsigned _BitInt(N)` | 任意位宽无符号整数 (C23) | ### 3. 复合类型 @@ -1337,6 +1341,13 @@ let tid = local_id(); > **注意:** `--cuda` 标志设置 `nvcc` 为编译器并隐含 `--cpp` 模式。需要安装 NVIDIA CUDA Toolkit。 +### C23 支持 + +当使用兼容的后端编译器(GCC 14+, Clang 14+)时,Zen C 支持现代 C23特性。 + +- **`auto`**: 如果 `__STDC_VERSION__ >= 202300L`,Zen C 会自动将类型推导映射到标准 C23 `auto`。 +- **`_BitInt(N)`**: 使用 `iN` 和 `uN` 类型(例如 `i256`, `u12`, `i24`)访问 C23 任意位宽整数。 + ### Objective-C 互操作 Zen C 可以通过 `--objc` 标志编译为 Objective-C (`.m`),允许你使用 Objective-C 框架(如 Cocoa/Foundation)和语法。 diff --git a/README_ZH_TW.md b/README_ZH_TW.md index fc9cef5..8618540 100644 --- a/README_ZH_TW.md +++ b/README_ZH_TW.md @@ -197,6 +197,8 @@ let y: const int = 10; // 只讀 (類型修飾) // y = 20; // 錯誤:無法賦值給 const 變量 ``` +> **型別推導**:Zen C 自動推導初始化變數的型別。在支援的編譯器上編譯為 C23 的 `auto`,否則使用 GCC 的 `__auto_type` 擴充功能。 + ### 2. 原始類型 | 類型 | C 等效類型 | 描述 | @@ -211,6 +213,8 @@ let y: const int = 10; // 只讀 (類型修飾) | `char` | `char` | 單個字符 | | `string` | `char*` | C-string (以 null 結尾) | | `U0`, `u0`, `void` | `void` | 空類型 | +| `iN` (例 `i256`) | `_BitInt(N)` | 任意位元寬度有號整數 (C23) | +| `uN` (例 `u42`) | `unsigned _BitInt(N)` | 任意位元寬度無號整數 (C23) | ### 3. 複合類型 @@ -1337,6 +1341,13 @@ let tid = local_id(); > **注意:** `--cuda` 標誌設置 `nvcc` 為編譯器並隱含 `--cpp` 模式。需要安裝 NVIDIA CUDA Toolkit。 +### C23 支援 + +當使用相容的後端編譯器(GCC 14+, Clang 14+)時,Zen C 支援現代 C23 特性。 + +- **`auto`**: 如果 `__STDC_VERSION__ >= 202300L`,Zen C 會自動將型別推導映射到標準 C23 `auto`。 +- **`_BitInt(N)`**: 使用 `iN` 和 `uN` 型別(例如 `i256`, `u12`, `i24`)存取 C23 任意位元寬度整數。 + ### Objective-C 互操作 Zen C 可以通過 `--objc` 標誌編譯為 Objective-C (`.m`),允許你使用 Objective-C 框架(如 Cocoa/Foundation)和語法。 diff --git a/src/ast/ast.c b/src/ast/ast.c index f4922a6..439a9f5 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -100,6 +100,7 @@ int is_integer_type(Type *t) t->kind == TYPE_I64 || t->kind == TYPE_U64 || t->kind == TYPE_USIZE || 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_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") || @@ -262,6 +263,18 @@ static char *type_to_string_impl(Type *t) return xstrdup("int"); case TYPE_FLOAT: return xstrdup("float"); + case TYPE_BITINT: + { + char *res = xmalloc(32); + sprintf(res, "i%d", t->array_size); + return res; + } + case TYPE_UBITINT: + { + char *res = xmalloc(32); + sprintf(res, "u%d", t->array_size); + return res; + } case TYPE_POINTER: { @@ -452,6 +465,18 @@ static char *type_to_c_string_impl(Type *t) return xstrdup("int"); case TYPE_FLOAT: return xstrdup("float"); + case TYPE_BITINT: + { + char *res = xmalloc(32); + sprintf(res, "_BitInt(%d)", t->array_size); + return res; + } + case TYPE_UBITINT: + { + char *res = xmalloc(40); + sprintf(res, "unsigned _BitInt(%d)", t->array_size); + return res; + } case TYPE_POINTER: { diff --git a/src/ast/ast.h b/src/ast/ast.h index a868bf0..71d9943 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -59,6 +59,8 @@ typedef enum TYPE_FUNCTION, ///< Function pointer or reference. TYPE_GENERIC, ///< Generic type parameter (T). TYPE_ALIAS, ///< Opaque type alias. + TYPE_BITINT, ///< C23 _BitInt(N). + TYPE_UBITINT, ///< C23 unsigned _BitInt(N). TYPE_UNKNOWN ///< Unknown/unresolved type. } TypeKind; @@ -75,7 +77,7 @@ typedef struct Type int is_const; ///< 1 if const-qualified. int is_explicit_struct; ///< 1 if defined with "struct" keyword explicitly. int is_raw; // Raw function pointer (fn*) - int array_size; ///< Size for fixed-size arrays. + int array_size; ///< Size for fixed-size arrays. For TYPE_BITINT, this is the bit width. union { int is_varargs; ///< 1 if function type is variadic. diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c index 31513ef..0b78676 100644 --- a/src/codegen/codegen_decl.c +++ b/src/codegen/codegen_decl.c @@ -85,7 +85,11 @@ void emit_preamble(ParserContext *ctx, FILE *out) else { // C mode + fputs("#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202300L\n", out); + fputs("#define ZC_AUTO auto\n", out); + fputs("#else\n", out); fputs("#define ZC_AUTO __auto_type\n", out); + fputs("#endif\n", out); fputs("#define ZC_CAST(T, x) ((T)(x))\n", out); fputs(ZC_TCC_COMPAT_STR, out); fputs("static inline const char* _z_bool_str(_Bool b) { return b ? \"true\" : " diff --git a/src/codegen/compat.h b/src/codegen/compat.h index 63a5af5..f8d9a4e 100644 --- a/src/codegen/compat.h +++ b/src/codegen/compat.h @@ -14,7 +14,11 @@ #define ZC_EXTERN_C_END } #else /* C mode */ -#define ZC_AUTO __auto_type ///< Auto type inference. +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202300L +#define ZC_AUTO auto ///< C23 standard auto. +#else +#define ZC_AUTO __auto_type ///< GCC/Clang extension. +#endif #define ZC_CAST(T, x) ((T)(x)) ///< Explicit cast. #define ZC_REINTERPRET(T, x) ((T)(x)) ///< Reinterpret cast. #define ZC_EXTERN_C ///< Extern "C" (no-op in C). diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c index 51d2baa..6156cc0 100644 --- a/src/parser/parser_expr.c +++ b/src/parser/parser_expr.c @@ -148,6 +148,8 @@ int is_type_copy(ParserContext *ctx, Type *t) case TYPE_POINTER: // Pointers are Copy case TYPE_FUNCTION: case TYPE_ENUM: // Enums are integers + case TYPE_BITINT: + case TYPE_UBITINT: return 1; case TYPE_STRUCT: diff --git a/src/parser/parser_type.c b/src/parser/parser_type.c index 65f2848..49e961c 100644 --- a/src/parser/parser_type.c +++ b/src/parser/parser_type.c @@ -300,6 +300,90 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l) free(name); return type_new(TYPE_I16); } + + // C23 BitInt Support (i42, u256, etc.) + if ((name[0] == 'i' || name[0] == 'u') && isdigit(name[1])) + { + // Verify it is a purely numeric suffix + int valid = 1; + for (size_t k = 1; k < strlen(name); k++) + { + if (!isdigit(name[k])) + { + valid = 0; + break; + } + } + if (valid) + { + int width = atoi(name + 1); + if (width > 0) + { + // Map standard widths to standard types for standard ABI/C compabitility + if (name[0] == 'i') + { + if (width == 8) + { + free(name); + return type_new(TYPE_I8); + } + if (width == 16) + { + free(name); + return type_new(TYPE_I16); + } + if (width == 32) + { + free(name); + return type_new(TYPE_I32); + } + if (width == 64) + { + free(name); + return type_new(TYPE_I64); + } + if (width == 128) + { + free(name); + return type_new(TYPE_I128); + } + } + else + { + if (width == 8) + { + free(name); + return type_new(TYPE_U8); + } + if (width == 16) + { + free(name); + return type_new(TYPE_U16); + } + if (width == 32) + { + free(name); + return type_new(TYPE_U32); + } + if (width == 64) + { + free(name); + return type_new(TYPE_U64); + } + if (width == 128) + { + free(name); + return type_new(TYPE_U128); + } + } + + Type *t = type_new(name[0] == 'u' ? TYPE_UBITINT : TYPE_BITINT); + t->array_size = width; + free(name); + return t; + } + } + } if (strcmp(name, "u16") == 0) { free(name); -- cgit v1.2.3 From 4acbcb77483ff8fc127dca261864877675fa3b5a Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Sat, 31 Jan 2026 01:30:34 +0000 Subject: Update codegen for robustness --- src/codegen/codegen.h | 4 ++-- src/codegen/codegen_decl.c | 6 +++--- src/codegen/codegen_main.c | 2 +- src/codegen/codegen_stmt.c | 2 +- src/codegen/codegen_utils.c | 49 +++++++++++++++++++++------------------------ 5 files changed, 30 insertions(+), 33 deletions(-) (limited to 'src/codegen/codegen_decl.c') diff --git a/src/codegen/codegen.h b/src/codegen/codegen.h index b3e971d..89614f7 100644 --- a/src/codegen/codegen.h +++ b/src/codegen/codegen.h @@ -48,7 +48,7 @@ char *replace_string_type(const char *args); const char *parse_original_method_name(const char *mangled); void emit_auto_type(ParserContext *ctx, ASTNode *init_expr, Token t, FILE *out); char *codegen_type_to_string(Type *t); -void emit_func_signature(FILE *out, ASTNode *func, const char *name_override); +void emit_func_signature(ParserContext *ctx, FILE *out, ASTNode *func, const char *name_override); char *strip_template_suffix(const char *name); int emit_move_invalidation(ParserContext *ctx, ASTNode *node, FILE *out); void codegen_expression_with_move(ParserContext *ctx, ASTNode *node, FILE *out); @@ -66,7 +66,7 @@ void emit_trait_defs(ASTNode *node, FILE *out); void emit_enum_protos(ASTNode *node, FILE *out); void emit_globals(ParserContext *ctx, ASTNode *node, FILE *out); void emit_lambda_defs(ParserContext *ctx, FILE *out); -void emit_protos(ASTNode *node, FILE *out); +void emit_protos(ParserContext *ctx, ASTNode *node, FILE *out); void emit_impl_vtables(ParserContext *ctx, FILE *out); /** diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c index 0b78676..9d23617 100644 --- a/src/codegen/codegen_decl.c +++ b/src/codegen/codegen_decl.c @@ -698,7 +698,7 @@ void emit_globals(ParserContext *ctx, ASTNode *node, FILE *out) } // Emit function prototypes -void emit_protos(ASTNode *node, FILE *out) +void emit_protos(ParserContext *ctx, ASTNode *node, FILE *out) { ASTNode *f = node; while (f) @@ -721,7 +721,7 @@ void emit_protos(ASTNode *node, FILE *out) } else { - emit_func_signature(out, f, NULL); + emit_func_signature(ctx, out, f, NULL); fprintf(out, ";\n"); } } @@ -799,7 +799,7 @@ void emit_protos(ASTNode *node, FILE *out) } else { - emit_func_signature(out, m, proto); + emit_func_signature(ctx, out, m, proto); fprintf(out, ";\n"); } diff --git a/src/codegen/codegen_main.c b/src/codegen/codegen_main.c index a140070..b298700 100644 --- a/src/codegen/codegen_main.c +++ b/src/codegen/codegen_main.c @@ -616,7 +616,7 @@ void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out) } } - emit_protos(merged_funcs, out); + emit_protos(ctx, merged_funcs, out); emit_impl_vtables(ctx, out); diff --git a/src/codegen/codegen_stmt.c b/src/codegen/codegen_stmt.c index 2f9a2ba..7828ecf 100644 --- a/src/codegen/codegen_stmt.c +++ b/src/codegen/codegen_stmt.c @@ -750,7 +750,7 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) { fprintf(out, "inline "); } - emit_func_signature(out, node, NULL); + emit_func_signature(ctx, out, node, NULL); fprintf(out, "\n"); fprintf(out, "{\n"); char *prev_ret = g_current_func_ret_type; diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c index 39d1861..08707cc 100644 --- a/src/codegen/codegen_utils.c +++ b/src/codegen/codegen_utils.c @@ -41,7 +41,7 @@ char *strip_template_suffix(const char *name) } // Helper to emit C declaration (handle arrays, function pointers correctly) -void emit_c_decl(FILE *out, const char *type_str, const char *name) +void emit_c_decl(ParserContext *ctx, FILE *out, const char *type_str, const char *name) { char *bracket = strchr(type_str, '['); char *generic = strchr(type_str, '<'); @@ -64,40 +64,38 @@ void emit_c_decl(FILE *out, const char *type_str, const char *name) } else if (generic && (!bracket || generic < bracket)) { - // Special case: Async should NOT be mangled to Async_T - // because it compiles to the opaque struct 'Async' (defined in codegen_decl) - if (strncmp(type_str, "Async<", 6) == 0) - { - fprintf(out, "Async %s", name); - if (bracket) - { - fprintf(out, "%s", bracket); - } - return; - } - + char mangled_candidate[256]; char *gt = strchr(generic, '>'); + int success = 0; + if (gt) { int base_len = generic - type_str; int arg_len = gt - generic - 1; - fprintf(out, "%.*s_%.*s %s", base_len, type_str, arg_len, generic + 1, name); - - if (bracket) + // Limit check + if (base_len + arg_len + 2 < 256) { - fprintf(out, "%s", bracket); + snprintf(mangled_candidate, 256, "%.*s_%.*s", base_len, type_str, arg_len, + generic + 1); + + if (find_struct_def_codegen(ctx, mangled_candidate)) + { + fprintf(out, "%s %s", mangled_candidate, name); + success = 1; + } } } - else + + if (!success) { int base_len = generic - type_str; fprintf(out, "%.*s %s", base_len, type_str, name); + } - if (bracket) - { - fprintf(out, "%s", bracket); - } + if (bracket) + { + fprintf(out, "%s", bracket); } } else if (bracket) @@ -114,8 +112,7 @@ void emit_c_decl(FILE *out, const char *type_str, const char *name) // Helper to emit variable declarations with array types. void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str, const char *var_name) { - (void)ctx; - emit_c_decl(out, type_str, var_name); + emit_c_decl(ctx, out, type_str, var_name); } // Find struct definition @@ -671,7 +668,7 @@ char *codegen_type_to_string(Type *t) } // Emit function signature using Type info for correct C codegen -void emit_func_signature(FILE *out, ASTNode *func, const char *name_override) +void emit_func_signature(ParserContext *ctx, FILE *out, ASTNode *func, const char *name_override) { if (!func || func->type != NODE_FUNCTION) { @@ -763,7 +760,7 @@ void emit_func_signature(FILE *out, ASTNode *func, const char *name_override) } // check if array type - emit_c_decl(out, type_str, name); + emit_c_decl(ctx, out, type_str, name); free(type_str); } if (func->func.is_varargs) -- cgit v1.2.3 From 91ed9fdd65e09bd6cd32e44dd07c390f2cf79c22 Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Sat, 31 Jan 2026 17:06:50 +0000 Subject: Fix codegen regressions: casting precedence and process segfault --- src/codegen/codegen.c | 16 ++++++++++++++-- src/codegen/codegen_decl.c | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'src/codegen/codegen_decl.c') diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index 53373e9..37415c2 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -290,6 +290,18 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) { is_null_compare = 1; } + else if (node->binary.right->type == NODE_EXPR_LITERAL && + node->binary.right->literal.type_kind == LITERAL_INT && + node->binary.right->literal.int_val == 0) + { + is_null_compare = 1; + } + else if (node->binary.left->type == NODE_EXPR_LITERAL && + node->binary.left->literal.type_kind == LITERAL_INT && + node->binary.left->literal.int_val == 0) + { + is_null_compare = 1; + } if (is_null_compare) { @@ -1121,9 +1133,9 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) break; } case NODE_EXPR_CAST: - fprintf(out, "(%s)(", node->cast.target_type); + fprintf(out, "((%s)(", node->cast.target_type); codegen_expression(ctx, node->cast.expr, out); - fprintf(out, ")"); + fprintf(out, "))"); break; case NODE_EXPR_SIZEOF: if (node->size_of.target_type) diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c index 9d23617..31bd2ee 100644 --- a/src/codegen/codegen_decl.c +++ b/src/codegen/codegen_decl.c @@ -50,6 +50,7 @@ void emit_preamble(ParserContext *ctx, FILE *out) else { // Standard hosted preamble. + fputs("#define _GNU_SOURCE\n", out); fputs("#include \n#include \n#include " "\n#include \n", out); -- cgit v1.2.3