Пишите как на высокоуровневом языке, запускайте как Си.
Zen C — это современный язык системного программирования, который компилируется в человекочитаемом GNU C/C11. Он предоставляет богатый набор возможностей, включая вывод типов, сопоставление с паттернами, генерику, трейты, async/await и ручное управление памятью с возможностями RAII, при этом поддерживая 100% совместимость с ABI Си.
Приглашаем вас присоединиться к нам на официальном Discord-сервере Zen C! Здесь можно обсуждать проект, делиться примерами, задавать вопросы и сообщать об ошибках.
- Discord: Присоединиться
- RFC: Предложить функции
Проект Zen C состоит из нескольких репозиториев. Ниже приведены основные из них:
| Репозиторий | Описание | Статус |
|---|---|---|
| zenc | Ядро компилятора Zen C (zc), CLI и стандартная библиотека. |
Активная разработка |
| docs | Официальная техническая документация и спецификация языка. | Активен |
| rfcs | Репозиторий запросов на комментарии (RFC). Формируйте будущее языка. | Активен |
| vscode-zenc | Официальное расширение VS Code (подсветка синтаксиса, сниппеты). | Alpha |
| www | Исходный код zenc-lang.org. |
Активен |
| awesome-zenc | Курируемый список отличных примеров Zen C. | Растет |
| zenc.vim | Официальный плагин для Vim/Neovim (Синтаксис, Отступы). | Активен |
Посмотрите на эти проекты, созданные на Zen C:
- ZC-pong-3ds: Клон Pong для Nintendo 3DS.
- zen-c-parin: Базовый пример использования Zen C с Parin.
- almond: Минималистичный веб-браузер, написанный на Zen C.
git clone https://github.com/zenc-lang/zenc.git
cd Zen-C
make clean # удалить старые файлы сборки
make
sudo make installZen C имеет полную нативную поддержку Windows (x86_64). Вы можете выполнить сборку, используя прилагаемый пакетный скрипт с GCC (MinGW):
build.batЭто соберет компилятор (zc.exe). Сетевые операции, операции с файловой системой и процессами полностью поддерживаются через уровень абстракции платформы (PAL).
Кроме того, вы можете использовать make, если у вас есть Unix-подобная среда (MSYS2, Cygwin, git-bash).
Zen C можно скомпилировать как Actually Portable Executable (APE) с помощью Cosmopolitan Libc. Это создаёт один исполняемый файл (.com), работающий нативно на Linux, macOS, Windows, FreeBSD, OpenBSD и NetBSD на архитектурах x86_64 и aarch64.
Требования:
- Набор инструментов
cosmocc(должен быть в PATH)
Сборка и установка:
make ape
sudo env "PATH=$PATH" make install-apeСозданные файлы:
out/bin/zc.com: Портативный компилятор Zen-C с встроенной стандартной библиотекой.out/bin/zc-boot.com: Установщик для создания новых проектов Zen-C.
Использование:
# Запустить на любой поддерживаемой ОС
./out/bin/zc.com build hello.zc -o hello# Компилировать и запустить
zc run hello.zc
# Создать исполняемый файл
zc build hello.zc -o hello
# Интерактивная оболочка
zc repl
# Показать Дзэн факты
zc build hello.zc --zenУстановите ZC_ROOT для указания пути к стандартной библиотеке (для импортов типа import "std/vec.zc"). Это позволяет запускать zc из любого каталога.
export ZC_ROOT=/path/to/Zen-CZen C различает константы времени компиляции и переменные времени выполнения.
Значения, которые существуют только во время компиляции (встраиваются в код). Используйте их для размеров массивов, фиксированной конфигурации и магических чисел.
def MAX_SIZE = 1024;
let buffer: char[MAX_SIZE]; // Допустимый размер массива
Расположения в памяти. Могут быть изменяемыми или только для чтения (const).
let x = 10; // Переменная
x = 20; // ОК
let y: const int = 10; // Только для чтения (квалификатор типа)
// y = 20; // Ошибка: невозможно присвоить const переменной
Tip
Вывод типов: Zen C автоматически выводит типы для инициализированных переменных. Он компилируется в C23 auto на поддерживаемых компиляторах или, в противном случае, с помощью GCC __auto_type.
| Тип | Эквивалент Си | Описание |
|---|---|---|
int, uint |
int32_t, uint32_t |
32-битовое целое число со знаком/без знака |
c_char, c_uchar |
char, unsigned char |
C char / unsigned char (Interop) |
c_short, c_ushort |
short, unsigned short |
C short / unsigned short (Interop) |
c_int, c_uint |
int, unsigned int |
C int / unsigned int (Interop) |
c_long, c_ulong |
long, unsigned long |
C long / unsigned long (Interop) |
c_long_long, c_ulong_long |
long long, unsigned long long |
C long long / unsigned long long (Interop) |
I8 .. I128 или i8 .. i128 |
int8_t .. __int128_t |
Целые числа со знаком фиксированной ширины |
U8 .. U128 или u8 .. u128 |
uint8_t .. __uint128_t |
Целые числа без знака фиксированной ширины |
isize, usize |
ptrdiff_t, size_t |
Целые числа размером с указатель |
byte |
uint8_t |
Псевдоним для U8 |
F32, F64 или f32, f64 |
float, double |
Числа с плавающей точкой |
bool |
bool |
true или false |
char |
char |
Одиночный символ |
string |
char* |
Си-строка (с нулевым завершением) |
U0, u0, void |
void |
Пустой тип |
iN (например, i256) |
_BitInt(N) |
Целое число со знаком произвольной ширины (C23) |
uN (например, u42) |
unsigned _BitInt(N) |
Целое число без знака произвольной ширины (C23) |
- Целые: Десятичные (
123), Шестнадцатеричные (0xFF), Восьмеричные (0o755), Двоичные (0b1011).- Примечание: Числа с ведущими нулями считаются десятичными (
0123это123), в отличие от C. - Примечание: Числа могут содержать подчеркивания для читаемости (
1_000_000,0b_1111_0000).
- Примечание: Числа с ведущими нулями считаются десятичными (
- С плавающей точкой: Стандартные (
3.14), Научные (1e-5,1.2E3). Числа с плавающей точкой также поддерживают подчеркивания (3_14.15_92).
Important
Лучшие практики для портативного кода
- Используйте Портативные типы (
int,uint,i64,u8и т.д.) для чистой логики Zen C.intгарантированно имеет "ширину" в 32 бита и является знаковым на всех архитектурах. - Используйте Типы Interop C (
c_int,c_char,c_long, ``c_ulong``, ``c_long_long``, ``c_ulong_long``) **только** при взаимодействии с библиотеками C (FFI). Их размер зависит от платформы и компилятора C (например, размерc_long` отличается между Windows и Linux). - Используйте
isizeиusizeдля индексирования массивов и арифметики указателей на память.
Массивы фиксированного размера с семантикой значения.
def SIZE = 5;
let ints: int[SIZE] = [1, 2, 3, 4, 5];
let zeros: [int; SIZE]; // Инициализировано нулями
Группируйте несколько значений вместе, получайте доступ к элементам по индексу.
let pair = (1, "Hello");
let x = pair.0; // 1
let s = pair.1; // "Hello"
Множественные возвращаемые значения
Функции могут возвращать кортежи для предоставления нескольких результатов:
fn add_and_subtract(a: int, b: int) -> (int, int) {
return (a + b, a - b);
}
let result = add_and_subtract(3, 2);
let sum = result.0; // 5
let diff = result.1; // 1
Деструктуризация
Кортежи могут быть деструктурированы прямо в переменные:
let (sum, diff) = add_and_subtract(3, 2);
// sum = 5, diff = 1
Типизированная деструктуризация позволяет указывать явные аннотации типов:
let (a: string, b: u8) = ("hello", 42);
let (x, y: i32) = (1, 2); // Смешанный: x выводится, y явный
Структуры данных с опциональными битовыми полями.
struct Point {
x: int;
y: int;
}
// Инициализация структуры
let p = Point { x: 10, y: 20 };
// Битфилды
struct Flags {
valid: U8 : 1;
mode: U8 : 3;
}
Note
Структуры используют Move Semantics по умолчанию. Доступ к полям можно получить через . даже на указателях (Auto-Dereference).
Вы можете определить структуру как opaque, чтобы ограничить доступ к её полям только определяющим модулем, при этом позволяя структуре размещаться на стеке (размер известен).
// В user.zc
opaque struct User {
id: int;
name: string;
}
fn new_user(name: string) -> User {
return User{id: 1, name: name}; // ОК: внутри модуля
}
// В main.zc
import "user.zc";
fn main() {
let u = new_user("Alice");
// let id = u.id; // Ошибка: нет доступа к приватному полю 'id'
}
Теговые объединения (Sum types), способные содержать данные.
enum Shape {
Circle(float), // Держит радиус
Rect(float, float), // Держит ширину, высоту
Point // Нет данных
}
Стандартные C объединения (небезопасно).
union Data {
i: int;
f: float;
}
Нативные SIMD-векторные типы с использованием расширений GCC/Clang. Аннотируйте структуру с помощью @vector(N) для определения вектора из N элементов.
import "std/simd.zc";
fn main() {
let a = f32x4{v: 1.0}; // Широковещание: {1.0, 1.0, 1.0, 1.0}
let b = f32x4{1.0, 2.0, 3.0, 4.0}; // Поэлементная инициализация
let c = a + b; // Поэлементное сложение
let x = c[0]; // Доступ к элементу (float)
}
Арифметические (+, -, *, /) и побитовые (&, |, ^) операторы работают поэлементно. См. std/simd.zc для предопределённых типов.
Создайте новое имя для существующего типа.
alias ID = int;
alias PointMap = Map<string, Point>
alias OpFunc = fn(int, int) -> int
Примечание: Завершающая точка с запятой необязательна для псевдонимов типов.
Вы можете определить псевдоним типа как opaque, чтобы создать новый тип, который отличается от своего базового типа вне определяющего модуля. Это обеспечивает сильную инкапсуляцию и безопасность типов без накладных расходов времени выполнения структуры-оболочки.
// В library.zc
opaque alias Handle = int;
fn make_handle(v: int) -> Handle {
return v; // Неявное преобразование разрешено внутри модуля
}
// В main.zc
import "library.zc";
fn main() {
let h: Handle = make_handle(42);
// let i: int = h; // Ошибка: проверка типа не прошла
// let h2: Handle = 10; // Ошибка: проверка типа не прошла
}
fn add(a: int, b: int) -> int {
return a + b;
}
// Именованные аргументы поддерживаются при вызовах
add(a: 10, b: 20);
Note
Именованные аргументы должны строго следовать порядку определённых параметров. add(b: 20, a: 10) недопустимо.
Аргументы функции могут быть помечены как const для обеспечения семантики только для чтения. Это квалификатор типа, а не манифест-константа.
fn print_val(v: const int) {
// v = 10; // Ошибка: невозможно присвоить const переменной
println "{v}";
}
Функции могут определять значения по умолчанию для завершающих аргументов. Это могут быть литералы, выражения или допустимый код Zen C (например, конструкторы структур).
// Простое значение по умолчанию
fn increment(val: int, amount: int = 1) -> int {
return val + amount;
}
// Значение по умолчанию как выражение (вычисляется в месте вызова)
fn offset(val: int, pad: int = 10 * 2) -> int {
return val + pad;
}
// Значение по умолчанию для структуры
struct Config { debug: bool; }
fn init(cfg: Config = Config { debug: true }) {
if cfg.debug { println "Debug Mode"; }
}
fn main() {
increment(10); // 11
offset(5); // 25
init(); // Выводит "Debug Mode"
}
Анонимные функции, которые могут захватывать своё окружение.
let factor = 2;
let udvoit = x -> x * factor; // Синтаксис стрелок
let full = fn(x: int) -> int { return x * factor; }; // Блочный синтаксис
// Захват по ссылке (Блочный синтаксис)
let val = 10;
let modify = fn[&]() { val += 1; };
modify(); // val теперь 11
// Захват по ссылке (Стрелочный синтаксис)
let modify_arrow = [&] x -> val += x;
modify_arrow(5); // val теперь 16
// Захват по ссылке (Стрелочный синтаксис с несколькими аргументами)
let sum_into = [&] (a, b) -> val += (a + b);
sum_into(2, 2); // val теперь 20
// Захват по значению (По умолчанию)
let original = 100;
let implicit = x -> original + x; // Неявный захват по значению (без скобок)
let explicit = [=] x -> original + x; // Явный захват по значению
// let fail = x -> original += x; // Ошибка: нельзя присвоить захваченному значению
Zen C поддерживает сырые (Raw) указатели на функции Си, используя синтаксис fn*. Это позволяет беспрепятственно взаимодействовать с библиотеками Си, которые ожидают указателей на функции без накладных расходов замыкания.
// Функция, принимающая сырой указатель на функцию
fn set_callback(cb: fn*(int)) {
cb(42);
}
// Функция, возвращающая сырой указатель на функцию
fn get_callback() -> fn*(int) {
return my_handler;
}
// Поддерживаются указатели на указатели функций (fn**)
let pptr: fn**(int) = &ptr;
Функции могут принимать переменное число аргументов, используя ... и тип va_list.
fn log(lvl: int, fmt: char*, ...) {
let ap: va_list;
va_start(ap, fmt);
vprintf(fmt, ap); // Используем C stdio
va_end(ap);
}
if x > 10 {
print("Large");
} else if x > 5 {
print("Medium");
} else {
print("Small");
}
// Тернарно
let y = x > 10 ? 1 : 0;
// If-выражение (для сложных условий)
let категория = if (x > 100) { "огромный" } else if (x > 10) { "большой" } else { "маленький" };
Мощная альтернатива switch.
match val {
1 => { print "One" },
2 || 3 => { print "Two or Three" }, // ИЛИ с ||
4 or 5 => { print "Four or Five" }, // ИЛИ с 'or'
6, 7, 8 => { print "Six to Eight" }, // ИЛИ с запятой
10 .. 15 => { print "10 to 14" }, // Исключающий диапазон (устаревший)
10 ..< 15 => { print "10 to 14" }, // Исключающий диапазон (явно)
20 ..= 25 => { print "20 to 25" }, // Включающий диапазон
_ => { print "Other" },
}
// Деструктуризация перечислений
match shape {
Shape::Circle(r) => { println "Radius: {r}" },
Shape::Rect(w, h) => { println "Area: {w*h}" },
Shape::Point => { println "Point" },
}
Чтобы проверить значение без передачи владения (перемещения), используйте ключевое слово ref в паттерне. Это необходимо для типов, которые реализуют Move Semantics (такие как Option, Result, структуры без Copy).
let opt = Some(NonCopyVal{...});
match opt {
Some(ref x) => {
// 'x' - это указатель на значение внутри 'opt'
// 'opt' НЕ перемещается/не потребляется здесь
println "{x.field}";
},
None => {}
}
// Диапазон
for i in 0..10 { ... } // Исключительно (0 до 9)
for i in 0..<10 { ... } // Исключительно (явно)
for i in 0..=10 { ... } // Включительно (0 до 10)
for i in 0..10 step 2 { ... }
for i in 10..0 step -1 { ... } // Descending loop
// Итератор (Vec или пользовательский Iterable)
for item in vec { ... }
// Нумерованный: получить индекс и значение
for i, val in arr { ... } // i = 0, 1, 2, ...
for i, val in 0..10 step 2 { ... } // i = 0, 1, 2, ...; val = 0, 2, 4, ...
// Итерация по массивам фиксированного размера напрямую
let arr: int[5] = [1, 2, 3, 4, 5];
for val in arr {
// val - это int
println "{val}";
}
// While
while x < 10 { ... }
// Бесконечный цикл с меткой
outer: loop {
if done { break outer; }
}
// Повторить N раз
for _ in 0..5 { ... }
// Guard: выполнить else и вернуться, если условие ложно
guard ptr != NULL else { return; }
// Unless: если не верно
unless is_valid { return; }
Zen C поддерживает перегрузку операторов для пользовательских структур путём реализации специфических имён методов.
| Категория | Оператор | Имя метода |
|---|---|---|
| Арифметика | +, -, *, /, %, ** |
add, sub, mul, div, rem, pow |
| Сравнение | ==, != |
eq, neq |
<, >, <=, >= |
lt, gt, le, ge |
|
| Побитовые | &, |, ^ |
bitand, bitor, bitxor |
<<, >> |
shl, shr |
|
| Унарные | - |
neg |
! |
not |
|
~ |
bitnot |
|
| Индекс | a[i] |
get(a, i) |
a[i, j] |
get(a, i, j) |
|
a[i] = v |
set(a, i, v) |
Note
Примечание о равенстве строк:
string == stringвыполняет сравнение значений (эквивалентstrcmp).char* == char*выполняет сравнение указателей (проверяет адреса памяти).- Смешанные сравнения (например
string == char*) по умолчанию используют сравнение указателей.
Пример:
impl Point {
fn add(self, other: Point) -> Point {
return Point{x: self.x + other.x, y: self.y + other.y};
}
}
let p3 = p1 + p2; // Вызывает p1.add(p2)
Пример мульти-индекса:
struct Matrix {
data: int[9];
}
impl Matrix {
fn get(self, row: int, col: int) -> int {
return self.data[row * 3 + col];
}
}
let m = Matrix{data: [1,0,0, 0,1,0, 0,0,1]};
let val = m[1, 2]; // Вызывает Matrix.get(m, 1, 2)
| \|> | Pipeline | x \|> f(y) раскрывается в f(x, y) |
| ?? | Null Coalescing | val ?? default возвращает default, если val равно NULL (указатели) |
| ??= | Null Assignment | val ??= init присваивает, если val равно NULL |
| ?. | Safe Navigation | ptr?.field получает доступ к полю только если ptr не равно NULL |
| ? | Try Operator | res? возвращает ошибку, если она присутствует (типы Result/Option) |
Auto-Dereference:
Доступ к полям указателя (ptr.field) и вызовы методов (ptr.method()) автоматически разыменовывают указатель, эквивалентно (*ptr).field.
Zen C предоставляет универсальные опции для печати на консоль, включая ключевые слова и краткие сокращения.
| Ключевое слово | Описание |
|---|---|
print "text" |
Выводит на stdout без перевода строки. |
println "text" |
Выводит на stdout с переводом строки. |
eprint "text" |
Выводит на stderr без перевода строки. |
eprintln "text" |
Выводит на stderr с переводом строки. |
Zen C позволяет использовать строковые литералы непосредственно как операторы для быстрой печати:
| Синтаксис | Эквивалент | Описание |
|---|---|---|
"Hello World" |
println "Hello World" |
Неявно добавляет перевод строки. |
"Hello World".. |
print "Hello World" |
Без перевода строки. |
!"Error" |
eprintln "Error" |
Вывод на stderr. |
!"Error".. |
eprint "Error" |
Вывод на stderr, без перевода строки. |
Вы можете встраивать выражения непосредственно в строковые литералы, используя синтаксис {}. Это работает со всеми методами вывода и сокращениями строк.
Интерполяция строк в Zen C неявная: если ваша строка содержит {...}, она будет автоматически обработана как интерполированная строка. Вы также можете явно использовать префикс f (например, f"..."), чтобы принудительно задать семантику интерполяции.
let x = 42;
let name = "Zen";
println "Значение: {x}, Имя: {name}";
"Значение: {x}, Имя: {name}"; // сокращение println
Экранирование фигурных скобок: Используйте {{ для вывода литеральной { и }} для литеральной }:
let json = "JSON: {{\"ключ\": \"значение\"}}";
// Вывод: JSON: {"ключ": "значение"}
Сырые строки (Raw Strings): Чтобы определить строку, в которой интерполяция и escape-последовательности полностью игнорируются, используйте префикс r (например, r"..."):
let regex = r"\w+"; // Содержит ровно \ w +
let raw_json = r'{"ключ": "значение"}'; // Экранирование фигурных скобок не требуется
Zen C поддерживает блоки сырых многострочных строк, используя разделитель """. Это чрезвычайно полезно для написания встроенных языков (GLSL, HTML) или генерации C-кода в блоках comptime без необходимости ручного экранирования переносов строк и внутренних кавычек.
Как и стандартные строки, многострочные строки поддерживают неявную интерполяцию. Вы также можете явно использовать для них префикс:
f"""...""": Явно помечает их как блок интерполированной строки.r"""...""": Явно помечает их как блок сырой строки (без интерполяции, без escape-последовательностей).
let prompt = """
Пожалуйста, введите ваше имя:
Введите "exit" для отмены.
""";
let world = "мир";
let script = """
fn hello() {
println "привет, {world}!";
}
""";
let pure_raw = r"""
Здесь {скобки} - это просто текст, а \n - буквально слэш-n.
""";
Zen C поддерживает сокращение для запроса ввода пользователя с использованием префикса ?.
? "Prompt text": Выводит подсказку (без перевода строки) и ожидает ввода (читает строку).? "Enter age: " (age): Выводит подсказку и сканирует ввод в переменнуюage.- Спецификаторы формата автоматически выводятся на основе типа переменной.
let age: int;
? "How old are you? " (age);
println "You are {age} years old.";
Zen C позволяет ручное управление памятью с удобными помощниками.
Выполните код при выходе из текущей области видимости. Операторы Defer выполняются в порядке LIFO (последний вошёл, первый вышел).
let f = fopen("file.txt", "r");
defer fclose(f);
Чтобы предотвратить неопределённое поведение, операторы управления потоком (
return,break,continue,goto) недопустимы внутри блокаdefer.
Автоматически освобождает переменную при выходе из области видимости.
autofree let types = malloc(1024);
Zen C рассматривает типы с деструкторами (такие как File, Vec или выделённые malloc указатели) как Ресурсы. Чтобы предотвратить ошибки двойного освобождения, ресурсы не могут быть неявно дублированы.
- Move по умолчанию: Присвоение переменной ресурса передаёт владение. Исходная переменная становится недействительной (Moved).
- Типы Copy: Типы без деструкторов могут выбрать поведение
Copy, делая присвоение дублированием.
Диагностика и философия: Если вы видите ошибку "Use of moved value", компилятор говорит вам: "Этот тип владеет ресурсом (типа память или дескриптор) и слепое копирование небезопасно."
Контраст: В отличие от C/C++, Zen C не неявно дублирует значения, владеющие ресурсами.
Аргументы функции: Передача значения функции следует тем же правилам, что и присвоение: ресурсы перемещаются, если только они не передаются по ссылке.
fn process(r: Resource) { ... } // 'r' перемещается в функцию
fn peek(r: Resource*) { ... } // 'r' заимствуется (ссылка)
Явное клонирование: Если вы действительно хотите две копии ресурса, сделайте это явным:
let b = a.clone(); // Вызывает метод 'clone' из трейта Clone
Opt-in Copy (типы значений): Для малых типов без деструкторов:
struct Point { x: int; y: int; }
impl Copy for Point {} // Opt-in для неявного дублирования
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = p1; // Скопирована. p1 остаётся допустимой.
}
Реализуйте Drop для автоматического запуска логики очистки.
impl Drop for MyStruct {
fn drop(self) {
self.free();
}
}
Определяйте методы на типах с помощью impl.
impl Point {
// Статический метод (соглашение конструктора)
fn new(x: int, y: int) -> Self {
return Point{x: x, y: y};
}
// Метод экземпляра
fn dist(self) -> float {
return sqrt(self.x * self.x + self.y * self.y);
}
}
Сокращение self: В методах с параметром self можно использовать .поле как сокращение для self.поле:
impl Point {
fn dist(self) -> float {
return sqrt(.x * .x + .y * .y); // Эквивалентно self.x, self.y
}
}
Zen C позволяет определить методы на примитивных типах (например, int, bool, и т.д.) с помощью той же синтаксиса impl.
impl int {
fn abs(self) -> int {
return *self < 0 ? -(*self) : *self;
}
}
let x = -10;
let y = x.abs(); // 10
let z = (-5).abs(); // 5 (Literals supported)
Определяйте общее поведение.
struct Circle { radius: f32; }
trait Drawable {
fn draw(self);
}
impl Drawable for Circle {
fn draw(self) { ... }
}
let circle = Circle{};
let drawable: Drawable = &circle;
Zen C включает стандартные трейты, которые интегрируются с синтаксисом языка.
Iterable
Реализуйте Iterable<T> для включения циклов for-in для ваших пользовательских типов.
import "std/iter.zc"
// Определите итератор
struct MyIter {
curr: int;
stop: int;
}
impl MyIter {
fn next(self) -> Option<int> {
if self.curr < self.stop {
self.curr += 1;
return Option<int>::Some(self.curr - 1);
}
return Option<int>::None();
}
}
// Реализуйте Iterable
impl MyRange {
fn iterator(self) -> MyIter {
return MyIter{curr: self.start, stop: self.end};
}
}
// Используйте в цикле
for i in my_range {
println "{i}";
}
Drop
Реализуйте Drop для определения деструктора, который запускается при выходе объекта из области видимости (RAII).
import "std/mem.zc"
struct Resource {
ptr: void*;
}
impl Drop for Resource {
fn drop(self) {
if self.ptr != NULL {
free(self.ptr);
}
}
}
Note
Если переменная перемещена, drop не вызывается для оригинала. Подробнее в семантике ресурсов.
Copy
Маркерный трейт для выбора поведения Copy (неявное дублирование) вместо Move семантики. Используется через @derive(Copy).
Caution
Типы, которые реализуют Copy, не должны определять деструктор (Drop).
@derive(Copy)
struct Point { x: int; y: int; }
fn main() {
let p1 = Point{x: 1, y: 2};
let p2 = p1; // Скопирована! p1 остаётся допустимой.
}
Clone
Реализуйте Clone для разрешения явного дублирования типов, владеющих ресурсами.
import "std/mem.zc"
struct MyBox { val: int; }
impl Clone for MyBox {
fn clone(self) -> MyBox {
return MyBox{val: self.val};
}
}
fn main() {
let b1 = MyBox{val: 42};
let b2 = b1.clone(); // Явное копирование
}
Используйте use для встраивания других структур. Вы можете либо смешивать их (уплощение полей), либо давать им названия (вложение полей).
struct Entity { id: int; }
struct Player {
// Миксин (без имени): уплощает поля
use Entity; // добавляет 'id' в Player напрямую
name: string;
}
struct Match {
// Композиция (с именем): вложение полей
use p1: Player; // доступ через match.p1
use p2: Player; // доступ через match.p2
}
Типобезопасные шаблоны для структур и функций.
// Обобщённая структура
struct Box<T> {
item: T;
}
// Обобщённая функция
fn identity<T>(val: T) -> T {
return val;
}
// Обобщения с несколькими параметрами
struct Pair<K, V> {
key: K;
value: V;
}
Встроен на основе pthreads.
async fn fetch_data() -> string {
// Запускается в фоне
return "Data";
}
fn main() {
let future = fetch_data();
let result = await future;
}
Выполняйте код во время компиляции для генерации исходного кода или вывода сообщений.
comptime {
// Генерирует код во время компиляции (выводится в stdout)
println "let build_date = \"2024-01-01\";";
}
println "Build Date: {build_date}";
Вспомогательные функции
Специальные функции, доступные внутри блоков comptime:
yield(str)- Явно выводит сгенерированный код (альтернативаprintf)compile_error(msg)- Прерывает компиляцию с фатальной ошибкойcompile_warn(msg)- Выводит предупреждение во время компиляции (позволяет продолжить компиляцию)
comptime {
compile_warn("Генерация оптимизированного кода...");
let ENABLE_FEATURE = 1;
if (ENABLE_FEATURE == 0) {
compile_error("Функция должна быть включена!");
}
println "let FEATURE_ENABLED = 1;";
}
Метаданные сборки
Доступ к информации о сборке компилятора во время компиляции:
__COMPTIME_TARGET__- Строка платформы:"linux","windows"или"macos"__COMPTIME_FILE__- Имя текущего исходного файла
comptime {
// Генерация кода для конкретной платформы
println "let PLATFORM = \"{__COMPTIME_TARGET__}\";";
}
println "Запуск на: {PLATFORM}";
Note
Используйте {{ и }} для экранирования фигурных скобок внутри строк comptime.
Встраивайте файлы как указанные типы.
// По умолчанию (Slice_char)
let data = embed "assets/logo.png";
// Типизированное встраивание
let text = embed "shader.glsl" as string; // Встроить как C-строку
let rom = embed "bios.bin" as u8[1024]; // Встроить как фиксированный массив
let wav = embed "sound.wav" as u8[]; // Встроить как Slice_u8
Импортируйте плагины компилятора для расширения синтаксиса.
import plugin "regex"
let re = regex! { ^[a-z]+$ };
Передавайте макросы препроцессора в C.
Tip
Для простых констант используйте def. Используйте #define, когда вам нужны макросы препроцессора C или флаги условной компиляции.
#define MAX_BUFFER 1024
Используйте @cfg() для условного включения или исключения любого объявления верхнего уровня на основе флагов -D.
// Сборка: zc build app.zc -DUSE_OPENGL
@cfg(USE_OPENGL)
import "opengl_backend.zc";
@cfg(USE_VULKAN)
import "vulkan_backend.zc";
@cfg(not(USE_OPENGL))
@cfg(not(USE_VULKAN))
fn fallback_init() { println "Бэкенд не выбран"; }
| Форма | Значение |
|---|---|
@cfg(NAME) |
Включить, если -DNAME установлен |
@cfg(not(NAME)) |
Включить, если -DNAME НЕ установлен |
@cfg(any(A, B, ...)) |
Включить, если ЛЮБОЕ условие истинно (OR) |
@cfg(all(A, B, ...)) |
Включить, если ВСЕ условия истинны (AND) |
Несколько @cfg на одном объявлении объединяются через AND. not() можно использовать внутри any() и all(). Работает с любыми объявлениями верхнего уровня: fn, struct, import, impl, raw, def, test и т.д.
Украшайте функции и структуры для изменения поведения компилятора.
| Атрибут | Область | Описание |
|---|---|---|
@must_use |
Fn | Предупредить, если возвращаемое значение проигнорировано. |
@deprecated("msg") |
Fn/Struct | Предупредить при использовании с сообщением. |
@inline |
Fn | Подсказка компилятору встроить. |
@noinline |
Fn | Предотвратить встраивание. |
@packed |
Struct | Удалить заполнение между полями. |
@align(N) |
Struct | Принудительное выравнивание на N байт. |
@constructor |
Fn | Запустить перед main. |
@destructor |
Fn | Запустить после выхода из main. |
@unused |
Fn/Var | Подавить предупреждения о неиспользуемых переменных. |
@weak |
Fn | Слабая символьная компоновка. |
@section("name") |
Fn | Поместить код в специальный раздел. |
@noreturn |
Fn | Функция не возвращается (например exit). |
@pure |
Fn | Функция не имеет побочных эффектов (подсказка оптимизации). |
@cold |
Fn | Функция вряд ли будет выполняться (подсказка предсказания ветви). |
@hot |
Fn | Функция часто выполняется (подсказка оптимизации). |
@export |
Fn/Struct | Экспортировать символ (видимость по умолчанию). |
@global |
Fn | CUDA: Точка входа ядра (__global__). |
@device |
Fn | CUDA: Функция устройства (__device__). |
@host |
Fn | CUDA: Функция хоста (__host__). |
@comptime |
Fn | Вспомогательная функция, доступная для выполнения во время компиляции. |
@cfg(NAME) |
Любое | Условная компиляция: включает только если передан -DNAME. Поддерживает not(), any(), all(). |
@derive(...) |
Struct | Автоматически реализовать трейты. Поддерживает Debug, Eq (Smart Derive), Copy, Clone. |
@ctype("type") |
Fn Param | Переопределяет созданный тип C для параметра. |
@<custom> |
Any | Передаёт пользовательские атрибуты в C (например @flatten, @alias("name")). |
Zen C поддерживает мощную систему Пользовательских атрибутов, которая позволяет использовать любой __attribute__ GCC/Clang прямо в вашем коде. Любой атрибут, который не явно признан компилятором Zen C, рассматривается как обобщённый атрибут и передаётся в сгенерированный код C.
Это обеспечивает доступ к продвинутым функциям компилятора, оптимизациям и директивам компоновщика без необходимости явной поддержки в ядре языка.
Атрибуты Zen C отображаются прямо на атрибуты Си:
@name→__attribute__((name))@name(args)→__attribute__((name(args)))@name("string")→__attribute__((name("string")))
Zen C предоставляет "Умные производные", которые уважают Move Semantics:
@derive(Eq): Генерирует метод равенства, который принимает аргументы по ссылке (fn eq(self, other: T*)).- При сравнении двух структур без Copy (
a == b), компилятор автоматически передаётbпо ссылке (&b), чтобы избежать его перемещения. - Рекурсивные проверки равенства на полях также предпочитают доступ по указателю, чтобы предотвратить передачу владения.
- При сравнении двух структур без Copy (
Zen C предоставляет первоклассную поддержку встроения ассемблера прямо в код, транспилируя в GCC-стиле расширенный asm.
Напишите сырой ассемблер в блоках asm. Строки автоматически конкатенируются.
asm {
"nop"
"mfence"
}
Предотвратите оптимизацию ассемблера компилятором, который имеет побочные эффекты.
asm volatile {
"rdtsc"
}
Zen C упрощает сложный синтаксис ограничений GCC с именованными привязками.
// Синтаксис: : out(variable) : in(variable) : clobber(reg)
// Использует синтаксис заполнителя {variable} для читаемости
fn add_five(x: int) -> int {
let result: int;
asm {
"mov {x}, {result}"
"add $5, {result}"
: out(result)
: in(x)
: clobber("cc")
}
return result;
}
| Тип | Синтаксис | Эквивалент GCC |
|---|---|---|
| Output | : out(variable) |
"=r"(variable) |
| Input | : in(variable) |
"r"(variable) |
| Clobber | : clobber("rax") |
"rax" |
| Memory | : clobber("memory") |
"memory" |
Note
При использовании синтаксиса Intel (через -masm=intel) убедитесь, что ваша сборка правильно настроена (например, //> cflags: -masm=intel). TCC не поддерживает синтаксис Intel ассемблера.
Zen C поддерживает специальные комментарии в начале вашего исходного файла для настройки процесса сборки без необходимости сложной системы сборки или Makefile.
| Директива | Аргументы | Описание |
|---|---|---|
//> link: |
-lfoo или path/to/lib.a |
Ссылка на библиотеку или объектный файл. |
//> lib: |
path/to/libs |
Добавить путь поиска библиотеки (-L). |
//> include: |
path/to/headers |
Добавить путь поиска заголовков (-I). |
//> framework: |
Cocoa |
Ссылка на фреймворк macOS. |
//> cflags: |
-Wall -O3 |
Передать произвольные флаги компилятору C. |
//> define: |
MACRO или KEY=VAL |
Определить макрос препроцессора (-D). |
//> pkg-config: |
gtk+-3.0 |
Запустить pkg-config и добавить --cflags и --libs. |
//> shell: |
command |
Выполнить команду оболочки во время сборки. |
//> get: |
http://url/file |
Загрузить файл, если конкретный файл не существует. |
1. Защита ОС
Префиксируйте директивы названием ОС, чтобы применить их только на определённых платформах.
Поддерживаемые префиксы: linux:, windows:, macos: (или darwin:).
//> linux: link: -lm
//> windows: link: -lws2_32
//> macos: framework: Cocoa
2. Расширение переменных окружения
Используйте синтаксис ${VAR} для расширения переменных окружения в ваших директивах.
//> include: ${HOME}/mylib/include
//> lib: ${ZC_ROOT}/std
//> include: ./include
//> lib: ./libs
//> link: -lraylib -lm
//> cflags: -Ofast
//> pkg-config: gtk+-3.0
import "raylib.h"
fn main() { ... }
Следующие ключевые слова зарезервированы в Zen C.
alias, def, enum, fn, impl, import, let, module, opaque, struct, trait, union, use
async, await, break, catch, continue, defer, else, for, goto, guard, if, loop, match, return, try, unless, while
asm, assert, autofree, comptime, const, embed, launch, ref, sizeof, static, test, volatile
true, false, null
Следующие идентификаторы зарезервированы, так как они являются ключевыми словами в C11:
auto, case, char, default, do, double, extern, float, inline, int, long, register, restrict, short, signed, switch, typedef, unsigned, void, _Atomic, _Bool, _Complex, _Generic, _Imaginary, _Noreturn, _Static_assert, _Thread_local
and, or
Zen C предлагает два способа взаимодействия с кодом Си: Доверительные импорты (Удобно) и Явный FFI (Безопасно/Точно).
Вы можете импортировать заголовок Си напрямую, используя ключевое слово import с расширением .h. Это рассматривает заголовок как модуль и предполагает, что все доступные символы существуют.
//> link: -lm
import "math.h" as c_math;
fn main() {
// Компилятор доверяет корректности; выводит 'cos(...)' напрямую
let x = c_math::cos(3.14159);
}
Note
Плюсы: Просто и быстро. Минусы: Нет проверки типов со стороны Zen C (ошибки перехватываются позже компилятором Си).
Для строгой проверки типов или когда вы не хотите включать текст заголовка, используйте extern fn.
include <stdio.h> // Выводит #include <stdio.h> в сгенерированный Си код
// Определите строгую сигнатуру
extern fn printf(fmt: char*, ...) -> c_int;
fn main() {
printf("Hello FFI: %d\n", 42); // Проверено типами Zen C
}
Note
Плюсы: Zen C гарантирует совпадение типов. Минусы: Нужно писать сигнатуры.
import "file.h": Регистрирует заголовок как именованный модуль. Включает неявный доступ к символам (например,file::function()).include <file.h>: Просто выводит#include <file.h>в сгенерированный код C. Не вводит никаких символов в компилятор Zen C; вы должны использоватьextern fnдля их доступа.
Zen C включает стандартную библиотеку (std), охватывающую основную функциональность.
Просмотрите документацию стандартной библиотеки
Нажмите, чтобы посмотреть все модули стандартной библиотеки
| Модуль | Описание | Docs |
|---|---|---|
std/bigfloat.zc |
Арифметика с плавающей запятой произвольной точности. | Docs |
std/bigint.zc |
Целое число произвольной точности BigInt. |
Docs |
std/bits.zc |
Низкоуровневые побитовые операции (rotl, rotr и т.д.). |
Docs |
std/complex.zc |
Арифметика комплексных чисел Complex. |
Docs |
std/vec.zc |
Растущий динамический массив Vec<T>. |
Docs |
std/string.zc |
Выделенный в heap тип String с поддержкой UTF-8. |
Docs |
std/queue.zc |
FIFO очередь (Ring Buffer). | Docs |
std/map.zc |
Обобщённая хеш-таблица Map<V>. |
Docs |
std/fs.zc |
Операции файловой системы. | Docs |
std/io.zc |
Стандартный ввод/вывод (print/println). |
Docs |
std/option.zc |
Опциональные значения (Some/None). |
Docs |
std/result.zc |
Обработка ошибок (Ok/Err). |
Docs |
std/path.zc |
Кроссплатформенная манипуляция путями. | Docs |
std/env.zc |
Переменные окружения процесса. | Docs |
std/net/ |
TCP, UDP, HTTP, DNS, URL. | Docs |
std/thread.zc |
Потоки и синхронизация. | Docs |
std/time.zc |
Измерение времени и сон. | Docs |
std/json.zc |
Парсинг и сериализация JSON. | Docs |
std/stack.zc |
LIFO стек Stack<T>. |
Docs |
std/set.zc |
Обобщённое хеш-множество Set<T>. |
Docs |
std/process.zc |
Выполнение и управление процессами. | Docs |
std/regex.zc |
Регулярные выражения (на основе TRE). | Docs |
std/simd.zc |
Нативные SIMD-векторные типы. | Docs |
Zen C предоставляет встроенный языковой сервер и REPL для того, чтобы вам было комфортно программировать на этом ЯП!
Языковой сервер Zen C (LSP) поддерживает стандартные функции LSP для интеграции в редактор, предоставляя:
- Переходы к определению
- Нахождения ссылок
- Информация при наведении
- Автозаполнения: (имена функций/структур, dot-дополнение для методов/полей)
- Символы документа (outline)
- Справка для сигнатур
- Диагностика (ошибки синтаксиса/семантики)
Чтобы запустить языковой сервер (обычно настраивается в настройках LSP вашего редактора):
zc lspРаботает это через стандартный I/O (JSON-RPC 2.0).
Цикл Read-Eval-Print позволяет вам интерактивно экспериментировать с кодом Zen C.
zc repl- Интерактивное кодирование: Вводите выражения или операторы для немедленного вычисления.
- Постоянная история: Команды сохраняются в
~/.zprep_history. - Стартовый скрипт: Автоматически загружает команды из
~/.zprep_init.zc.
| Команда | Описание |
|---|---|
:help |
Показать доступные команды. |
:reset |
Очистить историю текущей сессии (переменные/функции). |
:vars |
Показать активные переменные. |
:funcs |
Показать определённые пользователем функции. |
:structs |
Показать определённые пользователем структуры. |
:imports |
Показать активные импорты. |
:history |
Показать историю ввода сессии. |
:type <expr> |
Показать тип выражения. |
:c <stmt> |
Показать сгенерированный код C для оператора. |
:time <expr> |
Провести бенчмарк выражения (запускает 1000 итераций). |
:edit [n] |
Редактировать команду n (по умолчанию: последняя) в $EDITOR. |
:save <file> |
Сохранить текущую сессию в файл .zc. |
:load <file> |
Загрузить и выполнить файл .zc в сессию. |
:watch <expr> |
Наблюдать за выражением (переоценивается после каждого ввода). |
:unwatch <n> |
Удалить наблюдение. |
:undo |
Удалить последнюю команду из сессии. |
:delete <n> |
Удалить команду с индексом n. |
:clear |
Очистить экран. |
:quit |
Выйти из REPL. |
! <cmd> |
Запустить команду оболочки (например !ls). |
Zen C включает встроенный языковой сервер для интеграции с редакторами.
- Руководство по установке и настройке
- Поддерживаемые редакторы: VS Code, Neovim, Vim, Zed и любой редактор с поддержкой LSP.
Используйте zc lsp для запуска сервера.
Программы на Zen C можно отлаживать с помощью стандартных отладчиков C, таких как LLDB или GDB.
Для наилучшей работы в VS Code установите официальное расширение Zen C. Для отладки вы можете использовать расширения C/C++ (от Microsoft) или CodeLLDB.
Добавьте эти конфигурации в вашу директорию .vscode, чтобы включить отладку одним щелчком мыши:
tasks.json (Задача сборки):
{
"label": "Zen C: Build Debug",
"type": "shell",
"command": "zc",
"args": [ "${file}", "-g", "-o", "${fileDirname}/app", "-O0" ],
"group": { "kind": "build", "isDefault": true }
}launch.json (Отладчик):
{
"name": "Zen C: Debug (LLDB)",
"type": "lldb",
"request": "launch",
"program": "${fileDirname}/app",
"preLaunchTask": "Zen C: Build Debug"
}Zen C разработан для работы с большинством компиляторов C11. Некоторые функции полагаются на расширения GNU C, но они часто работают на других компиляторах. Используйте флаг --cc для переключения бэкендов.
zc run app.zc --cc clang
zc run app.zc --cc zigНажмите, чтобы посмотреть детали поддержки компиляторов
| Компилятор | Процент успеха | Поддерживаемые функции | Известные ограничения |
|---|---|---|---|
| GCC | 100% (Полная) | Все функции | Нет. |
| Clang | 100% (Полная) | Все функции | Нет. |
| Clang | 100% (Полная) | Все функции | Нет. |
| Zig | 100% (Полная) | Все функции | Нет. Использует zig cc как замену C компилятора. |
| TCC | 98% (Высокая) | Структуры, Дженерики, Трейты, Сопоставление с образцом | Нет Intel ASM, Нет __attribute__((constructor)). |
Warning
ПРЕДУПРЕЖДЕНИЕ О СБОРКЕ: Хотя Zig CC отлично работает в качестве бэкенда для ваших программ Zen C, сборка самого компилятора Zen C с его помощью может пройти проверку, но создать нестабильный бинарный файл, который не пройдет тесты. Мы рекомендуем собирать компилятор с помощью GCC или Clang и использовать Zig только как бэкенд для вашего рабочего кода.
Команда zig cc Zig предоставляет drop-in замену для GCC/Clang с отличной поддержкой кроссплатформенной компиляции. Чтобы использовать Zig:
# Компилировать и запустить программу Zen C с Zig
zc run app.zc --cc zig
# Собрать сам компилятор Zen C с Zig
make zigZen C может генерировать код, совместимый с C++, с флагом --cpp, позволяя беспрепятственную интеграцию с библиотеками C++.
# Прямая компиляция с g++
zc app.zc --cpp
# Или транспилировать для ручной сборки
zc transpile app.zc --cpp
g++ out.c my_cpp_lib.o -o appВключите заголовки C++ и используйте сырые блоки для кода C++:
include <vector>
include <iostream>
raw {
std::vector<int> make_vec(int a, int b) {
return {a, b};
}
}
fn main() {
let v = make_vec(1, 2);
raw { std::cout << "Size: " << v.size() << std::endl; }
}
Примечание: Флаг
--cppпереключает бэкенд наg++и выводит совместимый с C++ код (используетautoвместо__auto_type, перегрузки функций вместо_Genericи явные приведения дляvoid*).
Zen C поддерживает GPU-программирование путём транспиляции в CUDA C++. Это позволяет вам использовать мощные функции C++ (шаблоны, constexpr) в ваших ядрах, сохраняя эргономичный синтаксис Zen C.
# Прямая компиляция с nvcc
zc run app.zc --cuda
# Или транспилировать для ручной сборки
zc transpile app.zc --cuda -o app.cu
nvcc app.cu -o app| Атрибут | Эквивалент CUDA | Описание |
|---|---|---|
@global |
__global__ |
Функция ядра (запускается на GPU, вызывается с хоста). |
@device |
__device__ |
Функция устройства (запускается на GPU, вызывается с GPU). |
@host |
__host__ |
Функция хоста (явно только для CPU). |
Zen C предоставляет чистый оператор launch для вызова ядер CUDA:
launch kernel_name(args) with {
grid: num_blocks,
block: threads_per_block,
shared_mem: 1024, // Опционально
stream: my_stream // Опционально
};
Это транспилируется в: kernel_name<<<grid, block, shared, stream>>>(args);
Используйте синтаксис функции Zen C с @global и оператором launch:
import "std/cuda.zc"
@global
fn add_kernel(a: float*, b: float*, c: float*, n: int) {
let i = thread_id();
if i < n {
c[i] = a[i] + b[i];
}
}
fn main() {
def N = 1024;
let d_a = cuda_alloc<float>(N);
let d_b = cuda_alloc<float>(N);
let d_c = cuda_alloc<float>(N);
defer cuda_free(d_a);
defer cuda_free(d_b);
defer cuda_free(d_c);
// ... init data ...
launch add_kernel(d_a, d_b, d_c, N) with {
grid: (N + 255) / 256,
block: 256
};
cuda_sync();
}
Zen C предоставляет стандартную библиотеку для общих операций CUDA, чтобы уменьшить использование raw блоков:
import "std/cuda.zc"
// Управление памятью
let d_ptr = cuda_alloc<float>(1024);
cuda_copy_to_device(d_ptr, h_ptr, 1024 * sizeof(float));
defer cuda_free(d_ptr);
// Синхронизация
cuda_sync();
// Индексирование потоков (использование внутри ядер)
let i = thread_id(); // Глобальный индекс
let bid = block_id();
let tid = local_id();
Note
Примечание: Флаг --cuda устанавливает nvcc как компилятор и подразумевает режим --cpp. Требует NVIDIA CUDA Toolkit.
Zen C поддерживает современные функции стандарта C23 при использовании совместимого компилятора бэкенда (GCC 14+, Clang 14+, TCC (частичная)).
auto: Zen C автоматически отображает вывод типов на стандартный C23auto, если__STDC_VERSION__ >= 202300L._BitInt(N): Используйте типыiNиuN(например,i256,u12,i24) для доступа к целым числам произвольной ширины C23.
Zen C может компилировать в Objective-C (.m), используя флаг --objc, позволяя вам использовать фреймворки Objective-C (такие как Cocoa/Foundation) и синтаксис.
# Компилировать с clang (или gcc/gnustep)
zc app.zc --objc --cc clangИспользуйте include для заголовков и raw блоки для синтаксиса Objective-C (@interface, [...], @"").
//> macos: framework: Foundation
//> linux: cflags: -fconstant-string-class=NSConstantString -D_NATIVE_OBJC_EXCEPTIONS
//> linux: link: -lgnustep-base -lobjc
include <Foundation/Foundation.h>
fn main() {
raw {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(@"Hello from Objective-C!");
[pool drain];
}
println "Zen C works too!";
}
Note
Примечание: Интерполяция строк Zen C работает с объектами Objective-C (id), вызывая debugDescription или description.
Мы приветствуем ваш вклад! Исправление ошибок, добавление документации или предложение новых функций.
Пожалуйста, ознакомьтесь с CONTRIBUTING_RU.md для получения подробных инструкций о том, как внести свой вклад, запустить тесты и отправить запрос на слияние.
Инструкции по сообщению об уязвимостях см. в SECURITY_RU.md.
Этот проект использует сторонние библиотеки. Полные тексты лицензий можно найти в каталоге LICENSES/.
- cJSON (MIT License): Используется для парсинга JSON и генерации в языковом сервере.
- zc-ape (MIT License): Оригинальный порт Actually Portable Executable Zen-C от Eugene Olonov.
- Cosmopolitan Libc (ISC License): Основополагающая библиотека, которая делает APE возможной.
- TRE (BSD License): Используется для движка регулярных выражений в стандартной библиотеке.
- zenc.vim (лицензия MIT): Официальный плагин для Vim/Neovim, основной автор — davidscholberg.
Copyright © 2026 Язык Программирования Zen C.
Начните своё путешествие сегодня.
Discord • GitHub • Документация • Примеры • RFC • Внести вклад