Calc/.doc/refactoring/remover_corchetes.md

36 KiB

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

# 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

# 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

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:

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:

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:

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)

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)

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:

# 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:

# 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

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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

"""
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

"""
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)

"""
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

# 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

# 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.