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/CHANGELOG.md +36 -0
- package/dist/index.js +27 -25
- package/dist/index.js.map +8 -8
- package/dist/worker.js +29 -27
- package/dist/worker.js.map +6 -6
- package/docs/arquitectura.md +46 -1
- package/docs/especificacion-v1.md +19 -14
- package/docs/integracion.md +1 -0
- package/docs/lenguaje.md +22 -8
- package/package.json +1 -1
- package/src/catalog.ts +1 -0
- package/src/error-codes.ts +5 -0
- package/src/index.ts +2 -2
- package/src/runtime/builtins.ts +44 -0
- package/src/runtime/values.ts +1 -0
- package/src/sandbox-host.ts +15 -2
- package/src/syntax/parser.ts +295 -87
package/dist/index.js
CHANGED
|
@@ -1,27 +1,29 @@
|
|
|
1
|
-
class
|
|
2
|
-
`)};var ee;((l)=>{l.NUMBER="NUMBER";l.STRING="STRING";l.IDENT="IDENT";l.LET="LET";l.CONST="CONST";l.DOMAIN="DOMAIN";l.FUNCTION="FUNCTION";l.RETURN="RETURN";l.IF="IF";l.ELSEIF="ELSEIF";l.ELSE="ELSE";l.WHILE="WHILE";l.DO="DO";l.UNTIL="UNTIL";l.FOR="FOR";l.FOR_EACH="FOR_EACH";l.FROM="FROM";l.TO="TO";l.STEP="STEP";l.IN="IN";l.REPEAT="REPEAT";l.TIMES="TIMES";l.BREAK="BREAK";l.CONTINUE="CONTINUE";l.TRUE="TRUE";l.FALSE="FALSE";l.NULL="NULL";l.AND="AND";l.OR="OR";l.NOT="NOT";l.MOD="MOD";l.DIV="DIV";l.ASSIGN="ASSIGN";l.PLUS_ASSIGN="PLUS_ASSIGN";l.MINUS_ASSIGN="MINUS_ASSIGN";l.STAR_ASSIGN="STAR_ASSIGN";l.SLASH_ASSIGN="SLASH_ASSIGN";l.PERCENT_ASSIGN="PERCENT_ASSIGN";l.INCREMENT="INCREMENT";l.DECREMENT="DECREMENT";l.PLUS="PLUS";l.MINUS="MINUS";l.STAR="STAR";l.SLASH="SLASH";l.PERCENT="PERCENT";l.CARET="CARET";l.BANG="BANG";l.EQ="EQ";l.NEQ="NEQ";l.LT="LT";l.GT="GT";l.LTE="LTE";l.GTE="GTE";l.COMMA="COMMA";l.COLON="COLON";l.DOT="DOT";l.SEMICOLON="SEMICOLON";l.LPAREN="LPAREN";l.RPAREN="RPAREN";l.LBRACE="LBRACE";l.RBRACE="RBRACE";l.LBRACKET="LBRACKET";l.RBRACKET="RBRACKET";l.EOF="EOF";l.ILLEGAL="ILLEGAL"})(ee||={});var z={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"},
|
|
1
|
+
class q 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 It=(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 ee;((l)=>{l.NUMBER="NUMBER";l.STRING="STRING";l.IDENT="IDENT";l.LET="LET";l.CONST="CONST";l.DOMAIN="DOMAIN";l.FUNCTION="FUNCTION";l.RETURN="RETURN";l.IF="IF";l.ELSEIF="ELSEIF";l.ELSE="ELSE";l.WHILE="WHILE";l.DO="DO";l.UNTIL="UNTIL";l.FOR="FOR";l.FOR_EACH="FOR_EACH";l.FROM="FROM";l.TO="TO";l.STEP="STEP";l.IN="IN";l.REPEAT="REPEAT";l.TIMES="TIMES";l.BREAK="BREAK";l.CONTINUE="CONTINUE";l.TRUE="TRUE";l.FALSE="FALSE";l.NULL="NULL";l.AND="AND";l.OR="OR";l.NOT="NOT";l.MOD="MOD";l.DIV="DIV";l.ASSIGN="ASSIGN";l.PLUS_ASSIGN="PLUS_ASSIGN";l.MINUS_ASSIGN="MINUS_ASSIGN";l.STAR_ASSIGN="STAR_ASSIGN";l.SLASH_ASSIGN="SLASH_ASSIGN";l.PERCENT_ASSIGN="PERCENT_ASSIGN";l.INCREMENT="INCREMENT";l.DECREMENT="DECREMENT";l.PLUS="PLUS";l.MINUS="MINUS";l.STAR="STAR";l.SLASH="SLASH";l.PERCENT="PERCENT";l.CARET="CARET";l.BANG="BANG";l.EQ="EQ";l.NEQ="NEQ";l.LT="LT";l.GT="GT";l.LTE="LTE";l.GTE="GTE";l.COMMA="COMMA";l.COLON="COLON";l.DOT="DOT";l.SEMICOLON="SEMICOLON";l.LPAREN="LPAREN";l.RPAREN="RPAREN";l.LBRACE="LBRACE";l.RBRACE="RBRACE";l.LBRACKET="LBRACKET";l.RBRACKET="RBRACKET";l.EOF="EOF";l.ILLEGAL="ILLEGAL"})(ee||={});var z={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"},je=(e)=>Object.hasOwn(z,e),At={["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(z).map(([e,t])=>[t,e])),ye=(e)=>{let t=At[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"}},F=(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 je(e.lexeme)?`la palabra \`${e.lexeme}\``:ye(e.type)}};var oe=/[A-Za-z_À-ÖØ-öø-ÿ]/,G=/[0-9]/,Ct=/\s/,Tt={"==":"EQ","!=":"NEQ","<=":"LTE",">=":"GTE","+=":"PLUS_ASSIGN","-=":"MINUS_ASSIGN","*=":"STAR_ASSIGN","/=":"SLASH_ASSIGN","%=":"PERCENT_ASSIGN","++":"INCREMENT","--":"DECREMENT"},Dt={"=":"ASSIGN","+":"PLUS","-":"MINUS","*":"STAR","/":"SLASH","%":"PERCENT","^":"CARET","!":"BANG","<":"LT",">":"GT",",":"COMMA",":":"COLON",".":"DOT",";":"SEMICOLON","(":"LPAREN",")":"RPAREN","{":"LBRACE","}":"RBRACE","[":"LBRACKET","]":"RBRACKET"},Pt=new Set(["IDENT","NUMBER","STRING","RPAREN","RBRACKET","RBRACE"]),wt={n:`
|
|
3
3
|
`,t:"\t","\\":"\\",'"':'"',"'":"'"},$t={"&":"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 `'`."},Se=(e)=>{let t=[],r=[],a=0,n=1,i=1,s=()=>({line:n,column:i,offset:a}),o=(d=0)=>e[a+d]??"",c=()=>{let d=e[a]??"";if(a+=1,d===`
|
|
4
|
-
`)n+=1,i=1;else i+=1;return d},p=(d,u,I)=>{let
|
|
5
|
-
`)c();continue}if(d==="/"&&o(1)==="*"){c(),c();while(a<e.length&&!(o()==="*"&&o(1)==="/"))c();if(a>=e.length)E("COMENTARIO_SIN_CERRAR","El comentario que empieza aquí no se cerró.",{start:u,end:{line:u.line,column:u.column+2,offset:u.offset+2}},"Cierra el comentario con `*/`.");else c(),c();continue}let I=t[t.length-1],
|
|
6
|
-
`){let xe=c();if(xe===L){B=!0;break}if(xe==="\\"){let Ot={line:n,column:i-1,offset:a-1},ve=c(),je=wt[ve];if(je===void 0)E("ESCAPE_DESCONOCIDO",`\`\\${ve}\` no es una secuencia de escape válida.`,{start:Ot,end:s()},"Las secuencias válidas son `\\n`, `\\t`, `\\\\`, `\\\"` y `\\'`."),O+=ve;else O+=je;continue}O+=xe}let _t=p("STRING",u,O);if(!B)E("TEXTO_SIN_CERRAR","El texto que empieza aquí no se cerró.",_t.span,`Agrega la comilla \`${L}\` al final. Un texto no puede ocupar varias líneas; usa \`\\n\` para un salto de línea.`);continue}let b=d+o(1),R=Dt[b];if(R){c(),c(),p(R,u);continue}let g=Tt[d];if(g){c(),p(g,u);continue}c();let _=p("ILLEGAL",u);E("CARÁCTER_DESCONOCIDO",`Codexivo no reconoce el carácter \`${d}\`.`,_.span,$t[d])}let h=s();return t.push({type:"EOF",lexeme:"",span:{start:h,end:h}}),{tokens:t,errors:r}};var j=(e)=>{let{tokens:t,errors:r}=Se(e),a=new He(t),n=a.parseProgram(),i=[...r,...a.errors].sort((s,o)=>(s.span?.start.offset??0)-(o.span?.start.offset??0));return{program:n,tokens:t,errors:i}};var ce={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},Mt={PLUS:"+",MINUS:"-",STAR:"*",SLASH:"/",PERCENT:"mod",MOD:"mod",DIV:"div",CARET:"^",EQ:"==",NEQ:"!=",LT:"<",GT:">",LTE:"<=",GTE:">="},Vt={ASSIGN:"=",PLUS_ASSIGN:"+=",MINUS_ASSIGN:"-=",STAR_ASSIGN:"*=",SLASH_ASSIGN:"/=",PERCENT_ASSIGN:"%="},We={INCREMENT:"++",DECREMENT:"--"},Bt=new Set(["LET","DOMAIN","CONST","REPEAT","FOR_EACH","BREAK","CONTINUE","FUNCTION","RETURN","IF","WHILE","DO","FOR"]),qt=new Set(["VariableDeclaration","FunctionDeclaration","Domain"]),Ke=(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}},Ft={sino:"En Codexivo se escribe `si_no`, con guion bajo.",pero:"En Codexivo se escribe `pero_si`, con guion bajo."},Re=50,Ut=400;class te{}class He{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<Re)this.errors.push(new M({kind:"sintaxis",code:e,message:t,hint:a,span:r}));throw new te}failSilentlyOnIllegal(){if(this.check("ILLEGAL"))throw new te}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 ${ye(e)} ${t}, pero se encontró ${F(a)}.`,a.span,n)}enter(){if(this.depth+=1,this.depth>Ut)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(Bt.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 te))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>=Re)return;this.errors.push(new M({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",`${F(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=Ft[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 ${F(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ó ${F(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=Vt[this.current.type],a=We[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,i=this.parseCondition(r),s=this.parseBlock(`después de la condición de \`${r}\``);t.push({condition:i,body:s,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 i;if(!this.check("SEMICOLON"))if(this.check("LET")||this.check("CONST"))i=this.parseVariableDeclaration();else i=this.parseSimpleStatement();this.expect("SEMICOLON","después del inicio del `para`");let s=this.check("SEMICOLON")?void 0:this.parseExpression();this.expect("SEMICOLON","después de la condición del `para`");let o;if(!this.check("RPAREN"))o=this.parseSimpleStatement();return this.expect("RPAREN","para cerrar el `para`"),{init:i,condition:s,update:o}}),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=(c)=>{if(this.check("LBRACE"))this.error("EXPRESIÓN_ESPERADA",`Falta un valor después de \`${c}\`.`,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 i=a("hasta"),s=this.match("STEP")?a("con_paso"):void 0,o=this.parseLoopBody("después de `para … desde … hasta …`");return{kind:"ForRange",id:this.id(),variable:r,from:n,to:i,step:s,body:o,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 s of n){if(qt.has(s.kind)||this.errors.length>=Re)continue;this.errors.push(new M({kind:"sintaxis",code:"DOMINIO_SOLO_DECLARACIONES",message:Ke(s)?`Un dominio solo agrupa declaraciones, y \`${Ke(s)}\` 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:s.span}))}let i={kind:"Block",id:this.id(),statements:n,span:this.spanFrom(r.span.start)};return{kind:"Domain",id:this.id(),name:t,body:i,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 i=this.parseExpression(),s=this.parseLoopBody("después de `para_cada … en …`");return{kind:"ForEach",id:this.id(),variable:r,value:a,iterable:i,body:s,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<(ce[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&&We[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 te;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",`${F(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ó ${F(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ó ${F(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=ce[t.type],n=this.parseExpression(a),i=t.type==="AND"?"y":"o";return{kind:"Logical",id:this.id(),operator:i,left:e,right:n,span:this.spanFrom(r)}}default:{this.advance();let a=ce[t.type],n=this.parseExpression(a===8?a-1:a),i=Mt[t.type];if(a===4&&ce[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:i,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 i=this.grouped(()=>this.parseExpression());if(n.kind==="StringLiteral"||n.kind==="NumberLiteral"){if(r.has(n.value))this.error("CLAVE_REPETIDA",`La clave ${zt(n.value)} está repetida.`,n.span);if(r.add(n.value),i.kind==="FunctionLiteral"&&!i.name&&n.kind==="StringLiteral")i.name=n.value}if(t.push({key:n,value:i,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ó ${F(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 s=this.parseBindingName("como parámetro");if(a.has(s.name))this.error("PARÁMETRO_REPETIDO",`El parámetro \`${s.name}\` está repetido.`,s.span);if(a.add(s.name),r.push(s),!this.match("COMMA"))break}this.expect("RPAREN","para cerrar los parámetros");let n=this.loopDepth;this.loopDepth=0;let i;try{i=this.parseBlock("para el cuerpo del procedimiento")}finally{this.loopDepth=n}return{kind:"FunctionLiteral",id:this.id(),name:t,params:r,body:i,span:this.spanFrom(e)}}}var zt=(e)=>typeof e==="string"?JSON.stringify(e):String(e);var re=(e)=>{let t=new ke({name:"programa",params:[],code:[],node:e},!1);return t.statements(e.statements),t.emit({op:"halt"}),t.chunk},le=(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 ke(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},jt={"+=":"+","-=":"-","*=":"*","/=":"/","%=":"mod"};class ke{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:le(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:jt[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:le(e,this.hidden),node:e});return}}}var W=Symbol("esperar-entrada"),x=(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",K=(e)=>typeof e==="object"&&e!==null&&e.kind==="dominio",A=(e)=>typeof e==="object"&&e!==null&&e.kind==="diccionario",pe=(e)=>typeof e==="object"&&e!==null&&(e.kind==="procedimiento"||e.kind==="nativo"),Gt=(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 Gt(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),V=(e,t,r=[])=>{if(e===t)return!0;let a=v(e)&&v(t),n=A(e)&&A(t);if(!a&&!n)return!1;if(r.some(([s,o])=>s===e&&o===t))return!0;r.push([e,t]);let i;if(a){let[s,o]=[e,t];i=s.items.length===o.items.length&&s.items.every((c,p)=>V(c,o.items[p],r))}else{let[s,o]=[e,t];i=s.entries.size===o.entries.size&&[...s.entries].every(([c,p])=>o.entries.has(c)&&V(p,o.entries.get(c),r))}return r.pop(),i};class q{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 de=(e)=>{if(typeof e==="string")return e.length;if(e!==void 0&&v(e))return e.items.length;if(e!==void 0&&A(e))return e.entries.size;return 1},k=(e)=>Math.max(1,de(e[0])),Wt=(e)=>{let t=de(e[0]);return Math.max(1,t*Math.ceil(Math.log2(Math.max(t,1))))},Qe=(e)=>(t)=>Math.max(1,Math.ceil(e(t))),Xe=(e)=>e.length===1?k(e):Math.max(1,e.length),Je=(e)=>Math.max(1,de(e[1])),Ye=(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)},ue={máximo:Xe,mínimo:Xe,mayúsculas:k,minúsculas:k,recortar:k,dividir:k,unir:k,reemplazar:k,contiene:k,posición_de:k,empieza_con:Je,termina_con:Je,subtexto:Ye,caracteres:k,invertir:k,insertar:k,quitar:k,quitar_primero:k,desencolar:k,rebanar:Ye,copiar:k,sumar:k,ordenar:Wt,rango:Kt,índices:k,lista_de:Qe((e)=>typeof e[0]==="number"?e[0]*de(e[1]):1),matriz:Qe((e)=>typeof e[0]==="number"&&typeof e[1]==="number"?e[0]*e[1]:1),claves:k,valores:k,mapear:k,filtrar:k,reducir:k,alguno:k,todos:k,buscar:k};var m=(e,t,r,a)=>{let n=ue[e];return{kind:"nativo",name:e,arity:t,call:a,doc:r,...n?{cost:n}:{}}},me=(e,t,r)=>({kind:"nativo",name:e,arity:t,call:r}),P=(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(x(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(x(t))}.`)},D=(e,t,r,a="")=>{if(typeof t==="string")return t;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera un texto${a}, pero recibió ${y(x(t))} (${f(t)}).`)},fe=(e,t,r,a="")=>{let n=P(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(…)`.")},Ne=(e,t,r,a="")=>{let n=fe(e,t,r,a);if(n>=0&&n<=Q)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 ${Q}.`)},Ht=(e,t,r,a)=>{let n=r?"la lista":"el texto",i=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} ${i}.`,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).`)},Oe=(e,t,r,a,n,i=!1)=>{if(typeof t!=="number"||!Number.isInteger(t))throw n.fail("ÍNDICE_INVÁLIDO",`\`${e}\` necesita un índice entero, pero recibió ${f(t)}.`);let s=t<0?r+t:t;if(s<0||s>r||s===r&&!i)throw Ht(t,r,a,n);return s},be=(e,t,r=new Map)=>{if(!v(e)&&!A(e))return e;let a=r.get(e);if(a)return a;if(v(e)){let i=t.newList([]);r.set(e,i);for(let s of e.items)i.items.push(be(s,t,r));return i}let n=t.newDict(new Map);r.set(e,n);for(let[i,s]of e.entries)n.entries.set(i,be(s,t,r));return n},Ze=(e,t)=>t.fail("LISTA_VACÍA",`\`${e}\` no puede tomar nada de una lista vacía.`),et=(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(x(t))}.`)},tt=(e,t,r,a,n)=>{let i=Oe(e,t[1],r,a,n,!0),s=Oe(e,t[2],r,a,n,!0);if(i>s)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[i,s]},Qt=(e,t)=>{let a=typeof e.items[0]==="string"?"texto":"número";for(let n of e.items)if(x(n)!==a)throw t.fail("TIPO_DE_ARGUMENTO",`\`ordenar\` necesita que todos los elementos sean números o todos textos, pero encontró ${y(x(n))} (${f(n)}).`);return a},H=(e,t,r,a)=>{if(v(r))return r;throw a.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera una ${t} (una lista), pero recibió ${y(x(r))}.`,`Una ${t} empieza como una lista vacía: \`variable ${t} = [];\`.`)},he=(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\`.`),_e=(e,t,r)=>{if(A(t))return t;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera un diccionario, pero recibió ${y(x(t))}.`)},rt=(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(x(t))}.`)},at=(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)=>P(e,n,r))},nt=(e)=>{let t=e.trim();if(!/^[-+]?(\d+(\.\d*)?|\.\d+)$/.test(t))return;return Number(t)},Xt=(e,t)=>{let r=10**t;return Math.sign(e)*Math.round(Math.abs(e)*r)/r},Q=1e7,it=[m("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
|
|
4
|
+
`)n+=1,i=1;else i+=1;return d},p=(d,u,I)=>{let L={type:d,lexeme:e.slice(u.offset,a),span:{start:u,end:s()}};if(I!==void 0)L.value=I;return t.push(L),L},E=(d,u,I,L)=>{r.push(new q({kind:"léxico",code:d,message:u,hint:L,span:I}))};while(a<e.length){let d=o();if(Ct.test(d)){c();continue}let u=s();if(d==="/"&&o(1)==="/"){while(a<e.length&&o()!==`
|
|
5
|
+
`)c();continue}if(d==="/"&&o(1)==="*"){c(),c();while(a<e.length&&!(o()==="*"&&o(1)==="/"))c();if(a>=e.length)E("COMENTARIO_SIN_CERRAR","El comentario que empieza aquí no se cerró.",{start:u,end:{line:u.line,column:u.column+2,offset:u.offset+2}},"Cierra el comentario con `*/`.");else c(),c();continue}let I=t[t.length-1],L=I!==void 0&&Pt.has(I.type)&&I.span.end.offset===a;if(G.test(d)||d==="."&&G.test(o(1))&&!L){let C=()=>{while(G.test(o())||o()==="_")c()};if(C(),o()==="."&&G.test(o(1)))c(),C();if(oe.test(o())&&o()!=="_"){while(oe.test(o())||G.test(o()))c();let V=p("ILLEGAL",u);E("NOMBRE_EMPIEZA_CON_NÚMERO",`\`${V.lexeme}\` no es un número ni un nombre válido.`,V.span,"Los nombres no pueden empezar con un dígito. Si es una multiplicación, escribe el operador: `2 * x`.");continue}let O=e.slice(u.offset,a);if(/(^|\D)_|_(\D|$)/.test(O)){let V=p("ILLEGAL",u);E("SEPARADOR_INVÁLIDO",`\`${O}\` no es un número válido.`,V.span,"El `_` solo puede ir entre dos dígitos para separar grupos, como en `1_000_000`.");continue}p("NUMBER",u,Number(O.replaceAll("_","")));continue}if(oe.test(d)){while(oe.test(o())||G.test(o()))c();let C=e.slice(u.offset,a);p(z[C]??"IDENT",u);continue}if(d==='"'||d==="'"){let C=c(),O="",V=!1;while(a<e.length&&o()!==`
|
|
6
|
+
`){let xe=c();if(xe===C){V=!0;break}if(xe==="\\"){let Ot={line:n,column:i-1,offset:a-1},ve=c(),ze=wt[ve];if(ze===void 0)E("ESCAPE_DESCONOCIDO",`\`\\${ve}\` no es una secuencia de escape válida.`,{start:Ot,end:s()},"Las secuencias válidas son `\\n`, `\\t`, `\\\\`, `\\\"` y `\\'`."),O+=ve;else O+=ze;continue}O+=xe}let _t=p("STRING",u,O);if(!V)E("TEXTO_SIN_CERRAR","El texto que empieza aquí no se cerró.",_t.span,`Agrega la comilla \`${C}\` al final. Un texto no puede ocupar varias líneas; usa \`\\n\` para un salto de línea.`);continue}let b=d+o(1),R=Tt[b];if(R){c(),c(),p(R,u);continue}let g=Dt[d];if(g){c(),p(g,u);continue}c();let _=p("ILLEGAL",u);E("CARÁCTER_DESCONOCIDO",`Codexivo no reconoce el carácter \`${d}\`.`,_.span,$t[d])}let h=s();return t.push({type:"EOF",lexeme:"",span:{start:h,end:h}}),{tokens:t,errors:r}};var j=(e,t={})=>{let{tokens:r,errors:a}=Se(e),n=new He(r,t.trace?[]:void 0),i=n.parseProgram(),s=[...a,...n.errors].sort((o,c)=>(o.span?.start.offset??0)-(c.span?.start.offset??0));return n.trace?{program:i,tokens:r,errors:s,trace:n.trace}:{program:i,tokens:r,errors:s}},Mt=(e)=>j(e,{trace:!0});var ce={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},Vt={PLUS:"+",MINUS:"-",STAR:"*",SLASH:"/",PERCENT:"mod",MOD:"mod",DIV:"div",CARET:"^",EQ:"==",NEQ:"!=",LT:"<",GT:">",LTE:"<=",GTE:">="},qt={ASSIGN:"=",PLUS_ASSIGN:"+=",MINUS_ASSIGN:"-=",STAR_ASSIGN:"*=",SLASH_ASSIGN:"/=",PERCENT_ASSIGN:"%="},Ge={INCREMENT:"++",DECREMENT:"--"},Bt=new Set(["LET","DOMAIN","CONST","REPEAT","FOR_EACH","BREAK","CONTINUE","FUNCTION","RETURN","IF","WHILE","DO","FOR"]),Ft=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}},Ut={sino:"En Codexivo se escribe `si_no`, con guion bajo.",pero:"En Codexivo se escribe `pero_si`, con guion bajo."},Ke=50,zt=400;class te{}class He{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 te}report(e,t,r,a){if(this.errors.length>=Ke)return;this.errors.push(new q({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 te}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 ${ye(e)} ${t}, pero se encontró ${F(a)}.`,a.span,n)}enter(){if(this.depth+=1,this.depth>zt)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(Bt.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 i=this.position;this.skip(),this.traceRecover(n,i);continue}this.error("LLAVE_SOBRANTE","Sobra esta `}`: no hay ningún bloque abierto.",this.current.span)}t.push(this.parseStatement()),r=!1}catch(i){if(!(i instanceof te))throw i;this.depth=0,r=!0;let s=this.position;this.synchronize(a),this.traceRecover(n,s)}}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>=Ke)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",`${F(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=Ut[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 ${F(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ó ${F(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=qt[this.current.type],a=Ge[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,i=this.parseCondition(r),s=this.parseBlock(`después de la condición de \`${r}\``);t.push({condition:i,body:s,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 i;if(!this.check("SEMICOLON"))if(this.check("LET")||this.check("CONST"))i=this.parseVariableDeclaration();else i=this.parseSimpleStatement();this.expect("SEMICOLON","después del inicio del `para`");let s=this.check("SEMICOLON")?void 0:this.parseExpression();this.expect("SEMICOLON","después de la condición del `para`");let o;if(!this.check("RPAREN"))o=this.parseSimpleStatement();return this.expect("RPAREN","para cerrar el `para`"),{init:i,condition:s,update:o}}),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=(c)=>{if(this.check("LBRACE"))this.error("EXPRESIÓN_ESPERADA",`Falta un valor después de \`${c}\`.`,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 i=a("hasta"),s=this.match("STEP")?a("con_paso"):void 0,o=this.parseLoopBody("después de `para … desde … hasta …`");return this.finish({kind:"ForRange",id:this.id(),variable:r,from:n,to:i,step:s,body:o,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 s of n){if(Ft.has(s.kind))continue;this.report("DOMINIO_SOLO_DECLARACIONES",We(s)?`Un dominio solo agrupa declaraciones, y \`${We(s)}\` no es una.`:"Un dominio solo agrupa declaraciones, y esta sentencia no es una.",s.span,"Dentro de `dominio` van `variable`, `constante`, `procedimiento` u otro `dominio`. Lo demás va en un procedimiento o fuera del dominio.")}let i=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:i,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 i=this.parseExpression(),s=this.parseLoopBody("después de `para_cada … en …`");return this.finish({kind:"ForEach",id:this.id(),variable:r,value:a,iterable:i,body:s,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=ce[this.current.type];if(n===void 0)break;if(this.grouping===0&&this.current.span.start.line>this.previous.span.end.line)break;let i=e<n;if(this.trace)this.tracePrecedence(a,e,r,n,i);if(!i)break;a=this.parseInfix(a)}if(!t&&Ge[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 te;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",`${F(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ó ${F(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ó ${F(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",(i,s)=>({kind:"StringLiteral",id:s,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=ce[t.type],n=this.parseExpression(a,!1,t),i=t.type==="AND"?"y":"o";return this.finish({kind:"Logical",id:this.id(),operator:i,left:e,right:n,span:this.spanFrom(r)})}default:{this.begin("Binary",e),this.advance();let a=ce[t.type],n=this.parseExpression(a===8?a-1:a,!1,t),i=Vt[t.type];if(a===4&&ce[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:i,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 i=this.grouped(()=>this.parseExpression());if(n.kind==="StringLiteral"||n.kind==="NumberLiteral"){if(r.has(n.value))this.error("CLAVE_REPETIDA",`La clave ${jt(n.value)} está repetida.`,n.span);if(r.add(n.value),i.kind==="FunctionLiteral"&&!i.name&&n.kind==="StringLiteral")i.name=n.value}if(t.push({key:n,value:i,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ó ${F(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 s=this.parseBindingName("como parámetro");if(a.has(s.name))this.error("PARÁMETRO_REPETIDO",`El parámetro \`${s.name}\` está repetido.`,s.span);if(a.add(s.name),r.push(s),!this.match("COMMA"))break}this.expect("RPAREN","para cerrar los parámetros");let n=this.loopDepth;this.loopDepth=0;let i;try{i=this.parseBlock("para el cuerpo del procedimiento")}finally{this.loopDepth=n}return this.finish({kind:"FunctionLiteral",id:this.id(),name:t,params:r,body:i,span:this.spanFrom(e)})}}var jt=(e)=>typeof e==="string"?JSON.stringify(e):String(e);var re=(e)=>{let t=new Re({name:"programa",params:[],code:[],node:e},!1);return t.statements(e.statements),t.emit({op:"halt"}),t.chunk},le=(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 Re(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},Gt={"+=":"+","-=":"-","*=":"*","/=":"/","%=":"mod"};class Re{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:le(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:Gt[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:le(e,this.hidden),node:e});return}}}var W=Symbol("esperar-entrada"),x=(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",K=(e)=>typeof e==="object"&&e!==null&&e.kind==="dominio",A=(e)=>typeof e==="object"&&e!==null&&e.kind==="diccionario",pe=(e)=>typeof e==="object"&&e!==null&&(e.kind==="procedimiento"||e.kind==="nativo"),Wt=(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 Wt(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=y(e)&&y(t),n=A(e)&&A(t);if(!a&&!n)return!1;if(r.some(([s,o])=>s===e&&o===t))return!0;r.push([e,t]);let i;if(a){let[s,o]=[e,t];i=s.items.length===o.items.length&&s.items.every((c,p)=>M(c,o.items[p],r))}else{let[s,o]=[e,t];i=s.entries.size===o.entries.size&&[...s.entries].every(([c,p])=>o.entries.has(c)&&M(p,o.entries.get(c),r))}return r.pop(),i};class B{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 de=(e)=>{if(typeof e==="string")return e.length;if(e!==void 0&&y(e))return e.items.length;if(e!==void 0&&A(e))return e.entries.size;return 1},k=(e)=>Math.max(1,de(e[0])),Kt=(e)=>{let t=de(e[0]);return Math.max(1,t*Math.ceil(Math.log2(Math.max(t,1))))},Qe=(e)=>(t)=>Math.max(1,Math.ceil(e(t))),Xe=(e)=>e.length===1?k(e):Math.max(1,e.length),Je=(e)=>Math.max(1,de(e[1])),Ye=(e)=>typeof e[1]==="number"&&typeof e[2]==="number"?Math.max(1,Math.abs(e[2]-e[1])):1,Ht=(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)},ue={máximo:Xe,mínimo:Xe,mayúsculas:k,minúsculas:k,recortar:k,dividir:k,unir:k,reemplazar:k,contiene:k,posición_de:k,empieza_con:Je,termina_con:Je,subtexto:Ye,caracteres:k,invertir:k,insertar:k,quitar:k,quitar_primero:k,desencolar:k,rebanar:Ye,copiar:k,sumar:k,ordenar:Kt,rango:Ht,índices:k,lista_de:Qe((e)=>typeof e[0]==="number"?e[0]*de(e[1]):1),matriz:Qe((e)=>typeof e[0]==="number"&&typeof e[1]==="number"?e[0]*e[1]:1),claves:k,valores:k,mapear:k,filtrar:k,reducir:k,alguno:k,todos:k,buscar:k};var m=(e,t,r,a)=>{let n=ue[e];return{kind:"nativo",name:e,arity:t,call:a,doc:r,...n?{cost:n}:{}}},me=(e,t,r)=>({kind:"nativo",name:e,arity:t,call:r}),P=(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(x(t))} (${f(t)}).`)},w=(e,t,r)=>{if(y(t))return t;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera una lista, pero recibió ${v(x(t))}.`)},T=(e,t,r,a="")=>{if(typeof t==="string")return t;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera un texto${a}, pero recibió ${v(x(t))} (${f(t)}).`)},fe=(e,t,r,a="")=>{let n=P(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(…)`.")},ke=(e,t,r,a="")=>{let n=fe(e,t,r,a);if(n>=0&&n<=Q)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 ${Q}.`)},Qt=(e,t,r,a)=>{let n=r?"la lista":"el texto",i=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} ${i}.`,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).`)},_e=(e,t,r,a,n,i=!1)=>{if(typeof t!=="number"||!Number.isInteger(t))throw n.fail("ÍNDICE_INVÁLIDO",`\`${e}\` necesita un índice entero, pero recibió ${f(t)}.`);let s=t<0?r+t:t;if(s<0||s>r||s===r&&!i)throw Qt(t,r,a,n);return s},be=(e,t,r=new Map)=>{if(!y(e)&&!A(e))return e;let a=r.get(e);if(a)return a;if(y(e)){let i=t.newList([]);r.set(e,i);for(let s of e.items)i.items.push(be(s,t,r));return i}let n=t.newDict(new Map);r.set(e,n);for(let[i,s]of e.entries)n.entries.set(i,be(s,t,r));return n},Ze=(e,t)=>t.fail("LISTA_VACÍA",`\`${e}\` no puede tomar nada de una lista vacía.`),et=(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(x(t))}.`)},tt=(e,t,r,a,n)=>{let i=_e(e,t[1],r,a,n,!0),s=_e(e,t[2],r,a,n,!0);if(i>s)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[i,s]},Xt=(e,t)=>{let a=typeof e.items[0]==="string"?"texto":"número";for(let n of e.items)if(x(n)!==a)throw t.fail("TIPO_DE_ARGUMENTO",`\`ordenar\` necesita que todos los elementos sean números o todos textos, pero encontró ${v(x(n))} (${f(n)}).`);return a},H=(e,t,r,a)=>{if(y(r))return r;throw a.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera una ${t} (una lista), pero recibió ${v(x(r))}.`,`Una ${t} empieza como una lista vacía: \`variable ${t} = [];\`.`)},he=(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\`.`),Ne=(e,t,r)=>{if(A(t))return t;throw r.fail("TIPO_DE_ARGUMENTO",`\`${e}\` espera un diccionario, pero recibió ${v(x(t))}.`)},rt=(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(x(t))}.`)},at=(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)=>P(e,n,r))},nt=(e)=>{let t=e.trim();if(!/^[-+]?(\d+(\.\d*)?|\.\d+)$/.test(t))return;return Number(t)},Jt=(e,t)=>{let r=10**t;return Math.sign(e)*Math.round(Math.abs(e)*r)/r},Q=1e7,it=[m("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)),m("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()),m("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===W)return r;if(r===null)throw t.fail("SIN_ENTRADA","`leer_número` no encontró más datos de entrada.");let a=nt(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}),m("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)=>x(e[0])),m("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])),m("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=nt(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 ${
|
|
10
|
+
edad`,result:"0"}]},(e,t)=>{let r=t.readLine();if(r===W)return r;if(r===null)throw t.fail("SIN_ENTRADA","`leer_número` no encontró más datos de entrada.");let a=nt(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}),m("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)=>x(e[0])),m("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])),m("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=nt(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(x(r))} en número.`)}),m("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(A(r))return r.entries.size;throw t.fail("TIPO_DE_ARGUMENTO",`\`longitud\` espera un texto, una lista o un diccionario, pero recibió ${v(x(r))}.`)}),m("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ó ${v(x(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 ${v(x(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.")}),m("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(P("absoluto",e[0],t))),m("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(...at("máximo",e,t))),m("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(...at("mínimo",e,t))),m("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=P("redondear",e[0],t),a=e.length>1?P("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 Jt(r,a)}),m("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)),m("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=P("rango",e[0],t," como inicio"),a=P("rango",e[1],t," como final"),n=e.length>2?P("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 i=Math.floor((a-r)/n+0.000000001)+1;if(!Number.isFinite(i))throw t.fail("TIPO_DE_ARGUMENTO","`rango` necesita números finitos.");if(i>Q)throw t.fail("RANGO_DEMASIADO_GRANDE",`\`rango\` produciría ${i} números; el máximo es ${Q}.`,"Revisa el inicio, el final y el paso.");return t.newList(Array.from({length:Math.max(0,i)},(s,o)=>r+o*n))}),m("í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"&&!
|
|
14
|
+
l`,result:"[1, 2]"}]},(e,t)=>(w("agregar",e[0],t).items.push(e[1]),null)),m("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=P("rango",e[0],t," como inicio"),a=P("rango",e[1],t," como final"),n=e.length>2?P("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 i=Math.floor((a-r)/n+0.000000001)+1;if(!Number.isFinite(i))throw t.fail("TIPO_DE_ARGUMENTO","`rango` necesita números finitos.");if(i>Q)throw t.fail("RANGO_DEMASIADO_GRANDE",`\`rango\` produciría ${i} números; el máximo es ${Q}.`,"Revisa el inicio, el final y el paso.");return t.newList(Array.from({length:Math.max(0,i)},(s,o)=>r+o*n))}),m("í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(x(r))}.`);let a=typeof r==="string"?r.length:r.items.length;return t.newList(Array.from({length:a},(n,i)=>i))}),m("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(A(r)){let s=rt("quitar",e[1],t);if(!r.entries.has(s))throw t.fail("CLAVE_INEXISTENTE",`\`quitar\` no encontró la clave ${f(s)} en el diccionario.`,`Usa \`tiene(d, ${f(s)})\` para preguntar antes de quitarla.`);let o=r.entries.get(s);return r.entries.delete(s),o}if(!
|
|
15
|
-
dado >= 1 y dado <= 6`,result:"verdadero"}]},(e,t)=>{let r=fe("aleatorio",e[0],t," como mínimo"),a=fe("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))}),m("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)=>
|
|
16
|
+
[quitar(d, "a"), d]`,result:"[1, {}]"}]},(e,t)=>{let r=e[0];if(A(r)){let s=rt("quitar",e[1],t);if(!r.entries.has(s))throw t.fail("CLAVE_INEXISTENTE",`\`quitar\` no encontró la clave ${f(s)} en el diccionario.`,`Usa \`tiene(d, ${f(s)})\` para preguntar antes de quitarla.`);let o=r.entries.get(s);return r.entries.delete(s),o}if(!y(r))throw t.fail("TIPO_DE_ARGUMENTO",`\`quitar\` espera una lista o un diccionario, pero recibió ${v(x(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,i=a<0?n+a:a;if(i<0||i>=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(i,1)[0]}),m("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]}),m("ú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]}),m("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([...Ne("claves",e[0],t).entries.keys()])),m("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([...Ne("valores",e[0],t).entries.values()])),m("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)=>Ne("tiene",e[0],t).entries.has(rt("tiene",e[1],t))),m("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(P("piso",e[0],t))),m("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(P("techo",e[0],t))),m("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(P("entero",e[0],t))),m("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=P("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)}),m("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=fe("aleatorio",e[0],t," como mínimo"),a=fe("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))}),m("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)=>T("mayúsculas",e[0],t).toLocaleUpperCase("es")),m("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)=>T("minúsculas",e[0],t).toLocaleLowerCase("es")),m("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)=>T("recortar",e[0],t).trim()),m("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=T("dividir",e[0],t),a=T("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))}),m("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=T("unir",e[1],t," como separador");return r.items.map(ae).join(a)}),m("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=T("reemplazar",e[0],t),a=T("reemplazar",e[1],t," para buscar"),n=T("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)}),m("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)=>M(a,e[1]));return et("contiene",r,t).includes(T("contiene",e[1],t," para buscar"))}),m("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)=>M(a,e[1]));return et("posición_de",r,t).indexOf(T("posición_de",e[1],t," para buscar"))}),m("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)=>T("empieza_con",e[0],t).startsWith(T("empieza_con",e[1],t," como inicio"))),m("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)=>T("termina_con",e[0],t).endsWith(T("termina_con",e[1],t," como final"))),m("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=T("subtexto",e[0],t),[a,n]=tt("subtexto",e,r.length,!1,t);return r.slice(a,n)}),m("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([...T("caracteres",e[0],t)])),m("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=T("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)}),m("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=fe("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)}),m("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=
|
|
19
|
+
l`,result:"[1, 2, 3]"}]},(e,t)=>{let r=w("insertar",e[0],t),a=_e("insertar",e[1],r.items.length,!0,t,!0);return r.items.splice(a,0,e[2]),null}),m("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
20
|
[quitar_último(l), l]`,result:"[2, [1]]"}]},(e,t)=>{let r=w("quitar_último",e[0],t);if(r.items.length===0)throw Ze("quitar_último",t);return r.items.pop()}),m("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
21
|
[quitar_primero(l), l]`,result:"[1, [2]]"}]},(e,t)=>{let r=w("quitar_primero",e[0],t);if(r.items.length===0)throw Ze("quitar_primero",t);return r.items.shift()}),m("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(A(r))r.entries.clear();else w("vaciar",r,t).items.length=0;return null}),m("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]=tt("rebanar",e,r.items.length,!0,t);return t.newList(r.items.slice(a,n))}),m("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(
|
|
23
|
+
l`,result:"[]"}]},(e,t)=>{let r=e[0];if(A(r))r.entries.clear();else w("vaciar",r,t).items.length=0;return null}),m("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]=tt("rebanar",e,r.items.length,!0,t);return t.newList(r.items.slice(a,n))}),m("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(x(r))}.`)}),m("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(A(r))return t.newDict(new Map(r.entries));return t.newList([...w("copiar",r,t).items])}),m("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+P("sumar",a,t," en cada elemento"),0)),m("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=
|
|
26
|
+
[a, b]`,result:"[[1], [1, 2]]"}]},(e,t)=>{let r=e[0];if(A(r))return t.newDict(new Map(r.entries));return t.newList([...w("copiar",r,t).items])}),m("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+P("sumar",a,t," en cada elemento"),0)),m("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=ke("lista_de",e[0],t," como cantidad");return t.newList(Array.from({length:r},()=>be(e[1],t)))}),m("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=ke("matriz",e[0],t," como filas"),a=ke("matriz",e[1],t," como columnas");if(r*a>Q)throw t.fail("TIPO_DE_ARGUMENTO",`\`matriz\` produciría ${r*a} casillas; el máximo es ${Q}.`);return t.newList(Array.from({length:r},()=>t.newList(Array.from({length:a},()=>be(e[2],t)))))}),m("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)=>(H("apilar","pila",e[0],t).items.push(e[1]),null)),m("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)=>(H("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)=>(H("encolar","cola",e[0],t).items.push(e[1]),null)),m("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=H("desencolar","cola",e[0],t);if(r.items.length===0)throw he("desencolar","cola",t);return r.items.shift()}),m("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=H("frente","cola",e[0],t);if(r.items.length===0)throw he("frente","cola",t);return r.items[0]})],X={PI:Math.PI},st={PI:{level:"principiante",description:"La razón entre el perímetro de un círculo y su diámetro: 3.14159…"}},
|
|
34
|
+
[desencolar(c), c]`,result:"[1, [2]]"}]},(e,t)=>{let r=H("desencolar","cola",e[0],t);if(r.items.length===0)throw he("desencolar","cola",t);return r.items.shift()}),m("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=H("frente","cola",e[0],t);if(r.items.length===0)throw he("frente","cola",t);return r.items[0]})],X={PI:Math.PI},st={PI:{level:"principiante",description:"La razón entre el perímetro de un círculo y su diámetro: 3.14159…"}},Oe=(e)=>e.replace(/[áéíóúü]/g,(t)=>({á:"a",é:"e",í:"i",ó:"o",ú:"u",ü:"u"})[t]??t),ne=(()=>{let e={};for(let t of it)e[t.name]=t;for(let t of it){let r=Oe(t.name);if(!e[r])e[r]=t}return e})(),ot=Object.fromEntries([me("revisar_lista",2,(e,t)=>(w(e[0],e[1],t),null)),me("revisar_procedimiento",2,(e,t)=>{let r=e[1];if(pe(r))return null;throw t.fail("TIPO_DE_ARGUMENTO",`\`${e[0]}\` espera un procedimiento, pero recibió ${v(x(r))} (${f(r)}).`,"Pasa el nombre del procedimiento sin paréntesis (`mapear(lista, doble)`), o escribe uno: `procedimiento(x) { regresa x * 2 }`.")}),me("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`?")}),me("ordenar_natural",1,(e,t)=>{let r=w("ordenar",e[0],t),a=Xt(r,t),n=[...r.items].sort((i,s)=>a==="número"?i-s:i<s?-1:i>s?1:0);return t.newList(n)})].map((e)=>[e.name,e]));var Yt=`
|
|
33
35
|
procedimiento mapear(lista, f) {
|
|
34
36
|
revisar_lista("mapear", lista);
|
|
35
37
|
revisar_procedimiento("mapear", f);
|
|
@@ -134,21 +136,21 @@ procedimiento ordenar(lista, va_antes) {
|
|
|
134
136
|
}
|
|
135
137
|
regresa mezclar(lista);
|
|
136
138
|
}
|
|
137
|
-
`,J=()=>[{name:"lista",type:"lista"},{name:"f",type:"procedimiento"}],ct={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]"}]}},Yt={ordenar:1},Zt=()=>{let{program:e,errors:t}=j(Jt);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=le(a.function,!0),i=Yt[a.name.name];if(i!==void 0)n.minParams=i;r.set(a.name.name,n)}return r},ge=(()=>{let e=new q,t={},r=-1;for(let[a,n]of Zt()){let i={kind:"procedimiento",id:r--,name:a,chunk:n,closure:e};e.variables.set(a,i),t[a]=i}return t})();var er=(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 i=1;i<=t.length;i++){let s=r[i],o=e[a-1]===t[i-1]?0:1;r[i]=Math.min(r[i]+1,r[i-1]+1,n+o),n=s}}return r[t.length]},lt=(e)=>e.normalize("NFD").replace(/[̀-ͯ]/g,"").toLowerCase(),Ae=(e,t)=>{let r=lt(e),a=r.length<=3?1:2,n,i=1/0;for(let s of new Set(t)){if(s===e)continue;let o=er(r,lt(s));if(o<=a&&o<i)n=s,i=o}return n};var N=(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}},Ce={maxOperations:1e7,maxCallDepth:1e4,maxOutput:1e6},tr=new Set(["llama","regresa","imprime","lee","error","fin"]),pt=new Set(["step","enterScope","exitScope","jump","dup","pop","repeatStart","forEachStart","rangeStart","halt"]);class Y{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={...ne,...t.world?.natives,...t.natives},this.limits={maxOperations:t.maxOperations??Ce.maxOperations,maxCallDepth:t.maxCallDepth??Ce.maxCallDepth,maxOutput:t.maxOutput??Ce.maxOutput},this.inputQueue=typeof t.input==="string"?dt(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 q;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(...dt(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)&&!A(a)&&!K(a)||e[a.id])return;if(K(a))e[a.id]={domain:a.name,members:[...a.scope.variables].map(([n,i])=>({name:n,value:N(i),writable:a.writable.has(n)}))},a.scope.variables.forEach(t);else if(v(a))e[a.id]=a.items.map(N),a.items.forEach(t);else e[a.id]={entries:[...a.entries].map(([n,i])=>({key:N(n),value:N(i)}))},a.entries.forEach(t)},r=this.frames.map((a)=>{let n=[],i=a.scope;while(i){let s={};for(let[o,c]of i.variables)s[o]=N(c),t(c);if(n.unshift(s),i===a.root)break;i=i.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],i=n.chunk.code[n.pc];if(!pt.has(i.op)&&!n.chunk.hidden){if(this.operations>=this.limits.maxOperations)throw this.limitError("DEMASIADAS_OPERACIONES",`El programa superó el límite de ${Le(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(i.op==="step"&&e==="run"&&!n.chunk.hidden&&a>0&&this.breakpoints.has(i.node.span.start.line))return this.status="pausada",this.pauseReason="punto_de_interrupción",this.status;let s=this.exec(n,i);a+=1;let o=this.status;if(o==="esperando_entrada"||o==="terminada")return o;if(s&&!n.chunk.hidden){if(this.steps+=1,e==="step")return this.status="pausada",this.pauseReason="paso",this.status}}}catch(n){if(!(n instanceof M))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,!pt.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,i)=>{a.set(this.dictKey(r[i*2],n.key),r[i*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?ot[t.name]:void 0)??this.natives[t.name]??ge[t.name]??X[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:N(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(K(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:N(n),after:N(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:N(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:i}=t,s=(h,d,u)=>{if(typeof h==="number"&&Number.isFinite(h))return h;throw this.runtimeError("PARA_LÍMITE_INVÁLIDO",`${u} de \`para\` tiene que ser un número, pero es ${f(h)}.`,d,typeof h==="string"?"Convierte el texto con `número(…)`.":void 0)},o=s(n,i.from,"El inicio"),c=s(a,i.to,"El final"),p=s(r,i.step??i,"El paso");if(p===0)throw this.runtimeError("PARA_PASO_CERO","El paso de un `para` no puede ser 0: nunca llegaría al final.",i.step??i,"Usa un paso positivo para contar hacia arriba o uno negativo para contar hacia abajo.");if((c-o)*p<0)throw this.runtimeError("PARA_SENTIDO_CONTRARIO",p>0?`El \`para\` empieza en ${f(o)}, que es mayor que ${f(c)}, y cuenta hacia arriba.`:`El \`para\` empieza en ${f(o)}, que es menor que ${f(c)}, y cuenta hacia abajo.`,t.explicitStep?i.step:i.to,t.explicitStep?`Para ir de ${f(o)} a ${f(c)} el paso tiene que ser ${p>0?"negativo":"positivo"}.`:"Para contar hacia atrás escribe `con_paso -1`.");let E=Math.floor((c-o)/p+0.000000001)+1;return this.stack.push(o,p,E,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 i=this.stack[this.stack.length-4],s=this.stack[this.stack.length-3];this.stack[this.stack.length-1]=r+1,this.stack.push(Number((i+r*s).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(A(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(x(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(x(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],i=typeof a==="number"?a:a.items.length;if(v(n)&&n.items.length!==i)throw this.runtimeError("LISTA_MODIFICADA_AL_RECORRER",`La lista cambió de tamaño mientras se recorría: tenía ${i} 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(A(n)&&(n.entries.size!==i||r<i&&!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 s=r<i;if(s)if(this.stack[this.stack.length-1]=r+1,A(n)){let o=a.items[r];if(t.owner.value)this.stack.push(n.entries.get(o));this.stack.push(o)}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:s,nodeId:t.node.id,ownerId:t.owner.id,span:t.node.span}),!s)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 q(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:N(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:N(e)})}call(e,t,r){let a=this.stack.length-t-1,n=this.stack[a],i=n!==null&&typeof n==="object"&&"name"in n&&n.name?n.name:ut(r);if(!pe(n))throw this.runtimeError("NO_ES_PROCEDIMIENTO",`\`${ut(r)}\` no es un procedimiento, es ${y(x(n))}.`,r.callee);let s=this.stack.slice(a+1),o=n.kind==="nativo"?n.arity:n.chunk.minParams!==void 0?[n.chunk.minParams,n.chunk.params.length]:n.chunk.params.length;if(rr(o,t,i,(p,E)=>this.runtimeError("CANTIDAD_DE_ARGUMENTOS",p,r,E??(e.chunk.hidden?`\`${e.name}\` llama al procedimiento que recibió con ${t===1?"1 valor":`${t} valores`}.`:void 0))),n.kind==="nativo"){let p=n.call(s,this.nativeContext(r));if(p===W){if(e.pc-=1,!e.chunk.hidden)this.operations-=1;return this.status="esperando_entrada",!0}if(!e.chunk.hidden)this.charge(n.cost,s);return this.stack.length=a,this.stack.push(p),!1}if(this.frames.length-1>=this.limits.maxCallDepth)throw this.limitError("DEMASIADAS_LLAMADAS",`Hay más de ${Le(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(ue[n.chunk.name],s);let c=new q(n.closure);if(n.chunk.params.forEach((p,E)=>c.variables.set(p,s[E]??null)),!(e.chunk.hidden&&n.chunk.hidden))this.emit({type:"llama",name:i,args:s.map(N),nodeId:r.id,span:r.span});return this.frames.push({chunk:n.chunk,pc:0,scope:c,root:c,stackBase:a+1,name:i,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 ${Le(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 W;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(x(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(x(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,De(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 ${y(x(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 i=t**r;if(Number.isNaN(i))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 i}if(r===0){let i=a.kind==="Binary"?a.right:a.kind==="Assignment"?a.value:a;throw this.runtimeError("DIVISIÓN_ENTRE_CERO","No se puede dividir entre cero.",i,"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(x(t))} con ${y(x(r))} usando \`${e}\`.`,a,"`<`, `>`, `<=` y `>=` comparan dos números o dos textos.");let i=t,s=r;if(e==="<")return i<s;if(e===">")return i>s;if(e==="<=")return i<=s;return i>=s}}}index(e,t,r){if(K(e))return e.scope.variables.get(this.member(e,t,r));if(A(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(x(e))}.`:`Solo se puede usar \`[ ]\` con listas, textos y diccionarios, no con ${y(x(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(K(e)){let o=this.member(e,t,a);if(!e.writable.has(o))throw this.runtimeError("MIEMBRO_DE_SOLO_LECTURA",`\`${e.name}.${o}\` 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 c=e.scope.variables.get(o);if(e.scope.variables.set(o,r),this.onEvent)this.emit({type:"asigna",name:`${e.name}.${o}`,before:N(c),after:N(r),nodeId:n.id,span:n.span});return}if(a.dot&&!A(e))throw this.runtimeError("ÍNDICE_NO_SOPORTADO",`Solo los diccionarios y los dominios tienen campos con \`.\`, no ${y(x(e))}.`,a.object);if(A(e)){let o=this.dictKey(t,a.index),c=e.entries.has(o)?e.entries.get(o):null;e.entries.set(o,r),this.emitModification(e,o,c,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(x(e))}.`,a.object);let i=this.position(e,t,a),s=e.items[i];e.items[i]=r,this.emitModification(e,i,s,r,n)}emitModification(e,t,r,a,n){if(!this.onEvent)return;this.emit({type:"modifica",ref:e.id,key:N(t),before:N(r),after:N(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=Ae(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 i=v(e)?"la lista":"el texto",s=v(e)?"elemento":"carácter";throw this.runtimeError("ÍNDICE_FUERA_DE_RANGO",`El índice ${t} está fuera de ${i}, que tiene ${a} ${a===1?s:s==="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 M({kind:"ejecución",code:e,message:t,hint:a,span:this.visibleSpan(r.span),callStack:this.callStack()})}limitError(e,t,r,a){return new M({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(x(t))} y ${y(x(r))}.`,a)}unknownName(e,t,r){let a=[...Object.keys(this.natives),...Object.keys(ge),...Object.keys(X)];for(let i=e.scope;i;i=i.parent)a.push(...i.variables.keys());let n=Ae(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(x(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(X,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]||ge[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,De(t))}nonBooleanLogical(e,t,r){return this.runtimeError("OPERANDO_NO_LÓGICO",`\`${e.operator}\` une valores \`verdadero\` o \`falso\`, pero recibió ${f(t)}.`,r,De(t))}emitOperation(e,t,r,a){this.emit({type:"opera",operator:e,operands:t.map(N),result:N(r),nodeId:a.id,span:a.span})}emit(e){if(!this.onEvent)return;if(this.frames[this.frames.length-1]?.chunk.hidden&&!tr.has(e.type))return;this.onEvent({...e,operations:this.operations,depth:this.frames.length})}}var dt=(e)=>e.replace(/\r\n/g,`
|
|
139
|
+
`,J=()=>[{name:"lista",type:"lista"},{name:"f",type:"procedimiento"}],ct={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]"}]}},Zt={ordenar:1},er=()=>{let{program:e,errors:t}=j(Yt);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=le(a.function,!0),i=Zt[a.name.name];if(i!==void 0)n.minParams=i;r.set(a.name.name,n)}return r},ge=(()=>{let e=new B,t={},r=-1;for(let[a,n]of er()){let i={kind:"procedimiento",id:r--,name:a,chunk:n,closure:e};e.variables.set(a,i),t[a]=i}return t})();var tr=(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 i=1;i<=t.length;i++){let s=r[i],o=e[a-1]===t[i-1]?0:1;r[i]=Math.min(r[i]+1,r[i-1]+1,n+o),n=s}}return r[t.length]},lt=(e)=>e.normalize("NFD").replace(/[̀-ͯ]/g,"").toLowerCase(),Ie=(e,t)=>{let r=lt(e),a=r.length<=3?1:2,n,i=1/0;for(let s of new Set(t)){if(s===e)continue;let o=tr(r,lt(s));if(o<=a&&o<i)n=s,i=o}return n};var N=(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}},Ae={maxOperations:1e7,maxCallDepth:1e4,maxOutput:1e6},rr=new Set(["llama","regresa","imprime","lee","error","fin"]),pt=new Set(["step","enterScope","exitScope","jump","dup","pop","repeatStart","forEachStart","rangeStart","halt"]);class Y{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={...ne,...t.world?.natives,...t.natives},this.limits={maxOperations:t.maxOperations??Ae.maxOperations,maxCallDepth:t.maxCallDepth??Ae.maxCallDepth,maxOutput:t.maxOutput??Ae.maxOutput},this.inputQueue=typeof t.input==="string"?dt(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 B;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(...dt(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)&&!A(a)&&!K(a)||e[a.id])return;if(K(a))e[a.id]={domain:a.name,members:[...a.scope.variables].map(([n,i])=>({name:n,value:N(i),writable:a.writable.has(n)}))},a.scope.variables.forEach(t);else if(y(a))e[a.id]=a.items.map(N),a.items.forEach(t);else e[a.id]={entries:[...a.entries].map(([n,i])=>({key:N(n),value:N(i)}))},a.entries.forEach(t)},r=this.frames.map((a)=>{let n=[],i=a.scope;while(i){let s={};for(let[o,c]of i.variables)s[o]=N(c),t(c);if(n.unshift(s),i===a.root)break;i=i.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],i=n.chunk.code[n.pc];if(!pt.has(i.op)&&!n.chunk.hidden){if(this.operations>=this.limits.maxOperations)throw this.limitError("DEMASIADAS_OPERACIONES",`El programa superó el límite de ${Le(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(i.op==="step"&&e==="run"&&!n.chunk.hidden&&a>0&&this.breakpoints.has(i.node.span.start.line))return this.status="pausada",this.pauseReason="punto_de_interrupción",this.status;let s=this.exec(n,i);a+=1;let o=this.status;if(o==="esperando_entrada"||o==="terminada")return o;if(s&&!n.chunk.hidden){if(this.steps+=1,e==="step")return this.status="pausada",this.pauseReason="paso",this.status}}}catch(n){if(!(n instanceof q))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,!pt.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,i)=>{a.set(this.dictKey(r[i*2],n.key),r[i*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?ot[t.name]:void 0)??this.natives[t.name]??ge[t.name]??X[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:N(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(K(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:N(n),after:N(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:N(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:i}=t,s=(h,d,u)=>{if(typeof h==="number"&&Number.isFinite(h))return h;throw this.runtimeError("PARA_LÍMITE_INVÁLIDO",`${u} de \`para\` tiene que ser un número, pero es ${f(h)}.`,d,typeof h==="string"?"Convierte el texto con `número(…)`.":void 0)},o=s(n,i.from,"El inicio"),c=s(a,i.to,"El final"),p=s(r,i.step??i,"El paso");if(p===0)throw this.runtimeError("PARA_PASO_CERO","El paso de un `para` no puede ser 0: nunca llegaría al final.",i.step??i,"Usa un paso positivo para contar hacia arriba o uno negativo para contar hacia abajo.");if((c-o)*p<0)throw this.runtimeError("PARA_SENTIDO_CONTRARIO",p>0?`El \`para\` empieza en ${f(o)}, que es mayor que ${f(c)}, y cuenta hacia arriba.`:`El \`para\` empieza en ${f(o)}, que es menor que ${f(c)}, y cuenta hacia abajo.`,t.explicitStep?i.step:i.to,t.explicitStep?`Para ir de ${f(o)} a ${f(c)} el paso tiene que ser ${p>0?"negativo":"positivo"}.`:"Para contar hacia atrás escribe `con_paso -1`.");let E=Math.floor((c-o)/p+0.000000001)+1;return this.stack.push(o,p,E,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 i=this.stack[this.stack.length-4],s=this.stack[this.stack.length-3];this.stack[this.stack.length-1]=r+1,this.stack.push(Number((i+r*s).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(A(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(x(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(x(r))}.`,t.node,typeof r==="number"?`Para contar, recorre un rango: \`rango(1, ${f(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],i=typeof a==="number"?a:a.items.length;if(y(n)&&n.items.length!==i)throw this.runtimeError("LISTA_MODIFICADA_AL_RECORRER",`La lista cambió de tamaño mientras se recorría: tenía ${i} 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(A(n)&&(n.entries.size!==i||r<i&&!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 s=r<i;if(s)if(this.stack[this.stack.length-1]=r+1,A(n)){let o=a.items[r];if(t.owner.value)this.stack.push(n.entries.get(o));this.stack.push(o)}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:s,nodeId:t.node.id,ownerId:t.owner.id,span:t.node.span}),!s)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 B(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:N(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:N(e)})}call(e,t,r){let a=this.stack.length-t-1,n=this.stack[a],i=n!==null&&typeof n==="object"&&"name"in n&&n.name?n.name:ut(r);if(!pe(n))throw this.runtimeError("NO_ES_PROCEDIMIENTO",`\`${ut(r)}\` no es un procedimiento, es ${v(x(n))}.`,r.callee);let s=this.stack.slice(a+1),o=n.kind==="nativo"?n.arity:n.chunk.minParams!==void 0?[n.chunk.minParams,n.chunk.params.length]:n.chunk.params.length;if(ar(o,t,i,(p,E)=>this.runtimeError("CANTIDAD_DE_ARGUMENTOS",p,r,E??(e.chunk.hidden?`\`${e.name}\` llama al procedimiento que recibió con ${t===1?"1 valor":`${t} valores`}.`:void 0))),n.kind==="nativo"){let p=n.call(s,this.nativeContext(r));if(p===W){if(e.pc-=1,!e.chunk.hidden)this.operations-=1;return this.status="esperando_entrada",!0}if(!e.chunk.hidden)this.charge(n.cost,s);return this.stack.length=a,this.stack.push(p),!1}if(this.frames.length-1>=this.limits.maxCallDepth)throw this.limitError("DEMASIADAS_LLAMADAS",`Hay más de ${Le(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(ue[n.chunk.name],s);let c=new B(n.closure);if(n.chunk.params.forEach((p,E)=>c.variables.set(p,s[E]??null)),!(e.chunk.hidden&&n.chunk.hidden))this.emit({type:"llama",name:i,args:s.map(N),nodeId:r.id,span:r.span});return this.frames.push({chunk:n.chunk,pc:0,scope:c,root:c,stackBase:a+1,name:i,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 ${Le(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 W;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(x(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(x(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,Ce(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 ${v(x(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 i=t**r;if(Number.isNaN(i))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 i}if(r===0){let i=a.kind==="Binary"?a.right:a.kind==="Assignment"?a.value:a;throw this.runtimeError("DIVISIÓN_ENTRE_CERO","No se puede dividir entre cero.",i,"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(x(t))} con ${v(x(r))} usando \`${e}\`.`,a,"`<`, `>`, `<=` y `>=` comparan dos números o dos textos.");let i=t,s=r;if(e==="<")return i<s;if(e===">")return i>s;if(e==="<=")return i<=s;return i>=s}}}index(e,t,r){if(K(e))return e.scope.variables.get(this.member(e,t,r));if(A(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(x(e))}.`:`Solo se puede usar \`[ ]\` con listas, textos y diccionarios, no con ${v(x(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(K(e)){let o=this.member(e,t,a);if(!e.writable.has(o))throw this.runtimeError("MIEMBRO_DE_SOLO_LECTURA",`\`${e.name}.${o}\` 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 c=e.scope.variables.get(o);if(e.scope.variables.set(o,r),this.onEvent)this.emit({type:"asigna",name:`${e.name}.${o}`,before:N(c),after:N(r),nodeId:n.id,span:n.span});return}if(a.dot&&!A(e))throw this.runtimeError("ÍNDICE_NO_SOPORTADO",`Solo los diccionarios y los dominios tienen campos con \`.\`, no ${v(x(e))}.`,a.object);if(A(e)){let o=this.dictKey(t,a.index),c=e.entries.has(o)?e.entries.get(o):null;e.entries.set(o,r),this.emitModification(e,o,c,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(x(e))}.`,a.object);let i=this.position(e,t,a),s=e.items[i];e.items[i]=r,this.emitModification(e,i,s,r,n)}emitModification(e,t,r,a,n){if(!this.onEvent)return;this.emit({type:"modifica",ref:e.id,key:N(t),before:N(r),after:N(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=Ie(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=y(e)?e.items.length:e.length,n=t<0?a+t:t;if(n<0||n>=a){let i=y(e)?"la lista":"el texto",s=y(e)?"elemento":"carácter";throw this.runtimeError("ÍNDICE_FUERA_DE_RANGO",`El índice ${t} está fuera de ${i}, que tiene ${a} ${a===1?s:s==="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 q({kind:"ejecución",code:e,message:t,hint:a,span:this.visibleSpan(r.span),callStack:this.callStack()})}limitError(e,t,r,a){return new q({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(x(t))} y ${v(x(r))}.`,a)}unknownName(e,t,r){let a=[...Object.keys(this.natives),...Object.keys(ge),...Object.keys(X)];for(let i=e.scope;i;i=i.parent)a.push(...i.variables.keys());let n=Ie(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(x(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(X,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]||ge[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,Ce(t))}nonBooleanLogical(e,t,r){return this.runtimeError("OPERANDO_NO_LÓGICO",`\`${e.operator}\` une valores \`verdadero\` o \`falso\`, pero recibió ${f(t)}.`,r,Ce(t))}emitOperation(e,t,r,a){this.emit({type:"opera",operator:e,operands:t.map(N),result:N(r),nodeId:a.id,span:a.span})}emit(e){if(!this.onEvent)return;if(this.frames[this.frames.length-1]?.chunk.hidden&&!rr.has(e.type))return;this.onEvent({...e,operations:this.operations,depth:this.frames.length})}}var dt=(e)=>e.replace(/\r\n/g,`
|
|
138
140
|
`).replace(/\n$/,"").split(`
|
|
139
|
-
`),Le=(e)=>e.toLocaleString("es-MX").replace(/,/g," "),ut=(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"},
|
|
140
|
-
mirando_al_norte()`,"verdadero"),girar_izquierda:
|
|
141
|
-
mirando_al_oeste()`,"verdadero"),recoger_zumbador:
|
|
142
|
-
hay_zumbador()`,"verdadero"),dejar_zumbador:
|
|
143
|
-
tengo_zumbadores()`,"falso"),apagar:
|
|
144
|
-
frente_libre()`,"verdadero"),frente_libre:T("`verdadero` si no hay pared enfrente del robot.","frente_libre()","verdadero",!0),izquierda_libre:T("`verdadero` si no hay pared a la izquierda del robot.","izquierda_libre()","falso",!0),derecha_libre:T("`verdadero` si no hay pared a la derecha del robot.","derecha_libre()","falso",!0),hay_zumbador:T("`verdadero` si hay al menos un zumbador en la casilla del robot.","hay_zumbador()","verdadero",!0),tengo_zumbadores:T("`verdadero` si hay al menos un zumbador en la mochila.","tengo_zumbadores()","verdadero",!0),mirando_al_norte:T("`verdadero` si el robot mira al norte.","mirando_al_norte()","verdadero",!0),mirando_al_este:T("`verdadero` si el robot mira al este.","mirando_al_este()","falso",!0),mirando_al_sur:T("`verdadero` si el robot mira al sur.","mirando_al_sur()","falso",!0),mirando_al_oeste:T("`verdadero` si el robot mira al oeste.","mirando_al_oeste()","falso",!0)},nr=(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[s,o]of Object.entries(t.robot??{})){if(o===void 0)continue;let c=e.robot[s];if(c!==o)r.push({field:`robot.${s}`,expected:o,actual:c,message:s==="on"?o?"El robot tenía que quedar encendido.":"El robot tenía que terminar apagado: falta `apagar()`.":`En ${a[s]} se esperaba ${JSON.stringify(o)}, pero quedó ${JSON.stringify(c)}.`})}let n=new Map(e.beepers.map((s)=>[`${s.x},${s.y}`,s.count])),i=new Set;for(let s of t.beepers??[]){let o=`${s.x},${s.y}`;i.add(o);let c=n.get(o)??0;if(c!==s.count)r.push({field:`beepers(${s.x}, ${s.y})`,expected:s.count,actual:c,message:`En la casilla (${s.x}, ${s.y}) se esperaban ${s.count} zumbadores, pero hay ${c}.`})}if(t.onlyListedBeepers)for(let[s,o]of n){if(i.has(s))continue;let[c,p]=s.split(",");r.push({field:`beepers(${c}, ${p})`,expected:0,actual:o,message:`La casilla (${c}, ${p}) tenía que quedar vacía, pero tiene ${o} zumbadores.`})}return{ok:r.length===0,differences:r}};var vt={CARÁCTER_DESCONOCIDO:{kind:"léxico",explanation:"El código tiene un carácter que Codexivo no usa, como `&`, `|` o `@`. Suele pasar al copiar código de otro lenguaje o de un documento: las comillas tipográficas (“ ”) no son comillas de código. Para «y» y «o» se escriben las palabras `y` y `o`."},COMENTARIO_SIN_CERRAR:{kind:"léxico",explanation:"Un comentario `/*` sigue hasta encontrar `*/`. Si falta el cierre, todo lo que viene después queda dentro del comentario y no se ejecuta."},ESCAPE_DESCONOCIDO:{kind:"léxico",explanation:"Dentro de un texto, `\\` empieza una secuencia de escape. Las válidas son `\\n` (salto de línea), `\\t` (tabulador), `\\\\`, `\\\"` y `\\'`. Para escribir una diagonal invertida se escribe dos veces."},NOMBRE_EMPIEZA_CON_NÚMERO:{kind:"léxico",explanation:"Los nombres pueden llevar dígitos, pero no al principio: `x2` es un nombre y `2x` no. Si querías multiplicar, escribe el operador: `2 * x`."},SEPARADOR_INVÁLIDO:{kind:"léxico",explanation:"En los números, `_` solo sirve para separar grupos de dígitos y que se lean mejor: `1_000_000`. Tiene que ir entre dos dígitos; `1__0` o `1_` no son números."},TEXTO_SIN_CERRAR:{kind:"léxico",explanation:"Un texto empieza y termina con la misma comilla, en la misma línea. Si falta la comilla final, Codexivo no sabe dónde termina. Para un salto de línea dentro del texto usa `\\n`."},ANIDAMIENTO_EXCESIVO:{kind:"sintaxis",explanation:"La expresión tiene demasiados niveles de paréntesis o de operaciones anidadas. Guarda partes en variables intermedias: además de evitar el error, el programa se lee mejor."},ASIGNACIÓN_INVÁLIDA:{kind:"sintaxis",explanation:"A la izquierda de `=` (o de `+=`, `++`…) tiene que ir algo que pueda guardar un valor: una variable (`x = 1`), un elemento (`lista[0] = 1`) o un campo (`persona.edad = 16`). `5 = x` o `f(x) = 1` no tienen dónde guardarlo."},BLOQUE_SIN_CERRAR:{kind:"sintaxis",explanation:"Cada `{` necesita su `}`. El error señala la llave que abrió el bloque; revisa que cada `si`, `mientras` o procedimiento cierre su bloque antes de que empiece lo que sigue."},CAMPO_ESPERADO:{kind:"sintaxis",explanation:'Después de `.` va el nombre de un campo de un diccionario: `persona.edad`. Solo funciona con claves que son nombres válidos y que no son palabras reservadas; para las demás se escribe `d["clave"]`.'},CLAVE_REPETIDA:{kind:"sintaxis",explanation:"Un diccionario escrito entre llaves tiene la misma clave dos veces. Solo se quedaría el último valor, así que casi siempre es un error de copiar y pegar."},CLAVE_SIN_COMILLAS:{kind:"sintaxis",explanation:'Las claves de un diccionario son textos o números, y los textos van entre comillas: `{ "nombre": "Ana" }`. Si querías usar el valor de una variable como clave, escríbela entre paréntesis: `{ (clave): 1 }`.'},COMPARACIÓN_ENCADENADA:{kind:"sintaxis",explanation:"`1 < x < 5` parece natural, pero esconde dos comparaciones. En Codexivo se escriben las dos y se unen con `y`: `1 < x y x < 5`."},CONTINUAR_FUERA_DE_BUCLE:{kind:"sintaxis",explanation:"`continuar` salta a la siguiente vuelta del bucle más cercano, así que solo tiene sentido dentro de uno. Dentro de un procedimiento no afecta al bucle desde donde se llamó."},DICCIONARIO_COMO_SENTENCIA:{kind:"sintaxis",explanation:'Al inicio de una sentencia, `{` abre un bloque. Un diccionario tiene que estar en una expresión: guárdalo en una variable (`variable d = { "a": 1 }`) o pásalo a un procedimiento.'},DOMINIO_SOLO_DECLARACIONES:{kind:"sintaxis",explanation:"Un `dominio` es un espacio de nombres: solo agrupa declaraciones (`variable`, `constante`, `procedimiento` y otros `dominio`). El código que hace algo, como un `si` o un `imprimir`, va dentro de un procedimiento del dominio o fuera de él."},EN_SIN_PARA_CADA:{kind:"sintaxis",explanation:"`en` es parte de `para_cada elemento en lista { … }` y no se puede usar sola ni como nombre."},EXPRESIÓN_ESPERADA:{kind:"sintaxis",explanation:"En ese lugar iba un valor (un número, un texto, una variable, una llamada…) y apareció otra cosa. Suele faltar un operando, como en `x = * 2`, o se usó una palabra reservada como si fuera un nombre."},EXPRESIÓN_INCOMPLETA:{kind:"sintaxis",explanation:"El programa terminó a mitad de una expresión, como en `x = 1 +`. Revisa que no falte el último valor."},FIN_DE_SENTENCIA:{kind:"sintaxis",explanation:"Dos sentencias en la misma línea tienen que separarse con `;`. Si querías combinar dos valores, falta un operador entre ellos: `1 2` no significa nada, pero `1 + 2` sí."},HASTA_QUE_SIN_HACER:{kind:"sintaxis",explanation:"`hasta_que (condición)` cierra un bucle `hacer { … }` y tiene que ir justo después de su `}`. Para repetir mientras algo sea verdadero, usa `mientras`."},INCREMENTO_EN_EXPRESIÓN:{kind:"sintaxis",explanation:"`i++` e `i--` son sentencias: cambian la variable y nada más. No se pueden usar dentro de una expresión (`x = i++`) ni antes del nombre (`++i`). Escribe `i++` en su propia línea y después usa `i`."},LLAVE_SOBRANTE:{kind:"sintaxis",explanation:"Hay una `}` que no cierra ningún bloque. Suele ser una llave de más, o una que falta más arriba."},NOMBRE_ESPERADO:{kind:"sintaxis",explanation:"Después de `variable`, `constante`, `procedimiento` o `para_cada` va un nombre: letras, dígitos y `_`, sin empezar con dígito."},NOMBRE_REPETIDO:{kind:"sintaxis",explanation:"Un procedimiento con nombre existe desde el inicio de su bloque, para poder llamarlo antes de la línea donde se escribe. Por eso su nombre no se puede repetir en el mismo bloque, ni con otro procedimiento ni con una variable."},PALABRA_DESCONOCIDA:{kind:"sintaxis",explanation:"Se parece a una palabra de Codexivo, pero no lo es. Las palabras compuestas llevan guion bajo: `si_no`, `pero_si`, `hasta_que`."},PALABRA_RESERVADA:{kind:"sintaxis",explanation:"Las palabras reservadas (`si`, `mientras`, `y`, `veces`, `mod`…) tienen un significado fijo y no se pueden usar como nombres. Elige otro nombre, por ejemplo agregando una palabra: `valor_inicial`."},PARÁMETRO_REPETIDO:{kind:"sintaxis",explanation:"Dos parámetros de un procedimiento (o las dos variables de un `para_cada`) tienen el mismo nombre, así que no habría forma de distinguirlos."},PROCEDIMIENTO_CON_NOMBRE:{kind:"sintaxis",explanation:"`procedimiento nombre(…) { … }` es una sentencia y no puede ir dentro de una expresión. Como valor, el procedimiento se escribe sin nombre: `variable doble = procedimiento(x) { regresa x * 2 }`."},RAMA_SIN_SI:{kind:"sintaxis",explanation:"`pero_si` y `si_no` continúan un `si` y van justo después de la `}` de su bloque. Una sentencia en medio rompe la cadena."},REPETIR_SIN_CANTIDAD:{kind:"sintaxis",explanation:"Falta cuántas veces se repite el bloque. La forma completa es `repetir 4 veces { … }`."},ROMPER_FUERA_DE_BUCLE:{kind:"sintaxis",explanation:"`romper` sale del bucle más cercano, así que solo tiene sentido dentro de uno. Para salir de un procedimiento se usa `regresa`."},SENTENCIA_EN_EXPRESIÓN:{kind:"sintaxis",explanation:"Palabras como `si`, `mientras` o `variable` empiezan una sentencia y no dan un valor, así que no pueden ir donde se espera uno, como a la derecha de `=`."},SI_NO_SI:{kind:"sintaxis",explanation:"Después de `si_no` va directamente un bloque. Para probar otra condición se usa `pero_si (condición) { … }`."},TOKEN_ESPERADO:{kind:"sintaxis",explanation:"Falta un símbolo o una palabra que la sentencia necesita, como el `(` de una condición o el `veces` de `repetir`. El mensaje dice qué se esperaba y qué se encontró."},ÍNDICE_VACÍO:{kind:"sintaxis",explanation:'Entre los corchetes va la posición o la clave: `lista[0]`, `d["nombre"]`.'},CANTIDAD_DE_ARGUMENTOS:{kind:"ejecución",explanation:"Un procedimiento se llama con exactamente tantos valores como parámetros tiene. Si faltan o sobran, es un error: Codexivo no rellena los que faltan."},CHOQUE_CON_PARED:{kind:"ejecución",explanation:"El robot intentó avanzar hacia una pared o fuera del mundo. Antes de `avanzar()`, pregunta con `frente_libre()`."},CLAVE_INEXISTENTE:{kind:"ejecución",explanation:'Se leyó una clave que el diccionario no tiene. Pregunta antes con `tiene(d, clave)`, o revisa que la clave esté bien escrita: `"Edad"` y `"edad"` son distintas, igual que `1` y `"1"`.'},CLAVE_INVÁLIDA:{kind:"ejecución",explanation:"Las claves de un diccionario son textos o números. Una lista, un lógico o `nulo` no pueden ser clave."},COLA_VACÍA:{kind:"ejecución",explanation:"Se intentó sacar o consultar el frente de una cola que no tiene nada. Revisa antes con `longitud(cola) > 0`."},COMPARACIÓN_INVÁLIDA:{kind:"ejecución",explanation:"`<`, `>`, `<=` y `>=` comparan dos números o dos textos. Comparar un número con un texto no tiene un orden claro; convierte uno de los dos con `número(…)` o `texto(…)`."},CONDICIÓN_NO_LÓGICA:{kind:"ejecución",explanation:"Las condiciones de `si`, `mientras`, `hasta_que` y `para` tienen que ser `verdadero` o `falso`. En otros lenguajes `si (x)` funciona con números; en Codexivo se dice lo que se quiere preguntar: `si (x es_distinto_de 0)`."},CONSTANTE_REASIGNADA:{kind:"ejecución",explanation:"Una `constante` no cambia de valor. Si necesitas cambiarlo, declárala con `variable` en lugar de `constante`."},DICCIONARIO_MODIFICADO_AL_RECORRER:{kind:"ejecución",explanation:"Se agregaron o quitaron claves de un diccionario mientras `para_cada` lo recorría. Recorre `claves(d)`, que es una lista nueva, o junta los cambios y aplícalos al terminar."},DIVISIÓN_ENTRE_CERO:{kind:"ejecución",explanation:"Dividir entre cero (con `/`, `div`, `mod` o `%`) no tiene resultado. Revisa el divisor antes de dividir."},LISTA_MODIFICADA_AL_RECORRER:{kind:"ejecución",explanation:"La lista cambió de tamaño mientras `para_cada` la recorría, así que no queda claro qué elemento sigue. Cambiar elementos está bien; para agregar o quitar, recorre una copia o junta los cambios en otra lista."},LISTA_VACÍA:{kind:"ejecución",explanation:"El procedimiento necesita al menos un elemento y la lista está vacía. Revisa antes con `longitud(lista) > 0`."},MIEMBRO_DE_SOLO_LECTURA:{kind:"ejecución",explanation:"Desde fuera de un dominio solo se pueden cambiar sus miembros declarados con `variable`. Sus constantes, sus procedimientos y los dominios que contiene quedan fijos, para que `calculadora.suma` siempre sea el mismo procedimiento."},MIEMBRO_INEXISTENTE:{kind:"ejecución",explanation:"El dominio no tiene un miembro con ese nombre. Puede estar mal escrito; los miembros se declaran dentro de las llaves del dominio y desde fuera no se pueden agregar."},MOCHILA_VACÍA:{kind:"ejecución",explanation:"El robot intentó dejar un zumbador sin tener ninguno. Pregunta antes con `tengo_zumbadores()`."},NOMBRE_DESCONOCIDO:{kind:"ejecución",explanation:"No hay ninguna variable ni procedimiento con ese nombre a la vista. Puede estar mal escrito, declararse más abajo, o haberse declarado dentro de un bloque que ya terminó."},NOMBRE_RESERVADO_POR_BIBLIOTECA:{kind:"ejecución",explanation:"Ese nombre es de la biblioteca de Codexivo y no se le puede asignar un valor. Si quieres una variable propia con ese nombre, declárala con `variable`; mientras exista, oculta al de la biblioteca."},NO_ES_NÚMERO:{kind:"ejecución",explanation:'Se esperaba un número y el texto no lo es, como `"doce"` o `"3,5"`. Los decimales se escriben con punto: `"3.5"`.'},NO_ES_PROCEDIMIENTO:{kind:"ejecución",explanation:"Se intentó llamar con `( )` a algo que no es un procedimiento, como un número o una lista. Revisa que no estés usando una variable con el mismo nombre que el procedimiento."},OPERANDO_NO_LÓGICO:{kind:"ejecución",explanation:'`y`, `o` y `no` unen valores `verdadero` o `falso`. Para preguntar algo sobre un número o un texto, compáralo primero: `x > 0 y nombre != ""`.'},PARA_LÍMITE_INVÁLIDO:{kind:"ejecución",explanation:"En `para i desde a hasta b con_paso p`, el inicio, el final y el paso tienen que ser números. Si vienen de `leer()`, conviértelos con `número(…)` o usa `leer_número()`."},PARA_PASO_CERO:{kind:"ejecución",explanation:"Con `con_paso 0`, la variable nunca cambiaría y el `para` no llegaría al final. Usa un paso positivo para contar hacia arriba o uno negativo para contar hacia abajo."},PARA_SENTIDO_CONTRARIO:{kind:"ejecución",explanation:"El paso va en el sentido contrario al que hay que recorrer: por ejemplo, `para i desde 10 hasta 1` sin paso cuenta hacia arriba y nunca llegaría al 1. Codexivo no lo invierte solo ni hace cero vueltas en silencio: para contar hacia atrás escribe `con_paso -1`."},PARA_CADA_INVÁLIDO:{kind:"ejecución",explanation:"`para_cada` recorre listas, textos y diccionarios. Para contar se recorre un rango (`para_cada i en rango(1, 10)`), y la forma con dos variables (`para_cada clave, valor en d`) es solo para diccionarios."},PILA_VACÍA:{kind:"ejecución",explanation:"Se intentó sacar o consultar el tope de una pila que no tiene nada. Revisa antes con `longitud(pila) > 0`."},POTENCIA_INVÁLIDA:{kind:"ejecución",explanation:"Un número negativo elevado a un exponente con decimales, como `(-8) ^ 0.5`, no tiene un resultado real. Con exponentes enteros sí funciona."},RANGO_DEMASIADO_GRANDE:{kind:"ejecución",explanation:"El `rango` tendría millones de números, lo que casi siempre es un error en el inicio, el final o el paso. Revísalos."},RANGO_INVÁLIDO:{kind:"ejecución",explanation:'En `subtexto` y `rebanar`, el inicio no puede estar después del fin. El fin no se incluye: `subtexto("hola", 1, 3)` es `"ol"`.'},RANGO_PASO_CERO:{kind:"ejecución",explanation:"Con paso 0, `rango` nunca llegaría al final. Usa un paso positivo para contar hacia arriba o uno negativo para contar hacia abajo."},RAÍZ_DE_NEGATIVO:{kind:"ejecución",explanation:"La raíz cuadrada solo existe para números de 0 en adelante."},REPETICIONES_INVÁLIDAS:{kind:"ejecución",explanation:"`repetir n veces` necesita que `n` sea un número entero, 0 o más. Con un texto, conviértelo con `número(…)`; con decimales, redondéalo."},ROBOT_APAGADO:{kind:"ejecución",explanation:"Después de `apagar()` el robot ya no hace ninguna acción. `apagar()` va al final del programa."},SEPARADOR_VACÍO:{kind:"ejecución",explanation:"`dividir` y `reemplazar` necesitan un texto que buscar que no esté vacío. Para separar letra por letra, usa `caracteres(t)`."},SIN_ENTRADA:{kind:"ejecución",explanation:"`leer_número()` no encontró más datos de entrada. Revisa cuántos datos espera el programa y cuántos se le dieron."},SIN_ZUMBADORES:{kind:"ejecución",explanation:"El robot intentó recoger un zumbador donde no hay. Pregunta antes con `hay_zumbador()`."},TEXTO_INMUTABLE:{kind:"ejecución",explanation:'Los textos no se cambian letra por letra: `t[0] = "H"` es un error. Se construye un texto nuevo, por ejemplo uniendo partes con `+` o con `reemplazar`.'},TIPOS_MEZCLADOS:{kind:"ejecución",explanation:'`+` suma dos números o une dos textos, pero no mezcla: `"Total: " + 5` es un error. Convierte el número con `texto(5)`, o imprime los dos por separado: `imprimir("Total:", 5)`.'},TIPO_DE_ARGUMENTO:{kind:"ejecución",explanation:"Un procedimiento de la biblioteca recibió un valor de un tipo que no acepta, como un texto donde esperaba un número. El mensaje dice qué esperaba y qué recibió."},TIPO_INVÁLIDO:{kind:"ejecución",explanation:"El operador no funciona con esos tipos, como `-` entre dos textos o `no` con un número. Revisa qué valor tiene cada lado."},VARIABLE_DE_PARA:{kind:"ejecución",explanation:"La variable de `para i desde … hasta …` avanza sola en cada vuelta, así que no se puede cambiar dentro del bucle. Si necesitas otro valor, guárdalo en una variable nueva; si necesitas controlar el avance a mano, usa `mientras`."},VARIABLE_NO_DECLARADA:{kind:"ejecución",explanation:"Se le asignó un valor a una variable que no existe. La primera vez se declara con `variable x = …`; después se cambia con `x = …`."},VARIABLE_REPETIDA:{kind:"ejecución",explanation:"La variable ya existe en este bloque. Para cambiar su valor se escribe `x = …`, sin `variable`. Un bloque interno sí puede declarar otra con el mismo nombre."},ÍNDICE_FUERA_DE_RANGO:{kind:"ejecución",explanation:"Las posiciones van de 0 a `longitud - 1` (o de -1 a `-longitud` desde el final). Un error clásico es usar `longitud(lista)` como índice: el último es `longitud(lista) - 1`."},ÍNDICE_INVÁLIDO:{kind:"ejecución",explanation:"La posición de una lista o texto tiene que ser un número entero. Las claves de texto son para diccionarios."},ÍNDICE_NO_SOPORTADO:{kind:"ejecución",explanation:"`[ ]` funciona con listas, textos y diccionarios, y `.campo` solo con diccionarios. Revisa qué valor tiene la variable."},DEMASIADAS_LLAMADAS:{kind:"límite",explanation:"Hay demasiadas llamadas anidadas, casi siempre por una recursión sin caso base: el procedimiento se llama a sí mismo sin una condición que lo detenga."},DEMASIADAS_OPERACIONES:{kind:"límite",explanation:"El programa hizo más operaciones de las permitidas. Casi siempre es un bucle cuya condición nunca cambia; revisa que algo dentro del bucle lo acerque al final."},DEMASIADA_SALIDA:{kind:"límite",explanation:"El programa imprimió demasiado texto, casi siempre por un `imprimir` dentro de un bucle que no termina."}};var ir={LET:"declaración",CONST:"declaración",DOMAIN:"declaración",FUNCTION:"declaración",RETURN:"declaración",IF:"control",ELSEIF:"control",ELSE:"control",WHILE:"control",DO:"control",UNTIL:"control",FOR:"control",FOR_EACH:"control",FROM:"control",TO:"control",STEP:"control",IN:"control",REPEAT:"control",TIMES:"control",BREAK:"control",CONTINUE:"control",AND:"lógicas",OR:"lógicas",NOT:"lógicas",TRUE:"literales",FALSE:"literales",NULL:"literales",MOD:"operadores",DIV:"operadores",EQ:"operadores",NEQ:"operadores",LT:"operadores",GT:"operadores",LTE:"operadores",GTE:"operadores"},sr=["declaración","control","lógicas","literales","operadores"],or=(e)=>ir[z[e]??""],cr=sr.map((e)=>({group:e,words:Object.keys(z).filter((t)=>or(t)===e)})),lr=[{word:"o",precedence:1,associativity:"izquierda",description:"Verdadero si alguno de los dos lo es. Si el izquierdo es verdadero, no evalúa el derecho."},{word:"y",precedence:2,associativity:"izquierda",description:"Verdadero si los dos lo son. Si el izquierdo es falso, no evalúa el derecho."},{symbol:"!",word:"no",precedence:3,associativity:"prefijo",description:"Cambia verdadero por falso y al revés."},{symbol:"==",word:"es_igual_a",precedence:4,associativity:"sin encadenar",description:"Igualdad por valor, también de listas y diccionarios."},{symbol:"!=",word:"es_distinto_de",precedence:4,associativity:"sin encadenar",description:"Lo contrario de `==`."},{symbol:"<",word:"es_menor_que",precedence:4,associativity:"sin encadenar",description:"Compara dos números o dos textos."},{symbol:">",word:"es_mayor_que",precedence:4,associativity:"sin encadenar",description:"Compara dos números o dos textos."},{symbol:"<=",word:"es_menor_o_igual_a",precedence:4,associativity:"sin encadenar",description:"Compara dos números o dos textos."},{symbol:">=",word:"es_mayor_o_igual_a",precedence:4,associativity:"sin encadenar",description:"Compara dos números o dos textos."},{symbol:"+",precedence:5,associativity:"izquierda",description:"Suma dos números o une dos textos; no los mezcla."},{symbol:"-",precedence:5,associativity:"izquierda",description:"Resta."},{symbol:"*",precedence:6,associativity:"izquierda",description:"Multiplica."},{symbol:"/",precedence:6,associativity:"izquierda",description:"División real: `7 / 2` es `3.5`."},{word:"div",precedence:6,associativity:"izquierda",description:"División entera hacia abajo: `-7 div 2` es `-4`."},{symbol:"%",word:"mod",precedence:6,associativity:"izquierda",description:"Residuo consistente con `div`: `-7 mod 3` es `2`."},{symbol:"-",precedence:7,associativity:"prefijo",description:"Cambia el signo."},{symbol:"^",precedence:8,associativity:"derecha",description:"Potencia: `2 ^ 3 ^ 2` es `2 ^ 9`, y `-2 ^ 2` es `-4`."},{symbol:"( )",precedence:9,associativity:"izquierda",description:"Llama a un procedimiento: `f(x)`."},{symbol:"[ ]",precedence:9,associativity:"izquierda",description:'Elemento de una lista o texto, o clave de un diccionario: `l[0]`, `d["a"]`.'},{symbol:".",precedence:9,associativity:"izquierda",description:'Campo de un diccionario: `persona.edad` es `persona["edad"]`.'}],Ve=(e,t)=>{let r=Ie(e);return{name:e,aliases:r===e?[]:[r],...t}},pr=[...Object.values(ne).filter((e,t,r)=>r.indexOf(e)===t).map((e)=>Ve(e.name,e.doc)),...Object.entries(ct).map(([e,t])=>Ve(e,t)),...Object.entries(Ee).map(([e,t])=>Ve(e,t))],Be=["entrada y salida","tipos","matemáticas","texto","listas","diccionarios","procedimientos","estructuras","mundo"],dr={version:1,keywords:cr,operators:lr,library:pr.sort((e,t)=>Be.indexOf(e.category)-Be.indexOf(t.category)||e.name.localeCompare(t.name,"es")),constants:Object.entries(st).map(([e,t])=>({name:e,value:X[e],...t})),errors:Object.entries(vt).map(([e,t])=>({code:e,...t})).sort((e,t)=>e.code.localeCompare(t.code,"es")),categories:Be,exampleWorld:xt};var qe=(e)=>{switch(e.kind){case"Logical":return e.operator==="o"?1:2;case"Unary":return e.operator==="no"?3:7;case"Binary":switch(e.operator){case"==":case"!=":case"<":case">":case"<=":case">=":return 4;case"+":case"-":return 5;case"^":return 8;default:return 6}default:return 10}},Z=(e,t,r)=>{let a=S(e),n=qe(e);return n<t||r&&n===t?`(${a})`:a},S=(e)=>{switch(e.kind){case"NumberLiteral":case"StringLiteral":case"BooleanLiteral":return f(e.value);case"NullLiteral":return"nulo";case"Identifier":return e.name;case"ListLiteral":return`[${e.elements.map(S).join(", ")}]`;case"DictLiteral":return e.entries.length===0?"{}":`{ ${e.entries.map((t)=>{let r=S(t.key);return`${t.key.kind==="StringLiteral"||t.key.kind==="NumberLiteral"?r:`(${r})`}: ${S(t.value)}`}).join(", ")} }`;case"Unary":{let t=qe(e),r=Z(e.operand,t,!1);return e.operator==="no"?`no ${r}`:`-${r}`}case"Binary":case"Logical":{let t=qe(e),r=e.kind==="Binary"&&e.operator==="^",a=t===4,n=Z(e.left,t,r||a),i=Z(e.right,t,!r||a);return`${n} ${e.operator} ${i}`}case"Call":return`${Z(e.callee,10,!1)}(${e.args.map(S).join(", ")})`;case"Index":if(e.dot&&e.index.kind==="StringLiteral")return`${Z(e.object,10,!1)}.${e.index.value}`;return`${Z(e.object,10,!1)}[${S(e.index)}]`;case"FunctionLiteral":return`procedimiento(${e.params.map((t)=>t.name).join(", ")}) { … }`}},U=(e)=>{switch(e.kind){case"VariableDeclaration":return`${e.constant?"constante":"variable"} ${e.name.name} = ${S(e.value)}`;case"FunctionDeclaration":return`procedimiento ${e.name.name}(${e.function.params.map((t)=>t.name).join(", ")})`;case"Assignment":return`${S(e.target)} ${e.operator} ${S(e.value)}`;case"Increment":return`${S(e.target)}${e.operator}`;case"ExpressionStatement":return S(e.expression);case"Return":return e.value?`regresa ${S(e.value)}`:"regresa";case"Domain":return`dominio ${e.name.name}`;case"Break":return"romper";case"Continue":return"continuar";case"Block":return"{ … }";case"If":return`si (${S(e.branches[0].condition)})`;case"While":return`mientras (${S(e.condition)})`;case"DoUntil":return`hasta_que (${S(e.condition)})`;case"For":return`para (${e.condition?S(e.condition):""})`;case"Repeat":return`repetir ${S(e.count)} veces`;case"ForRange":return`para ${e.variable.name} desde ${S(e.from)} hasta ${S(e.to)}${e.step?` con_paso ${S(e.step)}`:""}`;case"ForEach":return`para_cada ${e.variable.name}${e.value?`, ${e.value.name}`:""} en ${S(e.iterable)}`}};var ur=new Set(["leer","leer_número","leer_numero"]),mr=new Set(["imprimir"]),yt=(e)=>{if(e.kind==="Identifier")return e.name;if(e.kind==="Index"&&e.dot&&e.index.kind==="StringLiteral"){let t=yt(e.object);return t&&`${t}.${e.index.value}`}return},hr=(e)=>e?.kind==="Call"?yt(e.callee):void 0;class St{options;procedures;nodes=[];edges=[];loops=[];returns=[];groupable;constructor(e,t){this.options=e;this.procedures=t}label(e){let t=this.options.maxLabelLength;return t&&e.length>t?`${e.slice(0,Math.max(1,t-1))}…`:e}node(e,t,r){let a=`n${this.nodes.length}`;return this.nodes.push({id:a,kind:e,label:this.label(t),astIds:r.map((n)=>n.id),spans:r.map((n)=>n.span),rank:0}),this.groupable=void 0,a}connect(e,t){for(let{from:r,kind:a="normal",label:n}of e)this.edges.push({id:`e${this.edges.length}`,from:r,to:t,kind:a,...n?{label:n}:{}})}simple(e,t,r,a){let n=e==="proceso"&&this.options.group,i=this.groupable?this.nodes.find((o)=>o.id===this.groupable):void 0;if(n&&i&&r.length===1&&r[0].from===i.id&&!r[0].label)return i.label=`${i.label}
|
|
145
|
-
${this.label(U(t))}`,i.astIds.push(t.id),i.spans.push(t.span),r;let s=this.node(e,U(t),[t]);if(a)this.nodes[this.nodes.length-1].procedure=a;if(this.connect(r,s),n)this.groupable=s;return[{from:s}]}kindOf(e){let t=e.kind==="ExpressionStatement"?e.expression:e.kind==="VariableDeclaration"||e.kind==="Assignment"?e.value:void 0,r=
|
|
141
|
+
`),Le=(e)=>e.toLocaleString("es-MX").replace(/,/g," "),ut=(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"},Ce=(e)=>{switch(x(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}},ar=(e,t,r,a)=>{let[n,i]=typeof e==="number"?[e,e]:e;if(t>=n&&t<=i)return;let s=(c)=>c===1?"1 valor":`${c} valores`,o;if(n===i)o=s(n);else if(i===1/0)o=`al menos ${s(n)}`;else o=`entre ${n} y ${s(i)}`;throw a(`\`${r}\` espera ${o}, pero recibió ${t}.`)};var mt=(e,t={})=>{let{program:r,tokens:a,errors:n}=j(e);if(n.length>0)return{program:r,tokens:a,errors:n};return{program:r,tokens:a,errors:n,machine:new Y(re(r),t)}},ie=(e,t={})=>{let{trace:r,maxEvents:a=1e5,...n}=t,i=[],s=!1,o=r?(E)=>{if(i.length<a)i.push(E);else s=!0}:void 0,{errors:c,machine:p}=mt(e,{...n,onEvent:o});if(!p)return{status:"error",ok:!1,result:null,resultText:"nulo",output:"",errors:c.map((E)=>E.toJSON()),operations:0,...r?{events:i,eventsTruncated:s}:{}};return p.run(),{status:p.status,ok:p.status==="terminada",result:p.result,resultText:f(p.result),output:p.output,errors:p.error?[p.error.toJSON()]:[],operations:p.operations,seed:p.seed,...t.world?{world:t.world.state()}:{},...r?{events:i,eventsTruncated:s}:{}}};class ht{options;globals=new B;constructor(e={}){this.options=e}run(e){return ie(e,{...this.options,globals:this.globals,allowRedeclare:!0})}}var ft=(e)=>{switch(e.kind){case"Program":case"Block":return e.statements;case"ListLiteral":return e.elements;case"DictLiteral":return e.entries.flatMap((t)=>[t.key,t.value]);case"Unary":return[e.operand];case"Binary":case"Logical":return[e.left,e.right];case"Call":return[e.callee,...e.args];case"Index":return[e.object,e.index];case"FunctionLiteral":return[...e.params,e.body];case"VariableDeclaration":return[e.name,e.value];case"Assignment":return[e.target,e.value];case"Increment":return[e.target];case"FunctionDeclaration":return[e.name,e.function];case"ForEach":return[e.variable,...e.value?[e.value]:[],e.iterable,e.body];case"ExpressionStatement":return[e.expression];case"Return":return e.value?[e.value]:[];case"If":return[...e.branches.flatMap((t)=>[t.condition,t.body]),...e.alternative?[e.alternative]:[]];case"While":return[e.condition,e.body];case"DoUntil":return[e.body,e.condition];case"Domain":return[e.name,e.body];case"ForRange":return[e.variable,e.from,e.to,...e.step?[e.step]:[],e.body];case"Repeat":return[e.count,e.body];case"For":return[...e.init?[e.init]:[],...e.condition?[e.condition]:[],...e.update?[e.update]:[],e.body];default:return[]}},bt=(e,t,r)=>{t(e,r);for(let a of ft(e))bt(a,t,e)};var we=["norte","este","sur","oeste"];var Te=(e)=>typeof e==="object"&&e!==null&&!Array.isArray(e),Et=(e)=>{let t=[],r=(h,d)=>t.push({path:h,message:d});if(!Te(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,d)=>{let u=e[h];if(typeof u!=="number"||!Number.isInteger(u)||u<1||u>100)return r(h,`\`${h}\` (${d}) tiene que ser un entero entre 1 y 100, pero es ${JSON.stringify(u)}.`),0;return u},n=a("width","casillas de oeste a este"),i=a("height","casillas de sur a norte"),s=(h,d)=>{for(let[u,I]of[["x",n],["y",i]]){let L=h[u];if(typeof L!=="number"||!Number.isInteger(L))r(`${d}.${u}`,`\`${d}.${u}\` tiene que ser un número entero, pero es ${JSON.stringify(L)}.`);else if(I>0&&(L<1||L>I))r(`${d}.${u}`,`\`${d}.${u}\` tiene que estar entre 1 y ${I}, pero es ${L}.`)}},o=(h,d)=>{if(!we.includes(h))r(d,`\`${d}\` tiene que ser "norte", "este", "sur" u "oeste", pero es ${JSON.stringify(h)}.`)},c=(h,d)=>{if(h==="infinito")return;if(typeof h!=="number"||!Number.isInteger(h)||h<0)r(d,`\`${d}\` tiene que ser un entero de 0 en adelante o "infinito", pero es ${JSON.stringify(h)}.`)},p=(h,d)=>{let u=e[h];if(u===void 0)return;if(!Array.isArray(u)){r(h,`\`${h}\` tiene que ser una lista.`);return}u.forEach((I,L)=>{let b=`${h}[${L}]`;if(!Te(I))r(b,`\`${b}\` tiene que ser un objeto.`);else d(I,b)})};p("walls",(h,d)=>{s(h,d),o(h.side,`${d}.side`)});let E=new Set;if(p("beepers",(h,d)=>{s(h,d),c(h.count,`${d}.count`);let u=`${h.x},${h.y}`;if(E.has(u))r(d,`La casilla (${h.x}, ${h.y}) aparece dos veces en \`beepers\`.`);E.add(u)}),!Te(e.robot))r("robot","Falta `robot`, con su posición, orientación y zumbadores.");else s(e.robot,"robot"),o(e.robot.facing,"robot.facing"),c(e.robot.beepers,"robot.beepers");if(t.length>0)return{errors:t};return{world:e,errors:[]}},gt={norte:"oeste",oeste:"sur",sur:"este",este:"norte"},nr={norte:"este",este:"sur",sur:"oeste",oeste:"norte"},De={norte:[0,1],este:[1,0],sur:[0,-1],oeste:[-1,0]},Pe=(e)=>e==="infinito"?1/0:e,se=(e)=>e===1/0?"infinito":e,$e=(e)=>{let{world:t,errors:r}=Et(e);if(!t)throw Error(`El mundo no es válido: ${r.map((b)=>b.message).join(" ")}`);let{width:a,height:n}=t,i=new Set,s=(b,R,g)=>{if(g==="sur")return`n:${b}:${R-1}`;if(g==="oeste")return`e:${b-1}:${R}`;return`${g==="norte"?"n":"e"}:${b}:${R}`};for(let b of t.walls??[])i.add(s(b.x,b.y,b.side));let o=new Map;for(let b of t.beepers??[])if(Pe(b.count)>0)o.set(`${b.x},${b.y}`,Pe(b.count));let c={...t.robot,beepers:Pe(t.robot.beepers),on:!0},p=(b,R,g)=>{let[_,C]=De[g],O=b+_,V=R+C;if(O<1||V<1||O>a||V>n)return!1;return!i.has(s(b,R,g))},E=()=>({x:c.x,y:c.y,facing:c.facing,beepers:se(c.beepers),on:c.on}),h=()=>`El robot está en (${c.x}, ${c.y}) mirando al ${c.facing}.`,d=(b,R)=>({kind:"nativo",name:b,arity:0,doc:Ee[b],call:(g,_)=>{if(!c.on)throw _.fail("ROBOT_APAGADO",`El robot ya está apagado; no puede \`${b}\`.`,"`apagar()` va al final: después de apagarlo, el robot ya no hace nada.");let C=E(),O=R(_);return _.emit({type:"robot",action:b,before:C,after:E(),...O?{cell:O}:{}}),null}}),u=(b,R)=>({kind:"nativo",name:b,arity:0,doc:Ee[b],call:R}),I=[d("avanzar",(b)=>{if(!p(c.x,c.y,c.facing)){let[_,C]=De[c.facing],O=c.x+_<1||c.y+C<1||c.x+_>a||c.y+C>n;throw b.fail("CHOQUE_CON_PARED",`El robot chocó con ${O?"el borde del mundo":"una pared"}. ${h()}`,"Pregunta antes con `frente_libre()`.")}let[R,g]=De[c.facing];c.x+=R,c.y+=g}),d("girar_izquierda",()=>{c.facing=gt[c.facing]}),d("recoger_zumbador",(b)=>{let R=`${c.x},${c.y}`,g=o.get(R)??0;if(g===0)throw b.fail("SIN_ZUMBADORES",`No hay zumbadores que recoger. ${h()}`,"Pregunta antes con `hay_zumbador()`.");if(g!==1/0)if(g===1)o.delete(R);else o.set(R,g-1);if(c.beepers!==1/0)c.beepers+=1;return{x:c.x,y:c.y,beepers:se(o.get(R)??0)}}),d("dejar_zumbador",(b)=>{if(c.beepers===0)throw b.fail("MOCHILA_VACÍA",`El robot no tiene zumbadores que dejar. ${h()}`,"Pregunta antes con `tengo_zumbadores()`.");if(c.beepers!==1/0)c.beepers-=1;let R=`${c.x},${c.y}`,g=o.get(R)??0;if(g!==1/0)o.set(R,g+1);return{x:c.x,y:c.y,beepers:se(o.get(R))}}),d("apagar",()=>{c.on=!1}),u("frente_libre",()=>p(c.x,c.y,c.facing)),u("izquierda_libre",()=>p(c.x,c.y,gt[c.facing])),u("derecha_libre",()=>p(c.x,c.y,nr[c.facing])),u("hay_zumbador",()=>(o.get(`${c.x},${c.y}`)??0)>0),u("tengo_zumbadores",()=>c.beepers>0),...we.map((b)=>u(`mirando_al_${b}`,()=>c.facing===b))],L=()=>{let b=[...i].map((g)=>{let[_,C,O]=g.split(":");return{x:Number(C),y:Number(O),side:_==="n"?"norte":"este"}}).filter((g)=>g.side==="norte"?g.y>=1&&g.y<n&&g.x>=1:g.x>=1&&g.x<a&&g.y>=1).sort((g,_)=>g.y-_.y||g.x-_.x||g.side.localeCompare(_.side)),R=[...o].map(([g,_])=>{let[C,O]=g.split(",").map(Number);return{x:C,y:O,count:se(_)}}).sort((g,_)=>g.y-_.y||g.x-_.x);return{version:1,width:a,height:n,walls:b,beepers:R,robot:{x:c.x,y:c.y,facing:c.facing,beepers:se(c.beepers),on:c.on}}};return{natives:Object.fromEntries(I.map((b)=>[b.name,b])),state:L}},xt={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}},D=(e,t,r,a=!1)=>({level:"principiante",category:"mundo",params:[],returns:a?"lógico":"nulo",description:e,examples:[{code:t,result:r}]}),Ee={avanzar:D("Avanza una casilla hacia donde mira el robot. Chocar con una pared es un error.",`avanzar()
|
|
142
|
+
mirando_al_norte()`,"verdadero"),girar_izquierda:D("Gira 90° a la izquierda.",`girar_izquierda()
|
|
143
|
+
mirando_al_oeste()`,"verdadero"),recoger_zumbador:D("Toma un zumbador de la casilla y lo guarda en la mochila.",`recoger_zumbador()
|
|
144
|
+
hay_zumbador()`,"verdadero"),dejar_zumbador:D("Deja un zumbador de la mochila en la casilla.",`dejar_zumbador()
|
|
145
|
+
tengo_zumbadores()`,"falso"),apagar:D("Apaga el robot. Después ya no puede hacer ninguna acción.",`apagar()
|
|
146
|
+
frente_libre()`,"verdadero"),frente_libre:D("`verdadero` si no hay pared enfrente del robot.","frente_libre()","verdadero",!0),izquierda_libre:D("`verdadero` si no hay pared a la izquierda del robot.","izquierda_libre()","falso",!0),derecha_libre:D("`verdadero` si no hay pared a la derecha del robot.","derecha_libre()","falso",!0),hay_zumbador:D("`verdadero` si hay al menos un zumbador en la casilla del robot.","hay_zumbador()","verdadero",!0),tengo_zumbadores:D("`verdadero` si hay al menos un zumbador en la mochila.","tengo_zumbadores()","verdadero",!0),mirando_al_norte:D("`verdadero` si el robot mira al norte.","mirando_al_norte()","verdadero",!0),mirando_al_este:D("`verdadero` si el robot mira al este.","mirando_al_este()","falso",!0),mirando_al_sur:D("`verdadero` si el robot mira al sur.","mirando_al_sur()","falso",!0),mirando_al_oeste:D("`verdadero` si el robot mira al oeste.","mirando_al_oeste()","falso",!0)},ir=(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[s,o]of Object.entries(t.robot??{})){if(o===void 0)continue;let c=e.robot[s];if(c!==o)r.push({field:`robot.${s}`,expected:o,actual:c,message:s==="on"?o?"El robot tenía que quedar encendido.":"El robot tenía que terminar apagado: falta `apagar()`.":`En ${a[s]} se esperaba ${JSON.stringify(o)}, pero quedó ${JSON.stringify(c)}.`})}let n=new Map(e.beepers.map((s)=>[`${s.x},${s.y}`,s.count])),i=new Set;for(let s of t.beepers??[]){let o=`${s.x},${s.y}`;i.add(o);let c=n.get(o)??0;if(c!==s.count)r.push({field:`beepers(${s.x}, ${s.y})`,expected:s.count,actual:c,message:`En la casilla (${s.x}, ${s.y}) se esperaban ${s.count} zumbadores, pero hay ${c}.`})}if(t.onlyListedBeepers)for(let[s,o]of n){if(i.has(s))continue;let[c,p]=s.split(",");r.push({field:`beepers(${c}, ${p})`,expected:0,actual:o,message:`La casilla (${c}, ${p}) tenía que quedar vacía, pero tiene ${o} zumbadores.`})}return{ok:r.length===0,differences:r}};var vt={CARÁCTER_DESCONOCIDO:{kind:"léxico",explanation:"El código tiene un carácter que Codexivo no usa, como `&`, `|` o `@`. Suele pasar al copiar código de otro lenguaje o de un documento: las comillas tipográficas (“ ”) no son comillas de código. Para «y» y «o» se escriben las palabras `y` y `o`."},COMENTARIO_SIN_CERRAR:{kind:"léxico",explanation:"Un comentario `/*` sigue hasta encontrar `*/`. Si falta el cierre, todo lo que viene después queda dentro del comentario y no se ejecuta."},ESCAPE_DESCONOCIDO:{kind:"léxico",explanation:"Dentro de un texto, `\\` empieza una secuencia de escape. Las válidas son `\\n` (salto de línea), `\\t` (tabulador), `\\\\`, `\\\"` y `\\'`. Para escribir una diagonal invertida se escribe dos veces."},NOMBRE_EMPIEZA_CON_NÚMERO:{kind:"léxico",explanation:"Los nombres pueden llevar dígitos, pero no al principio: `x2` es un nombre y `2x` no. Si querías multiplicar, escribe el operador: `2 * x`."},SEPARADOR_INVÁLIDO:{kind:"léxico",explanation:"En los números, `_` solo sirve para separar grupos de dígitos y que se lean mejor: `1_000_000`. Tiene que ir entre dos dígitos; `1__0` o `1_` no son números."},TEXTO_SIN_CERRAR:{kind:"léxico",explanation:"Un texto empieza y termina con la misma comilla, en la misma línea. Si falta la comilla final, Codexivo no sabe dónde termina. Para un salto de línea dentro del texto usa `\\n`."},ANIDAMIENTO_EXCESIVO:{kind:"sintaxis",explanation:"La expresión tiene demasiados niveles de paréntesis o de operaciones anidadas. Guarda partes en variables intermedias: además de evitar el error, el programa se lee mejor."},ASIGNACIÓN_INVÁLIDA:{kind:"sintaxis",explanation:"A la izquierda de `=` (o de `+=`, `++`…) tiene que ir algo que pueda guardar un valor: una variable (`x = 1`), un elemento (`lista[0] = 1`) o un campo (`persona.edad = 16`). `5 = x` o `f(x) = 1` no tienen dónde guardarlo."},BLOQUE_SIN_CERRAR:{kind:"sintaxis",explanation:"Cada `{` necesita su `}`. El error señala la llave que abrió el bloque; revisa que cada `si`, `mientras` o procedimiento cierre su bloque antes de que empiece lo que sigue."},CAMPO_ESPERADO:{kind:"sintaxis",explanation:'Después de `.` va el nombre de un campo de un diccionario: `persona.edad`. Solo funciona con claves que son nombres válidos y que no son palabras reservadas; para las demás se escribe `d["clave"]`.'},CLAVE_REPETIDA:{kind:"sintaxis",explanation:"Un diccionario escrito entre llaves tiene la misma clave dos veces. Solo se quedaría el último valor, así que casi siempre es un error de copiar y pegar."},CLAVE_SIN_COMILLAS:{kind:"sintaxis",explanation:'Las claves de un diccionario son textos o números, y los textos van entre comillas: `{ "nombre": "Ana" }`. Si querías usar el valor de una variable como clave, escríbela entre paréntesis: `{ (clave): 1 }`.'},COMPARACIÓN_ENCADENADA:{kind:"sintaxis",explanation:"`1 < x < 5` parece natural, pero esconde dos comparaciones. En Codexivo se escriben las dos y se unen con `y`: `1 < x y x < 5`."},CONTINUAR_FUERA_DE_BUCLE:{kind:"sintaxis",explanation:"`continuar` salta a la siguiente vuelta del bucle más cercano, así que solo tiene sentido dentro de uno. Dentro de un procedimiento no afecta al bucle desde donde se llamó."},DICCIONARIO_COMO_SENTENCIA:{kind:"sintaxis",explanation:'Al inicio de una sentencia, `{` abre un bloque. Un diccionario tiene que estar en una expresión: guárdalo en una variable (`variable d = { "a": 1 }`) o pásalo a un procedimiento.'},DOMINIO_SOLO_DECLARACIONES:{kind:"sintaxis",explanation:"Un `dominio` es un espacio de nombres: solo agrupa declaraciones (`variable`, `constante`, `procedimiento` y otros `dominio`). El código que hace algo, como un `si` o un `imprimir`, va dentro de un procedimiento del dominio o fuera de él."},EN_SIN_PARA_CADA:{kind:"sintaxis",explanation:"`en` es parte de `para_cada elemento en lista { … }` y no se puede usar sola ni como nombre."},EXPRESIÓN_ESPERADA:{kind:"sintaxis",explanation:"En ese lugar iba un valor (un número, un texto, una variable, una llamada…) y apareció otra cosa. Suele faltar un operando, como en `x = * 2`, o se usó una palabra reservada como si fuera un nombre."},EXPRESIÓN_INCOMPLETA:{kind:"sintaxis",explanation:"El programa terminó a mitad de una expresión, como en `x = 1 +`. Revisa que no falte el último valor."},FIN_DE_SENTENCIA:{kind:"sintaxis",explanation:"Dos sentencias en la misma línea tienen que separarse con `;`. Si querías combinar dos valores, falta un operador entre ellos: `1 2` no significa nada, pero `1 + 2` sí."},HASTA_QUE_SIN_HACER:{kind:"sintaxis",explanation:"`hasta_que (condición)` cierra un bucle `hacer { … }` y tiene que ir justo después de su `}`. Para repetir mientras algo sea verdadero, usa `mientras`."},INCREMENTO_EN_EXPRESIÓN:{kind:"sintaxis",explanation:"`i++` e `i--` son sentencias: cambian la variable y nada más. No se pueden usar dentro de una expresión (`x = i++`) ni antes del nombre (`++i`). Escribe `i++` en su propia línea y después usa `i`."},LLAVE_SOBRANTE:{kind:"sintaxis",explanation:"Hay una `}` que no cierra ningún bloque. Suele ser una llave de más, o una que falta más arriba."},NOMBRE_ESPERADO:{kind:"sintaxis",explanation:"Después de `variable`, `constante`, `procedimiento` o `para_cada` va un nombre: letras, dígitos y `_`, sin empezar con dígito."},NOMBRE_REPETIDO:{kind:"sintaxis",explanation:"Un procedimiento con nombre existe desde el inicio de su bloque, para poder llamarlo antes de la línea donde se escribe. Por eso su nombre no se puede repetir en el mismo bloque, ni con otro procedimiento ni con una variable."},PALABRA_DESCONOCIDA:{kind:"sintaxis",explanation:"Se parece a una palabra de Codexivo, pero no lo es. Las palabras compuestas llevan guion bajo: `si_no`, `pero_si`, `hasta_que`."},PALABRA_RESERVADA:{kind:"sintaxis",explanation:"Las palabras reservadas (`si`, `mientras`, `y`, `veces`, `mod`…) tienen un significado fijo y no se pueden usar como nombres. Elige otro nombre, por ejemplo agregando una palabra: `valor_inicial`."},PARÁMETRO_REPETIDO:{kind:"sintaxis",explanation:"Dos parámetros de un procedimiento (o las dos variables de un `para_cada`) tienen el mismo nombre, así que no habría forma de distinguirlos."},PROCEDIMIENTO_CON_NOMBRE:{kind:"sintaxis",explanation:"`procedimiento nombre(…) { … }` es una sentencia y no puede ir dentro de una expresión. Como valor, el procedimiento se escribe sin nombre: `variable doble = procedimiento(x) { regresa x * 2 }`."},RAMA_SIN_SI:{kind:"sintaxis",explanation:"`pero_si` y `si_no` continúan un `si` y van justo después de la `}` de su bloque. Una sentencia en medio rompe la cadena."},REPETIR_SIN_CANTIDAD:{kind:"sintaxis",explanation:"Falta cuántas veces se repite el bloque. La forma completa es `repetir 4 veces { … }`."},ROMPER_FUERA_DE_BUCLE:{kind:"sintaxis",explanation:"`romper` sale del bucle más cercano, así que solo tiene sentido dentro de uno. Para salir de un procedimiento se usa `regresa`."},SENTENCIA_EN_EXPRESIÓN:{kind:"sintaxis",explanation:"Palabras como `si`, `mientras` o `variable` empiezan una sentencia y no dan un valor, así que no pueden ir donde se espera uno, como a la derecha de `=`."},SI_NO_SI:{kind:"sintaxis",explanation:"Después de `si_no` va directamente un bloque. Para probar otra condición se usa `pero_si (condición) { … }`."},TOKEN_ESPERADO:{kind:"sintaxis",explanation:"Falta un símbolo o una palabra que la sentencia necesita, como el `(` de una condición o el `veces` de `repetir`. El mensaje dice qué se esperaba y qué se encontró."},ÍNDICE_VACÍO:{kind:"sintaxis",explanation:'Entre los corchetes va la posición o la clave: `lista[0]`, `d["nombre"]`.'},AFIRMACIÓN_FALLIDA:{kind:"ejecución",explanation:"Una llamada a `afirmar(condición, mensaje)` encontró que su condición era falsa. Afirmar es escribir lo que esperas que sea cierto en ese punto del programa; si no lo es, el programa se detiene ahí, antes de que el error llegue más lejos. Revisa los valores de las variables justo antes de esa línea."},CANTIDAD_DE_ARGUMENTOS:{kind:"ejecución",explanation:"Un procedimiento se llama con exactamente tantos valores como parámetros tiene. Si faltan o sobran, es un error: Codexivo no rellena los que faltan."},CHOQUE_CON_PARED:{kind:"ejecución",explanation:"El robot intentó avanzar hacia una pared o fuera del mundo. Antes de `avanzar()`, pregunta con `frente_libre()`."},CLAVE_INEXISTENTE:{kind:"ejecución",explanation:'Se leyó una clave que el diccionario no tiene. Pregunta antes con `tiene(d, clave)`, o revisa que la clave esté bien escrita: `"Edad"` y `"edad"` son distintas, igual que `1` y `"1"`.'},CLAVE_INVÁLIDA:{kind:"ejecución",explanation:"Las claves de un diccionario son textos o números. Una lista, un lógico o `nulo` no pueden ser clave."},COLA_VACÍA:{kind:"ejecución",explanation:"Se intentó sacar o consultar el frente de una cola que no tiene nada. Revisa antes con `longitud(cola) > 0`."},COMPARACIÓN_INVÁLIDA:{kind:"ejecución",explanation:"`<`, `>`, `<=` y `>=` comparan dos números o dos textos. Comparar un número con un texto no tiene un orden claro; convierte uno de los dos con `número(…)` o `texto(…)`."},CONDICIÓN_NO_LÓGICA:{kind:"ejecución",explanation:"Las condiciones de `si`, `mientras`, `hasta_que` y `para` tienen que ser `verdadero` o `falso`. En otros lenguajes `si (x)` funciona con números; en Codexivo se dice lo que se quiere preguntar: `si (x es_distinto_de 0)`."},CONSTANTE_REASIGNADA:{kind:"ejecución",explanation:"Una `constante` no cambia de valor. Si necesitas cambiarlo, declárala con `variable` en lugar de `constante`."},DICCIONARIO_MODIFICADO_AL_RECORRER:{kind:"ejecución",explanation:"Se agregaron o quitaron claves de un diccionario mientras `para_cada` lo recorría. Recorre `claves(d)`, que es una lista nueva, o junta los cambios y aplícalos al terminar."},DIVISIÓN_ENTRE_CERO:{kind:"ejecución",explanation:"Dividir entre cero (con `/`, `div`, `mod` o `%`) no tiene resultado. Revisa el divisor antes de dividir."},LISTA_MODIFICADA_AL_RECORRER:{kind:"ejecución",explanation:"La lista cambió de tamaño mientras `para_cada` la recorría, así que no queda claro qué elemento sigue. Cambiar elementos está bien; para agregar o quitar, recorre una copia o junta los cambios en otra lista."},LISTA_VACÍA:{kind:"ejecución",explanation:"El procedimiento necesita al menos un elemento y la lista está vacía. Revisa antes con `longitud(lista) > 0`."},MIEMBRO_DE_SOLO_LECTURA:{kind:"ejecución",explanation:"Desde fuera de un dominio solo se pueden cambiar sus miembros declarados con `variable`. Sus constantes, sus procedimientos y los dominios que contiene quedan fijos, para que `calculadora.suma` siempre sea el mismo procedimiento."},MIEMBRO_INEXISTENTE:{kind:"ejecución",explanation:"El dominio no tiene un miembro con ese nombre. Puede estar mal escrito; los miembros se declaran dentro de las llaves del dominio y desde fuera no se pueden agregar."},MOCHILA_VACÍA:{kind:"ejecución",explanation:"El robot intentó dejar un zumbador sin tener ninguno. Pregunta antes con `tengo_zumbadores()`."},NOMBRE_DESCONOCIDO:{kind:"ejecución",explanation:"No hay ninguna variable ni procedimiento con ese nombre a la vista. Puede estar mal escrito, declararse más abajo, o haberse declarado dentro de un bloque que ya terminó."},NOMBRE_RESERVADO_POR_BIBLIOTECA:{kind:"ejecución",explanation:"Ese nombre es de la biblioteca de Codexivo y no se le puede asignar un valor. Si quieres una variable propia con ese nombre, declárala con `variable`; mientras exista, oculta al de la biblioteca."},NO_ES_NÚMERO:{kind:"ejecución",explanation:'Se esperaba un número y el texto no lo es, como `"doce"` o `"3,5"`. Los decimales se escriben con punto: `"3.5"`.'},NO_ES_PROCEDIMIENTO:{kind:"ejecución",explanation:"Se intentó llamar con `( )` a algo que no es un procedimiento, como un número o una lista. Revisa que no estés usando una variable con el mismo nombre que el procedimiento."},OPERANDO_NO_LÓGICO:{kind:"ejecución",explanation:'`y`, `o` y `no` unen valores `verdadero` o `falso`. Para preguntar algo sobre un número o un texto, compáralo primero: `x > 0 y nombre != ""`.'},PARA_LÍMITE_INVÁLIDO:{kind:"ejecución",explanation:"En `para i desde a hasta b con_paso p`, el inicio, el final y el paso tienen que ser números. Si vienen de `leer()`, conviértelos con `número(…)` o usa `leer_número()`."},PARA_PASO_CERO:{kind:"ejecución",explanation:"Con `con_paso 0`, la variable nunca cambiaría y el `para` no llegaría al final. Usa un paso positivo para contar hacia arriba o uno negativo para contar hacia abajo."},PARA_SENTIDO_CONTRARIO:{kind:"ejecución",explanation:"El paso va en el sentido contrario al que hay que recorrer: por ejemplo, `para i desde 10 hasta 1` sin paso cuenta hacia arriba y nunca llegaría al 1. Codexivo no lo invierte solo ni hace cero vueltas en silencio: para contar hacia atrás escribe `con_paso -1`."},PARA_CADA_INVÁLIDO:{kind:"ejecución",explanation:"`para_cada` recorre listas, textos y diccionarios. Para contar se recorre un rango (`para_cada i en rango(1, 10)`), y la forma con dos variables (`para_cada clave, valor en d`) es solo para diccionarios."},PILA_VACÍA:{kind:"ejecución",explanation:"Se intentó sacar o consultar el tope de una pila que no tiene nada. Revisa antes con `longitud(pila) > 0`."},POTENCIA_INVÁLIDA:{kind:"ejecución",explanation:"Un número negativo elevado a un exponente con decimales, como `(-8) ^ 0.5`, no tiene un resultado real. Con exponentes enteros sí funciona."},RANGO_DEMASIADO_GRANDE:{kind:"ejecución",explanation:"El `rango` tendría millones de números, lo que casi siempre es un error en el inicio, el final o el paso. Revísalos."},RANGO_INVÁLIDO:{kind:"ejecución",explanation:'En `subtexto` y `rebanar`, el inicio no puede estar después del fin. El fin no se incluye: `subtexto("hola", 1, 3)` es `"ol"`.'},RANGO_PASO_CERO:{kind:"ejecución",explanation:"Con paso 0, `rango` nunca llegaría al final. Usa un paso positivo para contar hacia arriba o uno negativo para contar hacia abajo."},RAÍZ_DE_NEGATIVO:{kind:"ejecución",explanation:"La raíz cuadrada solo existe para números de 0 en adelante."},REPETICIONES_INVÁLIDAS:{kind:"ejecución",explanation:"`repetir n veces` necesita que `n` sea un número entero, 0 o más. Con un texto, conviértelo con `número(…)`; con decimales, redondéalo."},ROBOT_APAGADO:{kind:"ejecución",explanation:"Después de `apagar()` el robot ya no hace ninguna acción. `apagar()` va al final del programa."},SEPARADOR_VACÍO:{kind:"ejecución",explanation:"`dividir` y `reemplazar` necesitan un texto que buscar que no esté vacío. Para separar letra por letra, usa `caracteres(t)`."},SIN_ENTRADA:{kind:"ejecución",explanation:"`leer_número()` no encontró más datos de entrada. Revisa cuántos datos espera el programa y cuántos se le dieron."},SIN_ZUMBADORES:{kind:"ejecución",explanation:"El robot intentó recoger un zumbador donde no hay. Pregunta antes con `hay_zumbador()`."},TEXTO_INMUTABLE:{kind:"ejecución",explanation:'Los textos no se cambian letra por letra: `t[0] = "H"` es un error. Se construye un texto nuevo, por ejemplo uniendo partes con `+` o con `reemplazar`.'},TIPOS_MEZCLADOS:{kind:"ejecución",explanation:'`+` suma dos números o une dos textos, pero no mezcla: `"Total: " + 5` es un error. Convierte el número con `texto(5)`, o imprime los dos por separado: `imprimir("Total:", 5)`.'},TIPO_DE_ARGUMENTO:{kind:"ejecución",explanation:"Un procedimiento de la biblioteca recibió un valor de un tipo que no acepta, como un texto donde esperaba un número. El mensaje dice qué esperaba y qué recibió."},TIPO_INVÁLIDO:{kind:"ejecución",explanation:"El operador no funciona con esos tipos, como `-` entre dos textos o `no` con un número. Revisa qué valor tiene cada lado."},VARIABLE_DE_PARA:{kind:"ejecución",explanation:"La variable de `para i desde … hasta …` avanza sola en cada vuelta, así que no se puede cambiar dentro del bucle. Si necesitas otro valor, guárdalo en una variable nueva; si necesitas controlar el avance a mano, usa `mientras`."},VARIABLE_NO_DECLARADA:{kind:"ejecución",explanation:"Se le asignó un valor a una variable que no existe. La primera vez se declara con `variable x = …`; después se cambia con `x = …`."},VARIABLE_REPETIDA:{kind:"ejecución",explanation:"La variable ya existe en este bloque. Para cambiar su valor se escribe `x = …`, sin `variable`. Un bloque interno sí puede declarar otra con el mismo nombre."},ÍNDICE_FUERA_DE_RANGO:{kind:"ejecución",explanation:"Las posiciones van de 0 a `longitud - 1` (o de -1 a `-longitud` desde el final). Un error clásico es usar `longitud(lista)` como índice: el último es `longitud(lista) - 1`."},ÍNDICE_INVÁLIDO:{kind:"ejecución",explanation:"La posición de una lista o texto tiene que ser un número entero. Las claves de texto son para diccionarios."},ÍNDICE_NO_SOPORTADO:{kind:"ejecución",explanation:"`[ ]` funciona con listas, textos y diccionarios, y `.campo` solo con diccionarios. Revisa qué valor tiene la variable."},DEMASIADAS_LLAMADAS:{kind:"límite",explanation:"Hay demasiadas llamadas anidadas, casi siempre por una recursión sin caso base: el procedimiento se llama a sí mismo sin una condición que lo detenga."},DEMASIADAS_OPERACIONES:{kind:"límite",explanation:"El programa hizo más operaciones de las permitidas. Casi siempre es un bucle cuya condición nunca cambia; revisa que algo dentro del bucle lo acerque al final."},DEMASIADA_SALIDA:{kind:"límite",explanation:"El programa imprimió demasiado texto, casi siempre por un `imprimir` dentro de un bucle que no termina."}};var sr={LET:"declaración",CONST:"declaración",DOMAIN:"declaración",FUNCTION:"declaración",RETURN:"declaración",IF:"control",ELSEIF:"control",ELSE:"control",WHILE:"control",DO:"control",UNTIL:"control",FOR:"control",FOR_EACH:"control",FROM:"control",TO:"control",STEP:"control",IN:"control",REPEAT:"control",TIMES:"control",BREAK:"control",CONTINUE:"control",AND:"lógicas",OR:"lógicas",NOT:"lógicas",TRUE:"literales",FALSE:"literales",NULL:"literales",MOD:"operadores",DIV:"operadores",EQ:"operadores",NEQ:"operadores",LT:"operadores",GT:"operadores",LTE:"operadores",GTE:"operadores"},or=["declaración","control","lógicas","literales","operadores"],cr=(e)=>sr[z[e]??""],lr=or.map((e)=>({group:e,words:Object.keys(z).filter((t)=>cr(t)===e)})),pr=[{word:"o",precedence:1,associativity:"izquierda",description:"Verdadero si alguno de los dos lo es. Si el izquierdo es verdadero, no evalúa el derecho."},{word:"y",precedence:2,associativity:"izquierda",description:"Verdadero si los dos lo son. Si el izquierdo es falso, no evalúa el derecho."},{symbol:"!",word:"no",precedence:3,associativity:"prefijo",description:"Cambia verdadero por falso y al revés."},{symbol:"==",word:"es_igual_a",precedence:4,associativity:"sin encadenar",description:"Igualdad por valor, también de listas y diccionarios."},{symbol:"!=",word:"es_distinto_de",precedence:4,associativity:"sin encadenar",description:"Lo contrario de `==`."},{symbol:"<",word:"es_menor_que",precedence:4,associativity:"sin encadenar",description:"Compara dos números o dos textos."},{symbol:">",word:"es_mayor_que",precedence:4,associativity:"sin encadenar",description:"Compara dos números o dos textos."},{symbol:"<=",word:"es_menor_o_igual_a",precedence:4,associativity:"sin encadenar",description:"Compara dos números o dos textos."},{symbol:">=",word:"es_mayor_o_igual_a",precedence:4,associativity:"sin encadenar",description:"Compara dos números o dos textos."},{symbol:"+",precedence:5,associativity:"izquierda",description:"Suma dos números o une dos textos; no los mezcla."},{symbol:"-",precedence:5,associativity:"izquierda",description:"Resta."},{symbol:"*",precedence:6,associativity:"izquierda",description:"Multiplica."},{symbol:"/",precedence:6,associativity:"izquierda",description:"División real: `7 / 2` es `3.5`."},{word:"div",precedence:6,associativity:"izquierda",description:"División entera hacia abajo: `-7 div 2` es `-4`."},{symbol:"%",word:"mod",precedence:6,associativity:"izquierda",description:"Residuo consistente con `div`: `-7 mod 3` es `2`."},{symbol:"-",precedence:7,associativity:"prefijo",description:"Cambia el signo."},{symbol:"^",precedence:8,associativity:"derecha",description:"Potencia: `2 ^ 3 ^ 2` es `2 ^ 9`, y `-2 ^ 2` es `-4`."},{symbol:"( )",precedence:9,associativity:"izquierda",description:"Llama a un procedimiento: `f(x)`."},{symbol:"[ ]",precedence:9,associativity:"izquierda",description:'Elemento de una lista o texto, o clave de un diccionario: `l[0]`, `d["a"]`.'},{symbol:".",precedence:9,associativity:"izquierda",description:'Campo de un diccionario: `persona.edad` es `persona["edad"]`.'}],Me=(e,t)=>{let r=Oe(e);return{name:e,aliases:r===e?[]:[r],...t}},dr=[...Object.values(ne).filter((e,t,r)=>r.indexOf(e)===t).map((e)=>Me(e.name,e.doc)),...Object.entries(ct).map(([e,t])=>Me(e,t)),...Object.entries(Ee).map(([e,t])=>Me(e,t))],Ve=["entrada y salida","tipos","verificación","matemáticas","texto","listas","diccionarios","procedimientos","estructuras","mundo"],ur={version:1,keywords:lr,operators:pr,library:dr.sort((e,t)=>Ve.indexOf(e.category)-Ve.indexOf(t.category)||e.name.localeCompare(t.name,"es")),constants:Object.entries(st).map(([e,t])=>({name:e,value:X[e],...t})),errors:Object.entries(vt).map(([e,t])=>({code:e,...t})).sort((e,t)=>e.code.localeCompare(t.code,"es")),categories:Ve,exampleWorld:xt};var qe=(e)=>{switch(e.kind){case"Logical":return e.operator==="o"?1:2;case"Unary":return e.operator==="no"?3:7;case"Binary":switch(e.operator){case"==":case"!=":case"<":case">":case"<=":case">=":return 4;case"+":case"-":return 5;case"^":return 8;default:return 6}default:return 10}},Z=(e,t,r)=>{let a=S(e),n=qe(e);return n<t||r&&n===t?`(${a})`:a},S=(e)=>{switch(e.kind){case"NumberLiteral":case"StringLiteral":case"BooleanLiteral":return f(e.value);case"NullLiteral":return"nulo";case"Identifier":return e.name;case"ListLiteral":return`[${e.elements.map(S).join(", ")}]`;case"DictLiteral":return e.entries.length===0?"{}":`{ ${e.entries.map((t)=>{let r=S(t.key);return`${t.key.kind==="StringLiteral"||t.key.kind==="NumberLiteral"?r:`(${r})`}: ${S(t.value)}`}).join(", ")} }`;case"Unary":{let t=qe(e),r=Z(e.operand,t,!1);return e.operator==="no"?`no ${r}`:`-${r}`}case"Binary":case"Logical":{let t=qe(e),r=e.kind==="Binary"&&e.operator==="^",a=t===4,n=Z(e.left,t,r||a),i=Z(e.right,t,!r||a);return`${n} ${e.operator} ${i}`}case"Call":return`${Z(e.callee,10,!1)}(${e.args.map(S).join(", ")})`;case"Index":if(e.dot&&e.index.kind==="StringLiteral")return`${Z(e.object,10,!1)}.${e.index.value}`;return`${Z(e.object,10,!1)}[${S(e.index)}]`;case"FunctionLiteral":return`procedimiento(${e.params.map((t)=>t.name).join(", ")}) { … }`}},U=(e)=>{switch(e.kind){case"VariableDeclaration":return`${e.constant?"constante":"variable"} ${e.name.name} = ${S(e.value)}`;case"FunctionDeclaration":return`procedimiento ${e.name.name}(${e.function.params.map((t)=>t.name).join(", ")})`;case"Assignment":return`${S(e.target)} ${e.operator} ${S(e.value)}`;case"Increment":return`${S(e.target)}${e.operator}`;case"ExpressionStatement":return S(e.expression);case"Return":return e.value?`regresa ${S(e.value)}`:"regresa";case"Domain":return`dominio ${e.name.name}`;case"Break":return"romper";case"Continue":return"continuar";case"Block":return"{ … }";case"If":return`si (${S(e.branches[0].condition)})`;case"While":return`mientras (${S(e.condition)})`;case"DoUntil":return`hasta_que (${S(e.condition)})`;case"For":return`para (${e.condition?S(e.condition):""})`;case"Repeat":return`repetir ${S(e.count)} veces`;case"ForRange":return`para ${e.variable.name} desde ${S(e.from)} hasta ${S(e.to)}${e.step?` con_paso ${S(e.step)}`:""}`;case"ForEach":return`para_cada ${e.variable.name}${e.value?`, ${e.value.name}`:""} en ${S(e.iterable)}`}};var mr=new Set(["leer","leer_número","leer_numero"]),hr=new Set(["imprimir"]),yt=(e)=>{if(e.kind==="Identifier")return e.name;if(e.kind==="Index"&&e.dot&&e.index.kind==="StringLiteral"){let t=yt(e.object);return t&&`${t}.${e.index.value}`}return},fr=(e)=>e?.kind==="Call"?yt(e.callee):void 0;class St{options;procedures;nodes=[];edges=[];loops=[];returns=[];groupable;constructor(e,t){this.options=e;this.procedures=t}label(e){let t=this.options.maxLabelLength;return t&&e.length>t?`${e.slice(0,Math.max(1,t-1))}…`:e}node(e,t,r){let a=`n${this.nodes.length}`;return this.nodes.push({id:a,kind:e,label:this.label(t),astIds:r.map((n)=>n.id),spans:r.map((n)=>n.span),rank:0}),this.groupable=void 0,a}connect(e,t){for(let{from:r,kind:a="normal",label:n}of e)this.edges.push({id:`e${this.edges.length}`,from:r,to:t,kind:a,...n?{label:n}:{}})}simple(e,t,r,a){let n=e==="proceso"&&this.options.group,i=this.groupable?this.nodes.find((o)=>o.id===this.groupable):void 0;if(n&&i&&r.length===1&&r[0].from===i.id&&!r[0].label)return i.label=`${i.label}
|
|
147
|
+
${this.label(U(t))}`,i.astIds.push(t.id),i.spans.push(t.span),r;let s=this.node(e,U(t),[t]);if(a)this.nodes[this.nodes.length-1].procedure=a;if(this.connect(r,s),n)this.groupable=s;return[{from:s}]}kindOf(e){let t=e.kind==="ExpressionStatement"?e.expression:e.kind==="VariableDeclaration"||e.kind==="Assignment"?e.value:void 0,r=fr(t);if(r&&mr.has(r))return{kind:"entrada"};if(r&&hr.has(r)&&e.kind==="ExpressionStatement")return{kind:"salida"};if(r&&this.procedures.has(r))return this.options.expandProcedures?{kind:"llamada",procedure:r}:{kind:"llamada"};if(e.kind==="ExpressionStatement"&&r)return{kind:"llamada"};return{kind:"proceso"}}statements(e,t){let r=t;for(let a of e)r=this.statement(a,r);return r}loop(e){let t={breaks:[],continues:[]};this.loops.push(t);let r=e();return this.loops.pop(),{out:r,loop:t}}back(e,t="vuelta"){return e.map((r)=>r.kind&&r.kind!=="normal"?r:{...r,kind:t})}statement(e,t){switch(e.kind){case"FunctionDeclaration":return t;case"Domain":{let r=[],a=(i)=>{r.push(i);for(let s of i.body.statements)if(s.kind==="VariableDeclaration")r.push(s);else if(s.kind==="Domain")a(s)};a(e);let n=this.node("proceso",U(e),r);return this.connect(t,n),[{from:n}]}case"VariableDeclaration":case"Assignment":case"Increment":case"ExpressionStatement":{let{kind:r,procedure:a}=this.kindOf(e);return this.simple(r,e,t,a)}case"Return":{let r=this.node("retorno",U(e),[e]);return this.connect(t,r),this.returns.push({from:r,kind:"regresa"}),[]}case"Break":case"Continue":{let r=this.node("proceso",U(e),[e]);this.connect(t,r);let a=this.loops[this.loops.length-1];if(e.kind==="Break")a?.breaks.push({from:r,kind:"romper"});else a?.continues.push({from:r,kind:"continuar"});return[]}case"Block":return this.statements(e.statements,t);case"If":{let r=t,a=[];if(e.branches.forEach((n,i)=>{let s=i===0?[e,n.condition]:[n.condition],o=this.node("decisión",S(n.condition),s);this.connect(r,o),a.push(...this.statements(n.body.statements,[{from:o,label:"sí"}])),r=[{from:o,label:"no"}]}),e.alternative)a.push(...this.statements(e.alternative.statements,r));else a.push(...r);return a}case"While":{let r=this.node("decisión",S(e.condition),[e,e.condition]);this.connect(t,r);let{out:a,loop:n}=this.loop(()=>this.statements(e.body.statements,[{from:r,label:"sí"}]));return this.connect(this.back([...a,...n.continues]),r),[{from:r,label:"no"},...n.breaks]}case"DoUntil":{let r=this.nodes.length,{out:a,loop:n}=this.loop(()=>this.statements(e.body.statements,t)),i=this.node("decisión",`hasta_que ${S(e.condition)}`,[e,e.condition]);this.connect([...a,...n.continues],i);let s=this.nodes[r]?.id??i;if(this.connect([{from:i,label:"no",kind:"vuelta"}],s),s===i)this.edges.pop();return[{from:i,label:"sí"},...n.breaks]}case"For":{let r=t,a=[];if(e.init){this.groupable=void 0;let p=this.nodes.length;r=this.statement(e.init,r),this.nodes[p]?.astIds.unshift(e.id),this.nodes[p]?.spans.unshift(e.span)}else a.push(e);let n=e.condition,i=this.node("decisión",n?S(n):"verdadero",[...a,...n?[n]:[]]);this.connect(r,i);let{out:s,loop:o}=this.loop(()=>this.statements(e.body.statements,[{from:i,label:"sí"}])),c=[...s,...o.continues];if(e.update)c=this.statement(e.update,c);return this.connect(this.back(c),i),[...n?[{from:i,label:"no"}]:[],...o.breaks]}case"Repeat":case"ForRange":case"ForEach":{let r=e.kind==="Repeat"?e.count:e.kind==="ForRange"?e.to:e.iterable,a=this.node("decisión",U(e),[e,r]);this.connect(t,a);let{out:n,loop:i}=this.loop(()=>this.statements(e.body.statements,[{from:a,label:"repetir"}]));return this.connect(this.back([...n,...i.continues]),a),[{from:a,label:"salir"},...i.breaks]}}}finish(e,t){let r=this.node("fin","fin",[]);this.connect([...t,...this.returns],r);let a=this.edges.filter((i)=>i.kind!=="vuelta"&&i.kind!=="continuar"),n=new Map([[e,0]]);for(let i=!0,s=0;i&&s<=this.nodes.length;s++){i=!1;for(let o of a){let c=(n.get(o.from)??-1)+1;if(n.has(o.from)&&c>(n.get(o.to)??-1))n.set(o.to,c),i=!0}}for(let i of this.nodes)i.rank=n.get(i.id)??0}}var br=(e,t=[])=>{let r=new Set(t),a=(n,i)=>{for(let s of n){if(s.kind==="FunctionDeclaration")r.add(`${i}${s.name.name}`);if(s.kind==="Domain")a(s.body.statements,`${i}${s.name.name}.`)}};return a(e,""),r},gr=(e,t={})=>Rt(e,t,[]),Rt=(e,t,r)=>{let a=e.kind==="Program"?e.statements:e.kind==="FunctionDeclaration"?e.function.body.statements:e.body.statements,n=br(a,r),i=new St(t,n),s=e.kind==="Program"?"inicio":e.kind==="FunctionDeclaration"?U(e):`procedimiento(${e.params.map((E)=>E.name).join(", ")})`,o=i.node("inicio",s,e.kind==="Program"?[]:[e]),c=i.statements(a,[{from:o}]);i.finish(o,c);let p={nodes:i.nodes,edges:i.edges};if(t.expandProcedures){p.procedures=[];let E=(h,d)=>{for(let u of h){if(u.kind==="Domain")E(u.body.statements,`${d}${u.name.name}.`);if(u.kind!=="FunctionDeclaration")continue;p.procedures.push({name:`${d}${u.name.name}`,astId:u.id,graph:Rt(u,t,n)})}};E(a,"")}return p};var Er=1e6,Be=(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(Be).join(", ")}]`;return`{ ${Object.entries(e).map(([t,r])=>`${JSON.stringify(t)}: ${Be(r)}`).join(", ")} }`},Fe=(e)=>{if(e===null||typeof e!=="object")return e;if(Array.isArray(e))return{kind:"lista",id:0,items:e.map(Fe)};return{kind:"diccionario",id:0,entries:new Map(Object.entries(e).map(([t,r])=>[t,Fe(r)]))}},kt=(e)=>e.replace(/\r\n/g,`
|
|
146
148
|
`).split(`
|
|
147
149
|
`).map((t)=>t.replace(/[ \t]+$/,"")).join(`
|
|
148
|
-
`).replace(/\n+$/,""),
|
|
150
|
+
`).replace(/\n+$/,""),Ue=(e,t)=>{let r=t.procedure??"resolver",a=t.cases.map((n,i)=>{let s=n.name??`Caso ${i+1}`,o=n.maxOperations??t.maxOperations??Er,c=n.input??[];if(t.style==="salida"){let d=ie(e,{input:c,maxOperations:o}),u=n.expectedOutput??"",I=t.exactOutput?d.output===u:kt(d.output)===kt(u);return{name:s,passed:d.ok&&I,output:d.output,expectedOutput:u,operations:d.operations,...d.errors[0]?{error:d.errors[0]}:{}}}let p=`
|
|
149
151
|
;
|
|
150
|
-
regresa ${r}(${(n.args??[]).map(
|
|
151
|
-
`,E=ie(e+p,{input:c,maxOperations:o}),h=
|
|
152
|
+
regresa ${r}(${(n.args??[]).map(Be).join(", ")});
|
|
153
|
+
`,E=ie(e+p,{input:c,maxOperations:o}),h=Fe(n.expected??null);return{name:s,passed:E.ok&&M(E.result,h),output:E.output,result:E.resultText,expected:f(h),operations:E.operations,...E.errors[0]?{error:E.errors[0]}:{}}});return{passed:a.every((n)=>n.passed),cases:a}};var xr=(e)=>{let t=1,r=new Map,a=new Set,n=new Set;e.addEventListener("message",({data:o})=>{if(o.type==="progreso"){for(let p of a)p(o);return}if(o.type==="detenido")for(let p of n)p(o);let c=r.get(o.id);if(!c)return;if(r.delete(o.id),o.type==="error")c.reject(Error(o.message));else if(o.type==="hecho")c.resolve(void 0);else if(o.type==="reto")c.resolve(o.result);else c.resolve(o)});let i=(o,c={})=>new Promise((p,E)=>{let h=t++;r.set(h,{resolve:p,reject:E}),e.postMessage({type:o,id:h,...c})}),s=(o,c)=>(o.add(c),()=>o.delete(c));return{load:(o,c)=>i("cargar",{source:o,...c?{options:c}:{}}),run:()=>i("ejecutar"),resume:()=>i("continuar"),step:()=>i("paso"),pause:()=>i("pausar"),goToStep:(o)=>i("ir_a_paso",{step:o}),provideInput:(o)=>i("entrada",{line:o}),closeInput:()=>i("cerrar_entrada"),setBreakpoints:(o)=>i("puntos_de_interrupción",{lines:o}),evaluarReto:(o,c)=>i("evaluar_reto",{source:o,challenge:c}),onProgress:(o)=>{let c=s(a,o);return()=>void c()},onStopped:(o)=>{let c=s(n,o);return()=>void c()},terminate:()=>{e.terminate?.();for(let o of r.values())o.reject(Error("El sandbox se cerró."));r.clear()}}};var vr={nada:new Set,pasos:new Set(["paso","robot"]),decisiones:new Set(["paso","robot","decide"]),todo:"todo"},yr=()=>new Promise((e)=>setTimeout(e,0));class Nt{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:Ue(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:i,trace:s}=j(t,{trace:r.parseTrace??!1});this.program=i.length===0?a:void 0,this.machine=this.program?this.createMachine():void 0,this.post({type:"cargado",id:e,tokens:n,program:a,errors:i.map((o)=>o.toJSON()),seed:this.seed,...s?{parseTrace:s}:{}})}createMachine(){let{options:e}=this,t=vr[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:$e(e.world)}:{}};return new Y(re(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 yr(),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,i=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:i,operations:t.operations,steps:t.steps,output:this.newOutput(t),...a?{replaceOutput:!0}:{},events:this.takeEvents(),snapshot:t.snapshot(),...n==="terminada"?{result:N(t.result)}:{},...t.error?{error:t.error.toJSON()}:{}})}}export{q as CodexivoError,we as DIRECTIONS,z as KEYWORDS,Y as Machine,Nt as SandboxHost,B as Scope,ht as Session,ee as TokenType,W as WAIT_FOR_INPUT,ur as catalog,ir as checkGoal,ft as childrenOf,re as compile,xr as createSandbox,$e as createWorld,M as equals,Ue as evaluarReto,gr as flowGraph,It as formatError,je as isKeyword,ne as natives,j as parse,mt as prepare,S as printExpression,U as printStatement,f as repr,ie as run,N as serialize,ae as show,Se as tokenize,Mt as traceParse,x as typeOf,Et as validateWorld,bt as walk};
|
|
152
154
|
|
|
153
|
-
//# debugId=
|
|
155
|
+
//# debugId=0A9E489F41211E6264756E2164756E21
|
|
154
156
|
//# sourceMappingURL=index.js.map
|