codexivo 0.2.0 → 1.0.0

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,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.
package/docs/recetario.md CHANGED
@@ -7,10 +7,10 @@ archivo `.codexivo`.
7
7
 
8
8
  ```codexivo
9
9
  variable saludar = procedimiento(nombre) {
10
- regresa "Hola " + nombre + "!";
11
- };
10
+ regresa "Hola " + nombre + "!"
11
+ }
12
12
 
13
- saludar("Codexivo");
13
+ saludar("Codexivo")
14
14
  ```
15
15
 
16
16
  Demuestra cómo declarar un procedimiento, concatenar cadenas y retornar un valor.
@@ -19,16 +19,16 @@ Demuestra cómo declarar un procedimiento, concatenar cadenas y retornar un valo
19
19
 
20
20
  ```codexivo
21
21
  variable clasificar = procedimiento(edad) {
22
- si (edad >= 18) {
23
- regresa "adulto";
24
- } pero_si (edad >= 13) {
25
- regresa "adolescente";
22
+ si (edad es_mayor_o_igual_a 18) {
23
+ regresa "adulto"
24
+ } pero_si (edad es_mayor_o_igual_a 13) {
25
+ regresa "adolescente"
26
26
  } si_no {
27
- regresa "niño";
27
+ regresa "niño"
28
28
  }
29
- };
29
+ }
30
30
 
31
- clasificar(16);
31
+ clasificar(16)
32
32
  ```
33
33
 
34
34
  Ilustra el encadenamiento `si`/`pero_si`/`si_no` y el uso de operadores relacionales.
@@ -37,11 +37,11 @@ Ilustra el encadenamiento `si`/`pero_si`/`si_no` y el uso de operadores relacion
37
37
 
38
38
  ```codexivo
39
39
  variable promedio = procedimiento(a, b, c) {
40
- variable total = a + b + c;
41
- regresa total / 3;
42
- };
40
+ variable total = a + b + c
41
+ regresa total / 3
42
+ }
43
43
 
44
- promedio(8, 7.5, 9);
44
+ promedio(8, 7.5, 9)
45
45
  ```
46
46
 
47
47
  Combina números enteros y decimales con operaciones aritméticas.
@@ -50,30 +50,30 @@ Combina números enteros y decimales con operaciones aritméticas.
50
50
 
51
51
  ```codexivo
52
52
  variable valorAbsoluto = procedimiento(n) {
53
- si (n < 0) {
54
- regresa -n;
53
+ si (n es_menor_que 0) {
54
+ regresa -n
55
55
  } si_no {
56
- regresa n;
56
+ regresa n
57
57
  }
58
- };
58
+ }
59
59
 
60
- valorAbsoluto(-42);
60
+ valorAbsoluto(-42)
61
61
  ```
62
62
 
63
- Ejemplo de uso del operador prefijo `-` y del comportamiento truthy/falsy en condicionales.
63
+ Ejemplo de uso del operador prefijo `-`.
64
64
 
65
65
  ## 5. Contador recursivo
66
66
 
67
67
  ```codexivo
68
68
  variable cuentaRegresiva = procedimiento(n) {
69
- si (n == 0) {
70
- regresa "¡Despegue!";
69
+ si (n es_igual_a 0) {
70
+ regresa "¡Despegue!"
71
71
  }
72
72
 
73
- regresa cuentaRegresiva(n - 1);
74
- };
73
+ regresa cuentaRegresiva(n - 1)
74
+ }
75
75
 
76
- cuentaRegresiva(3);
76
+ cuentaRegresiva(3)
77
77
  ```
78
78
 
79
79
  Muestra cómo las funciones en Codexivo soportan recursión gracias a que conservan su entorno de definición.
@@ -82,48 +82,48 @@ Muestra cómo las funciones en Codexivo soportan recursión gracias a que conser
82
82
 
83
83
  ```codexivo
84
84
  variable esNombreValido = procedimiento(nombre) {
85
- variable tamaño = longitud(nombre);
86
- si (tamaño >= 3 y tamaño <= 15) {
87
- regresa verdadero;
85
+ variable tamaño = longitud(nombre)
86
+ si (tamaño es_mayor_o_igual_a 3 y tamaño es_menor_o_igual_a 15) {
87
+ regresa verdadero
88
88
  }
89
- regresa falso;
90
- };
89
+ regresa falso
90
+ }
91
91
 
92
- esNombreValido("Ana");
92
+ esNombreValido("Ana")
93
93
  ```
94
94
 
95
95
  Ejemplo de uso del builtin `longitud` junto con operadores de comparación y booleanos.
