diff --git a/.doc/Guia_Base.md b/.doc/Guia_Base.md new file mode 100644 index 0000000..8a0356f --- /dev/null +++ b/.doc/Guia_Base.md @@ -0,0 +1,234 @@ +# Guía de Principios Arquitectónicos - Calculadora Algebraica Híbrida + +## Filosofía Central: Álgebra Simbólica con Evaluación Numérica + +### Concepto Fundamental + +La calculadora mantiene **forma simbólica como representación primaria** y proporciona **evaluación numérica como información complementaria** cuando es posible y útil. + +**Principio de Evaluación Numérica**: El resultado siempre se intenta calcular numéricamente y se muestra cuando la representación string del resultado numérico difiere del resultado algebraico. + +## Sistema de Asignaciones y Ecuaciones + +### Principio: Separación clara entre asignaciones y ecuaciones + +```python +# Asignación de variable +x = 5 # → Se guarda el valor simbólico (NO se agrega como ecuación) +y = x + a # → Se guarda la expresión simbólica + +# Ecuación pura (detección automática) +x**2 + 2*x = 8 # → Se agrega como ecuación Eq(x**2 + 2*x, 8) +a + b = 10 # → Se agrega como ecuación Eq(a + b, 10) +a > b + 1 # → Se agrega como desigualdad +``` + +### Criterios de Detección de Ecuaciones + +**Se considera ecuación si:** + +- Contiene `=` Y NO es una asignación simple de variable +- Contiene operadores de comparación: `==`, `>`, `<`, `>=`, `<=` +- Tiene estructura algebraica en ambos lados del `=` + +**NO se considera ecuación:** + +- Asignaciones simples: `x = 5`, `y = expresión` +- Líneas con solo números o comentarios + +--- + +## Uso de `solve()` y el Atajo `=?` + +### Principio: `variable=?` es equivalente a `solve(variable)` + +```python +# Ambas formas son idénticas: +solve(a) # Resuelve 'a' usando el sistema de ecuaciones +a=? # Atajo sintáctico para solve(a) +``` + +El comando intenta resolver algebraicamente y numéricamente en paralelo cuando es posible. + +--- + +## Sistema de Tokenización con Prioridades + +### Principio: Las prioridades son números arbitrarios que determinan el orden de aplicación + +``` +Prioridad 5: IntBase → Patrón: 16#FF (muy específico) +Prioridad 6: Hex/Bin → Patrón: 0xFF, 0b1010 (específico) +Prioridad 10: FourBytes → Patrón: x.x.x.x (general) +``` + +**Regla**: Menor número = se aplica primero + +Esto garantiza que `16#10.10.10.10` se tokenice como IntBase antes de considerar FourBytes. + +--- + +## Conversión Perezosa y Tipos Especializados + +### Principio: Mantener el tipo especializado siempre que sea posible + +**Se evita convertir a SymPy al máximo**. Los objetos especializados mantienen su tipo cuando pueden resolver la operación internamente. + +```python +# Operaciones que mantienen el tipo especializado: +Hex(15) + 1 → Hex(16) # Hex maneja la suma +16#15 + 1 → 16#16 # IntBase maneja la suma +10.1.1.1 + 1 → 10.1.1.2 # FourBytes maneja la suma + +# Conversión a SymPy solo cuando es necesario: +sin(16#FF) → sin(255) # sin() requiere SymPy +solve(x + 16#10) → solve() # Álgebra compleja requiere SymPy +``` + +**Regla**: Solo se convierte a SymPy cuando el token no es atómico o la operación requiere capacidades algebraicas avanzadas. + +--- + +## Contexto Limpio por Evaluación + +### Principio: Cada evaluación comienza desde cero + +**Contexto Limpio** significa: + +- Se eliminan todas las ecuaciones previas +- Se eliminan todas las variables definidas +- Se re-evalúa todo el contenido línea por línea +- Comportamiento idéntico a iniciar una nueva sesión + +Esto garantiza resultados predecibles y sin efectos secundarios acumulados. + +--- + +## Resultados Interactivos + +### Principio: Activación mediante clicks en elementos con binding + +Los resultados interactivos se crean cuando el tipo de resultado requiere visualización expandida: + +```python +plot(sin(x)) → "📊 Ver Plot" # Click abre ventana matplotlib +Matrix([[1,2],[3,4]]) → "📋 Ver Matriz" # Click muestra matriz formateada +[1,2,3,4,5,6,7,8] → "📋 Ver Lista" # Click expande contenido +``` + +**Activación**: Click en el texto con binding dispara la ventana emergente correspondiente. + +--- + +## Sistema de Autocompletado + +### Principio: Sistema extensible con prioridades (por implementar) + +Se propone usar un sistema de prioridades numéricas similar al de tokenización: + +``` +Prioridad 1: Métodos del objeto específico +Prioridad 2: Métodos de la clase base +Prioridad 3: Funciones SymPy relevantes +Prioridad 4: Helpers contextuales +``` + +--- + +## Integración con SymPy + +### Principio: En casos de conflicto, siempre prevalece SymPy + +Cuando hay ambigüedad o conflicto entre tipos personalizados y SymPy: + +- Funciones SymPy tienen precedencia +- Símbolos SymPy (como `e`, `pi`) mantienen su significado matemático +- Las conversiones fallidas retornan a comportamiento SymPy estándar + +--- + +## Arquitectura de Tipos Personalizados + +### Clases Base Universales + +**IntBase**: Números en cualquier base + +- Aritmética nativa que preserva la base +- Conversión a SymPy solo cuando necesario +- Soporte para símbolos algebraicos + +**FourBytes**: Patrones x.x.x.x + +- Aritmética de 32-bit para IPs +- Soporte para elementos simbólicos +- Base para tipos como IP4 + +### Principio de Extensibilidad + +Cada tipo en `custom_types/` define: + +1. Su lógica de tokenización +2. Sus operaciones aritméticas +3. Sus métodos de conversión +4. Su ayuda contextual +5. Sus sugerencias de autocompletado + +--- + +## Flujo de Evaluación Completo + +``` +Entrada Usuario + ↓ +[Tokenización Universal] + ↓ +¿Asignación o Ecuación? + ├─ Asignación → Guardar valor simbólico + └─ Ecuación → Agregar al sistema + ↓ +[Evaluación] + ├─ Mantener tipo especializado si es posible + └─ Convertir a SymPy si es necesario + ↓ +[Resultado] + ├─ Forma simbólica (siempre) + └─ Evaluación numérica (si difiere como string) +``` + +--- + +## Ejemplos Integrales + +```python +# Tokenización y tipos especializados +16#FF + 2#1010 → 16#109 # IntBase preservado +192.168.1.1 + 5 → 192.168.1.6 # FourBytes preservado + +# Asignaciones vs ecuaciones +x = 10 # Asignación (solo guarda valor) +x + y = 15 # Ecuación (se agrega al sistema) +y=? # solve(y) → y = 5 + +# Evaluación numérica automática +4/5 → 4/5 ≈ 0.8 # Difiere como string +sqrt(2) → √2 ≈ 1.414 # Difiere como string +2 + 3 → 5 # NO se muestra ≈ (igual string) + +# Resultados interactivos +plot(sin(x)) → "📊 Ver Plot" [clickeable] +Matrix([[1,2]]) → "📋 Ver Matriz" [clickeable] +``` + +--- + +## Principios de Diseño Resumidos + +1. **Álgebra primero**: Mantener forma simbólica siempre +2. **Evaluación numérica inteligente**: Mostrar solo cuando agrega valor +3. **Tipos especializados preservados**: Conversión a SymPy solo cuando necesario +4. **Tokenización ordenada**: Sistema de prioridades simple y predecible +5. **Contexto limpio**: Cada evaluación desde cero +6. **Extensibilidad**: Nuevos tipos se integran automáticamente +7. **SymPy prevalece**: En conflictos, el comportamiento matemático estándar gana + +Este documento ahora refleja con precisión la arquitectura implementada y sirve como guía coherente para el desarrollo futuro. \ No newline at end of file diff --git a/.doc/LLM_DEBUG_API_GUIDE.md b/.doc/LLM_DEBUG_API_GUIDE.md index 83a303a..083a39f 100644 --- a/.doc/LLM_DEBUG_API_GUIDE.md +++ b/.doc/LLM_DEBUG_API_GUIDE.md @@ -2,12 +2,16 @@ ## Propósito -Esta API permite debuggear la **Calculadora MAV CAS** sin modificar código fuente ni crear scripts adicionales. Como LLM, puedes usar esta herramienta para: +Esta API permite debuggear la **Calculadora MAV CAS** sin modificar código fuente ni crear scripts adicionales. + +**Principio**: **texto → texto** tal como se muestra en la aplicación. +**NO duplica lógica**, solo encapsula llamadas directas al motor existente. + +Como LLM, puedes usar esta herramienta para: - **Diagnosticar problemas** en el motor de evaluación - **Verificar comportamiento** de tipos personalizados (FourBytes, IntBase, IP4Mask, etc.) - **Inspeccionar el estado interno** del motor (variables, contexto, tipos registrados) -- **Analizar el formato de salida** exacto como se muestra en la aplicación - **Testing de regresión** para verificar que cambios no rompan funcionalidad existente ## Flujo de Trabajo Recomendado @@ -22,6 +26,7 @@ Esta API permite debuggear la **Calculadora MAV CAS** sin modificar código fuen ### Query tipo `input` Evalúa expresiones como si el usuario las escribiera en la calculadora. +**Resultado**: texto tal como se muestra en la aplicación. ```json {"index": 0, "type": "input", "content": "10.1.1.1 + 1"} @@ -29,6 +34,7 @@ Evalúa expresiones como si el usuario las escribiera en la calculadora. ### Query tipo `exec` Ejecuta código Python para inspeccionar el estado interno del motor. +**Resultado**: valor directo de la evaluación Python. ```json {"index": 1, "type": "exec", "content": "type(engine.last_result).__name__"} @@ -126,7 +132,7 @@ Ejecuta código Python para inspeccionar el estado interno del motor. ```json { "execution_info": { - "timestamp": "2025-06-05T18:01:28.644799Z", + "timestamp": "2025-06-05T18:25:22.256442Z", "total_queries": 5, "successful": 4, "failed": 1 @@ -141,22 +147,10 @@ Ejecuta código Python para inspeccionar el estado interno del motor. { "index": 0, "input": "10.1.1.1 + 1", - "output": "10.1.1.2", // Resultado básico + "output": "10.1.1.2", // Texto tal como se muestra en la app "result_type": "FourBytes", // Tipo del objeto resultado "success": true, - "error": null, - "display_class": "[FourBytes]", // Nombre de clase para display - "output_raw": { // ⭐ INFORMACIÓN DE FORMATO - "parts": [ - ["custom_type", "10.1.1.2"], // [tag_color, contenido] - ["class_hint", "[FourBytes]"] - ], - "formatted_text": "[custom_type]10.1.1.2[class_hint][FourBytes]", - "tag_info": { // Información de colores - "custom_type": {"fg": "#f9a825"}, - "class_hint": {"fg": "#888888"} - } - } + "error": null } ``` @@ -166,11 +160,11 @@ Ejecuta código Python para inspeccionar el estado interno del motor. { "index": 1, "input": "type(engine.last_result).__name__", - "output": "FourBytes", - "result_type": "str", + "output": "FourBytes", // String del resultado + "result_type": "str", // Tipo del resultado de la evaluación "success": true, "error": null, - "exec_result": "FourBytes" // Resultado crudo del exec + "exec_result": "FourBytes" // Valor directo (serializado si es necesario) } ``` @@ -183,12 +177,7 @@ Ejecuta código Python para inspeccionar el estado interno del motor. "output": "None", "result_type": "NoneType", "success": false, - "error": "❌ Máscara inválida: 255.240.0.3...", - "output_raw": { - "parts": [["error", "Error: ❌ Máscara inválida..."]], - "formatted_text": "[error]Error: ❌ Máscara inválida...", - "tag_info": {"error": {"fg": "#ff6b6b", "font": "bold"}} - } + "error": "❌ Máscara inválida: 255.240.0.3..." } ``` @@ -297,9 +286,9 @@ Ejecuta código Python para inspeccionar el estado interno del motor. } ``` -### 4. Debugging de Display/Formato +### 4. Debugging de Comportamiento -**Cuándo usar**: Verificar cómo se muestra exactamente un resultado. +**Cuándo usar**: Verificar comportamiento específico de tipos o funciones. ```json { @@ -314,33 +303,6 @@ Ejecuta código Python para inspeccionar el estado interno del motor. --- -## Análisis de output_raw - -El campo `output_raw` contiene información exacta sobre cómo se muestra el resultado en la aplicación: - -### Tags de Color Importantes - -- **`error`** (`#ff6b6b`): Errores -- **`custom_type`** (`#f9a825`): Tipos personalizados (FourBytes, IntBase, etc.) -- **`symbolic`** (`#82aaff`): Expresiones simbólicas -- **`numeric`** (`#c3e88d`): Aproximaciones numéricas -- **`class_hint`** (`#888888`): Pistas de clase `[FourBytes]` -- **`ip`** (`#fff176`): Direcciones IP específicamente -- **`hex`** (`#f9a825`): Números hexadecimales - -### Interpretación de Parts - -```json -"parts": [ - ["custom_type", "192.168.1.2"], // Resultado principal en naranja - ["class_hint", "[FourBytes]"] // Pista de clase en gris -] -``` - -Esto significa: "192.168.1.2" se muestra en color naranjo seguido de "[FourBytes]" en gris. - ---- - ## Comandos de Ejecución ```bash @@ -375,11 +337,6 @@ python simple_debug.py mi_debug.json --verbose - Tipos registrados - Sistema de ecuaciones -### Formato de Display -**Archivo**: `debug_templates/display_format_test.json` -- Demostración de `output_raw` -- Diferentes tipos de formato - ### Tokenización **Archivo**: `debug_templates/tokenization_test.json` - Debug del sistema de parsing @@ -391,7 +348,6 @@ python simple_debug.py mi_debug.json --verbose ### 1. Identificar el Problema - ¿Es un error de evaluación? -- ¿Es un problema de formato/display? - ¿Es un problema de tokenización? - ¿Es un problema de estado del motor? @@ -402,8 +358,7 @@ python simple_debug.py mi_debug.json --verbose ### 3. Analizar Resultados - Verificar `success: true/false` - Examinar `error` si hay fallo -- Analizar `output_raw` para problemas de display -- Usar `exec_result` para inspección detallada +- Comparar `output` (texto de la app) con `exec_result` (valor interno) ### 4. Iterar - Crear nuevas queries basadas en hallazgos @@ -438,8 +393,8 @@ python simple_debug.py debug_problema.json ``` ### Paso 3: Analizar resultados -- Verificar si `192.168.1.x + 1` se evalúa correctamente -- Comprobar el tipo resultante +- Verificar si `192.168.1.x + 1` se evalúa correctamente (campo `output`) +- Comprobar el tipo resultante con `exec` - Verificar si tiene símbolos - Examinar cómo se tokeniza la expresión - Probar operaciones relacionadas @@ -452,13 +407,12 @@ Este flujo te permite identificar exactamente dónde está el problema y verific 1. **Siempre usa índices secuenciales** en las queries para facilitar la lectura 2. **Combina queries `input` y `exec`** para obtener contexto completo -3. **Verifica tanto `output` como `output_raw`** para problemas de display -4. **Usa los templates existentes** como punto de partida -5. **Examina `success` y `error`** antes de analizar resultados -6. **Aprovecha `exec_result`** para inspección detallada del estado +3. **El campo `output` es texto tal como se muestra en la aplicación** +4. **El campo `exec_result` es el valor directo de la evaluación Python** +5. **Usa los templates existentes** como punto de partida +6. **Examina `success` y `error`** antes de analizar resultados 7. **Crea queries incrementales** que construyan sobre resultados anteriores - -Con esta API, puedes debuggear efectivamente la calculadora sin necesidad de modificar código fuente o crear scripts adicionales. +8. **NO intentes duplicar lógica de la aplicación** - usa solo llamadas directas --- @@ -489,9 +443,8 @@ python simple_debug.py archivo.json -o resultado.json # Salida específica ```python "success": true/false # ¿Éxito? "error": "mensaje" # Error si falla -"output": "resultado" # Resultado básico -"output_raw": {...} # Formato exacto de la app -"exec_result": valor # Resultado crudo (exec queries) +"output": "resultado" # Texto tal como se muestra en la app +"exec_result": valor # Valor directo (exec queries) ``` ### Templates Disponibles @@ -499,10 +452,11 @@ python simple_debug.py archivo.json -o resultado.json # Salida específica - `debug_templates/error_debug.json` - Testing de errores - `debug_templates/context_debug.json` - Estado del motor - `debug_templates/tokenization_test.json` - Debug de parsing -- `debug_templates/display_format_test.json` - Formato de display ### Workflow Típico 1. **Identificar problema** → Crear query `input` para reproducir 2. **Inspeccionar estado** → Añadir queries `exec` para diagnosticar -3. **Analizar resultados** → Examinar `success`, `error`, `output_raw` -4. **Iterar** → Crear nuevas queries basadas en hallazgos \ No newline at end of file +3. **Analizar resultados** → Examinar `success`, `error`, `output` +4. **Iterar** → Crear nuevas queries basadas en hallazgos + +**Principio clave**: texto → texto. La API no interpreta ni procesa, solo encapsula llamadas directas al motor existente. \ No newline at end of file diff --git a/SISTEMA_TOKENIZACION_DISTRIBUIDA.md b/.doc/SISTEMA_TOKENIZACION_DISTRIBUIDA.md similarity index 100% rename from SISTEMA_TOKENIZACION_DISTRIBUIDA.md rename to .doc/SISTEMA_TOKENIZACION_DISTRIBUIDA.md diff --git a/LLM_DEBUG_API_GUIDE.md b/LLM_DEBUG_API_GUIDE.md deleted file mode 100644 index 083a39f..0000000 --- a/LLM_DEBUG_API_GUIDE.md +++ /dev/null @@ -1,462 +0,0 @@ -# Simple Debug API - Guía para LLM - -## Propósito - -Esta API permite debuggear la **Calculadora MAV CAS** sin modificar código fuente ni crear scripts adicionales. - -**Principio**: **texto → texto** tal como se muestra en la aplicación. -**NO duplica lógica**, solo encapsula llamadas directas al motor existente. - -Como LLM, puedes usar esta herramienta para: - -- **Diagnosticar problemas** en el motor de evaluación -- **Verificar comportamiento** de tipos personalizados (FourBytes, IntBase, IP4Mask, etc.) -- **Inspeccionar el estado interno** del motor (variables, contexto, tipos registrados) -- **Testing de regresión** para verificar que cambios no rompan funcionalidad existente - -## Flujo de Trabajo Recomendado - -### 1. Crear archivo JSON con queries -### 2. Ejecutar: `python simple_debug.py archivo.json` -### 3. Analizar resultados en el archivo `*_results.json` - ---- - -## Tipos de Query - -### Query tipo `input` -Evalúa expresiones como si el usuario las escribiera en la calculadora. -**Resultado**: texto tal como se muestra en la aplicación. - -```json -{"index": 0, "type": "input", "content": "10.1.1.1 + 1"} -``` - -### Query tipo `exec` -Ejecuta código Python para inspeccionar el estado interno del motor. -**Resultado**: valor directo de la evaluación Python. - -```json -{"index": 1, "type": "exec", "content": "type(engine.last_result).__name__"} -``` - ---- - -## Plantilla Base - -```json -{ - "queries": [ - {"index": 0, "type": "input", "content": "EXPRESION_A_EVALUAR"}, - {"index": 1, "type": "exec", "content": "CODIGO_PYTHON_INSPECCION"} - ] -} -``` - ---- - -## Casos de Uso Comunes - -### Debugging de Tipos Personalizados - -**Problema**: Verificar si FourBytes maneja correctamente operaciones IP. - -```json -{ - "queries": [ - {"index": 0, "type": "input", "content": "192.168.1.1 + 1"}, - {"index": 1, "type": "exec", "content": "type(engine.last_result).__name__"}, - {"index": 2, "type": "exec", "content": "engine.last_result.original"}, - {"index": 3, "type": "exec", "content": "engine.last_result._numeric_value"}, - {"index": 4, "type": "input", "content": "10.0.0.0/8 + 256"}, - {"index": 5, "type": "exec", "content": "engine.last_result.has_symbols"} - ] -} -``` - -### Debugging de Tokenización - -**Problema**: La expresión `192.168.1.x + 16#FF` no se tokeniza correctamente. - -```json -{ - "queries": [ - {"index": 0, "type": "exec", "content": "engine.parser.process_expression('192.168.1.x + 16#FF')"}, - {"index": 1, "type": "input", "content": "192.168.1.x + 16#FF"}, - {"index": 2, "type": "exec", "content": "engine.parser.get_tokenization_info()"}, - {"index": 3, "type": "exec", "content": "len(engine.parser.tokenizer.tokenization_rules)"} - ] -} -``` - -### Debugging de Errores - -**Problema**: IP4Mask rechaza máscaras que deberían ser válidas. - -```json -{ - "queries": [ - {"index": 0, "type": "input", "content": "mask = 255.240.0.0"}, - {"index": 1, "type": "input", "content": "IP4Mask(mask)"}, - {"index": 2, "type": "exec", "content": "engine.last_result"}, - {"index": 3, "type": "input", "content": "IP4Mask(255.240.0.3)"}, - {"index": 4, "type": "exec", "content": "engine.last_result"} - ] -} -``` - -### Debugging de Estado del Motor - -**Problema**: Las variables no se están guardando correctamente. - -```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.symbol_table)"}, - {"index": 4, "type": "exec", "content": "list(engine.symbol_table.keys())"}, - {"index": 5, "type": "input", "content": "solve(z**2 + x, z)"}, - {"index": 6, "type": "exec", "content": "len(engine.equations)"} - ] -} -``` - ---- - -## Interpretación de Resultados - -### Estructura del Resultado - -```json -{ - "execution_info": { - "timestamp": "2025-06-05T18:25:22.256442Z", - "total_queries": 5, - "successful": 4, - "failed": 1 - }, - "results": [...] -} -``` - -### Resultado Individual (Query `input`) - -```json -{ - "index": 0, - "input": "10.1.1.1 + 1", - "output": "10.1.1.2", // Texto tal como se muestra en la app - "result_type": "FourBytes", // Tipo del objeto resultado - "success": true, - "error": null -} -``` - -### Resultado Individual (Query `exec`) - -```json -{ - "index": 1, - "input": "type(engine.last_result).__name__", - "output": "FourBytes", // String del resultado - "result_type": "str", // Tipo del resultado de la evaluación - "success": true, - "error": null, - "exec_result": "FourBytes" // Valor directo (serializado si es necesario) -} -``` - -### Resultado con Error - -```json -{ - "index": 2, - "input": "IP4Mask(255.240.0.3)", - "output": "None", - "result_type": "NoneType", - "success": false, - "error": "❌ Máscara inválida: 255.240.0.3..." -} -``` - ---- - -## Funciones de Inspección Útiles - -### Estado del Motor - -```python -"engine.symbol_table" # Variables actuales -"engine.last_result" # Último resultado -"engine.symbolic_mode" # ¿Modo simbólico activo? -"len(engine.equations)" # Cantidad de ecuaciones en el sistema -"engine.debug" # ¿Debug habilitado? -"list(engine.base_context.keys())[:10]" # Funciones disponibles (muestra 10) -``` - -### Información de Tipos - -```python -"engine.get_available_types()" # Info completa de tipos registrados -"list(engine.registered_types_info['registered_classes'].keys())" # Lista de tipos -"engine.registered_types_info['class_count']" # Cantidad de tipos registrados -"type(engine.last_result).__name__" # Tipo del último resultado -"hasattr(engine.last_result, 'has_symbols')" # ¿El resultado tiene símbolos? -``` - -### Tokenización y Parsing - -```python -"engine.parser.get_tokenization_info()" # Info completa de tokenización -"engine.parser.process_expression('test')" # Procesar expresión específica -"len(engine.parser.tokenizer.tokenization_rules)" # Cantidad de reglas -"engine._classify_line('x = 5')" # Clasificar tipo de línea -"engine._extract_variable_names('x + y')" # Extraer nombres de variables -``` - -### Análisis de Objetos Específicos - -```python -# Para FourBytes -"engine.last_result.original" # String original -"engine.last_result._numeric_value" # Valor numérico interno -"engine.last_result.has_symbols" # ¿Tiene símbolos? - -# Para IntBase -"engine.last_result.base" # Base numérica (10, 16, 8, 2) -"engine.last_result.value_str" # String del valor -"engine.last_result._symbols" # Símbolos detectados - -# Para IP4Mask -"engine.last_result.get_prefix_int()" # Prefijo como entero -"engine.last_result.is_valid()" # ¿Es máscara válida? -``` - ---- - -## Patrones de Debugging - -### 1. Debugging de Regresión - -**Cuándo usar**: Verificar que cambios no rompan funcionalidad existente. - -```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 + 10"}, - {"index": 3, "type": "exec", "content": "engine.last_result.base"}, - {"index": 4, "type": "input", "content": "IP4Mask(255.255.0.0)"}, - {"index": 5, "type": "exec", "content": "engine.last_result.get_prefix_int()"} - ] -} -``` - -### 2. Debugging de Nuevas Funcionalidades - -**Cuándo usar**: Verificar que nueva funcionalidad trabaja correctamente. - -```json -{ - "queries": [ - {"index": 0, "type": "input", "content": "NUEVA_FUNCIONALIDAD_AQUI"}, - {"index": 1, "type": "exec", "content": "type(engine.last_result)"}, - {"index": 2, "type": "exec", "content": "dir(engine.last_result)"}, - {"index": 3, "type": "exec", "content": "str(engine.last_result)"}, - {"index": 4, "type": "exec", "content": "engine.symbol_table"} - ] -} -``` - -### 3. Debugging de Performance - -**Cuándo usar**: Identificar operaciones lentas o problemáticas. - -```json -{ - "queries": [ - {"index": 0, "type": "exec", "content": "import time; start = time.time()"}, - {"index": 1, "type": "input", "content": "OPERACION_LENTA"}, - {"index": 2, "type": "exec", "content": "time.time() - start"}, - {"index": 3, "type": "exec", "content": "len(engine.symbol_table)"} - ] -} -``` - -### 4. Debugging de Comportamiento - -**Cuándo usar**: Verificar comportamiento específico de tipos o funciones. - -```json -{ - "queries": [ - {"index": 0, "type": "input", "content": "EXPRESION"}, - {"index": 1, "type": "exec", "content": "str(engine.last_result)"}, - {"index": 2, "type": "exec", "content": "repr(engine.last_result)"}, - {"index": 3, "type": "exec", "content": "type(engine.last_result).__name__"} - ] -} -``` - ---- - -## Comandos de Ejecución - -```bash -# Ejecución básica -python simple_debug.py mi_debug.json - -# Con archivo de salida específico -python simple_debug.py mi_debug.json --output resultados.json - -# Modo verboso (para ver progreso) -python simple_debug.py mi_debug.json --verbose -``` - ---- - -## Templates Existentes para Reutilizar - -### Casos Básicos -**Archivo**: `debug_templates/basic_test.json` -- Operaciones con FourBytes e IntBase -- Variables y SymPy básico - -### Testing de Errores -**Archivo**: `debug_templates/error_debug.json` -- Máscaras inválidas -- IPs fuera de rango -- Expresiones malformadas - -### Información de Contexto -**Archivo**: `debug_templates/context_debug.json` -- Estado completo del motor -- Tipos registrados -- Sistema de ecuaciones - -### Tokenización -**Archivo**: `debug_templates/tokenization_test.json` -- Debug del sistema de parsing -- Reglas de tokenización - ---- - -## Flujo de Resolución de Problemas - -### 1. Identificar el Problema -- ¿Es un error de evaluación? -- ¿Es un problema de tokenización? -- ¿Es un problema de estado del motor? - -### 2. Crear Query de Diagnóstico -- Usar query `input` para reproducir el problema -- Usar query `exec` para inspeccionar el estado - -### 3. Analizar Resultados -- Verificar `success: true/false` -- Examinar `error` si hay fallo -- Comparar `output` (texto de la app) con `exec_result` (valor interno) - -### 4. Iterar -- Crear nuevas queries basadas en hallazgos -- Profundizar en áreas problemáticas -- Verificar soluciones con queries adicionales - ---- - -## Ejemplo Completo de Debugging - -**Problema**: "La operación `192.168.1.x + 1` no funciona correctamente" - -### Paso 1: Crear archivo de debug - -```json -{ - "queries": [ - {"index": 0, "type": "input", "content": "192.168.1.x + 1"}, - {"index": 1, "type": "exec", "content": "type(engine.last_result).__name__"}, - {"index": 2, "type": "exec", "content": "engine.last_result.has_symbols"}, - {"index": 3, "type": "exec", "content": "engine.parser.process_expression('192.168.1.x + 1')"}, - {"index": 4, "type": "input", "content": "ip = 192.168.1.x"}, - {"index": 5, "type": "exec", "content": "type(engine.symbol_table['ip'])"}, - {"index": 6, "type": "input", "content": "ip.substitute(x=5)"} - ] -} -``` - -### Paso 2: Ejecutar -```bash -python simple_debug.py debug_problema.json -``` - -### Paso 3: Analizar resultados -- Verificar si `192.168.1.x + 1` se evalúa correctamente (campo `output`) -- Comprobar el tipo resultante con `exec` -- Verificar si tiene símbolos -- Examinar cómo se tokeniza la expresión -- Probar operaciones relacionadas - -Este flujo te permite identificar exactamente dónde está el problema y verificar la solución. - ---- - -## Consejos para LLMs - -1. **Siempre usa índices secuenciales** en las queries para facilitar la lectura -2. **Combina queries `input` y `exec`** para obtener contexto completo -3. **El campo `output` es texto tal como se muestra en la aplicación** -4. **El campo `exec_result` es el valor directo de la evaluación Python** -5. **Usa los templates existentes** como punto de partida -6. **Examina `success` y `error`** antes de analizar resultados -7. **Crea queries incrementales** que construyan sobre resultados anteriores -8. **NO intentes duplicar lógica de la aplicación** - usa solo llamadas directas - ---- - -## Referencia Rápida - -### Query Básica -```json -{"index": N, "type": "input|exec", "content": "CONTENIDO"} -``` - -### Comandos Esenciales -```bash -python simple_debug.py archivo.json # Ejecutar debug -python simple_debug.py archivo.json --verbose # Con detalles -python simple_debug.py archivo.json -o resultado.json # Salida específica -``` - -### Inspección Básica del Motor -```python -"engine.last_result" # Último resultado -"type(engine.last_result)" # Tipo del resultado -"engine.symbol_table" # Variables actuales -"engine.get_available_types()" # Tipos registrados -"engine.parser.get_tokenization_info()" # Info de parsing -``` - -### Análisis de Resultado -```python -"success": true/false # ¿Éxito? -"error": "mensaje" # Error si falla -"output": "resultado" # Texto tal como se muestra en la app -"exec_result": valor # Valor directo (exec queries) -``` - -### Templates Disponibles -- `debug_templates/basic_test.json` - Pruebas básicas -- `debug_templates/error_debug.json` - Testing de errores -- `debug_templates/context_debug.json` - Estado del motor -- `debug_templates/tokenization_test.json` - Debug de parsing - -### Workflow Típico -1. **Identificar problema** → Crear query `input` para reproducir -2. **Inspeccionar estado** → Añadir queries `exec` para diagnosticar -3. **Analizar resultados** → Examinar `success`, `error`, `output` -4. **Iterar** → Crear nuevas queries basadas en hallazgos - -**Principio clave**: texto → texto. La API no interpreta ni procesa, solo encapsula llamadas directas al motor existente. \ No newline at end of file diff --git a/context_debug_results.json b/context_debug_results.json deleted file mode 100644 index c334c60..0000000 --- a/context_debug_results.json +++ /dev/null @@ -1,135 +0,0 @@ -{ - "execution_info": { - "timestamp": "2025-06-05T18:02:09.698502Z", - "total_queries": 11, - "successful": 11, - "failed": 0, - "input_file": "debug_templates\\context_debug.json" - }, - "results": [ - { - "index": 0, - "input": "x = 5", - "output": "5", - "result_type": "Integer", - "success": true, - "error": null, - "display_class": "[Integer]" - }, - { - "index": 1, - "input": "y = x + 10", - "output": "15", - "result_type": "Integer", - "success": true, - "error": null, - "display_class": "[Integer]" - }, - { - "index": 2, - "input": "engine.symbol_table", - "output": "{'x': 5, 'y': 15}", - "result_type": "dict", - "success": true, - "error": null, - "exec_result": "{'x': 5, 'y': 15, 'z': [-sqrt(-b), sqrt(-b)]}" - }, - { - "index": 3, - "input": "len(engine.equations)", - "output": "2", - "result_type": "int", - "success": true, - "error": null, - "exec_result": 2 - }, - { - "index": 4, - "input": "list(engine.base_context.keys())[:10]", - "output": "['pi', 'e', 'I', 'oo', 'sin', 'cos', 'tan', 'asin', 'acos', 'atan']", - "result_type": "list", - "success": true, - "error": null, - "exec_result": [ - "pi", - "e", - "I", - "oo", - "sin", - "cos", - "tan", - "asin", - "acos", - "atan" - ] - }, - { - "index": 5, - "input": "engine.get_available_types()", - "output": "{'registered_classes': {'Bin': , 'Chr': , 'Dec': , 'FourBytes': , 'Hex': , 'IntBase': , 'IP4': , 'IP4Mask': , 'LaTeX': }, 'bracket_classes': ['bin', 'intbase', 'IP4', 'chr', 'IntBase', 'ip4', 'fourbytes', 'Chr', 'dec', 'latex', 'FourBytes', 'Hex', 'LaTeX', 'Dec', 'Bin', 'IP4Mask', 'hex', 'ip4mask'], 'total_context_entries': 64, 'helper_functions_count': 9}", - "result_type": "dict", - "success": true, - "error": null, - "exec_result": "{'registered_classes': {'Bin': , 'Chr': , 'Dec': , 'FourBytes': , 'Hex': , 'IntBase': , 'IP4': , 'IP4Mask': , 'LaTeX': }, 'bracket_classes': ['bin', 'intbase', 'IP4', 'chr', 'IntBase', 'ip4', 'fourbytes', 'Chr', 'dec', 'latex', 'FourBytes', 'Hex', 'LaTeX', 'Dec', 'Bin', 'IP4Mask', 'hex', 'ip4mask'], 'total_context_entries': 64, 'helper_functions_count': 9}" - }, - { - "index": 6, - "input": "list(engine.registered_types_info['registered_classes'].keys())", - "output": "['Bin', 'Chr', 'Dec', 'FourBytes', 'Hex', 'IntBase', 'IP4', 'IP4Mask', 'LaTeX']", - "result_type": "list", - "success": true, - "error": null, - "exec_result": [ - "Bin", - "Chr", - "Dec", - "FourBytes", - "Hex", - "IntBase", - "IP4", - "IP4Mask", - "LaTeX" - ] - }, - { - "index": 7, - "input": "engine.registered_types_info['class_count']", - "output": "9", - "result_type": "int", - "success": true, - "error": null, - "exec_result": 9 - }, - { - "index": 8, - "input": "z = solve(a**2 + b, a)", - "output": "[-sqrt(-b), sqrt(-b)]", - "result_type": "list", - "success": true, - "error": null, - "display_class": "[list]" - }, - { - "index": 9, - "input": "engine._classify_line('z = solve(a**2 + b, a)')", - "output": "assignment", - "result_type": "str", - "success": true, - "error": null, - "exec_result": "assignment" - }, - { - "index": 10, - "input": "engine._extract_variable_names('x + y + z')", - "output": "['x', 'y', 'z']", - "result_type": "list", - "success": true, - "error": null, - "exec_result": [ - "x", - "y", - "z" - ] - } - ] -} \ No newline at end of file diff --git a/custom_types/intbase_type.py b/custom_types/intbase_type.py index ae80927..016e1d1 100644 --- a/custom_types/intbase_type.py +++ b/custom_types/intbase_type.py @@ -14,7 +14,9 @@ class IntBase(ClassBase): def __init__(self, value_str, base=10): self.value_str = str(value_str) self.base = int(base) - self.has_symbols = bool(re.search(r'[a-zA-Z_]', self.value_str)) + + # CORREGIDO: Detectar símbolos considerando la base numérica + self.has_symbols = self._has_algebraic_symbols() if self.has_symbols: # Modo algebraico: mantener símbolos INTERNAMENTE, no convertir a SymPy aún @@ -32,9 +34,44 @@ class IntBase(ClassBase): except ValueError: raise ValueError(f"Valor inválido '{self.value_str}' para base {self.base}") + def _has_algebraic_symbols(self): + """ + Detecta si hay símbolos algebraicos reales, no dígitos válidos para la base + """ + # Definir dígitos válidos según la base + if self.base <= 10: + valid_digits = set(f"0123456789"[:self.base]) + else: + # Para bases > 10, usar 0-9 y A-Z hasta la base + valid_digits = set("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[:self.base]) + valid_digits.update(set("0123456789abcdefghijklmnopqrstuvwxyz"[:self.base])) + + # Verificar si hay caracteres que NO son dígitos válidos para esta base + for char in self.value_str: + if char not in valid_digits and char not in '()+-*/': # Permitir operadores básicos + # Si encontramos una letra que NO es dígito válido, es un símbolo + if re.match(r'[a-zA-Z_]', char): + return True + + return False + def _extract_symbols(self): - """Extrae símbolos de la cadena, manteniendo caracteres únicos""" - return list(set(re.findall(r'[a-zA-Z_][a-zA-Z0-9_]*', self.value_str))) + """Extrae símbolos algebraicos reales (no dígitos de la base)""" + # Definir dígitos válidos según la base + if self.base <= 10: + valid_digits = set(f"0123456789"[:self.base]) + else: + valid_digits = set("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[:self.base]) + valid_digits.update(set("0123456789abcdefghijklmnopqrstuvwxyz"[:self.base])) + + symbols = [] + for match in re.finditer(r'[a-zA-Z_][a-zA-Z0-9_]*', self.value_str): + symbol = match.group() + # Solo agregar si NO es completamente un dígito válido + if not all(c in valid_digits for c in symbol): + symbols.append(symbol) + + return list(set(symbols)) def substitute(self, **kwargs): """Sustitución NATIVA - mantiene como IntBase""" diff --git a/display_format_test_results.json b/display_format_test_results.json deleted file mode 100644 index 03c8e7f..0000000 --- a/display_format_test_results.json +++ /dev/null @@ -1,569 +0,0 @@ -{ - "execution_info": { - "timestamp": "2025-06-05T18:11:38.337776Z", - "total_queries": 12, - "successful": 12, - "failed": 0, - "input_file": "debug_templates\\display_format_test.json" - }, - "results": [ - { - "index": 0, - "input": "192.168.1.1 + 1", - "output": "192.168.1.2", - "result_type": "FourBytes", - "success": true, - "error": null, - "output_raw": { - "parts": [ - [ - "custom_type", - "192.168.1.2" - ], - [ - "class_hint", - "[FourBytes]" - ] - ], - "formatted_text": "[custom_type]192.168.1.2[class_hint][FourBytes]", - "tag_info": { - "error": { - "fg": "#ff6b6b", - "font": "bold" - }, - "result": { - "fg": "#abdbe3" - }, - "symbolic": { - "fg": "#82aaff" - }, - "numeric": { - "fg": "#c3e88d" - }, - "equation": { - "fg": "#c792ea" - }, - "info": { - "fg": "#ffcb6b" - }, - "comment": { - "fg": "#546e7a" - }, - "class_hint": { - "fg": "#888888" - }, - "type_hint": { - "fg": "#6a6a6a" - }, - "custom_type": { - "fg": "#f9a825" - }, - "hex": { - "fg": "#f9a825" - }, - "bin": { - "fg": "#4fc3f7" - }, - "ip": { - "fg": "#fff176" - }, - "date": { - "fg": "#ff8a80" - }, - "chr_type": { - "fg": "#80cbc4" - }, - "helper": { - "fg": "#ffd700", - "font": "italic" - } - } - }, - "display_class": "[FourBytes]" - }, - { - "index": 1, - "input": "engine.last_result", - "output": "192.168.1.2", - "result_type": "FourBytes", - "success": true, - "error": null, - "exec_result": "192.168.1.2" - }, - { - "index": 2, - "input": "16#FF00", - "output": "16#FF00", - "result_type": "IntBase", - "success": true, - "error": null, - "output_raw": { - "parts": [ - [ - "custom_type", - "16#FF00" - ], - [ - "class_hint", - "[IntBase]" - ] - ], - "formatted_text": "[custom_type]16#FF00[class_hint][IntBase]", - "tag_info": { - "error": { - "fg": "#ff6b6b", - "font": "bold" - }, - "result": { - "fg": "#abdbe3" - }, - "symbolic": { - "fg": "#82aaff" - }, - "numeric": { - "fg": "#c3e88d" - }, - "equation": { - "fg": "#c792ea" - }, - "info": { - "fg": "#ffcb6b" - }, - "comment": { - "fg": "#546e7a" - }, - "class_hint": { - "fg": "#888888" - }, - "type_hint": { - "fg": "#6a6a6a" - }, - "custom_type": { - "fg": "#f9a825" - }, - "hex": { - "fg": "#f9a825" - }, - "bin": { - "fg": "#4fc3f7" - }, - "ip": { - "fg": "#fff176" - }, - "date": { - "fg": "#ff8a80" - }, - "chr_type": { - "fg": "#80cbc4" - }, - "helper": { - "fg": "#ffd700", - "font": "italic" - } - } - }, - "display_class": "[IntBase]" - }, - { - "index": 3, - "input": "engine.last_result.base", - "output": "16", - "result_type": "int", - "success": true, - "error": null, - "exec_result": 16 - }, - { - "index": 4, - "input": "x = solve(a**2 - 4, a)", - "output": "[-2, 2]", - "result_type": "list", - "success": true, - "error": null, - "output_raw": { - "parts": [ - [ - "info", - "x = [-2, 2]" - ] - ], - "formatted_text": "[info]x = [-2, 2]", - "tag_info": { - "error": { - "fg": "#ff6b6b", - "font": "bold" - }, - "result": { - "fg": "#abdbe3" - }, - "symbolic": { - "fg": "#82aaff" - }, - "numeric": { - "fg": "#c3e88d" - }, - "equation": { - "fg": "#c792ea" - }, - "info": { - "fg": "#ffcb6b" - }, - "comment": { - "fg": "#546e7a" - }, - "class_hint": { - "fg": "#888888" - }, - "type_hint": { - "fg": "#6a6a6a" - }, - "custom_type": { - "fg": "#f9a825" - }, - "hex": { - "fg": "#f9a825" - }, - "bin": { - "fg": "#4fc3f7" - }, - "ip": { - "fg": "#fff176" - }, - "date": { - "fg": "#ff8a80" - }, - "chr_type": { - "fg": "#80cbc4" - }, - "helper": { - "fg": "#ffd700", - "font": "italic" - } - } - }, - "display_class": "[list]" - }, - { - "index": 5, - "input": "type(engine.symbol_table['x'])", - "output": "", - "result_type": "type", - "success": true, - "error": null, - "exec_result": "" - }, - { - "index": 6, - "input": "IP4Mask(255.255.255.0)", - "output": "255.255.255.0", - "result_type": "IP4Mask", - "success": true, - "error": null, - "output_raw": { - "parts": [ - [ - "custom_type", - "255.255.255.0" - ], - [ - "class_hint", - "[IP4Mask]" - ] - ], - "formatted_text": "[custom_type]255.255.255.0[class_hint][IP4Mask]", - "tag_info": { - "error": { - "fg": "#ff6b6b", - "font": "bold" - }, - "result": { - "fg": "#abdbe3" - }, - "symbolic": { - "fg": "#82aaff" - }, - "numeric": { - "fg": "#c3e88d" - }, - "equation": { - "fg": "#c792ea" - }, - "info": { - "fg": "#ffcb6b" - }, - "comment": { - "fg": "#546e7a" - }, - "class_hint": { - "fg": "#888888" - }, - "type_hint": { - "fg": "#6a6a6a" - }, - "custom_type": { - "fg": "#f9a825" - }, - "hex": { - "fg": "#f9a825" - }, - "bin": { - "fg": "#4fc3f7" - }, - "ip": { - "fg": "#fff176" - }, - "date": { - "fg": "#ff8a80" - }, - "chr_type": { - "fg": "#80cbc4" - }, - "helper": { - "fg": "#ffd700", - "font": "italic" - } - } - }, - "display_class": "[IP4Mask]" - }, - { - "index": 7, - "input": "engine.last_result.get_prefix_int()", - "output": "24", - "result_type": "int", - "success": true, - "error": null, - "exec_result": 24 - }, - { - "index": 8, - "input": "# Este es un comentario", - "output": "None", - "result_type": "NoneType", - "success": true, - "error": null, - "output_raw": { - "parts": [ - [ - "comment", - "# Este es un comentario" - ] - ], - "formatted_text": "[comment]# Este es un comentario", - "tag_info": { - "error": { - "fg": "#ff6b6b", - "font": "bold" - }, - "result": { - "fg": "#abdbe3" - }, - "symbolic": { - "fg": "#82aaff" - }, - "numeric": { - "fg": "#c3e88d" - }, - "equation": { - "fg": "#c792ea" - }, - "info": { - "fg": "#ffcb6b" - }, - "comment": { - "fg": "#546e7a" - }, - "class_hint": { - "fg": "#888888" - }, - "type_hint": { - "fg": "#6a6a6a" - }, - "custom_type": { - "fg": "#f9a825" - }, - "hex": { - "fg": "#f9a825" - }, - "bin": { - "fg": "#4fc3f7" - }, - "ip": { - "fg": "#fff176" - }, - "date": { - "fg": "#ff8a80" - }, - "chr_type": { - "fg": "#80cbc4" - }, - "helper": { - "fg": "#ffd700", - "font": "italic" - } - } - }, - "display_class": "[NoneType]" - }, - { - "index": 9, - "input": "invalid_expression", - "output": "invalid_expression", - "result_type": "Symbol", - "success": true, - "error": null, - "output_raw": { - "parts": [ - [ - "symbolic", - "invalid_expression" - ], - [ - "class_hint", - "[Sympy]" - ] - ], - "formatted_text": "[symbolic]invalid_expression[class_hint][Sympy]", - "tag_info": { - "error": { - "fg": "#ff6b6b", - "font": "bold" - }, - "result": { - "fg": "#abdbe3" - }, - "symbolic": { - "fg": "#82aaff" - }, - "numeric": { - "fg": "#c3e88d" - }, - "equation": { - "fg": "#c792ea" - }, - "info": { - "fg": "#ffcb6b" - }, - "comment": { - "fg": "#546e7a" - }, - "class_hint": { - "fg": "#888888" - }, - "type_hint": { - "fg": "#6a6a6a" - }, - "custom_type": { - "fg": "#f9a825" - }, - "hex": { - "fg": "#f9a825" - }, - "bin": { - "fg": "#4fc3f7" - }, - "ip": { - "fg": "#fff176" - }, - "date": { - "fg": "#ff8a80" - }, - "chr_type": { - "fg": "#80cbc4" - }, - "helper": { - "fg": "#ffd700", - "font": "italic" - } - } - }, - "display_class": "[Symbol]" - }, - { - "index": 10, - "input": "matriz = Matrix([[1, 2], [3, 4]])", - "output": "Matrix([[1, 2], [3, 4]])", - "result_type": "MutableDenseMatrix", - "success": true, - "error": null, - "output_raw": { - "parts": [ - [ - "info", - "matriz = Matrix([[1, 2], [3, 4]])" - ], - [ - "numeric", - "≈ Matrix([[1.00000000000000, 2.00000000000000], [3.00000000000000, 4.00000000000000]])" - ] - ], - "formatted_text": "[info]matriz = Matrix([[1, 2], [3, 4]]) [numeric]≈ Matrix([[1.00000000000000, 2.00000000000000], [3.00000000000000, 4.00000000000000]])", - "tag_info": { - "error": { - "fg": "#ff6b6b", - "font": "bold" - }, - "result": { - "fg": "#abdbe3" - }, - "symbolic": { - "fg": "#82aaff" - }, - "numeric": { - "fg": "#c3e88d" - }, - "equation": { - "fg": "#c792ea" - }, - "info": { - "fg": "#ffcb6b" - }, - "comment": { - "fg": "#546e7a" - }, - "class_hint": { - "fg": "#888888" - }, - "type_hint": { - "fg": "#6a6a6a" - }, - "custom_type": { - "fg": "#f9a825" - }, - "hex": { - "fg": "#f9a825" - }, - "bin": { - "fg": "#4fc3f7" - }, - "ip": { - "fg": "#fff176" - }, - "date": { - "fg": "#ff8a80" - }, - "chr_type": { - "fg": "#80cbc4" - }, - "helper": { - "fg": "#ffd700", - "font": "italic" - } - } - }, - "display_class": "[MutableDenseMatrix]" - }, - { - "index": 11, - "input": "engine.symbol_table['matriz'].shape", - "output": "(2, 2)", - "result_type": "tuple", - "success": true, - "error": null, - "exec_result": [ - 2, - 2 - ] - } - ] -} \ No newline at end of file diff --git a/error_debug_results.json b/error_debug_results.json deleted file mode 100644 index 6f6e4a7..0000000 --- a/error_debug_results.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "execution_info": { - "timestamp": "2025-06-05T18:27:08.607608Z", - "total_queries": 11, - "successful": 9, - "failed": 2, - "input_file": "debug_templates\\error_debug.json" - }, - "results": [ - { - "index": 0, - "input": "bad_mask = 255.240.0.3", - "output": "255.240.0.3", - "result_type": "FourBytes", - "success": true, - "error": null - }, - { - "index": 1, - "input": "IP4Mask(bad_mask)", - "output": "None", - "result_type": "NoneType", - "success": false, - "error": "\n❌ Máscara inválida: 255.240.0.3 (0xFFF00003)\n\n🔍 Análisis binario: 11111111.11110000.00000000.00000011\n Los bits deben ser contiguos: todos los 1s seguidos de todos los 0s\n\n✅ Ejemplos de máscaras válidas:\n /24 → 255.255.255.0 (11111111.11111111.11111111.00000000)\n /20 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n /16 → 255.255.0.0 (11111111.11111111.00000000.00000000)\n /12 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n\n💡 ¿Quizás querías usar 255.240.0.0 en lugar de 255.240.0.3?\n" - }, - { - "index": 2, - "input": "engine.last_result", - "output": "None", - "result_type": "NoneType", - "success": true, - "error": null, - "exec_result": null - }, - { - "index": 3, - "input": "invalid_ip = 300.1.1.1", - "output": "None", - "result_type": "NoneType", - "success": false, - "error": "Error en asignación: Elemento inválido: '300'" - }, - { - "index": 4, - "input": "hasattr(engine, 'last_error')", - "output": "False", - "result_type": "bool", - "success": true, - "error": null, - "exec_result": false - }, - { - "index": 5, - "input": "16#GG", - "output": "16", - "result_type": "Integer", - "success": true, - "error": null - }, - { - "index": 6, - "input": "engine.debug", - "output": "False", - "result_type": "bool", - "success": true, - "error": null, - "exec_result": false - }, - { - "index": 7, - "input": "divide_by_zero = 10/0", - "output": "zoo", - "result_type": "ComplexInfinity", - "success": true, - "error": null - }, - { - "index": 8, - "input": "engine.symbolic_mode", - "output": "True", - "result_type": "bool", - "success": true, - "error": null, - "exec_result": true - }, - { - "index": 9, - "input": "undefined_var + 5", - "output": "undefined_var + 5", - "result_type": "Add", - "success": true, - "error": null - }, - { - "index": 10, - "input": "list(engine.symbol_table.keys())", - "output": "['bad_mask', 'divide_by_zero']", - "result_type": "list", - "success": true, - "error": null, - "exec_result": [ - "bad_mask", - "divide_by_zero" - ] - } - ] -} \ No newline at end of file diff --git a/error_test_with_raw.json b/error_test_with_raw.json deleted file mode 100644 index c0ee0fa..0000000 --- a/error_test_with_raw.json +++ /dev/null @@ -1,491 +0,0 @@ -{ - "execution_info": { - "timestamp": "2025-06-05T18:11:18.061505Z", - "total_queries": 11, - "successful": 9, - "failed": 2, - "input_file": "debug_templates\\error_debug.json" - }, - "results": [ - { - "index": 0, - "input": "bad_mask = 255.240.0.3", - "output": "255.240.0.3", - "result_type": "FourBytes", - "success": true, - "error": null, - "output_raw": { - "parts": [ - [ - "info", - "bad_mask = 255.240.0.3" - ] - ], - "formatted_text": "[info]bad_mask = 255.240.0.3", - "tag_info": { - "error": { - "fg": "#ff6b6b", - "font": "bold" - }, - "result": { - "fg": "#abdbe3" - }, - "symbolic": { - "fg": "#82aaff" - }, - "numeric": { - "fg": "#c3e88d" - }, - "equation": { - "fg": "#c792ea" - }, - "info": { - "fg": "#ffcb6b" - }, - "comment": { - "fg": "#546e7a" - }, - "class_hint": { - "fg": "#888888" - }, - "type_hint": { - "fg": "#6a6a6a" - }, - "custom_type": { - "fg": "#f9a825" - }, - "hex": { - "fg": "#f9a825" - }, - "bin": { - "fg": "#4fc3f7" - }, - "ip": { - "fg": "#fff176" - }, - "date": { - "fg": "#ff8a80" - }, - "chr_type": { - "fg": "#80cbc4" - }, - "helper": { - "fg": "#ffd700", - "font": "italic" - } - } - }, - "display_class": "[FourBytes]" - }, - { - "index": 1, - "input": "IP4Mask(bad_mask)", - "output": "None", - "result_type": "NoneType", - "success": false, - "error": "\n❌ Máscara inválida: 255.240.0.3 (0xFFF00003)\n\n🔍 Análisis binario: 11111111.11110000.00000000.00000011\n Los bits deben ser contiguos: todos los 1s seguidos de todos los 0s\n\n✅ Ejemplos de máscaras válidas:\n /24 → 255.255.255.0 (11111111.11111111.11111111.00000000)\n /20 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n /16 → 255.255.0.0 (11111111.11111111.00000000.00000000)\n /12 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n\n💡 ¿Quizás querías usar 255.240.0.0 en lugar de 255.240.0.3?\n", - "output_raw": { - "parts": [ - [ - "error", - "Error: \n❌ Máscara inválida: 255.240.0.3 (0xFFF00003)\n\n🔍 Análisis binario: 11111111.11110000.00000000.00000011\n Los bits deben ser contiguos: todos los 1s seguidos de todos los 0s\n\n✅ Ejemplos de máscaras válidas:\n /24 → 255.255.255.0 (11111111.11111111.11111111.00000000)\n /20 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n /16 → 255.255.0.0 (11111111.11111111.00000000.00000000)\n /12 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n\n💡 ¿Quizás querías usar 255.240.0.0 en lugar de 255.240.0.3?\n" - ] - ], - "formatted_text": "[error]Error: \n❌ Máscara inválida: 255.240.0.3 (0xFFF00003)\n\n🔍 Análisis binario: 11111111.11110000.00000000.00000011\n Los bits deben ser contiguos: todos los 1s seguidos de todos los 0s\n\n✅ Ejemplos de máscaras válidas:\n /24 → 255.255.255.0 (11111111.11111111.11111111.00000000)\n /20 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n /16 → 255.255.0.0 (11111111.11111111.00000000.00000000)\n /12 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n\n💡 ¿Quizás querías usar 255.240.0.0 en lugar de 255.240.0.3?\n", - "tag_info": { - "error": { - "fg": "#ff6b6b", - "font": "bold" - }, - "result": { - "fg": "#abdbe3" - }, - "symbolic": { - "fg": "#82aaff" - }, - "numeric": { - "fg": "#c3e88d" - }, - "equation": { - "fg": "#c792ea" - }, - "info": { - "fg": "#ffcb6b" - }, - "comment": { - "fg": "#546e7a" - }, - "class_hint": { - "fg": "#888888" - }, - "type_hint": { - "fg": "#6a6a6a" - }, - "custom_type": { - "fg": "#f9a825" - }, - "hex": { - "fg": "#f9a825" - }, - "bin": { - "fg": "#4fc3f7" - }, - "ip": { - "fg": "#fff176" - }, - "date": { - "fg": "#ff8a80" - }, - "chr_type": { - "fg": "#80cbc4" - }, - "helper": { - "fg": "#ffd700", - "font": "italic" - } - } - }, - "display_class": "[NoneType]" - }, - { - "index": 2, - "input": "engine.last_result", - "output": "None", - "result_type": "NoneType", - "success": true, - "error": null, - "exec_result": null - }, - { - "index": 3, - "input": "invalid_ip = 300.1.1.1", - "output": "None", - "result_type": "NoneType", - "success": false, - "error": "Error en asignación: Elemento inválido: '300'", - "output_raw": { - "parts": [ - [ - "error", - "Error: Error en asignación: Elemento inválido: '300'" - ] - ], - "formatted_text": "[error]Error: Error en asignación: Elemento inválido: '300'", - "tag_info": { - "error": { - "fg": "#ff6b6b", - "font": "bold" - }, - "result": { - "fg": "#abdbe3" - }, - "symbolic": { - "fg": "#82aaff" - }, - "numeric": { - "fg": "#c3e88d" - }, - "equation": { - "fg": "#c792ea" - }, - "info": { - "fg": "#ffcb6b" - }, - "comment": { - "fg": "#546e7a" - }, - "class_hint": { - "fg": "#888888" - }, - "type_hint": { - "fg": "#6a6a6a" - }, - "custom_type": { - "fg": "#f9a825" - }, - "hex": { - "fg": "#f9a825" - }, - "bin": { - "fg": "#4fc3f7" - }, - "ip": { - "fg": "#fff176" - }, - "date": { - "fg": "#ff8a80" - }, - "chr_type": { - "fg": "#80cbc4" - }, - "helper": { - "fg": "#ffd700", - "font": "italic" - } - } - }, - "display_class": "[NoneType]" - }, - { - "index": 4, - "input": "hasattr(engine, 'last_error')", - "output": "False", - "result_type": "bool", - "success": true, - "error": null, - "exec_result": false - }, - { - "index": 5, - "input": "16#GG", - "output": "16", - "result_type": "Integer", - "success": true, - "error": null, - "output_raw": { - "parts": [ - [ - "symbolic", - "16" - ], - [ - "class_hint", - "[Integer]" - ] - ], - "formatted_text": "[symbolic]16[class_hint][Integer]", - "tag_info": { - "error": { - "fg": "#ff6b6b", - "font": "bold" - }, - "result": { - "fg": "#abdbe3" - }, - "symbolic": { - "fg": "#82aaff" - }, - "numeric": { - "fg": "#c3e88d" - }, - "equation": { - "fg": "#c792ea" - }, - "info": { - "fg": "#ffcb6b" - }, - "comment": { - "fg": "#546e7a" - }, - "class_hint": { - "fg": "#888888" - }, - "type_hint": { - "fg": "#6a6a6a" - }, - "custom_type": { - "fg": "#f9a825" - }, - "hex": { - "fg": "#f9a825" - }, - "bin": { - "fg": "#4fc3f7" - }, - "ip": { - "fg": "#fff176" - }, - "date": { - "fg": "#ff8a80" - }, - "chr_type": { - "fg": "#80cbc4" - }, - "helper": { - "fg": "#ffd700", - "font": "italic" - } - } - }, - "display_class": "[Integer]" - }, - { - "index": 6, - "input": "engine.debug", - "output": "False", - "result_type": "bool", - "success": true, - "error": null, - "exec_result": false - }, - { - "index": 7, - "input": "divide_by_zero = 10/0", - "output": "zoo", - "result_type": "ComplexInfinity", - "success": true, - "error": null, - "output_raw": { - "parts": [ - [ - "info", - "divide_by_zero = zoo" - ] - ], - "formatted_text": "[info]divide_by_zero = zoo", - "tag_info": { - "error": { - "fg": "#ff6b6b", - "font": "bold" - }, - "result": { - "fg": "#abdbe3" - }, - "symbolic": { - "fg": "#82aaff" - }, - "numeric": { - "fg": "#c3e88d" - }, - "equation": { - "fg": "#c792ea" - }, - "info": { - "fg": "#ffcb6b" - }, - "comment": { - "fg": "#546e7a" - }, - "class_hint": { - "fg": "#888888" - }, - "type_hint": { - "fg": "#6a6a6a" - }, - "custom_type": { - "fg": "#f9a825" - }, - "hex": { - "fg": "#f9a825" - }, - "bin": { - "fg": "#4fc3f7" - }, - "ip": { - "fg": "#fff176" - }, - "date": { - "fg": "#ff8a80" - }, - "chr_type": { - "fg": "#80cbc4" - }, - "helper": { - "fg": "#ffd700", - "font": "italic" - } - } - }, - "display_class": "[ComplexInfinity]" - }, - { - "index": 8, - "input": "engine.symbolic_mode", - "output": "True", - "result_type": "bool", - "success": true, - "error": null, - "exec_result": true - }, - { - "index": 9, - "input": "undefined_var + 5", - "output": "undefined_var + 5", - "result_type": "Add", - "success": true, - "error": null, - "output_raw": { - "parts": [ - [ - "symbolic", - "undefined_var + 5" - ], - [ - "class_hint", - "[Sympy]" - ], - [ - "numeric", - "≈ undefined_var + 5.0" - ] - ], - "formatted_text": "[symbolic]undefined_var + 5[class_hint][Sympy] [numeric]≈ undefined_var + 5.0", - "tag_info": { - "error": { - "fg": "#ff6b6b", - "font": "bold" - }, - "result": { - "fg": "#abdbe3" - }, - "symbolic": { - "fg": "#82aaff" - }, - "numeric": { - "fg": "#c3e88d" - }, - "equation": { - "fg": "#c792ea" - }, - "info": { - "fg": "#ffcb6b" - }, - "comment": { - "fg": "#546e7a" - }, - "class_hint": { - "fg": "#888888" - }, - "type_hint": { - "fg": "#6a6a6a" - }, - "custom_type": { - "fg": "#f9a825" - }, - "hex": { - "fg": "#f9a825" - }, - "bin": { - "fg": "#4fc3f7" - }, - "ip": { - "fg": "#fff176" - }, - "date": { - "fg": "#ff8a80" - }, - "chr_type": { - "fg": "#80cbc4" - }, - "helper": { - "fg": "#ffd700", - "font": "italic" - } - } - }, - "display_class": "[Add]" - }, - { - "index": 10, - "input": "list(engine.symbol_table.keys())", - "output": "['bad_mask', 'divide_by_zero']", - "result_type": "list", - "success": true, - "error": null, - "exec_result": [ - "bad_mask", - "divide_by_zero" - ] - } - ] -} \ No newline at end of file diff --git a/hybrid_calc_history.txt b/hybrid_calc_history.txt index 20b4b22..4b60771 100644 --- a/hybrid_calc_history.txt +++ b/hybrid_calc_history.txt @@ -1,12 +1,12 @@ -mask=255.240.0.0 -mask.ToHex() -ip=10.1.1.x -10.1.1.1 + 1 -ip +a = x + 5 +x=? +solve(x) -IP4(10.1.1.4) -IP4Mask(mask) -IP4Mask(255.255.0.0) +m=t+u * 5 -bad_mask = 255.240.0.3 \ No newline at end of file +t=4 +m=3 +u=? + +u \ No newline at end of file diff --git a/main_evaluation.py b/main_evaluation.py index b786f3f..97cbd19 100644 --- a/main_evaluation.py +++ b/main_evaluation.py @@ -1,5 +1,6 @@ """ Motor de evaluación híbrida INTEGRADO con el sistema de auto-descubrimiento de tipos +VERSIÓN CORREGIDA siguiendo principios de Guía_Base.md """ import sympy from sympy import symbols, Symbol, sympify, solve, Eq, simplify @@ -27,7 +28,7 @@ from tl_popup import PlotResult class HybridEvaluationEngine: """ Motor de evaluación híbrida que combina SymPy con clases especializadas - VERSIÓN INTEGRADA con auto-descubrimiento de tipos + VERSIÓN CORREGIDA siguiendo principios de Guía_Base.md """ def __init__(self, auto_discover_types: bool = True, types_directory: str = "custom_types"): @@ -49,9 +50,9 @@ class HybridEvaluationEngine: self.registered_types_info = {} self.helper_functions = [] - # NUEVA CONFIGURACIÓN: Modo simbólico + # NUEVA CONFIGURACIÓN: Modo simbólico según Guía Base self.symbolic_mode = True # Por defecto, mantener forma simbólica - self.show_numeric_approximation = True # Mostrar aproximación numérica + self.show_numeric_approximation = True # Mostrar aproximación numérica cuando es útil self.keep_symbolic_fractions = True # Mantener fracciones como 4/5 self.auto_simplify = False # No simplificar automáticamente @@ -106,7 +107,7 @@ class HybridEvaluationEngine: 'integrate': sympy.integrate, 'limit': sympy.limit, 'series': sympy.series, - 'solve': sympy.solve, + 'solve': self._smart_solve, 'simplify': sympy.simplify, 'expand': sympy.expand, 'factor': sympy.factor, @@ -198,67 +199,83 @@ class HybridEvaluationEngine: print("✅ Tipos recargados") def get_available_types(self) -> Dict[str, Any]: - """Retorna información sobre los tipos disponibles""" - return { - 'registered_classes': self.registered_types_info.get('registered_classes', {}), - 'bracket_classes': list(self.registered_types_info.get('bracket_classes', set())), - 'total_context_entries': len(self.base_context), - 'helper_functions_count': len(self.helper_functions) - } + """Retorna información completa sobre tipos disponibles""" + return self.registered_types_info def get_type_help(self, type_name: str) -> Optional[str]: """Obtiene ayuda para un tipo específico""" - # Buscar en clases registradas - registered_classes = self.registered_types_info.get('registered_classes', {}) - if type_name in registered_classes: - cls = registered_classes[type_name] + # Buscar la clase en el registro + classes = self.registered_types_info.get('registered_classes', {}) + if type_name in classes: + cls = classes[type_name] if hasattr(cls, 'Helper'): - return cls.Helper(type_name) - + return cls.Helper("") return None def _create_plot_placeholder(self, *args, **kwargs): - """Crear placeholder para plots que será manejado por resultados interactivos""" - if self.debug: - print(f"🎯 Creando PlotResult con args: {args}, kwargs: {kwargs}") - return PlotResult('plot', args, kwargs) + """Crea un resultado interactivo para plot""" + return PlotResult("plot", args, kwargs) def _create_plot3d_placeholder(self, *args, **kwargs): - """Crear placeholder para plots 3D""" - if self.debug: - print(f"🎯 Creando PlotResult 3D con args: {args}, kwargs: {kwargs}") - return PlotResult('plot3d', args, kwargs) + """Crea un resultado interactivo para plot3d""" + return PlotResult("plot3d", args, kwargs) def _help_function(self, obj=None): - """Función de ayuda integrada que usa el sistema de helpers""" + """Función de ayuda personalizada""" if obj is None: - return "Ayuda disponible. Use help(función) para ayuda específica." + return "Ayuda general disponible. Usa help(objeto) para ayuda específica." - # Primero intentar con el objeto directamente - if hasattr(obj, '__doc__') and obj.__doc__: - return obj.__doc__ - elif hasattr(obj, 'Helper'): - return obj.Helper("") - - # Luego buscar en helpers registrados - obj_name = getattr(obj, '__name__', str(obj)) + # Si es un tipo personalizado, buscar en helpers + obj_type = type(obj).__name__ for helper_func in self.helper_functions: try: - help_result = helper_func(obj_name) - if help_result: - return help_result + help_text = helper_func(obj_type) + if help_text and help_text.strip(): + return help_text except: continue - return f"No hay ayuda disponible para {obj}" + # Fallback a ayuda de Python + import pydoc + return pydoc.render_doc(obj, renderer=pydoc.plaintext) - # ========== RESTO DE MÉTODOS EXISTENTES ========== - # (Los métodos de evaluación permanecen igual) + def _smart_solve(self, *args, **kwargs): + """ + Función solve inteligente que usa nuestro sistema de ecuaciones + cuando es apropiado, o llama a sympy.solve en otros casos + """ + try: + # Caso 1: solve(variable) → usar nuestro sistema + if len(args) == 1 and isinstance(args[0], (sympy.Symbol, str)): + var_name = str(args[0]) + return self._solve_variable_in_system(var_name) + + # Caso 2: solve(ecuacion, variable) → usar sympy.solve directamente + elif len(args) == 2: + return sympy.solve(*args, **kwargs) + + # Caso 3: solve(lista_ecuaciones, lista_variables) → usar sympy.solve + elif len(args) >= 1 and isinstance(args[0], (list, tuple)): + return sympy.solve(*args, **kwargs) + + # Caso 4: solve() sin argumentos → resolver todas las variables del sistema + elif len(args) == 0: + return self.solve_system() + + # Otros casos → usar sympy.solve directamente + else: + return sympy.solve(*args, **kwargs) + + except Exception as e: + if self.debug: + print(f"⚠️ Error en _smart_solve: {e}") + # Fallback a sympy.solve + return sympy.solve(*args, **kwargs) def evaluate_line(self, line: str) -> 'EvaluationResult': """ Evalúa una línea de código y retorna el resultado - NUEVA LÓGICA: Priorizar asignaciones, intentar ecuaciones silenciosamente + VERSIÓN CORREGIDA siguiendo principios de Guía_Base.md """ try: # 1. Aplicar tokenización distribuida @@ -267,27 +284,21 @@ class HybridEvaluationEngine: if self.debug: print(f"Parse: '{line}' → '{parsed_line}'") - # 2. Clasificar tipo de línea - line_type = self._classify_line(parsed_line) + # 2. NUEVA LÓGICA: Detectar atajo =? + if self._is_solve_shortcut(line): + return self._evaluate_solve_shortcut(line) + + # 3. Clasificar tipo de línea según criterios de la Guía Base + line_type = self._classify_line(parsed_line, line) if line_type == "comment": return EvaluationResult(None, "comment", original_line=line) elif line_type == "assignment": - # NUEVA LÓGICA: Para asignaciones, también intentar agregar como ecuación silenciosamente - assignment_result = self._evaluate_assignment(parsed_line, line) - - # Intentar agregar como ecuación silenciosamente (sin mostrar errores) - if not assignment_result.is_error: - try: - self._add_equation_silently(line) - except: - pass # Ignorar errores de ecuación - - return assignment_result + return self._evaluate_assignment(parsed_line, line) elif line_type == "equation": return self._evaluate_equation_addition(parsed_line, line) - # 3. Evaluación SymPy + # 4. Evaluación SymPy con evaluación numérica automática return self._evaluate_sympy_expression(parsed_line, line_type, line) except Exception as e: @@ -297,137 +308,154 @@ class HybridEvaluationEngine: original_line=line ) - def _classify_line(self, parsed_line: str) -> str: - """Clasifica el tipo de línea después del parsing""" + def _is_solve_shortcut(self, line: str) -> bool: + """Detecta el atajo variable=? según Guía Base""" + # Patrón: variable=? + pattern = r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*\?\s*$' + return bool(re.match(pattern, line.strip())) + + def _evaluate_solve_shortcut(self, line: str) -> 'EvaluationResult': + """Evalúa el atajo variable=? como solve(variable)""" + try: + # Extraer variable del patrón variable=? + pattern = r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*\?\s*$' + match = re.match(pattern, line.strip()) + if not match: + raise ValueError("Formato inválido para =?") + + var_name = match.group(1) + + # Crear llamada a solve + solve_expression = f"solve({var_name})" + + # Evaluar usando _solve_variable_in_system directamente + result = self._solve_variable_in_system(var_name) + + return EvaluationResult( + result, "solve_result", + symbolic_result=result, + info=f"Resolviendo {var_name} en el sistema de ecuaciones", + original_line=line + ) + + except Exception as e: + return EvaluationResult( + None, "error", + error=f"Error en solve shortcut: {e}", + original_line=line + ) + + def _classify_line(self, parsed_line: str, original_line: str) -> str: + """ + Clasifica el tipo de línea según criterios de la Guía Base - # Simplificado: priorizar asignaciones, ser menos estricto - if self._is_assignment(parsed_line): - return "assignment" - elif self._is_standalone_equation(parsed_line): - return "equation" - elif not parsed_line or parsed_line.strip().startswith('#'): + Criterios: + - Asignación: variable = expresión (solo variables simples) + - Ecuación: contiene = Y NO es asignación simple O contiene operadores de comparación + - Comentario: línea vacía o con # + - Expresión: todo lo demás + """ + + # Comentarios + if not parsed_line or parsed_line.strip().startswith('#'): return "comment" - else: - return "expression" + + # Verificar si contiene operadores de ecuación + has_equals = '=' in parsed_line + has_comparison = any(op in parsed_line for op in ['==', '!=', '<=', '>=', '<', '>']) + + if has_comparison: + return "equation" + + if has_equals: + # Verificar si es asignación simple según Guía Base + if self._is_simple_assignment(parsed_line): + return "assignment" + else: + # Es una ecuación (estructura algebraica en ambos lados) + return "equation" + + return "expression" - def _is_assignment(self, line: str) -> bool: + def _is_simple_assignment(self, line: str) -> bool: """ - Detecta si una línea es una asignación de variable - NUEVA LÓGICA: Priorizar asignaciones, ser menos estricto + Detecta asignaciones simples según Guía Base: + - variable = expresión + - Solo un = (no ==, !=, etc.) + - Lado izquierdo es un identificador válido de Python """ try: - # Pattern: variable = expresión (que no sea comparación) - if '=' in line and not any(op in line for op in ['==', '!=', '<=', '>=']): - # Verificar que sea una asignación válida de Python - parts = line.split('=', 1) - if len(parts) == 2: - var_part = parts[0].strip() - expr_part = parts[1].strip() - - # Verificar que la parte izquierda sea un identificador válido - if re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', var_part) and expr_part: - return True - - return False - except: - return False - - def _is_standalone_equation(self, line: str) -> bool: - """ - Determina si una línea es una ecuación standalone - NUEVA LÓGICA: Solo ecuaciones matemáticas obvias, no asignaciones - """ - try: - # Primero verificar si contiene '=' simple - if '=' not in line or any(op in line for op in ['==', '!=', '<=', '>=']): + # Verificar que solo tiene un = y no es comparación + if line.count('=') != 1 or any(op in line for op in ['==', '!=', '<=', '>=']): return False - # NUEVA LÓGICA: Si ya fue clasificada como asignación, NO es ecuación - if self._is_assignment(line): + parts = line.split('=', 1) + if len(parts) != 2: return False - - # Solo considerar como ecuación si tiene estructura matemática compleja - # Por ejemplo: expressions con funciones matemáticas, símbolos algebraicos, etc. - if any(pattern in line for pattern in ['sin(', 'cos(', 'log(', 'sqrt(', 'diff(', 'integrate(']): - return True - # O si contiene múltiples variables en un formato algebraico - # Básicamente, ecuaciones que NO son asignaciones simples - return False + var_part = parts[0].strip() + expr_part = parts[1].strip() + + # Verificar que la parte izquierda sea un identificador válido + if not re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', var_part): + return False + + # Verificar que la parte derecha no esté vacía + if not expr_part: + return False + + return True except: return False def _evaluate_assignment(self, parsed_line: str, original_line: str) -> 'EvaluationResult': - """Maneja la asignación de variables""" + """ + Evalúa asignación de variable según Guía Base + - Guarda el valor simbólico + - Si contiene variables no definidas, también agrega como ecuación implícita + - Proporciona evaluación numérica cuando es útil + """ try: - # ARREGLO: Transformar asignación en llamada a _assign_variable - # parsed_line es algo como: mask=FourBytes("255.240.0.3") - # Necesitamos convertirlo en: _assign_variable("mask", FourBytes("255.240.0.3")) - - if '=' in parsed_line: - parts = parsed_line.split('=', 1) - var_name = parts[0].strip() - expr_part = parts[1].strip() - - # Crear llamada a _assign_variable - assign_call = f'_assign_variable("{var_name}", {expr_part})' - - if self.debug: - print(f"🔧 Transformando asignación: '{parsed_line}' → '{assign_call}'") - - # Ejecutar la asignación transformada - result = self._eval_in_context(assign_call) - - # Obtener el valor asignado - assigned_value = self.symbol_table.get(var_name) - else: - # Fallback: si no hay '=' algo está mal + if '=' not in parsed_line: raise ValueError(f"Línea de asignación sin '=': {parsed_line}") + parts = parsed_line.split('=', 1) + var_name = parts[0].strip() + expr_part = parts[1].strip() - # Generar evaluación numérica si está configurado para mostrarla - numeric_result = None - if self.show_numeric_approximation and hasattr(assigned_value, 'evalf'): + # Evaluar la expresión en el contexto actual + result = self._eval_in_context(expr_part) + + # Guardar en symbol_table + self.symbol_table[var_name] = result + self.last_result = result + + # NUEVO: Si la asignación contiene símbolos no definidos, agregarla como ecuación implícita + if self._assignment_has_undefined_symbols(var_name, result): try: - numeric_eval = assigned_value.evalf() - # MEJORADO: Solo mostrar aproximación si es realmente útil - if hasattr(assigned_value, 'is_Rational') and assigned_value.is_Rational: - # Es una fracción racional, mostrar aproximación decimal - numeric_result = numeric_eval - elif hasattr(assigned_value, 'is_Integer') and assigned_value.is_Integer: - # Es un entero SymPy, no mostrar aproximación - numeric_result = None - elif hasattr(assigned_value, 'is_number') and assigned_value.is_number: - # Es un número, verificar si la aproximación es diferente significativamente - try: - if abs(float(numeric_eval) - float(assigned_value)) > 1e-10: - numeric_result = numeric_eval - except: - # Si no se puede comparar, mostrar solo si el string es diferente - if str(numeric_eval) != str(assigned_value): - numeric_result = numeric_eval - elif numeric_eval != assigned_value: - # Para otros casos, mostrar si son diferentes - try: - # Intentar comparación numérica más robusta - if abs(float(numeric_eval) - float(assigned_value)) > 1e-10: - numeric_result = numeric_eval - except: - # Si la comparación falla, asumir que son diferentes solo si el string es diferente - if str(numeric_eval) != str(assigned_value): - numeric_result = numeric_eval - except Exception as e: + # Crear ecuación implícita: var_name = result + var_symbol = sympy.Symbol(var_name) + equation = sympy.Eq(var_symbol, result) + self.equations.append(equation) + if self.debug: - print(f"DEBUG: Error en evaluación numérica: {e}") - pass + print(f"🔗 Asignación con símbolos agregada como ecuación: {equation}") + + except Exception as eq_error: + if self.debug: + print(f"⚠️ No se pudo agregar asignación como ecuación: {eq_error}") + + # Generar evaluación numérica según Guía Base + numeric_result = self._generate_numeric_approximation(result) return EvaluationResult( - assigned_value, "assignment", + result, "assignment", symbolic_result=result, numeric_result=numeric_result, original_line=original_line ) + except Exception as e: return EvaluationResult( None, "error", @@ -435,25 +463,110 @@ class HybridEvaluationEngine: original_line=original_line ) - def _evaluate_equation_addition(self, parsed_line: str, original_line: str) -> 'EvaluationResult': - """Maneja la adición de ecuaciones al sistema""" + def _assignment_has_undefined_symbols(self, var_name: str, result: Any) -> bool: + """ + Determina si una asignación contiene símbolos no definidos + y por tanto debería agregarse como ecuación implícita + """ try: - # Ejecutar _add_equation - result = self._eval_in_context(parsed_line) + # Obtener símbolos libres del resultado + if hasattr(result, 'free_symbols'): + free_symbols = result.free_symbols + + # Verificar si hay símbolos que no están definidos en symbol_table + # (excluyendo el símbolo de la variable que se está asignando) + for symbol in free_symbols: + symbol_name = str(symbol) + if symbol_name != var_name and symbol_name not in self.symbol_table: + return True + # También verificar si el símbolo existe pero es solo un Symbol sin valor + elif symbol_name in self.symbol_table: + symbol_value = self.symbol_table[symbol_name] + if isinstance(symbol_value, sympy.Symbol) and str(symbol_value) == symbol_name: + return True + + return False + + except Exception as e: + if self.debug: + print(f"⚠️ Error verificando símbolos de asignación: {e}") + return False + + def _generate_numeric_approximation(self, result: Any) -> Optional[Any]: + """ + Genera aproximación numérica según Guía Base: + - Solo mostrar cuando la representación string difiere del resultado algebraico + """ + if not self.show_numeric_approximation: + return None + + try: + # Intentar evaluación numérica + if hasattr(result, 'evalf'): + numeric_eval = result.evalf() + + # Solo mostrar si el string es diferente + if str(numeric_eval) != str(result): + return numeric_eval + elif hasattr(result, '__float__'): + try: + numeric_eval = float(result) + if str(numeric_eval) != str(result): + return numeric_eval + except: + pass + + except Exception as e: + if self.debug: + print(f"DEBUG: Error en evaluación numérica: {e}") + + return None + + def _evaluate_equation_addition(self, parsed_line: str, original_line: str) -> 'EvaluationResult': + """ + Evalúa y agrega ecuación al sistema según Guía Base + - Se agrega automáticamente al sistema de ecuaciones + """ + try: + # Detectar si es ecuación con = o con operadores de comparación + if '=' in parsed_line and '==' not in parsed_line: + # Ecuación con = → convertir a Eq() + parts = parsed_line.split('=', 1) + left_expr = parts[0].strip() + right_expr = parts[1].strip() + + # Evaluar ambos lados + left_val = self._eval_in_context(left_expr) + right_val = self._eval_in_context(right_expr) + + # Crear ecuación SymPy + equation = sympy.Eq(left_val, right_val) + + else: + # Ecuación con operadores de comparación → evaluar directamente + equation = self._eval_in_context(parsed_line) + + # Agregar al sistema + self.equations.append(equation) + return EvaluationResult( - result, "equation_added", - symbolic_result=f"Ecuación agregada: {original_line}", + equation, "equation_added", + symbolic_result=equation, + info=f"Ecuación agregada al sistema: {equation}", original_line=original_line ) + except Exception as e: return EvaluationResult( None, "error", error=f"Error agregando ecuación: {e}", original_line=original_line ) - + def _evaluate_sympy_expression(self, expression: str, parse_info: str, original_line: str) -> 'EvaluationResult': - """Evalúa una expresión usando SymPy""" + """ + Evalúa una expresión usando SymPy con evaluación numérica automática + """ try: # Evaluar en contexto SymPy result = self._eval_in_context(expression) @@ -466,47 +579,25 @@ class HybridEvaluationEngine: # Actualizar last_result self.last_result = result - # Intentar evaluación numérica si está configurado para mostrarla - numeric_result = None - if self.show_numeric_approximation and hasattr(result, 'evalf'): - try: - numeric_eval = result.evalf() - # MEJORADO: Solo mostrar evaluación numérica si es realmente útil - if hasattr(result, 'is_Integer') and result.is_Integer: - # Es un entero SymPy, no mostrar aproximación - numeric_result = None - elif hasattr(result, 'is_Rational') and result.is_Rational: - # Es una fracción racional, mostrar aproximación decimal - numeric_result = numeric_eval - elif hasattr(result, 'is_number') and result.is_number: - # Es un número, verificar si la aproximación es diferente significativamente - try: - if abs(float(numeric_eval) - float(result)) > 1e-10: - numeric_result = numeric_eval - except: - # Si no se puede comparar, mostrar solo si el string es diferente - if str(numeric_eval) != str(result): - numeric_result = numeric_eval - elif (str(numeric_eval) != str(result) and numeric_eval != result and - not (isinstance(result, (int, float)) or - (hasattr(result, 'is_number') and result.is_number and - hasattr(result, 'is_Integer') and result.is_Integer))): - numeric_result = numeric_eval - except: - pass + # Generar evaluación numérica automática según Guía Base + numeric_result = self._generate_numeric_approximation(result) + + # Determinar tipo de resultado + result_type = type(result).__name__ return EvaluationResult( - result, "expression", + result, result_type, symbolic_result=result, numeric_result=numeric_result, parse_info=parse_info, original_line=original_line ) - except NameError as e: - # Intentar crear símbolos automáticamente - return self._handle_undefined_symbols(expression, original_line, e) except Exception as e: + # Manejar símbolos no definidos + if "undefined" in str(e).lower() or "not defined" in str(e).lower(): + return self._handle_undefined_symbols(expression, original_line, e) + return EvaluationResult( None, "error", error=str(e), @@ -719,12 +810,87 @@ class HybridEvaluationEngine: return None def _get_full_context(self) -> Dict[str, Any]: - """Obtiene el contexto completo para evaluación""" + """Obtiene el contexto completo para evaluación con sustituciones automáticas""" context = self.base_context.copy() - context.update(self.symbol_table) + + # NUEVO: Aplicar sustituciones automáticas en expresiones simbólicas + substituted_table = self._apply_automatic_substitutions() + context.update(substituted_table) context['last'] = self.last_result return context + def _apply_automatic_substitutions(self) -> Dict[str, Any]: + """ + Aplica sustituciones automáticas a expresiones simbólicas usando valores conocidos + """ + substituted = {} + + # Separar valores numéricos de expresiones simbólicas + numeric_values = {} + symbolic_expressions = {} + + for var_name, value in self.symbol_table.items(): + if hasattr(value, 'free_symbols') and value.free_symbols: + # Es una expresión simbólica + symbolic_expressions[var_name] = value + else: + # Es un valor numérico o constante + numeric_values[var_name] = value + substituted[var_name] = value + + # Crear diccionario de sustituciones con valores numéricos + substitutions = {} + for var_name, value in numeric_values.items(): + if hasattr(value, 'evalf'): + # Es un valor SymPy, conservar forma exacta para sustitución + substitutions[sympy.Symbol(var_name)] = value + elif isinstance(value, (int, float)): + # Es un valor numérico Python + substitutions[sympy.Symbol(var_name)] = value + else: + # Otros tipos, intentar convertir a SymPy + try: + substitutions[sympy.Symbol(var_name)] = sympy.sympify(value) + except: + substitutions[sympy.Symbol(var_name)] = value + + # Aplicar sustituciones a expresiones simbólicas + for var_name, expr in symbolic_expressions.items(): + try: + if substitutions: + # Aplicar todas las sustituciones conocidas + substituted_expr = expr.subs(substitutions) + + # Si después de las sustituciones no quedan símbolos libres, evaluar numéricamente + if hasattr(substituted_expr, 'free_symbols') and not substituted_expr.free_symbols: + try: + # Evaluar completamente la expresión + evaluated = substituted_expr.evalf() if hasattr(substituted_expr, 'evalf') else substituted_expr + substituted[var_name] = evaluated + + if self.debug: + print(f"🔄 Sustitución automática: {var_name} = {expr} → {evaluated}") + except: + # Si falla la evaluación, usar la expresión sustituida + substituted[var_name] = substituted_expr + else: + # Aún hay símbolos libres, usar expresión parcialmente sustituida + substituted[var_name] = substituted_expr + + if self.debug and substituted_expr != expr: + print(f"🔄 Sustitución parcial: {var_name} = {expr} → {substituted_expr}") + else: + # No hay sustituciones disponibles, mantener expresión original + substituted[var_name] = expr + + except Exception as e: + if self.debug: + print(f"⚠️ Error en sustitución automática para {var_name}: {e}") + # En caso de error, mantener expresión original + substituted[var_name] = expr + + return substituted + def _assign_variable(self, var_name: str, expression) -> str: """Asigna un valor a una variable""" try: diff --git a/test_fixed_issues.py b/test_fixed_issues.py deleted file mode 100644 index 72d1406..0000000 --- a/test_fixed_issues.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python3 -""" -Prueba de las correcciones: -1. FourBytes + int mantiene tipo FourBytes -2. IP4Mask con mejor mensaje de error para máscaras inválidas -""" - -if __name__ == "__main__": - print("🔧 PROBANDO CORRECCIONES") - print("=" * 50) - - try: - from main_evaluation import HybridEvaluationEngine - - # Crear motor - engine = HybridEvaluationEngine() - - print("📋 Reproduciendo casos del usuario:\n") - - # 1. Reproducir secuencia completa - print("1. mask=255.240.0.3") - result1 = engine.evaluate_line("mask=255.240.0.3") - print(f" → {result1.result} (tipo: {type(result1.result).__name__})") - - print(f"\n2. mask") - result2 = engine.evaluate_line("mask") - print(f" → {result2.result} (tipo: {type(result2.result).__name__})") - - print(f"\n3. ip=10.1.1.x") - result3 = engine.evaluate_line("ip=10.1.1.x") - print(f" → {result3.result} (tipo: {type(result3.result).__name__})") - - print(f"\n4. 10.1.1.1 + 1 (DEBE DEVOLVER FourBytes)") - result4 = engine.evaluate_line("10.1.1.1 + 1") - print(f" → {result4.result} (tipo: {type(result4.result).__name__})") - - if hasattr(result4.result, 'original'): - print(f" → Valor original: {result4.result.original}") - - print(f"\n5. ip") - result5 = engine.evaluate_line("ip") - print(f" → {result5.result} (tipo: {type(result5.result).__name__})") - - print(f"\n6. IP4(10.1.1.4)") - result6 = engine.evaluate_line("IP4(10.1.1.4)") - print(f" → {result6.result} (tipo: {type(result6.result).__name__})") - - print(f"\n7. IP4Mask(mask) - DEBE DAR ERROR INFORMATIVO") - result7 = engine.evaluate_line("IP4Mask(mask)") - if result7.is_error: - print(f" ❌ Error (como esperado): {result7.error}") - else: - print(f" → {result7.result} (tipo: {type(result7.result).__name__})") - - print(f"\n8. Probando con máscara válida: IP4Mask(255.255.0.0)") - result8 = engine.evaluate_line("IP4Mask(255.255.0.0)") - print(f" → {result8.result} (tipo: {type(result8.result).__name__})") - - print(f"\n🎯 RESUMEN DE RESULTADOS:") - print(f" ✅ mask asignada como FourBytes: {type(result1.result).__name__ == 'FourBytes'}") - print(f" ✅ 10.1.1.1 + 1 mantiene FourBytes: {type(result4.result).__name__ == 'FourBytes'}") - print(f" ✅ IP4Mask da error informativo para máscara inválida: {result7.is_error}") - print(f" ✅ IP4Mask funciona con máscara válida: {not result8.is_error}") - - # Verificación adicional de aritmética FourBytes - print(f"\n🔍 VERIFICACIÓN ADICIONAL DE ARITMÉTICA:") - - # Test más casos aritméticos - test_cases = [ - "192.168.1.1 + 5", - "10.0.0.0 + 256", - "255.255.255.255 - 1" - ] - - for case in test_cases: - result = engine.evaluate_line(case) - print(f" {case} → {result.result} ({type(result.result).__name__})") - - except Exception as e: - print(f"Error: {e}") - import traceback - traceback.print_exc() \ No newline at end of file diff --git a/test_fourbytes_add.py b/test_fourbytes_add.py deleted file mode 100644 index a2c4e3f..0000000 --- a/test_fourbytes_add.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python3 -""" -Prueba específica de FourBytes + int -""" - -if __name__ == "__main__": - print("🔍 PROBANDO FourBytes + int") - print("=" * 40) - - try: - from main_evaluation import HybridEvaluationEngine - - # Crear motor - engine = HybridEvaluationEngine() - - # 1. Crear FourBytes directamente - print("1. Creando FourBytes directamente:") - FourBytes = engine.base_context.get('FourBytes') - print(f" FourBytes: {FourBytes}") - - fb = FourBytes("10.1.1.1") - print(f" fb = {fb} (tipo: {type(fb)})") - print(f" fb._numeric_value = {fb._numeric_value}") - - # 2. Probar suma directa - print(f"\n2. Suma directa en Python:") - result_direct = fb + 1 - print(f" fb + 1 = {result_direct} (tipo: {type(result_direct)})") - - # 3. Probar a través del motor de evaluación - print(f"\n3. Evaluación a través del motor:") - engine.symbol_table['test_fb'] = fb - - result_engine = engine.evaluate_line("test_fb + 1") - print(f" test_fb + 1 = {result_engine.result} (tipo: {type(result_engine.result)})") - - # 4. Probar tokenización + evaluación - print(f"\n4. Tokenización completa:") - result_tokenized = engine.evaluate_line("10.1.1.1 + 1") - print(f" 10.1.1.1 + 1 = {result_tokenized.result} (tipo: {type(result_tokenized.result)})") - - # 5. Verificar paso a paso qué pasa en la tokenización - print(f"\n5. Análisis paso a paso:") - from tl_bracket_parser import UniversalTokenizer - tokenizer = UniversalTokenizer() - tokenizer.debug = True - - tokenized = tokenizer.preprocess_tokens("10.1.1.1 + 1") - print(f" Tokenizado: {tokenized}") - - # 6. Evaluar el tokenizado directamente - print(f"\n6. Evaluando tokenizado directamente:") - try: - direct_eval = engine._eval_in_context(tokenized) - print(f" Resultado directo: {direct_eval} (tipo: {type(direct_eval)})") - except Exception as e: - print(f" Error: {e}") - - # 7. Probar operación manual paso a paso - print(f"\n7. Operación manual paso a paso:") - try: - fb_manual = engine._eval_in_context('FourBytes("10.1.1.1")') - print(f" FourBytes manual: {fb_manual} (tipo: {type(fb_manual)})") - - add_result = engine._eval_in_context('FourBytes("10.1.1.1") + 1') - print(f" Suma manual: {add_result} (tipo: {type(add_result)})") - except Exception as e: - print(f" Error en operación manual: {e}") - import traceback - traceback.print_exc() - - except Exception as e: - print(f"Error: {e}") - import traceback - traceback.print_exc() \ No newline at end of file