codexivo 0.2.3 → 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.
package/dist/worker.js CHANGED
@@ -1,27 +1,29 @@
1
- class P 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 jt=(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 fe;((p)=>{p.NUMBER="NUMBER";p.STRING="STRING";p.IDENT="IDENT";p.LET="LET";p.CONST="CONST";p.DOMAIN="DOMAIN";p.FUNCTION="FUNCTION";p.RETURN="RETURN";p.IF="IF";p.ELSEIF="ELSEIF";p.ELSE="ELSE";p.WHILE="WHILE";p.DO="DO";p.UNTIL="UNTIL";p.FOR="FOR";p.FOR_EACH="FOR_EACH";p.FROM="FROM";p.TO="TO";p.STEP="STEP";p.IN="IN";p.REPEAT="REPEAT";p.TIMES="TIMES";p.BREAK="BREAK";p.CONTINUE="CONTINUE";p.TRUE="TRUE";p.FALSE="FALSE";p.NULL="NULL";p.AND="AND";p.OR="OR";p.NOT="NOT";p.MOD="MOD";p.DIV="DIV";p.ASSIGN="ASSIGN";p.PLUS_ASSIGN="PLUS_ASSIGN";p.MINUS_ASSIGN="MINUS_ASSIGN";p.STAR_ASSIGN="STAR_ASSIGN";p.SLASH_ASSIGN="SLASH_ASSIGN";p.PERCENT_ASSIGN="PERCENT_ASSIGN";p.INCREMENT="INCREMENT";p.DECREMENT="DECREMENT";p.PLUS="PLUS";p.MINUS="MINUS";p.STAR="STAR";p.SLASH="SLASH";p.PERCENT="PERCENT";p.CARET="CARET";p.BANG="BANG";p.EQ="EQ";p.NEQ="NEQ";p.LT="LT";p.GT="GT";p.LTE="LTE";p.GTE="GTE";p.COMMA="COMMA";p.COLON="COLON";p.DOT="DOT";p.SEMICOLON="SEMICOLON";p.LPAREN="LPAREN";p.RPAREN="RPAREN";p.LBRACE="LBRACE";p.RBRACE="RBRACE";p.LBRACKET="LBRACKET";p.RBRACKET="RBRACKET";p.EOF="EOF";p.ILLEGAL="ILLEGAL"})(fe||={});var X={variable:"LET",constante:"CONST",dominio:"DOMAIN",procedimiento:"FUNCTION",regresa:"RETURN",si:"IF",pero_si:"ELSEIF",si_no:"ELSE",mientras:"WHILE",hacer:"DO",hasta_que:"UNTIL",para:"FOR",para_cada:"FOR_EACH",desde:"FROM",hasta:"TO",con_paso:"STEP",en:"IN",repetir:"REPEAT",veces:"TIMES",romper:"BREAK",continuar:"CONTINUE",verdadero:"TRUE",falso:"FALSE",nulo:"NULL",y:"AND",o:"OR",no:"NOT",mod:"MOD",div:"DIV",es_igual_a:"EQ",es_distinto_de:"NEQ",es_menor_que:"LT",es_mayor_que:"GT",es_menor_o_igual_a:"LTE",es_mayor_o_igual_a:"GTE"},ct=(e)=>Object.hasOwn(X,e),pt={["ASSIGN"]:"=",["PLUS_ASSIGN"]:"+=",["MINUS_ASSIGN"]:"-=",["STAR_ASSIGN"]:"*=",["SLASH_ASSIGN"]:"/=",["PERCENT_ASSIGN"]:"%=",["INCREMENT"]:"++",["DECREMENT"]:"--",["PLUS"]:"+",["MINUS"]:"-",["STAR"]:"*",["SLASH"]:"/",["PERCENT"]:"%",["CARET"]:"^",["BANG"]:"!",["EQ"]:"==",["NEQ"]:"!=",["LT"]:"<",["GT"]:">",["LTE"]:"<=",["GTE"]:">=",["COMMA"]:",",["COLON"]:":",["DOT"]:".",["SEMICOLON"]:";",["LPAREN"]:"(",["RPAREN"]:")",["LBRACE"]:"{",["RBRACE"]:"}",["LBRACKET"]:"[",["RBRACKET"]:"]"},lt=Object.fromEntries(Object.entries(X).map(([e,t])=>[t,e])),ge=(e)=>{let t=pt[e];if(t)return`\`${t}\``;let r=lt[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 ct(e.lexeme)?`la palabra \`${e.lexeme}\``:ge(e.type)}};var Y=/[A-Za-z_À-ÖØ-öø-ÿ]/,F=/[0-9]/,ut=/\s/,dt={"==":"EQ","!=":"NEQ","<=":"LTE",">=":"GTE","+=":"PLUS_ASSIGN","-=":"MINUS_ASSIGN","*=":"STAR_ASSIGN","/=":"SLASH_ASSIGN","%=":"PERCENT_ASSIGN","++":"INCREMENT","--":"DECREMENT"},mt={"=":"ASSIGN","+":"PLUS","-":"MINUS","*":"STAR","/":"SLASH","%":"PERCENT","^":"CARET","!":"BANG","<":"LT",">":"GT",",":"COMMA",":":"COLON",".":"DOT",";":"SEMICOLON","(":"LPAREN",")":"RPAREN","{":"LBRACE","}":"RBRACE","[":"LBRACKET","]":"RBRACKET"},ht=new Set(["IDENT","NUMBER","STRING","RPAREN","RBRACKET","RBRACE"]),ft={n:`
3
- `,t:"\t","\\":"\\",'"':'"',"'":"'"},gt={"&":"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 `'`."},De=(e)=>{let t=[],r=[],a=0,n=1,s=1,i=()=>({line:n,column:s,offset:a}),c=(l=0)=>e[a+l]??"",o=()=>{let l=e[a]??"";if(a+=1,l===`
4
- `)n+=1,s=1;else s+=1;return l},m=(l,d,k)=>{let A={type:l,lexeme:e.slice(d.offset,a),span:{start:d,end:i()}};if(k!==void 0)A.value=k;return t.push(A),A},x=(l,d,k,A)=>{r.push(new P({kind:"léxico",code:l,message:d,hint:A,span:k}))};while(a<e.length){let l=c();if(ut.test(l)){o();continue}let d=i();if(l==="/"&&c(1)==="/"){while(a<e.length&&c()!==`
5
- `)o();continue}if(l==="/"&&c(1)==="*"){o(),o();while(a<e.length&&!(c()==="*"&&c(1)==="/"))o();if(a>=e.length)x("COMENTARIO_SIN_CERRAR","El comentario que empieza aquí no se cerró.",{start:d,end:{line:d.line,column:d.column+2,offset:d.offset+2}},"Cierra el comentario con `*/`.");else o(),o();continue}let k=t[t.length-1],A=k!==void 0&&ht.has(k.type)&&k.span.end.offset===a;if(F.test(l)||l==="."&&F.test(c(1))&&!A){let T=()=>{while(F.test(c())||c()==="_")o()};if(T(),c()==="."&&F.test(c(1)))o(),T();if(Y.test(c())&&c()!=="_"){while(Y.test(c())||F.test(c()))o();let M=m("ILLEGAL",d);x("NOMBRE_EMPIEZA_CON_NÚMERO",`\`${M.lexeme}\` no es un número ni un nombre válido.`,M.span,"Los nombres no pueden empezar con un dígito. Si es una multiplicación, escribe el operador: `2 * x`.");continue}let _=e.slice(d.offset,a);if(/(^|\D)_|_(\D|$)/.test(_)){let M=m("ILLEGAL",d);x("SEPARADOR_INVÁLIDO",`\`${_}\` no es un número válido.`,M.span,"El `_` solo puede ir entre dos dígitos para separar grupos, como en `1_000_000`.");continue}m("NUMBER",d,Number(_.replaceAll("_","")));continue}if(Y.test(l)){while(Y.test(c())||F.test(c()))o();let T=e.slice(d.offset,a);m(X[T]??"IDENT",d);continue}if(l==='"'||l==="'"){let T=o(),_="",M=!1;while(a<e.length&&c()!==`
6
- `){let me=o();if(me===T){M=!0;break}if(me==="\\"){let ot={line:n,column:s-1,offset:a-1},he=o(),Le=ft[he];if(Le===void 0)x("ESCAPE_DESCONOCIDO",`\`\\${he}\` no es una secuencia de escape válida.`,{start:ot,end:i()},"Las secuencias válidas son `\\n`, `\\t`, `\\\\`, `\\\"` y `\\'`."),_+=he;else _+=Le;continue}_+=me}let it=m("STRING",d,_);if(!M)x("TEXTO_SIN_CERRAR","El texto que empieza aquí no se cerró.",it.span,`Agrega la comilla \`${T}\` al final. Un texto no puede ocupar varias líneas; usa \`\\n\` para un salto de línea.`);continue}let f=l+c(1),S=dt[f];if(S){o(),o(),m(S,d);continue}let E=mt[l];if(E){o(),m(E,d);continue}o();let N=m("ILLEGAL",d);x("CARÁCTER_DESCONOCIDO",`Codexivo no reconoce el carácter \`${l}\`.`,N.span,gt[l])}let h=i();return t.push({type:"EOF",lexeme:"",span:{start:h,end:h}}),{tokens:t,errors:r}};var q=(e)=>{let{tokens:t,errors:r}=De(e),a=new Pe(t),n=a.parseProgram(),s=[...r,...a.errors].sort((i,c)=>(i.span?.start.offset??0)-(c.span?.start.offset??0));return{program:n,tokens:t,errors:s}};var Z={OR:1,AND:2,EQ:4,NEQ:4,LT:4,GT:4,LTE:4,GTE:4,PLUS:5,MINUS:5,STAR:6,SLASH:6,PERCENT:6,MOD:6,DIV:6,CARET:8,LPAREN:9,LBRACKET:9,DOT:9},Et={PLUS:"+",MINUS:"-",STAR:"*",SLASH:"/",PERCENT:"mod",MOD:"mod",DIV:"div",CARET:"^",EQ:"==",NEQ:"!=",LT:"<",GT:">",LTE:"<=",GTE:">="},bt={ASSIGN:"=",PLUS_ASSIGN:"+=",MINUS_ASSIGN:"-=",STAR_ASSIGN:"*=",SLASH_ASSIGN:"/=",PERCENT_ASSIGN:"%="},$e={INCREMENT:"++",DECREMENT:"--"},yt=new Set(["LET","DOMAIN","CONST","REPEAT","FOR_EACH","BREAK","CONTINUE","FUNCTION","RETURN","IF","WHILE","DO","FOR"]),vt=new Set(["VariableDeclaration","FunctionDeclaration","Domain"]),we=(e)=>{switch(e.kind){case"If":return"si";case"While":return"mientras";case"DoUntil":return"hacer";case"For":return"para";case"ForEach":return"para_cada";case"Repeat":return"repetir";case"Return":return"regresa";case"Break":return"romper";case"Continue":return"continuar";case"Block":return"{";default:return}},xt={sino:"En Codexivo se escribe `si_no`, con guion bajo.",pero:"En Codexivo se escribe `pero_si`, con guion bajo."},Ee=50,St=400;class K{}class Pe{tokens;errors=[];position=0;nextId=1;depth=0;grouping=0;loopDepth=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,a){if(this.errors.length<Ee)this.errors.push(new P({kind:"sintaxis",code:e,message:t,hint:a,span:r}));throw new K}failSilentlyOnIllegal(){if(this.check("ILLEGAL"))throw new K}expect(e,t,r){if(this.check(e))return this.advance();this.failSilentlyOnIllegal();let a=this.current,n=r;if(e==="RPAREN"&&a.type==="ASSIGN")n="Para comparar se usa `es_igual_a` (o `==`); un solo `=` sirve para asignar.";this.error("TOKEN_ESPERADO",`Se esperaba ${ge(e)} ${t}, pero se encontró ${B(a)}.`,a.span,n)}enter(){if(this.depth+=1,this.depth>St)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(yt.has(this.current.type))return;if(this.current.span.start.line>e.span.start.line)return;this.advance()}}parseStatements(e){let t=this.grouping;this.grouping=0;try{return this.parseStatementList(e)}finally{this.grouping=t}}parseStatementList(e){let t=[],r=!1;while(!this.check(e)&&!this.check("EOF")){let a=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 K))throw n;this.depth=0,r=!0,this.synchronize(a)}}return this.checkDuplicateProcedures(t),t}checkDuplicateProcedures(e){let t=new Set(e.flatMap((a)=>a.kind==="FunctionDeclaration"?[a.name.name]:[]));if(t.size===0)return;let r=new Set;for(let a of e){if(a.kind!=="FunctionDeclaration"&&a.kind!=="VariableDeclaration")continue;let{name:n}=a;if(!t.has(n.name))continue;if(a.kind==="FunctionDeclaration"&&!r.has(n.name)){r.add(n.name);continue}if(this.errors.length>=Ee)return;this.errors.push(new P({kind:"sintaxis",code:"NOMBRE_REPETIDO",message:`Ya hay un procedimiento llamado \`${n.name}\` en este bloque.`,hint:"Un procedimiento con nombre existe desde el inicio de su bloque; cambia uno de los dos nombres.",span:n.span}))}}parseStatement(){let e=this.current;switch(e.type){case"SEMICOLON":return this.advance(),this.parseStatementAfterStraySemicolon();case"LET":case"CONST":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"REPEAT":return this.endCompoundStatement(this.parseRepeat());case"FOR_EACH":return this.endCompoundStatement(this.parseForEach());case"DOMAIN":return this.endCompoundStatement(this.parseDomain());case"BREAK":case"CONTINUE":return this.endSimpleStatement(this.parseLoopJump());case"IN":this.error("EN_SIN_PARA_CADA","`en` solo se usa dentro de `para_cada … en …`.",e.span);case"FUNCTION":if(this.peek().type==="IDENT")return this.endCompoundStatement(this.parseFunctionDeclaration());return this.endSimpleStatement(this.parseSimpleStatement());case"LBRACE":if((this.peek().type==="STRING"||this.peek().type==="NUMBER")&&this.peek(2).type==="COLON")this.error("DICCIONARIO_COMO_SENTENCIA","Al inicio de una sentencia, `{` abre un bloque, no un diccionario.",e.span,'Guarda el diccionario en una variable: `variable d = { "clave": valor }`.');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=xt[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);return this.endSimpleStatement(this.parseSimpleStatement())}default:return this.endSimpleStatement(this.parseSimpleStatement())}}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(),t=e.type==="CONST",r=this.parseBindingName(`después de \`${e.lexeme}\``);this.expect("ASSIGN",`después de \`${e.lexeme} ${r.name}\``,t?`Toda constante necesita su valor desde el principio: \`constante ${r.name} = 10\`.`:`Toda variable empieza con un valor: \`variable ${r.name} = 0\`.`);let a=this.parseExpression();if(a.kind==="FunctionLiteral"&&!a.name)a.name=r.name;return{kind:"VariableDeclaration",id:this.id(),constant:t,name:r,value:a,span:this.spanFrom(e.span.start)}}parseBindingName(e){let t=this.current;if(t.type!=="IDENT"){if(this.failSilentlyOnIllegal(),t.lexeme&&/^[a-z_]+$/.test(t.lexeme)&&t.type!=="EOF")this.advance(),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}}parseSimpleStatement(){let e=this.current.span.start,t=this.parseExpression(0,!0),r=bt[this.current.type],a=$e[this.current.type];if(!r&&!a)return{kind:"ExpressionStatement",id:this.id(),expression:t,span:this.spanFrom(e)};if(t.kind!=="Identifier"&&t.kind!=="Index")this.error("ASIGNACIÓN_INVÁLIDA",a?`\`${this.current.lexeme}\` solo funciona con una variable o un elemento, como \`i${this.current.lexeme}\` o \`lista[0]${this.current.lexeme}\`.`:"Solo se le puede asignar un valor a una variable o a un elemento, como `x = 1` o `lista[0] = 1`.",this.current.span);if(this.advance(),a)return{kind:"Increment",id:this.id(),target:t,operator:a,span:this.spanFrom(e)};let n=this.parseExpression();if(n.kind==="FunctionLiteral"&&!n.name&&t.kind==="Identifier")n.name=t.name;return{kind:"Assignment",id:this.id(),target:t,operator:r,value:n,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)}}grouped(e){this.grouping+=1;try{return e()}finally{this.grouping-=1}}parseCondition(e){this.expect("LPAREN",`después de \`${e}\``,`La condición va entre paréntesis: \`${e} (x > 0)\`.`);let t=this.grouped(()=>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,s=this.parseCondition(r),i=this.parseBlock(`después de la condición de \`${r}\``);t.push({condition:s,body:i,span:this.spanFrom(n)}),r="pero_si"}while(this.check("ELSEIF"));let a;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 (…)`.");a=this.parseBlock("después de `si_no`")}return{kind:"If",id:this.id(),branches:t,alternative:a,span:this.spanFrom(e)}}parseWhile(){let e=this.advance().span.start,t=this.parseCondition("mientras"),r=this.parseLoopBody("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.parseLoopBody("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;if(!this.check("LPAREN"))return this.parseForRange(e);this.expect("LPAREN","después de `para`","La forma completa es `para (variable i = 0; i < 10; i += 1) { … }`.");let{init:t,condition:r,update:a}=this.grouped(()=>{let s;if(!this.check("SEMICOLON"))if(this.check("LET")||this.check("CONST"))s=this.parseVariableDeclaration();else s=this.parseSimpleStatement();this.expect("SEMICOLON","después del inicio del `para`");let i=this.check("SEMICOLON")?void 0:this.parseExpression();this.expect("SEMICOLON","después de la condición del `para`");let c;if(!this.check("RPAREN"))c=this.parseSimpleStatement();return this.expect("RPAREN","para cerrar el `para`"),{init:s,condition:i,update:c}}),n=this.parseLoopBody("después de `para (…)`");return{kind:"For",id:this.id(),init:t,condition:r,update:a,body:n,span:this.spanFrom(e)}}parseForRange(e){if(this.check("LET"))this.advance(),this.error("TOKEN_ESPERADO","La variable de `para … desde … hasta` se escribe sin `variable`.",this.current.span,"La forma completa es `para i desde 1 hasta 10 { … }`.");let r=this.parseBindingName("después de `para`");if(this.check("ASSIGN"))this.error("TOKEN_ESPERADO",`Para contar se escribe \`desde\`, no \`=\`: \`para ${r.name} desde 1 hasta 10\`.`,this.current.span,"La forma clásica `para (variable i = 0; i < 10; i++)` va entre paréntesis.");let a=(o)=>{if(this.check("LBRACE"))this.error("EXPRESIÓN_ESPERADA",`Falta un valor después de \`${o}\`.`,this.current.span,"La forma completa es `para i desde 1 hasta 10 { … }`.");return this.parseExpression()};this.expect("FROM",`después de \`para ${r.name}\``,"La forma completa es `para i desde 1 hasta 10 { … }`.");let n=a("desde");this.expect("TO","después del inicio del `para`","La forma completa es `para i desde 1 hasta 10 { … }`.");let s=a("hasta"),i=this.match("STEP")?a("con_paso"):void 0,c=this.parseLoopBody("después de `para … desde … hasta …`");return{kind:"ForRange",id:this.id(),variable:r,from:n,to:s,step:i,body:c,span:this.spanFrom(e)}}parseRepeat(){let e=this.advance();if(this.check("TIMES")||this.check("LBRACE"))this.error("REPETIR_SIN_CANTIDAD","Falta cuántas veces se repite.",this.current.span,"La forma completa es `repetir 4 veces { … }`.");let t=this.parseExpression();this.expect("TIMES","después de la cantidad de `repetir`","La forma completa es `repetir 4 veces { … }`.");let r=this.parseLoopBody("después de `repetir … veces`");return{kind:"Repeat",id:this.id(),count:t,body:r,span:this.spanFrom(e.span.start)}}parseDomain(){let e=this.advance(),t=this.parseBindingName("después de `dominio`"),r=this.expect("LBRACE",`después de \`dominio ${t.name}\``,"Lo que agrupa un dominio va entre llaves: `dominio calculadora { … }`."),a=this.loopDepth;this.loopDepth=0;let n;try{n=this.parseStatements("RBRACE")}finally{this.loopDepth=a}if(!this.check("RBRACE"))this.error("BLOQUE_SIN_CERRAR",`Falta \`}\` para cerrar el dominio que empieza en la línea ${r.span.start.line}.`,r.span);this.advance();for(let i of n){if(vt.has(i.kind)||this.errors.length>=Ee)continue;this.errors.push(new P({kind:"sintaxis",code:"DOMINIO_SOLO_DECLARACIONES",message:we(i)?`Un dominio solo agrupa declaraciones, y \`${we(i)}\` no es una.`:"Un dominio solo agrupa declaraciones, y esta sentencia no es una.",hint:"Dentro de `dominio` van `variable`, `constante`, `procedimiento` u otro `dominio`. Lo demás va en un procedimiento o fuera del dominio.",span:i.span}))}let s={kind:"Block",id:this.id(),statements:n,span:this.spanFrom(r.span.start)};return{kind:"Domain",id:this.id(),name:t,body:s,span:this.spanFrom(e.span.start)}}parseForEach(){let e=this.advance(),t="La forma completa es `para_cada fruta en frutas { … }`, sin paréntesis.";if(this.check("LPAREN"))this.error("TOKEN_ESPERADO","`para_cada` no lleva paréntesis.",this.current.span,"La forma completa es `para_cada fruta en frutas { … }`, sin paréntesis.");let r=this.parseBindingName("después de `para_cada`"),a;if(this.match("COMMA")){if(a=this.parseBindingName(`después de \`para_cada ${r.name},\``),a.name===r.name)this.error("PARÁMETRO_REPETIDO",`La variable \`${a.name}\` está repetida.`,a.span)}let n=a?`${r.name}, ${a.name}`:r.name;this.expect("IN",`después de \`para_cada ${n}\``,"La forma completa es `para_cada fruta en frutas { … }`, sin paréntesis.");let s=this.parseExpression(),i=this.parseLoopBody("después de `para_cada … en …`");return{kind:"ForEach",id:this.id(),variable:r,value:a,iterable:s,body:i,span:this.spanFrom(e.span.start)}}parseLoopBody(e){this.loopDepth+=1;try{return this.parseBlock(e)}finally{this.loopDepth-=1}}parseLoopJump(){let e=this.advance(),t=e.type==="BREAK"?"Break":"Continue";if(this.loopDepth===0)this.error(t==="Break"?"ROMPER_FUERA_DE_BUCLE":"CONTINUAR_FUERA_DE_BUCLE",`\`${e.lexeme}\` solo se puede usar dentro de un bucle.`,e.span,t==="Break"?"Para salir de un procedimiento usa `regresa`.":"Dentro de un procedimiento, `continuar` no afecta al bucle desde donde se llamó.");return{kind:t,id:this.id(),span:e.span}}parseExpression(e=0,t=!1){this.enter();let r=this.parsePrefix();while(e<(Z[this.current.type]??0)){if(this.grouping===0&&this.current.span.start.line>this.previous.span.end.line)break;r=this.parseInfix(r)}if(!t&&$e[this.current.type])this.incrementInExpression(r);return this.leave(),r}incrementInExpression(e){let t=this.current.lexeme,r=e?.kind==="Identifier"?e.name:"i";this.error("INCREMENTO_EN_EXPRESIÓN",`\`${t}\` es una sentencia: va sola, justo después del nombre de una variable.`,this.current.span,e?.kind==="Identifier"||!e?`Escribe \`${r}${t}\` en su propia línea y después usa \`${r}\`.`:`Escribe el \`${t}\` en su propia línea, antes o después de usar el valor.`)}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.grouped(()=>this.parseExpression());return this.expect("RPAREN","para cerrar el paréntesis"),t}case"LBRACKET":return this.parseList();case"LBRACE":return this.parseDict();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":if(this.peek().type==="IDENT")this.error("PROCEDIMIENTO_CON_NOMBRE","Un procedimiento con nombre es una sentencia; no puede ir dentro de una expresión.",this.peek().span,"Como valor, escríbelo sin nombre: `variable doble = procedimiento(x) { … }`.");return this.parseFunction(this.advance().span.start);case"INCREMENT":case"DECREMENT":if(this.peek().type==="IDENT"){let t=this.peek().lexeme;this.error("INCREMENTO_EN_EXPRESIÓN",`En Codexivo \`${e.lexeme}\` va después del nombre de la variable.`,{start:e.span.start,end:this.peek().span.end},`Escribe \`${t}${e.lexeme}\` como una sentencia propia.`)}this.incrementInExpression();case"ILLEGAL":throw new K;case"IF":case"WHILE":case"FOR":case"DO":case"LET":case"CONST":case"DOMAIN":case"REPEAT":case"FOR_EACH":case"BREAK":case"CONTINUE":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 a=this.parseList_("RPAREN","para cerrar los argumentos");return{kind:"Call",id:this.id(),callee:e,args:a,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 a=this.grouped(()=>this.parseExpression());return this.expect("RBRACKET","para cerrar el índice"),{kind:"Index",id:this.id(),object:e,index:a,span:this.spanFrom(r)}}case"DOT":{this.advance();let a=this.current;if(a.type!=="IDENT"){if(this.failSilentlyOnIllegal(),a.type!=="EOF")this.advance();this.error("CAMPO_ESPERADO",`Después de \`.\` va el nombre de un campo, pero se encontró ${B(a)}.`,a.span,'Con `.` solo se usan claves que son nombres válidos; para las demás escribe `d["clave"]`.')}this.advance();let n={kind:"StringLiteral",id:this.id(),value:a.lexeme,span:a.span};return{kind:"Index",id:this.id(),object:e,index:n,dot:!0,span:this.spanFrom(r)}}case"AND":case"OR":{this.advance();let a=Z[t.type],n=this.parseExpression(a),s=t.type==="AND"?"y":"o";return{kind:"Logical",id:this.id(),operator:s,left:e,right:n,span:this.spanFrom(r)}}default:{this.advance();let a=Z[t.type],n=this.parseExpression(a===8?a-1:a),s=Et[t.type];if(a===4&&Z[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:s,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)}}parseDict(){let e=this.advance(),t=[],r=new Set;while(!this.check("RBRACE")){let a=this.current.span.start;if(this.check("IDENT")&&this.peek().type==="COLON")this.error("CLAVE_SIN_COMILLAS",`La clave \`${this.current.lexeme}\` necesita comillas.`,this.current.span,`Escribe \`"${this.current.lexeme}": …\`. Si querías usar el valor de una variable como clave, ponla entre paréntesis: \`(${this.current.lexeme}): …\`.`);let n=this.grouped(()=>this.parseExpression());this.expect("COLON","después de la clave","Cada elemento de un diccionario se escribe `clave: valor`.");let s=this.grouped(()=>this.parseExpression());if(n.kind==="StringLiteral"||n.kind==="NumberLiteral"){if(r.has(n.value))this.error("CLAVE_REPETIDA",`La clave ${Rt(n.value)} está repetida.`,n.span);if(r.add(n.value),s.kind==="FunctionLiteral"&&!s.name&&n.kind==="StringLiteral")s.name=n.value}if(t.push({key:n,value:s,span:this.spanFrom(a)}),!this.match("COMMA"))break}if(!this.check("RBRACE"))this.failSilentlyOnIllegal(),this.error("TOKEN_ESPERADO",`Se esperaba \`,\` o \`}\` en el diccionario que empieza en la línea ${e.span.start.line}, pero se encontró ${B(this.current)}.`,this.current.span);return this.advance(),{kind:"DictLiteral",id:this.id(),entries:t,span:this.spanFrom(e.span.start)}}parseList_(e,t){let r=[];while(!this.check(e))if(r.push(this.grouped(()=>this.parseExpression())),!this.match("COMMA"))break;return this.expect(e,t),r}parseFunctionDeclaration(){let e=this.advance().span.start,t=this.parseBindingName("después de `procedimiento`"),r=this.parseFunction(e,t.name);return{kind:"FunctionDeclaration",id:this.id(),name:t,function:r,span:r.span}}parseFunction(e,t){this.expect("LPAREN","después de `procedimiento`","Los parámetros van entre paréntesis: `procedimiento(a, b) { … }`.");let r=[],a=new Set;while(!this.check("RPAREN")){let i=this.parseBindingName("como parámetro");if(a.has(i.name))this.error("PARÁMETRO_REPETIDO",`El parámetro \`${i.name}\` está repetido.`,i.span);if(a.add(i.name),r.push(i),!this.match("COMMA"))break}this.expect("RPAREN","para cerrar los parámetros");let n=this.loopDepth;this.loopDepth=0;let s;try{s=this.parseBlock("para el cuerpo del procedimiento")}finally{this.loopDepth=n}return{kind:"FunctionLiteral",id:this.id(),name:t,params:r,body:s,span:this.spanFrom(e)}}}var Rt=(e)=>typeof e==="string"?JSON.stringify(e):String(e);var te=(e)=>{let t=new be({name:"programa",params:[],code:[],node:e},!1);return t.statements(e.statements),t.emit({op:"halt"}),t.chunk},ee=(e,t=!1)=>{let r={name:e.name??"procedimiento",params:e.params.map((n)=>n.name),code:[],node:e};if(t)r.hidden=!0;let a=new be(r,t);return a.statements(e.body.statements),a.emit({op:"const",value:null,node:e}),a.emit({op:"return",hasValue:!0,node:{kind:"Return",id:e.id,span:e.body.span}}),a.chunk},Nt={"+=":"+","-=":"-","*=":"*","/=":"/","%=":"mod"};class be{chunk;hidden;scopeDepth=0;loops=[];constructor(e,t){this.chunk=e;this.hidden=t}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}enterScope(){this.emit({op:"enterScope"}),this.scopeDepth+=1}exitScope(){this.emit({op:"exitScope"}),this.scopeDepth-=1}statements(e){for(let t of e){if(t.kind!=="FunctionDeclaration")continue;this.emit({op:"closure",chunk:ee(t.function,this.hidden),node:t.function}),this.emit({op:"declare",name:t.name.name,constant:!1,target:t.name,node:t})}for(let t of e)this.statement(t)}loop(e,t){let r={breaks:[],continues:[],scopeDepth:this.scopeDepth,slots:e};return this.loops.push(r),t(),this.loops.pop(),r}connect(e,t){for(let r of e)this.patch(r,t)}statement(e){if(e.kind==="FunctionDeclaration")return;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,constant:e.constant,target:e.name,node:e});return;case"Assignment":case"Increment":this.assignment(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"Domain":this.emit({op:"domainStart"}),this.scopeDepth+=1,this.statements(e.body.statements),this.scopeDepth-=1,this.emit({op:"domainEnd",name:e.name.name,writable:e.body.statements.flatMap((t)=>t.kind==="VariableDeclaration"&&!t.constant?[t.name.name]:[]),node:e});return;case"Break":case"Continue":{let t=this.loops[this.loops.length-1];for(let r=this.scopeDepth;r>t.scopeDepth;r--)this.emit({op:"exitScope"});if(e.kind==="Break"){if(t.slots>0)this.emit({op:"pop",count:t.slots});t.breaks.push(this.emit({op:"jump",target:-1}))}else t.continues.push(this.emit({op:"jump",target:-1}));return}case"If":{let t=[];if(e.branches.forEach((r,a)=>{this.expression(r.condition);let n=this.emit({op:"branch",target:-1,jumpWhen:!1,label:a===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}),a=this.loop(0,()=>this.block(e.body));this.emit({op:"jump",target:t}),this.patch(r,this.here),this.connect(a.continues,t),this.connect(a.breaks,this.here);return}case"DoUntil":{let t=this.here,r=this.loop(0,()=>this.block(e.body));this.connect(r.continues,this.here),this.expression(e.condition),this.emit({op:"branch",target:t,jumpWhen:!1,label:"hasta_que",node:e.condition,owner:e}),this.connect(r.breaks,this.here);return}case"For":{if(this.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});let a=this.loop(0,()=>this.block(e.body));if(this.connect(a.continues,this.here),e.update)this.statement(e.update);if(this.emit({op:"jump",target:t}),r>=0)this.patch(r,this.here);this.connect(a.breaks,this.here),this.exitScope();return}case"Repeat":{this.expression(e.count),this.emit({op:"repeatStart",node:e.count,owner:e});let t=this.here,r=this.emit({op:"repeatNext",target:-1,node:e.count,owner:e}),a=this.loop(1,()=>this.block(e.body));this.emit({op:"jump",target:t}),this.patch(r,this.here),this.connect(a.continues,t),this.connect(a.breaks,this.here);return}case"ForRange":{if(this.expression(e.from),this.expression(e.to),e.step)this.expression(e.step);else this.emit({op:"const",value:1,node:e});this.emit({op:"rangeStart",explicitStep:Boolean(e.step),owner:e});let t=this.here,r=this.emit({op:"rangeNext",target:-1,node:e.to,owner:e}),a=this.loop(4,()=>{this.enterScope(),this.emit({op:"declare",name:e.variable.name,constant:!1,loop:!0,target:e.variable,node:e.variable}),this.statements(e.body.statements),this.exitScope()});this.emit({op:"jump",target:t}),this.patch(r,this.here),this.connect(a.continues,t),this.connect(a.breaks,this.here);return}case"ForEach":{this.expression(e.iterable),this.emit({op:"forEachStart",node:e.iterable,owner:e});let t=this.here,r=this.emit({op:"forEachNext",target:-1,node:e.iterable,owner:e}),a=this.loop(3,()=>{this.enterScope();for(let n of[e.variable,...e.value?[e.value]:[]])this.emit({op:"declare",name:n.name,constant:!1,target:n,node:n});this.statements(e.body.statements),this.exitScope()});this.emit({op:"jump",target:t}),this.patch(r,this.here),this.connect(a.continues,t),this.connect(a.breaks,this.here);return}}}assignment(e){let t=e.kind==="Increment"?e.operator==="++"?"+":"-":e.operator==="="?void 0:Nt[e.operator],r=()=>{if(e.kind==="Increment")this.emit({op:"const",value:1,node:e.target});else this.expression(e.value)},{target:a}=e;if(a.kind==="Identifier"){if(t)this.emit({op:"load",name:a.name,node:a,forUpdate:!0});if(r(),t)this.emit({op:"binary",operator:t,node:e});this.emit({op:"store",name:a.name,target:a,node:e});return}if(this.expression(a.object),this.expression(a.index),t)this.emit({op:"dup",count:2}),this.emit({op:"index",node:a});if(r(),t)this.emit({op:"binary",operator:t,node:e});this.emit({op:"setIndex",target:a,node:e})}block(e){this.enterScope(),this.statements(e.statements),this.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"DictLiteral":for(let t of e.entries)this.expression(t.key),this.expression(t.value);this.emit({op:"dict",count:e.entries.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:ee(e,this.hidden),node:e});return}}}var H=Symbol("esperar-entrada"),b=(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"||e.kind==="diccionario"||e.kind==="dominio"?e.kind:"procedimiento"}},v=(e)=>{switch(e){case"lista":return"una lista";case"nulo":return"`nulo`";default:return`un ${e}`}},y=(e)=>typeof e==="object"&&e!==null&&e.kind==="lista",z=(e)=>typeof e==="object"&&e!==null&&e.kind==="dominio",I=(e)=>typeof e==="object"&&e!==null&&e.kind==="diccionario",re=(e)=>typeof e==="object"&&e!==null&&(e.kind==="procedimiento"||e.kind==="nativo"),Ot=(e)=>{if(Object.is(e,-0))return"0";if(e===1/0)return"infinito";if(e===-1/0)return"-infinito";return String(e)},g=(e,t=new Set)=>{if(e===null)return"nulo";switch(typeof e){case"number":return Ot(e);case"string":return JSON.stringify(e);case"boolean":return e?"verdadero":"falso"}if(e.kind==="dominio")return`dominio ${e.name}`;if(e.kind==="lista"||e.kind==="diccionario"){if(t.has(e))return e.kind==="lista"?"[…]":"{…}";t.add(e);let r=e.kind==="lista"?e.items.map((a)=>g(a,t)):[...e.entries].map(([a,n])=>`${g(a)}: ${g(n,t)}`);return t.delete(e),e.kind==="lista"?`[${r.join(", ")}]`:`{${r.join(", ")}}`}return e.name?`procedimiento ${e.name}`:"procedimiento"},ae=(e)=>typeof e==="string"?e:g(e),V=(e,t,r=[])=>{if(e===t)return!0;let a=y(e)&&y(t),n=I(e)&&I(t);if(!a&&!n)return!1;if(r.some(([i,c])=>i===e&&c===t))return!0;r.push([e,t]);let s;if(a){let[i,c]=[e,t];s=i.items.length===c.items.length&&i.items.every((o,m)=>V(o,c.items[m],r))}else{let[i,c]=[e,t];s=i.entries.size===c.entries.size&&[...i.entries].every(([o,m])=>c.entries.has(o)&&V(m,c.entries.get(o),r))}return r.pop(),s};class U{parent;variables=new Map;constants=new Set;loopVariables=new Set;constructor(e){this.parent=e}lookup(e){let t=this;while(t){if(t.variables.has(e))return t;t=t.parent}return}}var ne=(e)=>{if(typeof e==="string")return e.length;if(e!==void 0&&y(e))return e.items.length;if(e!==void 0&&I(e))return e.entries.size;return 1},R=(e)=>Math.max(1,ne(e[0])),_t=(e)=>{let t=ne(e[0]);return Math.max(1,t*Math.ceil(Math.log2(Math.max(t,1))))},Me=(e)=>(t)=>Math.max(1,Math.ceil(e(t))),Ve=(e)=>e.length===1?R(e):Math.max(1,e.length),Be=(e)=>Math.max(1,ne(e[1])),Ue=(e)=>typeof e[1]==="number"&&typeof e[2]==="number"?Math.max(1,Math.abs(e[2]-e[1])):1,kt=(e)=>{let[t,r,a=1]=e;if(typeof t!=="number"||typeof r!=="number"||typeof a!=="number"||a===0)return 1;return Math.max(1,Math.floor((r-t)/a)+1)},se={máximo:Ve,mínimo:Ve,mayúsculas:R,minúsculas:R,recortar:R,dividir:R,unir:R,reemplazar:R,contiene:R,posición_de:R,empieza_con:Be,termina_con:Be,subtexto:Ue,caracteres:R,invertir:R,insertar:R,quitar:R,quitar_primero:R,desencolar:R,rebanar:Ue,copiar:R,sumar:R,ordenar:_t,rango:kt,índices:R,lista_de:Me((e)=>typeof e[0]==="number"?e[0]*ne(e[1]):1),matriz:Me((e)=>typeof e[0]==="number"&&typeof e[1]==="number"?e[0]*e[1]:1),claves:R,valores:R,mapear:R,filtrar:R,reducir:R,alguno:R,todos:R,buscar:R};var u=(e,t,r,a)=>{let n=se[e];return{kind:"nativo",name:e,arity:t,call:a,doc:r,...n?{cost:n}:{}}},ie=(e,t,r)=>({kind:"nativo",name:e,arity:t,call:r}),D=(e,t,r,a="")=>{if(typeof t==="number")return t;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera un número${a}, pero recibió ${v(b(t))} (${g(t)}).`)},w=(e,t,r)=>{if(y(t))return t;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera una lista, pero recibió ${v(b(t))}.`)},C=(e,t,r,a="")=>{if(typeof t==="string")return t;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera un texto${a}, pero recibió ${v(b(t))} (${g(t)}).`)},ce=(e,t,r,a="")=>{let n=D(e,t,r,a);if(Number.isInteger(n))return n;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera un número entero${a}, pero recibió ${g(n)}.`,"Redondea el valor con `redondear(…)`, `piso(…)` o `techo(…)`.")},ye=(e,t,r,a="")=>{let n=ce(e,t,r,a);if(n>=0&&n<=W)return n;throw r.fail("TIPO_DE_ARGUMENTO",n<0?`\`${e}\` necesita una cantidad de 0 o más${a}, pero recibió ${n}.`:`\`${e}\` produciría ${n} elementos; el máximo es ${W}.`)},It=(e,t,r,a)=>{let n=r?"la lista":"el texto",s=r?t===1?"elemento":"elementos":t===1?"carácter":"caracteres";return a.fail("ÍNDICE_FUERA_DE_RANGO",`El índice ${e} está fuera de ${n}, que tiene ${t} ${s}.`,t===0?r?"La lista está vacía.":"El texto está vacío.":`Los índices válidos van de 0 a ${t-1} (o de -1 a -${t} desde el final).`)},xe=(e,t,r,a,n,s=!1)=>{if(typeof t!=="number"||!Number.isInteger(t))throw n.fail("ÍNDICE_INVÁLIDO",`\`${e}\` necesita un índice entero, pero recibió ${g(t)}.`);let i=t<0?r+t:t;if(i<0||i>r||i===r&&!s)throw It(t,r,a,n);return i},pe=(e,t,r=new Map)=>{if(!y(e)&&!I(e))return e;let a=r.get(e);if(a)return a;if(y(e)){let s=t.newList([]);r.set(e,s);for(let i of e.items)s.items.push(pe(i,t,r));return s}let n=t.newDict(new Map);r.set(e,n);for(let[s,i]of e.entries)n.entries.set(s,pe(i,t,r));return n},Fe=(e,t)=>t.fail("LISTA_VACÍA",`\`${e}\` no puede tomar nada de una lista vacía.`),qe=(e,t,r)=>{if(typeof t==="string")return t;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera un texto o una lista, pero recibió ${v(b(t))}.`)},ze=(e,t,r,a,n)=>{let s=xe(e,t[1],r,a,n,!0),i=xe(e,t[2],r,a,n,!0);if(s>i)throw n.fail("RANGO_INVÁLIDO",`\`${e}\` necesita que el inicio (${g(t[1])}) no esté después del fin (${g(t[2])}).`,'El fin no se incluye: `subtexto("hola", 1, 3)` es `"ol"`.');return[s,i]},At=(e,t)=>{let a=typeof e.items[0]==="string"?"texto":"número";for(let n of e.items)if(b(n)!==a)throw t.fail("TIPO_DE_ARGUMENTO",`\`ordenar\` necesita que todos los elementos sean números o todos textos, pero encontró ${v(b(n))} (${g(n)}).`);return a},G=(e,t,r,a)=>{if(y(r))return r;throw a.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera una ${t} (una lista), pero recibió ${v(b(r))}.`,`Una ${t} empieza como una lista vacía: \`variable ${t} = [];\`.`)},oe=(e,t,r)=>r.fail(t==="pila"?"PILA_VACÍA":"COLA_VACÍA",e==="tope"||e==="frente"?`Una ${t} vacía no tiene \`${e}\`.`:`No se puede \`${e}\` de una ${t} vacía.`,`Revisa antes con \`longitud(${t}) > 0\`.`),ve=(e,t,r)=>{if(I(t))return t;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera un diccionario, pero recibió ${v(b(t))}.`)},Ge=(e,t,r)=>{if(typeof t==="string"||typeof t==="number")return t;throw r.fail("CLAVE_INVÁLIDA",`\`${e}\` espera una clave de texto o número, pero recibió ${v(b(t))}.`)},We=(e,t,r)=>{let a=t.length===1&&y(t[0])?t[0].items:t;if(a.length===0)throw r.fail("LISTA_VACÍA",`\`${e}\` necesita al menos un número, pero la lista está vacía.`);return a.map((n)=>D(e,n,r))},je=(e)=>{let t=e.trim();if(!/^[-+]?(\d+(\.\d*)?|\.\d+)$/.test(t))return;return Number(t)},Tt=(e,t)=>{let r=10**t;return Math.sign(e)*Math.round(Math.abs(e)*r)/r},W=1e7,Ke=[u("imprimir",[0,1/0],{level:"principiante",category:"entrada y salida",params:[{name:"valor",type:"cualquiera",optional:!0}],variadic:!0,returns:"nulo",description:"Escribe los valores separados por un espacio y termina la línea. Los textos se escriben sin comillas.",examples:[{code:'imprimir("Hola,", "Ana", 16)',output:`Hola, Ana 16
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 Wt=(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 fe;((p)=>{p.NUMBER="NUMBER";p.STRING="STRING";p.IDENT="IDENT";p.LET="LET";p.CONST="CONST";p.DOMAIN="DOMAIN";p.FUNCTION="FUNCTION";p.RETURN="RETURN";p.IF="IF";p.ELSEIF="ELSEIF";p.ELSE="ELSE";p.WHILE="WHILE";p.DO="DO";p.UNTIL="UNTIL";p.FOR="FOR";p.FOR_EACH="FOR_EACH";p.FROM="FROM";p.TO="TO";p.STEP="STEP";p.IN="IN";p.REPEAT="REPEAT";p.TIMES="TIMES";p.BREAK="BREAK";p.CONTINUE="CONTINUE";p.TRUE="TRUE";p.FALSE="FALSE";p.NULL="NULL";p.AND="AND";p.OR="OR";p.NOT="NOT";p.MOD="MOD";p.DIV="DIV";p.ASSIGN="ASSIGN";p.PLUS_ASSIGN="PLUS_ASSIGN";p.MINUS_ASSIGN="MINUS_ASSIGN";p.STAR_ASSIGN="STAR_ASSIGN";p.SLASH_ASSIGN="SLASH_ASSIGN";p.PERCENT_ASSIGN="PERCENT_ASSIGN";p.INCREMENT="INCREMENT";p.DECREMENT="DECREMENT";p.PLUS="PLUS";p.MINUS="MINUS";p.STAR="STAR";p.SLASH="SLASH";p.PERCENT="PERCENT";p.CARET="CARET";p.BANG="BANG";p.EQ="EQ";p.NEQ="NEQ";p.LT="LT";p.GT="GT";p.LTE="LTE";p.GTE="GTE";p.COMMA="COMMA";p.COLON="COLON";p.DOT="DOT";p.SEMICOLON="SEMICOLON";p.LPAREN="LPAREN";p.RPAREN="RPAREN";p.LBRACE="LBRACE";p.RBRACE="RBRACE";p.LBRACKET="LBRACKET";p.RBRACKET="RBRACKET";p.EOF="EOF";p.ILLEGAL="ILLEGAL"})(fe||={});var X={variable:"LET",constante:"CONST",dominio:"DOMAIN",procedimiento:"FUNCTION",regresa:"RETURN",si:"IF",pero_si:"ELSEIF",si_no:"ELSE",mientras:"WHILE",hacer:"DO",hasta_que:"UNTIL",para:"FOR",para_cada:"FOR_EACH",desde:"FROM",hasta:"TO",con_paso:"STEP",en:"IN",repetir:"REPEAT",veces:"TIMES",romper:"BREAK",continuar:"CONTINUE",verdadero:"TRUE",falso:"FALSE",nulo:"NULL",y:"AND",o:"OR",no:"NOT",mod:"MOD",div:"DIV",es_igual_a:"EQ",es_distinto_de:"NEQ",es_menor_que:"LT",es_mayor_que:"GT",es_menor_o_igual_a:"LTE",es_mayor_o_igual_a:"GTE"},ct=(e)=>Object.hasOwn(X,e),pt={["ASSIGN"]:"=",["PLUS_ASSIGN"]:"+=",["MINUS_ASSIGN"]:"-=",["STAR_ASSIGN"]:"*=",["SLASH_ASSIGN"]:"/=",["PERCENT_ASSIGN"]:"%=",["INCREMENT"]:"++",["DECREMENT"]:"--",["PLUS"]:"+",["MINUS"]:"-",["STAR"]:"*",["SLASH"]:"/",["PERCENT"]:"%",["CARET"]:"^",["BANG"]:"!",["EQ"]:"==",["NEQ"]:"!=",["LT"]:"<",["GT"]:">",["LTE"]:"<=",["GTE"]:">=",["COMMA"]:",",["COLON"]:":",["DOT"]:".",["SEMICOLON"]:";",["LPAREN"]:"(",["RPAREN"]:")",["LBRACE"]:"{",["RBRACE"]:"}",["LBRACKET"]:"[",["RBRACKET"]:"]"},lt=Object.fromEntries(Object.entries(X).map(([e,t])=>[t,e])),ge=(e)=>{let t=pt[e];if(t)return`\`${t}\``;let r=lt[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 ct(e.lexeme)?`la palabra \`${e.lexeme}\``:ge(e.type)}};var Y=/[A-Za-z_À-ÖØ-öø-ÿ]/,q=/[0-9]/,ut=/\s/,dt={"==":"EQ","!=":"NEQ","<=":"LTE",">=":"GTE","+=":"PLUS_ASSIGN","-=":"MINUS_ASSIGN","*=":"STAR_ASSIGN","/=":"SLASH_ASSIGN","%=":"PERCENT_ASSIGN","++":"INCREMENT","--":"DECREMENT"},mt={"=":"ASSIGN","+":"PLUS","-":"MINUS","*":"STAR","/":"SLASH","%":"PERCENT","^":"CARET","!":"BANG","<":"LT",">":"GT",",":"COMMA",":":"COLON",".":"DOT",";":"SEMICOLON","(":"LPAREN",")":"RPAREN","{":"LBRACE","}":"RBRACE","[":"LBRACKET","]":"RBRACKET"},ht=new Set(["IDENT","NUMBER","STRING","RPAREN","RBRACKET","RBRACE"]),ft={n:`
3
+ `,t:"\t","\\":"\\",'"':'"',"'":"'"},gt={"&":"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 `'`."},Ce=(e)=>{let t=[],r=[],a=0,n=1,s=1,i=()=>({line:n,column:s,offset:a}),c=(l=0)=>e[a+l]??"",o=()=>{let l=e[a]??"";if(a+=1,l===`
4
+ `)n+=1,s=1;else s+=1;return l},m=(l,d,O)=>{let A={type:l,lexeme:e.slice(d.offset,a),span:{start:d,end:i()}};if(O!==void 0)A.value=O;return t.push(A),A},x=(l,d,O,A)=>{r.push(new V({kind:"léxico",code:l,message:d,hint:A,span:O}))};while(a<e.length){let l=c();if(ut.test(l)){o();continue}let d=i();if(l==="/"&&c(1)==="/"){while(a<e.length&&c()!==`
5
+ `)o();continue}if(l==="/"&&c(1)==="*"){o(),o();while(a<e.length&&!(c()==="*"&&c(1)==="/"))o();if(a>=e.length)x("COMENTARIO_SIN_CERRAR","El comentario que empieza aquí no se cerró.",{start:d,end:{line:d.line,column:d.column+2,offset:d.offset+2}},"Cierra el comentario con `*/`.");else o(),o();continue}let O=t[t.length-1],A=O!==void 0&&ht.has(O.type)&&O.span.end.offset===a;if(q.test(l)||l==="."&&q.test(c(1))&&!A){let T=()=>{while(q.test(c())||c()==="_")o()};if(T(),c()==="."&&q.test(c(1)))o(),T();if(Y.test(c())&&c()!=="_"){while(Y.test(c())||q.test(c()))o();let P=m("ILLEGAL",d);x("NOMBRE_EMPIEZA_CON_NÚMERO",`\`${P.lexeme}\` no es un número ni un nombre válido.`,P.span,"Los nombres no pueden empezar con un dígito. Si es una multiplicación, escribe el operador: `2 * x`.");continue}let _=e.slice(d.offset,a);if(/(^|\D)_|_(\D|$)/.test(_)){let P=m("ILLEGAL",d);x("SEPARADOR_INVÁLIDO",`\`${_}\` no es un número válido.`,P.span,"El `_` solo puede ir entre dos dígitos para separar grupos, como en `1_000_000`.");continue}m("NUMBER",d,Number(_.replaceAll("_","")));continue}if(Y.test(l)){while(Y.test(c())||q.test(c()))o();let T=e.slice(d.offset,a);m(X[T]??"IDENT",d);continue}if(l==='"'||l==="'"){let T=o(),_="",P=!1;while(a<e.length&&c()!==`
6
+ `){let me=o();if(me===T){P=!0;break}if(me==="\\"){let ot={line:n,column:s-1,offset:a-1},he=o(),Le=ft[he];if(Le===void 0)x("ESCAPE_DESCONOCIDO",`\`\\${he}\` no es una secuencia de escape válida.`,{start:ot,end:i()},"Las secuencias válidas son `\\n`, `\\t`, `\\\\`, `\\\"` y `\\'`."),_+=he;else _+=Le;continue}_+=me}let it=m("STRING",d,_);if(!P)x("TEXTO_SIN_CERRAR","El texto que empieza aquí no se cerró.",it.span,`Agrega la comilla \`${T}\` al final. Un texto no puede ocupar varias líneas; usa \`\\n\` para un salto de línea.`);continue}let g=l+c(1),S=dt[g];if(S){o(),o(),m(S,d);continue}let b=mt[l];if(b){o(),m(b,d);continue}o();let N=m("ILLEGAL",d);x("CARÁCTER_DESCONOCIDO",`Codexivo no reconoce el carácter \`${l}\`.`,N.span,gt[l])}let h=i();return t.push({type:"EOF",lexeme:"",span:{start:h,end:h}}),{tokens:t,errors:r}};var U=(e,t={})=>{let{tokens:r,errors:a}=Ce(e),n=new Pe(r,t.trace?[]:void 0),s=n.parseProgram(),i=[...a,...n.errors].sort((c,o)=>(c.span?.start.offset??0)-(o.span?.start.offset??0));return n.trace?{program:s,tokens:r,errors:i,trace:n.trace}:{program:s,tokens:r,errors:i}},tr=(e)=>U(e,{trace:!0});var Z={OR:1,AND:2,EQ:4,NEQ:4,LT:4,GT:4,LTE:4,GTE:4,PLUS:5,MINUS:5,STAR:6,SLASH:6,PERCENT:6,MOD:6,DIV:6,CARET:8,LPAREN:9,LBRACKET:9,DOT:9},bt={PLUS:"+",MINUS:"-",STAR:"*",SLASH:"/",PERCENT:"mod",MOD:"mod",DIV:"div",CARET:"^",EQ:"==",NEQ:"!=",LT:"<",GT:">",LTE:"<=",GTE:">="},Et={ASSIGN:"=",PLUS_ASSIGN:"+=",MINUS_ASSIGN:"-=",STAR_ASSIGN:"*=",SLASH_ASSIGN:"/=",PERCENT_ASSIGN:"%="},De={INCREMENT:"++",DECREMENT:"--"},yt=new Set(["LET","DOMAIN","CONST","REPEAT","FOR_EACH","BREAK","CONTINUE","FUNCTION","RETURN","IF","WHILE","DO","FOR"]),vt=new Set(["VariableDeclaration","FunctionDeclaration","Domain"]),$e=(e)=>{switch(e.kind){case"If":return"si";case"While":return"mientras";case"DoUntil":return"hacer";case"For":return"para";case"ForEach":return"para_cada";case"Repeat":return"repetir";case"Return":return"regresa";case"Break":return"romper";case"Continue":return"continuar";case"Block":return"{";default:return}},xt={sino:"En Codexivo se escribe `si_no`, con guion bajo.",pero:"En Codexivo se escribe `pero_si`, con guion bajo."},we=50,St=400;class K{}class Pe{tokens;trace;errors=[];position=0;nextId=1;depth=0;grouping=0;loopDepth=0;open=[];constructor(e,t){this.tokens=e;this.trace=t}parseProgram(){let e=this.current.span.start;this.begin("Program");let t=this.parseStatements("EOF");return this.finish({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")return e;return this.trace?.push({type:"consume",index:this.position,tokenType:e.type,lexeme:e.lexeme,span:e.span}),this.position+=1,e}skip(){if(!this.check("EOF"))this.position+=1}begin(e,t,r=this.current.span){if(!this.trace)return;this.open.push(e),this.trace.push(t?{type:"enter",kind:e,left:t.id,span:r}:{type:"enter",kind:e,span:r})}finish(e){if(!this.trace)return e;return this.open.pop(),this.trace.push({type:"exit",kind:e.kind,id:e.id,span:e.span}),e}leaf(e,t){this.begin(e);let r=this.advance();return this.finish(t(r,this.id()))}tracePrecedence(e,t,r,a,n){this.trace.push({type:"precedence",operand:e.id,...r?{building:{operator:r.lexeme,precedence:t}}:{},incoming:{operator:this.current.lexeme,precedence:a},decision:n?"se_lo_lleva":"se_queda",span:this.current.span})}traceRecover(e,t){if(!this.trace)return;let r=this.position,a=r>t?{start:this.tokens[t].span.start,end:this.tokens[r-1].span.end}:{start:this.current.span.start,end:this.current.span.start},n=this.open.splice(e).reverse();this.trace.push({type:"recover",abandoned:n,skipped:{from:t,to:r},span:a})}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,a){throw this.report(e,t,r,a),new K}report(e,t,r,a){if(this.errors.length>=we)return;this.errors.push(new V({kind:"sintaxis",code:e,message:t,hint:a,span:r})),this.trace?.push({type:"error",code:e,message:t,span:r})}failSilentlyOnIllegal(){if(this.check("ILLEGAL"))throw new K}expect(e,t,r){if(this.check(e))return this.advance();this.failSilentlyOnIllegal();let a=this.current,n=r;if(e==="RPAREN"&&a.type==="ASSIGN")n="Para comparar se usa `es_igual_a` (o `==`); un solo `=` sirve para asignar.";this.error("TOKEN_ESPERADO",`Se esperaba ${ge(e)} ${t}, pero se encontró ${B(a)}.`,a.span,n)}enter(){if(this.depth+=1,this.depth>St)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.skip();while(!this.check("EOF")){if(this.previous.type==="SEMICOLON")return;if(this.check("RBRACE"))return;if(yt.has(this.current.type))return;if(this.current.span.start.line>e.span.start.line)return;this.skip()}}parseStatements(e){let t=this.grouping;this.grouping=0;try{return this.parseStatementList(e)}finally{this.grouping=t}}parseStatementList(e){let t=[],r=!1;while(!this.check(e)&&!this.check("EOF")){let a=this.current,n=this.open.length;try{if(e==="EOF"&&this.check("RBRACE")){if(r){let s=this.position;this.skip(),this.traceRecover(n,s);continue}this.error("LLAVE_SOBRANTE","Sobra esta `}`: no hay ningún bloque abierto.",this.current.span)}t.push(this.parseStatement()),r=!1}catch(s){if(!(s instanceof K))throw s;this.depth=0,r=!0;let i=this.position;this.synchronize(a),this.traceRecover(n,i)}}return this.checkDuplicateProcedures(t),t}checkDuplicateProcedures(e){let t=new Set(e.flatMap((a)=>a.kind==="FunctionDeclaration"?[a.name.name]:[]));if(t.size===0)return;let r=new Set;for(let a of e){if(a.kind!=="FunctionDeclaration"&&a.kind!=="VariableDeclaration")continue;let{name:n}=a;if(!t.has(n.name))continue;if(a.kind==="FunctionDeclaration"&&!r.has(n.name)){r.add(n.name);continue}if(this.errors.length>=we)return;this.report("NOMBRE_REPETIDO",`Ya hay un procedimiento llamado \`${n.name}\` en este bloque.`,n.span,"Un procedimiento con nombre existe desde el inicio de su bloque; cambia uno de los dos nombres.")}}parseStatement(){let e=this.current;switch(e.type){case"SEMICOLON":return this.advance(),this.parseStatementAfterStraySemicolon();case"LET":case"CONST":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"REPEAT":return this.endCompoundStatement(this.parseRepeat());case"FOR_EACH":return this.endCompoundStatement(this.parseForEach());case"DOMAIN":return this.endCompoundStatement(this.parseDomain());case"BREAK":case"CONTINUE":return this.endSimpleStatement(this.parseLoopJump());case"IN":this.error("EN_SIN_PARA_CADA","`en` solo se usa dentro de `para_cada … en …`.",e.span);case"FUNCTION":if(this.peek().type==="IDENT")return this.endCompoundStatement(this.parseFunctionDeclaration());return this.endSimpleStatement(this.parseSimpleStatement());case"LBRACE":if((this.peek().type==="STRING"||this.peek().type==="NUMBER")&&this.peek(2).type==="COLON")this.error("DICCIONARIO_COMO_SENTENCIA","Al inicio de una sentencia, `{` abre un bloque, no un diccionario.",e.span,'Guarda el diccionario en una variable: `variable d = { "clave": valor }`.');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=xt[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);return this.endSimpleStatement(this.parseSimpleStatement())}default:return this.endSimpleStatement(this.parseSimpleStatement())}}parseStatementAfterStraySemicolon(){if(this.check("EOF")||this.check("RBRACE")){let e=this.previous.span;return this.begin("Block",void 0,e),this.finish({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(){this.begin("VariableDeclaration");let e=this.advance(),t=e.type==="CONST",r=this.parseBindingName(`después de \`${e.lexeme}\``);this.expect("ASSIGN",`después de \`${e.lexeme} ${r.name}\``,t?`Toda constante necesita su valor desde el principio: \`constante ${r.name} = 10\`.`:`Toda variable empieza con un valor: \`variable ${r.name} = 0\`.`);let a=this.parseExpression();if(a.kind==="FunctionLiteral"&&!a.name)a.name=r.name;return this.finish({kind:"VariableDeclaration",id:this.id(),constant:t,name:r,value:a,span:this.spanFrom(e.span.start)})}parseBindingName(e){let t=this.current;if(t.type!=="IDENT"){if(this.failSilentlyOnIllegal(),t.lexeme&&/^[a-z_]+$/.test(t.lexeme)&&t.type!=="EOF")this.advance(),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.leaf("Identifier",(r,a)=>({kind:"Identifier",id:a,name:r.lexeme,span:r.span}))}parseSimpleStatement(){let e=this.current.span.start,t=this.parseExpression(0,!0),r=Et[this.current.type],a=De[this.current.type];if(!r&&!a)return this.begin("ExpressionStatement",t,t.span),this.finish({kind:"ExpressionStatement",id:this.id(),expression:t,span:this.spanFrom(e)});if(t.kind!=="Identifier"&&t.kind!=="Index")this.error("ASIGNACIÓN_INVÁLIDA",a?`\`${this.current.lexeme}\` solo funciona con una variable o un elemento, como \`i${this.current.lexeme}\` o \`lista[0]${this.current.lexeme}\`.`:"Solo se le puede asignar un valor a una variable o a un elemento, como `x = 1` o `lista[0] = 1`.",this.current.span);if(this.begin(a?"Increment":"Assignment",t),this.advance(),a)return this.finish({kind:"Increment",id:this.id(),target:t,operator:a,span:this.spanFrom(e)});let n=this.parseExpression();if(n.kind==="FunctionLiteral"&&!n.name&&t.kind==="Identifier")n.name=t.name;return this.finish({kind:"Assignment",id:this.id(),target:t,operator:r,value:n,span:this.spanFrom(e)})}parseReturn(){this.begin("Return");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 this.finish({kind:"Return",id:this.id(),value:r,span:this.spanFrom(e.span.start)})}parseBlock(e){this.begin("Block");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(),this.finish({kind:"Block",id:this.id(),statements:r,span:this.spanFrom(t.span.start)})}grouped(e){this.grouping+=1;try{return e()}finally{this.grouping-=1}}parseCondition(e){this.expect("LPAREN",`después de \`${e}\``,`La condición va entre paréntesis: \`${e} (x > 0)\`.`);let t=this.grouped(()=>this.parseExpression());return this.expect("RPAREN",`para cerrar la condición de \`${e}\``),t}parseIf(){let e=this.current.span.start;this.begin("If");let t=[],r="si";do{let n=this.advance().span.start,s=this.parseCondition(r),i=this.parseBlock(`después de la condición de \`${r}\``);t.push({condition:s,body:i,span:this.spanFrom(n)}),r="pero_si"}while(this.check("ELSEIF"));let a;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 (…)`.");a=this.parseBlock("después de `si_no`")}return this.finish({kind:"If",id:this.id(),branches:t,alternative:a,span:this.spanFrom(e)})}parseWhile(){this.begin("While");let e=this.advance().span.start,t=this.parseCondition("mientras"),r=this.parseLoopBody("después de la condición de `mientras`");return this.finish({kind:"While",id:this.id(),condition:t,body:r,span:this.spanFrom(e)})}parseDoUntil(){this.begin("DoUntil");let e=this.advance().span.start,t=this.parseLoopBody("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 this.finish({kind:"DoUntil",id:this.id(),body:t,condition:r,span:this.spanFrom(e)})}parseFor(){this.begin(this.peek().type==="LPAREN"?"For":"ForRange");let e=this.advance().span.start;if(!this.check("LPAREN"))return this.parseForRange(e);this.expect("LPAREN","después de `para`","La forma completa es `para (variable i = 0; i < 10; i += 1) { … }`.");let{init:t,condition:r,update:a}=this.grouped(()=>{let s;if(!this.check("SEMICOLON"))if(this.check("LET")||this.check("CONST"))s=this.parseVariableDeclaration();else s=this.parseSimpleStatement();this.expect("SEMICOLON","después del inicio del `para`");let i=this.check("SEMICOLON")?void 0:this.parseExpression();this.expect("SEMICOLON","después de la condición del `para`");let c;if(!this.check("RPAREN"))c=this.parseSimpleStatement();return this.expect("RPAREN","para cerrar el `para`"),{init:s,condition:i,update:c}}),n=this.parseLoopBody("después de `para (…)`");return this.finish({kind:"For",id:this.id(),init:t,condition:r,update:a,body:n,span:this.spanFrom(e)})}parseForRange(e){if(this.check("LET"))this.advance(),this.error("TOKEN_ESPERADO","La variable de `para … desde … hasta` se escribe sin `variable`.",this.current.span,"La forma completa es `para i desde 1 hasta 10 { … }`.");let r=this.parseBindingName("después de `para`");if(this.check("ASSIGN"))this.error("TOKEN_ESPERADO",`Para contar se escribe \`desde\`, no \`=\`: \`para ${r.name} desde 1 hasta 10\`.`,this.current.span,"La forma clásica `para (variable i = 0; i < 10; i++)` va entre paréntesis.");let a=(o)=>{if(this.check("LBRACE"))this.error("EXPRESIÓN_ESPERADA",`Falta un valor después de \`${o}\`.`,this.current.span,"La forma completa es `para i desde 1 hasta 10 { … }`.");return this.parseExpression()};this.expect("FROM",`después de \`para ${r.name}\``,"La forma completa es `para i desde 1 hasta 10 { … }`.");let n=a("desde");this.expect("TO","después del inicio del `para`","La forma completa es `para i desde 1 hasta 10 { … }`.");let s=a("hasta"),i=this.match("STEP")?a("con_paso"):void 0,c=this.parseLoopBody("después de `para … desde … hasta …`");return this.finish({kind:"ForRange",id:this.id(),variable:r,from:n,to:s,step:i,body:c,span:this.spanFrom(e)})}parseRepeat(){this.begin("Repeat");let e=this.advance();if(this.check("TIMES")||this.check("LBRACE"))this.error("REPETIR_SIN_CANTIDAD","Falta cuántas veces se repite.",this.current.span,"La forma completa es `repetir 4 veces { … }`.");let t=this.parseExpression();this.expect("TIMES","después de la cantidad de `repetir`","La forma completa es `repetir 4 veces { … }`.");let r=this.parseLoopBody("después de `repetir … veces`");return this.finish({kind:"Repeat",id:this.id(),count:t,body:r,span:this.spanFrom(e.span.start)})}parseDomain(){this.begin("Domain");let e=this.advance(),t=this.parseBindingName("después de `dominio`");this.begin("Block");let r=this.expect("LBRACE",`después de \`dominio ${t.name}\``,"Lo que agrupa un dominio va entre llaves: `dominio calculadora { … }`."),a=this.loopDepth;this.loopDepth=0;let n;try{n=this.parseStatements("RBRACE")}finally{this.loopDepth=a}if(!this.check("RBRACE"))this.error("BLOQUE_SIN_CERRAR",`Falta \`}\` para cerrar el dominio que empieza en la línea ${r.span.start.line}.`,r.span);this.advance();for(let i of n){if(vt.has(i.kind))continue;this.report("DOMINIO_SOLO_DECLARACIONES",$e(i)?`Un dominio solo agrupa declaraciones, y \`${$e(i)}\` no es una.`:"Un dominio solo agrupa declaraciones, y esta sentencia no es una.",i.span,"Dentro de `dominio` van `variable`, `constante`, `procedimiento` u otro `dominio`. Lo demás va en un procedimiento o fuera del dominio.")}let s=this.finish({kind:"Block",id:this.id(),statements:n,span:this.spanFrom(r.span.start)});return this.finish({kind:"Domain",id:this.id(),name:t,body:s,span:this.spanFrom(e.span.start)})}parseForEach(){this.begin("ForEach");let e=this.advance(),t="La forma completa es `para_cada fruta en frutas { … }`, sin paréntesis.";if(this.check("LPAREN"))this.error("TOKEN_ESPERADO","`para_cada` no lleva paréntesis.",this.current.span,t);let r=this.parseBindingName("después de `para_cada`"),a;if(this.match("COMMA")){if(a=this.parseBindingName(`después de \`para_cada ${r.name},\``),a.name===r.name)this.error("PARÁMETRO_REPETIDO",`La variable \`${a.name}\` está repetida.`,a.span)}let n=a?`${r.name}, ${a.name}`:r.name;this.expect("IN",`después de \`para_cada ${n}\``,t);let s=this.parseExpression(),i=this.parseLoopBody("después de `para_cada … en …`");return this.finish({kind:"ForEach",id:this.id(),variable:r,value:a,iterable:s,body:i,span:this.spanFrom(e.span.start)})}parseLoopBody(e){this.loopDepth+=1;try{return this.parseBlock(e)}finally{this.loopDepth-=1}}parseLoopJump(){let e=this.check("BREAK")?"Break":"Continue";this.begin(e);let t=this.advance();if(this.loopDepth===0)this.error(e==="Break"?"ROMPER_FUERA_DE_BUCLE":"CONTINUAR_FUERA_DE_BUCLE",`\`${t.lexeme}\` solo se puede usar dentro de un bucle.`,t.span,e==="Break"?"Para salir de un procedimiento usa `regresa`.":"Dentro de un procedimiento, `continuar` no afecta al bucle desde donde se llamó.");return this.finish({kind:e,id:this.id(),span:t.span})}parseExpression(e=0,t=!1,r){this.enter();let a=this.parsePrefix();while(!0){let n=Z[this.current.type];if(n===void 0)break;if(this.grouping===0&&this.current.span.start.line>this.previous.span.end.line)break;let s=e<n;if(this.trace)this.tracePrecedence(a,e,r,n,s);if(!s)break;a=this.parseInfix(a)}if(!t&&De[this.current.type])this.incrementInExpression(a);return this.leave(),a}incrementInExpression(e){let t=this.current.lexeme,r=e?.kind==="Identifier"?e.name:"i";this.error("INCREMENTO_EN_EXPRESIÓN",`\`${t}\` es una sentencia: va sola, justo después del nombre de una variable.`,this.current.span,e?.kind==="Identifier"||!e?`Escribe \`${r}${t}\` en su propia línea y después usa \`${r}\`.`:`Escribe el \`${t}\` en su propia línea, antes o después de usar el valor.`)}parsePrefix(){let e=this.current;switch(e.type){case"NUMBER":return this.leaf("NumberLiteral",(t,r)=>({kind:"NumberLiteral",id:r,value:e.value,span:e.span}));case"STRING":return this.leaf("StringLiteral",(t,r)=>({kind:"StringLiteral",id:r,value:e.value,span:e.span}));case"TRUE":case"FALSE":return this.leaf("BooleanLiteral",(t,r)=>({kind:"BooleanLiteral",id:r,value:e.type==="TRUE",span:e.span}));case"NULL":return this.leaf("NullLiteral",(t,r)=>({kind:"NullLiteral",id:r,span:e.span}));case"IDENT":return this.leaf("Identifier",(t,r)=>({kind:"Identifier",id:r,name:e.lexeme,span:e.span}));case"LPAREN":{this.advance();let t=this.grouped(()=>this.parseExpression());return this.expect("RPAREN","para cerrar el paréntesis"),t}case"LBRACKET":return this.parseList();case"LBRACE":return this.parseDict();case"MINUS":{this.begin("Unary"),this.advance();let t=this.parseExpression(7,!1,e);return this.finish({kind:"Unary",id:this.id(),operator:"-",operand:t,span:this.spanFrom(e.span.start)})}case"NOT":case"BANG":{this.begin("Unary"),this.advance();let t=this.parseExpression(3,!1,e);return this.finish({kind:"Unary",id:this.id(),operator:"no",operand:t,span:this.spanFrom(e.span.start)})}case"FUNCTION":if(this.peek().type==="IDENT")this.error("PROCEDIMIENTO_CON_NOMBRE","Un procedimiento con nombre es una sentencia; no puede ir dentro de una expresión.",this.peek().span,"Como valor, escríbelo sin nombre: `variable doble = procedimiento(x) { … }`.");return this.begin("FunctionLiteral"),this.parseFunction(this.advance().span.start);case"INCREMENT":case"DECREMENT":if(this.peek().type==="IDENT"){let t=this.peek().lexeme;this.error("INCREMENTO_EN_EXPRESIÓN",`En Codexivo \`${e.lexeme}\` va después del nombre de la variable.`,{start:e.span.start,end:this.peek().span.end},`Escribe \`${t}${e.lexeme}\` como una sentencia propia.`)}this.incrementInExpression();case"ILLEGAL":throw new K;case"IF":case"WHILE":case"FOR":case"DO":case"LET":case"CONST":case"DOMAIN":case"REPEAT":case"FOR_EACH":case"BREAK":case"CONTINUE":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.begin("Call",e),this.advance();let a=this.parseList_("RPAREN","para cerrar los argumentos");return this.finish({kind:"Call",id:this.id(),callee:e,args:a,span:this.spanFrom(r)})}case"LBRACKET":{if(this.begin("Index",e),this.advance(),this.check("RBRACKET"))this.error("ÍNDICE_VACÍO","Falta el índice entre los corchetes.",this.current.span,"Por ejemplo: `lista[0]`.");let a=this.grouped(()=>this.parseExpression());return this.expect("RBRACKET","para cerrar el índice"),this.finish({kind:"Index",id:this.id(),object:e,index:a,span:this.spanFrom(r)})}case"DOT":{this.begin("Index",e),this.advance();let a=this.current;if(a.type!=="IDENT"){if(this.failSilentlyOnIllegal(),a.type!=="EOF")this.advance();this.error("CAMPO_ESPERADO",`Después de \`.\` va el nombre de un campo, pero se encontró ${B(a)}.`,a.span,'Con `.` solo se usan claves que son nombres válidos; para las demás escribe `d["clave"]`.')}let n=this.leaf("StringLiteral",(s,i)=>({kind:"StringLiteral",id:i,value:a.lexeme,span:a.span}));return this.finish({kind:"Index",id:this.id(),object:e,index:n,dot:!0,span:this.spanFrom(r)})}case"AND":case"OR":{this.begin("Logical",e),this.advance();let a=Z[t.type],n=this.parseExpression(a,!1,t),s=t.type==="AND"?"y":"o";return this.finish({kind:"Logical",id:this.id(),operator:s,left:e,right:n,span:this.spanFrom(r)})}default:{this.begin("Binary",e),this.advance();let a=Z[t.type],n=this.parseExpression(a===8?a-1:a,!1,t),s=bt[t.type];if(a===4&&Z[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 this.finish({kind:"Binary",id:this.id(),operator:s,left:e,right:n,span:this.spanFrom(r)})}}}parseList(){this.begin("ListLiteral");let e=this.advance().span.start,t=this.parseList_("RBRACKET","para cerrar la lista");return this.finish({kind:"ListLiteral",id:this.id(),elements:t,span:this.spanFrom(e)})}parseDict(){this.begin("DictLiteral");let e=this.advance(),t=[],r=new Set;while(!this.check("RBRACE")){let a=this.current.span.start;if(this.check("IDENT")&&this.peek().type==="COLON")this.error("CLAVE_SIN_COMILLAS",`La clave \`${this.current.lexeme}\` necesita comillas.`,this.current.span,`Escribe \`"${this.current.lexeme}": …\`. Si querías usar el valor de una variable como clave, ponla entre paréntesis: \`(${this.current.lexeme}): …\`.`);let n=this.grouped(()=>this.parseExpression());this.expect("COLON","después de la clave","Cada elemento de un diccionario se escribe `clave: valor`.");let s=this.grouped(()=>this.parseExpression());if(n.kind==="StringLiteral"||n.kind==="NumberLiteral"){if(r.has(n.value))this.error("CLAVE_REPETIDA",`La clave ${Rt(n.value)} está repetida.`,n.span);if(r.add(n.value),s.kind==="FunctionLiteral"&&!s.name&&n.kind==="StringLiteral")s.name=n.value}if(t.push({key:n,value:s,span:this.spanFrom(a)}),!this.match("COMMA"))break}if(!this.check("RBRACE"))this.failSilentlyOnIllegal(),this.error("TOKEN_ESPERADO",`Se esperaba \`,\` o \`}\` en el diccionario que empieza en la línea ${e.span.start.line}, pero se encontró ${B(this.current)}.`,this.current.span);return this.advance(),this.finish({kind:"DictLiteral",id:this.id(),entries:t,span:this.spanFrom(e.span.start)})}parseList_(e,t){let r=[];while(!this.check(e))if(r.push(this.grouped(()=>this.parseExpression())),!this.match("COMMA"))break;return this.expect(e,t),r}parseFunctionDeclaration(){this.begin("FunctionDeclaration");let e=this.advance().span.start,t=this.parseBindingName("después de `procedimiento`");this.begin("FunctionLiteral");let r=this.parseFunction(e,t.name);return this.finish({kind:"FunctionDeclaration",id:this.id(),name:t,function:r,span:r.span})}parseFunction(e,t){this.expect("LPAREN","después de `procedimiento`","Los parámetros van entre paréntesis: `procedimiento(a, b) { … }`.");let r=[],a=new Set;while(!this.check("RPAREN")){let i=this.parseBindingName("como parámetro");if(a.has(i.name))this.error("PARÁMETRO_REPETIDO",`El parámetro \`${i.name}\` está repetido.`,i.span);if(a.add(i.name),r.push(i),!this.match("COMMA"))break}this.expect("RPAREN","para cerrar los parámetros");let n=this.loopDepth;this.loopDepth=0;let s;try{s=this.parseBlock("para el cuerpo del procedimiento")}finally{this.loopDepth=n}return this.finish({kind:"FunctionLiteral",id:this.id(),name:t,params:r,body:s,span:this.spanFrom(e)})}}var Rt=(e)=>typeof e==="string"?JSON.stringify(e):String(e);var te=(e)=>{let t=new be({name:"programa",params:[],code:[],node:e},!1);return t.statements(e.statements),t.emit({op:"halt"}),t.chunk},ee=(e,t=!1)=>{let r={name:e.name??"procedimiento",params:e.params.map((n)=>n.name),code:[],node:e};if(t)r.hidden=!0;let a=new be(r,t);return a.statements(e.body.statements),a.emit({op:"const",value:null,node:e}),a.emit({op:"return",hasValue:!0,node:{kind:"Return",id:e.id,span:e.body.span}}),a.chunk},Nt={"+=":"+","-=":"-","*=":"*","/=":"/","%=":"mod"};class be{chunk;hidden;scopeDepth=0;loops=[];constructor(e,t){this.chunk=e;this.hidden=t}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}enterScope(){this.emit({op:"enterScope"}),this.scopeDepth+=1}exitScope(){this.emit({op:"exitScope"}),this.scopeDepth-=1}statements(e){for(let t of e){if(t.kind!=="FunctionDeclaration")continue;this.emit({op:"closure",chunk:ee(t.function,this.hidden),node:t.function}),this.emit({op:"declare",name:t.name.name,constant:!1,target:t.name,node:t})}for(let t of e)this.statement(t)}loop(e,t){let r={breaks:[],continues:[],scopeDepth:this.scopeDepth,slots:e};return this.loops.push(r),t(),this.loops.pop(),r}connect(e,t){for(let r of e)this.patch(r,t)}statement(e){if(e.kind==="FunctionDeclaration")return;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,constant:e.constant,target:e.name,node:e});return;case"Assignment":case"Increment":this.assignment(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"Domain":this.emit({op:"domainStart"}),this.scopeDepth+=1,this.statements(e.body.statements),this.scopeDepth-=1,this.emit({op:"domainEnd",name:e.name.name,writable:e.body.statements.flatMap((t)=>t.kind==="VariableDeclaration"&&!t.constant?[t.name.name]:[]),node:e});return;case"Break":case"Continue":{let t=this.loops[this.loops.length-1];for(let r=this.scopeDepth;r>t.scopeDepth;r--)this.emit({op:"exitScope"});if(e.kind==="Break"){if(t.slots>0)this.emit({op:"pop",count:t.slots});t.breaks.push(this.emit({op:"jump",target:-1}))}else t.continues.push(this.emit({op:"jump",target:-1}));return}case"If":{let t=[];if(e.branches.forEach((r,a)=>{this.expression(r.condition);let n=this.emit({op:"branch",target:-1,jumpWhen:!1,label:a===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}),a=this.loop(0,()=>this.block(e.body));this.emit({op:"jump",target:t}),this.patch(r,this.here),this.connect(a.continues,t),this.connect(a.breaks,this.here);return}case"DoUntil":{let t=this.here,r=this.loop(0,()=>this.block(e.body));this.connect(r.continues,this.here),this.expression(e.condition),this.emit({op:"branch",target:t,jumpWhen:!1,label:"hasta_que",node:e.condition,owner:e}),this.connect(r.breaks,this.here);return}case"For":{if(this.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});let a=this.loop(0,()=>this.block(e.body));if(this.connect(a.continues,this.here),e.update)this.statement(e.update);if(this.emit({op:"jump",target:t}),r>=0)this.patch(r,this.here);this.connect(a.breaks,this.here),this.exitScope();return}case"Repeat":{this.expression(e.count),this.emit({op:"repeatStart",node:e.count,owner:e});let t=this.here,r=this.emit({op:"repeatNext",target:-1,node:e.count,owner:e}),a=this.loop(1,()=>this.block(e.body));this.emit({op:"jump",target:t}),this.patch(r,this.here),this.connect(a.continues,t),this.connect(a.breaks,this.here);return}case"ForRange":{if(this.expression(e.from),this.expression(e.to),e.step)this.expression(e.step);else this.emit({op:"const",value:1,node:e});this.emit({op:"rangeStart",explicitStep:Boolean(e.step),owner:e});let t=this.here,r=this.emit({op:"rangeNext",target:-1,node:e.to,owner:e}),a=this.loop(4,()=>{this.enterScope(),this.emit({op:"declare",name:e.variable.name,constant:!1,loop:!0,target:e.variable,node:e.variable}),this.statements(e.body.statements),this.exitScope()});this.emit({op:"jump",target:t}),this.patch(r,this.here),this.connect(a.continues,t),this.connect(a.breaks,this.here);return}case"ForEach":{this.expression(e.iterable),this.emit({op:"forEachStart",node:e.iterable,owner:e});let t=this.here,r=this.emit({op:"forEachNext",target:-1,node:e.iterable,owner:e}),a=this.loop(3,()=>{this.enterScope();for(let n of[e.variable,...e.value?[e.value]:[]])this.emit({op:"declare",name:n.name,constant:!1,target:n,node:n});this.statements(e.body.statements),this.exitScope()});this.emit({op:"jump",target:t}),this.patch(r,this.here),this.connect(a.continues,t),this.connect(a.breaks,this.here);return}}}assignment(e){let t=e.kind==="Increment"?e.operator==="++"?"+":"-":e.operator==="="?void 0:Nt[e.operator],r=()=>{if(e.kind==="Increment")this.emit({op:"const",value:1,node:e.target});else this.expression(e.value)},{target:a}=e;if(a.kind==="Identifier"){if(t)this.emit({op:"load",name:a.name,node:a,forUpdate:!0});if(r(),t)this.emit({op:"binary",operator:t,node:e});this.emit({op:"store",name:a.name,target:a,node:e});return}if(this.expression(a.object),this.expression(a.index),t)this.emit({op:"dup",count:2}),this.emit({op:"index",node:a});if(r(),t)this.emit({op:"binary",operator:t,node:e});this.emit({op:"setIndex",target:a,node:e})}block(e){this.enterScope(),this.statements(e.statements),this.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"DictLiteral":for(let t of e.entries)this.expression(t.key),this.expression(t.value);this.emit({op:"dict",count:e.entries.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:ee(e,this.hidden),node:e});return}}}var H=Symbol("esperar-entrada"),E=(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"||e.kind==="diccionario"||e.kind==="dominio"?e.kind:"procedimiento"}},y=(e)=>{switch(e){case"lista":return"una lista";case"nulo":return"`nulo`";default:return`un ${e}`}},v=(e)=>typeof e==="object"&&e!==null&&e.kind==="lista",z=(e)=>typeof e==="object"&&e!==null&&e.kind==="dominio",I=(e)=>typeof e==="object"&&e!==null&&e.kind==="diccionario",re=(e)=>typeof e==="object"&&e!==null&&(e.kind==="procedimiento"||e.kind==="nativo"),kt=(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 kt(e);case"string":return JSON.stringify(e);case"boolean":return e?"verdadero":"falso"}if(e.kind==="dominio")return`dominio ${e.name}`;if(e.kind==="lista"||e.kind==="diccionario"){if(t.has(e))return e.kind==="lista"?"[…]":"{…}";t.add(e);let r=e.kind==="lista"?e.items.map((a)=>f(a,t)):[...e.entries].map(([a,n])=>`${f(a)}: ${f(n,t)}`);return t.delete(e),e.kind==="lista"?`[${r.join(", ")}]`:`{${r.join(", ")}}`}return e.name?`procedimiento ${e.name}`:"procedimiento"},ae=(e)=>typeof e==="string"?e:f(e),M=(e,t,r=[])=>{if(e===t)return!0;let a=v(e)&&v(t),n=I(e)&&I(t);if(!a&&!n)return!1;if(r.some(([i,c])=>i===e&&c===t))return!0;r.push([e,t]);let s;if(a){let[i,c]=[e,t];s=i.items.length===c.items.length&&i.items.every((o,m)=>M(o,c.items[m],r))}else{let[i,c]=[e,t];s=i.entries.size===c.entries.size&&[...i.entries].every(([o,m])=>c.entries.has(o)&&M(m,c.entries.get(o),r))}return r.pop(),s};class F{parent;variables=new Map;constants=new Set;loopVariables=new Set;constructor(e){this.parent=e}lookup(e){let t=this;while(t){if(t.variables.has(e))return t;t=t.parent}return}}var ne=(e)=>{if(typeof e==="string")return e.length;if(e!==void 0&&v(e))return e.items.length;if(e!==void 0&&I(e))return e.entries.size;return 1},R=(e)=>Math.max(1,ne(e[0])),_t=(e)=>{let t=ne(e[0]);return Math.max(1,t*Math.ceil(Math.log2(Math.max(t,1))))},Me=(e)=>(t)=>Math.max(1,Math.ceil(e(t))),Ve=(e)=>e.length===1?R(e):Math.max(1,e.length),Be=(e)=>Math.max(1,ne(e[1])),Fe=(e)=>typeof e[1]==="number"&&typeof e[2]==="number"?Math.max(1,Math.abs(e[2]-e[1])):1,Ot=(e)=>{let[t,r,a=1]=e;if(typeof t!=="number"||typeof r!=="number"||typeof a!=="number"||a===0)return 1;return Math.max(1,Math.floor((r-t)/a)+1)},se={máximo:Ve,mínimo:Ve,mayúsculas:R,minúsculas:R,recortar:R,dividir:R,unir:R,reemplazar:R,contiene:R,posición_de:R,empieza_con:Be,termina_con:Be,subtexto:Fe,caracteres:R,invertir:R,insertar:R,quitar:R,quitar_primero:R,desencolar:R,rebanar:Fe,copiar:R,sumar:R,ordenar:_t,rango:Ot,índices:R,lista_de:Me((e)=>typeof e[0]==="number"?e[0]*ne(e[1]):1),matriz:Me((e)=>typeof e[0]==="number"&&typeof e[1]==="number"?e[0]*e[1]:1),claves:R,valores:R,mapear:R,filtrar:R,reducir:R,alguno:R,todos:R,buscar:R};var u=(e,t,r,a)=>{let n=se[e];return{kind:"nativo",name:e,arity:t,call:a,doc:r,...n?{cost:n}:{}}},ie=(e,t,r)=>({kind:"nativo",name:e,arity:t,call:r}),D=(e,t,r,a="")=>{if(typeof t==="number")return t;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera un número${a}, pero recibió ${y(E(t))} (${f(t)}).`)},w=(e,t,r)=>{if(v(t))return t;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera una lista, pero recibió ${y(E(t))}.`)},L=(e,t,r,a="")=>{if(typeof t==="string")return t;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera un texto${a}, pero recibió ${y(E(t))} (${f(t)}).`)},ce=(e,t,r,a="")=>{let n=D(e,t,r,a);if(Number.isInteger(n))return n;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera un número entero${a}, pero recibió ${f(n)}.`,"Redondea el valor con `redondear(…)`, `piso(…)` o `techo(…)`.")},Ee=(e,t,r,a="")=>{let n=ce(e,t,r,a);if(n>=0&&n<=j)return n;throw r.fail("TIPO_DE_ARGUMENTO",n<0?`\`${e}\` necesita una cantidad de 0 o más${a}, pero recibió ${n}.`:`\`${e}\` produciría ${n} elementos; el máximo es ${j}.`)},It=(e,t,r,a)=>{let n=r?"la lista":"el texto",s=r?t===1?"elemento":"elementos":t===1?"carácter":"caracteres";return a.fail("ÍNDICE_FUERA_DE_RANGO",`El índice ${e} está fuera de ${n}, que tiene ${t} ${s}.`,t===0?r?"La lista está vacía.":"El texto está vacío.":`Los índices válidos van de 0 a ${t-1} (o de -1 a -${t} desde el final).`)},ve=(e,t,r,a,n,s=!1)=>{if(typeof t!=="number"||!Number.isInteger(t))throw n.fail("ÍNDICE_INVÁLIDO",`\`${e}\` necesita un índice entero, pero recibió ${f(t)}.`);let i=t<0?r+t:t;if(i<0||i>r||i===r&&!s)throw It(t,r,a,n);return i},pe=(e,t,r=new Map)=>{if(!v(e)&&!I(e))return e;let a=r.get(e);if(a)return a;if(v(e)){let s=t.newList([]);r.set(e,s);for(let i of e.items)s.items.push(pe(i,t,r));return s}let n=t.newDict(new Map);r.set(e,n);for(let[s,i]of e.entries)n.entries.set(s,pe(i,t,r));return n},Ue=(e,t)=>t.fail("LISTA_VACÍA",`\`${e}\` no puede tomar nada de una lista vacía.`),qe=(e,t,r)=>{if(typeof t==="string")return t;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera un texto o una lista, pero recibió ${y(E(t))}.`)},ze=(e,t,r,a,n)=>{let s=ve(e,t[1],r,a,n,!0),i=ve(e,t[2],r,a,n,!0);if(s>i)throw n.fail("RANGO_INVÁLIDO",`\`${e}\` necesita que el inicio (${f(t[1])}) no esté después del fin (${f(t[2])}).`,'El fin no se incluye: `subtexto("hola", 1, 3)` es `"ol"`.');return[s,i]},At=(e,t)=>{let a=typeof e.items[0]==="string"?"texto":"número";for(let n of e.items)if(E(n)!==a)throw t.fail("TIPO_DE_ARGUMENTO",`\`ordenar\` necesita que todos los elementos sean números o todos textos, pero encontró ${y(E(n))} (${f(n)}).`);return a},G=(e,t,r,a)=>{if(v(r))return r;throw a.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera una ${t} (una lista), pero recibió ${y(E(r))}.`,`Una ${t} empieza como una lista vacía: \`variable ${t} = [];\`.`)},oe=(e,t,r)=>r.fail(t==="pila"?"PILA_VACÍA":"COLA_VACÍA",e==="tope"||e==="frente"?`Una ${t} vacía no tiene \`${e}\`.`:`No se puede \`${e}\` de una ${t} vacía.`,`Revisa antes con \`longitud(${t}) > 0\`.`),ye=(e,t,r)=>{if(I(t))return t;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera un diccionario, pero recibió ${y(E(t))}.`)},Ge=(e,t,r)=>{if(typeof t==="string"||typeof t==="number")return t;throw r.fail("CLAVE_INVÁLIDA",`\`${e}\` espera una clave de texto o número, pero recibió ${y(E(t))}.`)},je=(e,t,r)=>{let a=t.length===1&&v(t[0])?t[0].items:t;if(a.length===0)throw r.fail("LISTA_VACÍA",`\`${e}\` necesita al menos un número, pero la lista está vacía.`);return a.map((n)=>D(e,n,r))},We=(e)=>{let t=e.trim();if(!/^[-+]?(\d+(\.\d*)?|\.\d+)$/.test(t))return;return Number(t)},Tt=(e,t)=>{let r=10**t;return Math.sign(e)*Math.round(Math.abs(e)*r)/r},j=1e7,Ke=[u("imprimir",[0,1/0],{level:"principiante",category:"entrada y salida",params:[{name:"valor",type:"cualquiera",optional:!0}],variadic:!0,returns:"nulo",description:"Escribe los valores separados por un espacio y termina la línea. Los textos se escriben sin comillas.",examples:[{code:'imprimir("Hola,", "Ana", 16)',output:`Hola, Ana 16
7
7
  `}]},(e,t)=>(t.write(`${e.map(ae).join(" ")}
8
8
  `),null)),u("leer",0,{level:"principiante",category:"entrada y salida",params:[],returns:"texto | nulo",description:"Lee la siguiente línea de entrada como texto. Si ya no hay más entrada, regresa `nulo`.",examples:[{code:"leer()",result:"nulo"}]},(e,t)=>t.readLine()),u("leer_número",0,{level:"principiante",category:"entrada y salida",params:[],returns:"número",description:"Lee la siguiente línea de entrada y la convierte en número. Si no es un número, es un error.",examples:[{code:`variable edad = 0
9
9
  si (falso) { edad = leer_número() }
10
- edad`,result:"0"}]},(e,t)=>{let r=t.readLine();if(r===H)return r;if(r===null)throw t.fail("SIN_ENTRADA","`leer_número` no encontró más datos de entrada.");let a=je(r);if(a===void 0)throw t.fail("NO_ES_NÚMERO",`\`leer_número\` esperaba un número, pero leyó ${JSON.stringify(r)}.`);return a}),u("tipo",1,{level:"principiante",category:"tipos",params:[{name:"x",type:"cualquiera"}],returns:"texto",description:'El nombre del tipo de un valor: "número", "texto", "lógico", "nulo", "lista", "diccionario", "dominio" o "procedimiento".',examples:[{code:"tipo(3.5)",result:'"número"'},{code:"tipo([1, 2])",result:'"lista"'}]},(e)=>b(e[0])),u("texto",1,{level:"principiante",category:"tipos",params:[{name:"x",type:"cualquiera"}],returns:"texto",description:"Convierte cualquier valor en texto, como lo escribiría `imprimir`.",examples:[{code:'texto(42) + "!"',result:'"42!"'}]},(e)=>ae(e[0])),u("número",1,{level:"principiante",category:"tipos",params:[{name:"x",type:"texto | número"}],returns:"número",description:"Convierte un texto en número. Si el texto no es un número, es un error.",examples:[{code:'número("3.5") * 2',result:"7"}]},(e,t)=>{let r=e[0];if(typeof r==="number")return r;if(typeof r==="string"){let a=je(r);if(a!==void 0)return a;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 ${v(b(r))} en número.`)}),u("longitud",1,{level:"principiante",category:"tipos",params:[{name:"x",type:"texto | lista | diccionario"}],returns:"número",description:"Cuántos caracteres tiene un texto, cuántos elementos una lista o cuántas claves un diccionario.",examples:[{code:'longitud("año")',result:"3"},{code:"longitud([1, 2, 3])",result:"3"}]},(e,t)=>{let r=e[0];if(typeof r==="string")return[...r].length;if(y(r))return r.items.length;if(I(r))return r.entries.size;throw t.fail("TIPO_DE_ARGUMENTO",`\`longitud\` espera un texto, una lista o un diccionario, pero recibió ${v(b(r))}.`)}),u("absoluto",1,{level:"principiante",category:"matemáticas",params:[{name:"x",type:"número"}],returns:"número",description:"El valor absoluto: la distancia del número al 0.",examples:[{code:"absoluto(-3)",result:"3"}]},(e,t)=>Math.abs(D("absoluto",e[0],t))),u("máximo",[1,1/0],{level:"principiante",category:"matemáticas",params:[{name:"números",type:"número | lista"}],variadic:!0,returns:"número",description:"El mayor de varios números, o de los números de una lista.",examples:[{code:"máximo(3, 9, 2)",result:"9"},{code:"máximo([4, 1])",result:"4"}]},(e,t)=>Math.max(...We("máximo",e,t))),u("mínimo",[1,1/0],{level:"principiante",category:"matemáticas",params:[{name:"números",type:"número | lista"}],variadic:!0,returns:"número",description:"El menor de varios números, o de los números de una lista.",examples:[{code:"mínimo(3, 9, 2)",result:"2"}]},(e,t)=>Math.min(...We("mínimo",e,t))),u("redondear",[1,2],{level:"principiante",category:"matemáticas",params:[{name:"x",type:"número"},{name:"decimales",type:"número",optional:!0}],returns:"número",description:"Redondea al entero más cercano, o a cierta cantidad de decimales. Los .5 se alejan del cero.",examples:[{code:"redondear(2.5)",result:"3"},{code:"redondear(3.14159, 2)",result:"3.14"}]},(e,t)=>{let r=D("redondear",e[0],t),a=e.length>1?D("redondear",e[1],t," de decimales"):0;if(!Number.isInteger(a)||a<0)throw t.fail("TIPO_DE_ARGUMENTO","La cantidad de decimales de `redondear` tiene que ser un entero positivo o 0.");return Tt(r,a)}),u("agregar",2,{level:"principiante",category:"listas",params:[{name:"lista",type:"lista"},{name:"x",type:"cualquiera"}],returns:"nulo",mutates:!0,description:"Agrega `x` al final de la lista.",examples:[{code:`variable l = [1]
10
+ edad`,result:"0"}]},(e,t)=>{let r=t.readLine();if(r===H)return r;if(r===null)throw t.fail("SIN_ENTRADA","`leer_número` no encontró más datos de entrada.");let a=We(r);if(a===void 0)throw t.fail("NO_ES_NÚMERO",`\`leer_número\` esperaba un número, pero leyó ${JSON.stringify(r)}.`);return a}),u("tipo",1,{level:"principiante",category:"tipos",params:[{name:"x",type:"cualquiera"}],returns:"texto",description:'El nombre del tipo de un valor: "número", "texto", "lógico", "nulo", "lista", "diccionario", "dominio" o "procedimiento".',examples:[{code:"tipo(3.5)",result:'"número"'},{code:"tipo([1, 2])",result:'"lista"'}]},(e)=>E(e[0])),u("texto",1,{level:"principiante",category:"tipos",params:[{name:"x",type:"cualquiera"}],returns:"texto",description:"Convierte cualquier valor en texto, como lo escribiría `imprimir`.",examples:[{code:'texto(42) + "!"',result:'"42!"'}]},(e)=>ae(e[0])),u("número",1,{level:"principiante",category:"tipos",params:[{name:"x",type:"texto | número"}],returns:"número",description:"Convierte un texto en número. Si el texto no es un número, es un error.",examples:[{code:'número("3.5") * 2',result:"7"}]},(e,t)=>{let r=e[0];if(typeof r==="number")return r;if(typeof r==="string"){let a=We(r);if(a!==void 0)return a;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 ${y(E(r))} en número.`)}),u("longitud",1,{level:"principiante",category:"tipos",params:[{name:"x",type:"texto | lista | diccionario"}],returns:"número",description:"Cuántos caracteres tiene un texto, cuántos elementos una lista o cuántas claves un diccionario.",examples:[{code:'longitud("año")',result:"3"},{code:"longitud([1, 2, 3])",result:"3"}]},(e,t)=>{let r=e[0];if(typeof r==="string")return[...r].length;if(v(r))return r.items.length;if(I(r))return r.entries.size;throw t.fail("TIPO_DE_ARGUMENTO",`\`longitud\` espera un texto, una lista o un diccionario, pero recibió ${y(E(r))}.`)}),u("afirmar",[1,2],{level:"principiante",category:"verificación",params:[{name:"condición",type:"lógico"},{name:"mensaje",type:"texto",optional:!0}],returns:"nulo",description:"Detiene el programa con un error si la condición es falsa. Sirve para escribir lo que esperas que sea cierto y comprobarlo.",examples:[{code:`variable total = 2 + 2
11
+ afirmar(total es_igual_a 4, "la suma está mal")
12
+ total`,result:"4"}]},(e,t)=>{let r=e[0];if(typeof r!=="boolean")throw t.fail("TIPO_DE_ARGUMENTO",`\`afirmar\` espera una condición \`verdadero\` o \`falso\`, pero recibió ${y(E(r))} (${f(r)}).`,"¿Quisiste comparar? Por ejemplo: `afirmar(x es_mayor_que 0)`.");let a=e[1];if(a!==void 0&&typeof a!=="string")throw t.fail("TIPO_DE_ARGUMENTO",`El mensaje de \`afirmar\` tiene que ser un texto, pero es ${y(E(a))}.`,'Escribe el mensaje entre comillas: `afirmar(x es_mayor_que 0, "x tenía que ser positivo")`.');if(r)return null;throw t.fail("AFIRMACIÓN_FALLIDA",a?`La afirmación no se cumplió: ${a}`:"La afirmación no se cumplió.","Lo que afirmaste que era cierto no lo es en este punto. Revisa los valores de las variables justo antes de esta línea.")}),u("absoluto",1,{level:"principiante",category:"matemáticas",params:[{name:"x",type:"número"}],returns:"número",description:"El valor absoluto: la distancia del número al 0.",examples:[{code:"absoluto(-3)",result:"3"}]},(e,t)=>Math.abs(D("absoluto",e[0],t))),u("máximo",[1,1/0],{level:"principiante",category:"matemáticas",params:[{name:"números",type:"número | lista"}],variadic:!0,returns:"número",description:"El mayor de varios números, o de los números de una lista.",examples:[{code:"máximo(3, 9, 2)",result:"9"},{code:"máximo([4, 1])",result:"4"}]},(e,t)=>Math.max(...je("máximo",e,t))),u("mínimo",[1,1/0],{level:"principiante",category:"matemáticas",params:[{name:"números",type:"número | lista"}],variadic:!0,returns:"número",description:"El menor de varios números, o de los números de una lista.",examples:[{code:"mínimo(3, 9, 2)",result:"2"}]},(e,t)=>Math.min(...je("mínimo",e,t))),u("redondear",[1,2],{level:"principiante",category:"matemáticas",params:[{name:"x",type:"número"},{name:"decimales",type:"número",optional:!0}],returns:"número",description:"Redondea al entero más cercano, o a cierta cantidad de decimales. Los .5 se alejan del cero.",examples:[{code:"redondear(2.5)",result:"3"},{code:"redondear(3.14159, 2)",result:"3.14"}]},(e,t)=>{let r=D("redondear",e[0],t),a=e.length>1?D("redondear",e[1],t," de decimales"):0;if(!Number.isInteger(a)||a<0)throw t.fail("TIPO_DE_ARGUMENTO","La cantidad de decimales de `redondear` tiene que ser un entero positivo o 0.");return Tt(r,a)}),u("agregar",2,{level:"principiante",category:"listas",params:[{name:"lista",type:"lista"},{name:"x",type:"cualquiera"}],returns:"nulo",mutates:!0,description:"Agrega `x` al final de la lista.",examples:[{code:`variable l = [1]
11
13
  agregar(l, 2)
12
- l`,result:"[1, 2]"}]},(e,t)=>(w("agregar",e[0],t).items.push(e[1]),null)),u("rango",[2,3],{level:"intermedio",category:"listas",params:[{name:"inicio",type:"número"},{name:"fin",type:"número"},{name:"paso",type:"número",optional:!0}],returns:"lista",description:"Lista de números de `inicio` a `fin`, incluyendo `fin`. Con un paso negativo cuenta hacia abajo.",examples:[{code:"rango(1, 5)",result:"[1, 2, 3, 4, 5]"},{code:"rango(10, 0, -5)",result:"[10, 5, 0]"}]},(e,t)=>{let r=D("rango",e[0],t," como inicio"),a=D("rango",e[1],t," como final"),n=e.length>2?D("rango",e[2],t," como paso"):1;if(n===0)throw t.fail("RANGO_PASO_CERO","El paso de `rango` no puede ser 0: nunca llegaría al final.","Usa un paso positivo para contar hacia arriba o uno negativo para contar hacia abajo.");let s=Math.floor((a-r)/n+0.000000001)+1;if(!Number.isFinite(s))throw t.fail("TIPO_DE_ARGUMENTO","`rango` necesita números finitos.");if(s>W)throw t.fail("RANGO_DEMASIADO_GRANDE",`\`rango\` produciría ${s} números; el máximo es ${W}.`,"Revisa el inicio, el final y el paso.");return t.newList(Array.from({length:Math.max(0,s)},(i,c)=>r+c*n))}),u("índices",1,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista | texto"}],returns:"lista",description:"Las posiciones válidas de una lista o texto, de 0 a `longitud - 1`.",examples:[{code:'índices(["a", "b", "c"])',result:"[0, 1, 2]"}]},(e,t)=>{let r=e[0];if(typeof r!=="string"&&!y(r))throw t.fail("TIPO_DE_ARGUMENTO",`\`índices\` espera una lista o un texto, pero recibió ${v(b(r))}.`);let a=typeof r==="string"?r.length:r.items.length;return t.newList(Array.from({length:a},(n,s)=>s))}),u("quitar",2,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista | diccionario"},{name:"posición",type:"número | texto"}],returns:"cualquiera",mutates:!0,description:"Quita el elemento en esa posición de una lista, o esa clave de un diccionario, y regresa lo que quitó.",examples:[{code:`variable l = [1, 2, 3]
14
+ l`,result:"[1, 2]"}]},(e,t)=>(w("agregar",e[0],t).items.push(e[1]),null)),u("rango",[2,3],{level:"intermedio",category:"listas",params:[{name:"inicio",type:"número"},{name:"fin",type:"número"},{name:"paso",type:"número",optional:!0}],returns:"lista",description:"Lista de números de `inicio` a `fin`, incluyendo `fin`. Con un paso negativo cuenta hacia abajo.",examples:[{code:"rango(1, 5)",result:"[1, 2, 3, 4, 5]"},{code:"rango(10, 0, -5)",result:"[10, 5, 0]"}]},(e,t)=>{let r=D("rango",e[0],t," como inicio"),a=D("rango",e[1],t," como final"),n=e.length>2?D("rango",e[2],t," como paso"):1;if(n===0)throw t.fail("RANGO_PASO_CERO","El paso de `rango` no puede ser 0: nunca llegaría al final.","Usa un paso positivo para contar hacia arriba o uno negativo para contar hacia abajo.");let s=Math.floor((a-r)/n+0.000000001)+1;if(!Number.isFinite(s))throw t.fail("TIPO_DE_ARGUMENTO","`rango` necesita números finitos.");if(s>j)throw t.fail("RANGO_DEMASIADO_GRANDE",`\`rango\` produciría ${s} números; el máximo es ${j}.`,"Revisa el inicio, el final y el paso.");return t.newList(Array.from({length:Math.max(0,s)},(i,c)=>r+c*n))}),u("índices",1,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista | texto"}],returns:"lista",description:"Las posiciones válidas de una lista o texto, de 0 a `longitud - 1`.",examples:[{code:'índices(["a", "b", "c"])',result:"[0, 1, 2]"}]},(e,t)=>{let r=e[0];if(typeof r!=="string"&&!v(r))throw t.fail("TIPO_DE_ARGUMENTO",`\`índices\` espera una lista o un texto, pero recibió ${y(E(r))}.`);let a=typeof r==="string"?r.length:r.items.length;return t.newList(Array.from({length:a},(n,s)=>s))}),u("quitar",2,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista | diccionario"},{name:"posición",type:"número | texto"}],returns:"cualquiera",mutates:!0,description:"Quita el elemento en esa posición de una lista, o esa clave de un diccionario, y regresa lo que quitó.",examples:[{code:`variable l = [1, 2, 3]
13
15
  [quitar(l, 0), l]`,result:"[1, [2, 3]]"},{code:`variable d = { "a": 1 }
14
- [quitar(d, "a"), d]`,result:"[1, {}]"}]},(e,t)=>{let r=e[0];if(I(r)){let i=Ge("quitar",e[1],t);if(!r.entries.has(i))throw t.fail("CLAVE_INEXISTENTE",`\`quitar\` no encontró la clave ${g(i)} en el diccionario.`,`Usa \`tiene(d, ${g(i)})\` para preguntar antes de quitarla.`);let c=r.entries.get(i);return r.entries.delete(i),c}if(!y(r))throw t.fail("TIPO_DE_ARGUMENTO",`\`quitar\` espera una lista o un diccionario, pero recibió ${v(b(r))}.`);let a=e[1];if(typeof a!=="number"||!Number.isInteger(a))throw t.fail("ÍNDICE_INVÁLIDO",`El índice tiene que ser un número entero, pero es ${g(a)}.`);let n=r.items.length,s=a<0?n+a:a;if(s<0||s>=n)throw t.fail("ÍNDICE_FUERA_DE_RANGO",`El índice ${a} está fuera de la lista, que tiene ${n} ${n===1?"elemento":"elementos"}.`,n===0?"La lista está vacía.":`Los índices válidos van de 0 a ${n-1} (o de -1 a -${n} desde el final).`);return r.items.splice(s,1)[0]}),u("primero",1,{level:"principiante",category:"listas",params:[{name:"lista",type:"lista"}],returns:"cualquiera",description:"El primer elemento de la lista.",examples:[{code:"primero([7, 8, 9])",result:"7"}]},(e,t)=>{let r=w("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]}),u("último",1,{level:"principiante",category:"listas",params:[{name:"lista",type:"lista"}],returns:"cualquiera",description:"El último elemento de la lista.",examples:[{code:"último([7, 8, 9])",result:"9"}]},(e,t)=>{let r=w("ú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]}),u("claves",1,{level:"intermedio",category:"diccionarios",params:[{name:"d",type:"diccionario"}],returns:"lista",description:"Una lista nueva con las claves del diccionario, en el orden en que se agregaron.",examples:[{code:'claves({ "a": 1, "b": 2 })',result:'["a", "b"]'}]},(e,t)=>t.newList([...ve("claves",e[0],t).entries.keys()])),u("valores",1,{level:"intermedio",category:"diccionarios",params:[{name:"d",type:"diccionario"}],returns:"lista",description:"Una lista nueva con los valores del diccionario, en el orden de sus claves.",examples:[{code:'valores({ "a": 1, "b": 2 })',result:"[1, 2]"}]},(e,t)=>t.newList([...ve("valores",e[0],t).entries.values()])),u("tiene",2,{level:"intermedio",category:"diccionarios",params:[{name:"d",type:"diccionario"},{name:"clave",type:"texto | número"}],returns:"lógico",description:"`verdadero` si el diccionario tiene esa clave.",examples:[{code:'tiene({ "a": 1 }, "a")',result:"verdadero"},{code:'tiene({ "a": 1 }, "b")',result:"falso"}]},(e,t)=>ve("tiene",e[0],t).entries.has(Ge("tiene",e[1],t))),u("piso",1,{level:"principiante",category:"matemáticas",params:[{name:"x",type:"número"}],returns:"número",description:"Redondea hacia abajo.",examples:[{code:"piso(2.7)",result:"2"},{code:"piso(-2.1)",result:"-3"}]},(e,t)=>Math.floor(D("piso",e[0],t))),u("techo",1,{level:"principiante",category:"matemáticas",params:[{name:"x",type:"número"}],returns:"número",description:"Redondea hacia arriba.",examples:[{code:"techo(2.1)",result:"3"}]},(e,t)=>Math.ceil(D("techo",e[0],t))),u("entero",1,{level:"principiante",category:"matemáticas",params:[{name:"x",type:"número"}],returns:"número",description:"Quita los decimales, sin redondear.",examples:[{code:"entero(-2.7)",result:"-2"}]},(e,t)=>Math.trunc(D("entero",e[0],t))),u("raíz",1,{level:"principiante",category:"matemáticas",params:[{name:"x",type:"número"}],returns:"número",description:"La raíz cuadrada. De un número negativo es un error.",examples:[{code:"raíz(16)",result:"4"}]},(e,t)=>{let r=D("raíz",e[0],t);if(r<0)throw t.fail("RAÍZ_DE_NEGATIVO",`\`raíz\` no tiene un resultado real para ${g(r)}.`,"La raíz cuadrada solo existe para números de 0 en adelante. ¿Quisiste usar `absoluto(…)` antes?");return Math.sqrt(r)}),u("aleatorio",2,{level:"principiante",category:"matemáticas",params:[{name:"mínimo",type:"número"},{name:"máximo",type:"número"}],returns:"número",description:"Un entero al azar entre `mínimo` y `máximo`, los dos incluidos. Con la misma semilla da los mismos números.",examples:[{code:`variable dado = aleatorio(1, 6)
15
- dado >= 1 y dado <= 6`,result:"verdadero"}]},(e,t)=>{let r=ce("aleatorio",e[0],t," como mínimo"),a=ce("aleatorio",e[1],t," como máximo");if(r>a)throw t.fail("TIPO_DE_ARGUMENTO",`\`aleatorio\` necesita que el mínimo (${r}) no sea mayor que el máximo (${a}).`);return r+Math.floor(t.random()*(a-r+1))}),u("mayúsculas",1,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto"}],returns:"texto",description:"El mismo texto en mayúsculas.",examples:[{code:'mayúsculas("Ñandú")',result:'"ÑANDÚ"'}]},(e,t)=>C("mayúsculas",e[0],t).toLocaleUpperCase("es")),u("minúsculas",1,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto"}],returns:"texto",description:"El mismo texto en minúsculas.",examples:[{code:'minúsculas("ÁRBOL")',result:'"árbol"'}]},(e,t)=>C("minúsculas",e[0],t).toLocaleLowerCase("es")),u("recortar",1,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto"}],returns:"texto",description:"El texto sin espacios ni saltos de línea al principio y al final.",examples:[{code:'recortar(" hola ")',result:'"hola"'}]},(e,t)=>C("recortar",e[0],t).trim()),u("dividir",2,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto"},{name:"separador",type:"texto"}],returns:"lista",description:"Parte el texto en cada aparición del separador.",examples:[{code:'dividir("a,b,c", ",")',result:'["a", "b", "c"]'}]},(e,t)=>{let r=C("dividir",e[0],t),a=C("dividir",e[1],t," como separador");if(a==="")throw t.fail("SEPARADOR_VACÍO","`dividir` necesita un separador que no esté vacío.","Para separar un texto letra por letra usa `caracteres(t)`.");return t.newList(r.split(a))}),u("unir",2,{level:"intermedio",category:"texto",params:[{name:"lista",type:"lista"},{name:"separador",type:"texto"}],returns:"texto",description:"Une los elementos de la lista en un texto, con el separador entre cada uno.",examples:[{code:'unir(["a", "b", "c"], "-")',result:'"a-b-c"'}]},(e,t)=>{let r=w("unir",e[0],t),a=C("unir",e[1],t," como separador");return r.items.map(ae).join(a)}),u("reemplazar",3,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto"},{name:"buscar",type:"texto"},{name:"poner",type:"texto"}],returns:"texto",description:"Cambia cada aparición de `buscar` por `poner`.",examples:[{code:'reemplazar("la casa", "a", "o")',result:'"lo coso"'}]},(e,t)=>{let r=C("reemplazar",e[0],t),a=C("reemplazar",e[1],t," para buscar"),n=C("reemplazar",e[2],t," para poner");if(a==="")throw t.fail("SEPARADOR_VACÍO","`reemplazar` necesita un texto que buscar que no esté vacío.");return r.replaceAll(a,n)}),u("contiene",2,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto | lista"},{name:"x",type:"cualquiera"}],returns:"lógico",description:"En un texto, si aparece la parte buscada; en una lista, si está el valor (comparando por valor).",examples:[{code:'contiene("murciélago", "cié")',result:"verdadero"},{code:"contiene([1, 2], 3)",result:"falso"}]},(e,t)=>{let r=e[0];if(y(r))return r.items.some((a)=>V(a,e[1]));return qe("contiene",r,t).includes(C("contiene",e[1],t," para buscar"))}),u("posición_de",2,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto | lista"},{name:"x",type:"cualquiera"}],returns:"número",description:"Dónde aparece por primera vez la parte o el valor buscado, o `-1` si no está.",examples:[{code:'posición_de("banana", "na")',result:"2"},{code:"posición_de([5, 6], 7)",result:"-1"}]},(e,t)=>{let r=e[0];if(y(r))return r.items.findIndex((a)=>V(a,e[1]));return qe("posición_de",r,t).indexOf(C("posición_de",e[1],t," para buscar"))}),u("empieza_con",2,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto"},{name:"inicio",type:"texto"}],returns:"lógico",description:"`verdadero` si el texto empieza con `inicio`.",examples:[{code:'empieza_con("Codexivo", "Code")',result:"verdadero"}]},(e,t)=>C("empieza_con",e[0],t).startsWith(C("empieza_con",e[1],t," como inicio"))),u("termina_con",2,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto"},{name:"final",type:"texto"}],returns:"lógico",description:"`verdadero` si el texto termina con `final`.",examples:[{code:'termina_con("Codexivo", "vo")',result:"verdadero"}]},(e,t)=>C("termina_con",e[0],t).endsWith(C("termina_con",e[1],t," como final"))),u("subtexto",3,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto"},{name:"inicio",type:"número"},{name:"fin",type:"número"}],returns:"texto",description:"Los caracteres desde `inicio` hasta antes de `fin`.",examples:[{code:'subtexto("hola", 1, 3)',result:'"ol"'}]},(e,t)=>{let r=C("subtexto",e[0],t),[a,n]=ze("subtexto",e,r.length,!1,t);return r.slice(a,n)}),u("caracteres",1,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto"}],returns:"lista",description:"Una lista con cada carácter del texto.",examples:[{code:'caracteres("año")',result:'["a", "ñ", "o"]'}]},(e,t)=>t.newList([...C("caracteres",e[0],t)])),u("código_de",1,{level:"intermedio",category:"texto",params:[{name:"c",type:"texto"}],returns:"número",description:"El código Unicode de un carácter.",examples:[{code:'código_de("A")',result:"65"}]},(e,t)=>{let r=C("código_de",e[0],t),a=[...r];if(a.length!==1)throw t.fail("TIPO_DE_ARGUMENTO",`\`código_de\` espera un solo carácter, pero recibió ${g(r)}.`,"Para un texto más largo, recorre `caracteres(t)`.");return a[0].codePointAt(0)}),u("carácter_de",1,{level:"intermedio",category:"texto",params:[{name:"n",type:"número"}],returns:"texto",description:"El carácter que tiene ese código Unicode.",examples:[{code:"carácter_de(241)",result:'"ñ"'}]},(e,t)=>{let r=ce("carácter_de",e[0],t);if(r<0||r>1114111||r>=55296&&r<=57343)throw t.fail("TIPO_DE_ARGUMENTO",`${r} no es el código de ningún carácter.`);return String.fromCodePoint(r)}),u("insertar",3,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista"},{name:"posición",type:"número"},{name:"x",type:"cualquiera"}],returns:"nulo",mutates:!0,description:"Pone `x` en esa posición y recorre los demás elementos.",examples:[{code:`variable l = [1, 3]
16
+ [quitar(d, "a"), d]`,result:"[1, {}]"}]},(e,t)=>{let r=e[0];if(I(r)){let i=Ge("quitar",e[1],t);if(!r.entries.has(i))throw t.fail("CLAVE_INEXISTENTE",`\`quitar\` no encontró la clave ${f(i)} en el diccionario.`,`Usa \`tiene(d, ${f(i)})\` para preguntar antes de quitarla.`);let c=r.entries.get(i);return r.entries.delete(i),c}if(!v(r))throw t.fail("TIPO_DE_ARGUMENTO",`\`quitar\` espera una lista o un diccionario, pero recibió ${y(E(r))}.`);let a=e[1];if(typeof a!=="number"||!Number.isInteger(a))throw t.fail("ÍNDICE_INVÁLIDO",`El índice tiene que ser un número entero, pero es ${f(a)}.`);let n=r.items.length,s=a<0?n+a:a;if(s<0||s>=n)throw t.fail("ÍNDICE_FUERA_DE_RANGO",`El índice ${a} está fuera de la lista, que tiene ${n} ${n===1?"elemento":"elementos"}.`,n===0?"La lista está vacía.":`Los índices válidos van de 0 a ${n-1} (o de -1 a -${n} desde el final).`);return r.items.splice(s,1)[0]}),u("primero",1,{level:"principiante",category:"listas",params:[{name:"lista",type:"lista"}],returns:"cualquiera",description:"El primer elemento de la lista.",examples:[{code:"primero([7, 8, 9])",result:"7"}]},(e,t)=>{let r=w("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]}),u("último",1,{level:"principiante",category:"listas",params:[{name:"lista",type:"lista"}],returns:"cualquiera",description:"El último elemento de la lista.",examples:[{code:"último([7, 8, 9])",result:"9"}]},(e,t)=>{let r=w("ú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]}),u("claves",1,{level:"intermedio",category:"diccionarios",params:[{name:"d",type:"diccionario"}],returns:"lista",description:"Una lista nueva con las claves del diccionario, en el orden en que se agregaron.",examples:[{code:'claves({ "a": 1, "b": 2 })',result:'["a", "b"]'}]},(e,t)=>t.newList([...ye("claves",e[0],t).entries.keys()])),u("valores",1,{level:"intermedio",category:"diccionarios",params:[{name:"d",type:"diccionario"}],returns:"lista",description:"Una lista nueva con los valores del diccionario, en el orden de sus claves.",examples:[{code:'valores({ "a": 1, "b": 2 })',result:"[1, 2]"}]},(e,t)=>t.newList([...ye("valores",e[0],t).entries.values()])),u("tiene",2,{level:"intermedio",category:"diccionarios",params:[{name:"d",type:"diccionario"},{name:"clave",type:"texto | número"}],returns:"lógico",description:"`verdadero` si el diccionario tiene esa clave.",examples:[{code:'tiene({ "a": 1 }, "a")',result:"verdadero"},{code:'tiene({ "a": 1 }, "b")',result:"falso"}]},(e,t)=>ye("tiene",e[0],t).entries.has(Ge("tiene",e[1],t))),u("piso",1,{level:"principiante",category:"matemáticas",params:[{name:"x",type:"número"}],returns:"número",description:"Redondea hacia abajo.",examples:[{code:"piso(2.7)",result:"2"},{code:"piso(-2.1)",result:"-3"}]},(e,t)=>Math.floor(D("piso",e[0],t))),u("techo",1,{level:"principiante",category:"matemáticas",params:[{name:"x",type:"número"}],returns:"número",description:"Redondea hacia arriba.",examples:[{code:"techo(2.1)",result:"3"}]},(e,t)=>Math.ceil(D("techo",e[0],t))),u("entero",1,{level:"principiante",category:"matemáticas",params:[{name:"x",type:"número"}],returns:"número",description:"Quita los decimales, sin redondear.",examples:[{code:"entero(-2.7)",result:"-2"}]},(e,t)=>Math.trunc(D("entero",e[0],t))),u("raíz",1,{level:"principiante",category:"matemáticas",params:[{name:"x",type:"número"}],returns:"número",description:"La raíz cuadrada. De un número negativo es un error.",examples:[{code:"raíz(16)",result:"4"}]},(e,t)=>{let r=D("raíz",e[0],t);if(r<0)throw t.fail("RAÍZ_DE_NEGATIVO",`\`raíz\` no tiene un resultado real para ${f(r)}.`,"La raíz cuadrada solo existe para números de 0 en adelante. ¿Quisiste usar `absoluto(…)` antes?");return Math.sqrt(r)}),u("aleatorio",2,{level:"principiante",category:"matemáticas",params:[{name:"mínimo",type:"número"},{name:"máximo",type:"número"}],returns:"número",description:"Un entero al azar entre `mínimo` y `máximo`, los dos incluidos. Con la misma semilla da los mismos números.",examples:[{code:`variable dado = aleatorio(1, 6)
17
+ dado >= 1 y dado <= 6`,result:"verdadero"}]},(e,t)=>{let r=ce("aleatorio",e[0],t," como mínimo"),a=ce("aleatorio",e[1],t," como máximo");if(r>a)throw t.fail("TIPO_DE_ARGUMENTO",`\`aleatorio\` necesita que el mínimo (${r}) no sea mayor que el máximo (${a}).`);return r+Math.floor(t.random()*(a-r+1))}),u("mayúsculas",1,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto"}],returns:"texto",description:"El mismo texto en mayúsculas.",examples:[{code:'mayúsculas("Ñandú")',result:'"ÑANDÚ"'}]},(e,t)=>L("mayúsculas",e[0],t).toLocaleUpperCase("es")),u("minúsculas",1,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto"}],returns:"texto",description:"El mismo texto en minúsculas.",examples:[{code:'minúsculas("ÁRBOL")',result:'"árbol"'}]},(e,t)=>L("minúsculas",e[0],t).toLocaleLowerCase("es")),u("recortar",1,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto"}],returns:"texto",description:"El texto sin espacios ni saltos de línea al principio y al final.",examples:[{code:'recortar(" hola ")',result:'"hola"'}]},(e,t)=>L("recortar",e[0],t).trim()),u("dividir",2,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto"},{name:"separador",type:"texto"}],returns:"lista",description:"Parte el texto en cada aparición del separador.",examples:[{code:'dividir("a,b,c", ",")',result:'["a", "b", "c"]'}]},(e,t)=>{let r=L("dividir",e[0],t),a=L("dividir",e[1],t," como separador");if(a==="")throw t.fail("SEPARADOR_VACÍO","`dividir` necesita un separador que no esté vacío.","Para separar un texto letra por letra usa `caracteres(t)`.");return t.newList(r.split(a))}),u("unir",2,{level:"intermedio",category:"texto",params:[{name:"lista",type:"lista"},{name:"separador",type:"texto"}],returns:"texto",description:"Une los elementos de la lista en un texto, con el separador entre cada uno.",examples:[{code:'unir(["a", "b", "c"], "-")',result:'"a-b-c"'}]},(e,t)=>{let r=w("unir",e[0],t),a=L("unir",e[1],t," como separador");return r.items.map(ae).join(a)}),u("reemplazar",3,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto"},{name:"buscar",type:"texto"},{name:"poner",type:"texto"}],returns:"texto",description:"Cambia cada aparición de `buscar` por `poner`.",examples:[{code:'reemplazar("la casa", "a", "o")',result:'"lo coso"'}]},(e,t)=>{let r=L("reemplazar",e[0],t),a=L("reemplazar",e[1],t," para buscar"),n=L("reemplazar",e[2],t," para poner");if(a==="")throw t.fail("SEPARADOR_VACÍO","`reemplazar` necesita un texto que buscar que no esté vacío.");return r.replaceAll(a,n)}),u("contiene",2,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto | lista"},{name:"x",type:"cualquiera"}],returns:"lógico",description:"En un texto, si aparece la parte buscada; en una lista, si está el valor (comparando por valor).",examples:[{code:'contiene("murciélago", "cié")',result:"verdadero"},{code:"contiene([1, 2], 3)",result:"falso"}]},(e,t)=>{let r=e[0];if(v(r))return r.items.some((a)=>M(a,e[1]));return qe("contiene",r,t).includes(L("contiene",e[1],t," para buscar"))}),u("posición_de",2,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto | lista"},{name:"x",type:"cualquiera"}],returns:"número",description:"Dónde aparece por primera vez la parte o el valor buscado, o `-1` si no está.",examples:[{code:'posición_de("banana", "na")',result:"2"},{code:"posición_de([5, 6], 7)",result:"-1"}]},(e,t)=>{let r=e[0];if(v(r))return r.items.findIndex((a)=>M(a,e[1]));return qe("posición_de",r,t).indexOf(L("posición_de",e[1],t," para buscar"))}),u("empieza_con",2,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto"},{name:"inicio",type:"texto"}],returns:"lógico",description:"`verdadero` si el texto empieza con `inicio`.",examples:[{code:'empieza_con("Codexivo", "Code")',result:"verdadero"}]},(e,t)=>L("empieza_con",e[0],t).startsWith(L("empieza_con",e[1],t," como inicio"))),u("termina_con",2,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto"},{name:"final",type:"texto"}],returns:"lógico",description:"`verdadero` si el texto termina con `final`.",examples:[{code:'termina_con("Codexivo", "vo")',result:"verdadero"}]},(e,t)=>L("termina_con",e[0],t).endsWith(L("termina_con",e[1],t," como final"))),u("subtexto",3,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto"},{name:"inicio",type:"número"},{name:"fin",type:"número"}],returns:"texto",description:"Los caracteres desde `inicio` hasta antes de `fin`.",examples:[{code:'subtexto("hola", 1, 3)',result:'"ol"'}]},(e,t)=>{let r=L("subtexto",e[0],t),[a,n]=ze("subtexto",e,r.length,!1,t);return r.slice(a,n)}),u("caracteres",1,{level:"intermedio",category:"texto",params:[{name:"t",type:"texto"}],returns:"lista",description:"Una lista con cada carácter del texto.",examples:[{code:'caracteres("año")',result:'["a", "ñ", "o"]'}]},(e,t)=>t.newList([...L("caracteres",e[0],t)])),u("código_de",1,{level:"intermedio",category:"texto",params:[{name:"c",type:"texto"}],returns:"número",description:"El código Unicode de un carácter.",examples:[{code:'código_de("A")',result:"65"}]},(e,t)=>{let r=L("código_de",e[0],t),a=[...r];if(a.length!==1)throw t.fail("TIPO_DE_ARGUMENTO",`\`código_de\` espera un solo carácter, pero recibió ${f(r)}.`,"Para un texto más largo, recorre `caracteres(t)`.");return a[0].codePointAt(0)}),u("carácter_de",1,{level:"intermedio",category:"texto",params:[{name:"n",type:"número"}],returns:"texto",description:"El carácter que tiene ese código Unicode.",examples:[{code:"carácter_de(241)",result:'"ñ"'}]},(e,t)=>{let r=ce("carácter_de",e[0],t);if(r<0||r>1114111||r>=55296&&r<=57343)throw t.fail("TIPO_DE_ARGUMENTO",`${r} no es el código de ningún carácter.`);return String.fromCodePoint(r)}),u("insertar",3,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista"},{name:"posición",type:"número"},{name:"x",type:"cualquiera"}],returns:"nulo",mutates:!0,description:"Pone `x` en esa posición y recorre los demás elementos.",examples:[{code:`variable l = [1, 3]
16
18
  insertar(l, 1, 2)
17
- l`,result:"[1, 2, 3]"}]},(e,t)=>{let r=w("insertar",e[0],t),a=xe("insertar",e[1],r.items.length,!0,t,!0);return r.items.splice(a,0,e[2]),null}),u("quitar_último",1,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista"}],returns:"cualquiera",mutates:!0,description:"Quita el último elemento y lo regresa.",examples:[{code:`variable l = [1, 2]
18
- [quitar_último(l), l]`,result:"[2, [1]]"}]},(e,t)=>{let r=w("quitar_último",e[0],t);if(r.items.length===0)throw Fe("quitar_último",t);return r.items.pop()}),u("quitar_primero",1,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista"}],returns:"cualquiera",mutates:!0,description:"Quita el primer elemento y lo regresa.",examples:[{code:`variable l = [1, 2]
19
- [quitar_primero(l), l]`,result:"[1, [2]]"}]},(e,t)=>{let r=w("quitar_primero",e[0],t);if(r.items.length===0)throw Fe("quitar_primero",t);return r.items.shift()}),u("vaciar",1,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista | diccionario"}],returns:"nulo",mutates:!0,description:"Quita todos los elementos de una lista (o todas las claves de un diccionario).",examples:[{code:`variable l = [1, 2]
19
+ l`,result:"[1, 2, 3]"}]},(e,t)=>{let r=w("insertar",e[0],t),a=ve("insertar",e[1],r.items.length,!0,t,!0);return r.items.splice(a,0,e[2]),null}),u("quitar_último",1,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista"}],returns:"cualquiera",mutates:!0,description:"Quita el último elemento y lo regresa.",examples:[{code:`variable l = [1, 2]
20
+ [quitar_último(l), l]`,result:"[2, [1]]"}]},(e,t)=>{let r=w("quitar_último",e[0],t);if(r.items.length===0)throw Ue("quitar_último",t);return r.items.pop()}),u("quitar_primero",1,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista"}],returns:"cualquiera",mutates:!0,description:"Quita el primer elemento y lo regresa.",examples:[{code:`variable l = [1, 2]
21
+ [quitar_primero(l), l]`,result:"[1, [2]]"}]},(e,t)=>{let r=w("quitar_primero",e[0],t);if(r.items.length===0)throw Ue("quitar_primero",t);return r.items.shift()}),u("vaciar",1,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista | diccionario"}],returns:"nulo",mutates:!0,description:"Quita todos los elementos de una lista (o todas las claves de un diccionario).",examples:[{code:`variable l = [1, 2]
20
22
  vaciar(l)
21
- l`,result:"[]"}]},(e,t)=>{let r=e[0];if(I(r))r.entries.clear();else w("vaciar",r,t).items.length=0;return null}),u("rebanar",3,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista"},{name:"inicio",type:"número"},{name:"fin",type:"número"}],returns:"lista",description:"Una lista nueva con los elementos desde `inicio` hasta antes de `fin`.",examples:[{code:"rebanar([1, 2, 3, 4], 1, 3)",result:"[2, 3]"}]},(e,t)=>{let r=w("rebanar",e[0],t),[a,n]=ze("rebanar",e,r.items.length,!0,t);return t.newList(r.items.slice(a,n))}),u("invertir",1,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista | texto"}],returns:"lista | texto",description:"Una lista (o un texto) nueva al revés.",examples:[{code:"invertir([1, 2, 3])",result:"[3, 2, 1]"},{code:'invertir("hola")',result:'"aloh"'}]},(e,t)=>{let r=e[0];if(typeof r==="string")return[...r].reverse().join("");if(y(r))return t.newList([...r.items].reverse());throw t.fail("TIPO_DE_ARGUMENTO",`\`invertir\` espera una lista o un texto, pero recibió ${v(b(r))}.`)}),u("copiar",1,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista | diccionario"}],returns:"lista | diccionario",description:"Una lista o diccionario nuevo con los mismos elementos. Los elementos que son listas se siguen compartiendo.",examples:[{code:`variable a = [1]
23
+ l`,result:"[]"}]},(e,t)=>{let r=e[0];if(I(r))r.entries.clear();else w("vaciar",r,t).items.length=0;return null}),u("rebanar",3,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista"},{name:"inicio",type:"número"},{name:"fin",type:"número"}],returns:"lista",description:"Una lista nueva con los elementos desde `inicio` hasta antes de `fin`.",examples:[{code:"rebanar([1, 2, 3, 4], 1, 3)",result:"[2, 3]"}]},(e,t)=>{let r=w("rebanar",e[0],t),[a,n]=ze("rebanar",e,r.items.length,!0,t);return t.newList(r.items.slice(a,n))}),u("invertir",1,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista | texto"}],returns:"lista | texto",description:"Una lista (o un texto) nueva al revés.",examples:[{code:"invertir([1, 2, 3])",result:"[3, 2, 1]"},{code:'invertir("hola")',result:'"aloh"'}]},(e,t)=>{let r=e[0];if(typeof r==="string")return[...r].reverse().join("");if(v(r))return t.newList([...r.items].reverse());throw t.fail("TIPO_DE_ARGUMENTO",`\`invertir\` espera una lista o un texto, pero recibió ${y(E(r))}.`)}),u("copiar",1,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista | diccionario"}],returns:"lista | diccionario",description:"Una lista o diccionario nuevo con los mismos elementos. Los elementos que son listas se siguen compartiendo.",examples:[{code:`variable a = [1]
22
24
  variable b = copiar(a)
23
25
  agregar(b, 2)
24
- [a, b]`,result:"[[1], [1, 2]]"}]},(e,t)=>{let r=e[0];if(I(r))return t.newDict(new Map(r.entries));return t.newList([...w("copiar",r,t).items])}),u("sumar",1,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista"}],returns:"número",description:"La suma de los números de la lista.",examples:[{code:"sumar([1, 2, 3.5])",result:"6.5"}]},(e,t)=>w("sumar",e[0],t).items.reduce((r,a)=>r+D("sumar",a,t," en cada elemento"),0)),u("lista_de",2,{level:"intermedio",category:"listas",params:[{name:"n",type:"número"},{name:"valor",type:"cualquiera"}],returns:"lista",description:"Una lista con `n` copias de `valor`. Si el valor es una lista, cada casilla tiene su propia copia.",examples:[{code:"lista_de(3, 0)",result:"[0, 0, 0]"}]},(e,t)=>{let r=ye("lista_de",e[0],t," como cantidad");return t.newList(Array.from({length:r},()=>pe(e[1],t)))}),u("matriz",3,{level:"intermedio",category:"listas",params:[{name:"filas",type:"número"},{name:"columnas",type:"número"},{name:"valor",type:"cualquiera"}],returns:"lista",description:"Una lista de filas, cada una con `columnas` copias de `valor`.",examples:[{code:"matriz(2, 3, 0)",result:"[[0, 0, 0], [0, 0, 0]]"}]},(e,t)=>{let r=ye("matriz",e[0],t," como filas"),a=ye("matriz",e[1],t," como columnas");if(r*a>W)throw t.fail("TIPO_DE_ARGUMENTO",`\`matriz\` produciría ${r*a} casillas; el máximo es ${W}.`);return t.newList(Array.from({length:r},()=>t.newList(Array.from({length:a},()=>pe(e[2],t)))))}),u("apilar",2,{level:"avanzado",category:"estructuras",params:[{name:"pila",type:"lista"},{name:"x",type:"cualquiera"}],returns:"nulo",mutates:!0,description:"Pone `x` encima de la pila.",examples:[{code:`variable p = []
26
+ [a, b]`,result:"[[1], [1, 2]]"}]},(e,t)=>{let r=e[0];if(I(r))return t.newDict(new Map(r.entries));return t.newList([...w("copiar",r,t).items])}),u("sumar",1,{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista"}],returns:"número",description:"La suma de los números de la lista.",examples:[{code:"sumar([1, 2, 3.5])",result:"6.5"}]},(e,t)=>w("sumar",e[0],t).items.reduce((r,a)=>r+D("sumar",a,t," en cada elemento"),0)),u("lista_de",2,{level:"intermedio",category:"listas",params:[{name:"n",type:"número"},{name:"valor",type:"cualquiera"}],returns:"lista",description:"Una lista con `n` copias de `valor`. Si el valor es una lista, cada casilla tiene su propia copia.",examples:[{code:"lista_de(3, 0)",result:"[0, 0, 0]"}]},(e,t)=>{let r=Ee("lista_de",e[0],t," como cantidad");return t.newList(Array.from({length:r},()=>pe(e[1],t)))}),u("matriz",3,{level:"intermedio",category:"listas",params:[{name:"filas",type:"número"},{name:"columnas",type:"número"},{name:"valor",type:"cualquiera"}],returns:"lista",description:"Una lista de filas, cada una con `columnas` copias de `valor`.",examples:[{code:"matriz(2, 3, 0)",result:"[[0, 0, 0], [0, 0, 0]]"}]},(e,t)=>{let r=Ee("matriz",e[0],t," como filas"),a=Ee("matriz",e[1],t," como columnas");if(r*a>j)throw t.fail("TIPO_DE_ARGUMENTO",`\`matriz\` produciría ${r*a} casillas; el máximo es ${j}.`);return t.newList(Array.from({length:r},()=>t.newList(Array.from({length:a},()=>pe(e[2],t)))))}),u("apilar",2,{level:"avanzado",category:"estructuras",params:[{name:"pila",type:"lista"},{name:"x",type:"cualquiera"}],returns:"nulo",mutates:!0,description:"Pone `x` encima de la pila.",examples:[{code:`variable p = []
25
27
  apilar(p, 1)
26
28
  apilar(p, 2)
27
29
  p`,result:"[1, 2]"}]},(e,t)=>(G("apilar","pila",e[0],t).items.push(e[1]),null)),u("desapilar",1,{level:"avanzado",category:"estructuras",params:[{name:"pila",type:"lista"}],returns:"cualquiera",mutates:!0,description:"Quita el elemento de encima de la pila y lo regresa.",examples:[{code:`variable p = [1, 2]
@@ -29,7 +31,7 @@ p`,result:"[1, 2]"}]},(e,t)=>(G("apilar","pila",e[0],t).items.push(e[1]),null)),
29
31
  encolar(c, 1)
30
32
  encolar(c, 2)
31
33
  c`,result:"[1, 2]"}]},(e,t)=>(G("encolar","cola",e[0],t).items.push(e[1]),null)),u("desencolar",1,{level:"avanzado",category:"estructuras",params:[{name:"cola",type:"lista"}],returns:"cualquiera",mutates:!0,description:"Quita el primer elemento de la cola y lo regresa.",examples:[{code:`variable c = [1, 2]
32
- [desencolar(c), c]`,result:"[1, [2]]"}]},(e,t)=>{let r=G("desencolar","cola",e[0],t);if(r.items.length===0)throw oe("desencolar","cola",t);return r.items.shift()}),u("frente",1,{level:"avanzado",category:"estructuras",params:[{name:"cola",type:"lista"}],returns:"cualquiera",description:"El primer elemento de la cola, sin quitarlo.",examples:[{code:"frente([1, 2])",result:"1"}]},(e,t)=>{let r=G("frente","cola",e[0],t);if(r.items.length===0)throw oe("frente","cola",t);return r.items[0]})],le={PI:Math.PI},pr={PI:{level:"principiante",description:"La razón entre el perímetro de un círculo y su diámetro: 3.14159…"}},Ct=(e)=>e.replace(/[áéíóúü]/g,(t)=>({á:"a",é:"e",í:"i",ó:"o",ú:"u",ü:"u"})[t]??t),He=(()=>{let e={};for(let t of Ke)e[t.name]=t;for(let t of Ke){let r=Ct(t.name);if(!e[r])e[r]=t}return e})(),Qe=Object.fromEntries([ie("revisar_lista",2,(e,t)=>(w(e[0],e[1],t),null)),ie("revisar_procedimiento",2,(e,t)=>{let r=e[1];if(re(r))return null;throw t.fail("TIPO_DE_ARGUMENTO",`\`${e[0]}\` espera un procedimiento, pero recibió ${v(b(r))} (${g(r)}).`,"Pasa el nombre del procedimiento sin paréntesis (`mapear(lista, doble)`), o escribe uno: `procedimiento(x) { regresa x * 2 }`.")}),ie("revisar_logico",2,(e,t)=>{let r=e[1];if(typeof r==="boolean")return r;throw t.fail("TIPO_DE_ARGUMENTO",`El procedimiento que recibió \`${e[0]}\` tiene que regresar \`verdadero\` o \`falso\`, pero regresó ${g(r)}.`,e[0]==="ordenar"?"El comparador de `ordenar(lista, va_antes)` responde «¿va `a` antes que `b`?»: `procedimiento(a, b) { regresa a < b }`.":"¿Le falta un `regresa` con una comparación, como `regresa x > 0`?")}),ie("ordenar_natural",1,(e,t)=>{let r=w("ordenar",e[0],t),a=At(r,t),n=[...r.items].sort((s,i)=>a==="número"?s-i:s<i?-1:s>i?1:0);return t.newList(n)})].map((e)=>[e.name,e]));var Lt=`
34
+ [desencolar(c), c]`,result:"[1, [2]]"}]},(e,t)=>{let r=G("desencolar","cola",e[0],t);if(r.items.length===0)throw oe("desencolar","cola",t);return r.items.shift()}),u("frente",1,{level:"avanzado",category:"estructuras",params:[{name:"cola",type:"lista"}],returns:"cualquiera",description:"El primer elemento de la cola, sin quitarlo.",examples:[{code:"frente([1, 2])",result:"1"}]},(e,t)=>{let r=G("frente","cola",e[0],t);if(r.items.length===0)throw oe("frente","cola",t);return r.items[0]})],le={PI:Math.PI},lr={PI:{level:"principiante",description:"La razón entre el perímetro de un círculo y su diámetro: 3.14159…"}},Lt=(e)=>e.replace(/[áéíóúü]/g,(t)=>({á:"a",é:"e",í:"i",ó:"o",ú:"u",ü:"u"})[t]??t),He=(()=>{let e={};for(let t of Ke)e[t.name]=t;for(let t of Ke){let r=Lt(t.name);if(!e[r])e[r]=t}return e})(),Qe=Object.fromEntries([ie("revisar_lista",2,(e,t)=>(w(e[0],e[1],t),null)),ie("revisar_procedimiento",2,(e,t)=>{let r=e[1];if(re(r))return null;throw t.fail("TIPO_DE_ARGUMENTO",`\`${e[0]}\` espera un procedimiento, pero recibió ${y(E(r))} (${f(r)}).`,"Pasa el nombre del procedimiento sin paréntesis (`mapear(lista, doble)`), o escribe uno: `procedimiento(x) { regresa x * 2 }`.")}),ie("revisar_logico",2,(e,t)=>{let r=e[1];if(typeof r==="boolean")return r;throw t.fail("TIPO_DE_ARGUMENTO",`El procedimiento que recibió \`${e[0]}\` tiene que regresar \`verdadero\` o \`falso\`, pero regresó ${f(r)}.`,e[0]==="ordenar"?"El comparador de `ordenar(lista, va_antes)` responde «¿va `a` antes que `b`?»: `procedimiento(a, b) { regresa a < b }`.":"¿Le falta un `regresa` con una comparación, como `regresa x > 0`?")}),ie("ordenar_natural",1,(e,t)=>{let r=w("ordenar",e[0],t),a=At(r,t),n=[...r.items].sort((s,i)=>a==="número"?s-i:s<i?-1:s>i?1:0);return t.newList(n)})].map((e)=>[e.name,e]));var Ct=`
33
35
  procedimiento mapear(lista, f) {
34
36
  revisar_lista("mapear", lista);
35
37
  revisar_procedimiento("mapear", f);
@@ -134,20 +136,20 @@ procedimiento ordenar(lista, va_antes) {
134
136
  }
135
137
  regresa mezclar(lista);
136
138
  }
137
- `,j=()=>[{name:"lista",type:"lista"},{name:"f",type:"procedimiento"}],hr={mapear:{level:"avanzado",category:"procedimientos",params:j(),returns:"lista",description:"Una lista nueva con el resultado de `f(x)` para cada elemento.",examples:[{code:"mapear([1, 2, 3], procedimiento(x) { regresa x * 2 })",result:"[2, 4, 6]"}]},filtrar:{level:"avanzado",category:"procedimientos",params:j(),returns:"lista",description:"Una lista nueva con los elementos para los que `f(x)` es `verdadero`.",examples:[{code:"filtrar([1, 2, 3, 4], procedimiento(x) { regresa x mod 2 == 0 })",result:"[2, 4]"}]},reducir:{level:"avanzado",category:"procedimientos",params:[...j(),{name:"inicial",type:"cualquiera"}],returns:"cualquiera",description:"Junta la lista en un solo valor: empieza con `inicial` y en cada elemento hace `f(acumulado, x)`.",examples:[{code:"reducir([1, 2, 3], procedimiento(total, x) { regresa total + x }, 0)",result:"6"}]},alguno:{level:"avanzado",category:"procedimientos",params:j(),returns:"lógico",description:"`verdadero` si `f(x)` es `verdadero` para al menos un elemento.",examples:[{code:"alguno([1, 5], procedimiento(x) { regresa x > 3 })",result:"verdadero"}]},todos:{level:"avanzado",category:"procedimientos",params:j(),returns:"lógico",description:"`verdadero` si `f(x)` es `verdadero` para todos los elementos.",examples:[{code:"todos([1, 5], procedimiento(x) { regresa x > 3 })",result:"falso"}]},buscar:{level:"avanzado",category:"procedimientos",params:j(),returns:"cualquiera",description:"El primer elemento para el que `f(x)` es `verdadero`, o `nulo` si no hay.",examples:[{code:"buscar([1, 5, 7], procedimiento(x) { regresa x > 3 })",result:"5"}]},ordenar:{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista"},{name:"va_antes",type:"procedimiento",optional:!0}],returns:"lista",description:"Una lista nueva ordenada. Sin comparador, todos tienen que ser números o todos textos; `va_antes(a, b)` responde «¿va `a` antes que `b`?». Es estable.",examples:[{code:"ordenar([3, 1, 2])",result:"[1, 2, 3]"},{code:"ordenar([3, 1, 2], procedimiento(a, b) { regresa a > b })",result:"[3, 2, 1]"}]}},Dt={ordenar:1},$t=()=>{let{program:e,errors:t}=q(Lt);if(t.length>0)throw Error(`La biblioteca en Codexivo tiene errores: ${t[0].message}`);let r=new Map;for(let a of e.statements){if(a.kind!=="FunctionDeclaration")continue;let n=ee(a.function,!0),s=Dt[a.name.name];if(s!==void 0)n.minParams=s;r.set(a.name.name,n)}return r},ue=(()=>{let e=new U,t={},r=-1;for(let[a,n]of $t()){let s={kind:"procedimiento",id:r--,name:a,chunk:n,closure:e};e.variables.set(a,s),t[a]=s}return t})();var wt=(e,t)=>{let r=Array.from({length:t.length+1},(a,n)=>n);for(let a=1;a<=e.length;a++){let n=r[0];r[0]=a;for(let s=1;s<=t.length;s++){let i=r[s],c=e[a-1]===t[s-1]?0:1;r[s]=Math.min(r[s]+1,r[s-1]+1,n+c),n=i}}return r[t.length]},Je=(e)=>e.normalize("NFD").replace(/[̀-ͯ]/g,"").toLowerCase(),Se=(e,t)=>{let r=Je(e),a=r.length<=3?1:2,n,s=1/0;for(let i of new Set(t)){if(i===e)continue;let c=wt(r,Je(i));if(c<=a&&c<s)n=i,s=c}return n};var O=(e)=>{let t=g(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};if(e.kind==="diccionario")return{type:"diccionario",ref:e.id,size:e.entries.size,repr:t};if(e.kind==="dominio")return{type:"dominio",ref:e.id,name:e.name,repr:t};return{type:"procedimiento",name:e.name,repr:t}},Re={maxOperations:1e7,maxCallDepth:1e4,maxOutput:1e6},Pt=new Set(["llama","regresa","imprime","lee","error","fin"]),Xe=new Set(["step","enterScope","exitScope","jump","dup","pop","repeatStart","forEachStart","rangeStart","halt"]);class Q{options;frames=[];stack=[];natives;inputQueue;breakpoints;limits;onEvent;outputParts=[];outputLength=0;nextId=1;lastValue=null;inputClosed=!1;currentNode;seed;randomState;status="lista";operations=0;steps=0;pauseReason;result=null;error;constructor(e,t={}){this.options=t;this.natives={...He,...t.world?.natives,...t.natives},this.limits={maxOperations:t.maxOperations??Re.maxOperations,maxCallDepth:t.maxCallDepth??Re.maxCallDepth,maxOutput:t.maxOutput??Re.maxOutput},this.inputQueue=typeof t.input==="string"?Ye(t.input):[...t.input??[]],this.breakpoints=new Set(t.breakpoints??[]),this.onEvent=t.onEvent,this.seed=(t.seed??Math.floor(Math.random()*4294967296))>>>0,this.randomState=this.seed;let r=t.globals??new U;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)}setBreakpoints(e){this.breakpoints=new Set(e)}provideInput(e){if(this.inputQueue.push(...Ye(e)),this.status==="esperando_entrada")this.status="pausada"}closeInput(){if(this.inputClosed=!0,this.status==="esperando_entrada")this.status="pausada"}snapshot(){let e={},t=(a)=>{if(!y(a)&&!I(a)&&!z(a)||e[a.id])return;if(z(a))e[a.id]={domain:a.name,members:[...a.scope.variables].map(([n,s])=>({name:n,value:O(s),writable:a.writable.has(n)}))},a.scope.variables.forEach(t);else if(y(a))e[a.id]=a.items.map(O),a.items.forEach(t);else e[a.id]={entries:[...a.entries].map(([n,s])=>({key:O(n),value:O(s)}))},a.entries.forEach(t)},r=this.frames.map((a)=>{let n=[],s=a.scope;while(s){let i={};for(let[c,o]of s.variables)i[c]=O(o),t(o);if(n.unshift(i),s===a.root)break;s=s.parent}if(a.chunk.hidden)return{name:a.name,callSpan:a.callNode?.span,library:!0,scopes:[]};return{name:a.name,callSpan:a.callNode?.span,scopes:n}});return{status:this.status,operations:this.operations,steps:this.steps,nodeId:this.currentNode?.id,span:this.currentNode?.span,frames:r,heap:e,output:this.output,...this.options.world?{world:this.options.world.state()}:{}}}execute(e,t){if(this.done||this.status==="esperando_entrada")return this.status;let r=this.operations+t,a=0;try{while(!0){let n=this.frames[this.frames.length-1],s=n.chunk.code[n.pc];if(!Xe.has(s.op)&&!n.chunk.hidden){if(this.operations>=this.limits.maxOperations)throw this.limitError("DEMASIADAS_OPERACIONES",`El programa superó el límite de ${Ne(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.pauseReason="presupuesto",this.status}if(s.op==="step"&&e==="run"&&!n.chunk.hidden&&a>0&&this.breakpoints.has(s.node.span.start.line))return this.status="pausada",this.pauseReason="punto_de_interrupción",this.status;let i=this.exec(n,s);a+=1;let c=this.status;if(c==="esperando_entrada"||c==="terminada")return c;if(i&&!n.chunk.hidden){if(this.steps+=1,e==="step")return this.status="pausada",this.pauseReason="paso",this.status}}}catch(n){if(!(n instanceof P))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,!Xe.has(t.op)&&!e.chunk.hidden)this.operations+=1;switch(t.op){case"step":if(e.chunk.hidden)return!1;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"dict":{let r=this.stack.splice(this.stack.length-t.count*2,t.count*2),a=new Map;return t.node.entries.forEach((n,s)=>{a.set(this.dictKey(r[s*2],n.key),r[s*2+1])}),this.stack.push(this.newDict(a)),!1}case"load":{let r=e.scope.lookup(t.name);if(r)return this.stack.push(r.variables.get(t.name)),!1;let a=(e.chunk.hidden?Qe[t.name]:void 0)??this.natives[t.name]??ue[t.name]??le[t.name];if(a!==void 0&&!t.forUpdate)return this.stack.push(a),!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.target,`Para cambiar su valor escribe \`${t.name} = …\`, sin \`variable\`.`);if(e.scope.variables.set(t.name,r),t.constant)e.scope.constants.add(t.name);else e.scope.constants.delete(t.name);if(t.loop)e.scope.loopVariables.add(t.name);else e.scope.loopVariables.delete(t.name);return this.emit({type:"declara",name:t.name,constant:t.constant,value:O(r),nodeId:t.node.id,span:t.node.span}),!1}case"store":{let r=this.stack.pop(),a=e.scope.lookup(t.name);if(!a)throw this.undeclaredAssignment(t.name,t.target);if(a.loopVariables.has(t.name))throw this.runtimeError("VARIABLE_DE_PARA",`La variable de un \`para\` avanza sola; \`${t.name}\` no se puede cambiar dentro del bucle.`,t.node,`Si necesitas otro valor, guárdalo en una variable nueva: \`variable otro = ${t.name} + 1\`.`);if(a.constants.has(t.name)){if(z(a.variables.get(t.name)))throw this.runtimeError("CONSTANTE_REASIGNADA",`\`${t.name}\` es un dominio; su nombre no se puede usar para otra cosa.`,t.node,`Para cambiar un miembro escribe \`${t.name}.miembro = …\`; para otro valor, usa otro nombre.`);throw this.constantReassignment(t.name,t.node)}let n=a.variables.get(t.name);if(a.variables.set(t.name,r),this.onEvent)this.emit({type:"asigna",name:t.name,before:O(n),after:O(r),nodeId:t.node.id,span:t.node.span});return!1}case"setIndex":{let r=this.stack.pop(),a=this.stack.pop(),n=this.stack.pop();return this.setIndex(n,a,r,t.target,t.node),!1}case"dup":return this.stack.push(...this.stack.slice(this.stack.length-t.count)),!1;case"pop":return this.stack.length-=t.count,!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(),a=this.unary(t.operator,r,t.node);if(this.stack.push(a),this.onEvent)this.emitOperation(t.operator,[r],a,t.node);return!1}case"binary":{let r=this.stack.pop(),a=this.stack.pop(),n=this.binary(t.operator,a,r,t.node);if(this.stack.push(n),this.onEvent)this.emitOperation(t.operator,[a,r],n,t.node);return!1}case"index":{let r=this.stack.pop(),a=this.stack.pop();return this.stack.push(this.index(a,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;let a=this.frames[this.frames.length-2];if(!(e.chunk.hidden&&a.chunk.hidden))this.emit({type:"regresa",name:e.name,value:O(r)});return 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(!e.chunk.hidden)this.currentNode=t.node;if(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"repeatStart":{let r=this.stack[this.stack.length-1];if(typeof r!=="number"||!Number.isInteger(r)||r<0)throw this.invalidRepeatCount(r,t.node);return!1}case"repeatNext":{let r=this.stack[this.stack.length-1],a=r>0;if(a)this.stack[this.stack.length-1]=r-1;else this.stack.pop();if(!e.chunk.hidden)this.currentNode=t.node;if(this.emit({type:"decide",label:"repetir",value:a,nodeId:t.node.id,ownerId:t.owner.id,span:t.node.span}),!a)e.pc=t.target;return!0}case"rangeStart":{let r=this.stack.pop(),a=this.stack.pop(),n=this.stack.pop(),{owner:s}=t,i=(h,l,d)=>{if(typeof h==="number"&&Number.isFinite(h))return h;throw this.runtimeError("PARA_LÍMITE_INVÁLIDO",`${d} de \`para\` tiene que ser un número, pero es ${g(h)}.`,l,typeof h==="string"?"Convierte el texto con `número(…)`.":void 0)},c=i(n,s.from,"El inicio"),o=i(a,s.to,"El final"),m=i(r,s.step??s,"El paso");if(m===0)throw this.runtimeError("PARA_PASO_CERO","El paso de un `para` no puede ser 0: nunca llegaría al final.",s.step??s,"Usa un paso positivo para contar hacia arriba o uno negativo para contar hacia abajo.");if((o-c)*m<0)throw this.runtimeError("PARA_SENTIDO_CONTRARIO",m>0?`El \`para\` empieza en ${g(c)}, que es mayor que ${g(o)}, y cuenta hacia arriba.`:`El \`para\` empieza en ${g(c)}, que es menor que ${g(o)}, y cuenta hacia abajo.`,t.explicitStep?s.step:s.to,t.explicitStep?`Para ir de ${g(c)} a ${g(o)} el paso tiene que ser ${m>0?"negativo":"positivo"}.`:"Para contar hacia atrás escribe `con_paso -1`.");let x=Math.floor((o-c)/m+0.000000001)+1;return this.stack.push(c,m,x,0),!1}case"rangeNext":{let r=this.stack[this.stack.length-1],a=this.stack[this.stack.length-2],n=r<a;if(n){let s=this.stack[this.stack.length-4],i=this.stack[this.stack.length-3];this.stack[this.stack.length-1]=r+1,this.stack.push(Number((s+r*i).toPrecision(15)))}else this.stack.length-=4;if(!e.chunk.hidden)this.currentNode=t.node;if(this.emit({type:"decide",label:"para",value:n,nodeId:t.node.id,ownerId:t.owner.id,span:t.node.span}),!n)e.pc=t.target;return!0}case"forEachStart":{let r=this.stack[this.stack.length-1],{owner:a}=t;if(I(r)){let n={kind:"lista",id:0,items:[...r.entries.keys()]};return this.stack.push(n,0),!1}if(a.value)throw this.runtimeError("PARA_CADA_INVÁLIDO",`\`para_cada ${a.variable.name}, ${a.value.name}\` recorre un diccionario, no ${v(b(r))}.`,t.node,y(r)||typeof r==="string"?`Para una lista o un texto usa una sola variable: \`para_cada ${a.value.name} en …\`.`:void 0);if(!y(r)&&typeof r!=="string")throw this.runtimeError("PARA_CADA_INVÁLIDO",`\`para_cada\` recorre listas, textos y diccionarios, no ${v(b(r))}.`,t.node,typeof r==="number"?`Para contar, recorre un rango: \`rango(1, ${g(r)})\`.`:void 0);return this.stack.push(y(r)?r.items.length:r.length,0),!1}case"forEachNext":{let r=this.stack[this.stack.length-1],a=this.stack[this.stack.length-2],n=this.stack[this.stack.length-3],s=typeof a==="number"?a:a.items.length;if(y(n)&&n.items.length!==s)throw this.runtimeError("LISTA_MODIFICADA_AL_RECORRER",`La lista cambió de tamaño mientras se recorría: tenía ${s} y ahora tiene ${n.items.length}.`,t.node,"Recorre una copia o junta los cambios en otra lista y aplícalos al terminar el `para_cada`.");if(I(n)&&(n.entries.size!==s||r<s&&!n.entries.has(a.items[r])))throw this.runtimeError("DICCIONARIO_MODIFICADO_AL_RECORRER","Se agregaron o quitaron claves del diccionario mientras se recorría.",t.node,"Recorre `claves(d)`, que es una lista nueva, o junta los cambios y aplícalos al terminar.");let i=r<s;if(i)if(this.stack[this.stack.length-1]=r+1,I(n)){let c=a.items[r];if(t.owner.value)this.stack.push(n.entries.get(c));this.stack.push(c)}else this.stack.push(y(n)?n.items[r]:n[r]);else this.stack.length-=3;if(!e.chunk.hidden)this.currentNode=t.node;if(this.emit({type:"decide",label:"para_cada",value:i,nodeId:t.node.id,ownerId:t.owner.id,span:t.node.span}),!i)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 a=t.node.operator==="y";this.emitOperation(t.node.operator,[a,r],r,t.node)}return!1}case"enterScope":case"domainStart":return e.scope=new U(e.scope),!1;case"domainEnd":{let r={kind:"dominio",id:this.nextId++,name:t.name,scope:e.scope,writable:new Set(t.writable)};if(e.scope=e.scope.parent??e.scope,e.scope.variables.has(t.name)&&!this.options.allowRedeclare)throw this.runtimeError("VARIABLE_REPETIDA",`Ya existe algo llamado \`${t.name}\` en este bloque.`,t.node.name,"Cada dominio necesita un nombre propio en su bloque.");return e.scope.variables.set(t.name,r),e.scope.constants.add(t.name),this.emit({type:"declara",name:t.name,constant:!0,value:O(r),nodeId:t.node.id,span:t.node.span}),!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:O(e)})}call(e,t,r){let a=this.stack.length-t-1,n=this.stack[a],s=n!==null&&typeof n==="object"&&"name"in n&&n.name?n.name:Ze(r);if(!re(n))throw this.runtimeError("NO_ES_PROCEDIMIENTO",`\`${Ze(r)}\` no es un procedimiento, es ${v(b(n))}.`,r.callee);let i=this.stack.slice(a+1),c=n.kind==="nativo"?n.arity:n.chunk.minParams!==void 0?[n.chunk.minParams,n.chunk.params.length]:n.chunk.params.length;if(Mt(c,t,s,(m,x)=>this.runtimeError("CANTIDAD_DE_ARGUMENTOS",m,r,x??(e.chunk.hidden?`\`${e.name}\` llama al procedimiento que recibió con ${t===1?"1 valor":`${t} valores`}.`:void 0))),n.kind==="nativo"){let m=n.call(i,this.nativeContext(r));if(m===H){if(e.pc-=1,!e.chunk.hidden)this.operations-=1;return this.status="esperando_entrada",!0}if(!e.chunk.hidden)this.charge(n.cost,i);return this.stack.length=a,this.stack.push(m),!1}if(this.frames.length-1>=this.limits.maxCallDepth)throw this.limitError("DEMASIADAS_LLAMADAS",`Hay más de ${Ne(this.limits.maxCallDepth)} llamadas anidadas.`,"Si es recursión, revisa que exista un caso base que deje de llamar al procedimiento.",r);if(n.chunk.hidden&&!e.chunk.hidden)this.charge(se[n.chunk.name],i);let o=new U(n.closure);if(n.chunk.params.forEach((m,x)=>o.variables.set(m,i[x]??null)),!(e.chunk.hidden&&n.chunk.hidden))this.emit({type:"llama",name:s,args:i.map(O),nodeId:r.id,span:r.span});return this.frames.push({chunk:n.chunk,pc:0,scope:o,root:o,stackBase:a+1,name:s,callNode:r}),!1}charge(e,t){if(e)this.operations+=Math.max(0,e(t)-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 ${Ne(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 H;return this.emit({type:"lee",text:null}),null},newList:(t)=>this.newList(t),newDict:(t)=>this.newDict(t),random:()=>this.random(),fail:(t,r,a)=>this.runtimeError(t,r,e,a),emit:(t)=>this.emit({...t,nodeId:e.id,span:e.span})}}newList(e){return{kind:"lista",id:this.nextId++,items:e}}random(){this.randomState=this.randomState+1831565813>>>0;let e=this.randomState;return e=Math.imul(e^e>>>15,e|1),e^=e+Math.imul(e^e>>>7,e|61),((e^e>>>14)>>>0)/4294967296}newDict(e){return{kind:"diccionario",id:this.nextId++,entries:e}}dictKey(e,t){if(typeof e==="string"||typeof e==="number")return e;throw this.runtimeError("CLAVE_INVÁLIDA",`Las claves de un diccionario son textos o números, no ${v(b(e))}.`,t)}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 ${v(b(t))}.`,r);return-t}if(typeof t!=="boolean")throw this.runtimeError("TIPO_INVÁLIDO",`\`no\` solo funciona con \`verdadero\` o \`falso\`, pero recibió ${g(t)}.`,r.operand,Oe(t));return!t}binary(e,t,r,a){switch(e){case"==":return V(t,r);case"!=":return!V(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 ${v(b(n))}.`,a,`Convierte el valor con \`texto(…)\`, o imprime los dos por separado: \`imprimir("Total:", ${g(n)})\`.`)}throw this.operandError(e,t,r,a);case"-":case"*":case"/":case"div":case"mod":case"^":{if(typeof t!=="number"||typeof r!=="number")throw this.operandError(e,t,r,a);if(e==="-")return t-r;if(e==="*")return t*r;if(e==="^"){let s=t**r;if(Number.isNaN(s))throw this.runtimeError("POTENCIA_INVÁLIDA",`\`${t<0?`(${g(t)})`:g(t)} ^ ${g(r)}\` no tiene un resultado real.`,a,"Un número negativo solo se puede elevar a un exponente entero.");return s}if(r===0){let s=a.kind==="Binary"?a.right:a.kind==="Assignment"?a.value:a;throw this.runtimeError("DIVISIÓN_ENTRE_CERO","No se puede dividir entre cero.",s,"Revisa que el divisor no valga 0 antes de dividir.")}if(e==="/")return t/r;let n=Math.floor(t/r);return e==="div"?n:t-r*n}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 ${v(b(t))} con ${v(b(r))} usando \`${e}\`.`,a,"`<`, `>`, `<=` y `>=` comparan dos números o dos textos.");let s=t,i=r;if(e==="<")return s<i;if(e===">")return s>i;if(e==="<=")return s<=i;return s>=i}}}index(e,t,r){if(z(e))return e.scope.variables.get(this.member(e,t,r));if(I(e)){let n=this.dictKey(t,r.index);if(!e.entries.has(n))throw this.missingKey(n,r);return e.entries.get(n)}if(r.dot||!y(e)&&typeof e!=="string")throw this.runtimeError("ÍNDICE_NO_SOPORTADO",r.dot?`Solo los diccionarios y los dominios tienen campos con \`.\`, no ${v(b(e))}.`:`Solo se puede usar \`[ ]\` con listas, textos y diccionarios, no con ${v(b(e))}.`,r.object);let a=this.position(e,t,r);return y(e)?e.items[a]:e[a]}setIndex(e,t,r,a,n){if(typeof e==="string")throw this.runtimeError("TEXTO_INMUTABLE","Un texto no se puede cambiar letra por letra.",a,"Construye un texto nuevo, por ejemplo uniendo partes con `+`.");if(z(e)){let c=this.member(e,t,a);if(!e.writable.has(c))throw this.runtimeError("MIEMBRO_DE_SOLO_LECTURA",`\`${e.name}.${c}\` no se puede cambiar desde fuera del dominio.`,a,"Desde fuera de un dominio solo se cambian sus miembros declarados con `variable`; las constantes, los procedimientos y los dominios que contiene quedan fijos.");let o=e.scope.variables.get(c);if(e.scope.variables.set(c,r),this.onEvent)this.emit({type:"asigna",name:`${e.name}.${c}`,before:O(o),after:O(r),nodeId:n.id,span:n.span});return}if(a.dot&&!I(e))throw this.runtimeError("ÍNDICE_NO_SOPORTADO",`Solo los diccionarios y los dominios tienen campos con \`.\`, no ${v(b(e))}.`,a.object);if(I(e)){let c=this.dictKey(t,a.index),o=e.entries.has(c)?e.entries.get(c):null;e.entries.set(c,r),this.emitModification(e,c,o,r,n);return}if(!y(e))throw this.runtimeError("ÍNDICE_NO_SOPORTADO",`Solo se puede cambiar un elemento de una lista o de un diccionario, no de ${v(b(e))}.`,a.object);let s=this.position(e,t,a),i=e.items[s];e.items[s]=r,this.emitModification(e,s,i,r,n)}emitModification(e,t,r,a,n){if(!this.onEvent)return;this.emit({type:"modifica",ref:e.id,key:O(t),before:O(r),after:O(a),nodeId:n.id,span:n.span})}member(e,t,r){if(!r.dot)throw this.runtimeError("ÍNDICE_NO_SOPORTADO",`Los miembros del dominio \`${e.name}\` se usan con \`.\`, no con \`[ ]\`.`,r,typeof t==="string"&&/^[\p{L}_][\p{L}\d_]*$/u.test(t)?`Escribe \`${e.name}.${t}\`.`:`Por ejemplo: \`${e.name}.miembro\`.`);let a=t;if(!e.scope.variables.has(a)){let n=Se(a,[...e.scope.variables.keys()]);throw this.runtimeError("MIEMBRO_INEXISTENTE",`El dominio \`${e.name}\` no tiene nada llamado \`${a}\`.`,r.index,n?`¿Quisiste decir \`${e.name}.${n}\`?`:"Los miembros de un dominio se declaran dentro de sus llaves; desde fuera no se pueden agregar.")}return a}missingKey(e,t){let r=t.dot?`\`${e}\``:g(e);return this.runtimeError("CLAVE_INEXISTENTE",`El diccionario no tiene la clave ${r}.`,t.index,`Usa \`tiene(d, ${g(e)})\` para preguntar antes de leerla.`)}position(e,t,r){if(typeof t!=="number"||!Number.isInteger(t))throw this.runtimeError("ÍNDICE_INVÁLIDO",`El índice tiene que ser un número entero, pero es ${g(t)}.`,r.index,typeof t==="number"?"Redondea el índice con `redondear(…)`.":typeof t==="string"?"Las claves de texto son para diccionarios; una lista se recorre con posiciones 0, 1, 2…":void 0);let a=y(e)?e.items.length:e.length,n=t<0?a+t:t;if(n<0||n>=a){let s=y(e)?"la lista":"el texto",i=y(e)?"elemento":"carácter";throw this.runtimeError("ÍNDICE_FUERA_DE_RANGO",`El índice ${t} está fuera de ${s}, que tiene ${a} ${a===1?i:i==="carácter"?"caracteres":"elementos"}.`,r.index,a===0?y(e)?"La lista está vacía.":"El texto está vacío.":`Los índices válidos van de 0 a ${a-1} (o de -1 a -${a} desde el final).`)}return n}callStack(){return this.frames.filter((e,t)=>!(e.chunk.hidden&&this.frames[t-1]?.chunk.hidden)).map((e)=>e.name)}visibleSpan(e){let t=this.frames.length-1;if(!this.frames[t]?.chunk.hidden)return e;while(t>0&&this.frames[t-1].chunk.hidden)t-=1;return this.frames[t].callNode?.span??e}runtimeError(e,t,r,a){return new P({kind:"ejecución",code:e,message:t,hint:a,span:this.visibleSpan(r.span),callStack:this.callStack()})}limitError(e,t,r,a){return new P({kind:"límite",code:e,message:t,hint:r,span:this.visibleSpan(a?.span??this.currentNode?.span),callStack:this.callStack()})}operandError(e,t,r,a){return this.runtimeError("TIPO_INVÁLIDO",`\`${e}\` no funciona entre ${v(b(t))} y ${v(b(r))}.`,a)}unknownName(e,t,r){let a=[...Object.keys(this.natives),...Object.keys(ue),...Object.keys(le)];for(let s=e.scope;s;s=s.parent)a.push(...s.variables.keys());let n=Se(t,a);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} = …\`.`)}constantReassignment(e,t){return this.runtimeError("CONSTANTE_REASIGNADA",`\`${e}\` es una constante; su valor no puede cambiar.`,t,`Si necesitas cambiarlo, declárala con \`variable ${e} = …\` en lugar de \`constante\`.`)}invalidRepeatCount(e,t){if(typeof e!=="number")return this.runtimeError("REPETICIONES_INVÁLIDAS",`\`repetir\` necesita un número de veces, pero recibió ${v(b(e))} (${g(e)}).`,t,typeof e==="string"?"Convierte el texto con `número(…)`.":void 0);return this.runtimeError("REPETICIONES_INVÁLIDAS",`\`repetir\` necesita un número entero de veces, 0 o más, pero recibió ${g(e)}.`,t,e<0?"Revisa que la cantidad no sea negativa.":"Redondea la cantidad con `redondear(…)`.")}undeclaredAssignment(e,t){if(Object.hasOwn(le,e))return this.runtimeError("NOMBRE_RESERVADO_POR_BIBLIOTECA",`\`${e}\` es una constante de Codexivo; no se le puede asignar un valor.`,t,`Si quieres tu propia variable con ese nombre, declárala: \`variable ${e} = …\`.`);if(this.natives[e]||ue[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 ${g(t)}.`,r,Oe(t))}nonBooleanLogical(e,t,r){return this.runtimeError("OPERANDO_NO_LÓGICO",`\`${e.operator}\` une valores \`verdadero\` o \`falso\`, pero recibió ${g(t)}.`,r,Oe(t))}emitOperation(e,t,r,a){this.emit({type:"opera",operator:e,operands:t.map(O),result:O(r),nodeId:a.id,span:a.span})}emit(e){if(!this.onEvent)return;if(this.frames[this.frames.length-1]?.chunk.hidden&&!Pt.has(e.type))return;this.onEvent({...e,operations:this.operations,depth:this.frames.length})}}var Ye=(e)=>e.replace(/\r\n/g,`
139
+ `,W=()=>[{name:"lista",type:"lista"},{name:"f",type:"procedimiento"}],fr={mapear:{level:"avanzado",category:"procedimientos",params:W(),returns:"lista",description:"Una lista nueva con el resultado de `f(x)` para cada elemento.",examples:[{code:"mapear([1, 2, 3], procedimiento(x) { regresa x * 2 })",result:"[2, 4, 6]"}]},filtrar:{level:"avanzado",category:"procedimientos",params:W(),returns:"lista",description:"Una lista nueva con los elementos para los que `f(x)` es `verdadero`.",examples:[{code:"filtrar([1, 2, 3, 4], procedimiento(x) { regresa x mod 2 == 0 })",result:"[2, 4]"}]},reducir:{level:"avanzado",category:"procedimientos",params:[...W(),{name:"inicial",type:"cualquiera"}],returns:"cualquiera",description:"Junta la lista en un solo valor: empieza con `inicial` y en cada elemento hace `f(acumulado, x)`.",examples:[{code:"reducir([1, 2, 3], procedimiento(total, x) { regresa total + x }, 0)",result:"6"}]},alguno:{level:"avanzado",category:"procedimientos",params:W(),returns:"lógico",description:"`verdadero` si `f(x)` es `verdadero` para al menos un elemento.",examples:[{code:"alguno([1, 5], procedimiento(x) { regresa x > 3 })",result:"verdadero"}]},todos:{level:"avanzado",category:"procedimientos",params:W(),returns:"lógico",description:"`verdadero` si `f(x)` es `verdadero` para todos los elementos.",examples:[{code:"todos([1, 5], procedimiento(x) { regresa x > 3 })",result:"falso"}]},buscar:{level:"avanzado",category:"procedimientos",params:W(),returns:"cualquiera",description:"El primer elemento para el que `f(x)` es `verdadero`, o `nulo` si no hay.",examples:[{code:"buscar([1, 5, 7], procedimiento(x) { regresa x > 3 })",result:"5"}]},ordenar:{level:"intermedio",category:"listas",params:[{name:"lista",type:"lista"},{name:"va_antes",type:"procedimiento",optional:!0}],returns:"lista",description:"Una lista nueva ordenada. Sin comparador, todos tienen que ser números o todos textos; `va_antes(a, b)` responde «¿va `a` antes que `b`?». Es estable.",examples:[{code:"ordenar([3, 1, 2])",result:"[1, 2, 3]"},{code:"ordenar([3, 1, 2], procedimiento(a, b) { regresa a > b })",result:"[3, 2, 1]"}]}},Dt={ordenar:1},$t=()=>{let{program:e,errors:t}=U(Ct);if(t.length>0)throw Error(`La biblioteca en Codexivo tiene errores: ${t[0].message}`);let r=new Map;for(let a of e.statements){if(a.kind!=="FunctionDeclaration")continue;let n=ee(a.function,!0),s=Dt[a.name.name];if(s!==void 0)n.minParams=s;r.set(a.name.name,n)}return r},ue=(()=>{let e=new F,t={},r=-1;for(let[a,n]of $t()){let s={kind:"procedimiento",id:r--,name:a,chunk:n,closure:e};e.variables.set(a,s),t[a]=s}return t})();var wt=(e,t)=>{let r=Array.from({length:t.length+1},(a,n)=>n);for(let a=1;a<=e.length;a++){let n=r[0];r[0]=a;for(let s=1;s<=t.length;s++){let i=r[s],c=e[a-1]===t[s-1]?0:1;r[s]=Math.min(r[s]+1,r[s-1]+1,n+c),n=i}}return r[t.length]},Je=(e)=>e.normalize("NFD").replace(/[̀-ͯ]/g,"").toLowerCase(),xe=(e,t)=>{let r=Je(e),a=r.length<=3?1:2,n,s=1/0;for(let i of new Set(t)){if(i===e)continue;let c=wt(r,Je(i));if(c<=a&&c<s)n=i,s=c}return n};var k=(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};if(e.kind==="diccionario")return{type:"diccionario",ref:e.id,size:e.entries.size,repr:t};if(e.kind==="dominio")return{type:"dominio",ref:e.id,name:e.name,repr:t};return{type:"procedimiento",name:e.name,repr:t}},Se={maxOperations:1e7,maxCallDepth:1e4,maxOutput:1e6},Pt=new Set(["llama","regresa","imprime","lee","error","fin"]),Xe=new Set(["step","enterScope","exitScope","jump","dup","pop","repeatStart","forEachStart","rangeStart","halt"]);class Q{options;frames=[];stack=[];natives;inputQueue;breakpoints;limits;onEvent;outputParts=[];outputLength=0;nextId=1;lastValue=null;inputClosed=!1;currentNode;seed;randomState;status="lista";operations=0;steps=0;pauseReason;result=null;error;constructor(e,t={}){this.options=t;this.natives={...He,...t.world?.natives,...t.natives},this.limits={maxOperations:t.maxOperations??Se.maxOperations,maxCallDepth:t.maxCallDepth??Se.maxCallDepth,maxOutput:t.maxOutput??Se.maxOutput},this.inputQueue=typeof t.input==="string"?Ye(t.input):[...t.input??[]],this.breakpoints=new Set(t.breakpoints??[]),this.onEvent=t.onEvent,this.seed=(t.seed??Math.floor(Math.random()*4294967296))>>>0,this.randomState=this.seed;let r=t.globals??new F;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)}setBreakpoints(e){this.breakpoints=new Set(e)}provideInput(e){if(this.inputQueue.push(...Ye(e)),this.status==="esperando_entrada")this.status="pausada"}closeInput(){if(this.inputClosed=!0,this.status==="esperando_entrada")this.status="pausada"}snapshot(){let e={},t=(a)=>{if(!v(a)&&!I(a)&&!z(a)||e[a.id])return;if(z(a))e[a.id]={domain:a.name,members:[...a.scope.variables].map(([n,s])=>({name:n,value:k(s),writable:a.writable.has(n)}))},a.scope.variables.forEach(t);else if(v(a))e[a.id]=a.items.map(k),a.items.forEach(t);else e[a.id]={entries:[...a.entries].map(([n,s])=>({key:k(n),value:k(s)}))},a.entries.forEach(t)},r=this.frames.map((a)=>{let n=[],s=a.scope;while(s){let i={};for(let[c,o]of s.variables)i[c]=k(o),t(o);if(n.unshift(i),s===a.root)break;s=s.parent}if(a.chunk.hidden)return{name:a.name,callSpan:a.callNode?.span,library:!0,scopes:[]};return{name:a.name,callSpan:a.callNode?.span,scopes:n}});return{status:this.status,operations:this.operations,steps:this.steps,nodeId:this.currentNode?.id,span:this.currentNode?.span,frames:r,heap:e,output:this.output,...this.options.world?{world:this.options.world.state()}:{}}}execute(e,t){if(this.done||this.status==="esperando_entrada")return this.status;let r=this.operations+t,a=0;try{while(!0){let n=this.frames[this.frames.length-1],s=n.chunk.code[n.pc];if(!Xe.has(s.op)&&!n.chunk.hidden){if(this.operations>=this.limits.maxOperations)throw this.limitError("DEMASIADAS_OPERACIONES",`El programa superó el límite de ${Re(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.pauseReason="presupuesto",this.status}if(s.op==="step"&&e==="run"&&!n.chunk.hidden&&a>0&&this.breakpoints.has(s.node.span.start.line))return this.status="pausada",this.pauseReason="punto_de_interrupción",this.status;let i=this.exec(n,s);a+=1;let c=this.status;if(c==="esperando_entrada"||c==="terminada")return c;if(i&&!n.chunk.hidden){if(this.steps+=1,e==="step")return this.status="pausada",this.pauseReason="paso",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,!Xe.has(t.op)&&!e.chunk.hidden)this.operations+=1;switch(t.op){case"step":if(e.chunk.hidden)return!1;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"dict":{let r=this.stack.splice(this.stack.length-t.count*2,t.count*2),a=new Map;return t.node.entries.forEach((n,s)=>{a.set(this.dictKey(r[s*2],n.key),r[s*2+1])}),this.stack.push(this.newDict(a)),!1}case"load":{let r=e.scope.lookup(t.name);if(r)return this.stack.push(r.variables.get(t.name)),!1;let a=(e.chunk.hidden?Qe[t.name]:void 0)??this.natives[t.name]??ue[t.name]??le[t.name];if(a!==void 0&&!t.forUpdate)return this.stack.push(a),!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.target,`Para cambiar su valor escribe \`${t.name} = …\`, sin \`variable\`.`);if(e.scope.variables.set(t.name,r),t.constant)e.scope.constants.add(t.name);else e.scope.constants.delete(t.name);if(t.loop)e.scope.loopVariables.add(t.name);else e.scope.loopVariables.delete(t.name);return this.emit({type:"declara",name:t.name,constant:t.constant,value:k(r),nodeId:t.node.id,span:t.node.span}),!1}case"store":{let r=this.stack.pop(),a=e.scope.lookup(t.name);if(!a)throw this.undeclaredAssignment(t.name,t.target);if(a.loopVariables.has(t.name))throw this.runtimeError("VARIABLE_DE_PARA",`La variable de un \`para\` avanza sola; \`${t.name}\` no se puede cambiar dentro del bucle.`,t.node,`Si necesitas otro valor, guárdalo en una variable nueva: \`variable otro = ${t.name} + 1\`.`);if(a.constants.has(t.name)){if(z(a.variables.get(t.name)))throw this.runtimeError("CONSTANTE_REASIGNADA",`\`${t.name}\` es un dominio; su nombre no se puede usar para otra cosa.`,t.node,`Para cambiar un miembro escribe \`${t.name}.miembro = …\`; para otro valor, usa otro nombre.`);throw this.constantReassignment(t.name,t.node)}let n=a.variables.get(t.name);if(a.variables.set(t.name,r),this.onEvent)this.emit({type:"asigna",name:t.name,before:k(n),after:k(r),nodeId:t.node.id,span:t.node.span});return!1}case"setIndex":{let r=this.stack.pop(),a=this.stack.pop(),n=this.stack.pop();return this.setIndex(n,a,r,t.target,t.node),!1}case"dup":return this.stack.push(...this.stack.slice(this.stack.length-t.count)),!1;case"pop":return this.stack.length-=t.count,!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(),a=this.unary(t.operator,r,t.node);if(this.stack.push(a),this.onEvent)this.emitOperation(t.operator,[r],a,t.node);return!1}case"binary":{let r=this.stack.pop(),a=this.stack.pop(),n=this.binary(t.operator,a,r,t.node);if(this.stack.push(n),this.onEvent)this.emitOperation(t.operator,[a,r],n,t.node);return!1}case"index":{let r=this.stack.pop(),a=this.stack.pop();return this.stack.push(this.index(a,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;let a=this.frames[this.frames.length-2];if(!(e.chunk.hidden&&a.chunk.hidden))this.emit({type:"regresa",name:e.name,value:k(r)});return 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(!e.chunk.hidden)this.currentNode=t.node;if(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"repeatStart":{let r=this.stack[this.stack.length-1];if(typeof r!=="number"||!Number.isInteger(r)||r<0)throw this.invalidRepeatCount(r,t.node);return!1}case"repeatNext":{let r=this.stack[this.stack.length-1],a=r>0;if(a)this.stack[this.stack.length-1]=r-1;else this.stack.pop();if(!e.chunk.hidden)this.currentNode=t.node;if(this.emit({type:"decide",label:"repetir",value:a,nodeId:t.node.id,ownerId:t.owner.id,span:t.node.span}),!a)e.pc=t.target;return!0}case"rangeStart":{let r=this.stack.pop(),a=this.stack.pop(),n=this.stack.pop(),{owner:s}=t,i=(h,l,d)=>{if(typeof h==="number"&&Number.isFinite(h))return h;throw this.runtimeError("PARA_LÍMITE_INVÁLIDO",`${d} de \`para\` tiene que ser un número, pero es ${f(h)}.`,l,typeof h==="string"?"Convierte el texto con `número(…)`.":void 0)},c=i(n,s.from,"El inicio"),o=i(a,s.to,"El final"),m=i(r,s.step??s,"El paso");if(m===0)throw this.runtimeError("PARA_PASO_CERO","El paso de un `para` no puede ser 0: nunca llegaría al final.",s.step??s,"Usa un paso positivo para contar hacia arriba o uno negativo para contar hacia abajo.");if((o-c)*m<0)throw this.runtimeError("PARA_SENTIDO_CONTRARIO",m>0?`El \`para\` empieza en ${f(c)}, que es mayor que ${f(o)}, y cuenta hacia arriba.`:`El \`para\` empieza en ${f(c)}, que es menor que ${f(o)}, y cuenta hacia abajo.`,t.explicitStep?s.step:s.to,t.explicitStep?`Para ir de ${f(c)} a ${f(o)} el paso tiene que ser ${m>0?"negativo":"positivo"}.`:"Para contar hacia atrás escribe `con_paso -1`.");let x=Math.floor((o-c)/m+0.000000001)+1;return this.stack.push(c,m,x,0),!1}case"rangeNext":{let r=this.stack[this.stack.length-1],a=this.stack[this.stack.length-2],n=r<a;if(n){let s=this.stack[this.stack.length-4],i=this.stack[this.stack.length-3];this.stack[this.stack.length-1]=r+1,this.stack.push(Number((s+r*i).toPrecision(15)))}else this.stack.length-=4;if(!e.chunk.hidden)this.currentNode=t.node;if(this.emit({type:"decide",label:"para",value:n,nodeId:t.node.id,ownerId:t.owner.id,span:t.node.span}),!n)e.pc=t.target;return!0}case"forEachStart":{let r=this.stack[this.stack.length-1],{owner:a}=t;if(I(r)){let n={kind:"lista",id:0,items:[...r.entries.keys()]};return this.stack.push(n,0),!1}if(a.value)throw this.runtimeError("PARA_CADA_INVÁLIDO",`\`para_cada ${a.variable.name}, ${a.value.name}\` recorre un diccionario, no ${y(E(r))}.`,t.node,v(r)||typeof r==="string"?`Para una lista o un texto usa una sola variable: \`para_cada ${a.value.name} en …\`.`:void 0);if(!v(r)&&typeof r!=="string")throw this.runtimeError("PARA_CADA_INVÁLIDO",`\`para_cada\` recorre listas, textos y diccionarios, no ${y(E(r))}.`,t.node,typeof r==="number"?`Para contar, recorre un rango: \`rango(1, ${f(r)})\`.`:void 0);return this.stack.push(v(r)?r.items.length:r.length,0),!1}case"forEachNext":{let r=this.stack[this.stack.length-1],a=this.stack[this.stack.length-2],n=this.stack[this.stack.length-3],s=typeof a==="number"?a:a.items.length;if(v(n)&&n.items.length!==s)throw this.runtimeError("LISTA_MODIFICADA_AL_RECORRER",`La lista cambió de tamaño mientras se recorría: tenía ${s} y ahora tiene ${n.items.length}.`,t.node,"Recorre una copia o junta los cambios en otra lista y aplícalos al terminar el `para_cada`.");if(I(n)&&(n.entries.size!==s||r<s&&!n.entries.has(a.items[r])))throw this.runtimeError("DICCIONARIO_MODIFICADO_AL_RECORRER","Se agregaron o quitaron claves del diccionario mientras se recorría.",t.node,"Recorre `claves(d)`, que es una lista nueva, o junta los cambios y aplícalos al terminar.");let i=r<s;if(i)if(this.stack[this.stack.length-1]=r+1,I(n)){let c=a.items[r];if(t.owner.value)this.stack.push(n.entries.get(c));this.stack.push(c)}else this.stack.push(v(n)?n.items[r]:n[r]);else this.stack.length-=3;if(!e.chunk.hidden)this.currentNode=t.node;if(this.emit({type:"decide",label:"para_cada",value:i,nodeId:t.node.id,ownerId:t.owner.id,span:t.node.span}),!i)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 a=t.node.operator==="y";this.emitOperation(t.node.operator,[a,r],r,t.node)}return!1}case"enterScope":case"domainStart":return e.scope=new F(e.scope),!1;case"domainEnd":{let r={kind:"dominio",id:this.nextId++,name:t.name,scope:e.scope,writable:new Set(t.writable)};if(e.scope=e.scope.parent??e.scope,e.scope.variables.has(t.name)&&!this.options.allowRedeclare)throw this.runtimeError("VARIABLE_REPETIDA",`Ya existe algo llamado \`${t.name}\` en este bloque.`,t.node.name,"Cada dominio necesita un nombre propio en su bloque.");return e.scope.variables.set(t.name,r),e.scope.constants.add(t.name),this.emit({type:"declara",name:t.name,constant:!0,value:k(r),nodeId:t.node.id,span:t.node.span}),!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:k(e)})}call(e,t,r){let a=this.stack.length-t-1,n=this.stack[a],s=n!==null&&typeof n==="object"&&"name"in n&&n.name?n.name:Ze(r);if(!re(n))throw this.runtimeError("NO_ES_PROCEDIMIENTO",`\`${Ze(r)}\` no es un procedimiento, es ${y(E(n))}.`,r.callee);let i=this.stack.slice(a+1),c=n.kind==="nativo"?n.arity:n.chunk.minParams!==void 0?[n.chunk.minParams,n.chunk.params.length]:n.chunk.params.length;if(Mt(c,t,s,(m,x)=>this.runtimeError("CANTIDAD_DE_ARGUMENTOS",m,r,x??(e.chunk.hidden?`\`${e.name}\` llama al procedimiento que recibió con ${t===1?"1 valor":`${t} valores`}.`:void 0))),n.kind==="nativo"){let m=n.call(i,this.nativeContext(r));if(m===H){if(e.pc-=1,!e.chunk.hidden)this.operations-=1;return this.status="esperando_entrada",!0}if(!e.chunk.hidden)this.charge(n.cost,i);return this.stack.length=a,this.stack.push(m),!1}if(this.frames.length-1>=this.limits.maxCallDepth)throw this.limitError("DEMASIADAS_LLAMADAS",`Hay más de ${Re(this.limits.maxCallDepth)} llamadas anidadas.`,"Si es recursión, revisa que exista un caso base que deje de llamar al procedimiento.",r);if(n.chunk.hidden&&!e.chunk.hidden)this.charge(se[n.chunk.name],i);let o=new F(n.closure);if(n.chunk.params.forEach((m,x)=>o.variables.set(m,i[x]??null)),!(e.chunk.hidden&&n.chunk.hidden))this.emit({type:"llama",name:s,args:i.map(k),nodeId:r.id,span:r.span});return this.frames.push({chunk:n.chunk,pc:0,scope:o,root:o,stackBase:a+1,name:s,callNode:r}),!1}charge(e,t){if(e)this.operations+=Math.max(0,e(t)-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 ${Re(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 H;return this.emit({type:"lee",text:null}),null},newList:(t)=>this.newList(t),newDict:(t)=>this.newDict(t),random:()=>this.random(),fail:(t,r,a)=>this.runtimeError(t,r,e,a),emit:(t)=>this.emit({...t,nodeId:e.id,span:e.span})}}newList(e){return{kind:"lista",id:this.nextId++,items:e}}random(){this.randomState=this.randomState+1831565813>>>0;let e=this.randomState;return e=Math.imul(e^e>>>15,e|1),e^=e+Math.imul(e^e>>>7,e|61),((e^e>>>14)>>>0)/4294967296}newDict(e){return{kind:"diccionario",id:this.nextId++,entries:e}}dictKey(e,t){if(typeof e==="string"||typeof e==="number")return e;throw this.runtimeError("CLAVE_INVÁLIDA",`Las claves de un diccionario son textos o números, no ${y(E(e))}.`,t)}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 ${y(E(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,Ne(t));return!t}binary(e,t,r,a){switch(e){case"==":return M(t,r);case"!=":return!M(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 ${y(E(n))}.`,a,`Convierte el valor con \`texto(…)\`, o imprime los dos por separado: \`imprimir("Total:", ${f(n)})\`.`)}throw this.operandError(e,t,r,a);case"-":case"*":case"/":case"div":case"mod":case"^":{if(typeof t!=="number"||typeof r!=="number")throw this.operandError(e,t,r,a);if(e==="-")return t-r;if(e==="*")return t*r;if(e==="^"){let s=t**r;if(Number.isNaN(s))throw this.runtimeError("POTENCIA_INVÁLIDA",`\`${t<0?`(${f(t)})`:f(t)} ^ ${f(r)}\` no tiene un resultado real.`,a,"Un número negativo solo se puede elevar a un exponente entero.");return s}if(r===0){let s=a.kind==="Binary"?a.right:a.kind==="Assignment"?a.value:a;throw this.runtimeError("DIVISIÓN_ENTRE_CERO","No se puede dividir entre cero.",s,"Revisa que el divisor no valga 0 antes de dividir.")}if(e==="/")return t/r;let n=Math.floor(t/r);return e==="div"?n:t-r*n}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 ${y(E(t))} con ${y(E(r))} usando \`${e}\`.`,a,"`<`, `>`, `<=` y `>=` comparan dos números o dos textos.");let s=t,i=r;if(e==="<")return s<i;if(e===">")return s>i;if(e==="<=")return s<=i;return s>=i}}}index(e,t,r){if(z(e))return e.scope.variables.get(this.member(e,t,r));if(I(e)){let n=this.dictKey(t,r.index);if(!e.entries.has(n))throw this.missingKey(n,r);return e.entries.get(n)}if(r.dot||!v(e)&&typeof e!=="string")throw this.runtimeError("ÍNDICE_NO_SOPORTADO",r.dot?`Solo los diccionarios y los dominios tienen campos con \`.\`, no ${y(E(e))}.`:`Solo se puede usar \`[ ]\` con listas, textos y diccionarios, no con ${y(E(e))}.`,r.object);let a=this.position(e,t,r);return v(e)?e.items[a]:e[a]}setIndex(e,t,r,a,n){if(typeof e==="string")throw this.runtimeError("TEXTO_INMUTABLE","Un texto no se puede cambiar letra por letra.",a,"Construye un texto nuevo, por ejemplo uniendo partes con `+`.");if(z(e)){let c=this.member(e,t,a);if(!e.writable.has(c))throw this.runtimeError("MIEMBRO_DE_SOLO_LECTURA",`\`${e.name}.${c}\` no se puede cambiar desde fuera del dominio.`,a,"Desde fuera de un dominio solo se cambian sus miembros declarados con `variable`; las constantes, los procedimientos y los dominios que contiene quedan fijos.");let o=e.scope.variables.get(c);if(e.scope.variables.set(c,r),this.onEvent)this.emit({type:"asigna",name:`${e.name}.${c}`,before:k(o),after:k(r),nodeId:n.id,span:n.span});return}if(a.dot&&!I(e))throw this.runtimeError("ÍNDICE_NO_SOPORTADO",`Solo los diccionarios y los dominios tienen campos con \`.\`, no ${y(E(e))}.`,a.object);if(I(e)){let c=this.dictKey(t,a.index),o=e.entries.has(c)?e.entries.get(c):null;e.entries.set(c,r),this.emitModification(e,c,o,r,n);return}if(!v(e))throw this.runtimeError("ÍNDICE_NO_SOPORTADO",`Solo se puede cambiar un elemento de una lista o de un diccionario, no de ${y(E(e))}.`,a.object);let s=this.position(e,t,a),i=e.items[s];e.items[s]=r,this.emitModification(e,s,i,r,n)}emitModification(e,t,r,a,n){if(!this.onEvent)return;this.emit({type:"modifica",ref:e.id,key:k(t),before:k(r),after:k(a),nodeId:n.id,span:n.span})}member(e,t,r){if(!r.dot)throw this.runtimeError("ÍNDICE_NO_SOPORTADO",`Los miembros del dominio \`${e.name}\` se usan con \`.\`, no con \`[ ]\`.`,r,typeof t==="string"&&/^[\p{L}_][\p{L}\d_]*$/u.test(t)?`Escribe \`${e.name}.${t}\`.`:`Por ejemplo: \`${e.name}.miembro\`.`);let a=t;if(!e.scope.variables.has(a)){let n=xe(a,[...e.scope.variables.keys()]);throw this.runtimeError("MIEMBRO_INEXISTENTE",`El dominio \`${e.name}\` no tiene nada llamado \`${a}\`.`,r.index,n?`¿Quisiste decir \`${e.name}.${n}\`?`:"Los miembros de un dominio se declaran dentro de sus llaves; desde fuera no se pueden agregar.")}return a}missingKey(e,t){let r=t.dot?`\`${e}\``:f(e);return this.runtimeError("CLAVE_INEXISTENTE",`El diccionario no tiene la clave ${r}.`,t.index,`Usa \`tiene(d, ${f(e)})\` para preguntar antes de leerla.`)}position(e,t,r){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(…)`.":typeof t==="string"?"Las claves de texto son para diccionarios; una lista se recorre con posiciones 0, 1, 2…":void 0);let a=v(e)?e.items.length:e.length,n=t<0?a+t:t;if(n<0||n>=a){let s=v(e)?"la lista":"el texto",i=v(e)?"elemento":"carácter";throw this.runtimeError("ÍNDICE_FUERA_DE_RANGO",`El índice ${t} está fuera de ${s}, que tiene ${a} ${a===1?i:i==="carácter"?"caracteres":"elementos"}.`,r.index,a===0?v(e)?"La lista está vacía.":"El texto está vacío.":`Los índices válidos van de 0 a ${a-1} (o de -1 a -${a} desde el final).`)}return n}callStack(){return this.frames.filter((e,t)=>!(e.chunk.hidden&&this.frames[t-1]?.chunk.hidden)).map((e)=>e.name)}visibleSpan(e){let t=this.frames.length-1;if(!this.frames[t]?.chunk.hidden)return e;while(t>0&&this.frames[t-1].chunk.hidden)t-=1;return this.frames[t].callNode?.span??e}runtimeError(e,t,r,a){return new V({kind:"ejecución",code:e,message:t,hint:a,span:this.visibleSpan(r.span),callStack:this.callStack()})}limitError(e,t,r,a){return new V({kind:"límite",code:e,message:t,hint:r,span:this.visibleSpan(a?.span??this.currentNode?.span),callStack:this.callStack()})}operandError(e,t,r,a){return this.runtimeError("TIPO_INVÁLIDO",`\`${e}\` no funciona entre ${y(E(t))} y ${y(E(r))}.`,a)}unknownName(e,t,r){let a=[...Object.keys(this.natives),...Object.keys(ue),...Object.keys(le)];for(let s=e.scope;s;s=s.parent)a.push(...s.variables.keys());let n=xe(t,a);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} = …\`.`)}constantReassignment(e,t){return this.runtimeError("CONSTANTE_REASIGNADA",`\`${e}\` es una constante; su valor no puede cambiar.`,t,`Si necesitas cambiarlo, declárala con \`variable ${e} = …\` en lugar de \`constante\`.`)}invalidRepeatCount(e,t){if(typeof e!=="number")return this.runtimeError("REPETICIONES_INVÁLIDAS",`\`repetir\` necesita un número de veces, pero recibió ${y(E(e))} (${f(e)}).`,t,typeof e==="string"?"Convierte el texto con `número(…)`.":void 0);return this.runtimeError("REPETICIONES_INVÁLIDAS",`\`repetir\` necesita un número entero de veces, 0 o más, pero recibió ${f(e)}.`,t,e<0?"Revisa que la cantidad no sea negativa.":"Redondea la cantidad con `redondear(…)`.")}undeclaredAssignment(e,t){if(Object.hasOwn(le,e))return this.runtimeError("NOMBRE_RESERVADO_POR_BIBLIOTECA",`\`${e}\` es una constante de Codexivo; no se le puede asignar un valor.`,t,`Si quieres tu propia variable con ese nombre, declárala: \`variable ${e} = …\`.`);if(this.natives[e]||ue[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,Ne(t))}nonBooleanLogical(e,t,r){return this.runtimeError("OPERANDO_NO_LÓGICO",`\`${e.operator}\` une valores \`verdadero\` o \`falso\`, pero recibió ${f(t)}.`,r,Ne(t))}emitOperation(e,t,r,a){this.emit({type:"opera",operator:e,operands:t.map(k),result:k(r),nodeId:a.id,span:a.span})}emit(e){if(!this.onEvent)return;if(this.frames[this.frames.length-1]?.chunk.hidden&&!Pt.has(e.type))return;this.onEvent({...e,operations:this.operations,depth:this.frames.length})}}var Ye=(e)=>e.replace(/\r\n/g,`
138
140
  `).replace(/\n$/,"").split(`
139
- `),Ne=(e)=>e.toLocaleString("es-MX").replace(/,/g," "),Ze=(e)=>{let{callee:t}=e;if(t.kind==="Identifier")return t.name;if(t.kind==="Index"&&t.dot&&t.object.kind==="Identifier"&&t.index.kind==="StringLiteral")return`${t.object.name}.${t.index.value}`;return"esto"},Oe=(e)=>{switch(b(e)){case"número":return"¿Quisiste comparar? Por ejemplo: `x es_distinto_de 0` o `x es_mayor_que 0`.";case"texto":return'¿Quisiste comparar? Por ejemplo: `texto es_distinto_de ""`.';case"lista":return"¿Quisiste decir `longitud(lista) es_mayor_que 0`?";case"diccionario":return"¿Quisiste decir `longitud(d) es_mayor_que 0` o `tiene(d, clave)`?";case"nulo":return"¿Quisiste decir `x es_distinto_de nulo`?";default:return}},Mt=(e,t,r,a)=>{let[n,s]=typeof e==="number"?[e,e]:e;if(t>=n&&t<=s)return;let i=(o)=>o===1?"1 valor":`${o} valores`,c;if(n===s)c=i(n);else if(s===1/0)c=`al menos ${i(n)}`;else c=`entre ${n} y ${i(s)}`;throw a(`\`${r}\` espera ${c}, pero recibió ${t}.`)};var Vt=(e,t={})=>{let{program:r,tokens:a,errors:n}=q(e);if(n.length>0)return{program:r,tokens:a,errors:n};return{program:r,tokens:a,errors:n,machine:new Q(te(r),t)}},de=(e,t={})=>{let{trace:r,maxEvents:a=1e5,...n}=t,s=[],i=!1,c=r?(x)=>{if(s.length<a)s.push(x);else i=!0}:void 0,{errors:o,machine:m}=Vt(e,{...n,onEvent:c});if(!m)return{status:"error",ok:!1,result:null,resultText:"nulo",output:"",errors:o.map((x)=>x.toJSON()),operations:0,...r?{events:s,eventsTruncated:i}:{}};return m.run(),{status:m.status,ok:m.status==="terminada",result:m.result,resultText:g(m.result),output:m.output,errors:m.error?[m.error.toJSON()]:[],operations:m.operations,seed:m.seed,...t.world?{world:t.world.state()}:{},...r?{events:s,eventsTruncated:i}:{}}};class Bt{options;globals=new U;constructor(e={}){this.options=e}run(e){return de(e,{...this.options,globals:this.globals,allowRedeclare:!0})}}var Ut=1e6,_e=(e)=>{if(e===null)return"nulo";if(typeof e==="boolean")return e?"verdadero":"falso";if(typeof e==="number"){if(!Number.isFinite(e))throw Error(`El reto tiene un número que no es finito: ${e}.`);return e<0?`(${e})`:String(e)}if(typeof e==="string")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(_e).join(", ")}]`;return`{ ${Object.entries(e).map(([t,r])=>`${JSON.stringify(t)}: ${_e(r)}`).join(", ")} }`},ke=(e)=>{if(e===null||typeof e!=="object")return e;if(Array.isArray(e))return{kind:"lista",id:0,items:e.map(ke)};return{kind:"diccionario",id:0,entries:new Map(Object.entries(e).map(([t,r])=>[t,ke(r)]))}},et=(e)=>e.replace(/\r\n/g,`
141
+ `),Re=(e)=>e.toLocaleString("es-MX").replace(/,/g," "),Ze=(e)=>{let{callee:t}=e;if(t.kind==="Identifier")return t.name;if(t.kind==="Index"&&t.dot&&t.object.kind==="Identifier"&&t.index.kind==="StringLiteral")return`${t.object.name}.${t.index.value}`;return"esto"},Ne=(e)=>{switch(E(e)){case"número":return"¿Quisiste comparar? Por ejemplo: `x es_distinto_de 0` o `x es_mayor_que 0`.";case"texto":return'¿Quisiste comparar? Por ejemplo: `texto es_distinto_de ""`.';case"lista":return"¿Quisiste decir `longitud(lista) es_mayor_que 0`?";case"diccionario":return"¿Quisiste decir `longitud(d) es_mayor_que 0` o `tiene(d, clave)`?";case"nulo":return"¿Quisiste decir `x es_distinto_de nulo`?";default:return}},Mt=(e,t,r,a)=>{let[n,s]=typeof e==="number"?[e,e]:e;if(t>=n&&t<=s)return;let i=(o)=>o===1?"1 valor":`${o} valores`,c;if(n===s)c=i(n);else if(s===1/0)c=`al menos ${i(n)}`;else c=`entre ${n} y ${i(s)}`;throw a(`\`${r}\` espera ${c}, pero recibió ${t}.`)};var Vt=(e,t={})=>{let{program:r,tokens:a,errors:n}=U(e);if(n.length>0)return{program:r,tokens:a,errors:n};return{program:r,tokens:a,errors:n,machine:new Q(te(r),t)}},de=(e,t={})=>{let{trace:r,maxEvents:a=1e5,...n}=t,s=[],i=!1,c=r?(x)=>{if(s.length<a)s.push(x);else i=!0}:void 0,{errors:o,machine:m}=Vt(e,{...n,onEvent:c});if(!m)return{status:"error",ok:!1,result:null,resultText:"nulo",output:"",errors:o.map((x)=>x.toJSON()),operations:0,...r?{events:s,eventsTruncated:i}:{}};return m.run(),{status:m.status,ok:m.status==="terminada",result:m.result,resultText:f(m.result),output:m.output,errors:m.error?[m.error.toJSON()]:[],operations:m.operations,seed:m.seed,...t.world?{world:t.world.state()}:{},...r?{events:s,eventsTruncated:i}:{}}};class Bt{options;globals=new F;constructor(e={}){this.options=e}run(e){return de(e,{...this.options,globals:this.globals,allowRedeclare:!0})}}var Ft=1e6,ke=(e)=>{if(e===null)return"nulo";if(typeof e==="boolean")return e?"verdadero":"falso";if(typeof e==="number"){if(!Number.isFinite(e))throw Error(`El reto tiene un número que no es finito: ${e}.`);return e<0?`(${e})`:String(e)}if(typeof e==="string")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(ke).join(", ")}]`;return`{ ${Object.entries(e).map(([t,r])=>`${JSON.stringify(t)}: ${ke(r)}`).join(", ")} }`},_e=(e)=>{if(e===null||typeof e!=="object")return e;if(Array.isArray(e))return{kind:"lista",id:0,items:e.map(_e)};return{kind:"diccionario",id:0,entries:new Map(Object.entries(e).map(([t,r])=>[t,_e(r)]))}},et=(e)=>e.replace(/\r\n/g,`
140
142
  `).split(`
