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
JavaScript
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