""" 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 tokenizador desde sympy_Base try: from sympy_Base import preprocess_tokens TOKENIZER_AVAILABLE = True except ImportError: TOKENIZER_AVAILABLE = False print("⚠️ Tokenizador no disponible, usando parser básico") 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_tokenizer: bool = True): self.debug = False self.use_tokenizer = use_tokenizer and TOKENIZER_AVAILABLE # Estadísticas de tokenización self.stats = { 'intbase_conversions': 0, 'fourbytes_conversions': 0, 'total_lines_processed': 0 } def get_tokenization_stats(self) -> Dict[str, int]: """Retorna estadísticas de tokenización""" return self.stats.copy() 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 tokenización automática Returns: (transformed_code, parse_info): Código transformado e información de parsing """ original_line = code_line.strip() 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) if has_solve_shortcut: return transformed_line, "solve_shortcut" # 2. Detectar asignaciones de variables if self._is_assignment(original_line): return self._transform_assignment(original_line), "assignment" # 3. Detectar ecuaciones standalone if self._is_standalone_equation(original_line): return f'_add_equation("{original_line}")', "equation" # 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 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")' """ # Pattern: variable_name = ? pattern = r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*\?$' match = re.match(pattern, line.strip()) if match: var_name = match.group(1) return f'_solve_variable_in_system("{var_name}")', True return line, False def _is_assignment(self, line: str) -> bool: """ Detecta si una línea es una asignación de variable NUEVA LÓGICA: Priorizar asignaciones, ser menos estricto """ try: # 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) if len(parts) == 2: var_part = parts[0].strip() expr_part = parts[1].strip() # 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: 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 def _transform_assignment(self, line: str) -> str: """Transforma asignación a llamada de función especial""" parts = line.split('=', 1) var_name = parts[0].strip() expression = parts[1].strip() # 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 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 try: tree = ast.parse(line.strip()) if not tree.body: return False node = tree.body[0] # Si es una expresión (no asignación), verificar comparaciones if isinstance(node, ast.Expr): if isinstance(node.value, ast.Compare): for op in node.value.ops: if isinstance(op, (ast.Eq, ast.NotEq, ast.Lt, ast.LtE, ast.Gt, ast.GtE)): return True return False except SyntaxError: # 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 preview_tokenization(self, text: str) -> str: """ Muestra preview de cómo se tokenizaría el texto sin ejecutar Útil para debugging y testing """ if not TOKENIZER_AVAILABLE: return "Tokenizador no disponible" lines = text.split('\n') preview_lines = [] 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: preview_lines.append(f"{i:2}: {line}") return '\n'.join(preview_lines) def test_patterns(self) -> str: """ 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 ] 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()