Mejora del motor de evaluación con la adición de funciones de plotting y compatibilidad con tipos SymPy en las clases IP4 y FourBytes. Se actualiza el contexto base para incluir nuevas funciones matemáticas y se implementa la verificación de clases requeridas en el contexto. Se optimiza el manejo de errores y se mejora la conversión de tipos SymPy a nativos.

This commit is contained in:
Miguel 2025-06-06 09:09:39 +02:00
parent 7d2033c60e
commit 556c63ad31
3 changed files with 83 additions and 4 deletions

View File

@ -24,6 +24,7 @@ class FourBytes(ClassBase):
# Modo algebraico: mantener símbolos INTERNAMENTE, no convertir a SymPy aún
self._symbols = self._extract_symbols()
self._numeric_value = None # No hay valor numérico directo
self._symbolic_value = self.to_sympy() # AÑADIDO: compatibilidad con IP4
super().__init__(self.original, self.original) # ClassBase, NO SympyClassBase
else:
# Modo numérico: validar rangos y convertir
@ -43,6 +44,7 @@ class FourBytes(ClassBase):
self._numeric_elements[2] << 8 |
self._numeric_elements[3])
self._symbols = []
self._symbolic_value = self._numeric_value # AÑADIDO: compatibilidad con IP4
super().__init__(self._numeric_value, self.original) # ClassBase
def _extract_symbols(self):

View File

@ -59,6 +59,14 @@ class IP4Mask(ClassBase):
def _parse_mask(self, mask_input: Union[int, str]) -> int:
"""Helper to parse mask_input and return prefix."""
prefix_val: int
# Manejar tipos SymPy (convirtiendo a int nativo)
if hasattr(mask_input, '__int__'): # SymPy Integer, etc.
try:
mask_input = int(mask_input)
except (ValueError, TypeError):
pass
if isinstance(mask_input, int):
if not (0 <= mask_input <= 32):
raise ValueError(f"Invalid prefix length: {mask_input}. Must be between 0 and 32.")
@ -361,7 +369,16 @@ class Class_IP4(SympyClassBase):
# Obtener clases dinámicamente
IntBase, FourBytes = get_base_classes()
# Procesar dirección
# Procesar dirección (con compatibilidad SymPy)
# Convertir objetos SymPy de vuelta a strings si es necesario
if hasattr(address, '__str__') and not isinstance(address, (str, int, float)):
# Probablemente es un objeto SymPy - convertir a string
address_str = str(address)
if '.' in address_str and len(address_str.split('.')) == 4:
# Parece una IP, usar el string
address = address_str
if FourBytes and isinstance(address, FourBytes):
# address es FourBytes (ya tokenizado desde x.x.x.x)
self.address = address
@ -407,12 +424,39 @@ class Class_IP4(SympyClassBase):
else:
self._ip_int = self.address._numeric_value
self._has_symbols = False
elif hasattr(address, '__int__'): # SymPy Integer u otros números
# Convertir número a IP string (asumiendo formato de 32 bits)
try:
addr_int = int(address)
self._ip_str = f"{(addr_int >> 24) & 0xFF}.{(addr_int >> 16) & 0xFF}.{(addr_int >> 8) & 0xFF}.{addr_int & 0xFF}"
if FourBytes:
self.address = FourBytes(self._ip_str)
else:
self.address = type('MockFourBytes', (), {
'original': self._ip_str,
'has_symbols': False,
'_numeric_value': addr_int
})()
self._ip_int = addr_int
self._has_symbols = False
except (ValueError, TypeError):
raise TypeError(f"No se pudo convertir {address} (tipo: {type(address)}) a dirección IP")
else:
raise TypeError(f"address debe ser FourBytes o string, recibido: {type(address)}")
raise TypeError(f"address debe ser FourBytes, string, o número, recibido: {type(address)}")
# Procesar máscara
if mask is not None:
# Convertir tipos SymPy a tipos nativos
if hasattr(mask, '__int__'): # SymPy Integer, etc.
try:
mask = int(mask)
except (ValueError, TypeError):
pass
if isinstance(mask, IP4Mask):
self._mask_obj = mask
elif isinstance(mask, (int, str)):
@ -424,7 +468,7 @@ class Class_IP4(SympyClassBase):
# Máscara desde hex (ej: 16#ffffff00)
self._mask_obj = IP4Mask(mask)
else:
raise TypeError(f"mask debe ser int, str, IP4Mask, FourBytes, o IntBase")
raise TypeError(f"mask debe ser int, str, IP4Mask, FourBytes, o IntBase, recibido: {type(mask)}")
else:
self._mask_obj = None

View File

@ -61,9 +61,11 @@ class PureAlgebraicEngine:
def _load_base_context(self):
"""Carga el contexto base con funciones y tipos"""
try:
# Contexto de SymPy
# Contexto de SymPy básico
self.context.update({
'sin': sp.sin, 'cos': sp.cos, 'tan': sp.tan,
'asin': sp.asin, 'acos': sp.acos, 'atan': sp.atan,
'sinh': sp.sinh, 'cosh': sp.cosh, 'tanh': sp.tanh,
'log': sp.log, 'ln': sp.ln, 'exp': sp.exp,
'sqrt': sp.sqrt, 'abs': sp.Abs,
'pi': sp.pi, 'e': sp.E, 'I': sp.I,
@ -73,12 +75,43 @@ class PureAlgebraicEngine:
'expand': sp.expand, 'factor': sp.factor,
'diff': sp.diff, 'integrate': sp.integrate,
'Matrix': sp.Matrix, 'symbols': sp.symbols,
'Symbol': sp.Symbol, 'Rational': sp.Rational,
'Float': sp.Float, 'Integer': sp.Integer,
'limit': sp.limit, 'series': sp.series,
'summation': sp.summation, 'product': sp.product,
'binomial': sp.binomial, 'factorial': sp.factorial,
'gcd': sp.gcd, 'lcm': sp.lcm,
'ceiling': sp.ceiling, 'floor': sp.floor,
'Piecewise': sp.Piecewise,
})
# Funciones de plotting
try:
from sympy.plotting import plot, plot3d, plot_parametric, plot3d_parametric_line
self.context.update({
'plot': plot,
'plot3d': plot3d,
'plot_parametric': plot_parametric,
'plot3d_parametric_line': plot3d_parametric_line,
})
self.logger.debug("Funciones de plotting cargadas")
except Exception as e:
self.logger.warning(f"Error cargando funciones de plotting: {e}")
# Contexto dinámico de tipos personalizados
dynamic_context = get_registered_base_context()
self.context.update(dynamic_context)
# Verificar que las clases principales estén disponibles
required_classes = ['IP4', 'IP4Mask', 'FourBytes', 'IntBase', 'Hex', 'Bin', 'Dec', 'Chr', 'LaTeX']
missing_classes = []
for cls_name in required_classes:
if cls_name not in self.context:
missing_classes.append(cls_name)
if missing_classes:
self.logger.warning(f"Clases faltantes en contexto: {missing_classes}")
self.logger.debug(f"Contexto cargado: {len(self.context)} elementos")
except Exception as e: