Comodità di un linguaggio ad alto livello, veloce come il C
Panoramica • Comunità • Guida Rapida • Ecosistema • Riferimento del Linguaggio • Libreria Standard • Toolchain
Zen C è un linguaggio di programmazione di sistemi moderno che genera codice GNU C/C11. Fornisce allo sviluppatore un ricco set di funzionalità, tra cui inferenza di tipo, pattern matching, generici, tratti, async/await, e gestione manuale della memoria con funzionalità RAII, mantenendo al contempo una compatibilità al 100% con l'ABI C
Unisciti alla conversazione, condividi delle demo, fai domande o segnala dei bug nel server ufficiale Discord Zen C
- Discord: Unisciti qui
- RFC: Proponi funzionalità
Il progetto Zen C è composto da diversi repository. Di seguito trovi i principali:
| Repository | Descrizione | Stato |
|---|---|---|
| zenc | Il compilatore core di Zen C (zc), CLI e libreria standard. |
Sviluppo Attivo |
| docs | La documentazione tecnica ufficiale e la specifica del linguaggio. | Attivo |
| rfcs | Il repository delle Request for Comments (RFC). Dai forma al futuro del linguaggio. | Attivo |
| vscode-zenc | Estensione ufficiale di VS Code (Sintassi, Snippet). | Alpha |
| www | Codice sorgente di zenc-lang.org. |
Attivo |
| awesome-zenc | Una lista curata di fantastici esempi di Zen C. | In crescita |
Dai un'occhiata a questi progetti creati con Zen C:
- ZC-pong-3ds: Un clone di Pong per Nintendo 3DS.
- zen-c-parin: Un esempio base usando Zen C con Parin.
- almond: Un browser web minimale scritto in Zen C.
git clone https://github.com/zenc-lang/zenc.git
cd Zen-C
make clean # rimuove i vecchi file di build
make
sudo make installZen C ha il pieno supporto nativo per Windows (x86_64). È possibile compilare utilizzando lo script batch fornito con GCC (MinGW):
build.batQuesto costruirà il compilatore (zc.exe). Le operazioni di Rete, File System e Processo sono completamente supportate tramite il Platform Abstraction Layer (PAL).
In alternativa, è possibile utilizzare make se si dispone di un ambiente Unix-like (MSYS2, Cygwin, git-bash).
Il codice Zen C può come un Actually Portable Executable (APE) (lett. Eseguibile Effetivamente Portatile) utilizzando la Cosmopolitan Libc. Ciò produrrà un singolo eseguibile (.com) che potrà essere eseguito nativamente su Linux, macOS, Windows, FreeBSD, OpenBSD e NetBSD sia sulle architetture x86_64 e aarch64.
Prerequisiti:
- Strumenti
cosmocc(deve trovarsi nella tua PATH)
Builda e Installa:
make ape
sudo env "PATH=$PATH" make install-apeArtefatti:
out/bin/zc.com: Il compilatore Zen-C portatile. Inlude la libreria standard, incorporata nell'eseguibile.out/bin/zc-boot.com: Un installer bootstrap auto-contenuto per configurare nuovi progetti Zen-C rapidamente.
Utilizzo:
# Eseguibile su qualunque OS supportato
./out/bin/zc.com build hello.zc -o hello# Compila e avvia
zc run hello.zc
# Builda eseguibile
zc build hello.zc -o hello
# Shell interattiva
zc repl
# Mostra curiosità Zen
zc build hello.zc --zenPuoi impostare ZC_ROOT per specificare la posizione della Libreria Standard (per inclusioni standard come import "std/vector.zc"). Ciò ti permetterà di eseguire il comando zc da qualsiasi directory.
export ZC_ROOT=/path/to/Zen-CZen C differenzia le costanti al tempo di compilazione e le variabili di esecuzione.
Valori che esistono solo durante la compilazione (integrate nel codice). Utilizzale per le grandezze degli array, configurazioni fisse, e numeri magici.
def MAX_SIZE = 1024;
let buffer: char[MAX_SIZE]; // Grandezza valida per l'array
Locazioni di memoria. Possono essere mutabili o di sola lettura (const).
let x = 10; // Mutabile
x = 20; // OK
let y: const int = 10; // Sola lettura (Tipo qualificato)
// y = 20; // Errore: impossibile assegnare un valore ad una variabile costante
Tip
Inferenza di tipo: Zen C inferisce automaticamente il tipo per le variabili inizializzate. Compilando ciò alla keyword auto dello standard C23 nei compilatori supportati, oppure alla estensione GCC __auto_type.
| Tipo | C Equivalent | Descrizione |
|---|---|---|
int, uint |
int32_t, uint32_t |
Intero a 32 bit con segno/senza segno |
c_char, c_uchar |
char, unsigned char |
C char (Interop) |
c_short, c_ushort |
short, unsigned short |
C short (Interop) |
c_int, c_uint |
int, unsigned int |
C int (Interop) |
c_long, c_ulong |
long, unsigned long |
C long (Interop) |
c_long_long, c_ulong_long |
long long, unsigned long long |
C long long / unsigned long long (Interop) |
I8 .. I128 or i8 .. i128 |
int8_t .. __int128_t |
Interi a grandezza fissa con segno |
U8 .. U128 or u8 .. u128 |
uint8_t .. __uint128_t |
Interi a grandezza fissa senza segno |
isize, usize |
ptrdiff_t, size_t |
Interi con grandezza di un puntatore |
byte |
uint8_t |
Alias per U8 |
F32, F64 or f32, f64 |
float, double |
Numeri con parte decimale |
bool |
bool |
true (lett. vero) o false (lett. falso) |
char |
char |
Carattere singolo |
string |
char* |
Stringhe C terminate da NULL |
U0, u0, void |
void |
Tipo vuoto |
iN (Per esempio, i256) |
_BitInt(N) |
Intero con segno a larghezza arbitraria di bit (C23) |
uN (Per esempio, u42) |
unsigned _BitInt(N) |
Intero senza segno a larghezza arbitraria di bit (C23) |
- Interi: Decimali (
123), Hex (0xFF), Ottali (0o755), Binari (0b1011).- Nota: I numeri con zeri iniziali sono trattati come decimali (
0123è123), a differenza del C. - Nota: I numeri possono contenere trattini bassi per leggibilità (
1_000_000,0b_1111_0000).
- Nota: I numeri con zeri iniziali sono trattati come decimali (
- A virgola mobile: Standard (
3.14), Scientifico (1e-5,1.2E3). I numeri in virgola mobile supportano anche i trattini bassi (3_14.15_92).
Important
Best Practice per Codice Portabile
- Usa Tipi Portabili (
int,uint,i64,u8, ecc.) per tutta la logica Zen C pura.intè garantito essere a 32-bit con segno su tutte le architetture. - Usa Tipi di Interop C (
c_int,c_char,c_long,c_ulong,c_long_long,c_ulong_long) solo quando interagisci con librerie C (FFI). La loro dimensione varia in base alla piattaforma e al compilatore C. - Usa
isizeeusizeper indicizzazione di array e aritmetica dei puntatori.
Array a lunghezza fissa con valori arbitrari.
def GRANDEZZA = 5;
let interi: int[GRANDEZZA] = [1, 2, 3, 4, 5];
let zeri: [int; GRANDEZZA]; // Inizializzato a zero
Valori molteplici raggruppati assieme, accesso agli elementi indicizzato.
let paio = (1, "Ciao!");
let x = paio.0; // 1
let s = paio.1; // "Ciao!"
Molteplici Valori di Ritorno
Le funzioni posso restituire delle tuple per fornire diversi risultati:
fn somma_e_differenza(a: int, b: int) -> (int, int) {
return (a + b, a - b);
}
let risultato = somma_e_differenza(3, 2);
let somma = risultato.0; // 5
let differenza = risultato.1; // 1
Separazione
Le tuple possono essere separate direttamente in variabili singole.
let (somma, differenza) = somma_e_differenza(3, 2);
// somma = 5, differenza = 1
La separazione delle tuple tipizzata permette annotazioni di tipo esplicite:
let (a: string, b: u8) = ("hello", 42);
let (x, y: i32) = (1, 2); // Misto: x inferito, y esplicito
Strutture dati con campi di bit opzionali.
struct Punto {
x: int;
y: int;
}
// Inizializzazione struct
let p = Punto { x: 10, y: 20 };
// Campi di bit
struct Flags {
valido: U8 : 1;
modalità: U8 : 3;
}
Note
Gli struct usano le Semantiche di Spostamento di default. I campi di uno struct possono essere acceduti via . anche sui puntatori (Dereferenza-Automatica).
Puoi definire uno struct come opaque (lett. opaco) per restringere l'accesso ai suoi campi al modulo che lo ha definito, permettendo comunque l'allocazione sullo stack dello struct (la grandezza è data).
// In utente.zc
opaque struct Utente {
id: int;
nome: string;
}
fn nuovo_utente(nome: string) -> Utente {
return Utente{id: 1, nome: nome}; // OK: Dentro il modulo
}
// In main.zc
import "utente.zc";
fn main() {
let u = nuovo_utente("Alice");
// let id = u.id; // Error: Impossibile accedere al campo privato 'id'
}
Unioni taggate (tipi somma) capaci di contenere dati.
enum Forma {
Cerchio(float), // Contiene il raggio
Rettangolo(float, float), // Contiene la larghezza e l'altezza
Punto // Non contiene dati
}
Unioni standard C (accesso non sicuro).
union Dati {
i: int;
f: float;
}
Tipi di vettore SIMD nativi utilizzando le estensioni vettoriali di GCC/Clang. Annota uno struct con @vector(N) per definire un vettore di N elementi.
import "std/simd.zc";
fn main() {
let a = f32x4{v: 1.0}; // Broadcast: {1.0, 1.0, 1.0, 1.0}
let b = f32x4{1.0, 2.0, 3.0, 4.0}; // Inizializzazione per elemento
let c = a + b; // Addizione per elemento
let x = c[0]; // Accesso all'elemento (float)
}
Gli operatori aritmetici (+, -, *, /) e bitwise (&, |, ^) funzionano per elemento. Vedi std/simd.zc per i tipi predefiniti.
Crea un alias per un tipo già esistente.
alias ID = int;
alias PuntoDellaMappa = Mappa<string, Punto>
alias OpFunc = fn(int, int) -> int
Nota: Il punto e virgola finale è opzionale per gli alias di tipo.
Puoi definire un alias del tipo come opaque (lett. opaco) per creare un nuovo tipo che si distingue dal suo tipo sottostante al di fuori del modulo che l'ha definito. Questo permette una forte incapsulamento e sicurezza dei tipi senza overhead extra durante l'esecuzione di un wrapper struct.
// In libreria.zc
opaque alias Handle = int;
fn crea_handle(v: int) -> Handle {
return v; // Conversione implicita consentita all'interno del modulo
}
// In main.zc
import "libreria.zc";
fn main() {
let h: Handle = crea_handle(42);
// let i: int = h; // Errore: Validazione del tipo fallita
// let h2: Handle = 10; // Errore: Validazione del tipo fallita
}
fn somma(a: int, b: int) -> int {
return a + b;
}
// Supporto per argomenti nominati nelle chiamate
somma(a: 10, b: 20);
Note
Gli argomenti nominati devono seguire rigorosamente l'ordine predefinito dei parametri. somma(b: 20, a: 10) è errato.
Gli argomenti di una funzione possono essere marcati come const (lett. costanti) per reinforzare semantiche di sola lettura. Questo è un qualificatore del tipo, non una costante esplicita.
fn stampa_valore(v: const int) {
// v = 10; // Errore: Impossibile assegnare un valore ad una variabile costante
println "{v}";
}
Le funzioni posso definire dei valori default per gli argomenti in caso che questi non vengano specificati durante la chiamata. Questi valori possono essere letterali, espressioni, o codice Zen C valido (come il costruttore di uno struct).
// Valore default semplice
fn incrementa(val: int, quantità: int = 1) -> int {
return val + quantità;
}
// Espressione come valore default (calcolato)
fn offset(val: int, pad: int = 10 * 2) -> int {
return val + pad;
}
// Struct come valore default
struct Config { debug: bool; }
fn init(cfg: Config = Config { debug: true }) {
if cfg.debug { println "Modalità Debug"; }
}
fn main() {
incrementa(10); // 11
offset(5); // 25
init(); // Stampa "Modalità Debug"
}
Funzioni anonime che possono catturare il loro ambiente.
let fattore = 2;
let raddoppia = x -> x * fattore; // Sintassi con freccia
let pieno = fn(x: int) -> int { return x * fattore; }; // Sintassi a blocco
// Cattura per Riferimento (Sintassi a Blocco)
let val = 10;
let modify = fn[&]() { val += 1; };
modify(); // val ora è 11
// Cattura per Riferimento (Sintassi a Freccia)
let modify_arrow = [&] x -> val += x;
modify_arrow(5); // val ora è 16
// Cattura per Riferimento (Sintassi a Freccia con Argomenti Multipli)
let sum_into = [&] (a, b) -> val += (a + b);
sum_into(2, 2); // val ora è 20
// Cattura per Valore (Predefinito)
let original = 100;
let implicit = x -> original + x; // Cattura implicita per valore (senza parentesi)
let explicit = [=] x -> original + x; // Cattura esplicita per valore
// let fail = x -> original += x; // Errore: impossibile assegnare a valore catturato
Zen C supporta i puntatori-funzione grezzi utilizzando la sintassi fn*. Questo permette un'interoperabilità fluida con le librerie C che si aspettano puntatori-funzione senza overhead di closure.
// Funzione che prende un puntatore-funzione grezzo
fn imposta_callback(cb: fn*(int)) {
cb(42);
}
// Funzione che restituisce un puntatore-funzione grezzo
fn ottieni_callback() -> fn*(int) {
return il_mio_handler;
}
// I puntatori a puntatori-funzione sono supportati (fn**)
let pptr: fn**(int) = &ptr;
Le funzioni possono accettare un numero variabile di argomenti utilizzando la sintassi ... e il tipo va_list.
fn log(lvl: int, fmt: char*, ...) {
let ap: va_list;
va_start(ap, fmt);
vprintf(fmt, ap); // Usa lo stdio C
va_end(ap);
}
if x > 10 {
print("Grande");
} else if x > 5 {
print("Medio");
} else {
print("Piccolo");
}
// Operatore ternario
let y = x > 10 ? 1 : 0; // Se x è maggiore di 10 y sarà uguale a 1, in ogni altro caso, y sarà uguale a 0
// If-Expression (per condizioni complesse)
let categoria = if (x > 100) { "enorme" } else if (x > 10) { "grande" } else { "piccolo" };
Alternativa potente agli switch.
match val {
1 => { print "Uno" },
2 || 3 => { print "Due o Tre" }, // OR logico con ||
4 or 5 => { print "Quattro or Cinque" }, // OR logico con 'or'
6, 7, 8 => { print "Da Sei a Otto" }, // OR logico con la virgola (,)
10 .. 15 => { print "Da 10 a 14" }, // Range Esclusivo (Legacy)
10 ..< 15 => { print "Da 10 a 14" }, // Range Esclusivo (Esplicito)
20 ..= 25 => { print "Da 20 a 25" }, // Range Inclusivo
_ => { print "Altro" },
}
// Destrutturazione degli Enums
match forma {
Forma::Cerchio(r) => { println "Raggio: {r}" },
Forma::Rettangolo(w, h) => { println "Area: {w*h}" },
Forma::Punto => { println "Punto" },
}
Per ispezionare un valore senza assumerne la proprietà (spostarlo) puoi usare la keyword ref nel pattern. Questo è essenziale per i tipi che implementano Semantiche di Movimento (come Option, Result, struct non-copiabile).
let opt = Qualche(ValoreNonCopiable{...});
match opt {
Some(ref x) => {
// 'x' è un puntatore che punta al valore contenuto in 'opt'
// 'opt' NON viene né mosso né consumato qui
println "{x.field}";
},
None => {}
}
// Range
for i in 0..10 { ... } // Esclusivo (Da 0 a 9)
for i in 0..<10 { ... } // Esclusivo (Esplicito)
for i in 0..=10 { ... } // Inclusivo (Da 0 a 10)
for i in 0..10 step 2 { ... }
for i in 10..0 step -1 { ... } // Descending loop
// Iteratore (Vec, Array, oppure un Iteratore personalizzato)
for item in collection { ... }
// Enumerato: ottieni indice e valore
for i, val in collection { ... } // i = 0, 1, 2, ...
for i, val in 0..10 step 2 { ... } // i = 0, 1, 2, ...; val = 0, 2, 4, ...
// While (lett. mentre)
while x < 10 { ... }
// Infinito con etichetta
esterno: loop {
if done { break esterno; }
}
// Ripeti N volte
for _ in 0..5 { ... }
// Guard (lett. 'guardia'): Esegue il caso 'else' e ritorna se la condizione è falsa
guard ptr != NULL else { return; }
// Unless (lett. 'a meno che'): Se non vero
unless è_valido { return; }
Zen C supporta l'overloading di operatori per gli struct definiti dall'utente per implementare nomi specifici di metodi.
| Categoria | Operatore | Nome del Metodo |
|---|---|---|
| Aritmetico | +, -, *, /, %, ** |
add, sub, mul, div, rem, pow |
| Paragone | ==, != |
eq, neq |
<, >, <=, >= |
lt, gt, le, ge |
|
| Bitwise | &, |, ^ |
bitand, bitor, bitxor |
<<, >> |
shl, shr |
|
| Unari | - |
neg |
! |
not |
|
~ |
bitnot |
|
| Indice | a[i] |
get(a, i) |
a[i, j] |
get(a, i, j) |
|
a[i] = v |
set(a, i, v) |
Nota sull'uguaglianza delle stringhe:
string == stringperforma un controllo del valore (equivalente astrcmp).char* == char*performa un controllo dei puntatori (controlla gli indirizzi di memoria).- Paragoni misti (e.g.
string == char*) defaulta al controllo dei pointer.
Esempio:
impl Punto {
fn add(self, altro: Punto) -> Punto {
return Punto{x: self.x + altro.x, y: self.y + altro.y};
}
}
let p3 = p1 + p2; // Chiama p1.somma(p2)
Esempio Multi-Indice:
struct Matrice {
data: int[9];
}
impl Matrice {
fn get(self, riga: int, col: int) -> int {
return self.data[riga * 3 + col];
}
}
let m = Matrice{data: [1,0,0, 0,1,0, 0,0,1]};
let val = m[1, 2]; // Chiama Matrice.get(m, 1, 2)
Questi operatori sono funzionalità integrate del linguaggio e non è possibile overloadarli.
| Operatore | Nome | Descrizione |
|---|---|---|
|> |
Pipeline | x |> f(y) viene dezuccherato a f(x, y) |
?? |
Coalescenza nulla | val ?? default restituisce default se val è NULL (puntatori) |
??= |
Assegnazione nulla | val ??= init assegna se val è NULL |
?. |
Safe Navigation | ptr?.campo accede a 'campo' solo se ptr non è NULL |
? |
Try Operator | res? restituisce un errore se presente (tipi Result/Option) |
Dereferenza Automatica:
Pointer field access (ptr.field) and method calls (ptr.method()) automatically dereference the pointer, equivalent to (*ptr).field.
Accesso ai campi da un puntatore (puntatore.campo) e chiamate ai metodi (puntatore.metodo()) dereferenzano automaticamente il puntatore, ciò è equivalente a (*puntatore).campo
Zen C fornisce opzioni versatili per stampare alla console, includendo keyword e scorciatoie coincise.
| Keyword | Descrizione |
|---|---|
print "testo" |
Stampa a stdout senza aggiunzione di una newline automatica. |
println "testo" |
Stampa a stdout aggiungendo una newline automatica. |
eprint "testo" |
Stampa a stderr senza aggiunzione di una newline automatica. |
eprintln "testo" |
Stampa a stderr aggiungendo una newline automatica. |
Zen C ti permette di utilizzare stringhe letterali direttamente come istruzione di stampaggio veloce:
| Sintassi | Equivalente | Descrizione |
|---|---|---|
"Ciao!" |
println "Ciao!" |
Aggiunge una newline implicitamente. |
"Ciao!".. |
print "Ciao!" |
Non aggiunge una newline. |
!"Errore" |
eprintln "Errore" |
Output a stderr. |
!"Errore".. |
eprint "Errore" |
Output a stderr, senza newline. |
Puoi incorporare espressioni direttamente all'interno di stringhe letterali utilizzando la sintassi {}. Questo funziona con tutti i metodi di stampaggio, incluse le scorciatoie.
L'interpolazione di stringhe in Zen C è implicita: se la tua stringa contiene {...}, verrà analizzata automaticamente come una stringa interpolata. Puoi anche usare esplicitamente il prefisso f (es. f"...") per forzare la semantica di interpolazione.
let x = 42;
let nome = "Max";
println "Valore: {x}, Nome: {name}";
"Valore: {x}, Nome: {name}"; // scorciatoia per println
Escape delle Parentesi Graffe: Usa {{ per produrre una parentesi graffa letterale { e }} per una } letterale:
let json = "JSON: {{\"chiave\": \"valore\"}}";
// Output: JSON: {"chiave": "valore"}
Stringhe Grezze (Raw Strings): Per definire una stringa in cui le sequenze di interpolazione e di escape vengono completamente ignorate, inserici il prefisso r (es. r"..."):
let regex = r"\w+"; // Contiene esattamente \ w +
let raw_json = r'{"chiave": "valore"}'; // Non è necessario l'escape delle parentesi
Zen C supporta blocchi di stringhe multilinea grezzi utilizzando il delimitatore """. Questo è estremamente utile per la scrittura di linguaggi incorporati (GLSL, HTML) o per la generazione di codice C all'interno di blocchi comptime senza il bisogno di inserire manualmente l'escape a capo ed alle virgolette all'interno.
Come le stringhe standard, le stringhe multilinea supportano l'interpolazione implicita. Puoi aggiugere l'escape in maniera esplicita usando:
f"""...""": Contrassegna in modo esplicito un blocco di stringa interpolato.r"""...""": Contrassegna in modo esplicito un blocco di stringa grezzo (nessuna interpolazione, nessuna sequenza di escape).
let prompt = """
Per favore, inserisci il tuo nome:
Digita "exit" per annullare.
""";
let mondo = "mondo";
let script = """
fn ciao() {
println "ciao, {mondo}!";
}
""";
let pure_raw = r"""
Qui le {parentesi} sono solo testo, e \n e' letteralmente barra ed n.
""";
Zen C supporta una scorciatoia per richiedere input dall'utente utilizzando il prefisso ?.
? "Inserisci il tuo nome": Stampa il prompt (senza newline) e aspetta per dell'input (legge una linea).? "Inserisci la tua età: " (età): Stampa il prompt e memorizza l'input nella variabileetà.- Gli specificatori del formato vengono automaticamente inferiti in base al tipo della variabile.
let età: int;
? "Inserisci la tua età: " (età);
println "Hai {età} anni.";
Zen C permette una gestione manuale della memoria con aiuti ergonomici.
Esegui il codice quando l’ambito corrente termina. Le istruzioni defer vengono eseguite in ordine LIFO (last-in, first-out).
let f = fopen("file.txt", "r");
defer fclose(f);
Per prevenire comportamenti indefiniti, le istruzioni del controllo di flusso (
return,break,continue,goto) non sono ammesse dentro un bloccodefer.
Libera automaticamente la memoria occupata dalla variabile quando l'ambito corrente termina.
autofree let tipi = malloc(1024);
Zen C tratta i tipi con distruttori (come File, Vec, o puntatori allocati manualmente con malloc) come Risorse. Per prevenire errori di doppia-liberazione, le risorse non possono essere implicitamente duplicate.
- Muovi di Default: Assegnare una risorsa variabile ne trasferisce il proprietario. La variabile originale diventa invalida (Spostata).
- Tipi di Copia: Tipi senza distruttori possono opzionalmente avere un comportamento
Copy, rendendo l'assegnazione una duplicazione.
Diagnostica & Filosofia: Se vedi un errore "Utilizzo di una variabile spostata", il compilatore ti sta dicendo: "Questo tipo è proprietario di una risorsa (come memoria o un handle) e copiarlo ciecamente non è sicuro."
Contrasto: Al contrario di come fanno C/C++, Zen C non duplica implicitamente i valori che posseggono risorse.
Argomento di una funzione: Passare un valore ad una funzione segue le stesse regole dell'assegnazione: le risorse vengono spostate se non passate per referenza.
fn processo(r: Risorsa) { ... } // 'r' viene spostato nella funzione
fn peek(r: Risorsa*) { ... } // 'r' viene preso in prestito (referenza)
Clonazione Esplicita: Se vuoi avere più copie di una risorsa, rendilo esplicito:
let b = a.clona(); // Chiama il metodo `clona` dal tratto `Clone`
Duplicazione opt-in (Tipi dei valori): Per tipi piccoli senza distruttore:
struct Punto { x: int; y: int; }
impl Copy for Punto {} // Opt-in per la duplicazione implicita
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = p1; // Copiato. p1 rimane valido.
}
Implementa Drop per una logica di pulizia automatica.
impl Drop for MioStruct {
fn drop(self) {
self.free();
}
}
Definisci metodi sui tipi utilizziando impl.
impl Punto {
// Metodo statico (convenzione del costruttore)
fn nuovo(x: int, y: int) -> Self {
return Point{x: x, y: y};
}
// Metodo d'instanza
fn dist(self) -> float {
return sqrt(self.x * self.x + self.y * self.y);
}
}
Scorciatoia di Self: Nei metodi con un parametro self, puoi usare .campo come abbreviazione per self.campo:
impl Point {
fn dist(self) -> float {
return sqrt(.x * .x + .y * .y); // Equivalente a self.x, self.y
}
}
Zen C permette di definire metodi su tipi primitivi (come int, bool, etc.) usando la stessa sintassi 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)
Definisci un comportamento condiviso.
struct Cerchio { raggio: f32; }
trait Disegnabile {
fn disegna(self);
}
impl Disegna for Cerchio {
fn disegna(self) { ... }
}
let cerchio = Cerchio{};
let disegnabile: Disegnabile = &cerchio;
Zen C include dei tratti standard che si integrano con la sintassi del linguaggio.
Iterable (lett. Iterabile)
Implementa Iterable<T> per abilitare loop for-in (lett. per in) nei tuoi tipi personalizzati.
import "std/iter.zc"
// Definisci un Iteratore
struct MioIteratore {
curr: int;
stop: int;
}
impl MioIteratore {
fn next(self) -> Option<int> {
if self.curr < self.stop {
self.curr += 1;
return Option<int>::Some(self.curr - 1);
}
return Option<int>::None();
}
}
// Implementa Iterable
impl MioRange {
fn iterator(self) -> MioIteratore {
return MioIteratore{curr: self.start, stop: self.end};
}
}
// Usalo in un loop
for i in mio_range {
println "{i}";
}
Drop (lett. rilascia)
Implementa Drop per definire un distruttore che esegue quando l'oggetto va fuori ambito (RAII).
import "std/mem.zc"
struct Risorsa {
ptr: void*;
}
impl Drop for Risorsa {
fn drop(self) {
if self.ptr != NULL {
free(self.ptr);
}
}
}
Note
Se una variabile viene spostata, drop NON verrà chiamato sulla variabile originale. Aderisce alle Semantiche delle Risorse
Copy (lett. copia)
Tratto marcatore opt-in per il comportamento Copy (duplicazione implicita) al posto delle semantiche Move. Utilizzato tramite @derive(Copy)
Regola: I tipi che implementano
Copynon dovrà definire un distruttore (Drop).
@derive(Copy)
struct Punto { x: int; y: int; }
fn main() {
let p1 = Punto{x: 1, y: 2};
let p2 = p1; // Copiato! p1 rimane valido.
}
Clone (lett. clona)
Implementa Clone per permettere la duplicazione esplicita di tipi che posseggono risorse.
import "std/mem.zc"
struct Scatola { val: int; }
impl Clone for Scatola {
fn clone(self) -> Scatola {
return Scatola{val: self.val};
}
}
fn main() {
let b1 = Scatola{val: 42};
let b2 = b1.clone(); // Explicit copy
}
Usa use per incorporare altri struct. Puoi mischiarli (campi piatti) o nominarli (campi nidificato).
struct Entità { id: int; }
struct Giocatore {
// Mischiati (Non nominati): Campi piatti
use Entità; // Aggiunge 'id' a 'Giocatore' direttamente
nome: string;
}
struct Partita {
// Composizione (Nominati): Campi nidificati
use p1: Giocatore; // Vi si accede tramite partita.p1
use p2: Giocatore; // Vi si accede tramite partita.p2
}
Template type-safe per struct e funzioni.
// Struct Generico
struct Scatola<T> {
oggetto: T;
}
// Funzione Generica
fn identità<T>(valore: T) -> T {
return valore;
}
// Generici Multi-parametro
struct Paio<K, V> {
chiavi: K;
valore: V;
}
Costruito sui pthreads.
async fn ottieni_dati() -> string {
// Esegue in background
return "Dati";
}
fn main() {
let futuro = ottieni_dati();
let risultato = await futuro; // (lett. 'aspetta')
}
Esegui codice al momento della compilazione per generare sorgente o stampare messaggi.
comptime {
// Genera codice al momento della compilazione (scritto su stdout)
println "let data_compilazione = \"2024-01-01\";";
}
println "Data compilazione: {data_compilazione}";
Funzioni Helper
Funzioni speciali disponibili all'interno dei blocchi comptime:
| Funzione | Descrizione |
|---|---|
yield(str) |
Emette esplicitamente codice generato (alternativa a printf) |
code(str) |
Alias di yield() - intento più chiaro per generazione codice |
compile_error(msg) |
Interrompe la compilazione con un messaggio di errore fatale |
compile_warn(msg) |
Emette un avviso al momento della compilazione (consente di continuare) |
Esempio:
comptime {
compile_warn("Generazione codice ottimizzato...");
let ENABLE_FEATURE = 1;
if (ENABLE_FEATURE == 0) {
compile_error("La funzionalità deve essere abilitata!");
}
// Usa code() con raw strings per generazione pulita
code(r"let FEATURE_ENABLED = 1;");
}
Metadati di Build
Accedi alle informazioni di build del compilatore al momento della compilazione:
| Costante | Tipo | Descrizione |
|---|---|---|
__COMPTIME_TARGET__ |
string | Piattaforma: "linux", "windows" o "macos" |
__COMPTIME_FILE__ |
string | Nome del file sorgente corrente in compilazione |
Esempio:
comptime {
// Generazione codice specifico per piattaforma
println "let PLATFORM = \"{__COMPTIME_TARGET__}\";";
}
println "In esecuzione su: {PLATFORM}";
Tip
Usa raw strings (r"...") in comptime per evitare di eseguire l'escape delle parentesi graffe: code(r"fn test() { return 42; }"). Altrimenti, usa {{ e }} per l'escape nelle stringhe normali.
Incorpora file come tipi specificati.
// Default (Slice_char)
let data = embed "assets/logo.png";
// Incorporazioni tipizzate
let testo = embed "shader.glsl" as string; // Incorpora come una stringa C
let rom = embed "bios.bin" as u8[1024]; // Incorpora come un array a dimensione fissa
let wav = embed "sound.wav" as u8[]; // Incorpora come Slice_u8
Importa plugin del compilatore per estendere la sintassi.
import plugin "regex"
let re = regex! { ^[a-z]+$ };
Passa delle macro del preprocessore C.
Tip
Per delle semplici costanti, utilizza def. Usa #define solo quanto ti servono macro del preprocessore C o flag di compilazione condizionale.
#define BUFFER_MASSIMO 1024
Usa @cfg() per includere o escludere condizionalmente qualsiasi dichiarazione di livello superiore in base ai flag -D.
// Compila con: 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 "Nessun backend selezionato"; }
| Forma | Significato |
|---|---|
@cfg(NAME) |
Includi se -DNAME è definito |
@cfg(not(NAME)) |
Includi se -DNAME NON è definito |
@cfg(any(A, B, ...)) |
Includi se QUALSIASI condizione è vera (OR) |
@cfg(all(A, B, ...)) |
Includi se TUTTE le condizioni sono vere (AND) |
Più @cfg su una dichiarazione vengono combinati con AND. not() può essere usato dentro any() e all(). Funziona con qualsiasi dichiarazione di livello superiore: fn, struct, import, impl, raw, def, test, ecc.
Decora le funzioni e gli struct per modificare il comportamento del compilatore.
| Attributo | Ambito | Descrizione |
|---|---|---|
@must_use |
Fn | Avvereti se il valore di ritorno viene ignorato. |
@deprecated("msg") |
Fn/Struct | Avverti all'uso con 'msg' |
@inline |
Fn | Suggerisci al compilatore di rendere il codice inline |
@noinline |
Fn | Previeni l'inline automatico |
@packed |
Struct | Rimuovi il padding (lett. imbottitura) automatico in mezzo ai campi. |
@align(N) |
Struct | Forza l'allineamento a N byte. |
@constructor |
Fn | Esegui prima di main. |
@destructor |
Fn | Esegue dopo la terminazione di main. |
@unused |
Fn/Var | Sopprimi gli errori di 'variabile inutilizzata' |
@weak |
Fn | Linking dei simboli weak (lett. debole). |
@section("name") |
Fn | Inserisci il codice in una specifica sezione. |
@noreturn |
Fn | La funzione non restituisce valori. (e.g. exit). |
@pure |
Fn | La funzione non ha effetti collaterali (indizio per l'ottimizzazione). |
@cold |
Fn | La funzione è usata poco spesso (indizio per la branch prediction). |
@hot |
Fn | La funzione è usata molto spesso (indizio per l'ottimizzazione). |
@export |
Fn/Struct | Esporta simbolo (visibilità default). |
@global |
Fn | CUDA: Entry point del Kernel (__global__). |
@device |
Fn | CUDA: Funzione del Device (__device__). |
@host |
Fn | CUDA: Funzione dell'Host (__host__). |
@comptime |
Fn | Funzione di supporto disponibile per l'esecuzione al tempo di compilazione. |
@cfg(NAME) |
Qualsiasi | Compilazione condizionale: includi solo se viene passato -DNAME. Supporta not(), any(), all(). |
@derive(...) |
Struct | Implementa automaticamente i tratti. Supporta Debug, Eq (Derivazione Intelligente), Copy, Clone. |
@<custom> |
Any | Passa gli attributi generici direttamente al C (e.g. @flatten, @alias("nome")) |
Zen C supporta un potente sistema di Attributi Personalizzati che ti permettono di utilizzare ogni __attributo__ GCC/Clang direttamente nel tuo codice Zen C. Qualsiasi attributo non riconosciuto dal compilatore Zen C viene trattato come un attributo generico e passato direttamente nel codice C generato.
Ciò fornisce accesso a delle avanzate funzionalità, ottimizzazioni e direttive del linker senza necessitare di un supporto esplicito nel cuore del linguaggio.
Zen C attributes are mapped directly to C attributes:
@name→__attribute__((name))@name(args)→__attribute__((name(args)))@name("string")→__attribute__((name("string")))
Zen C fornisce delle "derivazioni intelligenti" che rispettano le Semantiche di Movimento:
@derive(Eq): Genera un metodo di uguaglianza che prende argomenti per referenza (fn eq(self, other: T*)).- Quando si confrontano due struct non-Copy (
a == b), il compilatore passa automaticamentebper referenza (&b) per non doverlo spostare. - I controlli di uguaglianza ricorsivi preferiscono l'accesso da puntatore per prevenire il trasferimento del proprietario.
- Quando si confrontano due struct non-Copy (
Zen C fornisce supporto di prima-classe per l'assembly inline, traspilando direttamente ad asm con estensioni in stile GCC.
Scrivi assembly grezzo all'interno di blocchi asm. Le stringhe vengono concatenate automaticamente.
asm {
"nop"
"mfence"
}
Impedisci al compilatore di eliminare automaticamente istruzioni assembly (e.g. ottimizzazione) se ciò potrebbe avere ripercussioni.
asm volatile {
"rdtsc"
}
Zen C semplifica la sintassi complessa dei vincoli di GCC con dei binding nominati.
Nota per i lettori italiani: Con 'clobber' si intende la sovrascrizione.
// Sintassi: : out(variable) : in(variable) : clobber(reg)
// Usa una sintassi placeholder (`{variabile}`) per la leggibilità
fn aggiungi_cinque(x: int) -> int {
let risultato: int;
asm {
"mov {x}, {risultato}"
"add $5, {risultato}"
: out(risultato)
: in(x)
: clobber("cc")
}
return risultato;
}
| Tipo | Sintassi | Equivalente GCC |
|---|---|---|
| Output | : out(variabile) |
"=r"(variabile) |
| Input | : in(variabile) |
"r"(variabile) |
| Clobber | : clobber("rax") |
"rax" |
| Memory | : clobber("memoria") |
"memoria" |
Note
Quando si usa la sintassi Intel (via -masm=intel), dovrai assicurarti che la tua build sia configurata correttamente (per esempio, //> cflags: -masm=intel). TCC non supporta la sintassi assembly Intel.
Zen C supporta dei commenti speciali all'inizio del tuo file sorgente che ti permettono di configurare il processo di build senza necessitare di un sistema di build complesso o di un Makefile.
| Direttiva | Argomenti | Descrizione |
|---|---|---|
//> link: |
-lfoo oppure path/to/lib.a |
Linka con una libreria o un file object. |
//> lib: |
path/to/libs |
Aggiunge una directory dove cercare le librerie (-L). |
//> include: |
path/to/headers |
Aggiunge una directory dove cercare i file include (-I). |
//> framework: |
Cocoa |
Linka con un framework macOS. |
//> cflags: |
-Wall -O3 |
Passa flag arbitrare al compilatore C. |
//> define: |
MACRO or KEY=VAL |
Definisci una macro del preprocessore (-D). |
//> pkg-config: |
gtk+-3.0 |
Esegui pkg-config e aggiungi --cflags e --libs. |
//> shell: |
command |
Esegui un comando sulla shell durante il processo di build. |
//> get: |
http://url/file |
Scarica un file se un file specifico non esiste. |
1. OS Guarding (lett. Protezione OS)
Prefissa delle direttive con il nome di un OS per applicarle solo su piattaforme specifiche.
Prefissi supportati: linux:, windows:, macos: (or darwin:).
//> linux: link: -lm
//> windows: link: -lws2_32
//> macos: framework: Cocoa
2. Environment Variable Expansion
Utilizza la sintassi ${VAR} per espandare variabili d'ambiente nelle tue direttive.
//> include: ${HOME}/MiaLibreria/include
//> lib: ${ZC_ROOT}/std
//> include: ./include
//> lib: ./librerie
//> link: -lraylib -lm
//> cflags: -Ofast
//> pkg-config: gtk+-3.0
import "raylib.h"
fn main() { ... }
Le keyword che seguono sono riservate in 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
Gli identifiers seguenti sono riservati poiché sono keyword nello standard 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 offre due modi per interagire con il codice C: Import Trusted (Conveniente) e FFI Esplicita (Sicuro/Preciso).
Puoi importare un header C direttamente usando la parola chiave import con l'estensione .h. Questo tratta l'header come un modulo e assume che tutti i simboli acceduti esistano.
//> link: -lm
import "math.h" as c_math;
fn main() {
// Il compilatore si fida della correttezza; emette 'cos(...)' direttamente
let x = c_math::cos(3.14159);
}
Pro: Zero boilerplate. Accesso immediato a tutto nell'header. Contro: Nessuna sicurezza dei tipi da Zen C (errori catturati dal compilatore C dopo).
Per un controllo rigoroso dei tipi o quando non vuoi includere il testo di un header, usa extern fn.
include <stdio.h> // Emette #include <stdio.h> nel C generato
// Definisci firma rigorosa
extern fn printf(fmt: char*, ...) -> c_int;
fn main() {
printf("Ciao FFI: %d\n", 42); // Controllato nei tipi da Zen C
}
Pro: Zen C assicura che i tipi corrispondano. Contro: Richiede dichiarazione manuale delle funzioni.
import "file.h": Registra l'header come un modulo con nome. Abilita l'accesso implicito ai simboli (es.file::function()).include <file.h>: Emette puramente#include <file.h>nel codice C generato. Non introduce alcun simbolo nel compilatore Zen C; devi usareextern fnper accedervi.
Zen C include una libreria standard (std) che ricopre funzionalità essenziali.
Scopri la documentazione della Libreria Standard
Clicca per vedere tutti i moduli della Libreria Standard
| Modulo | Descrizione | Documentazione |
|---|---|---|
std/bigfloat.zc |
Aritmetica in virgola mobile a precisione arbitraria. | Docs |
std/bigint.zc |
Intero a precisione arbitraria BigInt. |
Docs |
std/bits.zc |
Operazioni bit a bit a basso livello (rotl, rotr, ecc.). |
Docs |
std/complex.zc |
Aritmetica dei numeri complessi Complex. |
Docs |
std/vec.zc |
Array dinamico espandibile Vec<T>. |
Docs |
std/string.zc |
Tipo String allocato sull'Heap con supporto UTF-8. |
Docs |
std/queue.zc |
Coda FIFO (Buffer Circolare). | Docs |
std/map.zc |
Hash Map Generica Map<V>. |
Docs |
std/fs.zc |
Operazioni del File System. | Docs |
std/io.zc |
Standard Input/Output (print/println). |
Docs |
std/option.zc |
Valori opzionali (Some/None). |
Docs |
std/result.zc |
Gestione degli errori (Ok/Err). |
Docs |
std/path.zc |
Manipolazione dei percorsi Cross-platform. | Docs |
std/env.zc |
Variabili d'ambiente del processo. | Docs |
std/net/ |
TCP, UDP, HTTP, DNS, URL. | Docs |
std/thread.zc |
Thread e Sincronizzazione. | Docs |
std/time.zc |
Misuramenti di tempo e sleep. |
Docs |
std/json.zc |
Parsing JSON e serializzazione. | Docs |
std/stack.zc |
Stack LIFO Stack<T>. |
Docs |
std/set.zc |
Hash Set Generico Set<T>. |
Docs |
std/process.zc |
Esecuzione e gestione di processi. | Docs |
std/regex.zc |
Espressioni Regolari (basato su TRE). | Docs |
std/simd.zc |
Tipi di vettore SIMD nativi. | Docs |
Zen C fornisce un Language Server (LSP) e un REPL per migliorare l'esperienza degli sviluppatori.
Il server del linguaggio (LSP) di Zen C supporta le feature standard per l'integrazione con gli editor, esso fornisce:
- Vai alla definizione
- Trova riferimenti
- Informazioni sull'hover
- Completamenti automatici (Nomi di funzioni/struct, Completamento dal punto per i methods/campi)
- Simboli dei documenti (Outline)
- Aiuto con le signature delle funzioni
- Diagnostiche (Errori sintattici/semantici)
Per avviare il server del linguaggio (tipicamente configurato nelle impostazioni LSP del tuo editor):
zc lspIl server comunica via lo Standard I/o (JSON-RPC 2.0).
Il Read-Eval-Print-Loop (REPL, lett. Leggi-Esegui-Stampa-Ripeti) ti permette ti sperimentare con il codice Zen C in maniera interattiva.
zc repl- Coding interattivo: Scrivi espressioni o istruzioni per una esecuzione immediata.
- Storia persistente: I comandi vengono salvati in
~/.zprep_history. - Script di avvio: I comandi di avvio (auto-load) sono salvati in
~/.zprep_init.zc.
| Comande | Descrizione |
|---|---|
:help |
Mostra i comandi disponibili. |
:reset |
Cancella la storia della sessione corrente (variabili/funzioni). |
:vars |
Mostra le variabili attive. |
:funcs |
Mostra le funzioni definite dall'utente. |
:structs |
Mostra gli struct definiti dall'utente. |
:imports |
Mostra gli 'import' attivi. |
:history |
Mostra la storia dell'input della sessione. |
:type <expr> |
Mostra il tipo di un espressione. |
:c <stmt> |
Mostra il codice C generato per un istruzione. |
:time <expr> |
Esegui un benchmark per l'espressione data. (Esegue 1000 iterazioni). |
:edit [n] |
Modifica il comando n (default: l'ultimo comando) in $EDITOR. |
:save <file> |
Salva la sessione corrente in un file .zc. |
:load <file> |
Carica ed esegui un file .zc nella sessione corrente. |
:watch <expr> |
Watch (lett. guarda) un espressione (rieseguita dopo ogni entry). |
:unwatch <n> |
Rimuovi un watch. |
:undo |
Rimuovi l'ultimo comando dalla sessione. |
:delete <n> |
Rimuovi il comando all'indice n. |
:clear |
Pulisce lo schermo. |
:quit |
Esce dal REPL. |
! <cmd> |
Esegue un comando sulla shell (e.g. !ls). |
Zen C include un Server di Linguaggio integrato per l'integrazione con gli editor.
- Guida all'Installazione e Configurazione
- Editor Supportati: VS Code, Neovim, Vim, Zed, e qualsiasi editor compatibile con LSP.
Usa zc lsp per avviare il server.
I programmi Zen C possono essere sottoposti a debug utilizzando i debugger C standard come LLDB o GDB.
Per la migliore esperienza in VS Code, installa l'estensione ufficiale Zen C. Per il debugging, puoi utilizzare l'estensione C/C++ (di Microsoft) o CodeLLDB.
Aggiungi queste configurazioni alla tua directory .vscode per abilitare il debugging con un clic:
tasks.json (Attività di compilazione):
{
"label": "Zen C: Build Debug",
"type": "shell",
"command": "zc",
"args": [ "${file}", "-g", "-o", "${fileDirname}/app", "-O0" ],
"group": { "kind": "build", "isDefault": true }
}launch.json (Debugger):
{
"name": "Zen C: Debug (LLDB)",
"type": "lldb",
"request": "launch",
"program": "${fileDirname}/app",
"preLaunchTask": "Zen C: Build Debug"
}Zen C è stato creato in modo tale da poter funzionare con la maggior parte dei compilatori C11. Alcune funzionalità potrebbero affidarsi ad estensioni GNU C, ma spesso queste funzionano anche su altri compilatori. Utilizza la flag --cc per modificare il backend.
zc run app.zc --cc clang
zc run app.zc --cc zigClicca per vedere i dettagli del supporto del compilatore
| Compilatore | Percentuale di Superamento | Funzionalità Supportate | Limitazioni Nota |
|---|---|---|---|
| GCC | 100% (Completo) | Tutte le funzionalità | Nessuna. |
| Clang | 100% (Completo) | Tutte le funzionalità | Nessuna. |
| Zig | 100% (Completo) | Tutte le funzionalità | Nessuna. Usa zig cc come compilatore C. |
| TCC | 98% (Alto) | Strutture, Generici, Tratti, Pattern Matching | Niente ASM Intel, Niente __attribute__((constructor)). |
Warning
AVVISO DI COMPILAZIONE: Sebbene Zig CC funzioni ottimamente come backend per i tuoi programmi Zen C, compilare il compilatore Zen C stesso con esso potrebbe verificare ma produrre un binario instabile che fallisce i test. Consigliamo di compilare il compilatore con GCC o Clang e usare Zig solo come backend per il tuo codice operativo.
Il comando zig cc di Zig fornisce un rimpiazzamento drop-in per GCC/Clang con eccellente supporto per la cross-compilation. Per usare Zig:
# Compila ed esegui un programma Zen C con Zig
zc run app.zc --cc zig
# Puoi compilare persino il compilatore Zen C stesso con Zig
make zigZen C può generare codice compatibile con C++ utilizzando l'opzione --cpp, permettendo una integrazione fluida con le librerie C++.
# Compilazione diretta con g++
zc app.zc --cpp
# O traspila per le build manuali
zc transpile app.zc --cpp
g++ out.c my_cpp_lib.o -o appIncludi header C++ e usa blocchi grezzi per codice C++:
include <vector>
include <iostream>
raw {
std::vector<int> crea_vettore(int a, int b) {
return {a, b};
}
}
fn main() {
let v = crea_vettore(1, 2);
raw { std::cout << "Dimensione: " << v.size() << std::endl; }
}
Nota: L'opzione
--cpprende il backendg++ed emette codice valido per C++ (utilizzaautoal posto di__auto_type, overload delle funzioni al posto di_Generice i cast espliciti pervoid*)
Zen C supporta la programmazione GPU traspilando a CUDA C++. Questo ti permette di utilizzare potenti funzionalità C++ (template, constexpr) all'interno dei tuoi kernel mantenendo la sintassi ergonomica di Zen C.
# Compilazione diretta con nvcc
zc run app.zc --cuda
# O traspila per le build manuali
zc transpile app.zc --cuda -o app.cu
nvcc app.cu -o app| Attributo | Equivalente CUDA | Descrizione |
|---|---|---|
@global |
__global__ |
Function Kernel (esegue sulla GPU, chiamato dall'host) |
@device |
__device__ |
Funzione Device (esegue sulla GPU, chiamato dalla GPU) |
@host |
__host__ |
Funzione Host (Solo CPU esplicita) |
Zen C fornisce un'istruzione chiara launch per richiamare kernel CUDA:
launch kernel_name(args) with {
grid: num_blocks,
block: threads_per_block,
shared_mem: 1024, // Opzionale
stream: my_stream // Opzionale
};
Questo traspila a: kernel_name<<<grid, block, shared, stream>>>(args);
Utilizza la sintassi delle funzioni Zen C con @global e l'istruzione launch:
import "std/cuda.zc"
@global
fn aggiungi_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 aggiungi_kernel(d_a, d_b, d_c, N) with {
grid: (N + 255) / 256,
block: 256
};
cuda_sync();
}
Zen C fornisce una libreria standard per delle operazioni comuni in CUDA per ridurre la mole di blocchi raw (grezzi):
import "std/cuda.zc"
// Gestione della memoria
let d_ptr = cuda_alloc<float>(1024);
cuda_copy_to_device(d_ptr, h_ptr, 1024 * sizeof(float));
defer cuda_free(d_ptr);
// Sincronizzazione
cuda_sync();
// Indicizzazione dei thread (usa all'interno del kernel)
let i = thread_id(); // Indice globale
let bid = block_id();
let tid = local_id();
Note
Nota: La flag --cuda imposta nvcc come compilatore e implica la modalità --cpp. Richiede l'installazione dell'NVIDIA CUDA Toolkit.
Zen C supporta le funzionalità moderne dello standard C23 quando si usa un backend compatibile (GCC 14+, Clang 14+, TCC (parziale)).
auto: Zen C mappa automaticamente l'inferenza del tipo alla keywordautodi C23 (se__STDC_VERSION__ >= 202300L)._BitInt(N): Usa i tipiiNeuN(e.g.,i256,u12,i24) per accedere agli interi di lunghezza arbitraria di C23.
Zen C può compilare a Objective-C (.m) utilizzando la flag --objc, permettendoti di utilizzare i framework (come Cocoa/Foundation) e la sintassi Obj-C
# Compila con clang (o gcc/gnustep)
zc app.zc --objc --cc clangUtilizza include per gli header e i blocchi raw per la sintassi Obj-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(@"Ciao da Objective-C!");
[pool drain];
}
println "Funziona anche Zen C!";
}
Note
Nota: L'interpolazione delle stringhe di Zen C funziona con gli oggetti dell'Objective-C (id) chiamando debugDescription oppure description.
Qui accogliamo tutti i contributi! Che siano fix di bug, miglioramenti alla documentazione, o la proposta di nuove funzionalità.
Per favore, consulta CONTRIBUTING_IT.md per le linee guida dettagliate su come contribuire, eseguire i test e inviare pull request.
Per istruzioni sulla segnalazione di vulnerabilità, vedi SECURITY_IT.md.
Questo progetto utilizza librerie esterne. I testi di licenza completi possono essere trovati nella directory LICENSES/.
- cJSON (Licenza MIT): Usato per il parsing e la generazione di JSON nel Language Server.
- zc-ape (Licenza MIT): La versione originale di Actually Portable Executable di Zen-C, realizzata da Eugene Olonov.
- Cosmopolitan Libc (Licenza ISC): La libreria fondamentale che rende possibile APE.
- TRE (Licenza BSD): Usato per il motore di espressioni regolari nella libreria standard.
- zenc.vim (Licenza MIT): Il plugin ufficiale per Vim/Neovim, scritto principalmente da davidscholberg.
Copyright © 2026 Zen C Programming Language.
Inizia il tuo viaggio oggi.
Discord • GitHub • Documentazione • Esempi • RFC • Contribuisci