codexivo 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/errors.ts", "../src/syntax/token.ts", "../src/syntax/lexer.ts", "../src/syntax/parser.ts", "../src/runtime/compiler.ts", "../src/runtime/values.ts", "../src/runtime/builtins.ts", "../src/runtime/suggest.ts", "../src/runtime/machine.ts", "../src/engine.ts", "../src/syntax/ast.ts"],
4
+ "sourcesContent": [
5
+ "import type { Span } from './syntax/token';\n\nexport type ErrorKind = 'léxico' | 'sintaxis' | 'ejecución' | 'límite';\n\nexport interface ErrorInfo {\n kind: ErrorKind;\n code: string;\n message: string;\n hint?: string;\n span?: Span;\n /** Nombres de los procedimientos activos cuando ocurrió el error, del más externo al más interno. */\n callStack?: string[];\n}\n\n/**\n * Error de Codexivo. Siempre describe qué pasó (`message`) y, cuando es posible,\n * qué revisar (`hint`) y dónde (`span`).\n */\nexport class CodexivoError extends Error implements ErrorInfo {\n kind: ErrorKind;\n code: string;\n hint?: string;\n span?: Span;\n callStack?: string[];\n\n constructor(info: ErrorInfo) {\n super(info.message);\n this.name = 'CodexivoError';\n this.kind = info.kind;\n this.code = info.code;\n this.hint = info.hint;\n this.span = info.span;\n this.callStack = info.callStack;\n }\n\n toJSON(): ErrorInfo {\n return {\n kind: this.kind,\n code: this.code,\n message: this.message,\n hint: this.hint,\n span: this.span,\n callStack: this.callStack,\n };\n }\n}\n\n/** Texto legible de un error, como lo muestran el REPL y la consola del sandbox. */\nexport const formatError = (error: ErrorInfo): string => {\n const where = error.span ? ` en la línea ${error.span.start.line}, columna ${error.span.start.column}` : '';\n const lines = [`Error de ${error.kind}${where}: ${error.message}`];\n if (error.hint) {\n lines.push(`Pista: ${error.hint}`);\n }\n return lines.join('\\n');\n};\n",
6
+ "export interface Position {\n /** Línea, desde 1. */\n line: number;\n /** Columna, desde 1. */\n column: number;\n /** Desplazamiento en caracteres desde el inicio del código, desde 0. */\n offset: number;\n}\n\n/** Rango del código fuente. `end` apunta justo después del último carácter. */\nexport interface Span {\n start: Position;\n end: Position;\n}\n\nexport enum TokenType {\n // literales y nombres\n NUMBER = 'NUMBER',\n STRING = 'STRING',\n IDENT = 'IDENT',\n\n // palabras reservadas\n LET = 'LET',\n FUNCTION = 'FUNCTION',\n RETURN = 'RETURN',\n IF = 'IF',\n ELSEIF = 'ELSEIF',\n ELSE = 'ELSE',\n WHILE = 'WHILE',\n DO = 'DO',\n UNTIL = 'UNTIL',\n FOR = 'FOR',\n TRUE = 'TRUE',\n FALSE = 'FALSE',\n NULL = 'NULL',\n AND = 'AND',\n OR = 'OR',\n NOT = 'NOT',\n\n // operadores\n ASSIGN = 'ASSIGN',\n PLUS_ASSIGN = 'PLUS_ASSIGN',\n MINUS_ASSIGN = 'MINUS_ASSIGN',\n STAR_ASSIGN = 'STAR_ASSIGN',\n SLASH_ASSIGN = 'SLASH_ASSIGN',\n PLUS = 'PLUS',\n MINUS = 'MINUS',\n STAR = 'STAR',\n SLASH = 'SLASH',\n BANG = 'BANG',\n EQ = 'EQ',\n NEQ = 'NEQ',\n LT = 'LT',\n GT = 'GT',\n LTE = 'LTE',\n GTE = 'GTE',\n\n // delimitadores\n COMMA = 'COMMA',\n SEMICOLON = 'SEMICOLON',\n LPAREN = 'LPAREN',\n RPAREN = 'RPAREN',\n LBRACE = 'LBRACE',\n RBRACE = 'RBRACE',\n LBRACKET = 'LBRACKET',\n RBRACKET = 'RBRACKET',\n\n EOF = 'EOF',\n ILLEGAL = 'ILLEGAL',\n}\n\nexport interface Token {\n type: TokenType;\n /** Texto exacto del código. */\n lexeme: string;\n /** Valor ya interpretado: el número de un NUMBER o el contenido (con escapes resueltos) de un STRING. */\n value?: number | string;\n span: Span;\n}\n\nexport const KEYWORDS: Readonly<Record<string, TokenType>> = {\n variable: TokenType.LET,\n procedimiento: TokenType.FUNCTION,\n regresa: TokenType.RETURN,\n si: TokenType.IF,\n pero_si: TokenType.ELSEIF,\n si_no: TokenType.ELSE,\n mientras: TokenType.WHILE,\n hacer: TokenType.DO,\n hasta_que: TokenType.UNTIL,\n para: TokenType.FOR,\n verdadero: TokenType.TRUE,\n falso: TokenType.FALSE,\n nulo: TokenType.NULL,\n y: TokenType.AND,\n o: TokenType.OR,\n no: TokenType.NOT,\n};\n\nexport const isKeyword = (word: string): boolean => Object.hasOwn(KEYWORDS, word);\n\nconst SYMBOLS: Partial<Record<TokenType, string>> = {\n [TokenType.ASSIGN]: '=',\n [TokenType.PLUS_ASSIGN]: '+=',\n [TokenType.MINUS_ASSIGN]: '-=',\n [TokenType.STAR_ASSIGN]: '*=',\n [TokenType.SLASH_ASSIGN]: '/=',\n [TokenType.PLUS]: '+',\n [TokenType.MINUS]: '-',\n [TokenType.STAR]: '*',\n [TokenType.SLASH]: '/',\n [TokenType.BANG]: '!',\n [TokenType.EQ]: '==',\n [TokenType.NEQ]: '!=',\n [TokenType.LT]: '<',\n [TokenType.GT]: '>',\n [TokenType.LTE]: '<=',\n [TokenType.GTE]: '>=',\n [TokenType.COMMA]: ',',\n [TokenType.SEMICOLON]: ';',\n [TokenType.LPAREN]: '(',\n [TokenType.RPAREN]: ')',\n [TokenType.LBRACE]: '{',\n [TokenType.RBRACE]: '}',\n [TokenType.LBRACKET]: '[',\n [TokenType.RBRACKET]: ']',\n};\n\nconst KEYWORD_BY_TYPE = Object.fromEntries(Object.entries(KEYWORDS).map(([word, type]) => [type, word])) as Partial<\n Record<TokenType, string>\n>;\n\n/** Cómo nombrar un tipo de token en un mensaje: \"`)`\", \"la palabra `si`\", \"un número\"… */\nexport const describeTokenType = (type: TokenType): string => {\n const symbol = SYMBOLS[type];\n if (symbol) return `\\`${symbol}\\``;\n const keyword = KEYWORD_BY_TYPE[type];\n if (keyword) return `la palabra \\`${keyword}\\``;\n switch (type) {\n case TokenType.NUMBER:\n return 'un número';\n case TokenType.STRING:\n return 'un texto';\n case TokenType.IDENT:\n return 'un nombre';\n case TokenType.EOF:\n return 'el final del programa';\n default:\n return 'un carácter no válido';\n }\n};\n\n/** Cómo nombrar un token concreto en un mensaje. */\nexport const describeToken = (token: Token): string => {\n switch (token.type) {\n case TokenType.NUMBER:\n return `el número \\`${token.lexeme}\\``;\n case TokenType.STRING:\n return `el texto ${token.lexeme}`;\n case TokenType.IDENT:\n return `el nombre \\`${token.lexeme}\\``;\n case TokenType.ILLEGAL:\n return `el carácter \\`${token.lexeme}\\``;\n default:\n return describeTokenType(token.type);\n }\n};\n",
7
+ "import { CodexivoError } from '../errors';\nimport { KEYWORDS, TokenType } from './token';\nimport type { Position, Span, Token } from './token';\n\nexport interface LexResult {\n tokens: Token[];\n errors: CodexivoError[];\n}\n\nconst LETTER = /[A-Za-z_À-ÖØ-öø-ÿ]/;\nconst DIGIT = /[0-9]/;\nconst WHITESPACE = /\\s/;\n\nconst TWO_CHAR_OPERATORS: Readonly<Record<string, TokenType>> = {\n '==': TokenType.EQ,\n '!=': TokenType.NEQ,\n '<=': TokenType.LTE,\n '>=': TokenType.GTE,\n '+=': TokenType.PLUS_ASSIGN,\n '-=': TokenType.MINUS_ASSIGN,\n '*=': TokenType.STAR_ASSIGN,\n '/=': TokenType.SLASH_ASSIGN,\n};\n\nconst ONE_CHAR_OPERATORS: Readonly<Record<string, TokenType>> = {\n '=': TokenType.ASSIGN,\n '+': TokenType.PLUS,\n '-': TokenType.MINUS,\n '*': TokenType.STAR,\n '/': TokenType.SLASH,\n '!': TokenType.BANG,\n '<': TokenType.LT,\n '>': TokenType.GT,\n ',': TokenType.COMMA,\n ';': TokenType.SEMICOLON,\n '(': TokenType.LPAREN,\n ')': TokenType.RPAREN,\n '{': TokenType.LBRACE,\n '}': TokenType.RBRACE,\n '[': TokenType.LBRACKET,\n ']': TokenType.RBRACKET,\n};\n\nconst ESCAPES: Readonly<Record<string, string>> = {\n n: '\\n',\n t: '\\t',\n '\\\\': '\\\\',\n '\"': '\"',\n \"'\": \"'\",\n};\n\nconst ILLEGAL_HINTS: Readonly<Record<string, string>> = {\n '&': 'Para el «y» lógico escribe la palabra `y`.',\n '|': 'Para el «o» lógico escribe la palabra `o`.',\n '“': 'Parece una comilla tipográfica; usa comillas rectas `\"`.',\n '”': 'Parece una comilla tipográfica; usa comillas rectas `\"`.',\n '‘': \"Parece una comilla tipográfica; usa comillas rectas `'`.\",\n '’': \"Parece una comilla tipográfica; usa comillas rectas `'`.\",\n};\n\n/** Convierte código fuente en tokens. Nunca lanza: los problemas se reportan en `errors`. */\nexport const tokenize = (source: string): LexResult => {\n const tokens: Token[] = [];\n const errors: CodexivoError[] = [];\n\n let offset = 0;\n let line = 1;\n let column = 1;\n\n const position = (): Position => ({ line, column, offset });\n const peek = (ahead = 0): string => source[offset + ahead] ?? '';\n const advance = (): string => {\n const character = source[offset] ?? '';\n offset += 1;\n if (character === '\\n') {\n line += 1;\n column = 1;\n } else {\n column += 1;\n }\n return character;\n };\n\n const push = (type: TokenType, start: Position, value?: number | string): Token => {\n const token: Token = { type, lexeme: source.slice(start.offset, offset), span: { start, end: position() } };\n if (value !== undefined) token.value = value;\n tokens.push(token);\n return token;\n };\n\n const lexicalError = (code: string, message: string, span: Span, hint?: string) => {\n errors.push(new CodexivoError({ kind: 'léxico', code, message, hint, span }));\n };\n\n while (offset < source.length) {\n const character = peek();\n\n if (WHITESPACE.test(character)) {\n advance();\n continue;\n }\n\n const start = position();\n\n // comentarios\n if (character === '/' && peek(1) === '/') {\n while (offset < source.length && peek() !== '\\n') advance();\n continue;\n }\n if (character === '/' && peek(1) === '*') {\n advance();\n advance();\n while (offset < source.length && !(peek() === '*' && peek(1) === '/')) advance();\n if (offset >= source.length) {\n lexicalError(\n 'COMENTARIO_SIN_CERRAR',\n 'El comentario que empieza aquí no se cerró.',\n {\n start,\n end: { line: start.line, column: start.column + 2, offset: start.offset + 2 },\n },\n 'Cierra el comentario con `*/`.',\n );\n } else {\n advance();\n advance();\n }\n continue;\n }\n\n // números\n if (DIGIT.test(character) || (character === '.' && DIGIT.test(peek(1)))) {\n while (DIGIT.test(peek())) advance();\n if (peek() === '.' && DIGIT.test(peek(1))) {\n advance();\n while (DIGIT.test(peek())) advance();\n }\n if (LETTER.test(peek())) {\n while (LETTER.test(peek()) || DIGIT.test(peek())) advance();\n const token = push(TokenType.ILLEGAL, start);\n lexicalError(\n 'NOMBRE_EMPIEZA_CON_NÚMERO',\n `\\`${token.lexeme}\\` no es un número ni un nombre válido.`,\n token.span,\n 'Los nombres no pueden empezar con un dígito. Si es una multiplicación, escribe el operador: `2 * x`.',\n );\n continue;\n }\n push(TokenType.NUMBER, start, Number(source.slice(start.offset, offset)));\n continue;\n }\n\n // nombres y palabras reservadas\n if (LETTER.test(character)) {\n while (LETTER.test(peek()) || DIGIT.test(peek())) advance();\n const word = source.slice(start.offset, offset);\n push(KEYWORDS[word] ?? TokenType.IDENT, start);\n continue;\n }\n\n // textos\n if (character === '\"' || character === \"'\") {\n const quote = advance();\n let value = '';\n let closed = false;\n while (offset < source.length && peek() !== '\\n') {\n const current = advance();\n if (current === quote) {\n closed = true;\n break;\n }\n if (current === '\\\\') {\n const escapeStart = { line, column: column - 1, offset: offset - 1 };\n const escaped = advance();\n const replacement = ESCAPES[escaped];\n if (replacement === undefined) {\n lexicalError(\n 'ESCAPE_DESCONOCIDO',\n `\\`\\\\${escaped}\\` no es una secuencia de escape válida.`,\n { start: escapeStart, end: position() },\n 'Las secuencias válidas son `\\\\n`, `\\\\t`, `\\\\\\\\`, `\\\\\"` y `\\\\\\'`.',\n );\n value += escaped;\n } else {\n value += replacement;\n }\n continue;\n }\n value += current;\n }\n const token = push(TokenType.STRING, start, value);\n if (!closed) {\n lexicalError(\n 'TEXTO_SIN_CERRAR',\n 'El texto que empieza aquí no se cerró.',\n token.span,\n `Agrega la comilla \\`${quote}\\` al final. Un texto no puede ocupar varias líneas; usa \\`\\\\n\\` para un salto de línea.`,\n );\n }\n continue;\n }\n\n // operadores y delimitadores\n const pair = character + peek(1);\n const twoChar = TWO_CHAR_OPERATORS[pair];\n if (twoChar) {\n advance();\n advance();\n push(twoChar, start);\n continue;\n }\n const oneChar = ONE_CHAR_OPERATORS[character];\n if (oneChar) {\n advance();\n push(oneChar, start);\n continue;\n }\n\n advance();\n const token = push(TokenType.ILLEGAL, start);\n lexicalError(\n 'CARÁCTER_DESCONOCIDO',\n `Codexivo no reconoce el carácter \\`${character}\\`.`,\n token.span,\n ILLEGAL_HINTS[character],\n );\n }\n\n const end = position();\n tokens.push({ type: TokenType.EOF, lexeme: '', span: { start: end, end } });\n return { tokens, errors };\n};\n",
8
+ "import { CodexivoError } from '../errors';\nimport type * as ast from './ast';\nimport { tokenize } from './lexer';\nimport { describeToken, describeTokenType, TokenType } from './token';\nimport type { Position, Span, Token } from './token';\n\nexport interface ParseResult {\n program: ast.Program;\n tokens: Token[];\n /** Errores léxicos y sintácticos, en el orden en que aparecen en el código. */\n errors: CodexivoError[];\n}\n\n/** Analiza el código y construye el AST. Nunca lanza: los problemas se reportan en `errors`. */\nexport const parse = (source: string): ParseResult => {\n const { tokens, errors: lexicalErrors } = tokenize(source);\n const parser = new Parser(tokens);\n const program = parser.parseProgram();\n const errors = [...lexicalErrors, ...parser.errors].sort(\n (a, b) => (a.span?.start.offset ?? 0) - (b.span?.start.offset ?? 0),\n );\n return { program, tokens, errors };\n};\n\nenum Precedence {\n LOWEST,\n OR,\n AND,\n NOT,\n COMPARE,\n SUM,\n PRODUCT,\n UNARY,\n CALL,\n}\n\nconst INFIX_PRECEDENCE: Partial<Record<TokenType, Precedence>> = {\n [TokenType.OR]: Precedence.OR,\n [TokenType.AND]: Precedence.AND,\n [TokenType.EQ]: Precedence.COMPARE,\n [TokenType.NEQ]: Precedence.COMPARE,\n [TokenType.LT]: Precedence.COMPARE,\n [TokenType.GT]: Precedence.COMPARE,\n [TokenType.LTE]: Precedence.COMPARE,\n [TokenType.GTE]: Precedence.COMPARE,\n [TokenType.PLUS]: Precedence.SUM,\n [TokenType.MINUS]: Precedence.SUM,\n [TokenType.STAR]: Precedence.PRODUCT,\n [TokenType.SLASH]: Precedence.PRODUCT,\n [TokenType.LPAREN]: Precedence.CALL,\n [TokenType.LBRACKET]: Precedence.CALL,\n};\n\nconst BINARY_OPERATORS: Partial<Record<TokenType, ast.BinaryOperator>> = {\n [TokenType.PLUS]: '+',\n [TokenType.MINUS]: '-',\n [TokenType.STAR]: '*',\n [TokenType.SLASH]: '/',\n [TokenType.EQ]: '==',\n [TokenType.NEQ]: '!=',\n [TokenType.LT]: '<',\n [TokenType.GT]: '>',\n [TokenType.LTE]: '<=',\n [TokenType.GTE]: '>=',\n};\n\nconst ASSIGNMENT_OPERATORS: Partial<Record<TokenType, ast.AssignmentOperator>> = {\n [TokenType.ASSIGN]: '=',\n [TokenType.PLUS_ASSIGN]: '+=',\n [TokenType.MINUS_ASSIGN]: '-=',\n [TokenType.STAR_ASSIGN]: '*=',\n [TokenType.SLASH_ASSIGN]: '/=',\n};\n\nconst STATEMENT_KEYWORDS = new Set([\n TokenType.LET,\n TokenType.RETURN,\n TokenType.IF,\n TokenType.WHILE,\n TokenType.DO,\n TokenType.FOR,\n]);\n\nconst KEYWORD_LIKE_TYPOS: Readonly<Record<string, string>> = {\n sino: 'En Codexivo se escribe `si_no`, con guion bajo.',\n pero: 'En Codexivo se escribe `pero_si`, con guion bajo.',\n hasta: 'En Codexivo se escribe `hasta_que`, con guion bajo.',\n};\n\nconst MAX_ERRORS = 50;\nconst MAX_DEPTH = 400;\n\n/** Señal interna para abandonar la sentencia actual. El error, si lo hay, ya se registró. */\nclass ParseFailure {}\n\nclass Parser {\n readonly errors: CodexivoError[] = [];\n private position = 0;\n private nextId = 1;\n private depth = 0;\n\n constructor(private readonly tokens: Token[]) {}\n\n parseProgram(): ast.Program {\n const start = this.current.span.start;\n const statements = this.parseStatements(TokenType.EOF);\n return { kind: 'Program', id: this.id(), statements, span: { start, end: this.current.span.end } };\n }\n\n // ── utilidades ─────────────────────────────────────────────────────────────\n\n private get current(): Token {\n return this.tokens[this.position] ?? this.tokens[this.tokens.length - 1]!;\n }\n\n private get previous(): Token {\n return this.tokens[this.position - 1] ?? this.current;\n }\n\n private peek(ahead = 1): Token {\n return this.tokens[this.position + ahead] ?? this.tokens[this.tokens.length - 1]!;\n }\n\n private id(): number {\n return this.nextId++;\n }\n\n private advance(): Token {\n const token = this.current;\n if (token.type !== TokenType.EOF) this.position += 1;\n return token;\n }\n\n private check(type: TokenType): boolean {\n return this.current.type === type;\n }\n\n private match(type: TokenType): boolean {\n if (!this.check(type)) return false;\n this.advance();\n return true;\n }\n\n private spanFrom(start: Position): Span {\n return { start, end: this.previous.span.end };\n }\n\n private error(code: string, message: string, span: Span, hint?: string): never {\n if (this.errors.length < MAX_ERRORS) {\n this.errors.push(new CodexivoError({ kind: 'sintaxis', code, message, hint, span }));\n }\n throw new ParseFailure();\n }\n\n /** Un token ILLEGAL ya tiene su error léxico; no lo reportamos dos veces. */\n private failSilentlyOnIllegal(): void {\n if (this.check(TokenType.ILLEGAL)) throw new ParseFailure();\n }\n\n private expect(type: TokenType, context: string, hint?: string): Token {\n if (this.check(type)) return this.advance();\n this.failSilentlyOnIllegal();\n const found = this.current;\n let finalHint = hint;\n if (type === TokenType.RPAREN && found.type === TokenType.ASSIGN) {\n finalHint = 'Para comparar se usa `==`; un solo `=` sirve para asignar.';\n }\n this.error(\n 'TOKEN_ESPERADO',\n `Se esperaba ${describeTokenType(type)} ${context}, pero se encontró ${describeToken(found)}.`,\n found.span,\n finalHint,\n );\n }\n\n private enter(): void {\n this.depth += 1;\n if (this.depth > MAX_DEPTH) {\n this.error(\n 'ANIDAMIENTO_EXCESIVO',\n 'El programa tiene demasiados niveles anidados.',\n this.current.span,\n 'Divide la expresión en variables intermedias.',\n );\n }\n }\n\n private leave(): void {\n this.depth -= 1;\n }\n\n /** Salta tokens hasta un punto seguro para seguir analizando después de un error. */\n private synchronize(failedAt: Token): void {\n if (this.position === this.tokens.indexOf(failedAt) && !this.check(TokenType.EOF)) this.advance();\n while (!this.check(TokenType.EOF)) {\n if (this.previous.type === TokenType.SEMICOLON) return;\n if (this.check(TokenType.RBRACE)) return;\n if (STATEMENT_KEYWORDS.has(this.current.type)) return;\n if (this.current.span.start.line > failedAt.span.start.line) return;\n this.advance();\n }\n }\n\n // ── sentencias ─────────────────────────────────────────────────────────────\n\n private parseStatements(terminator: TokenType.EOF | TokenType.RBRACE): ast.Statement[] {\n const statements: ast.Statement[] = [];\n let recovering = false;\n while (!this.check(terminator) && !this.check(TokenType.EOF)) {\n const failedAt = this.current;\n try {\n if (terminator === TokenType.EOF && this.check(TokenType.RBRACE)) {\n // Una `}` sobrante después de un error suele ser parte de lo que ya se reportó.\n if (recovering) {\n this.advance();\n continue;\n }\n this.error('LLAVE_SOBRANTE', 'Sobra esta `}`: no hay ningún bloque abierto.', this.current.span);\n }\n statements.push(this.parseStatement());\n recovering = false;\n } catch (error) {\n if (!(error instanceof ParseFailure)) throw error;\n this.depth = 0;\n recovering = true;\n this.synchronize(failedAt);\n }\n }\n return statements;\n }\n\n private parseStatement(): ast.Statement {\n const token = this.current;\n switch (token.type) {\n case TokenType.SEMICOLON:\n this.advance();\n return this.parseStatementAfterStraySemicolon();\n case TokenType.LET:\n return this.endSimpleStatement(this.parseVariableDeclaration());\n case TokenType.RETURN:\n return this.endSimpleStatement(this.parseReturn());\n case TokenType.IF:\n return this.endCompoundStatement(this.parseIf());\n case TokenType.WHILE:\n return this.endCompoundStatement(this.parseWhile());\n case TokenType.DO:\n return this.endSimpleStatement(this.parseDoUntil());\n case TokenType.FOR:\n return this.endCompoundStatement(this.parseFor());\n case TokenType.LBRACE:\n return this.endCompoundStatement(this.parseBlock('para abrir el bloque'));\n case TokenType.ELSE:\n case TokenType.ELSEIF:\n this.error(\n 'RAMA_SIN_SI',\n `${describeToken(token)} tiene que ir justo después del \\`}\\` de un \\`si\\`.`,\n token.span,\n );\n case TokenType.UNTIL:\n this.error('HASTA_QUE_SIN_HACER', '`hasta_que` tiene que ir justo después del `}` de un `hacer`.', token.span);\n case TokenType.IDENT: {\n const typoHint = KEYWORD_LIKE_TYPOS[token.lexeme];\n const next = this.peek();\n if (\n typoHint &&\n (next.type === TokenType.LBRACE || next.type === TokenType.IF || next.type === TokenType.LPAREN)\n ) {\n this.error('PALABRA_DESCONOCIDA', `\\`${token.lexeme}\\` no es una palabra de Codexivo.`, token.span, typoHint);\n }\n if (ASSIGNMENT_OPERATORS[next.type]) {\n return this.endSimpleStatement(this.parseAssignment());\n }\n return this.endSimpleStatement(this.parseExpressionStatement());\n }\n default:\n return this.endSimpleStatement(this.parseExpressionStatement());\n }\n }\n\n private parseStatementAfterStraySemicolon(): ast.Statement {\n // `;;` o un `;` suelto no hacen nada; seguimos con lo que venga.\n if (this.check(TokenType.EOF) || this.check(TokenType.RBRACE)) {\n const span = this.previous.span;\n return { kind: 'Block', id: this.id(), statements: [], span };\n }\n return this.parseStatement();\n }\n\n /** Después de una sentencia simple va `;`, el final de la línea, `}` o el final del programa. */\n private endSimpleStatement<T extends ast.Statement>(statement: T): T {\n if (this.match(TokenType.SEMICOLON)) return statement;\n if (this.check(TokenType.RBRACE) || this.check(TokenType.EOF)) return statement;\n if (this.current.span.start.line > this.previous.span.end.line) return statement;\n this.failSilentlyOnIllegal();\n this.error(\n 'FIN_DE_SENTENCIA',\n `Se esperaba \\`;\\` o un salto de línea antes de ${describeToken(this.current)}.`,\n this.current.span,\n 'Si querías combinar dos valores, falta un operador entre ellos.',\n );\n }\n\n private endCompoundStatement<T extends ast.Statement>(statement: T): T {\n this.match(TokenType.SEMICOLON);\n return statement;\n }\n\n private parseVariableDeclaration(): ast.VariableDeclaration {\n const start = this.advance().span.start; // variable\n const name = this.parseBindingName('después de `variable`');\n this.expect(\n TokenType.ASSIGN,\n `después de \\`variable ${name.name}\\``,\n `Toda variable empieza con un valor: \\`variable ${name.name} = 0\\`.`,\n );\n const value = this.parseExpression();\n if (value.kind === 'FunctionLiteral' && !value.name) value.name = name.name;\n return { kind: 'VariableDeclaration', id: this.id(), name, value, span: this.spanFrom(start) };\n }\n\n private parseBindingName(context: string): ast.Identifier {\n const token = this.current;\n if (token.type !== TokenType.IDENT) {\n this.failSilentlyOnIllegal();\n if (token.lexeme && /^[a-z_]+$/.test(token.lexeme) && token.type !== TokenType.EOF) {\n this.error(\n 'PALABRA_RESERVADA',\n `\\`${token.lexeme}\\` es una palabra reservada y no se puede usar como nombre.`,\n token.span,\n 'Elige otro nombre, por ejemplo agregando una palabra: `valor_inicial`.',\n );\n }\n this.error(\n 'NOMBRE_ESPERADO',\n `Se esperaba un nombre ${context}, pero se encontró ${describeToken(token)}.`,\n token.span,\n );\n }\n this.advance();\n return { kind: 'Identifier', id: this.id(), name: token.lexeme, span: token.span };\n }\n\n private parseAssignment(): ast.Assignment {\n const targetToken = this.advance();\n const target: ast.Identifier = {\n kind: 'Identifier',\n id: this.id(),\n name: targetToken.lexeme,\n span: targetToken.span,\n };\n const operator = ASSIGNMENT_OPERATORS[this.advance().type]!;\n const value = this.parseExpression();\n if (value.kind === 'FunctionLiteral' && !value.name) value.name = target.name;\n return { kind: 'Assignment', id: this.id(), target, operator, value, span: this.spanFrom(targetToken.span.start) };\n }\n\n private parseExpressionStatement(): ast.ExpressionStatement {\n const start = this.current.span.start;\n const expression = this.parseExpression();\n if (ASSIGNMENT_OPERATORS[this.current.type]) {\n this.error(\n 'ASIGNACIÓN_INVÁLIDA',\n 'Solo se le puede asignar un valor a una variable.',\n this.current.span,\n expression.kind === 'Index'\n ? 'Cambiar un elemento de una lista con `lista[i] = valor` llegará en la siguiente versión.'\n : undefined,\n );\n }\n return { kind: 'ExpressionStatement', id: this.id(), expression, span: this.spanFrom(start) };\n }\n\n private parseReturn(): ast.Return {\n const keyword = this.advance();\n const endsHere =\n this.check(TokenType.SEMICOLON) ||\n this.check(TokenType.RBRACE) ||\n this.check(TokenType.EOF) ||\n this.current.span.start.line > keyword.span.end.line;\n const value = endsHere ? undefined : this.parseExpression();\n return { kind: 'Return', id: this.id(), value, span: this.spanFrom(keyword.span.start) };\n }\n\n private parseBlock(context: string): ast.Block {\n const open = this.expect(TokenType.LBRACE, context, 'Los bloques de Codexivo siempre van entre llaves `{ }`.');\n const statements = this.parseStatements(TokenType.RBRACE);\n if (!this.check(TokenType.RBRACE)) {\n this.error(\n 'BLOQUE_SIN_CERRAR',\n `Falta \\`}\\` para cerrar el bloque que empieza en la línea ${open.span.start.line}.`,\n open.span,\n );\n }\n this.advance();\n return { kind: 'Block', id: this.id(), statements, span: this.spanFrom(open.span.start) };\n }\n\n private parseCondition(keyword: string): ast.Expression {\n this.expect(\n TokenType.LPAREN,\n `después de \\`${keyword}\\``,\n `La condición va entre paréntesis: \\`${keyword} (x > 0)\\`.`,\n );\n const condition = this.parseExpression();\n this.expect(TokenType.RPAREN, `para cerrar la condición de \\`${keyword}\\``);\n return condition;\n }\n\n private parseIf(): ast.If {\n const start = this.current.span.start;\n const branches: ast.IfBranch[] = [];\n let keyword = 'si';\n do {\n const branchStart = this.advance().span.start; // si / pero_si\n const condition = this.parseCondition(keyword);\n const body = this.parseBlock(`después de la condición de \\`${keyword}\\``);\n branches.push({ condition, body, span: this.spanFrom(branchStart) });\n keyword = 'pero_si';\n } while (this.check(TokenType.ELSEIF));\n\n let alternative: ast.Block | undefined;\n if (this.match(TokenType.ELSE)) {\n if (this.check(TokenType.IF)) {\n this.error(\n 'SI_NO_SI',\n 'Después de `si_no` va directamente un bloque.',\n this.current.span,\n 'Para otra condición usa `pero_si (…)`.',\n );\n }\n alternative = this.parseBlock('después de `si_no`');\n }\n return { kind: 'If', id: this.id(), branches, alternative, span: this.spanFrom(start) };\n }\n\n private parseWhile(): ast.While {\n const start = this.advance().span.start;\n const condition = this.parseCondition('mientras');\n const body = this.parseBlock('después de la condición de `mientras`');\n return { kind: 'While', id: this.id(), condition, body, span: this.spanFrom(start) };\n }\n\n private parseDoUntil(): ast.DoUntil {\n const start = this.advance().span.start;\n const body = this.parseBlock('después de `hacer`');\n this.expect(\n TokenType.UNTIL,\n 'después del bloque de `hacer`',\n 'La forma completa es `hacer { … } hasta_que (condición);`.',\n );\n const condition = this.parseCondition('hasta_que');\n return { kind: 'DoUntil', id: this.id(), body, condition, span: this.spanFrom(start) };\n }\n\n private parseFor(): ast.For {\n const start = this.advance().span.start;\n this.expect(\n TokenType.LPAREN,\n 'después de `para`',\n 'La forma completa es `para (variable i = 0; i < 10; i += 1) { … }`.',\n );\n\n let init: ast.For['init'];\n if (!this.check(TokenType.SEMICOLON)) {\n if (this.check(TokenType.LET)) init = this.parseVariableDeclaration();\n else if (this.check(TokenType.IDENT) && ASSIGNMENT_OPERATORS[this.peek().type]) init = this.parseAssignment();\n else init = this.parseExpressionStatement();\n }\n this.expect(TokenType.SEMICOLON, 'después del inicio del `para`');\n\n const condition = this.check(TokenType.SEMICOLON) ? undefined : this.parseExpression();\n this.expect(TokenType.SEMICOLON, 'después de la condición del `para`');\n\n let update: ast.For['update'];\n if (!this.check(TokenType.RPAREN)) {\n if (this.check(TokenType.IDENT) && ASSIGNMENT_OPERATORS[this.peek().type]) update = this.parseAssignment();\n else update = this.parseExpressionStatement();\n }\n this.expect(TokenType.RPAREN, 'para cerrar el `para`');\n\n const body = this.parseBlock('después de `para (…)`');\n return { kind: 'For', id: this.id(), init, condition, update, body, span: this.spanFrom(start) };\n }\n\n // ── expresiones ────────────────────────────────────────────────────────────\n\n private parseExpression(precedence: Precedence = Precedence.LOWEST): ast.Expression {\n this.enter();\n let left = this.parsePrefix();\n while (precedence < (INFIX_PRECEDENCE[this.current.type] ?? Precedence.LOWEST)) {\n left = this.parseInfix(left);\n }\n this.leave();\n return left;\n }\n\n private parsePrefix(): ast.Expression {\n const token = this.current;\n switch (token.type) {\n case TokenType.NUMBER:\n this.advance();\n return { kind: 'NumberLiteral', id: this.id(), value: token.value as number, span: token.span };\n case TokenType.STRING:\n this.advance();\n return { kind: 'StringLiteral', id: this.id(), value: token.value as string, span: token.span };\n case TokenType.TRUE:\n case TokenType.FALSE:\n this.advance();\n return { kind: 'BooleanLiteral', id: this.id(), value: token.type === TokenType.TRUE, span: token.span };\n case TokenType.NULL:\n this.advance();\n return { kind: 'NullLiteral', id: this.id(), span: token.span };\n case TokenType.IDENT:\n this.advance();\n return { kind: 'Identifier', id: this.id(), name: token.lexeme, span: token.span };\n case TokenType.LPAREN: {\n this.advance();\n const expression = this.parseExpression();\n this.expect(TokenType.RPAREN, 'para cerrar el paréntesis');\n return expression;\n }\n case TokenType.LBRACKET:\n return this.parseList();\n case TokenType.MINUS: {\n this.advance();\n const operand = this.parseExpression(Precedence.UNARY);\n return { kind: 'Unary', id: this.id(), operator: '-', operand, span: this.spanFrom(token.span.start) };\n }\n case TokenType.NOT:\n case TokenType.BANG: {\n this.advance();\n const operand = this.parseExpression(Precedence.NOT);\n return { kind: 'Unary', id: this.id(), operator: 'no', operand, span: this.spanFrom(token.span.start) };\n }\n case TokenType.FUNCTION:\n return this.parseFunction();\n case TokenType.ILLEGAL:\n throw new ParseFailure();\n case TokenType.IF:\n case TokenType.WHILE:\n case TokenType.FOR:\n case TokenType.DO:\n case TokenType.LET:\n case TokenType.RETURN:\n this.error(\n 'SENTENCIA_EN_EXPRESIÓN',\n `${describeToken(token)} empieza una sentencia y no se puede usar dentro de una expresión.`,\n token.span,\n );\n case TokenType.EOF:\n this.error(\n 'EXPRESIÓN_INCOMPLETA',\n 'El programa terminó a mitad de una expresión.',\n token.span,\n 'Revisa que no falte un valor al final.',\n );\n default:\n this.error(\n 'EXPRESIÓN_ESPERADA',\n `Se esperaba un valor, pero se encontró ${describeToken(token)}.`,\n token.span,\n /^[a-z_]+$/.test(token.lexeme)\n ? `\\`${token.lexeme}\\` es una palabra reservada; no se puede usar como nombre de variable.`\n : undefined,\n );\n }\n }\n\n private parseInfix(left: ast.Expression): ast.Expression {\n const token = this.current;\n const start = left.span.start;\n switch (token.type) {\n case TokenType.LPAREN: {\n this.advance();\n const args = this.parseList_(TokenType.RPAREN, 'para cerrar los argumentos');\n return { kind: 'Call', id: this.id(), callee: left, args, span: this.spanFrom(start) };\n }\n case TokenType.LBRACKET: {\n this.advance();\n if (this.check(TokenType.RBRACKET)) {\n this.error(\n 'ÍNDICE_VACÍO',\n 'Falta el índice entre los corchetes.',\n this.current.span,\n 'Por ejemplo: `lista[0]`.',\n );\n }\n const index = this.parseExpression();\n this.expect(TokenType.RBRACKET, 'para cerrar el índice');\n return { kind: 'Index', id: this.id(), object: left, index, span: this.spanFrom(start) };\n }\n case TokenType.AND:\n case TokenType.OR: {\n this.advance();\n const precedence = INFIX_PRECEDENCE[token.type]!;\n const right = this.parseExpression(precedence);\n const operator = token.type === TokenType.AND ? 'y' : 'o';\n return { kind: 'Logical', id: this.id(), operator, left, right, span: this.spanFrom(start) };\n }\n default: {\n this.advance();\n const precedence = INFIX_PRECEDENCE[token.type]!;\n const right = this.parseExpression(precedence);\n const operator = BINARY_OPERATORS[token.type]!;\n if (precedence === Precedence.COMPARE && INFIX_PRECEDENCE[this.current.type] === Precedence.COMPARE) {\n this.error(\n 'COMPARACIÓN_ENCADENADA',\n 'No se pueden encadenar comparaciones.',\n { start, end: this.current.span.end },\n 'Une las dos comparaciones con `y`, por ejemplo: `1 < x y x < 5`.',\n );\n }\n return { kind: 'Binary', id: this.id(), operator, left, right, span: this.spanFrom(start) };\n }\n }\n }\n\n private parseList(): ast.ListLiteral {\n const start = this.advance().span.start;\n const elements = this.parseList_(TokenType.RBRACKET, 'para cerrar la lista');\n return { kind: 'ListLiteral', id: this.id(), elements, span: this.spanFrom(start) };\n }\n\n /** Expresiones separadas por comas hasta `closing`. Se permite una coma final. */\n private parseList_(closing: TokenType, context: string): ast.Expression[] {\n const items: ast.Expression[] = [];\n while (!this.check(closing)) {\n items.push(this.parseExpression());\n if (!this.match(TokenType.COMMA)) break;\n }\n this.expect(closing, context);\n return items;\n }\n\n private parseFunction(): ast.FunctionLiteral {\n const start = this.advance().span.start;\n if (this.check(TokenType.IDENT)) {\n const name = this.current.lexeme;\n this.error(\n 'PROCEDIMIENTO_CON_NOMBRE',\n 'Los procedimientos con nombre todavía no están disponibles.',\n this.current.span,\n `Por ahora escribe \\`variable ${name} = procedimiento(…) { … };\\`.`,\n );\n }\n this.expect(\n TokenType.LPAREN,\n 'después de `procedimiento`',\n 'Los parámetros van entre paréntesis: `procedimiento(a, b) { … }`.',\n );\n const params: ast.Identifier[] = [];\n const seen = new Set<string>();\n while (!this.check(TokenType.RPAREN)) {\n const param = this.parseBindingName('como parámetro');\n if (seen.has(param.name)) {\n this.error('PARÁMETRO_REPETIDO', `El parámetro \\`${param.name}\\` está repetido.`, param.span);\n }\n seen.add(param.name);\n params.push(param);\n if (!this.match(TokenType.COMMA)) break;\n }\n this.expect(TokenType.RPAREN, 'para cerrar los parámetros');\n const body = this.parseBlock('para el cuerpo del procedimiento');\n return { kind: 'FunctionLiteral', id: this.id(), params, body, span: this.spanFrom(start) };\n }\n}\n",
9
+ "import type * as ast from '../syntax/ast';\nimport type { Value } from './values';\n\nexport type DecisionLabel = 'si' | 'pero_si' | 'mientras' | 'hasta_que' | 'para';\n\n/**\n * Instrucciones de la máquina de Codexivo. Cada una conserva el nodo del AST que la originó, así la\n * máquina puede decir qué parte del código está ejecutando y dónde ocurrió un error.\n */\nexport type Instruction =\n /** Inicio de una sentencia: punto natural para pausar. */\n | { op: 'step'; node: ast.Statement }\n | { op: 'const'; value: Value; node: ast.Expression }\n | { op: 'list'; count: number; node: ast.ListLiteral }\n | { op: 'load'; name: string; node: ast.Identifier; forUpdate?: boolean }\n /** Saca un valor de la pila y crea la variable en el alcance actual. */\n | { op: 'declare'; name: string; node: ast.VariableDeclaration }\n /** Saca un valor de la pila y lo guarda en una variable existente. */\n | { op: 'store'; name: string; node: ast.Assignment }\n /** Saca un valor de la pila; es el valor de una sentencia de expresión. */\n | { op: 'result'; node: ast.ExpressionStatement }\n | { op: 'unary'; operator: ast.UnaryOperator; node: ast.Unary }\n | { op: 'binary'; operator: ast.BinaryOperator; node: ast.Binary | ast.Assignment }\n | { op: 'index'; node: ast.Index }\n | { op: 'closure'; chunk: Chunk; node: ast.FunctionLiteral }\n | { op: 'call'; argc: number; node: ast.Call }\n | { op: 'return'; hasValue: boolean; node: ast.Return }\n | { op: 'jump'; target: number }\n /** Saca la condición (debe ser lógica) y salta si vale `jumpWhen`. */\n | {\n op: 'branch';\n target: number;\n jumpWhen: boolean;\n label: DecisionLabel;\n node: ast.Expression;\n owner: ast.Statement;\n }\n /** Cortocircuito: revisa el lado izquierdo de `y`/`o`; si ya decide el resultado, lo deja y salta. */\n | { op: 'shortCircuit'; target: number; node: ast.Logical }\n /** Revisa que el lado derecho de `y`/`o` sea lógico. */\n | { op: 'checkLogical'; node: ast.Logical }\n | { op: 'enterScope' }\n | { op: 'exitScope' }\n | { op: 'halt' };\n\n/** Código de un procedimiento o del programa principal. */\nexport interface Chunk {\n name: string;\n params: string[];\n code: Instruction[];\n node: ast.Program | ast.FunctionLiteral;\n}\n\n/** Traduce el AST a instrucciones. El AST debe venir sin errores de sintaxis. */\nexport const compile = (program: ast.Program): Chunk => {\n const compiler = new Compiler({ name: 'programa', params: [], code: [], node: program });\n for (const statement of program.statements) compiler.statement(statement);\n compiler.emit({ op: 'halt' });\n return compiler.chunk;\n};\n\nconst compileFunction = (node: ast.FunctionLiteral): Chunk => {\n const compiler = new Compiler({\n name: node.name ?? 'procedimiento',\n params: node.params.map(param => param.name),\n code: [],\n node,\n });\n // El cuerpo comparte alcance con los parámetros: así `variable x` dentro de `procedimiento(x)` es un error.\n for (const statement of node.body.statements) compiler.statement(statement);\n compiler.emit({ op: 'const', value: null, node });\n compiler.emit({ op: 'return', hasValue: true, node: { kind: 'Return', id: node.id, span: node.body.span } });\n return compiler.chunk;\n};\n\nclass Compiler {\n constructor(readonly chunk: Chunk) {}\n\n emit(instruction: Instruction): number {\n this.chunk.code.push(instruction);\n return this.chunk.code.length - 1;\n }\n\n private get here(): number {\n return this.chunk.code.length;\n }\n\n private patch(at: number, target: number): void {\n const instruction = this.chunk.code[at];\n if (instruction && 'target' in instruction) instruction.target = target;\n }\n\n statement(node: ast.Statement): void {\n if (node.kind !== 'Block') this.emit({ op: 'step', node });\n switch (node.kind) {\n case 'VariableDeclaration':\n this.expression(node.value);\n this.emit({ op: 'declare', name: node.name.name, node });\n return;\n case 'Assignment':\n if (node.operator !== '=') {\n this.emit({ op: 'load', name: node.target.name, node: node.target, forUpdate: true });\n }\n this.expression(node.value);\n if (node.operator !== '=') {\n this.emit({ op: 'binary', operator: node.operator[0] as ast.BinaryOperator, node });\n }\n this.emit({ op: 'store', name: node.target.name, node });\n return;\n case 'ExpressionStatement':\n this.expression(node.expression);\n this.emit({ op: 'result', node });\n return;\n case 'Return':\n if (node.value) this.expression(node.value);\n this.emit({ op: 'return', hasValue: Boolean(node.value), node });\n return;\n case 'Block':\n this.block(node);\n return;\n case 'If': {\n const exits: number[] = [];\n node.branches.forEach((branch, index) => {\n this.expression(branch.condition);\n const skip = this.emit({\n op: 'branch',\n target: -1,\n jumpWhen: false,\n label: index === 0 ? 'si' : 'pero_si',\n node: branch.condition,\n owner: node,\n });\n this.block(branch.body);\n exits.push(this.emit({ op: 'jump', target: -1 }));\n this.patch(skip, this.here);\n });\n if (node.alternative) this.block(node.alternative);\n for (const exit of exits) this.patch(exit, this.here);\n return;\n }\n case 'While': {\n const start = this.here;\n this.expression(node.condition);\n const exit = this.emit({\n op: 'branch',\n target: -1,\n jumpWhen: false,\n label: 'mientras',\n node: node.condition,\n owner: node,\n });\n this.block(node.body);\n this.emit({ op: 'jump', target: start });\n this.patch(exit, this.here);\n return;\n }\n case 'DoUntil': {\n const start = this.here;\n this.block(node.body);\n this.expression(node.condition);\n this.emit({\n op: 'branch',\n target: start,\n jumpWhen: false,\n label: 'hasta_que',\n node: node.condition,\n owner: node,\n });\n return;\n }\n case 'For': {\n this.emit({ op: 'enterScope' });\n if (node.init) this.statement(node.init);\n const start = this.here;\n let exit = -1;\n if (node.condition) {\n this.expression(node.condition);\n exit = this.emit({\n op: 'branch',\n target: -1,\n jumpWhen: false,\n label: 'para',\n node: node.condition,\n owner: node,\n });\n }\n this.block(node.body);\n if (node.update) this.statement(node.update);\n this.emit({ op: 'jump', target: start });\n if (exit >= 0) this.patch(exit, this.here);\n this.emit({ op: 'exitScope' });\n return;\n }\n }\n }\n\n private block(node: ast.Block): void {\n this.emit({ op: 'enterScope' });\n for (const statement of node.statements) this.statement(statement);\n this.emit({ op: 'exitScope' });\n }\n\n private expression(node: ast.Expression): void {\n switch (node.kind) {\n case 'NumberLiteral':\n case 'StringLiteral':\n case 'BooleanLiteral':\n this.emit({ op: 'const', value: node.value, node });\n return;\n case 'NullLiteral':\n this.emit({ op: 'const', value: null, node });\n return;\n case 'ListLiteral':\n for (const element of node.elements) this.expression(element);\n this.emit({ op: 'list', count: node.elements.length, node });\n return;\n case 'Identifier':\n this.emit({ op: 'load', name: node.name, node });\n return;\n case 'Unary':\n this.expression(node.operand);\n this.emit({ op: 'unary', operator: node.operator, node });\n return;\n case 'Binary':\n this.expression(node.left);\n this.expression(node.right);\n this.emit({ op: 'binary', operator: node.operator, node });\n return;\n case 'Logical': {\n this.expression(node.left);\n const skip = this.emit({ op: 'shortCircuit', target: -1, node });\n this.expression(node.right);\n this.emit({ op: 'checkLogical', node });\n this.patch(skip, this.here);\n return;\n }\n case 'Call':\n this.expression(node.callee);\n for (const arg of node.args) this.expression(arg);\n this.emit({ op: 'call', argc: node.args.length, node });\n return;\n case 'Index':\n this.expression(node.object);\n this.expression(node.index);\n this.emit({ op: 'index', node });\n return;\n case 'FunctionLiteral':\n this.emit({ op: 'closure', chunk: compileFunction(node), node });\n return;\n }\n }\n}\n",
10
+ "import type { CodexivoError } from '../errors';\nimport type { Chunk } from './compiler';\n\n/**\n * Valores en tiempo de ejecución. Números, textos, lógicos y `nulo` son primitivos de JavaScript;\n * listas y procedimientos son objetos con un `id` para poder dibujar cuándo dos variables\n * comparten el mismo valor.\n */\nexport type Value = number | string | boolean | null | ListValue | ProcedureValue | NativeValue;\n\nexport interface ListValue {\n kind: 'lista';\n id: number;\n items: Value[];\n}\n\nexport interface ProcedureValue {\n kind: 'procedimiento';\n id: number;\n name?: string;\n chunk: Chunk;\n closure: Scope;\n}\n\n/** Contexto que la máquina le da a los procedimientos nativos. */\nexport interface NativeContext {\n write(text: string): void;\n /** Siguiente línea de entrada, `null` si ya no hay, o `WAIT_FOR_INPUT` si hay que pedirla. */\n readLine(): string | null | typeof WAIT_FOR_INPUT;\n newList(items: Value[]): ListValue;\n /** Error de ejecución ubicado en la llamada actual. */\n fail(code: string, message: string, hint?: string): CodexivoError;\n}\n\nexport const WAIT_FOR_INPUT: unique symbol = Symbol('esperar-entrada');\n\nexport interface NativeValue {\n kind: 'nativo';\n name: string;\n /** Cantidad de argumentos: exacta, o `[mínimo, máximo]` (`máximo` puede ser `Infinity`). */\n arity: number | [number, number];\n call(args: Value[], context: NativeContext): Value | typeof WAIT_FOR_INPUT;\n}\n\nexport type TypeName = 'número' | 'texto' | 'lógico' | 'nulo' | 'lista' | 'procedimiento';\n\nexport const typeOf = (value: Value): TypeName => {\n if (value === null) return 'nulo';\n switch (typeof value) {\n case 'number':\n return 'número';\n case 'string':\n return 'texto';\n case 'boolean':\n return 'lógico';\n default:\n return value.kind === 'lista' ? 'lista' : 'procedimiento';\n }\n};\n\n/** \"un número\", \"una lista\"… para mensajes. */\nexport const describeType = (type: TypeName): string => {\n switch (type) {\n case 'lista':\n return 'una lista';\n case 'nulo':\n return '`nulo`';\n default:\n return `un ${type}`;\n }\n};\n\nexport const isList = (value: Value): value is ListValue =>\n typeof value === 'object' && value !== null && value.kind === 'lista';\n\nexport const isCallable = (value: Value): value is ProcedureValue | NativeValue =>\n typeof value === 'object' && value !== null && (value.kind === 'procedimiento' || value.kind === 'nativo');\n\nconst formatNumber = (value: number): string => {\n if (Object.is(value, -0)) return '0';\n if (value === Infinity) return 'infinito';\n if (value === -Infinity) return '-infinito';\n return String(value);\n};\n\n/** Representación como se escribiría en código: los textos van entre comillas. */\nexport const repr = (value: Value, seen: Set<ListValue> = new Set()): string => {\n if (value === null) return 'nulo';\n switch (typeof value) {\n case 'number':\n return formatNumber(value);\n case 'string':\n return JSON.stringify(value);\n case 'boolean':\n return value ? 'verdadero' : 'falso';\n }\n if (value.kind === 'lista') {\n if (seen.has(value)) return '[…]';\n seen.add(value);\n const inner = value.items.map(item => repr(item, seen)).join(', ');\n seen.delete(value);\n return `[${inner}]`;\n }\n return value.name ? `procedimiento ${value.name}` : 'procedimiento';\n};\n\n/** Como lo muestra `imprimir`: los textos sin comillas. */\nexport const show = (value: Value): string => (typeof value === 'string' ? value : repr(value));\n\n/** Igualdad por valor: dos listas son iguales si tienen los mismos elementos en el mismo orden. */\nexport const equals = (a: Value, b: Value, seen: Array<[ListValue, ListValue]> = []): boolean => {\n if (a === b) return true;\n if (isList(a) && isList(b)) {\n if (a.items.length !== b.items.length) return false;\n if (seen.some(([x, y]) => x === a && y === b)) return true;\n seen.push([a, b]);\n const result = a.items.every((item, index) => equals(item, b.items[index]!, seen));\n seen.pop();\n return result;\n }\n return false;\n};\n\n/**\n * Un alcance: las variables de un bloque. `parent` apunta al alcance que lo contiene.\n */\nexport class Scope {\n readonly variables = new Map<string, Value>();\n\n constructor(readonly parent?: Scope) {}\n\n lookup(name: string): Scope | undefined {\n let scope: Scope | undefined = this;\n while (scope) {\n if (scope.variables.has(name)) return scope;\n scope = scope.parent;\n }\n return undefined;\n }\n}\n",
11
+ "import { describeType, isList, repr, show, typeOf, WAIT_FOR_INPUT } from './values';\nimport type { NativeContext, NativeValue, Value } from './values';\n\ntype NativeFn = (args: Value[], context: NativeContext) => Value | typeof WAIT_FOR_INPUT;\n\nconst define = (name: string, arity: NativeValue['arity'], call: NativeFn): NativeValue => ({\n kind: 'nativo',\n name,\n arity,\n call,\n});\n\nconst expectNumber = (name: string, value: Value, context: NativeContext, position = ''): number => {\n if (typeof value === 'number') return value;\n throw context.fail(\n 'TIPO_DE_ARGUMENTO',\n `\\`${name}\\` espera un número${position}, pero recibió ${describeType(typeOf(value))} (${repr(value)}).`,\n );\n};\n\nconst expectList = (name: string, value: Value, context: NativeContext) => {\n if (isList(value)) return value;\n throw context.fail('TIPO_DE_ARGUMENTO', `\\`${name}\\` espera una lista, pero recibió ${describeType(typeOf(value))}.`);\n};\n\nconst numbersOf = (name: string, args: Value[], context: NativeContext): number[] => {\n const values = args.length === 1 && isList(args[0]!) ? args[0].items : args;\n if (values.length === 0) {\n throw context.fail('LISTA_VACÍA', `\\`${name}\\` necesita al menos un número, pero la lista está vacía.`);\n }\n return values.map(value => expectNumber(name, value, context));\n};\n\nconst parseNumber = (text: string): number | undefined => {\n const trimmed = text.trim();\n if (!/^[-+]?(\\d+(\\.\\d*)?|\\.\\d+)$/.test(trimmed)) return undefined;\n return Number(trimmed);\n};\n\n/** Redondeo escolar: los .5 se alejan del cero (2.5 → 3, -2.5 → -3). */\nconst roundHalfAway = (value: number, decimals: number): number => {\n const factor = 10 ** decimals;\n return (Math.sign(value) * Math.round(Math.abs(value) * factor)) / factor;\n};\n\nconst library: NativeValue[] = [\n // entrada y salida\n define('imprimir', [0, Infinity], (args, context) => {\n context.write(`${args.map(show).join(' ')}\\n`);\n return null;\n }),\n define('leer', 0, (_args, context) => context.readLine()),\n define('leer_número', 0, (_args, context) => {\n const line = context.readLine();\n if (line === WAIT_FOR_INPUT) return line;\n if (line === null) throw context.fail('SIN_ENTRADA', '`leer_número` no encontró más datos de entrada.');\n const value = parseNumber(line);\n if (value === undefined) {\n throw context.fail('NO_ES_NÚMERO', `\\`leer_número\\` esperaba un número, pero leyó ${JSON.stringify(line)}.`);\n }\n return value;\n }),\n\n // tipos y conversiones\n define('tipo', 1, args => typeOf(args[0]!)),\n define('texto', 1, args => show(args[0]!)),\n define('número', 1, (args, context) => {\n const value = args[0]!;\n if (typeof value === 'number') return value;\n if (typeof value === 'string') {\n const parsed = parseNumber(value);\n if (parsed !== undefined) return parsed;\n throw context.fail('NO_ES_NÚMERO', `No se puede convertir ${JSON.stringify(value)} en número.`);\n }\n throw context.fail('TIPO_DE_ARGUMENTO', `No se puede convertir ${describeType(typeOf(value))} en número.`);\n }),\n define('longitud', 1, (args, context) => {\n const value = args[0]!;\n if (typeof value === 'string') return [...value].length;\n if (isList(value)) return value.items.length;\n throw context.fail(\n 'TIPO_DE_ARGUMENTO',\n `\\`longitud\\` espera un texto o una lista, pero recibió ${describeType(typeOf(value))}.`,\n );\n }),\n\n // matemáticas\n define('absoluto', 1, (args, context) => Math.abs(expectNumber('absoluto', args[0]!, context))),\n define('máximo', [1, Infinity], (args, context) => Math.max(...numbersOf('máximo', args, context))),\n define('mínimo', [1, Infinity], (args, context) => Math.min(...numbersOf('mínimo', args, context))),\n define('redondear', [1, 2], (args, context) => {\n const value = expectNumber('redondear', args[0]!, context);\n const decimals = args.length > 1 ? expectNumber('redondear', args[1]!, context, ' de decimales') : 0;\n if (!Number.isInteger(decimals) || decimals < 0) {\n throw context.fail(\n 'TIPO_DE_ARGUMENTO',\n 'La cantidad de decimales de `redondear` tiene que ser un entero positivo o 0.',\n );\n }\n return roundHalfAway(value, decimals);\n }),\n\n // listas\n define('agregar', 2, (args, context) => {\n expectList('agregar', args[0]!, context).items.push(args[1]!);\n return null;\n }),\n define('primero', 1, (args, context) => {\n const list = expectList('primero', args[0]!, context);\n if (list.items.length === 0) throw context.fail('LISTA_VACÍA', '`primero` no puede tomar nada de una lista vacía.');\n return list.items[0]!;\n }),\n define('último', 1, (args, context) => {\n const list = expectList('último', args[0]!, context);\n if (list.items.length === 0) throw context.fail('LISTA_VACÍA', '`último` no puede tomar nada de una lista vacía.');\n return list.items[list.items.length - 1]!;\n }),\n];\n\n/** Quita los acentos agudos y la diéresis, pero conserva la ñ. */\nexport const withoutAccents = (name: string): string =>\n name.replace(/[áéíóúü]/g, character => ({ á: 'a', é: 'e', í: 'i', ó: 'o', ú: 'u', ü: 'u' })[character] ?? character);\n\n/** Biblioteca estándar. Cada nombre con acento también se acepta sin él (`máximo` y `maximo`). */\nexport const natives: Record<string, NativeValue> = (() => {\n const table: Record<string, NativeValue> = {};\n for (const native of library) table[native.name] = native;\n for (const native of library) {\n const alias = withoutAccents(native.name);\n if (!table[alias]) table[alias] = native;\n }\n return table;\n})();\n",
12
+ "const distance = (a: string, b: string): number => {\n const previous = Array.from({ length: b.length + 1 }, (_, index) => index);\n for (let i = 1; i <= a.length; i++) {\n let diagonal = previous[0]!;\n previous[0] = i;\n for (let j = 1; j <= b.length; j++) {\n const above = previous[j]!;\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n previous[j] = Math.min(previous[j]! + 1, previous[j - 1]! + 1, diagonal + cost);\n diagonal = above;\n }\n }\n return previous[b.length]!;\n};\n\nconst fold = (text: string): string => text.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase();\n\n/** El nombre más parecido a `name` entre `candidates`, si hay uno razonablemente cerca. */\nexport const suggestName = (name: string, candidates: Iterable<string>): string | undefined => {\n const target = fold(name);\n const limit = target.length <= 3 ? 1 : 2;\n let best: string | undefined;\n let bestDistance = Infinity;\n for (const candidate of new Set(candidates)) {\n if (candidate === name) continue;\n const current = distance(target, fold(candidate));\n if (current <= limit && current < bestDistance) {\n best = candidate;\n bestDistance = current;\n }\n }\n return best;\n};\n",
13
+ "import { CodexivoError } from '../errors';\nimport type { ErrorInfo } from '../errors';\nimport type * as ast from '../syntax/ast';\nimport type { Span } from '../syntax/token';\nimport { natives as defaultNatives } from './builtins';\nimport type { Chunk, DecisionLabel, Instruction } from './compiler';\nimport { describeType, equals, isCallable, isList, repr, Scope, typeOf, WAIT_FOR_INPUT } from './values';\nimport type { ListValue, NativeContext, NativeValue, ProcedureValue, Value } from './values';\nimport { suggestName } from './suggest';\n\nexport type MachineStatus = 'lista' | 'pausada' | 'esperando_entrada' | 'terminada' | 'error';\n\nexport interface MachineOptions {\n /** Máximo de operaciones antes de detener el programa. Por defecto 10 000 000. */\n maxOperations?: number;\n /** Máximo de llamadas anidadas. Por defecto 10 000. */\n maxCallDepth?: number;\n /** Máximo de caracteres de salida. Por defecto 1 000 000. */\n maxOutput?: number;\n /** Líneas de entrada para `leer()`. */\n input?: string | string[];\n /** Si es `true`, `leer()` sin entrada pausa la máquina en lugar de regresar `nulo`. */\n interactiveInput?: boolean;\n /** Procedimientos nativos adicionales (por ejemplo, los del modo mundo). */\n natives?: Record<string, NativeValue>;\n /** Líneas donde `run()` se detiene antes de ejecutar la sentencia. */\n breakpoints?: number[];\n /** Alcance global compartido entre ejecuciones (lo usa el REPL). */\n globals?: Scope;\n /** Permite volver a declarar una variable existente (útil en el REPL). */\n allowRedeclare?: boolean;\n /** Recibe cada evento de la ejecución. Activarlo hace la ejecución más lenta. */\n onEvent?: (event: MachineEvent) => void;\n}\n\n// ── eventos y fotografías ────────────────────────────────────────────────────\n\nexport type SerializedValue =\n | { type: 'número'; value: number; repr: string }\n | { type: 'texto'; value: string; repr: string }\n | { type: 'lógico'; value: boolean; repr: string }\n | { type: 'nulo'; value: null; repr: string }\n | { type: 'lista'; ref: number; length: number; repr: string }\n | { type: 'procedimiento'; name?: string; repr: string };\n\nexport const serialize = (value: Value): SerializedValue => {\n const text = repr(value);\n if (value === null) return { type: 'nulo', value: null, repr: text };\n switch (typeof value) {\n case 'number':\n return { type: 'número', value, repr: text };\n case 'string':\n return { type: 'texto', value, repr: text };\n case 'boolean':\n return { type: 'lógico', value, repr: text };\n }\n if (value.kind === 'lista') return { type: 'lista', ref: value.id, length: value.items.length, repr: text };\n return { type: 'procedimiento', name: value.name, repr: text };\n};\n\ninterface EventBase {\n /** Operaciones ejecutadas hasta este evento. */\n operations: number;\n /** Profundidad de llamadas: 1 en el programa principal. */\n depth: number;\n}\n\nexport type MachineEvent = EventBase &\n (\n | { type: 'paso'; nodeId: number; nodeKind: ast.Statement['kind']; span: Span }\n | { type: 'declara'; name: string; value: SerializedValue; nodeId: number; span: Span }\n | { type: 'asigna'; name: string; before: SerializedValue; after: SerializedValue; nodeId: number; span: Span }\n | { type: 'decide'; label: DecisionLabel; value: boolean; nodeId: number; ownerId: number; span: Span }\n | {\n type: 'opera';\n operator: string;\n operands: SerializedValue[];\n result: SerializedValue;\n nodeId: number;\n span: Span;\n }\n | { type: 'llama'; name: string; args: SerializedValue[]; nodeId: number; span: Span }\n | { type: 'regresa'; name: string; value: SerializedValue }\n | { type: 'imprime'; text: string }\n | { type: 'lee'; text: string | null }\n | { type: 'error'; error: ErrorInfo }\n | { type: 'fin'; result: SerializedValue }\n );\n\nexport interface FrameSnapshot {\n name: string;\n callSpan?: Span;\n /** Alcances del procedimiento, del más externo al más interno. */\n scopes: Array<Record<string, SerializedValue>>;\n}\n\nexport interface Snapshot {\n status: MachineStatus;\n operations: number;\n /** Nodo donde está detenida la máquina. */\n nodeId?: number;\n span?: Span;\n /** Llamadas activas, del programa principal a la más reciente. */\n frames: FrameSnapshot[];\n /** Contenido de las listas que aparecen en `frames`, por `ref`. */\n heap: Record<number, SerializedValue[]>;\n output: string;\n}\n\n// ── máquina ──────────────────────────────────────────────────────────────────\n\ninterface Frame {\n chunk: Chunk;\n pc: number;\n scope: Scope;\n /** Primer alcance del marco; el que contiene a los parámetros. */\n root: Scope;\n /** Posición en la pila de valores donde empieza este marco (el procedimiento llamado está justo antes). */\n stackBase: number;\n name: string;\n callNode?: ast.Call;\n}\n\nconst DEFAULTS = {\n maxOperations: 10_000_000,\n maxCallDepth: 10_000,\n maxOutput: 1_000_000,\n};\n\nconst NO_COUNT = new Set<Instruction['op']>(['step', 'enterScope', 'exitScope', 'jump', 'halt']);\n\n/**\n * Ejecuta un programa compilado. Tiene su propia pila de llamadas, así que la recursión no depende\n * de la pila de JavaScript, y se puede pausar en cualquier sentencia o decisión.\n */\nexport class Machine {\n private readonly frames: Frame[] = [];\n private readonly stack: Value[] = [];\n private readonly natives: Record<string, NativeValue>;\n private readonly inputQueue: string[];\n private readonly breakpoints: Set<number>;\n private readonly limits: typeof DEFAULTS;\n private readonly onEvent?: (event: MachineEvent) => void;\n private readonly outputParts: string[] = [];\n private outputLength = 0;\n private nextId = 1;\n private lastValue: Value = null;\n private inputClosed = false;\n private currentNode?: ast.Node;\n\n status: MachineStatus = 'lista';\n operations = 0;\n result: Value = null;\n error?: CodexivoError;\n\n constructor(\n program: Chunk,\n private readonly options: MachineOptions = {},\n ) {\n this.natives = { ...defaultNatives, ...options.natives };\n this.limits = {\n maxOperations: options.maxOperations ?? DEFAULTS.maxOperations,\n maxCallDepth: options.maxCallDepth ?? DEFAULTS.maxCallDepth,\n maxOutput: options.maxOutput ?? DEFAULTS.maxOutput,\n };\n this.inputQueue = typeof options.input === 'string' ? splitLines(options.input) : [...(options.input ?? [])];\n this.breakpoints = new Set(options.breakpoints ?? []);\n this.onEvent = options.onEvent;\n const globals = options.globals ?? new Scope();\n this.frames.push({ chunk: program, pc: 0, scope: globals, root: globals, stackBase: 0, name: 'programa' });\n }\n\n /** Todo lo que el programa ha impreso. */\n get output(): string {\n return this.outputParts.join('');\n }\n\n get done(): boolean {\n return this.status === 'terminada' || this.status === 'error';\n }\n\n /** Ejecuta hasta terminar, fallar, esperar entrada, llegar a un punto de interrupción o agotar `budget` operaciones. */\n run(budget = Infinity): MachineStatus {\n return this.execute('run', budget);\n }\n\n /** Ejecuta hasta la siguiente sentencia o decisión. */\n step(): MachineStatus {\n return this.execute('step', Infinity);\n }\n\n /** Entrega una línea a un `leer()` que está esperando. */\n provideInput(line: string): void {\n this.inputQueue.push(...splitLines(line));\n if (this.status === 'esperando_entrada') this.status = 'pausada';\n }\n\n /** Indica que no habrá más entrada: desde ahora `leer()` regresa `nulo` en lugar de esperar. */\n closeInput(): void {\n this.inputClosed = true;\n if (this.status === 'esperando_entrada') this.status = 'pausada';\n }\n\n snapshot(): Snapshot {\n const heap: Record<number, SerializedValue[]> = {};\n const collect = (value: Value) => {\n if (!isList(value) || heap[value.id]) return;\n heap[value.id] = value.items.map(serialize);\n value.items.forEach(collect);\n };\n const frames = this.frames.map(frame => {\n const scopes: Array<Record<string, SerializedValue>> = [];\n let scope: Scope | undefined = frame.scope;\n while (scope) {\n const variables: Record<string, SerializedValue> = {};\n for (const [name, value] of scope.variables) {\n variables[name] = serialize(value);\n collect(value);\n }\n scopes.unshift(variables);\n if (scope === frame.root) break;\n scope = scope.parent;\n }\n return { name: frame.name, callSpan: frame.callNode?.span, scopes };\n });\n return {\n status: this.status,\n operations: this.operations,\n nodeId: this.currentNode?.id,\n span: this.currentNode?.span,\n frames,\n heap,\n output: this.output,\n };\n }\n\n // ── ciclo principal ────────────────────────────────────────────────────────\n\n private execute(mode: 'run' | 'step', budget: number): MachineStatus {\n if (this.done || this.status === 'esperando_entrada') return this.status;\n const stopAt = this.operations + budget;\n let executed = 0;\n\n try {\n while (true) {\n const frame = this.frames[this.frames.length - 1]!;\n const instruction = frame.chunk.code[frame.pc]!;\n\n if (!NO_COUNT.has(instruction.op)) {\n if (this.operations >= this.limits.maxOperations) {\n throw this.limitError(\n 'DEMASIADAS_OPERACIONES',\n `El programa superó el límite de ${formatCount(this.limits.maxOperations)} operaciones.`,\n '¿Hay un bucle que nunca termina? Revisa que su condición cambie en algún momento.',\n );\n }\n if (this.operations >= stopAt) {\n this.status = 'pausada';\n return this.status;\n }\n }\n\n // Los puntos de interrupción detienen `run()` antes de la sentencia.\n if (\n instruction.op === 'step' &&\n mode === 'run' &&\n executed > 0 &&\n this.breakpoints.has(instruction.node.span.start.line)\n ) {\n this.status = 'pausada';\n return this.status;\n }\n\n const pause = this.exec(frame, instruction);\n executed += 1;\n // `exec` puede cambiar el estado.\n const status = this.status as MachineStatus;\n if (status === 'esperando_entrada' || status === 'terminada') return status;\n if (mode === 'step' && pause) {\n this.status = 'pausada';\n return this.status;\n }\n }\n } catch (error) {\n if (!(error instanceof CodexivoError)) throw error;\n this.error = error;\n this.status = 'error';\n this.emit({ type: 'error', error: error.toJSON() });\n return this.status;\n }\n }\n\n /** Ejecuta una instrucción. Regresa `true` si es un punto de pausa para `step()`. */\n private exec(frame: Frame, instruction: Instruction): boolean {\n frame.pc += 1;\n if (!NO_COUNT.has(instruction.op)) this.operations += 1;\n\n switch (instruction.op) {\n case 'step':\n this.currentNode = instruction.node;\n this.emit({\n type: 'paso',\n nodeId: instruction.node.id,\n nodeKind: instruction.node.kind,\n span: instruction.node.span,\n });\n return true;\n\n case 'const':\n this.stack.push(instruction.value);\n return false;\n\n case 'list': {\n const items = this.stack.splice(this.stack.length - instruction.count, instruction.count);\n this.stack.push(this.newList(items));\n return false;\n }\n\n case 'load': {\n const scope = frame.scope.lookup(instruction.name);\n if (scope) {\n this.stack.push(scope.variables.get(instruction.name)!);\n return false;\n }\n const native = this.natives[instruction.name];\n if (native && !instruction.forUpdate) {\n this.stack.push(native);\n return false;\n }\n if (instruction.forUpdate) throw this.undeclaredAssignment(instruction.name, instruction.node);\n throw this.unknownName(frame, instruction.name, instruction.node);\n }\n\n case 'declare': {\n const value = this.stack.pop()!;\n if (frame.scope.variables.has(instruction.name) && !this.options.allowRedeclare) {\n throw this.runtimeError(\n 'VARIABLE_REPETIDA',\n `La variable \\`${instruction.name}\\` ya existe en este bloque.`,\n instruction.node.name,\n `Para cambiar su valor escribe \\`${instruction.name} = …\\`, sin \\`variable\\`.`,\n );\n }\n frame.scope.variables.set(instruction.name, value);\n this.emit({\n type: 'declara',\n name: instruction.name,\n value: serialize(value),\n nodeId: instruction.node.id,\n span: instruction.node.span,\n });\n return false;\n }\n\n case 'store': {\n const value = this.stack.pop()!;\n const scope = frame.scope.lookup(instruction.name);\n if (!scope) throw this.undeclaredAssignment(instruction.name, instruction.node.target);\n const before = scope.variables.get(instruction.name)!;\n scope.variables.set(instruction.name, value);\n if (this.onEvent) {\n this.emit({\n type: 'asigna',\n name: instruction.name,\n before: serialize(before),\n after: serialize(value),\n nodeId: instruction.node.id,\n span: instruction.node.span,\n });\n }\n return false;\n }\n\n case 'result': {\n const value = this.stack.pop()!;\n if (this.frames.length === 1) this.lastValue = value;\n return false;\n }\n\n case 'unary': {\n const operand = this.stack.pop()!;\n const result = this.unary(instruction.operator, operand, instruction.node);\n this.stack.push(result);\n if (this.onEvent) this.emitOperation(instruction.operator, [operand], result, instruction.node);\n return false;\n }\n\n case 'binary': {\n const right = this.stack.pop()!;\n const left = this.stack.pop()!;\n const result = this.binary(instruction.operator, left, right, instruction.node);\n this.stack.push(result);\n if (this.onEvent) this.emitOperation(instruction.operator, [left, right], result, instruction.node);\n return false;\n }\n\n case 'index': {\n const index = this.stack.pop()!;\n const object = this.stack.pop()!;\n this.stack.push(this.index(object, index, instruction.node));\n return false;\n }\n\n case 'closure': {\n const procedure: ProcedureValue = {\n kind: 'procedimiento',\n id: this.nextId++,\n name: instruction.node.name,\n chunk: instruction.chunk,\n closure: frame.scope,\n };\n this.stack.push(procedure);\n return false;\n }\n\n case 'call':\n return this.call(frame, instruction.argc, instruction.node);\n\n case 'return': {\n const value = instruction.hasValue ? this.stack.pop()! : null;\n if (this.frames.length === 1) {\n this.finish(value);\n return true;\n }\n this.emit({ type: 'regresa', name: frame.name, value: serialize(value) });\n this.frames.pop();\n this.stack.length = frame.stackBase - 1;\n this.stack.push(value);\n return false;\n }\n\n case 'jump':\n frame.pc = instruction.target;\n return false;\n\n case 'branch': {\n const condition = this.stack.pop()!;\n if (typeof condition !== 'boolean')\n throw this.nonBooleanCondition(instruction.label, condition, instruction.node);\n this.currentNode = instruction.node;\n this.emit({\n type: 'decide',\n label: instruction.label,\n value: condition,\n nodeId: instruction.node.id,\n ownerId: instruction.owner.id,\n span: instruction.node.span,\n });\n if (condition === instruction.jumpWhen) frame.pc = instruction.target;\n return true;\n }\n\n case 'shortCircuit': {\n const left = this.stack[this.stack.length - 1]!;\n if (typeof left !== 'boolean') throw this.nonBooleanLogical(instruction.node, left, instruction.node.left);\n const decided = instruction.node.operator === 'y' ? left === false : left === true;\n if (decided) {\n frame.pc = instruction.target;\n if (this.onEvent) this.emitOperation(instruction.node.operator, [left], left, instruction.node);\n } else {\n this.stack.pop();\n }\n return false;\n }\n\n case 'checkLogical': {\n const right = this.stack[this.stack.length - 1]!;\n if (typeof right !== 'boolean') throw this.nonBooleanLogical(instruction.node, right, instruction.node.right);\n if (this.onEvent) {\n const left = instruction.node.operator === 'y';\n this.emitOperation(instruction.node.operator, [left, right], right, instruction.node);\n }\n return false;\n }\n\n case 'enterScope':\n frame.scope = new Scope(frame.scope);\n return false;\n\n case 'exitScope':\n frame.scope = frame.scope.parent ?? frame.scope;\n return false;\n\n case 'halt':\n this.finish(this.lastValue);\n return true;\n }\n }\n\n private finish(value: Value): void {\n this.result = value;\n this.status = 'terminada';\n this.emit({ type: 'fin', result: serialize(value) });\n }\n\n // ── llamadas ───────────────────────────────────────────────────────────────\n\n private call(frame: Frame, argc: number, node: ast.Call): boolean {\n const calleeIndex = this.stack.length - argc - 1;\n const callee = this.stack[calleeIndex]!;\n const name =\n callee !== null && typeof callee === 'object' && 'name' in callee && callee.name\n ? callee.name\n : describeCallee(node);\n\n if (!isCallable(callee)) {\n throw this.runtimeError(\n 'NO_ES_PROCEDIMIENTO',\n `\\`${describeCallee(node)}\\` no es un procedimiento, es ${describeType(typeOf(callee))}.`,\n node.callee,\n );\n }\n\n const args = this.stack.slice(calleeIndex + 1);\n const expected = callee.kind === 'nativo' ? callee.arity : callee.chunk.params.length;\n checkArity(expected, argc, name, (message, hint) =>\n this.runtimeError('CANTIDAD_DE_ARGUMENTOS', message, node, hint),\n );\n\n if (callee.kind === 'nativo') {\n const result = callee.call(args, this.nativeContext(node));\n if (result === WAIT_FOR_INPUT) {\n // Repetimos la llamada cuando llegue la entrada.\n frame.pc -= 1;\n this.operations -= 1;\n this.status = 'esperando_entrada';\n return true;\n }\n this.stack.length = calleeIndex;\n this.stack.push(result);\n return false;\n }\n\n // El primer marco es el programa principal, no una llamada.\n if (this.frames.length - 1 >= this.limits.maxCallDepth) {\n throw this.limitError(\n 'DEMASIADAS_LLAMADAS',\n `Hay más de ${formatCount(this.limits.maxCallDepth)} llamadas anidadas.`,\n 'Si es recursión, revisa que exista un caso base que deje de llamar al procedimiento.',\n node,\n );\n }\n\n const scope = new Scope(callee.closure);\n callee.chunk.params.forEach((param, index) => scope.variables.set(param, args[index]!));\n this.emit({ type: 'llama', name, args: args.map(serialize), nodeId: node.id, span: node.span });\n this.frames.push({\n chunk: callee.chunk,\n pc: 0,\n scope,\n root: scope,\n stackBase: calleeIndex + 1,\n name,\n callNode: node,\n });\n return false;\n }\n\n private nativeContext(node: ast.Call): NativeContext {\n return {\n write: text => {\n this.outputLength += text.length;\n if (this.outputLength > this.limits.maxOutput) {\n throw this.limitError(\n 'DEMASIADA_SALIDA',\n `El programa imprimió más de ${formatCount(this.limits.maxOutput)} caracteres.`,\n '¿Hay un `imprimir` dentro de un bucle que nunca termina?',\n node,\n );\n }\n this.outputParts.push(text);\n this.emit({ type: 'imprime', text });\n },\n readLine: () => {\n if (this.inputQueue.length > 0) {\n const line = this.inputQueue.shift()!;\n this.emit({ type: 'lee', text: line });\n return line;\n }\n if (this.options.interactiveInput && !this.inputClosed) return WAIT_FOR_INPUT;\n this.emit({ type: 'lee', text: null });\n return null;\n },\n newList: items => this.newList(items),\n fail: (code, message, hint) => this.runtimeError(code, message, node, hint),\n };\n }\n\n private newList(items: Value[]): ListValue {\n return { kind: 'lista', id: this.nextId++, items };\n }\n\n // ── operadores ─────────────────────────────────────────────────────────────\n\n private unary(operator: ast.UnaryOperator, operand: Value, node: ast.Unary): Value {\n if (operator === '-') {\n if (typeof operand !== 'number') {\n throw this.runtimeError(\n 'TIPO_INVÁLIDO',\n `El signo \\`-\\` solo funciona con números, no con ${describeType(typeOf(operand))}.`,\n node,\n );\n }\n return -operand;\n }\n if (typeof operand !== 'boolean') {\n throw this.runtimeError(\n 'TIPO_INVÁLIDO',\n `\\`no\\` solo funciona con \\`verdadero\\` o \\`falso\\`, pero recibió ${repr(operand)}.`,\n node.operand,\n conditionHint(operand),\n );\n }\n return !operand;\n }\n\n private binary(operator: ast.BinaryOperator, left: Value, right: Value, node: ast.Binary | ast.Assignment): Value {\n switch (operator) {\n case '==':\n return equals(left, right);\n case '!=':\n return !equals(left, right);\n case '+':\n if (typeof left === 'number' && typeof right === 'number') return left + right;\n if (typeof left === 'string' && typeof right === 'string') return left + right;\n if (typeof left === 'string' || typeof right === 'string') {\n const other = typeof left === 'string' ? right : left;\n throw this.runtimeError(\n 'TIPOS_MEZCLADOS',\n `\\`+\\` no puede unir un texto con ${describeType(typeOf(other))}.`,\n node,\n `Convierte el valor con \\`texto(…)\\`, o imprime los dos por separado: \\`imprimir(\"Total:\", ${repr(other)})\\`.`,\n );\n }\n throw this.operandError(operator, left, right, node);\n case '-':\n case '*':\n case '/': {\n if (typeof left !== 'number' || typeof right !== 'number') throw this.operandError(operator, left, right, node);\n if (operator === '-') return left - right;\n if (operator === '*') return left * right;\n if (right === 0) {\n const divisor = node.kind === 'Binary' ? node.right : node.value;\n throw this.runtimeError(\n 'DIVISIÓN_ENTRE_CERO',\n 'No se puede dividir entre cero.',\n divisor,\n 'Revisa que el divisor no valga 0 antes de dividir.',\n );\n }\n return left / right;\n }\n case '<':\n case '>':\n case '<=':\n case '>=': {\n const comparable =\n (typeof left === 'number' && typeof right === 'number') ||\n (typeof left === 'string' && typeof right === 'string');\n if (!comparable) {\n throw this.runtimeError(\n 'COMPARACIÓN_INVÁLIDA',\n `No se puede comparar ${describeType(typeOf(left))} con ${describeType(typeOf(right))} usando \\`${operator}\\`.`,\n node,\n '`<`, `>`, `<=` y `>=` comparan dos números o dos textos.',\n );\n }\n const a = left as number | string;\n const b = right as number | string;\n if (operator === '<') return a < b;\n if (operator === '>') return a > b;\n if (operator === '<=') return a <= b;\n return a >= b;\n }\n }\n }\n\n private index(object: Value, index: Value, node: ast.Index): Value {\n if (!isList(object) && typeof object !== 'string') {\n throw this.runtimeError(\n 'ÍNDICE_NO_SOPORTADO',\n `Solo se puede usar \\`[ ]\\` con listas y textos, no con ${describeType(typeOf(object))}.`,\n node.object,\n );\n }\n if (typeof index !== 'number' || !Number.isInteger(index)) {\n throw this.runtimeError(\n 'ÍNDICE_INVÁLIDO',\n `El índice tiene que ser un número entero, pero es ${repr(index)}.`,\n node.index,\n typeof index === 'number' ? 'Redondea el índice con `redondear(…)`.' : undefined,\n );\n }\n const length = isList(object) ? object.items.length : object.length;\n const position = index < 0 ? length + index : index;\n if (position < 0 || position >= length) {\n const what = isList(object) ? 'la lista' : 'el texto';\n const unit = isList(object) ? 'elemento' : 'carácter';\n throw this.runtimeError(\n 'ÍNDICE_FUERA_DE_RANGO',\n `El índice ${index} está fuera de ${what}, que tiene ${length} ${length === 1 ? unit : unit === 'carácter' ? 'caracteres' : 'elementos'}.`,\n node.index,\n length === 0\n ? `${what[0]!.toUpperCase()}${what.slice(1)} está vacía.`\n : `Los índices válidos van de 0 a ${length - 1} (o de -1 a -${length} desde el final).`,\n );\n }\n return isList(object) ? object.items[position]! : object[position]!;\n }\n\n // ── errores ────────────────────────────────────────────────────────────────\n\n private callStack(): string[] {\n return this.frames.map(frame => frame.name);\n }\n\n private runtimeError(code: string, message: string, node: { span: Span }, hint?: string): CodexivoError {\n return new CodexivoError({ kind: 'ejecución', code, message, hint, span: node.span, callStack: this.callStack() });\n }\n\n private limitError(code: string, message: string, hint: string, node?: { span: Span }): CodexivoError {\n return new CodexivoError({\n kind: 'límite',\n code,\n message,\n hint,\n span: node?.span ?? this.currentNode?.span,\n callStack: this.callStack(),\n });\n }\n\n private operandError(operator: string, left: Value, right: Value, node: { span: Span }): CodexivoError {\n return this.runtimeError(\n 'TIPO_INVÁLIDO',\n `\\`${operator}\\` no funciona entre ${describeType(typeOf(left))} y ${describeType(typeOf(right))}.`,\n node,\n );\n }\n\n private unknownName(frame: Frame, name: string, node: ast.Identifier): CodexivoError {\n const visible: string[] = [...Object.keys(this.natives)];\n for (let scope: Scope | undefined = frame.scope; scope; scope = scope.parent)\n visible.push(...scope.variables.keys());\n const suggestion = suggestName(name, visible);\n return this.runtimeError(\n 'NOMBRE_DESCONOCIDO',\n `No existe nada llamado \\`${name}\\`.`,\n node,\n suggestion\n ? `¿Quisiste decir \\`${suggestion}\\`?`\n : `Si es una variable nueva, declárala con \\`variable ${name} = …\\`.`,\n );\n }\n\n private undeclaredAssignment(name: string, node: ast.Identifier): CodexivoError {\n if (this.natives[name]) {\n return this.runtimeError(\n 'NOMBRE_RESERVADO_POR_BIBLIOTECA',\n `\\`${name}\\` es un procedimiento de Codexivo; no se le puede asignar un valor.`,\n node,\n `Si quieres tu propia variable con ese nombre, declárala: \\`variable ${name} = …\\`.`,\n );\n }\n return this.runtimeError(\n 'VARIABLE_NO_DECLARADA',\n `La variable \\`${name}\\` no existe.`,\n node,\n `¿Olvidaste declararla? Escribe \\`variable ${name} = …\\` la primera vez.`,\n );\n }\n\n private nonBooleanCondition(label: DecisionLabel, value: Value, node: ast.Expression): CodexivoError {\n return this.runtimeError(\n 'CONDICIÓN_NO_LÓGICA',\n `La condición de \\`${label}\\` tiene que ser \\`verdadero\\` o \\`falso\\`, pero vale ${repr(value)}.`,\n node,\n conditionHint(value),\n );\n }\n\n private nonBooleanLogical(node: ast.Logical, value: Value, side: ast.Expression): CodexivoError {\n return this.runtimeError(\n 'OPERANDO_NO_LÓGICO',\n `\\`${node.operator}\\` une valores \\`verdadero\\` o \\`falso\\`, pero recibió ${repr(value)}.`,\n side,\n conditionHint(value),\n );\n }\n\n private emitOperation(operator: string, operands: Value[], result: Value, node: ast.Node): void {\n this.emit({\n type: 'opera',\n operator,\n operands: operands.map(serialize),\n result: serialize(result),\n nodeId: node.id,\n span: node.span,\n });\n }\n\n private emit(event: DistributiveOmit<MachineEvent, 'operations' | 'depth'>): void {\n if (!this.onEvent) return;\n this.onEvent({ ...event, operations: this.operations, depth: this.frames.length } as MachineEvent);\n }\n}\n\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;\n\nconst splitLines = (text: string): string[] => text.replace(/\\r\\n/g, '\\n').replace(/\\n$/, '').split('\\n');\n\nconst formatCount = (count: number): string => count.toLocaleString('es-MX').replace(/,/g, ' ');\n\nconst describeCallee = (node: ast.Call): string => {\n if (node.callee.kind === 'Identifier') return node.callee.name;\n return 'esto';\n};\n\nconst conditionHint = (value: Value): string | undefined => {\n switch (typeOf(value)) {\n case 'número':\n return `¿Quisiste comparar? Por ejemplo: \\`x != 0\\` o \\`x > 0\\`.`;\n case 'texto':\n return '¿Quisiste comparar? Por ejemplo: `texto != \"\"`.';\n case 'lista':\n return '¿Quisiste decir `longitud(lista) > 0`?';\n case 'nulo':\n return '¿Quisiste decir `x != nulo`?';\n default:\n return undefined;\n }\n};\n\nconst checkArity = (\n expected: number | [number, number],\n received: number,\n name: string,\n fail: (message: string, hint?: string) => CodexivoError,\n): void => {\n const [min, max] = typeof expected === 'number' ? [expected, expected] : expected;\n if (received >= min && received <= max) return;\n const plural = (count: number) => (count === 1 ? '1 valor' : `${count} valores`);\n let wanted: string;\n if (min === max) wanted = plural(min);\n else if (max === Infinity) wanted = `al menos ${plural(min)}`;\n else wanted = `entre ${min} y ${plural(max)}`;\n throw fail(`\\`${name}\\` espera ${wanted}, pero recibió ${received}.`);\n};\n",
14
+ "import type { CodexivoError, ErrorInfo } from './errors';\nimport type * as ast from './syntax/ast';\nimport { parse } from './syntax/parser';\nimport type { Token } from './syntax/token';\nimport { compile } from './runtime/compiler';\nimport { Machine } from './runtime/machine';\nimport type { MachineEvent, MachineOptions, MachineStatus } from './runtime/machine';\nimport { repr, Scope } from './runtime/values';\nimport type { Value } from './runtime/values';\n\nexport interface PreparedProgram {\n program: ast.Program;\n tokens: Token[];\n /** Errores léxicos y sintácticos. Si hay alguno, `machine` no existe. */\n errors: CodexivoError[];\n machine?: Machine;\n}\n\n/** Analiza el código y, si no tiene errores, prepara una máquina lista para ejecutarlo paso a paso. */\nexport const prepare = (source: string, options: MachineOptions = {}): PreparedProgram => {\n const { program, tokens, errors } = parse(source);\n if (errors.length > 0) return { program, tokens, errors };\n return { program, tokens, errors, machine: new Machine(compile(program), options) };\n};\n\nexport interface RunOptions extends Omit<MachineOptions, 'onEvent'> {\n /** Guarda los eventos de la ejecución en `events`. */\n trace?: boolean;\n /** Máximo de eventos a guardar con `trace`. Por defecto 100 000. */\n maxEvents?: number;\n}\n\nexport interface RunResult {\n status: MachineStatus;\n /** `true` si el programa terminó sin errores. */\n ok: boolean;\n /** Valor de la última expresión del programa, o lo que regresó con `regresa`. */\n result: Value;\n /** `result` como se escribiría en código. */\n resultText: string;\n output: string;\n errors: ErrorInfo[];\n operations: number;\n events?: MachineEvent[];\n /** `true` si se guardaron menos eventos de los que ocurrieron. */\n eventsTruncated?: boolean;\n}\n\n/** Ejecuta un programa completo. */\nexport const run = (source: string, options: RunOptions = {}): RunResult => {\n const { trace, maxEvents = 100_000, ...machineOptions } = options;\n const events: MachineEvent[] = [];\n let eventsTruncated = false;\n const onEvent = trace\n ? (event: MachineEvent) => {\n if (events.length < maxEvents) events.push(event);\n else eventsTruncated = true;\n }\n : undefined;\n\n const { errors, machine } = prepare(source, { ...machineOptions, onEvent });\n if (!machine) {\n return {\n status: 'error',\n ok: false,\n result: null,\n resultText: 'nulo',\n output: '',\n errors: errors.map(error => error.toJSON()),\n operations: 0,\n ...(trace ? { events, eventsTruncated } : {}),\n };\n }\n\n machine.run();\n return {\n status: machine.status,\n ok: machine.status === 'terminada',\n result: machine.result,\n resultText: repr(machine.result),\n output: machine.output,\n errors: machine.error ? [machine.error.toJSON()] : [],\n operations: machine.operations,\n ...(trace ? { events, eventsTruncated } : {}),\n };\n};\n\n/** Varias ejecuciones que comparten variables, como en el REPL. */\nexport class Session {\n private readonly globals = new Scope();\n\n constructor(private readonly options: Omit<RunOptions, 'globals' | 'allowRedeclare'> = {}) {}\n\n run(source: string): RunResult {\n return run(source, { ...this.options, globals: this.globals, allowRedeclare: true });\n }\n}\n",
15
+ "import type { Span } from './token';\n\n/**\n * El AST está hecho de objetos planos: se puede serializar, enviar a un Web Worker y dibujar sin\n * perder información. Cada nodo tiene un `id` único dentro de su programa y su rango en el código.\n */\ninterface BaseNode {\n id: number;\n span: Span;\n}\n\nexport type BinaryOperator = '+' | '-' | '*' | '/' | '==' | '!=' | '<' | '>' | '<=' | '>=';\nexport type LogicalOperator = 'y' | 'o';\nexport type UnaryOperator = '-' | 'no';\nexport type AssignmentOperator = '=' | '+=' | '-=' | '*=' | '/=';\n\n// ── expresiones ──────────────────────────────────────────────────────────────\n\nexport interface NumberLiteral extends BaseNode {\n kind: 'NumberLiteral';\n value: number;\n}\n\nexport interface StringLiteral extends BaseNode {\n kind: 'StringLiteral';\n value: string;\n}\n\nexport interface BooleanLiteral extends BaseNode {\n kind: 'BooleanLiteral';\n value: boolean;\n}\n\nexport interface NullLiteral extends BaseNode {\n kind: 'NullLiteral';\n}\n\nexport interface ListLiteral extends BaseNode {\n kind: 'ListLiteral';\n elements: Expression[];\n}\n\nexport interface Identifier extends BaseNode {\n kind: 'Identifier';\n name: string;\n}\n\nexport interface Unary extends BaseNode {\n kind: 'Unary';\n operator: UnaryOperator;\n operand: Expression;\n}\n\nexport interface Binary extends BaseNode {\n kind: 'Binary';\n operator: BinaryOperator;\n left: Expression;\n right: Expression;\n}\n\n/** `y` / `o`: se evalúan en cortocircuito, por eso no son un `Binary`. */\nexport interface Logical extends BaseNode {\n kind: 'Logical';\n operator: LogicalOperator;\n left: Expression;\n right: Expression;\n}\n\nexport interface Call extends BaseNode {\n kind: 'Call';\n callee: Expression;\n args: Expression[];\n}\n\nexport interface Index extends BaseNode {\n kind: 'Index';\n object: Expression;\n index: Expression;\n}\n\nexport interface FunctionLiteral extends BaseNode {\n kind: 'FunctionLiteral';\n /** Nombre con el que se declaró, si se conoce (`variable doble = procedimiento…` → `doble`). */\n name?: string;\n params: Identifier[];\n body: Block;\n}\n\nexport type Expression =\n | NumberLiteral\n | StringLiteral\n | BooleanLiteral\n | NullLiteral\n | ListLiteral\n | Identifier\n | Unary\n | Binary\n | Logical\n | Call\n | Index\n | FunctionLiteral;\n\n// ── sentencias ───────────────────────────────────────────────────────────────\n\nexport interface VariableDeclaration extends BaseNode {\n kind: 'VariableDeclaration';\n name: Identifier;\n value: Expression;\n}\n\nexport interface Assignment extends BaseNode {\n kind: 'Assignment';\n target: Identifier;\n operator: AssignmentOperator;\n value: Expression;\n}\n\nexport interface ExpressionStatement extends BaseNode {\n kind: 'ExpressionStatement';\n expression: Expression;\n}\n\nexport interface Return extends BaseNode {\n kind: 'Return';\n value?: Expression;\n}\n\nexport interface Block extends BaseNode {\n kind: 'Block';\n statements: Statement[];\n}\n\nexport interface IfBranch {\n /** Rango de la rama completa: desde `si` / `pero_si` hasta su `}`. */\n span: Span;\n condition: Expression;\n body: Block;\n}\n\n/** `si … pero_si … si_no`: las ramas se guardan en orden, no anidadas. */\nexport interface If extends BaseNode {\n kind: 'If';\n branches: IfBranch[];\n alternative?: Block;\n}\n\nexport interface While extends BaseNode {\n kind: 'While';\n condition: Expression;\n body: Block;\n}\n\n/** `hacer { … } hasta_que (condición)`: repite el cuerpo hasta que la condición sea verdadera. */\nexport interface DoUntil extends BaseNode {\n kind: 'DoUntil';\n body: Block;\n condition: Expression;\n}\n\nexport interface For extends BaseNode {\n kind: 'For';\n init?: VariableDeclaration | Assignment | ExpressionStatement;\n condition?: Expression;\n update?: Assignment | ExpressionStatement;\n body: Block;\n}\n\nexport type Statement =\n VariableDeclaration | Assignment | ExpressionStatement | Return | Block | If | While | DoUntil | For;\n\nexport interface Program extends BaseNode {\n kind: 'Program';\n statements: Statement[];\n}\n\nexport type Node = Program | Statement | Expression;\n\n/** Hijos directos de un nodo, en el orden en que aparecen en el código. */\nexport const childrenOf = (node: Node): Node[] => {\n switch (node.kind) {\n case 'Program':\n case 'Block':\n return node.statements;\n case 'ListLiteral':\n return node.elements;\n case 'Unary':\n return [node.operand];\n case 'Binary':\n case 'Logical':\n return [node.left, node.right];\n case 'Call':\n return [node.callee, ...node.args];\n case 'Index':\n return [node.object, node.index];\n case 'FunctionLiteral':\n return [...node.params, node.body];\n case 'VariableDeclaration':\n return [node.name, node.value];\n case 'Assignment':\n return [node.target, node.value];\n case 'ExpressionStatement':\n return [node.expression];\n case 'Return':\n return node.value ? [node.value] : [];\n case 'If':\n return [\n ...node.branches.flatMap(branch => [branch.condition, branch.body]),\n ...(node.alternative ? [node.alternative] : []),\n ];\n case 'While':\n return [node.condition, node.body];\n case 'DoUntil':\n return [node.body, node.condition];\n case 'For':\n return [\n ...(node.init ? [node.init] : []),\n ...(node.condition ? [node.condition] : []),\n ...(node.update ? [node.update] : []),\n node.body,\n ];\n default:\n return [];\n }\n};\n\n/** Recorre el árbol en profundidad, del padre a los hijos. */\nexport const walk = (node: Node, visit: (node: Node, parent?: Node) => void, parent?: Node): void => {\n visit(node, parent);\n for (const child of childrenOf(node)) walk(child, visit, node);\n};\n"
16
+ ],
17
+ "mappings": "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",
18
+ "debugId": "3028B7122A20F3F064756E2164756E21",
19
+ "names": []
20
+ }
package/docs/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # Documentación de Codexivo
2
+
3
+ Bienvenido al compendio oficial del lenguaje **Codexivo**. Este directorio recopila guías para dos audiencias principales:
4
+
5
+ 1. **Personas que desean escribir programas en Codexivo** y necesitan una referencia clara de la sintaxis, los tipos y las herramientas disponibles.
6
+ 2. **Personas que quieren entender cómo está construido el intérprete** para poder extenderlo, depurarlo o utilizarlo como recurso educativo.
7
+
8
+ ## Guías disponibles
9
+
10
+ - [Especificación v1 (propuesta en revisión)](./especificacion-v1.md): el diseño objetivo del lenguaje por niveles, con las brechas frente a la implementación actual y las decisiones abiertas.
11
+ - [Especificación del lenguaje](./lenguaje.md): describe la sintaxis, palabras reservadas, tipos de dato y reglas semánticas que definen a Codexivo como si fuera un lenguaje formal.
12
+ - [Arquitectura del intérprete](./arquitectura.md): recorre el código fuente módulo por módulo para explicar cómo se procesa un programa desde el texto hasta su evaluación.
13
+ - [Guía práctica: entender el intérprete desde el código](./guia-aprender-desde-el-codigo.md): actividades guiadas para relacionar cada módulo con ejemplos reales del lenguaje.
14
+ - [Uso del REPL y flujo de trabajo](./repl.md): muestra cómo ejecutar el intérprete, guardar sesiones y aprovechar las herramientas de trazado en tiempo real.
15
+ - [Recetario de programas](./recetario.md): colección de ejemplos completos que ilustran patrones comunes de solución.
16
+
17
+ > ℹ️ Las guías están pensadas para leerse de forma progresiva. Si tu objetivo es aprender el lenguaje, comienza por la especificación y luego pon en práctica los ejemplos del recetario. Si deseas contribuir al código, revisa primero la arquitectura y después ejecuta los escenarios del REPL.
18
+
19
+ ## Convenciones
20
+
21
+ - Todo el contenido está en español y emplea la terminología oficial del proyecto ("procedimiento", "variable", etc.).
22
+ - Los bloques de código usan resaltado de sintaxis cuando aplica para facilitar la lectura comparando con JavaScript.
23
+ - Las referencias al código fuente incluyen rutas y nombres de clases/funciones exactas para que puedas ubicarlas rápidamente.
24
+
25
+ ¡Disfruta explorando Codexivo!
@@ -0,0 +1,92 @@
1
+ # Arquitectura del intérprete
2
+
3
+ Codexivo convierte texto en resultados en cuatro etapas. Cada una vive en su propio módulo y se puede usar por separado; eso es
4
+ lo que permite que el sandbox muestre los tokens, el árbol y la ejecución paso a paso del mismo programa.
5
+
6
+ ```
7
+ código ──tokenize──▶ tokens ──parse──▶ AST ──compile──▶ instrucciones ──Machine──▶ resultado, salida y eventos
8
+ ```
9
+
10
+ ## 1. Tokens y lexer (`src/syntax/token.ts`, `src/syntax/lexer.ts`)
11
+
12
+ `tokenize(código)` recorre el texto carácter por carácter y regresa `{ tokens, errors }`.
13
+
14
+ - Cada `Token` tiene su tipo, el texto exacto (`lexeme`), su valor ya interpretado (para números y textos) y su `span`: línea,
15
+ columna y desplazamiento de inicio y fin.
16
+ - Ignora espacios y comentarios (`//` y `/* */`).
17
+ - Nunca lanza una excepción: un carácter desconocido o un texto sin cerrar se reporta en `errors` y produce un token `ILLEGAL`
18
+ para que el parser pueda seguir.
19
+
20
+ ## 2. AST y parser (`src/syntax/ast.ts`, `src/syntax/parser.ts`)
21
+
22
+ `parse(código)` regresa `{ program, tokens, errors }`.
23
+
24
+ - El AST está hecho de **objetos planos** con un campo `kind`, un `id` único y su `span`. Se puede convertir a JSON o enviar a
25
+ un Web Worker sin perder nada. `walk` y `childrenOf` sirven para recorrerlo.
26
+ - El parser es de tipo Pratt: cada operador tiene una precedencia y el parser decide cómo agrupar comparando precedencias.
27
+ `y`/`o` tienen menos precedencia que las comparaciones, así que `1 < 2 y 5 < 4` se agrupa como `(1 < 2) y (5 < 4)`.
28
+ - `si … pero_si … si_no` se guarda como una lista de ramas en orden, no como `si` anidados.
29
+ - Ante un error, el parser lo registra, salta hasta un punto seguro (un `;`, una `}`, una palabra que empieza sentencia o una
30
+ línea nueva) y sigue. Así reporta varios errores en una pasada sin inventar errores en cascada.
31
+
32
+ ## 3. Compilador (`src/runtime/compiler.ts`)
33
+
34
+ `compile(program)` traduce el AST a una lista de instrucciones para una máquina de pila. Por ejemplo, `x += 1` se convierte en:
35
+
36
+ ```
37
+ step (inicio de la sentencia)
38
+ load x
39
+ const 1
40
+ binary +
41
+ store x
42
+ ```
43
+
44
+ Cada instrucción guarda el nodo del AST que la originó: así la máquina sabe qué parte del código está ejecutando, y los errores
45
+ y eventos apuntan al lugar exacto.
46
+
47
+ Las decisiones (`si`, `mientras`, `hasta_que`, `para`) se compilan como `branch`: sacan la condición de la pila, revisan que
48
+ sea lógica y saltan o no. `y`/`o` usan `shortCircuit` para no evaluar el lado derecho cuando no hace falta.
49
+
50
+ ## 4. Máquina (`src/runtime/machine.ts`)
51
+
52
+ `Machine` ejecuta las instrucciones con su **propia pila de valores y de llamadas**. Por eso:
53
+
54
+ - La recursión no depende de la pila de JavaScript: el límite lo pone la configuración (10 000 llamadas por defecto).
55
+ - Se puede **pausar** en cualquier momento y continuar después:
56
+ - `run(presupuesto)` ejecuta hasta terminar o hasta gastar cierto número de operaciones. Sirve para que un Web Worker ceda el
57
+ control y la página siga respondiendo.
58
+ - `step()` avanza hasta la siguiente sentencia o decisión.
59
+ - Con `breakpoints` (números de línea), `run()` se detiene antes de esas sentencias.
60
+ - Si `leer()` no tiene entrada y `interactiveInput` está activo, la máquina queda `esperando_entrada` hasta
61
+ `provideInput(línea)`.
62
+ - `snapshot()` regresa las llamadas activas con las variables de cada alcance, el contenido de las listas por referencia, el
63
+ nodo actual y la salida.
64
+ - Con `onEvent`, la máquina reporta lo que hace: `paso`, `declara`, `asigna` (con el valor anterior), `decide` (con la rama
65
+ tomada), `opera` (operandos y resultado), `llama`, `regresa`, `imprime`, `lee`, `error` y `fin`.
66
+ - Cuenta **operaciones** de forma determinista: el mismo programa con la misma entrada siempre da el mismo número.
67
+
68
+ Los valores (`src/runtime/values.ts`) son primitivos de JavaScript para números, textos, lógicos y `nulo`, y objetos con `id`
69
+ para listas y procedimientos. Los alcances (`Scope`) forman una cadena: cada bloque crea uno nuevo que apunta al de afuera.
70
+
71
+ ## 5. Biblioteca (`src/runtime/builtins.ts`)
72
+
73
+ Cada procedimiento nativo declara cuántos argumentos acepta y recibe un contexto con lo que necesita de la máquina: escribir
74
+ salida, leer entrada, crear listas y reportar errores ubicados en la llamada. Todo nombre con acento se registra también sin él.
75
+
76
+ ## 6. Errores (`src/errors.ts`)
77
+
78
+ `CodexivoError` tiene `kind` (`léxico`, `sintaxis`, `ejecución` o `límite`), un `code` estable, `message`, `hint`, `span` y la
79
+ pila de llamadas. `formatError` lo convierte en el texto que muestran el REPL y la consola.
80
+
81
+ ## 7. API (`src/engine.ts`, `src/index.ts`)
82
+
83
+ - `run(código, opciones)` ejecuta un programa completo y regresa resultado, salida, errores, operaciones y, con `trace: true`,
84
+ los eventos.
85
+ - `prepare(código, opciones)` analiza el código y regresa una `Machine` lista para ejecutar paso a paso.
86
+ - `Session` comparte variables entre ejecuciones; la usa el REPL.
87
+
88
+ ## Para estudiar el código
89
+
90
+ 1. `bun run repl` y escribe programas pequeños.
91
+ 2. Lee los tests de `tests/` en el orden del pipeline: `lexer`, `parser`, `machine`, `stepping`.
92
+ 3. Usa `tokenize`, `parse` y `run(código, { trace: true })` para ver cada etapa del mismo programa.