Eliminado de corchetes y creacion de FourBytes y IntBase primera version

This commit is contained in:
Miguel 2025-06-05 13:27:35 +02:00
parent 8f0b0287a5
commit 6a7311e358
11 changed files with 1255 additions and 611 deletions

View File

@ -1,46 +1,174 @@
""" """
Clase híbrida para números binarios - ADAPTADA AL NUEVO SISTEMA Clase híbrida para números binarios - ADAPTADA AL NUEVO SISTEMA
Ahora usa IntBase como clase base universal
""" """
from sympy_Base import SympyClassBase from sympy_Base import SympyClassBase, IntBase
import re import re
class Class_Bin(SympyClassBase): class Class_Bin(SympyClassBase):
"""Clase híbrida para números binarios""" """Clase híbrida para números binarios basada en IntBase"""
def __new__(cls, value_input):
"""Crear objeto SymPy válido"""
obj = SympyClassBase.__new__(cls)
return obj
def __init__(self, value_input): def __init__(self, value_input):
"""Inicialización de Bin""" """Inicialización de Bin usando IntBase como base"""
# Convertir input a entero
if isinstance(value_input, str):
# Remover prefijo 0b si existe
if value_input.startswith('0b'):
value_input = value_input[2:]
# Convertir a entero
value = int(value_input, 2)
else:
value = int(value_input)
# Llamar al constructor base if isinstance(value_input, IntBase):
super().__init__(value, f"0b{value:08b}") # value es IntBase (ya tokenizado desde 2#valor)
if value_input.base == 2:
self.int_base = value_input
else:
# Convertir de otra base a binario
if not value_input.has_symbols:
bin_value = IntBase._convert_to_base_string(value_input._numeric_value, 2)
self.int_base = IntBase(bin_value, 2)
else:
# Mantener simbólico para análisis algebraico
self.int_base = value_input
elif isinstance(value_input, str):
# Conversión desde string
clean_value = value_input
if clean_value.startswith('0b'):
clean_value = clean_value[2:]
self.int_base = IntBase(clean_value, 2)
elif isinstance(value_input, (int, float)) or hasattr(value_input, '__int__'):
# Conversión desde entero (incluyendo sympy.Integer)
try:
int_val = int(value_input)
bin_value = IntBase._convert_to_base_string(int_val, 2)
self.int_base = IntBase(bin_value, 2)
except (ValueError, TypeError):
raise TypeError(f"No se puede convertir {type(value_input)} a entero")
else:
raise TypeError(f"Bin no puede inicializarse con {type(value_input)}")
# Llamar al constructor base con el valor del IntBase
super().__init__(self.int_base.value, str(self.int_base))
def __str__(self): def __str__(self):
"""Representación string para display""" """Representación string para display"""
return f"0b{self._value:08b}" if self.int_base.has_symbols:
return f"0b{self.int_base.value_str}"
else:
return f"0b{self.int_base._numeric_value:08b}"
def _sympystr(self, printer): def _sympystr(self, printer):
"""Representación SymPy string""" """Representación SymPy string"""
return str(self) return str(self)
def __repr__(self):
return f"Bin({self.int_base.value_str})"
# ========== MÉTODOS DE CONVERSIÓN ==========
def toDecimal(self):
"""Convierte a decimal"""
if self.int_base.has_symbols:
return self.int_base._symbolic_value # Expresión simbólica
else:
return self.int_base._numeric_value
def toHex(self):
"""Convierte a hexadecimal"""
if self.int_base.has_symbols:
return f"Hex({self.int_base._symbolic_value})"
else:
hex_value = IntBase._convert_to_base_string(self.int_base._numeric_value, 16)
return f"0x{hex_value}"
def toOctal(self):
"""Convierte a octal"""
if self.int_base.has_symbols:
return f"Oct({self.int_base._symbolic_value})"
else:
oct_value = IntBase._convert_to_base_string(self.int_base._numeric_value, 8)
return f"0o{oct_value}"
def toBase(self, base):
"""Convierte a cualquier base"""
return self.int_base.to_base(base)
# ========== OPERADORES ARITMÉTICOS (delegados a IntBase) ==========
def __add__(self, other):
"""Suma delegada a IntBase"""
if isinstance(other, Class_Bin):
result_intbase = self.int_base + other.int_base
elif isinstance(other, IntBase):
result_intbase = self.int_base + other
elif isinstance(other, int):
result_intbase = self.int_base + other
else:
return NotImplemented
return Class_Bin(result_intbase)
def __sub__(self, other):
"""Resta delegada a IntBase"""
if isinstance(other, Class_Bin):
result_intbase = self.int_base - other.int_base
elif isinstance(other, IntBase):
result_intbase = self.int_base - other
elif isinstance(other, int):
result_intbase = self.int_base - other
else:
return NotImplemented
return Class_Bin(result_intbase)
def __mul__(self, other):
"""Multiplicación delegada a IntBase"""
if isinstance(other, Class_Bin):
result_intbase = self.int_base * other.int_base
elif isinstance(other, IntBase):
result_intbase = self.int_base * other
elif isinstance(other, int):
result_intbase = self.int_base * other
else:
return NotImplemented
return Class_Bin(result_intbase)
def __truediv__(self, other):
"""División delegada a IntBase"""
if isinstance(other, Class_Bin):
result_intbase = self.int_base / other.int_base
elif isinstance(other, IntBase):
result_intbase = self.int_base / other
elif isinstance(other, int):
result_intbase = self.int_base / other
else:
return NotImplemented
return Class_Bin(result_intbase)
def __mod__(self, other):
"""Módulo delegado a IntBase"""
if isinstance(other, Class_Bin):
result_intbase = self.int_base % other.int_base
elif isinstance(other, IntBase):
result_intbase = self.int_base % other
elif isinstance(other, int):
result_intbase = self.int_base % other
else:
return NotImplemented
return Class_Bin(result_intbase)
# Operadores reversos
def __radd__(self, other):
return self.__add__(other)
def __rmul__(self, other):
return self.__mul__(other)
# ========== MÉTODOS ESTÁTICOS ==========
@staticmethod @staticmethod
def Helper(input_str): def Helper(input_str):
"""Ayuda contextual para Bin""" """Ayuda contextual para Bin"""
if re.match(r"^\s*Bin\b", input_str, re.IGNORECASE): if re.match(r"^\s*Bin\b", input_str, re.IGNORECASE):
return 'Ej: Bin[1010], Bin[10]\nFunciones: toDecimal()' return ('Ej: 2#1010 (se convierte automáticamente), Bin(10)\n'
'Funciones: toDecimal(), toHex(), toOctal(), toBase(n)')
return None return None
@staticmethod @staticmethod
@ -48,23 +176,23 @@ class Class_Bin(SympyClassBase):
"""Lista de métodos sugeridos para autocompletado de Bin""" """Lista de métodos sugeridos para autocompletado de Bin"""
return [ return [
("toDecimal", "Convierte a decimal"), ("toDecimal", "Convierte a decimal"),
("toHex", "Convierte a hexadecimal"),
("toOctal", "Convierte a octal"),
("toBase", "Convierte a base específica"),
] ]
def toDecimal(self):
"""Convierte a decimal"""
return self._value
# ========== FUNCIÓN DE REGISTRO - NUEVA ========== # ========== FUNCIÓN DE REGISTRO ==========
def register_classes_in_module(): def register_classes_in_module():
""" """
Devuelve una lista de clases definidas en este módulo para ser registradas. Devuelve una lista de clases definidas en este módulo para ser registradas.
NOTA: Ya no se registra para corchetes, el tokenizador maneja 2#valor automáticamente
""" """
return [ return [
("Bin", Class_Bin, "SympyClassBase", { ("Bin", Class_Bin, "SympyClassBase", {
"add_lowercase": True, "add_lowercase": True,
"supports_brackets": True, "supports_brackets": False, # Ya no usa corchetes
"description": "Números binarios" "description": "Números binarios (usar 2#valor para tokenización automática)"
}), }),
] ]

View File

@ -14,9 +14,19 @@ class Class_Dec(SympyClassBase):
return obj return obj
def __init__(self, value_input): def __init__(self, value_input):
"""Inicialización de Dec""" """Inicialización de Dec - MEJORADA para compatibilidad con IntBase"""
# Convertir input a entero
if isinstance(value_input, str): # Manejar diferentes tipos de input
if hasattr(value_input, '_numeric_value'):
# Es un IntBase u objeto similar con valor numérico
value = value_input._numeric_value
elif hasattr(value_input, 'value') and isinstance(value_input.value, (int, float)):
# Objeto con propiedad value numérica
value = int(value_input.value)
elif isinstance(value_input, str):
value = int(value_input)
elif hasattr(value_input, '__int__'):
# Cualquier objeto convertible a int (incluyendo sympy.Integer)
value = int(value_input) value = int(value_input)
else: else:
value = int(value_input) value = int(value_input)

View File

