Ridgun
The language itself: a compiler that compiles itself, the stable project tool, the specification and the book
| 1 | # Capítulo 02: Tus dos mejores amigos: `ridgunc` y `stable` (Cero archivos YAML, prometido) |
| 2 | |
| 3 | Hay lenguajes cuyo compilador es maravilloso, pero cuyo sistema de proyectos parece diseñado por tu peor enemigo: que si un `Makefile` de 400 líneas con tabuladores invisibles, que si un `pom.xml` infinito, que si un `webpack.config.js` que deja de funcionar si lo miras fijo... |
| 4 | |
| 5 | En Ridgun nos hicimos una promesa muy simple desde el primer día: |
| 6 | |
| 7 | > *"Sólo habrá dos ejecutables en todo el ecosistema (`ridgunc` y `stable`), ambos estarán escritos en el propio Ridgun, y el archivo de configuración del proyecto (`build.rg`) será código Ridgun normal y corriente. Ni JSON, ni YAML, ni TOML, ni inventos raros."* |
| 8 | |
| 9 | Vamos a ver cómo se usan. |
| 10 | |
| 11 | --- |
| 12 | |
| 13 | ## 2.1. `ridgunc`: El francotirador para archivos sueltos |
| 14 | |
| 15 | Cuando tienes un único archivo `.rg` entre manos y quieres probar una idea rápida, analizar sus tipos o compilar un ejecutable, hablas directamente con **`ridgunc`**. |
| 16 | |
| 17 | Sólo tiene 4 comandos que de verdad necesitas recordar: |
| 18 | |
| 19 | ```bash |
| 20 | # 1. "¿Compila esto o he metido la pata con algún tipo?" |
| 21 | ridgunc check experimento.rg |
| 22 | |
| 23 | # 2. "¡Ejecútalo ya!" (Compila y corre de un tirón) |
| 24 | ridgunc run experimento.rg |
| 25 | |
| 26 | # 3. "Hazme un binario nativo ELF64 listo para llevar" |
| 27 | ridgunc build experimento.rg -o mi-programa |
| 28 | |
| 29 | # 4. "Compílame una librería reutilizable (.rglib y .so ELF64 sin necesitar main)" |
| 30 | ridgunc build --lib utilidades.rg -o target/utilidades.rglib |
| 31 | ridgunc build --so utilidades.rg -o target/libutilidades.so |
| 32 | |
| 33 | # 5. "Sácame el artefacto intermedio de bytecode (.rgc)" |
| 34 | ridgunc compile experimento.rg -o experimento.rgc |
| 35 | ``` |
| 36 | |
| 37 | ### El superpoder oculto de `ridgunc check` |
| 38 | Cuando lanzas `ridgunc check`, no solo te dice *"0 errores"*. También te chiva qué estrategia ha elegido el analizador estático para cada una de tus funciones: |
| 39 | - **`InlineSequential`**: *"Esta función es pequeñita y directa; la voy a ejecutar en línea sin meter sobrecoste de planificación"*. |
| 40 | - **`LazyStealableTask`**: *"Aquí hay recursión o un grafo paralelo `||`; la preparo para que otros núcleos de CPU puedan robar trabajo si hace falta"*. |
| 41 | |
| 42 | ### El bisturí de `ridgunc build` (Por qué tus binarios no pesan 20 MB) |
| 43 | Cuando ejecutas `ridgunc build`, el compilador no se limita a escupir todo el código de las librerías que hayas importado: |
| 44 | 1. Empieza en tu función `main` y recorre el grafo de llamadas para ver **qué funciones usas de verdad**. Si importaste una librería enorme de 80 funciones pero solo llamas a 2, las otras 78 se descartan antes siquiera de tocar el ensamblador (*Tree-Shaking*). |
| 45 | 2. Busca todas las funciones recursivas de cola y las convierte en bucles `loop` nativos que consumen **0 bytes de pila**. |
| 46 | 3. Pasa el enlace final con optimización global (*fat LTO*) y limpia la basura de depuración DWARF para dejarte un binario compacto y afilado (~350 KB). |
| 47 | |
| 48 | --- |
| 49 | |
| 50 | ## 2.2. `stable`: Cuando tu proyecto crece y quieres orden |
| 51 | |
| 52 | Un archivo suelto está genial para un script, pero en cuanto tienes varios módulos, librerías compartidas y tests, necesitas un director de orquesta. Ese es **`stable`**. |
| 53 | |
| 54 | La estructura de carpetas de un proyecto Ridgun no tiene misterio: |
| 55 | |
| 56 | ```text |
| 57 | mi-super-herramienta/ |
| 58 | ├── build.rg # El manifiesto del proyecto (¡es código Ridgun!) |
| 59 | ├── src/ |
| 60 | │ ├── main/ |
| 61 | │ │ └── ridgun/ |
| 62 | │ │ ├── lib/ # Tus módulos reutilizables (`import utilidades`) |
| 63 | │ │ └── app/ |
| 64 | │ │ └── main.rg # Punto de entrada (`main => ()`) |
| 65 | │ └── test/ |
| 66 | │ └── ridgun/ # Tus baterías de tests (`test *.rg`) |
| 67 | │ └── test todo.rg |
| 68 | └── target/ # Donde aparecen los binarios compilados |
| 69 | ``` |
| 70 | |
| 71 | --- |
| 72 | |
| 73 | ## 2.3. `build.rg`: Un archivo de configuración que nunca te mentirá |
| 74 | |
| 75 | ¿Por qué nos negamos a usar YAML o TOML para configurar un proyecto Ridgun? |
| 76 | Porque en YAML te equivocas en dos espacios de indentación y no te enteras hasta que falla el despliegue. En cambio, como **`build.rg` es un archivo Ridgun**, tiene tipos estáticos, pasa por `stable check` y se alinea solo con `stable format`: |
| 77 | |
| 78 | ```ridgun |
| 79 | fn :: project name => String |
| 80 | project name = "mi-super-herramienta" |
| 81 | |
| 82 | fn :: project version => String |
| 83 | project version = "0.1.0" |
| 84 | |
| 85 | -- "exe" (ejecutable), "lib" (librería .rglib + .so) o "both" (ambos) |
| 86 | fn :: project kind => String |
| 87 | project kind = "both" |
| 88 | |
| 89 | -- Opcional: modo estricto (prohíbe '_' en nombres fn/op y exige retornos '=> 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 verificar todo => String |
| 112 | task verificar todo = "./bin/stable check && ./bin/stable test" |
| 113 | ``` |
| 114 | |
| 115 | Si te equivocas al escribir un campo o un tipo, el propio compilador te avisa. Y fíjate en que todas las funciones de configuración usan **espacios naturales** (`project name`, `project kind`, `source dir`, `task verificar todo`) en lugar de guiones bajos. |
| 116 | |
| 117 | --- |
| 118 | |
| 119 | ## 2.4. Tu rutina diaria con `stable` (Los 4 fantásticos) |
| 120 | |
| 121 | Cuando estás trabajando dentro de tu proyecto, tu día a día se reduce a cuatro comandos: |
| 122 | |
| 123 | ### 1. `stable format` — El fin de las discusiones de estilo en el equipo |
| 124 | ```bash |
| 125 | stable format |
| 126 | ``` |
| 127 | ¿Que tu compañero pone tres espacios antes del `=>` y tú pones uno? Da igual. Ejecutas `stable format` y en menos de un segundo deja **todos** los archivos `.rg` del repositorio alineados como una tabla suiza. |
| 128 | |
| 129 | ### 2. `stable check` — El radar instantáneo |
| 130 | ```bash |
| 131 | stable check |
| 132 | ``` |
| 133 | Recorre todos los módulos de `src/main/ridgun/`, verifica tipos, constructores, aridades y dimensiones de tensores, y te da el veredicto módulo por módulo. |
| 134 | |
| 135 | ### 3. `stable test` — Dormir tranquilo por las noches |
| 136 | ```bash |
| 137 | stable test |
| 138 | ``` |
| 139 | Busca todas las suites `src/test/ridgun/test *.rg`, las ejecuta y comprueba cada `assert equals` y `assert true`. |
| 140 | |
| 141 | ```ridgun |
| 142 | -- Ejemplo de src/test/ridgun/test mates.rg |
| 143 | import test |
| 144 | |
| 145 | doble Int => Int |
| 146 | doble x = x * 2 |
| 147 | |
| 148 | main => () |
| 149 | main = |
| 150 | assert equals (doble 21) 42 |
| 151 | assert true ((doble 5) > 0) |
| 152 | ``` |
| 153 | |
| 154 | ### 4. `stable built` (y `stable clean`) — A producción |
| 155 | ```bash |
| 156 | stable built |
| 157 | ``` |
| 158 | Compila todos los objetivos nativos en `target/`. Y si algún día quieres borrar `target/` y empezar limpio: `stable clean`. |
| 159 | |
| 160 | --- |
| 161 | |
| 162 | ## 2.5. Crear y usar Librerías Reutilizables (`.rglib` y `.so` ELF64) y Plugins de `stable` |
| 163 | |
| 164 | Una librería no tiene función `main => ()`: ofrece tipos (`type`, `expand type`), funciones (`fn ::`) y operadores (`op ::`) para que otros proyectos los importen. |
| 165 | |
| 166 | ### 1. Crear un proyecto de librería con `stable` |
| 167 | ```bash |
| 168 | stable new --lib geometria |
| 169 | cd geometria |
| 170 | stable built lib |
| 171 | ``` |
| 172 | Al compilar en modo librería (`project kind = "lib"` o `stable built lib`), el compilador desactiva la poda desde `main` para conservar **todas** las funciones y tipos exportados, y genera dos artefactos en `target/`: |
| 173 | - **`target/geometria.rglib`**: paquete autocontenido de librería Ridgun (con todas sus fuentes y dependencias internas resueltas), listo para importar desde cualquier otro proyecto con `import geometria` o `import "target/geometria.rglib"`. |
| 174 | - **`target/libgeometria.so`**: biblioteca dinámica nativa ELF64 (`ET_DYN`, para `x86_64` o `arm64-linux`). |
| 175 | |
| 176 | ```ridgun |
| 177 | -- src/main/ridgun/lib/geometria.rg (¡Sin función main!) |
| 178 | type Punto = x: Int, y: Int |
| 179 | |
| 180 | fn :: desplazar punto Punto Int Int => Punto |
| 181 | desplazar punto p dx dy = Punto ((x p) + dx) ((y p) + dy) |
| 182 | |
| 183 | fn :: coordenadas punto Punto => Int Int |
| 184 | coordenadas punto p = (x p, y p) |
| 185 | ``` |
| 186 | |
| 187 | ### 2. ¿Cómo importo un módulo o librería desde otro archivo? (`import`) |
| 188 | |
| 189 | Sin rutas relativas infernales tipo `../../../lib/cosas.rg`. Si tienes un módulo `src/main/ridgun/lib/geometria.rg` o un paquete `geometria.rglib` (en `target/`, `lib/` o `.stable/deps/` tras ejecutar `stable deps`), en cualquier otro archivo escribes arriba del todo: |
| 190 | |
| 191 | ```ridgun |
| 192 | import geometria |
| 193 | ``` |
| 194 | |
| 195 | Y listo: todas las funciones, operadores y tipos de `geometria` están disponibles como si estuvieran en tu propio archivo. Si varios módulos hacen `import geometria`, el cargador de Ridgun recuerda qué archivos canónicos ha leído ya y **solo lo incluye una vez**. |
| 196 | |
| 197 | ### 3. Extender `stable` con tareas (`fn :: task`) y plugins (`plugins/*.rg`) |
| 198 | ¿Quieres añadir comandos propios para tu framework web, tus migraciones de base de datos o tu flujo de despliegue? |
| 199 | - **En `build.rg`**: declara cualquier función `fn :: task <palabras...> => String` (por ejemplo, `fn :: task levantar servidor => String`) y ejecútala con `stable levantar servidor`. |
| 200 | - **En `plugins/*.rg` o `.stable/plugins/*.rg`**: coloca módulos `.rg` que definan tareas `fn :: task ...` o su propio `fn :: main => ()`, o extiende el tipo abierto `expand type StableCommand` de `stable`. Ejecuta `stable tasks` y `stable plugins` para inspeccionarlos en cualquier momento. |
| 201 | |
| 202 | ### 4. El modo estricto opcional (`--strict` y `strict mode = True`) |
| 203 | Por defecto, ni `ridgunc` ni los proyectos nuevos creados con `stable new` o `stable init` activan el modo estricto. Sin embargo, cuando quieras garantizar en tu propio proyecto que todo el equipo siga al 100% la convención canónica de espacios naturales de Ridgun, puedes pasar `--strict` a `ridgunc` (`ridgunc check --strict`, `ridgunc build --strict`) o declarar `strict mode = True` en tu `build.rg`. El modo estricto convierte en **error de compilación**: |
| 204 | 1. Cualquier guion bajo `_` dentro del nombre de una declaración `fn ::` u `op ::` (debiendo escribirse `fn :: calcular total`, nunca `fn :: calcular_total`; las variables locales y campos de registros sí pueden usar `_`). |
| 205 | 2. Cualquier tipo de salida múltiple escrito con paréntesis o comas tras `=>` (debiendo escribirse `=> Int Int` o `=> RToken [RToken]`, nunca `=> (Int, Int)`). |
| 206 | |
| 207 | --- |
| 208 | |
| 209 | ## 2.6. El Ecosistema de Librerías Estándar y Criptográficas (`ridgun-libs`) |
| 210 | |
| 211 | Ridgun cuenta con una suite oficial de **32 librerías puras** (`ridgun-libs`), compiladas como paquetes `.rglib` y bibliotecas dinámicas `.so` ELF64 listas para importar con `import <nombre>`: |
| 212 | |
| 213 | | Familia | Librerías (`import ...`) | Qué aportan a tus proyectos | |
| 214 | |---|---|---| |
| 215 | | **1. Texto, Formatos y Plantillas** | `str`, `fmt`, `regex`, `diff`, `template`, `json`, `csv`, `ini`, `codec` | Manipulación avanzada de cadenas, formateo tabular, expresiones regulares Thompson NFA, diferencias Myers (`diff`), plantillas sin lógica, parsers/serializadores `json`/`csv`/`ini` y codificación hexadecimal/Base64 (`codec`). | |
| 216 | | **2. Colecciones, Matemáticas y Álgebra Tensorial** | `list`, `btree`, `math`, `tensor`, `linalg` | Operaciones funcionales sobre listas, árboles B ordenados en memoria (`btree`), aritmética avanzada (`math`), vistas y constructores de tensores tipados (`tensor`) y descomposición/álgebra lineal (`linalg`). | |
| 217 | | **3. Sistema Operativo, Archivos y CLI** | `posix`, `io`, `path`, `glob`, `cli`, `time`, `log`, `uuid`, `test` | Descriptores y señales POSIX (`posix`), E/S con `with timeout` (`io`), rutas y patrones (`path`, `glob`), argumentos CLI tipados (`cli`), relojes monotónicos (`time`), logging estructurado (`log`), RFC 4122 `uuid` y aserciones (`test`). | |
| 218 | | **4. Criptografía y Seguridad** | `crypto`, `argon2id`, `jwt` | SHA-256 binario directo (`sha256 raw` / `sha256 bytes` en 32 bytes crudos), HMAC-SHA-256, PBKDF2-HMAC-SHA-256, HKDF, ChaCha20-Poly1305 AEAD (`crypto`), hashing de contraseñas RFC 9106 `argon2id` sobre `Tensor[U64 * 8 * 16]` con permutación BlaMka sin alojar tuplas en el heap (`argon2id`), y tokens firmados (`jwt`). | |
| 219 | | **5. Redes, Protocolos y Agentes** | `socket`, `http`, `ws`, `agent`, `pool`, `net_agent` | Sockets TCP/UDP POSIX (`socket`), cliente/servidor HTTP/1.1 (`http`), WebSockets RFC 6455 (`ws`), supervisores de agentes (`agent`), pools de trabajadores (`pool`) y servidores reactivos orientados a agentes (`net_agent`). | |
| 220 | |
| 221 | Ahora que tienes la caja de herramientas y las librerías controladas, pasemos al **Capítulo 03** para dominar las funciones con espacios naturales, las firmas de tipos y los bloques. |