# Refactorización: Sistema de Tokenización Algebraica ## Resumen Ejecutivo Reemplazar el sistema actual de corchetes por un tokenizador que convierte automáticamente patrones específicos en objetos tipados con capacidades algebraicas completas. ## Problemas del Sistema Actual ### Sistema de Corchetes - ❌ Complejidad innecesaria del `BracketParser` - ❌ Conflictos con `eval()` y SymPy - ❌ Sintaxis no estándar - ❌ Beneficios limitados (solo IPs realmente) - ❌ Dificulta la integración algebraica ### Impacto - Código complejo para casos simples - Curva de aprendizaje innecesaria - Mantenimiento difícil - Extensibilidad limitada ## Nuevo Sistema: Tokenización Algebraica ### Filosofía Central **El usuario escribe código Python normal. El parser mejora automáticamente los tipos de datos.** ### Patrones de Tokenización #### 1. **IntBase**: `base#valor` ```python # Input usuario # Post-tokenización 16#FF → IntBase('FF', 16) 2#1010 → IntBase('1010', 2) 8#777 → IntBase('777', 8) 16#x0 → IntBase('x0', 16) # ← ALGEBRAICO 2#101x → IntBase('101x', 2) # ← ALGEBRAICO ``` #### 2. **FourBytes**: `x.x.x.x` ```python # Input usuario # Post-tokenización 192.168.1.1 → FourBytes('192.168.1.1') 255.255.0.0 → FourBytes('255.255.0.0') 10.1.x.2 → FourBytes('10.1.x.2') # ← ALGEBRAICO a.b.c.d → FourBytes('a.b.c.d') # ← ALGEBRAICO ``` ### Precedencia de Tokenización ```python def preprocess_tokens(expression): # 1. MAYOR PRECEDENCIA: IntBase (patrón más específico) expression = re.sub(r'(\d+)#([0-9A-Fa-fx]+)', r'IntBase("\2", \1)', expression) # 2. MENOR PRECEDENCIA: FourBytes (patrón más general) expression = re.sub(r'\b([a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+)\b', r'FourBytes("\1")', expression) return expression ``` ## Problema Arquitectónico Crítico: Conversión Prematura a SymPy ### Diagnóstico del Problema Actual #### **Resultado Problemático Observado:** ```python mm = 255.240.0.x # → 255.240.0.x[Sympy] ❌ IP4(mm, 24) # → Error: MutableDenseMatrix + int ❌ j.next_ip() # → Error: unsupported operand ❌ ``` #### **Causa Raíz:** - **Conversión prematura**: FourBytes se convierte inmediatamente a SymPy Matrix - **Pérdida de control**: SymPy toma control antes de que el objeto pueda manejar sus operaciones - **Incompatibilidad operacional**: Matrices de SymPy no entienden aritmética de direcciones IP ### Arquitectura Correcta: Objetos Nativos con Conversión Perezosa #### **Principio Fundamental:** > **"Cada objeto debe mantenerse en su forma nativa hasta que se involucre en una ecuación algebraica compleja"** #### **Jerarquía Revisada:** ```python ClassBase (SIN SymPy - manejo interno de símbolos) ├── FourBytes (símbolos internos, aritmética nativa) ├── IntBase (símbolos internos, aritmética nativa) ├── IP4, Hex, Dec, etc. (usan objetos nativos) └── IP4Mask (nativo) SympyClassBase (solo cuando sea necesario) ├── Para ecuaciones algebraicas complejas ├── Para integración/derivación └── Para sistemas de ecuaciones ``` #### **Comportamiento Deseado:** ```python mm = 255.240.0.x # → FourBytes('255.240.0.x') [FourBytes] IP4(mm, 24) # → IP4 con FourBytes interno [IP4] j.substitute(x=1) # → Lógica interna de FourBytes [IP4] j.next_ip() # → Aritmética interna nativa [IP4] hh = 16#ff # → IntBase('ff', 16) [IntBase] Dec(hh) # → Dec con IntBase interno [Dec] ``` ## Clases Base Universales (Arquitectura Corregida) ### IntBase: Universal para Bases Numéricas (ClassBase) ```python class IntBase(ClassBase): # ← CAMBIO: ClassBase, NO SympyClassBase """ Representación universal de números en cualquier base con manejo interno de símbolos y conversión perezosa a SymPy """ def __init__(self, value_str, base=10): self.value_str = value_str self.base = base self.has_symbols = bool(re.search(r'[a-zA-Z_]', value_str)) if self.has_symbols: # Modo algebraico: mantener símbolos INTERNAMENTE self._symbols = self._extract_symbols() self._expression = self._build_internal_expression() super().__init__(self._expression, f"{base}#{value_str}") else: # Modo numérico: convertir a entero self._numeric_value = int(value_str, base) super().__init__(self._numeric_value, f"{base}#{value_str}") def _extract_symbols(self): """Extrae símbolos sin convertir a SymPy aún""" return re.findall(r'[a-zA-Z_][a-zA-Z0-9_]*', self.value_str) def _build_internal_expression(self): """Construye expresión interna SIN SymPy""" # Mantener como string procesable internamente # Solo convertir a SymPy cuando se llame to_sympy() return f"base{self.base}_expression({self.value_str})" def substitute(self, **kwargs): """Sustitución interna sin involucrar SymPy""" if not self.has_symbols: return self new_value_str = self.value_str for symbol, value in kwargs.items(): new_value_str = new_value_str.replace(symbol, str(value)) return IntBase(new_value_str, self.base) def to_sympy(self): """Conversión EXPLÍCITA a SymPy cuando se necesite álgebra""" if self.has_symbols: # AHORA sí convertir a expresión SymPy result = 0 digits = list(self.value_str) for i, digit in enumerate(reversed(digits)): power = self.base ** i if digit.isdigit(): coefficient = int(digit) elif digit in 'ABCDEF': coefficient = ord(digit) - ord('A') + 10 elif digit in 'abcdef': coefficient = ord(digit) - ord('a') + 10 else: # Es un símbolo coefficient = sympy.Symbol(digit) result += coefficient * power return result else: return sympy.sympify(self._numeric_value) # ========== OPERADORES ARITMÉTICOS NATIVOS ========== def __add__(self, other): """Suma: aritmética nativa, mantiene la base""" if self.has_symbols: # Para símbolos, mantener como expresión interna return IntBase(f"({self.value_str}) + ({other})", self.base) if isinstance(other, IntBase): if other.has_symbols: return IntBase(f"({self.value_str}) + ({other.value_str})", self.base) result_value = self._numeric_value + other._numeric_value elif isinstance(other, int): result_value = self._numeric_value + other else: # Para operaciones complejas, convertir a SymPy return self.to_sympy() + other result_str = self._convert_to_base_string(result_value, self.base) return IntBase(result_str, self.base) # ... resto de operadores aritméticos nativos ``` ### FourBytes: Universal para Patrones x.x.x.x (ClassBase) ```python class FourBytes(ClassBase): # ← CAMBIO: ClassBase, NO SympyClassBase """ Representación universal de patrones de 4 elementos con manejo interno de símbolos y conversión perezosa a SymPy """ def __init__(self, dotted_string): self.original = dotted_string self.elements = dotted_string.split('.') if len(self.elements) != 4: raise ValueError(f"FourBytes requiere exactamente 4 elementos: {dotted_string}") self.has_symbols = any(not elem.isdigit() for elem in self.elements) if self.has_symbols: # Modo algebraico: mantener símbolos INTERNAMENTE self._symbols = self._extract_symbols() super().__init__(dotted_string, dotted_string) # Mantener como string else: # Modo numérico: validar rangos y convertir self._numeric_elements = [int(elem) for elem in self.elements] # Crear valor como entero de 32 bits (para IPs) self._numeric_value = (self._numeric_elements[0] << 24 | self._numeric_elements[1] << 16 | self._numeric_elements[2] << 8 | self._numeric_elements[3]) super().__init__(self._numeric_value, dotted_string) def _extract_symbols(self): """Extrae símbolos sin convertir a SymPy aún""" symbols = [] for elem in self.elements: if not elem.isdigit(): if elem not in symbols: symbols.append(elem) return symbols def substitute(self, **kwargs): """Sustitución interna sin involucrar SymPy""" if not self.has_symbols: return self new_elements = [] for elem in self.elements: if elem in kwargs: new_elements.append(str(kwargs[elem])) else: new_elements.append(elem) return FourBytes('.'.join(new_elements)) def to_sympy(self): """Conversión EXPLÍCITA a SymPy cuando se necesite álgebra""" if self.has_symbols: symbolic_elements = [] for elem in self.elements: if elem.isdigit(): symbolic_elements.append(int(elem)) else: symbolic_elements.append(sympy.Symbol(elem)) # Retornar como Matrix o expresión según el contexto return sympy.Matrix(symbolic_elements) else: return sympy.sympify(self._numeric_value) def __add__(self, other): """Suma: aritmética nativa de 32-bit""" if self.has_symbols: # Para símbolos, mantener como expresión interna return FourBytes(f"({self.original}) + ({other})") if isinstance(other, FourBytes): if other.has_symbols: return FourBytes(f"({self.original}) + ({other.original})") result_int = self._numeric_value + other._numeric_value elif isinstance(other, int): result_int = self._numeric_value + other else: # Para operaciones complejas, convertir a SymPy return self.to_sympy() + other # Mantener en rango de 32 bits result_int = result_int & 0xFFFFFFFF result_str = self._int_to_fourbytes(result_int) return FourBytes(result_str) # ... resto de operadores aritméticos nativos ``` ## Conversión Perezosa a SymPy ### **Cuándo Convertir:** ```python # NUNCA conversión automática - mantener nativo: mm = FourBytes('255.240.0.x') # → [FourBytes] ✅ ip = IP4(mm, 24) # → [IP4] ✅ ip.next_ip() # → [IP4] ✅ # Conversión EXPLÍCITA solo cuando se necesite álgebra: eq = Eq(mm.to_sympy(), other_expr) # → [SymPy Equation] ✅ solve(eq, [x, y]) # → [SymPy Solutions] ✅ ``` ### **Beneficios de la Conversión Perezosa:** 1. **Control granular**: Cada clase decide cuándo necesita SymPy 2. **Operaciones nativas**: Sin overhead para operaciones simples 3. **Errores claros**: Comportamiento predecible por tipo 4. **Flexibilidad**: Conversión explícita cuando se requiera álgebra compleja ### **Integración con Ecuaciones:** ```python # Para resolver ecuaciones complejas: x, y = symbols('x y') fb = FourBytes('10.x.1.y') # Crear constraints usando conversión explícita: constraints = [ fb.to_sympy()[1] >= 0, # x >= 0 fb.to_sympy()[1] <= 255, # x <= 255 fb.to_sympy()[3] >= 0, # y >= 0 fb.to_sympy()[3] <= 255 # y <= 255 ] solutions = solve(constraints, [x, y]) ``` ## Integración con Clases Especializadas ### Constructores Mejorados ```python class IP4(SympyClassBase): def __init__(self, address, mask=None): # address es FourBytes (ya tokenizado) if not isinstance(address, FourBytes): raise TypeError("address debe ser FourBytes") self.address = address if mask is not None: if isinstance(mask, int): self.mask = IP4Mask(mask) # CIDR elif isinstance(mask, FourBytes): self.mask = IP4Mask(mask) # Dotted decimal elif isinstance(mask, IntBase): # Conversión automática desde hex: 16#ffffff00 → máscara if not mask.has_symbols: mask_int = mask._numeric_value # Convertir a FourBytes primero mask_fourbytes = FourBytes(FourBytes._int_to_fourbytes(mask_int)) self.mask = IP4Mask(mask_fourbytes) else: self.mask = mask # Mantener simbólico else: self.mask = mask class Hex(SympyClassBase): def __init__(self, value): if isinstance(value, IntBase): # value es IntBase (ya tokenizado) self.int_base = value super().__init__(value.value, value.original) elif isinstance(value, FourBytes): # Conversión automática desde FourBytes if not value.has_symbols: # Convertir a valor hex único (32 bits) hex_value = hex(value._numeric_value)[2:].upper() self.int_base = IntBase(hex_value, 16) super().__init__(value._numeric_value, f"16#{hex_value}") else: # Mantener simbólico para análisis algebraico self.int_base = value super().__init__(value._symbolic_value, str(value)) else: raise TypeError("value debe ser IntBase o FourBytes") class Bin(SympyClassBase): def __init__(self, value): if isinstance(value, IntBase): # Conversión automática de cualquier base a binario if not value.has_symbols: bin_value = bin(value._numeric_value)[2:] # Remover '0b' self.int_base = IntBase(bin_value, 2) super().__init__(value._numeric_value, f"2#{bin_value}") else: self.int_base = value super().__init__(value._symbolic_value, str(value)) elif isinstance(value, FourBytes): # Convertir cada elemento a binario if not value.has_symbols: # Para FourBytes, crear representación elemento por elemento bin_elements = [] for elem in value._numeric_elements: bin_elements.append(bin(elem)[2:].zfill(8)) # 8 bits por elemento bin_representation = '.'.join(f"2#{elem}" for elem in bin_elements) # Crear IntBase con valor completo full_bin = ''.join(bin_elements) self.int_base = IntBase(full_bin, 2) super().__init__(value._numeric_value, bin_representation) else: self.int_base = value super().__init__(value._symbolic_value, str(value)) else: raise TypeError("value debe ser IntBase o FourBytes") class IP4Mask(ClassBase): def __init__(self, mask_input): if isinstance(mask_input, int): # CIDR notation self.prefix = mask_input self.mask_int = self._prefix_to_mask_int(mask_input) elif isinstance(mask_input, FourBytes): # Dotted decimal desde tokenización automática if not mask_input.has_symbols: self.mask_int = mask_input._numeric_value self.prefix = self._mask_int_to_prefix(self.mask_int) else: # Mantener simbólico self.prefix = None self.mask_int = mask_input._symbolic_value elif isinstance(mask_input, IntBase): # Desde hex u otra base if not mask_input.has_symbols: self.mask_int = mask_input._numeric_value self.prefix = self._mask_int_to_prefix(self.mask_int) else: # Mantener simbólico self.prefix = None self.mask_int = mask_input._symbolic_value else: raise TypeError("mask_input debe ser int, FourBytes, o IntBase") super().__init__(self.mask_int, str(mask_input)) ``` ### Conversiones Automáticas Bidireccionales ```python # Sistema de conversión fluido entre tipos: class UniversalConverter: """Conversiones automáticas entre todos los tipos""" @staticmethod def auto_convert(source, target_type): """Convierte automáticamente entre tipos compatibles""" if target_type == Hex: if isinstance(source, FourBytes): return Hex(source) # Usa constructor mejorado elif isinstance(source, IntBase): return Hex(source) elif target_type == IP4: if isinstance(source, FourBytes): return IP4(source) elif isinstance(source, str) and '.' in source: # Tokenización automática tokenized = preprocess_tokens(source) return eval(f"IP4({tokenized})") elif target_type == Bin: if isinstance(source, (IntBase, FourBytes)): return Bin(source) # Continuar para otros tipos... # Ejemplos de uso fluido: # 1. IP con máscara hexadecimal (automático): ip1 = IP4(192.168.1.1, 16#ffffff00) # Tokeniza a: IP4(FourBytes('192.168.1.1'), IntBase('ffffff00', 16)) # Constructor IP4 convierte IntBase → IP4Mask automáticamente # 2. Hex desde IP (automático): ip_bytes = FourBytes('192.168.1.1') hex_ip = Hex(ip_bytes) # Constructor Hex convierte FourBytes → representación hex # 3. Análisis de máscara en múltiples bases: mask = FourBytes('255.255.255.0') mask_hex = Hex(mask) # → Hex basado en 32-bit value mask_bin = Bin(mask) # → Bin con representación por elementos mask_cidr = IP4Mask(mask) # → /24 # 4. Operaciones mixtas automáticas: base_ip = FourBytes('10.0.0.0') offset = 16#100 # Tokeniza a IntBase('100', 16) = 256 next_ip = base_ip + offset # → FourBytes('10.0.1.0') ``` ## Casos de Uso Algebraicos ### 1. **Redes con Variables** ```python # Usuario escribe: network = IP4(192.168.x.0, 24) # Tokenizado automáticamente a: network = IP4(FourBytes('192.168.x.0'), 24) # Operaciones algebraicas: network.substitute(x=1) # → IP4(192.168.1.0, 24) network.address[2] # → Symbol('x') ``` ### 2. **Aritmética de Bases** ```python # Usuario escribe: hex_val = Hex(16#x0) # Tokenizado automáticamente a: hex_val = Hex(IntBase('x0', 16)) # Operaciones algebraicas: hex_val.substitute(x=15) # → Hex(240) hex_val + 1 # → 16*x + 1 ``` ### 3. **Operaciones Aritméticas con IntBase** ```python # Operaciones mantienen la base original: a = 16#FF # → IntBase('FF', 16) b = 16#10 # → IntBase('10', 16) result = a + b # → IntBase('10F', 16) [255 + 16 = 271] # Operaciones mixtas con enteros: c = 16#A0 + 32 # → IntBase('C0', 16) [160 + 32 = 192] d = 2#1010 * 3 # → IntBase('11110', 2) [10 * 3 = 30] # Operaciones entre bases diferentes: e = 16#FF + 2#1010 # → IntBase('109', 16) [255 + 10 = 265] ``` ### 4. **Operaciones Aritméticas con FourBytes** ```python # Aritmética de direcciones: ip1 = FourBytes('192.168.1.1') # → ip_int = 3232235777 ip2 = FourBytes('0.0.0.5') # → ip_int = 5 next_ip = ip1 + ip2 # → FourBytes('192.168.1.6') # Incremento simple: base_ip = FourBytes('10.0.0.0') next_net = base_ip + 256 # → FourBytes('10.0.1.0') # Cálculos de rango: start = FourBytes('192.168.1.0') end = start + 255 # → FourBytes('192.168.1.255') ``` ### 5. **Conversiones Automáticas Bidireccionales** ```python # FourBytes → Otras bases: ip = FourBytes('10.1.3.15') ip.ToHex() # → "16#A.16#1.16#3.16#F" ip.ToBinary() # → "2#1010.2#1.2#11.2#1111" ip.ToBase(8) # → "8#12.8#1.8#3.8#17" # Conversión directa en constructores: hex_ip = Hex(FourBytes('10.1.3.15')) # Automático via tokenización ip_from_hex = IP4(FourBytes('10.1.3.16#ff')) # 16#ff se tokeniza a IntBase # Conversiones fluidas: mask = FourBytes('255.255.255.0') mask.ToBase(2) # → "2#11111111.2#11111111.2#11111111.2#0" ``` ### 6. **Análisis de Rangos con Constraints** ```python # Red con parámetros: net = IP4(10.a.b.0, c) # Encontrar valores válidos: constraints = [ net.address[1] >= 0, # a >= 0 net.address[1] <= 255, # a <= 255 net.address[2] >= 0, # b >= 0 net.address[2] <= 255, # b <= 255 c >= 8, # c >= 8 c <= 30 # c <= 30 ] solutions = solve(constraints, [a, b, c]) ``` ### 7. **Ejemplos Prácticos de Conversión** ```python # Ejemplo 1: IP con máscara hexadecimal ip_hex_mask = IP4(192.168.1.1, 16#ffffff00) # Tokeniza automáticamente # Equivale a: IP4(FourBytes('192.168.1.1'), IntBase('ffffff00', 16)) # Ejemplo 2: Conversión de máscara mask_fourbytes = FourBytes('255.255.255.0') mask_hex = mask_fourbytes.ToHex() # → "16#FF.16#FF.16#FF.16#0" mask_cidr = mask_fourbytes.ToCIDR(24) # → "255.255.255.0/24" # Ejemplo 3: Aritmética mixta base_net = FourBytes('192.168.0.0') subnet_size = 2#100000000 # 256 en binario next_subnet = base_net + subnet_size # → FourBytes('192.168.1.0') # Ejemplo 4: Análisis de subredes network = FourBytes('10.0.0.0') for i in range(4): subnet = network + (i * 256) print(f"Subred {i}: {subnet}") # Subred 0: 10.0.0.0 # Subred 1: 10.0.1.0 # Subred 2: 10.0.2.0 # Subred 3: 10.0.3.0 ``` ## Ventajas del Nuevo Sistema ### 1. **Simplicidad** - ✅ Sin parser complejo - ✅ Sintaxis estándar (Python puro) - ✅ Tokenización simple y rápida - ✅ Integración natural con eval() ### 2. **Potencia Algebraica** - ✅ Variables en cualquier posición - ✅ Sustituciones automáticas - ✅ Integración completa con SymPy - ✅ Análisis simbólico de redes/números ### 3. **Extensibilidad** - ✅ Fácil agregar nuevos patrones - ✅ Clases especializadas más simples - ✅ Reutilización de lógica base - ✅ Escalabilidad natural ### 4. **Intuitividad** - ✅ Usuario escribe código normal - ✅ Sin sintaxis especial que memorizar - ✅ Comportamiento predecible - ✅ Curva de aprendizaje mínima ## Posibles Problemas y Soluciones ### 1. **Ambigüedad x.y vs x.y.z.w** ```python # PROBLEMA: ¿x.y es acceso a atributo o parte de FourBytes? # SOLUCIÓN: Solo tokenizar si hay exactamente 4 elementos pattern = r'\b([a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+)\b' ``` ### 2. **Precedencia de Operadores** ```python # PROBLEMA: 192.168.1.1 + 1 # SOLUCIÓN: Tokenización antes de parsing de operadores # Resultado: FourBytes('192.168.1.1') + 1 ``` ### 3. **Validación con Símbolos** ```python # PROBLEMA: ¿Cómo validar 10.x.1.2 como IP? # SOLUCIÓN: Validación condicional y constraint solving def is_valid_ip_symbolic(fourbytes): if not fourbytes.has_symbols: return fourbytes.is_valid_ip_range() # Crear constraints para símbolos constraints = [] for i, elem in enumerate(fourbytes.elements): if not elem.isdigit(): symbol = Symbol(elem) constraints.extend([symbol >= 0, symbol <= 255]) return constraints ``` ## Integración Completa en el Sistema de Tipos ### **Todas las Clases en `custom_types/`** #### **Consistencia Arquitectónica:** - **IntBase** y **FourBytes** son tipos como cualquier otro - Deben seguir el mismo patrón de registro y auto-descubrimiento - El parser los importa desde el registro, no desde ubicaciones hardcodeadas #### **Estructura de Directorios Revisada:** ``` custom_types/ ├── intbase_type.py # IntBase + función de registro ├── fourbytes_type.py # FourBytes + función de registro ├── hex_type.py # Hex (usa IntBase del registro) ├── bin_type.py # Bin (usa IntBase del registro) ├── dec_type.py # Dec (usa IntBase del registro) ├── ip4_type.py # IP4 + IP4Mask (usan FourBytes del registro) ├── chr_type.py # Chr └── latex_type.py # LaTeX ``` ### **Archivos de Tipos Fundamentales** #### **`custom_types/intbase_type.py`** ```python """ Clase base universal para números en cualquier base - TIPO REGISTRADO """ from class_base import ClassBase import sympy import re class IntBase(ClassBase): """ Representación universal de números en cualquier base con manejo interno de símbolos y conversión perezosa a SymPy """ def __init__(self, value_str, base=10): self.value_str = value_str self.base = base self.has_symbols = bool(re.search(r'[a-zA-Z_]', value_str)) if self.has_symbols: # Modo algebraico: mantener símbolos INTERNAMENTE self._symbols = self._extract_symbols() self._expression = self._build_internal_expression() super().__init__(self._expression, f"{base}#{value_str}") else: # Modo numérico: convertir a entero self._numeric_value = int(value_str, base) super().__init__(self._numeric_value, f"{base}#{value_str}") # ... implementación completa ... @staticmethod def Helper(input_str): """Ayuda contextual para IntBase""" if re.search(r'\d+#[0-9A-Fa-fx]+', input_str): return '''IntBase - Números en cualquier base con álgebra simbólica Sintaxis: base#valor Ejemplos: 16#FF → IntBase('FF', 16) = 255 2#1010 → IntBase('1010', 2) = 10 8#777 → IntBase('777', 8) = 511 16#x0 → IntBase('x0', 16) = simbólico Operaciones aritméticas mantienen la base original: 16#FF + 16#10 → IntBase('10F', 16) 2#1010 * 3 → IntBase('11110', 2) Métodos disponibles: .to_base(nueva_base) - Convierte a otra base .substitute(x=valor) - Sustituye símbolos .to_sympy() - Conversión explícita a SymPy''' return None @staticmethod def PopupFunctionList(): """Lista de métodos para autocompletado""" return [ ("to_base", "Convierte a otra base"), ("substitute", "Sustituye símbolos por valores"), ("to_sympy", "Conversión explícita a SymPy para álgebra"), ("to_decimal", "Obtiene valor decimal"), ("to_hex", "Convierte a hexadecimal"), ("to_binary", "Convierte a binario"), ("to_octal", "Convierte a octal"), ] def register_classes_in_module(): """Registro de IntBase en el sistema de tipos""" return [ ("IntBase", IntBase, "ClassBase", { "add_lowercase": True, "supports_brackets": False, # Se maneja por tokenización "is_fundamental": True, # Clase fundamental del sistema "description": "Números universales en cualquier base con álgebra simbólica" }), ] ``` #### **`custom_types/fourbytes_type.py`** ```python """ Clase base universal para patrones x.x.x.x - TIPO REGISTRADO """ from class_base import ClassBase import sympy import re class FourBytes(ClassBase): """ Representación universal de patrones de 4 elementos con manejo interno de símbolos y conversión perezosa a SymPy """ def __init__(self, dotted_string): self.original = dotted_string self.elements = dotted_string.split('.') if len(self.elements) != 4: raise ValueError(f"FourBytes requiere exactamente 4 elementos: {dotted_string}") self.has_symbols = any(not elem.isdigit() for elem in self.elements) if self.has_symbols: # Modo algebraico: mantener símbolos INTERNAMENTE self._symbols = self._extract_symbols() super().__init__(dotted_string, dotted_string) # Mantener como string else: # Modo numérico: validar rangos y convertir self._numeric_elements = [int(elem) for elem in self.elements] # Crear valor como entero de 32 bits (para IPs) self._numeric_value = (self._numeric_elements[0] << 24 | self._numeric_elements[1] << 16 | self._numeric_elements[2] << 8 | self._numeric_elements[3]) super().__init__(self._numeric_value, dotted_string) # ... implementación completa ... @staticmethod def Helper(input_str): """Ayuda contextual para FourBytes""" if re.search(r'\d+\.\d+\.\d+\.\d+', input_str): return '''FourBytes - Patrones x.x.x.x con álgebra simbólica Sintaxis: x.y.z.w (donde cada elemento puede ser número o símbolo) Ejemplos: 192.168.1.1 → FourBytes('192.168.1.1') 255.255.0.0 → FourBytes('255.255.0.0') 10.x.1.y → FourBytes('10.x.1.y') = simbólico Aritmética de 32-bit: 192.168.1.1 + 5 → FourBytes('192.168.1.6') 10.0.0.0 + 256 → FourBytes('10.0.1.0') Métodos disponibles: .ToBase(base) - Convierte cada elemento a base específica .ToHex() - Convierte a hexadecimal .ToBinary() - Convierte a binario .substitute(x=val) - Sustituye símbolos .to_sympy() - Conversión explícita a SymPy''' return None @staticmethod def PopupFunctionList(): """Lista de métodos para autocompletado""" return [ ("ToBase", "Convierte cada elemento a base específica"), ("ToHex", "Convierte cada elemento a hexadecimal"), ("ToBinary", "Convierte cada elemento a binario"), ("ToOctal", "Convierte cada elemento a octal"), ("ToCIDR", "Añade notación CIDR"), ("substitute", "Sustituye símbolos por valores"), ("to_sympy", "Conversión explícita a SymPy para álgebra"), ("is_valid_ip_range", "Verifica si es rango IP válido"), ] def register_classes_in_module(): """Registro de FourBytes en el sistema de tipos""" return [ ("FourBytes", FourBytes, "ClassBase", { "add_lowercase": True, "supports_brackets": False, # Se maneja por tokenización "is_fundamental": True, # Clase fundamental del sistema "description": "Patrones universales x.x.x.x con álgebra simbólica" }), ] ``` ### **Parser Integrado con el Registro** #### **`tl_bracket_parser.py` (Actualizado)** ```python """ Tokenizador integrado con el sistema de auto-descubrimiento de tipos """ import re from typing import Tuple, Dict, Any # Importar desde el registro de tipos (NO hardcodeado) try: from type_registry import get_registered_base_context TYPE_REGISTRY_AVAILABLE = True except ImportError: TYPE_REGISTRY_AVAILABLE = False class UniversalTokenizer: """Tokenizador que usa el registro de tipos para conversiones automáticas""" def __init__(self): self.debug = False self._update_fundamental_classes() def _update_fundamental_classes(self): """Obtiene clases fundamentales desde el registro""" if not TYPE_REGISTRY_AVAILABLE: # Fallback básico self.IntBase = None self.FourBytes = None return try: # Obtener clases desde el registro automáticamente registered_context = get_registered_base_context() self.IntBase = registered_context.get('IntBase') self.FourBytes = registered_context.get('FourBytes') if self.debug: print(f"🔧 Clases fundamentales cargadas desde registro:") print(f" IntBase: {self.IntBase}") print(f" FourBytes: {self.FourBytes}") except Exception as e: if self.debug: print(f"⚠️ Error cargando clases fundamentales: {e}") self.IntBase = None self.FourBytes = None def reload_fundamental_classes(self): """Recarga clases fundamentales del registro""" if self.debug: print("🔄 Recargando clases fundamentales...") self._update_fundamental_classes() def preprocess_tokens(self, expression): """Convierte patrones específicos en objetos tipados""" if not self.IntBase or not self.FourBytes: if self.debug: print("⚠️ Clases fundamentales no disponibles") return expression # Fase 1: IntBase (mayor precedencia) # 16#FF → IntBase('FF', 16) def replace_intbase(match): base = match.group(1) value = match.group(2) return f'IntBase("{value}", {base})' expression = re.sub( r'(\d+)#([0-9A-Fa-fx]+)', replace_intbase, expression ) # Fase 2: FourBytes # 192.168.1.1 → FourBytes('192.168.1.1') def replace_fourbytes(match): dotted_value = match.group(1) return f'FourBytes("{dotted_value}")' 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 ) if self.debug: print(f"🔧 Tokenización: '{original}' → '{expression}'") return expression ``` ### **Ventajas de la Integración Completa** #### **1. Consistencia Arquitectónica** - **Todo** el sistema de tipos en un lugar - Patrón unificado de registro y descubrimiento - No hay clases "especiales" hardcodeadas #### **2. Modularidad Completa** - Cada tipo es independiente y auto-contenido - Fácil agregar/remover tipos sin modificar código core - Sistema verdaderamente extensible #### **3. Autocompletado y Ayuda Unificados** - **IntBase** y **FourBytes** tienen `PopupFunctionList()` automáticamente - Ayuda contextual mediante `Helper()` - Integración completa con el sistema de autocompletado #### **4. Carga Dinámica** - Parser obtiene clases desde el registro, no por importación directa - Permite recargar tipos en tiempo de ejecución - Sistema totalmente dinámico #### **5. Escalabilidad** ```python # Agregar nuevos tipos fundamentales es trivial: custom_types/ ├── intbase_type.py # Base universal para números ├── fourbytes_type.py # Base universal para x.x.x.x ├── sixbytes_type.py # NUEVO: Para MACs (xx:xx:xx:xx:xx:xx) ├── time_type.py # NUEVO: Para HH:MM:SS └── ... ``` #### **6. Pureza del Parser** ```python # ANTES (hardcodeado): from intbase import IntBase from fourbytes import FourBytes # DESPUÉS (dinámico): IntBase = registry.get('IntBase') FourBytes = registry.get('FourBytes') ``` ## Conclusión Esta refactorización elimina complejidad innecesaria mientras añade capacidades algebraicas poderosas. El resultado será: - **Más simple** para casos básicos - **Más poderoso** para casos avanzados - **Más intuitivo** para el usuario - **Más extensible** para el futuro El tokenizador automático hace que el usuario no tenga que aprender sintaxis especial, mientras que el soporte algebraico completo permite análisis sofisticados cuando se necesitan.