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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Historial de cambios
2
+
3
+ ## 0.2.0
4
+
5
+ - Núcleo reescrito como lexer, parser, compilador y máquina reanudable (#23).
6
+ - Ejecución paso a paso, eventos, snapshots, límites deterministas y entrada interactiva.
7
+ - API de biblioteca para navegador y Bun, con tipos publicados como fuentes TypeScript.
8
+ - Comando `codexivo` instalable desde npm, que ejecuta el binario nativo del sistema sin depender de Node.js ni Bun durante la ejecución.
9
+ - Ejecutables autónomos para Linux (glibc y musl), macOS y Windows, en x64 y ARM64.
10
+ - Publicación desde tags `v*` mediante GitHub Actions, con OIDC, provenance y artefactos en GitHub Releases.
11
+ - Formato integrado con `bun fmt` y configuración en `bunfig.toml`.
12
+ - Incremento automático `patch` con `bun pm version` al integrar cambios en `main`, y publicación manual `patch`/`minor`/`major`.
13
+
14
+ Esta versión reemplaza el núcleo anterior. La sintaxis implementada se describe en
15
+ [`docs/lenguaje.md`](./docs/lenguaje.md).
package/README.md CHANGED
@@ -1,3 +1,159 @@
1
- # Temporary Holding Version
1
+ # Codexivo
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Bienvenido al proyecto de nuestro nuevo y emocionante lenguaje de programación: Codexivo. Durante las últimas semanas, he estado inmerso en esta apasionante tarea de crear una herramienta que facilite el aprendizaje de la programación. En un principio, mi objetivo era desarrollar un intérprete de pseudocódigo con una visualización de diagrama de flujo integrada, pero a medida que investigaba, surgió una idea aún más emocionante: crear un "lenguaje de programación" similar al que solía utilizar en mi infancia con el emulador de robot llamado Karel.
4
+
5
+ Inspirado por esta idea, me embarqué en este proyecto con la determinación de crear una plataforma que permita a los principiantes aprender sobre lógica, algoritmos básicos, bucles y mucho más. Para lograrlo, definí una serie de características clave que el lenguaje debía poseer:
6
+
7
+ 1. **Simplicidad**: Con el aprendizaje en mente, decidí que nuestro lenguaje solo incluirá tipos de datos simples como números, cadenas y arreglos, y posiblemente diccionarios en el futuro.
8
+
9
+ 2. **Verbalidad**: Para hacer el código más comprensible y legible, he optado por utilizar más palabras que símbolos en la sintaxis del lenguaje.
10
+
11
+ 3. **En español**: Creo firmemente que el aprendizaje se facilita cuando se utiliza el idioma nativo, así que he elegido el español como el idioma principal de nuestro lenguaje. Incluso estoy considerando permitir el uso de la "ñ" y acentos dentro del código.
12
+
13
+ 4. **Fácil acceso**: Quiero que nuestro lenguaje sea fácil de usar para todos, por lo que he diseñado una solución que se puede ejecutar directamente en un navegador, sin necesidad de instalaciones adicionales ni acceso a internet.
14
+
15
+ ## Un ejemplo
16
+
17
+ ```codexivo
18
+ // La suma de los números del 1 al 10
19
+ variable suma = 0;
20
+ para (variable i = 1; i <= 10; i += 1) {
21
+ suma += i;
22
+ }
23
+ imprimir("La suma es", suma);
24
+ ```
25
+
26
+ ```codexivo
27
+ variable clasificar = procedimiento(edad) {
28
+ si (edad >= 18) {
29
+ regresa "adulto";
30
+ } pero_si (edad >= 13) {
31
+ regresa "adolescente";
32
+ } si_no {
33
+ regresa "niño";
34
+ }
35
+ };
36
+ imprimir(clasificar(16));
37
+ ```
38
+
39
+ Lo que funciona hoy está en [`docs/lenguaje.md`](./docs/lenguaje.md), y todos sus ejemplos se ejecutan tal cual. Hacia dónde va
40
+ el lenguaje, nivel por nivel, está en la [especificación v1](./docs/especificacion-v1.md).
41
+
42
+ ## Cómo usarlo
43
+
44
+ Para desarrollar o ejecutar el código fuente necesitas [Bun](https://bun.com) 1.4.4 o más reciente.
45
+
46
+ ```sh
47
+ bun install
48
+ bun run repl # REPL interactivo
49
+ bun run src/cli.ts programa.cdx # ejecutar un archivo; la entrada estándar alimenta a leer()
50
+ ```
51
+
52
+ Como biblioteca, en el navegador o en Bun (una vez publicada la versión):
53
+
54
+ ```sh
55
+ npm install codexivo@0.2.0
56
+ ```
57
+
58
+ ```ts
59
+ import { run } from 'codexivo';
60
+
61
+ const resultado = run('imprimir("Hola desde Codexivo");');
62
+ console.log(resultado.output); // "Hola desde Codexivo\n"
63
+ ```
64
+
65
+ El paquete incluye un bundle ESM compatible con navegador y los tipos como fuentes TypeScript. Importar la biblioteca no
66
+ inicia el REPL ni necesita Bun. El principal declara `bin` y las variantes nativas en `optionalDependencies`; cada variante
67
+ incluye los campos `os`, `cpu` y, en Linux, `libc`. El gestor instala la variante compatible y `postinstall` verifica su versión
68
+ y enlaza o copia el ejecutable a `bin/codexivo.exe` del principal. El nombre interno también funciona en Linux y macOS.
69
+
70
+ ```sh
71
+ npm install --global codexivo@0.2.0
72
+ codexivo # REPL
73
+ codexivo programa.cdx # ejecutar un archivo
74
+ ```
75
+
76
+ Con Bun, usa `bun add --global --trust codexivo@0.2.0` para permitir el script de instalación. Node se usa únicamente durante
77
+ la instalación con npm; el comando resultante es el ejecutable nativo y funciona sin Node.js ni Bun en el `PATH`.
78
+
79
+ Si bloqueas scripts, puedes usar la biblioteca normalmente. Para preparar el comando tras una instalación local, ejecuta
80
+ `node node_modules/codexivo/postinstall.mjs` una vez. El instalador toma el paquete opcional ya instalado por el gestor y exige
81
+ que su versión coincida con la del principal; no descarga ni compila binarios.
82
+
83
+ Las variantes publicadas son:
84
+
85
+ | Sistema | Arquitectura | libc | Paquete |
86
+ | -------------- | ------------ | ----- | --------------------------- |
87
+ | Linux | x64 | glibc | `codexivo-linux-x64` |
88
+ | Linux | ARM64 | glibc | `codexivo-linux-arm64` |
89
+ | Linux (Alpine) | x64 | musl | `codexivo-linux-x64-musl` |
90
+ | Linux (Alpine) | ARM64 | musl | `codexivo-linux-arm64-musl` |
91
+ | macOS | x64 | — | `codexivo-darwin-x64` |
92
+ | macOS | ARM64 | — | `codexivo-darwin-arm64` |
93
+ | Windows | x64 | — | `codexivo-windows-x64` |
94
+ | Windows | ARM64 | — | `codexivo-windows-arm64` |
95
+
96
+ También puedes descargar un ejecutable autónomo desde [Releases](https://github.com/UrielCuriel/codexivo/releases), sin
97
+ instalar Node.js ni Bun. Hay versiones x64 y ARM64 para Linux, macOS y Windows; Linux ofrece glibc y musl (Alpine).
98
+ En Linux y macOS, dale permiso de ejecución con `chmod +x codexivo-*` antes de usarlo.
99
+
100
+ ```sh
101
+ ./codexivo-linux-x64 programa.cdx
102
+ ```
103
+
104
+ En Windows, por ejemplo: `.\codexivo-windows-x64.exe programa.cdx`.
105
+
106
+ El intérprete compila el programa a instrucciones para una máquina propia que se puede pausar, ejecutar paso a paso y que
107
+ reporta cada decisión, asignación y llamada. Eso es lo que permite visualizar cómo piensa un programa. Los detalles están en la
108
+ [arquitectura](./docs/arquitectura.md).
109
+
110
+ ## Scripts
111
+
112
+ - `bun test` — pruebas.
113
+ - `bun check` — revisión de tipos.
114
+ - `bun fmt` / `bun fmt --check` — aplicar o comprobar formato; configuración en `bunfig.toml`.
115
+ - `bun run repl` — REPL.
116
+ - `bun run build` — biblioteca ESM y ejecutable autónomo para el sistema actual.
117
+ - `bun run build:lib` — construir solamente la biblioteca para navegador.
118
+ - `bun run build:binary` — ejecutable autónomo para el sistema actual en `dist/binaries/`.
119
+ - `bun run build:binary bun-windows-x64` — compilación cruzada para un destino específico.
120
+ - `bun run build:binaries` — los ocho destinos de Linux/macOS/Windows, x64/ARM64.
121
+ - `bun run test:package` — instalar los tarballs reales con npm, compilar con Vite y probar el comando nativo sin Node.js ni Bun en el `PATH`.
122
+
123
+ Al integrar cambios en `main` (por merge o push), el workflow incrementa automáticamente la versión con `bun pm version patch`,
124
+ crea el commit y el tag y continúa con las validaciones y la publicación en la misma ejecución. El commit automático no
125
+ dispara otra publicación. El workflow toma el estado más reciente de `main`, por lo que merges muy próximos pueden agruparse.
126
+ Desde **Actions → Publicar → Run workflow**, en `main`, se puede elegir `patch`, `minor` o `major` para una publicación manual.
127
+
128
+ También se admiten tags `v*` creados manualmente: deben coincidir con `package.json` y no incrementan la versión otra vez.
129
+ Todas las compilaciones usan el SHA del commit de la versión. Los paquetes nativos se publican antes que el paquete
130
+ principal y sus versiones se sincronizan con `bun pm version`. El principal declara `bin` y su `postinstall` coloca allí el ejecutable del paquete nativo compatible. Las versiones preliminares se publican con el dist-tag `next`;
131
+ las estables, con `latest`. Los ejecutables y sus sumas SHA-256 se adjuntan a la release. Los binarios no están firmados ni
132
+ notarizados.
133
+
134
+ ## Qué sigue
135
+
136
+ - [x] Lexer, parser y máquina con posiciones exactas, errores con pistas y límites deterministas
137
+ - [x] Ejecución paso a paso, eventos y fotografías del estado para visualización
138
+ - [x] `imprimir`, `leer`, `leer_número`, comentarios, `nulo`
139
+ - [ ] Nivel principiante de la v1: `repetir … veces`, `++`/`--`, `mod`/`div`, operadores en palabras, `constante`
140
+ - [ ] Nivel intermedio: procedimientos con nombre, `para_cada`, `romper`/`continuar`, diccionarios, más biblioteca
141
+ - [ ] Nivel avanzado: procedimientos como valores en la biblioteca, pilas y colas
142
+ - [ ] Modo mundo (Karel)
143
+ - [ ] Sitio con documentación y sandbox: `codexivo.urielcuriel.dev`
144
+
145
+ ## Cómo contribuir
146
+
147
+ - Reportando errores o problemas
148
+ - Sugiriendo nuevas características
149
+ - Contribuyendo con código (lee [`AGENTS.md`](./AGENTS.md))
150
+ - Mejorando la documentación
151
+ - Compartiendo el proyecto con otras personas
152
+
153
+ ## ¿Quién está detrás de este proyecto?
154
+
155
+ Mi nombre es [Uriel Curiel](https://twitter.com/UrielCuriel), soy un desarrollador de software con más de 8 años de
156
+ experiencia en el desarrollo de aplicaciones web y móviles. Me apasiona la tecnología y el aprendizaje, y me encanta compartir mis conocimientos con los demás. Puedes encontrarme en [Twitter](https://twitter.com/UrielCuriel) y [LinkedIn](https://www.linkedin.com/in/urielcuriel/).
157
+
158
+ Puedes seguir las actualizaciones de este proyecto en mi blog [urielcuriel.dev](https://urielcuriel.dev/blog), en la serie
159
+ «Construyendo Codexivo».
@@ -0,0 +1 @@
1
+ La instalación coloca aquí el ejecutable nativo de Codexivo. Habilita postinstall o ejecuta el instalador manualmente.
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ class v extends Error{kind;code;hint;span;callStack;constructor(e){super(e.message);this.name="CodexivoError",this.kind=e.kind,this.code=e.code,this.hint=e.hint,this.span=e.span,this.callStack=e.callStack}toJSON(){return{kind:this.kind,code:this.code,message:this.message,hint:this.hint,span:this.span,callStack:this.callStack}}}var ve=(e)=>{let t=e.span?` en la línea ${e.span.start.line}, columna ${e.span.start.column}`:"",r=[`Error de ${e.kind}${t}: ${e.message}`];if(e.hint)r.push(`Pista: ${e.hint}`);return r.join(`
2
+ `)};var P;((i)=>{i.NUMBER="NUMBER";i.STRING="STRING";i.IDENT="IDENT";i.LET="LET";i.FUNCTION="FUNCTION";i.RETURN="RETURN";i.IF="IF";i.ELSEIF="ELSEIF";i.ELSE="ELSE";i.WHILE="WHILE";i.DO="DO";i.UNTIL="UNTIL";i.FOR="FOR";i.TRUE="TRUE";i.FALSE="FALSE";i.NULL="NULL";i.AND="AND";i.OR="OR";i.NOT="NOT";i.ASSIGN="ASSIGN";i.PLUS_ASSIGN="PLUS_ASSIGN";i.MINUS_ASSIGN="MINUS_ASSIGN";i.STAR_ASSIGN="STAR_ASSIGN";i.SLASH_ASSIGN="SLASH_ASSIGN";i.PLUS="PLUS";i.MINUS="MINUS";i.STAR="STAR";i.SLASH="SLASH";i.BANG="BANG";i.EQ="EQ";i.NEQ="NEQ";i.LT="LT";i.GT="GT";i.LTE="LTE";i.GTE="GTE";i.COMMA="COMMA";i.SEMICOLON="SEMICOLON";i.LPAREN="LPAREN";i.RPAREN="RPAREN";i.LBRACE="LBRACE";i.RBRACE="RBRACE";i.LBRACKET="LBRACKET";i.RBRACKET="RBRACKET";i.EOF="EOF";i.ILLEGAL="ILLEGAL"})(P||={});var L={variable:"LET",procedimiento:"FUNCTION",regresa:"RETURN",si:"IF",pero_si:"ELSEIF",si_no:"ELSE",mientras:"WHILE",hacer:"DO",hasta_que:"UNTIL",para:"FOR",verdadero:"TRUE",falso:"FALSE",nulo:"NULL",y:"AND",o:"OR",no:"NOT"},Ne=(e)=>Object.hasOwn(L,e),ye={["ASSIGN"]:"=",["PLUS_ASSIGN"]:"+=",["MINUS_ASSIGN"]:"-=",["STAR_ASSIGN"]:"*=",["SLASH_ASSIGN"]:"/=",["PLUS"]:"+",["MINUS"]:"-",["STAR"]:"*",["SLASH"]:"/",["BANG"]:"!",["EQ"]:"==",["NEQ"]:"!=",["LT"]:"<",["GT"]:">",["LTE"]:"<=",["GTE"]:">=",["COMMA"]:",",["SEMICOLON"]:";",["LPAREN"]:"(",["RPAREN"]:")",["LBRACE"]:"{",["RBRACE"]:"}",["LBRACKET"]:"[",["RBRACKET"]:"]"},ke=Object.fromEntries(Object.entries(L).map(([e,t])=>[t,e])),G=(e)=>{let t=ye[e];if(t)return`\`${t}\``;let r=ke[e];if(r)return`la palabra \`${r}\``;switch(e){case"NUMBER":return"un número";case"STRING":return"un texto";case"IDENT":return"un nombre";case"EOF":return"el final del programa";default:return"un carácter no válido"}},b=(e)=>{switch(e.type){case"NUMBER":return`el número \`${e.lexeme}\``;case"STRING":return`el texto ${e.lexeme}`;case"IDENT":return`el nombre \`${e.lexeme}\``;case"ILLEGAL":return`el carácter \`${e.lexeme}\``;default:return G(e.type)}};var B=/[A-Za-z_À-ÖØ-öø-ÿ]/,I=/[0-9]/,be=/\s/,Ie={"==":"EQ","!=":"NEQ","<=":"LTE",">=":"GTE","+=":"PLUS_ASSIGN","-=":"MINUS_ASSIGN","*=":"STAR_ASSIGN","/=":"SLASH_ASSIGN"},Re={"=":"ASSIGN","+":"PLUS","-":"MINUS","*":"STAR","/":"SLASH","!":"BANG","<":"LT",">":"GT",",":"COMMA",";":"SEMICOLON","(":"LPAREN",")":"RPAREN","{":"LBRACE","}":"RBRACE","[":"LBRACKET","]":"RBRACKET"},Ae={n:`
3
+ `,t:"\t","\\":"\\",'"':'"',"'":"'"},Le={"&":"Para el «y» lógico escribe la palabra `y`.","|":"Para el «o» lógico escribe la palabra `o`.","“":'Parece una comilla tipográfica; usa comillas rectas `"`.',"”":'Parece una comilla tipográfica; usa comillas rectas `"`.',"‘":"Parece una comilla tipográfica; usa comillas rectas `'`.","’":"Parece una comilla tipográfica; usa comillas rectas `'`."},q=(e)=>{let t=[],r=[],s=0,n=1,a=1,p=()=>({line:n,column:a,offset:s}),o=(u=0)=>e[s+u]??"",c=()=>{let u=e[s]??"";if(s+=1,u===`
4
+ `)n+=1,a=1;else a+=1;return u},l=(u,h,R)=>{let N={type:u,lexeme:e.slice(h.offset,s),span:{start:h,end:p()}};if(R!==void 0)N.value=R;return t.push(N),N},S=(u,h,R,N)=>{r.push(new v({kind:"léxico",code:u,message:h,hint:N,span:R}))};while(s<e.length){let u=o();if(be.test(u)){c();continue}let h=p();if(u==="/"&&o(1)==="/"){while(s<e.length&&o()!==`
5
+ `)c();continue}if(u==="/"&&o(1)==="*"){c(),c();while(s<e.length&&!(o()==="*"&&o(1)==="/"))c();if(s>=e.length)S("COMENTARIO_SIN_CERRAR","El comentario que empieza aquí no se cerró.",{start:h,end:{line:h.line,column:h.column+2,offset:h.offset+2}},"Cierra el comentario con `*/`.");else c(),c();continue}if(I.test(u)||u==="."&&I.test(o(1))){while(I.test(o()))c();if(o()==="."&&I.test(o(1))){c();while(I.test(o()))c()}if(B.test(o())){while(B.test(o())||I.test(o()))c();let k=l("ILLEGAL",h);S("NOMBRE_EMPIEZA_CON_NÚMERO",`\`${k.lexeme}\` no es un número ni un nombre válido.`,k.span,"Los nombres no pueden empezar con un dígito. Si es una multiplicación, escribe el operador: `2 * x`.");continue}l("NUMBER",h,Number(e.slice(h.offset,s)));continue}if(B.test(u)){while(B.test(o())||I.test(o()))c();let k=e.slice(h.offset,s);l(L[k]??"IDENT",h);continue}if(u==='"'||u==="'"){let k=c(),_="",ee=!1;while(s<e.length&&o()!==`
6
+ `){let F=c();if(F===k){ee=!0;break}if(F==="\\"){let ge={line:n,column:a-1,offset:s-1},U=c(),te=Ae[U];if(te===void 0)S("ESCAPE_DESCONOCIDO",`\`\\${U}\` no es una secuencia de escape válida.`,{start:ge,end:p()},"Las secuencias válidas son `\\n`, `\\t`, `\\\\`, `\\\"` y `\\'`."),_+=U;else _+=te;continue}_+=F}let xe=l("STRING",h,_);if(!ee)S("TEXTO_SIN_CERRAR","El texto que empieza aquí no se cerró.",xe.span,`Agrega la comilla \`${k}\` al final. Un texto no puede ocupar varias líneas; usa \`\\n\` para un salto de línea.`);continue}let R=u+o(1),N=Ie[R];if(N){c(),c(),l(N,h);continue}let Z=Re[u];if(Z){c(),l(Z,h);continue}c();let Se=l("ILLEGAL",h);S("CARÁCTER_DESCONOCIDO",`Codexivo no reconoce el carácter \`${u}\`.`,Se.span,Le[u])}let J=p();return t.push({type:"EOF",lexeme:"",span:{start:J,end:J}}),{tokens:t,errors:r}};var W=(e)=>{let{tokens:t,errors:r}=q(e),s=new re(t),n=s.parseProgram(),a=[...r,...s.errors].sort((p,o)=>(p.span?.start.offset??0)-(o.span?.start.offset??0));return{program:n,tokens:t,errors:a}};var D={OR:1,AND:2,EQ:4,NEQ:4,LT:4,GT:4,LTE:4,GTE:4,PLUS:5,MINUS:5,STAR:6,SLASH:6,LPAREN:8,LBRACKET:8},Oe={PLUS:"+",MINUS:"-",STAR:"*",SLASH:"/",EQ:"==",NEQ:"!=",LT:"<",GT:">",LTE:"<=",GTE:">="},O={ASSIGN:"=",PLUS_ASSIGN:"+=",MINUS_ASSIGN:"-=",STAR_ASSIGN:"*=",SLASH_ASSIGN:"/="},Te=new Set(["LET","RETURN","IF","WHILE","DO","FOR"]),Ce={sino:"En Codexivo se escribe `si_no`, con guion bajo.",pero:"En Codexivo se escribe `pero_si`, con guion bajo.",hasta:"En Codexivo se escribe `hasta_que`, con guion bajo."},_e=50,Pe=400;class T{}class re{tokens;errors=[];position=0;nextId=1;depth=0;constructor(e){this.tokens=e}parseProgram(){let e=this.current.span.start,t=this.parseStatements("EOF");return{kind:"Program",id:this.id(),statements:t,span:{start:e,end:this.current.span.end}}}get current(){return this.tokens[this.position]??this.tokens[this.tokens.length-1]}get previous(){return this.tokens[this.position-1]??this.current}peek(e=1){return this.tokens[this.position+e]??this.tokens[this.tokens.length-1]}id(){return this.nextId++}advance(){let e=this.current;if(e.type!=="EOF")this.position+=1;return e}check(e){return this.current.type===e}match(e){if(!this.check(e))return!1;return this.advance(),!0}spanFrom(e){return{start:e,end:this.previous.span.end}}error(e,t,r,s){if(this.errors.length<_e)this.errors.push(new v({kind:"sintaxis",code:e,message:t,hint:s,span:r}));throw new T}failSilentlyOnIllegal(){if(this.check("ILLEGAL"))throw new T}expect(e,t,r){if(this.check(e))return this.advance();this.failSilentlyOnIllegal();let s=this.current,n=r;if(e==="RPAREN"&&s.type==="ASSIGN")n="Para comparar se usa `==`; un solo `=` sirve para asignar.";this.error("TOKEN_ESPERADO",`Se esperaba ${G(e)} ${t}, pero se encontró ${b(s)}.`,s.span,n)}enter(){if(this.depth+=1,this.depth>Pe)this.error("ANIDAMIENTO_EXCESIVO","El programa tiene demasiados niveles anidados.",this.current.span,"Divide la expresión en variables intermedias.")}leave(){this.depth-=1}synchronize(e){if(this.position===this.tokens.indexOf(e)&&!this.check("EOF"))this.advance();while(!this.check("EOF")){if(this.previous.type==="SEMICOLON")return;if(this.check("RBRACE"))return;if(Te.has(this.current.type))return;if(this.current.span.start.line>e.span.start.line)return;this.advance()}}parseStatements(e){let t=[],r=!1;while(!this.check(e)&&!this.check("EOF")){let s=this.current;try{if(e==="EOF"&&this.check("RBRACE")){if(r){this.advance();continue}this.error("LLAVE_SOBRANTE","Sobra esta `}`: no hay ningún bloque abierto.",this.current.span)}t.push(this.parseStatement()),r=!1}catch(n){if(!(n instanceof T))throw n;this.depth=0,r=!0,this.synchronize(s)}}return t}parseStatement(){let e=this.current;switch(e.type){case"SEMICOLON":return this.advance(),this.parseStatementAfterStraySemicolon();case"LET":return this.endSimpleStatement(this.parseVariableDeclaration());case"RETURN":return this.endSimpleStatement(this.parseReturn());case"IF":return this.endCompoundStatement(this.parseIf());case"WHILE":return this.endCompoundStatement(this.parseWhile());case"DO":return this.endSimpleStatement(this.parseDoUntil());case"FOR":return this.endCompoundStatement(this.parseFor());case"LBRACE":return this.endCompoundStatement(this.parseBlock("para abrir el bloque"));case"ELSE":case"ELSEIF":this.error("RAMA_SIN_SI",`${b(e)} tiene que ir justo después del \`}\` de un \`si\`.`,e.span);case"UNTIL":this.error("HASTA_QUE_SIN_HACER","`hasta_que` tiene que ir justo después del `}` de un `hacer`.",e.span);case"IDENT":{let t=Ce[e.lexeme],r=this.peek();if(t&&(r.type==="LBRACE"||r.type==="IF"||r.type==="LPAREN"))this.error("PALABRA_DESCONOCIDA",`\`${e.lexeme}\` no es una palabra de Codexivo.`,e.span,t);if(O[r.type])return this.endSimpleStatement(this.parseAssignment());return this.endSimpleStatement(this.parseExpressionStatement())}default:return this.endSimpleStatement(this.parseExpressionStatement())}}parseStatementAfterStraySemicolon(){if(this.check("EOF")||this.check("RBRACE")){let e=this.previous.span;return{kind:"Block",id:this.id(),statements:[],span:e}}return this.parseStatement()}endSimpleStatement(e){if(this.match("SEMICOLON"))return e;if(this.check("RBRACE")||this.check("EOF"))return e;if(this.current.span.start.line>this.previous.span.end.line)return e;this.failSilentlyOnIllegal(),this.error("FIN_DE_SENTENCIA",`Se esperaba \`;\` o un salto de línea antes de ${b(this.current)}.`,this.current.span,"Si querías combinar dos valores, falta un operador entre ellos.")}endCompoundStatement(e){return this.match("SEMICOLON"),e}parseVariableDeclaration(){let e=this.advance().span.start,t=this.parseBindingName("después de `variable`");this.expect("ASSIGN",`después de \`variable ${t.name}\``,`Toda variable empieza con un valor: \`variable ${t.name} = 0\`.`);let r=this.parseExpression();if(r.kind==="FunctionLiteral"&&!r.name)r.name=t.name;return{kind:"VariableDeclaration",id:this.id(),name:t,value:r,span:this.spanFrom(e)}}parseBindingName(e){let t=this.current;if(t.type!=="IDENT"){if(this.failSilentlyOnIllegal(),t.lexeme&&/^[a-z_]+$/.test(t.lexeme)&&t.type!=="EOF")this.error("PALABRA_RESERVADA",`\`${t.lexeme}\` es una palabra reservada y no se puede usar como nombre.`,t.span,"Elige otro nombre, por ejemplo agregando una palabra: `valor_inicial`.");this.error("NOMBRE_ESPERADO",`Se esperaba un nombre ${e}, pero se encontró ${b(t)}.`,t.span)}return this.advance(),{kind:"Identifier",id:this.id(),name:t.lexeme,span:t.span}}parseAssignment(){let e=this.advance(),t={kind:"Identifier",id:this.id(),name:e.lexeme,span:e.span},r=O[this.advance().type],s=this.parseExpression();if(s.kind==="FunctionLiteral"&&!s.name)s.name=t.name;return{kind:"Assignment",id:this.id(),target:t,operator:r,value:s,span:this.spanFrom(e.span.start)}}parseExpressionStatement(){let e=this.current.span.start,t=this.parseExpression();if(O[this.current.type])this.error("ASIGNACIÓN_INVÁLIDA","Solo se le puede asignar un valor a una variable.",this.current.span,t.kind==="Index"?"Cambiar un elemento de una lista con `lista[i] = valor` llegará en la siguiente versión.":void 0);return{kind:"ExpressionStatement",id:this.id(),expression:t,span:this.spanFrom(e)}}parseReturn(){let e=this.advance(),r=this.check("SEMICOLON")||this.check("RBRACE")||this.check("EOF")||this.current.span.start.line>e.span.end.line?void 0:this.parseExpression();return{kind:"Return",id:this.id(),value:r,span:this.spanFrom(e.span.start)}}parseBlock(e){let t=this.expect("LBRACE",e,"Los bloques de Codexivo siempre van entre llaves `{ }`."),r=this.parseStatements("RBRACE");if(!this.check("RBRACE"))this.error("BLOQUE_SIN_CERRAR",`Falta \`}\` para cerrar el bloque que empieza en la línea ${t.span.start.line}.`,t.span);return this.advance(),{kind:"Block",id:this.id(),statements:r,span:this.spanFrom(t.span.start)}}parseCondition(e){this.expect("LPAREN",`después de \`${e}\``,`La condición va entre paréntesis: \`${e} (x > 0)\`.`);let t=this.parseExpression();return this.expect("RPAREN",`para cerrar la condición de \`${e}\``),t}parseIf(){let e=this.current.span.start,t=[],r="si";do{let n=this.advance().span.start,a=this.parseCondition(r),p=this.parseBlock(`después de la condición de \`${r}\``);t.push({condition:a,body:p,span:this.spanFrom(n)}),r="pero_si"}while(this.check("ELSEIF"));let s;if(this.match("ELSE")){if(this.check("IF"))this.error("SI_NO_SI","Después de `si_no` va directamente un bloque.",this.current.span,"Para otra condición usa `pero_si (…)`.");s=this.parseBlock("después de `si_no`")}return{kind:"If",id:this.id(),branches:t,alternative:s,span:this.spanFrom(e)}}parseWhile(){let e=this.advance().span.start,t=this.parseCondition("mientras"),r=this.parseBlock("después de la condición de `mientras`");return{kind:"While",id:this.id(),condition:t,body:r,span:this.spanFrom(e)}}parseDoUntil(){let e=this.advance().span.start,t=this.parseBlock("después de `hacer`");this.expect("UNTIL","después del bloque de `hacer`","La forma completa es `hacer { … } hasta_que (condición);`.");let r=this.parseCondition("hasta_que");return{kind:"DoUntil",id:this.id(),body:t,condition:r,span:this.spanFrom(e)}}parseFor(){let e=this.advance().span.start;this.expect("LPAREN","después de `para`","La forma completa es `para (variable i = 0; i < 10; i += 1) { … }`.");let t;if(!this.check("SEMICOLON"))if(this.check("LET"))t=this.parseVariableDeclaration();else if(this.check("IDENT")&&O[this.peek().type])t=this.parseAssignment();else t=this.parseExpressionStatement();this.expect("SEMICOLON","después del inicio del `para`");let r=this.check("SEMICOLON")?void 0:this.parseExpression();this.expect("SEMICOLON","después de la condición del `para`");let s;if(!this.check("RPAREN"))if(this.check("IDENT")&&O[this.peek().type])s=this.parseAssignment();else s=this.parseExpressionStatement();this.expect("RPAREN","para cerrar el `para`");let n=this.parseBlock("después de `para (…)`");return{kind:"For",id:this.id(),init:t,condition:r,update:s,body:n,span:this.spanFrom(e)}}parseExpression(e=0){this.enter();let t=this.parsePrefix();while(e<(D[this.current.type]??0))t=this.parseInfix(t);return this.leave(),t}parsePrefix(){let e=this.current;switch(e.type){case"NUMBER":return this.advance(),{kind:"NumberLiteral",id:this.id(),value:e.value,span:e.span};case"STRING":return this.advance(),{kind:"StringLiteral",id:this.id(),value:e.value,span:e.span};case"TRUE":case"FALSE":return this.advance(),{kind:"BooleanLiteral",id:this.id(),value:e.type==="TRUE",span:e.span};case"NULL":return this.advance(),{kind:"NullLiteral",id:this.id(),span:e.span};case"IDENT":return this.advance(),{kind:"Identifier",id:this.id(),name:e.lexeme,span:e.span};case"LPAREN":{this.advance();let t=this.parseExpression();return this.expect("RPAREN","para cerrar el paréntesis"),t}case"LBRACKET":return this.parseList();case"MINUS":{this.advance();let t=this.parseExpression(7);return{kind:"Unary",id:this.id(),operator:"-",operand:t,span:this.spanFrom(e.span.start)}}case"NOT":case"BANG":{this.advance();let t=this.parseExpression(3);return{kind:"Unary",id:this.id(),operator:"no",operand:t,span:this.spanFrom(e.span.start)}}case"FUNCTION":return this.parseFunction();case"ILLEGAL":throw new T;case"IF":case"WHILE":case"FOR":case"DO":case"LET":case"RETURN":this.error("SENTENCIA_EN_EXPRESIÓN",`${b(e)} empieza una sentencia y no se puede usar dentro de una expresión.`,e.span);case"EOF":this.error("EXPRESIÓN_INCOMPLETA","El programa terminó a mitad de una expresión.",e.span,"Revisa que no falte un valor al final.");default:this.error("EXPRESIÓN_ESPERADA",`Se esperaba un valor, pero se encontró ${b(e)}.`,e.span,/^[a-z_]+$/.test(e.lexeme)?`\`${e.lexeme}\` es una palabra reservada; no se puede usar como nombre de variable.`:void 0)}}parseInfix(e){let t=this.current,r=e.span.start;switch(t.type){case"LPAREN":{this.advance();let s=this.parseList_("RPAREN","para cerrar los argumentos");return{kind:"Call",id:this.id(),callee:e,args:s,span:this.spanFrom(r)}}case"LBRACKET":{if(this.advance(),this.check("RBRACKET"))this.error("ÍNDICE_VACÍO","Falta el índice entre los corchetes.",this.current.span,"Por ejemplo: `lista[0]`.");let s=this.parseExpression();return this.expect("RBRACKET","para cerrar el índice"),{kind:"Index",id:this.id(),object:e,index:s,span:this.spanFrom(r)}}case"AND":case"OR":{this.advance();let s=D[t.type],n=this.parseExpression(s),a=t.type==="AND"?"y":"o";return{kind:"Logical",id:this.id(),operator:a,left:e,right:n,span:this.spanFrom(r)}}default:{this.advance();let s=D[t.type],n=this.parseExpression(s),a=Oe[t.type];if(s===4&&D[this.current.type]===4)this.error("COMPARACIÓN_ENCADENADA","No se pueden encadenar comparaciones.",{start:r,end:this.current.span.end},"Une las dos comparaciones con `y`, por ejemplo: `1 < x y x < 5`.");return{kind:"Binary",id:this.id(),operator:a,left:e,right:n,span:this.spanFrom(r)}}}}parseList(){let e=this.advance().span.start,t=this.parseList_("RBRACKET","para cerrar la lista");return{kind:"ListLiteral",id:this.id(),elements:t,span:this.spanFrom(e)}}parseList_(e,t){let r=[];while(!this.check(e))if(r.push(this.parseExpression()),!this.match("COMMA"))break;return this.expect(e,t),r}parseFunction(){let e=this.advance().span.start;if(this.check("IDENT")){let n=this.current.lexeme;this.error("PROCEDIMIENTO_CON_NOMBRE","Los procedimientos con nombre todavía no están disponibles.",this.current.span,`Por ahora escribe \`variable ${n} = procedimiento(…) { … };\`.`)}this.expect("LPAREN","después de `procedimiento`","Los parámetros van entre paréntesis: `procedimiento(a, b) { … }`.");let t=[],r=new Set;while(!this.check("RPAREN")){let n=this.parseBindingName("como parámetro");if(r.has(n.name))this.error("PARÁMETRO_REPETIDO",`El parámetro \`${n.name}\` está repetido.`,n.span);if(r.add(n.name),t.push(n),!this.match("COMMA"))break}this.expect("RPAREN","para cerrar los parámetros");let s=this.parseBlock("para el cuerpo del procedimiento");return{kind:"FunctionLiteral",id:this.id(),params:t,body:s,span:this.spanFrom(e)}}}var j=(e)=>{let t=new z({name:"programa",params:[],code:[],node:e});for(let r of e.statements)t.statement(r);return t.emit({op:"halt"}),t.chunk},Be=(e)=>{let t=new z({name:e.name??"procedimiento",params:e.params.map((r)=>r.name),code:[],node:e});for(let r of e.body.statements)t.statement(r);return t.emit({op:"const",value:null,node:e}),t.emit({op:"return",hasValue:!0,node:{kind:"Return",id:e.id,span:e.body.span}}),t.chunk};class z{chunk;constructor(e){this.chunk=e}emit(e){return this.chunk.code.push(e),this.chunk.code.length-1}get here(){return this.chunk.code.length}patch(e,t){let r=this.chunk.code[e];if(r&&"target"in r)r.target=t}statement(e){if(e.kind!=="Block")this.emit({op:"step",node:e});switch(e.kind){case"VariableDeclaration":this.expression(e.value),this.emit({op:"declare",name:e.name.name,node:e});return;case"Assignment":if(e.operator!=="=")this.emit({op:"load",name:e.target.name,node:e.target,forUpdate:!0});if(this.expression(e.value),e.operator!=="=")this.emit({op:"binary",operator:e.operator[0],node:e});this.emit({op:"store",name:e.target.name,node:e});return;case"ExpressionStatement":this.expression(e.expression),this.emit({op:"result",node:e});return;case"Return":if(e.value)this.expression(e.value);this.emit({op:"return",hasValue:Boolean(e.value),node:e});return;case"Block":this.block(e);return;case"If":{let t=[];if(e.branches.forEach((r,s)=>{this.expression(r.condition);let n=this.emit({op:"branch",target:-1,jumpWhen:!1,label:s===0?"si":"pero_si",node:r.condition,owner:e});this.block(r.body),t.push(this.emit({op:"jump",target:-1})),this.patch(n,this.here)}),e.alternative)this.block(e.alternative);for(let r of t)this.patch(r,this.here);return}case"While":{let t=this.here;this.expression(e.condition);let r=this.emit({op:"branch",target:-1,jumpWhen:!1,label:"mientras",node:e.condition,owner:e});this.block(e.body),this.emit({op:"jump",target:t}),this.patch(r,this.here);return}case"DoUntil":{let t=this.here;this.block(e.body),this.expression(e.condition),this.emit({op:"branch",target:t,jumpWhen:!1,label:"hasta_que",node:e.condition,owner:e});return}case"For":{if(this.emit({op:"enterScope"}),e.init)this.statement(e.init);let t=this.here,r=-1;if(e.condition)this.expression(e.condition),r=this.emit({op:"branch",target:-1,jumpWhen:!1,label:"para",node:e.condition,owner:e});if(this.block(e.body),e.update)this.statement(e.update);if(this.emit({op:"jump",target:t}),r>=0)this.patch(r,this.here);this.emit({op:"exitScope"});return}}}block(e){this.emit({op:"enterScope"});for(let t of e.statements)this.statement(t);this.emit({op:"exitScope"})}expression(e){switch(e.kind){case"NumberLiteral":case"StringLiteral":case"BooleanLiteral":this.emit({op:"const",value:e.value,node:e});return;case"NullLiteral":this.emit({op:"const",value:null,node:e});return;case"ListLiteral":for(let t of e.elements)this.expression(t);this.emit({op:"list",count:e.elements.length,node:e});return;case"Identifier":this.emit({op:"load",name:e.name,node:e});return;case"Unary":this.expression(e.operand),this.emit({op:"unary",operator:e.operator,node:e});return;case"Binary":this.expression(e.left),this.expression(e.right),this.emit({op:"binary",operator:e.operator,node:e});return;case"Logical":{this.expression(e.left);let t=this.emit({op:"shortCircuit",target:-1,node:e});this.expression(e.right),this.emit({op:"checkLogical",node:e}),this.patch(t,this.here);return}case"Call":this.expression(e.callee);for(let t of e.args)this.expression(t);this.emit({op:"call",argc:e.args.length,node:e});return;case"Index":this.expression(e.object),this.expression(e.index),this.emit({op:"index",node:e});return;case"FunctionLiteral":this.emit({op:"closure",chunk:Be(e),node:e});return}}}var A=Symbol("esperar-entrada"),d=(e)=>{if(e===null)return"nulo";switch(typeof e){case"number":return"número";case"string":return"texto";case"boolean":return"lógico";default:return e.kind==="lista"?"lista":"procedimiento"}},m=(e)=>{switch(e){case"lista":return"una lista";case"nulo":return"`nulo`";default:return`un ${e}`}},x=(e)=>typeof e==="object"&&e!==null&&e.kind==="lista",se=(e)=>typeof e==="object"&&e!==null&&(e.kind==="procedimiento"||e.kind==="nativo"),De=(e)=>{if(Object.is(e,-0))return"0";if(e===1/0)return"infinito";if(e===-1/0)return"-infinito";return String(e)},f=(e,t=new Set)=>{if(e===null)return"nulo";switch(typeof e){case"number":return De(e);case"string":return JSON.stringify(e);case"boolean":return e?"verdadero":"falso"}if(e.kind==="lista"){if(t.has(e))return"[…]";t.add(e);let r=e.items.map((s)=>f(s,t)).join(", ");return t.delete(e),`[${r}]`}return e.name?`procedimiento ${e.name}`:"procedimiento"},w=(e)=>typeof e==="string"?e:f(e),C=(e,t,r=[])=>{if(e===t)return!0;if(x(e)&&x(t)){if(e.items.length!==t.items.length)return!1;if(r.some(([n,a])=>n===e&&a===t))return!0;r.push([e,t]);let s=e.items.every((n,a)=>C(n,t.items[a],r));return r.pop(),s}return!1};class y{parent;variables=new Map;constructor(e){this.parent=e}lookup(e){let t=this;while(t){if(t.variables.has(e))return t;t=t.parent}return}}var E=(e,t,r)=>({kind:"nativo",name:e,arity:t,call:r}),M=(e,t,r,s="")=>{if(typeof t==="number")return t;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera un número${s}, pero recibió ${m(d(t))} (${f(t)}).`)},H=(e,t,r)=>{if(x(t))return t;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera una lista, pero recibió ${m(d(t))}.`)},ne=(e,t,r)=>{let s=t.length===1&&x(t[0])?t[0].items:t;if(s.length===0)throw r.fail("LISTA_VACÍA",`\`${e}\` necesita al menos un número, pero la lista está vacía.`);return s.map((n)=>M(e,n,r))},ae=(e)=>{let t=e.trim();if(!/^[-+]?(\d+(\.\d*)?|\.\d+)$/.test(t))return;return Number(t)},we=(e,t)=>{let r=10**t;return Math.sign(e)*Math.round(Math.abs(e)*r)/r},ie=[E("imprimir",[0,1/0],(e,t)=>(t.write(`${e.map(w).join(" ")}
7
+ `),null)),E("leer",0,(e,t)=>t.readLine()),E("leer_número",0,(e,t)=>{let r=t.readLine();if(r===A)return r;if(r===null)throw t.fail("SIN_ENTRADA","`leer_número` no encontró más datos de entrada.");let s=ae(r);if(s===void 0)throw t.fail("NO_ES_NÚMERO",`\`leer_número\` esperaba un número, pero leyó ${JSON.stringify(r)}.`);return s}),E("tipo",1,(e)=>d(e[0])),E("texto",1,(e)=>w(e[0])),E("número",1,(e,t)=>{let r=e[0];if(typeof r==="number")return r;if(typeof r==="string"){let s=ae(r);if(s!==void 0)return s;throw t.fail("NO_ES_NÚMERO",`No se puede convertir ${JSON.stringify(r)} en número.`)}throw t.fail("TIPO_DE_ARGUMENTO",`No se puede convertir ${m(d(r))} en número.`)}),E("longitud",1,(e,t)=>{let r=e[0];if(typeof r==="string")return[...r].length;if(x(r))return r.items.length;throw t.fail("TIPO_DE_ARGUMENTO",`\`longitud\` espera un texto o una lista, pero recibió ${m(d(r))}.`)}),E("absoluto",1,(e,t)=>Math.abs(M("absoluto",e[0],t))),E("máximo",[1,1/0],(e,t)=>Math.max(...ne("máximo",e,t))),E("mínimo",[1,1/0],(e,t)=>Math.min(...ne("mínimo",e,t))),E("redondear",[1,2],(e,t)=>{let r=M("redondear",e[0],t),s=e.length>1?M("redondear",e[1],t," de decimales"):0;if(!Number.isInteger(s)||s<0)throw t.fail("TIPO_DE_ARGUMENTO","La cantidad de decimales de `redondear` tiene que ser un entero positivo o 0.");return we(r,s)}),E("agregar",2,(e,t)=>(H("agregar",e[0],t).items.push(e[1]),null)),E("primero",1,(e,t)=>{let r=H("primero",e[0],t);if(r.items.length===0)throw t.fail("LISTA_VACÍA","`primero` no puede tomar nada de una lista vacía.");return r.items[0]}),E("último",1,(e,t)=>{let r=H("último",e[0],t);if(r.items.length===0)throw t.fail("LISTA_VACÍA","`último` no puede tomar nada de una lista vacía.");return r.items[r.items.length-1]})],Me=(e)=>e.replace(/[áéíóúü]/g,(t)=>({á:"a",é:"e",í:"i",ó:"o",ú:"u",ü:"u"})[t]??t),K=(()=>{let e={};for(let t of ie)e[t.name]=t;for(let t of ie){let r=Me(t.name);if(!e[r])e[r]=t}return e})();var Ve=(e,t)=>{let r=Array.from({length:t.length+1},(s,n)=>n);for(let s=1;s<=e.length;s++){let n=r[0];r[0]=s;for(let a=1;a<=t.length;a++){let p=r[a],o=e[s-1]===t[a-1]?0:1;r[a]=Math.min(r[a]+1,r[a-1]+1,n+o),n=p}}return r[t.length]},oe=(e)=>e.normalize("NFD").replace(/[̀-ͯ]/g,"").toLowerCase(),pe=(e,t)=>{let r=oe(e),s=r.length<=3?1:2,n,a=1/0;for(let p of new Set(t)){if(p===e)continue;let o=Ve(r,oe(p));if(o<=s&&o<a)n=p,a=o}return n};var g=(e)=>{let t=f(e);if(e===null)return{type:"nulo",value:null,repr:t};switch(typeof e){case"number":return{type:"número",value:e,repr:t};case"string":return{type:"texto",value:e,repr:t};case"boolean":return{type:"lógico",value:e,repr:t}}if(e.kind==="lista")return{type:"lista",ref:e.id,length:e.items.length,repr:t};return{type:"procedimiento",name:e.name,repr:t}},Q={maxOperations:1e7,maxCallDepth:1e4,maxOutput:1e6},ce=new Set(["step","enterScope","exitScope","jump","halt"]);class V{options;frames=[];stack=[];natives;inputQueue;breakpoints;limits;onEvent;outputParts=[];outputLength=0;nextId=1;lastValue=null;inputClosed=!1;currentNode;status="lista";operations=0;result=null;error;constructor(e,t={}){this.options=t;this.natives={...K,...t.natives},this.limits={maxOperations:t.maxOperations??Q.maxOperations,maxCallDepth:t.maxCallDepth??Q.maxCallDepth,maxOutput:t.maxOutput??Q.maxOutput},this.inputQueue=typeof t.input==="string"?le(t.input):[...t.input??[]],this.breakpoints=new Set(t.breakpoints??[]),this.onEvent=t.onEvent;let r=t.globals??new y;this.frames.push({chunk:e,pc:0,scope:r,root:r,stackBase:0,name:"programa"})}get output(){return this.outputParts.join("")}get done(){return this.status==="terminada"||this.status==="error"}run(e=1/0){return this.execute("run",e)}step(){return this.execute("step",1/0)}provideInput(e){if(this.inputQueue.push(...le(e)),this.status==="esperando_entrada")this.status="pausada"}closeInput(){if(this.inputClosed=!0,this.status==="esperando_entrada")this.status="pausada"}snapshot(){let e={},t=(s)=>{if(!x(s)||e[s.id])return;e[s.id]=s.items.map(g),s.items.forEach(t)},r=this.frames.map((s)=>{let n=[],a=s.scope;while(a){let p={};for(let[o,c]of a.variables)p[o]=g(c),t(c);if(n.unshift(p),a===s.root)break;a=a.parent}return{name:s.name,callSpan:s.callNode?.span,scopes:n}});return{status:this.status,operations:this.operations,nodeId:this.currentNode?.id,span:this.currentNode?.span,frames:r,heap:e,output:this.output}}execute(e,t){if(this.done||this.status==="esperando_entrada")return this.status;let r=this.operations+t,s=0;try{while(!0){let n=this.frames[this.frames.length-1],a=n.chunk.code[n.pc];if(!ce.has(a.op)){if(this.operations>=this.limits.maxOperations)throw this.limitError("DEMASIADAS_OPERACIONES",`El programa superó el límite de ${Y(this.limits.maxOperations)} operaciones.`,"¿Hay un bucle que nunca termina? Revisa que su condición cambie en algún momento.");if(this.operations>=r)return this.status="pausada",this.status}if(a.op==="step"&&e==="run"&&s>0&&this.breakpoints.has(a.node.span.start.line))return this.status="pausada",this.status;let p=this.exec(n,a);s+=1;let o=this.status;if(o==="esperando_entrada"||o==="terminada")return o;if(e==="step"&&p)return this.status="pausada",this.status}}catch(n){if(!(n instanceof v))throw n;return this.error=n,this.status="error",this.emit({type:"error",error:n.toJSON()}),this.status}}exec(e,t){if(e.pc+=1,!ce.has(t.op))this.operations+=1;switch(t.op){case"step":return this.currentNode=t.node,this.emit({type:"paso",nodeId:t.node.id,nodeKind:t.node.kind,span:t.node.span}),!0;case"const":return this.stack.push(t.value),!1;case"list":{let r=this.stack.splice(this.stack.length-t.count,t.count);return this.stack.push(this.newList(r)),!1}case"load":{let r=e.scope.lookup(t.name);if(r)return this.stack.push(r.variables.get(t.name)),!1;let s=this.natives[t.name];if(s&&!t.forUpdate)return this.stack.push(s),!1;if(t.forUpdate)throw this.undeclaredAssignment(t.name,t.node);throw this.unknownName(e,t.name,t.node)}case"declare":{let r=this.stack.pop();if(e.scope.variables.has(t.name)&&!this.options.allowRedeclare)throw this.runtimeError("VARIABLE_REPETIDA",`La variable \`${t.name}\` ya existe en este bloque.`,t.node.name,`Para cambiar su valor escribe \`${t.name} = …\`, sin \`variable\`.`);return e.scope.variables.set(t.name,r),this.emit({type:"declara",name:t.name,value:g(r),nodeId:t.node.id,span:t.node.span}),!1}case"store":{let r=this.stack.pop(),s=e.scope.lookup(t.name);if(!s)throw this.undeclaredAssignment(t.name,t.node.target);let n=s.variables.get(t.name);if(s.variables.set(t.name,r),this.onEvent)this.emit({type:"asigna",name:t.name,before:g(n),after:g(r),nodeId:t.node.id,span:t.node.span});return!1}case"result":{let r=this.stack.pop();if(this.frames.length===1)this.lastValue=r;return!1}case"unary":{let r=this.stack.pop(),s=this.unary(t.operator,r,t.node);if(this.stack.push(s),this.onEvent)this.emitOperation(t.operator,[r],s,t.node);return!1}case"binary":{let r=this.stack.pop(),s=this.stack.pop(),n=this.binary(t.operator,s,r,t.node);if(this.stack.push(n),this.onEvent)this.emitOperation(t.operator,[s,r],n,t.node);return!1}case"index":{let r=this.stack.pop(),s=this.stack.pop();return this.stack.push(this.index(s,r,t.node)),!1}case"closure":{let r={kind:"procedimiento",id:this.nextId++,name:t.node.name,chunk:t.chunk,closure:e.scope};return this.stack.push(r),!1}case"call":return this.call(e,t.argc,t.node);case"return":{let r=t.hasValue?this.stack.pop():null;if(this.frames.length===1)return this.finish(r),!0;return this.emit({type:"regresa",name:e.name,value:g(r)}),this.frames.pop(),this.stack.length=e.stackBase-1,this.stack.push(r),!1}case"jump":return e.pc=t.target,!1;case"branch":{let r=this.stack.pop();if(typeof r!=="boolean")throw this.nonBooleanCondition(t.label,r,t.node);if(this.currentNode=t.node,this.emit({type:"decide",label:t.label,value:r,nodeId:t.node.id,ownerId:t.owner.id,span:t.node.span}),r===t.jumpWhen)e.pc=t.target;return!0}case"shortCircuit":{let r=this.stack[this.stack.length-1];if(typeof r!=="boolean")throw this.nonBooleanLogical(t.node,r,t.node.left);if(t.node.operator==="y"?r===!1:r===!0){if(e.pc=t.target,this.onEvent)this.emitOperation(t.node.operator,[r],r,t.node)}else this.stack.pop();return!1}case"checkLogical":{let r=this.stack[this.stack.length-1];if(typeof r!=="boolean")throw this.nonBooleanLogical(t.node,r,t.node.right);if(this.onEvent){let s=t.node.operator==="y";this.emitOperation(t.node.operator,[s,r],r,t.node)}return!1}case"enterScope":return e.scope=new y(e.scope),!1;case"exitScope":return e.scope=e.scope.parent??e.scope,!1;case"halt":return this.finish(this.lastValue),!0}}finish(e){this.result=e,this.status="terminada",this.emit({type:"fin",result:g(e)})}call(e,t,r){let s=this.stack.length-t-1,n=this.stack[s],a=n!==null&&typeof n==="object"&&"name"in n&&n.name?n.name:ue(r);if(!se(n))throw this.runtimeError("NO_ES_PROCEDIMIENTO",`\`${ue(r)}\` no es un procedimiento, es ${m(d(n))}.`,r.callee);let p=this.stack.slice(s+1),o=n.kind==="nativo"?n.arity:n.chunk.params.length;if(Fe(o,t,a,(l,S)=>this.runtimeError("CANTIDAD_DE_ARGUMENTOS",l,r,S)),n.kind==="nativo"){let l=n.call(p,this.nativeContext(r));if(l===A)return e.pc-=1,this.operations-=1,this.status="esperando_entrada",!0;return this.stack.length=s,this.stack.push(l),!1}if(this.frames.length-1>=this.limits.maxCallDepth)throw this.limitError("DEMASIADAS_LLAMADAS",`Hay más de ${Y(this.limits.maxCallDepth)} llamadas anidadas.`,"Si es recursión, revisa que exista un caso base que deje de llamar al procedimiento.",r);let c=new y(n.closure);return n.chunk.params.forEach((l,S)=>c.variables.set(l,p[S])),this.emit({type:"llama",name:a,args:p.map(g),nodeId:r.id,span:r.span}),this.frames.push({chunk:n.chunk,pc:0,scope:c,root:c,stackBase:s+1,name:a,callNode:r}),!1}nativeContext(e){return{write:(t)=>{if(this.outputLength+=t.length,this.outputLength>this.limits.maxOutput)throw this.limitError("DEMASIADA_SALIDA",`El programa imprimió más de ${Y(this.limits.maxOutput)} caracteres.`,"¿Hay un `imprimir` dentro de un bucle que nunca termina?",e);this.outputParts.push(t),this.emit({type:"imprime",text:t})},readLine:()=>{if(this.inputQueue.length>0){let t=this.inputQueue.shift();return this.emit({type:"lee",text:t}),t}if(this.options.interactiveInput&&!this.inputClosed)return A;return this.emit({type:"lee",text:null}),null},newList:(t)=>this.newList(t),fail:(t,r,s)=>this.runtimeError(t,r,e,s)}}newList(e){return{kind:"lista",id:this.nextId++,items:e}}unary(e,t,r){if(e==="-"){if(typeof t!=="number")throw this.runtimeError("TIPO_INVÁLIDO",`El signo \`-\` solo funciona con números, no con ${m(d(t))}.`,r);return-t}if(typeof t!=="boolean")throw this.runtimeError("TIPO_INVÁLIDO",`\`no\` solo funciona con \`verdadero\` o \`falso\`, pero recibió ${f(t)}.`,r.operand,X(t));return!t}binary(e,t,r,s){switch(e){case"==":return C(t,r);case"!=":return!C(t,r);case"+":if(typeof t==="number"&&typeof r==="number")return t+r;if(typeof t==="string"&&typeof r==="string")return t+r;if(typeof t==="string"||typeof r==="string"){let n=typeof t==="string"?r:t;throw this.runtimeError("TIPOS_MEZCLADOS",`\`+\` no puede unir un texto con ${m(d(n))}.`,s,`Convierte el valor con \`texto(…)\`, o imprime los dos por separado: \`imprimir("Total:", ${f(n)})\`.`)}throw this.operandError(e,t,r,s);case"-":case"*":case"/":{if(typeof t!=="number"||typeof r!=="number")throw this.operandError(e,t,r,s);if(e==="-")return t-r;if(e==="*")return t*r;if(r===0){let n=s.kind==="Binary"?s.right:s.value;throw this.runtimeError("DIVISIÓN_ENTRE_CERO","No se puede dividir entre cero.",n,"Revisa que el divisor no valga 0 antes de dividir.")}return t/r}case"<":case">":case"<=":case">=":{if(!(typeof t==="number"&&typeof r==="number"||typeof t==="string"&&typeof r==="string"))throw this.runtimeError("COMPARACIÓN_INVÁLIDA",`No se puede comparar ${m(d(t))} con ${m(d(r))} usando \`${e}\`.`,s,"`<`, `>`, `<=` y `>=` comparan dos números o dos textos.");let a=t,p=r;if(e==="<")return a<p;if(e===">")return a>p;if(e==="<=")return a<=p;return a>=p}}}index(e,t,r){if(!x(e)&&typeof e!=="string")throw this.runtimeError("ÍNDICE_NO_SOPORTADO",`Solo se puede usar \`[ ]\` con listas y textos, no con ${m(d(e))}.`,r.object);if(typeof t!=="number"||!Number.isInteger(t))throw this.runtimeError("ÍNDICE_INVÁLIDO",`El índice tiene que ser un número entero, pero es ${f(t)}.`,r.index,typeof t==="number"?"Redondea el índice con `redondear(…)`.":void 0);let s=x(e)?e.items.length:e.length,n=t<0?s+t:t;if(n<0||n>=s){let a=x(e)?"la lista":"el texto",p=x(e)?"elemento":"carácter";throw this.runtimeError("ÍNDICE_FUERA_DE_RANGO",`El índice ${t} está fuera de ${a}, que tiene ${s} ${s===1?p:p==="carácter"?"caracteres":"elementos"}.`,r.index,s===0?`${a[0].toUpperCase()}${a.slice(1)} está vacía.`:`Los índices válidos van de 0 a ${s-1} (o de -1 a -${s} desde el final).`)}return x(e)?e.items[n]:e[n]}callStack(){return this.frames.map((e)=>e.name)}runtimeError(e,t,r,s){return new v({kind:"ejecución",code:e,message:t,hint:s,span:r.span,callStack:this.callStack()})}limitError(e,t,r,s){return new v({kind:"límite",code:e,message:t,hint:r,span:s?.span??this.currentNode?.span,callStack:this.callStack()})}operandError(e,t,r,s){return this.runtimeError("TIPO_INVÁLIDO",`\`${e}\` no funciona entre ${m(d(t))} y ${m(d(r))}.`,s)}unknownName(e,t,r){let s=[...Object.keys(this.natives)];for(let a=e.scope;a;a=a.parent)s.push(...a.variables.keys());let n=pe(t,s);return this.runtimeError("NOMBRE_DESCONOCIDO",`No existe nada llamado \`${t}\`.`,r,n?`¿Quisiste decir \`${n}\`?`:`Si es una variable nueva, declárala con \`variable ${t} = …\`.`)}undeclaredAssignment(e,t){if(this.natives[e])return this.runtimeError("NOMBRE_RESERVADO_POR_BIBLIOTECA",`\`${e}\` es un procedimiento de Codexivo; no se le puede asignar un valor.`,t,`Si quieres tu propia variable con ese nombre, declárala: \`variable ${e} = …\`.`);return this.runtimeError("VARIABLE_NO_DECLARADA",`La variable \`${e}\` no existe.`,t,`¿Olvidaste declararla? Escribe \`variable ${e} = …\` la primera vez.`)}nonBooleanCondition(e,t,r){return this.runtimeError("CONDICIÓN_NO_LÓGICA",`La condición de \`${e}\` tiene que ser \`verdadero\` o \`falso\`, pero vale ${f(t)}.`,r,X(t))}nonBooleanLogical(e,t,r){return this.runtimeError("OPERANDO_NO_LÓGICO",`\`${e.operator}\` une valores \`verdadero\` o \`falso\`, pero recibió ${f(t)}.`,r,X(t))}emitOperation(e,t,r,s){this.emit({type:"opera",operator:e,operands:t.map(g),result:g(r),nodeId:s.id,span:s.span})}emit(e){if(!this.onEvent)return;this.onEvent({...e,operations:this.operations,depth:this.frames.length})}}var le=(e)=>e.replace(/\r\n/g,`
8
+ `).replace(/\n$/,"").split(`
9
+ `),Y=(e)=>e.toLocaleString("es-MX").replace(/,/g," "),ue=(e)=>{if(e.callee.kind==="Identifier")return e.callee.name;return"esto"},X=(e)=>{switch(d(e)){case"número":return"¿Quisiste comparar? Por ejemplo: `x != 0` o `x > 0`.";case"texto":return'¿Quisiste comparar? Por ejemplo: `texto != ""`.';case"lista":return"¿Quisiste decir `longitud(lista) > 0`?";case"nulo":return"¿Quisiste decir `x != nulo`?";default:return}},Fe=(e,t,r,s)=>{let[n,a]=typeof e==="number"?[e,e]:e;if(t>=n&&t<=a)return;let p=(c)=>c===1?"1 valor":`${c} valores`,o;if(n===a)o=p(n);else if(a===1/0)o=`al menos ${p(n)}`;else o=`entre ${n} y ${p(a)}`;throw s(`\`${r}\` espera ${o}, pero recibió ${t}.`)};var he=(e,t={})=>{let{program:r,tokens:s,errors:n}=W(e);if(n.length>0)return{program:r,tokens:s,errors:n};return{program:r,tokens:s,errors:n,machine:new V(j(r),t)}},de=(e,t={})=>{let{trace:r,maxEvents:s=1e5,...n}=t,a=[],p=!1,o=r?(S)=>{if(a.length<s)a.push(S);else p=!0}:void 0,{errors:c,machine:l}=he(e,{...n,onEvent:o});if(!l)return{status:"error",ok:!1,result:null,resultText:"nulo",output:"",errors:c.map((S)=>S.toJSON()),operations:0,...r?{events:a,eventsTruncated:p}:{}};return l.run(),{status:l.status,ok:l.status==="terminada",result:l.result,resultText:f(l.result),output:l.output,errors:l.error?[l.error.toJSON()]:[],operations:l.operations,...r?{events:a,eventsTruncated:p}:{}}};class me{options;globals=new y;constructor(e={}){this.options=e}run(e){return de(e,{...this.options,globals:this.globals,allowRedeclare:!0})}}var fe=(e)=>{switch(e.kind){case"Program":case"Block":return e.statements;case"ListLiteral":return e.elements;case"Unary":return[e.operand];case"Binary":case"Logical":return[e.left,e.right];case"Call":return[e.callee,...e.args];case"Index":return[e.object,e.index];case"FunctionLiteral":return[...e.params,e.body];case"VariableDeclaration":return[e.name,e.value];case"Assignment":return[e.target,e.value];case"ExpressionStatement":return[e.expression];case"Return":return e.value?[e.value]:[];case"If":return[...e.branches.flatMap((t)=>[t.condition,t.body]),...e.alternative?[e.alternative]:[]];case"While":return[e.condition,e.body];case"DoUntil":return[e.body,e.condition];case"For":return[...e.init?[e.init]:[],...e.condition?[e.condition]:[],...e.update?[e.update]:[],e.body];default:return[]}},Ee=(e,t,r)=>{t(e,r);for(let s of fe(e))Ee(s,t,e)};export{v as CodexivoError,L as KEYWORDS,V as Machine,y as Scope,me as Session,P as TokenType,A as WAIT_FOR_INPUT,fe as childrenOf,j as compile,C as equals,ve as formatError,Ne as isKeyword,K as natives,W as parse,he as prepare,f as repr,de as run,g as serialize,w as show,q as tokenize,d as typeOf,Ee as walk};
10
+
11
+ //# debugId=3028B7122A20F3F064756E2164756E21
12
+ //# sourceMappingURL=index.js.map