Eliminado de corchetes y creacion de FourBytes y IntBase primera version
This commit is contained in:
parent
8f0b0287a5
commit
6a7311e358
|
@ -1,46 +1,174 @@
|
|||
"""
|
||||
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
|
||||
|
||||
|
||||
class Class_Bin(SympyClassBase):
|
||||
"""Clase híbrida para números binarios"""
|
||||
|
||||
def __new__(cls, value_input):
|
||||
"""Crear objeto SymPy válido"""
|
||||
obj = SympyClassBase.__new__(cls)
|
||||
return obj
|
||||
"""Clase híbrida para números binarios basada en IntBase"""
|
||||
|
||||
def __init__(self, value_input):
|
||||
"""Inicialización de Bin"""
|
||||
# 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)
|
||||
"""Inicialización de Bin usando IntBase como base"""
|
||||
|
||||
# Llamar al constructor base
|
||||
super().__init__(value, f"0b{value:08b}")
|
||||
if isinstance(value_input, IntBase):
|
||||
# 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):
|
||||
"""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):
|
||||
"""Representación SymPy string"""
|
||||
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
|
||||
def Helper(input_str):
|
||||
"""Ayuda contextual para Bin"""
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
|
@ -48,23 +176,23 @@ class Class_Bin(SympyClassBase):
|
|||
"""Lista de métodos sugeridos para autocompletado de Bin"""
|
||||
return [
|
||||
("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():
|
||||
"""
|
||||
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 [
|
||||
("Bin", Class_Bin, "SympyClassBase", {
|
||||
"add_lowercase": True,
|
||||
"supports_brackets": True,
|
||||
"description": "Números binarios"
|
||||
"supports_brackets": False, # Ya no usa corchetes
|
||||
"description": "Números binarios (usar 2#valor para tokenización automática)"
|
||||
}),
|
||||
]
|
|
@ -14,9 +14,19 @@ class Class_Dec(SympyClassBase):
|
|||
return obj
|
||||
|
||||
def __init__(self, value_input):
|
||||
"""Inicialización de Dec"""
|
||||
# Convertir input a entero
|
||||
if isinstance(value_input, str):
|
||||
"""Inicialización de Dec - MEJORADA para compatibilidad con IntBase"""
|
||||
|
||||
# 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)
|
||||
else:
|
||||
value = int(value_input)
|
||||
|
|
|
@ -1,46 +1,175 @@
|
|||
"""
|
||||
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
|
||||
|
||||
|
||||
class Class_Hex(SympyClassBase):
|
||||
"""Clase híbrida para números hexadecimales"""
|
||||
|
||||
def __new__(cls, value_input):
|
||||
"""Crear objeto SymPy válido"""
|
||||
obj = SympyClassBase.__new__(cls)
|
||||
return obj
|
||||
"""Clase híbrida para números hexadecimales basada en IntBase"""
|
||||
|
||||
def __init__(self, value_input):
|
||||
"""Inicialización de Hex"""
|
||||
# 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)
|
||||
"""Inicialización de Hex usando IntBase como base"""
|
||||
|
||||
# Llamar al constructor base
|
||||
super().__init__(value, f"0x{value:02x}")
|
||||
if isinstance(value_input, IntBase):
|
||||
# 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):
|
||||
"""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):
|
||||
"""Representación SymPy string"""
|
||||
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
|
||||
def Helper(input_str):
|
||||
"""Ayuda contextual para Hex"""
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
|
@ -48,22 +177,23 @@ class Class_Hex(SympyClassBase):
|
|||
"""Lista de métodos sugeridos para autocompletado de Hex"""
|
||||
return [
|
||||
("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():
|
||||
"""
|
||||
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 [
|
||||
("Hex", Class_Hex, "SympyClassBase", {
|
||||
"add_lowercase": True,
|
||||
"supports_brackets": True,
|
||||
"description": "Números hexadecimales"
|
||||
"supports_brackets": False, # Ya no usa corchetes
|
||||
"description": "Números hexadecimales (usar 16#valor para tokenización automática)"
|
||||
}),
|
||||
]
|
|
@ -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 sympy_Base import SympyClassBase
|
||||
from sympy_Base import SympyClassBase, FourBytes, IntBase
|
||||
from typing import Optional, Union, List, Tuple
|
||||
import re
|
||||
import sympy
|
||||
|
@ -10,16 +11,37 @@ import sympy
|
|||
class IP4Mask(ClassBase):
|
||||
"""
|
||||
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
|
||||
_mask_int: int
|
||||
|
||||
def __init__(self, mask_input: Union[int, str]):
|
||||
prefix = self._parse_mask(mask_input)
|
||||
super().__init__(prefix, str(mask_input))
|
||||
self._prefix = prefix
|
||||
self._mask_int = self._prefix_to_mask_int(self._prefix)
|
||||
def __init__(self, mask_input: Union[int, str, FourBytes, IntBase]):
|
||||
if isinstance(mask_input, FourBytes):
|
||||
# Máscara desde tokenización automática x.x.x.x
|
||||
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):
|
||||
# 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:
|
||||
"""Helper to parse mask_input and return prefix."""
|
||||
|
@ -80,6 +102,19 @@ class IP4Mask(ClassBase):
|
|||
return 0
|
||||
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:
|
||||
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):
|
||||
"""Clase híbrida para direcciones IPv4"""
|
||||
"""Clase híbrida para direcciones IPv4 - ADAPTADA AL NUEVO SISTEMA"""
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
temp_ip_str_candidate = args[0] if args else ""
|
||||
temp_ip_str = ""
|
||||
if isinstance(temp_ip_str_candidate, str):
|
||||
if '/' in temp_ip_str_candidate:
|
||||
temp_ip_str = temp_ip_str_candidate.split('/', 1)[0].strip()
|
||||
elif ' ' in temp_ip_str_candidate:
|
||||
temp_ip_str = temp_ip_str_candidate.split(' ', 1)[0].strip()
|
||||
else:
|
||||
temp_ip_str = temp_ip_str_candidate.strip()
|
||||
def __init__(self, address, mask=None):
|
||||
"""
|
||||
Constructor mejorado que acepta FourBytes desde tokenización automática
|
||||
|
||||
Args:
|
||||
address: FourBytes (tokenizado automáticamente), string, o Class_IP4 existente
|
||||
mask: int, string, IP4Mask, FourBytes, o IntBase
|
||||
"""
|
||||
|
||||
# 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):
|
||||
ip_parts_for_new = [int(x) for x in temp_ip_str.split('.')]
|
||||
ip_int_for_new = (ip_parts_for_new[0] << 24) | \
|
||||
(ip_parts_for_new[1] << 16) | \
|
||||
(ip_parts_for_new[2] << 8) | \
|
||||
ip_parts_for_new[3]
|
||||
obj = SympyClassBase.__new__(cls, ip_int_for_new)
|
||||
if address.has_symbols:
|
||||
# Modo algebraico
|
||||
self._ip_int = address._symbolic_value
|
||||
self._has_symbols = True
|
||||
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:
|
||||
if not args:
|
||||
raise ValueError("IP4 constructor requires arguments.")
|
||||
obj = SympyClassBase.__new__(cls, *args)
|
||||
|
||||
return obj
|
||||
raise TypeError(f"address debe ser FourBytes o string, recibido: {type(address)}")
|
||||
|
||||
# Procesar máscara
|
||||
if mask is not None:
|
||||
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):
|
||||
arg_reprs = [repr(arg) for arg in self._raw_constructor_args]
|
||||
return f"{self.__class__.__name__}({', '.join(arg_reprs)})"
|
||||
if self._mask_obj:
|
||||
return f"IP4({self._ip_str!r}, {self._mask_obj.get_prefix_int()})"
|
||||
return f"IP4({self._ip_str!r})"
|
||||
|
||||
def __str__(self):
|
||||
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
|
||||
|
||||
def _sympystr(self, printer):
|
||||
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
|
||||
def Helper(input_str):
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
|
@ -401,6 +444,9 @@ class Class_IP4(SympyClassBase):
|
|||
("BroadcastAddress", "Dirección de broadcast"),
|
||||
("Nodes", "Hosts disponibles"),
|
||||
("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"),
|
||||
("subtract_hosts", "Restar N hosts a la dirección"),
|
||||
("next_ip", "Siguiente dirección IP"),
|
||||
|
@ -496,15 +542,33 @@ class Class_IP4(SympyClassBase):
|
|||
|
||||
def add_hosts(self, n: int) -> 'Class_IP4':
|
||||
"""Suma N hosts a la dirección"""
|
||||
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)
|
||||
if self._has_symbols:
|
||||
# NUEVO: Para IPs simbólicas, crear nueva instancia con expresión simbólica actualizada
|
||||
new_ip_expr = self._ip_int + n
|
||||
|
||||
# Crear nueva instancia directamente con la expresión simbólica
|
||||
new_ip = Class_IP4.__new__(Class_IP4)
|
||||
new_ip._ip_int = new_ip_expr
|
||||
new_ip._has_symbols = True
|
||||
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':
|
||||
"""Resta N hosts a la dirección"""
|
||||
|
@ -882,16 +946,17 @@ class Class_IP4(SympyClassBase):
|
|||
def register_classes_in_module():
|
||||
"""
|
||||
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 [
|
||||
("IP4", Class_IP4, "SympyClassBase", {
|
||||
"add_lowercase": True,
|
||||
"supports_brackets": True,
|
||||
"description": "Direcciones IPv4 con máscara"
|
||||
"supports_brackets": False, # Ya no usa corchetes
|
||||
"description": "Direcciones IPv4 con máscara (usar x.x.x.x para tokenización automática)"
|
||||
}),
|
||||
("IP4Mask", IP4Mask, "ClassBase", {
|
||||
"add_lowercase": True,
|
||||
"supports_brackets": True,
|
||||
"supports_brackets": False, # Ya no usa corchetes
|
||||
"description": "Máscaras de red IPv4"
|
||||
}),
|
||||
]
|
||||
|
@ -902,15 +967,20 @@ def register_classes_in_module():
|
|||
def get_module_info():
|
||||
"""Información adicional sobre este módulo"""
|
||||
return {
|
||||
"name": "IPv4 Network Types",
|
||||
"description": "Clases para manejo de direcciones IP y máscaras de red",
|
||||
"version": "1.0",
|
||||
"name": "IPv4 Network Types - Tokenization System",
|
||||
"description": "Clases para manejo de direcciones IP y máscaras con tokenización automática",
|
||||
"version": "2.0",
|
||||
"depends_on": ["class_base", "sympy_Base"],
|
||||
"examples": [
|
||||
"IP4[192.168.1.1/24]",
|
||||
"IP4[10.0.0.1;255.255.255.0]",
|
||||
"IP4[192.168.1.1/24].NetworkAddress()",
|
||||
"IP4Mask[24]",
|
||||
"IP4Mask[255.255.255.0]"
|
||||
"192.168.1.1 # → FourBytes('192.168.1.1') automáticamente",
|
||||
"IP4(192.168.1.1, 24)",
|
||||
"ip = 10.x.y.0; ip.substitute(x=1, y=2)",
|
||||
"mask = 16#ffffff00 # → IntBase('ffffff00', 16)",
|
||||
"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)"
|
||||
]
|
||||
}
|
|
@ -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()
|
||||
m.hosts_count()
|
||||
hh=16#ff
|
||||
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)
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"window_geometry": "1020x700+708+147",
|
||||
"sash_pos_x": 368,
|
||||
"window_geometry": "1020x700+236+1240",
|
||||
"sash_pos_x": 360,
|
||||
"symbolic_mode": true,
|
||||
"show_numeric_approximation": true,
|
||||
"keep_symbolic_fractions": true,
|
||||
|
|
|
@ -20,6 +20,14 @@ from type_registry import (
|
|||
from tl_bracket_parser import BracketParser
|
||||
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:
|
||||
"""
|
||||
|
@ -28,7 +36,12 @@ class HybridEvaluationEngine:
|
|||
"""
|
||||
|
||||
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.equations: List[sympy.Eq] = []
|
||||
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),
|
||||
}
|
||||
|
||||
# ========== 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
|
||||
self.base_context = {
|
||||
**math_functions,
|
||||
**specialized_classes,
|
||||
**utility_functions
|
||||
**utility_functions,
|
||||
**base_classes # ← NUEVO: Incluir clases base
|
||||
}
|
||||
|
||||
# 6. ACTUALIZAR HELPER FUNCTIONS
|
||||
|
@ -150,6 +174,10 @@ class HybridEvaluationEngine:
|
|||
if self.debug:
|
||||
print(f"📋 Contexto base configurado: {len(self.base_context)} entradas")
|
||||
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):
|
||||
"""Actualiza el BracketParser con las clases descubiertas"""
|
||||
|
@ -233,6 +261,7 @@ class HybridEvaluationEngine:
|
|||
def evaluate_line(self, line: str) -> 'EvaluationResult':
|
||||
"""
|
||||
Evalúa una línea de código y retorna el resultado
|
||||
NUEVA LÓGICA: Priorizar asignaciones, intentar ecuaciones silenciosamente
|
||||
"""
|
||||
try:
|
||||
# 1. Parsear la línea
|
||||
|
@ -244,10 +273,20 @@ class HybridEvaluationEngine:
|
|||
# 2. Manejar casos especiales
|
||||
if parse_info == "comment":
|
||||
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":
|
||||
return self._evaluate_equation_addition(parsed_line, line)
|
||||
elif parse_info == "assignment":
|
||||
return self._evaluate_assignment(parsed_line, line)
|
||||
|
||||
# 3. Evaluación SymPy
|
||||
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'):
|
||||
try:
|
||||
numeric_eval = assigned_value.evalf()
|
||||
# Verificar si el resultado numérico es diferente del simbólico
|
||||
# Para fracciones racionales, siempre mostrar la aproximación decimal
|
||||
# MEJORADO: Solo mostrar aproximación si es realmente útil
|
||||
if hasattr(assigned_value, 'is_Rational') and assigned_value.is_Rational:
|
||||
# Es una fracción racional, mostrar aproximación decimal
|
||||
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:
|
||||
# Para otros casos, mostrar si son diferentes
|
||||
try:
|
||||
# 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
|
||||
except:
|
||||
# Si la comparación falla, asumir que son diferentes
|
||||
numeric_result = numeric_eval
|
||||
# Si la comparación falla, asumir que son diferentes solo si el string es diferente
|
||||
if str(numeric_eval) != str(assigned_value):
|
||||
numeric_result = numeric_eval
|
||||
except Exception as e:
|
||||
if self.debug:
|
||||
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'):
|
||||
try:
|
||||
numeric_eval = result.evalf()
|
||||
# Solo mostrar evaluación numérica si es diferente del resultado simbólico
|
||||
if (str(numeric_eval) != str(result) and numeric_eval != result and
|
||||
# MEJORADO: Solo mostrar evaluación numérica si es realmente útil
|
||||
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
|
||||
(hasattr(result, 'is_number') and result.is_number and
|
||||
hasattr(result, 'is_Integer') and result.is_Integer))):
|
||||
|
@ -577,6 +643,34 @@ class HybridEvaluationEngine:
|
|||
except Exception as 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]:
|
||||
"""Resuelve el sistema de ecuaciones"""
|
||||
if not self.equations:
|
||||
|
|
407
sympy_Base.py
407
sympy_Base.py
|
@ -246,4 +246,409 @@ class SympyClassBase(ClassBase, sympy.Basic):
|
|||
@staticmethod
|
||||
def Helper(input_str):
|
||||
"""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
|
|
@ -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()
|
|
@ -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()
|
|
@ -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 re
|
||||
from typing import Tuple, Optional, Set, Dict
|
||||
|
||||
# Importar sistema de tipos
|
||||
# Importar tokenizador desde sympy_Base
|
||||
try:
|
||||
from type_registry import get_registered_bracket_classes
|
||||
TYPE_REGISTRY_AVAILABLE = True
|
||||
from sympy_Base import preprocess_tokens
|
||||
TOKENIZER_AVAILABLE = True
|
||||
except ImportError:
|
||||
TYPE_REGISTRY_AVAILABLE = False
|
||||
print("⚠️ Sistema de tipos no disponible, usando clases hardcodeadas")
|
||||
TOKENIZER_AVAILABLE = False
|
||||
print("⚠️ Tokenizador no disponible, usando parser básico")
|
||||
|
||||
|
||||
class BracketParser:
|
||||
"""Parser que convierte sintaxis con corchetes y detecta ecuaciones contextualmente"""
|
||||
|
||||
# Clases de fallback mínimas si falla el registro dinámico
|
||||
FALLBACK_BRACKET_CLASSES = {'Hex', 'Bin'}
|
||||
class TokenizationParser:
|
||||
"""
|
||||
Nuevo parser que reemplaza el sistema de corchetes
|
||||
Convierte automáticamente patrones en objetos tipados
|
||||
"""
|
||||
|
||||
# Operadores de comparación que pueden formar ecuaciones
|
||||
EQUATION_OPERATORS = {'==', '!=', '<', '<=', '>', '>=', '='}
|
||||
|
||||
def __init__(self, use_type_registry: bool = True):
|
||||
def __init__(self, use_tokenizer: bool = True):
|
||||
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
|
||||
self._update_bracket_classes()
|
||||
# Estadísticas de tokenización
|
||||
self.stats = {
|
||||
'intbase_conversions': 0,
|
||||
'fourbytes_conversions': 0,
|
||||
'total_lines_processed': 0
|
||||
}
|
||||
|
||||
def _get_dynamic_bracket_classes(self) -> Set[str]:
|
||||
"""
|
||||
Obtiene las clases de corchetes dinámicamente del registro de tipos
|
||||
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 get_tokenization_stats(self) -> Dict[str, int]:
|
||||
"""Retorna estadísticas de tokenización"""
|
||||
return self.stats.copy()
|
||||
|
||||
def _update_bracket_classes(self):
|
||||
"""
|
||||
Actualiza las clases que soportan sintaxis con corchetes dinámicamente
|
||||
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 reset_stats(self):
|
||||
"""Reinicia estadísticas"""
|
||||
self.stats = {k: 0 for k in self.stats}
|
||||
|
||||
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:
|
||||
(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('#'):
|
||||
return code_line, "comment"
|
||||
|
||||
self.stats['total_lines_processed'] += 1
|
||||
|
||||
try:
|
||||
# 1. Detectar y transformar atajo solve
|
||||
transformed_line, has_solve_shortcut = self._transform_solve_shortcut(original_line)
|
||||
|
@ -120,27 +70,46 @@ class BracketParser:
|
|||
if self._is_standalone_equation(original_line):
|
||||
return f'_add_equation("{original_line}")', "equation"
|
||||
|
||||
# 4. Transformar sintaxis con corchetes
|
||||
transformed_line = self._transform_brackets(original_line)
|
||||
# 4. NUEVO: Aplicar tokenización automática
|
||||
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
|
||||
if transformed_line == original_line:
|
||||
return original_line, "expression"
|
||||
else:
|
||||
return transformed_line, "bracket_transform"
|
||||
return original_line, "expression"
|
||||
|
||||
except Exception as e:
|
||||
if self.debug:
|
||||
print(f"Error parsing line '{original_line}': {e}")
|
||||
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]:
|
||||
"""
|
||||
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 = r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*\?$'
|
||||
|
@ -148,7 +117,6 @@ class BracketParser:
|
|||
|
||||
if match:
|
||||
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 line, False
|
||||
|
@ -156,10 +124,10 @@ class BracketParser:
|
|||
def _is_assignment(self, line: str) -> bool:
|
||||
"""
|
||||
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:
|
||||
# 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 ['==', '!=', '<=', '>=']):
|
||||
# Verificar que sea una asignación válida de Python
|
||||
parts = line.split('=', 1)
|
||||
|
@ -169,44 +137,9 @@ class BracketParser:
|
|||
|
||||
# Verificar que la parte izquierda sea un identificador válido
|
||||
if re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', var_part):
|
||||
|
||||
# NUEVA LÓGICA: Detectar símbolos desconocidos en la expresión
|
||||
# Si hay símbolos desconocidos, puede ser mejor tratarlo como ecuación
|
||||
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
|
||||
# NUEVA LÓGICA: Si tiene formato de asignación válida, asumir que ES asignación
|
||||
# Las ecuaciones son solo para casos muy específicos como solve()
|
||||
return True
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
@ -217,23 +150,26 @@ class BracketParser:
|
|||
var_name = parts[0].strip()
|
||||
expression = parts[1].strip()
|
||||
|
||||
# Transformar corchetes en la expresión
|
||||
expression = self._transform_brackets(expression)
|
||||
# Aplicar tokenización a la expresión
|
||||
if self.use_tokenizer:
|
||||
expression = self._apply_tokenization(expression)
|
||||
|
||||
return f'_assign_variable("{var_name}", {expression})'
|
||||
|
||||
def _is_standalone_equation(self, line: str) -> bool:
|
||||
"""
|
||||
Determina si una línea es una ecuación standalone usando análisis AST
|
||||
SIMPLIFICADO: Evita recursión y es más directo
|
||||
Determina si una línea es una ecuación standalone
|
||||
NUEVA LÓGICA: Solo ecuaciones matemáticas obvias, no asignaciones
|
||||
"""
|
||||
try:
|
||||
# Primero verificar si contiene '=' simple
|
||||
if '=' not in line or any(op in line for op in ['==', '!=', '<=', '>=']):
|
||||
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:
|
||||
tree = ast.parse(line.strip())
|
||||
|
||||
|
@ -242,52 +178,6 @@ class BracketParser:
|
|||
|
||||
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
|
||||
if isinstance(node, ast.Expr):
|
||||
if isinstance(node.value, ast.Compare):
|
||||
|
@ -298,107 +188,157 @@ class BracketParser:
|
|||
return False
|
||||
|
||||
except SyntaxError:
|
||||
# Error de sintaxis: probablemente ecuación mal formada como "x + 2 = 5"
|
||||
if self.debug:
|
||||
print(f"🔍 '{line}' error AST → Tratando como ECUACIÓN")
|
||||
return True
|
||||
# NUEVA LÓGICA: Solo tratar como ecuación si NO tiene formato de asignación válida
|
||||
parts = line.split('=', 1)
|
||||
if len(parts) == 2:
|
||||
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:
|
||||
if self.debug:
|
||||
print(f"🚨 Error en _is_standalone_equation: {e}")
|
||||
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
|
||||
VERSIÓN DINÁMICA que usa las clases registradas
|
||||
Muestra preview de cómo se tokenizaría el texto sin ejecutar
|
||||
Útil para debugging y testing
|
||||
"""
|
||||
# Crear pattern dinámicamente basado en clases registradas
|
||||
if not self.BRACKET_CLASSES:
|
||||
return line # No hay clases registradas
|
||||
if not TOKENIZER_AVAILABLE:
|
||||
return "Tokenizador no disponible"
|
||||
|
||||
# Pattern principal: ClassName[contenido] usando clases dinámicas
|
||||
bracket_classes_pattern = '|'.join(re.escape(cls) for cls in self.BRACKET_CLASSES)
|
||||
pattern = rf'(\b(?:{bracket_classes_pattern})\b)\[([^\]]*)\]'
|
||||
lines = text.split('\n')
|
||||
preview_lines = []
|
||||
|
||||
if self.debug:
|
||||
print(f"🔍 Usando pattern: {pattern}")
|
||||
print(f"🔧 Clases registradas: {self.BRACKET_CLASSES}")
|
||||
|
||||
def replace_match(match):
|
||||
class_name = match.group(1)
|
||||
args_content = match.group(2).strip()
|
||||
|
||||
if not args_content:
|
||||
# Caso: Class[] → Class()
|
||||
return f'{class_name}()'
|
||||
for i, line in enumerate(lines, 1):
|
||||
original = line.strip()
|
||||
if not original or original.startswith('#'):
|
||||
preview_lines.append(f"{i:2}: {line}")
|
||||
continue
|
||||
|
||||
tokenized = self._apply_tokenization(original)
|
||||
if tokenized != original:
|
||||
preview_lines.append(f"{i:2}: {original}")
|
||||
preview_lines.append(f" → {tokenized}")
|
||||
else:
|
||||
# Split arguments by semicolon if present
|
||||
# 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)})'
|
||||
preview_lines.append(f"{i:2}: {line}")
|
||||
|
||||
# Aplicar transformación repetidamente hasta que no haya más cambios
|
||||
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"""
|
||||
return '\n'.join(preview_lines)
|
||||
|
||||
@staticmethod
|
||||
def is_equation_in_context(code: str, context: str = "standalone") -> bool:
|
||||
def test_patterns(self) -> str:
|
||||
"""
|
||||
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:
|
||||
code: Código a analizar
|
||||
context: Contexto ("standalone", "function_arg", "assignment")
|
||||
"""
|
||||
try:
|
||||
tree = ast.parse(code.strip())
|
||||
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Compare):
|
||||
# Verificar operadores de ecuación
|
||||
for op in node.ops:
|
||||
if isinstance(op, (ast.Eq, ast.NotEq, ast.Lt, ast.LtE, ast.Gt, ast.GtE)):
|
||||
if context == "standalone":
|
||||
# En contexto standalone, es una ecuación
|
||||
return True
|
||||
elif context == "function_arg":
|
||||
# En argumentos de función, generalmente NO es ecuación
|
||||
return False
|
||||
|
||||
# Verificar '=' simple (no comparación)
|
||||
if '=' in code and context == "standalone":
|
||||
# Verificar que no sea asignación Python válida
|
||||
try:
|
||||
ast.parse(code.strip())
|
||||
return False # Es sintaxis Python válida
|
||||
except SyntaxError:
|
||||
return True # Puede ser ecuación mal formada para Python
|
||||
|
||||
return False
|
||||
|
||||
except Exception:
|
||||
return False
|
||||
results = []
|
||||
for test in test_cases:
|
||||
tokenized = self._apply_tokenization(test) if TOKENIZER_AVAILABLE else test
|
||||
status = "✓ TOKENIZED" if tokenized != test else "○ NO CHANGE"
|
||||
results.append(f"{status}: '{test}' → '{tokenized}'")
|
||||
|
||||
return '\n'.join(results)
|
||||
|
||||
|
||||
# Mantener compatibilidad con el sistema anterior
|
||||
class BracketParser(TokenizationParser):
|
||||
"""
|
||||
Alias para compatibilidad con código existente
|
||||
Ahora usa el nuevo sistema de tokenización
|
||||
"""
|
||||
|
||||
def __init__(self, use_type_registry: bool = True):
|
||||
# Ignorar use_type_registry, ya no se usa
|
||||
super().__init__(use_tokenizer=True)
|
||||
|
||||
def reload_bracket_classes(self):
|
||||
"""Método de compatibilidad - no hace nada en el nuevo sistema"""
|
||||
if self.debug:
|
||||
print("🔄 reload_bracket_classes() llamado - no necesario en nuevo sistema")
|
||||
|
||||
def add_bracket_class(self, class_name: str):
|
||||
"""Método de compatibilidad - no hace nada en el nuevo sistema"""
|
||||
if self.debug:
|
||||
print(f"🔄 add_bracket_class({class_name}) llamado - no necesario en nuevo sistema")
|
||||
|
||||
def remove_bracket_class(self, class_name: str):
|
||||
"""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()
|
Loading…
Reference in New Issue