""" Clase base híbrida que combina SymPy con funcionalidad especializada """ import sympy from sympy import Basic, Symbol, sympify from typing import Any, Optional, Dict import re from .class_base import ClassBase class SympyClassBase(ClassBase, sympy.Basic): """Para clases que necesitan álgebra completa""" def __new__(cls, *args, **kwargs): # La subclase (ej. Class_IP4) es responsable de pasar los argumentos correctos # que sympy.Basic.__new__ espera. obj = sympy.Basic.__new__(cls, *args, **kwargs) return obj def __init__(self, value, original_str=""): ClassBase.__init__(self, value, original_str) # La inicialización de sympy.Basic se maneja principalmente a través de __new__. # Atributos adicionales de sympy como self.args son establecidos por sympy.Basic # basado en los argumentos pasados a __new__. def _sympystr(self, printer): # Las subclases deben implementar esto para una representación SymPy adecuada. # Por defecto, usa el __str__ de ClassBase, que es str(self.value). # Si self.value es un objeto Sympy, str(self.value) ya dará una buena representación. # Si self.value no es un objeto Sympy pero se desea una forma SymPy específica, # esto debería ser sobreescrito. return str(self.value) def _init_specialized(self): """Override en subclases para inicialización especializada""" pass @property def value(self): """Acceso al valor interno""" return self._value @property def original_str(self): """String original de entrada""" return self._original_str # Propiedades requeridas por SymPy @property def args(self): """Argumentos para SymPy - retorna valor como argumento""" return (sympify(self._value),) @property def func(self): """Función constructora para SymPy""" return self.__class__ def _latex(self, printer): """Representación LaTeX""" return self._sympystr(printer) def __str__(self): """Representación string para display""" return str(self._value) def __repr__(self): """Representación para debugging""" return f"{self.__class__.__name__}('{self._original_str}')" def evalf(self, n=15, **options): """Evaluación numérica si es posible""" if isinstance(self._value, (int, float, complex)): return sympy.Float(self._value) return self def _eval_evalf(self, prec): """Evaluación numérica para SymPy""" if isinstance(self._value, (int, float, complex)): return sympy.Float(self._value, prec) return None def __dec__(self): """Conversión a decimal para compatibilidad""" if isinstance(self._value, (int, float, complex)): return self._value raise TypeError(f"Cannot convert {self.__class__.__name__} to decimal") # Métodos requeridos por SymPy para manipulación algebraica def as_coeff_Mul(self, rational=True): """Descomponer como coeficiente * términos""" if isinstance(self._value, (int, float)): return sympify(self._value), sympify(1) return sympify(1), self def as_coeff_Add(self, rational=True): """Descomponer como coeficiente + términos""" if isinstance(self._value, (int, float)): return sympify(self._value), sympify(0) return sympify(0), self def as_base_exp(self): """Descomponer como base^exponente""" return self, sympify(1) def as_numer_denom(self): """Descomponer como numerador/denominador""" return self, sympify(1) # Propiedades de tipo para SymPy @property def is_number(self): """Indica si es un número""" return isinstance(self._value, (int, float, complex)) @property def is_real(self): """Indica si es real""" return isinstance(self._value, (int, float)) and not isinstance(self._value, complex) @property def is_integer(self): """Indica si es entero""" return isinstance(self._value, int) @property def is_rational(self): """Indica si es racional""" return isinstance(self._value, (int, float)) @property def is_irrational(self): """Indica si es irracional""" return False @property def is_positive(self): """Indica si es positivo""" if isinstance(self._value, (int, float)): return self._value > 0 return None @property def is_negative(self): """Indica si es negativo""" if isinstance(self._value, (int, float)): return self._value < 0 return None @property def is_zero(self): """Indica si es cero""" if isinstance(self._value, (int, float)): return self._value == 0 return None @property def is_finite(self): """Indica si es finito""" return isinstance(self._value, (int, float, complex)) @property def is_infinite(self): """Indica si es infinito""" return False @property def is_commutative(self): """Indica si es conmutativo""" return True # Operaciones aritméticas simples que retornan el valor numérico para integración con SymPy def __add__(self, other): """Suma que integra con SymPy""" if isinstance(other, (int, float, complex)): # Retornar valor numérico para que SymPy maneje la operación return sympify(self._value + other) elif hasattr(other, '_value'): return sympify(self._value + other._value) else: # Dejar que SymPy maneje la operación return sympify(self._value) + sympify(other) def __radd__(self, other): """Suma reversa""" return sympify(other) + sympify(self._value) def __mul__(self, other): """Multiplicación que integra con SymPy""" if isinstance(other, (int, float, complex)): return sympify(self._value * other) elif hasattr(other, '_value'): return sympify(self._value * other._value) else: return sympify(self._value) * sympify(other) def __rmul__(self, other): """Multiplicación reversa""" return sympify(other) * sympify(self._value) def __sub__(self, other): """Resta""" if isinstance(other, (int, float, complex)): return sympify(self._value - other) elif hasattr(other, '_value'): return sympify(self._value - other._value) else: return sympify(self._value) - sympify(other) def __rsub__(self, other): """Resta reversa""" return sympify(other) - sympify(self._value) def __truediv__(self, other): """División""" if isinstance(other, (int, float, complex)): if other == 0: raise ZeroDivisionError("Division by zero") return sympify(self._value / other) elif hasattr(other, '_value'): if other._value == 0: raise ZeroDivisionError("Division by zero") return sympify(self._value / other._value) else: return sympify(self._value) / sympify(other) def __rtruediv__(self, other): """División reversa""" if self._value == 0: raise ZeroDivisionError("Division by zero") return sympify(other) / sympify(self._value) def __pow__(self, other): """Potencia""" if isinstance(other, (int, float, complex)): return sympify(self._value ** other) elif hasattr(other, '_value'): return sympify(self._value ** other._value) else: return sympify(self._value) ** sympify(other) def __rpow__(self, other): """Potencia reversa""" return sympify(other) ** sympify(self._value) @staticmethod def Helper(input_str): """Override en subclases para ayuda contextual""" return None # ========== FUNCIÓN DE TOKENIZACIÓN ACTUALIZADA ========== def preprocess_tokens(expression): """ Función de tokenización que usa el registro dinámico de tipos NUEVA VERSIÓN: Obtiene clases desde custom_types/ automáticamente """ # Intentar obtener las clases desde el registro try: from type_registry import get_registered_base_context context = get_registered_base_context() IntBase = context.get('IntBase') FourBytes = context.get('FourBytes') if not IntBase or not FourBytes: # Fallback: tokenización básica sin clases return _basic_tokenization(expression) except ImportError: # Si el registro no está disponible, usar tokenización básica return _basic_tokenization(expression) # Tokenización completa con clases disponibles return _full_tokenization(expression, IntBase, FourBytes) def _full_tokenization(expression, IntBase, FourBytes): """Tokenización completa usando las clases registradas""" import re def replace_intbase(match): base = match.group(1) value = match.group(2) return f'IntBase("{value}", {base})' def replace_fourbytes(match): pattern = match.group(1) return f'FourBytes("{pattern}")' # PASO 1: IntBase - precedencia MAYOR (patrón más específico) # Patrón: número#valor (ej: 16#FF, 2#1010) expression = re.sub(r'(\d+)#([0-9A-Fa-fx]+)', replace_intbase, expression) # PASO 2: FourBytes - precedencia MENOR (patrón más general) # Patrón: x.x.x.x donde x puede ser número o símbolo # Usar \b para boundary, evitar conflictos con acceso a atributos expression = re.sub(r'\b([a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+)\b', replace_fourbytes, expression) return expression def _basic_tokenization(expression): """Tokenización básica cuando las clases no están disponibles""" # Solo hacer transformaciones básicas sin objetos return expression