codexivo 0.0.0-stage → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,701 @@
1
+ # Codexivo v1 — Especificación del lenguaje
2
+
3
+ > **Estado:** aprobada. Se implementa por pasos (§16). Lo que ya funciona está en
4
+ > [`lenguaje.md`](./lenguaje.md), y todos sus ejemplos se prueban automáticamente.
5
+ >
6
+ > Las secciones marcadas con **✅ Decidido** fueron decisiones abiertas que ya se resolvieron.
7
+
8
+ ## 0. Para qué existe este documento
9
+
10
+ Codexivo es un lenguaje para **practicar lógica**, no para construir productos. Si hoy una IA puede
11
+ escribir el código, lo que sigue siendo humano es entenderlo y verificarlo. Esta especificación define
12
+ un lenguaje que sirva para eso desde el primer `imprimir` hasta un recorrido en anchura sobre un grafo.
13
+
14
+ Principios, en orden de prioridad cuando chocan:
15
+
16
+ 1. **Visible.** Todo lo que el lenguaje hace debe poder mostrarse paso a paso: cada decisión, cada
17
+ vuelta, cada valor. Si una característica no se puede visualizar con honestidad, no entra.
18
+ 2. **Sin magia.** No hay conversiones implícitas ni comportamientos sorpresa. Cuando algo no tiene
19
+ sentido, el programa se detiene y explica por qué.
20
+ 3. **Verbal y en español.** Palabras antes que símbolos, con `ñ` y acentos. Los símbolos clásicos se
21
+ conservan como alternativa porque el siguiente lenguaje que aprenda la persona los va a usar.
22
+ 4. **Simple.** Pocos tipos, una sola forma recomendada de hacer cada cosa y nada de módulos ni
23
+ `importar`: todo lo necesario ya está disponible.
24
+ 5. **En el navegador y determinista.** Sin instalar nada. El mismo programa con la misma entrada
25
+ produce siempre el mismo resultado y la misma cantidad de operaciones.
26
+
27
+ > **✅ Decidido (#43) — regla de redacción.** Los ejemplos de nivel principiante no usan abreviaturas (`+=`, `++`) ni
28
+ > símbolos que tengan una alternativa en palabras, y no llevan `;` al final de línea. Las abreviaturas se presentan
29
+ > después, como atajos de algo que ya se entiende. Aplica al README, a `lenguaje.md`, al recetario, a la §9 y a los
30
+ > mensajes de error que sugieren código.
31
+
32
+ ### Niveles
33
+
34
+ Cada característica pertenece a un nivel. Los niveles ordenan la documentación y los retos; no
35
+ bloquean nada en el editor.
36
+
37
+ | Nivel | Qué se practica |
38
+ | ------------------- | ---------------------------------------------------------------------- |
39
+ | 🟢 **Principiante** | Secuencia, variables, entrada/salida, condiciones, bucles |
40
+ | 🟡 **Intermedio** | Procedimientos, listas, texto, diccionarios, recursión |
41
+ | 🔴 **Avanzado** | Procedimientos como valores, estructuras de datos, algoritmos clásicos |
42
+
43
+ ## 1. Estado actual frente a v1
44
+
45
+ Probado contra `main` (`1ca0398`), cuyos 56 tests pasaban sin cubrir estos casos. La columna "Estado" dice qué ya resolvió
46
+ la reescritura del núcleo y los pasos siguientes de §16.
47
+
48
+ | Caso | Antes | v1 | Estado |
49
+ | -------------------------------- | ---------------------------------------------------------------------------------------------------- | ----------------------------------------- | ----------------- |
50
+ | `1 < 2 y 5 < 4` | Error. `y`/`o` tenían **más** precedencia que las comparaciones y se agrupaba como `1 < (2 y 5) < 4` | `falso` | ✅ |
51
+ | `no 1 == 2` | `falso` (se agrupaba como `(no 1) == 2`) | `verdadero` | ✅ |
52
+ | Varios `pero_si` seguidos | Solo se conservaba el último | Todas las ramas, en orden | ✅ |
53
+ | `procedimiento suma(a, b) { … }` | Error de sintaxis | Declaración con nombre | ✅ (paso 5) |
54
+ | `// comentario` | Excepción de JavaScript (`value is null or undefined`) | Comentario | ✅ |
55
+ | `%`, `n++`, `n--` | Error de sintaxis | Soportados | ✅ (paso 4) |
56
+ | `a[0] = 9` | Error de sintaxis | Asignación por índice | ✅ (paso 5) |
57
+ | `nulo` | "identificador no encontrado" | Literal | ✅ |
58
+ | `imprimir(…)` / `leer()` | No existían; el programa solo devolvía su última expresión | Entrada y salida | ✅ |
59
+ | `1 / 0` | `Infinity` (el error `DIVISION_BY_ZERO` existía pero no se usaba) | Error | ✅ |
60
+ | `[1, 2] == [1, 2]` | `falso` | `verdadero` | ✅ |
61
+ | `mientras (verdadero) {}` | Bloqueaba el proceso | Error al superar el límite de operaciones | ✅ |
62
+ | `f(20000)` recursivo | `Maximum call stack size exceeded` | Funciona (límite configurable) | ✅ |
63
+ | `hasta_que` | Se tokenizaba como `WHILE` | Token propio `UNTIL` | ✅ |
64
+ | Mensajes de error | "en la linea" | "en la línea", con pista | ✅ |
65
+ | Docs y README | Usaban `sino`, `n++`, `%` y procedimientos con nombre que nunca funcionaron | Alineados con esta spec | ✅ (y se prueban) |
66
+
67
+ ## 2. Léxico
68
+
69
+ ### 2.1 Identificadores
70
+
71
+ - Letras del alfabeto latino con acentos y diéresis (`á é í ó ú ü ñ` y mayúsculas), dígitos y `_`.
72
+ - No pueden empezar con dígito. Distinguen mayúsculas de minúsculas.
73
+ - Convención recomendada: `minúsculas_con_guion_bajo`.
74
+
75
+ ```codexivo
76
+ variable año = 2026;
77
+ variable canción_favorita = "Cielito lindo";
78
+ ```
79
+
80
+ ### 2.2 Comentarios
81
+
82
+ ```codexivo
83
+ // comentario de una línea
84
+ /* comentario
85
+ de varias líneas */
86
+ ```
87
+
88
+ ### 2.3 Literales
89
+
90
+ | Literal | Ejemplos | Notas |
91
+ | ----------- | ------------------------------------- | ------------------------------------------------------------------- |
92
+ | Número | `10`, `3.14`, `.5`, `1_000_000` | Un solo tipo numérico (doble precisión). `_` solo entre dos dígitos |
93
+ | Texto | `"hola"`, `'mundo'`, `"línea\nnueva"` | Escapes: `\n`, `\t`, `\"`, `\'`, `\\` |
94
+ | Lógico | `verdadero`, `falso` | |
95
+ | Nulo | `nulo` | Ausencia de valor |
96
+ | Lista | `[1, 2, 3]`, `[]` | |
97
+ | Diccionario | `{ "nombre": "Ana", "edad": 16 }` | Ver §4.6 |
98
+
99
+ > **✅ Decidido — interpolación de texto.** ¿`"Hola, {nombre}"`? No en v1. `imprimir("Hola,", nombre)` resuelve el caso común sin agregar sintaxis
100
+ > dentro de las cadenas, y `texto(x)` cubre el resto.
101
+
102
+ ### 2.4 Punto y coma
103
+
104
+ Separa sentencias. Solo hace falta para poner dos en la misma línea; al final de línea es opcional y los ejemplos no lo
105
+ escriben (§0).
106
+
107
+ Sin `;`, un salto de línea termina la expresión: una línea que empieza con `-`, `(` o `[` es otra sentencia, no la
108
+ continuación de la anterior. Una expresión sigue en la línea siguiente solo si la línea termina en un operador
109
+ (`total = a +`) o si está dentro de paréntesis, corchetes o llaves. Así `x = 1` seguido de `-2` en la otra línea no se
110
+ convierte en silencio en `x = 1 - 2`.
111
+
112
+ ### 2.5 Palabras reservadas
113
+
114
+ ```
115
+ variable constante procedimiento regresa dominio
116
+ si pero_si si_no
117
+ mientras hacer hasta_que para desde hasta con_paso para_cada en repetir veces
118
+ romper continuar
119
+ verdadero falso nulo
120
+ y o no mod div
121
+ es_igual_a es_distinto_de es_menor_que es_mayor_que
122
+ es_menor_o_igual_a es_mayor_o_igual_a
123
+ ```
124
+
125
+ ### 2.6 Regla de acentos
126
+
127
+ Todo nombre que en español lleva acento lo lleva en su forma canónica (`máximo`, `número`,
128
+ `último`), y **también se acepta sin acento** (`maximo`, `numero`, `ultimo`), porque no todos los
129
+ teclados lo facilitan. La documentación y los mensajes usan siempre la forma canónica.
130
+
131
+ ## 3. Tipos y valores
132
+
133
+ | Tipo (`tipo(x)`) | Valores |
134
+ | ----------------- | --------------------------------------------- |
135
+ | `"número"` | `0`, `-3`, `2.5` |
136
+ | `"texto"` | `"hola"` |
137
+ | `"lógico"` | `verdadero`, `falso` |
138
+ | `"nulo"` | `nulo` |
139
+ | `"lista"` | `[1, "a", verdadero]` |
140
+ | `"diccionario"` | `{ "a": 1 }` |
141
+ | `"dominio"` | `dominio calculadora { … }` (§5.6) |
142
+ | `"procedimiento"` | Procedimientos del usuario y de la biblioteca |
143
+
144
+ - **Valor contra referencia.** Números, textos, lógicos y `nulo` se copian. Las listas y los
145
+ diccionarios se comparten: dos variables pueden apuntar a la misma lista. El sandbox lo dibuja con
146
+ flechas, porque es uno de los errores más comunes y más difíciles de ver.
147
+ - **Texto inmutable.** `t[0] = "x"` es un error; se construye un texto nuevo.
148
+
149
+ > **✅ Decidido — nombre del tipo de las listas.** Antes los errores decían `ARREGLO`. Ahora es `lista` en la superficie (docs, `tipo()`, mensajes). Es la palabra que alguien
150
+ > usa antes de saber qué es un arreglo, y "arreglo" se explica en los docs avanzados.
151
+
152
+ ## 4. Expresiones
153
+
154
+ ### 4.1 Precedencia (de menor a mayor)
155
+
156
+ | Nivel | Operadores | Asociatividad |
157
+ | ----- | ---------------------------------------------------- | ----------------- |
158
+ | 1 | `o` | izquierda |
159
+ | 2 | `y` | izquierda |
160
+ | 3 | `no` (`!`) | prefijo |
161
+ | 4 | `==` `!=` `<` `>` `<=` `>=` y sus formas en palabras | **sin encadenar** |
162
+ | 5 | `+` `-` | izquierda |
163
+ | 6 | `*` `/` `div` `mod` (`%`) | izquierda |
164
+ | 7 | `-` unario | prefijo |
165
+ | 8 | `^` (potencia) | **derecha** |
166
+ | 9 | llamada `f(x)`, índice `l[i]`, acceso `d.campo` | izquierda |
167
+
168
+ - `1 < x < 5` es un error con la pista _"escribe `1 < x y x < 5`"_. Encadenar comparaciones se ve
169
+ natural pero esconde dos comparaciones.
170
+ - `-2 ^ 2` es `-4`, como en matemáticas.
171
+
172
+ ### 4.2 Operadores en palabras
173
+
174
+ Cada forma en palabras es **exactamente equivalente** a su símbolo. El sandbox puede mostrar
175
+ cualquiera de las dos.
176
+
177
+ | Símbolo | Palabra | Ejemplo |
178
+ | ------- | -------------------- | ------------------------------------- |
179
+ | `==` | `es_igual_a` | `si (respuesta es_igual_a 42)` |
180
+ | `!=` | `es_distinto_de` | `mientras (letra es_distinto_de "x")` |
181
+ | `<` | `es_menor_que` | `edad es_menor_que 18` |
182
+ | `>` | `es_mayor_que` | `puntos es_mayor_que récord` |
183
+ | `<=` | `es_menor_o_igual_a` | `i es_menor_o_igual_a 10` |
184
+ | `>=` | `es_mayor_o_igual_a` | `edad es_mayor_o_igual_a 18` |
185
+ | `%` | `mod` | `n mod 2 == 0` |
186
+ | — | `div` | `7 div 2` → `3` |
187
+ | `!` | `no` | `no encontrado` |
188
+
189
+ No hay `&&` ni `||`: solo `y` / `o`.
190
+
191
+ ### 4.3 Aritmética
192
+
193
+ - `/` es división real: `7 / 2` → `3.5`.
194
+ - `div` es división entera hacia abajo: `7 div 2` → `3`, `-7 div 2` → `-4`.
195
+ - `mod` / `%` es el residuo consistente con `div`: `a == (a div b) * b + (a mod b)`. Así
196
+ `-7 mod 3` → `2`, que es lo que se espera al preguntar "¿es par?" o al dar vueltas en un círculo.
197
+ - Dividir entre cero (con `/`, `div` o `mod`) es un error.
198
+ - `+` suma números o une textos. **No mezcla**: `"Total: " + 5` es un error con la pista
199
+ _"convierte el número con `texto(5)` o usa `imprimir("Total:", 5)`"_.
200
+
201
+ ### 4.4 Comparación e igualdad
202
+
203
+ - `==` compara por valor, también listas y diccionarios: `[1, 2] == [1, 2]` → `verdadero`.
204
+ - Comparar tipos distintos con `==` regresa `falso`. Con `<`, `>`, `<=`, `>=` es un error.
205
+ - Los textos se comparan en orden alfabético por carácter.
206
+
207
+ ### 4.5 Lógica
208
+
209
+ `y` y `o` evalúan en cortocircuito; la visualización muestra cuándo una parte **no** se evaluó.
210
+
211
+ > **✅ Decidido — condiciones estrictas.** Antes `si (1)` funcionaba porque `0`, `""` y `nulo`
212
+ > contaban como falsos. En v1 las condiciones de `si`, `mientras`, `hasta_que`, `y`, `o` y `no` deben ser
213
+ > lógicas. `si (lista)` es un error con la pista _"¿quisiste decir `longitud(lista) > 0`?"_. Obliga a
214
+ > decir lo que uno quiere decir, que es justo lo que se practica.
215
+
216
+ ### 4.6 Listas y diccionarios
217
+
218
+ ```codexivo
219
+ variable números = [3, 1, 2];
220
+ números[0]; // 3
221
+ números[-1]; // 2 (desde el final)
222
+ números[0] = 10;
223
+
224
+ variable persona = { "nombre": "Ana", "edad": 16 };
225
+ persona["nombre"]; // "Ana"
226
+ persona.edad; // 16, azúcar para persona["edad"]
227
+ persona.ciudad = "León"; // agrega la clave
228
+ ```
229
+
230
+ - Índices enteros desde `0`. Fuera de rango es un error que muestra el índice y la longitud.
231
+ - Las claves de un diccionario son textos o números. `d.campo` solo funciona con claves que sean
232
+ identificadores válidos.
233
+ - Leer una clave que no existe es un error con la pista _"usa `tiene(d, clave)` para preguntar
234
+ antes"_.
235
+ - Al inicio de una sentencia, `{` abre un bloque; en cualquier otra posición, un diccionario.
236
+ - Las claves de un literal van entre comillas (`{ nombre: "Ana" }` es un error con pista); una clave calculada va
237
+ entre paréntesis: `{ (clave): valor }`. Una clave literal repetida es un error de sintaxis.
238
+ - `1` y `"1"` son claves distintas. `para_cada clave en d` recorre las claves; agregar o quitar claves durante el
239
+ recorrido es un error.
240
+
241
+ ## 5. Sentencias
242
+
243
+ ### 5.1 Variables y constantes
244
+
245
+ ```codexivo
246
+ variable contador = 0;
247
+ constante PI = 3.14159;
248
+ contador = contador + 1;
249
+ ```
250
+
251
+ - Asignar a una variable no declarada es un error: _"¿olvidaste escribir `variable`?"_.
252
+ - Declarar dos veces el mismo nombre en el mismo bloque es un error.
253
+ - Reasignar una `constante` es un error.
254
+ - **Alcance por bloque:** lo declarado dentro de `{ }` deja de existir al cerrar el bloque.
255
+ - Un nombre local puede ocultar uno de la biblioteca (`variable máximo = 0;`); el editor lo advierte.
256
+
257
+ ### 5.2 Asignación compuesta, incremento y decremento
258
+
259
+ ```codexivo
260
+ total += precio; // también -=, *=, /=, %=
261
+ i++;
262
+ i--;
263
+ ```
264
+
265
+ `++` y `--` son **sentencias**, no expresiones: `x = i++` es un error. Así desaparece la pregunta de
266
+ "¿`i++` o `++i`?".
267
+
268
+ ### 5.3 Condiciones
269
+
270
+ ```codexivo
271
+ si (edad >= 18) {
272
+ imprimir("adulto");
273
+ } pero_si (edad >= 13) {
274
+ imprimir("adolescente");
275
+ } si_no {
276
+ imprimir("niño");
277
+ }
278
+ ```
279
+
280
+ Las llaves son obligatorias.
281
+
282
+ > **✅ Decidido — `según`/`caso`** (equivalente a `switch`): fuera de v1. Una cadena de `pero_si` cubre lo mismo y no hay que
283
+ > explicar por qué un caso "se cae" al siguiente.
284
+
285
+ ### 5.4 Bucles
286
+
287
+ Una forma por intención:
288
+
289
+ | Intención | Forma | Nivel |
290
+ | --------------------------------------- | ---------------------------------------------- | ----- |
291
+ | Repetir n veces | `repetir n veces { }` | 🟢 |
292
+ | Contar | `para i desde a hasta b [con_paso p] { }` | 🟢 |
293
+ | Mientras se cumpla algo | `mientras (c) { }` / `hacer { } hasta_que (c)` | 🟢 |
294
+ | Recorrer una lista, texto o diccionario | `para_cada x en colección { }` | 🟡 |
295
+ | Puente hacia otros lenguajes | `para (inicio; condición; paso) { }` | 🔴 |
296
+
297
+ ```codexivo
298
+ // 🟢 repite un número fijo de veces (herencia directa de Karel)
299
+ repetir 4 veces {
300
+ avanzar()
301
+ }
302
+
303
+ // 🟢 cuenta, incluyendo los dos extremos
304
+ para i desde 1 hasta 10 {
305
+ suma = suma + i
306
+ }
307
+ para i desde 10 hasta 0 con_paso -2 {
308
+ imprimir(i)
309
+ }
310
+
311
+ // 🟢 mientras la condición sea verdadera
312
+ mientras (intentos es_menor_que 3) {
313
+ intentos = intentos + 1
314
+ }
315
+
316
+ // 🟢 al menos una vez, hasta que la condición sea verdadera
317
+ hacer {
318
+ número = leer_número()
319
+ } hasta_que (número es_mayor_que 0)
320
+
321
+ // 🟡 recorrer una lista o un texto
322
+ para_cada fruta en ["mango", "piña"] {
323
+ imprimir(fruta)
324
+ }
325
+
326
+ // 🟡 recorrer un diccionario
327
+ para_cada clave, valor en persona {
328
+ imprimir(clave, valor)
329
+ }
330
+ ```
331
+
332
+ > **✅ Decidido (#43) — `para … desde … hasta` es el `para` principal.** «Para i desde 1 hasta 10» es lo que la persona
333
+ > quiere decir; el `for` de C tiene tres partes separadas por `;` que no dicen qué son. `con_paso` lleva guion bajo para no
334
+ > reservar `paso`, que es un nombre común. `hasta` y `hasta_que` son palabras distintas. Después de `para`, un `(` empieza
335
+ > la forma clásica y un nombre, esta.
336
+
337
+ - **`para i desde a hasta b con_paso p`:**
338
+ - `i` se declara sola, sin `variable`, es nueva en cada vuelta y solo existe dentro del cuerpo. Cambiarla dentro del
339
+ bucle es el error `VARIABLE_DE_PARA` («la variable de un `para` avanza sola»).
340
+ - Incluye los dos extremos. El paso por defecto es `1`.
341
+ - `desde`, `hasta` y `con_paso` se evalúan **una vez**, al empezar; cambiar después la variable usada en `hasta` no
342
+ afecta al bucle.
343
+ - Sin magia: `desde` mayor que `hasta` sin `con_paso` es un error con la pista _«para contar hacia atrás escribe
344
+ `con_paso -1`»_ (no se invierte solo ni hace cero vueltas en silencio). Un paso en el sentido contrario también
345
+ (`PARA_SENTIDO_CONTRARIO`), igual que `con_paso 0` (`PARA_PASO_CERO`) o límites que no son números
346
+ (`PARA_LÍMITE_INVÁLIDO`).
347
+ - Cada vuelta emite `decide` con `label: 'para'` y `declara` con el nuevo valor de `i`.
348
+ - `para_cada i en rango(1, 10)` deja de ser la forma recomendada para contar; `rango()` sigue creando listas.
349
+ - `romper` sale del bucle más cercano; `continuar` salta a la siguiente vuelta. Fuera de un bucle son un error de
350
+ sintaxis, también dentro de un procedimiento: no cruzan el límite de una llamada.
351
+ - `repetir n veces` exige un número entero no negativo.
352
+ - Modificar la longitud de una lista mientras se recorre con `para_cada` es un error.
353
+
354
+ **🔴 Puente hacia otros lenguajes.** El `para` clásico se conserva para practicar antes de pasar a C, Java o JavaScript:
355
+
356
+ ```codexivo
357
+ para (variable i = 0; i < 10; i++) {
358
+ imprimir(i)
359
+ }
360
+ ```
361
+
362
+ Es `para i desde 0 hasta 9` escrito a mano: la condición se vuelve a evaluar en cada vuelta y la variable se puede cambiar
363
+ dentro del cuerpo. La documentación lo presenta en su propia sección, comparándolo con `para … desde … hasta`.
364
+
365
+ ### 5.5 Procedimientos
366
+
367
+ ```codexivo
368
+ // 🟡 declaración con nombre
369
+ procedimiento área_rectángulo(base, altura) {
370
+ regresa base * altura;
371
+ }
372
+
373
+ // 🔴 como valor
374
+ variable doble = procedimiento(x) { regresa x * 2; };
375
+ mapear([1, 2, 3], doble); // [2, 4, 6]
376
+ ```
377
+
378
+ - Las declaraciones con nombre se pueden usar antes de aparecer en su bloque; eso permite la
379
+ recursión mutua. Por eso su nombre no se puede repetir en el bloque, ni con otro procedimiento ni con una variable.
380
+ - Llamar con un número distinto de argumentos es un error. Hoy los faltantes se rellenan con `nulo`;
381
+ en v1 no.
382
+ - `regresa` sin valor regresa `nulo`, y un procedimiento que termina sin `regresa` también regresa `nulo`.
383
+ El valor de la última expresión **no** se regresa solo: devolver un valor siempre se dice con `regresa`.
384
+ - Los procedimientos capturan las variables de donde se crearon (closures).
385
+ - La profundidad de recursión solo la limita la configuración (por defecto 10 000), no JavaScript.
386
+
387
+ ### 5.6 Dominios
388
+
389
+ > **✅ Decidido (#19) — `dominio` es un espacio de nombres**, no un objeto ni un módulo. Agrupa declaraciones bajo un
390
+ > nombre; no hay `importar` ni archivos.
391
+
392
+ ```codexivo
393
+ dominio calculadora {
394
+ variable operaciones = 0;
395
+ procedimiento suma(a, b) {
396
+ operaciones++;
397
+ regresa a + b;
398
+ }
399
+ }
400
+ calculadora.suma(1, 3); // 4
401
+ ```
402
+
403
+ - **Contenido:** solo declaraciones (`variable`, `constante`, `procedimiento` y otros `dominio`). Cualquier otra
404
+ sentencia es el error de sintaxis `DOMINIO_SOLO_DECLARACIONES`. Las declaraciones se ejecutan en orden al llegar al
405
+ dominio, y los procedimientos con nombre existen desde el inicio, como en cualquier bloque.
406
+ - **Alcance:** dentro del dominio sus miembros se usan sin prefijo y se ve lo que está fuera. Fuera se usan con `.`
407
+ (`geometría.círculo.área(1)`); con `[ ]` es un error. Un miembro que no existe es `MIEMBRO_INEXISTENTE`, con
408
+ sugerencia si se parece a otro.
409
+ - **Asignar desde fuera:** solo a los miembros declarados con `variable`. Las constantes, los procedimientos y los
410
+ dominios internos son `MIEMBRO_DE_SOLO_LECTURA`, y agregar un miembro nuevo es `MIEMBRO_INEXISTENTE`: así
411
+ `calculadora.suma` siempre es el mismo procedimiento.
412
+ - **Anidados:** un dominio puede contener otros.
413
+ - **Valor:** el nombre del dominio es constante. El dominio es un valor de tipo `"dominio"` con referencia: el sandbox
414
+ lo muestra en la memoria con sus miembros (`snapshot().heap`), y cambiar un miembro emite `asigna` con su ruta
415
+ (`calculadora.operaciones`). No es un diccionario: no se recorre con `para_cada` ni tiene `longitud`.
416
+
417
+ ## 6. Entrada y salida
418
+
419
+ | Procedimiento | Qué hace |
420
+ | ------------------- | --------------------------------------------------------------------------- |
421
+ | `imprimir(a, b, …)` | Escribe los valores separados por un espacio y termina la línea |
422
+ | `leer()` | Lee la siguiente línea de entrada como texto. Si no hay más, regresa `nulo` |
423
+ | `leer_número()` | Lee la siguiente línea y la convierte en número; si no es un número, error |
424
+
425
+ - En el **sandbox libre**, `leer()` pausa el programa y pide el dato en la consola.
426
+ - En los **retos**, la entrada viene del caso de prueba.
427
+ - El valor de la última expresión se sigue mostrando en el REPL y en el sandbox como "resultado",
428
+ pero los retos solo comparan la salida o el valor regresado (§10).
429
+
430
+ ## 7. Errores
431
+
432
+ Cada error es un objeto, no solo un texto:
433
+
434
+ ```ts
435
+ {
436
+ tipo: "léxico" | "sintaxis" | "ejecución" | "límite",
437
+ código: "DIVISIÓN_ENTRE_CERO",
438
+ mensaje: "No se puede dividir entre cero.",
439
+ pista: "Revisa que `divisor` no valga 0 antes de dividir.",
440
+ inicio: { línea: 4, columna: 12 },
441
+ fin: { línea: 4, columna: 23 },
442
+ pila: ["programa", "promedio(lista)"]
443
+ }
444
+ ```
445
+
446
+ - Los mensajes dicen **qué pasó** y la pista **qué revisar**, en español correcto y con acentos.
447
+ - El parser se recupera y reporta varios errores, pero el sandbox destaca el primero.
448
+ - `afirmar(condición, mensaje)` detiene el programa si la condición es falsa. Es la herramienta más
449
+ directa para practicar verificación.
450
+ - Sin `intentar`/`capturar` en v1: un error detiene el programa, y eso es lo que queremos que se vea.
451
+
452
+ ## 8. Biblioteca estándar
453
+
454
+ Todo es global; no existe `importar`. Regla para listas: **`agregar`, `insertar`, `quitar` y `vaciar`
455
+ cambian la lista; todo lo demás regresa un valor nuevo.**
456
+
457
+ ### 🟢 Básicos
458
+
459
+ | Nombre | Descripción |
460
+ | --------------------------------- | -------------------------------------------------------------- |
461
+ | `imprimir`, `leer`, `leer_número` | §6 |
462
+ | `tipo(x)` | Nombre del tipo (§3) |
463
+ | `texto(x)`, `número(x)` | Conversiones explícitas |
464
+ | `longitud(x)` | Elementos de una lista o diccionario, o caracteres de un texto |
465
+ | `afirmar(cond, mensaje)` | §7 |
466
+
467
+ ### 🟢 Matemáticas
468
+
469
+ `absoluto`, `máximo(…)`, `mínimo(…)`, `redondear(x, decimales?)`, `piso`, `techo`, `raíz`, `entero`
470
+ (trunca), `aleatorio(a, b)` (entero entre `a` y `b` inclusive), la constante `PI`.
471
+
472
+ `aleatorio` usa una semilla por ejecución. El sandbox la muestra y permite repetirla, así un programa
473
+ "aleatorio" se puede depurar.
474
+
475
+ ### 🟡 Texto
476
+
477
+ `mayúsculas`, `minúsculas`, `recortar`, `dividir(t, separador)`, `unir(lista, separador)`,
478
+ `reemplazar(t, buscar, poner)`, `contiene`, `empieza_con`, `termina_con`, `posición_de` (regresa `-1`
479
+ si no está), `subtexto(t, inicio, fin)`, `caracteres(t)`, `código_de(c)`, `carácter_de(n)`.
480
+
481
+ > **✅ Decidido — `subtexto` y `rebanar` no incluyen el fin.** `subtexto("hola", 1, 3)` es `"ol"`. Así
482
+ > `fin - inicio` es la cantidad de elementos y `rebanar(l, 0, longitud(l))` es la lista completa. `rango`, que
483
+ > enumera, sí incluye el final; estos dos, que cortan, no.
484
+
485
+ ### 🟡 Listas
486
+
487
+ | Nombre | Descripción |
488
+ | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
489
+ | `agregar(l, x)` · `insertar(l, i, x)` | Cambian la lista |
490
+ | `quitar(l, i)` · `quitar_último(l)` · `quitar_primero(l)` · `vaciar(l)` | Cambian la lista; los tres primeros regresan lo que quitaron |
491
+ | `primero`, `último`, `contiene`, `posición_de` | Consulta |
492
+ | `rango(a, b, paso?)` | Lista de `a` a `b` **inclusive** |
493
+ | `índices(l)` | `rango(0, longitud(l) - 1)` |
494
+ | `rebanar(l, inicio, fin)`, `invertir(l)`, `ordenar(l, comparador?)`, `copiar(l)` | Nuevas listas |
495
+ | `sumar(l)`, `lista_de(n, valor)`, `matriz(filas, columnas, valor)` | Construcción y resumen. `lista_de` y `matriz` copian el valor en cada casilla, para no compartir sublistas por accidente |
496
+
497
+ > **✅ Decidido — `rango` inclusivo.** `rango(1, 10)` incluye el 10. Es como lo dice una persona ("del 1 al 10"); `índices(l)` evita el
498
+ > `- 1` en el caso típico.
499
+
500
+ ### 🟡 Diccionarios
501
+
502
+ `claves(d)`, `valores(d)`, `tiene(d, clave)`, `quitar(d, clave)`. El orden de recorrido es el de
503
+ inserción.
504
+
505
+ ### 🔴 Procedimientos como valores
506
+
507
+ `mapear(l, f)`, `filtrar(l, f)`, `reducir(l, f, inicial)`, `alguno(l, f)`, `todos(l, f)`,
508
+ `buscar(l, f)` (el primero que cumple, o `nulo`), `ordenar(l, va_antes)`.
509
+
510
+ > **✅ Decidido — el comparador de `ordenar` regresa un lógico.** `va_antes(a, b)` responde «¿va `a` antes que `b`?»
511
+ > (`procedimiento(a, b) { regresa a < b; }`). Es la pregunta que uno se hace al ordenar a mano, no exige entender el
512
+ > signo de una resta, y un número es un error. El orden es estable.
513
+ >
514
+ > Se implementan en Codexivo (ver `docs/arquitectura.md`), así una llamada a `f` se pausa, se traza y cuenta
515
+ > operaciones como cualquier otra.
516
+
517
+ ### 🔴 Estructuras de datos
518
+
519
+ Pilas y colas son listas con un vocabulario propio, para que el algoritmo se lea como se explica:
520
+
521
+ | Pila | Cola | Equivale a |
522
+ | -------------- | --------------- | -------------------- |
523
+ | `apilar(p, x)` | `encolar(c, x)` | `agregar` |
524
+ | `desapilar(p)` | — | `quitar_último` |
525
+ | — | `desencolar(c)` | `quitar_primero` |
526
+ | `tope(p)` | `frente(c)` | `último` / `primero` |
527
+
528
+ Los conjuntos se modelan con diccionarios (`visitados[nodo] = verdadero`), y los registros con
529
+ diccionarios y `.`.
530
+
531
+ ## 9. Ejemplos por nivel
532
+
533
+ ```codexivo
534
+ // 🟢 FizzBuzz
535
+ para n desde 1 hasta 15 {
536
+ si (n mod 15 es_igual_a 0) {
537
+ imprimir("FizzBuzz")
538
+ } pero_si (n mod 3 es_igual_a 0) {
539
+ imprimir("Fizz")
540
+ } pero_si (n mod 5 es_igual_a 0) {
541
+ imprimir("Buzz")
542
+ } si_no {
543
+ imprimir(n)
544
+ }
545
+ }
546
+ ```
547
+
548
+ ```codexivo
549
+ // 🟡 Búsqueda binaria
550
+ procedimiento buscar_binaria(lista, objetivo) {
551
+ variable inicio = 0
552
+ variable fin = longitud(lista) - 1
553
+ mientras (inicio es_menor_o_igual_a fin) {
554
+ variable medio = (inicio + fin) div 2
555
+ si (lista[medio] es_igual_a objetivo) {
556
+ regresa medio
557
+ } pero_si (lista[medio] es_menor_que objetivo) {
558
+ inicio = medio + 1
559
+ } si_no {
560
+ fin = medio - 1
561
+ }
562
+ }
563
+ regresa -1
564
+ }
565
+ ```
566
+
567
+ ```codexivo
568
+ // 🔴 Recorrido en anchura: distancia mínima en un grafo
569
+ procedimiento distancia(grafo, origen, destino) {
570
+ variable visitados = { }
571
+ variable cola = [[origen, 0]]
572
+ visitados[origen] = verdadero
573
+
574
+ mientras (longitud(cola) > 0) {
575
+ variable actual = desencolar(cola)
576
+ si (actual[0] == destino) {
577
+ regresa actual[1]
578
+ }
579
+ para_cada vecino en grafo[actual[0]] {
580
+ si (no tiene(visitados, vecino)) {
581
+ visitados[vecino] = verdadero
582
+ encolar(cola, [vecino, actual[1] + 1])
583
+ }
584
+ }
585
+ }
586
+ regresa -1
587
+ }
588
+ ```
589
+
590
+ ## 10. Ejecución, límites y determinismo
591
+
592
+ - **Operaciones.** Cada operación básica (leer una variable, poner un valor, sumar, comparar, asignar, decidir, llamar)
593
+ cuenta como una. Los procedimientos de la biblioteca
594
+ cuentan según el trabajo que hacen (por ejemplo, `ordenar` sobre `n` elementos cuesta
595
+ `n · ⌈log₂ n⌉`, y `contiene` cuesta `n`). La tabla vive en `src/runtime/costs.ts` y se documenta en `lenguaje.md`. La misma ejecución da siempre el mismo número, y los retos lo usan en lugar del
596
+ tiempo.
597
+ - **Límites configurables** con valores por defecto: operaciones (10 millones en el sandbox; cada
598
+ reto define el suyo), profundidad de recursión (10 000) y tamaño de la salida (1 MB). Superarlos es
599
+ un error de tipo `límite` que explica qué se superó y dónde estaba el programa.
600
+ - **Ejecución reanudable.** El intérprete avanza un paso a la vez y se puede pausar, avanzar o esperar a
601
+ `leer()`. Retroceder es volver a ejecutar desde el inicio hasta el paso anterior con la misma entrada: como la
602
+ ejecución es determinista, el resultado es idéntico. En el sandbox corre en un Web Worker y cede el control cada
603
+ cierto número de operaciones para no congelar la página.
604
+ - **Contratos de los retos:**
605
+ - _Estilo HackerRank:_ el programa lee con `leer()` y se compara lo que imprime.
606
+ - _Estilo LeetCode:_ el reto llama a un procedimiento (`resolver(...)`) y compara lo que regresa.
607
+
608
+ ## 11. Contrato para la visualización
609
+
610
+ Es lo que necesitan el sandbox, los diagramas, la máquina de estados y el modo mundo.
611
+
612
+ - Cada nodo del AST tiene un identificador estable y su **rango** en el código (inicio y fin, en
613
+ línea, columna y desplazamiento).
614
+ - Cada token conserva su rango, para la vista de tokens.
615
+ - La máquina emite eventos: `paso` (inicio de una sentencia), `declara`, `asigna` (antes/después), `modifica` (un
616
+ elemento de una lista o diccionario, con su `ref`, la clave y el valor antes/después),
617
+ `decide` (condición, valor y rama tomada), `opera` (operandos y resultado), `llama`/`regresa`,
618
+ `imprime`/`lee`, `error` y `fin`. Cada uno lleva el número de operaciones y la profundidad de llamadas.
619
+ - En cualquier pausa se puede pedir una fotografía (`snapshot`) con las llamadas activas y las variables de
620
+ cada alcance. Listas y diccionarios tienen un identificador de referencia, para dibujar cuándo dos
621
+ variables comparten el mismo valor. `heap` guarda, por referencia, los elementos de cada lista y los pares
622
+ `{ key, value }` de cada diccionario.
623
+ - El diagrama de flujo se genera desde el AST; la máquina de estados se anima desde los eventos.
624
+ Ninguno de los dos inventa información que el intérprete no produjo.
625
+
626
+ ## 12. Modo mundo (Karel)
627
+
628
+ El mismo lenguaje con procedimientos adicionales para controlar un robot en una cuadrícula. Solo
629
+ existen cuando el programa corre en un mundo.
630
+
631
+ | Acción | Pregunta |
632
+ | -------------------- | ----------------------------------------------------------------------------------- |
633
+ | `avanzar()` | `frente_libre()`, `izquierda_libre()`, `derecha_libre()` |
634
+ | `girar_izquierda()` | `hay_zumbador()` |
635
+ | `recoger_zumbador()` | `tengo_zumbadores()` |
636
+ | `dejar_zumbador()` | `mirando_al_norte()`, `mirando_al_sur()`, `mirando_al_este()`, `mirando_al_oeste()` |
637
+ | `apagar()` | |
638
+
639
+ Chocar con una pared (`CHOQUE_CON_PARED`), recoger donde no hay zumbadores (`SIN_ZUMBADORES`), dejar sin tener
640
+ (`MOCHILA_VACÍA`) o hacer cualquier acción después de `apagar()` (`ROBOT_APAGADO`) es un error de ejecución, con la
641
+ posición y la orientación del robot. `apagar()` no termina el programa; las preguntas siguen funcionando. No hay
642
+ `girar_derecha()`: construirlo con tres `girar_izquierda()` es el primer ejercicio, igual que en Karel.
643
+
644
+ El formato del mundo (versión 1), los eventos `robot` y los objetivos de un reto están en
645
+ [`modo-mundo.md`](./modo-mundo.md).
646
+
647
+ ## 13. Fuera de v1
648
+
649
+ Módulos e `importar`, clases y métodos, `intentar`/`capturar`, asincronía, archivos, red, tipos
650
+ estáticos y `según`/`caso`. Se pueden reconsiderar después, siempre con el principio 1 como filtro.
651
+
652
+ `dominio` (§5.6) no es un módulo: agrupa nombres dentro del mismo programa, sin archivos ni `importar`.
653
+
654
+ ## 14. Cambios que rompen programas existentes
655
+
656
+ Codexivo no tiene usuarios todavía, así que v1 se permite romper compatibilidad:
657
+
658
+ - condiciones estrictas (si se aprueba §4.5);
659
+ - error al llamar con un número distinto de argumentos;
660
+ - error al redeclarar o al asignar sin declarar;
661
+ - `agregar` pasa a modificar la lista (antes regresaba una nueva);
662
+ - un procedimiento sin `regresa` regresa `nulo` (antes regresaba el valor de su última expresión);
663
+ - `1 / 0` pasa a ser error;
664
+ - `ARREGLO` pasa a llamarse `lista` en mensajes y en `tipo()`.
665
+
666
+ ## 15. Decisiones
667
+
668
+ Todas aprobadas.
669
+
670
+ | # | Tema | Decisión |
671
+ | --- | ---------------------------------- | -------- |
672
+ | 1 | Interpolación de texto (§2.3) | No en v1 |
673
+ | 2 | Nombre del tipo de las listas (§3) | `lista` |
674
+ | 3 | Condiciones estrictas (§4.5) | Sí |
675
+ | 4 | `según`/`caso` (§5.3) | No en v1 |
676
+ | 5 | `rango` inclusivo (§8) | Sí |
677
+
678
+ ## 16. Orden de implementación propuesto
679
+
680
+ 1. ✅ **Cimientos:** Bun 1.4.4 (`bun check` en lugar de `tsc`; la única dependencia de desarrollo es
681
+ `@types/bun`), rangos en tokens y nodos, errores estructurados.
682
+ 2. ✅ **Correcciones:** precedencia de `y`/`o`/`no`, `pero_si` encadenados, comentarios, `nulo`, división
683
+ entre cero, igualdad por valor, `hasta_que`.
684
+ 3. ✅ **Ejecución reanudable:** el AST se compila a instrucciones para una máquina de pila con su propia
685
+ pila de llamadas, límites, eventos y fotografías (§10–11). Reemplaza al evaluador recursivo y al
686
+ `RuntimeTracer`. Se descartaron los generadores anidados porque, con `yield*`, la recursión profunda
687
+ vuelve a depender de la pila de JavaScript. Incluye `imprimir`, `leer`, `leer_número`, `tipo`, `texto`
688
+ y `número`, porque la entrada y salida son parte del contrato de la máquina.
689
+ 4. ✅ **Nivel principiante:** `imprimir`/`leer`, `repetir … veces`, `++`/`--`, `%`/`mod`/`div`, `^`,
690
+ operadores en palabras, `constante`, alcance por bloque, `1_000_000` y `PI`. `repetir` emite `decide` con la
691
+ etiqueta `repetir` en cada vuelta y `declara` indica si el nombre es constante.
692
+ 5. ✅ **Nivel intermedio:** ✅ procedimientos con nombre, ✅ `para_cada`, ✅ `romper`/`continuar`, ✅ asignación por
693
+ índice (evento `modifica`), ✅ `rango` e `índices`, ✅ diccionarios, ✅ biblioteca de texto y listas (con
694
+ `cost` registrado en cada procedimiento nativo; el cobro llega en el paso 6).
695
+ 6. ✅ **Nivel avanzado:** procedimientos como valores, estructuras de datos, costo por operación de la
696
+ biblioteca.
697
+ 7. ✅ **Modo mundo:** formato JSON versionado con `validateWorld`, `createWorld`, evento `robot`, estado en
698
+ `snapshot()` y objetivos de reto con `checkGoal` (ver [`modo-mundo.md`](./modo-mundo.md)).
699
+
700
+ Cada paso llega con tests y actualiza `lenguaje.md` para que la documentación nunca vuelva a prometer
701
+ lo que el código no hace.