UNPKG

subcodex

Version:

Lenguaje de programación en español simple, educativo y brutal: SubCodeX 0.0.4 versión estable

583 lines 23.4 kB
import { SubLexer } from "./lexer.js"; import { parserInstance } from "./parser.js"; import * as fs from 'fs'; import * as path from 'path'; export class SubCodeXInterpreter { variables; funciones; output; modoDiosActivado; constructor() { this.variables = new Map(); this.funciones = new Map(); this.output = []; this.modoDiosActivado = false; } ejecutarArchivo(rutaArchivo) { try { if (!fs.existsSync(rutaArchivo)) { throw new Error(`❌ Archivo no encontrado: ${rutaArchivo}`); } const extension = path.extname(rutaArchivo); if (extension !== '.subx') { throw new Error(`❌ Formato de archivo no válido. Use archivos .subx`); } const codigo = fs.readFileSync(rutaArchivo, 'utf-8'); console.log(`📁 Leyendo archivo: ${rutaArchivo}`); console.log(`📝 Contenido del archivo:\n${codigo}\n`); return this.ejecutar(codigo); } catch (error) { return { success: false, output: this.output, error: error instanceof Error ? error.message : 'Error desconocido' }; } } ejecutar(codigo) { try { console.log("🔄 Iniciando tokenización..."); const lexingResult = SubLexer.tokenize(codigo); if (lexingResult.errors.length > 0) { const errorMsg = `Error de léxico: ${lexingResult.errors[0].message}`; console.error(`❌ ${errorMsg}`); throw new Error(errorMsg); } console.log(`✅ Tokenización exitosa - ${lexingResult.tokens.length} tokens encontrados`); console.log("🔄 Iniciando parseo..."); parserInstance.input = lexingResult.tokens; const cst = parserInstance.programa(); if (parserInstance.errors.length > 0) { const errorMsg = `Error de sintaxis: ${parserInstance.errors[0].message}`; console.error(`❌ ${errorMsg}`); throw new Error(errorMsg); } console.log("✅ Parseo exitoso - AST generado"); console.log("\n🚀 Ejecutando código SubCodeX:\n"); this.visitarPrograma(cst); return { success: true, output: this.output, variables: this.convertirVariables() }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Error desconocido', output: this.output }; } } convertirVariables() { const resultado = {}; for (const [nombre, variable] of this.variables) { resultado[nombre] = variable.valor; } return resultado; } visitarPrograma(cst) { if (cst.children && cst.children.instruccion) { for (const instruccion of cst.children.instruccion) { try { this.visitarInstruccion(instruccion); } catch (error) { console.error(`❌ Error ejecutando instrucción: ${error}`); throw error; } } } } visitarInstruccion(cst) { if (!cst.children) return null; const children = cst.children; try { if (children.declararVariable) { return this.visitarDeclararVariable(children.declararVariable[0]); } else if (children.decir) { return this.visitarDecir(children.decir[0]); } else if (children.esperar) { return this.visitarEsperar(children.esperar[0]); } else if (children.condicionalSi) { return this.visitarCondicionalSi(children.condicionalSi[0]); } else if (children.repetir) { return this.visitarRepetir(children.repetir[0]); } else if (children.operacionMatematica) { return this.visitarOperacionMatematica(children.operacionMatematica[0]); } else if (children.funcionTexto) { return this.visitarFuncionTexto(children.funcionTexto[0]); } else if (children.entradaUsuario) { return this.visitarEntradaUsuario(children.entradaUsuario[0]); } else if (children.lista) { return this.visitarLista(children.lista[0]); } else if (children.funcion) { return this.visitarFuncion(children.funcion[0]); } else if (children.llamarFuncion) { return this.visitarLlamarFuncion(children.llamarFuncion[0]); } else if (children.modoDios) { return this.visitarModoDios(children.modoDios[0]); } } catch (error) { console.error(`❌ Error en instrucción: ${error}`); throw error; } return null; } visitarDeclararVariable(cst) { if (!cst.children) return; const children = cst.children; if (!children.Identificador || children.Identificador.length === 0) { throw new Error("Error: Nombre de variable no especificado"); } const nombre = children.Identificador[0].image; const esConstante = !!(children.Constante && children.Constante.length > 0); const existente = this.variables.get(nombre); if (existente && existente.esConstante) { throw new Error(`Error: No se puede redeclarar la constante '${nombre}'`); } let valor = null; if (children.Cadena && children.Cadena.length > 0) { valor = children.Cadena[0].image.slice(1, -1); } else if (children.Numero && children.Numero.length > 0) { valor = parseInt(children.Numero[0].image); } else if (children.Identificador.length > 1) { const varRef = this.variables.get(children.Identificador[1].image); if (varRef) { valor = varRef.valor; } else { throw new Error(`Error: Variable '${children.Identificador[1].image}' no definida`); } } this.variables.set(nombre, { valor, esConstante }); console.log(`📝 Variable ${esConstante ? 'constante' : ''} '${nombre}' = ${valor}`); } visitarDecir(cst) { if (!cst.children) return; const children = cst.children; let mensaje = ""; if (children.Cadena && children.Cadena[0]) { mensaje += children.Cadena[0].image.slice(1, -1); } else if (children.Identificador && children.Identificador[0]) { const variable = this.variables.get(children.Identificador[0].image); if (variable) { mensaje += String(variable.valor); } else { throw new Error(`Error: Variable '${children.Identificador[0].image}' no definida`); } } if (children.Mas && children.Mas.length > 0) { const rhs = (children.Cadena?.[1] || children.Identificador?.[1]); if (rhs) { if (rhs.tokenType.name === 'Cadena') { mensaje += rhs.image.slice(1, -1); } else { const nombreVar = rhs.image; const variable = this.variables.get(nombreVar); if (variable) { mensaje += String(variable.valor); } else { throw new Error(`Error: Variable '${nombreVar}' no definida`); } } } } console.log(mensaje); this.output.push(mensaje); } visitarEsperar(cst) { if (!cst.children || !cst.children.Numero) return; const children = cst.children; const segundos = parseInt(children.Numero[0].image); if (isNaN(segundos) || segundos < 0) { throw new Error("Error: Tiempo de espera inválido"); } const mensaje = `⏱️ Esperando ${segundos} segundo(s)...`; console.log(mensaje); this.output.push(mensaje); const start = Date.now(); while (Date.now() - start < segundos * 1000) { } const mensajeCompleto = `✅ Espera de ${segundos} segundo(s) completada`; console.log(mensajeCompleto); this.output.push(mensajeCompleto); } visitarCondicionalSi(cst) { if (!cst.children || !cst.children.Identificador) return; const children = cst.children; const variable = children.Identificador[0].image; let operador = '=='; if (children.Igual) operador = '=='; else if (children.Menor) operador = '<'; else if (children.Mayor) operador = '>'; let valorComparacion; if (children.Numero && children.Numero.length > 0) { valorComparacion = parseInt(children.Numero[0].image); } else if (children.Cadena && children.Cadena.length > 0) { valorComparacion = children.Cadena[0].image.slice(1, -1); } else if (children.Identificador.length > 1) { const var2 = this.variables.get(children.Identificador[1].image); if (var2) { valorComparacion = var2.valor; } else { throw new Error(`Error: Variable '${children.Identificador[1].image}' no definida`); } } const var1 = this.variables.get(variable); if (!var1) { throw new Error(`Error: Variable '${variable}' no definida`); } const valor1 = var1.valor; let condicion = false; switch (operador) { case '==': condicion = valor1 == valorComparacion; break; case '<': condicion = Number(valor1) < Number(valorComparacion); break; case '>': condicion = Number(valor1) > Number(valorComparacion); break; } console.log(`🔍 Evaluando: ${valor1} ${operador} ${valorComparacion} = ${condicion}`); if (condicion) { console.log("✅ Condición verdadera - ejecutando bloque 'entonces'"); if (children.instruccion) { for (const instruccion of children.instruccion) { this.visitarInstruccion(instruccion); } } } else if (children.Sino) { console.log("❌ Condición falsa - ejecutando bloque 'sino'"); if (children.instruccion2) { for (const instruccion of children.instruccion2) { this.visitarInstruccion(instruccion); } } } else { console.log("❌ Condición falsa - no hay bloque 'sino'"); } } visitarRepetir(cst) { if (!cst.children || !cst.children.Numero) return; const children = cst.children; const veces = parseInt(children.Numero[0].image); if (isNaN(veces) || veces < 0) { throw new Error("Error: Número de repeticiones inválido"); } console.log(`🔄 Iniciando bucle: ${veces} repeticiones`); for (let i = 0; i < veces; i++) { console.log(`🔄 Iteración ${i + 1}/${veces}`); if (children.instruccion) { for (const instruccion of children.instruccion) { this.visitarInstruccion(instruccion); } } } console.log(`✅ Bucle completado`); } visitarOperacionMatematica(cst) { if (!cst.children || !cst.children.Identificador) return; const children = cst.children; let operacion = ''; if (children.Sumar) operacion = 'sumar'; else if (children.Restar) operacion = 'restar'; else if (children.Multiplicar) operacion = 'multiplicar'; else if (children.Dividir) operacion = 'dividir'; const variable = children.Identificador[0].image; let valor1 = 0, valor2 = 0; const val1Token = (children.Numero?.[0] || children.Identificador?.[1]); const val2Token = (children.Numero?.[1] || children.Identificador?.[2]); if (val1Token.tokenType.name === 'Numero') { valor1 = parseInt(val1Token.image); } else { const var1 = this.variables.get(val1Token.image); if (!var1) throw new Error(`Variable ${val1Token.image} no definida`); valor1 = Number(var1.valor); } if (val2Token.tokenType.name === 'Numero') { valor2 = parseInt(val2Token.image); } else { const var2 = this.variables.get(val2Token.image); if (!var2) throw new Error(`Variable ${val2Token.image} no definida`); valor2 = Number(var2.valor); } let resultado = 0; switch (operacion) { case 'sumar': resultado = valor1 + valor2; break; case 'restar': resultado = valor1 - valor2; break; case 'multiplicar': resultado = valor1 * valor2; break; case 'dividir': if (valor2 === 0) throw new Error("División por cero"); resultado = valor1 / valor2; break; } console.log(`🧮 ${operacion}: ${valor1} ... ${valor2} = ${resultado}`); this.variables.set(variable, { valor: resultado, esConstante: false }); } visitarFuncionTexto(cst) { if (!cst.children || !cst.children.Identificador) return; const children = cst.children; let funcion = ''; if (children.Mayusculas) funcion = 'mayusculas'; else if (children.Minusculas) funcion = 'minusculas'; else if (children.Concatenar) funcion = 'concatenar'; else if (children.Longitud) funcion = 'longitud'; const variable = children.Identificador[0].image; const var1 = this.variables.get(variable); if (!var1) throw new Error(`Error: Variable '${variable}' no definida`); let texto = String(var1.valor); let resultado; switch (funcion) { case 'mayusculas': resultado = texto.toUpperCase(); break; case 'minusculas': resultado = texto.toLowerCase(); break; case 'concatenar': if (children.Identificador.length > 1) { const var2 = this.variables.get(children.Identificador[1].image); if (var2) resultado = texto + String(var2.valor); else throw new Error(`Error: Variable '${children.Identificador[1].image}' no definida`); } else { resultado = texto; } break; case 'longitud': resultado = texto.length; break; } console.log(`📝 ${funcion}("${texto}") = ${resultado}`); this.variables.set(variable, { valor: resultado, esConstante: false }); } visitarEntradaUsuario(cst) { if (!cst.children || !cst.children.Cadena || !cst.children.Identificador) return; const children = cst.children; const pregunta = children.Cadena[0].image.slice(1, -1); const variable = children.Identificador[0].image; console.log(`❓ ${pregunta}`); this.output.push(`❓ ${pregunta}`); const respuesta = "respuesta_simulada"; console.log(`👤 Respuesta: ${respuesta}`); this.variables.set(variable, { valor: respuesta, esConstante: false }); } visitarModoDios(_cst) { this.modoDiosActivado = !this.modoDiosActivado; const mensaje = this.modoDiosActivado ? "🔥 MODO DIOS ACTIVADO - Poderes ilimitados desbloqueados" : "✨ Modo Dios desactivado - Volviendo a la normalidad"; console.log(mensaje); this.output.push(mensaje); if (this.modoDiosActivado) { this.variables.set("poder", { valor: 9999, esConstante: false }); this.variables.set("invencible", { valor: true, esConstante: false }); console.log("⚡ Variables especiales agregadas: poder = 9999, invencible = true"); } } visitarLista(cst) { if (!cst.children || !cst.children.Identificador) return; const children = cst.children; let operacion = ''; if (children.ListaNueva) operacion = 'nueva'; else if (children.ListaAgregar) operacion = 'agregar'; else if (children.ListaEliminar) operacion = 'eliminar'; const variable = children.Identificador[0].image; switch (operacion) { case 'nueva': this.variables.set(variable, { valor: [], esConstante: false }); console.log(`📋 Lista '${variable}' creada`); break; case 'agregar': const lista = this.variables.get(variable); if (!lista) throw new Error(`Error: Lista '${variable}' no definida`); if (!Array.isArray(lista.valor)) throw new Error(`Error: '${variable}' no es una lista`); let valor = null; if (children.Numero?.[0]) valor = parseInt(children.Numero[0].image); else if (children.Cadena?.[0]) valor = children.Cadena[0].image.slice(1, -1); else if (children.Identificador?.[1]) { const var2 = this.variables.get(children.Identificador[1].image); if (var2) valor = var2.valor; else throw new Error(`Error: Variable '${children.Identificador[1].image}' no definida`); } lista.valor.push(valor); console.log(`📋 Agregado '${valor}' a lista '${variable}'`); break; case 'eliminar': const lista2 = this.variables.get(variable); if (!lista2) throw new Error(`Error: Lista '${variable}' no definida`); if (!Array.isArray(lista2.valor)) throw new Error(`Error: '${variable}' no es una lista`); if (children.Numero && children.Numero.length > 0) { const indice = parseInt(children.Numero[0].image); if (indice < 0 || indice >= lista2.valor.length) throw new Error(`Error: Índice ${indice} fuera de rango`); const eliminado = lista2.valor.splice(indice, 1)[0]; console.log(`📋 Eliminado '${eliminado}' de lista '${variable}' en índice ${indice}`); } else { const eliminado = lista2.valor.pop(); console.log(`📋 Eliminado '${eliminado}' de lista '${variable}'`); } break; } } visitarFuncion(cst) { if (!cst.children || !cst.children.Identificador) return; const children = cst.children; const nombre = children.Identificador[0].image; let retorno = null; if (children.Numero?.[0]) retorno = parseInt(children.Numero[0].image); else if (children.Cadena?.[0]) retorno = children.Cadena[0].image.slice(1, -1); else if (children.Identificador?.[1]) { const varRetorno = this.variables.get(children.Identificador[1].image); if (varRetorno) retorno = varRetorno.valor; } this.funciones.set(nombre, { instrucciones: children.instruccion || [], retorno: retorno }); console.log(`⚙️ Función '${nombre}' definida`); } visitarLlamarFuncion(cst) { if (!cst.children || !cst.children.Identificador) return null; const children = cst.children; const nombre = children.Identificador[0].image; const funcion = this.funciones.get(nombre); if (!funcion) throw new Error(`Error: Función '${nombre}' no definida`); console.log(`🔧 Ejecutando función '${nombre}'`); for (const instruccion of funcion.instrucciones) { this.visitarInstruccion(instruccion); } console.log(`↩️ Función '${nombre}' completada`); return funcion.retorno; } limpiarOutput() { this.output = []; } reiniciar() { this.variables.clear(); this.funciones.clear(); this.output = []; this.modoDiosActivado = false; } validarArchivo(rutaArchivo) { try { if (!fs.existsSync(rutaArchivo)) { throw new Error(`❌ Archivo no encontrado: ${rutaArchivo}`); } const extension = path.extname(rutaArchivo); if (extension !== '.subx') { throw new Error(`❌ Formato de archivo no válido. Use archivos .subx`); } const codigo = fs.readFileSync(rutaArchivo, 'utf-8'); const lexingResult = SubLexer.tokenize(codigo); if (lexingResult.errors.length > 0) { const errorMsg = `Error de léxico: ${lexingResult.errors[0].message}`; throw new Error(errorMsg); } parserInstance.input = lexingResult.tokens; parserInstance.programa(); if (parserInstance.errors.length > 0) { const errorMsg = `Error de sintaxis: ${parserInstance.errors[0].message}`; throw new Error(errorMsg); } return { success: true, output: [`✅ Archivo válido - Sin errores de sintaxis`] }; } catch (error) { return { success: false, output: [], error: error instanceof Error ? error.message : 'Error desconocido' }; } } mostrarEstadoFinal() { console.log("\n📊 Estado final del programa:"); console.log("Variables:", this.convertirVariables()); console.log("Funciones definidas:", Array.from(this.funciones.keys())); console.log("Modo Dios:", this.modoDiosActivado ? "Activado" : "Desactivado"); } } //# sourceMappingURL=interpreter.js.map