141
143
  `).map((t)=>t.replace(/[ \t]+$/,"")).join(`
142
- `).replace(/\n+$/,""),tt=(e,t)=>{let r=t.procedure??"resolver",a=t.cases.map((n,s)=>{let i=n.name??`Caso ${s+1}`,c=n.maxOperations??t.maxOperations??Ut,o=n.input??[];if(t.style==="salida"){let l=de(e,{input:o,maxOperations:c}),d=n.expectedOutput??"",k=t.exactOutput?l.output===d:et(l.output)===et(d);return{name:i,passed:l.ok&&k,output:l.output,expectedOutput:d,operations:l.operations,...l.errors[0]?{error:l.errors[0]}:{}}}let m=`
144
+ `).replace(/\n+$/,""),tt=(e,t)=>{let r=t.procedure??"resolver",a=t.cases.map((n,s)=>{let i=n.name??`Caso ${s+1}`,c=n.maxOperations??t.maxOperations??Ft,o=n.input??[];if(t.style==="salida"){let l=de(e,{input:o,maxOperations:c}),d=n.expectedOutput??"",O=t.exactOutput?l.output===d:et(l.output)===et(d);return{name:i,passed:l.ok&&O,output:l.output,expectedOutput:d,operations:l.operations,...l.errors[0]?{error:l.errors[0]}:{}}}let m=`
143
145
  ;
