Files
ZapLang/interprete.py
2026-05-07 18:01:06 -06:00

556 lines
24 KiB
Python

"""
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 <archivo.zap>")
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)