36 KiB
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:
- Control granular: Cada clase decide cuándo necesita SymPy
- Operaciones nativas: Sin overhead para operaciones simples
- Errores claros: Comportamiento predecible por tipo
- 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.