diff --git a/.doc/CONTEXTO_LIMPIO_SOLUCION.md b/.doc/CONTEXTO_LIMPIO_SOLUCION.md deleted file mode 100644 index e50775a..0000000 --- a/.doc/CONTEXTO_LIMPIO_SOLUCION.md +++ /dev/null @@ -1,150 +0,0 @@ -# Solución: Contexto Limpio en Cada Evaluación - -## Problema Identificado - -La aplicación tenía un comportamiento inesperado donde al evaluar una secuencia como: -``` -x -x=1 -y+x -x=x+1 -x -``` - -El primer `x` ya mostraba un valor (en este caso `2[Integer]`) cuando debería estar indefinido inicialmente. - -## Causa Raíz REAL - -El problema **NO era solo el historial**, sino que la aplicación mantenía el contexto de variables entre evaluaciones. Cuando el usuario modificaba cualquier línea, la aplicación reevaluaba **manteniendo las variables previamente definidas**, en lugar de construir el contexto desde cero. - -Esto significaba que: -1. Si había `x=1` en alguna línea anterior -2. Y luego el usuario añadía una línea `x` al principio -3. El `x` del principio usaba el valor de la evaluación anterior en lugar de iniciar limpio - -## Solución Implementada - -### Modificación Principal: Contexto Limpio en Cada Evaluación - -**Antes:** -```python -def _evaluate_and_update(self): - """Evalúa todas las líneas y actualiza la salida""" - try: - input_content = self.input_text.get("1.0", tk.END) - if not input_content.strip(): - self._clear_output() - return - - lines = input_content.splitlines() - self._evaluate_lines(lines) # ← Mantenía contexto anterior - - except Exception as e: - self._show_error(f"Error durante evaluación: {e}") -``` - -**Después:** -```python -def _evaluate_and_update(self): - """Evalúa todas las líneas y actualiza la salida""" - try: - input_content = self.input_text.get("1.0", tk.END) - if not input_content.strip(): - self._clear_output() - return - - # NUEVO: Limpiar completamente el contexto antes de cada evaluación - # Esto garantiza que cada modificación reevalúe todo desde cero - self.engine.clear_all() - - lines = input_content.splitlines() - self._evaluate_lines(lines) - - except Exception as e: - self._show_error(f"Error durante evaluación: {e}") -``` - -### Cambios Adicionales - -1. **Eliminación de llamadas redundantes**: Los métodos `clear_variables()`, `clear_equations()` y `clear_all()` ya no llaman a `_evaluate_and_update()` porque ahora cada evaluación limpia automáticamente. - -2. **Limpieza del historial**: También se limpió el historial, pero esto era secundario al problema real. - -3. **Opción manual para limpiar historial**: Se añadió en el menú **Editar → Limpiar historial**. - -## Comportamiento Resultante - -Ahora **cada vez que se modifica cualquier línea**: - -1. **Se limpia completamente el contexto** - `engine.clear_all()` -2. **Se reevalúa TODO desde la línea 1** - construcción paso a paso -3. **El contexto se construye de arriba hacia abajo** - sin memoria de evaluaciones anteriores -4. **Variables solo existen si están definidas en líneas anteriores** - comportamiento predictible - -## Prueba de Verificación - -El script `test_contexto_limpio.py` confirma el comportamiento: - -``` -=== PRUEBA DE CONTEXTO LIMPIO POR EVALUACIÓN === - ---- PRIMERA EVALUACIÓN: x solo --- -Resultado: x # ← Símbolo puro, sin valor -Variables después: {} # ← Contexto limpio - ---- TERCERA EVALUACIÓN: x, x=1, y+x --- -x (antes de asignar): x # ← Siempre símbolo puro al inicio -x=1: 1 # ← Se asigna -y+x: y + 1 # ← Usa el x=1 asignado arriba -Variables después: {'x': 1} # ← Contexto construido paso a paso -``` - -## Beneficios de la Solución Real - -- ✅ **Contexto predecible**: Cada evaluación inicia completamente limpia -- ✅ **Construcción desde arriba**: Las variables solo existen si se definen arriba -- ✅ **Sin memoria persistente**: No hay variables "fantasma" de evaluaciones anteriores -- ✅ **Comportamiento intuitivo**: Lo que ves es lo que tienes -- ✅ **Reevaluación completa**: Cada cambio reconstruye todo desde cero - -## Archivos Modificados - -1. `main_calc_app.py` - Método `_evaluate_and_update()` con limpieza automática -2. `main_calc_app.py` - Métodos `clear_*()` simplificados -3. `main_calc_app.py` - **Menús simplificados**: Eliminadas opciones redundantes -4. `hybrid_calc_history.txt` - Limpiado (cambio menor) -5. `test_contexto_limpio.py` - Script de prueba (nuevo) - -### Cambios en la Interfaz de Usuario - -**Opciones ELIMINADAS del menú (ya no tienen sentido):** - -- **Menú Editar**: - - ❌ "Limpiar variables" - - ❌ "Limpiar ecuaciones" - - ❌ "Limpiar todo" - -- **Menú CAS completo**: - - ❌ "Mostrar variables" - - ❌ "Mostrar ecuaciones" - - ❌ "Resolver sistema" - -**Opciones CONSERVADAS (aún útiles):** - -- **Menú Editar**: - - ✅ "Limpiar entrada" - limpia el editor de texto - - ✅ "Limpiar salida" - limpia el panel de resultados - - ✅ "Limpiar historial" - elimina el archivo de historial - -- **Menú Archivo**: - - ✅ "Nuevo" - inicia sesión nueva (limpia entrada y salida) - - ✅ "Cargar..." / "Guardar como..." - manejo de archivos - -**La solución real no era el historial, sino garantizar que cada evaluación construye el contexto completamente desde cero.** - -## Impacto en la Experiencia de Usuario - -**✅ Interfaz más limpia**: Sin opciones confusas que no funcionan como se espera -**✅ Comportamiento predecible**: Lo que ves en el editor es exactamente lo que tienes -**✅ Simplicidad**: Menos opciones que manejar, enfoque en la funcionalidad principal -**✅ Consistencia**: El comportamiento es coherente con la filosofía "de arriba hacia abajo" \ No newline at end of file diff --git a/.doc/Desarrollo.md b/.doc/Desarrollo.md new file mode 100644 index 0000000..9f62b4c --- /dev/null +++ b/.doc/Desarrollo.md @@ -0,0 +1,426 @@ +# Guía de Desarrollo - Calculadora MAV CAS Híbrido + +## Visión General + +Este documento describe el estado actual y los objetivos de desarrollo para la **Calculadora MAV**, un **Sistema de Álgebra Computacional (CAS) híbrido**. El proyecto busca combinar la potencia de SymPy como motor algebraico central con capacidades especializadas (IP4, Hex, etc.) integradas de forma nativa. + +## Objetivos Principales + +### 1. **SymPy como Motor Central** +- ✅ Integrar todas las funciones de SymPy directamente +- ✅ SymPy maneja toda la evaluación algebraica y ecuaciones +- ✅ Cada línea de ingreso corresponde a una sola línea de resultados +- ✅ **CONTEXTO LIMPIO POR EVALUACIÓN**: Cada vez que se hace una evaluación se comienza con el contexto completamente limpio y se evalúa desde arriba hacia abajo, línea por línea. Esto garantiza comportamiento predecible sin "memoria" de evaluaciones anteriores. +- ✅ La interfaz de usuario se divide en 2 columnas, a la izquierda el área de ingreso de datos y ecuaciones a ser evaluadas, a la derecha el área de resultados que se colorean según cada tipo de respuesta. + +### 2. **Sistema de Tokenización Automática** ⭐ **ACTUALIZADO** +- ❌ ~~Usar **exclusivamente** `Class[args]` en lugar de `Class("args")`~~ **ELIMINADO** +- ✅ **Tokenización automática de patrones específicos**: + - `16#FF` → `IntBase('FF', 16)` automáticamente + - `192.168.1.1` → `FourBytes('192.168.1.1')` automáticamente +- ✅ **Sistema distribuido**: Cada clase define sus propios patrones de tokenización +- ✅ **Precedencia automática**: Patrones más específicos tienen mayor prioridad +- ✅ **Parser genérico**: No hardcodea lógica específica de tipos + +### 3. **Detección Automática y Manejo de Ecuaciones** +- ✅ Ecuaciones ingresadas directamente (ej. `3+b=5+c`) se detectan y añaden al sistema interno de SymPy. +- ✅ Soporte para comparaciones SymPy (ej. `x > 5`, `a == b`, `x <= 10`) como expresiones evaluables. +- ✅ Sintaxis de atajo para solve: `variable=?` equivale a `solve(variable)` + +### 4. **Evaluación Híbrida con Variables SymPy Puras** +- ✅ Resultado simbólico (siempre disponible) +- ✅ Evaluación numérica con `.evalf()` (cuando sea aplicable y las variables estén definidas). Esta evaluación se muestra a la derecha del resultado simbólico. +- ✅ Las asignaciones son siempre simbólicas, solo los resultados se muestra la evaluación numérica. +- ✅ **Todas las variables son símbolos SymPy**: Sin variables Python tradicionales +- 🔄 **Conversión perezosa a SymPy**: Objetos nativos hasta que se necesite álgebra compleja + +### 5. **Resultados Interactivos** +- ✅ **Tags clickeables** en el widget Text para resultados complejos que necesitan más de una línea de resultado como Plot, Matrices y Listas. +- ✅ **Detección automática** de tipos que requieren visualización especial: + - ✅ Plots de SymPy → "📊 Ver Plot" (abre ventana matplotlib). + - ✅ Matrices → "📋 Ver Matriz" (muestra matriz formateada). + - ✅ Listas largas → "📋 Ver Lista" (muestra contenido expandido). + - ✅ Objetos complejos → "🔍 Ver Detalles" (representación completa). +- ✅ **Implementación con tags**: Usar `text.tag_bind()` en widget existente +- ✅ **Ventanas popup** para contenido que no cabe en línea. + +### 6. **Sistema de Tipos Auto-Descubrible** ⭐ **NUEVO** +- ✅ **Directorio unificado**: Todos los tipos en `custom_types/` +- ✅ **Auto-registro**: Sistema automático de descubrimiento de clases +- ✅ **Función de registro**: Cada módulo tiene `register_classes_in_module()` +- ✅ **Metadatos**: Categorías, opciones y descripciones por tipo +- ✅ **Helpers dinámicos**: Sistema de ayuda contextual distribuido + +### 7. **Clases Base Universales** ⭐ **NUEVO** +- ✅ **IntBase**: Representación universal de números en cualquier base +- ✅ **FourBytes**: Representación universal de patrones x.x.x.x +- ✅ **Manejo interno de símbolos**: Sin conversión prematura a SymPy +- ✅ **Aritmética nativa**: Operaciones que mantienen el tipo original +- ✅ **Conversión explícita**: `.to_sympy()` cuando se necesite álgebra compleja + +## Arquitectura Actual ⭐ **ACTUALIZADA** + +### Estructura de archivos +``` +/ +├── logs/ # Subcarpeta de logs +├── docs/ # Documentación técnica +├── custom_types/ # ⭐ NUEVO: Sistema de tipos unificado +│ ├── intbase_type.py # IntBase + tokenización + registro +│ ├── fourbytes_type.py # FourBytes + tokenización + registro +│ ├── hex_type.py # Hex (usa IntBase) +│ ├── bin_type.py # Bin (usa IntBase) +│ ├── dec_type.py # Dec +│ ├── chr_type.py # Chr +│ ├── ip4_type.py # IP4 + IP4Mask (usa FourBytes) +│ └── latex_type.py # LaTeX +├── calc.py # Launcher principal +├── main_calc_app.py # Aplicación principal +├── main_evaluation.py # Motor de evaluación híbrido +├── type_registry.py # ⭐ NUEVO: Auto-descubrimiento de tipos +├── tl_popup.py # Motor de ventanas emergentes +├── tl_bracket_parser.py # ⭐ ACTUALIZADO: Tokenizador universal +├── class_base.py # Clases base +├── sympy_Base.py # Base para integración SymPy +└── sympy_helper.py # Helpers para SymPy +``` + +### Flujo de Procesamiento ⭐ **ACTUALIZADO** +``` +Entrada Usuario → [Universal Tokenizer] → [Evaluación Híbrida] → { + 1. Resultado Nativo (FourBytes, IntBase, etc.) + 2. Conversión a SymPy (solo si es necesario) + 3. Evaluación numérica (evalf si aplica) + 4. Métodos especializados disponibles +} +``` + +### Transformación de Sintaxis ⭐ **ACTUALIZADA** +```python +# Tokenización automática (NUEVO): +16#FF + 2#1010 → IntBase("FF", 16) + IntBase("1010", 2) +192.168.1.1 + 5 → FourBytes("192.168.1.1") + 5 +IP4(10.1.1.1, 255.255.0.0) → IP4(FourBytes("10.1.1.1"), FourBytes("255.255.0.0")) + +# Detección contextual de ecuaciones (EXISTENTE): +3+b=5+c → Se detecta como ecuación standalone (agregar al sistema SymPy) +"x > 5" → Se procesa como desigualdad SymPy +x**2 + 2*x == 0 → Se detecta como igualdad lógica standalone + +# Sintaxis de atajo para solve (EXISTENTE): +x=? → solve(x) +``` + +## Componentes Técnicos ⭐ **ACTUALIZADOS** + +### 1. **Sistema de Auto-Descubrimiento de Tipos** +```python +# type_registry.py +class TypeRegistry: + def discover_and_register_all(self): + """Descubre y registra todas las clases en custom_types/""" + for type_file in self.types_directory.glob("*_type.py"): + self._process_type_file(type_file) + + def _register_classes_from_info(self, class_info_list): + """Registra clases basándose en metadatos""" + for name, class_obj, category, options in class_info_list: + self._register_single_class(name, class_obj, category, options) +``` + +### 2. **Tokenizador Universal Distribuido** +```python +# tl_bracket_parser.py +class UniversalTokenizer: + def _discover_tokenization_rules(self): + """Auto-descubre reglas desde todas las clases registradas""" + for class_name, class_obj in registered_classes.items(): + if hasattr(class_obj, 'get_tokenization_patterns'): + patterns = class_obj.get_tokenization_patterns() + for pattern_info in patterns: + self.tokenization_rules.append({ + 'class_name': class_name, + 'class_obj': class_obj, + **pattern_info + }) +``` + +### 3. **Clases con Tokenización Propia** +```python +# custom_types/intbase_type.py +class IntBase(ClassBase): + @staticmethod + def get_tokenization_patterns(): + return [ + { + 'pattern': r'(\d+)#([0-9A-Fa-fx]+)', + 'replacement': lambda match: f'IntBase("{match.group(2)}", {match.group(1)})', + 'priority': 5, + 'description': 'Números con base: 16#FF, 2#1010' + } + ] +``` + +### 4. **Conversión Perezosa a SymPy** ⭐ **NUEVO CONCEPTO** +```python +class FourBytes(ClassBase): # No SympyClassBase + def substitute(self, **kwargs): + """Sustitución interna sin involucrar SymPy""" + # Lógica nativa + + def to_sympy(self): + """Conversión EXPLÍCITA cuando se necesite álgebra""" + # Solo convertir cuando se requiera +``` + +### 5. **Sistema de Evaluación Integrado** +```python +# main_evaluation.py +class HybridEvaluationEngine: + def __init__(self, auto_discover_types=True): + # Auto-descubrir tipos dinámicamente + self.registered_types_info = discover_and_register_types() + + # Configurar parser con tokenización distribuida + self.parser = BracketParser(enable_tokenization=True) + + # Configurar contexto con tipos descubiertos + self._setup_base_context() +``` + +## Capacidades Expandidas ⭐ **ACTUALIZADAS** + +### 1. **Funciones SymPy Completas con Objetos Nativos** +```python +# Objetos nativos en álgebra (conversión automática cuando es necesario): +diff(IP4(192.168.x.1) + offset, x) # FourBytes → SymPy automático +integrate(Hex(16#x0) * sin(x), x) # IntBase → SymPy automático +solve([IP4(192.168.x.y) == IP4(192.168.1.10)], [x, y]) + +# Aritmética nativa (mantiene tipos): +base_ip = 192.168.1.1 # → FourBytes automático +next_ip = base_ip + 5 # → FourBytes('192.168.1.6') +hex_val = 16#FF + 16#10 # → IntBase('10F', 16) +``` + +### 2. **Conversiones Automáticas Bidireccionales** +```python +# Conversiones fluidas entre tipos: +ip = 192.168.1.1 # → FourBytes automático +ip_hex = ip.ToHex() # → "16#C0.16#A8.16#1.16#1" +mask = 255.255.255.0 # → FourBytes automático +mask_cidr = IP4Mask(mask) # → /24 automático + +# Constructores inteligentes: +IP4(192.168.1.1, 16#ffffff00) # FourBytes + IntBase → IP4 +Hex(192.168.1.1) # FourBytes → Hex automático +``` + +### 3. **Sistema de Ayuda Dinámico** +```python +# Helpers auto-descubiertos: +help_functions = get_registered_helper_functions() # De todas las clases +help_context = obtener_ayuda("16#FF") # Helper de IntBase +autocompletado = obj.PopupFunctionList() # Métodos disponibles +``` + +## Ejemplos de Uso Objetivo ⭐ **ACTUALIZADOS** + +```python +# Tokenización automática de patrones: +16#FF + 2#1010 # → IntBase('FF', 16) + IntBase('1010', 2) +192.168.1.1 + 5 # → FourBytes('192.168.1.6') +10.x.1.y # → FourBytes('10.x.1.y') simbólico + +# Constructores inteligentes: +IP4(192.168.1.1, 24) # → IP4(FourBytes('192.168.1.1'), 24) +IP4(10.1.1.1, 255.255.0.0) # → Ambos FourBytes automáticamente +Hex(192.168.1.1) # → Conversión FourBytes → Hex + +# Conversiones fluidas: +mask = 255.255.255.0 # → FourBytes automático +mask.ToHex() # → "16#FF.16#FF.16#FF.16#0" +mask.ToBase(2) # → "2#11111111.2#11111111.2#11111111.2#0" + +# Álgebra con objetos nativos: +network = 10.x.0.0 # → FourBytes simbólico +solve(network + 256 == 10.5.0.0, x) # → x = 5 + +# Aritmética que preserva tipos: +base = 192.168.0.0 # → FourBytes +for i in range(4): + subnet = base + (i * 256) # → Cada resultado es FourBytes + print(f"Red {i}: {subnet}") + +# Evaluación directa con SymPy: +solve(x + 10 - 15, x) # → [5] +diff(16#x0, x) # → 16*log(16) (conversión automática) + +# Variables puras SymPy: +x = 5 # → x es Symbol('x') con valor 5 +y = x + 2 # → y es expresión simbólica: Symbol('x') + 2 + +# Funciones avanzadas con resultados interactivos: +plot(sin(2*pi*16#x/256), (x, 0, 255)) # → "📊 Ver Plot" +Matrix([[1, 2, 3], [4, 5, 6]]) # → "📋 Ver Matriz" +``` + +## Beneficios Logrados ⭐ **ACTUALIZADOS** + +### 1. **Usabilidad Mejorada** +- ✅ Sintaxis natural sin artificios (no más `Class[args]`) +- ✅ Tokenización invisible al usuario +- ✅ Acceso completo a capacidades de CAS +- ✅ Evaluación automática simbólica + numérica + +### 2. **Potencia Matemática Expandida** +- ✅ Todas las funciones de SymPy disponibles +- ✅ Objetos especializados participan en álgebra cuando es necesario +- ✅ Aritmética nativa que preserva tipos +- ✅ Conversiones automáticas bidireccionales + +### 3. **Extensibilidad Real** +- ✅ Sistema verdaderamente modular sin hardcoding +- ✅ Fácil agregar nuevos tipos sin modificar código core +- ✅ Tokenización distribuida y auto-descubrible +- ✅ Compatible con ecosystem SymPy completo + +### 4. **Arquitectura Limpia** +- ✅ Responsabilidad única: cada clase maneja su tokenización +- ✅ Parser genérico sin conocimiento específico de tipos +- ✅ Auto-descubrimiento completo del sistema de tipos +- ✅ Manejo unificado de errores + +## Sistema de Autocompletado y Ayuda Contextual ⭐ **IMPLEMENTADO** + +### 1. **Helpers Dinámicos** +- ✅ **Auto-descubrimiento**: Sistema obtiene helpers de todas las clases registradas +- ✅ **Ayuda contextual**: Cada tipo define su propio `Helper(input_str)` +- ✅ **Integración con SymPy**: Helper especializado para funciones SymPy +- ✅ **Manejo centralizado**: Lista dinámica de helpers disponibles + +### 2. **Autocompletado Inteligente** +- ✅ **Popup tras punto**: Evaluación automática del objeto para métodos disponibles +- ✅ **PopupFunctionList()**: Cada clase define sus métodos sugeridos +- ✅ **Filtrado inteligente**: Solo métodos útiles y públicos +- ✅ **No invasivo**: Solo aparece cuando el usuario lo solicita + +### 3. **Ejemplos de Integración** +```python +# Sistema distribuido: +HELPERS = get_registered_helper_functions() # Auto-descubierto + +# Ayuda contextual: +16#FF. # → Popup con métodos de IntBase +192.168.1.1. # → Popup con métodos de FourBytes +IP4(10.1.1.1). # → Popup con métodos de IP4 + +# Helpers específicos: +ayuda = obtener_ayuda("16#FF") # → "Números con base: 16#FF, 2#1010..." +``` + +## Estado de Implementación ⭐ **ACTUALIZADO** + +### ✅ **Completamente Implementado** +- **Sistema de auto-descubrimiento**: `type_registry.py` +- **Tokenizador universal**: `tl_bracket_parser.py` refactorizado +- **Clases base universales**: `intbase_type.py`, `fourbytes_type.py` +- **Motor híbrido integrado**: `main_evaluation.py` con auto-descubrimiento +- **Tipos en custom_types/**: Todos los tipos migrados al sistema unificado +- **Helpers dinámicos**: Sistema de ayuda contextual distribuido +- **Resultados interactivos**: Tags clickeables y ventanas popup +- **Contexto limpio**: Evaluación completa desde cero en cada cambio + +### 🔄 **En Progreso** +- **Conversión perezosa a SymPy**: Algunos objetos aún se convierten prematuramente +- **Aritmética que preserva tipos**: Necesita refinamiento para todos los casos +- **Optimización de precedencia**: Ajuste fino de prioridades de tokenización + +### 📋 **Pendiente** +- **Tipos adicionales**: MAC addresses, timestamps, coordenadas +- **Performance optimization**: Caching y evaluación lazy +- **Documentación de usuario**: Guías y ejemplos actualizados + +## Métricas de Éxito ⭐ **ACTUALIZADAS** + +- ✅ **Sistema de tokenización automática funcional**: + - ✅ Patrones `16#FF`, `2#1010` → IntBase automático + - ✅ Patrones `192.168.1.1` → FourBytes automático + - ✅ Precedencia automática por especificidad + - ✅ Parser genérico sin hardcoding + +- ✅ **Sistema de tipos auto-descubrible**: + - ✅ Todas las clases en `custom_types/` + - ✅ Función `register_classes_in_module()` en cada módulo + - ✅ Auto-registro con metadatos y opciones + - ✅ Helpers dinámicos desde todas las clases + +- ✅ **Clases base universales funcionales**: + - ✅ IntBase con aritmética nativa y tokenización propia + - ✅ FourBytes con aritmética nativa y tokenización propia + - ✅ Conversión explícita `.to_sympy()` cuando sea necesario + - ✅ Operadores que mantienen tipos nativos + +- ✅ **Motor híbrido integrado**: + - ✅ Auto-descubrimiento de tipos al inicializar + - ✅ Tokenización distribuida automática + - ✅ Contexto limpio por evaluación + - ✅ Evaluación simbólica + numérica + +- 🔄 **Conversión perezosa optimizada**: + - ✅ Objetos nativos por defecto + - 🔄 Conversión solo cuando se necesite álgebra compleja + - 🔄 Aritmética preservando tipos en todos los casos + +- ✅ **Detección contextual de ecuaciones**: + - ✅ Ecuaciones standalone detectadas y agregadas al sistema + - ✅ Sintaxis de atajo `variable=?` → `solve(variable)` + - ✅ Protección contra detección en argumentos de función + +- ✅ **Resultados interactivos**: + - ✅ Tags clickeables para plots, matrices, listas + - ✅ Ventanas popup con posicionamiento inteligente + - ✅ Detección automática de tipos complejos + +- ✅ **Sistema de ayuda avanzado**: + - ✅ Helpers auto-descubiertos desde todas las clases + - ✅ Autocompletado tras punto con métodos disponibles + - ✅ Ayuda contextual no invasiva + +- ✅ **Performance aceptable**: Debounce y evaluación optimizada +- ✅ **Manejo robusto de errores**: Logging centralizado y presentación clara +- ✅ **Documentación actualizada**: Guías técnicas y de desarrollo + +## Consideraciones Futuras + +### **Tipos Adicionales Planeados** +```python +custom_types/ +├── mac_type.py # Direcciones MAC: AA:BB:CC:DD:EE:FF +├── time_type.py # Timestamps: HH:MM:SS, ISO format +├── coordinate_type.py # Coordenadas: lat,lon +├── ipv6_type.py # IPv6: 2001:db8::1 +└── range_type.py # Rangos: 1..100, a..z +``` + +### **Optimizaciones de Performance** +- Caching de patrones de tokenización frecuentes +- Evaluación lazy de `evalf()` solo cuando se muestre +- Memoización de conversiones SymPy costosas + +### **Extensiones del Parser** +- Soporte para sintaxis de múltiples líneas +- Macros y templates definidos por usuario +- Validación contextual más sofisticada + +## Conclusión + +La calculadora MAV ha evolucionado hacia un **sistema verdaderamente híbrido y extensible** que combina: + +1. **Potencia algebraica completa** de SymPy +2. **Objetos nativos especializados** con aritmética propia +3. **Tokenización automática invisible** al usuario +4. **Arquitectura modular** sin hardcoding +5. **Auto-descubrimiento completo** del sistema de tipos + +El resultado es una herramienta que es **más simple para el usuario** (sintaxis natural) pero **más poderosa internamente** (álgebra simbólica + tipos especializados), con una **arquitectura limpia y extensible** que facilita agregar nuevas capacidades sin modificar código existente. \ No newline at end of file diff --git a/.doc/Documentacion.md b/.doc/Documentacion.md new file mode 100644 index 0000000..0ab8f5e --- /dev/null +++ b/.doc/Documentacion.md @@ -0,0 +1,712 @@ +# Calculadora MAV - CAS Híbrido + +## Descripción General + +La Calculadora MAV es un **Sistema de Álgebra Computacional (CAS) Híbrido** que combina la potencia de SymPy con clases especializadas para networking, programación y análisis numérico, usando un **sistema de tokenización automática** que convierte patrones específicos en objetos tipados. + +### Características Principales + +- **Motor SymPy completo**: Todas las funciones de cálculo simbólico +- **Tokenización automática invisible**: `16#FF` y `192.168.1.1` se convierten automáticamente en objetos tipados +- **Sistema de tipos dinámico**: Auto-descubrimiento desde `custom_types/` +- **Detección automática de ecuaciones**: Sin necesidad de comillas especiales +- **Resultados interactivos**: Plots, matrices y listas clickeables +- **Clases especializadas**: IntBase, FourBytes, IP4, Hex, Bin, Chr y más +- **Variables SymPy puras**: Todas las variables son símbolos automáticamente +- **Contexto limpio por evaluación**: Comportamiento predecible sin "memoria" de evaluaciones anteriores + +## Instalación + +### Método 1: Instalación Automática +```bash +python calc.py --setup +``` + +### Método 2: Instalación Manual +```bash +# Instalar dependencias +pip install sympy matplotlib numpy + +# En Linux: instalar tkinter +sudo apt-get install python3-tk + +# Ejecutar tests (opcional) +python test_suite.py + +# Iniciar aplicación +python calc.py +``` + +### Dependencias Requeridas +- **Python 3.8+** +- **SymPy ≥ 1.12** (motor algebraico) +- **Matplotlib ≥ 3.7.0** (plotting) +- **NumPy ≥ 1.24.0** (cálculos numéricos) +- **Tkinter** (interfaz gráfica, incluido con Python) + +### Dependencias Opcionales +- **Markdown ≥ 3.4.0** (ayuda mejorada) +- **pytest ≥ 7.0.0** (testing) + +## Guía de Uso + +### Tokenización Automática ⭐ **NUEVO** + +La aplicación reconoce patrones específicos y los convierte automáticamente en objetos tipados: + +#### Números con Base (IntBase) +```python +# Tokenización automática de números con base +16#FF # → IntBase('FF', 16) = 255 +2#1010 # → IntBase('1010', 2) = 10 +8#777 # → IntBase('777', 8) = 511 +16#x0 # → IntBase('x0', 16) = simbólico + +# Aritmética que mantiene la base original +16#FF + 16#10 # → IntBase('10F', 16) = 271 +2#1010 * 3 # → IntBase('11110', 2) = 30 +``` + +#### Patrones x.x.x.x (FourBytes) +```python +# Tokenización automática de patrones dotted +192.168.1.1 # → FourBytes('192.168.1.1') +255.255.255.0 # → FourBytes('255.255.255.0') +10.x.1.y # → FourBytes('10.x.1.y') = simbólico + +# Aritmética de direcciones (32-bit) +192.168.1.1 + 5 # → FourBytes('192.168.1.6') +10.0.0.0 + 256 # → FourBytes('10.0.1.0') +``` + +### Clases Especializadas + +#### Uso Natural con Constructores +```python +# Los constructores reciben objetos ya tokenizados +IP4(192.168.1.100, 24) # FourBytes automático +IP4(10.0.0.1, 255.255.0.0) # Ambos son FourBytes +Hex(16#FF) # IntBase automático +Dec(255) # Entero normal +Chr(65) # Carácter ASCII +``` + +#### Métodos de Clases Especializadas +```python +# Métodos de IP4 +ip = IP4(192.168.1.100, 24) +ip.NetworkAddress() # → IP4(192.168.1.0, 24) +ip.BroadcastAddress() # → IP4(192.168.1.255, 24) +ip.Nodes() # → 254 (hosts disponibles) + +# Conversiones de IntBase +val = 16#FF # → IntBase('FF', 16) +val.to_decimal() # → 255 +val.to_base(2) # → IntBase('11111111', 2) + +# Conversiones de FourBytes +addr = 192.168.1.1 # → FourBytes('192.168.1.1') +addr.ToHex() # → "16#C0.16#A8.16#1.16#1" +addr.ToBinary() # → "2#11000000.2#10101000.2#1.2#1" +``` + +### Conversiones Automáticas Bidireccionales + +#### Flujo Natural Entre Tipos +```python +# IP con máscara hexadecimal +IP4(192.168.1.1, 16#ffffff00) # IntBase → IP4Mask automático + +# Conversión desde IP a Hex +ip_bytes = 192.168.1.1 # → FourBytes +hex_ip = Hex(ip_bytes) # → Hex con valor 32-bit + +# Análisis de máscara en múltiples bases +mask = 255.255.255.0 # → FourBytes +mask_hex = Hex(mask) # → Hex basado en 32-bit +mask_bin = Bin(mask) # → Bin por elementos +mask_cidr = IP4Mask(mask) # → /24 + +# Operaciones mixtas +base_ip = 192.168.0.0 # → FourBytes +offset = 16#100 # → IntBase('100', 16) = 256 +next_ip = base_ip + offset # → FourBytes('192.168.1.0') +``` + +### Ecuaciones y Álgebra + +#### Detección Automática de Ecuaciones +```python +# Ecuaciones simples (detectadas automáticamente) +x + 2 = 5 +3*a + b = 10 +y**2 = 16 + +# Desigualdades +x > 5 +a <= 10 +b != 0 + +# Ecuaciones complejas +sin(x) = 1/2 +log(y) + 3 = 5 + +# Ecuaciones con objetos tipados +10.x.1.1 + 256 = 10.5.1.1 # Resuelve: x = 4 +``` + +#### Resolución de Ecuaciones +```python +# Resolver ecuación específica +solve(x**2 + 2*x - 8, x) # [-4, 2] + +# Atajo para resolver variable +x=? # Equivale a solve(x) + +# Resolver sistema de ecuaciones +x + y = 10 +x - y = 2 +solve([x + y - 10, x - y - 2], [x, y]) # {x: 6, y: 4} + +# Sistemas con objetos tipados +network = 10.a.b.0 +solve(network + 256 == 10.5.1.0, [a, b]) # Encuentra valores de a, b +``` + +### Variables y Símbolos + +#### Variables SymPy Automáticas +```python +# Todas las variables son símbolos SymPy automáticamente +x + 2*y # Expresión simbólica +z = 5 # z es Symbol('z') con valor 5 +w = z**2 + 3 # w es expresión: Symbol('z')**2 + 3 + +# Con objetos tipados +ip_base = 10.x.1.1 # FourBytes simbólico +ip_final = ip_base + 10 # Aritmética simbólica +substitute(ip_final, x=5) # → FourBytes('10.5.1.11') +``` + +#### Contexto Limpio por Evaluación +```python +# Cada modificación reevalúa todo desde cero: +x # → Symbol('x') puro (sin valor) +x = 1 # → Asigna x = 1 +y = x + 2 # → y = 3 (usa x=1 definido arriba) + +# Si modifica cualquier línea, se reevalúa todo nuevamente +# Esto garantiza comportamiento predecible y determinista +``` + +### Funciones Matemáticas + +#### Cálculo Diferencial e Integral +```python +# Derivadas con objetos tipados +diff(16#x0, x) # → 16*log(16) +diff(sin(192.168.x.1), x) # → cos(3232235777 + x) + +# Integrales +integrate(x**2, x) # → x**3/3 +integrate(sin(x), (x, 0, pi)) # → 2 + +# Límites +limit(sin(x)/x, x, 0) # → 1 + +# Series de Taylor +series(exp(x), x, 0, 5) # → 1 + x + x**2/2 + x**3/6 + x**4/24 + O(x**5) +``` + +#### Funciones Trigonométricas +```python +# Funciones básicas +sin(pi/2) # → 1 +cos(0) # → 1 +tan(pi/4) # → 1 + +# Con valores hexadecimales +sin(16#FF / 255 * pi) # → Seno de π (aproximadamente) +``` + +#### Álgebra y Simplificación +```python +# Simplificación +simplify((x**2 - 1)/(x - 1)) # → x + 1 +expand((x + 1)**3) # → x**3 + 3*x**2 + 3*x + 1 +factor(x**2 - 1) # → (x - 1)*(x + 1) + +# Con objetos especializados +simplify(16#FF + 2#1010 - 265) # → 0 (IntBase convertido automáticamente) +``` + +### Álgebra Lineal + +#### Matrices +```python +# Crear matrices +M = Matrix([[1, 2], [3, 4]]) +N = Matrix([[5, 6], [7, 8]]) + +# Operaciones básicas +M + N # Suma de matrices +M * N # Multiplicación +M**2 # Potencia + +# Propiedades (clickeables en interfaz) +det(M) # Determinante: -2 +inv(M) # Matriz inversa +M.transpose() # Transpuesta +``` + +### Plotting Interactivo + +#### Plots 2D +```python +# Plot básico +plot(sin(x), (x, -2*pi, 2*pi)) + +# Múltiples funciones +plot(sin(x), cos(x), (x, 0, 2*pi)) + +# Plot con objetos tipados +plot(16#x/256, (x, 0, 255)) # Hex values +plot(sin(192.168.x.1 / 1000), (x, 0, 255)) # IP arithmetic +``` + +#### Plots 3D +```python +# Superficie 3D +plot3d(x**2 + y**2, (x, -5, 5), (y, -5, 5)) + +# Con funciones trigonométricas +plot3d(sin(x)*cos(y), (x, 0, 2*pi), (y, 0, 2*pi)) + +# Con objetos tipados +plot3d(16#x + 16#y, (x, 0, 255), (y, 0, 255)) +``` + +### Resultados Interactivos + +#### Elementos Clickeables +- **📊 Ver Plot**: Abre ventana matplotlib para plots +- **📋 Ver Matriz**: Muestra matriz formateada con operaciones +- **📋 Ver Lista**: Expande listas largas +- **🔍 Ver Detalles**: Información completa de objetos + +#### Ejemplo de Uso +```python +# Estos resultados serán clickeables en la interfaz +Matrix([[1, 2, 3], [4, 5, 6]]) # → 📋 Ver Matriz 2×3 +plot(x**2, (x, -10, 10)) # → 📊 Ver Plot +solve(x**3 - 6*x**2 + 11*x - 6, x) # → 📋 Ver Soluciones +``` + +## Casos de Uso Avanzados + +### Análisis de Redes con Álgebra +```python +# Definir red con variables +network = 192.168.x.0 # → FourBytes simbólico +subnet_size = 2#100000000 # → IntBase('100000000', 2) = 256 + +# Encontrar valores específicos +solve(network + subnet_size == 192.168.5.0, x) # → x = 4 + +# Análisis de rangos +base_ip = 10.a.b.0 +constraints = [ + base_ip[1] >= 0, # a >= 0 + base_ip[1] <= 255, # a <= 255 + base_ip[2] >= 0, # b >= 0 + base_ip[2] <= 255 # b <= 255 +] +valid_ranges = solve(constraints, [a, b]) +``` + +### Programación y Conversiones Avanzadas +```python +# Análisis de valores hex con variables +hex_pattern = 16#x0 # → IntBase simbólico +solve(hex_pattern + 16#10 == 256, x) # → x = 15 (0xF) + +# Conversiones automáticas en cadena +ip = 192.168.1.1 # → FourBytes +ip_hex = ip.ToHex() # → "16#C0.16#A8.16#1.16#1" +ip_bin = ip.ToBinary() # → "2#11000000.2#10101000.2#1.2#1" + +# Operaciones bit a bit con conversión automática +mask1 = 255.255.255.0 # → FourBytes +mask2 = 255.255.0.0 # → FourBytes +combined = mask1 & mask2 # → Operación bit a bit +``` + +### Análisis Matemático con Tipos Especializados +```python +# Función compleja con valores hex +f = sin(16#x * pi / 256) # IntBase convertido automáticamente + +# Análisis completo +df_dx = diff(f, x) # Derivada +critical_points = solve(df_dx, x) # Puntos críticos +integral = integrate(f, (x, 0, 255)) # Integral en rango hex + +# Visualización +plot(f, df_dx, (x, 0, 255)) # Plot función y derivada +``` + +### Sistemas de Ecuaciones con Redes +```python +# Sistema con direcciones IP +ip1 = 192.168.x.y +ip2 = 10.a.b.c + +# Condiciones de red +ip1 + 256 = 192.168.5.10 +ip2.NetworkAddress() = 10.1.0.0 + +# Resolver para encontrar valores válidos +solve([ + ip1 + 256 - 192.168.5.10, + ip2[1] - 1, # a = 1 + ip2[2] - 0 # b = 0 +], [x, y, a, b, c]) +``` + +## Sistema de Tipos Dinámico ⭐ **NUEVO** + +### Auto-descubrimiento de Tipos +```python +# El sistema automáticamente descubre tipos desde custom_types/ +custom_types/ +├── intbase_type.py # IntBase + tokenización + registro +├── fourbytes_type.py # FourBytes + tokenización + registro +├── hex_type.py # Hex (usa IntBase) +├── bin_type.py # Bin (usa IntBase) +├── ip4_type.py # IP4 + IP4Mask (usa FourBytes) +├── chr_type.py # Chr +└── latex_type.py # LaTeX +``` + +### Tokenización Distribuida +Cada clase define sus propios patrones de reconocimiento: +```python +# En intbase_type.py +@staticmethod +def get_tokenization_patterns(): + return [ + { + 'pattern': r'(\d+)#([0-9A-Fa-fx]+)', + 'replacement': lambda match: f'IntBase("{match.group(2)}", {match.group(1)})', + 'priority': 5, + 'description': 'Números con base: 16#FF, 2#1010' + } + ] +``` + +### Precedencia Automática +- **IntBase**: Prioridad 5 (alta) - patrón muy específico +- **FourBytes**: Prioridad 10 (media) - patrón más general +- Sistema ordena automáticamente por especificidad + +### Información de Tipos +Use **Menú Tipos → Información de tipos** para ver tipos descubiertos automáticamente. + +## Interfaz de Usuario + +### Paneles +- **Panel Izquierdo**: Entrada de código +- **Panel Derecho**: Resultados con colores y elementos interactivos + +### Sistema de Ayuda Dinámico +- **Helpers auto-descubiertos**: Cada tipo define su ayuda contextual +- **Autocompletado inteligente**: Escribir `.` muestra métodos del objeto +- **PopupFunctionList**: Lista de métodos disponibles por tipo + +### Menús Actualizados +- **Archivo**: Nuevo, Cargar, Guardar +- **Editar**: Limpiar entrada/salida, Limpiar historial +- **Configuración**: Modo simbólico, Recargar tipos personalizados +- **Tipos**: Información de tipos, Sintaxis de tipos ⭐ **NUEVO** +- **Ayuda**: Guías, sintaxis, funciones SymPy + +### Autocompletado Avanzado +```python +# Ejemplos de autocompletado tras punto: +192.168.1.1. # → Métodos de FourBytes +16#FF. # → Métodos de IntBase +IP4(10.1.1.1). # → Métodos de IP4 +Matrix([[1,2]]). # → Métodos de SymPy Matrix +``` + +## Configuración y Personalización + +### Archivos de Configuración +- **`hybrid_calc_settings.json`**: Configuración de ventana y UI +- **`hybrid_calc_history.txt`**: Historial de sesión anterior +- **`logs/`**: Archivos de log para debugging + +### Modo Simbólico Configurable +- **Resultado simbólico**: Siempre disponible +- **Aproximación numérica**: Configurable (mostrar/ocultar) +- **Fracciones simbólicas**: Mantener como fracción vs decimal +- **Auto-simplificación**: Activar/desactivar + +## Resolución de Problemas + +### Errores Comunes + +#### Dependencias Faltantes +```bash +# Error: ModuleNotFoundError: No module named 'sympy' +pip install sympy matplotlib numpy + +# Linux: tkinter no disponible +sudo apt-get install python3-tk +``` + +#### Problemas de Tokenización +```python +# Problema: Pattern no reconocido +192.168.1 # No es x.x.x.x completo, no se tokeniza + +# Solución: Usar patrón completo +192.168.1.1 # → FourBytes automático + +# Verificar tipos cargados +# Menú Tipos → Información de tipos +``` + +#### Problemas de Variables +```python +# Las variables son símbolos automáticamente +x = 5 # → x es Symbol('x') con valor 5, no variable Python +y = x + 2 # → y es Symbol('x') + 2, evaluado como 7 + +# Con objetos tipados +ip = 10.x.1.1 # → FourBytes simbólico +ip.substitute(x=5) # → FourBytes('10.5.1.1') +``` + +### Performance + +#### Optimizaciones Implementadas +- **Contexto limpio**: Cada evaluación es independiente y predecible +- **Tokenización cacheada**: Patrones frecuentes se cachean +- **Evaluación lazy**: `evalf()` solo cuando se muestra resultado +- **Auto-descubrimiento optimizado**: Una sola carga al inicio + +#### Límites Conocidos +- Sistemas de ecuaciones muy grandes pueden ser lentos +- Plots 3D complejos requieren tiempo de renderizado +- Matrices muy grandes pueden consumir memoria +- Objetos simbólicos complejos necesitan más tiempo de evaluación + +### Debugging del Sistema de Tipos + +#### Verificación de Auto-descubrimiento +```python +# En código o logs +print(f"Tipos descubiertos: {len(registered_classes)}") +print(f"Reglas de tokenización: {len(tokenization_rules)}") + +# En interfaz +# Menú Tipos → Información de tipos +``` + +#### Recarga de Tipos en Desarrollo +```python +# Para desarrollo en tiempo real +# Menú Configuración → Recargar Tipos Personalizados +# O reiniciar aplicación para cambios en custom_types/ +``` + +## Desarrollo y Extensión + +### Estructura Actual del Proyecto +``` +calculadora-mav-cas/ +├── calc.py # Launcher principal +├── main_calc_app.py # Aplicación principal +├── main_evaluation.py # Motor CAS híbrido +├── type_registry.py # Auto-descubrimiento de tipos ⭐ +├── tl_bracket_parser.py # Tokenizador universal ⭐ +├── tl_popup.py # Resultados interactivos +├── class_base.py # Clases base +├── sympy_Base.py # Base para integración SymPy +├── sympy_helper.py # Helpers para SymPy +├── custom_types/ # ⭐ Sistema de tipos unificado +│ ├── intbase_type.py # IntBase + tokenización +│ ├── fourbytes_type.py # FourBytes + tokenización +│ ├── hex_type.py # Hex (usa IntBase) +│ ├── bin_type.py # Bin (usa IntBase) +│ ├── ip4_type.py # IP4 + IP4Mask (usa FourBytes) +│ ├── chr_type.py # Chr +│ └── latex_type.py # LaTeX +├── logs/ # Logs de debugging +└── docs/ # Documentación técnica +``` + +### Agregar Nuevos Tipos ⭐ **SIMPLIFICADO** + +#### 1. Crear Archivo en `custom_types/` +```python +# custom_types/ejemplo_type.py +from class_base import ClassBase +import re + +class Class_Ejemplo(ClassBase): + def __init__(self, value_input): + # Procesar entrada + super().__init__(processed_value, original_str) + + @staticmethod + def get_tokenization_patterns(): + """Define cómo reconocer y tokenizar este tipo""" + return [ + { + 'pattern': r'@([a-zA-Z0-9_]+)', + 'replacement': lambda match: f'Ejemplo("{match.group(1)}")', + 'priority': 8, + 'description': 'Patrones @ejemplo' + } + ] + + @staticmethod + def Helper(input_str): + if re.match(r"^\s*@\w+", input_str): + return "Ej: @usuario, @sistema. Funciones: .validate(), .expand()" + return None + + @staticmethod + def PopupFunctionList(): + return [ + ("validate", "Valida formato del ejemplo"), + ("expand", "Expande el valor del ejemplo") + ] + +def register_classes_in_module(): + return [ + ("Ejemplo", Class_Ejemplo, "ClassBase", { + "add_lowercase": True, + "supports_brackets": False, + "has_tokenization": True, + "description": "Ejemplos con @patron" + }) + ] +``` + +#### 2. Usar Automáticamente +```python +# Después de crear el archivo, automáticamente funciona: +@usuario # → Ejemplo('usuario') +@sistema.validate() # → Autocompletado disponible +``` + +### Tokenización Personalizada Avanzada + +#### Múltiples Patrones por Tipo +```python +@staticmethod +def get_tokenization_patterns(): + return [ + { + 'pattern': r'([0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2})', + 'replacement': lambda match: f'MAC("{match.group(1)}", format="colon")', + 'priority': 6, + 'description': 'MAC con dos puntos: AA:BB:CC:DD:EE:FF' + }, + { + 'pattern': r'([0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2})', + 'replacement': lambda match: f'MAC("{match.group(1)}", format="dash")', + 'priority': 6, + 'description': 'MAC con guiones: AA-BB-CC-DD-EE-FF' + } + ] +``` + +### Herencia de Tipos Base + +#### Usar IntBase para Nuevas Bases +```python +# custom_types/octal_type.py +from sympy_Base import SympyClassBase + +class Class_Octal(SympyClassBase): + def __init__(self, value_input): + # Obtener IntBase dinámicamente del registro + IntBase = get_intbase_class() + + if isinstance(value_input, IntBase): + if value_input.base == 8: + self.int_base = value_input + else: + # Convertir a octal + octal_value = IntBase._convert_to_base_string(value_input._numeric_value, 8) + self.int_base = IntBase(octal_value, 8) + + super().__init__(self.int_base.value, str(self.int_base)) + +# Automáticamente disponible: 8#777 → IntBase, Octal(8#777) → Class_Octal +``` + +## Changelog + +### Versión 2.1 (Sistema de Tipos Dinámico) ⭐ **ACTUAL** +- ✅ **Tokenización automática**: Patrones `16#FF` y `192.168.1.1` convertidos automáticamente +- ✅ **Sistema de tipos dinámico**: Auto-descubrimiento desde `custom_types/` +- ✅ **Clases base universales**: IntBase y FourBytes como tipos fundamentales +- ✅ **Tokenización distribuida**: Cada clase define sus propios patrones +- ✅ **Parser genérico**: Sin hardcoding de tipos específicos +- ✅ **Conversión perezosa a SymPy**: Objetos nativos hasta que se necesite álgebra +- ✅ **Helpers dinámicos**: Sistema de ayuda contextual auto-descubierto +- ✅ **Aritmética nativa**: Operaciones que preservan tipos originales + +### Versión 2.0 (CAS Híbrido) - **ANTERIOR** +- ✅ Motor SymPy completo como base +- ❌ ~~Sintaxis con corchetes únicamente~~ **ELIMINADO** +- ✅ Detección automática de ecuaciones +- ✅ Variables SymPy puras +- ✅ Resultados interactivos clickeables +- ✅ Sistema de plotting integrado +- ✅ Arquitectura modular extensible + +### Diferencias vs Versión Anterior +| Característica | v2.0 | v2.1 | +|---|---|---| +| Sintaxis | `Class[args]` | Tokenización automática | +| Tipos | Hardcodeados | Auto-descubrimiento | +| Parser | Específico | Genérico distribuido | +| Extensibilidad | Manual | Automática | +| Aritmética | SymPy siempre | Nativa + SymPy cuando se necesite | +| Tokenización | Centralizada | Distribuida por clase | + +## FAQ + +### ¿Cómo funciona la tokenización automática? +El sistema reconoce patrones como `16#FF` y `192.168.1.1` automáticamente y los convierte en objetos `IntBase` y `FourBytes` respectivamente. No necesita sintaxis especial. + +### ¿Puedo usar la sintaxis de corchetes? +No. La v2.1 usa tokenización automática invisible que es más natural e intuitiva. + +### ¿Cómo agregar nuevos tipos? +1. Crear archivo en `custom_types/nuevo_type.py` +2. Implementar clase con `get_tokenization_patterns()` y `register_classes_in_module()` +3. El sistema lo detecta automáticamente + +### ¿Los resultados son exactos? +Sí. Los objetos mantienen precisión nativa hasta que se convierten a SymPy para álgebra compleja. + +### ¿Cómo funcionan las conversiones entre tipos? +Son automáticas y bidireccionales. Por ejemplo: `Hex(192.168.1.1)` convierte FourBytes a Hex automáticamente. + +### ¿Puedo crear tipos que usen múltiples patrones? +Sí. Cada tipo puede definir múltiples patrones con diferentes prioridades en `get_tokenization_patterns()`. + +### ¿Cómo debuggear problemas de tipos? +Use **Menú Tipos → Información de tipos** para ver tipos descubiertos y **Menú Configuración → Recargar Tipos** para desarrollo. + +--- + +*Calculadora MAV - CAS Híbrido v2.1* +*Sistema extensible con tokenización automática para cálculo matemático avanzado* \ No newline at end of file diff --git a/.doc/Guia_Desarrollo.md b/.doc/Guia_Desarrollo.md deleted file mode 100644 index 68d7cec..0000000 --- a/.doc/Guia_Desarrollo.md +++ /dev/null @@ -1,549 +0,0 @@ -# Guía de Desarrollo - Calculadora MAV CAS Híbrido - -## Visión General - -Este documento describe el estado actual y los objetivos de desarrollo para la **Calculadora MAV**, un **Sistema de Álgebra Computacional (CAS) híbrido**. El proyecto busca combinar la potencia de SymPy como motor algebraico central con capacidades especializadas (IP4, Hex, etc.) integradas de forma nativa. - -## Objetivos Principales -### 1. **SymPy como Motor Central** -- Integrar todas las funciones de SymPy directamente -- SymPy maneja toda la evaluación algebraica y ecuaciones -- Cada linea de ingreso corresponde a una sola linea de resultados -- **CONTEXTO LIMPIO POR EVALUACIÓN**: Cada vez que se hace una evaluación se comienza con el contexto completamente limpio y se evalúa desde arriba hacia abajo, línea por línea. Esto garantiza comportamiento predecible sin "memoria" de evaluaciones anteriores. -- La interfaz de usuario se divide en 2 columnas, a la izquierda el area de ingreso de datos y equaciones a ser evaluadas, a la derecha el area de resultados que se colorean segun cada tipo de respuesta. - -### 2. **Sintaxis Simplificada con Corchetes (Única)** -- Usar **exclusivamente** `Class[args]` en lugar de `Class("args")` . Cuando se usan los corchetes los parametros se separan por `;` -- Los corchetes indican parsing especial (agregar comillas automáticamente) -- Mantiene sintaxis limpia y reduce errores de tipeo - -### 3. **Detección Automática y Manejo de Ecuaciones** -- Ecuaciones ingresadas directamente (ej. `3+b=5+c`) se detectan y añaden al sistema interno de SymPy. -- Soporte para comparaciones SymPy (ej. `x > 5`, `a == b`, `x <= 10`) como expresiones evaluables. -- Sintaxis de atajo para solve: `variable=?` equivale a `solve(variable)` - -### 4. **Evaluación Híbrida con Variables SymPy Puras** -- Resultado simbólico (siempre disponible) -- Evaluación numérica con `.evalf()` (cuando sea aplicable y las variables estén definidas). Esta evaluacion se muestra a la derecha del resultado simbolico. -- Las asignaciones son siempre simbolicas, solo los resultados se muestra la evaluacion numerica. -- **Todas las variables son símbolos SymPy**: Sin variables Python tradicionales -- **Sin eval/exec por defecto**: Solo SymPy, a menos que se indique sintaxis especial - -### 4. **Resultados Interactivos** -- **Tags clickeables** en el widget Text para resultados complejos que necesitan mas de una linea de resultado como Plot, Matrices y Listas. -- **Detección automática** de tipos que requieren visualización especial: - - Plots de SymPy → "📊 Ver Plot" (abre ventana matplotlib). - - Matrices → "📋 Ver Matriz" (muestra matriz formateada). - - Listas largas → "📋 Ver Lista" (muestra contenido expandido). - - Objetos complejos → "🔍 Ver Detalles" (representación completa). -- **Implementación simple**: Usar `text.tag_bind()` en widget existente -- **Ventanas popup** para contenido que no cabe en línea. - - **Actualización automática**: Las ventanas de plot, listas y matrices deben tener la capacidad de actualizarse automáticamente si los datos subyacentes cambian y la ventana permanece abierta. - - **Posicionamiento inteligente**: - - Cuando no hay otras ventanas de resultados (plot, lista, matriz) abiertas, la primera que se abra debe tener su altura igual a la de la ventana principal de la aplicación. - - Esta primera ventana se posicionará automáticamente a la derecha de la ventana principal de la aplicación. -### 5. **Clases Especializadas como Objetos SymPy** -- Todas las clases (IP4, Hex, Bin, etc.) heredan de `sympy.Basic` -- Participan en manipulación algebraica: `solve()`, `diff()`, `integrate()` -- Mantienen funcionalidad especializada (métodos `.NetworkAddress()`, `.toHex()`, etc.) - -## Arquitectura Propuesta - -### Estructura de archivos -- Subcarperta de log "/.log" -- Subcarpeta de documentos md de ayuda y documentacion tecnica "/.doc" -- Aplicacion principal launcher: main_calc.py -- Aplicacion principal app: main_calc_app.py -- Motor de evaluacion: main_evaluation.py -- Motor de ventanas emergentes: tl_popup.py -- Objetos especiales: [nombre objeto]_type.py . Ejemplo ip4_type.py -- Tools del motor: tl_[nombre tool].py . Ejemplo: tl_bracket_parser.py - - -### Flujo de Procesamiento -``` -Entrada Usuario → [Bracket Parser] → [SymPy Evaluation] → { - 1. Resultado Simbólico (siempre) - 2. evalf() (si variables definidas) - 3. Métodos especializados disponibles -} -``` - -### Transformación de Sintaxis -```python -# Clases con corchetes: -IP4[192.168.1.1/24].NetworkAddress[] → IP4("192.168.1.1/24").NetworkAddress() - -# Detección contextual de ecuaciones: -3+b=5+c → Se detecta como ecuación standalone (agregar al sistema SymPy) -"x > 5" → Se procesa como desigualdad SymPy -x**2 + 2*x == 0 → Se detecta como igualdad lógica standalone - -# NO se detectan como ecuaciones (parsing contextual): -solve(x + 2 = 5, x) → "=" es argumento de función, no ecuación -result = solve(a + b = 10, a) → "=" izquierdo es asignación Python -IP4[192.168.1.1].Mask() == "255.255.255.0" → Expresión de comparación - -# Sintaxis de atajo para solve: -x=? → solve(x) -``` - -## Componentes Técnicos - -### 1. **Bracket Parser y Detección Contextual de Ecuaciones** -- Preprocesador que convierte `Class[args]` → `Class("args")` -- La separacion de argumentos se hace con el ";" -- Soporte para clases: `IP4`, `Hex`, `Bin`, `Date`, `Dec`, `Chr` -- **Detección contextual inteligente de ecuaciones**: - - Ecuaciones standalone: `3+b=5+c` (detectar como ecuación) - - Strings con ecuaciones: `"expresión = expresión"` (detectar como ecuación) - - **NO detectar** cuando `=`, `==`, `>`, etc. están dentro de llamadas a funciones: `solve(x=5, y)` - - **NO detectar** asignaciones Python: `result = solve(...)` -- Conversión de atajo: `variable=?` → `solve(variable)` -- Usa AST parsing para distinguir contexto sintáctico -- Extensible para futuras transformaciones más complejas - -### 2. **Clases Híbridas** -```python -class IP4(BaseCalcType, sympy.Basic): - def __new__(cls, address, mask=None): - # Crear objeto SymPy válido - obj = sympy.Basic.__new__(cls) - # Inicializar funcionalidad IP4 - obj._init_ip4(address, mask) - return obj - - def _sympystr(self, printer): - return f"IP4({self.address})" -``` - -### 3. **Sistema de Evaluación y Detección Contextual** -```python -def detect_equation_context(code_line): - """ - Usa AST para determinar si una línea contiene una ecuación standalone - vs. comparaciones dentro de funciones o asignaciones Python - """ - tree = ast.parse(code_line) - - # Casos que NO son ecuaciones: - # - Asignaciones: result = solve(...) - # - Argumentos de función: solve(x = 5, y) - # - Expresiones dentro de llamadas: func(a == b) - - # Casos que SÍ son ecuaciones: - # - Comparaciones standalone: x + 2 == 5 - # - Strings con ecuaciones: "a = b" - - return is_standalone_equation - -def evaluate_expression(expr): - # 1. Detección contextual de ecuaciones - if detect_equation_context(expr): - add_to_equation_system(expr) - return "Ecuación agregada al sistema" - - # 2. Evaluación simbólica (SymPy) - symbolic_result = sympy.sympify(expr) - - # 3. Intentar evaluación numérica - try: - if all_variables_defined(symbolic_result): - numeric_result = symbolic_result.evalf() - else: - numeric_result = None - except: - numeric_result = None - - return { - 'symbolic': symbolic_result, - 'numeric': numeric_result - } -``` - -## Capacidades Expandidas - -### 1. **Funciones SymPy Completas** -```python -# Todas disponibles directamente: -diff(IP4[192.168.x.1] + offset, x) -integrate(Hex[x] * sin(x), x) -solve([IP4[192.168.x.y] == IP4[192.168.1.10]], [x, y]) -limit(Dec[x]/x, x, 0) -series(exp(Hex[x]), x, 0, 5) -``` - -### 2. **Plotting Integrado con Interface Interactiva** -```python -# SymPy plotting nativo: -plot(sin(Hex[x]), (x, 0, 255)) # Muestra: "📊 Ver Plot" (clickeable) -plot3d(IP4[192.168.x.y].to_decimal(), (x, 1, 254), (y, 1, 254)) - -# Resultados complejos interactivos: -Matrix([[1, 2], [3, 4]]) # Muestra: "📋 Ver Matriz" (clickeable) -solve(x**2 + 2*x + 1, x) # Lista: "📋 Ver Soluciones" (clickeable) -``` - -### 3. **Manipulación Algebraica de Objetos Especializados** -```python -# Objetos especializados en expresiones algebraicas: -network = IP4[192.168.x.0/24] -solve(network.NetworkAddress() == IP4[192.168.5.0/24], x) -# Resultado: x = 5 -``` - -## Ejemplos de Uso Objetivo - -```python -# Evaluación directa con SymPy: -solve(x + 10 - 15, x) # Resultado: [5] - -# Detección automática de ecuaciones: -3+b=5+c # Se agrega automáticamente al sistema -x > 5 # Desigualdad procesada por SymPy -a == b # Igualdad lógica procesada por SymPy - -# Sintaxis de atajo: -x=? # Equivale a solve(x) - -# Solo sintaxis con corchetes: -IP4[192.168.1.1/24].NetworkAddress[] # Resultado: 192.168.1.0/24 - -# Variables puras SymPy: -x = 5 # x es Symbol('x') con valor 5, no variable Python -y = x + 2 # y es expresión simbólica: Symbol('x') + 2 - -# Álgebra con objetos especializados: -base_ip = IP4[10.0.x.1] -solve(base_ip + 255 == IP4[10.0.5.0], x) # Resultado: x = 4 - -# Funciones avanzadas con resultados interactivos: -diff(Hex[x**2], x) # Derivada: 2*x*log(16) -plot(sin(2*pi*Hex[x]/256), (x, 0, 255)) # Resultado: "📊 Ver Plot" - -# Matrices y estructuras complejas: -Matrix([[1, 2, 3], [4, 5, 6]]) # Resultado: "📋 Ver Matriz" -solve([x + y == 10, x - y == 2], [x, y]) # Resultado: "📋 Ver Soluciones" - -# eval/exec opcional con sintaxis especial: -@eval: import os; os.getcwd() # Solo cuando se necesite Python puro -``` - -## Beneficios Esperados - -### 1. **Usabilidad** -- Sintaxis más limpia (sin comillas constantes) -- Acceso completo a capacidades de CAS -- Evaluación automática simbólica + numérica - -### 2. **Potencia Matemática** -- Todas las funciones de SymPy disponibles -- Objetos especializados participan en álgebra -- Capacidades de plotting integradas - -### 3. **Extensibilidad** -- Parser extensible para futuras sintaxis -- Fácil agregar nuevas clases especializadas -- Compatible con ecosystem SymPy completo - -### 4. **Consistencia** -- Un solo motor de evaluación (SymPy) -- Reglas algebraicas consistentes -- Manejo unificado de errores - -## Fases de Implementación - -### Fase 1: Foundation -- Implementar bracket parser básico -- Convertir clases existentes a `sympy.Basic` -- Sistema de evaluación híbrida - -### Fase 2: Integration -- Remover clase `solve` separada -- Integrar todas las funciones SymPy -- Testing de manipulación algebraica - -### Fase 3: Enhancement -- Capacidades de plotting -- Sintaxis extendida del parser -- Optimizaciones de performance - -### Fase 4: Polish -- Documentación completa -- Ejemplos avanzados -- UI/UX improvements - -## Consideraciones Adicionales - -### **Performance y Caching (Crítico)** -- **Caching de líneas precedentes**: Las líneas anteriores no se recalculan -- **Incremental evaluation**: Solo evaluar líneas nuevas/modificadas -- **Caching de parsing**: Bracket parsing se cachea para expresiones repetidas -- **Lazy evalf()**: Solo evaluar numéricamente cuando se solicite mostrar -- **Optimización SymPy**: Configurar para performance en contexto interactivo - -### **Manejo Robusto de Errores y Logging** -- **Logging centralizado enfocado en errores**: El sistema de logging (`main_launcher.py`) debe registrar principalmente errores para facilitar la depuración, evitando logs informativos excesivos en producción. Los logs detallados (DEBUG) pueden activarse con un flag. Solo crear un archivo de log bajo la carpeta "/.logs" -- **Errores de SymPy y del Motor CAS**: Priorizar mensajes de error claros provenientes de SymPy o del motor de evaluación. -- **Errores contextuales**: Mensajes específicos para errores de parsing vs evaluación -- **Sugerencias automáticas**: "¿Quisiste decir...?" para funciones mal escritas -- **eval/exec explícito**: Solo disponible con sintaxis especial cuando sea necesario - -### **Extensibilidad del Parser** -- **Arquitectura modular**: Fácil agregar nuevas transformaciones sintácticas -- **Plugin system**: Permitir extensiones de terceros -- **Configurabilidad**: Usuario puede habilitar/deshabilitar transformaciones - -### **Sistema de Ayuda Avanzado** -- **Autocompletado SymPy**: Funciones y métodos disponibles según contexto o cuando se presiona el "." -- **Help integrado**: `help(diff)` muestra documentación SymPy completa -- **Ejemplos interactivos**: Templates para operaciones comunes -- **Cheat sheet**: Referencia rápida de sintaxis nueva vs antigua - - -## Sistema de Autocompletado y Ayuda Contextual (Helpers) - -### Objetivo - -Proveer al usuario de una experiencia de ayuda y autocompletado inteligente, no invasiva y extensible, que facilite el uso de funciones avanzadas y objetos personalizados, integrando tanto métodos propios como todas las funciones de SymPy. - ---- - -### 1. **Helpers: Ayuda Contextual por Tipo** - -Cada tipo de objeto (por ejemplo, `IP4`, `Hex`, etc.) debe definir una función `Helper(input_str)` que: - -- Recibe el string de entrada del usuario. -- Decide, usando su propia lógica (por ejemplo, expresiones regulares), si puede ofrecer una ayuda relevante. -- Devuelve un string de ayuda (ejemplo de uso, sintaxis, sugerencia) o `None` si no aplica. -- Las respuestas no pueden tener mas de una linea nunca. - -**Ejemplo:** -```python -# En ip4_type.py -def Helper(input_str): - if re.match(r"^\s*IP4(\b|\s*\[.*)?", input_str, re.IGNORECASE): - return 'Ej: IP4[192.168.1.1/24], IP4[10.0.0.1, 8], o IP4[172.16.0.5, 255.255.0.0]' - return None -``` - -**Ventajas:** -- Cada Helper es autocontenido y puede evolucionar de forma independiente. -- Permite personalizar la ayuda para cada tipo de objeto o función. - ---- - -### 2. **Helper para SymPy** - -Dado que ahora todas las funciones de SymPy están disponibles, se debe agregar un Helper general para SymPy que: - -- Detecte si el usuario está comenzando a escribir el nombre de una función de SymPy (por ejemplo, `diff`, `integrate`, `solve`, `limit`, etc.). -- Sugiera la sintaxis básica y ejemplos de uso para la función detectada. -- Puede usar una lista de funciones comunes de SymPy y sus descripciones. - -**Ejemplo:** -```python -# En sympy_helper.py -def Helper(input_str): - sympy_funcs = { - "diff": "Derivada: diff(expr, var). Ej: diff(sin(x), x)", - "integrate": "Integral: integrate(expr, var). Ej: integrate(x**2, x)", - "solve": "Resolver ecuaciones: solve(expr, var). Ej: solve(x**2-1, x)", - "limit": "Límite: limit(expr, var, valor). Ej: limit(sin(x)/x, x, 0)", - # ...agregar más funciones comunes - } - for func, ayuda in sympy_funcs.items(): - if input_str.strip().startswith(func): - return ayuda - return None -``` - ---- - -### 3. **Manejo Centralizado de Helpers** - -En el motor principal de la aplicación, se debe mantener una lista de todas las funciones Helper disponibles (incluyendo la de SymPy). -Al evaluar la línea de entrada y esta da error de evaluacion entonces: - -- Se llama a cada Helper en orden. -- Si alguna Helper retorna una ayuda, se muestra esa ayuda al usuario (en la línea de resultado, tooltip, etc.). -- Si ninguna Helper ayuda, se muestra el mensaje de error real. - -**Ejemplo:** -```python -HELPERS = [ - IP4.Helper, - Hex.Helper, - SympyHelper.Helper, - # ...otros helpers -] - -def obtener_ayuda(input_str): - for helper in HELPERS: - ayuda = helper(input_str) - if ayuda: - return ayuda - return None -``` - ---- - -### 4. **Autocompletado de Métodos y Funciones (Popup tras el punto)** - -- Cuando el usuario escribe un punto (`.`) después de un objeto válido, se evalúa el objeto y se obtiene la lista de métodos disponibles. -- Se muestra un popup de autocompletado con los métodos relevantes (filtrando los no útiles). La lista de funciones se debe obtener de una funcione de cada objeto llamada PopupFunctionList() esta funcion en cada objeto mantendra la lista de las funciones disponibles y una explicacion corta tipo hint. Esta funcion retorna una lista de tuplas con el nombre de la funcion y el hint. -- El usuario puede seleccionar un método con el teclado o mouse, y se inserta automáticamente (con paréntesis si corresponde). -- El popup solo aparece tras el punto, no en cada pulsación de tecla, para no ser invasivo. - -**Puntos clave:** -- Priorizar métodos útiles y públicos. -- Permitir que cada tipo defina una lista de métodos sugeridos. -- Cerrar el popup fácilmente (Escape, clic fuera, etc.). - ---- - -### 5. **Flujo de Evaluación y Ayuda** - -1. El usuario escribe una línea. -2. Si presiona `.`, se muestra el popup de autocompletado de métodos. -3. Si la línea tiene error: - - Se consulta a los Helpers para mostrar ayuda contextual. - - Si ninguna Helper ayuda, se muestra el mensaje de error real. -4. Si la línea es válida, se evalúa normalmente. - ---- - -### 6. **Extensibilidad** - -- Para agregar ayuda para un nuevo tipo, solo se debe definir un nuevo Helper y registrarlo en la lista central. -- El Helper de SymPy puede expandirse para cubrir más funciones y ejemplos. -- El sistema de autocompletado puede ampliarse para sugerir funciones de SymPy al escribir el nombre de la función (no solo tras el punto). - ---- - -### 7. **Resumen de Beneficios** - -- **No invasivo:** El autocompletado solo aparece cuando el usuario lo solicita (tras el punto). -- **Ayuda contextual:** Los Helpers ofrecen ayuda específica y relevante según el contexto. -- **Extensible:** Fácil de agregar nuevos tipos y funciones de ayuda. -- **Consistente:** El usuario recibe ayuda o autocompletado solo cuando es útil, no en cada pulsación. - ---- - -### 8. **Ejemplo de Integración** - -```python -# En el motor principal: -ayuda = obtener_ayuda(linea_usuario) -if ayuda: - mostrar_ayuda(ayuda) -elif error: - mostrar_error(error) -else: - mostrar_resultado(resultado) -``` - ---- - -**Este sistema permite que la ayuda y el autocompletado evolucionen de forma incremental, mejorando la experiencia del usuario sin ser molestos ni invasivos.** - - -### **Gestión de Variables Puras SymPy** -- **Solo símbolos SymPy**: Todas las variables son `Symbol()` automáticamente -- **Sin variables Python**: Eliminación de `eval()` como mecanismo por defecto -- **Evaluación consistente**: `x = 5` crea `Symbol('x')` con valor 5 en contexto -- **eval/exec opcional**: Solo disponible con sintaxis especial (ej: `@eval: código_python`) -- **Contexto unificado**: Un solo namespace SymPy para toda la sesión - -### **Comportamiento de Contexto Limpio (IMPLEMENTADO)** - -**Principio fundamental**: Cada modificación del usuario resulta en una evaluación completa desde cero. - -#### Implementación -- **Limpieza automática**: Cada evaluación inicia con `engine.clear_all()` -- **Construcción incremental**: El contexto se construye línea por línea, de arriba hacia abajo -- **Sin persistencia**: No hay variables "fantasma" de evaluaciones anteriores -- **Predictibilidad total**: El primer `x` en cualquier secuencia siempre es un símbolo puro -- **Evaluación inicial del historial**: Al cargar la aplicación, se evalúa el historial una vez para mostrar resultados - -#### Ejemplo de Comportamiento -```python -# Primera evaluación: solo "x" -x # → Symbol('x') puro, sin valor - -# Segunda evaluación: "x, x=1" -x # → Symbol('x') puro (contexto limpio) -x=1 # → Asigna x=1 - -# Tercera evaluación: "x, x=1, y+x" -x # → Symbol('x') puro (contexto limpio) -x=1 # → Asigna x=1 -y+x # → y + 1 (usa x=1 definido arriba) -``` - -#### Implicaciones para el Desarrollo -- **No necesidad de gestión manual de contexto**: El sistema lo maneja automáticamente -- **Comportamiento determinista**: Misma entrada → mismo resultado, siempre -- **Simplicidad de debugging**: El estado siempre es predecible -- **Eliminación de opciones de menú obsoletas**: "Limpiar variables/ecuaciones" no tienen sentido - -#### Cambios en la Interfaz de Usuario -**Opciones ELIMINADAS (redundantes):** -- Menú CAS completo (variables, ecuaciones, resolver sistema) -- "Limpiar variables", "Limpiar ecuaciones", "Limpiar todo" - -**Opciones CONSERVADAS (útiles):** -- "Limpiar entrada/salida" (afecta interfaz visual) -- "Limpiar historial" (afecta archivo persistente) - -## Métricas de Éxito - -- [X] Sintaxis `Class[args]` funciona consistentemente (`bracket_parser.py`, `hybrid_base_types.py`). -- [+] **Detección contextual inteligente de ecuaciones (Motor CAS/Parser):** - - [+] Ecuaciones standalone: `3+b=5+c`, `x**2 == 0` se detectan y añaden al sistema (`bracket_parser.py`, `hybrid_evaluation_engine.py`). `x**2==0` se evalúa como expresión booleana, no se añade como ecuación al sistema automáticamente. - - [ ] _(Parser)_ Strings con ecuaciones: `"a=b"`, `"x>5"` se procesan como ecuaciones (actualmente el parser maneja `a=b` directo, no como string literal). - - [+] **NO detectar** comparaciones en argumentos: `solve(x=5, y)`, `func(a==b)` (AST en `bracket_parser.py` ayuda, `EquationDetector` es un prototipo). - - [X] **NO detectar** asignaciones Python: `result = solve(...)` (`bracket_parser.py` lo maneja). -- [X] Sintaxis de atajo `variable=?` funciona como `solve(variable)` (`bracket_parser.py`). -- [+] Todas las funciones SymPy disponibles en interfaz (`hybrid_evaluation_engine.py` tiene contexto base amplio; `hybrid_calc_app.py` expone algunas en menús). -- [+] Objetos especializados participan en `solve()`, `diff()`, etc. (`hybrid_base_types.py` heredan de `sympy.Basic`; la profundidad de integración en operaciones aritméticas necesita revisión para preservar el tipo). -- [X] Evaluación híbrida (simbólica + numérica) automática (`hybrid_evaluation_engine.py`). -- [+] Plotting funcional con objetos especializados (Infraestructura en `hybrid_evaluation_engine.py` y `interactive_results.py`; depende de la convertibilidad del objeto a datos ploteables). -- [+] **Resultados interactivos funcionando:** - - [X] Implementación de ventanas popup (Toplevel) para mostrar información detallada (ej. diálogo de error, plots, matrices, listas en `main_launcher.py`, `interactive_results.py`). - - [X] Tags clickeables en el widget Text para resultados complejos (`hybrid_calc_app.py`, `interactive_results.py`). - - [+] Detección automática de tipos para visualización especial: - - [X] Plots de SymPy → "📊 Ver Plot" (`interactive_results.py`). - - [X] Matrices → "📋 Ver Matriz" (`interactive_results.py`). - - [X] Listas largas → "📋 Ver Lista" (`interactive_results.py`). - - [ ] _(UI/Engine)_ Objetos complejos → "🔍 Ver Detalles" (Infraestructura parcial, no implementado genéricamente). - - [ ] _(UI)_ Ventanas de plot/lista/matriz con actualización automática si se mantienen abiertas. - - [+] _(UI)_ Posicionamiento y dimensionamiento inteligente de la primera ventana de plot/lista/matriz (`interactive_results.py` tiene base, necesita lógica de "solo si no hay otras abiertas" y ajuste de altura). -- [+] Performance aceptable para uso interactivo (Debounce en `hybrid_calc_app.py` ayuda). -- [+] **Sistema de ayuda avanzado:** - - [X] Ayuda básica por línea de comandos (`main_launcher.py`). - - [ ] _(UI)_ Autocompletado para funciones SymPy y métodos de objetos (`hybrid_calc_app.py` tiene TODO). - - [+] _(Engine/UI)_ `help()` integrado con documentación SymPy (`hybrid_evaluation_engine.py` tiene base, `hybrid_calc_app.py` tiene menús estáticos). - - [+] _(UI)_ Ejemplos y templates interactivos (`hybrid_calc_app.py` tiene `insert_example`). -- [X] **Manejo robusto de errores y logging (Launcher):** - - [X] Logging centralizado de errores (`main_launcher.py`, configurable para ser error-only). - - [X] Presentación de errores al usuario en ventana dedicada (`main_launcher.py`). - - [X] Verificación de dependencias y archivos de la aplicación al inicio (`main_launcher.py`). - - [X] Intento de instalación automática de dependencias (`main_launcher.py`). - - [X] Provisión de comandos para troubleshooting (`main_launcher.py`). -- [+] **Manejo robusto de errores (Motor CAS):** - - [+] Solo errores de SymPy, sin fallbacks automáticos a eval/exec (Motor (`hybrid_evaluation_engine.py`) usa `eval` y `sympify`; se busca minimizar `eval` directo de input de usuario). - - [+] Errores contextuales: mensajes específicos para errores de parsing vs. evaluación (`EvaluationResult` tiene campo de error). - - [ ] _(Engine)_ Sugerencias automáticas: "¿Quisiste decir...?" para funciones mal escritas. - - [ ] _(Engine/Parser)_ `eval/exec` explícito: solo disponible con sintaxis especial (ej. `@eval:`) (Planeado, no implementado). -- [ ] **Performance optimizada (Motor CAS):** - - [ ] Caching de expresiones repetidas - - [ ] Evaluación lazy de `evalf()` - - [ ] Tiempo de respuesta < 200ms para operaciones comunes -- [+] **Parser extensible** con arquitectura modular (`bracket_parser.py` es un buen inicio). -- [+] **Documentación y ejemplos completos:** - - [X] Docstrings en el código (`main_launcher.py`, `hybrid_evaluation_engine.py`, etc.). - - [X] Guía de Desarrollo (este documento). - - [X] Splash screen eliminado (`main_launcher.py`). - - [+] Documentación de usuario final completa (`comprehensive_documentation.md` es una base sólida). - - [+] Ejemplos de uso avanzados en la documentación (`comprehensive_documentation.md`). \ No newline at end of file diff --git a/.doc/OLD_calc.py b/.doc/OLD_calc.py deleted file mode 100644 index 7ae8f73..0000000 --- a/.doc/OLD_calc.py +++ /dev/null @@ -1,1262 +0,0 @@ -""" -Calculadora MAV - Archivo principal -""" -import tkinter as tk -from tkinter import scrolledtext, messagebox, Menu, filedialog -import ast -import json -import os -import math -import sympy -import tkinter.font as tkFont - -# Importar las clases desde los módulos separados -from base_types import BaseCalcType -from hex_type import Hex -from bin_type import Bin -from ip4_type import IP4 -from date_type import Date -from dec_type import Dec -from chr_type import Chr # Importar la nueva clase Chr -from solve_type import solve -from line_analyzer import LineResultCapture -from sympy_integration import SymPyIntegration - -# Para la ayuda en HTML -MARKDOWN_AVAILABLE = False -HTML_VIEWER_TYPE = None - -try: - import markdown - MARKDOWN_AVAILABLE = True -except ImportError: - # markdown not available, MARKDOWN_AVAILABLE remains False - pass # Se mostrará una advertencia más adelante si es necesario - -# Intentar importar visores HTML -try: - import tkinterweb - HTML_VIEWER_TYPE = "tkinterweb" -except ImportError: - try: - from tkhtmlview import HTMLScrolledText - HTML_VIEWER_TYPE = "tkhtmlview" - except ImportError: - HTML_VIEWER_TYPE = None # Ambos visores fallaron - -if not MARKDOWN_AVAILABLE: - print("Advertencia: La librería 'markdown' no está instalada. La ayuda podría mostrarse en texto plano o con formato limitado.") -if MARKDOWN_AVAILABLE and HTML_VIEWER_TYPE is None: # Si markdown está pero no hay visor - print("Advertencia: 'markdown' está disponible, pero no se encontró un visor HTML (tkinterweb/tkhtmlview). La ayuda se mostrará en texto plano.") - -class CalculatorApp: - SETTINGS_FILE = "calc_settings.json" - CUSTOM_CLASSES_WITH_HELPERS = [Hex, Bin, IP4, Date, Dec, solve, Chr] # Agregar Chr - OPERATOR_HELP_MESSAGES = { - "^": "Operador XOR a nivel de bits (ej: 5 ^ 3) o diferencia simétrica para conjuntos (ej: {1,2} ^ {2,3}). Requiere un segundo operando.", - "+": "Operador de suma. Requiere un segundo operando.", - "-": "Operador de resta. Requiere un segundo operando.", - "*": "Operador de multiplicación. Requiere un segundo operando.", - "/": "Operador de división. Requiere un segundo operando.", - "**": "Operador de potencia. Requiere un segundo operando.", - "//": "Operador de división entera. Requiere un segundo operando.", - "%": "Operador de módulo. Requiere un segundo operando.", - "&": "Operador AND a nivel de bits. Requiere un segundo operando.", - "|": "Operador OR a nivel de bits. Requiere un segundo operando.", - } - - def __init__(self, root): - self.root = root - self.root.title("Calculadora MAV") - - # Cargar y establecer el ícono de la aplicación - try: - self.app_icon = tk.PhotoImage(file="icon.png") - self.root.iconphoto(True, self.app_icon) - except tk.TclError: - print("Advertencia: No se pudo cargar o establecer 'icon.png' como ícono de la aplicación.") - - self.settings = self._load_app_settings() - self.root.geometry(self.settings.get("window_geometry", "900x600")) - self.root.configure(bg="#2b2b2b") - - # Integración con SymPy - self.sympy_integration = SymPyIntegration() - - # Funciones y constantes matemáticas - math_functions = { - "pi": math.pi, "e": math.e, - "sin": math.sin, "cos": math.cos, "tan": math.tan, - "asin": math.asin, "acos": math.acos, "atan": math.atan, - "degrees": math.degrees, "radians": math.radians, - "exp": math.exp, "log": math.log, "log10": math.log10, "log2": math.log2, - "sqrt": math.sqrt, - } - - self.calc_context_base = { - "Hex": Hex, "Bin": Bin, "IP4": IP4, "Date": Date, "Dec": Dec, "Chr": Chr, - "solve": solve, - "hex": Hex, "bin": Bin, "ip4": IP4, "date": Date, "dec": Dec, "chr": Chr, # Alias en minúscula - **math_functions, - "__builtins__": { - "print": self._custom_print, - "abs": abs, "round": round, "len": len, "str": str, "int": int, "float": float, - "list": list, "dict": dict, "tuple": tuple, "pow": pow, "sum": sum, - "SyntaxError": SyntaxError, - "min": min, "max": max, "range": range, "enumerate": enumerate, "zip": zip, - "Hex": Hex, "Bin": Bin, "IP4": IP4, "Date": Date, "Dec": Dec, "Chr": Chr, - "solve": solve, - "hex": Hex, "bin": Bin, "ip4": IP4, "date": Date, "dec": Dec, "chr": Chr, # Alias en minúscula - **math_functions, - }, - } - - self.output_buffer = [] - self._debounce_job = None - self._syncing_yview = False - self._cached_input_font = None # Para cachear el objeto de fuente del panel de entrada - self.create_widgets() - self.load_persistent_input() - - def _load_app_settings(self): - if os.path.exists(self.SETTINGS_FILE): - try: - with open(self.SETTINGS_FILE, "r", encoding="utf-8") as f: - return json.load(f) - except (IOError, json.JSONDecodeError): - return {} - return {} - - def _save_app_settings(self): - self.settings["window_geometry"] = self.root.winfo_geometry() - if hasattr(self, "paned_window"): - try: - sash_x_pos = self.paned_window.sash_coord(0)[0] - self.settings["sash_pos_x"] = sash_x_pos - except tk.TclError: - pass - try: - with open(self.SETTINGS_FILE, "w", encoding="utf-8") as f: - json.dump(self.settings, f, indent=4) - except IOError: - messagebox.showwarning("Error", "No se pudieron guardar los ajustes de la interfaz.") - - def create_widgets(self): - main_frame = tk.Frame(self.root, bg="#2b2b2b", bd=0) - main_frame.pack(fill=tk.BOTH, expand=True, padx=0, pady=0) - - self.paned_window = tk.PanedWindow( - main_frame, orient=tk.HORIZONTAL, bg="#2b2b2b", - sashrelief=tk.FLAT, sashwidth=4, bd=0, - showhandle=False, opaqueresize=True, - ) - self.paned_window.pack(fill=tk.BOTH, expand=True, padx=0, pady=0) - - initial_input_width = self.settings.get("sash_pos_x", 400) - - self.input_text = scrolledtext.ScrolledText( - self.paned_window, font=("Consolas", 11), bg="#1e1e1e", fg="#d4d4d4", - insertbackground="#ffffff", selectbackground="#264f78", undo=True, - wrap=tk.NONE, borderwidth=0, highlightthickness=0, relief=tk.FLAT, - ) - self.paned_window.add(self.input_text, width=initial_input_width, stretch="always", minsize=150) - - self.output_text = scrolledtext.ScrolledText( - self.paned_window, font=("Consolas", 11), bg="#0f0f0f", fg="#00ff00", - state="disabled", wrap=tk.NONE, borderwidth=0, - highlightthickness=0, relief=tk.FLAT, - ) - self.paned_window.add(self.output_text, stretch="always", minsize=150) - - self.input_text.bind("", self.on_key_release) - self.input_text.bind("", lambda e: self._show_context_menu(e, self.input_text, "input")) - self.output_text.bind("", lambda e: self._show_context_menu(e, self.output_text, "output")) - - self.setup_scroll_sync() - self.setup_output_tags() - - def _yscroll_input_command(self, *args): - self.input_text.vbar.set(*args) - if not self._syncing_yview: - self._syncing_yview = True - self.output_text.yview_moveto(args[0]) - self._syncing_yview = False - - def _yscroll_output_command(self, *args): - self.output_text.vbar.set(*args) - if not self._syncing_yview: - self._syncing_yview = True - self.input_text.yview_moveto(args[0]) - self._syncing_yview = False - - def _unified_mouse_wheel(self, event): - if self._syncing_yview: - return "break" - if hasattr(event, "widget") and event.widget: - event.widget.yview_scroll(int(-1 * (event.delta / 120)), "units") - return "break" - - def setup_scroll_sync(self): - self.input_text.config(yscrollcommand=self._yscroll_input_command) - self.output_text.config(yscrollcommand=self._yscroll_output_command) - self.input_text.bind("", self._unified_mouse_wheel) - self.output_text.bind("", self._unified_mouse_wheel) - - def _show_context_menu(self, event, widget_ref, widget_type): - context_menu = Menu( - self.root, tearoff=0, bg="#3c3c3c", fg="white", - activebackground="#007acc", activeforeground="white", - relief=tk.FLAT, bd=1, - ) - - if widget_type == "input": - context_menu.add_command(label="Cortar", command=lambda: widget_ref.event_generate("<>")) - context_menu.add_command(label="Copiar", command=lambda: widget_ref.event_generate("<>")) - context_menu.add_command(label="Pegar", command=lambda: widget_ref.event_generate("<>")) - context_menu.add_separator(background="#555555") - context_menu.add_command(label="Borrar Todo", command=self._clear_input_text) - context_menu.add_separator(background="#555555") - context_menu.add_command(label="Guardar como...", command=self._save_input_as) - context_menu.add_command(label="Cargar archivo...", command=self._load_input_from_file) - elif widget_type == "output": - context_menu.add_command(label="Copiar Todo", command=self._copy_all_output) - context_menu.add_command(label="Borrar Salida", command=self._clear_output_text) - - context_menu.add_separator(background="#555555") - context_menu.add_command(label="Ayuda", command=self._show_help_window) - - try: - context_menu.tk_popup(event.x_root, event.y_root) - finally: - context_menu.grab_release() - - def _clear_output_text(self): - self.output_text.config(state=tk.NORMAL) - self.output_text.delete("1.0", tk.END) - self.output_text.config(state=tk.DISABLED) - - def _copy_all_output(self): - self.output_text.config(state=tk.NORMAL) - content = self.output_text.get("1.0", tk.END).strip() - self.output_text.config(state=tk.DISABLED) - if content: - self.root.clipboard_clear() - self.root.clipboard_append(content) - - def _show_help_window(self): - help_win = tk.Toplevel(self.root) - help_win.title("Ayuda - Calculadora") - help_win.geometry("750x600") - # Usar un fondo oscuro consistente para la ventana de ayuda - help_win.configure(bg="#1e1e1e") # Coincide con el fondo del HTMLScrolledText y CSS - help_win.transient(self.root) - - readme_content = self._get_help_content() - - if MARKDOWN_AVAILABLE and HTML_VIEWER_TYPE: - try: - # CSS para un tema oscuro, consistente con la UI de la calculadora - # Simplificado para no usar selectores 'body' o 'html' - # y asumiendo que los estilos base se aplican al widget. - dark_theme_css = """ - -""" - html_fragment = markdown.markdown( - readme_content, extensions=["fenced_code", "codehilite", "tables", "nl2br", "admonition"], - extension_configs={"codehilite": {"noclasses": True, "pygments_style": "monokai"}} - ) - - # Construir un documento HTML completo - content_for_viewer = f""" - - - - - Ayuda de Calculadora - {dark_theme_css} - - - {html_fragment} - - -""" - if HTML_VIEWER_TYPE == "tkinterweb": - # tkinterweb.HtmlFrame no toma bg/fg directamente en constructor para el contenido - html_viewer = tkinterweb.HtmlFrame(help_win, messages_enabled=False) - html_viewer.load_html(content_for_viewer) - # El fondo del Frame en sí se puede configurar si es necesario, - # pero el body style debería controlarlo. - # html_viewer.configure(background="#1e1e1e") - elif HTML_VIEWER_TYPE == "tkhtmlview": - html_viewer = HTMLScrolledText(help_win) - html_viewer.configure(bg="#1e1e1e") - html_viewer.set_html(content_for_viewer) - - html_viewer.pack(padx=0, pady=0, fill=tk.BOTH, expand=True) # El padding se maneja en el CSS - except Exception as e: - print(f"Error al renderizar Markdown a HTML: {e}") # Para depuración - # Fallback to text if HTML fails - self._show_text_help(help_win, readme_content) - else: - self._show_text_help(help_win, readme_content) - - close_button = tk.Button( - help_win, text="Cerrar", command=help_win.destroy, - bg="#3c3c3c", fg="white", relief=tk.FLAT, padx=10, - ) - close_button.pack(pady=(5, 10)) - - def _get_help_content(self): - """Obtiene el contenido de ayuda desde readme.md o genera uno por defecto""" - try: - readme_path = "readme.md" - if os.path.exists(readme_path): - with open(readme_path, "r", encoding="utf-8") as f: - return f.read() - except IOError: - pass - - # Contenido por defecto si no se encuentra readme.md - return """# Calculadora MAV - Ayuda - -## Nuevas Características: -- **Ecuaciones algebraicas**: Usa comillas para definir ecuaciones: `"x + 10 = 15"` -- **Resolver ecuaciones**: Usa `solve(x)` para resolver una variable -- **Resolver múltiples variables**: `solve(x, y)` o `solve()` para todas -- **Contexto dinámico**: Las variables definidas después de solve se usan automáticamente - -## Ejemplo de uso algebraico: -``` -"x + 10 = 15" -"a*y - 5 = 20" -solve(x) # x = 5 -solve(y) # y = 25/a -a = 3 -y/25 # 1/3 -``` - -## Clases disponibles: -- `Hex()`: Números hexadecimales -- `Bin()`: Números binarios -- `IP4()`: Direcciones IPv4 -- `Date()`: Fechas -- `Dec()`: Decimales - -## Funciones matemáticas: -- Constantes: `pi`, `e` -- Trigonométricas: `sin()`, `cos()`, `tan()`, `asin()`, `acos()`, `atan()` -- Conversión Angular: `degrees()`, `radians()` -- Exponenciales/Logarítmicas: `exp()`, `log()`, `log10()`, `log2()`, `sqrt()` -- Variable `last` para el último resultado - -## Menú Contextual (clic derecho): -- En entrada: Cortar, Copiar, Pegar, Borrar Todo, Guardar como..., Cargar archivo... -- En salida: Copiar Todo, Borrar Salida -""" - - def _show_text_help(self, help_win, content): - """Muestra la ayuda en texto plano cuando markdown no está disponible""" - text_widget = scrolledtext.ScrolledText( - help_win, font=("Consolas", 10), bg="#1e1e1e", fg="#d4d4d4", - wrap=tk.WORD, borderwidth=0, highlightthickness=0 - ) - text_widget.insert("1.0", content) - text_widget.config(state="disabled") - text_widget.pack(padx=5, pady=5, fill=tk.BOTH, expand=True) - - def setup_output_tags(self): - self.output_text.tag_configure("error", foreground="#ff6b6b", font=("Consolas", 11, "bold")) - self.output_text.tag_configure("result", foreground="#abdbe3") - self.output_text.tag_configure("hex", foreground="#f9a825") - self.output_text.tag_configure("bin", foreground="#4fc3f7") - self.output_text.tag_configure("ip", foreground="#fff176") - self.output_text.tag_configure("date", foreground="#ff8a80") - self.output_text.tag_configure("comment", foreground="#7f8c8d") - self.output_text.tag_configure("print_output", foreground="#b0bec5") - self.output_text.tag_configure("chr_type_tag", foreground="#80cbc4") # Teal-ish para Chr - self.output_text.tag_configure("equation", foreground="#c792ea") - self.output_text.tag_configure("type_hint", foreground="#6a6a6a") # Gris oscuro para el hint de tipo - self.output_text.tag_configure("symbolic", foreground="#82aaff") - - def _custom_print(self, *args, **kwargs): - sep = kwargs.get("sep", " ") - output = sep.join(map(str, args)) - self.output_buffer.append(("print", output)) - - - def evaluate_all_lines(self): - input_content = self.input_text.get("1.0", tk.END) - if not input_content.strip(): - self.set_output_text_with_colors([]) - return - - lines = input_content.splitlines() - output_data = [] - - # Limpiar ecuaciones de SymPy para re-evaluación completa - self.sympy_integration.clear() - - # Crear un nuevo contexto para esta pasada de evaluación - current_pass_context = self.calc_context_base.copy() - current_pass_context["__builtins__"] = self.calc_context_base["__builtins__"].copy() - last_val_for_current_line = None - line_result_capture = LineResultCapture() - - # Store the context after each line for potential use by autocomplete - # This is a simplification; a more robust system might be needed. - self._line_contexts_for_autocomplete = {} - - for line_number, line_text_original in enumerate(lines, 1): - self.output_buffer = [] - current_pass_context["last"] = last_val_for_current_line - current_line_outputs = [] - comment_part = "" - code_to_evaluate = line_text_original - - # Separar comentarios - if "#" in code_to_evaluate: - code_to_evaluate, comment_part = code_to_evaluate.split("#", 1) - comment_part = "#" + comment_part - code_to_evaluate_stripped = code_to_evaluate.strip() - - # Reemplazar "var = ?" por "solve('var')" - if code_to_evaluate_stripped.endswith("=?"): - parts = code_to_evaluate_stripped.rsplit("=?", 1) - var_name_to_solve = parts[0].strip() - if var_name_to_solve: - # Escapar comillas dobles si estuvieran en var_name_to_solve (poco probable para nombres de var) - safe_var_name = var_name_to_solve.replace('"', '\\"') - code_to_evaluate_stripped = f'solve("{safe_var_name}")' - - # Verificar si es solo el nombre de una clase (para mostrar ayuda) - showed_proactive_hint_for_class_name = False - if code_to_evaluate_stripped: - for cls in self.CUSTOM_CLASSES_WITH_HELPERS: - if code_to_evaluate_stripped.lower() == cls.__name__.lower(): - hint = cls.Helper(code_to_evaluate_stripped) - if hint: - current_line_outputs.append(("comment", hint)) - if comment_part: - current_line_outputs.append(("comment", " " + comment_part.strip())) - showed_proactive_hint_for_class_name = True - break - - if showed_proactive_hint_for_class_name: - line_actual_object = last_val_for_current_line - output_data.append(current_line_outputs) - continue - - # Línea vacía - if not code_to_evaluate_stripped: - if comment_part: - current_line_outputs.append(("comment", comment_part.strip())) - else: - current_line_outputs.append(("", "")) - line_actual_object = last_val_for_current_line - else: - # Evaluar la línea - if "__resultado_linea__" in current_pass_context: - del current_pass_context["__resultado_linea__"] - - try: - final_code_to_exec = line_result_capture.analyze_line(code_to_evaluate_stripped) - processed_result = False # Inicializar al inicio - - # Verificar si es una ecuación antes de ejecutar - if '=' in code_to_evaluate_stripped: - # Intentar agregar como ecuación primero - if self.sympy_integration.add_equation_from_string(code_to_evaluate_stripped, line_number): - current_line_outputs.append(("equation", f"Ecuación: {code_to_evaluate_stripped}")) - line_actual_object = None - processed_result = True - - if not processed_result: - exec(final_code_to_exec, current_pass_context) - line_actual_object = current_pass_context.get("__resultado_linea__") - - if isinstance(line_actual_object, SyntaxError): - raise line_actual_object - - # Actualizar el contexto de SymPy una vez después de exec y antes del procesamiento simbólico - self.sympy_integration.update_context(current_pass_context) - - # Si el resultado de exec es una expresión SymPy, intentar evaluarla/simplificarla más - if isinstance(line_actual_object, sympy.Expr): - original_expr_from_exec = line_actual_object - substitutions = self.sympy_integration._create_substitutions() - - if substitutions: - evaluated_expr = original_expr_from_exec.subs(substitutions) - else: - evaluated_expr = original_expr_from_exec - - if evaluated_expr.is_number: - line_actual_object = self.sympy_integration._format_number(evaluated_expr) - else: - simplified_expr = sympy.simplify(evaluated_expr) - if simplified_expr.is_number: - line_actual_object = self.sympy_integration._format_number(simplified_expr) - else: - line_actual_object = simplified_expr - - # Procesar el resultado - if self.sympy_integration.is_equation_string(line_actual_object): - if self.sympy_integration.add_equation_from_string(line_actual_object, line_number): - current_line_outputs.append(("equation", f"Ecuación: {line_actual_object}")) - else: - current_line_outputs.append(("error", f"Error parseando ecuación: {line_actual_object}")) - processed_result = True - - # Manejar instancias de solve - elif isinstance(line_actual_object, solve): - solve_obj = line_actual_object - solve_obj.execute(self.sympy_integration, current_pass_context) - - # Mostrar output de print si hay - for type_tag, printed_text in self.output_buffer: - current_line_outputs.append((type_tag, printed_text)) - - if solve_obj.error: - current_line_outputs.append(("error", solve_obj.error)) - line_actual_object = last_val_for_current_line - else: - current_line_outputs.append(("symbolic", str(solve_obj))) - if solve_obj.solutions is not None: - if isinstance(solve_obj.solutions, dict): - for var_name, sol_value in solve_obj.solutions.items(): - if isinstance(sol_value, (int, float, complex, sympy.Expr, list, tuple)): - current_pass_context[var_name] = sol_value - elif solve_obj.var_names and len(solve_obj.var_names) == 1: - var_name = solve_obj.var_names[0] - if isinstance(solve_obj.solutions, (int, float, complex, sympy.Expr, list, tuple)): - current_pass_context[var_name] = solve_obj.solutions - line_actual_object = solve_obj.solutions - processed_result = True - - # Verificar si podemos evaluar simbólicamente expresiones con variables no definidas - elif not processed_result and isinstance(line_actual_object, NameError): - sympy_result = self.sympy_integration.evaluate_expression( - code_to_evaluate_stripped, current_pass_context - ) - if sympy_result is not None: - current_line_outputs.append(("symbolic", sympy_result)) - line_actual_object = sympy_result - processed_result = True - else: - current_line_outputs.append(("error", f"NameError: {line_actual_object}")) - line_actual_object = last_val_for_current_line - processed_result = True - - # Resultado normal - if not processed_result: - # Mostrar output de print - for type_tag, printed_text in self.output_buffer: - current_line_outputs.append((type_tag, printed_text)) - - # Mostrar resultado si no es None - if line_actual_object is not None: - current_line_outputs.append(("result", line_actual_object)) - - except NameError as e: - # Verificar si el error contiene un '=' y tratarlo como ecuación - if '=' in code_to_evaluate_stripped: - # Intentar agregar como ecuación - if self.sympy_integration.add_equation_from_string(code_to_evaluate_stripped, line_number): - current_line_outputs.append(("equation", f"Ecuación: {code_to_evaluate_stripped}")) - line_actual_object = None # No hay resultado directo - else: - # Intentar evaluar simbólicamente - sympy_result = self.sympy_integration.evaluate_expression( - code_to_evaluate_stripped, current_pass_context - ) - if sympy_result is not None: - current_line_outputs.append(("symbolic", sympy_result)) - line_actual_object = sympy_result - else: - # Mostrar error normal si no se pudo agregar como ecuación - current_line_outputs.append(("error", f"NameError: {e}")) - line_actual_object = last_val_for_current_line - else: - # Intentar evaluar simbólicamente - sympy_result = self.sympy_integration.evaluate_expression( - code_to_evaluate_stripped, current_pass_context - ) - if sympy_result is not None: - current_line_outputs.append(("symbolic", sympy_result)) - line_actual_object = sympy_result - else: - # Mostrar error normal - current_line_outputs.append(("error", f"NameError: {e}")) - line_actual_object = last_val_for_current_line - - except Exception as e: - # Mostrar output de print antes del error - for type_tag, printed_text in self.output_buffer: - current_line_outputs.append((type_tag, printed_text)) - - # Manejo de errores mejorado - custom_error_message_applied = False - if isinstance(e, SyntaxError): - # Verificar si es un operador al final - import re - op_match = re.match(r"(.+?)\s*([+\-*/%^&|<>=!]{1,2})\s*$", code_to_evaluate_stripped) - if op_match: - operator = op_match.group(2) - if operator == "*" and code_to_evaluate_stripped.endswith("**"): - operator = "**" - elif operator == "/" and code_to_evaluate_stripped.endswith("//"): - operator = "//" - - if operator in self.OPERATOR_HELP_MESSAGES: - help_text = self.OPERATOR_HELP_MESSAGES[operator] - error_msg = f"Error de sintaxis cerca de '{operator}'. Ayuda: {help_text}" - current_line_outputs.append(("error", error_msg)) - custom_error_message_applied = True - - if not custom_error_message_applied: - # Verificar si hay ayuda disponible - hint_message_from_helper = None - for cls in self.CUSTOM_CLASSES_WITH_HELPERS: - hint = cls.Helper(code_to_evaluate_stripped) - if hint: - hint_message_from_helper = hint - break - - if hint_message_from_helper: - current_line_outputs.append(("comment", hint_message_from_helper)) - else: - error_type_name = type(e).__name__ - error_detail = e.msg if hasattr(e, 'msg') else str(e) - if hasattr(e, 'text') and e.text and not str(error_detail).startswith("Sintaxis inválida:"): - error_detail += f" (en: '{e.text.strip()}')" - standard_error_message = f"{error_type_name}: {error_detail}" - current_line_outputs.append(("error", standard_error_message)) - - line_actual_object = last_val_for_current_line - - # Limpiar variable temporal - if "__resultado_linea__" in current_pass_context: - del current_pass_context["__resultado_linea__"] - - # Agregar comentario si existe - if comment_part and code_to_evaluate_stripped: - prefix_space = " " if current_line_outputs else "" - current_line_outputs.append(("comment", prefix_space + comment_part.strip())) - - output_data.append(current_line_outputs) - last_val_for_current_line = line_actual_object - # Store a copy of the context as it is after this line's evaluation - self._line_contexts_for_autocomplete[line_number] = current_pass_context.copy() - - self.set_output_text_with_colors(output_data) - - def _get_input_font(self): - """Obtiene o crea y cachea el objeto tk.Font para el panel de entrada.""" - if not self._cached_input_font: - # Asume la fuente configurada en create_widgets: ("Consolas", 11) - self._cached_input_font = tkFont.Font(family="Consolas", size=11) - return self._cached_input_font - - def _adjust_input_pane_width(self): - """Ajusta el ancho del panel de entrada según su contenido.""" - if not hasattr(self, 'paned_window') or not self.paned_window.winfo_exists(): - return - - # Esperar a que la ventana tenga un tamaño válido - if self.paned_window.winfo_width() <= 1: - return # Se reintentará en la siguiente llamada (ej. por KeyRelease) - - # Obtener contenido excluyendo el último newline automático del widget Text - input_content = self.input_text.get("1.0", f"{tk.END}-1c") - lines = input_content.splitlines() - input_font = self._get_input_font() - - max_pixel_width = 0 - if not input_content.strip(): # Si está vacío o solo espacios en blanco - max_pixel_width = 5 # Ancho mínimo para el cursor o como placeholder - else: - for line in lines: - measured_width = input_font.measure(line) if line.strip() else input_font.measure(" ") - if measured_width > max_pixel_width: - max_pixel_width = measured_width - - padding = 40 # Relleno para barra de desplazamiento, márgenes, etc. - width_needed_by_text = max_pixel_width + padding - - # DEBUGGING PRINTS (descomentar si el problema persiste) - # print(f"--- Adjusting Input Pane ---") - # print(f"Input content: '{input_content[:50]}...'") # Muestra los primeros 50 chars del input - # print(f"Max pixel width of text: {max_pixel_width}") - # print(f"Width needed by text (max_pixel_width + padding): {width_needed_by_text}") - # END DEBUGGING PRINTS - - min_input_pane_width = 150 # Definido en create_widgets - min_output_pane_width = 150 # Definido en create_widgets - total_width = self.paned_window.winfo_width() - - current_sash_pos = 0 - try: - sash_coords = self.paned_window.sash_coord(0) - if sash_coords: - current_sash_pos = sash_coords[0] - else: - # print("DEBUG: Could not get sash_coord.") # DEBUG - return - except tk.TclError: - # print("DEBUG: TclError getting sash_coord.") # DEBUG - return - - # print(f"Current sash position (input pane width): {current_sash_pos}") # DEBUG - - if width_needed_by_text > current_sash_pos: - # print(f"Condition MET: Text needs more space ({width_needed_by_text} > {current_sash_pos})") # DEBUG - new_input_width = width_needed_by_text # Punto de partida - - # Asegurar que el nuevo ancho no sea menor que el mínimo del panel de entrada - new_input_width = max(new_input_width, min_input_pane_width) - - # Asegurar que el panel de salida conserve su espacio mínimo - if total_width - new_input_width < min_output_pane_width: - new_input_width = total_width - min_output_pane_width - new_input_width = max(new_input_width, min_input_pane_width) # Re-verificar mínimo del input - - # Aplicar un ratio máximo para el panel de entrada - max_input_ratio = 0.75 # Podría ser una constante de clase - max_width_by_ratio = int(total_width * max_input_ratio) - - if new_input_width > max_width_by_ratio: - if max_width_by_ratio >= min_input_pane_width and \ - (total_width - max_width_by_ratio) >= min_output_pane_width: - new_input_width = max_width_by_ratio - - final_new_input_width = max(0, int(new_input_width)) # No debe ser negativo - # print(f"Calculated final new input width: {final_new_input_width}") # DEBUG - - # Mover el sash solo si el nuevo ancho es significativamente mayor que el actual (umbral ajustado) - sash_adjustment_threshold = 3 # Píxeles - if final_new_input_width > current_sash_pos and \ - (final_new_input_width - current_sash_pos) >= sash_adjustment_threshold: - # print(f"Condition MET for sash_place: New width {final_new_input_width} is significantly larger (diff >= {sash_adjustment_threshold}).") # DEBUG - try: - if self.paned_window.winfo_exists() and total_width >= (min_input_pane_width + min_output_pane_width): # type: ignore - self.paned_window.sash_place(0, final_new_input_width, 0) # Añadido el argumento y=0 - # print(f"Sash placed at: {final_new_input_width}") # DEBUG - # else: - # print("DEBUG: Paned window not ready or total width too small for sash_place.") #DEBUG - except tk.TclError as e_sash: - # print(f"DEBUG: TclError during sash_place: {e_sash}") # DEBUG - pass - # else: - # print(f"Condition NOT MET for sash_place: final_new_input_width ({final_new_input_width}) vs current_sash_pos ({current_sash_pos}) or threshold ({sash_adjustment_threshold}).") # DEBUG - # else: - # print(f"Condition NOT MET: Text does not need more space ({width_needed_by_text} <= {current_sash_pos})") # DEBUG - # print(f"--- End Adjusting Input Pane ---") - - _autocomplete_popup = None - _autocomplete_listbox = None - - def _build_context_for_autocomplete(self, current_line_num_1_based): - """ - Builds an evaluation context based on lines executed *before* the current line. - Uses a temporary SymPyIntegration instance to avoid side effects on the main one. - """ - lines = self.input_text.get("1.0", tk.END).splitlines() - - # Start with the base context - eval_context = self.calc_context_base.copy() - eval_context["__builtins__"] = self.calc_context_base["__builtins__"].copy() - - # Use a temporary SymPyIntegration for this context building - temp_sympy_integration = SymPyIntegration() - temp_sympy_integration.update_context(eval_context) # Prime with base symbols/values - - last_val_for_context_build = None - line_result_capture = LineResultCapture() - - for i in range(current_line_num_1_based - 1): # Iterate lines *before* current - if i >= len(lines): break # Should not happen if current_line_num is valid - - line_text = lines[i] - eval_context["last"] = last_val_for_context_build - - code_to_run = line_text - if "#" in code_to_run: - code_to_run, _ = code_to_run.split("#", 1) - code_to_run_stripped = code_to_run.strip() - - if not code_to_run_stripped: - # Carry over 'last' or set to None if line is empty. - # For simplicity, let's assume an empty line doesn't change 'last' from previous. - continue - - try: - final_code_to_exec = line_result_capture.analyze_line(code_to_run_stripped) - exec(final_code_to_exec, eval_context) - line_res_obj = eval_context.get("__resultado_linea__") - - if isinstance(line_res_obj, solve): - line_res_obj.execute(temp_sympy_integration, eval_context) - if line_res_obj.solutions is not None: - if isinstance(line_res_obj.solutions, dict): - for var_name, sol_val in line_res_obj.solutions.items(): - eval_context[var_name] = sol_val - elif line_res_obj.var_names and len(line_res_obj.var_names) == 1: - eval_context[line_res_obj.var_names[0]] = line_res_obj.solutions - last_val_for_context_build = line_res_obj.solutions - elif temp_sympy_integration.is_equation_string(line_res_obj): - temp_sympy_integration.add_equation_from_string(line_res_obj) - last_val_for_context_build = line_res_obj - else: - last_val_for_context_build = line_res_obj - - temp_sympy_integration.update_context(eval_context) - - if "__resultado_linea__" in eval_context: - del eval_context["__resultado_linea__"] - except Exception: - pass # Ignore errors in preceding lines for autocomplete context - - eval_context["last"] = last_val_for_context_build # Set 'last' for the current line's expression - return eval_context - - def _handle_dot_autocomplete(self): - self._close_autocomplete_popup() # Close any existing one first - - cursor_index_str = self.input_text.index(tk.INSERT) - line_num_str, char_num_str = cursor_index_str.split('.') - current_line_num = int(line_num_str) # 1-based - char_idx_of_dot = int(char_num_str) # 0-based index of char after dot (where cursor is) - - # Get the text on the current line BEFORE the dot - # The dot is at char_idx_of_dot - 1 - obj_expr_str = self.input_text.get(f"{current_line_num}.0", f"{current_line_num}.{char_idx_of_dot -1}") - - if not obj_expr_str.strip(): - return - - # Build context from lines *before* the current one - eval_context = self._build_context_for_autocomplete(current_line_num) - - obj = None - try: - obj = eval(obj_expr_str, eval_context) - except Exception: - return # Can't evaluate object expression - - if obj is not None: - methods = [] - # Prioritize known, useful methods for custom types - if isinstance(obj, IP4): - methods.extend(["Mask", "Prefix", "Nodes", "NetworkAddress", "BroadcastAddress", "toHex"]) - elif isinstance(obj, Date): - methods.extend([]) # No specific simple methods other than operators yet - # Add other callable attributes, excluding most dunder/private - - # Define methods from BaseCalcType that should be excluded from suggestions - # if not overridden by the subclass in a meaningful public way. - base_calc_type_public_methods_to_exclude = {"Helper", "original_str_for_repr"} - - for attr_name in dir(obj): - if attr_name.startswith('_') and not attr_name.startswith('__'): # Exclude single underscore - continue - if attr_name.startswith('__'): # Exclude all attributes starting with double underscore - continue - if isinstance(obj, BaseCalcType) and attr_name in base_calc_type_public_methods_to_exclude: - continue - try: - attr = getattr(obj, attr_name) - if callable(attr): - if attr_name not in methods: # Avoid duplicates - methods.append(attr_name) - except Exception: continue - methods.sort() - - if methods: - self._show_autocomplete_popup(methods, obj_expr_str, eval_context) - - def _show_autocomplete_popup(self, suggestions, obj_expr_str_for_paren, eval_context_for_paren): - cursor_bbox = self.input_text.bbox(tk.INSERT) - if not cursor_bbox: return - - x, y, _, height = cursor_bbox - popup_x = self.input_text.winfo_rootx() + x - popup_y = self.input_text.winfo_rooty() + y + height + 2 - - self._autocomplete_popup = tk.Toplevel(self.root) - self._autocomplete_popup.wm_overrideredirect(True) - self._autocomplete_popup.wm_geometry(f"+{popup_x}+{popup_y}") - self._autocomplete_popup.attributes('-topmost', True) - self.root.after(100, lambda: self._autocomplete_popup.attributes('-topmost', False) if self._autocomplete_popup else None) - - self._autocomplete_listbox = tk.Listbox( - self._autocomplete_popup, bg="#3c3f41", fg="#bbbbbb", - selectbackground="#007acc", selectforeground="white", - borderwidth=1, relief="solid", exportselection=False, activestyle="none" - ) - for item in suggestions: self._autocomplete_listbox.insert(tk.END, item) - - if suggestions: - self._autocomplete_listbox.select_set(0) - self._autocomplete_listbox.pack(expand=True, fill=tk.BOTH) - # Pass the expression and context needed for parenthesis logic - self._autocomplete_listbox.bind("", lambda e, expr=obj_expr_str_for_paren, ctx=eval_context_for_paren: self._on_autocomplete_select(e, expr, ctx)) - self._autocomplete_listbox.bind("", lambda e: self._close_autocomplete_popup()) - self._autocomplete_listbox.bind("", lambda e, expr=obj_expr_str_for_paren, ctx=eval_context_for_paren: self._on_autocomplete_select(e, expr, ctx)) - self._autocomplete_listbox.focus_set() - self._autocomplete_listbox.bind("", lambda e: self._navigate_autocomplete(e, -1)) - self._autocomplete_listbox.bind("", lambda e: self._navigate_autocomplete(e, 1)) - - self.input_text.bind("", self._on_input_focus_out_while_autocomplete, add=True) - self.input_text.bind("", lambda e: self._close_autocomplete_popup(), add=True) # Click in input text - self.root.bind("", self._on_root_click_while_autocomplete, add=True) # Click outside input text - self.input_text.bind("", self._on_input_keypress_while_autocomplete, add=True) - - max_len = max(len(s) for s in suggestions) if suggestions else 10 - width = max(15, min(max_len + 2, 50)) - height = min(len(suggestions), 10) - self._autocomplete_listbox.config(width=width, height=height) - else: - self._close_autocomplete_popup() - - def _navigate_autocomplete(self, event, direction): - if not self._autocomplete_listbox: return "break" - current_selection = self._autocomplete_listbox.curselection() - if not current_selection: - new_idx = 0 if direction == 1 else self._autocomplete_listbox.size() -1 - else: - idx = current_selection[0] - new_idx = idx + direction - - if 0 <= new_idx < self._autocomplete_listbox.size(): - if current_selection: self._autocomplete_listbox.select_clear(current_selection[0]) - self._autocomplete_listbox.select_set(new_idx) - self._autocomplete_listbox.activate(new_idx) - self._autocomplete_listbox.see(new_idx) - return "break" - - def _on_autocomplete_select(self, event, obj_expr_str, eval_context): - if not self._autocomplete_listbox: return "break" - selection = self._autocomplete_listbox.curselection() - if not selection: self._close_autocomplete_popup(); return "break" - - selected_method = self._autocomplete_listbox.get(selection[0]) - self.input_text.insert(tk.INSERT, selected_method) - - try: # Add parentheses if it's a method that usually takes them - obj = eval(obj_expr_str, eval_context) # Re-eval to get the object - method_attr = getattr(obj, selected_method, None) - # Heuristic: if it's callable and not a class type itself (like Hex referring to Hex class) - # and not a dunder that isn't usually called with () like __str__ - if callable(method_attr) and not isinstance(method_attr, type) \ - and not (selected_method.startswith("__") and selected_method.endswith("__")): - self.input_text.insert(tk.INSERT, "()") - self.input_text.mark_set(tk.INSERT, f"{tk.INSERT}-1c") # Cursor inside () - except Exception: pass # If unsure, just insert name - - self._close_autocomplete_popup() - self.input_text.focus_set() - self.on_key_release() # Trigger re-evaluation - return "break" - - def _close_autocomplete_popup(self, event=None): - if self._autocomplete_popup: - self.input_text.unbind("", self._on_input_focus_out_while_autocomplete_binding_id) - self.input_text.unbind("") # Reconsider if this is too broad - self.root.unbind("", self._on_root_click_while_autocomplete_binding_id) - self.input_text.unbind("", self._on_input_keypress_while_autocomplete_binding_id) - - self._autocomplete_popup.destroy() - self._autocomplete_popup = None - self._autocomplete_listbox = None - # Do not shift focus here if it was closed due to focus out or click elsewhere - - # Store binding IDs to unbind them correctly - _on_input_focus_out_while_autocomplete_binding_id = None - _on_root_click_while_autocomplete_binding_id = None - _on_input_keypress_while_autocomplete_binding_id = None - - def _on_input_focus_out_while_autocomplete(self, event): - # Close if focus is not going to the popup itself - if self._autocomplete_popup and self.root.focus_get() != self._autocomplete_listbox: - self._close_autocomplete_popup() - - def _on_root_click_while_autocomplete(self, event): - if self._autocomplete_popup: - # Check if the click was outside the input_text and outside the popup - if event.widget != self.input_text and event.widget != self._autocomplete_listbox and \ - (not isinstance(event.widget, tk.Listbox) or event.widget.master != self._autocomplete_popup): - self._close_autocomplete_popup() - - def _on_input_keypress_while_autocomplete(self, event): - if self._autocomplete_popup: - if self.root.focus_get() == self._autocomplete_listbox: - # Let listbox handle Up, Down, Return, Escape - if event.keysym in ["Up", "Down", "Return", "Escape"]: - return - - # For other keys (letters, numbers, backspace, space, etc.) when input has focus - if event.keysym not in ["Shift_L", "Shift_R", "Control_L", "Control_R", "Alt_L", "Alt_R", "Caps_Lock", "Tab"]: - if event.char != '.': # Typing a new dot will re-trigger _handle_dot_autocomplete - self._close_autocomplete_popup() - # Do not return "break", let the key press be processed by input_text - # Allow event to propagate - - # In create_widgets, after self.input_text.bind("", ...): - # Store binding IDs for later unbinding - # self._on_input_focus_out_while_autocomplete_binding_id = self.input_text.bind("", self._on_input_focus_out_while_autocomplete, add=True) - # self._on_root_click_while_autocomplete_binding_id = self.root.bind("", self._on_root_click_while_autocomplete, add=True) - # self._on_input_keypress_while_autocomplete_binding_id = self.input_text.bind("", self._on_input_keypress_while_autocomplete, add=True) - # The above bindings are now added dynamically when popup is shown. - - def set_output_text_with_colors(self, output_data_lines): - self.output_text.config(state="normal") - self.output_text.delete("1.0", tk.END) - - num_input_lines_str = self.input_text.index(f"{tk.END}-1c").split(".")[0] - num_input_lines = int(num_input_lines_str) if num_input_lines_str else 0 - - for i, line_parts in enumerate(output_data_lines): - is_output_line_effectively_empty = not line_parts or \ - (len(line_parts) == 1 and line_parts[0][0] == "" and line_parts[0][1] == "") - - if not is_output_line_effectively_empty: - first_part_on_line = True - for item_index, (type_tag, content_obj) in enumerate(line_parts): - if not first_part_on_line: - prev_type_tag = line_parts[item_index - 1][0] if item_index > 0 else "" - if type_tag == "comment" and prev_type_tag not in ["", "comment"]: - self.output_text.insert(tk.END, " ") - elif prev_type_tag != "": - self.output_text.insert(tk.END, " ; ") - - content_str = "" - type_hint_str = "" - - if content_obj is not None: - is_pre_stringified_content = type_tag in ["equation", "error", "comment", "print_output"] or \ - (type_tag == "symbolic" and isinstance(content_obj, str)) - - if is_pre_stringified_content: - content_str = str(content_obj) - else: - content_str = str(content_obj) - type_name = "" - if isinstance(content_obj, BaseCalcType): - type_name = content_obj.__class__.__name__ - elif isinstance(content_obj, sympy.Basic): # sympy.Expr, sympy.Integer, etc. - type_name = "sympy" # Ejemplo: y = 229/3 [sympy] - elif isinstance(content_obj, list): - if content_obj and all(isinstance(item, sympy.Basic) for item in content_obj): - type_name = "list[sympy]" - else: - type_name = "list" - elif isinstance(content_obj, tuple): - type_name = "tuple" - elif isinstance(content_obj, dict): - type_name = "dict" - elif not (isinstance(content_obj, BaseCalcType) or \ - isinstance(content_obj, sympy.Basic) or \ - isinstance(content_obj, (list, tuple, dict, str))): # Excluir str aquí - type_name = type(content_obj).__name__ - elif isinstance(content_obj, str) and type_tag == "result": # String resultado de una función - type_name = "str" - - if type_name: - type_hint_str = f" [{type_name}]" - - tag_to_apply = type_tag - - # Aplicar tags especiales según el tipo de objeto - if type_tag == "result": - if isinstance(content_obj, Hex): tag_to_apply = "hex" - elif isinstance(content_obj, Bin): tag_to_apply = "bin" - elif isinstance(content_obj, IP4): tag_to_apply = "ip" - elif isinstance(content_obj, Date): tag_to_apply = "date" - elif isinstance(content_obj, Chr): tag_to_apply = "chr_type_tag" - - if content_str or type_hint_str: # Solo insertar si hay algo que mostrar - if content_str: # Puede ser que solo haya hint si content_str es "" (para None, pero se evita) - self.output_text.insert(tk.END, content_str, tag_to_apply) - if type_hint_str: - self.output_text.insert(tk.END, type_hint_str, "type_hint") - first_part_on_line = False - - # Agregar saltos de línea - if i < len(output_data_lines) - 1: - self.output_text.insert(tk.END, "\n") - elif i < num_input_lines - 1: - self.output_text.insert(tk.END, "\n") - - self.output_text.config(state="disabled") - - def save_persistent_input(self): - try: - content_to_save = self.input_text.get("1.0", tk.END).rstrip("\n") - if content_to_save: - with open("calc_input_history.txt", "w", encoding="utf-8") as f: - f.write(content_to_save) - elif os.path.exists("calc_input_history.txt"): - os.remove("calc_input_history.txt") - except Exception as e: - messagebox.showwarning("Error", f"No se pudo guardar el historial: {e}") - - def load_persistent_input(self): - try: - if os.path.exists("calc_input_history.txt"): - with open("calc_input_history.txt", "r", encoding="utf-8") as f: - content = f.read() - if content: - self.input_text.insert("1.0", content) - self.root.after_idle(self._process_input_and_adjust_layout) - return - - # Ejemplo por defecto - example_code = '''# Calculadora MAV - Ejemplos -# Evaluación directa de Python -2 + 2 * 3 -pi * 2 -sin(pi/2) - -# Clases especiales -Hex("FF") + 1 -IP4("192.168.1.1") + 255 - -# Nueva clase Chr para ASCII -Chr("A") -Chr("Hello") -Dec(66).toChr() # Devuelve Chr('B') -Hex(0x43).toChr() # Devuelve Chr('C') -# Ecuaciones algebraicas (usar comillas) -"x + 10 = 15" -"a*y - 5 = 20" - -# Resolver ecuaciones -solve(x) -solve(y) - -# Definir variables después -a = 3 - -# Evaluar expresiones con variables -y/25 - -# Expresiones simbólicas -b + c * 2 -''' - self.input_text.insert("1.0", example_code) - self.root.after_idle(self._process_input_and_adjust_layout) - except Exception as e: - messagebox.showerror("Error", f"No se pudo cargar el historial o ejemplos: {e}") - - def on_close(self): - self.save_persistent_input() - self._save_app_settings() - self.root.destroy() - - def _process_input_and_adjust_layout(self): - """Evalúa todas las líneas y luego ajusta el ancho del panel de entrada.""" - self.evaluate_all_lines() - self._adjust_input_pane_width() - - # --- Métodos actualizados para usar _process_input_and_adjust_layout --- - - def on_key_release(self, event=None): - if self._debounce_job: - self.root.after_cancel(self._debounce_job) - - if event and event.char == '.' and self.input_text.focus_get() == self.input_text: - self._handle_dot_autocomplete() - - self._debounce_job = self.root.after(250, self._process_input_and_adjust_layout) - - def _clear_input_text(self): - self.input_text.delete("1.0", tk.END) - self.sympy_integration.clear() - self._process_input_and_adjust_layout() - - def _save_input_as(self): # Este método no cambia su lógica interna de guardado - filepath = filedialog.asksaveasfilename( - defaultextension=".txt", - filetypes=[("Text Files", "*.txt"), ("Python Files", "*.py"), ("All Files", "*.*")], - title="Guardar entrada como..." - ) - if not filepath: return - try: - with open(filepath, "w", encoding="utf-8") as f: - f.write(self.input_text.get("1.0", tk.END)) - except IOError as e: - messagebox.showerror("Error al guardar", f"No se pudo guardar el archivo:\n{e}") - - def _load_input_from_file(self): - filepath = filedialog.askopenfilename( - filetypes=[("Text Files", "*.txt"), ("Python Files", "*.py"), ("All Files", "*.*")], - title="Cargar entrada desde archivo..." - ) - if not filepath: return - try: - with open(filepath, "r", encoding="utf-8") as f: content = f.read() - self.input_text.delete("1.0", tk.END) - self.input_text.insert("1.0", content) - self._process_input_and_adjust_layout() # Evaluar y ajustar layout - except IOError as e: messagebox.showerror("Error al cargar", f"No se pudo cargar el archivo:\n{e}") - except Exception as e: messagebox.showerror("Error al procesar", f"Error al procesar el archivo:\n{e}") - -def main(): - root = tk.Tk() - app = CalculatorApp(root) - root.protocol("WM_DELETE_WINDOW", app.on_close) - try: - root.iconname("Calculadora MAV") - except tk.TclError: - pass - root.mainloop() - - -if __name__ == "__main__": - main() diff --git a/.doc/comprehensive_documentation.md b/.doc/comprehensive_documentation.md deleted file mode 100644 index 4ffc85b..0000000 --- a/.doc/comprehensive_documentation.md +++ /dev/null @@ -1,469 +0,0 @@ -# Calculadora MAV - CAS Híbrido - -## Descripción General - -La Calculadora MAV es un **Sistema de Álgebra Computacional (CAS) Híbrido** que combina la potencia de SymPy con clases especializadas para networking, programación y análisis numérico. - -### Características Principales - -- **Motor SymPy completo**: Todas las funciones de cálculo simbólico -- **Sintaxis simplificada**: `Class[args]` en lugar de `Class("args")` -- **Detección automática de ecuaciones**: Sin necesidad de comillas especiales -- **Resultados interactivos**: Plots, matrices y listas clickeables -- **Clases especializadas**: IP4, Hex, Bin, Date, Dec, Chr -- **Variables SymPy puras**: Todas las variables son símbolos automáticamente - -## Instalación - -### Método 1: Instalación Automática -```bash -python launcher.py --setup -``` - -### Método 2: Instalación Manual -```bash -# Instalar dependencias -pip install sympy matplotlib numpy - -# En Linux: instalar tkinter -sudo apt-get install python3-tk - -# Ejecutar tests (opcional) -python test_suite.py - -# Iniciar aplicación -python launcher.py -``` - -### Dependencias Requeridas -- **Python 3.8+** -- **SymPy ≥ 1.12** (motor algebraico) -- **Matplotlib ≥ 3.7.0** (plotting) -- **NumPy ≥ 1.24.0** (cálculos numéricos) -- **Tkinter** (interfaz gráfica, incluido con Python) - -### Dependencias Opcionales -- **Markdown ≥ 3.4.0** (ayuda mejorada) -- **pytest ≥ 7.0.0** (testing) - -## Guía de Uso - -### Sintaxis Básica - -#### Clases Especializadas (Solo Corchetes) -```python -# Direcciones IP con funcionalidad de red -IP4[192.168.1.100/24] -IP4[10.0.0.1, 8] -IP4[172.16.0.5, 255.255.0.0] - -# Números en diferentes bases -Hex[FF] # Hexadecimal: 0xFF -Bin[1010] # Binario: 0b1010 -Dec[10.5] # Decimal: 10.5 - -# Caracteres ASCII -Chr[A] # Carácter único: 'A' (ASCII 65) -Chr[Hello] # String: 'Hello' -``` - -#### Métodos de Clases Especializadas -```python -# Métodos de IP4 -ip = IP4[192.168.1.100/24] -ip.NetworkAddress[] # 192.168.1.0/24 -ip.BroadcastAddress[] # 192.168.1.255/24 -ip.Nodes() # 254 (hosts disponibles) - -# Conversiones -Hex[255].toDecimal() # 255 -Dec[66].toChr() # Chr('B') -``` - -### Ecuaciones y Álgebra - -#### Detección Automática de Ecuaciones -```python -# Ecuaciones simples (detectadas automáticamente) -x + 2 = 5 -3*a + b = 10 -y**2 = 16 - -# Desigualdades -x > 5 -a <= 10 -b != 0 - -# Ecuaciones complejas -sin(x) = 1/2 -log(y) + 3 = 5 -``` - -#### Resolución de Ecuaciones -```python -# Resolver ecuación específica -solve(x**2 + 2*x - 8, x) # [-4, 2] - -# Atajo para resolver variable -x=? # Equivale a solve(x) - -# Resolver sistema de ecuaciones -x + y = 10 -x - y = 2 -solve([x + y - 10, x - y - 2], [x, y]) # {x: 6, y: 4} -``` - -### Variables y Símbolos - -#### Variables SymPy Automáticas -```python -# Todas las variables son símbolos SymPy automáticamente -x + 2*y # Expresión simbólica -z = 5 # z es Symbol('z') con valor 5 -w = z**2 + 3 # w es expresión: Symbol('z')**2 + 3 - -# Evaluación automática cuando es posible -a = 10 -b = a + 5 # b = 15 (evaluado) -c = a + x # c = 10 + x (simbólico) -``` - -### Funciones Matemáticas - -#### Cálculo Diferencial e Integral -```python -# Derivadas -diff(x**3, x) # 3*x**2 -diff(sin(x)*cos(x), x) # -sin(x)**2 + cos(x)**2 - -# Integrales -integrate(x**2, x) # x**3/3 -integrate(sin(x), (x, 0, pi)) # 2 - -# Límites -limit(sin(x)/x, x, 0) # 1 - -# Series de Taylor -series(exp(x), x, 0, 5) # 1 + x + x**2/2 + x**3/6 + x**4/24 + O(x**5) -``` - -#### Funciones Trigonométricas -```python -# Funciones básicas -sin(pi/2) # 1 -cos(0) # 1 -tan(pi/4) # 1 - -# Funciones inversas -asin(1) # pi/2 -acos(0) # pi/2 -atan(1) # pi/4 - -# Funciones hiperbólicas -sinh(0) # 0 -cosh(0) # 1 -tanh(0) # 0 -``` - -#### Álgebra y Simplificación -```python -# Simplificación -simplify((x**2 - 1)/(x - 1)) # x + 1 -expand((x + 1)**3) # x**3 + 3*x**2 + 3*x + 1 -factor(x**2 - 1) # (x - 1)*(x + 1) - -# Manipulación de expresiones -collect(x**2 + 2*x + x**2, x) # 2*x**2 + 2*x -cancel((x**2 - 1)/(x - 1)) # x + 1 -``` - -### Álgebra Lineal - -#### Matrices -```python -# Crear matrices -M = Matrix([[1, 2], [3, 4]]) -N = Matrix([[5, 6], [7, 8]]) - -# Operaciones básicas -M + N # Suma de matrices -M * N # Multiplicación -M**2 # Potencia - -# Propiedades (clickeables en interfaz) -det(M) # Determinante: -2 -inv(M) # Matriz inversa -M.transpose() # Transpuesta -``` - -### Plotting Interactivo - -#### Plots 2D -```python -# Plot básico -plot(sin(x), (x, -2*pi, 2*pi)) - -# Múltiples funciones -plot(sin(x), cos(x), (x, 0, 2*pi)) - -# Plot con clases especializadas -plot(Hex[x]/256, (x, 0, 255)) -``` - -#### Plots 3D -```python -# Superficie 3D -plot3d(x**2 + y**2, (x, -5, 5), (y, -5, 5)) - -# Con funciones trigonométricas -plot3d(sin(x)*cos(y), (x, 0, 2*pi), (y, 0, 2*pi)) -``` - -### Resultados Interactivos - -#### Elementos Clickeables -- **📊 Ver Plot**: Abre ventana matplotlib para plots -- **📋 Ver Matriz**: Muestra matriz formateada con operaciones -- **📋 Ver Lista**: Expande listas largas -- **🔍 Ver Detalles**: Información completa de objetos - -#### Ejemplo de Uso -```python -# Estos resultados serán clickeables en la interfaz -Matrix([[1, 2, 3], [4, 5, 6]]) # 📋 Ver Matriz 2×3 -plot(x**2, (x, -10, 10)) # 📊 Ver Plot -solve(x**3 - 6*x**2 + 11*x - 6, x) # 📋 Ver Soluciones -``` - -## Casos de Uso Avanzados - -### Análisis de Redes -```python -# Definir red -network = IP4[192.168.0.0/24] -host = IP4[192.168.0.100/24] - -# Análisis -network.Nodes() # 254 hosts disponibles -host.NetworkAddress[] # 192.168.0.0/24 -host.BroadcastAddress[] # 192.168.0.255/24 - -# Cálculos con variables -base_ip = IP4[10.0.x.0/24] -solve(base_ip.Nodes() == 254, x) # Encuentra x para 254 hosts -``` - -### Programación y Conversiones -```python -# Conversiones entre bases -hex_val = Hex[FF] # 255 en decimal -bin_val = Bin[hex_val] # Convertir a binario -chr_val = Chr[hex_val] # Carácter ASCII - -# Análisis de caracteres -text = Chr[Hello World] -ascii_values = text.value # Lista de valores ASCII - -# Operaciones bit a bit (con SymPy) -a = Hex[F0] -b = Hex[0F] -a | b # OR bit a bit -a & b # AND bit a bit -a ^ b # XOR bit a bit -``` - -### Análisis Matemático Completo -```python -# Definir función compleja -f = sin(x) * exp(-x**2/2) - -# Análisis completo -df_dx = diff(f, x) # Derivada -critical_points = solve(df_dx, x) # Puntos críticos -integral = integrate(f, (x, -oo, oo)) # Integral impropia - -# Visualización -plot(f, df_dx, (x, -3, 3)) # Plot función y derivada - -# Serie de Taylor en punto específico -taylor_series = series(f, x, 0, 6) # Serie alrededor de x=0 -``` - -### Resolución de Sistemas Complejos -```python -# Sistema de ecuaciones no lineales -x**2 + y**2 = 25 -x*y = 12 - -# Resolver -solutions = solve([x**2 + y**2 - 25, x*y - 12], [x, y]) - -# Análisis paramétrico -# Ecuación con parámetro -a*x**2 + b*x + c = 0 - -# Resolver para diferentes valores -a_val = 1 -b_val = 2 -c_val = -3 -solve(a_val*x**2 + b_val*x + c_val, x) -``` - -## Interfaz de Usuario - -### Paneles -- **Panel Izquierdo**: Entrada de código -- **Panel Derecho**: Resultados con colores y elementos interactivos - -### Menús -- **Archivo**: Nuevo, Cargar, Guardar -- **Editar**: Limpiar, operaciones de texto -- **CAS**: Mostrar variables/ecuaciones, resolver sistema -- **Ayuda**: Guías, sintaxis, funciones SymPy - -### Atajos de Teclado -- **Ctrl+S**: Guardar archivo -- **Ctrl+O**: Abrir archivo -- **Ctrl+N**: Nueva sesión -- **F1**: Ayuda rápida - -### Menú Contextual (Clic Derecho) -- **Panel Entrada**: Cortar, Copiar, Pegar, Insertar ejemplo -- **Panel Salida**: Copiar todo, Limpiar salida - -## Configuración y Personalización - -### Archivos de Configuración -- **`hybrid_calc_settings.json`**: Configuración de ventana y UI -- **`hybrid_calc_history.txt`**: Historial de sesión anterior - -### Variables de Entorno -- **`PYTHONPATH`**: Asegurar que módulos sean encontrados -- **`MPLBACKEND`**: Backend de matplotlib (ej: `TkAgg`) - -## Resolución de Problemas - -### Errores Comunes - -#### Dependencias Faltantes -```bash -# Error: ModuleNotFoundError: No module named 'sympy' -pip install sympy matplotlib numpy - -# Linux: tkinter no disponible -sudo apt-get install python3-tk -``` - -#### Errores de Sintaxis -```python -# Incorrecto: sintaxis antigua -IP4("192.168.1.1/24") - -# Correcto: nueva sintaxis -IP4[192.168.1.1/24] -``` - -#### Problemas de Variables -```python -# Las variables son símbolos automáticamente -x = 5 # x es Symbol('x') con valor 5, no variable Python -y = x + 2 # y es Symbol('x') + 2, evaluado como 7 - -# Para variables Python tradicionales, usar eval explícito: -@eval: python_var = 5 # Sintaxis especial (si implementada) -``` - -### Performance - -#### Optimizaciones -- Las líneas anteriores se cachean (no se re-evalúan) -- Parsing de corchetes se cachea para expresiones repetidas -- `evalf()` es lazy (solo cuando se muestra resultado) - -#### Límites Conocidos -- Sistemas de ecuaciones muy grandes pueden ser lentos -- Plots 3D complejos requieren tiempo de renderizado -- Matrices muy grandes pueden consumir memoria - -## Desarrollo y Extensión - -### Estructura del Proyecto -``` -calculadora-mav-cas/ -├── launcher.py # Launcher principal -├── setup.py # Script de instalación -├── test_suite.py # Tests unitarios -├── requirements.txt # Dependencias -├── bracket_parser.py # Parser de sintaxis -├── hybrid_base_types.py # Clases especializadas -├── hybrid_evaluation_engine.py # Motor CAS -├── interactive_results.py # Resultados clickeables -└── hybrid_calc_app.py # Aplicación principal -``` - -### Agregar Nuevas Clases -```python -# En hybrid_base_types.py -class HybridNewType(HybridCalcType): - def __new__(cls, value_input): - obj = HybridCalcType.__new__(cls) - return obj - - def __init__(self, value_input): - # Procesar entrada - super().__init__(processed_value, original_str) - - def specialized_method(self): - # Funcionalidad específica - pass -``` - -### Extender Parser -```python -# En bracket_parser.py -class BracketParser: - BRACKET_CLASSES = {'IP4', 'Hex', 'Bin', 'Date', 'Dec', 'Chr', 'NewType'} -``` - -## Changelog - -### Versión 2.0 (CAS Híbrido) -- ✅ Motor SymPy completo como base -- ✅ Sintaxis con corchetes únicamente -- ✅ Detección automática de ecuaciones -- ✅ Variables SymPy puras -- ✅ Resultados interactivos clickeables -- ✅ Sistema de plotting integrado -- ✅ Arquitectura modular extensible - -### Diferencias vs Versión 1.0 -| Característica | v1.0 | v2.0 | -|---|---|---| -| Motor | Python eval/exec | SymPy completo | -| Sintaxis | `Class("args")` | `Class[args]` | -| Ecuaciones | `"x + 2 = 5"` | `x + 2 = 5` (automático) | -| Variables | Python mixto | SymPy puro | -| Resultados | Texto plano | Interactivos clickeables | -| Funciones | Básicas math | SymPy completo | - -## FAQ - -### ¿Puedo usar la sintaxis antigua? -No. La v2.0 usa exclusivamente sintaxis con corchetes para consistencia y simplicidad. - -### ¿Cómo evalúo código Python tradicional? -Todas las operaciones se manejan con SymPy. Para casos especiales, se puede implementar sintaxis `@eval:` en futuras versiones. - -### ¿Los resultados son exactos? -Sí. SymPy maneja aritmética exacta por defecto. Use `.evalf()` para aproximaciones decimales. - -### ¿Puedo crear plots personalizados? -Sí. Los resultados de `plot()` son clickeables y abren matplotlib completo con opciones de personalización. - -### ¿Cómo reportar bugs? -Ejecute `python test_suite.py` para diagnosticar problemas y reporte cualquier test que falle. - ---- - -*Calculadora MAV - CAS Híbrido v2.0* -*Desarrollado para cálculo matemático avanzado con soporte especializado* \ No newline at end of file diff --git a/.doc/corrections_readme.md b/.doc/corrections_readme.md deleted file mode 100644 index 166c104..0000000 --- a/.doc/corrections_readme.md +++ /dev/null @@ -1,160 +0,0 @@ -# Correcciones Implementadas - CAS Híbrido - -## 🔧 Problemas Identificados y Solucionados - -### 1. **Operaciones Aritméticas con Clases Híbridas** -**Problema:** `Error: unsupported operand type(s) for +: 'HybridHex' and 'One'` - -**Solución:** -- ✅ Implementados métodos mágicos de SymPy en `HybridCalcType` -- ✅ Agregados `__add__`, `__mul__`, `__sub__`, `__truediv__`, `__pow__` -- ✅ Integración completa con operaciones SymPy - -```python -# Ahora funciona: -Hex[FF] + 1 # Retorna expresión SymPy válida -Bin[1010] * 2 # Multiplicación correcta -``` - -### 2. **Parser de Corchetes - Métodos Vacíos** -**Problema:** `IP4("192.168.1.100/24").NetworkAddress[]` no parseaba correctamente - -**Solución:** -- ✅ Agregado patrón para `método[]` → `método()` -- ✅ Expresión regular mejorada para corchetes vacíos - -```python -# Ahora funciona: -IP4[192.168.1.100/24].NetworkAddress[] # → IP4("192.168.1.100/24").NetworkAddress() -``` - -### 3. **Asignaciones de Variables** -**Problema:** SymPy no puede parsear `z = 5` directamente - -**Solución:** -- ✅ Detección automática de asignaciones en parser -- ✅ Función especial `_assign_variable()` en motor -- ✅ Integración con tabla de símbolos - -```python -# Ahora funciona: -z = 5 # Detectado como asignación -w = z**2 + 3 # Evaluación correcta -``` - -### 4. **Integración SymPy Mejorada** -**Problema:** Conflictos entre evaluación SymPy y clases especializadas - -**Solución:** -- ✅ Evaluación híbrida: SymPy primero, fallback a eval -- ✅ Manejo de contexto mejorado -- ✅ Propiedades SymPy implementadas en clases base - -## 🧪 Verificar Correcciones - -### Ejecutar Tests de Debug -```bash -python debug_and_test.py -``` - -### Ejecutar Ejemplos Corregidos -```bash -python launcher.py -# Luego pegar el contenido de corrected_examples.py -``` - -### Verificar Suite Completa -```bash -python test_suite.py -``` - -## 📋 Resultados Esperados - -### Antes (Errores) -``` -Hex[FF] + 1 → Error: unsupported operand type(s) -IP4[...].NetworkAddress[] → SyntaxError: invalid syntax -z = 5 → SyntaxError: invalid syntax -``` - -### Después (Correcto) -``` -Hex[FF] + 1 → 256 (o expresión SymPy equivalente) -IP4[192.168.1.100/24].NetworkAddress[] → 192.168.1.0/24 -z = 5 → z = 5 (variable asignada) -w = z**2 + 3 → w = 28 (evaluado) -``` - -## 🔍 Cambios Técnicos Específicos - -### En `bracket_parser.py` -- ✅ Método `_is_assignment()` para detectar asignaciones -- ✅ Método `_transform_assignment()` para convertir a función especial -- ✅ Patrón regex para `método[]` → `método()` - -### En `hybrid_base_types.py` -- ✅ Métodos mágicos completos en `HybridCalcType` -- ✅ Propiedades SymPy: `is_number`, `is_real`, `is_integer` -- ✅ Método `_eval_evalf()` para evaluación numérica - -### En `hybrid_evaluation_engine.py` -- ✅ Función `_assign_variable()` para manejar asignaciones -- ✅ Método `_evaluate_assignment()` para procesar asignaciones -- ✅ Evaluación híbrida mejorada en `_eval_in_context()` - -### En `hybrid_calc_app.py` -- ✅ Manejo de resultado tipo "assignment" -- ✅ Tag de color "info" para asignaciones - -## 🚨 Problemas Conocidos y Limitaciones - -### Limitaciones Actuales -1. **Operaciones complejas**: Algunas operaciones muy complejas pueden requerir evaluación manual -2. **Performance**: Evaluación híbrida puede ser más lenta que SymPy puro -3. **Compatibilidad**: Algunas funciones SymPy avanzadas pueden requerir ajustes - -### Soluciones de Trabajo -```python -# Si una operación no funciona automáticamente: -result = sympify("Hex[FF] + 1") # Forzar evaluación SymPy - -# Para debugging: -engine.debug = True # Activar modo debug -``` - -## 📈 Próximos Pasos - -### Mejoras Sugeridas -1. **Performance**: Implementar caching más agresivo -2. **Operaciones**: Agregar más operaciones especializadas -3. **UI**: Mejorar feedback de errores en interfaz -4. **Testing**: Expandir suite de tests para casos edge - -### Testing Continuo -```bash -# Ejecutar antes de cada sesión: -python debug_and_test.py - -# Verificar funcionalidad específica: -python -c " -from hybrid_evaluation_engine import HybridEvaluationEngine -engine = HybridEvaluationEngine() -print(engine.evaluate_line('Hex[FF] + 1')) -" -``` - -## ✅ Checklist de Verificación - -- [ ] `python debug_and_test.py` pasa todos los tests -- [ ] `Hex[FF] + 1` retorna resultado válido -- [ ] `IP4[...].NetworkAddress[]` funciona correctamente -- [ ] `z = 5` se asigna correctamente -- [ ] Ejemplos de `corrected_examples.py` funcionan -- [ ] Interfaz gráfica inicia sin errores -- [ ] Resultados interactivos son clickeables - ---- - -**¡Las correcciones están implementadas y listas para usar!** 🎉 - -Para cualquier problema adicional, ejecutar `debug_and_test.py` para diagnosticar. \ No newline at end of file diff --git a/.doc/plots_interactive_readme.md b/.doc/plots_interactive_readme.md deleted file mode 100644 index 7acf119..0000000 --- a/.doc/plots_interactive_readme.md +++ /dev/null @@ -1,147 +0,0 @@ -# Plots Interactivos - Correcciones Implementadas - -## 🎯 **Estado Actual** - -✅ **Operaciones aritméticas corregidas**: `Hex[FF] + 1`, `Bin[1010] * 2` -✅ **Asignaciones funcionando**: `z = 5`, `w = z + 3` -🔧 **Plots interactivos**: En proceso de corrección - -## 🔧 **Correcciones Implementadas para Plots** - -### **1. Reorganización de PlotResult** -- ✅ Movido `PlotResult` a `interactive_results.py` para evitar importaciones circulares -- ✅ Importación correcta en motor de evaluación y aplicación principal - -### **2. Detección de Resultados Interactivos** -- ✅ Método `is_interactive` corregido en `EvaluationResult` -- ✅ Detección de `PlotResult`, `Matrix`, y listas largas - -### **3. Manejo de Tags Clickeables** -- ✅ Método `create_interactive_tag()` retorna tupla `(tag, display_text)` -- ✅ Manejo robusto de casos donde no se puede crear tag - -## 🧪 **Scripts de Verificación** - -### **Test Operaciones Básicas** (YA FUNCIONA ✅) -```bash -python debug_and_test.py -``` -**Resultados esperados:** -``` -✅ Hex[FF] + 1 → 256 -✅ Bin[1010] * 2 → 20 -✅ IP4[...].NetworkAddress[] → 192.168.1.0/24 -✅ z = 5 → z = 5 -✅ w = z + 3 → w = 8 -``` - -### **Test Plots Interactivos** (NUEVO) -```bash -python test_interactive_plots.py -``` -**Verificará:** -- Creación de objetos `PlotResult` -- Evaluación de expresiones `plot()` -- Funcionalidad del `InteractiveResultManager` -- Flujo completo: evaluación → tag clickeable - -### **Test Ejemplos Completos** (NUEVO) -```bash -python final_examples_test.py -``` -**Ejecutará todos los ejemplos originales** que antes fallaban - -## 🎯 **Lo que Debería Funcionar Ahora** - -| Expresión | Estado | Resultado Esperado | -|-----------|--------|--------------------| -| `Hex[FF] + 1` | ✅ FUNCIONA | `256` | -| `Bin[1010] * 2` | ✅ FUNCIONA | `20` | -| `IP4[...].NetworkAddress[]` | ✅ FUNCIONA | `192.168.1.0/24` | -| `z = 5` | ✅ FUNCIONA | `z = 5` | -| `w = z + 3` | ✅ FUNCIONA | `w = 8` | -| `plot(sin(x), (x, -pi, pi))` | 🔧 EN TEST | `📊 Ver Plot` (clickeable) | -| `Matrix([[1, 2], [3, 4]])` | 🔧 EN TEST | `📋 Ver Matriz 2×2` (clickeable) | - -## 🚀 **Próximos Pasos** - -### **1. Verificar Plots** -```bash -# Ejecutar test específico -python test_interactive_plots.py - -# Si hay errores, revisar logs -python log_viewer.py -``` - -### **2. Test en Aplicación Completa** -```bash -# Iniciar aplicación -python launcher.py - -# Probar en interfaz: -plot(sin(x), (x, -2*pi, 2*pi)) -Matrix([[1, 2], [3, 4]]) -``` - -### **3. Verificar Funcionalidad Clickeable** -- Los resultados de `plot()` deberían mostrar `📊 Ver Plot` -- Al hacer click debería abrir ventana matplotlib -- Las matrices deberían mostrar `📋 Ver Matriz NxM` -- Al hacer click debería mostrar matriz formateada - -## 🔍 **Debugging si Plots No Funcionan** - -### **Verificar Creación de PlotResult** -```python -from interactive_results import PlotResult -plot_obj = PlotResult("plot", (sin(x), (x, -pi, pi)), {}) -print(plot_obj) # Debería mostrar: 📊 Ver Plot -``` - -### **Verificar Evaluación** -```python -from hybrid_evaluation_engine import HybridEvaluationEngine -engine = HybridEvaluationEngine() -result = engine.evaluate_line("plot(sin(x), (x, -pi, pi))") -print(f"Resultado: {result.result}") -print(f"Tipo: {type(result.result)}") -print(f"Es interactivo: {result.is_interactive}") -``` - -### **Verificar Manager** -```python -import tkinter as tk -from interactive_results import InteractiveResultManager -root = tk.Tk() -manager = InteractiveResultManager(root) -# Test con plot_obj... -``` - -## 📊 **Logs para Debugging** - -Todos los errores se registran automáticamente en: -``` -logs/mav_calc_YYYYMMDD_HHMMSS.log -``` - -Para ver logs en tiempo real: -```bash -tail -f logs/mav_calc_*.log | grep -A 5 "plot\|Plot\|interactive" -``` - -## ✅ **Checklist de Verificación** - -- [ ] `python debug_and_test.py` pasa todos los tests ✅ (YA FUNCIONA) -- [ ] `python test_interactive_plots.py` pasa todos los tests -- [ ] `python final_examples_test.py` muestra 100% éxito -- [ ] `plot(sin(x), (x, -pi, pi))` retorna `📊 Ver Plot` -- [ ] `Matrix([[1, 2], [3, 4]])` retorna `📋 Ver Matriz 2×2` -- [ ] La aplicación inicia sin errores: `python launcher.py` -- [ ] Los elementos clickeables funcionan en la interfaz - ---- - -**Estado**: ✅ Operaciones básicas corregidas, 🔧 plots interactivos en verificación - -**Siguiente paso**: Ejecutar `python test_interactive_plots.py` y reportar resultados \ No newline at end of file diff --git a/.doc/quick_start_readme.md b/.doc/quick_start_readme.md deleted file mode 100644 index e0c4493..0000000 --- a/.doc/quick_start_readme.md +++ /dev/null @@ -1,261 +0,0 @@ -# Calculadora MAV - CAS Híbrido - -## Estructura del Proyecto - -``` -📁 Calcv2/ -├── main_calc.py # 🚀 Launcher principal -├── main_calc_app.py # 🖥️ Interfaz gráfica -├── main_evaluation.py # 🧮 Motor CAS -├── hybrid_base.py # 🏗️ Clase base -├── ip4_type.py # 🌐 Clase IP4 -├── hex_type.py # 🔢 Clase Hex -├── bin_type.py # 🔢 Clase Bin -├── dec_type.py # 🔢 Clase Dec -├── chr_type.py # 🔤 Clase Chr -├── tl_bracket_parser.py # 📝 Parser sintaxis -└── tl_popup.py # 📊 Resultados clickeables -``` - -## Inicio Rápido - -1. Instalar dependencias: -```bash -pip install sympy matplotlib numpy -``` - -2. Ejecutar la aplicación: -```bash -python main_calc.py -``` - -3. Ejemplos de uso: -```python -# Tipos especializados -Hex[FF] + 1 -IP4[192.168.1.100/24].NetworkAddress[] -Bin[1010] * 2 - -# Matemáticas simbólicas -x + 2*y -diff(x**2 + sin(x), x) -integrate(x**2, x) - -# Ecuaciones -x**2 + 2*x - 8 = 0 -solve(x**2 + 2*x - 8, x) -``` - -## Características - -- ✅ Motor algebraico completo (SymPy) -- ✅ Sintaxis simplificada con corchetes -- ✅ Detección automática de ecuaciones -- ✅ Resultados interactivos clickeables -- ✅ Tipos especializados (IP4, Hex, Bin, etc.) -- ✅ Variables SymPy puras -- ✅ Plotting integrado - -## Comandos Útiles - -- `python main_calc.py --help` - Muestra ayuda -- `python main_calc.py --test` - Ejecuta tests -- `python main_calc.py --setup` - Instala dependencias -- `python main_calc.py --debug` - Activa logging detallado - -## 🚀 Inicio Rápido - -### Instalación Automática -```bash -python launcher.py --setup -``` - -### Instalación Manual -```bash -pip install sympy matplotlib numpy -python launcher.py -``` - -### En Linux (para tkinter) -```bash -sudo apt-get install python3-tk -``` - -## ✨ Características Principales - -- **🧮 Motor SymPy completo**: Cálculo simbólico avanzado -- **🔧 Sintaxis simplificada**: `IP4[192.168.1.1/24]` en lugar de `IP4("192.168.1.1/24")` -- **📐 Ecuaciones automáticas**: `x + 2 = 5` detectado automáticamente -- **📊 Resultados interactivos**: Plots y matrices clickeables -- **🌐 Clases especializadas**: IP4, Hex, Bin, Date, Dec, Chr - -## 📝 Ejemplos Básicos - -### Clases Especializadas -```python -# Redes -IP4[192.168.1.100/24].NetworkAddress[] # 192.168.1.0/24 -IP4[10.0.0.1/8].Nodes() # 16777214 hosts - -# Números -Hex[FF] + 1 # 0x100 -Bin[1010] * 2 # 0b10100 - -# Caracteres -Chr[A] # 'A' (ASCII 65) -``` - -### Matemáticas Simbólicas -```python -# Variables automáticas -x + 2*y # Expresión simbólica - -# Cálculo -diff(x**2 + sin(x), x) # 2*x + cos(x) -integrate(x**2, x) # x**3/3 - -# Ecuaciones (detección automática) -x**2 + 2*x - 8 = 0 # Agregada al sistema -solve(x**2 + 2*x - 8, x) # [-4, 2] -x=? # Atajo para solve(x) -``` - -### Plotting Interactivo -```python -plot(sin(x), (x, -2*pi, 2*pi)) # 📊 Ver Plot (clickeable) -Matrix([[1, 2], [3, 4]]) # 📋 Ver Matriz (clickeable) -``` - -## 🎯 Casos de Uso - -### Networking -```python -# Análisis de red -network = IP4[192.168.0.0/24] -network.Nodes() # 254 -network.BroadcastAddress[] # 192.168.0.255/24 - -# Cálculo con variables -base = IP4[10.0.x.0/24] -solve(base.Nodes() == 254, x) # Encuentra x -``` - -### Programación -```python -# Conversiones entre bases -Hex[255].toDecimal() # 255 -Dec[66].toChr() # Chr('B') - -# Análisis ASCII -Chr[Hello].value # [72, 101, 108, 108, 111] -``` - -### Matemáticas Avanzadas -```python -# Sistema de ecuaciones -x + y = 10 -x - y = 2 -solve([x + y - 10, x - y - 2], [x, y]) # {x: 6, y: 4} - -# Análisis completo -f = sin(x) * exp(-x**2) -diff(f, x) # Derivada -integrate(f, (x, -oo, oo)) # Integral impropia -series(f, x, 0, 5) # Serie de Taylor -``` - -## 🖥️ Interfaz - -### Paneles -- **Izquierda**: Editor de código con sintaxis nueva -- **Derecha**: Resultados coloreados e interactivos - -### Menús -- **Archivo**: Nuevo, Cargar, Guardar -- **CAS**: Variables, Ecuaciones, Resolver sistema -- **Ayuda**: Guías y documentación completa - -### Resultados Clickeables -- **📊 Ver Plot**: Abre ventana matplotlib -- **📋 Ver Matriz**: Vista expandida con operaciones -- **📋 Ver Lista**: Contenido completo de listas largas - -## 🔧 Archivos del Proyecto - -``` -calculadora-mav-cas/ -├── launcher.py # 🚀 Inicio principal -├── setup.py # 🛠️ Instalación -├── test_suite.py # 🧪 Tests -├── bracket_parser.py # 📝 Parser sintaxis -├── hybrid_base_types.py # 🏗️ Clases especializadas -├── hybrid_evaluation_engine.py # 🧮 Motor CAS -├── interactive_results.py # 📊 Resultados clickeables -├── hybrid_calc_app.py # 🖥️ Interfaz gráfica -└── requirements.txt # 📦 Dependencias -``` - -## 🆘 Resolución de Problemas - -### Errores Comunes -```bash -# Dependencias faltantes -pip install sympy matplotlib numpy - -# Linux: tkinter faltante -sudo apt-get install python3-tk - -# Verificar instalación -python test_suite.py -``` - -### Sintaxis Correcta -```python -# ✅ Correcto (nueva sintaxis) -IP4[192.168.1.1/24] -Hex[FF] - -# ❌ Incorrecto (sintaxis antigua) -IP4("192.168.1.1/24") -Hex("FF") -``` - -## 📚 Documentación Completa - -Ver `comprehensive_documentation.md` para: -- Guía completa de sintaxis -- Casos de uso avanzados -- API de desarrollo -- Ejemplos detallados - -## 🧪 Testing - -```bash -# Tests básicos -python test_suite.py - -# Tests con verbosidad -python test_suite.py --verbose - -# Setup con tests -python launcher.py --test -``` - -## 🚀 Ejecutar - -```bash -# Método recomendado -python launcher.py - -# Sin splash screen -python launcher.py --no-splash - -# Con verificación -python launcher.py --test -``` - ---- - -**¡Disfruta del poder del CAS híbrido!** 🎉 - -*Para soporte y documentación completa, consulta los archivos de documentación incluidos.* \ No newline at end of file diff --git a/.doc/refactoring/debug_api_docs.md b/.doc/refactoring/debug_api_docs.md new file mode 100644 index 0000000..5db22c2 --- /dev/null +++ b/.doc/refactoring/debug_api_docs.md @@ -0,0 +1,392 @@ +# Simple Debug API - Calculadora MAV CAS + +## Descripción + +API CLI simple que usa el motor de evaluación existente para generar debug traces sin modificar el código base. Permite debuggear "como si usaras la aplicación" mediante archivos JSON. + +--- + +## Uso + +```bash +# Ejecutar debug +python simple_debug.py debug_input.json + +# Con archivo de salida específico +python simple_debug.py debug_input.json --output debug_results.json + +# Modo verboso +python simple_debug.py debug_input.json --verbose +``` + +--- + +## Formato de Entrada + +### Estructura Simple + +```json +{ + "queries": [ + {"index": 0, "type": "input", "content": "10.1.1.1 + 1"}, + {"index": 1, "type": "input", "content": "16#FF + 10"}, + {"index": 2, "type": "exec", "content": "engine.symbol_table"}, + {"index": 3, "type": "input", "content": "x = 5"}, + {"index": 4, "type": "exec", "content": "len(engine.symbol_table)"} + ] +} +``` + +### Tipos de Query + +#### Input Query +Evalúa expresiones como si las escribieras en la calculadora: + +```json +{"index": 0, "type": "input", "content": "10.1.1.1 + 1"} +{"index": 1, "type": "input", "content": "mask = 255.255.0.0"} +{"index": 2, "type": "input", "content": "solve(x**2 + 1, x)"} +``` + +#### Exec Query +Ejecuta código Python para inspeccionar el estado interno: + +```json +{"index": 3, "type": "exec", "content": "engine.symbol_table"} +{"index": 4, "type": "exec", "content": "engine.parser.get_tokenization_info()"} +{"index": 5, "type": "exec", "content": "list(engine.base_context.keys())[:10]"} +``` + +--- + +## Formato de Salida + +### Estructura de Respuesta + +```json +{ + "execution_info": { + "timestamp": "2025-01-01T10:30:15Z", + "total_queries": 5, + "successful": 4, + "failed": 1 + }, + "results": [ + { + "index": 0, + "input": "10.1.1.1 + 1", + "output": "10.1.1.2", + "result_type": "FourBytes", + "success": true + }, + { + "index": 1, + "input": "16#FF + 10", + "output": "16#109", + "result_type": "IntBase", + "success": true + }, + { + "index": 2, + "input": "engine.symbol_table", + "output": "{'x': Symbol('x'), 'mask': FourBytes('255.255.0.0')}", + "result_type": "dict", + "success": true + } + ] +} +``` + +### Resultado Individual + +```json +{ + "index": 0, + "input": "10.1.1.1 + 1", + "output": "10.1.1.2", + "result_type": "FourBytes", + "success": true, + "error": null, + "display_class": "[FourBytes]" +} +``` + +### Resultado con Error + +```json +{ + "index": 3, + "input": "IP4Mask(255.240.0.3)", + "output": null, + "result_type": null, + "success": false, + "error": "ValueError: Máscara inválida: 255.240.0.3" +} +``` + +### Resultado de Exec + +```json +{ + "index": 4, + "input": "len(engine.symbol_table)", + "output": "3", + "result_type": "int", + "success": true, + "exec_result": 3 +} +``` + +--- + +## Casos de Uso Comunes + +### 1. Debug de Tokenización + +```json +{ + "queries": [ + {"index": 0, "type": "input", "content": "192.168.1.1 + 16#FF"}, + {"index": 1, "type": "exec", "content": "engine.parser.process_expression('192.168.1.1 + 16#FF')"}, + {"index": 2, "type": "exec", "content": "engine.parser.get_tokenization_info()"} + ] +} +``` + +### 2. Debug de Contexto + +```json +{ + "queries": [ + {"index": 0, "type": "input", "content": "x = 5"}, + {"index": 1, "type": "input", "content": "y = x + 10"}, + {"index": 2, "type": "exec", "content": "engine.symbol_table"}, + {"index": 3, "type": "exec", "content": "len(engine.equations)"} + ] +} +``` + +### 3. Debug de Tipos + +```json +{ + "queries": [ + {"index": 0, "type": "input", "content": "ip = 10.1.1.x"}, + {"index": 1, "type": "exec", "content": "type(engine.symbol_table['ip'])"}, + {"index": 2, "type": "exec", "content": "engine.symbol_table['ip'].has_symbols"}, + {"index": 3, "type": "input", "content": "ip.substitute(x=5)"} + ] +} +``` + +### 4. Debug de Errores + +```json +{ + "queries": [ + {"index": 0, "type": "input", "content": "bad_mask = 255.240.0.3"}, + {"index": 1, "type": "input", "content": "IP4Mask(bad_mask)"}, + {"index": 2, "type": "exec", "content": "engine.last_result"} + ] +} +``` + +### 5. Testing de Regresión + +```json +{ + "queries": [ + {"index": 0, "type": "input", "content": "10.1.1.1 + 1"}, + {"index": 1, "type": "exec", "content": "str(type(engine.last_result))"}, + {"index": 2, "type": "exec", "content": "engine.last_result.original"}, + {"index": 3, "type": "input", "content": "16#FF + 10"}, + {"index": 4, "type": "exec", "content": "engine.last_result.base"} + ] +} +``` + +--- + +## Funciones Útiles para Exec + +### Estado del Motor + +```python +# Contexto y variables +"engine.symbol_table" # Variables actuales +"list(engine.base_context.keys())" # Funciones disponibles +"len(engine.equations)" # Ecuaciones en el sistema +"engine.last_result" # Último resultado + +# Configuración del motor +"engine.symbolic_mode" # ¿Modo simbólico? +"engine.debug" # ¿Debug habilitado? +``` + +### Información de Tipos + +```python +# Tipos registrados +"engine.get_available_types()" # Info completa de tipos +"list(engine.registered_types_info['registered_classes'].keys())" # Tipos disponibles +"engine.registered_types_info['class_count']" # Cantidad de tipos + +# Análisis de objetos +"type(engine.last_result)" # Tipo del último resultado +"engine.last_result.__class__.__name__" # Nombre de la clase +"hasattr(engine.last_result, 'has_symbols')" # ¿Tiene símbolos? +``` + +### Tokenización y Parsing + +```python +# Tokenización +"engine.parser.get_tokenization_info()" # Info de tokenización +"engine.parser.process_expression('test')" # Procesar expresión +"len(engine.parser.tokenizer.tokenization_rules)" # Cantidad de reglas + +# Análisis de expresiones +"engine._classify_line('x = 5')" # Clasificar línea +"engine._extract_variable_names('x + y')" # Extraer variables +``` + +### Testing de Funciones Específicas + +```python +# Testing de FourBytes +"engine.last_result._numeric_value" # Valor numérico interno +"engine.last_result.has_symbols" # ¿Tiene símbolos? +"engine.last_result.original" # String original + +# Testing de IntBase +"engine.last_result.base" # Base numérica +"engine.last_result.value_str" # String del valor +"engine.last_result._symbols" # Símbolos detectados +``` + +--- + +## Implementación Simple + +### Estructura Mínima + +``` +simple_debug.py # CLI principal (~100 líneas) +debug_templates/ # Templates de ejemplo +├── basic_test.json +├── tokenization_test.json +└── regression_test.json +``` + +### CLI Principal (Pseudocódigo) + +```python +# simple_debug.py +import json +from main_evaluation import HybridEvaluationEngine + +def run_debug(input_file, output_file=None): + # Cargar queries + with open(input_file) as f: + data = json.load(f) + + # Crear motor + engine = HybridEvaluationEngine() + results = [] + + # Ejecutar cada query + for query in data['queries']: + if query['type'] == 'input': + result = engine.evaluate_line(query['content']) + output = { + 'index': query['index'], + 'input': query['content'], + 'output': str(result.result), + 'result_type': type(result.result).__name__, + 'success': not result.is_error, + 'error': result.error if result.is_error else None + } + elif query['type'] == 'exec': + try: + exec_result = eval(query['content'], {'engine': engine}) + output = { + 'index': query['index'], + 'input': query['content'], + 'output': str(exec_result), + 'result_type': type(exec_result).__name__, + 'success': True, + 'exec_result': exec_result + } + except Exception as e: + output = { + 'index': query['index'], + 'input': query['content'], + 'success': False, + 'error': str(e) + } + + results.append(output) + + # Guardar resultados + final_output = { + 'execution_info': {...}, + 'results': results + } + + with open(output_file, 'w') as f: + json.dump(final_output, f, indent=2) +``` + +--- + +## Templates de Ejemplo + +### basic_test.json +```json +{ + "queries": [ + {"index": 0, "type": "input", "content": "10.1.1.1 + 1"}, + {"index": 1, "type": "exec", "content": "type(engine.last_result).__name__"}, + {"index": 2, "type": "input", "content": "16#FF"}, + {"index": 3, "type": "exec", "content": "engine.last_result.base"} + ] +} +``` + +### tokenization_test.json +```json +{ + "queries": [ + {"index": 0, "type": "exec", "content": "engine.parser.process_expression('192.168.1.1 + 16#FF')"}, + {"index": 1, "type": "input", "content": "192.168.1.1 + 16#FF"}, + {"index": 2, "type": "exec", "content": "engine.parser.get_tokenization_info()['rules'][:3]"} + ] +} +``` + +### regression_test.json +```json +{ + "queries": [ + {"index": 0, "type": "input", "content": "mask=255.240.0.0"}, + {"index": 1, "type": "exec", "content": "type(engine.symbol_table['mask']).__name__"}, + {"index": 2, "type": "input", "content": "10.1.1.1 + 1"}, + {"index": 3, "type": "exec", "content": "type(engine.last_result).__name__"}, + {"index": 4, "type": "input", "content": "IP4Mask(255.255.0.0)"}, + {"index": 5, "type": "exec", "content": "engine.last_result.get_prefix_int()"} + ] +} +``` + +--- + +## Ventajas de este Diseño + +✅ **Simplicidad**: ~100 líneas de código total +✅ **Sin modificaciones**: Usa el motor existente tal como está +✅ **Flexibilidad**: Cualquier función del engine es accesible via exec +✅ **Debugging real**: Exactamente como usar la aplicación +✅ **Fácil testing**: JSON simple para casos de prueba +✅ **Serialización automática**: Python maneja la conversión a string + +Esta aproximación te permite debuggear efectivamente sin crear una infraestructura compleja, usando el poder del motor existente. \ No newline at end of file diff --git a/debug_assignment_problem.py b/debug_assignment_problem.py deleted file mode 100644 index 8f0cc0d..0000000 --- a/debug_assignment_problem.py +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env python3 -""" -Diagnóstico del problema de asignaciones -""" - -if __name__ == "__main__": - print("🔍 DIAGNOSTICANDO PROBLEMA DE ASIGNACIONES") - print("=" * 60) - - try: - from main_evaluation import HybridEvaluationEngine - from tl_bracket_parser import UniversalTokenizer - - # Crear motor de evaluación - engine = HybridEvaluationEngine() - engine.debug = True # Activar debug completo - - # Crear tokenizador para ver qué pasa - tokenizer = UniversalTokenizer() - tokenizer.debug = True - - # Casos de prueba exactos del usuario - test_cases = [ - "mask=255.240.0.3", - "mask", - "ip=10.1.1.1", - "10.1.1.1", - "ip" - ] - - print("\n🧪 Procesando casos de prueba del usuario:") - - for i, case in enumerate(test_cases, 1): - print(f"\n{'='*50}") - print(f"CASO {i}: '{case}'") - print(f"{'='*50}") - - # 1. Ver tokenización - tokenized = tokenizer.preprocess_tokens(case) - print(f"1. TOKENIZACIÓN: '{case}' → '{tokenized}'") - - # 2. Ver clasificación de línea - classification = engine._classify_line(tokenized) - print(f"2. CLASIFICACIÓN: {classification}") - - # 3. Evaluar con debug - print(f"3. EVALUACIÓN:") - result = engine.evaluate_line(case) - - print(f" RESULTADO:") - print(f" - Tipo de resultado: {result.result_type}") - print(f" - Es error: {result.is_error}") - if result.is_error: - print(f" - Error: {result.error}") - else: - print(f" - Valor: {result.result}") - print(f" - Tipo de valor: {type(result.result)}") - - # 4. Ver estado de variables - print(f"4. ESTADO DE VARIABLES:") - print(f" Variables en symbol_table: {list(engine.symbol_table.keys())}") - for var_name, var_value in engine.symbol_table.items(): - print(f" {var_name} = {var_value} (tipo: {type(var_value)})") - - # 5. Ver ecuaciones - print(f"5. ECUACIONES ALMACENADAS:") - print(f" Total ecuaciones: {len(engine.equations)}") - for j, eq in enumerate(engine.equations): - print(f" {j+1}. {eq}") - - print(f"\n{'_'*50}") - - # 6. Prueba manual de asignación directa - print(f"\n🔧 PRUEBA MANUAL DE ASIGNACIÓN DIRECTA:") - - try: - # Probar evaluación manual del lado derecho - print(f"6.1 Evaluando 'FourBytes(\"255.240.0.3\")' directamente:") - manual_result = engine._eval_in_context('FourBytes("255.240.0.3")') - print(f" Resultado: {manual_result} (tipo: {type(manual_result)})") - - # Probar asignación manual - print(f"6.2 Asignación manual 'test_var = FourBytes(\"255.240.0.3\")':") - exec_result = engine._eval_in_context('test_var = FourBytes("255.240.0.3")') - print(f" Resultado exec: {exec_result}") - - # Ver si se guardó - test_var_value = engine.get_variable('test_var') - print(f" test_var guardada: {test_var_value} (tipo: {type(test_var_value)})") - - except Exception as e: - print(f" Error en prueba manual: {e}") - import traceback - traceback.print_exc() - - except Exception as e: - print(f"Error general: {e}") - import traceback - traceback.print_exc() \ No newline at end of file diff --git a/debug_user_issues.py b/debug_user_issues.py deleted file mode 100644 index f6974a9..0000000 --- a/debug_user_issues.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env python3 -""" -Diagnóstico de problemas específicos del usuario: -1. IP4Mask(mask) no funciona -2. 10.1.1.1 + 1 devuelve Integer en lugar de FourBytes -""" - -if __name__ == "__main__": - print("🔍 DIAGNOSTICANDO PROBLEMAS ESPECÍFICOS DEL USUARIO") - print("=" * 60) - - try: - from main_evaluation import HybridEvaluationEngine - - # Crear motor con debug - engine = HybridEvaluationEngine() - engine.debug = True - - print("📋 Reproduciendo secuencia del usuario:") - - # 1. Crear mask - print(f"\n1. Creando mask:") - result1 = engine.evaluate_line("mask=255.240.0.3") - print(f" mask asignada: {result1.result} (tipo: {type(result1.result)})") - - # Ver qué hay en mask - mask_value = engine.get_variable('mask') - print(f" mask en symbol_table: {mask_value} (tipo: {type(mask_value)})") - - print(f"\n2. Problema 1: IP4Mask(mask) no funciona") - print(f" Intentando: IP4Mask(mask)") - - # Probar directamente - try: - result2 = engine.evaluate_line("IP4Mask(mask)") - print(f" Resultado: {result2.result} (tipo: {type(result2.result)})") - if result2.is_error: - print(f" Error: {result2.error}") - except Exception as e: - print(f" Excepción: {e}") - - # Ver qué pasa cuando llamamos IP4Mask directamente con el valor - print(f"\n Probando IP4Mask directamente con el valor de mask:") - try: - # Obtener IP4Mask del contexto - IP4Mask = engine.base_context.get('IP4Mask') - if IP4Mask: - print(f" IP4Mask disponible: {IP4Mask}") - direct_result = IP4Mask(mask_value) - print(f" Resultado directo: {direct_result} (tipo: {type(direct_result)})") - else: - print(f" IP4Mask no encontrada en contexto") - except Exception as e: - print(f" Error en llamada directa: {e}") - import traceback - traceback.print_exc() - - print(f"\n3. Problema 2: 10.1.1.1 + 1 devuelve Integer") - print(f" Intentando: 10.1.1.1 + 1") - - result3 = engine.evaluate_line("10.1.1.1 + 1") - print(f" Resultado: {result3.result} (tipo: {type(result3.result)})") - - # Analizar paso a paso - print(f"\n Análisis paso a paso:") - - # Crear FourBytes manualmente para probar - try: - FourBytes = engine.base_context.get('FourBytes') - if FourBytes: - print(f" FourBytes disponible: {FourBytes}") - fb = FourBytes("10.1.1.1") - print(f" FourBytes creado: {fb} (tipo: {type(fb)})") - print(f" fb._numeric_value: {fb._numeric_value}") - - # Probar suma manual - sum_result = fb + 1 - print(f" fb + 1 = {sum_result} (tipo: {type(sum_result)})") - - # Ver el método __add__ - print(f" Método __add__ de FourBytes: {fb.__add__}") - - except Exception as e: - print(f" Error en análisis: {e}") - import traceback - traceback.print_exc() - - print(f"\n4. Verificando contexto:") - relevant_classes = ['FourBytes', 'IP4Mask', 'IP4'] - for cls_name in relevant_classes: - cls_obj = engine.base_context.get(cls_name) - if cls_obj: - print(f" ✅ {cls_name}: {cls_obj}") - else: - print(f" ❌ {cls_name}: No encontrada") - - except Exception as e: - print(f"Error general: {e}") - import traceback - traceback.print_exc() \ No newline at end of file diff --git a/readme.md b/readme.md index 8b6bd1a..5b675ac 100644 --- a/readme.md +++ b/readme.md @@ -2,45 +2,140 @@ ## Descripción -Sistema de Álgebra Computacional (CAS) que combina SymPy con clases especializadas para networking, programación y análisis numérico. Incluye sistema de tipos dinámico con auto-descubrimiento. +Sistema de Álgebra Computacional (CAS) que combina SymPy con clases especializadas para networking, programación y análisis numérico. Incluye **sistema de tokenización automática** y **auto-descubrimiento dinámico** de tipos. ## Características Principales - **Motor SymPy completo**: Cálculo simbólico y numérico integrado -- **Sistema de tipos dinámico**: Auto-descubrimiento desde `custom_types/` -- **Sintaxis simplificada**: `Tipo[args]` en lugar de `Tipo("args")` +- **Tokenización automática**: `16#FF`, `192.168.1.1` se convierten en objetos tipados automáticamente +- **Sistema de tipos auto-descubrible**: Auto-registro desde `custom_types/` sin modificar código +- **Clases base universales**: IntBase (números en cualquier base) y FourBytes (patrones x.x.x.x) +- **Conversión perezosa a SymPy**: Objetos nativos hasta que se necesite álgebra compleja - **Detección automática de ecuaciones**: Sin sintaxis especial - **Contexto limpio por evaluación**: Cada modificación evalúa todo desde cero, garantizando comportamiento predecible - **Resultados interactivos**: Elementos clickeables para plots, matrices y listas - **Autocompletado inteligente**: Basado en tipos registrados dinámicamente +## Tokenización Automática ⭐ **NUEVO** + +El sistema reconoce automáticamente patrones específicos y los convierte en objetos tipados: + +### Números en Bases +```python +# Entrada del usuario → Conversión automática +16#FF # → IntBase('FF', 16) = 255 +2#1010 # → IntBase('1010', 2) = 10 +8#777 # → IntBase('777', 8) = 511 +16#x0 # → IntBase('x0', 16) = simbólico +``` + +### Patrones x.x.x.x +```python +# Entrada del usuario → Conversión automática +192.168.1.1 # → FourBytes('192.168.1.1') +255.255.0.0 # → FourBytes('255.255.0.0') +10.x.1.y # → FourBytes('10.x.1.y') = simbólico +``` + +### Constructores Inteligentes +```python +# Los constructores aceptan objetos tokenizados automáticamente +IP4(192.168.1.1, 24) # FourBytes tokenizado automáticamente +IP4(10.1.1.1, 255.255.0.0) # Ambos FourBytes automáticamente +Hex(16#FF) # IntBase tokenizado automáticamente +Hex(192.168.1.1) # Conversión FourBytes → Hex +``` ## Tipos Especializados -### Redes y Direcciones IP +### Clases Base Universales ⭐ **NUEVO** + +#### IntBase - Números Universales ```python -IP4[192.168.1.100/24] -IP4[10.0.0.1, 8] -IP4[172.16.0.5, 255.255.0.0] -IP4Mask[24] -IP4Mask[255.255.255.0] +# Aritmética que mantiene la base original: +a = 16#FF # → IntBase('FF', 16) +b = 16#10 # → IntBase('10', 16) +result = a + b # → IntBase('10F', 16) [271 en decimal] + +# Operaciones mixtas: +c = 16#A0 + 32 # → IntBase('C0', 16) [192 en decimal] +d = 2#1010 * 3 # → IntBase('11110', 2) [30 en decimal] + +# Conversiones: +a.to_base(2) # → IntBase('11111111', 2) +a.to_decimal() # → 255 ``` -### Sistemas Numéricos +#### FourBytes - Patrones x.x.x.x Universales ```python -Hex[FF] # Hexadecimal -Bin[1010] # Binario -Dec[255] # Decimal -Chr[A] # Caracteres ASCII +# Aritmética de direcciones IP: +ip1 = 192.168.1.1 # → FourBytes automático +ip2 = ip1 + 5 # → FourBytes('192.168.1.6') +next_net = 10.0.0.0 + 256 # → FourBytes('10.0.1.0') + +# Conversiones automáticas: +ip = 192.168.1.1 # → FourBytes +ip_hex = ip.ToHex() # → "16#C0.16#A8.16#1.16#1" +ip_bin = ip.ToBinary() # → "2#11000000.2#10101000.2#1.2#1" + +# Aritmética simbólica: +network = 10.x.0.0 # → FourBytes simbólico +network.substitute(x=5) # → FourBytes('10.5.0.0') +``` + +### Redes y Direcciones IP +```python +# Constructores automáticos con tokenización: +IP4(192.168.1.100, 24) # FourBytes + CIDR +IP4(10.0.0.1, 255.255.248.0) # FourBytes + máscara FourBytes +IP4(172.16.0.5, 16#ffffff00) # FourBytes + máscara IntBase + +# Métodos de red: +ip = IP4(192.168.1.100, 24) +ip.NetworkAddress() # → IP4(192.168.1.0, 24) +ip.BroadcastAddress() # → IP4(192.168.1.255, 24) +ip.Nodes() # → 254 hosts disponibles +ip.next_ip() # → IP4(192.168.1.101, 24) + +# Máscaras inteligentes: +IP4Mask(24) # → CIDR +IP4Mask(255.255.255.0) # → Dotted decimal (tokenizado) +IP4Mask(16#ffffff00) # → Desde hex (tokenizado) +``` + +### Sistemas Numéricos Especializados +```python +# Conversiones automáticas desde clases base: +Hex(16#FF) # IntBase → Hex +Bin(2#1010) # IntBase → Bin +Dec(255) # → Decimal +Chr(65) # → Chr('A') + +# Conversiones desde FourBytes: +Hex(192.168.1.1) # FourBytes → Hex (32-bit) +Bin(192.168.1.1) # FourBytes → Bin por elementos ``` ### Métodos Disponibles Los tipos incluyen métodos específicos accesibles con autocompletado: ```python -ip = IP4[192.168.1.100/24] -ip.NetworkAddress[] # Dirección de red -ip.Nodes() # Hosts disponibles -Hex[FF].toDecimal() # Conversiones +# IntBase universal: +val = 16#FF +val.to_base(2) # Conversión a otra base +val.substitute(x=15) # Sustitución simbólica +val.to_sympy() # Conversión explícita a SymPy + +# FourBytes universal: +addr = 192.168.1.1 +addr.ToHex() # → "16#C0.16#A8.16#1.16#1" +addr.substitute(x=5) # Para casos simbólicos +addr.is_valid_ip_range() # Validación de rango IP + +# IP4 especializado: +ip = IP4(192.168.1.100, 24) +ip.NetworkAddress() # Dirección de red +ip.add_hosts(10) # Suma hosts +ip.in_same_network(other_ip) # Comparación de redes ``` ## Sistema de Ecuaciones @@ -50,37 +145,46 @@ Hex[FF].toDecimal() # Conversiones x + 2 = 5 # Detectado automáticamente y**2 - 4 = 0 # Agregado al sistema sin(x) = 1/2 # Ecuaciones trigonométricas + +# Con objetos tokenizados: +16#x0 + 16#10 = 256 # Ecuación con IntBase +10.x.1.1 + 255 = 10.x.2.0 # Ecuación con FourBytes ``` ### Resolución ```python -solve(x**2 + 2*x - 8, x) # Resolver ecuación específica -x=? # Atajo para solve(x) +solve(x**2 + 2*x - 8, x) # Resolver ecuación específica +x=? # Atajo para solve(x) + +# Con objetos especializados: +solve(16#x0 + 16#10 - 256, x) # → x = 240 (en decimal) +network = 10.x.0.0 +solve(network + 256 == 10.5.0.0, x) # → x = 5 ``` ## Funciones Matemáticas ### Cálculo ```python -diff(x**3, x) # Derivadas -integrate(sin(x), (x, 0, pi)) # Integrales -limit(sin(x)/x, x, 0) # Límites -series(exp(x), x, 0, 5) # Series +# Con objetos nativos (conversión automática cuando es necesario): +diff(16#x0, x) # → 16*log(16) +integrate(sin(16#x), x) # Conversión a SymPy automática +limit(192.168.x.1 / x, x, 0) # Con FourBytes simbólico ``` ### Álgebra ```python -simplify(expr) # Simplificación -expand((x+1)**3) # Expansión -factor(x**2-1) # Factorización -solve([eq1, eq2], [x, y]) # Sistemas +simplify(expr) # Simplificación +expand((16#x + 1)**3) # Expansión con IntBase +factor(x**2 - 16#100) # Factorización mixta +solve([eq1, eq2], [x, y]) # Sistemas ``` ### Matrices ```python -Matrix([[1, 2], [3, 4]]) # Crear matriz -det(M) # Determinante -inv(M) # Inversa +Matrix([[16#FF, 2#1010], [192.168.1.1, 255.255.0.0]]) # Con objetos tokenizados +det(M) # Determinante +inv(M) # Inversa ``` ## Resultados Interactivos @@ -94,8 +198,10 @@ Los siguientes resultados son clickeables en la interfaz: ## Plotting ```python -plot(sin(x), (x, -2*pi, 2*pi)) # Plot 2D -plot3d(x**2 + y**2, (x, -5, 5), (y, -5, 5)) # Plot 3D +# Con objetos nativos (conversión automática): +plot(sin(16#x), (x, 0, 255)) # IntBase en plot +plot(192.168.x.1, (x, 0, 255)) # FourBytes simbólico +plot3d(x**2 + y**2, (x, -5, 5), (y, -5, 5)) # Plot 3D tradicional ``` ## Variables y Símbolos @@ -105,6 +211,10 @@ Todas las variables son símbolos SymPy automáticamente: x = 5 # x es Symbol('x') con valor 5 y = x + 2 # y es 7 (evaluado) z = x + a # z es Symbol('x') + Symbol('a') (simbólico) + +# Con objetos tokenizados: +ip_base = 192.168.x.0 # FourBytes simbólico +hex_val = 16#x0 # IntBase simbólico ``` ### Comportamiento de Contexto Limpio @@ -125,103 +235,194 @@ x=1 # → Asigna x=1 y+x # → y + 1 (usa x=1 definido arriba) x=x+1 # → Incrementa x a 2 x # → Muestra 2 + +# Con tokenización automática: +ip = 192.168.1.1 # → FourBytes('192.168.1.1') +ip + 5 # → FourBytes('192.168.1.6') ``` -**Importante**: Cada modificación reconstruye el contexto desde la línea 1 hacia abajo. - -#### Carga de Historial -- Al abrir la aplicación, se carga el historial anterior y se evalúa una vez para mostrar resultados -- A partir de ese momento, cada modificación limpia el contexto y reevalúa todo desde cero -- El historial se guarda automáticamente al cerrar la aplicación - ## Interfaz de Usuario ### Paneles - **Izquierdo**: Entrada de código - **Derecho**: Resultados con colores y elementos interactivos -### Autocompletado +### Autocompletado Dinámico - Escribir `.` después de cualquier objeto muestra métodos disponibles -- El sistema usa tipos registrados dinámicamente -- Funciona con objetos SymPy y tipos personalizados +- Sistema auto-descubre métodos desde tipos registrados dinámicamente +- Funciona con objetos SymPy, IntBase, FourBytes y tipos personalizados +- Soporte para objetos tokenizados automáticamente ### Menús - **Archivo**: Nuevo, Cargar, Guardar - **Editar**: Limpiar entrada/salida, Limpiar historial - **Configuración**: Modos simbólicos, Recargar tipos personalizados -- **Tipos**: Información de tipos, Sintaxis de tipos +- **Tipos**: Información de tipos, Sintaxis de tipos ⭐ **NUEVO** - **Ayuda**: Guías y referencia **Nota**: Se han eliminado las opciones relacionadas con limpiar variables/ecuaciones ya que el contexto se limpia automáticamente en cada evaluación. -## Sistema de Tipos Dinámico +## Sistema de Tipos Auto-Descubrible ⭐ **ACTUALIZADO** -### Auto-descubrimiento -Los tipos se cargan automáticamente desde `custom_types/`: -- Archivos `*_type.py` son detectados automáticamente -- Cada archivo define clases y función `register_classes_in_module()` -- Sistema modular y extensible sin modificar código central +### Auto-Descubrimiento Completo +- **Tipos fundamentales**: IntBase y FourBytes en `custom_types/` +- **Tipos especializados**: Todos los tipos en el directorio unificado +- **Tokenización distribuida**: Cada clase define sus propios patrones +- **Precedencia automática**: Patrones más específicos tienen mayor prioridad +- **Parser genérico**: Sin hardcoding de tipos específicos + +### Arquitectura Modular +``` +custom_types/ +├── intbase_type.py # IntBase + tokenización + registro +├── fourbytes_type.py # FourBytes + tokenización + registro +├── hex_type.py # Hex (usa IntBase) +├── bin_type.py # Bin (usa IntBase) +├── dec_type.py # Dec +├── chr_type.py # Chr +├── ip4_type.py # IP4 + IP4Mask (usa FourBytes) +└── latex_type.py # LaTeX +``` ### Información de Tipos Use **Menú Tipos → Información de tipos** para ver: - Clases registradas disponibles -- Sintaxis de corchetes soportada +- Patrones de tokenización activos +- Precedencia de conversión - Métodos de cada tipo -### Recargar Tipos +### Recarga Dinámica **Menú Tipos → Recargar tipos** para desarrollo en tiempo real. -## Casos de Uso +## Casos de Uso ⭐ **ACTUALIZADOS** -### Análisis de Redes +### Análisis de Redes con Tokenización ```python -network = IP4[192.168.0.0/24] -host = IP4[192.168.0.100/24] -network.Nodes() # Hosts disponibles -host.NetworkAddress[] # Dirección de red +# Tokenización automática en acción: +network = 192.168.0.0 # → FourBytes automático +mask = 255.255.255.0 # → FourBytes automático +ip = IP4(network, mask) # Constructor inteligente + +# Aritmética de redes: +first_host = network + 1 # → FourBytes('192.168.0.1') +last_host = network + 254 # → FourBytes('192.168.0.254') + +# Análisis con variables: +base = 10.x.0.0 # → FourBytes simbólico +solve(base + 256 == 10.5.0.0, x) # → x = 5 ``` -### Conversiones Numéricas +### Conversiones Automáticas entre Bases ```python -Hex[FF].toDecimal() # 255 -Dec[66].toChr() # Chr('B') +# IntBase universal: +hex_val = 16#FF # → IntBase('FF', 16) +bin_val = hex_val.to_base(2) # → IntBase('11111111', 2) +oct_val = hex_val.to_base(8) # → IntBase('377', 8) + +# Aritmética preservando base: +result = 16#FF + 16#10 # → IntBase('10F', 16) +mixed = 16#A0 + 32 # → IntBase('C0', 16) + +# FourBytes a otras representaciones: +ip = 192.168.1.1 # → FourBytes automático +ip_hex = ip.ToHex() # → "16#C0.16#A8.16#1.16#1" +ip_binary = ip.ToBinary() # → "2#11000000.2#10101000.2#1.2#1" ``` -### Análisis Matemático +### Análisis Matemático con Objetos Nativos ```python -f = sin(x) * exp(-x**2/2) -df_dx = diff(f, x) # Derivada +# Funciones con objetos tokenizados: +f = sin(16#x0) * exp(-x**2/2) # IntBase en función +df_dx = diff(f, x) # Derivada (conversión automática) critical_points = solve(df_dx, x) # Puntos críticos -plot(f, df_dx, (x, -3, 3)) # Visualización + +# Visualización: +plot(f, df_dx, (x, -3, 3)) # Plot función y derivada + +# Con FourBytes simbólico: +network_func = 10.x.1.0 + sin(x) +plot(network_func, (x, 0, 255)) ``` -## Extensibilidad +### Álgebra Simbólica Avanzada +```python +# Sistemas con objetos nativos: +ip1 = 192.168.x.1 # FourBytes simbólico +ip2 = 10.y.1.1 # FourBytes simbólico -### Crear Nuevos Tipos +# Ecuaciones con conversión automática: +solve([ip1 + 255 == 192.168.5.0, ip2 - 10.0.1.1 == 0], [x, y]) +# → {x: 4, y: 0} + +# Análisis de rangos: +hex_range = 16#x0 # IntBase simbólico +constraints = [hex_range >= 16#A0, hex_range <= 16#FF] +solve(constraints, x) # → Rango válido para x +``` + +## Extensibilidad ⭐ **ACTUALIZADA** + +### Crear Nuevos Tipos con Tokenización 1. Crear archivo en `custom_types/nuevo_type.py` 2. Definir clase heredando de `ClassBase` o `SympyClassBase` -3. Implementar función `register_classes_in_module()` -4. El sistema detecta automáticamente el nuevo tipo +3. Implementar `get_tokenization_patterns()` (opcional) +4. Implementar `register_classes_in_module()` +5. El sistema detecta automáticamente el nuevo tipo -### Ejemplo de Estructura +### Ejemplo Completo con Tokenización ```python -# custom_types/ejemplo_type.py -from sympy_Base import SympyClassBase +# custom_types/mac_type.py +from class_base import ClassBase +import re -class Class_Ejemplo(SympyClassBase): - def __init__(self, value): - super().__init__(processed_value, original_str) +class MACAddress(ClassBase): + def __init__(self, mac_string): + # Procesar dirección MAC + super().__init__(processed_value, mac_string) + + @staticmethod + def get_tokenization_patterns(): + """Define patrones de tokenización automática""" + return [ + { + 'pattern': r'\b([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})\b', + 'replacement': lambda match: f'MACAddress("{match.group(1)}")', + 'priority': 6, + 'description': 'MAC con dos puntos: AA:BB:CC:DD:EE:FF' + } + ] @staticmethod def Helper(input_str): - return "Ayuda para Ejemplo" + if re.search(r'([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}', input_str): + return "Direcciones MAC: AA:BB:CC:DD:EE:FF" + return None @staticmethod def PopupFunctionList(): - return [("metodo", "Descripción del método")] + return [ + ("to_vendor", "Busca fabricante por OUI"), + ("is_unicast", "Verifica si es unicast"), + ("to_binary", "Convierte a representación binaria") + ] def register_classes_in_module(): - return [("Ejemplo", Class_Ejemplo, "SympyClassBase", {})] + return [ + ("MACAddress", MACAddress, "ClassBase", { + "add_lowercase": True, + "supports_brackets": False, # Usa tokenización + "has_tokenization": True, + "description": "Direcciones MAC con auto-detección" + }) + ] +``` + +### Uso Automático del Nuevo Tipo +```python +# Después de agregar mac_type.py: +# AA:BB:CC:DD:EE:FF se convierte automáticamente en MACAddress("AA:BB:CC:DD:EE:FF") +mac = AA:BB:CC:DD:EE:FF # Tokenización automática +vendor = mac.to_vendor() # Métodos disponibles automáticamente ``` ## Resolución de Problemas @@ -229,35 +430,47 @@ def register_classes_in_module(): ### Errores Comunes - **Dependencias faltantes**: Ejecutar `pip install sympy matplotlib numpy` - **tkinter en Linux**: `sudo apt-get install python3-tk` -- **Sintaxis**: Usar `Tipo[args]` no `Tipo("args")` +- **Conversión prematura a SymPy**: Algunos objetos se convierten antes de tiempo (en desarrollo) ### Performance +- Tokenización automática es muy eficiente - Expresiones complejas pueden ser lentas en SymPy +- Objetos nativos son más rápidos que conversiones SymPy - Plots 3D requieren tiempo de renderizado -- Matrices grandes consumen memoria -### Debugging +### Debugging del Sistema de Tipos - Los logs se guardan en `logs/` -- Usar **Menú Tipos → Información** para verificar tipos cargados +- Usar **Menú Tipos → Información** para verificar tipos cargados y patrones activos - Verificar `custom_types/` para tipos personalizados +- Debug de tokenización: activar modo debug en el parser -## Arquitectura +## Arquitectura ⭐ **ACTUALIZADA** ### Componentes Principales -- **type_registry.py**: Sistema de auto-descubrimiento -- **main_evaluation.py**: Motor CAS híbrido -- **tl_bracket_parser.py**: Parser de sintaxis con corchetes +- **type_registry.py**: Sistema de auto-descubrimiento completo +- **custom_types/**: Directorio unificado de todos los tipos +- **tl_bracket_parser.py**: Tokenizador universal distribuido +- **main_evaluation.py**: Motor CAS híbrido integrado - **tl_popup.py**: Resultados interactivos - **main_calc_app.py**: Aplicación principal -### Flujo de Ejecución -1. Auto-descubrimiento de tipos en `custom_types/` -2. Registro dinámico de clases y métodos -3. Parser convierte sintaxis con corchetes -4. Motor SymPy evalúa expresiones -5. Resultados se presentan interactivamente +### Flujo de Ejecución ⭐ **ACTUALIZADO** +1. **Auto-descubrimiento**: Carga tipos desde `custom_types/` +2. **Registro dinámico**: Clases, métodos, helpers y patrones de tokenización +3. **Tokenización distribuida**: Cada clase define sus propios patrones +4. **Conversión automática**: Patrones específicos → objetos tipados +5. **Evaluación híbrida**: Objetos nativos + SymPy cuando es necesario +6. **Resultados interactivos**: Presentación con elementos clickeables + +### Precedencia de Tokenización +```python +# Orden automático por especificidad: +IntBase: priority = 5 # Muy específico: 16#FF +MAC: priority = 6 # Específico: AA:BB:CC:DD:EE:FF +FourBytes: priority = 10 # General: x.x.x.x +``` --- -**Calculadora MAV - CAS Híbrido** -*Sistema extensible para cálculo matemático y análisis especializado* \ No newline at end of file +**Calculadora MAV - CAS Híbrido v2.1** +*Sistema extensible con tokenización automática para cálculo matemático y análisis especializado* \ No newline at end of file diff --git a/test_distributed_tokenization.py b/test_distributed_tokenization.py deleted file mode 100644 index 43aada5..0000000 --- a/test_distributed_tokenization.py +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env python3 -""" -Script de prueba para el sistema de tokenización distribuida -""" - -def test_distributed_tokenization(): - """Prueba el sistema de tokenización distribuida completo""" - - print("🧪 PROBANDO SISTEMA DE TOKENIZACIÓN DISTRIBUIDA") - print("=" * 60) - - # Importar dependencias - try: - from tl_bracket_parser import TokenizationParser, preview_tokenization, test_tokenization_patterns - from type_registry import discover_and_register_types, get_registered_base_context - from main_evaluation import HybridEvaluationEngine - - print("✅ Todas las importaciones exitosas") - except ImportError as e: - print(f"❌ Error en importaciones: {e}") - return False - - # 1. Probar auto-descubrimiento de tipos - print("\n1️⃣ PROBANDO AUTO-DESCUBRIMIENTO DE TIPOS") - print("-" * 40) - - try: - registry_info = discover_and_register_types() - print(f"✅ Tipos descubiertos: {registry_info['class_count']} clases") - - registered_context = get_registered_base_context() - - # Verificar clases fundamentales - fundamental_classes = ['IntBase', 'FourBytes'] - for cls_name in fundamental_classes: - if cls_name in registered_context: - print(f" ✅ {cls_name} registrada") - - # Verificar si tiene tokenización - cls_obj = registered_context[cls_name] - if hasattr(cls_obj, 'get_tokenization_patterns'): - patterns = cls_obj.get_tokenization_patterns() - print(f" 🔧 Tiene {len(patterns)} reglas de tokenización") - for pattern in patterns: - print(f" 📋 Prioridad {pattern['priority']}: {pattern['description']}") - else: - print(f" ⚠️ Sin reglas de tokenización") - else: - print(f" ❌ {cls_name} NO registrada") - - except Exception as e: - print(f"❌ Error en auto-descubrimiento: {e}") - return False - - # 2. Probar tokenizador universal - print("\n2️⃣ PROBANDO TOKENIZADOR UNIVERSAL") - print("-" * 40) - - try: - parser = TokenizationParser(debug=True) - - # Casos de prueba - test_cases = [ - "16#FF", # IntBase - "2#1010", # IntBase - "192.168.1.1", # FourBytes - "10.x.1.y", # FourBytes simbólico - "0xFF", # Hex con prefijo - "0b1010", # Bin con prefijo - "16#FF + 192.168.1.1", # Mixto - "normal + variable", # Sin tokenización - ] - - print("🧪 Casos de prueba:") - results = parser.test_patterns(test_cases) - - print(f"\n📊 Resultados:") - print(f" ✅ Exitosos: {len(results['successful_conversions'])}") - print(f" ⚪ Sin cambios: {len(results['failed_conversions'])}") - - if results['successful_conversions']: - print(f"\n🎉 Conversiones exitosas:") - for expr in results['successful_conversions']: - result = results['tokenized_results'][expr] - print(f" '{expr}' → '{result}'") - - # Obtener información de reglas - tokenization_info = parser.get_tokenization_info() - print(f"\n📋 {len(tokenization_info['rules'])} reglas activas:") - for rule in tokenization_info['rules']: - print(f" {rule['priority']:2d}: {rule['class']} - {rule['description']}") - - except Exception as e: - print(f"❌ Error en tokenizador: {e}") - return False - - # 3. Probar integración con motor de evaluación - print("\n3️⃣ PROBANDO INTEGRACIÓN CON MOTOR DE EVALUACIÓN") - print("-" * 40) - - try: - engine = HybridEvaluationEngine(debug=True) - - # Probar expresiones tokenizadas - test_expressions = [ - "a = 16#FF", - "b = 192.168.1.1", - "c = a + 5", - "d = b + 256", - "16#10 + 2#1010", - ] - - print("🧪 Probando expresiones:") - for expr in test_expressions: - try: - result = engine.evaluate_line(expr) - if result.is_error: - print(f" ❌ '{expr}' → Error: {result.error}") - else: - print(f" ✅ '{expr}' → {result.result}") - except Exception as e: - print(f" ❌ '{expr}' → Excepción: {e}") - - except Exception as e: - print(f"❌ Error en motor de evaluación: {e}") - return False - - # 4. Prueba de casos específicos reportados por el usuario - print("\n4️⃣ PROBANDO CASOS ESPECÍFICOS DEL USUARIO") - print("-" * 40) - - try: - engine = HybridEvaluationEngine() - - # El caso específico que reportó el usuario - test_expression = "m=255.240.0.0 16#ff + 5" - - print(f"🎯 Probando: '{test_expression}'") - - # Primero ver cómo se tokeniza - preview = preview_tokenization(test_expression, debug=False) - print(f" 🔧 Tokenización: '{preview['original']}' → '{preview['tokenized']}'") - - # Ahora evaluar - result = engine.evaluate_line(test_expression) - if result.is_error: - print(f" ❌ Error: {result.error}") - else: - print(f" ✅ Resultado: {result.result}") - - except Exception as e: - print(f"❌ Error en caso específico: {e}") - return False - - print("\n🎉 TODAS LAS PRUEBAS COMPLETADAS") - return True - - -if __name__ == "__main__": - success = test_distributed_tokenization() - if success: - print("\n✅ Sistema de tokenización distribuida funcionando correctamente") - else: - print("\n❌ Hay problemas en el sistema de tokenización distribuida") \ No newline at end of file diff --git a/test_quick_tokenization.py b/test_quick_tokenization.py deleted file mode 100644 index c9404d9..0000000 --- a/test_quick_tokenization.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python3 -""" -Script rápido para probar tokenización -""" - -if __name__ == "__main__": - print("🧪 Prueba rápida de tokenización") - - try: - # 1. Verificar registro de tipos - print("\n1. Verificando registro de tipos...") - from type_registry import discover_and_register_types, get_registered_base_context - - registry_info = discover_and_register_types() - print(f"Clases registradas: {registry_info['class_count']}") - - context = get_registered_base_context() - print(f"Clases en contexto: {list(context.keys())}") - - # 2. Verificar si las clases tienen tokenización - print("\n2. Verificando métodos de tokenización...") - for name, cls in context.items(): - if hasattr(cls, 'get_tokenization_patterns'): - patterns = cls.get_tokenization_patterns() - print(f"{name}: {len(patterns)} patrones") - for p in patterns: - print(f" - Prioridad {p['priority']}: {p['description']}") - else: - print(f"{name}: sin tokenización") - - # 3. Probar tokenizador - print("\n3. Probando tokenizador...") - from tl_bracket_parser import UniversalTokenizer - - tokenizer = UniversalTokenizer() - tokenizer.debug = True - print(f"Reglas cargadas: {len(tokenizer.tokenization_rules)}") - - test_cases = [ - "192.168.1.1", - "16#FF", - "0xFF", - "2#1010", - "10.x.1.y" - ] - - for case in test_cases: - result = tokenizer.preprocess_tokens(case) - print(f"'{case}' → '{result}'") - - except Exception as e: - print(f"Error: {e}") - import traceback - traceback.print_exc() \ No newline at end of file diff --git a/test_real_usage.py b/test_real_usage.py deleted file mode 100644 index dbc1457..0000000 --- a/test_real_usage.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python3 -""" -Prueba de uso real del sistema corregido -""" - -if __name__ == "__main__": - print("🎯 PRUEBA DE USO REAL") - print("=" * 40) - - try: - from main_evaluation import HybridEvaluationEngine - - # Crear motor sin debug para simular uso real - engine = HybridEvaluationEngine() - engine.debug = False - - # Casos exactos del usuario - test_sequence = [ - "mask=255.240.0.3", - "mask", - "ip=10.1.1.1", - "10.1.1.1", - "ip" - ] - - print("Secuencia de comandos del usuario:") - print("-" * 40) - - for i, command in enumerate(test_sequence, 1): - print(f"\n{i}. {command}") - result = engine.evaluate_line(command) - - if result.is_error: - print(f" ❌ Error: {result.error}") - else: - print(f" ✅ {result.result}") - if hasattr(result.result, '__class__'): - print(f" [{result.result.__class__.__name__}]") - - print(f"\n" + "=" * 40) - print("ESTADO FINAL:") - print(f"Variables: {list(engine.symbol_table.keys())}") - for var, value in engine.symbol_table.items(): - print(f" {var} = {value}") - - except Exception as e: - print(f"Error: {e}") - import traceback - traceback.print_exc() \ No newline at end of file diff --git a/test_user_case.py b/test_user_case.py deleted file mode 100644 index b21fd6c..0000000 --- a/test_user_case.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python3 -""" -Prueba del caso específico del usuario: m=255.240.0.0 16#ff + 5 -""" - -if __name__ == "__main__": - print("🎯 Probando caso específico del usuario") - - try: - # Debug completo del sistema - from type_registry import discover_and_register_types, get_registered_base_context - from tl_bracket_parser import UniversalTokenizer - - print("\n🔍 DEBUG COMPLETO DEL SISTEMA:") - - # 1. Verificar registro de tipos - print("\n1. Verificando registro de tipos...") - registry_info = discover_and_register_types() - print(f"Clases registradas: {registry_info['class_count']}") - - context = get_registered_base_context() - fundamental_classes = ['IntBase', 'FourBytes'] - - for cls_name in fundamental_classes: - if cls_name in context: - print(f"✅ {cls_name} está registrada") - cls_obj = context[cls_name] - if hasattr(cls_obj, 'get_tokenization_patterns'): - patterns = cls_obj.get_tokenization_patterns() - print(f" 🔧 Tiene {len(patterns)} reglas de tokenización") - for i, pattern in enumerate(patterns): - print(f" {i+1}. Prioridad {pattern['priority']}: {pattern['description']}") - print(f" Patrón: {pattern['pattern']}") - else: - print(f" ❌ Sin reglas de tokenización") - else: - print(f"❌ {cls_name} NO está registrada") - - # 2. Crear tokenizador y verificar reglas - print("\n2. Creando tokenizador...") - tokenizer = UniversalTokenizer() - print(f"Reglas cargadas en tokenizador: {len(tokenizer.tokenization_rules)}") - - if len(tokenizer.tokenization_rules) > 0: - print("Reglas activas:") - for i, rule in enumerate(tokenizer.tokenization_rules): - print(f" {i+1}. {rule['class_name']} (prioridad {rule['priority']}): {rule['description']}") - else: - print("❌ No hay reglas cargadas en el tokenizador") - - # 3. Probar tokenización manual - print("\n3. Probando tokenización manual...") - tokenizer.debug = True - - test_simple = "192.168.1.1" - print(f"\nProbando: '{test_simple}'") - result = tokenizer.preprocess_tokens(test_simple) - print(f"Resultado: '{result}'") - - test_hex = "16#ff" - print(f"\nProbando: '{test_hex}'") - result = tokenizer.preprocess_tokens(test_hex) - print(f"Resultado: '{result}'") - - # 4. Verificar que las clases estén disponibles en motor - print("\n4. Verificando motor de evaluación...") - from main_evaluation import HybridEvaluationEngine - - engine = HybridEvaluationEngine() - engine.debug = True - - print(f"Contexto del motor tiene {len(engine.base_context)} entradas") - - # Verificar que IntBase y FourBytes estén en el contexto - for cls_name in ['IntBase', 'FourBytes']: - if cls_name in engine.base_context: - print(f"✅ {cls_name} está en contexto del motor") - else: - print(f"❌ {cls_name} NO está en contexto del motor") - - # 5. Probar evaluación directa - print("\n5. Probando evaluación directa...") - - # Probar evaluación manual sin tokenización - test_manual = "IntBase('ff', 16)" - print(f"Probando evaluación manual: {test_manual}") - try: - result = engine._eval_in_context(test_manual) - print(f"✅ Resultado: {result} (tipo: {type(result)})") - except Exception as e: - print(f"❌ Error: {e}") - - # Probar FourBytes manual - test_manual2 = "FourBytes('192.168.1.1')" - print(f"Probando evaluación manual: {test_manual2}") - try: - result = engine._eval_in_context(test_manual2) - print(f"✅ Resultado: {result} (tipo: {type(result)})") - except Exception as e: - print(f"❌ Error: {e}") - - except Exception as e: - print(f"Error: {e}") - import traceback - traceback.print_exc() \ No newline at end of file