96
96
 
97
- ## 7. Suma con bucle for
97
+ ## 7. Suma con un bucle que cuenta
98
98
 
99
99
  ```codexivo
100
- variable sumarHasta = procedimiento(n) {
101
- variable suma = 0;
102
- para (variable i = 1; i <= n; i += 1) {
103
- suma += i;
100
+ procedimiento sumar_hasta(n) {
101
+ variable suma = 0
102
+ para i desde 1 hasta n {
103
+ suma = suma + i
104
104
  }
105
- regresa suma;
106
- };
105
+ regresa suma
106
+ }
107
107
 
108
- sumarHasta(10); // Suma de 1 a 10 = 55
108
+ sumar_hasta(10) // Suma de 1 a 10 = 55
109
109
  ```
110
110
 
111
- Demuestra el uso de bucles `para` con operadores de asignación compuesta.
111
+ Demuestra `para … desde … hasta`, que incluye los dos extremos.
112
112
 
113
113
  ## 8. Búsqueda en arreglo
114
114
 
115
115
  ```codexivo
116
- variable buscar = procedimiento(arreglo, elemento) {
117
- para (variable i = 0; i < longitud(arreglo); i += 1) {
118
- si (arreglo[i] == elemento) {
119
- regresa i;
116
+ procedimiento buscar_posición(lista, elemento) {
117
+ para_cada i en índices(lista) {
118
+ si (lista[i] es_igual_a elemento) {
119
+ regresa i
120
120
  }
121
121
  }
122
- regresa -1;
123
- };
122
+ regresa -1
123
+ }
124
124
 
125
- variable numeros = [10, 20, 30, 40];
126
- buscar(numeros, 30); // Regresa 2
125
+ variable números = [10, 20, 30, 40]
126
+ buscar_posición(números, 30) // Regresa 2
127
127
  ```
128
128
 
129
129
  Combina bucles, acceso a arreglos, condicionales y funciones built-in.
@@ -132,25 +132,25 @@ Combina bucles, acceso a arreglos, condicionales y funciones built-in.
132
132
 
133
133
  ```codexivo
134
134
  variable estadisticas = procedimiento(datos) {
135
- variable suma = 0;
136
- variable mayor = primero(datos);
137
- variable menor = primero(datos);
138
-
139
- para (variable i = 0; i < longitud(datos); i += 1) {
140
- suma += datos[i];
141
- si (datos[i] > mayor) {
142
- mayor = datos[i];
135
+ variable suma = 0
136
+ variable mayor = primero(datos)
137
+ variable menor = primero(datos)
138
+
139
+ para_cada dato en datos {
140
+ suma = suma + dato
141
+ si (dato es_mayor_que mayor) {
142
+ mayor = dato
143
143
  }
144
- si (datos[i] < menor) {
145
- menor = datos[i];
144
+ si (dato es_menor_que menor) {
145
+ menor = dato
146
146
  }
147
147
  }
148
- variable promedio = suma / longitud(datos);
149
-
150
- regresa [redondear(promedio), mayor, menor];
151
- };
148
+ variable promedio = suma / longitud(datos)
152
149
 
153
- estadisticas([5, 2, 8, 1, 9]); // [5, 9, 1]
150
+ regresa [redondear(promedio), mayor, menor]
151
+ }
152
+
153
+ estadisticas([5, 2, 8, 1, 9]) // [5, 9, 1]
154
154
  ```
155
155
 
156
156
  Muestra el uso de las funciones matemáticas built-in y manipulación de arreglos.
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "codexivo",
3
- "version": "0.2.0",
3
+ "version": "1.0.0",
4
4
  "description": "Lenguaje de programación en español para practicar lógica y algoritmos, que se ejecuta en el navegador.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
8
8
  "types": "src/index.ts",
9
9
  "files": [
10
- "bin/codexivo.exe",
11
- "postinstall.mjs",
12
10
  "dist/index.js",
13
11
  "dist/index.js.map",
12
+ "dist/worker.js",
13
+ "dist/worker.js.map",
14
14
  "src",
15
15
  "!src/cli.ts",
16
16
  "docs",
@@ -20,6 +20,10 @@
20
20
  ".": {
21
21
  "types": "./src/index.ts",
22
22
  "import": "./dist/index.js"
23
+ },
24
+ "./worker": {
25
+ "types": "./src/worker.ts",
26
+ "import": "./dist/worker.js"
23
27
  }
24
28
  },
