codexivo 0.0.0-stage → 0.2.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,91 @@
1
+ # Guía práctica: entender Codexivo desde su código
2
+
3
+ Actividades para mirar debajo del capó: cada una toma el mismo programa y lo observa en una etapa distinta del intérprete. Lee
4
+ antes la [arquitectura](./arquitectura.md).
5
+
6
+ ## Programa semilla
7
+
8
+ ```codexivo
9
+ variable saludo = "Codexivo";
10
+ variable arma_mensaje = procedimiento(nombre) {
11
+ variable prefijo = "Hola, ";
12
+ regresa prefijo + nombre;
13
+ };
14
+ arma_mensaje(saludo);
15
+ ```
16
+
17
+ Guárdalo en `semilla.cdx` y ejecútalo con `bun run src/cli.ts semilla.cdx`, o pégalo en el REPL.
18
+
19
+ ## 1. Tokens
20
+
21
+ ```ts
22
+ import { readFileSync } from 'node:fs';
23
+ import { tokenize } from './src';
24
+
25
+ const { tokens } = tokenize(readFileSync('semilla.cdx', 'utf8'));
26
+ console.table(
27
+ tokens.map(t => ({ tipo: t.type, texto: t.lexeme, línea: t.span.start.line, columna: t.span.start.column })),
28
+ );
29
+ ```
30
+
31
+ - Ubica en `src/syntax/lexer.ts` dónde se reconocen los nombres y cómo se decide si son palabras reservadas (`KEYWORDS` en
32
+ `src/syntax/token.ts`).
33
+ - **Ejercicio:** agrega `#` al programa y observa el error léxico y su posición.
34
+
35
+ ## 2. Árbol
36
+
37
+ ```ts
38
+ import { parse, walk } from './src';
39
+
40
+ const { program } = parse(codigo);
41
+ walk(program, (nodo, padre) => console.log(`${padre?.kind ?? '—'} → ${nodo.kind}`));
42
+ ```
43
+
44
+ - En `src/syntax/parser.ts`, busca la tabla `INFIX_PRECEDENCE` y explica por qué `1 + 2 * 3` se agrupa como `1 + (2 * 3)`.
45
+ - **Ejercicio:** escribe `1 < x < 5` y lee el error. ¿Por qué el lenguaje prefiere no encadenar comparaciones?
46
+
47
+ ## 3. Instrucciones
48
+
49
+ ```ts
50
+ import { compile, parse } from './src';
51
+
52
+ const chunk = compile(parse(codigo).program);
53
+ console.log(chunk.code.map(i => i.op).join(' '));
54
+ ```
55
+
56
+ - Encuentra la instrucción `closure`: guarda el código del procedimiento junto con el alcance donde se creó.
57
+ - **Ejercicio:** compila `si (a) { } si_no { }` y dibuja a dónde salta cada `branch` y `jump`.
58
+
59
+ ## 4. Ejecución paso a paso
60
+
61
+ ```ts
62
+ import { prepare } from './src';
63
+
64
+ const { machine } = prepare(codigo);
65
+ while (machine && !machine.done) {
66
+ machine.step();
67
+ const foto = machine.snapshot();
68
+ console.log(
69
+ foto.span?.start.line,
70
+ foto.frames.map(f => f.name).join(' > '),
71
+ JSON.stringify(foto.frames.at(-1)?.scopes),
72
+ );
73
+ }
74
+ ```
75
+
76
+ - Observa cómo aparece el marco `arma_mensaje` mientras se ejecuta el procedimiento, con `nombre` y `prefijo`.
77
+ - **Ejercicio:** cambia el programa para que `arma_mensaje` se llame a sí mismo sin caso base. ¿Qué error aparece y por qué no
78
+ truena JavaScript?
79
+
80
+ ## 5. La biblioteca
81
+
82
+ - Lee cómo está definido `longitud` en `src/runtime/builtins.ts`: declara cuántos argumentos acepta y valida sus tipos.
83
+ - **Ejercicio:** agrega `mayúsculas(texto)` con su test en `tests/builtins.test.ts`. Comprueba que `mayusculas` también
84
+ funcione sin acento.
85
+
86
+ ## Autoevaluación
87
+
88
+ - [ ] Puedo explicar qué hace cada etapa: tokens, árbol, instrucciones, máquina.
89
+ - [ ] Sé dónde se decide la precedencia de un operador.
90
+ - [ ] Puedo leer una fotografía de la máquina e identificar en qué paso cambió una variable.
91
+ - [ ] Sé agregar un procedimiento a la biblioteca y probarlo.
@@ -0,0 +1,162 @@
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. El punto y coma al final de cada sentencia es opcional si la siguiente empieza en otra
9
+ línea. El valor de un programa es el de su última expresión, o lo que regrese con `regresa`.
10
+
11
+ El mismo lenguaje funciona mediante la biblioteca ESM `codexivo`, el comando npm `codexivo` (sin instalar Bun) y los ejecutables
12
+ autónomos de cada sistema. Consulta [REPL y terminal](./repl.md) para ejecutar archivos y proporcionar entrada estándar.
13
+
14
+ ```codexivo
15
+ // comentario de una línea
16
+ /* comentario
17
+ de varias líneas */
18
+ variable nombre = "Ana"
19
+ imprimir("Hola,", nombre)
20
+ ```
21
+
22
+ ## Palabras reservadas
23
+
24
+ ```
25
+ variable procedimiento regresa
26
+ si pero_si si_no mientras hacer hasta_que para
27
+ verdadero falso nulo y o no
28
+ ```
29
+
30
+ ## Valores
31
+
32
+ | Tipo | Ejemplos |
33
+ | ------------- | ----------------------------------------------------------------------------- |
34
+ | número | `10`, `3.14`, `.5`, `-2` |
35
+ | texto | `"hola"`, `'mundo'`, `"línea\nnueva"` (escapes: `\n`, `\t`, `\\`, `\"`, `\'`) |
36
+ | lógico | `verdadero`, `falso` |
37
+ | nulo | `nulo` |
38
+ | lista | `[1, "a", verdadero]` |
39
+ | procedimiento | `procedimiento(x) { regresa x * 2; }` |
40
+
41
+ Las listas se comparten: si dos variables apuntan a la misma lista, un cambio se ve desde las dos.
42
+
43
+ ## Variables
44
+
45
+ ```codexivo
46
+ variable contador = 0;
47
+ contador = contador + 1;
48
+ contador += 5; // también -=, *=, /=
49
+ ```
50
+
51
+ - Toda variable se declara una vez con `variable` y un valor inicial.
52
+ - Asignar a una variable sin declararla es un error.
53
+ - Lo que se declara dentro de un bloque `{ }` solo existe dentro de él.
54
+
55
+ Los nombres pueden llevar `ñ`, acentos y diéresis: `año`, `canción`, `pingüino`.
56
+
57
+ ## Operadores
58
+
59
+ De menor a mayor precedencia:
60
+
61
+ | Operadores | Notas |
62
+ | ----------------------------------- | ------------------------------------------------------------------------ |
63
+ | `o` | Cortocircuito |
64
+ | `y` | Cortocircuito |
65
+ | `no`, `!` | |
66
+ | `==` `!=` `<` `>` `<=` `>=` | No se pueden encadenar: `1 < x < 5` es un error; escribe `1 < x y x < 5` |
67
+ | `+` `-` | `+` también une dos textos |
68
+ | `*` `/` | `/` es división real; dividir entre cero es un error |
69
+ | `-` (signo) | |
70
+ | llamadas `f(x)`, índices `lista[i]` | |
71
+
72
+ - `==` compara por valor, también listas: `[1, 2] == [1, 2]` es `verdadero`.
73
+ - `+` no mezcla textos y números: `"Total: " + 5` es un error. Usa `texto(5)` o `imprimir("Total:", 5)`.
74
+ - `y`, `o`, `no` y las condiciones solo aceptan `verdadero` o `falso`. `si (1)` es un error con una pista.
75
+
76
+ ## Condiciones y bucles
77
+
78
+ ```codexivo
79
+ variable edad = 15;
80
+ si (edad >= 18) {
81
+ imprimir("adulto");
82
+ } pero_si (edad >= 13) {
83
+ imprimir("adolescente");
84
+ } si_no {
85
+ imprimir("niño");
86
+ }
87
+
88
+ variable i = 0;
89
+ mientras (i < 3) {
90
+ i += 1;
91
+ }
92
+
93
+ hacer {
94
+ i -= 1;
95
+ } hasta_que (i == 0);
96
+
97
+ para (variable j = 0; j < 3; j += 1) {
98
+ imprimir(j);
99
+ }
100
+ ```
101
+
102
+ - `hacer … hasta_que` ejecuta el bloque al menos una vez y repite **hasta que** la condición sea verdadera.
103
+ - La variable de un `para` solo existe dentro del bucle.
104
+
105
+ ## Procedimientos
106
+
107
+ ```codexivo
108
+ variable saludar = procedimiento(nombre) {
109
+ regresa "Hola, " + nombre;
110
+ };
111
+ saludar("Uriel");
112
+ ```
113
+
114
+ - Se crean con `procedimiento(parámetros) { … }` y se guardan en variables.
115
+ - Hay que llamarlos con exactamente tantos valores como parámetros.
116
+ - Sin `regresa`, un procedimiento regresa `nulo`.
117
+ - Recuerdan las variables de donde se crearon (closures) y pueden llamarse a sí mismos.
118
+
119
+ Los procedimientos con nombre (`procedimiento saludar(nombre) { … }`) llegarán con la v1.
120
+
121
+ ## Listas y textos
122
+
123
+ ```codexivo
124
+ variable números = [10, 20, 30];
125
+ números[0]; // 10
126
+ números[-1]; // 30, desde el final
127
+ "hola"[1]; // "o"
128
+ ```
129
+
130
+ Los índices son enteros desde 0; un índice fuera de rango es un error que dice cuántos elementos hay.
131
+
132
+ ## Biblioteca
133
+
134
+ Los nombres con acento también se aceptan sin él (`máximo` o `maximo`).
135
+
136
+ | Procedimiento | Qué hace |
137
+ | --------------------------------- | -------------------------------------------------------------------------- |
138
+ | `imprimir(a, b, …)` | Escribe los valores separados por un espacio y termina la línea |
139
+ | `leer()` | Siguiente línea de entrada como texto, o `nulo` si ya no hay |
140
+ | `leer_número()` | Siguiente línea de entrada como número |
141
+ | `tipo(x)` | `"número"`, `"texto"`, `"lógico"`, `"nulo"`, `"lista"` o `"procedimiento"` |
142
+ | `texto(x)`, `número(x)` | Conversiones explícitas |
143
+ | `longitud(x)` | Caracteres de un texto o elementos de una lista |
144
+ | `absoluto(x)` | Valor absoluto |
145
+ | `máximo(…)`, `mínimo(…)` | De varios números o de una lista |
146
+ | `redondear(x, decimales?)` | Redondea; los `.5` se alejan del cero |
147
+ | `agregar(lista, x)` | Agrega al final (cambia la lista) |
148
+ | `primero(lista)`, `último(lista)` | Primer y último elemento |
149
+
150
+ ## Errores
151
+
152
+ Cada error dice de qué tipo es (léxico, sintaxis, ejecución o límite), qué pasó, dónde y, casi siempre, una pista:
153
+
154
+ ```
155
+ Error de ejecución en la línea 2, columna 5: No se puede dividir entre cero.
156
+ Pista: Revisa que el divisor no valga 0 antes de dividir.
157
+ ```
158
+
159
+ ## Límites
160
+
161
+ 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
162
+ operaciones, 10 000 llamadas anidadas o 1 000 000 de caracteres impresos. Los tres se pueden configurar.
@@ -0,0 +1,159 @@
1
+ # Recetario de programas Codexivo
2
+
3
+ Esta colección de programas ilustra patrones útiles del lenguaje. Cada ejemplo está listo para copiarse en el REPL o en un
4
+ archivo `.codexivo`.
5
+
6
+ ## 1. Hola mundo personalizado
7
+
8
+ ```codexivo
9
+ variable saludar = procedimiento(nombre) {
10
+ regresa "Hola " + nombre + "!";
11
+ };
12
+
13
+ saludar("Codexivo");
14
+ ```
15
+
16
+ Demuestra cómo declarar un procedimiento, concatenar cadenas y retornar un valor.
17
+
18
+ ## 2. Clasificador por edades
19
+
20
+ ```codexivo
21
+ variable clasificar = procedimiento(edad) {
22
+ si (edad >= 18) {
23
+ regresa "adulto";
24
+ } pero_si (edad >= 13) {
25
+ regresa "adolescente";
26
+ } si_no {
27
+ regresa "niño";
28
+ }
29
+ };
30
+
31
+ clasificar(16);
32
+ ```
33
+
34
+ Ilustra el encadenamiento `si`/`pero_si`/`si_no` y el uso de operadores relacionales.
35
+
36
+ ## 3. Promedio simple
37
+
38
+ ```codexivo
39
+ variable promedio = procedimiento(a, b, c) {
40
+ variable total = a + b + c;
41
+ regresa total / 3;
42
+ };
43
+
44
+ promedio(8, 7.5, 9);
45
+ ```
46
+
47
+ Combina números enteros y decimales con operaciones aritméticas.
48
+
49
+ ## 4. Valor absoluto
50
+
51
+ ```codexivo
52
+ variable valorAbsoluto = procedimiento(n) {
53
+ si (n < 0) {
54
+ regresa -n;
55
+ } si_no {
56
+ regresa n;
57
+ }
58
+ };
59
+
60
+ valorAbsoluto(-42);
61
+ ```
62
+
63
+ Ejemplo de uso del operador prefijo `-` y del comportamiento truthy/falsy en condicionales.
64
+
65
+ ## 5. Contador recursivo
66
+
67
+ ```codexivo
68
+ variable cuentaRegresiva = procedimiento(n) {
69
+ si (n == 0) {
70
+ regresa "¡Despegue!";
71
+ }
72
+
73
+ regresa cuentaRegresiva(n - 1);
74
+ };
75
+
76
+ cuentaRegresiva(3);
77
+ ```
78
+
79
+ Muestra cómo las funciones en Codexivo soportan recursión gracias a que conservan su entorno de definición.
80
+
81
+ ## 6. Validación de longitud
82
+
83
+ ```codexivo
84
+ variable esNombreValido = procedimiento(nombre) {
85
+ variable tamaño = longitud(nombre);
86
+ si (tamaño >= 3 y tamaño <= 15) {
87
+ regresa verdadero;
88
+ }
89
+ regresa falso;
90
+ };
91
+
92
+ esNombreValido("Ana");
93
+ ```
94
+
95
+ Ejemplo de uso del builtin `longitud` junto con operadores de comparación y booleanos.
96
+
97
+ ## 7. Suma con bucle for
98
+
99
+ ```codexivo
100
+ variable sumarHasta = procedimiento(n) {
101
+ variable suma = 0;
102
+ para (variable i = 1; i <= n; i += 1) {
103
+ suma += i;
104
+ }
105
+ regresa suma;
106
+ };
107
+
108
+ sumarHasta(10); // Suma de 1 a 10 = 55
109
+ ```
110
+
111
+ Demuestra el uso de bucles `para` con operadores de asignación compuesta.
112
+
113
+ ## 8. Búsqueda en arreglo
114
+
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;
120
+ }
121
+ }
122
+ regresa -1;
123
+ };
124
+
125
+ variable numeros = [10, 20, 30, 40];
126
+ buscar(numeros, 30); // Regresa 2
127
+ ```
128
+
129
+ Combina bucles, acceso a arreglos, condicionales y funciones built-in.
130
+
131
+ ## 9. Trabajo con funciones matemáticas
132
+
133
+ ```codexivo
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];
143
+ }
144
+ si (datos[i] < menor) {
145
+ menor = datos[i];
146
+ }
147
+ }
148
+ variable promedio = suma / longitud(datos);
149
+
150
+ regresa [redondear(promedio), mayor, menor];
151
+ };
152
+
153
+ estadisticas([5, 2, 8, 1, 9]); // [5, 9, 1]
154
+ ```
155
+
156
+ Muestra el uso de las funciones matemáticas built-in y manipulación de arreglos.
157
+
158
+ Todos los ejemplos de este recetario se ejecutan con la versión actual del intérprete. Consulta [qué funciona hoy](./lenguaje.md)
159
+ y [hacia dónde va el lenguaje](./especificacion-v1.md).
package/docs/repl.md ADDED
@@ -0,0 +1,64 @@
1
+ # REPL y ejecución desde la terminal
2
+
3
+ Instala el principal con `npm install -g codexivo` o `bun add -g --trust codexivo`. El gestor selecciona el paquete nativo
4
+ compatible mediante `os`, `cpu` y `libc`; `postinstall` verifica la versión y coloca el ejecutable en la ruta `bin` del
5
+ principal. Una vez instalado, `codexivo` abre el REPL y `codexivo programa.cdx` ejecuta archivos sin necesitar Node.js ni Bun.
6
+
7
+ Si omites scripts, la biblioteca sigue disponible; para preparar el CLI tras una instalación local, ejecuta
8
+ `node node_modules/codexivo/postinstall.mjs` una vez. Los ejecutables de
9
+ [Releases](https://github.com/UrielCuriel/codexivo/releases) funcionan directamente sin gestores ni runtimes externos.
10
+ En Linux/macOS, aplica `chmod +x` al ejecutable descargado. Se admiten x64 y ARM64 en Linux, macOS y Windows; para Alpine
11
+ Linux elige la variante `musl`.
12
+
13
+ ## REPL
14
+
15
+ ```sh
16
+ bun install
17
+ bun run repl
18
+ ```
19
+
20
+ Escribe código y presiona Enter. Las variables se conservan entre entradas. Si una línea deja un bloque `{` abierto, el REPL
21
+ sigue leyendo (con el prompt `..`) hasta que lo cierres. Para salir escribe `salir`.
22
+
23
+ ```
24
+ >> variable saludar = procedimiento(nombre) { regresa "Hola, " + nombre; };
25
+ >> saludar("Codexivo")
26
+ "Hola, Codexivo"
27
+ >> si (verdadero) {
28
+ .. imprimir("dentro del bloque")
29
+ .. }
30
+ dentro del bloque
31
+ >> salir
32
+ ¡Adiós!
33
+ ```
34
+
35
+ En el REPL se puede volver a declarar una variable (`variable x = …` dos veces) para experimentar; en un programa normal es un
36
+ error.
37
+
38
+ ## Ejecutar un archivo
39
+
40
+ ```sh
41
+ bun run src/cli.ts programa.cdx
42
+ ```
43
+
44
+ Lo que llegue por la entrada estándar alimenta a `leer()` y `leer_número()`:
45
+
46
+ ```sh
47
+ echo 21 | bun run src/cli.ts doble.cdx
48
+ ```
49
+
50
+ ## Ver la ejecución desde código
51
+
52
+ ```ts
53
+ import { prepare, run } from 'codexivo';
54
+
55
+ // Todos los eventos de una ejecución
56
+ const { events } = run('variable x = 1; x += 1;', { trace: true });
57
+
58
+ // Paso a paso
59
+ const { machine } = prepare('variable x = 1;\nx += 1;');
60
+ while (machine && !machine.done) {
61
+ machine.step();
62
+ console.log(machine.snapshot().span?.start.line, machine.snapshot().frames[0]?.scopes);
63
+ }
64
+ ```
package/package.json CHANGED
@@ -1,6 +1,77 @@
1
1
  {
2
2
  "name": "codexivo",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "description": "Lenguaje de programación en español para practicar lógica y algoritmos, que se ejecuta en el navegador.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.js",
8
+ "types": "src/index.ts",
9
+ "files": [
10
+ "bin/codexivo.exe",
11
+ "postinstall.mjs",
12
+ "dist/index.js",
13
+ "dist/index.js.map",
14
+ "src",
15
+ "!src/cli.ts",
16
+ "docs",
17
+ "CHANGELOG.md"
18
+ ],
19
+ "exports": {
20
+ ".": {
21
+ "types": "./src/index.ts",
22
+ "import": "./dist/index.js"
23
+ }
24
+ },
25
+ "scripts": {
26
+ "test": "bun test",
27
+ "check": "bun check",
28
+ "repl": "bun run src/cli.ts",
29
+ "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",
31
+ "build:binary": "bun run scripts/build-binaries.ts",
32
+ "build:binaries": "bun run scripts/build-binaries.ts --all",
33
+ "test:package": "bun run scripts/test-package.ts",
34
+ "prepack": "bun run build:lib",
35
+ "version": "bun run scripts/sync-native-versions.ts && git add package.json bun.lock packages/*/package.json",
36
+ "publish:native": "bun run scripts/publish-native.ts",
37
+ "format": "bun fmt",
38
+ "format:check": "bun fmt --check",
39
+ "postinstall": "node ./postinstall.mjs"
40
+ },
41
+ "keywords": [
42
+ "lenguaje de programación",
43
+ "español",
44
+ "educación",
45
+ "intérprete"
46
+ ],
47
+ "author": "Uriel Curiel",
48
+ "license": "ISC",
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "git+https://github.com/UrielCuriel/codexivo.git"
52
+ },
53
+ "devDependencies": {
54
+ "@types/bun": "^1.4.3"
55
+ },
56
+ "packageManager": "bun@1.4.4",
57
+ "sideEffects": false,
58
+ "publishConfig": {
59
+ "access": "public"
60
+ },
61
+ "workspaces": [
62
+ "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
+ }
77
+ }
@@ -0,0 +1,38 @@
1
+ import { chmodSync, copyFileSync, existsSync, linkSync, readFileSync, unlinkSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const directory = dirname(fileURLToPath(import.meta.url));
7
+ const require = createRequire(import.meta.url);
8
+
9
+ // Development checkouts build their executables after installing dependencies.
10
+ if (existsSync(join(directory, 'src/cli.ts')) && existsSync(join(directory, 'packages'))) process.exit(0);
11
+
12
+ const root = JSON.parse(readFileSync(join(directory, 'package.json'), 'utf8'));
13
+ const platform = process.platform === 'win32' ? 'windows' : process.platform;
14
+ const musl = platform === 'linux' && !process.report.getReport().header.glibcVersionRuntime;
15
+ const name = `codexivo-${platform}-${process.arch}${musl ? '-musl' : ''}`;
16
+
17
+ try {
18
+ if (!root.optionalDependencies[name]) throw new Error(`No hay un ejecutable para ${platform}/${process.arch}.`);
19
+ const manifest = require.resolve(`${name}/package.json`);
20
+ const native = JSON.parse(readFileSync(manifest, 'utf8'));
21
+ if (native.version !== root.version || native.version !== root.optionalDependencies[name]) {
22
+ throw new Error(`La versión de ${name} (${native.version}) debe ser ${root.version}.`);
23
+ }
24
+ const source = join(dirname(manifest), 'bin', platform === 'windows' ? 'codexivo.exe' : 'codexivo');
25
+ if (!existsSync(source)) throw new Error(`No se encontró el ejecutable de ${name}.`);
26
+ const destination = resolve(directory, root.bin.codexivo);
27
+ if (existsSync(destination)) unlinkSync(destination);
28
+ try {
29
+ linkSync(source, destination);
30
+ } catch {
31
+ copyFileSync(source, destination);
32
+ }
33
+ chmodSync(destination, 0o755);
34
+ } catch (error) {
35
+ console.error(`No se pudo instalar Codexivo: ${error.message}`);
36
+ console.error('Reinstala el paquete con las dependencias opcionales y los scripts de instalación habilitados.');
37
+ process.exit(1);
38
+ }
package/src/engine.ts ADDED
@@ -0,0 +1,97 @@
1
+ import type { CodexivoError, ErrorInfo } from './errors';
2
+ import type * as ast from './syntax/ast';
3
+ import { parse } from './syntax/parser';
4
+ import type { Token } from './syntax/token';
5
+ import { compile } from './runtime/compiler';
6
+ import { Machine } from './runtime/machine';
7
+ import type { MachineEvent, MachineOptions, MachineStatus } from './runtime/machine';
8
+ import { repr, Scope } from './runtime/values';
9
+ import type { Value } from './runtime/values';
10
+
11
+ export interface PreparedProgram {
12
+ program: ast.Program;
13
+ tokens: Token[];
14
+ /** Errores léxicos y sintácticos. Si hay alguno, `machine` no existe. */
15
+ errors: CodexivoError[];
16
+ machine?: Machine;
17
+ }
18
+
19
+ /** Analiza el código y, si no tiene errores, prepara una máquina lista para ejecutarlo paso a paso. */
20
+ export const prepare = (source: string, options: MachineOptions = {}): PreparedProgram => {
21
+ const { program, tokens, errors } = parse(source);
22
+ if (errors.length > 0) return { program, tokens, errors };
23
+ return { program, tokens, errors, machine: new Machine(compile(program), options) };
24
+ };
25
+
26
+ export interface RunOptions extends Omit<MachineOptions, 'onEvent'> {
27
+ /** Guarda los eventos de la ejecución en `events`. */
28
+ trace?: boolean;
29
+ /** Máximo de eventos a guardar con `trace`. Por defecto 100 000. */
30
+ maxEvents?: number;
31
+ }
32
+
33
+ export interface RunResult {
34
+ status: MachineStatus;
35
+ /** `true` si el programa terminó sin errores. */
36
+ ok: boolean;
37
+ /** Valor de la última expresión del programa, o lo que regresó con `regresa`. */
38
+ result: Value;
39
+ /** `result` como se escribiría en código. */
40
+ resultText: string;
41
+ output: string;
42
+ errors: ErrorInfo[];
43
+ operations: number;
44
+ events?: MachineEvent[];
45
+ /** `true` si se guardaron menos eventos de los que ocurrieron. */
46
+ eventsTruncated?: boolean;
47
+ }
48
+
49
+ /** Ejecuta un programa completo. */
50
+ export const run = (source: string, options: RunOptions = {}): RunResult => {
51
+ const { trace, maxEvents = 100_000, ...machineOptions } = options;
52
+ const events: MachineEvent[] = [];
53
+ let eventsTruncated = false;
54
+ const onEvent = trace
55
+ ? (event: MachineEvent) => {
56
+ if (events.length < maxEvents) events.push(event);
57
+ else eventsTruncated = true;
58
+ }
59
+ : undefined;
60
+
61
+ const { errors, machine } = prepare(source, { ...machineOptions, onEvent });
62
+ if (!machine) {
63
+ return {
64
+ status: 'error',
65
+ ok: false,
66
+ result: null,
67
+ resultText: 'nulo',
68
+ output: '',
69
+ errors: errors.map(error => error.toJSON()),
70
+ operations: 0,
71
+ ...(trace ? { events, eventsTruncated } : {}),
72
+ };
73
+ }
74
+
75
+ machine.run();
76
+ return {
77
+ status: machine.status,
78
+ ok: machine.status === 'terminada',
79
+ result: machine.result,
80
+ resultText: repr(machine.result),
81
+ output: machine.output,
82
+ errors: machine.error ? [machine.error.toJSON()] : [],
83
+ operations: machine.operations,
84
+ ...(trace ? { events, eventsTruncated } : {}),
85
+ };
86
+ };
87
+
88
+ /** Varias ejecuciones que comparten variables, como en el REPL. */
89
+ export class Session {
90
+ private readonly globals = new Scope();
91
+
92
+ constructor(private readonly options: Omit<RunOptions, 'globals' | 'allowRedeclare'> = {}) {}
93
+
94
+ run(source: string): RunResult {
95
+ return run(source, { ...this.options, globals: this.globals, allowRedeclare: true });
96
+ }
97
+ }