Ridgun
The language itself: a compiler that compiles itself, the stable project tool, the specification and the book
| 1 | # Capitolo 02: I tuoi due migliori amici: `ridgunc` e `stable` (Zero file YAML, promesso) |
| 2 | |
| 3 | Ci sono linguaggi il cui compilatore è meraviglioso, ma il cui sistema di progetti sembra progettato dal tuo peggior nemico: ora un `Makefile` di 400 righe con tabulazioni invisibili, ora un `pom.xml` infinito, ora un `webpack.config.js` che smette di funzionare se lo guardi fisso... |
| 4 | |
| 5 | In Ridgun ci siamo fatti una promessa semplicissima fin dal primo giorno: |
| 6 | |
| 7 | > *"Ci saranno solo due eseguibili in tutto l'ecosistema (`ridgunc` e `stable`), entrambi saranno scritti nello stesso Ridgun, e il file di configurazione del progetto (`build.rg`) sarà normale codice Ridgun. Né JSON, né YAML, né TOML, né invenzioni strane."* |
| 8 | |
| 9 | Vediamo come si usano. |
| 10 | |
| 11 | --- |
| 12 | |
| 13 | ## 2.1. `ridgunc`: Il cecchino per file singoli |
| 14 | |
| 15 | Quando hai per le mani un singolo file `.rg` e vuoi provare un'idea veloce, analizzarne i tipi o compilare un eseguibile, parli direttamente con **`ridgunc`**. |
| 16 | |
| 17 | Ha solo 5 comandi che devi davvero ricordare: |
| 18 | |
| 19 | ```bash |
| 20 | # 1. "Compila questo o ho combinato un pasticcio con qualche tipo?" |
| 21 | ridgunc check esperimento.rg |
| 22 | |
| 23 | # 2. "Eseguilo subito!" (Compila ed esegue in un colpo solo) |
| 24 | ridgunc run esperimento.rg |
| 25 | |
| 26 | # 3. "Fammi un binario nativo ELF64 pronto da portare via" |
| 27 | ridgunc build esperimento.rg -o mio-programma |
| 28 | |
| 29 | # 4. "Compilami una libreria riutilizzabile (.rglib e .so ELF64 senza bisogno di main)" |
| 30 | ridgunc build --lib utilita.rg -o target/utilita.rglib |
| 31 | ridgunc build --so utilita.rg -o target/libutilita.so |
| 32 | |
| 33 | # 5. "Tirami fuori l'artefatto intermedio di bytecode (.rgc)" |
| 34 | ridgunc compile esperimento.rg -o esperimento.rgc |
| 35 | ``` |
| 36 | |
| 37 | ### Il superpotere nascosto di `ridgunc check` |
| 38 | Quando lanci `ridgunc check`, non ti dice solo *"0 errori"*. Ti svela anche quale strategia ha scelto l'analizzatore statico per ciascuna delle tue funzioni: |
| 39 | - **`InlineSequential`**: *"Questa funzione è piccolina e diretta; la eseguirò in linea senza aggiungere sovraccarico di pianificazione"*. |
| 40 | - **`LazyStealableTask`**: *"Qui c'è ricorsione o un grafo parallelo `||`; la preparo affinché altri core della CPU possano rubare lavoro se necessario"*. |
| 41 | |
| 42 | ### Il bisturi di `ridgunc build` (Perché i tuoi binari non pesano 20 MB) |
| 43 | Quando esegui `ridgunc build`, il compilatore non si limita a sputare fuori tutto il codice delle librerie che hai importato: |
| 44 | 1. Parte dalla tua funzione `main` e percorre il grafo delle chiamate per vedere **quali funzioni usi davvero**. Se hai importato una libreria enorme da 80 funzioni ma ne chiami solo 2, le altre 78 vengono scartate prima ancora di toccare l'assembler (*Tree-Shaking*). |
| 45 | 2. Cerca tutte le funzioni ricorsive di coda e le converte in cicli `loop` nativi che consumano **0 byte di stack**. |
| 46 | 3. Passa il collegamento finale con ottimizzazione globale (*fat LTO*) e pulisce le informazioni di debug DWARF superflue per lasciarti un binario compatto e affilato (~350 KB). |
| 47 | |
| 48 | --- |
| 49 | |
| 50 | ## 2.2. `stable`: Quando il tuo progetto cresce e vuoi ordine |
| 51 | |
| 52 | Un file singolo è fantastico per uno script, ma non appena hai più moduli, librerie condivise e test, hai bisogno di un direttore d'orchestra. Quello è **`stable`**. |
| 53 | |
| 54 | La struttura delle cartelle di un progetto Ridgun non ha misteri: |
| 55 | |
| 56 | ```text |
| 57 | mio-super-strumento/ |
| 58 | ├── build.rg # Il manifesto del progetto (è codice Ridgun!) |
| 59 | ├── src/ |
| 60 | │ ├── main/ |
| 61 | │ │ └── ridgun/ |
| 62 | │ │ ├── lib/ # I tuoi moduli riutilizzabili (`import utilita`) |
| 63 | │ │ └── app/ |
| 64 | │ │ └── main.rg # Punto di ingresso (`main => ()`) |
| 65 | │ └── test/ |
| 66 | │ └── ridgun/ # Le tue batterie di test (`test *.rg`) |
| 67 | │ └── test tutto.rg |
| 68 | └── target/ # Dove compaiono i binari compilati |
| 69 | ``` |
| 70 | |
| 71 | --- |
| 72 | |
| 73 | ## 2.3. `build.rg`: Un file di configurazione che non ti mentirà mai |
| 74 | |
| 75 | Perché ci rifiutiamo di usare YAML o TOML per configurare un progetto Ridgun? |
| 76 | Perché in YAML sbagli due spazi di indentazione e non te ne accorgi finché non fallisce il deploy. Invece, poiché **`build.rg` è un file Ridgun**, ha tipi statici, passa attraverso `stable check` e si allinea da solo con `stable format`: |
| 77 | |
| 78 | ```ridgun |
| 79 | fn :: project name => String |
| 80 | project name = "mio-super-strumento" |
| 81 | |
| 82 | fn :: project version => String |
| 83 | project version = "0.1.0" |
| 84 | |
| 85 | -- "exe" (eseguibile), "lib" (libreria .rglib + .so) o "both" (entrambi) |
| 86 | fn :: project kind => String |
| 87 | project kind = "both" |
| 88 | |
| 89 | -- Opzionale: modalità stretta (proibisce '_' nei nomi fn/op ed esige ritorni '=> Int Int') |
| 90 | fn :: strict mode => Bool |
| 91 | strict mode = True |
| 92 | |
| 93 | fn :: main entry => String |
| 94 | main entry = "src/main/ridgun/app/main.rg" |
| 95 | |
| 96 | fn :: source dir => String |
| 97 | source dir = "src/main/ridgun" |
| 98 | |
| 99 | fn :: test dir => String |
| 100 | test dir = "src/test/ridgun" |
| 101 | |
| 102 | fn :: target dir => String |
| 103 | target dir = "target" |
| 104 | |
| 105 | fn :: dependencies => [String] |
| 106 | dependencies = [] |
| 107 | |
| 108 | fn :: plugins => [String] |
| 109 | plugins = [] |
| 110 | |
| 111 | fn :: task verificare tutto => String |
| 112 | task verificare tutto = "./bin/stable check && ./bin/stable test" |
| 113 | ``` |
| 114 | |
| 115 | Se sbagli a scrivere un campo o un tipo, è lo stesso compilatore ad avvisarti. E nota che tutte le funzioni di configurazione usano **spazi naturali** (`project name`, `project kind`, `source dir`, `task verificare tutto`) invece di trattini bassi. |
| 116 | |
| 117 | --- |
| 118 | |
| 119 | ## 2.4. La tua routine quotidiana con `stable` (I fantastici 4) |
| 120 | |
| 121 | Quando lavori all'interno del tuo progetto, la tua giornata si riduce a quattro comandi: |
| 122 | |
| 123 | ### 1. `stable format` — La fine delle discussioni sullo stile nel team |
| 124 | ```bash |
| 125 | stable format |
| 126 | ``` |
| 127 | Il tuo collega mette tre spazi prima di `=>` e tu ne metti uno? Non importa. Esegui `stable format` e in meno di un secondo lascia **tutti** i file `.rg` del repository allineati come una tabella svizzera. |
| 128 | |
| 129 | ### 2. `stable check` — Il radar istantaneo |
| 130 | ```bash |
| 131 | stable check |
| 132 | ``` |
| 133 | Percorre tutti i moduli di `src/main/ridgun/`, verifica tipi, costruttori, arità e dimensioni dei tensori, e ti dà il verdetto modulo per modulo. |
| 134 | |
| 135 | ### 3. `stable test` — Dormire tranquilli la notte |
| 136 | ```bash |
| 137 | stable test |
| 138 | ``` |
| 139 | Cerca tutte le suite `src/test/ridgun/test *.rg`, le esegue e controlla ogni `assert equals` e `assert true`. |
| 140 | |
| 141 | ```ridgun |
| 142 | -- Esempio di src/test/ridgun/test matematica.rg |
| 143 | import test |
| 144 | |
| 145 | doppio Int => Int |
| 146 | doppio x = x * 2 |
| 147 | |
| 148 | main => () |
| 149 | main = |
| 150 | assert equals (doppio 21) 42 |
| 151 | assert true ((doppio 5) > 0) |
| 152 | ``` |
| 153 | |
| 154 | ### 4. `stable built` (e `stable clean`) — In produzione |
| 155 | ```bash |
| 156 | stable built |
| 157 | ``` |
| 158 | Compila tutti gli obiettivi nativi in `target/`. E se un giorno vuoi cancellare `target/` e ripartire da zero: `stable clean`. |
| 159 | |
| 160 | --- |
| 161 | |
| 162 | ## 2.5. Creare e usare Librerie Riutilizzabili (`.rglib` e `.so` ELF64) e Plugin di `stable` |
| 163 | |
| 164 | Una libreria non ha la funzione `main => ()`: offre tipi (`type`, `expand type`), funzioni (`fn ::`) e operatori (`op ::`) affinché altri progetti li importino. |
| 165 | |
| 166 | ### 1. Creare un progetto di libreria con `stable` |
| 167 | ```bash |
| 168 | stable new --lib geometria |
| 169 | cd geometria |
| 170 | stable built lib |
| 171 | ``` |
| 172 | Compilando in modalità libreria (`project kind = "lib"` o `stable built lib`), il compilatore disattiva la potatura da `main` per conservare **tutte** le funzioni e i tipi esportati, e genera due artefatti in `target/`: |
| 173 | - **`target/geometria.rglib`**: pacchetto autocontenuto di libreria Ridgun (con tutti i suoi sorgenti e dipendenze interne risolte), pronto per essere importato da qualsiasi altro progetto con `import geometria` o `import "target/geometria.rglib"`. |
| 174 | - **`target/libgeometria.so`**: libreria dinamica nativa ELF64 (`ET_DYN`, per `x86_64` o `arm64-linux`). |
| 175 | |
| 176 | ```ridgun |
| 177 | -- src/main/ridgun/lib/geometria.rg (Senza funzione main!) |
| 178 | type Punto = x: Int, y: Int |
| 179 | |
| 180 | fn :: sposta punto Punto Int Int => Punto |
| 181 | sposta punto p dx dy = Punto ((x p) + dx) ((y p) + dy) |
| 182 | |
| 183 | fn :: coordinate punto Punto => Int Int |
| 184 | coordinate punto p = (x p, y p) |
| 185 | ``` |
| 186 | |
| 187 | ### 2. Come importo un modulo o una libreria da un altro file? (`import`) |
| 188 | |
| 189 | Senza percorsi relativi infernali tipo `../../../lib/cose.rg`. Se hai un modulo `src/main/ridgun/lib/geometria.rg` o un pacchetto `geometria.rglib` (in `target/`, `lib/` o `.stable/deps/` dopo aver eseguito `stable deps`), in qualsiasi altro file scrivi in cima: |
| 190 | |
| 191 | ```ridgun |
| 192 | import geometria |
| 193 | ``` |
| 194 | |
| 195 | E fatto: tutte le funzioni, gli operatori e i tipi di `geometria` sono disponibili come se fossero nel tuo stesso file. Se più moduli fanno `import geometria`, il caricatore di Ridgun ricorda quali file canonici ha già letto e **lo include una sola volta**. |
| 196 | |
| 197 | ### 3. Estendere `stable` con attività (`fn :: task`) e plugin (`plugins/*.rg`) |
| 198 | Vuoi aggiungere comandi personalizzati per il tuo framework web, le tue migrazioni di database o il tuo flusso di deploy? |
| 199 | - **In `build.rg`**: dichiara qualsiasi funzione `fn :: task <parole...> => String` (ad esempio, `fn :: task avvia server => String`) ed eseguila con `stable avvia server`. |
| 200 | - **In `plugins/*.rg` o `.stable/plugins/*.rg`**: inserisci moduli `.rg` che definiscano attività `fn :: task ...` o il proprio `fn :: main => ()`, oppure estendi il tipo aperto `expand type StableCommand` di `stable`. Esegui `stable tasks` e `stable plugins` per ispezionarli in qualsiasi momento. |
| 201 | |
| 202 | ### 4. La modalità stretta opzionale (`--strict` e `strict mode = True`) |
| 203 | Per impostazione predefinita, né `ridgunc` né i nuovi progetti creati con `stable new` o `stable init` attivano la modalità stretta. Tuttavia, quando vuoi garantire nel tuo progetto che tutto il team segua al 100% la convenzione canonica degli spazi naturali di Ridgun, puoi passare `--strict` a `ridgunc` (`ridgunc check --strict`, `ridgunc build --strict`) o dichiarare `strict mode = True` nel tuo `build.rg`. La modalità stretta trasforma in **errore di compilazione**: |
| 204 | 1. Qualsiasi trattino basso `_` all'interno del nome di una dichiarazione `fn ::` o `op ::` (dovendo scrivere `fn :: calcola totale`, mai `fn :: calcola_totale`; le variabili locali e i campi dei record possono invece usare `_`). |
| 205 | 2. Qualsiasi tipo di ritorno multiplo scritto con parentesi o virgole dopo `=>` (dovendo scrivere `=> Int Int` o `=> RToken [RToken]`, mai `=> (Int, Int)`). |
| 206 | |
| 207 | --- |
| 208 | |
| 209 | ## 2.6. L'Ecosistema di Librerie Standard e Crittografiche (`ridgun-libs`) |
| 210 | |
| 211 | Ridgun dispone di una suite ufficiale di **32 librerie pure** (`ridgun-libs`), compilate come pacchetti `.rglib` e librerie dinamiche `.so` ELF64 pronte da importare con `import <nome>`: |
| 212 | |
| 213 | | Famiglia | Librerie (`import ...`) | Cosa portano nei tuoi progetti | |
| 214 | |---|---|---| |
| 215 | | **1. Testo, Formati e Template** | `str`, `fmt`, `regex`, `diff`, `template`, `json`, `csv`, `ini`, `codec` | Manipolazione avanzata di stringhe, formattazione tabellare, espressioni regolari Thompson NFA, differenze Myers (`diff`), template senza logica, parser/serializzatori `json`/`csv`/`ini` e codifica esadecimale/Base64 (`codec`). | |
| 216 | | **2. Collezioni, Matematica e Algebra Tensoriale** | `list`, `btree`, `math`, `tensor`, `linalg` | Operazioni funzionali su liste, B-tree ordinati in memoria (`btree`), aritmetica avanzata (`math`), viste e costruttori di tensori tipizzati (`tensor`) e decomposizione/algebra lineare (`linalg`). | |
| 217 | | **3. Sistema Operativo, File e CLI** | `posix`, `io`, `path`, `glob`, `cli`, `time`, `log`, `uuid`, `test` | Descrittori e segnali POSIX (`posix`), I/O con `with timeout` (`io`), percorsi e pattern (`path`, `glob`), argomenti CLI tipizzati (`cli`), orologi monotonici (`time`), logging strutturato (`log`), RFC 4122 `uuid` e asserzioni (`test`). | |
| 218 | | **4. Crittografia e Sicurezza** | `crypto`, `argon2id`, `jwt` | SHA-256 binario diretto (`sha256 raw` / `sha256 bytes` in 32 byte grezzi), HMAC-SHA-256, PBKDF2-HMAC-SHA-256, HKDF, ChaCha20-Poly1305 AEAD (`crypto`), hashing delle password RFC 9106 `argon2id` su `Tensor[U64 * 8 * 16]` con permutazione BlaMka senza allocare tuple nell'heap (`argon2id`), e token firmati (`jwt`). | |
| 219 | | **5. Reti, Protocolli e Agenti** | `socket`, `http`, `ws`, `agent`, `pool`, `net_agent` | Socket TCP/UDP POSIX (`socket`), client/server HTTP/1.1 (`http`), WebSocket RFC 6455 (`ws`), supervisori di agenti (`agent`), pool di worker (`pool`) e server reattivi orientati agli agenti (`net_agent`). | |
| 220 | |
| 221 | Ora che hai la cassetta degli attrezzi e le librerie sotto controllo, passiamo al **Capitolo 03** per padroneggiare le funzioni con spazi naturali, le firme dei tipi e i blocchi. |