25
29
  "scripts": {
@@ -27,16 +31,16 @@
27
31
  "check": "bun check",
28
32
  "repl": "bun run src/cli.ts",
29
33
  "build": "bun run build:lib && bun run build:binary",
30
- "build:lib": "bun build ./src/index.ts --target browser --format esm --outdir=dist --minify --sourcemap=linked",
34
+ "build:lib": "bun build ./src/index.ts ./src/worker.ts --target browser --format esm --outdir=dist --minify --sourcemap=linked",
31
35
  "build:binary": "bun run scripts/build-binaries.ts",
32
36
  "build:binaries": "bun run scripts/build-binaries.ts --all",
33
37
  "test:package": "bun run scripts/test-package.ts",
38
+ "docs:biblioteca": "bun run scripts/docs-biblioteca.ts",
34
39
  "prepack": "bun run build:lib",
35
40
  "version": "bun run scripts/sync-native-versions.ts && git add package.json bun.lock packages/*/package.json",
36
41
  "publish:native": "bun run scripts/publish-native.ts",
37
42
  "format": "bun fmt",
38
- "format:check": "bun fmt --check",
39
- "postinstall": "node ./postinstall.mjs"
43
+ "format:check": "bun fmt --check"
40
44
  },
41
45
  "keywords": [
42
46
  "lenguaje de programación",
@@ -60,18 +64,5 @@
60
64
  },
61
65
  "workspaces": [
62
66
  "packages/*"
63
- ],
64
- "optionalDependencies": {
65
- "codexivo-linux-x64": "0.2.0",
66
- "codexivo-linux-x64-musl": "0.2.0",
67
- "codexivo-linux-arm64": "0.2.0",
68
- "codexivo-linux-arm64-musl": "0.2.0",
69
- "codexivo-darwin-x64": "0.2.0",
70
- "codexivo-darwin-arm64": "0.2.0",
71
- "codexivo-windows-x64": "0.2.0",
72
- "codexivo-windows-arm64": "0.2.0"
73
- },
74
- "bin": {
75
- "codexivo": "./bin/codexivo.exe"
76
- }
67
+ ]
77
68
  }