@ -1,46 +1,175 @@
""" """
Clase híbrida para números hexadecimales - ADAPTADA AL NUEVO SISTEMA Clase híbrida para números hexadecimales - ADAPTADA AL NUEVO SISTEMA
Ahora usa IntBase como clase base universal
""" """
from sympy_Base import SympyClassBase from sympy_Base import SympyClassBase, IntBase
import re import re
class Class_Hex(SympyClassBase): class Class_Hex(SympyClassBase):
"""Clase híbrida para números hexadecimales""" """Clase híbrida para números hexadecimales basada en IntBase"""
def __new__(cls, value_input):
"""Crear objeto SymPy válido"""
obj = SympyClassBase.__new__(cls)
return obj
def __init__(self, value_input): def __init__(self, value_input):
"""Inicialización de Hex""" """Inicialización de Hex usando IntBase como base"""
# Convertir input a entero
if isinstance(value_input, str):
# Remover prefijo 0x si existe
if value_input.startswith('0x'):
value_input = value_input[2:]
# Convertir a entero
value = int(value_input, 16)
else:
value = int(value_input)
# Llamar al constructor base if isinstance(value_input, IntBase):
super().__init__(value, f"0x{value:02x}") # value es IntBase (ya tokenizado desde 16#valor)
if value_input.base == 16:
self.int_base = value_input
else:
# Convertir de otra base a hexadecimal
if not value_input.has_symbols:
hex_value = IntBase._convert_to_base_string(value_input._numeric_value, 16)
self.int_base = IntBase(hex_value, 16)
else:
# Mantener simbólico para análisis algebraico
self.int_base = value_input
elif isinstance(value_input, str):
# Conversión desde string
clean_value = value_input
if clean_value.startswith('0x'):
clean_value = clean_value[2:]
self.int_base = IntBase(clean_value, 16)
elif isinstance(value_input, (int, float)) or hasattr(value_input, '__int__'):
# Conversión desde entero (incluyendo sympy.Integer)
try:
int_val = int(value_input)
hex_value = IntBase._convert_to_base_string(int_val, 16)
self.int_base = IntBase(hex_value, 16)
except (ValueError, TypeError):
raise TypeError(f"No se puede convertir {type(value_input)} a entero")
else:
raise TypeError(f"Hex no puede inicializarse con {type(value_input)}")
# Llamar al constructor base con el valor del IntBase
super().__init__(self.int_base.value, str(self.int_base))
def __str__(self): def __str__(self):
"""Representación string para display""" """Representación string para display"""
return f"0x{self._value:02x}" if self.int_base.has_symbols:
return f"0x{self.int_base.value_str}"
else:
return f"0x{self.int_base._numeric_value:02X}"
def _sympystr(self, printer): def _sympystr(self, printer):
"""Representación SymPy string""" """Representación SymPy string"""
return str(self) return str(self)
def __repr__(self):
return f"Hex({self.int_base.value_str})"
# ========== MÉTODOS DE CONVERSIÓN ==========
def toDecimal(self):
"""Convierte a decimal"""
if self.int_base.has_symbols:
return self.int_base._symbolic_value # Expresión simbólica
else:
return self.int_base._numeric_value
def toBinary(self):
"""Convierte a binario"""
if self.int_base.has_symbols:
# Para valores simbólicos, crear expresión de conversión
return f"Bin({self.int_base._symbolic_value})"
else:
bin_value = IntBase._convert_to_base_string(self.int_base._numeric_value, 2)
return f"0b{bin_value}"
def toOctal(self):
"""Convierte a octal"""
if self.int_base.has_symbols:
return f"Oct({self.int_base._symbolic_value})"
else:
oct_value = IntBase._convert_to_base_string(self.int_base._numeric_value, 8)
return f"0o{oct_value}"
def toBase(self, base):
"""Convierte a cualquier base"""
return self.int_base.to_base(base)
# ========== OPERADORES ARITMÉTICOS (delegados a IntBase) ==========
def __add__(self, other):
"""Suma delegada a IntBase"""
if isinstance(other, Class_Hex):
result_intbase = self.int_base + other.int_base
elif isinstance(other, IntBase):
result_intbase = self.int_base + other
elif isinstance(other, int):
result_intbase = self.int_base + other
else:
return NotImplemented
return Class_Hex(result_intbase)
def __sub__(self, other):
"""Resta delegada a IntBase"""
if isinstance(other, Class_Hex):
result_intbase = self.int_base - other.int_base
elif isinstance(other, IntBase):
result_intbase = self.int_base - other
elif isinstance(other, int):
result_intbase = self.int_base - other
else:
return NotImplemented
return Class_Hex(result_intbase)
def __mul__(self, other):
"""Multiplicación delegada a IntBase"""
if isinstance(other, Class_Hex):
result_intbase = self.int_base * other.int_base
elif isinstance(other, IntBase):
result_intbase = self.int_base * other
elif isinstance(other, int):
result_intbase = self.int_base * other
else:
return NotImplemented
return Class_Hex(result_intbase)
def __truediv__(self, other):
"""División delegada a IntBase"""
if isinstance(other, Class_Hex):
result_intbase = self.int_base / other.int_base
elif isinstance(other, IntBase):
result_intbase = self.int_base / other
elif isinstance(other, int):
result_intbase = self.int_base / other
else:
return NotImplemented
return Class_Hex(result_intbase)
def __mod__(self, other):
"""Módulo delegado a IntBase"""
if isinstance(other, Class_Hex):
result_intbase = self.int_base % other.int_base
elif isinstance(other, IntBase):
result_intbase = self.int_base % other
elif isinstance(other, int):
result_intbase = self.int_base % other
else:
return NotImplemented
return Class_Hex(result_intbase)
# Operadores reversos
def __radd__(self, other):
return self.__add__(other)
def __rmul__(self, other):
return self.__mul__(other)
# ========== MÉTODOS ESTÁTICOS ==========
@staticmethod @staticmethod
def Helper(input_str): def Helper(input_str):
"""Ayuda contextual para Hex""" """Ayuda contextual para Hex"""
if re.match(r"^\s*Hex\b", input_str, re.IGNORECASE): if re.match(r"^\s*Hex\b", input_str, re.IGNORECASE):
return 'Ej: Hex[FF], Hex[255]\nFunciones: toDecimal()' return ('Ej: 16#FF (se convierte automáticamente), Hex(255)\n'
'Funciones: toDecimal(), toBinary(), toOctal(), toBase(n)')
return None return None
@staticmethod @staticmethod
@ -48,22 +177,23 @@ class Class_Hex(SympyClassBase):
"""Lista de métodos sugeridos para autocompletado de Hex""" """Lista de métodos sugeridos para autocompletado de Hex"""
return [ return [
("toDecimal", "Convierte a decimal"), ("toDecimal", "Convierte a decimal"),
("toBinary", "Convierte a binario"),
("toOctal", "Convierte a octal"),
("toBase", "Convierte a base específica"),
] ]
def toDecimal(self):
"""Convierte a decimal"""
return self._value
# ========== FUNCIÓN DE REGISTRO ==========
def register_classes_in_module(): def register_classes_in_module():
""" """
Devuelve una lista de clases definidas en este módulo para ser registradas. Devuelve una lista de clases definidas en este módulo para ser registradas.
NOTA: Ya no se registra para corchetes, el tokenizador maneja 16#valor automáticamente
""" """
return [ return [
("Hex", Class_Hex, "SympyClassBase", { ("Hex", Class_Hex, "SympyClassBase", {
"add_lowercase": True, "add_lowercase": True,
"supports_brackets": True, "supports_brackets": False, # Ya no usa corchetes
"description": "Números hexadecimales" "description": "Números hexadecimales (usar 16#valor para tokenización automática)"
}), }),
] ]

View File

