codexivo 1.0.0 → 1.0.1

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.
@@ -29,6 +29,51 @@ código ──tokenize──▶ tokens ──parse──▶ AST ──compile─
29
29
  - Ante un error, el parser lo registra, salta hasta un punto seguro (un `;`, una `}`, una palabra que empieza sentencia o una
30
30
  línea nueva) y sigue. Así reporta varios errores en una pasada sin inventar errores en cascada.
31
31
 
32
+ ### Traza del parser
33
+
34
+ `parse(código, { trace: true })` (o `traceParse(código)`) regresa además `trace`: los pasos con los que el parser construyó
35
+ el árbol, en orden. Con eso el sitio puede animar **cómo** se construye, no solo mostrar el resultado. Cada evento es un
36
+ objeto plano con su `span`:
37
+
38
+ | Evento | Qué cuenta |
39
+ | ------------ | -------------------------------------------------------------------------------------------------------------- |
40
+ | `consume` | El parser toma un token: `index` en `tokens`, `tokenType` y `lexeme`. |
41
+ | `enter` | Empieza a construir un nodo (`kind`). Si lo construye a partir de uno ya hecho, `left` es el `id` de ese nodo. |
42
+ | `exit` | Termina el nodo: `kind` e `id`, el mismo `id` que tiene en el AST. |
43
+ | `precedence` | Decide de quién es un valor (ver abajo). |
44
+ | `error` | Un error de sintaxis, con su `code` y `message` (el mismo que está en `errors`). |
45
+ | `recover` | Después de un error: qué nodos abiertos abandona y qué tokens salta para seguir. |
46
+
47
+ **Nodos abiertos.** Entre el `enter` y el `exit` de un nodo, el nodo está abierto esperando a sus hijos, que son los nodos
48
+ que terminan en medio. Un parser Pratt no sabe que `1` es el lado izquierdo de una suma hasta que ve el `+`: por eso el
49
+ `enter` del `Binary` llega después del `exit` del `1` y lleva `left` con su `id`; la vista mueve ese nodo dentro del nuevo.
50
+ Lo mismo pasa con las llamadas (`f` en `f(1)`), los índices, las asignaciones (`x` en `x = 1`) y las sentencias que son
51
+ una expresión.
52
+
53
+ **Precedencia.** Es la decisión central del parser. En `1 + 2 * 3`, después de leer `2` el parser está construyendo el lado
54
+ derecho del `+` y llega el `*`. El evento lleva:
55
+
56
+ - `operand`: el `id` del valor en disputa (el `2`);
57
+ - `building`: el operador que está esperando ese valor (`+`) y la precedencia con la que compara; no viene si ningún
58
+ operador lo espera (al inicio de una expresión o dentro de un paréntesis);
59
+ - `incoming`: el operador que llegó (`*`) y su precedencia;
60
+ - `decision`: `se_lo_lleva` si `incoming` tiene más precedencia (el `2` pasa a ser el lado izquierdo del `*`) o `se_queda`
61
+ si no (el `2` se queda como lado derecho del `+`).
62
+
63
+ En `(1 + 2) * 3` el paréntesis cierra la suma antes de que llegue el `*`: el evento del `*` no trae `building` y su
64
+ `operand` es la suma completa. En `1 < 2 y 5 < 4`, el `2` se queda con el `<` porque la `y` tiene menos precedencia. Como
65
+ `^` agrupa a la derecha, su lado derecho compara con una precedencia menos que la suya: en `2 ^ 3 ^ 2` el segundo `^` se
66
+ lleva al `3`. Las llamadas, los índices y el `.` también tienen precedencia (la más alta) y producen su evento.
67
+
68
+ **Errores.** Los nodos que estaban abiertos cuando falló la sentencia se abandonan: no tienen `exit` y no están en el AST.
69
+ `recover` los lista (`abandoned`, del más interno al más externo) junto con los tokens que saltó (`skipped.from` hasta
70
+ `skipped.to - 1`); el siguiente `consume` es el token `skipped.to`. Los errores léxicos no aparecen en la traza; el lexer
71
+ ya los reporta en `errors`.
72
+
73
+ La traza no cambia nada del análisis: sin `trace`, el parser no la construye; con ella, el AST y sus `id` son los mismos; el
74
+ mismo código produce siempre la misma traza, y los eventos se pueden mandar desde un Web Worker (en el sandbox, con la
75
+ opción `parseTrace` de `cargar`).
76
+
32
77
  ## 3. Compilador (`src/runtime/compiler.ts`)
