Skip to content
/ Zen-C Public
forked from z-libs/Zen-C

Write like a high-level language, run like C.

License

Notifications You must be signed in to change notification settings

mmpataki/Zen-C

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

110 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Zen C

Modern Ergonomics. Zero Overhead. Pure C.

Build Status License Version Platform

Write like a high-level language, run like C.


Overview

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.

Community

Join the discussion, share demos, ask questions, or report bugs in the official Zen C Discord server!


Quick Start

Installation

git clone https://github.com/z-libs/Zen-C.git
cd Zen-C
make
sudo make install

Usage

# Compile and run
zc run hello.zc

# Build executable
zc build hello.zc -o hello

# Interactive Shell
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.

export ZC_ROOT=/path/to/Zen-C

Language Reference

1. Variables and Constants

Zen 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

2. Primitive Types

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

3. Aggregate Types

Arrays

Fixed-size arrays with value semantics.

var ints: int[5] = {1, 2, 3, 4, 5};
var zeros: [int; 5]; // Zero-initialized

Tuples

Group multiple values together.

var pair = (1, "Hello");
var x = pair.0;
var s = pair.1;

Structs

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;
}

Enums

Tagged unions (Sum types) capable of holding data.

enum Shape {
    Circle(float),      // Holds radius
    Rect(float, float), // Holds width, height
    Point               // No data
}

Unions

Standard C unions (unsafe access).

union Data {
    i: int;
    f: float;
}

#### Type Aliases
Create a new name for an existing type.
```zc
alias ID = int;
alias PointMap = Map<string, Point>;

4. Functions & Lambdas

Functions

fn add(a: int, b: int) -> int {
    return a + b;
}

// Named arguments supported in calls
add(a: 10, b: 20);

Lambdas (Closures)

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

5. Control Flow

Conditionals

if x > 10 {
    print("Large");
} else if x > 5 {
    print("Medium");
} else {
    print("Small");
}

// Ternary
var y = x > 10 ? 1 : 0;

Pattern Matching

Powerful alternative to switch.

match val {
    1 => print("One"),
    2 | 3 => print("Two or Three"),
    4..10 => print("Range"),
    _ => print("Other")
}

// Destructuring Enums
match shape {
    Circle(r) => print(f"Radius: {r}"),
    Rect(w, h) => print(f"Area: {w*h}"),
    Point => print("Point")
}

Loops

// Range
for i in 0..10 { ... }      // Exclusive (0 to 9)
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
repeat 5 { ... }

Advanced Control

// Guard: Execute else and return if condition is false
guard ptr != NULL else { return; }

// Unless: If not true
unless is_valid { return; }

6. Operators

Operator Description Function Mapping
+, -, *, /, % Arithmetic add, sub, mul, div, rem
==, !=, <, > Comparison eq, neq, lt, gt
[] Indexing get, set
|> Pipeline (x |> f(y) -> f(x, y)) -
?? Null Coalescing (val ?? default) -
??= Null Assignment (val ??= init) -
?. Safe Navigation (ptr?.field) -
? Try Operator (res? returns error if present) -

7. Printing and String Interpolation

Zen C provides versatile options for printing to the console, including keywords and concise shorthands.

Keywords

  • print "text": Prints to stdout without a trailing newline.
  • println "text": Prints to stdout with a trailing newline.
  • eprint "text": Prints to stderr without a trailing newline.
  • eprintln "text": Prints to stderr with a trailing newline.

Shorthands

Zen C allows you to use string literals directly as statements for quick printing:

  • "Hello World": Equivalent to println "Hello World". (Implicitly adds newline)
  • "Hello World"..: Equivalent to print "Hello World". (No trailing newline)
  • !"Error": Equivalent to eprintln "Error". (Output to stderr)
  • !"Error"..: Equivalent to eprint "Error". (Output to stderr, no newline)

String Interpolation (F-strings)

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

Input Prompts (?)

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 variable age.
    • Format specifiers are automatically inferred based on variable type.
var age = "How old are you? ";
println "You are {age} years old.";

8. Memory Management

Zen C allows manual memory management with ergonomic aids.

Defer

Execute code when the current scope exits.

var f = fopen("file.txt", "r");
defer fclose(f);

Autofree

Automatically free the variable when scope exits.

autofree var types = malloc(1024);

RAII / Drop Trait

Implement Drop to run cleanup logic automatically.

impl Drop for MyStruct {
    fn drop(mut self) {
        free(self.data);
    }
}

9. Object Oriented Programming

Methods

Define methods on types using impl.

impl Point {
    // Static method (constructor convention)
    fn new(x: int, y: int) -> Point {
        return Point{x: x, y: y};
    }

    // Instance method
    fn dist(self) -> float {
        return sqrt(self.x * self.x + self.y * self.y);
    }
}

Traits

Define shared behavior.

trait Drawable {
    fn draw(self);
}

impl Drawable for Circle {
    fn draw(self) { ... }
}

Composition

Use use to mixin fields from another struct.

struct Entity { id: int; }
struct Player {
    use Entity; // Adds 'id' field
    name: string;
}

10. Generics

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;
}

11. Concurrency (Async/Await)

Built on pthreads.

async fn fetch_data() -> string {
    // Runs in background
    return "Data";
}

fn main() {
    var future = fetch_data();
    var result = await future;
}

12. Metaprogramming

Comptime

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

Embed files as byte arrays.

var png = embed "assets/logo.png";

Plugins

Import compiler plugins to extend syntax.

import plugin "regex"
var re = regex! { ^[a-z]+$ };

Generic C Macros

Pass preprocessor macros through to C.

#define MAX_BUFFER 1024

13. Attributes

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).

14. Inline Assembly

Zen C provides first-class support for inline assembly, transpiling directly to GCC-style extended asm.

Basic Usage

Write raw assembly within asm blocks. Strings are concatenated automatically.

asm {
    "nop"
    "mfence"
}

Volatile

Prevent the compiler from optimizing away assembly that has side effects.

asm volatile {
    "rdtsc"
}

Named Constraints

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.

15. Build Directives

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.

Examples

//> include: ./include
//> lib: ./libs
//> link: -lraylib -lm
//> cflags: -Ofast
//> pkg-config: gtk+-3.0

import "raylib.h"

fn main() { ... }

Compiler Support & Compatibility

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

Test Suite Status

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.

Building with Zig

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 zig

C++ Interop

Zen 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 app

Using C++ in Zen C

Include 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 --cpp flag switches the backend to g++ and emits C++-compatible code (uses auto instead of __auto_type, function overloads instead of _Generic, and explicit casts for void*).


Contributing

We welcome contributions! Whether it's fixing bugs, adding documentation, or proposing new features.

How to Contribute

  1. Fork the Repository: standard GitHub workflow.
  2. Create a Feature Branch: git checkout -b feature/NewThing.
  3. Code Guidelines:
    • Follow the existing C style.
    • Ensure all tests pass: make test.
    • Add new tests for your feature in tests/.
  4. Submit a Pull Request: Describe your changes clearly.

Running Tests

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

Extending the Compiler

  • Parser: src/parser/ - Recursive descent parser.
  • Codegen: src/codegen/ - Transpiler logic (Zen C -> GNU C/C11).
  • Standard Library: std/ - Written in Zen C itself.

About

Write like a high-level language, run like C.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C 99.2%
  • Other 0.8%