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,537 @@
1
+ # El lenguaje Codexivo, hoy
2
+
3
+ Esta guía describe **lo que funciona hoy** en el intérprete. Todos los ejemplos se ejecutan tal cual. El diseño completo hacia
4
+ donde va el lenguaje está en la [especificación v1](./especificacion-v1.md); lo que aquí no aparece, todavía no existe.
5
+
6
+ ## Un programa
7
+
8
+ Un programa es una lista de sentencias, una por línea. El punto y coma solo hace falta para poner dos sentencias en la misma
9
+ línea (`x = 1; y = 2`); los ejemplos de esta guía no lo usan. El valor de un programa es el de su última expresión, o lo que
10
+ regrese con `regresa`.
11
+
12
+ El mismo lenguaje funciona mediante la biblioteca ESM `codexivo`, el comando npm `codexivo` (sin instalar Bun) y los ejecutables
13
+ autónomos de cada sistema. Consulta [REPL y terminal](./repl.md) para ejecutar archivos y proporcionar entrada estándar.
14
+
15
+ ```codexivo
16
+ // comentario de una línea
17
+ /* comentario
18
+ de varias líneas */
19
+ variable nombre = "Ana"
20
+ imprimir("Hola,", nombre)
21
+ ```
22
+
23
+ ## Palabras reservadas
24
+
25
+ ```
26
+ variable constante procedimiento regresa dominio
27
+ si pero_si si_no
28
+ mientras hacer hasta_que para desde hasta con_paso para_cada en repetir veces
29
+ romper continuar
30
+ verdadero falso nulo
31
+ y o no mod div
32
+ es_igual_a es_distinto_de es_menor_que es_mayor_que
33
+ es_menor_o_igual_a es_mayor_o_igual_a
34
+ ```
35
+
36
+ ## Valores
37
+
38
+ | Tipo | Ejemplos |
39
+ | ------------- | ----------------------------------------------------------------------------- |
40
+ | número | `10`, `3.14`, `.5`, `-2`, `1_000_000` (el `_` solo separa grupos de dígitos) |
41
+ | texto | `"hola"`, `'mundo'`, `"línea\nnueva"` (escapes: `\n`, `\t`, `\\`, `\"`, `\'`) |
42
+ | lógico | `verdadero`, `falso` |
43
+ | nulo | `nulo` |
44
+ | lista | `[1, "a", verdadero]` |
45
+ | diccionario | `{ "nombre": "Ana", "edad": 16 }` |
46
+ | procedimiento | `procedimiento(x) { regresa x * 2; }` |
47
+
48
+ Las listas y los diccionarios se comparten: si dos variables apuntan a la misma lista, un cambio se ve desde las dos.
49
+
50
+ ## Variables
51
+
52
+ ```codexivo
53
+ variable contador = 0
54
+ contador = contador + 1
55
+
56
+ constante LADOS = 4
57
+ imprimir(LADOS * contador)
58
+ ```
59
+
60
+ - Toda variable se declara una vez con `variable` y un valor inicial.
61
+ - `=` **no** es una igualdad: guarda en la variable de la izquierda el valor de la derecha. `contador = contador + 1` se lee
62
+ «calcula `contador + 1` y guarda el resultado en `contador`». En el sandbox se ve el valor entrando en la variable.
63
+ - Asignar a una variable sin declararla es un error.
64
+ - Una `constante` no se puede cambiar: reasignarla es un error.
65
+ - Lo que se declara dentro de un bloque `{ }` solo existe dentro de él.
66
+
67
+ Los nombres pueden llevar `ñ`, acentos y diéresis: `año`, `canción`, `pingüino`.
68
+
69
+ ### Atajos
70
+
71
+ Cuando ya se entiende la asignación, hay formas cortas para los cambios más comunes:
72
+
73
+ ```codexivo
74
+ variable total = 10
75
+ total += 5 // lo mismo que total = total + 5; también -=, *=, /=, %=
76
+ total++ // suma 1
77
+ total-- // resta 1
78
+ ```
79
+
80
+ `++` y `--` son sentencias, no expresiones: `x = i++` es un error. Se escribe `i++` en su propia línea.
81
+
82
+ ## Operadores
83
+
84
+ De menor a mayor precedencia:
85
+
86
+ | Operadores | Notas |
87
+ | ---------------------------------------------------- | ------------------------------------------------------------------------ |
88
+ | `o` | Cortocircuito |
89
+ | `y` | Cortocircuito |
90
+ | `no`, `!` | |
91
+ | `==` `!=` `<` `>` `<=` `>=` y sus formas en palabras | No se pueden encadenar: `1 < x < 5` es un error; escribe `1 < x y x < 5` |
92
+ | `+` `-` | `+` también une dos textos |
93
+ | `*` `/` `div` `mod` (`%`) | Dividir entre cero (con `/`, `div` o `mod`) es un error |
94
+ | `-` (signo) | |
95
+ | `^` | Potencia, asociativa a la derecha: `2 ^ 3 ^ 2` es `2 ^ 9` |
96
+ | llamadas `f(x)`, índices `lista[i]` | |
97
+
98
+ ```codexivo
99
+ 7 / 2 // 3.5, división real
100
+ 7 div 2 // 3, división entera hacia abajo
101
+ -7 div 2 // -4
102
+ 7 mod 3 // 1, el residuo (también 7 % 3)
103
+ -7 mod 3 // 2: siempre se cumple a == (a div b) * b + (a mod b)
104
+ -2 ^ 2 // -4, como en matemáticas: la potencia va antes que el signo
105
+ ```
106
+
107
+ Cada comparación tiene una forma en palabras que significa exactamente lo mismo:
108
+
109
+ | Símbolo | Palabra |
110
+ | ------- | -------------------- |
111
+ | `==` | `es_igual_a` |
112
+ | `!=` | `es_distinto_de` |
113
+ | `<` | `es_menor_que` |
114
+ | `>` | `es_mayor_que` |
115
+ | `<=` | `es_menor_o_igual_a` |
116
+ | `>=` | `es_mayor_o_igual_a` |
117
+
118
+ ```codexivo
119
+ variable edad = 16
120
+ si (edad es_mayor_o_igual_a 13 y edad es_menor_que 18) {
121
+ imprimir("adolescente")
122
+ }
123
+ ```
124
+
125
+ - `==` compara por valor, también listas: `[1, 2] == [1, 2]` es `verdadero`.
126
+ - `+` no mezcla textos y números: `"Total: " + 5` es un error. Usa `texto(5)` o `imprimir("Total:", 5)`.
127
+ - `y`, `o`, `no` y las condiciones solo aceptan `verdadero` o `falso`. `si (1)` es un error con una pista.
128
+
129
+ ## Condiciones y bucles
130
+
131
+ ```codexivo
132
+ variable edad = 15
133
+ si (edad es_mayor_o_igual_a 18) {
134
+ imprimir("adulto")
135
+ } pero_si (edad es_mayor_o_igual_a 13) {
136
+ imprimir("adolescente")
137
+ } si_no {
138
+ imprimir("niño")
139
+ }
140
+ ```
141
+
142
+ Hay una forma de bucle para cada intención:
143
+
144
+ | Para… | Se escribe |
145
+ | --------------------------------------- | --------------------------------- |
146
+ | Repetir algo n veces | `repetir n veces { }` |
147
+ | Contar | `para i desde a hasta b { }` |
148
+ | Repetir mientras se cumpla algo | `mientras (condición) { }` |
149
+ | Repetir hasta que se cumpla algo | `hacer { } hasta_que (condición)` |
150
+ | Recorrer una lista, texto o diccionario | `para_cada x en colección { }` |
151
+
152
+ ```codexivo
153
+ repetir 3 veces {
154
+ imprimir("¡hola!")
155
+ }
156
+
157
+ variable suma = 0
158
+ para i desde 1 hasta 10 {
159
+ suma = suma + i
160
+ }
161
+ imprimir("La suma es", suma)
162
+
163
+ para i desde 10 hasta 0 con_paso -2 {
164
+ imprimir(i)
165
+ }
166
+
167
+ variable intentos = 0
168
+ mientras (intentos es_menor_que 3) {
169
+ intentos = intentos + 1
170
+ }
171
+
172
+ hacer {
173
+ intentos = intentos - 1
174
+ } hasta_que (intentos es_igual_a 0)
175
+
176
+ para_cada fruta en ["mango", "piña"] {
177
+ imprimir(fruta)
178
+ }
179
+
180
+ para n desde 1 hasta 10 {
181
+ si (n mod 2 es_igual_a 0) {
182
+ continuar // salta a la siguiente vuelta
183
+ }
184
+ si (n es_mayor_que 7) {
185
+ romper // sale del bucle
186
+ }
187
+ imprimir(n)
188
+ }
189
+ ```
190
+
191
+ - `para i desde a hasta b` incluye los dos extremos. El paso es 1 si no se escribe `con_paso`.
192
+ - `i` se crea sola, sin `variable`, es nueva en cada vuelta y solo existe dentro del cuerpo. Avanza sola: cambiarla
193
+ dentro del bucle es un error.
194
+ - `desde`, `hasta` y `con_paso` se calculan **una vez**, al empezar. Si después cambia la variable que se usó en
195
+ `hasta`, el bucle no se entera.
196
+ - Para contar hacia atrás hay que decirlo: `para i desde 10 hasta 1` es un error; se escribe `con_paso -1`. Un paso 0
197
+ también es un error.
198
+ - `repetir n veces` evalúa `n` una sola vez, al empezar. Tiene que ser un número entero, 0 o más; con 0 el bloque no se
199
+ ejecuta. Lleva la cuenta por dentro, sin crear ninguna variable.
200
+ - `hacer … hasta_que` ejecuta el bloque al menos una vez y repite **hasta que** la condición sea verdadera.
201
+ - `para_cada x en …` recorre una lista, un texto o un diccionario. `x` es nueva en cada vuelta y solo existe dentro del
202
+ cuerpo. Cambiar elementos de la lista mientras se recorre está bien; agregar o quitar elementos es un error.
203
+ - `romper` sale del bucle más cercano y `continuar` salta a su siguiente vuelta. Fuera de un bucle son un error, también
204
+ dentro de un procedimiento que se llama desde un bucle.
205
+
206
+ ## Procedimientos
207
+
208
+ ```codexivo
209
+ procedimiento área(base, altura) {
210
+ regresa base * altura
211
+ }
212
+ área(3, 4)
213
+
214
+ // también como valor, guardado en una variable
215
+ variable saludar = procedimiento(nombre) {
216
+ regresa "Hola, " + nombre
217
+ }
218
+ saludar("Uriel")
219
+ ```
220
+
221
+ - Un procedimiento con nombre existe desde el inicio de su bloque: se puede llamar antes de la línea donde se declara, y
222
+ dos procedimientos pueden llamarse entre sí. Su nombre no se puede repetir en el mismo bloque.
223
+ - Hay que llamarlos con exactamente tantos valores como parámetros.
224
+ - Sin `regresa`, un procedimiento regresa `nulo`.
225
+ - Recuerdan las variables de donde se crearon (closures) y pueden llamarse a sí mismos.
226
+
227
+ ## Listas y textos
228
+
229
+ ```codexivo
230
+ variable números = [10, 20, 30]
231
+ números[0] // 10
232
+ números[-1] // 30, desde el final
233
+ "hola"[1] // "o"
234
+
235
+ números[0] = 15
236
+ números[-1] += 1
237
+ números[1]++
238
+
239
+ variable matriz = [[1, 2], [3, 4]]
240
+ matriz[1][0] = 9
241
+ ```
242
+
243
+ - Los índices son enteros desde 0; un índice fuera de rango es un error que dice cuántos elementos hay.
244
+ - Los textos no se pueden cambiar letra por letra: `t[0] = "H"` es un error. Se construye un texto nuevo.
245
+
246
+ ## Diccionarios
247
+
248
+ ```codexivo
249
+ variable persona = { "nombre": "Ana", "edad": 16 }
250
+ persona["nombre"] // "Ana"
251
+ persona.edad // 16, lo mismo que persona["edad"]
252
+ persona.ciudad = "León" // agrega la clave
253
+ persona.edad += 1
254
+
255
+ para_cada clave, valor en persona {
256
+ imprimir(clave, valor)
257
+ }
258
+
259
+ si (tiene(persona, "correo")) {
260
+ imprimir(persona.correo)
261
+ }
262
+
263
+ variable calculadora = {
264
+ "suma": procedimiento(a, b) { regresa a + b; },
265
+ }
266
+ calculadora.suma(1, 3) // 4
267
+ ```
268
+
269
+ - Las claves son textos o números (`1` y `"1"` son claves distintas) y van entre comillas: `{ nombre: "Ana" }` es un
270
+ error. Para usar el valor de una variable como clave, escríbela entre paréntesis: `{ (clave): 1 }`.
271
+ - `d.campo` funciona con claves que son nombres válidos; para las demás, `d["clave con espacios"]`.
272
+ - Leer una clave que no existe es un error; pregunta antes con `tiene(d, clave)`.
273
+ - Se recorren en el orden en que se agregaron las claves: `para_cada clave en d` da solo las claves y
274
+ `para_cada clave, valor en d`, las dos. Agregar o quitar claves mientras se recorre es un error.
275
+ - `==` compara por valor sin importar el orden: `{ "a": 1, "b": 2 } == { "b": 2, "a": 1 }` es `verdadero`.
276
+ - Al inicio de una sentencia, `{` abre un bloque; en cualquier otra posición, un diccionario.
277
+
278
+ ## Dominios
279
+
280
+ Un `dominio` es un espacio de nombres: agrupa procedimientos, variables y constantes bajo un nombre, y desde fuera se usan
281
+ con `.`. No es un módulo ni hay que importarlo: se declara en el mismo programa.
282
+
283
+ ```codexivo
284
+ dominio calculadora {
285
+ constante PRECISIÓN = 2
286
+ variable operaciones = 0
287
+
288
+ procedimiento suma(a, b) {
289
+ operaciones++
290
+ regresa redondear(a + b, PRECISIÓN)
291
+ }
292
+
293
+ procedimiento promedio(a, b) {
294
+ regresa suma(a, b) / 2 // dentro del dominio, sin prefijo
295
+ }
296
+ }
297
+
298
+ calculadora.suma(1, 3) // 4
299
+ calculadora.promedio(2, 3) // 2.5
300
+ calculadora.operaciones // 2
301
+
302
+ dominio geometría {
303
+ dominio círculo {
304
+ procedimiento área(r) {
305
+ regresa PI * r ^ 2
306
+ }
307
+ }
308
+ }
309
+ geometría.círculo.área(1)
310
+ ```
311
+
312
+ - Dentro de un dominio solo van declaraciones: `variable`, `constante`, `procedimiento` y otros `dominio`. Un `si` o un
313
+ `imprimir` van dentro de un procedimiento del dominio o fuera de él.
314
+ - Dentro, los miembros se usan sin prefijo y ven lo que está fuera del dominio. Fuera, se usan con `.`:
315
+ `calculadora.suma`. Sin el prefijo no existen.
316
+ - Desde fuera solo se pueden cambiar los miembros declarados con `variable` (`calculadora.operaciones = 0`). Las
317
+ constantes, los procedimientos y los dominios que contiene quedan fijos, y no se pueden agregar miembros nuevos.
318
+ - El nombre del dominio no se puede reasignar. `tipo(calculadora)` es `"dominio"`.
319
+
320
+ ## Procedimientos como valores
321
+
322
+ Un procedimiento es un valor como cualquier otro: se guarda en variables, en listas y diccionarios, y se le pasa a otros
323
+ procedimientos. La biblioteca trae varios que reciben uno:
324
+
325
+ ```codexivo
326
+ variable números = [3, 8, 1, 6]
327
+ procedimiento es_par(n) {
328
+ regresa n mod 2 == 0
329
+ }
330
+
331
+ mapear(números, procedimiento(n) { regresa n * 10; }) // [30, 80, 10, 60]
332
+ filtrar(números, es_par) // [8, 6]
333
+ reducir(números, procedimiento(total, n) { regresa total + n; }, 0) // 18
334
+ alguno(números, es_par) // verdadero
335
+ todos(números, es_par) // falso
336
+ buscar(números, procedimiento(n) { regresa n > 5; }) // 8
337
+ ordenar(números, procedimiento(a, b) { regresa a > b; }) // [8, 6, 3, 1]
338
+ ```
339
+
340
+ | Procedimiento | Qué hace |
341
+ | ---------------------------- | --------------------------------------------------------------------------------------- |
342
+ | `mapear(lista, f)` | Lista nueva con `f(x)` de cada elemento |
343
+ | `filtrar(lista, f)` | Lista nueva con los elementos para los que `f(x)` es `verdadero` |
344
+ | `reducir(lista, f, inicial)` | Junta todo en un valor: empieza con `inicial` y en cada elemento hace `f(acumulado, x)` |
345
+ | `alguno(lista, f)` | `verdadero` si `f(x)` es `verdadero` para al menos un elemento |
346
+ | `todos(lista, f)` | `verdadero` si `f(x)` es `verdadero` para todos |
347
+ | `buscar(lista, f)` | El primer elemento con `f(x)` `verdadero`, o `nulo` |
348
+ | `ordenar(lista, va_antes)` | Lista nueva ordenada; `va_antes(a, b)` responde «¿va `a` antes que `b`?» |
349
+
350
+ - `f` de `filtrar`, `alguno`, `todos` y `buscar`, y el comparador de `ordenar`, tienen que regresar `verdadero` o `falso`.
351
+ - `ordenar` es estable: los elementos que el comparador no distingue conservan su orden.
352
+ - Un error dentro de `f` dice dónde está y desde dónde se llamó: `programa > mapear > f`.
353
+
354
+ ## Pilas y colas
355
+
356
+ Son listas con un vocabulario propio, para que el algoritmo se lea como se explica:
357
+
358
+ | Pila | Cola | Qué hace |
359
+ | -------------- | --------------- | -------------------------------------- |
360
+ | `apilar(p, x)` | `encolar(c, x)` | Agrega al final |
361
+ | `desapilar(p)` | `desencolar(c)` | Quita y regresa el último / el primero |
362
+ | `tope(p)` | `frente(c)` | Regresa, sin quitarlo, el que saldría |
363
+
364
+ ```codexivo
365
+ variable pendientes = []
366
+ encolar(pendientes, "lavar")
367
+ encolar(pendientes, "secar")
368
+ imprimir(desencolar(pendientes), frente(pendientes))
369
+
370
+ variable pila = []
371
+ apilar(pila, 1)
372
+ apilar(pila, 2)
373
+ imprimir(desapilar(pila), tope(pila))
374
+ ```
375
+
376
+ Sacar de una pila o cola vacía es un error: _"No se puede `desencolar` de una cola vacía."_
377
+
378
+ ## Biblioteca
379
+
380
+ Todo está disponible sin importar nada. Los nombres con acento también se aceptan sin él (`máximo` o `maximo`).
381
+
382
+ ### Entrada, salida y tipos
383
+
384
+ | Procedimiento | Qué hace |
385
+ | ----------------------- | -------------------------------------------------------------------------------------------------------- |
386
+ | `imprimir(a, b, …)` | Escribe los valores separados por un espacio y termina la línea |
387
+ | `leer()` | Siguiente línea de entrada como texto, o `nulo` si ya no hay |
388
+ | `leer_número()` | Siguiente línea de entrada como número |
389
+ | `tipo(x)` | `"número"`, `"texto"`, `"lógico"`, `"nulo"`, `"lista"`, `"diccionario"`, `"dominio"` o `"procedimiento"` |
390
+ | `texto(x)`, `número(x)` | Conversiones explícitas |
391
+ | `longitud(x)` | Caracteres de un texto, elementos de una lista o claves de un diccionario |
392
+
393
+ ### Matemáticas
394
+
395
+ | Procedimiento | Qué hace |
396
+ | -------------------------- | ---------------------------------------------------------------------- |
397
+ | `absoluto(x)` | Valor absoluto |
398
+ | `máximo(…)`, `mínimo(…)` | De varios números o de una lista |
399
+ | `redondear(x, decimales?)` | Redondea; los `.5` se alejan del cero |
400
+ | `piso(x)`, `techo(x)` | Redondea hacia abajo o hacia arriba: `piso(-2.1)` es `-3` |
401
+ | `entero(x)` | Quita los decimales: `entero(-2.7)` es `-2` |
402
+ | `raíz(x)` | Raíz cuadrada; de un número negativo es un error |
403
+ | `aleatorio(a, b)` | Un entero al azar entre `a` y `b`, los dos incluidos |
404
+ | `PI` | La constante 3.14159…; una variable tuya con el mismo nombre la oculta |
405
+
406
+ `aleatorio` usa una semilla por ejecución: con la misma semilla, el programa da exactamente los mismos números. `run`
407
+ regresa la semilla que usó en `seed`, y se puede fijar con la opción `seed`.
408
+
409
+ ```codexivo
410
+ variable dado = aleatorio(1, 6)
411
+ imprimir("Salió", dado)
412
+ imprimir(piso(7 / 2), techo(7 / 2), raíz(16))
413
+ ```
414
+
415
+ ### Texto
416
+
417
+ Ninguno cambia el texto original (los textos no cambian nunca): todos regresan un valor nuevo.
418
+
419
+ | Procedimiento | Qué hace |
420
+ | ------------------------------------------------ | --------------------------------------------------------------------------------------- |
421
+ | `mayúsculas(t)`, `minúsculas(t)` | El mismo texto en mayúsculas o en minúsculas |
422
+ | `recortar(t)` | Sin espacios ni saltos de línea al principio y al final |
423
+ | `dividir(t, separador)` | Lista con los pedazos de `t` entre cada separador |
424
+ | `unir(lista, separador)` | Un texto con los elementos de la lista separados por `separador` |
425
+ | `reemplazar(t, buscar, poner)` | Cambia cada aparición de `buscar` por `poner` |
426
+ | `contiene(t, parte)` | `verdadero` si `parte` aparece en `t` |
427
+ | `empieza_con(t, parte)`, `termina_con(t, parte)` | Si `t` empieza o termina con `parte` |
428
+ | `posición_de(t, parte)` | Dónde empieza la primera aparición de `parte`, o `-1` si no está |
429
+ | `subtexto(t, inicio, fin)` | Los caracteres de `inicio` hasta **antes** de `fin`: `subtexto("hola", 1, 3)` es `"ol"` |
430
+ | `caracteres(t)` | Lista con cada carácter |
431
+ | `invertir(t)` | El texto al revés |
432
+ | `código_de(c)`, `carácter_de(n)` | El código Unicode de un carácter, y el carácter de un código |
433
+
434
+ ```codexivo
435
+ variable frase = " El sol sale "
436
+ variable palabras = dividir(recortar(frase), " ")
437
+ imprimir(unir(palabras, "-"))
438
+ imprimir(mayúsculas(palabras[1]), contiene(frase, "sol"), posición_de(frase, "sale"))
439
+ ```
440
+
441
+ ### Listas
442
+
443
+ Regla: **`agregar`, `insertar`, `quitar` (y sus variantes) y `vaciar` cambian la lista; todo lo demás regresa un valor
444
+ nuevo.** Los índices siguen la misma convención que `[ ]`: desde 0, y los negativos cuentan desde el final.
445
+
446
+ | Procedimiento | Qué hace |
447
+ | ----------------------------------------------- | ------------------------------------------------------------------------------ |
448
+ | `agregar(lista, x)` | Agrega al final |
449
+ | `insertar(lista, i, x)` | Pone `x` en la posición `i` y recorre los demás |
450
+ | `quitar(lista, i)` | Quita el elemento en la posición `i` y lo regresa |
451
+ | `quitar_último(lista)`, `quitar_primero(lista)` | Quitan el último o el primero y lo regresan |
452
+ | `vaciar(lista)` | Quita todos los elementos (también sirve con un diccionario) |
453
+ | `primero(lista)`, `último(lista)` | Primer y último elemento |
454
+ | `contiene(lista, x)`, `posición_de(lista, x)` | Si `x` está (comparando por valor) y en qué posición, o `-1` |
455
+ | `rango(a, b, paso?)` | Lista de `a` a `b`, **incluyendo** `b`; con `paso` negativo cuenta hacia abajo |
456
+ | `índices(lista)` | Las posiciones válidas: `rango(0, longitud(lista) - 1)` |
457
+ | `rebanar(lista, inicio, fin)` | Los elementos de `inicio` hasta **antes** de `fin` |
458
+ | `invertir(lista)`, `copiar(lista)` | Una lista nueva al revés, o igual |
459
+ | `ordenar(lista)` | Una lista nueva ordenada; todos números o todos textos |
460
+ | `sumar(lista)` | La suma de sus números |
461
+ | `lista_de(n, valor)` | Lista con `n` copias de `valor` |
462
+ | `matriz(filas, columnas, valor)` | Lista de filas, cada una con `columnas` copias de `valor` |
463
+
464
+ `lista_de` y `matriz` copian el valor en cada casilla, así cambiar una no cambia las demás:
465
+
466
+ ```codexivo
467
+ variable tablero = matriz(3, 3, ".")
468
+ tablero[1][1] = "X"
469
+ para_cada fila en tablero {
470
+ imprimir(unir(fila, " "))
471
+ }
472
+
473
+ variable números = [5, 3, 8]
474
+ insertar(números, 0, 1)
475
+ imprimir(ordenar(números), sumar(números), quitar_último(números), números)
476
+ ```
477
+
478
+ ### Diccionarios
479
+
480
+ | Procedimiento | Qué hace |
481
+ | ------------------------- | ----------------------------------------------------------------- |
482
+ | `claves(d)`, `valores(d)` | Listas nuevas con las claves o los valores, en orden de inserción |
483
+ | `tiene(d, clave)` | `verdadero` si el diccionario tiene esa clave |
484
+ | `quitar(d, clave)` | Quita la clave y regresa su valor (cambia el diccionario) |
485
+ | `copiar(d)` | Un diccionario nuevo con las mismas claves y valores |
486
+
487
+ ## Errores
488
+
489
+ Cada error dice de qué tipo es (léxico, sintaxis, ejecución o límite), qué pasó, dónde y, casi siempre, una pista:
490
+
491
+ ```
492
+ Error de ejecución en la línea 2, columna 5: No se puede dividir entre cero.
493
+ Pista: Revisa que el divisor no valga 0 antes de dividir.
494
+ ```
495
+
496
+ ## Puente hacia otros lenguajes
497
+
498
+ Muchos lenguajes (C, Java, JavaScript) cuentan con un `for` de tres partes. Codexivo también lo tiene, para practicar antes de
499
+ pasar a ellos:
500
+
501
+ ```codexivo
502
+ para (variable i = 1; i <= 10; i += 1) {
503
+ imprimir(i)
504
+ }
505
+ ```
506
+
507
+ Es el mismo bucle que `para i desde 1 hasta 10`, escrito a mano:
508
+
509
+ | Parte | Qué hace | En `para … desde … hasta` |
510
+ | ---------------- | ---------------------------------------------------- | ------------------------- |
511
+ | `variable i = 1` | Se ejecuta una vez, al empezar | `desde 1` |
512
+ | `i <= 10` | Se revisa antes de cada vuelta; si es falsa, termina | `hasta 10` |
513
+ | `i += 1` | Se ejecuta al final de cada vuelta | `con_paso 1` |
514
+
515
+ La diferencia es que aquí todo es explícito y modificable: la condición se vuelve a evaluar en cada vuelta y la variable se
516
+ puede cambiar dentro del cuerpo. Cualquiera de las tres partes puede faltar (`para (; condición;)`). La variable solo existe
517
+ dentro del bucle.
518
+
519
+ ## Límites
520
+
521
+ Para que un error nunca congele la página, la ejecución se detiene con un error de límite al superar 10 000 000 de
522
+ operaciones, 10 000 llamadas anidadas o 1 000 000 de caracteres impresos. Los tres se pueden configurar.
523
+
524
+ ## Operaciones
525
+
526
+ Cada operación básica (leer una variable, poner un valor, sumar, comparar, asignar, decidir, llamar) cuesta 1. Los
527
+ procedimientos de la biblioteca cuestan según el trabajo que hacen, con `n` el tamaño de su primer argumento:
528
+
529
+ | Costo | Procedimientos |
530
+ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
531
+ | `n · ⌈log₂ n⌉` | `ordenar` |
532
+ | `n` | `contiene`, `posición_de`, `copiar`, `invertir`, `sumar`, `insertar`, `quitar`, `quitar_primero`, `desencolar`, `claves`, `valores`, `índices`, `máximo`, `mínimo`, `mapear`, `filtrar`, `reducir`, `alguno`, `todos`, `buscar`, y los de texto que recorren el texto |
533
+ | lo que crean | `rango`, `rebanar`, `subtexto`, `lista_de`, `matriz` |
534
+ | 1 | todos los demás |
535
+
536
+ Lo que hace el procedimiento que recibe `mapear` (y los demás) se cuenta aparte, como cualquier código. El mismo programa con
537
+ la misma entrada siempre da el mismo número de operaciones, y los retos lo usan en lugar del tiempo.
@@ -0,0 +1,128 @@
1
+ # Modo mundo
2
+
3
+ Codexivo nació de querer algo como el robot Karel. El **modo mundo** es el mismo lenguaje con procedimientos extra para
4
+ mover un robot en una cuadrícula. Este repositorio define el mundo, sus reglas y sus eventos; el sitio lo dibuja.
5
+
6
+ ## El mundo
7
+
8
+ Un mundo se describe en JSON (formato versión 1):
9
+
10
+ ```json
11
+ {
12
+ "version": 1,
13
+ "width": 5,
14
+ "height": 1,
15
+ "walls": [],
16
+ "beepers": [
17
+ { "x": 2, "y": 1, "count": 1 },
18
+ { "x": 4, "y": 1, "count": 3 }
19
+ ],
20
+ "robot": { "x": 1, "y": 1, "facing": "este", "beepers": 0 }
21
+ }
22
+ ```
23
+
24
+ | Campo | Qué es |
25
+ | ----------------- | -------------------------------------------------------------------------------------------------------------------- |
26
+ | `version` | Siempre `1` |
27
+ | `width`, `height` | Casillas de oeste a este y de sur a norte (de 1 a 100). La casilla suroeste es `(1, 1)` |
28
+ | `walls` | Paredes: `{ "x", "y", "side" }` es la pared del lado `side` (`"norte"`, `"este"`, `"sur"`, `"oeste"`) de esa casilla |
29
+ | `beepers` | Zumbadores por casilla: `{ "x", "y", "count" }`, con `count` un entero o `"infinito"` |
30
+ | `robot` | Posición, orientación (`facing`) y zumbadores en la mochila (`beepers`, entero o `"infinito"`) |
31
+
32
+ Los bordes de la cuadrícula siempre son paredes. `validateWorld(json)` revisa un mundo y regresa los problemas con su
33
+ ubicación (`robot.x`, `walls[2].side`…) para mostrarlos en el editor.
34
+
35
+ ## Los procedimientos
36
+
37
+ Solo existen cuando el programa corre en un mundo.
38
+
39
+ | Acción | Qué hace |
40
+ | -------------------- | ---------------------------------------------- |
41
+ | `avanzar()` | Avanza una casilla hacia donde mira |
42
+ | `girar_izquierda()` | Gira 90° a la izquierda |
43
+ | `recoger_zumbador()` | Toma un zumbador de la casilla y lo guarda |
44
+ | `dejar_zumbador()` | Deja un zumbador de la mochila en la casilla |
45
+ | `apagar()` | Apaga el robot; después ya no puede hacer nada |
46
+
47
+ | Pregunta | Responde `verdadero` si… |
48
+ | ----------------------------------------------------------------------------------- | -------------------------------------- |
49
+ | `frente_libre()`, `izquierda_libre()`, `derecha_libre()` | No hay pared en esa dirección |
50
+ | `hay_zumbador()` | Hay al menos un zumbador en la casilla |
51
+ | `tengo_zumbadores()` | Hay al menos un zumbador en la mochila |
52
+ | `mirando_al_norte()`, `mirando_al_sur()`, `mirando_al_este()`, `mirando_al_oeste()` | El robot mira hacia ese lado |
53
+
54
+ No hay `girar_derecha()`: construirlo es el primer ejercicio.
55
+
56
+ ## Un ejemplo
57
+
58
+ Recoger todos los zumbadores de la fila y volver al inicio:
59
+
60
+ ```codexivo
61
+ procedimiento girar_derecha() {
62
+ repetir 3 veces {
63
+ girar_izquierda()
64
+ }
65
+ }
66
+
67
+ procedimiento recoger_todos() {
68
+ mientras (hay_zumbador()) {
69
+ recoger_zumbador()
70
+ }
71
+ }
72
+
73
+ mientras (frente_libre()) {
74
+ avanzar()
75
+ recoger_todos()
76
+ }
77
+
78
+ // media vuelta y de regreso
79
+ girar_derecha()
80
+ girar_derecha()
81
+ mientras (frente_libre()) {
82
+ avanzar()
83
+ }
84
+ apagar()
85
+ ```
86
+
87
+ Al terminar, el robot está en `(1, 1)` mirando al oeste, apagado y con los 4 zumbadores en la mochila.
88
+
89
+ ## Errores
90
+
91
+ Son errores de ejecución que dicen dónde está y hacia dónde mira el robot:
92
+
93
+ | Código | Cuándo |
94
+ | ------------------ | ----------------------------------------------- |
95
+ | `CHOQUE_CON_PARED` | `avanzar()` contra una pared o el borde |
96
+ | `SIN_ZUMBADORES` | `recoger_zumbador()` donde no hay |
97
+ | `MOCHILA_VACÍA` | `dejar_zumbador()` sin zumbadores en la mochila |
98
+ | `ROBOT_APAGADO` | Cualquier acción después de `apagar()` |
99
+
100
+ ```
101
+ Error de ejecución en la línea 3, columna 1: El robot chocó con una pared. El robot está en (2, 1) mirando al este.
102
+ Pista: Pregunta antes con `frente_libre()`.
103
+ ```
104
+
105
+ ## Desde JavaScript
106
+
107
+ ```ts
108
+ import { checkGoal, createWorld, run } from 'codexivo';
109
+
110
+ const world = createWorld(definición);
111
+ const result = run(código, { world, trace: true });
112
+
113
+ result.world; // estado final: el mismo formato del mundo, más `robot.on`
114
+ result.events?.filter(event => event.type === 'robot'); // una por acción: before, after y la casilla
115
+
116
+ checkGoal(result.world!, {
117
+ robot: { x: 1, y: 1, on: false },
118
+ beepers: [],
119
+ onlyListedBeepers: true, // las casillas no listadas tienen que quedar vacías
120
+ });
121
+ // → { ok: true, differences: [] }
122
+ ```
123
+
124
+ - Cada acción emite un evento `robot` con `action`, el robot `before` y `after` (posición, orientación, mochila y si
125
+ está encendido) y, al recoger o dejar, los zumbadores que quedaron en la casilla (`cell`).
126
+ - `machine.snapshot().world` trae el estado del mundo en cualquier pausa.
127
+ - `checkGoal` compara el mundo final con un objetivo y solo revisa lo que el objetivo indica; cada diferencia trae un
128
+ mensaje para mostrarlo en el reto.