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.
- package/CHANGELOG.md +15 -0
- package/README.md +159 -2
- package/dist/index.js +154 -0
- package/dist/index.js.map +30 -0
- package/dist/worker.js +153 -0
- package/dist/worker.js.map +25 -0
- package/docs/README.md +27 -0
- package/docs/arquitectura.md +174 -0
- package/docs/especificacion-v1.md +701 -0
- package/docs/guia-aprender-desde-el-codigo.md +91 -0
- package/docs/integracion.md +104 -0
- package/docs/lenguaje.md +537 -0
- package/docs/modo-mundo.md +128 -0
- package/docs/recetario.md +159 -0
- package/docs/repl.md +64 -0
- package/package.json +66 -4
- package/src/catalog.ts +256 -0
- package/src/challenge.ts +138 -0
- package/src/engine.ts +104 -0
- package/src/error-codes.ts +427 -0
- package/src/errors.ts +56 -0
- package/src/flow.ts +385 -0
- package/src/index.ts +81 -0
- package/src/printer.ts +133 -0
- package/src/runtime/builtins.ts +1426 -0
- package/src/runtime/compiler.ts +499 -0
- package/src/runtime/costs.ts +96 -0
- package/src/runtime/machine.ts +1521 -0
- package/src/runtime/prelude.ts +230 -0
- package/src/runtime/suggest.ts +33 -0
- package/src/runtime/values.ts +241 -0
- package/src/sandbox-host.ts +310 -0
- package/src/sandbox.ts +95 -0
- package/src/syntax/ast.ts +344 -0
- package/src/syntax/lexer.ts +266 -0
- package/src/syntax/parser.ts +1090 -0
- package/src/syntax/token.ts +215 -0
- package/src/worker.ts +18 -0
- package/src/world.ts +518 -0
package/docs/lenguaje.md
ADDED
|
@@ -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.
|