@ -1,8 +1,9 @@
""" """
Clase híbrida para direcciones IPv4 Clase híbrida para direcciones IPv4 - ADAPTADA AL NUEVO SISTEMA
Ahora usa FourBytes como clase base universal para direcciones IP
""" """
from class_base import ClassBase from class_base import ClassBase
from sympy_Base import SympyClassBase from sympy_Base import SympyClassBase, FourBytes, IntBase
from typing import Optional, Union, List, Tuple from typing import Optional, Union, List, Tuple
import re import re
import sympy import sympy
@ -10,16 +11,37 @@ import sympy
class IP4Mask(ClassBase): class IP4Mask(ClassBase):
""" """
Helper class to manage IPv4 masks. Helper class to manage IPv4 masks.
It can be initialized with an integer prefix (0-32) or a netmask string (e.g., "255.255.255.0"). Ahora soporta IntBase y FourBytes desde tokenización automática
""" """
_prefix: int _prefix: int
_mask_int: int _mask_int: int
def __init__(self, mask_input: Union[int, str]): def __init__(self, mask_input: Union[int, str, FourBytes, IntBase]):
prefix = self._parse_mask(mask_input) if isinstance(mask_input, FourBytes):
super().__init__(prefix, str(mask_input)) # Máscara desde tokenización automática x.x.x.x
self._prefix = prefix if not mask_input.has_symbols:
self._mask_int = self._prefix_to_mask_int(self._prefix) 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):
# Máscara desde hex u otra base (ej: 16#ffffff00)
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:
# Procesamiento tradicional
prefix = self._parse_mask(mask_input)
self._prefix = prefix
self._mask_int = self._prefix_to_mask_int(self._prefix)
super().__init__(self._mask_int, str(mask_input))
def _parse_mask(self, mask_input: Union[int, str]) -> int: def _parse_mask(self, mask_input: Union[int, str]) -> int:
"""Helper to parse mask_input and return prefix.""" """Helper to parse mask_input and return prefix."""
@ -80,6 +102,19 @@ class IP4Mask(ClassBase):
return 0 return 0
return (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF return (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF
@staticmethod
def _mask_int_to_prefix(mask_int: int) -> int:
"""Convierte entero de máscara a prefijo CIDR"""
if mask_int == 0:
return 0
# Convertir a binario y verificar que sea una máscara válida
binary_mask = bin(mask_int)[2:].zfill(32)
if not re.fullmatch(r"1*0*", binary_mask): # Must be contiguous 1s followed by 0s
raise ValueError(f"Invalid mask: 0x{mask_int:08X} - bits not contiguous")
return binary_mask.count('1')
def get_mask_str(self) -> str: def get_mask_str(self) -> str:
return f"{(self._mask_int >> 24) & 0xFF}.{(self._mask_int >> 16) & 0xFF}.{(self._mask_int >> 8) & 0xFF}.{self._mask_int & 0xFF}" return f"{(self._mask_int >> 24) & 0xFF}.{(self._mask_int >> 16) & 0xFF}.{(self._mask_int >> 8) & 0xFF}.{self._mask_int & 0xFF}"
@ -282,116 +317,124 @@ class IP4Mask(ClassBase):
class Class_IP4(SympyClassBase): class Class_IP4(SympyClassBase):
"""Clase híbrida para direcciones IPv4""" """Clase híbrida para direcciones IPv4 - ADAPTADA AL NUEVO SISTEMA"""
def __new__(cls, *args, **kwargs): def __init__(self, address, mask=None):
temp_ip_str_candidate = args[0] if args else "" """
temp_ip_str = "" Constructor mejorado que acepta FourBytes desde tokenización automática
if isinstance(temp_ip_str_candidate, str):
if '/' in temp_ip_str_candidate: Args:
temp_ip_str = temp_ip_str_candidate.split('/', 1)[0].strip() address: FourBytes (tokenizado automáticamente), string, o Class_IP4 existente
elif ' ' in temp_ip_str_candidate: mask: int, string, IP4Mask, FourBytes, o IntBase
temp_ip_str = temp_ip_str_candidate.split(' ', 1)[0].strip() """
else:
temp_ip_str = temp_ip_str_candidate.strip() # Procesar dirección
if isinstance(address, FourBytes):
# address es FourBytes (ya tokenizado desde x.x.x.x)
self.address = address
self._ip_str = address.original
if IP4Mask._is_valid_ip_string(temp_ip_str): if address.has_symbols:
ip_parts_for_new = [int(x) for x in temp_ip_str.split('.')] # Modo algebraico
ip_int_for_new = (ip_parts_for_new[0] << 24) | \ self._ip_int = address._symbolic_value
(ip_parts_for_new[1] << 16) | \ self._has_symbols = True
(ip_parts_for_new[2] << 8) | \
ip_parts_for_new[3]
obj = SympyClassBase.__new__(cls, ip_int_for_new)
else: else:
raise ValueError(f"Invalid IP address format for __new__: {temp_ip_str}") # Modo numérico
self._ip_int = address._numeric_value
self._has_symbols = False
elif isinstance(address, str):
# String tradicional - puede incluir máscara
if '/' in address:
addr_part, mask_part = address.split('/', 1)
self._ip_str = addr_part.strip()
if mask is None:
mask = mask_part.strip()
elif ' ' in address:
addr_part, mask_part = address.split(' ', 1)
self._ip_str = addr_part.strip()
if mask is None:
mask = mask_part.strip()
else:
self._ip_str = address.strip()
# Crear FourBytes manualmente para consistencia
self.address = FourBytes(self._ip_str)
if self.address.has_symbols:
self._ip_int = self.address._symbolic_value
self._has_symbols = True
else:
self._ip_int = self.address._numeric_value
self._has_symbols = False
else: else:
if not args: raise TypeError(f"address debe ser FourBytes o string, recibido: {type(address)}")
raise ValueError("IP4 constructor requires arguments.")
obj = SympyClassBase.__new__(cls, *args) # Procesar máscara
if mask is not None:
return obj if isinstance(mask, IP4Mask):
self._mask_obj = mask
elif isinstance(mask, (int, str)):
self._mask_obj = IP4Mask(mask)
elif isinstance(mask, FourBytes):
# Máscara desde tokenización (ej: 255.255.255.0)
self._mask_obj = IP4Mask(mask)
elif isinstance(mask, IntBase):
# Máscara desde hex (ej: 16#ffffff00)
self._mask_obj = IP4Mask(mask)
else:
raise TypeError(f"mask debe ser int, str, IP4Mask, FourBytes, o IntBase")
else:
self._mask_obj = None
# Llamar al constructor base de SymPy
if self._has_symbols:
super().__init__(self._ip_int, str(self.address))
else:
super().__init__(self._ip_int, self._ip_str)
def __init__(self, *args):
self._raw_constructor_args = args
ip_str: str
self._mask_obj: Optional[IP4Mask] = None
if not args:
raise ValueError("IP4 constructor requires at least one argument (the IP string).")
ip_str_candidate = args[0]
if not isinstance(ip_str_candidate, str):
raise TypeError(f"First argument to IP4 must be a string (IP address), got {type(ip_str_candidate)}")
if len(args) == 1:
input_str = args[0]
if '/' in input_str:
parts = input_str.split('/', 1)
ip_str = parts[0].strip()
if len(parts) > 1 and parts[1].strip():
self._mask_obj = IP4Mask(parts[1].strip())
elif ' ' in input_str:
parts = input_str.split(' ', 1)
ip_str = parts[0].strip()
if len(parts) > 1 and parts[1].strip():
self._mask_obj = IP4Mask(parts[1].strip())
else:
ip_str = input_str.strip()
elif len(args) == 2:
ip_str = args[0].strip()
mask_arg = args[1]
if isinstance(mask_arg, IP4Mask):
self._mask_obj = mask_arg
elif isinstance(mask_arg, (str, int)):
self._mask_obj = IP4Mask(mask_arg)
else:
raise TypeError(f"Second argument (mask) for IP4 must be int, str, or IP4Mask instance, got {type(mask_arg)}")
else:
raise ValueError(f"IP4 constructor takes 1 or 2 arguments, got {len(args)}: {args}")
if not IP4Mask._is_valid_ip_string(ip_str):
raise ValueError(f"Invalid IP address format: {ip_str}")
self._ip_str = ip_str
ip_parts = [int(x) for x in ip_str.split('.')]
self._ip_int = (ip_parts[0] << 24) | (ip_parts[1] << 16) | (ip_parts[2] << 8) | ip_parts[3]
sympy_base_original_str: str
if len(args) == 1 and isinstance(args[0], str):
sympy_base_original_str = args[0]
elif len(args) == 2:
mask_arg_for_repr = args[1]
if isinstance(mask_arg_for_repr, IP4Mask):
mask_repr_str = str(mask_arg_for_repr.get_prefix_int())
elif isinstance(mask_arg_for_repr, int):
mask_repr_str = str(mask_arg_for_repr)
else:
mask_repr_str = str(mask_arg_for_repr)
sympy_base_original_str = f"{args[0]};{mask_repr_str}"
else:
sympy_base_original_str = self._ip_str
super().__init__(self._ip_int, sympy_base_original_str)
def __repr__(self): def __repr__(self):
arg_reprs = [repr(arg) for arg in self._raw_constructor_args] if self._mask_obj:
return f"{self.__class__.__name__}({', '.join(arg_reprs)})" return f"IP4({self._ip_str!r}, {self._mask_obj.get_prefix_int()})"
return f"IP4({self._ip_str!r})"
def __str__(self): def __str__(self):
if self._mask_obj: if self._mask_obj:
return f"{self._ip_str}/{self._mask_obj.get_prefix_int()}" if self._mask_obj._prefix is not None:
return f"{self._ip_str}/{self._mask_obj._prefix}"
else:
return f"{self._ip_str}/{self._mask_obj}"
return self._ip_str return self._ip_str
def _sympystr(self, printer): def _sympystr(self, printer):
return str(self) return str(self)
# ========== MÉTODOS PARA TRABAJAR CON SÍMBOLOS ==========
def substitute(self, **kwargs):
"""Sustituye símbolos por valores"""
if not self._has_symbols:
return self
new_address = self.address.substitute(**kwargs)
return Class_IP4(new_address, self._mask_obj)
def get_symbols(self):
"""Obtiene símbolos en la dirección"""
if self._has_symbols:
return [elem for elem in self.address.elements if not elem.isdigit()]
return []
def is_symbolic(self):
"""¿Contiene símbolos?"""
return self._has_symbols
@staticmethod @staticmethod
def Helper(input_str): def Helper(input_str):
if re.match(r"^\s*IP4\b", input_str, re.IGNORECASE): if re.match(r"^\s*IP4\b", input_str, re.IGNORECASE):
return ('Ej: IP4[192.168.1.1/24], IP4[10.0.0.1;8]. Funciones: NetworkAddress(), BroadcastAddress(), add_hosts(), next_ip(), mask()') return ('Ej: 192.168.1.1 (se tokeniza automáticamente), IP4(192.168.1.1, 24)\n'
'Soporte algebraico: 10.x.y.0/24, substitute(x=1, y=2)\n'
'Funciones: NetworkAddress(), BroadcastAddress(), add_hosts(), next_ip(), mask()')
return None return None
@staticmethod @staticmethod
@ -401,6 +444,9 @@ class Class_IP4(SympyClassBase):
("BroadcastAddress", "Dirección de broadcast"), ("BroadcastAddress", "Dirección de broadcast"),
("Nodes", "Hosts disponibles"), ("Nodes", "Hosts disponibles"),
("mask", "Objeto máscara con sus métodos"), ("mask", "Objeto máscara con sus métodos"),
("substitute", "Sustituir símbolos por valores"),
("get_symbols", "Obtener símbolos en la dirección"),
("is_symbolic", "¿Contiene símbolos?"),
("add_hosts", "Sumar N hosts a la dirección"), ("add_hosts", "Sumar N hosts a la dirección"),
("subtract_hosts", "Restar N hosts a la dirección"), ("subtract_hosts", "Restar N hosts a la dirección"),
("next_ip", "Siguiente dirección IP"), ("next_ip", "Siguiente dirección IP"),
@ -496,15 +542,33 @@ class Class_IP4(SympyClassBase):
def add_hosts(self, n: int) -> 'Class_IP4': def add_hosts(self, n: int) -> 'Class_IP4':
"""Suma N hosts a la dirección""" """Suma N hosts a la dirección"""
new_ip_int = (self._ip_int + n) & 0xFFFFFFFF if self._has_symbols:
new_parts = [ # NUEVO: Para IPs simbólicas, crear nueva instancia con expresión simbólica actualizada
(new_ip_int >> 24) & 0xFF, new_ip_expr = self._ip_int + n
(new_ip_int >> 16) & 0xFF,
(new_ip_int >> 8) & 0xFF, # Crear nueva instancia directamente con la expresión simbólica
new_ip_int & 0xFF new_ip = Class_IP4.__new__(Class_IP4)
] new_ip._ip_int = new_ip_expr
new_ip_str = '.'.join(str(x) for x in new_parts) new_ip._has_symbols = True
return Class_IP4(new_ip_str, self._mask_obj) new_ip._ip_str = f"({new_ip_expr})"
new_ip._mask_obj = self._mask_obj
new_ip.address = self.address # Mantener la referencia al FourBytes original
# Llamar constructor base SymPy
SympyClassBase.__init__(new_ip, new_ip_expr, new_ip._ip_str)
return new_ip
else:
# Modo numérico original
new_ip_int = (self._ip_int + n) & 0xFFFFFFFF
new_parts = [
(new_ip_int >> 24) & 0xFF,
(new_ip_int >> 16) & 0xFF,
(new_ip_int >> 8) & 0xFF,
new_ip_int & 0xFF
]
new_ip_str = '.'.join(str(x) for x in new_parts)
return Class_IP4(new_ip_str, self._mask_obj)
def subtract_hosts(self, n: int) -> 'Class_IP4': def subtract_hosts(self, n: int) -> 'Class_IP4':
"""Resta N hosts a la dirección""" """Resta N hosts a la dirección"""
@ -882,16 +946,17 @@ class Class_IP4(SympyClassBase):
def register_classes_in_module(): def register_classes_in_module():
""" """
Devuelve una lista de clases definidas en este módulo para ser registradas. Devuelve una lista de clases definidas en este módulo para ser registradas.
NOTA: Ya no se registra para corchetes, el tokenizador maneja x.x.x.x automáticamente
""" """
return [ return [
("IP4", Class_IP4, "SympyClassBase", { ("IP4", Class_IP4, "SympyClassBase", {
"add_lowercase": True, "add_lowercase": True,
"supports_brackets": True, "supports_brackets": False, # Ya no usa corchetes
"description": "Direcciones IPv4 con máscara" "description": "Direcciones IPv4 con máscara (usar x.x.x.x para tokenización automática)"
}), }),
("IP4Mask", IP4Mask, "ClassBase", { ("IP4Mask", IP4Mask, "ClassBase", {
"add_lowercase": True, "add_lowercase": True,
"supports_brackets": True, "supports_brackets": False, # Ya no usa corchetes
"description": "Máscaras de red IPv4" "description": "Máscaras de red IPv4"
}), }),
] ]
@ -902,15 +967,20 @@ def register_classes_in_module():
def get_module_info(): def get_module_info():
"""Información adicional sobre este módulo""" """Información adicional sobre este módulo"""
return { return {
"name": "IPv4 Network Types", "name": "IPv4 Network Types - Tokenization System",
"description": "Clases para manejo de direcciones IP y máscaras de red", "description": "Clases para manejo de direcciones IP y máscaras con tokenización automática",
"version": "1.0", "version": "2.0",
"depends_on": ["class_base", "sympy_Base"], "depends_on": ["class_base", "sympy_Base"],
"examples": [ "examples": [
"IP4[192.168.1.1/24]", "192.168.1.1 # → FourBytes('192.168.1.1') automáticamente",
"IP4[10.0.0.1;255.255.255.0]", "IP4(192.168.1.1, 24)",
"IP4[192.168.1.1/24].NetworkAddress()", "ip = 10.x.y.0; ip.substitute(x=1, y=2)",
"IP4Mask[24]", "mask = 16#ffffff00 # → IntBase('ffffff00', 16)",
"IP4Mask[255.255.255.0]" "net = IP4(192.168.1.0, mask)",
],
"tokenization_patterns": [
"x.x.x.x → FourBytes('x.x.x.x')",
"16#ffffff00 → IntBase('ffffff00', 16)",
"2#11111111 → IntBase('11111111', 2)"
] ]
} }

View File

@ -1,10 +1,19 @@
m=IP4Mask[255.240.0.0] mm=255.240.0.x
IP4(mm,24)
j=IP4(mm,24)
x=5
mm
j.substitute(x=1)
j.next_ip()
m.get_prefix_int() hh=16#ff
m.hosts_count() Dec(hh)
i=IP4("0.1.30.70",m.get_prefix_int()) h=16#ff + 25
h
9*3*36.0 IP4Mask(255.240.0.0)
IP4(10.1.280.1)
IP4(10.1.x.1)

View File

@ -1,6 +1,6 @@
{ {
"window_geometry": "1020x700+708+147", "window_geometry": "1020x700+236+1240",
"sash_pos_x": 368, "sash_pos_x": 360,
"symbolic_mode": true, "symbolic_mode": true,
"show_numeric_approximation": true, "show_numeric_approximation": true,
"keep_symbolic_fractions": true, "keep_symbolic_fractions": true,

View File

@ -20,6 +20,14 @@ from type_registry import (
from tl_bracket_parser import BracketParser from tl_bracket_parser import BracketParser
from tl_popup import PlotResult from tl_popup import PlotResult
# ========== NUEVO: Importar clases base para tokenización ==========
try:
from sympy_Base import IntBase, FourBytes
BASE_CLASSES_AVAILABLE = True
except ImportError:
BASE_CLASSES_AVAILABLE = False
print("⚠️ Clases base IntBase/FourBytes no disponibles")
class HybridEvaluationEngine: class HybridEvaluationEngine:
""" """
@ -28,7 +36,12 @@ class HybridEvaluationEngine:
""" """
def __init__(self, auto_discover_types: bool = True, types_directory: str = "custom_types"): def __init__(self, auto_discover_types: bool = True, types_directory: str = "custom_types"):
self.parser = BracketParser() # ========== NUEVO: Inicializar parser con tokenización habilitada ==========
self.parser = BracketParser(use_type_registry=True)
self.parser.use_tokenizer = BASE_CLASSES_AVAILABLE # Habilitar tokenizador si clases disponibles
if self.parser.use_tokenizer:
self.parser.debug = False # Será habilitado por self.debug más adelante
self.symbol_table: Dict[str, Any] = {} self.symbol_table: Dict[str, Any] = {}
self.equations: List[sympy.Eq] = [] self.equations: List[sympy.Eq] = []
self.last_result = None self.last_result = None
@ -134,11 +147,22 @@ class HybridEvaluationEngine:
'evalf': lambda expr, n=15: expr.evalf(n) if hasattr(expr, 'evalf') else float(expr), 'evalf': lambda expr, n=15: expr.evalf(n) if hasattr(expr, 'evalf') else float(expr),
} }
# ========== NUEVO: 4.5. CLASES BASE PARA TOKENIZACIÓN ==========
base_classes = {}
if BASE_CLASSES_AVAILABLE:
base_classes = {
'IntBase': IntBase,
'FourBytes': FourBytes,
}
if self.debug:
print("🔧 Clases base IntBase/FourBytes añadidas al contexto")
# 5. COMBINAR TODO EN EL CONTEXTO BASE # 5. COMBINAR TODO EN EL CONTEXTO BASE
self.base_context = { self.base_context = {
**math_functions, **math_functions,
**specialized_classes, **specialized_classes,
**utility_functions **utility_functions,
**base_classes # ← NUEVO: Incluir clases base
} }
# 6. ACTUALIZAR HELPER FUNCTIONS # 6. ACTUALIZAR HELPER FUNCTIONS
@ -150,6 +174,10 @@ class HybridEvaluationEngine:
if self.debug: if self.debug:
print(f"📋 Contexto base configurado: {len(self.base_context)} entradas") print(f"📋 Contexto base configurado: {len(self.base_context)} entradas")
print(f"🆘 Helper functions: {len(self.helper_functions)}") print(f"🆘 Helper functions: {len(self.helper_functions)}")
# ========== NUEVO: Sincronizar debug con parser ==========
if hasattr(self.parser, 'debug'):
self.parser.debug = self.debug
def _update_bracket_parser(self): def _update_bracket_parser(self):
"""Actualiza el BracketParser con las clases descubiertas""" """Actualiza el BracketParser con las clases descubiertas"""
@ -233,6 +261,7 @@ class HybridEvaluationEngine:
def evaluate_line(self, line: str) -> 'EvaluationResult': def evaluate_line(self, line: str) -> 'EvaluationResult':
""" """
Evalúa una línea de código y retorna el resultado Evalúa una línea de código y retorna el resultado
NUEVA LÓGICA: Priorizar asignaciones, intentar ecuaciones silenciosamente
""" """
try: try:
# 1. Parsear la línea # 1. Parsear la línea
@ -244,10 +273,20 @@ class HybridEvaluationEngine:
# 2. Manejar casos especiales # 2. Manejar casos especiales
if parse_info == "comment": if parse_info == "comment":
return EvaluationResult(None, "comment", original_line=line) return EvaluationResult(None, "comment", original_line=line)
elif parse_info == "assignment":
# NUEVA LÓGICA: Para asignaciones, también intentar agregar como ecuación silenciosamente
assignment_result = self._evaluate_assignment(parsed_line, line)
# Intentar agregar como ecuación silenciosamente (sin mostrar errores)
if not assignment_result.is_error:
try:
self._add_equation_silently(line)
except:
pass # Ignorar errores de ecuación
return assignment_result
elif parse_info == "equation": elif parse_info == "equation":
return self._evaluate_equation_addition(parsed_line, line) return self._evaluate_equation_addition(parsed_line, line)
elif parse_info == "assignment":
return self._evaluate_assignment(parsed_line, line)
# 3. Evaluación SymPy # 3. Evaluación SymPy
return self._evaluate_sympy_expression(parsed_line, parse_info, line) return self._evaluate_sympy_expression(parsed_line, parse_info, line)
@ -277,20 +316,32 @@ class HybridEvaluationEngine:
if self.show_numeric_approximation and hasattr(assigned_value, 'evalf'): if self.show_numeric_approximation and hasattr(assigned_value, 'evalf'):
try: try:
numeric_eval = assigned_value.evalf() numeric_eval = assigned_value.evalf()
# Verificar si el resultado numérico es diferente del simbólico # MEJORADO: Solo mostrar aproximación si es realmente útil
# Para fracciones racionales, siempre mostrar la aproximación decimal
if hasattr(assigned_value, 'is_Rational') and assigned_value.is_Rational: if hasattr(assigned_value, 'is_Rational') and assigned_value.is_Rational:
# Es una fracción racional, mostrar aproximación decimal # Es una fracción racional, mostrar aproximación decimal
numeric_result = numeric_eval numeric_result = numeric_eval
elif hasattr(assigned_value, 'is_Integer') and assigned_value.is_Integer:
# Es un entero SymPy, no mostrar aproximación
numeric_result = None
elif hasattr(assigned_value, 'is_number') and assigned_value.is_number:
# Es un número, verificar si la aproximación es diferente significativamente
try:
if abs(float(numeric_eval) - float(assigned_value)) > 1e-10:
numeric_result = numeric_eval
except:
# Si no se puede comparar, mostrar solo si el string es diferente
if str(numeric_eval) != str(assigned_value):
numeric_result = numeric_eval
elif numeric_eval != assigned_value: elif numeric_eval != assigned_value:
# Para otros casos, mostrar si son diferentes # Para otros casos, mostrar si son diferentes
try: try:
# Intentar comparación numérica más robusta # Intentar comparación numérica más robusta
if abs(float(numeric_eval) - float(assigned_value)) > 1e-15: if abs(float(numeric_eval) - float(assigned_value)) > 1e-10:
numeric_result = numeric_eval numeric_result = numeric_eval
except: except:
# Si la comparación falla, asumir que son diferentes # Si la comparación falla, asumir que son diferentes solo si el string es diferente
numeric_result = numeric_eval if str(numeric_eval) != str(assigned_value):
numeric_result = numeric_eval
except Exception as e: except Exception as e:
if self.debug: if self.debug:
print(f"DEBUG: Error en evaluación numérica: {e}") print(f"DEBUG: Error en evaluación numérica: {e}")
@ -345,8 +396,23 @@ class HybridEvaluationEngine:
if self.show_numeric_approximation and hasattr(result, 'evalf'): if self.show_numeric_approximation and hasattr(result, 'evalf'):
try: try:
numeric_eval = result.evalf() numeric_eval = result.evalf()
# Solo mostrar evaluación numérica si es diferente del resultado simbólico # MEJORADO: Solo mostrar evaluación numérica si es realmente útil
if (str(numeric_eval) != str(result) and numeric_eval != result and if hasattr(result, 'is_Integer') and result.is_Integer:
# Es un entero SymPy, no mostrar aproximación
numeric_result = None
elif hasattr(result, 'is_Rational') and result.is_Rational:
# Es una fracción racional, mostrar aproximación decimal
numeric_result = numeric_eval
elif hasattr(result, 'is_number') and result.is_number:
# Es un número, verificar si la aproximación es diferente significativamente
try:
if abs(float(numeric_eval) - float(result)) > 1e-10:
numeric_result = numeric_eval
except:
# Si no se puede comparar, mostrar solo si el string es diferente
if str(numeric_eval) != str(result):
numeric_result = numeric_eval
elif (str(numeric_eval) != str(result) and numeric_eval != result and
not (isinstance(result, (int, float)) or not (isinstance(result, (int, float)) or
(hasattr(result, 'is_number') and result.is_number and (hasattr(result, 'is_number') and result.is_number and
hasattr(result, 'is_Integer') and result.is_Integer))): hasattr(result, 'is_Integer') and result.is_Integer))):
@ -577,6 +643,34 @@ class HybridEvaluationEngine:
except Exception as e: except Exception as e:
raise ValueError(f"Error parseando ecuación '{equation_str}': {e}") raise ValueError(f"Error parseando ecuación '{equation_str}': {e}")
def _add_equation_silently(self, equation_str: str) -> bool:
"""
Intenta agregar una ecuación al sistema silenciosamente
Retorna True si tuvo éxito, False si falló
NUEVA FUNCIÓN: Para agregar ecuaciones sin mostrar errores
"""
try:
# Parsear ecuación
if '=' in equation_str and '==' not in equation_str:
# Ecuación simple: convertir a igualdad SymPy
left, right = equation_str.split('=', 1)
left_expr = sympify(left.strip(), locals=self._get_full_context())
right_expr = sympify(right.strip(), locals=self._get_full_context())
equation = Eq(left_expr, right_expr)
else:
# Ya es una comparación válida de SymPy
equation = sympify(equation_str, locals=self._get_full_context())
self.equations.append(equation)
if self.debug:
print(f"🔍 Ecuación agregada silenciosamente: {equation}")
return True
except Exception as e:
if self.debug:
print(f"🔍 No se pudo agregar como ecuación: {e}")
return False
def solve_system(self, variables: Optional[List[str]] = None) -> Dict[str, Any]: def solve_system(self, variables: Optional[List[str]] = None) -> Dict[str, Any]:
"""Resuelve el sistema de ecuaciones""" """Resuelve el sistema de ecuaciones"""
if not self.equations: if not self.equations:

View File

@ -246,4 +246,409 @@ class SympyClassBase(ClassBase, sympy.Basic):
@staticmethod @staticmethod
def Helper(input_str): def Helper(input_str):
"""Override en subclases para ayuda contextual""" """Override en subclases para ayuda contextual"""
return None return None
# ========== CLASES BASE UNIVERSALES PARA TOKENIZACIÓN ==========
class IntBase(SympyClassBase):
"""
Representación universal de números en cualquier base
con soporte algebraico completo y operaciones aritméticas
"""
def __init__(self, value_str, base=10):
self.value_str = str(value_str)
self.base = int(base)
# Detección mejorada de símbolos: considerar la base
self.has_symbols = self._has_symbols_for_base()
if self.has_symbols:
# Modo algebraico: mantener como expresión simbólica
self._symbolic_value = self._parse_symbolic_base()
self._numeric_value = None # No hay valor numérico
super().__init__(self._symbolic_value, f"{self.base}#{self.value_str}")
else:
# Modo numérico: convertir a entero
self._numeric_value = int(self.value_str, self.base)
self._symbolic_value = None # No hay valor simbólico
super().__init__(self._numeric_value, f"{self.base}#{self.value_str}")
def _has_symbols_for_base(self):
"""Detecta si hay símbolos considerando la base numérica"""
valid_digits = "0123456789ABCDEF"[:self.base]
for char in self.value_str.upper():
if char not in valid_digits:
# Es un símbolo (no un dígito válido para esta base)
return True
return False
def _parse_symbolic_base(self):
"""Convierte valor con símbolos a expresión SymPy"""
# Ejemplo: "x0" base 16 → x*16^1 + 0*16^0
# Ejemplo: "101x" base 2 → 1*2^3 + 0*2^2 + 1*2^1 + x*2^0
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
def to_base(self, new_base):
"""Convierte a nueva base"""
if self.has_symbols:
# En modo algebraico, mantener expresión
return IntBase(f"({self._symbolic_value})_base{new_base}", new_base)
else:
# En modo numérico, convertir directamente
new_value_str = self._convert_to_base_string(self._numeric_value, new_base)
return IntBase(new_value_str, new_base)
@staticmethod
def _convert_to_base_string(value, base):
"""Convierte entero a string en base específica"""
if value == 0:
return "0"
digits = "0123456789ABCDEF"
result = ""
while value:
result = digits[value % base] + result
value //= base
return result
@property
def value(self):
"""Propiedad para acceder al valor (numérico o simbólico)"""
if self.has_symbols:
return self._symbolic_value
else:
return self._numeric_value
# ========== OPERADORES ARITMÉTICOS ==========
def __add__(self, other):
"""Suma: mantiene la base del operando izquierdo"""
if self.has_symbols:
return super().__add__(other) # Delegar a SymPy
if isinstance(other, IntBase):
if other.has_symbols:
return super().__add__(other)
result_value = self._numeric_value + other._numeric_value
elif isinstance(other, int):
result_value = self._numeric_value + other
else:
return super().__add__(other)
result_str = self._convert_to_base_string(result_value, self.base)
return IntBase(result_str, self.base)
def __sub__(self, other):
"""Resta: mantiene la base del operando izquierdo"""
if self.has_symbols:
return super().__sub__(other)
if isinstance(other, IntBase):
if other.has_symbols:
return super().__sub__(other)
result_value = self._numeric_value - other._numeric_value
elif isinstance(other, int):
result_value = self._numeric_value - other
else:
return super().__sub__(other)
result_str = self._convert_to_base_string(result_value, self.base)
return IntBase(result_str, self.base)
def __mul__(self, other):
"""Multiplicación: mantiene la base del operando izquierdo"""
if self.has_symbols:
return super().__mul__(other)
if isinstance(other, IntBase):
if other.has_symbols:
return super().__mul__(other)
result_value = self._numeric_value * other._numeric_value
elif isinstance(other, int):
result_value = self._numeric_value * other
else:
return super().__mul__(other)
result_str = self._convert_to_base_string(result_value, self.base)
return IntBase(result_str, self.base)
def __truediv__(self, other):
"""División: mantiene la base del operando izquierdo"""
if self.has_symbols:
return super().__truediv__(other)
if isinstance(other, IntBase):
if other.has_symbols:
return super().__truediv__(other)
result_value = self._numeric_value // other._numeric_value # División entera
elif isinstance(other, int):
result_value = self._numeric_value // other
else:
return super().__truediv__(other)
result_str = self._convert_to_base_string(result_value, self.base)
return IntBase(result_str, self.base)
def __mod__(self, other):
"""Módulo: mantiene la base del operando izquierdo"""
if self.has_symbols:
return super().__mod__(other)
if isinstance(other, IntBase):
if other.has_symbols:
return super().__mod__(other)
result_value = self._numeric_value % other._numeric_value
elif isinstance(other, int):
result_value = self._numeric_value % other
else:
return super().__mod__(other)
result_str = self._convert_to_base_string(result_value, self.base)
return IntBase(result_str, self.base)
# Operadores reversos para int + IntBase
def __radd__(self, other):
return self.__add__(other)
def __rsub__(self, other):
if isinstance(other, int):
result_value = other - self._numeric_value
result_str = self._convert_to_base_string(result_value, self.base)
return IntBase(result_str, self.base)
return super().__rsub__(other)
def __rmul__(self, other):
return self.__mul__(other)
def __str__(self):
"""Representación string"""
return f"{self.base}#{self.value_str}"
def __repr__(self):
return f"IntBase('{self.value_str}', {self.base})"
class FourBytes(SympyClassBase):
"""
Representación universal de patrones de 4 elementos
con soporte algebraico completo y operaciones aritméticas
"""
def __init__(self, dotted_string):
self.original = str(dotted_string)
self.elements = self.original.split('.')
if len(self.elements) != 4:
raise ValueError(f"FourBytes requiere exactamente 4 elementos: {dotted_string}")
self.has_symbols = any(not elem.isdigit() for elem in self.elements)
if self.has_symbols:
# Modo algebraico: crear expresión simbólica
self._symbolic_elements = []
for elem in self.elements:
if elem.isdigit():
self._symbolic_elements.append(int(elem))
else:
self._symbolic_elements.append(sympy.Symbol(elem))
# CORREGIDO: Crear expresión escalar en lugar de Matrix para compatibilidad con IP4
# Convertir a representación de entero de 32 bits simbólico
self._symbolic_value = (self._symbolic_elements[0] * 2**24 +
self._symbolic_elements[1] * 2**16 +
self._symbolic_elements[2] * 2**8 +
self._symbolic_elements[3])
super().__init__(self._symbolic_value, dotted_string)
else:
# Modo numérico: validar rangos y convertir
self._numeric_elements = [int(elem) for elem in self.elements]
# Crear valor como entero de 32 bits (para IPs)
self._numeric_value = (self._numeric_elements[0] << 24 |
self._numeric_elements[1] << 16 |
self._numeric_elements[2] << 8 |
self._numeric_elements[3])
super().__init__(self._numeric_value, dotted_string)
def is_valid_ip_range(self):
"""Verifica si todos los elementos numéricos están en rango IP"""
if self.has_symbols:
return None # No se puede validar con símbolos
return all(0 <= x <= 255 for x in self._numeric_elements)
def substitute(self, **kwargs):
"""Sustituye símbolos por valores"""
if not self.has_symbols:
return self
new_elements = []
for elem in self.elements:
if elem in kwargs:
new_elements.append(str(kwargs[elem]))
else:
new_elements.append(elem)
return FourBytes('.'.join(new_elements))
def __getitem__(self, index):
"""Acceso a elementos individuales"""
if self.has_symbols:
return self._symbolic_elements[index]
else:
return self._numeric_elements[index]
def to_ip_int(self):
"""Convierte a entero de 32 bits (para IPs)"""
if self.has_symbols:
# CORREGIDO: Ya tenemos la expresión escalar directamente
return self._symbolic_value
else:
return self._numeric_value
@staticmethod
def _int_to_fourbytes(value):
"""Convierte entero de 32 bits a formato x.y.z.w"""
w = value & 0xFF
z = (value >> 8) & 0xFF
y = (value >> 16) & 0xFF
x = (value >> 24) & 0xFF
return f"{x}.{y}.{z}.{w}"
# ========== OPERADORES ARITMÉTICOS ==========
def __add__(self, other):
"""Suma: convierte a int, opera, reconvierte a FourBytes"""
if self.has_symbols:
return super().__add__(other) # Delegar a SymPy
if isinstance(other, FourBytes):
if other.has_symbols:
return super().__add__(other)
result_int = self._numeric_value + other._numeric_value
elif isinstance(other, int):
result_int = self._numeric_value + other
else:
return super().__add__(other)
# Mantener en rango de 32 bits
result_int = result_int & 0xFFFFFFFF
result_str = self._int_to_fourbytes(result_int)
return FourBytes(result_str)
def __sub__(self, other):
"""Resta: convierte a int, opera, reconvierte a FourBytes"""
if self.has_symbols:
return super().__sub__(other)
if isinstance(other, FourBytes):
if other.has_symbols:
return super().__sub__(other)
result_int = self._numeric_value - other._numeric_value
elif isinstance(other, int):
result_int = self._numeric_value - other
else:
return super().__sub__(other)
# Mantener en rango de 32 bits (underflow se convierte en valor alto)
result_int = result_int & 0xFFFFFFFF
result_str = self._int_to_fourbytes(result_int)
return FourBytes(result_str)
# ========== CONVERSIONES DE BASE ==========
def ToBase(self, base):
"""
Convierte cada elemento a la base especificada
Retorna expresión con cada elemento convertido
"""
if self.has_symbols:
# Para elementos simbólicos, retornar expresión algebraica
converted_elements = []
for elem in self.elements:
if elem.isdigit():
int_val = int(elem)
converted = IntBase._convert_to_base_string(int_val, base)
converted_elements.append(f"{base}#{converted}")
else:
# Símbolo: expresar como conversión algebraica
converted_elements.append(f"{base}#{elem}")
return '.'.join(converted_elements)
else:
# Para elementos numéricos, conversión directa
converted_elements = []
for elem_val in self._numeric_elements:
converted = IntBase._convert_to_base_string(elem_val, base)
converted_elements.append(f"{base}#{converted}")
return '.'.join(converted_elements)
def ToCIDR(self, prefix_length):
"""Convierte a notación CIDR"""
return f"{self.original}/{prefix_length}"
def ToHex(self):
"""Convierte cada elemento a hexadecimal"""
return self.ToBase(16)
def ToBinary(self):
"""Convierte cada elemento a binario"""
return self.ToBase(2)
def ToOctal(self):
"""Convierte cada elemento a octal"""
return self.ToBase(8)
def __str__(self):
"""Representación string"""
return self.original
def __repr__(self):
return f"FourBytes('{self.original}')"
# ========== TOKENIZADOR ALGEBRAICO ==========
def preprocess_tokens(expression):
"""
Tokenizador que convierte patrones específicos en objetos tipados
con precedencia correcta: IntBase (más específico) antes que FourBytes
"""
# 1. MAYOR PRECEDENCIA: IntBase (patrón más específico)
# Pattern: base#valor
expression = re.sub(r'(\d+)#([0-9A-Fa-fx]+)', r'IntBase("\2", \1)', expression)
# 2. MENOR PRECEDENCIA: FourBytes (patrón más general)
# Pattern: x.x.x.x pero evitar casos como obj.method
# Solo tokenizar si hay exactamente 4 elementos separados por puntos
# y no es parte de una cadena de métodos
pattern = r'\b([a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+)\b'
def replace_fourbytes(match):
candidate = match.group(1)
# Verificar que realmente tiene 4 elementos
parts = candidate.split('.')
if len(parts) == 4:
return f'FourBytes("{candidate}")'
return candidate
expression = re.sub(pattern, replace_fourbytes, expression)
return expression

View File

@ -1,92 +0,0 @@
#!/usr/bin/env python3
"""
Script de prueba final para verificar todas las funcionalidades del modo simbólico
"""
from main_evaluation import HybridEvaluationEngine
def test_comprehensive():
"""Prueba comprehensiva del modo simbólico"""
print("=" * 70)
print("PRUEBA COMPREHENSIVA - MODO SIMBÓLICO")
print("=" * 70)
# Crear motor en modo simbólico
engine = HybridEvaluationEngine()
engine.set_symbolic_mode(
symbolic_mode=True,
show_numeric=True,
keep_fractions=True
)
# Test cases
test_cases = [
# Fracciones simples
("4/5", "Fracción simple"),
("25/51", "Fracción compleja"),
("22/7", "Aproximación de π"),
# Asignaciones de fracciones
("a = 4/5", "Asignación fracción simple"),
("b = 25/51", "Asignación fracción compleja"),
("c = 22/7", "Asignación aproximación π"),
# Operaciones con fracciones
("3/4 + 1/6", "Suma de fracciones"),
("5/6 - 1/3", "Resta de fracciones"),
("2/3 * 3/4", "Multiplicación de fracciones"),
("5/6 / 2/3", "División de fracciones"),
# Asignaciones de operaciones
("d = 3/4 + 1/6", "Asignación suma fracciones"),
("e = 2/3 * 3/4", "Asignación multiplicación fracciones"),
# Expresiones simbólicas
("sqrt(2)", "Raíz cuadrada"),
("sin(pi/4)", "Función trigonométrica"),
("log(e)", "Logaritmo"),
# Asignaciones simbólicas
("f = sqrt(2)/2", "Asignación expresión simbólica"),
("g = sin(pi/6)", "Asignación función trigonométrica"),
]
print("\nRESULTADOS:")
print("-" * 70)
for i, (expression, description) in enumerate(test_cases, 1):
print(f"\n{i:2d}. {description}")
print(f" Expresión: {expression}")
result = engine.evaluate_line(expression)
if result.is_error:
print(f" ❌ ERROR: {result.error}")
else:
print(f" ✅ Resultado: {result.result}")
if result.numeric_result is not None:
print(f" 📊 Numérico: ≈ {result.numeric_result}")
else:
print(f" 📊 Numérico: (no disponible)")
# Verificar variables asignadas
print(f"\n{'-'*70}")
print("VARIABLES ASIGNADAS:")
print(f"{'-'*70}")
variables = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
for var in variables:
value = engine.get_variable(var)
if value is not None:
print(f" {var} = {value} (tipo: {type(value).__name__})")
print(f"\n{'='*70}")
print("MODO SIMBÓLICO: ✅ FUNCIONANDO CORRECTAMENTE")
print("- Fracciones se mantienen simbólicas: 4/5, 25/51, 22/7")
print("- Aproximaciones numéricas se muestran cuando corresponde")
print("- Asignaciones preservan la forma simbólica")
print("- Operaciones mantienen exactitud simbólica")
print(f"{'='*70}")
if __name__ == "__main__":
test_comprehensive()

View File

@ -1,50 +0,0 @@
#!/usr/bin/env python3
"""
Script de prueba para el modo simbólico de la calculadora - ASIGNACIONES
"""
from main_evaluation import HybridEvaluationEngine
def test_assignments():
"""Prueba las asignaciones en modo simbólico"""
print("=" * 60)
print("PRUEBA DE ASIGNACIONES EN MODO SIMBÓLICO")
print("=" * 60)
# Crear motor en modo simbólico
print("\n1. MODO SIMBÓLICO ACTIVADO:")
engine_symbolic = HybridEvaluationEngine()
engine_symbolic.set_symbolic_mode(
symbolic_mode=True,
show_numeric=True,
keep_fractions=True
)
# Pruebas de asignaciones
assignment_tests = [
"a = 25/51",
"b = 4/5",
"c = 22/7",
"d = 3/4 + 1/6",
"e = sqrt(2)/2"
]
for test in assignment_tests:
result = engine_symbolic.evaluate_line(test)
print(f" {test:15} → Tipo: {result.result_type}")
print(f" {' ':15} Resultado: {result.result}")
print(f" {' ':15} Simbólico: {result.symbolic_result}")
if result.numeric_result:
print(f" {' ':15} Numérico: ≈ {result.numeric_result}")
else:
print(f" {' ':15} Numérico: None")
print()
print("\n2. VERIFICAR VALORES ASIGNADOS:")
variables = ['a', 'b', 'c', 'd', 'e']
for var in variables:
value = engine_symbolic.get_variable(var)
print(f" {var} = {value} (tipo: {type(value)})")
if __name__ == "__main__":
test_assignments()

View File

@ -1,103 +1,51 @@
""" """
Bracket Parser - INTEGRADO con el sistema de auto-descubrimiento de tipos Tokenization Parser - SISTEMA NUEVO que reemplaza los corchetes
Convierte automáticamente patrones específicos en objetos tipados
""" """
import ast import ast
import re import re
from typing import Tuple, Optional, Set, Dict from typing import Tuple, Optional, Set, Dict
# Importar sistema de tipos # Importar tokenizador desde sympy_Base
try: try:
from type_registry import get_registered_bracket_classes from sympy_Base import preprocess_tokens
TYPE_REGISTRY_AVAILABLE = True TOKENIZER_AVAILABLE = True
except ImportError: except ImportError:
TYPE_REGISTRY_AVAILABLE = False TOKENIZER_AVAILABLE = False
print("⚠️ Sistema de tipos no disponible, usando clases hardcodeadas") print("⚠️ Tokenizador no disponible, usando parser básico")
class BracketParser: class TokenizationParser:
"""Parser que convierte sintaxis con corchetes y detecta ecuaciones contextualmente""" """
Nuevo parser que reemplaza el sistema de corchetes
# Clases de fallback mínimas si falla el registro dinámico Convierte automáticamente patrones en objetos tipados
FALLBACK_BRACKET_CLASSES = {'Hex', 'Bin'} """
# Operadores de comparación que pueden formar ecuaciones # Operadores de comparación que pueden formar ecuaciones
EQUATION_OPERATORS = {'==', '!=', '<', '<=', '>', '>=', '='} EQUATION_OPERATORS = {'==', '!=', '<', '<=', '>', '>=', '='}
def __init__(self, use_type_registry: bool = True): def __init__(self, use_tokenizer: bool = True):
self.debug = False self.debug = False
self.use_type_registry = use_type_registry and TYPE_REGISTRY_AVAILABLE self.use_tokenizer = use_tokenizer and TOKENIZER_AVAILABLE
# Inicializar clases de corchetes dinámicamente # Estadísticas de tokenización
self._update_bracket_classes() self.stats = {
'intbase_conversions': 0,
'fourbytes_conversions': 0,
'total_lines_processed': 0
}
def _get_dynamic_bracket_classes(self) -> Set[str]: def get_tokenization_stats(self) -> Dict[str, int]:
""" """Retorna estadísticas de tokenización"""
Obtiene las clases de corchetes dinámicamente del registro de tipos return self.stats.copy()
NUEVA FUNCIÓN que reemplaza el hardcoding de DEFAULT_BRACKET_CLASSES
"""
if not self.use_type_registry:
return self.FALLBACK_BRACKET_CLASSES.copy()
try:
# Obtener TODAS las clases registradas que soportan corchetes
registered_classes = get_registered_bracket_classes()
if self.debug:
print(f"🔧 Clases dinámicas obtenidas del registro: {registered_classes}")
# Si no hay clases registradas, usar fallback
if not registered_classes:
if self.debug:
print("⚠️ No se encontraron clases registradas, usando fallback")
return self.FALLBACK_BRACKET_CLASSES.copy()
return registered_classes
except Exception as e:
if self.debug:
print(f"⚠️ Error obteniendo clases dinámicas: {e}")
return self.FALLBACK_BRACKET_CLASSES.copy()
def _update_bracket_classes(self): def reset_stats(self):
""" """Reinicia estadísticas"""
Actualiza las clases que soportan sintaxis con corchetes dinámicamente self.stats = {k: 0 for k in self.stats}
MODIFICADO: Ya no usa DEFAULT_BRACKET_CLASSES hardcodeadas
"""
# Obtener clases dinámicamente del registro de tipos
self.BRACKET_CLASSES = self._get_dynamic_bracket_classes()
if self.debug:
print(f"🔧 Bracket classes actualizadas dinámicamente: {self.BRACKET_CLASSES}")
def reload_bracket_classes(self):
"""Recarga las clases de corchetes desde el registro dinámicamente"""
if self.debug:
print("🔄 Recargando bracket classes dinámicamente...")
self._update_bracket_classes()
def add_bracket_class(self, class_name: str):
"""Añade una clase que soporta sintaxis con corchetes"""
self.BRACKET_CLASSES.add(class_name)
if self.debug:
print(f" Añadida bracket class: {class_name}")
def remove_bracket_class(self, class_name: str):
"""Remueve una clase de la sintaxis con corchetes"""
self.BRACKET_CLASSES.discard(class_name)
if self.debug:
print(f" Removida bracket class: {class_name}")
def get_bracket_classes(self) -> Set[str]:
"""Retorna el set actual de clases con sintaxis de corchetes"""
return self.BRACKET_CLASSES.copy()
def has_bracket_class(self, class_name: str) -> bool:
"""Verifica si una clase está registrada para sintaxis con corchetes"""
return class_name in self.BRACKET_CLASSES
def parse_line(self, code_line: str) -> Tuple[str, str]: def parse_line(self, code_line: str) -> Tuple[str, str]:
""" """
Parsea una línea de código aplicando todas las transformaciones Parsea una línea de código aplicando tokenización automática
Returns: Returns:
(transformed_code, parse_info): Código transformado e información de parsing (transformed_code, parse_info): Código transformado e información de parsing
@ -106,6 +54,8 @@ class BracketParser:
if not original_line or original_line.startswith('#'): if not original_line or original_line.startswith('#'):
return code_line, "comment" return code_line, "comment"
self.stats['total_lines_processed'] += 1
try: try:
# 1. Detectar y transformar atajo solve # 1. Detectar y transformar atajo solve
transformed_line, has_solve_shortcut = self._transform_solve_shortcut(original_line) transformed_line, has_solve_shortcut = self._transform_solve_shortcut(original_line)
@ -120,27 +70,46 @@ class BracketParser:
if self._is_standalone_equation(original_line): if self._is_standalone_equation(original_line):
return f'_add_equation("{original_line}")', "equation" return f'_add_equation("{original_line}")', "equation"
# 4. Transformar sintaxis con corchetes # 4. NUEVO: Aplicar tokenización automática
transformed_line = self._transform_brackets(original_line) if self.use_tokenizer:
transformed_line = self._apply_tokenization(original_line)
# Contar conversiones para estadísticas
intbase_count = transformed_line.count('IntBase(')
fourbytes_count = transformed_line.count('FourBytes(')
self.stats['intbase_conversions'] += intbase_count
self.stats['fourbytes_conversions'] += fourbytes_count
if transformed_line != original_line:
if self.debug:
print(f"🔧 Tokenización: '{original_line}''{transformed_line}'")
return transformed_line, "tokenized"
# 5. Si no hay transformaciones, devolver original # 5. Si no hay transformaciones, devolver original
if transformed_line == original_line: return original_line, "expression"
return original_line, "expression"
else:
return transformed_line, "bracket_transform"
except Exception as e: except Exception as e:
if self.debug: if self.debug:
print(f"Error parsing line '{original_line}': {e}") print(f"Error parsing line '{original_line}': {e}")
return code_line, "parse_error" return code_line, "parse_error"
def _apply_tokenization(self, line: str) -> str:
"""
Aplica tokenización automática usando el tokenizador de sympy_Base
"""
if not TOKENIZER_AVAILABLE:
return line
try:
return preprocess_tokens(line)
except Exception as e:
if self.debug:
print(f"Error en tokenización: {e}")
return line
def _transform_solve_shortcut(self, line: str) -> Tuple[str, bool]: def _transform_solve_shortcut(self, line: str) -> Tuple[str, bool]:
""" """
Transforma 'variable=?' en '_solve_variable_in_system("variable")' Transforma 'variable=?' en '_solve_variable_in_system("variable")'
en lugar de 'solve(variable)' para resolver usando las ecuaciones del sistema
Returns:
(transformed_line, was_transformed)
""" """
# Pattern: variable_name = ? # Pattern: variable_name = ?
pattern = r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*\?$' pattern = r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*\?$'
@ -148,7 +117,6 @@ class BracketParser:
if match: if match:
var_name = match.group(1) var_name = match.group(1)
# CAMBIO: usar función personalizada que resuelve usando el sistema de ecuaciones
return f'_solve_variable_in_system("{var_name}")', True return f'_solve_variable_in_system("{var_name}")', True
return line, False return line, False
@ -156,10 +124,10 @@ class BracketParser:
def _is_assignment(self, line: str) -> bool: def _is_assignment(self, line: str) -> bool:
""" """
Detecta si una línea es una asignación de variable Detecta si una línea es una asignación de variable
MEJORADO: Considera si contiene símbolos desconocidos que sugieren ecuación NUEVA LÓGICA: Priorizar asignaciones, ser menos estricto
""" """
try: try:
# Pattern: variable = expresión (que no sea ecuación) # Pattern: variable = expresión (que no sea comparación)
if '=' in line and not any(op in line for op in ['==', '!=', '<=', '>=']): if '=' in line and not any(op in line for op in ['==', '!=', '<=', '>=']):
# Verificar que sea una asignación válida de Python # Verificar que sea una asignación válida de Python
parts = line.split('=', 1) parts = line.split('=', 1)
@ -169,44 +137,9 @@ class BracketParser:
# Verificar que la parte izquierda sea un identificador válido # Verificar que la parte izquierda sea un identificador válido
if re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', var_part): if re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', var_part):
# NUEVA LÓGICA: Si tiene formato de asignación válida, asumir que ES asignación
# NUEVA LÓGICA: Detectar símbolos desconocidos en la expresión # Las ecuaciones son solo para casos muy específicos como solve()
# Si hay símbolos desconocidos, puede ser mejor tratarlo como ecuación return True
symbols_in_expr = re.findall(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b', expr_part)
# Funciones y constantes conocidas de SymPy/matemáticas
known_functions = {
# Funciones trigonométricas
'sin', 'cos', 'tan', 'asin', 'acos', 'atan',
'sinh', 'cosh', 'tanh',
# Funciones exponenciales y logarítmicas
'exp', 'log', 'ln', 'sqrt',
# Constantes
'pi', 'e', 'I', 'oo',
# Funciones especiales
'abs', 'sign', 'floor', 'ceiling', 'factorial',
# Álgebra
'diff', 'integrate', 'limit', 'series', 'solve',
'simplify', 'expand', 'factor', 'collect',
'cancel', 'apart', 'together',
# Matrices
'Matrix', 'det', 'inv',
# Funciones de Python comunes
'int', 'float', 'str', 'len', 'max', 'min', 'sum'
}
# Encontrar símbolos que no son funciones conocidas
unknown_symbols = [s for s in symbols_in_expr if s not in known_functions]
if unknown_symbols:
if self.debug:
print(f"🔍 '{line}' contiene símbolos desconocidos: {unknown_symbols}")
print(f"🔍 Considerando como ECUACIÓN en lugar de asignación")
# Si hay símbolos desconocidos, tratarlo como ecuación
return False # No es asignación, será detectado como ecuación
else:
# Solo funciones/constantes conocidas: asignación normal
return True
return False return False
except: except:
return False return False
@ -217,23 +150,26 @@ class BracketParser:
var_name = parts[0].strip() var_name = parts[0].strip()
expression = parts[1].strip() expression = parts[1].strip()
# Transformar corchetes en la expresión # Aplicar tokenización a la expresión
expression = self._transform_brackets(expression) if self.use_tokenizer:
expression = self._apply_tokenization(expression)
return f'_assign_variable("{var_name}", {expression})' return f'_assign_variable("{var_name}", {expression})'
def _is_standalone_equation(self, line: str) -> bool: def _is_standalone_equation(self, line: str) -> bool:
""" """
Determina si una línea es una ecuación standalone usando análisis AST Determina si una línea es una ecuación standalone
SIMPLIFICADO: Evita recursión y es más directo NUEVA LÓGICA: Solo ecuaciones matemáticas obvias, no asignaciones
""" """
try: try:
# Primero verificar si contiene '=' simple # Primero verificar si contiene '=' simple
if '=' not in line or any(op in line for op in ['==', '!=', '<=', '>=']): if '=' not in line or any(op in line for op in ['==', '!=', '<=', '>=']):
return False return False
# NUEVA LÓGICA: Si ya fue clasificada como asignación, NO es ecuación
if self._is_assignment(line):
return False
# Si no es una asignación (según la lógica ya evaluada en parse_line)
# y tiene '=', entonces es una ecuación
try: try:
tree = ast.parse(line.strip()) tree = ast.parse(line.strip())
@ -242,52 +178,6 @@ class BracketParser:
node = tree.body[0] node = tree.body[0]
# Si es una asignación Python válida, NO es ecuación standalone
if isinstance(node, ast.Assign):
# Pero si contiene símbolos desconocidos, SÍ es ecuación
# Verificar si la expresión contiene símbolos desconocidos
if len(node.targets) == 1:
# Obtener la expresión del lado derecho
try:
expr_code = ast.get_source_segment(line, node.value)
if expr_code:
symbols_in_expr = re.findall(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b', expr_code)
known_functions = {
'sin', 'cos', 'tan', 'asin', 'acos', 'atan',
'sinh', 'cosh', 'tanh', 'exp', 'log', 'ln', 'sqrt',
'pi', 'e', 'I', 'oo', 'abs', 'sign', 'floor', 'ceiling',
'factorial', 'diff', 'integrate', 'limit', 'series',
'solve', 'simplify', 'expand', 'factor', 'collect',
'cancel', 'apart', 'together', 'Matrix', 'det', 'inv',
'int', 'float', 'str', 'len', 'max', 'min', 'sum'
}
unknown_symbols = [s for s in symbols_in_expr if s not in known_functions]
if unknown_symbols:
if self.debug:
print(f"🔍 '{line}' es asignación pero con símbolos desconocidos: {unknown_symbols} → ECUACIÓN")
return True
except:
# Si falla get_source_segment, usar método alternativo
parts = line.split('=', 1)
if len(parts) == 2:
expr_part = parts[1].strip()
symbols_in_expr = re.findall(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b', expr_part)
known_functions = {
'sin', 'cos', 'tan', 'asin', 'acos', 'atan',
'sinh', 'cosh', 'tanh', 'exp', 'log', 'ln', 'sqrt',
'pi', 'e', 'I', 'oo', 'abs', 'sign', 'floor', 'ceiling',
'factorial', 'diff', 'integrate', 'limit', 'series',
'solve', 'simplify', 'expand', 'factor', 'collect',
'cancel', 'apart', 'together', 'Matrix', 'det', 'inv',
'int', 'float', 'str', 'len', 'max', 'min', 'sum'
}
unknown_symbols = [s for s in symbols_in_expr if s not in known_functions]
if unknown_symbols:
if self.debug:
print(f"🔍 '{line}' es asignación pero con símbolos desconocidos: {unknown_symbols} → ECUACIÓN")
return True
return False
# Si es una expresión (no asignación), verificar comparaciones # Si es una expresión (no asignación), verificar comparaciones
if isinstance(node, ast.Expr): if isinstance(node, ast.Expr):
if isinstance(node.value, ast.Compare): if isinstance(node.value, ast.Compare):
@ -298,107 +188,157 @@ class BracketParser:
return False return False
except SyntaxError: except SyntaxError:
# Error de sintaxis: probablemente ecuación mal formada como "x + 2 = 5" # NUEVA LÓGICA: Solo tratar como ecuación si NO tiene formato de asignación válida
if self.debug: parts = line.split('=', 1)
print(f"🔍 '{line}' error AST → Tratando como ECUACIÓN") if len(parts) == 2:
return True var_part = parts[0].strip()
# Si la parte izquierda no es un identificador válido, puede ser ecuación matemática
if not re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', var_part):
if self.debug:
print(f"🔍 '{line}' error AST y no es asignación válida → Tratando como ECUACIÓN")
return True
return False
except Exception as e: except Exception as e:
if self.debug: if self.debug:
print(f"🚨 Error en _is_standalone_equation: {e}") print(f"🚨 Error en _is_standalone_equation: {e}")
return False return False
def _transform_brackets(self, line: str) -> str: def preview_tokenization(self, text: str) -> str:
""" """
Transforma sintaxis Class[args] Class("args") y maneja métodos Muestra preview de cómo se tokenizaría el texto sin ejecutar
VERSIÓN DINÁMICA que usa las clases registradas Útil para debugging y testing
""" """
# Crear pattern dinámicamente basado en clases registradas if not TOKENIZER_AVAILABLE:
if not self.BRACKET_CLASSES: return "Tokenizador no disponible"
return line # No hay clases registradas
# Pattern principal: ClassName[contenido] usando clases dinámicas lines = text.split('\n')
bracket_classes_pattern = '|'.join(re.escape(cls) for cls in self.BRACKET_CLASSES) preview_lines = []
pattern = rf'(\b(?:{bracket_classes_pattern})\b)\[([^\]]*)\]'
if self.debug: for i, line in enumerate(lines, 1):
print(f"🔍 Usando pattern: {pattern}") original = line.strip()
print(f"🔧 Clases registradas: {self.BRACKET_CLASSES}") if not original or original.startswith('#'):
preview_lines.append(f"{i:2}: {line}")
def replace_match(match): continue
class_name = match.group(1)
args_content = match.group(2).strip() tokenized = self._apply_tokenization(original)
if tokenized != original:
if not args_content: preview_lines.append(f"{i:2}: {original}")
# Caso: Class[] → Class() preview_lines.append(f"{tokenized}")
return f'{class_name}()'
else: else:
# Split arguments by semicolon if present preview_lines.append(f"{i:2}: {line}")
# Each argument will be individually quoted.
# Example: Class[arg1; arg2] -> Class("arg1", "arg2")
# Example: Class[arg1] -> Class("arg1")
args_list = [arg.strip() for arg in args_content.split(';')]
processed_args = []
for arg_val in args_list:
# Escape backslashes first, then double quotes for string literals
escaped_arg = arg_val.replace('\\', '\\\\').replace('"', '\\"')
processed_args.append(f'"{escaped_arg}"')
return f'{class_name}({", ".join(processed_args)})'
# Aplicar transformación repetidamente hasta que no haya más cambios return '\n'.join(preview_lines)
transformed = line
while True:
new_transformed = re.sub(pattern, replace_match, transformed)
if new_transformed == transformed:
break
transformed = new_transformed
# Transformar corchetes vacíos en métodos: .método[] → .método()
method_pattern = r'\.([a-zA-Z_][a-zA-Z0-9_]*)\[\]'
transformed = re.sub(method_pattern, r'.\1()', transformed)
return transformed
class EquationDetector:
"""Detector específico para ecuaciones con análisis AST avanzado"""
@staticmethod def test_patterns(self) -> str:
def is_equation_in_context(code: str, context: str = "standalone") -> bool:
""" """
Determina si el código contiene una ecuación considerando el contexto Prueba los patrones de tokenización con ejemplos
"""
test_cases = [
# IntBase patterns
"16#FF",
"2#1010",
"8#777",
"16#x0",
"2#101x",
# FourBytes patterns
"192.168.1.1",
"255.255.0.0",
"10.1.x.2",
"a.b.c.d",
# Mixed patterns
"16#FF + 192.168.1.1",
"2#1010 * 10.0.0.1",
# Should NOT be tokenized
"obj.method.call()",
"x.y", # Only 2 elements
"a.b.c.d.e", # 5 elements
]
Args: results = []
code: Código a analizar for test in test_cases:
context: Contexto ("standalone", "function_arg", "assignment") tokenized = self._apply_tokenization(test) if TOKENIZER_AVAILABLE else test
""" status = "✓ TOKENIZED" if tokenized != test else "○ NO CHANGE"
try: results.append(f"{status}: '{test}''{tokenized}'")
tree = ast.parse(code.strip())
return '\n'.join(results)
for node in ast.walk(tree):
if isinstance(node, ast.Compare):
# Verificar operadores de ecuación # Mantener compatibilidad con el sistema anterior
for op in node.ops: class BracketParser(TokenizationParser):
if isinstance(op, (ast.Eq, ast.NotEq, ast.Lt, ast.LtE, ast.Gt, ast.GtE)): """
if context == "standalone": Alias para compatibilidad con código existente
# En contexto standalone, es una ecuación Ahora usa el nuevo sistema de tokenización
return True """
elif context == "function_arg":
# En argumentos de función, generalmente NO es ecuación def __init__(self, use_type_registry: bool = True):
return False # Ignorar use_type_registry, ya no se usa
super().__init__(use_tokenizer=True)
# Verificar '=' simple (no comparación)
if '=' in code and context == "standalone": def reload_bracket_classes(self):
# Verificar que no sea asignación Python válida """Método de compatibilidad - no hace nada en el nuevo sistema"""
try: if self.debug:
ast.parse(code.strip()) print("🔄 reload_bracket_classes() llamado - no necesario en nuevo sistema")
return False # Es sintaxis Python válida
except SyntaxError: def add_bracket_class(self, class_name: str):
return True # Puede ser ecuación mal formada para Python """Método de compatibilidad - no hace nada en el nuevo sistema"""
if self.debug:
return False print(f"🔄 add_bracket_class({class_name}) llamado - no necesario en nuevo sistema")
except Exception: def remove_bracket_class(self, class_name: str):
return False """Método de compatibilidad - no hace nada en el nuevo sistema"""
if self.debug:
print(f"🔄 remove_bracket_class({class_name}) llamado - no necesario en nuevo sistema")
def get_bracket_classes(self) -> Set[str]:
"""Método de compatibilidad - retorna conjunto vacío"""
return set()
def has_bracket_class(self, class_name: str) -> bool:
"""Método de compatibilidad - siempre retorna False"""
return False
# ========== FUNCIONES DE UTILIDAD ==========
def test_tokenization_system():
"""Función de testing completa del sistema de tokenización"""
print("🧪 TESTING SISTEMA DE TOKENIZACIÓN")
print("=" * 50)
parser = TokenizationParser(use_tokenizer=True)
parser.debug = True
# Test patterns
print("\n1. Test de patrones básicos:")
print(parser.test_patterns())
# Test de líneas completas
test_lines = [
"ip = 192.168.1.1",
"mask = 16#ffffff00",
"net = 10.x.y.0",
"result = 16#FF + 2#1010",
"a.b.c.d + 255.255.255.0",
"x = solve(y + 2)", # No debería tokenizarse solve
]
print(f"\n2. Test de líneas completas:")
for line in test_lines:
transformed, info = parser.parse_line(line)
print(f" '{line}''{transformed}' [{info}]")
# Estadísticas
print(f"\n3. Estadísticas:")
stats = parser.get_tokenization_stats()
for key, value in stats.items():
print(f" {key}: {value}")
print("\n✅ Testing completado")
if __name__ == "__main__":
test_tokenization_system()