Zen C is a modern systems programming language that compiles to human-readable GNU C/C11. It provides a rich feature set including type inference, pattern matching, generics, traits, async/await, and manual memory management with RAII capabilities, all while maintaining 100% C ABI compatibility.
Join the discussion, share demos, ask questions, or report bugs in the official Zen C Discord server!
- Discord: Join here
- Overview
- Community
- Quick Start
- Language Reference
- 1. Variables and Constants
- 2. Primitive Types
- 3. Aggregate Types
- 4. Functions & Lambdas
- 5. Control Flow
- 6. Operators
- 7. Printing and String Interpolation
- 8. Memory Management
- 9. Object Oriented Programming
- 10. Generics
- 11. Concurrency (Async/Await)
- 12. Metaprogramming
- 13. Attributes
- 14. Inline Assembly
- 15. Build Directives
- Compiler Support & Compatibility
- Contributing
git clone https://github.com/z-libs/Zen-C.git
cd Zen-C
make
sudo make install# Compile and run
zc run hello.zc
# Build executable
zc build hello.zc -o hello
# Interactive Shell
zc replYou 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.
export ZC_ROOT=/path/to/Zen-CZen C uses type inference by default.
var x = 42; // Inferred as int
const PI = 3.14159; // Compile-time constant
var explicit: float = 1.0; // Explicit type
| Type | C Equivalent | Description |
|---|---|---|
int, uint |
int, unsigned int |
Platform standard integer |
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 |
byte |
uint8_t |
Alias for U8 |
F32, F64 or f32, f64 |
float, double |
Floating point numbers |
bool |
bool |
true or false |
char |
char |
Single character |
string |
char* |
C-string (null-terminated) |
U0, u0, void |
void |
Empty type |
Fixed-size arrays with value semantics.
var ints: int[5] = {1, 2, 3, 4, 5};
var zeros: [int; 5]; // Zero-initialized
Group multiple values together.
var pair = (1, "Hello");
var x = pair.0;
var s = pair.1;
Data structures with optional bitfields.
struct Point {
x: int;
y: int;
}
// Struct initialization
var p = Point { x: 10, y: 20 };
// Bitfields
struct Flags {
valid: U8 : 1;
mode: U8 : 3;
}
Tagged unions (Sum types) capable of holding data.
enum Shape {
Circle(float), // Holds radius
Rect(float, float), // Holds width, height
Point // No data
}
Standard C unions (unsafe access).
union Data {
i: int;
f: float;
}
Create a new name for an existing type.
alias ID = int;
alias PointMap = Map<string, Point>;
fn add(a: int, b: int) -> int {
return a + b;
}
// Named arguments supported in calls
add(a: 10, b: 20);
Anonymous functions that can capture their environment.
var factor = 2;
var double = x -> x * factor; // Arrow syntax
var full = fn(x: int) -> int { return x * factor; }; // Block syntax
if x > 10 {
print("Large");
} else if x > 5 {
print("Medium");
} else {
print("Small");
}
// Ternary
var y = x > 10 ? 1 : 0;
Powerful alternative to switch.
match val {
1 => print("One"),
2 || 3 => print("Two or Three"), // OR with ||
4 or 5 => print("Four or Five"), // OR with 'or'
6, 7, 8 => print("Six to Eight"), // OR with comma
10..15 => print("10 to 14"), // Exclusive range (Legacy)
10..<15 => print("10 to 14"), // Exclusive range (Explicit)
20..=25 => print("20 to 25"), // Inclusive range
_ => print("Other")
}
// Destructuring Enums
match shape {
Circle(r) => print(f"Radius: {r}"),
Rect(w, h) => print(f"Area: {w*h}"),
Point => print("Point")
}
// Range
for i in 0..10 { ... } // Exclusive (0 to 9)
for i in 0..<10 { ... } // Exclusive (Explicit)
for i in 0..=10 { ... } // Inclusive (0 to 10)
for i in 0..10 step 2 { ... }
// Iterator/Collection
for item in vec { ... }
// While
while x < 10 { ... }
// Infinite with label
outer: loop {
if done { break outer; }
}
// Repeat N times
for _ in 0..5 { ... }
// Guard: Execute else and return if condition is false
guard ptr != NULL else { return; }
// Unless: If not true
unless is_valid { return; }
Zen C supports operator overloading for user-defined structs by implementing specific method names.
| Category | Operator | Method Name |
|---|---|---|
| Arithmetic | +, -, *, /, % |
add, sub, mul, div, rem |
| Comparison | ==, != |
eq, neq |
<, >, <=, >= |
lt, gt, le, ge |
|
| Bitwise | &, |, ^ |
bitand, bitor, bitxor |
<<, >> |
shl, shr |
|
| Unary | - |
neg |
! |
not |
|
~ |
bitnot |
|
| Index | a[i] |
get(a, i) |
a[i] = v |
set(a, i, v) |
Example:
impl Point {
fn add(self, other: Point) -> Point {
return Point{x: self.x + other.x, y: self.y + other.y};
}
}
var p3 = p1 + p2; // Calls p1.add(p2)
These operators are built-in language features and cannot be overloaded directly.
| Operator | Name | Description |
|---|---|---|
|> |
Pipeline | x |> f(y) desugars to f(x, y) |
?? |
Null Coalescing | val ?? default returns default if val is NULL (pointers) |
??= |
Null Assignment | val ??= init assigns if val is NULL |
?. |
Safe Navigation | ptr?.field accesses field only if ptr is not NULL |
? |
Try Operator | res? returns error if present (Result/Option types) |
Zen C provides versatile options for printing to the console, including keywords and concise shorthands.
print "text": Prints tostdoutwithout a trailing newline.println "text": Prints tostdoutwith a trailing newline.eprint "text": Prints tostderrwithout a trailing newline.eprintln "text": Prints tostderrwith a trailing newline.
Zen C allows you to use string literals directly as statements for quick printing:
"Hello World": Equivalent toprintln "Hello World". (Implicitly adds newline)"Hello World"..: Equivalent toprint "Hello World". (No trailing newline)!"Error": Equivalent toeprintln "Error". (Output to stderr)!"Error"..: Equivalent toeprint "Error". (Output to stderr, no newline)
You can embed expressions directly into string literals using {} syntax. This works with all printing methods and string shorthands.
var x = 42;
var name = "Zen";
println "Value: {x}, Name: {name}";
"Value: {x}, Name: {name}"; // shorthand println
Zen C supports a shorthand for prompting user input using the ? prefix.
? "Prompt text": Prints the prompt (without newline) and waits for input (reads a line).? "Enter age: " (age): Prints prompt and scans input into the variableage.- Format specifiers are automatically inferred based on variable type.
var age: int;
? "How old are you? " (age);
println "You are {age} years old.";Zen C allows manual memory management with ergonomic aids.
Execute code when the current scope exits.
var f = fopen("file.txt", "r");
defer fclose(f);
Automatically free the variable when scope exits.
autofree var types = malloc(1024);
Implement Drop to run cleanup logic automatically.
impl Drop for MyStruct {
fn drop(mut self) {
free(self.data);
}
}
Define methods on types using impl.
impl Point {
// Static method (constructor convention)
fn new(x: int, y: int) -> Self {
return Point{x: x, y: y};
}
// Instance method
fn dist(self) -> float {
return sqrt(self.x * self.x + self.y * self.y);
}
}
Define shared behavior.
struct Circle { radius: f32; }
trait Drawable {
fn draw(self);
}
impl Drawable for Circle {
fn draw(self) { ... }
}
var circle = Circle{};
var drawable: Drawable = &circle;
Use use to embed other structs. You can either mix them in (flatten fields) or name them (nest fields).
struct Entity { id: int; }
struct Player {
// Mixin (Unnamed): Flattens fields
use Entity; // Adds 'id' to Player directly
name: string;
}
struct Match {
// Composition (Named): Nests fields
use p1: Player; // Accessed via match.p1
use p2: Player; // Accessed via match.p2
}
Type-safe templates for Structs and Functions.
// Generic Struct
struct Box<T> {
item: T;
}
// Generic Function
fn identity<T>(val: T) -> T {
return val;
}
// Multi-parameter Generics
struct Pair<K, V> {
key: K;
value: V;
}
Built on pthreads.
async fn fetch_data() -> string {
// Runs in background
return "Data";
}
fn main() {
var future = fetch_data();
var result = await future;
}
Run code at compile-time to generate source or print messages.
comptime {
// Generate code at compile-time (written to stdout)
println "var build_date = \"2024-01-01\";";
}
println "Build Date: {build_date}";
Embed files as specified types.
// Default (Slice_char)
var data = embed "assets/logo.png";
// Typed Embed
var text = embed "shader.glsl" as string; // Embbed as C-string
var rom = embed "bios.bin" as u8[1024]; // Embed as fixed array
var wav = embed "sound.wav" as u8[]; // Embed as Slice_u8
Import compiler plugins to extend syntax.
import plugin "regex"
var re = regex! { ^[a-z]+$ };
Pass preprocessor macros through to C.
#define MAX_BUFFER 1024
Decorate functions and structs to modify compiler behavior.
| Attribute | Scope | Description |
|---|---|---|
@must_use |
Fn | Warn if return value is ignored. |
@deprecated("msg") |
Fn/Struct | Warn on usage with message. |
@inline |
Fn | Hint compiler to inline. |
@noinline |
Fn | Prevent inlining. |
@packed |
Struct | Remove padding between fields. |
@align(N) |
Struct | Force alignment to N bytes. |
@constructor |
Fn | Run before main. |
@destructor |
Fn | Run after main exits. |
@unused |
Fn/Var | Suppress unused variable warnings. |
@weak |
Fn | Weak symbol linkage. |
@section("name") |
Fn | Place code in specific section. |
@noreturn |
Fn | Function does not return (e.g. exit). |
@derived(...) |
Struct | Auto-implement traits (e.g. Debug). |
Zen C provides first-class support for inline assembly, transpiling directly to GCC-style extended asm.
Write raw assembly within asm blocks. Strings are concatenated automatically.
asm {
"nop"
"mfence"
}
Prevent the compiler from optimizing away assembly that has side effects.
asm volatile {
"rdtsc"
}
Zen C simplifies the complex GCC constraint syntax with named bindings.
// Syntax: : out(var) : in(var) : clobber(reg)
// Uses {var} placeholder syntax for readability
fn add(a: int, b: int) -> int {
var result: int;
asm {
"add {result}, {a}, {b}"
: out(result)
: in(a), in(b)
: clobber("cc")
}
return result;
}
| Type | Syntax | GCC Equivalent |
|---|---|---|
| Output | : out(var) |
"=r"(var) |
| Input | : in(var) |
"r"(var) |
| Clobber | : clobber("rax") |
"rax" |
| Memory | : clobber("memory") |
"memory" |
Note: When using Intel syntax (via
-masm=intel), you must ensure your build is configured correctly (for example,//> cflags: -masm=intel). TCC does not support Intel syntax assembly.
Zen C supports special comments at the top of your source file to configure the build process without needing a complex build system or Makefile.
| Directive | Arguments | Description |
|---|---|---|
//> 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). |
//> 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. |
//> include: ./include
//> lib: ./libs
//> link: -lraylib -lm
//> cflags: -Ofast
//> pkg-config: gtk+-3.0
import "raylib.h"
fn main() { ... }
Zen C is designed to work with most C11 compilers. Some features rely on GNU C extensions, but these often work in other compilers. Use the --cc flag to switch backends.
zc run app.zc --cc clang
zc run app.zc --cc zig| Compiler | Pass Rate | Supported Features | Known Limitations |
|---|---|---|---|
| GCC | 100% | All Features | None. |
| Clang | 100% | All Features | None. |
| Zig | 100% | All Features | None. Uses zig cc as a drop-in C compiler. |
| TCC | ~70% | Basic Syntax, Generics, Traits | No __auto_type, No Intel ASM, No Nested Functions. |
Recommendation: Use GCC, Clang, or Zig for production builds. TCC is excellent for rapid prototyping due to its compilation speed but misses some advanced C extensions Zen C relies on for full feature support.
Zig's zig cc command provides a drop-in replacement for GCC/Clang with excellent cross-compilation support. To use Zig:
# Compile and run a Zen C program with Zig
zc run app.zc --cc zig
# Build the Zen C compiler itself with Zig
make zigZen C can generate C++-compatible code with the --cpp flag, allowing seamless integration with C++ libraries.
# Direct compilation with g++
zc app.zc --cpp
# Or transpile for manual build
zc transpile app.zc --cpp
g++ out.c my_cpp_lib.o -o appInclude C++ headers and use raw blocks for C++ code:
include <vector>
include <iostream>
raw {
std::vector<int> make_vec(int a, int b) {
return {a, b};
}
}
fn main() {
var v = make_vec(1, 2);
raw { std::cout << "Size: " << v.size() << std::endl; }
}
Note: The
--cppflag switches the backend tog++and emits C++-compatible code (usesautoinstead of__auto_type, function overloads instead of_Generic, and explicit casts forvoid*).
Zen C supports GPU programming by transpiling to CUDA C++. This allows you to leverage powerful C++ features (templates, constexpr) within your kernels while maintaining Zen C's ergonomic syntax.
# Direct compilation with nvcc
zc run app.zc --cuda
# Or transpile for manual build
zc transpile app.zc --cuda -o app.cu
nvcc app.cu -o app| Attribute | CUDA Equivalent | Description |
|---|---|---|
@global |
__global__ |
Kernel function (runs on GPU, called from host) |
@device |
__device__ |
Device function (runs on GPU, called from GPU) |
@host |
__host__ |
Host function (explicit CPU-only) |
Zen C provides a clean launch statement for invoking CUDA kernels:
launch kernel_name(args) with {
grid: num_blocks,
block: threads_per_block,
shared_mem: 1024, // Optional
stream: my_stream // Optional
};
This transpiles to: kernel_name<<<grid, block, shared, stream>>>(args);
Use Zen C function syntax with @global and the launch statement:
import "std/cuda.zc"
@global
fn add_kernel(a: float*, b: float*, c: float*, n: int) {
var i = thread_id();
if i < n {
c[i] = a[i] + b[i];
}
}
fn main() {
const N = 1024;
var d_a = cuda_alloc<float>(N);
var d_b = cuda_alloc<float>(N);
var d_c = cuda_alloc<float>(N);
defer cuda_free(d_a);
defer cuda_free(d_b);
defer cuda_free(d_c);
// ... init data ...
launch add_kernel(d_a, d_b, d_c, N) with {
grid: (N + 255) / 256,
block: 256
};
cuda_sync();
}
Zen C provides a standard library for common CUDA operations to reduce raw blocks:
import "std/cuda.zc"
// Memory management
var d_ptr = cuda_alloc<float>(1024);
cuda_copy_to_device(d_ptr, h_ptr, 1024 * sizeof(float));
defer cuda_free(d_ptr);
// Synchronization
cuda_sync();
// Thread Indexing (use inside kernels)
var i = thread_id(); // Global index
var bid = block_id();
var tid = local_id();
Note: The
--cudaflag setsnvccas the compiler and implies--cppmode. Requires the NVIDIA CUDA Toolkit.
We welcome contributions! Whether it's fixing bugs, adding documentation, or proposing new features.
- Fork the Repository: standard GitHub workflow.
- Create a Feature Branch:
git checkout -b feature/NewThing. - Code Guidelines:
- Follow the existing C style.
- Ensure all tests pass:
make test. - Add new tests for your feature in
tests/.
- Submit a Pull Request: Describe your changes clearly.
The test suite is your best friend.
# Run all tests (GCC)
make test
# Run specific test
./zc run tests/test_match.zc
# Run with different compiler
./tests/run_tests.sh --cc clang
./tests/run_tests.sh --cc zig
./tests/run_tests.sh --cc tcc- Parser:
src/parser/- Recursive descent parser. - Codegen:
src/codegen/- Transpiler logic (Zen C -> GNU C/C11). - Standard Library:
std/- Written in Zen C itself.