# 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 ``` ## Clases Base Universales ### IntBase: Universal para Bases Numéricas ```python class IntBase(SympyClassBase): """ Representación universal de números en cualquier base con soporte algebraico completo y operaciones aritméticas """ 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 como expresión simbólica self._symbolic_value = self._parse_symbolic_base() super().__init__(self._symbolic_value, 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 _parse_symbolic_base(self): """Convierte valor con símbolos a expresión SymPy""" # Ejemplo: "x0" base 16 → x*16^1 + 0*16^0 # Ejemplo: "101x" base 2 → 1*2^3 + 0*2^2 + 1*2^1 + x*2^0 symbols = [] 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) symbols.append(digit) result += coefficient * power return result def to_base(self, new_base): """Convierte a nueva base""" if self.has_symbols: # En modo algebraico, mantener expresión return IntBase(f"({self._symbolic_value})_base{new_base}", new_base) else: # En modo numérico, convertir directamente new_value_str = self._convert_to_base_string(self._numeric_value, new_base) return IntBase(new_value_str, new_base) @staticmethod def _convert_to_base_string(value, base): """Convierte entero a string en base específica""" if value == 0: return "0" digits = "0123456789ABCDEF" result = "" while value: result = digits[value % base] + result value //= base return result # ========== OPERADORES ARITMÉTICOS ========== def __add__(self, other): """Suma: mantiene la base del operando izquierdo""" if self.has_symbols: return super().__add__(other) # Delegar a SymPy if isinstance(other, IntBase): if other.has_symbols: return super().__add__(other) result_value = self._numeric_value + other._numeric_value elif isinstance(other, int): result_value = self._numeric_value + other else: return super().__add__(other) result_str = self._convert_to_base_string(result_value, self.base) return IntBase(result_str, self.base) def __sub__(self, other): """Resta: mantiene la base del operando izquierdo""" if self.has_symbols: return super().__sub__(other) if isinstance(other, IntBase): if other.has_symbols: return super().__sub__(other) result_value = self._numeric_value - other._numeric_value elif isinstance(other, int): result_value = self._numeric_value - other else: return super().__sub__(other) result_str = self._convert_to_base_string(result_value, self.base) return IntBase(result_str, self.base) def __mul__(self, other): """Multiplicación: mantiene la base del operando izquierdo""" if self.has_symbols: return super().__mul__(other) if isinstance(other, IntBase): if other.has_symbols: return super().__mul__(other) result_value = self._numeric_value * other._numeric_value elif isinstance(other, int): result_value = self._numeric_value * other else: return super().__mul__(other) result_str = self._convert_to_base_string(result_value, self.base) return IntBase(result_str, self.base) def __truediv__(self, other): """División: mantiene la base del operando izquierdo""" if self.has_symbols: return super().__truediv__(other) if isinstance(other, IntBase): if other.has_symbols: return super().__truediv__(other) result_value = self._numeric_value // other._numeric_value # División entera elif isinstance(other, int): result_value = self._numeric_value // other else: return super().__truediv__(other) result_str = self._convert_to_base_string(result_value, self.base) return IntBase(result_str, self.base) def __mod__(self, other): """Módulo: mantiene la base del operando izquierdo""" if self.has_symbols: return super().__mod__(other) if isinstance(other, IntBase): if other.has_symbols: return super().__mod__(other) result_value = self._numeric_value % other._numeric_value elif isinstance(other, int): result_value = self._numeric_value % other else: return super().__mod__(other) result_str = self._convert_to_base_string(result_value, self.base) return IntBase(result_str, self.base) # Operadores reversos para int + IntBase def __radd__(self, other): return self.__add__(other) def __rsub__(self, other): if isinstance(other, int): result_value = other - self._numeric_value result_str = self._convert_to_base_string(result_value, self.base) return IntBase(result_str, self.base) return super().__rsub__(other) def __rmul__(self, other): return self.__mul__(other) ``` ### FourBytes: Universal para Patrones x.x.x.x ```python class FourBytes(SympyClassBase): """ Representación universal de patrones de 4 elementos con soporte algebraico completo y operaciones aritméticas """ 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: crear expresión simbólica self._symbolic_elements = [] for elem in self.elements: if elem.isdigit(): self._symbolic_elements.append(int(elem)) else: self._symbolic_elements.append(sympy.Symbol(elem)) # Crear expresión como vector de 4 elementos self._symbolic_value = sympy.Matrix(self._symbolic_elements) super().__init__(self._symbolic_value, dotted_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 is_valid_ip_range(self): """Verifica si todos los elementos numéricos están en rango IP""" if self.has_symbols: return None # No se puede validar con símbolos return all(0 <= x <= 255 for x in self._numeric_elements) def substitute(self, **kwargs): """Sustituye símbolos por valores""" 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 __getitem__(self, index): """Acceso a elementos individuales""" if self.has_symbols: return self._symbolic_elements[index] else: return self._numeric_elements[index] def to_ip_int(self): """Convierte a entero de 32 bits (para IPs)""" if self.has_symbols: # Crear expresión algebraica return (self._symbolic_elements[0] * 2**24 + self._symbolic_elements[1] * 2**16 + self._symbolic_elements[2] * 2**8 + self._symbolic_elements[3]) else: return self._numeric_value @staticmethod def _int_to_fourbytes(value): """Convierte entero de 32 bits a formato x.y.z.w""" w = value & 0xFF z = (value >> 8) & 0xFF y = (value >> 16) & 0xFF x = (value >> 24) & 0xFF return f"{x}.{y}.{z}.{w}" # ========== OPERADORES ARITMÉTICOS ========== def __add__(self, other): """Suma: convierte a int, opera, reconvierte a FourBytes""" if self.has_symbols: return super().__add__(other) # Delegar a SymPy if isinstance(other, FourBytes): if other.has_symbols: return super().__add__(other) result_int = self._numeric_value + other._numeric_value elif isinstance(other, int): result_int = self._numeric_value + other else: return super().__add__(other) # Mantener en rango de 32 bits result_int = result_int & 0xFFFFFFFF result_str = self._int_to_fourbytes(result_int) return FourBytes(result_str) def __sub__(self, other): """Resta: convierte a int, opera, reconvierte a FourBytes""" if self.has_symbols: return super().__sub__(other) if isinstance(other, FourBytes): if other.has_symbols: return super().__sub__(other) result_int = self._numeric_value - other._numeric_value elif isinstance(other, int): result_int = self._numeric_value - other else: return super().__sub__(other) # Mantener en rango de 32 bits (underflow se convierte en valor alto) result_int = result_int & 0xFFFFFFFF result_str = self._int_to_fourbytes(result_int) return FourBytes(result_str) # ========== CONVERSIONES DE BASE ========== def ToBase(self, base): """ Convierte cada elemento a la base especificada Retorna expresión con cada elemento convertido """ if self.has_symbols: # Para elementos simbólicos, retornar expresión algebraica converted_elements = [] for elem in self.elements: if elem.isdigit(): int_val = int(elem) converted = IntBase._convert_to_base_string(int_val, base) converted_elements.append(f"{base}#{converted}") else: # Símbolo: expresar como conversión algebraica converted_elements.append(f"{base}#{elem}") return '.'.join(converted_elements) else: # Para elementos numéricos, conversión directa converted_elements = [] for elem_val in self._numeric_elements: converted = IntBase._convert_to_base_string(elem_val, base) converted_elements.append(f"{base}#{converted}") return '.'.join(converted_elements) def ToCIDR(self, prefix_length): """Convierte a notación CIDR""" return f"{self.original}/{prefix_length}" def ToHex(self): """Convierte cada elemento a hexadecimal""" return self.ToBase(16) def ToBinary(self): """Convierte cada elemento a binario""" return self.ToBase(2) def ToOctal(self): """Convierte cada elemento a octal""" return self.ToBase(8) ``` ## 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 ``` ## Plan de Implementación ### Fase 1: Clases Base 1. Implementar `IntBase` con soporte algebraico 2. Implementar `FourBytes` con soporte algebraico 3. Tests exhaustivos de ambas clases ### Fase 2: Tokenizador 1. Implementar `preprocess_tokens()` 2. Integrar con `HybridEvaluationEngine` 3. Tests de tokenización ### Fase 3: Migración de Clases 1. Migrar `Hex`, `Bin`, `Dec` a usar `IntBase` 2. Migrar `IP4`, `IP4Mask` a usar `FourBytes` 3. Eliminar sistema de corchetes ### Fase 4: Nuevas Capacidades 1. Operaciones algebraicas avanzadas 2. Constraint solving para redes 3. Análisis simbólico de rangos ## 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.