Chic Programming Language
Expressiveness. Explicit Workflow. Joy of Programming.
In development · Downloads are not yet available
Familiar Syntax
Chic brings the clarity and directness of C-style systems programming into the modern era, cleaner syntax, familiar foundations. Evolution, not revolution. Explicit pointers, visible control flow, and just enough modern convenience to remind you why coding close to the machine was fun in the first place.
Room to Express
Write reusable code with generics, add methods to existing types, and express decisions with pattern matching. Chic brings these tools together to make everyday systems programming clearer, from working with collections to building your own abstractions.
Explicit Control
You decide how memory is allocated and freed. Chic gives you a range of allocator strategies to match your intent, arena allocators for bulk lifetimes, scratch allocators for temporary work, and a general heap allocator when you need it. No hidden allocations, no surprises.
See Chic in Action
Clean, expressive syntax with powerful type inference and zero-cost generics.
// Define a struct with two fields
Rectangle : struct {
width : i32
height : i32
}
// Define a function that uses the struct
area : func(rectangle : Rectangle) -> i32 {
return rectangle.width * rectangle.height
}
main : func() -> i32 {
rectangle : Rectangle = { .width = 10, .height = 4 }
result := area(rectangle) // 40
return 0
} Point : struct {
x : f32
y : f32
}
@extension
direction : func(a : ^Point, b : ^Point) -> Point {
return Point {
.x = b.x - a.x,
.y = b.y - a.y
}
}
p0 : Point { .x = 0.0, .y = 0.0 }
p1 : Point { .x = 10.0, .y = 10.0 }
// with extension
dir0 := p0.direction(&p1)
//without extension. still works
dir1 := direction(&p0, &p1) // Fixed array with explicit length
numbers : [3]i32 = {10, 20, 30}
// Length inferred from the initializer
bytes : [?]u8 = {1, 2, 3}
// Canonical array composite literal
more := [?]u8{4, 5, 6}
matrix : [2][3]i32 = {
{1, 2, 3},
{4, 5, 6}
} arr : [5]i32 = {1, 2, 3, 4, 5}
s : []i32 = arr[:] // Full array slice
head : []i32 = arr[:3] // First 3 elements
tail : []i32 = arr[2:] // From index 2
mid : []i32 = arr[1:4] // Indices 1..3
// Iterate with for-in
for x in s {
println("%d", x)
} // A generic function
max : func<T>(a : T, b : T) -> T {
if (a > b) {
return a
}
return b
}
// A generic struct
Pair : struct<T> {
first : T
second : T
}
main : func() -> i32 {
integers : Pair<i32> = { .first = 10, .second = 20 }
decimals : Pair<f64> = { .first = 1.5, .second = 2.5 }
// The same function works with both types
biggest := max(integers.first, integers.second) // 20
decimal := max(decimals.first, decimals.second) // 2.5
return 0
} Color : enum {
Red,
Green,
Blue
}
color := Color.Red
switch (color) {
case Color.Red: println("Red")
case Color.Green: println("Green")
default: println("Other")
}
// Range matching (inclusive upper bounds)
score := 85
grade : string = match (score) {
0..=59 => "F",
60..=79 => "C",
80..=100 => "A",
else => "Invalid score",
} // grade is "A" import core = core
import memory = core.memory
main : func() -> i32 {
// Create one arena block of 4096 bytes
arena := memory.ArenaAllocator(4096, 1)
if !arena return 1
defer release arena
defer arena.destroy(); // Release backing storage on exit
// Allocate space for four integers
[data, status] := arena.allocate(4 * sizeof(i32))
if status != MemoryStatus.Ok return 2
values := cast<^i32>(data)
values[0] = 10
values[1] = 20
values[2] = 30
values[3] = 40
// Individual free is a no-op for an arena
arena.free(data)
// Reclaim all allocations together for reuse
arena.clear()
// Do not use values after clearing the arena
return 0
}