Mejorado de la documentacion
This commit is contained in:
parent
1eea99fde2
commit
3aadae3dc6
|
@ -0,0 +1,358 @@
|
|||
# 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
|
||||
- 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")`
|
||||
- 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")`
|
||||
- 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
|
||||
|
||||
### **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
|
||||
|
||||
## 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`).
|
Binary file not shown.
|
@ -1,181 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script de debug y testing para verificar correcciones del error as_coeff_Mul
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Agregar directorio actual al path
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
from bracket_parser import BracketParser
|
||||
from hybrid_evaluation_engine import HybridEvaluationEngine
|
||||
from hybrid_base_types import Hex, Bin, IP4, HybridCalcType
|
||||
import sympy
|
||||
|
||||
|
||||
def test_sympy_integration():
|
||||
"""Test específico de integración con SymPy"""
|
||||
print("=== Test Integración SymPy ===")
|
||||
|
||||
try:
|
||||
# Test creación básica
|
||||
h = Hex("FF")
|
||||
print(f"✅ Hex[FF] creado: {h}")
|
||||
print(f" Valor interno: {h._value}")
|
||||
print(f" Es SymPy Basic: {isinstance(h, sympy.Basic)}")
|
||||
print(f" Args: {h.args}")
|
||||
print(f" Func: {h.func}")
|
||||
|
||||
# Test métodos requeridos por SymPy
|
||||
print(f"✅ as_coeff_Mul(): {h.as_coeff_Mul()}")
|
||||
print(f"✅ as_coeff_Add(): {h.as_coeff_Add()}")
|
||||
print(f"✅ as_base_exp(): {h.as_base_exp()}")
|
||||
|
||||
# Test propiedades
|
||||
print(f"✅ is_number: {h.is_number}")
|
||||
print(f"✅ is_real: {h.is_real}")
|
||||
print(f"✅ is_integer: {h.is_integer}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error en test básico: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def test_arithmetic_operations():
|
||||
"""Test específico de operaciones aritméticas"""
|
||||
print("\n=== Test Operaciones Aritméticas ===")
|
||||
|
||||
try:
|
||||
h = Hex("FF")
|
||||
print(f"Hex[FF] creado: {h} (valor: {h._value})")
|
||||
|
||||
# Test suma simple
|
||||
print("\n1. Probando h + 1...")
|
||||
result1 = h + 1
|
||||
print(f" Resultado: {result1} (tipo: {type(result1)})")
|
||||
|
||||
# Test con SymPy sympify
|
||||
print("\n2. Probando sympify(h + 1)...")
|
||||
try:
|
||||
expr = sympy.sympify("h + 1", locals={'h': h})
|
||||
print(f" SymPy expr: {expr} (tipo: {type(expr)})")
|
||||
except Exception as e:
|
||||
print(f" Error en sympify: {e}")
|
||||
|
||||
# Test multiplicación
|
||||
print("\n3. Probando h * 2...")
|
||||
result3 = h * 2
|
||||
print(f" Resultado: {result3} (tipo: {type(result3)})")
|
||||
|
||||
# Test con Bin
|
||||
print("\n4. Probando Bin[1010] * 2...")
|
||||
b = Bin("1010")
|
||||
result4 = b * 2
|
||||
print(f" Bin[1010]: {b} (valor: {b._value})")
|
||||
print(f" Resultado: {result4} (tipo: {type(result4)})")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error en operaciones aritméticas: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def test_evaluation_engine():
|
||||
"""Test del motor de evaluación con las correcciones"""
|
||||
print("\n=== Test Motor de Evaluación ===")
|
||||
|
||||
engine = HybridEvaluationEngine()
|
||||
|
||||
test_expressions = [
|
||||
# Casos que fallaban antes
|
||||
"Hex[FF] + 1",
|
||||
"Bin[1010] * 2",
|
||||
"IP4[192.168.1.100/24].NetworkAddress[]",
|
||||
|
||||
# Casos básicos
|
||||
"Hex[FF]",
|
||||
"Bin[1010]",
|
||||
|
||||
# Asignaciones
|
||||
"z = 5",
|
||||
"w = z + 3",
|
||||
|
||||
# SymPy básico
|
||||
"x + 2*y",
|
||||
"diff(x**2, x)",
|
||||
]
|
||||
|
||||
for expr in test_expressions:
|
||||
try:
|
||||
print(f"\nEvaluando: '{expr}'")
|
||||
result = engine.evaluate_line(expr)
|
||||
|
||||
if result.is_error:
|
||||
print(f" ❌ Error: {result.error}")
|
||||
else:
|
||||
print(f" ✅ Resultado: {result.result} (tipo: {type(result.result)})")
|
||||
print(f" Info: {result.result_type}")
|
||||
if result.numeric_result:
|
||||
print(f" Numérico: {result.numeric_result}")
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Excepción: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def test_problematic_cases():
|
||||
"""Test de casos específicos que causaban problemas"""
|
||||
print("\n=== Test Casos Problemáticos ===")
|
||||
|
||||
# Test directo de la operación que fallaba
|
||||
print("1. Test directo Hex + int...")
|
||||
try:
|
||||
h = Hex("FF")
|
||||
result = h.__add__(1)
|
||||
print(f" h.__add__(1): {result} (tipo: {type(result)})")
|
||||
|
||||
# Test usando operador
|
||||
result2 = h + 1
|
||||
print(f" h + 1: {result2} (tipo: {type(result2)})")
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
# Test con SymPy sympify directo
|
||||
print("\n2. Test SymPy sympify directo...")
|
||||
try:
|
||||
h = Hex("FF")
|
||||
sympified = sympy.sympify(h)
|
||||
print(f" sympify(Hex[FF]): {sympified} (tipo: {type(sympified)})")
|
||||
|
||||
# Test operaciones con sympified
|
||||
result = sympified + 1
|
||||
print(f" sympified + 1: {result} (tipo: {type(result)})")
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def main():
|
||||
"""Función principal de testing"""
|
||||
print("Debug y Testing - Corrección error as_coeff_Mul")
|
||||
print("=" * 60)
|
||||
|
||||
test_sympy_integration()
|
||||
test_arithmetic_operations()
|
||||
test_evaluation_engine()
|
||||
test_problematic_cases()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Testing completado.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,30 +1,2 @@
|
|||
# Calculadora MAV - CAS Híbrido
|
||||
# Sintaxis nueva con corchetes
|
||||
|
||||
# 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 (detección automática)
|
||||
x**2 + 2*x - 8 = 0
|
||||
3*a + b = 10
|
||||
|
||||
# Resolver ecuaciones
|
||||
solve(x**2 + 2*x - 8, x)
|
||||
a=?
|
||||
|
||||
# Variables automáticas
|
||||
z = 5
|
||||
w = z**2 + 3
|
||||
|
||||
# Plotting interactivo
|
||||
plot(sin(x), (x, -2*pi, 2*pi))
|
||||
|
||||
# Matrices
|
||||
Matrix([[1, 2], [3, 4]])
|
||||
Hex[FF]
|
||||
ip=IP4[192.168.1.100/24]
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"window_geometry": "1000x700+127+123",
|
||||
"window_geometry": "1000x700+327+101",
|
||||
"sash_pos_x": 339
|
||||
}
|
245
setup_script.py
245
setup_script.py
|
@ -1,245 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script de setup e instalación para Calculadora MAV - CAS Híbrido
|
||||
"""
|
||||
import sys
|
||||
import subprocess
|
||||
import os
|
||||
from pathlib import Path
|
||||
import importlib.util
|
||||
|
||||
|
||||
def check_python_version():
|
||||
"""Verifica que la versión de Python sea compatible"""
|
||||
if sys.version_info < (3, 8):
|
||||
print("❌ Error: Se requiere Python 3.8 o superior")
|
||||
print(f" Versión actual: {sys.version}")
|
||||
return False
|
||||
|
||||
print(f"✅ Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")
|
||||
return True
|
||||
|
||||
|
||||
def check_module(module_name, package_name=None, optional=False):
|
||||
"""Verifica si un módulo está disponible"""
|
||||
try:
|
||||
importlib.import_module(module_name)
|
||||
print(f"✅ {module_name}")
|
||||
return True
|
||||
except ImportError:
|
||||
if optional:
|
||||
print(f"⚠️ {module_name} (opcional)")
|
||||
else:
|
||||
print(f"❌ {module_name} - {'usar: pip install ' + (package_name or module_name)}")
|
||||
return False
|
||||
|
||||
|
||||
def install_requirements():
|
||||
"""Instala las dependencias requeridas"""
|
||||
print("\n=== Instalando dependencias ===")
|
||||
|
||||
requirements = [
|
||||
"sympy>=1.12",
|
||||
"matplotlib>=3.7.0",
|
||||
"numpy>=1.24.0"
|
||||
]
|
||||
|
||||
for req in requirements:
|
||||
try:
|
||||
print(f"Instalando {req}...")
|
||||
subprocess.check_call([
|
||||
sys.executable, "-m", "pip", "install", req
|
||||
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
print(f"✅ {req}")
|
||||
except subprocess.CalledProcessError:
|
||||
print(f"❌ Error instalando {req}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def check_tkinter():
|
||||
"""Verifica que tkinter esté disponible"""
|
||||
try:
|
||||
import tkinter
|
||||
print("✅ tkinter")
|
||||
return True
|
||||
except ImportError:
|
||||
print("❌ tkinter - instalar python3-tk en Linux/Ubuntu")
|
||||
print(" Ubuntu/Debian: sudo apt-get install python3-tk")
|
||||
print(" CentOS/RHEL: sudo yum install tkinter")
|
||||
print(" macOS: tkinter debe estar incluido con Python")
|
||||
return False
|
||||
|
||||
|
||||
def check_optional_modules():
|
||||
"""Verifica módulos opcionales"""
|
||||
optional_modules = [
|
||||
("markdown", "markdown", True),
|
||||
("tkinterweb", "tkinterweb", True),
|
||||
("tkhtmlview", "tkhtmlview", True),
|
||||
("pytest", "pytest", True)
|
||||
]
|
||||
|
||||
for module_name, package_name, optional in optional_modules:
|
||||
check_module(module_name, package_name, optional)
|
||||
|
||||
|
||||
def create_desktop_shortcut():
|
||||
"""Crea acceso directo en el escritorio (Linux/Windows)"""
|
||||
current_dir = Path.cwd()
|
||||
main_script = current_dir / "hybrid_calc_app.py"
|
||||
|
||||
if not main_script.exists():
|
||||
print("⚠️ No se encontró hybrid_calc_app.py en el directorio actual")
|
||||
return False
|
||||
|
||||
try:
|
||||
if sys.platform.startswith('linux'):
|
||||
# Linux desktop entry
|
||||
desktop_dir = Path.home() / "Desktop"
|
||||
if desktop_dir.exists():
|
||||
shortcut_path = desktop_dir / "CalculadoraMAV.desktop"
|
||||
|
||||
shortcut_content = f"""[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=Calculadora MAV - CAS Híbrido
|
||||
Comment=Sistema de álgebra computacional híbrido
|
||||
Exec={sys.executable} "{main_script}"
|
||||
Icon=calculator
|
||||
Terminal=false
|
||||
Categories=Education;Science;Math;
|
||||
"""
|
||||
|
||||
with open(shortcut_path, 'w') as f:
|
||||
f.write(shortcut_content)
|
||||
|
||||
# Hacer ejecutable
|
||||
os.chmod(shortcut_path, 0o755)
|
||||
print(f"✅ Acceso directo creado: {shortcut_path}")
|
||||
return True
|
||||
|
||||
elif sys.platform == 'win32':
|
||||
# Windows shortcut (requiere pywin32)
|
||||
try:
|
||||
import win32com.client
|
||||
|
||||
desktop = Path.home() / "Desktop"
|
||||
shortcut_path = desktop / "Calculadora MAV.lnk"
|
||||
|
||||
shell = win32com.client.Dispatch("WScript.Shell")
|
||||
shortcut = shell.CreateShortCut(str(shortcut_path))
|
||||
shortcut.Targetpath = sys.executable
|
||||
shortcut.Arguments = f'"{main_script}"'
|
||||
shortcut.WorkingDirectory = str(current_dir)
|
||||
shortcut.IconLocation = sys.executable
|
||||
shortcut.save()
|
||||
|
||||
print(f"✅ Acceso directo creado: {shortcut_path}")
|
||||
return True
|
||||
|
||||
except ImportError:
|
||||
print("⚠️ Para crear acceso directo en Windows instalar: pip install pywin32")
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ Error creando acceso directo: {e}")
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def run_tests():
|
||||
"""Ejecuta tests básicos"""
|
||||
print("\n=== Ejecutando tests básicos ===")
|
||||
|
||||
try:
|
||||
# Test de importación
|
||||
from bracket_parser import BracketParser
|
||||
from hybrid_base_types import Hex, Bin, IP4
|
||||
from hybrid_evaluation_engine import HybridEvaluationEngine
|
||||
|
||||
print("✅ Importaciones básicas")
|
||||
|
||||
# Test de parser
|
||||
parser = BracketParser()
|
||||
result, info = parser.parse_line("Hex[FF]")
|
||||
assert result == 'Hex("FF")', f"Parser test failed: {result}"
|
||||
print("✅ Bracket parser")
|
||||
|
||||
# Test de clases híbridas
|
||||
h = Hex("FF")
|
||||
assert str(h) == "0xFF", f"Hex test failed: {h}"
|
||||
print("✅ Clases híbridas")
|
||||
|
||||
# Test de motor de evaluación
|
||||
engine = HybridEvaluationEngine()
|
||||
result = engine.evaluate_line("2 + 3")
|
||||
assert result.result == 5, f"Engine test failed: {result.result}"
|
||||
print("✅ Motor de evaluación")
|
||||
|
||||
print("✅ Todos los tests básicos pasaron")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error en tests: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
"""Función principal de setup"""
|
||||
print("=== Calculadora MAV - CAS Híbrido - Setup ===\n")
|
||||
|
||||
# Verificar Python
|
||||
if not check_python_version():
|
||||
sys.exit(1)
|
||||
|
||||
print("\n=== Verificando dependencias ===")
|
||||
|
||||
# Verificar tkinter
|
||||
if not check_tkinter():
|
||||
print("\n❌ tkinter es requerido para la interfaz gráfica")
|
||||
sys.exit(1)
|
||||
|
||||
# Verificar dependencias principales
|
||||
deps_ok = True
|
||||
deps_ok &= check_module("sympy")
|
||||
deps_ok &= check_module("matplotlib")
|
||||
deps_ok &= check_module("numpy")
|
||||
|
||||
# Si faltan dependencias, intentar instalar
|
||||
if not deps_ok:
|
||||
print("\n=== Faltan dependencias requeridas ===")
|
||||
response = input("¿Instalar automáticamente? (s/n): ").lower().strip()
|
||||
|
||||
if response in ['s', 'si', 'y', 'yes']:
|
||||
if not install_requirements():
|
||||
print("❌ Error instalando dependencias")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("❌ Instale las dependencias manualmente:")
|
||||
print(" pip install sympy matplotlib numpy")
|
||||
sys.exit(1)
|
||||
|
||||
# Verificar módulos opcionales
|
||||
print("\n=== Módulos opcionales ===")
|
||||
check_optional_modules()
|
||||
|
||||
# Ejecutar tests
|
||||
if not run_tests():
|
||||
print("❌ Tests fallaron")
|
||||
sys.exit(1)
|
||||
|
||||
# Crear acceso directo
|
||||
print("\n=== Configuración adicional ===")
|
||||
response = input("¿Crear acceso directo en el escritorio? (s/n): ").lower().strip()
|
||||
if response in ['s', 'si', 'y', 'yes']:
|
||||
create_desktop_shortcut()
|
||||
|
||||
print("\n✅ ¡Setup completado exitosamente!")
|
||||
print("\nPara ejecutar la calculadora:")
|
||||
print(f" python {Path.cwd() / 'hybrid_calc_app.py'}")
|
||||
print("\nO usa el acceso directo si lo creaste.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,177 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test específico para plots interactivos y resultados clickeables
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Agregar directorio actual al path
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
from hybrid_evaluation_engine import HybridEvaluationEngine
|
||||
from interactive_results import PlotResult, InteractiveResultManager
|
||||
import sympy
|
||||
import tkinter as tk
|
||||
|
||||
|
||||
def test_plot_creation():
|
||||
"""Test de creación de objetos PlotResult"""
|
||||
print("=== Test Creación de Plots ===")
|
||||
|
||||
try:
|
||||
# Test creación básica de PlotResult
|
||||
plot_obj = PlotResult("plot", (sympy.sin(sympy.Symbol('x')), (sympy.Symbol('x'), -10, 10)), {})
|
||||
print(f"✅ PlotResult creado: {plot_obj}")
|
||||
print(f" Tipo: {type(plot_obj)}")
|
||||
print(f" String: {str(plot_obj)}")
|
||||
print(f" Plot type: {plot_obj.plot_type}")
|
||||
|
||||
# Test detección como interactivo
|
||||
from hybrid_evaluation_engine import EvaluationResult
|
||||
result = EvaluationResult(plot_obj, "expression")
|
||||
print(f"✅ is_interactive: {result.is_interactive}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error en creación de plots: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def test_plot_evaluation():
|
||||
"""Test de evaluación de expresiones plot"""
|
||||
print("\n=== Test Evaluación de Plots ===")
|
||||
|
||||
engine = HybridEvaluationEngine()
|
||||
|
||||
plot_expressions = [
|
||||
"plot(sin(x), (x, -2*pi, 2*pi))",
|
||||
"plot(x**2, (x, -5, 5))",
|
||||
"plot(cos(x), (x, 0, 2*pi))",
|
||||
"Matrix([[1, 2], [3, 4]])", # También debería ser interactivo
|
||||
]
|
||||
|
||||
for expr in plot_expressions:
|
||||
try:
|
||||
print(f"\nEvaluando: '{expr}'")
|
||||
result = engine.evaluate_line(expr)
|
||||
|
||||
if result.is_error:
|
||||
print(f" ❌ Error: {result.error}")
|
||||
else:
|
||||
print(f" ✅ Resultado: {result.result}")
|
||||
print(f" Tipo: {type(result.result)}")
|
||||
print(f" Es interactivo: {result.is_interactive}")
|
||||
|
||||
# Verificar si es PlotResult
|
||||
if isinstance(result.result, PlotResult):
|
||||
print(f" ✅ Es PlotResult!")
|
||||
print(f" Plot type: {result.result.plot_type}")
|
||||
print(f" Args: {result.result.args}")
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Excepción: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def test_interactive_manager():
|
||||
"""Test del manager de resultados interactivos"""
|
||||
print("\n=== Test Interactive Manager ===")
|
||||
|
||||
# Necesitamos una ventana tk para el manager
|
||||
try:
|
||||
root = tk.Tk()
|
||||
root.withdraw() # Ocultar ventana
|
||||
|
||||
manager = InteractiveResultManager(root)
|
||||
|
||||
# Crear widget de texto simulado
|
||||
text_widget = tk.Text(root)
|
||||
|
||||
# Test con PlotResult
|
||||
plot_obj = PlotResult("plot", (sympy.sin(sympy.Symbol('x')), (sympy.Symbol('x'), -10, 10)), {})
|
||||
|
||||
interactive_info = manager.create_interactive_tag(plot_obj, text_widget, "1.0")
|
||||
|
||||
if interactive_info and len(interactive_info) == 2:
|
||||
tag, display_text = interactive_info
|
||||
print(f"✅ Tag interactivo creado: '{tag}'")
|
||||
print(f" Display text: '{display_text}'")
|
||||
else:
|
||||
print(f"❌ No se creó tag interactivo: {interactive_info}")
|
||||
|
||||
# Test con Matrix
|
||||
matrix = sympy.Matrix([[1, 2], [3, 4]])
|
||||
interactive_info2 = manager.create_interactive_tag(matrix, text_widget, "2.0")
|
||||
|
||||
if interactive_info2 and len(interactive_info2) == 2:
|
||||
tag2, display_text2 = interactive_info2
|
||||
print(f"✅ Tag para matriz creado: '{tag2}'")
|
||||
print(f" Display text: '{display_text2}'")
|
||||
else:
|
||||
print(f"❌ No se creó tag para matriz: {interactive_info2}")
|
||||
|
||||
root.destroy()
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error en test de manager: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def test_full_workflow():
|
||||
"""Test del flujo completo: evaluación → resultado interactivo"""
|
||||
print("\n=== Test Flujo Completo ===")
|
||||
|
||||
try:
|
||||
root = tk.Tk()
|
||||
root.withdraw()
|
||||
|
||||
# Simular el flujo de la aplicación
|
||||
engine = HybridEvaluationEngine()
|
||||
manager = InteractiveResultManager(root)
|
||||
text_widget = tk.Text(root)
|
||||
|
||||
# Evaluar plot
|
||||
result = engine.evaluate_line("plot(sin(x), (x, -pi, pi))")
|
||||
|
||||
print(f"Resultado evaluación: {result.result} (tipo: {type(result.result)})")
|
||||
print(f"Es interactivo: {result.is_interactive}")
|
||||
|
||||
if result.is_interactive:
|
||||
interactive_info = manager.create_interactive_tag(
|
||||
result.result, text_widget, "1.0"
|
||||
)
|
||||
|
||||
if interactive_info and len(interactive_info) == 2:
|
||||
tag, display_text = interactive_info
|
||||
print(f"✅ ÉXITO: Tag '{tag}' con texto '{display_text}'")
|
||||
else:
|
||||
print(f"❌ FALLO: No se creó tag interactivo")
|
||||
else:
|
||||
print("❌ FALLO: Resultado no marcado como interactivo")
|
||||
|
||||
root.destroy()
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error en flujo completo: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def main():
|
||||
"""Función principal de testing"""
|
||||
print("Test de Plots Interactivos - Calculadora MAV")
|
||||
print("=" * 50)
|
||||
|
||||
test_plot_creation()
|
||||
test_plot_evaluation()
|
||||
test_interactive_manager()
|
||||
test_full_workflow()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("Testing completado.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
410
test_suite.py
410
test_suite.py
|
@ -1,410 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Suite de tests unitarios para Calculadora MAV - CAS Híbrido
|
||||
"""
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Agregar directorio actual al path para importar módulos
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
import sympy
|
||||
from bracket_parser import BracketParser, EquationDetector
|
||||
from hybrid_base_types import (
|
||||
HybridHex, HybridBin, HybridDec, HybridIP4, HybridChr,
|
||||
Hex, Bin, Dec, IP4, Chr
|
||||
)
|
||||
from hybrid_evaluation_engine import HybridEvaluationEngine, EvaluationResult
|
||||
|
||||
|
||||
class TestBracketParser(unittest.TestCase):
|
||||
"""Tests para el bracket parser"""
|
||||
|
||||
def setUp(self):
|
||||
self.parser = BracketParser()
|
||||
|
||||
def test_bracket_transformation(self):
|
||||
"""Test transformación de sintaxis con corchetes"""
|
||||
test_cases = [
|
||||
("Hex[FF]", 'Hex("FF")'),
|
||||
("IP4[192.168.1.1/24]", 'IP4("192.168.1.1/24")'),
|
||||
("Bin[1010]", 'Bin("1010")'),
|
||||
("Dec[10.5]", 'Dec("10.5")'),
|
||||
("Chr[A]", 'Chr("A")'),
|
||||
]
|
||||
|
||||
for input_expr, expected in test_cases:
|
||||
with self.subTest(input_expr=input_expr):
|
||||
result, info = self.parser.parse_line(input_expr)
|
||||
self.assertEqual(result, expected)
|
||||
self.assertEqual(info, "bracket_transform")
|
||||
|
||||
def test_solve_shortcut(self):
|
||||
"""Test transformación de atajo solve"""
|
||||
test_cases = [
|
||||
("x=?", "solve(x)"),
|
||||
("variable_name=?", "solve(variable_name)"),
|
||||
("a=?", "solve(a)"),
|
||||
]
|
||||
|
||||
for input_expr, expected in test_cases:
|
||||
with self.subTest(input_expr=input_expr):
|
||||
result, info = self.parser.parse_line(input_expr)
|
||||
self.assertEqual(result, expected)
|
||||
self.assertEqual(info, "solve_shortcut")
|
||||
|
||||
def test_equation_detection(self):
|
||||
"""Test detección de ecuaciones standalone"""
|
||||
equation_cases = [
|
||||
"x + 2 = 5",
|
||||
"3*a + b = 10",
|
||||
"x**2 == 4",
|
||||
"y > 5",
|
||||
]
|
||||
|
||||
for equation in equation_cases:
|
||||
with self.subTest(equation=equation):
|
||||
result, info = self.parser.parse_line(equation)
|
||||
self.assertEqual(info, "equation")
|
||||
self.assertTrue(result.startswith('_add_equation('))
|
||||
|
||||
def test_non_equation_cases(self):
|
||||
"""Test casos que NO deben detectarse como ecuaciones"""
|
||||
non_equation_cases = [
|
||||
"result = solve(x + 2, x)", # Asignación Python
|
||||
"2 + 3", # Expresión simple
|
||||
"sin(pi/2)", # Función
|
||||
]
|
||||
|
||||
for expr in non_equation_cases:
|
||||
with self.subTest(expr=expr):
|
||||
result, info = self.parser.parse_line(expr)
|
||||
self.assertNotEqual(info, "equation")
|
||||
|
||||
def test_comments(self):
|
||||
"""Test manejo de comentarios"""
|
||||
comment_cases = [
|
||||
"# Esto es un comentario",
|
||||
" # Comentario con espacios",
|
||||
"", # Línea vacía
|
||||
]
|
||||
|
||||
for comment in comment_cases:
|
||||
with self.subTest(comment=comment):
|
||||
result, info = self.parser.parse_line(comment)
|
||||
self.assertEqual(info, "comment")
|
||||
|
||||
|
||||
class TestHybridBaseTypes(unittest.TestCase):
|
||||
"""Tests para las clases base híbridas"""
|
||||
|
||||
def test_hybrid_hex(self):
|
||||
"""Test clase HybridHex"""
|
||||
# Creación desde string
|
||||
h1 = Hex("FF")
|
||||
self.assertEqual(h1.value, 255)
|
||||
self.assertEqual(str(h1), "0xFF")
|
||||
|
||||
# Creación desde entero
|
||||
h2 = Hex(255)
|
||||
self.assertEqual(h2.value, 255)
|
||||
self.assertEqual(str(h2), "0xFF")
|
||||
|
||||
# Verificar que es instancia de SymPy Basic
|
||||
self.assertIsInstance(h1, sympy.Basic)
|
||||
|
||||
# Test conversión decimal
|
||||
self.assertEqual(h1.__dec__(), 255)
|
||||
|
||||
def test_hybrid_bin(self):
|
||||
"""Test clase HybridBin"""
|
||||
# Creación desde string binario
|
||||
b1 = Bin("1010")
|
||||
self.assertEqual(b1.value, 10)
|
||||
self.assertEqual(str(b1), "0b1010")
|
||||
|
||||
# Creación desde entero
|
||||
b2 = Bin(10)
|
||||
self.assertEqual(b2.value, 10)
|
||||
self.assertEqual(str(b2), "0b1010")
|
||||
|
||||
# Verificar SymPy Basic
|
||||
self.assertIsInstance(b1, sympy.Basic)
|
||||
|
||||
def test_hybrid_ip4(self):
|
||||
"""Test clase HybridIP4"""
|
||||
# IP con CIDR
|
||||
ip1 = IP4("192.168.1.100/24")
|
||||
self.assertEqual(ip1.ip_address, "192.168.1.100")
|
||||
self.assertEqual(ip1.prefix, 24)
|
||||
self.assertEqual(str(ip1), "192.168.1.100/24")
|
||||
|
||||
# IP sin máscara
|
||||
ip2 = IP4("10.0.0.1")
|
||||
self.assertEqual(ip2.ip_address, "10.0.0.1")
|
||||
self.assertIsNone(ip2.prefix)
|
||||
|
||||
# Verificar SymPy Basic
|
||||
self.assertIsInstance(ip1, sympy.Basic)
|
||||
|
||||
# Test métodos especializados
|
||||
network = ip1.NetworkAddress()
|
||||
self.assertEqual(str(network), "192.168.1.0/24")
|
||||
|
||||
broadcast = ip1.BroadcastAddress()
|
||||
self.assertEqual(str(broadcast), "192.168.1.255/24")
|
||||
|
||||
nodes = ip1.Nodes()
|
||||
self.assertEqual(nodes, 254) # 2^8 - 2
|
||||
|
||||
def test_hybrid_chr(self):
|
||||
"""Test clase HybridChr"""
|
||||
# Carácter único
|
||||
c1 = Chr("A")
|
||||
self.assertEqual(c1.value, 65)
|
||||
self.assertEqual(str(c1), "A")
|
||||
|
||||
# String múltiple
|
||||
c2 = Chr("Hello")
|
||||
self.assertEqual(c2.value, [72, 101, 108, 108, 111])
|
||||
self.assertEqual(str(c2), "Hello")
|
||||
|
||||
# Verificar SymPy Basic
|
||||
self.assertIsInstance(c1, sympy.Basic)
|
||||
|
||||
def test_hybrid_dec(self):
|
||||
"""Test clase HybridDec"""
|
||||
# Desde string decimal
|
||||
d1 = Dec("10.5")
|
||||
self.assertEqual(d1.value, 10.5)
|
||||
self.assertEqual(str(d1), "10.5")
|
||||
|
||||
# Desde entero
|
||||
d2 = Dec(10)
|
||||
self.assertEqual(d2.value, 10.0)
|
||||
self.assertEqual(str(d2), "10")
|
||||
|
||||
# Verificar SymPy Basic
|
||||
self.assertIsInstance(d1, sympy.Basic)
|
||||
|
||||
|
||||
class TestHybridEvaluationEngine(unittest.TestCase):
|
||||
"""Tests para el motor de evaluación híbrida"""
|
||||
|
||||
def setUp(self):
|
||||
self.engine = HybridEvaluationEngine()
|
||||
|
||||
def test_basic_expressions(self):
|
||||
"""Test expresiones básicas"""
|
||||
test_cases = [
|
||||
("2 + 3", 5),
|
||||
("10 * 2", 20),
|
||||
("15 / 3", 5),
|
||||
]
|
||||
|
||||
for expr, expected in test_cases:
|
||||
with self.subTest(expr=expr):
|
||||
result = self.engine.evaluate_line(expr)
|
||||
self.assertFalse(result.is_error)
|
||||
self.assertEqual(result.result, expected)
|
||||
|
||||
def test_sympy_functions(self):
|
||||
"""Test funciones de SymPy"""
|
||||
# Test sin con pi/2
|
||||
result = self.engine.evaluate_line("sin(pi/2)")
|
||||
self.assertFalse(result.is_error)
|
||||
self.assertEqual(result.result, 1)
|
||||
|
||||
# Test diferenciación
|
||||
result = self.engine.evaluate_line("diff(x**2, x)")
|
||||
self.assertFalse(result.is_error)
|
||||
self.assertEqual(str(result.result), "2*x")
|
||||
|
||||
def test_bracket_syntax(self):
|
||||
"""Test sintaxis con corchetes"""
|
||||
# Test Hex
|
||||
result = self.engine.evaluate_line("Hex[FF]")
|
||||
self.assertFalse(result.is_error)
|
||||
self.assertIsInstance(result.result, Hex)
|
||||
self.assertEqual(result.result.value, 255)
|
||||
|
||||
# Test IP4
|
||||
result = self.engine.evaluate_line("IP4[192.168.1.1/24]")
|
||||
self.assertFalse(result.is_error)
|
||||
self.assertIsInstance(result.result, IP4)
|
||||
self.assertEqual(result.result.ip_address, "192.168.1.1")
|
||||
|
||||
def test_equation_handling(self):
|
||||
"""Test manejo de ecuaciones"""
|
||||
# Agregar ecuación
|
||||
result = self.engine.evaluate_line("x + 2 = 5")
|
||||
self.assertFalse(result.is_error)
|
||||
self.assertEqual(result.result_type, "equation_added")
|
||||
|
||||
# Verificar que la ecuación se agregó
|
||||
self.assertEqual(len(self.engine.equations), 1)
|
||||
|
||||
def test_variable_creation(self):
|
||||
"""Test creación automática de variables"""
|
||||
# Usar variable no definida
|
||||
result = self.engine.evaluate_line("x + y")
|
||||
self.assertFalse(result.is_error)
|
||||
|
||||
# Verificar que las variables se crearon como símbolos
|
||||
self.assertIn("x", self.engine.symbol_table)
|
||||
self.assertIn("y", self.engine.symbol_table)
|
||||
self.assertIsInstance(self.engine.symbol_table["x"], sympy.Symbol)
|
||||
|
||||
def test_solve_shortcut(self):
|
||||
"""Test atajo de solve"""
|
||||
# Agregar ecuación
|
||||
self.engine.evaluate_line("x + 2 = 5")
|
||||
|
||||
# Usar atajo solve
|
||||
result = self.engine.evaluate_line("x=?")
|
||||
self.assertFalse(result.is_error)
|
||||
|
||||
# Verificar que x se resolvió
|
||||
self.assertIn("x", self.engine.symbol_table)
|
||||
|
||||
def test_error_handling(self):
|
||||
"""Test manejo de errores"""
|
||||
# División por cero
|
||||
result = self.engine.evaluate_line("1/0")
|
||||
self.assertTrue(result.is_error)
|
||||
|
||||
# Sintaxis inválida
|
||||
result = self.engine.evaluate_line("2 +")
|
||||
self.assertTrue(result.is_error)
|
||||
|
||||
def test_clear_operations(self):
|
||||
"""Test operaciones de limpieza"""
|
||||
# Agregar datos
|
||||
self.engine.evaluate_line("x = 5")
|
||||
self.engine.evaluate_line("y + 2 = 7")
|
||||
|
||||
# Verificar que hay datos
|
||||
self.assertTrue(len(self.engine.symbol_table) > 0)
|
||||
self.assertTrue(len(self.engine.equations) > 0)
|
||||
|
||||
# Limpiar variables
|
||||
self.engine.clear_variables()
|
||||
self.assertEqual(len(self.engine.symbol_table), 0)
|
||||
|
||||
# Limpiar ecuaciones
|
||||
self.engine.clear_equations()
|
||||
self.assertEqual(len(self.engine.equations), 0)
|
||||
|
||||
|
||||
class TestIntegration(unittest.TestCase):
|
||||
"""Tests de integración del sistema completo"""
|
||||
|
||||
def setUp(self):
|
||||
self.engine = HybridEvaluationEngine()
|
||||
|
||||
def test_specialized_class_methods(self):
|
||||
"""Test métodos de clases especializadas"""
|
||||
# IP4 NetworkAddress
|
||||
result = self.engine.evaluate_line("IP4[192.168.1.100/24].NetworkAddress[]")
|
||||
self.assertFalse(result.is_error)
|
||||
self.assertIsInstance(result.result, IP4)
|
||||
self.assertEqual(str(result.result), "192.168.1.0/24")
|
||||
|
||||
def test_mixed_operations(self):
|
||||
"""Test operaciones mixtas"""
|
||||
# Hex + entero
|
||||
result = self.engine.evaluate_line("Hex[FF] + 1")
|
||||
self.assertFalse(result.is_error)
|
||||
# El resultado depende de cómo implementemos las operaciones
|
||||
|
||||
def test_equation_solving_workflow(self):
|
||||
"""Test flujo completo de resolución de ecuaciones"""
|
||||
# Agregar ecuaciones
|
||||
self.engine.evaluate_line("x + y = 10")
|
||||
self.engine.evaluate_line("x - y = 2")
|
||||
|
||||
# Resolver sistema
|
||||
try:
|
||||
solutions = self.engine.solve_system()
|
||||
self.assertIsInstance(solutions, dict)
|
||||
# Verificar que x = 6, y = 4
|
||||
self.assertEqual(solutions.get("x"), 6)
|
||||
self.assertEqual(solutions.get("y"), 4)
|
||||
except Exception as e:
|
||||
self.fail(f"Solve system failed: {e}")
|
||||
|
||||
def test_sympy_integration(self):
|
||||
"""Test integración con SymPy"""
|
||||
# Diferenciación de expresión con variables
|
||||
result = self.engine.evaluate_line("diff(x**3 + 2*x**2 + x, x)")
|
||||
self.assertFalse(result.is_error)
|
||||
expected = "3*x**2 + 4*x + 1"
|
||||
self.assertEqual(str(result.result), expected)
|
||||
|
||||
# Integración
|
||||
result = self.engine.evaluate_line("integrate(2*x, x)")
|
||||
self.assertFalse(result.is_error)
|
||||
self.assertEqual(str(result.result), "x**2")
|
||||
|
||||
|
||||
def run_all_tests():
|
||||
"""Ejecuta todos los tests"""
|
||||
print("=== Ejecutando Suite de Tests ===\n")
|
||||
|
||||
# Crear suite de tests
|
||||
loader = unittest.TestLoader()
|
||||
suite = unittest.TestSuite()
|
||||
|
||||
# Agregar test classes
|
||||
test_classes = [
|
||||
TestBracketParser,
|
||||
TestHybridBaseTypes,
|
||||
TestHybridEvaluationEngine,
|
||||
TestIntegration
|
||||
]
|
||||
|
||||
for test_class in test_classes:
|
||||
tests = loader.loadTestsFromTestCase(test_class)
|
||||
suite.addTests(tests)
|
||||
|
||||
# Ejecutar tests
|
||||
runner = unittest.TextTestRunner(verbosity=2)
|
||||
result = runner.run(suite)
|
||||
|
||||
# Mostrar resumen
|
||||
print(f"\n=== Resumen ===")
|
||||
print(f"Tests ejecutados: {result.testsRun}")
|
||||
print(f"Errores: {len(result.errors)}")
|
||||
print(f"Fallos: {len(result.failures)}")
|
||||
|
||||
if result.errors:
|
||||
print("\nErrores:")
|
||||
for test, error in result.errors:
|
||||
print(f" {test}: {error}")
|
||||
|
||||
if result.failures:
|
||||
print("\nFallos:")
|
||||
for test, failure in result.failures:
|
||||
print(f" {test}: {failure}")
|
||||
|
||||
success = len(result.errors) == 0 and len(result.failures) == 0
|
||||
print(f"\n{'✅ Todos los tests pasaron' if success else '❌ Algunos tests fallaron'}")
|
||||
|
||||
return success
|
||||
|
||||
|
||||
def main():
|
||||
"""Función principal"""
|
||||
if len(sys.argv) > 1 and sys.argv[1] == "--verbose":
|
||||
# Ejecutar tests individuales con más detalle
|
||||
unittest.main(verbosity=2)
|
||||
else:
|
||||
# Ejecutar suite completa
|
||||
success = run_all_tests()
|
||||
sys.exit(0 if success else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in New Issue