""" Intérprete de zaplang usando ANTLR4 (visitor pattern). Incluye funciones IO integradas: imprimir, imprimirln, leer, leerEnt, leerFlot. """ import sys import math from antlr4 import * from antlr4.error.ErrorListener import ErrorListener from zaplangLexer import zaplangLexer from zaplangParser import zaplangParser # ───────────────────────────────────────────── # Señales de control de flujo # ───────────────────────────────────────────── class RetornarSenal(Exception): def __init__(self, valor): self.valor = valor class FinSenal(Exception): pass # break class InterrupSenal(Exception): pass # continue # ───────────────────────────────────────────── # Entorno (scoping) # ───────────────────────────────────────────── class Entorno: def __init__(self, padre=None): self.vars = {} self.padre = padre def definir(self, nombre, valor): self.vars[nombre] = valor def obtener(self, nombre): if nombre in self.vars: return self.vars[nombre] if self.padre: return self.padre.obtener(nombre) raise NameError(f"Variable no definida: '{nombre}'") def asignar(self, nombre, valor): if nombre in self.vars: self.vars[nombre] = valor return if self.padre: self.padre.asignar(nombre, valor) return raise NameError(f"Variable no definida: '{nombre}'") # ───────────────────────────────────────────── # Funciones IO integradas # ───────────────────────────────────────────── def _procesar_cadena(s): """Interpreta secuencias de escape en cadenas.""" return s.encode('raw_unicode_escape').decode('unicode_escape') FUNCIONES_IO = { # imprimir(val, ...) → imprime sin salto de línea "imprimir": lambda args: print(*[_fmt(a) for a in args], end=""), # imprimirln(val, ...) → imprime con salto de línea "imprimirln": lambda args: print(*[_fmt(a) for a in args]), # leer() → lee una línea como cadena "leer": lambda args: input(), # leerEnt() → lee un entero "leerEnt": lambda args: int(input()), # leerFlot() → lee un flotante "leerFlot": lambda args: float(input()), # leerCarac() → lee un carácter "leerCarac": lambda args: input()[0] if input() else '\0', # longitud(s) → len de cadena "longitud": lambda args: len(str(args[0])), } def _fmt(v): if isinstance(v, bool): return "verdadero" if v else "falso" if isinstance(v, float) and v == int(v): return str(int(v)) return str(v) # ───────────────────────────────────────────── # Visitor / Intérprete # ───────────────────────────────────────────── class Interprete: def __init__(self): self.global_env = Entorno() self.funciones = {} # nombre → ctx de declaracionFuncion self._registrar_io() def _registrar_io(self): """Registra las funciones IO como callables nativos.""" for nombre, fn in FUNCIONES_IO.items(): self.funciones[nombre] = fn # callable nativo # ── Entrada principal ────────────────────────────────────── def ejecutar(self, arbol): self.visitar_unidad(arbol, self.global_env) def visitar_unidad(self, ctx, env): for decl in ctx.declaracionExterna(): self.visitar_declaracion_externa(decl, env) # Buscar y ejecutar main if "main" not in self.funciones: raise RuntimeError("No se encontró la función 'main'") self.llamar_funcion("main", [], env) def visitar_declaracion_externa(self, ctx, env): if ctx.declaracionFuncion(): self.registrar_funcion(ctx.declaracionFuncion()) elif ctx.declaracionVariable(): self.visitar_decl_variable(ctx.declaracionVariable(), env) elif ctx.declaracionEstruct(): pass # extensible elif ctx.declaracionEnum(): self.visitar_decl_enum(ctx.declaracionEnum(), env) # ── Funciones ────────────────────────────────────────────── def registrar_funcion(self, ctx): nombre = ctx.identificadorFuncion().Identificador().getText() self.funciones[nombre] = ctx def llamar_funcion(self, nombre, args, env): fn = self.funciones.get(nombre) if fn is None: raise RuntimeError(f"Función no definida: '{nombre}'") # Nativa (IO) if callable(fn) and not hasattr(fn, 'listaParametros'): return fn(args) # Definida en zaplang local = Entorno(self.global_env) params = fn.listaParametros() if params: param_list = params.declaracionParametro() for i, param in enumerate(param_list): pnombre = param.Identificador().getText() valor = args[i] if i < len(args) else None local.definir(pnombre, valor) try: self.visitar_bloque(fn.bloque(), local) except RetornarSenal as r: return r.valor return None # ── Bloque y sentencias ──────────────────────────────────── def visitar_bloque(self, ctx, env): if ctx.LlaveIzq(): for dv in ctx.declaracionVariable(): self.visitar_decl_variable(dv, env) for sent in ctx.sentencia(): self.visitar_sentencia(sent, env) else: self.visitar_sentencia(ctx.sentencia(), env) def visitar_sentencia(self, ctx, env): if ctx.sentenciaCompuesta(): inner = Entorno(env) for s in ctx.sentenciaCompuesta().sentencia(): self.visitar_sentencia(s, inner) elif ctx.sentenciaDeclaracion(): self.visitar_decl_variable(ctx.sentenciaDeclaracion().declaracionVariable(), env) elif ctx.sentenciaExpresion(): se = ctx.sentenciaExpresion() if se.expresion(): self.visitar_expresion(se.expresion(), env) elif ctx.sentenciaSeleccion(): self.visitar_seleccion(ctx.sentenciaSeleccion(), env) elif ctx.sentenciaIteracion(): self.visitar_iteracion(ctx.sentenciaIteracion(), env) elif ctx.sentenciaSalto(): self.visitar_salto(ctx.sentenciaSalto(), env) # ── Declaración de variables ─────────────────────────────── def visitar_decl_variable(self, ctx, env): for init_var in ctx.inicializadorVariable(): nombre = init_var.Identificador().getText() if init_var.inicializadorArreglo(): items = [] la = init_var.inicializadorArreglo().listaInitializers() if la: for ae in la.asignacionExpresion(): items.append(self.visitar_asignacion(ae, env)) env.definir(nombre, items) elif init_var.inicializador(): val = self.visitar_asignacion( init_var.inicializador().asignacionExpresion(), env) env.definir(nombre, val) elif init_var.ConstanteEntera(): tamaño = int(init_var.ConstanteEntera().getText()) env.definir(nombre, [None] * tamaño) else: env.definir(nombre, None) # ── Enum ─────────────────────────────────────────────────── def visitar_decl_enum(self, ctx, env): contador = 0 for en in ctx.listaEnumeradores().enumerador(): nombre = en.Identificador().getText() if en.ConstanteEntera(): contador = int(en.ConstanteEntera().getText()) env.definir(nombre, contador) self.global_env.definir(nombre, contador) contador += 1 # ── Selección ────────────────────────────────────────────── def visitar_seleccion(self, ctx, env): if ctx.Si(): cond = self.visitar_expresion(ctx.expresion(), env) sentencias = ctx.sentencia() if cond: self.visitar_sentencia(sentencias[0], Entorno(env)) elif len(sentencias) > 1: self.visitar_sentencia(sentencias[1], Entorno(env)) elif ctx.Caso(): # switch-like: caso bul { caso X: ... pordef: ... } val_bul = self.visitar_expresion(ctx.expresion(), env) if ctx.expresion() else None ejecutando = False for etiqueta in ctx.casoEtiqueta(): val = self.visitar_constante(etiqueta.constanteExpresion(), env) if val == val_bul or ejecutando: ejecutando = True try: for s in etiqueta.sentencia(): self.visitar_sentencia(s, env) except FinSenal: return if ctx.etiquetaPorDef() and ejecutando is False: try: for s in ctx.etiquetaPorDef().sentencia(): self.visitar_sentencia(s, env) except FinSenal: pass # ── Iteración ────────────────────────────────────────────── def visitar_iteracion(self, ctx, env): if ctx.Mientras(): while self.visitar_expresion(ctx.expresion()[0], env): try: self.visitar_sentencia(ctx.sentencia(), Entorno(env)) except FinSenal: break except InterrupSenal: continue elif ctx.Por(): # Gramática: Por ( sentenciaExpresion? ; expresion? ; expresion? ) sentencia # sentenciaExpresion ya tiene su propio ';', luego viene ';' cond ';' paso loop_env = Entorno(env) # init: sentenciaExpresion opcional (tiene su ';') se = ctx.sentenciaExpresion() if se and se.expresion(): self.visitar_expresion(se.expresion(), loop_env) # cond y paso: expresion(0) y expresion(1) — pueden ser None exprs = ctx.expresion() cond_expr = exprs[0] if len(exprs) > 0 else None paso_expr = exprs[1] if len(exprs) > 1 else None while True: if cond_expr is not None and not self.visitar_expresion(cond_expr, loop_env): break try: self.visitar_sentencia(ctx.sentencia(), Entorno(loop_env)) except FinSenal: break except InterrupSenal: pass if paso_expr: self.visitar_expresion(paso_expr, loop_env) # ── Salto ───────────────────────────────────────────────── def visitar_salto(self, ctx, env): if ctx.Retornar(): val = self.visitar_expresion(ctx.expresion(), env) if ctx.expresion() else None raise RetornarSenal(val) elif ctx.Fin(): raise FinSenal() elif ctx.Interrup(): raise InterrupSenal() # ── Expresiones ──────────────────────────────────────────── def visitar_expresion(self, ctx, env): # expresion: asignacionExpresion | expresion Coma asignacionExpresion # En el árbol: puede haber asignacionExpresion() como hijo único o múltiples if hasattr(ctx, 'asignacionExpresion') and ctx.asignacionExpresion() is not None: ae = ctx.asignacionExpresion() val = self.visitar_asignacion(ae, env) # Si hay expresion hija (caso con coma), procesarla primero if ctx.expresion(): val = self.visitar_expresion(ctx.expresion(), env) val = self.visitar_asignacion(ae, env) return val return None def visitar_asignacion(self, ctx, env): if ctx.expresionCondicional(): return self.visitar_condicional(ctx.expresionCondicional(), env) # Asignación lhs = ctx.expresionUnaria() op = ctx.operadorAsignacion().getText() rhs = self.visitar_asignacion(ctx.asignacionExpresion(), env) nombre, idx = self._resolver_lvalue(lhs, env) if idx is not None: arr = env.obtener(nombre) viejo = arr[idx] arr[idx] = self._aplicar_op_asign(op, viejo, rhs) else: viejo = None try: viejo = env.obtener(nombre) except: pass env.asignar(nombre, self._aplicar_op_asign(op, viejo, rhs)) return rhs def _aplicar_op_asign(self, op, viejo, nuevo): if op == '=': return nuevo if op == '+=': return (viejo or 0) + nuevo if op == '-=': return (viejo or 0) - nuevo if op == '*=': return (viejo or 0) * nuevo if op == '/=': return (viejo or 0) / nuevo if op == '%=': return (viejo or 0) % nuevo if op == '&=': return int(viejo or 0) & int(nuevo) if op == '|=': return int(viejo or 0) | int(nuevo) if op == '^=': return int(viejo or 0) ^ int(nuevo) if op == '<<=': return int(viejo or 0) << int(nuevo) if op == '>>=': return int(viejo or 0) >> int(nuevo) return nuevo def _resolver_lvalue(self, ctx_unaria, env): """Devuelve (nombre, índice_o_None).""" pf = ctx_unaria.expresionPostfija() if pf and pf.expresionPostfija() and pf.CorcheteIzq(): nombre = pf.expresionPostfija().expresionPrimaria().Identificador().getText() idx = int(self.visitar_expresion(pf.expresion(), env)) return nombre, idx elif pf and pf.expresionPrimaria() and pf.expresionPrimaria().Identificador(): return pf.expresionPrimaria().Identificador().getText(), None return None, None def visitar_condicional(self, ctx, env): val = self.visitar_logica_o(ctx.expresionLogicaO(), env) if ctx.Pregunta(): if val: return self.visitar_expresion(ctx.expresion(), env) else: return self.visitar_condicional(ctx.expresionCondicional(), env) return val def visitar_logica_o(self, ctx, env): items = ctx.expresionLogicaY() val = self.visitar_logica_y(items[0], env) for i in range(1, len(items)): if val: return True val = self.visitar_logica_y(items[i], env) if len(items) == 1: return val # no convertir a bool si es expresión simple return bool(val) def visitar_logica_y(self, ctx, env): items = ctx.expresionIgualacion() val = self.visitar_igualacion(items[0], env) for i in range(1, len(items)): if not val: return False val = self.visitar_igualacion(items[i], env) if len(items) == 1: return val return bool(val) def visitar_igualacion(self, ctx, env): val = self.visitar_relacional(ctx.expresionRelacional(0), env) ops = [t.getText() for t in ctx.getChildren() if hasattr(t, 'getText') and t.getText() in ('==', '!=')] for i, op in enumerate(ops): r = self.visitar_relacional(ctx.expresionRelacional(i + 1), env) val = (val == r) if op == '==' else (val != r) return val def visitar_relacional(self, ctx, env): val = self.visitar_desplaz(ctx.expresionDesplazamiento(0), env) ops = [t.getText() for t in ctx.getChildren() if hasattr(t, 'getText') and t.getText() in ('<', '>', '<=', '>=')] for i, op in enumerate(ops): r = self.visitar_desplaz(ctx.expresionDesplazamiento(i + 1), env) if op == '<': val = val < r elif op == '>': val = val > r elif op == '<=': val = val <= r elif op == '>=': val = val >= r return val def visitar_desplaz(self, ctx, env): val = self.visitar_aditiva(ctx.expresionAditiva(0), env) ops = [t.getText() for t in ctx.getChildren() if hasattr(t, 'getText') and t.getText() in ('<<', '>>')] for i, op in enumerate(ops): r = self.visitar_aditiva(ctx.expresionAditiva(i + 1), env) val = (int(val) << int(r)) if op == '<<' else (int(val) >> int(r)) return val def visitar_aditiva(self, ctx, env): val = self.visitar_mult(ctx.expresionMultiplicativa(0), env) ops = [t.getText() for t in ctx.getChildren() if hasattr(t, 'getText') and t.getText() in ('+', '-')] for i, op in enumerate(ops): r = self.visitar_mult(ctx.expresionMultiplicativa(i + 1), env) val = val + r if op == '+' else val - r return val def visitar_mult(self, ctx, env): val = self.visitar_unaria(ctx.expresionUnaria(0), env) ops = [t.getText() for t in ctx.getChildren() if hasattr(t, 'getText') and t.getText() in ('*', '/', '%')] for i, op in enumerate(ops): r = self.visitar_unaria(ctx.expresionUnaria(i + 1), env) if op == '*': val = val * r elif op == '/': val = val // r if isinstance(val, int) and isinstance(r, int) else val / r elif op == '%': val = val % r return val def visitar_unaria(self, ctx, env): if ctx.operadorUnario(): op = ctx.operadorUnario().getText() inner = ctx.expresionUnaria() if op == '-': return -self.visitar_unaria(inner, env) if op == '+': return +self.visitar_unaria(inner, env) if op == '!': return not self.visitar_unaria(inner, env) if op == '~': return ~int(self.visitar_unaria(inner, env)) if op == '++': nombre, idx = self._resolver_lvalue(inner, env) v = self._get_lvalue(nombre, idx, env) + 1 self._set_lvalue(nombre, idx, v, env) return v if op == '--': nombre, idx = self._resolver_lvalue(inner, env) v = self._get_lvalue(nombre, idx, env) - 1 self._set_lvalue(nombre, idx, v, env) return v if op == '&': return self.visitar_unaria(inner, env) # simplificado if op == '*': return self.visitar_unaria(inner, env) # simplificado return self.visitar_postfija(ctx.expresionPostfija(), env) def _get_lvalue(self, nombre, idx, env): v = env.obtener(nombre) return v[idx] if idx is not None else v def _set_lvalue(self, nombre, idx, val, env): if idx is not None: env.obtener(nombre)[idx] = val else: env.asignar(nombre, val) def visitar_postfija(self, ctx, env): # Llamada a función if ctx.expresionPostfija() and ctx.ParenIzq(): nombre_fn = ctx.expresionPostfija().expresionPrimaria().Identificador().getText() args = [] if ctx.listaArgumentos(): for ae in ctx.listaArgumentos().asignacionExpresion(): args.append(self.visitar_asignacion(ae, env)) return self.llamar_funcion(nombre_fn, args, env) # Índice arreglo if ctx.expresionPostfija() and ctx.CorcheteIzq(): arr = self.visitar_postfija(ctx.expresionPostfija(), env) idx = int(self.visitar_expresion(ctx.expresion(), env)) return arr[idx] # Acceso miembro if ctx.expresionPostfija() and ctx.Punto(): obj = self.visitar_postfija(ctx.expresionPostfija(), env) campo = ctx.Identificador().getText() return obj.get(campo) # Postfix ++/-- if ctx.expresionPostfija() and ctx.MasMas(): nombre, idx = self._resolver_lvalue_pf(ctx.expresionPostfija(), env) v = self._get_lvalue(nombre, idx, env) self._set_lvalue(nombre, idx, v + 1, env) return v if ctx.expresionPostfija() and ctx.MenosMenos(): nombre, idx = self._resolver_lvalue_pf(ctx.expresionPostfija(), env) v = self._get_lvalue(nombre, idx, env) self._set_lvalue(nombre, idx, v - 1, env) return v return self.visitar_primaria(ctx.expresionPrimaria(), env) def _resolver_lvalue_pf(self, ctx_pf, env): if ctx_pf.expresionPrimaria() and ctx_pf.expresionPrimaria().Identificador(): return ctx_pf.expresionPrimaria().Identificador().getText(), None return None, None def visitar_primaria(self, ctx, env): if ctx.Identificador(): return env.obtener(ctx.Identificador().getText()) if ctx.constanteExpresion(): return self.visitar_constante(ctx.constanteExpresion(), env) if ctx.CadenaLiteral(): raw = ctx.CadenaLiteral().getText()[1:-1] # quitar comillas return raw.replace('\\n', '\n').replace('\\t', '\t').replace('\\r', '\r') if ctx.expresion(): return self.visitar_expresion(ctx.expresion(), env) return None def visitar_constante(self, ctx, env): if ctx.ConstanteEntera(): txt = ctx.ConstanteEntera().getText() return int(txt, 0) if ctx.ConstanteFlotante(): return float(ctx.ConstanteFlotante().getText()) if ctx.ConstanteCaracteres(): c = ctx.ConstanteCaracteres().getText()[1:-1] return ord(c.encode('raw_unicode_escape').decode('unicode_escape')) if ctx.Verdadero(): return True if ctx.Falso(): return False return None # ───────────────────────────────────────────── # Error listener # ───────────────────────────────────────────── class ErrorZap(ErrorListener): def syntaxError(self, recognizer, offendingSymbol, line, col, msg, e): raise SyntaxError(f"[Línea {line}:{col}] {msg}") # ───────────────────────────────────────────── # Punto de entrada # ───────────────────────────────────────────── def ejecutar_archivo(ruta): with open(ruta, 'r', encoding='utf-8') as f: codigo = f.read() flujo = InputStream(codigo) lexer = zaplangLexer(flujo) lexer.removeErrorListeners() lexer.addErrorListener(ErrorZap()) tokens = CommonTokenStream(lexer) parser = zaplangParser(tokens) parser.removeErrorListeners() parser.addErrorListener(ErrorZap()) arbol = parser.unidadTraduccion() interprete = Interprete() interprete.ejecutar(arbol) if __name__ == "__main__": if len(sys.argv) < 2: print("Uso: python interprete.py ") sys.exit(1) try: ejecutar_archivo(sys.argv[1]) except SyntaxError as e: print(f"Error de sintaxis: {e}", file=sys.stderr) sys.exit(1) except Exception as e: print(f"Error: {e}", file=sys.stderr) sys.exit(1)