package/src/catalog.ts ADDED
@@ -0,0 +1,257 @@
1
+ import { ERROR_CODES } from './error-codes';
2
+ import type { ErrorKind } from './errors';
3
+ import { constantDocs, constants, natives, withoutAccents } from './runtime/builtins';
4
+ import { libraryDocs } from './runtime/prelude';
5
+ import type { ProcedureCategory, ProcedureDoc, ProcedureLevel } from './runtime/values';
6
+ import { KEYWORDS, TokenType } from './syntax/token';
7
+ import { EXAMPLE_WORLD, worldDocs } from './world';
8
+ import type { WorldDefinition } from './world';
9
+
10
+ /**
11
+ * El lenguaje y su biblioteca como datos, para que el resaltado, el autocompletado, la documentación y los
12
+ * avisos del editor del sitio salgan de una sola fuente. Todo es serializable: no hay funciones.
13
+ */
14
+
15
+ export type KeywordGroup = 'declaración' | 'control' | 'lógicas' | 'literales' | 'operadores';
16
+
17
+ export interface OperatorEntry {
18
+ symbol?: string;
19
+ /** Forma en palabras (`es_igual_a`, `mod`), si la tiene. */
20
+ word?: string;
21
+ /** Nivel de precedencia de la §4.1: 1 es el que agrupa al final, 9 el primero. */
22
+ precedence: number;
23
+ associativity: 'izquierda' | 'derecha' | 'prefijo' | 'sin encadenar';
24
+ description: string;
25
+ }
26
+
27
+ export interface ProcedureEntry extends ProcedureDoc {
28
+ /** Nombre canónico, con acento si lo lleva. */
29
+ name: string;
30
+ /** Otras formas aceptadas (el nombre sin acento). */
31
+ aliases: string[];
32
+ }
33
+
34
+ export interface ConstantEntry {
35
+ name: string;
36
+ value: number;
37
+ level: ProcedureLevel;
38
+ description: string;
39
+ }
40
+
41
+ export interface ErrorEntry {
42
+ code: string;
43
+ kind: ErrorKind;
44
+ explanation: string;
45
+ }
46
+
47
+ export interface Catalog {
48
+ version: 1;
49
+ keywords: Array<{ group: KeywordGroup; words: string[] }>;
50
+ operators: OperatorEntry[];
51
+ /** Procedimientos de la biblioteca, incluidos los del modo mundo (categoría `mundo`). */
52
+ library: ProcedureEntry[];
53
+ constants: ConstantEntry[];
54
+ errors: ErrorEntry[];
55
+ categories: ProcedureCategory[];
56
+ /** Mundo con el que se ejecutan los ejemplos de la categoría `mundo`. */
57
+ exampleWorld: WorldDefinition;
58
+ }
59
+
60
+ const KEYWORD_GROUPS: Readonly<Record<string, KeywordGroup>> = {
61
+ [TokenType.LET]: 'declaración',
62
+ [TokenType.CONST]: 'declaración',
63
+ [TokenType.DOMAIN]: 'declaración',
64
+ [TokenType.FUNCTION]: 'declaración',
65
+ [TokenType.RETURN]: 'declaración',
66
+ [TokenType.IF]: 'control',
67
+ [TokenType.ELSEIF]: 'control',
68
+ [TokenType.ELSE]: 'control',
69
+ [TokenType.WHILE]: 'control',
70
+ [TokenType.DO]: 'control',
71
+ [TokenType.UNTIL]: 'control',
72
+ [TokenType.FOR]: 'control',
73
+ [TokenType.FOR_EACH]: 'control',
74
+ [TokenType.FROM]: 'control',
75
+ [TokenType.TO]: 'control',
76
+ [TokenType.STEP]: 'control',
77
+ [TokenType.IN]: 'control',
78
+ [TokenType.REPEAT]: 'control',
79
+ [TokenType.TIMES]: 'control',
80
+ [TokenType.BREAK]: 'control',
81
+ [TokenType.CONTINUE]: 'control',
82
+ [TokenType.AND]: 'lógicas',
83
+ [TokenType.OR]: 'lógicas',
84
+ [TokenType.NOT]: 'lógicas',
85
+ [TokenType.TRUE]: 'literales',
86
+ [TokenType.FALSE]: 'literales',
87
+ [TokenType.NULL]: 'literales',
88
+ [TokenType.MOD]: 'operadores',
89
+ [TokenType.DIV]: 'operadores',
90
+ [TokenType.EQ]: 'operadores',
91
+ [TokenType.NEQ]: 'operadores',
92
+ [TokenType.LT]: 'operadores',
93
+ [TokenType.GT]: 'operadores',
94
+ [TokenType.LTE]: 'operadores',
95
+ [TokenType.GTE]: 'operadores',
96
+ };
97
+
98
+ const GROUP_ORDER: KeywordGroup[] = ['declaración', 'control', 'lógicas', 'literales', 'operadores'];
99
+
100
+ /** Grupo de cada palabra reservada; una palabra sin grupo es un error del catálogo (lo revisa una prueba). */
101
+ export const keywordGroup = (word: string): KeywordGroup | undefined => KEYWORD_GROUPS[KEYWORDS[word] ?? ''];
102
+
103
+ const keywords = GROUP_ORDER.map(group => ({
104
+ group,
105
+ words: Object.keys(KEYWORDS).filter(word => keywordGroup(word) === group),
106
+ }));
107
+
108
+ const operators: OperatorEntry[] = [
109
+ {
110
+ word: 'o',
111
+ precedence: 1,
112
+ associativity: 'izquierda',
113
+ description: 'Verdadero si alguno de los dos lo es. Si el izquierdo es verdadero, no evalúa el derecho.',
114
+ },
115
+ {
116
+ word: 'y',
117
+ precedence: 2,
118
+ associativity: 'izquierda',
119
+ description: 'Verdadero si los dos lo son. Si el izquierdo es falso, no evalúa el derecho.',
120
+ },
121
+ {
122
+ symbol: '!',
123
+ word: 'no',
124
+ precedence: 3,
125
+ associativity: 'prefijo',
126
+ description: 'Cambia verdadero por falso y al revés.',
127
+ },
128
+ {
129
+ symbol: '==',
130
+ word: 'es_igual_a',
131
+ precedence: 4,
132
+ associativity: 'sin encadenar',
133
+ description: 'Igualdad por valor, también de listas y diccionarios.',
134
+ },
135
+ {
136
+ symbol: '!=',
137
+ word: 'es_distinto_de',
138
+ precedence: 4,
139
+ associativity: 'sin encadenar',
140
+ description: 'Lo contrario de `==`.',
141
+ },
142
+ {
143
+ symbol: '<',
144
+ word: 'es_menor_que',
145
+ precedence: 4,
146
+ associativity: 'sin encadenar',
147
+ description: 'Compara dos números o dos textos.',
148
+ },
149
+ {
150
+ symbol: '>',
151
+ word: 'es_mayor_que',
152
+ precedence: 4,
153
+ associativity: 'sin encadenar',
154
+ description: 'Compara dos números o dos textos.',
155
+ },
156
+ {
157
+ symbol: '<=',
158
+ word: 'es_menor_o_igual_a',
159
+ precedence: 4,
160
+ associativity: 'sin encadenar',
161
+ description: 'Compara dos números o dos textos.',
162
+ },
163
+ {
164
+ symbol: '>=',
165
+ word: 'es_mayor_o_igual_a',
166
+ precedence: 4,
167
+ associativity: 'sin encadenar',
168
+ description: 'Compara dos números o dos textos.',
169
+ },
170
+ {
171
+ symbol: '+',
172
+ precedence: 5,
173
+ associativity: 'izquierda',
174
+ description: 'Suma dos números o une dos textos; no los mezcla.',
175
+ },
176
+ { symbol: '-', precedence: 5, associativity: 'izquierda', description: 'Resta.' },
177
+ { symbol: '*', precedence: 6, associativity: 'izquierda', description: 'Multiplica.' },
178
+ { symbol: '/', precedence: 6, associativity: 'izquierda', description: 'División real: `7 / 2` es `3.5`.' },
179
+ {
180
+ word: 'div',
181
+ precedence: 6,
182
+ associativity: 'izquierda',
183
+ description: 'División entera hacia abajo: `-7 div 2` es `-4`.',
184
+ },
185
+ {
186
+ symbol: '%',
187
+ word: 'mod',
188
+ precedence: 6,
189
+ associativity: 'izquierda',
190
+ description: 'Residuo consistente con `div`: `-7 mod 3` es `2`.',
191
+ },
192
+ { symbol: '-', precedence: 7, associativity: 'prefijo', description: 'Cambia el signo.' },
193
+ {
194
+ symbol: '^',
195
+ precedence: 8,
196
+ associativity: 'derecha',
197
+ description: 'Potencia: `2 ^ 3 ^ 2` es `2 ^ 9`, y `-2 ^ 2` es `-4`.',
198
+ },
199
+ { symbol: '( )', precedence: 9, associativity: 'izquierda', description: 'Llama a un procedimiento: `f(x)`.' },
200
+ {
201
+ symbol: '[ ]',
202
+ precedence: 9,
203
+ associativity: 'izquierda',
204
+ description: 'Elemento de una lista o texto, o clave de un diccionario: `l[0]`, `d["a"]`.',
205
+ },
206
+ {
207
+ symbol: '.',
208
+ precedence: 9,
209
+ associativity: 'izquierda',
210
+ description: 'Campo de un diccionario: `persona.edad` es `persona["edad"]`.',
211
+ },
212
+ ];
213
+
214
+ const entry = (name: string, doc: ProcedureDoc): ProcedureEntry => {
215
+ const alias = withoutAccents(name);
216
+ return { name, aliases: alias === name ? [] : [alias], ...doc };
217
+ };
218
+
219
+ const library: ProcedureEntry[] = [
220
+ ...Object.values(natives)
221
+ .filter((native, index, all) => all.indexOf(native) === index)
222
+ .map(native => entry(native.name, native.doc!)),
223
+ ...Object.entries(libraryDocs).map(([name, doc]) => entry(name, doc)),
224
+ ...Object.entries(worldDocs).map(([name, doc]) => entry(name, doc)),
225
+ ];
226
+
227
+ const CATEGORIES: ProcedureCategory[] = [
228
+ 'entrada y salida',
229
+ 'tipos',
230
+ 'verificación',
231
+ 'matemáticas',
232
+ 'texto',
233
+ 'listas',
234
+ 'diccionarios',
235
+ 'procedimientos',
236
+ 'estructuras',
237
+ 'mundo',
238
+ ];
239
+
240
+ export const catalog: Catalog = {
241
+ version: 1,
242
+ keywords,
243
+ operators,
244
+ library: library.sort(
245
+ (a, b) => CATEGORIES.indexOf(a.category) - CATEGORIES.indexOf(b.category) || a.name.localeCompare(b.name, 'es'),
246
+ ),
247
+ constants: Object.entries(constantDocs).map(([name, doc]) => ({
248
+ name,
249
+ value: constants[name] as number,
250
+ ...doc,
251
+ })),
252
+ errors: Object.entries(ERROR_CODES)
253
+ .map(([code, info]) => ({ code, ...info }))
254
+ .sort((a, b) => a.code.localeCompare(b.code, 'es')),
255
+ categories: CATEGORIES,
256
+ exampleWorld: EXAMPLE_WORLD,
257
+ };