33
78
 
34
79
  `compile(program)` traduce el AST a una lista de instrucciones para una máquina de pila. Por ejemplo, `x += 1` se convierte en:
@@ -171,4 +216,4 @@ cualquier otra vista.
171
216
 
172
217
  1. `bun run repl` y escribe programas pequeños.
173
218
  2. Lee los tests de `tests/` en el orden del pipeline: `lexer`, `parser`, `machine`, `stepping`.
174
- 3. Usa `tokenize`, `parse` y `run(código, { trace: true })` para ver cada etapa del mismo programa.
219
+ 3. Usa `tokenize`, `traceParse` y `run(código, { trace: true })` para ver cada etapa del mismo programa.
@@ -62,6 +62,7 @@ promesa se rechaza.
62
62
  | `world` | — | Un mundo del [modo mundo](./modo-mundo.md), en JSON |
63
63
  | `detail` | `decisiones` | Eventos por tanda: `nada`, `pasos` (`paso` y `robot`), `decisiones` (más `decide`) o `todo` |
64
64
  | `batchSize` | 50 000 | Operaciones por tanda |
65
+ | `parseTrace` | `false` | `cargado` trae `parseTrace`: la [traza del parser](./arquitectura.md#traza-del-parser) |
65
66
 
66
67
  ## Tandas
67
68
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codexivo",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
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",
package/src/index.ts CHANGED
@@ -6,8 +6,8 @@ export type { ErrorInfo, ErrorKind } from './errors';
6
6
 
7
7
  export { tokenize } from './syntax/lexer';
8
8
  export type { LexResult } from './syntax/lexer';
9
- export { parse } from './syntax/parser';
10
- export type { ParseResult } from './syntax/parser';
9
+ export { parse, traceParse } from './syntax/parser';
10
+ export type { ParseResult, ParseOptions, ParseEvent, PrecedenceOperator } from './syntax/parser';
11
11
  export { TokenType, KEYWORDS, isKeyword } from './syntax/token';
12
12
  export type { Token, Span, Position } from './syntax/token';
13
13
  export { childrenOf, walk } from './syntax/ast';
@@ -5,6 +5,7 @@ import { Machine, serialize } from './runtime/machine';
5
5
  import type { MachineEvent, MachineOptions, MachineStatus, SerializedValue, Snapshot } from './runtime/machine';
6
6
  import { compile } from './runtime/compiler';
7
7
  import { parse } from './syntax/parser';
8
+ import type { ParseEvent } from './syntax/parser';
8
9
  import type * as ast from './syntax/ast';
9
10
  import type { Token } from './syntax/token';
10
11
  import { createWorld } from './world';
@@ -39,6 +40,8 @@ export interface SandboxOptions {
39
40
  detail?: EventDetail;
40
41
  /** Operaciones por tanda antes de ceder el control. Por defecto 50 000. */
41
42
  batchSize?: number;
43
+ /** Si `cargado` trae `parseTrace`: cómo construyó el parser el árbol, paso a paso. Por defecto `false`. */
44
+ parseTrace?: boolean;
42
45
  }
43
46
 
44
47
  export type SandboxRequest =
@@ -67,6 +70,8 @@ export interface Loaded {
67
70
  errors: ErrorInfo[];
68
71
  /** Semilla de `aleatorio` de esta carga. */
69
72
  seed: number;
73
+ /** La traza del parser (ver `parse(código, { trace: true })`), solo con la opción `parseTrace`. */
74
+ parseTrace?: ParseEvent[];
70
75
  }
71
76
 
72
77
  export interface Progress {
@@ -193,10 +198,18 @@ export class SandboxHost {
193
198
  this.inputClosed = false;
194
199
  this.events = [];
195
200
  this.sentOutput = 0;
196
- const { program, tokens, errors } = parse(source);
201
+ const { program, tokens, errors, trace } = parse(source, { trace: options.parseTrace ?? false });
197
202
  this.program = errors.length === 0 ? program : undefined;
198
203
  this.machine = this.program ? this.createMachine() : undefined;
199
- this.post({ type: 'cargado', id, tokens, program, errors: errors.map(error => error.toJSON()), seed: this.seed });
204
+ this.post({
205
+ type: 'cargado',
206
+ id,
207
+ tokens,
208
+ program,
209
+ errors: errors.map(error => error.toJSON()),
210
+ seed: this.seed,
211
+ ...(trace ? { parseTrace: trace } : {}),
212
+ });
200
213
  }
201
214
 
202
215
  private createMachine(): Machine {