1262 lines
44 KiB
Markdown
1262 lines
44 KiB
Markdown
# 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 Completamente Dinámico y Auto-Descubridor**
|
|
|
|
#### **Principio: Responsabilidad de Tokenización Distribuida**
|
|
> **"Cada clase define cómo debe ser reconocida y tokenizada, el parser simplemente ejecuta las reglas descubiertas"**
|
|
|
|
#### **`tl_universal_tokenizer.py` (Completamente Genérico)**
|
|
```python
|
|
"""
|
|
Tokenizador universal que auto-descubre reglas de tokenización
|
|
"""
|
|
import re
|
|
from typing import List, Dict, Any, Callable
|
|
|
|
# Importar desde el registro de tipos
|
|
try:
|
|
from type_registry import get_registered_base_context
|
|
TYPE_REGISTRY_AVAILABLE = True
|
|
except ImportError:
|
|
TYPE_REGISTRY_AVAILABLE = False
|
|
|
|
class UniversalTokenizer:
|
|
"""
|
|
Tokenizador que auto-descubre reglas de tokenización desde las clases registradas
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.debug = False
|
|
self.tokenization_rules: List[Dict[str, Any]] = []
|
|
self._discover_tokenization_rules()
|
|
|
|
def _discover_tokenization_rules(self):
|
|
"""Auto-descubre reglas de tokenización desde todas las clases registradas"""
|
|
if not TYPE_REGISTRY_AVAILABLE:
|
|
if self.debug:
|
|
print("⚠️ Sistema de tipos no disponible")
|
|
return
|
|
|
|
try:
|
|
# Obtener todas las clases registradas
|
|
registered_classes = get_registered_base_context()
|
|
|
|
self.tokenization_rules.clear()
|
|
|
|
for class_name, class_obj in registered_classes.items():
|
|
if hasattr(class_obj, 'get_tokenization_patterns'):
|
|
try:
|
|
patterns = class_obj.get_tokenization_patterns()
|
|
for pattern_info in patterns:
|
|
self.tokenization_rules.append({
|
|
'class_name': class_name,
|
|
'class_obj': class_obj,
|
|
**pattern_info
|
|
})
|
|
|
|
if self.debug:
|
|
print(f"📋 Regla tokenización: {class_name} - {pattern_info.get('description', 'Sin descripción')}")
|
|
|
|
except Exception as e:
|
|
if self.debug:
|
|
print(f"⚠️ Error obteniendo patrones de {class_name}: {e}")
|
|
|
|
# Ordenar por prioridad (menor número = mayor prioridad)
|
|
self.tokenization_rules.sort(key=lambda x: x.get('priority', 100))
|
|
|
|
if self.debug:
|
|
print(f"🔧 {len(self.tokenization_rules)} reglas de tokenización cargadas")
|
|
for rule in self.tokenization_rules:
|
|
print(f" {rule['priority']:2d}: {rule['class_name']} - {rule.get('description', '')}")
|
|
|
|
except Exception as e:
|
|
if self.debug:
|
|
print(f"⚠️ Error en auto-descubrimiento: {e}")
|
|
self.tokenization_rules.clear()
|
|
|
|
def reload_tokenization_rules(self):
|
|
"""Recarga reglas de tokenización del registro"""
|
|
if self.debug:
|
|
print("🔄 Recargando reglas de tokenización...")
|
|
self._discover_tokenization_rules()
|
|
|
|
def preprocess_tokens(self, expression: str) -> str:
|
|
"""Aplica todas las reglas de tokenización descubiertas"""
|
|
if not self.tokenization_rules:
|
|
return expression
|
|
|
|
result = expression
|
|
original = expression
|
|
|
|
for rule in self.tokenization_rules:
|
|
try:
|
|
pattern = rule['pattern']
|
|
replacement_func = rule['replacement']
|
|
|
|
# Aplicar transformación
|
|
if callable(replacement_func):
|
|
result = re.sub(pattern, replacement_func, result)
|
|
else:
|
|
result = re.sub(pattern, replacement_func, result)
|
|
|
|
except Exception as e:
|
|
if self.debug:
|
|
print(f"⚠️ Error aplicando regla {rule['class_name']}: {e}")
|
|
continue
|
|
|
|
if self.debug and result != original:
|
|
print(f"🔧 Tokenización: '{original}' → '{result}'")
|
|
|
|
return result
|
|
|
|
def get_tokenization_info(self) -> List[Dict[str, Any]]:
|
|
"""Retorna información sobre las reglas de tokenización activas"""
|
|
return [
|
|
{
|
|
'class': rule['class_name'],
|
|
'priority': rule.get('priority', 100),
|
|
'description': rule.get('description', 'Sin descripción'),
|
|
'pattern': rule['pattern']
|
|
}
|
|
for rule in self.tokenization_rules
|
|
]
|
|
```
|
|
|
|
### **Implementación en Clases de Tipos**
|
|
|
|
#### **`custom_types/intbase_type.py` (con Tokenización)**
|
|
```python
|
|
"""
|
|
Clase base universal para números en cualquier base - CON TOKENIZACIÓN PROPIA
|
|
"""
|
|
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):
|
|
# ... implementación completa ...
|
|
|
|
@staticmethod
|
|
def get_tokenization_patterns():
|
|
"""
|
|
Define cómo esta clase debe ser reconocida y tokenizada
|
|
|
|
Returns:
|
|
List[Dict]: Lista de reglas de tokenización
|
|
"""
|
|
return [
|
|
{
|
|
'pattern': r'(\d+)#([0-9A-Fa-fx]+)',
|
|
'replacement': lambda match: f'IntBase("{match.group(2)}", {match.group(1)})',
|
|
'priority': 5, # ALTA prioridad (patrón muy específico)
|
|
'description': 'Números con base: 16#FF, 2#1010, etc.'
|
|
}
|
|
]
|
|
|
|
# ... resto de la implementación ...
|
|
|
|
def register_classes_in_module():
|
|
"""Registro de IntBase en el sistema de tipos"""
|
|
return [
|
|
("IntBase", IntBase, "ClassBase", {
|
|
"add_lowercase": True,
|
|
"supports_brackets": False,
|
|
"is_fundamental": True,
|
|
"has_tokenization": True, # NUEVO: Indica que tiene tokenización
|
|
"description": "Números universales en cualquier base con álgebra simbólica"
|
|
}),
|
|
]
|
|
```
|
|
|
|
#### **`custom_types/fourbytes_type.py` (con Tokenización)**
|
|
```python
|
|
"""
|
|
Clase base universal para patrones x.x.x.x - CON TOKENIZACIÓN PROPIA
|
|
"""
|
|
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):
|
|
# ... implementación completa ...
|
|
|
|
@staticmethod
|
|
def get_tokenization_patterns():
|
|
"""
|
|
Define cómo esta clase debe ser reconocida y tokenizada
|
|
|
|
Returns:
|
|
List[Dict]: Lista de reglas de tokenización
|
|
"""
|
|
return [
|
|
{
|
|
'pattern': r'\b([a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+)\b',
|
|
'replacement': lambda match: f'FourBytes("{match.group(1)}")',
|
|
'priority': 10, # MENOR prioridad que IntBase (patrón más general)
|
|
'description': 'Patrones de 4 elementos: 192.168.1.1, 10.x.y.z'
|
|
}
|
|
]
|
|
|
|
# ... resto de la implementación ...
|
|
|
|
def register_classes_in_module():
|
|
"""Registro de FourBytes en el sistema de tipos"""
|
|
return [
|
|
("FourBytes", FourBytes, "ClassBase", {
|
|
"add_lowercase": True,
|
|
"supports_brackets": False,
|
|
"is_fundamental": True,
|
|
"has_tokenization": True, # NUEVO: Indica que tiene tokenización
|
|
"description": "Patrones universales x.x.x.x con álgebra simbólica"
|
|
}),
|
|
]
|
|
```
|
|
|
|
#### **Ejemplo: Tipo Futuro con Tokenización Compleja**
|
|
```python
|
|
# custom_types/mac_type.py
|
|
class MACAddress(ClassBase):
|
|
"""Direcciones MAC con tokenización sofisticada"""
|
|
|
|
@staticmethod
|
|
def get_tokenization_patterns():
|
|
"""Múltiples patrones de MAC address"""
|
|
return [
|
|
{
|
|
'pattern': r'\b([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})\b',
|
|
'replacement': lambda match: f'MACAddress("{match.group(1)}", format="colon")',
|
|
'priority': 6,
|
|
'description': 'MAC con dos puntos: AA:BB:CC:DD:EE:FF'
|
|
},
|
|
{
|
|
'pattern': r'\b([0-9A-Fa-f]{2}-[0-9A-Fa-f]{2}-[0-9A-Fa-f]{2}-[0-9A-Fa-f]{2}-[0-9A-Fa-f]{2}-[0-9A-Fa-f]{2})\b',
|
|
'replacement': lambda match: f'MACAddress("{match.group(1)}", format="dash")',
|
|
'priority': 6,
|
|
'description': 'MAC con guiones: AA-BB-CC-DD-EE-FF'
|
|
},
|
|
{
|
|
'pattern': r'\b([0-9A-Fa-f]{12})\b',
|
|
'replacement': lambda match: f'MACAddress("{match.group(1)}", format="compact")',
|
|
'priority': 15, # Baja prioridad (patrón muy general)
|
|
'description': 'MAC compacta: AABBCCDDEEFF'
|
|
}
|
|
]
|
|
|
|
# custom_types/time_type.py
|
|
class TimeStamp(ClassBase):
|
|
"""Timestamps con múltiples formatos"""
|
|
|
|
@staticmethod
|
|
def get_tokenization_patterns():
|
|
return [
|
|
{
|
|
'pattern': r'\b(\d{1,2}:\d{2}:\d{2})\b',
|
|
'replacement': lambda match: f'TimeStamp("{match.group(1)}", format="HMS")',
|
|
'priority': 8,
|
|
'description': 'Tiempo HH:MM:SS'
|
|
},
|
|
{
|
|
'pattern': r'\b(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})\b',
|
|
'replacement': lambda match: f'TimeStamp("{match.group(1)}", format="ISO")',
|
|
'priority': 7,
|
|
'description': 'ISO timestamp: 2024-01-01T12:30:45'
|
|
}
|
|
]
|
|
```
|
|
|
|
### **Ventajas de la Tokenización Distribuida**
|
|
|
|
#### **1. Responsabilidad Única**
|
|
- Cada clase es responsable de su reconocimiento
|
|
- Parser genérico sin conocimiento específico de tipos
|
|
- Lógica de tokenización encapsulada con la clase
|
|
|
|
#### **2. Escalabilidad Perfecta**
|
|
```python
|
|
# Agregar nuevo tipo con tokenización:
|
|
# 1. Crear clase en custom_types/
|
|
# 2. Implementar get_tokenization_patterns()
|
|
# 3. ¡AUTOMÁTICAMENTE funciona!
|
|
|
|
# NO se requiere:
|
|
# - Modificar parser
|
|
# - Modificar motor de evaluación
|
|
# - Registrar patrones manualmente
|
|
```
|
|
|
|
#### **3. Control de Precedencia Granular**
|
|
```python
|
|
# Precedencias típicas:
|
|
IntBase: priority = 5 # MUY específico: 16#FF
|
|
IPv6: priority = 6 # Específico: 2001:db8::1
|
|
MAC: priority = 7 # Específico: AA:BB:CC:DD:EE:FF
|
|
FourBytes: priority = 10 # General: x.x.x.x
|
|
Compact: priority = 15 # MUY general: AABBCCDD
|
|
```
|
|
|
|
#### **4. Flexibilidad Extrema**
|
|
- Múltiples patrones por clase
|
|
- Lógica de reemplazo compleja con funciones lambda
|
|
- Validación condicional dentro del patrón
|
|
- Soporte para formatos alternativos
|
|
|
|
#### **5. Auto-Descubrimiento Completo**
|
|
```python
|
|
# El sistema automáticamente descubre:
|
|
tokenizer.get_tokenization_info()
|
|
# [
|
|
# {'class': 'IntBase', 'priority': 5, 'description': 'Números con base'},
|
|
# {'class': 'IPv6', 'priority': 6, 'description': 'Direcciones IPv6'},
|
|
# {'class': 'FourBytes', 'priority': 10, 'description': 'Patrones x.x.x.x'}
|
|
# ]
|
|
```
|
|
|
|
#### **6. Debugging y Introspección**
|
|
```python
|
|
# Información completa del proceso:
|
|
tokenizer.debug = True
|
|
tokenizer.preprocess_tokens("16#FF + 192.168.1.1")
|
|
|
|
# Output:
|
|
# 📋 Regla tokenización: IntBase - Números con base: 16#FF, 2#1010
|
|
# 📋 Regla tokenización: FourBytes - Patrones de 4 elementos: 192.168.1.1, 10.x.y.z
|
|
# 🔧 Tokenización: '16#FF + 192.168.1.1' → 'IntBase("FF", 16) + FourBytes("192.168.1.1")'
|
|
```
|
|
|
|
### **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. |