144
- regresa ${r}(${(n.args??[]).map(_e).join(", ")});
145
- `,x=de(e+m,{input:o,maxOperations:c}),h=ke(n.expected??null);return{name:i,passed:x.ok&&V(x.result,h),output:x.output,result:x.resultText,expected:g(h),operations:x.operations,...x.errors[0]?{error:x.errors[0]}:{}}});return{passed:a.every((n)=>n.passed),cases:a}};var nt=["norte","este","sur","oeste"];var Ie=(e)=>typeof e==="object"&&e!==null&&!Array.isArray(e),Ft=(e)=>{let t=[],r=(h,l)=>t.push({path:h,message:l});if(!Ie(e))return r("","El mundo tiene que ser un objeto JSON."),{errors:t};if(e.version!==1)r("version",`La versión del formato tiene que ser 1, pero es ${JSON.stringify(e.version)}.`);let a=(h,l)=>{let d=e[h];if(typeof d!=="number"||!Number.isInteger(d)||d<1||d>100)return r(h,`\`${h}\` (${l}) tiene que ser un entero entre 1 y 100, pero es ${JSON.stringify(d)}.`),0;return d},n=a("width","casillas de oeste a este"),s=a("height","casillas de sur a norte"),i=(h,l)=>{for(let[d,k]of[["x",n],["y",s]]){let A=h[d];if(typeof A!=="number"||!Number.isInteger(A))r(`${l}.${d}`,`\`${l}.${d}\` tiene que ser un número entero, pero es ${JSON.stringify(A)}.`);else if(k>0&&(A<1||A>k))r(`${l}.${d}`,`\`${l}.${d}\` tiene que estar entre 1 y ${k}, pero es ${A}.`)}},c=(h,l)=>{if(!nt.includes(h))r(l,`\`${l}\` tiene que ser "norte", "este", "sur" u "oeste", pero es ${JSON.stringify(h)}.`)},o=(h,l)=>{if(h==="infinito")return;if(typeof h!=="number"||!Number.isInteger(h)||h<0)r(l,`\`${l}\` tiene que ser un entero de 0 en adelante o "infinito", pero es ${JSON.stringify(h)}.`)},m=(h,l)=>{let d=e[h];if(d===void 0)return;if(!Array.isArray(d)){r(h,`\`${h}\` tiene que ser una lista.`);return}d.forEach((k,A)=>{let f=`${h}[${A}]`;if(!Ie(k))r(f,`\`${f}\` tiene que ser un objeto.`);else l(k,f)})};m("walls",(h,l)=>{i(h,l),c(h.side,`${l}.side`)});let x=new Set;if(m("beepers",(h,l)=>{i(h,l),o(h.count,`${l}.count`);let d=`${h.x},${h.y}`;if(x.has(d))r(l,`La casilla (${h.x}, ${h.y}) aparece dos veces en \`beepers\`.`);x.add(d)}),!Ie(e.robot))r("robot","Falta `robot`, con su posición, orientación y zumbadores.");else i(e.robot,"robot"),c(e.robot.facing,"robot.facing"),o(e.robot.beepers,"robot.beepers");if(t.length>0)return{errors:t};return{world:e,errors:[]}},rt={norte:"oeste",oeste:"sur",sur:"este",este:"norte"},qt={norte:"este",este:"sur",sur:"oeste",oeste:"norte"},Ae={norte:[0,1],este:[1,0],sur:[0,-1],oeste:[-1,0]},Te=(e)=>e==="infinito"?1/0:e,J=(e)=>e===1/0?"infinito":e,st=(e)=>{let{world:t,errors:r}=Ft(e);if(!t)throw Error(`El mundo no es válido: ${r.map((f)=>f.message).join(" ")}`);let{width:a,height:n}=t,s=new Set,i=(f,S,E)=>{if(E==="sur")return`n:${f}:${S-1}`;if(E==="oeste")return`e:${f-1}:${S}`;return`${E==="norte"?"n":"e"}:${f}:${S}`};for(let f of t.walls??[])s.add(i(f.x,f.y,f.side));let c=new Map;for(let f of t.beepers??[])if(Te(f.count)>0)c.set(`${f.x},${f.y}`,Te(f.count));let o={...t.robot,beepers:Te(t.robot.beepers),on:!0},m=(f,S,E)=>{let[N,T]=Ae[E],_=f+N,M=S+T;if(_<1||M<1||_>a||M>n)return!1;return!s.has(i(f,S,E))},x=()=>({x:o.x,y:o.y,facing:o.facing,beepers:J(o.beepers),on:o.on}),h=()=>`El robot está en (${o.x}, ${o.y}) mirando al ${o.facing}.`,l=(f,S)=>({kind:"nativo",name:f,arity:0,doc:at[f],call:(E,N)=>{if(!o.on)throw N.fail("ROBOT_APAGADO",`El robot ya está apagado; no puede \`${f}\`.`,"`apagar()` va al final: después de apagarlo, el robot ya no hace nada.");let T=x(),_=S(N);return N.emit({type:"robot",action:f,before:T,after:x(),..._?{cell:_}:{}}),null}}),d=(f,S)=>({kind:"nativo",name:f,arity:0,doc:at[f],call:S}),k=[l("avanzar",(f)=>{if(!m(o.x,o.y,o.facing)){let[N,T]=Ae[o.facing],_=o.x+N<1||o.y+T<1||o.x+N>a||o.y+T>n;throw f.fail("CHOQUE_CON_PARED",`El robot chocó con ${_?"el borde del mundo":"una pared"}. ${h()}`,"Pregunta antes con `frente_libre()`.")}let[S,E]=Ae[o.facing];o.x+=S,o.y+=E}),l("girar_izquierda",()=>{o.facing=rt[o.facing]}),l("recoger_zumbador",(f)=>{let S=`${o.x},${o.y}`,E=c.get(S)??0;if(E===0)throw f.fail("SIN_ZUMBADORES",`No hay zumbadores que recoger. ${h()}`,"Pregunta antes con `hay_zumbador()`.");if(E!==1/0)if(E===1)c.delete(S);else c.set(S,E-1);if(o.beepers!==1/0)o.beepers+=1;return{x:o.x,y:o.y,beepers:J(c.get(S)??0)}}),l("dejar_zumbador",(f)=>{if(o.beepers===0)throw f.fail("MOCHILA_VACÍA",`El robot no tiene zumbadores que dejar. ${h()}`,"Pregunta antes con `tengo_zumbadores()`.");if(o.beepers!==1/0)o.beepers-=1;let S=`${o.x},${o.y}`,E=c.get(S)??0;if(E!==1/0)c.set(S,E+1);return{x:o.x,y:o.y,beepers:J(c.get(S))}}),l("apagar",()=>{o.on=!1}),d("frente_libre",()=>m(o.x,o.y,o.facing)),d("izquierda_libre",()=>m(o.x,o.y,rt[o.facing])),d("derecha_libre",()=>m(o.x,o.y,qt[o.facing])),d("hay_zumbador",()=>(c.get(`${o.x},${o.y}`)??0)>0),d("tengo_zumbadores",()=>o.beepers>0),...nt.map((f)=>d(`mirando_al_${f}`,()=>o.facing===f))],A=()=>{let f=[...s].map((E)=>{let[N,T,_]=E.split(":");return{x:Number(T),y:Number(_),side:N==="n"?"norte":"este"}}).filter((E)=>E.side==="norte"?E.y>=1&&E.y<n&&E.x>=1:E.x>=1&&E.x<a&&E.y>=1).sort((E,N)=>E.y-N.y||E.x-N.x||E.side.localeCompare(N.side)),S=[...c].map(([E,N])=>{let[T,_]=E.split(",").map(Number);return{x:T,y:_,count:J(N)}}).sort((E,N)=>E.y-N.y||E.x-N.x);return{version:1,width:a,height:n,walls:f,beepers:S,robot:{x:o.x,y:o.y,facing:o.facing,beepers:J(o.beepers),on:o.on}}};return{natives:Object.fromEntries(k.map((f)=>[f.name,f])),state:A}},Lr={version:1,width:3,height:3,walls:[{x:1,y:1,side:"este"}],beepers:[{x:1,y:1,count:2}],robot:{x:1,y:1,facing:"norte",beepers:1}},L=(e,t,r,a=!1)=>({level:"principiante",category:"mundo",params:[],returns:a?"lógico":"nulo",description:e,examples:[{code:t,result:r}]}),at={avanzar:L("Avanza una casilla hacia donde mira el robot. Chocar con una pared es un error.",`avanzar()
146
- mirando_al_norte()`,"verdadero"),girar_izquierda:L("Gira 90° a la izquierda.",`girar_izquierda()
147
- mirando_al_oeste()`,"verdadero"),recoger_zumbador:L("Toma un zumbador de la casilla y lo guarda en la mochila.",`recoger_zumbador()
148
- hay_zumbador()`,"verdadero"),dejar_zumbador:L("Deja un zumbador de la mochila en la casilla.",`dejar_zumbador()
149
- tengo_zumbadores()`,"falso"),apagar:L("Apaga el robot. Después ya no puede hacer ninguna acción.",`apagar()
150
- frente_libre()`,"verdadero"),frente_libre:L("`verdadero` si no hay pared enfrente del robot.","frente_libre()","verdadero",!0),izquierda_libre:L("`verdadero` si no hay pared a la izquierda del robot.","izquierda_libre()","falso",!0),derecha_libre:L("`verdadero` si no hay pared a la derecha del robot.","derecha_libre()","falso",!0),hay_zumbador:L("`verdadero` si hay al menos un zumbador en la casilla del robot.","hay_zumbador()","verdadero",!0),tengo_zumbadores:L("`verdadero` si hay al menos un zumbador en la mochila.","tengo_zumbadores()","verdadero",!0),mirando_al_norte:L("`verdadero` si el robot mira al norte.","mirando_al_norte()","verdadero",!0),mirando_al_este:L("`verdadero` si el robot mira al este.","mirando_al_este()","falso",!0),mirando_al_sur:L("`verdadero` si el robot mira al sur.","mirando_al_sur()","falso",!0),mirando_al_oeste:L("`verdadero` si el robot mira al oeste.","mirando_al_oeste()","falso",!0)},Dr=(e,t)=>{let r=[],a={x:"la columna (x)",y:"la fila (y)",facing:"la orientación",beepers:"los zumbadores en la mochila",on:"si está encendido"};for(let[i,c]of Object.entries(t.robot??{})){if(c===void 0)continue;let o=e.robot[i];if(o!==c)r.push({field:`robot.${i}`,expected:c,actual:o,message:i==="on"?c?"El robot tenía que quedar encendido.":"El robot tenía que terminar apagado: falta `apagar()`.":`En ${a[i]} se esperaba ${JSON.stringify(c)}, pero quedó ${JSON.stringify(o)}.`})}let n=new Map(e.beepers.map((i)=>[`${i.x},${i.y}`,i.count])),s=new Set;for(let i of t.beepers??[]){let c=`${i.x},${i.y}`;s.add(c);let o=n.get(c)??0;if(o!==i.count)r.push({field:`beepers(${i.x}, ${i.y})`,expected:i.count,actual:o,message:`En la casilla (${i.x}, ${i.y}) se esperaban ${i.count} zumbadores, pero hay ${o}.`})}if(t.onlyListedBeepers)for(let[i,c]of n){if(s.has(i))continue;let[o,m]=i.split(",");r.push({field:`beepers(${o}, ${m})`,expected:0,actual:c,message:`La casilla (${o}, ${m}) tenía que quedar vacía, pero tiene ${c} zumbadores.`})}return{ok:r.length===0,differences:r}};var zt={nada:new Set,pasos:new Set(["paso","robot"]),decisiones:new Set(["paso","robot","decide"]),todo:"todo"},Gt=()=>new Promise((e)=>setTimeout(e,0));class Ce{post;program;options={};seed=0;machine;inputLog=[];inputClosed=!1;events=[];muted=!1;sentOutput=0;running=!1;pauseRequested=!1;constructor(e){this.post=e}async handle(e){switch(e.type){case"cargar":if(this.running)return this.busy(e.id);return this.load(e.id,e.source,e.options??{});case"evaluar_reto":this.post({type:"reto",id:e.id,result:tt(e.source,e.challenge)});return;case"pausar":this.pauseRequested=this.running,this.post({type:"hecho",id:e.id});return}let t=this.machine;if(!t){this.post({type:"error",id:e.id,message:"No hay un programa cargado sin errores."});return}switch(e.type){case"entrada":this.inputLog.push(e.line),t.provideInput(e.line),this.post({type:"hecho",id:e.id});return;case"cerrar_entrada":this.inputClosed=!0,t.closeInput(),this.post({type:"hecho",id:e.id});return;case"puntos_de_interrupción":this.options={...this.options,breakpoints:e.lines},t.setBreakpoints(e.lines),this.post({type:"hecho",id:e.id});return}if(this.running)return this.busy(e.id);switch(e.type){case"paso":t.step(),this.stopped(e.id,t);return;case"ejecutar":case"continuar":return this.runInBatches(e.id,t,e.type==="ejecutar");case"ir_a_paso":this.goToStep(e.id,e.step);return}}busy(e){this.post({type:"error",id:e,message:"La ejecución sigue en curso; pausa antes de mandar otra orden."})}load(e,t,r){this.options=r,this.seed=(r.seed??Math.floor(Math.random()*4294967296))>>>0,this.inputLog=[],this.inputClosed=!1,this.events=[],this.sentOutput=0;let{program:a,tokens:n,errors:s}=q(t);this.program=s.length===0?a:void 0,this.machine=this.program?this.createMachine():void 0,this.post({type:"cargado",id:e,tokens:n,program:a,errors:s.map((i)=>i.toJSON()),seed:this.seed})}createMachine(){let{options:e}=this,t=zt[e.detail??"decisiones"],r={input:e.input??[],interactiveInput:e.interactiveInput??!0,seed:this.seed,breakpoints:e.breakpoints??[],onEvent:(a)=>{if(this.muted)return;if(t==="todo"||t.has(a.type))this.events.push(a)},...e.maxOperations!==void 0?{maxOperations:e.maxOperations}:{},...e.maxCallDepth!==void 0?{maxCallDepth:e.maxCallDepth}:{},...e.maxOutput!==void 0?{maxOutput:e.maxOutput}:{},...e.world?{world:st(e.world)}:{}};return new Q(te(this.program),r)}async runInBatches(e,t,r){this.running=!0,this.pauseRequested=!1;let a=this.options.breakpoints??[];if(r)t.setBreakpoints([]);try{while(!0){if(t.run(this.options.batchSize??50000)!=="pausada"||t.pauseReason!=="presupuesto")break;if(this.pauseRequested)break;if(this.post({type:"progreso",id:e,operations:t.operations,steps:t.steps,output:this.newOutput(t),events:this.takeEvents()}),await Gt(),this.pauseRequested)break}}finally{if(r)t.setBreakpoints(a);this.running=!1}this.stopped(e,t,this.pauseRequested&&t.status==="pausada"?"pausa":void 0),this.pauseRequested=!1}goToStep(e,t){let r=this.createMachine();for(let a of this.inputLog)r.provideInput(a);if(this.inputClosed)r.closeInput();this.muted=!0;try{while(r.steps<t&&!r.done&&r.status!=="esperando_entrada")r.step()}finally{this.muted=!1}this.machine=r,this.events=[],this.sentOutput=0,this.stopped(e,r,"retroceso",!0)}newOutput(e){let t=e.output,r=t.slice(this.sentOutput);return this.sentOutput=t.length,r}takeEvents(){let e=this.events;return this.events=[],e}stopped(e,t,r,a=!1){let n=t.status,s=r??(n==="terminada"?"fin":n==="error"?"error":n==="esperando_entrada"?"entrada":t.pauseReason==="punto_de_interrupción"?"punto_de_interrupción":"paso");this.post({type:"detenido",id:e,status:n,reason:s,operations:t.operations,steps:t.steps,output:this.newOutput(t),...a?{replaceOutput:!0}:{},events:this.takeEvents(),snapshot:t.snapshot(),...n==="terminada"?{result:O(t.result)}:{},...t.error?{error:t.error.toJSON()}:{}})}}var Wt=new Ce((e)=>self.postMessage(e));self.addEventListener("message",(e)=>{Wt.handle(e.data)});
146
+ regresa ${r}(${(n.args??[]).map(ke).join(", ")});
147
+ `,x=de(e+m,{input:o,maxOperations:c}),h=_e(n.expected??null);return{name:i,passed:x.ok&&M(x.result,h),output:x.output,result:x.resultText,expected:f(h),operations:x.operations,...x.errors[0]?{error:x.errors[0]}:{}}});return{passed:a.every((n)=>n.passed),cases:a}};var nt=["norte","este","sur","oeste"];var Oe=(e)=>typeof e==="object"&&e!==null&&!Array.isArray(e),Ut=(e)=>{let t=[],r=(h,l)=>t.push({path:h,message:l});if(!Oe(e))return r("","El mundo tiene que ser un objeto JSON."),{errors:t};if(e.version!==1)r("version",`La versión del formato tiene que ser 1, pero es ${JSON.stringify(e.version)}.`);let a=(h,l)=>{let d=e[h];if(typeof d!=="number"||!Number.isInteger(d)||d<1||d>100)return r(h,`\`${h}\` (${l}) tiene que ser un entero entre 1 y 100, pero es ${JSON.stringify(d)}.`),0;return d},n=a("width","casillas de oeste a este"),s=a("height","casillas de sur a norte"),i=(h,l)=>{for(let[d,O]of[["x",n],["y",s]]){let A=h[d];if(typeof A!=="number"||!Number.isInteger(A))r(`${l}.${d}`,`\`${l}.${d}\` tiene que ser un número entero, pero es ${JSON.stringify(A)}.`);else if(O>0&&(A<1||A>O))r(`${l}.${d}`,`\`${l}.${d}\` tiene que estar entre 1 y ${O}, pero es ${A}.`)}},c=(h,l)=>{if(!nt.includes(h))r(l,`\`${l}\` tiene que ser "norte", "este", "sur" u "oeste", pero es ${JSON.stringify(h)}.`)},o=(h,l)=>{if(h==="infinito")return;if(typeof h!=="number"||!Number.isInteger(h)||h<0)r(l,`\`${l}\` tiene que ser un entero de 0 en adelante o "infinito", pero es ${JSON.stringify(h)}.`)},m=(h,l)=>{let d=e[h];if(d===void 0)return;if(!Array.isArray(d)){r(h,`\`${h}\` tiene que ser una lista.`);return}d.forEach((O,A)=>{let g=`${h}[${A}]`;if(!Oe(O))r(g,`\`${g}\` tiene que ser un objeto.`);else l(O,g)})};m("walls",(h,l)=>{i(h,l),c(h.side,`${l}.side`)});let x=new Set;if(m("beepers",(h,l)=>{i(h,l),o(h.count,`${l}.count`);let d=`${h.x},${h.y}`;if(x.has(d))r(l,`La casilla (${h.x}, ${h.y}) aparece dos veces en \`beepers\`.`);x.add(d)}),!Oe(e.robot))r("robot","Falta `robot`, con su posición, orientación y zumbadores.");else i(e.robot,"robot"),c(e.robot.facing,"robot.facing"),o(e.robot.beepers,"robot.beepers");if(t.length>0)return{errors:t};return{world:e,errors:[]}},rt={norte:"oeste",oeste:"sur",sur:"este",este:"norte"},qt={norte:"este",este:"sur",sur:"oeste",oeste:"norte"},Ie={norte:[0,1],este:[1,0],sur:[0,-1],oeste:[-1,0]},Ae=(e)=>e==="infinito"?1/0:e,J=(e)=>e===1/0?"infinito":e,st=(e)=>{let{world:t,errors:r}=Ut(e);if(!t)throw Error(`El mundo no es válido: ${r.map((g)=>g.message).join(" ")}`);let{width:a,height:n}=t,s=new Set,i=(g,S,b)=>{if(b==="sur")return`n:${g}:${S-1}`;if(b==="oeste")return`e:${g-1}:${S}`;return`${b==="norte"?"n":"e"}:${g}:${S}`};for(let g of t.walls??[])s.add(i(g.x,g.y,g.side));let c=new Map;for(let g of t.beepers??[])if(Ae(g.count)>0)c.set(`${g.x},${g.y}`,Ae(g.count));let o={...t.robot,beepers:Ae(t.robot.beepers),on:!0},m=(g,S,b)=>{let[N,T]=Ie[b],_=g+N,P=S+T;if(_<1||P<1||_>a||P>n)return!1;return!s.has(i(g,S,b))},x=()=>({x:o.x,y:o.y,facing:o.facing,beepers:J(o.beepers),on:o.on}),h=()=>`El robot está en (${o.x}, ${o.y}) mirando al ${o.facing}.`,l=(g,S)=>({kind:"nativo",name:g,arity:0,doc:at[g],call:(b,N)=>{if(!o.on)throw N.fail("ROBOT_APAGADO",`El robot ya está apagado; no puede \`${g}\`.`,"`apagar()` va al final: después de apagarlo, el robot ya no hace nada.");let T=x(),_=S(N);return N.emit({type:"robot",action:g,before:T,after:x(),..._?{cell:_}:{}}),null}}),d=(g,S)=>({kind:"nativo",name:g,arity:0,doc:at[g],call:S}),O=[l("avanzar",(g)=>{if(!m(o.x,o.y,o.facing)){let[N,T]=Ie[o.facing],_=o.x+N<1||o.y+T<1||o.x+N>a||o.y+T>n;throw g.fail("CHOQUE_CON_PARED",`El robot chocó con ${_?"el borde del mundo":"una pared"}. ${h()}`,"Pregunta antes con `frente_libre()`.")}let[S,b]=Ie[o.facing];o.x+=S,o.y+=b}),l("girar_izquierda",()=>{o.facing=rt[o.facing]}),l("recoger_zumbador",(g)=>{let S=`${o.x},${o.y}`,b=c.get(S)??0;if(b===0)throw g.fail("SIN_ZUMBADORES",`No hay zumbadores que recoger. ${h()}`,"Pregunta antes con `hay_zumbador()`.");if(b!==1/0)if(b===1)c.delete(S);else c.set(S,b-1);if(o.beepers!==1/0)o.beepers+=1;return{x:o.x,y:o.y,beepers:J(c.get(S)??0)}}),l("dejar_zumbador",(g)=>{if(o.beepers===0)throw g.fail("MOCHILA_VACÍA",`El robot no tiene zumbadores que dejar. ${h()}`,"Pregunta antes con `tengo_zumbadores()`.");if(o.beepers!==1/0)o.beepers-=1;let S=`${o.x},${o.y}`,b=c.get(S)??0;if(b!==1/0)c.set(S,b+1);return{x:o.x,y:o.y,beepers:J(c.get(S))}}),l("apagar",()=>{o.on=!1}),d("frente_libre",()=>m(o.x,o.y,o.facing)),d("izquierda_libre",()=>m(o.x,o.y,rt[o.facing])),d("derecha_libre",()=>m(o.x,o.y,qt[o.facing])),d("hay_zumbador",()=>(c.get(`${o.x},${o.y}`)??0)>0),d("tengo_zumbadores",()=>o.beepers>0),...nt.map((g)=>d(`mirando_al_${g}`,()=>o.facing===g))],A=()=>{let g=[...s].map((b)=>{let[N,T,_]=b.split(":");return{x:Number(T),y:Number(_),side:N==="n"?"norte":"este"}}).filter((b)=>b.side==="norte"?b.y>=1&&b.y<n&&b.x>=1:b.x>=1&&b.x<a&&b.y>=1).sort((b,N)=>b.y-N.y||b.x-N.x||b.side.localeCompare(N.side)),S=[...c].map(([b,N])=>{let[T,_]=b.split(",").map(Number);return{x:T,y:_,count:J(N)}}).sort((b,N)=>b.y-N.y||b.x-N.x);return{version:1,width:a,height:n,walls:g,beepers:S,robot:{x:o.x,y:o.y,facing:o.facing,beepers:J(o.beepers),on:o.on}}};return{natives:Object.fromEntries(O.map((g)=>[g.name,g])),state:A}},Dr={version:1,width:3,height:3,walls:[{x:1,y:1,side:"este"}],beepers:[{x:1,y:1,count:2}],robot:{x:1,y:1,facing:"norte",beepers:1}},C=(e,t,r,a=!1)=>({level:"principiante",category:"mundo",params:[],returns:a?"lógico":"nulo",description:e,examples:[{code:t,result:r}]}),at={avanzar:C("Avanza una casilla hacia donde mira el robot. Chocar con una pared es un error.",`avanzar()
148
+ mirando_al_norte()`,"verdadero"),girar_izquierda:C("Gira 90° a la izquierda.",`girar_izquierda()
149
+ mirando_al_oeste()`,"verdadero"),recoger_zumbador:C("Toma un zumbador de la casilla y lo guarda en la mochila.",`recoger_zumbador()
150
+ hay_zumbador()`,"verdadero"),dejar_zumbador:C("Deja un zumbador de la mochila en la casilla.",`dejar_zumbador()
151
+ tengo_zumbadores()`,"falso"),apagar:C("Apaga el robot. Después ya no puede hacer ninguna acción.",`apagar()
152
+ frente_libre()`,"verdadero"),frente_libre:C("`verdadero` si no hay pared enfrente del robot.","frente_libre()","verdadero",!0),izquierda_libre:C("`verdadero` si no hay pared a la izquierda del robot.","izquierda_libre()","falso",!0),derecha_libre:C("`verdadero` si no hay pared a la derecha del robot.","derecha_libre()","falso",!0),hay_zumbador:C("`verdadero` si hay al menos un zumbador en la casilla del robot.","hay_zumbador()","verdadero",!0),tengo_zumbadores:C("`verdadero` si hay al menos un zumbador en la mochila.","tengo_zumbadores()","verdadero",!0),mirando_al_norte:C("`verdadero` si el robot mira al norte.","mirando_al_norte()","verdadero",!0),mirando_al_este:C("`verdadero` si el robot mira al este.","mirando_al_este()","falso",!0),mirando_al_sur:C("`verdadero` si el robot mira al sur.","mirando_al_sur()","falso",!0),mirando_al_oeste:C("`verdadero` si el robot mira al oeste.","mirando_al_oeste()","falso",!0)},$r=(e,t)=>{let r=[],a={x:"la columna (x)",y:"la fila (y)",facing:"la orientación",beepers:"los zumbadores en la mochila",on:"si está encendido"};for(let[i,c]of Object.entries(t.robot??{})){if(c===void 0)continue;let o=e.robot[i];if(o!==c)r.push({field:`robot.${i}`,expected:c,actual:o,message:i==="on"?c?"El robot tenía que quedar encendido.":"El robot tenía que terminar apagado: falta `apagar()`.":`En ${a[i]} se esperaba ${JSON.stringify(c)}, pero quedó ${JSON.stringify(o)}.`})}let n=new Map(e.beepers.map((i)=>[`${i.x},${i.y}`,i.count])),s=new Set;for(let i of t.beepers??[]){let c=`${i.x},${i.y}`;s.add(c);let o=n.get(c)??0;if(o!==i.count)r.push({field:`beepers(${i.x}, ${i.y})`,expected:i.count,actual:o,message:`En la casilla (${i.x}, ${i.y}) se esperaban ${i.count} zumbadores, pero hay ${o}.`})}if(t.onlyListedBeepers)for(let[i,c]of n){if(s.has(i))continue;let[o,m]=i.split(",");r.push({field:`beepers(${o}, ${m})`,expected:0,actual:c,message:`La casilla (${o}, ${m}) tenía que quedar vacía, pero tiene ${c} zumbadores.`})}return{ok:r.length===0,differences:r}};var zt={nada:new Set,pasos:new Set(["paso","robot"]),decisiones:new Set(["paso","robot","decide"]),todo:"todo"},Gt=()=>new Promise((e)=>setTimeout(e,0));class Te{post;program;options={};seed=0;machine;inputLog=[];inputClosed=!1;events=[];muted=!1;sentOutput=0;running=!1;pauseRequested=!1;constructor(e){this.post=e}async handle(e){switch(e.type){case"cargar":if(this.running)return this.busy(e.id);return this.load(e.id,e.source,e.options??{});case"evaluar_reto":this.post({type:"reto",id:e.id,result:tt(e.source,e.challenge)});return;case"pausar":this.pauseRequested=this.running,this.post({type:"hecho",id:e.id});return}let t=this.machine;if(!t){this.post({type:"error",id:e.id,message:"No hay un programa cargado sin errores."});return}switch(e.type){case"entrada":this.inputLog.push(e.line),t.provideInput(e.line),this.post({type:"hecho",id:e.id});return;case"cerrar_entrada":this.inputClosed=!0,t.closeInput(),this.post({type:"hecho",id:e.id});return;case"puntos_de_interrupción":this.options={...this.options,breakpoints:e.lines},t.setBreakpoints(e.lines),this.post({type:"hecho",id:e.id});return}if(this.running)return this.busy(e.id);switch(e.type){case"paso":t.step(),this.stopped(e.id,t);return;case"ejecutar":case"continuar":return this.runInBatches(e.id,t,e.type==="ejecutar");case"ir_a_paso":this.goToStep(e.id,e.step);return}}busy(e){this.post({type:"error",id:e,message:"La ejecución sigue en curso; pausa antes de mandar otra orden."})}load(e,t,r){this.options=r,this.seed=(r.seed??Math.floor(Math.random()*4294967296))>>>0,this.inputLog=[],this.inputClosed=!1,this.events=[],this.sentOutput=0;let{program:a,tokens:n,errors:s,trace:i}=U(t,{trace:r.parseTrace??!1});this.program=s.length===0?a:void 0,this.machine=this.program?this.createMachine():void 0,this.post({type:"cargado",id:e,tokens:n,program:a,errors:s.map((c)=>c.toJSON()),seed:this.seed,...i?{parseTrace:i}:{}})}createMachine(){let{options:e}=this,t=zt[e.detail??"decisiones"],r={input:e.input??[],interactiveInput:e.interactiveInput??!0,seed:this.seed,breakpoints:e.breakpoints??[],onEvent:(a)=>{if(this.muted)return;if(t==="todo"||t.has(a.type))this.events.push(a)},...e.maxOperations!==void 0?{maxOperations:e.maxOperations}:{},...e.maxCallDepth!==void 0?{maxCallDepth:e.maxCallDepth}:{},...e.maxOutput!==void 0?{maxOutput:e.maxOutput}:{},...e.world?{world:st(e.world)}:{}};return new Q(te(this.program),r)}async runInBatches(e,t,r){this.running=!0,this.pauseRequested=!1;let a=this.options.breakpoints??[];if(r)t.setBreakpoints([]);try{while(!0){if(t.run(this.options.batchSize??50000)!=="pausada"||t.pauseReason!=="presupuesto")break;if(this.pauseRequested)break;if(this.post({type:"progreso",id:e,operations:t.operations,steps:t.steps,output:this.newOutput(t),events:this.takeEvents()}),await Gt(),this.pauseRequested)break}}finally{if(r)t.setBreakpoints(a);this.running=!1}this.stopped(e,t,this.pauseRequested&&t.status==="pausada"?"pausa":void 0),this.pauseRequested=!1}goToStep(e,t){let r=this.createMachine();for(let a of this.inputLog)r.provideInput(a);if(this.inputClosed)r.closeInput();this.muted=!0;try{while(r.steps<t&&!r.done&&r.status!=="esperando_entrada")r.step()}finally{this.muted=!1}this.machine=r,this.events=[],this.sentOutput=0,this.stopped(e,r,"retroceso",!0)}newOutput(e){let t=e.output,r=t.slice(this.sentOutput);return this.sentOutput=t.length,r}takeEvents(){let e=this.events;return this.events=[],e}stopped(e,t,r,a=!1){let n=t.status,s=r??(n==="terminada"?"fin":n==="error"?"error":n==="esperando_entrada"?"entrada":t.pauseReason==="punto_de_interrupción"?"punto_de_interrupción":"paso");this.post({type:"detenido",id:e,status:n,reason:s,operations:t.operations,steps:t.steps,output:this.newOutput(t),...a?{replaceOutput:!0}:{},events:this.takeEvents(),snapshot:t.snapshot(),...n==="terminada"?{result:k(t.result)}:{},...t.error?{error:t.error.toJSON()}:{}})}}var jt=new Te((e)=>self.postMessage(e));self.addEventListener("message",(e)=>{jt.handle(e.data)});
151
153
 
152
- //# debugId=7FF2A3B17D47FC4F64756E2164756E21
154
+ //# debugId=6AB18EEF0AA987BC64756E2164756E21
153
155
  //# sourceMappingURL=worker.js.map