diff --git a/.doc/LLM_DEBUG_API_GUIDE.md b/.doc/LLM_DEBUG_API_GUIDE.md new file mode 100644 index 0000000..83a303a --- /dev/null +++ b/.doc/LLM_DEBUG_API_GUIDE.md @@ -0,0 +1,508 @@ +# 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. 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 + +### 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. + +```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. + +```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:01:28.644799Z", + "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", // Resultado básico + "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"} + } + } +} +``` + +### Resultado Individual (Query `exec`) + +```json +{ + "index": 1, + "input": "type(engine.last_result).__name__", + "output": "FourBytes", + "result_type": "str", + "success": true, + "error": null, + "exec_result": "FourBytes" // Resultado crudo del exec +} +``` + +### 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...", + "output_raw": { + "parts": [["error", "Error: ❌ Máscara inválida..."]], + "formatted_text": "[error]Error: ❌ Máscara inválida...", + "tag_info": {"error": {"fg": "#ff6b6b", "font": "bold"}} + } +} +``` + +--- + +## 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 Display/Formato + +**Cuándo usar**: Verificar cómo se muestra exactamente un resultado. + +```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__"} + ] +} +``` + +--- + +## 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 +# 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 + +### 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 +- Reglas de tokenización + +--- + +## Flujo de Resolución de Problemas + +### 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? + +### 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 +- Analizar `output_raw` para problemas de display +- Usar `exec_result` para inspección detallada + +### 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 +- Comprobar el tipo resultante +- 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. **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 +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. + +--- + +## 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" # Resultado básico +"output_raw": {...} # Formato exacto de la app +"exec_result": valor # Resultado crudo (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 +- `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 diff --git a/basic_test_results.json b/basic_test_results.json new file mode 100644 index 0000000..a4a4ef5 --- /dev/null +++ b/basic_test_results.json @@ -0,0 +1,339 @@ +{ + "execution_info": { + "timestamp": "2025-06-05T18:10:43.323812Z", + "total_queries": 8, + "successful": 8, + "failed": 0, + "input_file": "debug_templates\\basic_test.json" + }, + "results": [ + { + "index": 0, + "input": "10.1.1.1 + 1", + "output": "10.1.1.2", + "result_type": "FourBytes", + "success": true, + "error": null, + "output_raw": { + "parts": [ + [ + "custom_type", + "10.1.1.2" + ], + [ + "class_hint", + "[FourBytes]" + ] + ], + "formatted_text": "[custom_type]10.1.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": "type(engine.last_result).__name__", + "output": "FourBytes", + "result_type": "str", + "success": true, + "error": null, + "exec_result": "FourBytes" + }, + { + "index": 2, + "input": "16#FF", + "output": "16#FF", + "result_type": "IntBase", + "success": true, + "error": null, + "output_raw": { + "parts": [ + [ + "custom_type", + "16#FF" + ], + [ + "class_hint", + "[IntBase]" + ] + ], + "formatted_text": "[custom_type]16#FF[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": "mask = 255.255.0.0", + "output": "255.255.0.0", + "result_type": "FourBytes", + "success": true, + "error": null, + "output_raw": { + "parts": [ + [ + "info", + "mask = 255.255.0.0" + ] + ], + "formatted_text": "[info]mask = 255.255.0.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": "[FourBytes]" + }, + { + "index": 5, + "input": "engine.symbol_table", + "output": "{'mask': FourBytes('255.255.0.0')}", + "result_type": "dict", + "success": true, + "error": null, + "exec_result": "{'mask': FourBytes('255.255.0.0')}" + }, + { + "index": 6, + "input": "solve(x**2 + 1, x)", + "output": "[-I, I]", + "result_type": "list", + "success": true, + "error": null, + "output_raw": { + "parts": [ + [ + "result", + "[-I, I]" + ], + [ + "class_hint", + "[List]" + ] + ], + "formatted_text": "[result][-I, I][class_hint][List]", + "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": 7, + "input": "len(engine.symbol_table)", + "output": "1", + "result_type": "int", + "success": true, + "error": null, + "exec_result": 1 + } + ] +} \ No newline at end of file diff --git a/context_debug_results.json b/context_debug_results.json new file mode 100644 index 0000000..c334c60 --- /dev/null +++ b/context_debug_results.json @@ -0,0 +1,135 @@ +{ + "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/debug_templates/README.md b/debug_templates/README.md new file mode 100644 index 0000000..d5cf475 --- /dev/null +++ b/debug_templates/README.md @@ -0,0 +1,188 @@ +# Templates de Debug - Simple Debug API + +Esta carpeta contiene templates predefinidos para debuggear diferentes aspectos de la Calculadora MAV CAS. + +## Uso Básico + +```bash +# Ejecutar un template +python simple_debug.py debug_templates/basic_test.json + +# Con archivo de salida personalizado +python simple_debug.py debug_templates/basic_test.json --output mi_debug.json + +# Con información detallada +python simple_debug.py debug_templates/basic_test.json --verbose +``` + +## Templates Disponibles + +### `basic_test.json` +**Propósito**: Pruebas básicas de funcionalidad +- Operaciones con FourBytes (IPs) +- Operaciones con IntBase (números en diferentes bases) +- Variables y tabla de símbolos +- Funciones de SymPy + +### `tokenization_test.json` +**Propósito**: Debug del sistema de tokenización +- Análisis de expresiones complejas +- Información de reglas de tokenización +- Procesamiento de expressions mixtas (IP + hex) + +### `regression_test.json` +**Propósito**: Testing de regresión para casos específicos +- Verificación de tipos: FourBytes, IntBase, IP4Mask +- Operaciones simbólicas +- Substitución de variables + +### `error_debug.json` +**Propósito**: Debug de manejo de errores +- Máscaras IP inválidas +- IPs fuera de rango +- Números hexadecimales malformados +- División por cero +- Variables indefinidas + +### `context_debug.json` +**Propósito**: Debug del contexto y estado del motor +- Tabla de símbolos +- Tipos registrados +- Contexto base +- Sistema de ecuaciones + +### `display_format_test.json` +**Propósito**: Demostración de formatos de display y output_raw +- Diferentes tipos de resultados (IP, hex, símbolos, matrices) +- Comentarios y errores +- Comparación entre `output` y `output_raw` + +## Formato de Template + +Cada template es un archivo JSON con esta estructura: + +```json +{ + "queries": [ + { + "index": 0, + "type": "input", + "content": "10.1.1.1 + 1" + }, + { + "index": 1, + "type": "exec", + "content": "type(engine.last_result).__name__" + } + ] +} +``` + +### Tipos de Query + +- **`input`**: Evalúa expresiones como si las escribieras en la calculadora +- **`exec`**: Ejecuta código Python para inspeccionar el estado interno del motor + +## Casos de Uso Comunes + +### Debug de un Bug Específico +1. Copia `basic_test.json` a `mi_bug_debug.json` +2. Modifica las queries para reproducir el bug +3. Añade queries `exec` para inspeccionar el estado +4. Ejecuta: `python simple_debug.py mi_bug_debug.json` + +### Verificar una Nueva Funcionalidad +1. Crea un template con queries `input` que usen la nueva funcionalidad +2. Añade queries `exec` para verificar el estado interno +3. Compara resultados antes y después de los cambios + +### Testing de Regresión +1. Usa `regression_test.json` como base +2. Añade casos específicos que deben mantenerse funcionando +3. Ejecuta regularmente para detectar regresiones + +## Funciones Útiles para Queries `exec` + +### Estado del Motor +```python +"engine.symbol_table" # Variables actuales +"engine.last_result" # Último resultado +"engine.symbolic_mode" # ¿Modo simbólico? +"len(engine.equations)" # Cantidad de ecuaciones +``` + +### Información de Tipos +```python +"type(engine.last_result).__name__" # Tipo del resultado +"engine.get_available_types()" # Tipos disponibles +"list(engine.registered_types_info['registered_classes'].keys())" +``` + +### Tokenización +```python +"engine.parser.get_tokenization_info()" # Info de tokenización +"engine.parser.process_expression('test')" # Procesar expresión +``` + +### Análisis de Objetos +```python +"engine.last_result.original" # String original (FourBytes) +"engine.last_result.base" # Base numérica (IntBase) +"engine.last_result.has_symbols" # ¿Tiene símbolos? +``` + +## Formato de Resultado + +### Resultado Individual para Query `input` + +```json +{ + "index": 0, + "input": "10.1.1.1 + 1", + "output": "10.1.1.2", + "result_type": "FourBytes", + "success": true, + "error": null, + "display_class": "[FourBytes]", + "output_raw": { + "parts": [ + ["custom_type", "10.1.1.2"], + ["class_hint", "[FourBytes]"] + ], + "formatted_text": "[custom_type]10.1.1.2[class_hint][FourBytes]", + "tag_info": { + "custom_type": {"fg": "#f9a825"}, + "class_hint": {"fg": "#888888"} + } + } +} +``` + +### Campos del Resultado + +- **`output`**: Resultado básico (antes del post-procesamiento) +- **`output_raw`**: Resultado exacto como se muestra en la aplicación + - **`parts`**: Lista de tuplas `[tag, contenido]` con información de color/formato + - **`formatted_text`**: Texto formateado como se vería en la aplicación + - **`tag_info`**: Información de colores y estilos para cada tag +- **`result_type`**: Tipo del objeto resultado +- **`display_class`**: Nombre de clase para mostrar + +### Tags de Color Disponibles + +- **`error`**: `#ff6b6b` (rojo) - Errores +- **`result`**: `#abdbe3` (azul claro) - Resultados generales +- **`symbolic`**: `#82aaff` (azul) - Expresiones simbólicas +- **`numeric`**: `#c3e88d` (verde) - Aproximaciones numéricas +- **`custom_type`**: `#f9a825` (naranja) - Tipos personalizados +- **`ip`**: `#fff176` (amarillo) - Direcciones IP +- **`hex`**: `#f9a825` (naranja) - Números hexadecimales +- **`class_hint`**: `#888888` (gris) - Pistas de clase + +## Tips + +- Usa `index` secuencial para facilitar la lectura de resultados +- Combina queries `input` y `exec` para verificar comportamiento y estado +- El archivo de resultados contiene información detallada de éxito/error +- **Usa `output_raw` para entender exactamente cómo se mostraría el resultado en la aplicación** +- Puedes copiar y modificar templates existentes para casos específicos \ No newline at end of file diff --git a/debug_templates/basic_test.json b/debug_templates/basic_test.json new file mode 100644 index 0000000..a30fab6 --- /dev/null +++ b/debug_templates/basic_test.json @@ -0,0 +1,44 @@ +{ + "queries": [ + { + "index": 0, + "type": "input", + "content": "10.1.1.1 + 1" + }, + { + "index": 1, + "type": "exec", + "content": "type(engine.last_result).__name__" + }, + { + "index": 2, + "type": "input", + "content": "16#FF" + }, + { + "index": 3, + "type": "exec", + "content": "engine.last_result.base" + }, + { + "index": 4, + "type": "input", + "content": "mask = 255.255.0.0" + }, + { + "index": 5, + "type": "exec", + "content": "engine.symbol_table" + }, + { + "index": 6, + "type": "input", + "content": "solve(x**2 + 1, x)" + }, + { + "index": 7, + "type": "exec", + "content": "len(engine.symbol_table)" + } + ] +} \ No newline at end of file diff --git a/debug_templates/context_debug.json b/debug_templates/context_debug.json new file mode 100644 index 0000000..4ec517c --- /dev/null +++ b/debug_templates/context_debug.json @@ -0,0 +1,59 @@ +{ + "queries": [ + { + "index": 0, + "type": "input", + "content": "x = 5" + }, + { + "index": 1, + "type": "input", + "content": "y = x + 10" + }, + { + "index": 2, + "type": "exec", + "content": "engine.symbol_table" + }, + { + "index": 3, + "type": "exec", + "content": "len(engine.equations)" + }, + { + "index": 4, + "type": "exec", + "content": "list(engine.base_context.keys())[:10]" + }, + { + "index": 5, + "type": "exec", + "content": "engine.get_available_types()" + }, + { + "index": 6, + "type": "exec", + "content": "list(engine.registered_types_info['registered_classes'].keys())" + }, + { + "index": 7, + "type": "exec", + "content": "engine.registered_types_info['class_count']" + }, + { + "index": 8, + "type": "input", + "content": "z = solve(a**2 + b, a)" + }, + { + "index": 9, + "type": "exec", + "content": "engine._classify_line('z = solve(a**2 + b, a)')" + }, + { + "index": 10, + "type": "exec", + "content": "engine._extract_variable_names('x + y + z')" + } + ] +} \ No newline at end of file diff --git a/debug_templates/display_format_test.json b/debug_templates/display_format_test.json new file mode 100644 index 0000000..e0df407 --- /dev/null +++ b/debug_templates/display_format_test.json @@ -0,0 +1,64 @@ +{ + "queries": [ + { + "index": 0, + "type": "input", + "content": "192.168.1.1 + 1" + }, + { + "index": 1, + "type": "exec", + "content": "engine.last_result" + }, + { + "index": 2, + "type": "input", + "content": "16#FF00" + }, + { + "index": 3, + "type": "exec", + "content": "engine.last_result.base" + }, + { + "index": 4, + "type": "input", + "content": "x = solve(a**2 - 4, a)" + }, + { + "index": 5, + "type": "exec", + "content": "type(engine.symbol_table['x'])" + }, + { + "index": 6, + "type": "input", + "content": "IP4Mask(255.255.255.0)" + }, + { + "index": 7, + "type": "exec", + "content": "engine.last_result.get_prefix_int()" + }, + { + "index": 8, + "type": "input", + "content": "# Este es un comentario" + }, + { + "index": 9, + "type": "input", + "content": "invalid_expression" + }, + { + "index": 10, + "type": "input", + "content": "matriz = Matrix([[1, 2], [3, 4]])" + }, + { + "index": 11, + "type": "exec", + "content": "engine.symbol_table['matriz'].shape" + } + ] +} \ No newline at end of file diff --git a/debug_templates/error_debug.json b/debug_templates/error_debug.json new file mode 100644 index 0000000..15dc9e5 --- /dev/null +++ b/debug_templates/error_debug.json @@ -0,0 +1,59 @@ +{ + "queries": [ + { + "index": 0, + "type": "input", + "content": "bad_mask = 255.240.0.3" + }, + { + "index": 1, + "type": "input", + "content": "IP4Mask(bad_mask)" + }, + { + "index": 2, + "type": "exec", + "content": "engine.last_result" + }, + { + "index": 3, + "type": "input", + "content": "invalid_ip = 300.1.1.1" + }, + { + "index": 4, + "type": "exec", + "content": "hasattr(engine, 'last_error')" + }, + { + "index": 5, + "type": "input", + "content": "16#GG" + }, + { + "index": 6, + "type": "exec", + "content": "engine.debug" + }, + { + "index": 7, + "type": "input", + "content": "divide_by_zero = 10/0" + }, + { + "index": 8, + "type": "exec", + "content": "engine.symbolic_mode" + }, + { + "index": 9, + "type": "input", + "content": "undefined_var + 5" + }, + { + "index": 10, + "type": "exec", + "content": "list(engine.symbol_table.keys())" + } + ] +} \ No newline at end of file diff --git a/debug_templates/regression_test.json b/debug_templates/regression_test.json new file mode 100644 index 0000000..3b5b562 --- /dev/null +++ b/debug_templates/regression_test.json @@ -0,0 +1,74 @@ +{ + "queries": [ + { + "index": 0, + "type": "input", + "content": "mask=255.240.0.0" + }, + { + "index": 1, + "type": "exec", + "content": "type(engine.symbol_table['mask']).__name__" + }, + { + "index": 2, + "type": "input", + "content": "10.1.1.1 + 1" + }, + { + "index": 3, + "type": "exec", + "content": "type(engine.last_result).__name__" + }, + { + "index": 4, + "type": "exec", + "content": "engine.last_result.original" + }, + { + "index": 5, + "type": "input", + "content": "IP4Mask(255.255.0.0)" + }, + { + "index": 6, + "type": "exec", + "content": "engine.last_result.get_prefix_int()" + }, + { + "index": 7, + "type": "input", + "content": "16#FF + 10" + }, + { + "index": 8, + "type": "exec", + "content": "engine.last_result.base" + }, + { + "index": 9, + "type": "exec", + "content": "engine.last_result.value_str" + }, + { + "index": 10, + "type": "input", + "content": "ip = 10.1.1.x" + }, + { + "index": 11, + "type": "exec", + "content": "type(engine.symbol_table['ip'])" + }, + { + "index": 12, + "type": "exec", + "content": "engine.symbol_table['ip'].has_symbols" + }, + { + "index": 13, + "type": "input", + "content": "ip.substitute(x=5)" + } + ] +} \ No newline at end of file diff --git a/debug_templates/tokenization_test.json b/debug_templates/tokenization_test.json new file mode 100644 index 0000000..6979892 --- /dev/null +++ b/debug_templates/tokenization_test.json @@ -0,0 +1,44 @@ +{ + "queries": [ + { + "index": 0, + "type": "exec", + "content": "engine.parser.process_expression('192.168.1.1 + 16#FF')" + }, + { + "index": 1, + "type": "input", + "content": "192.168.1.1 + 16#FF" + }, + { + "index": 2, + "type": "exec", + "content": "engine.parser.get_tokenization_info()['rules'][:3]" + }, + { + "index": 3, + "type": "input", + "content": "10.0.0.0/8 + 1" + }, + { + "index": 4, + "type": "exec", + "content": "engine.parser.get_tokenization_info()" + }, + { + "index": 5, + "type": "input", + "content": "x = 192.168.y.z" + }, + { + "index": 6, + "type": "exec", + "content": "engine.parser.process_expression('x = 192.168.y.z')" + }, + { + "index": 7, + "type": "exec", + "content": "len(engine.parser.tokenizer.tokenization_rules)" + } + ] +} \ No newline at end of file diff --git a/display_format_test_results.json b/display_format_test_results.json new file mode 100644 index 0000000..03c8e7f --- /dev/null +++ b/display_format_test_results.json @@ -0,0 +1,569 @@ +{ + "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 new file mode 100644 index 0000000..15fa665 --- /dev/null +++ b/error_debug_results.json @@ -0,0 +1,113 @@ +{ + "execution_info": { + "timestamp": "2025-06-05T18:01:38.085095Z", + "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, + "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", + "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'", + "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, + "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, + "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, + "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/error_test_with_raw.json b/error_test_with_raw.json new file mode 100644 index 0000000..c0ee0fa --- /dev/null +++ b/error_test_with_raw.json @@ -0,0 +1,491 @@ +{ + "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 8784f81..20b4b22 100644 --- a/hybrid_calc_history.txt +++ b/hybrid_calc_history.txt @@ -1,5 +1,5 @@ -mask=255.240.0.x +mask=255.240.0.0 mask.ToHex() ip=10.1.1.x 10.1.1.1 + 1 @@ -7,4 +7,6 @@ ip IP4(10.1.1.4) IP4Mask(mask) -IP4Mask(255.255.0.0) \ No newline at end of file +IP4Mask(255.255.0.0) + +bad_mask = 255.240.0.3 \ No newline at end of file diff --git a/simple_debug.py b/simple_debug.py new file mode 100644 index 0000000..edd5569 --- /dev/null +++ b/simple_debug.py @@ -0,0 +1,462 @@ +#!/usr/bin/env python3 +""" +Simple Debug API - Calculadora MAV CAS + +API CLI simple que usa el motor de evaluación existente para generar debug traces +sin modificar el código base. Permite debuggear "como si usaras la aplicación" +mediante archivos JSON. + +Uso: + python simple_debug.py debug_input.json + python simple_debug.py debug_input.json --output debug_results.json + python simple_debug.py debug_input.json --verbose +""" + +import json +import sys +import argparse +from datetime import datetime +from pathlib import Path + +# Importar el motor de evaluación existente +from main_evaluation import HybridEvaluationEngine +import sympy + + +def _process_evaluation_result_for_debug(result, engine): + """ + Procesa el resultado de evaluación tal como lo haría la aplicación + Retorna información de formato, colores y contenido exacto + """ + output_parts = [] + + if result.is_error: + # Intentar obtener ayuda como lo hace la app + ayuda = _obtener_ayuda_simulada(result.original_line, engine) + if ayuda: + ayuda_linea = ayuda.replace("\n", " ").replace("\r", " ") + if len(ayuda_linea) > 120: + ayuda_linea = ayuda_linea[:117] + "..." + output_parts.append(("helper", ayuda_linea)) + else: + output_parts.append(("error", f"Error: {result.error}")) + elif result.result_type == "comment": + output_parts.append(("comment", result.original_line)) + elif result.result_type == "equation_added": + output_parts.append(("equation", result.symbolic_result)) + elif result.result_type == "assignment": + output_parts.append(("info", result.symbolic_result)) + # Mostrar evaluación numérica para asignaciones si existe + if result.numeric_result is not None and result.numeric_result != result.result: + output_parts.append(("numeric", f"≈ {result.numeric_result}")) + else: + # Resultado normal + if result.result is not None: + # Determinar tag basado en tipo (simulando lógica dinámica) + tag = _get_result_tag_dynamic_debug(result.result, engine) + + # Verificar si es resultado interactivo (simulado) + if _is_interactive_result(result.result): + interactive_tag, display_text = _create_interactive_tag_debug(result.result) + if interactive_tag: + output_parts.append((interactive_tag, display_text)) + else: + output_parts.append((tag, str(result.result))) + else: + output_parts.append((tag, str(result.result))) + + # Añadir pista de clase para el resultado principal + class_display_name = _get_class_display_name_dynamic_debug(result.result, engine) + if class_display_name: + output_parts.append(("class_hint", f"[{class_display_name}]")) + + # Mostrar evaluación numérica si existe + if result.numeric_result is not None and result.numeric_result != result.result: + output_parts.append(("numeric", f"≈ {result.numeric_result}")) + + # Mostrar información adicional + if result.info: + output_parts.append(("info", f"({result.info})")) + + # Convertir partes a string formateado como la app + formatted_output = _format_output_parts_debug(output_parts) + + return { + 'parts': output_parts, + 'formatted_text': formatted_output, + 'tag_info': _get_tag_color_info() + } + + +def _obtener_ayuda_simulada(input_str, engine): + """Simula la obtención de ayuda como lo hace la app""" + try: + # Intentar usar los helpers del engine si están disponibles + if hasattr(engine, 'HELPERS'): + for helper in engine.HELPERS: + try: + ayuda = helper(input_str) + if ayuda: + return ayuda + except: + continue + except: + pass + return None + + +def _get_result_tag_dynamic_debug(result, engine): + """Simula _get_result_tag_dynamic de la app""" + try: + registered_classes = engine.get_available_types().get('registered_classes', {}) + + # Verificar si es una instancia de alguna clase registrada + for name, cls in registered_classes.items(): + if isinstance(result, cls): + name_lower = name.lower() + if name_lower == "hex": + return "hex" + elif name_lower == "bin": + return "bin" + elif name_lower in ["ip4", "ip"]: + return "ip" + elif name_lower == "chr": + return "chr_type" + elif name_lower == "date": + return "date" + else: + return "custom_type" + + except Exception: + pass + + # Fallback a tags existentes para tipos no registrados + if isinstance(result, sympy.Basic): + return "symbolic" + else: + return "result" + + +def _get_class_display_name_dynamic_debug(obj, engine): + """Simula _get_class_display_name_dynamic de la app""" + try: + # Verificar si es una clase registrada dinámicamente + registered_classes = engine.get_available_types().get('registered_classes', {}) + + for name, cls in registered_classes.items(): + if isinstance(obj, cls): + return name + + except Exception: + pass + + # Fallback a lógica existente para tipos nativos + if isinstance(obj, sympy.logic.boolalg.BooleanAtom): + return "Boolean" + elif isinstance(obj, sympy.Basic): + if hasattr(obj, 'is_number') and obj.is_number: + if hasattr(obj, 'is_Integer') and obj.is_Integer: + return "Integer" + elif hasattr(obj, 'is_Rational') and obj.is_Rational and not obj.is_Integer: + return "Rational" + elif hasattr(obj, 'is_Float') and obj.is_Float: + return "Float" + else: + return "SympyNumber" + else: + return "Sympy" + elif isinstance(obj, bool): + return "Boolean" + elif isinstance(obj, (int, float, str, list, dict, tuple, type(None))): + class_display_name = type(obj).__name__.capitalize() + if class_display_name == "Nonetype": + class_display_name = "None" + return class_display_name + + return "" + + +def _is_interactive_result(result): + """Simula la lógica is_interactive de EvaluationResult""" + try: + # Intentar importar PlotResult si existe + from tl_popup import PlotResult + if isinstance(result, PlotResult): + return True + except ImportError: + pass + + return isinstance(result, sympy.Matrix) or \ + (isinstance(result, list) and len(result) > 3) + + +def _create_interactive_tag_debug(result): + """Simula la creación de tags interactivos""" + try: + from tl_popup import PlotResult + if isinstance(result, PlotResult): + return f"plot_{id(result)}", f"📊 Ver {result.plot_type.title()}" + except ImportError: + pass + + if isinstance(result, sympy.Matrix): + rows, cols = result.shape + return f"matrix_{id(result)}", f"📋 Ver Matriz {rows}×{cols}" + elif isinstance(result, list) and len(result) > 5: + return f"list_{id(result)}", f"📋 Ver Lista ({len(result)} elementos)" + elif isinstance(result, dict) and len(result) > 3: + return f"dict_{id(result)}", f"🔍 Ver Diccionario ({len(result)} entradas)" + + return None, None + + +def _format_output_parts_debug(output_parts): + """Simula cómo _display_output formatea las partes para mostrar""" + formatted_lines = [] + + for part_idx, (tag, content) in enumerate(output_parts): + if not content: + continue + + prefix = "" + if part_idx > 0: + prev_tag, prev_content = output_parts[part_idx-1] if part_idx > 0 else (None, None) + + if tag not in ["class_hint", "numeric", "info"] and prev_content: + prefix = " ; " + elif tag in ["numeric", "info"] and prev_content: + prefix = " " + + formatted_lines.append(f"{prefix}[{tag}]{content}") + + return "".join(formatted_lines) + + +def _get_tag_color_info(): + """Información sobre los colores de los tags como en setup_output_tags""" + return { + "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"} + } + + +def run_debug(input_file: str, output_file: str = None, verbose: bool = False): + """ + Ejecuta las queries de debug desde un archivo JSON y guarda los resultados. + + Args: + input_file: Archivo JSON con las queries + output_file: Archivo de salida (opcional) + verbose: Modo verboso + """ + + # Cargar queries del archivo de entrada + try: + with open(input_file, 'r', encoding='utf-8') as f: + data = json.load(f) + except FileNotFoundError: + print(f"Error: No se encontró el archivo '{input_file}'") + sys.exit(1) + except json.JSONDecodeError as e: + print(f"Error: JSON inválido en '{input_file}': {e}") + sys.exit(1) + + if 'queries' not in data: + print("Error: El archivo JSON debe contener una clave 'queries'") + sys.exit(1) + + # Crear motor de evaluación + if verbose: + print("Iniciando motor de evaluación...") + + engine = HybridEvaluationEngine() + results = [] + successful = 0 + failed = 0 + + # Ejecutar cada query + for query in data['queries']: + if verbose: + print(f"Ejecutando query {query.get('index', '?')}: {query.get('content', '')[:50]}...") + + try: + if query['type'] == 'input': + # Query de tipo input: evaluar expresión como si fuera entrada del usuario + result = engine.evaluate_line(query['content']) + + output = { + 'index': query['index'], + 'input': query['content'], + 'output': str(result.result) if hasattr(result, 'result') else str(result), + 'result_type': type(result.result).__name__ if hasattr(result, 'result') else type(result).__name__, + 'success': not (hasattr(result, 'is_error') and result.is_error), + 'error': result.error if hasattr(result, 'is_error') and result.is_error else None + } + + # Generar output_raw: resultado exacto como se muestra en la aplicación + try: + output_raw_data = _process_evaluation_result_for_debug(result, engine) + output['output_raw'] = output_raw_data + except Exception as e: + output['output_raw'] = f"Error generando output_raw: {e}" + + # Añadir información adicional si está disponible + if hasattr(result, 'result') and hasattr(result.result, '__class__'): + output['display_class'] = f"[{result.result.__class__.__name__}]" + + if not (hasattr(result, 'is_error') and result.is_error): + successful += 1 + else: + failed += 1 + + elif query['type'] == 'exec': + # Query de tipo exec: ejecutar código Python para inspeccionar el estado + exec_result = eval(query['content'], {'engine': engine}) + + output = { + 'index': query['index'], + 'input': query['content'], + 'output': str(exec_result), + 'result_type': type(exec_result).__name__, + 'success': True, + 'error': None, + 'exec_result': exec_result + } + successful += 1 + + else: + raise ValueError(f"Tipo de query desconocido: {query['type']}") + + except Exception as e: + # Manejar errores en la ejecución + output = { + 'index': query['index'], + 'input': query['content'], + 'output': None, + 'result_type': None, + 'success': False, + 'error': str(e) + } + failed += 1 + + if verbose: + print(f" Error: {str(e)}") + + results.append(output) + + # Preparar salida final + final_output = { + 'execution_info': { + 'timestamp': datetime.now().isoformat() + 'Z', + 'total_queries': len(data['queries']), + 'successful': successful, + 'failed': failed, + 'input_file': input_file + }, + 'results': results + } + + # Determinar archivo de salida + if output_file is None: + base_name = Path(input_file).stem + output_file = f"{base_name}_results.json" + + # Guardar resultados + try: + # Usar un encoder personalizado para objetos no serializables + def json_serializer(obj): + """Serializar objetos no estándar a string""" + try: + # Intentar conversión normal primero + json.dumps(obj) + return obj + except TypeError: + # Si no es serializable, convertir a string + return str(obj) + + # Aplicar serialización personalizada a exec_result + for result in final_output['results']: + if 'exec_result' in result: + result['exec_result'] = json_serializer(result['exec_result']) + + with open(output_file, 'w', encoding='utf-8') as f: + json.dump(final_output, f, indent=2, ensure_ascii=False, default=str) + + if verbose: + print(f"\nResultados guardados en: {output_file}") + print(f"Queries exitosas: {successful}") + print(f"Queries con error: {failed}") + + except Exception as e: + print(f"Error al guardar resultados: {e}") + sys.exit(1) + + return final_output + + +def main(): + """Función principal del CLI""" + parser = argparse.ArgumentParser( + description='Simple Debug API para Calculadora MAV CAS', + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Ejemplos de uso: + python simple_debug.py debug_input.json + python simple_debug.py debug_input.json --output custom_results.json + python simple_debug.py debug_input.json --verbose + """ + ) + + parser.add_argument('input_file', + help='Archivo JSON con las queries de debug') + parser.add_argument('--output', '-o', + help='Archivo de salida (por defecto: _results.json)') + parser.add_argument('--verbose', '-v', + action='store_true', + help='Modo verboso') + + args = parser.parse_args() + + # Verificar que el archivo de entrada existe + if not Path(args.input_file).exists(): + print(f"Error: El archivo '{args.input_file}' no existe") + sys.exit(1) + + # Ejecutar debug + try: + results = run_debug(args.input_file, args.output, args.verbose) + + # Mostrar resumen si no es modo verboso + if not args.verbose: + info = results['execution_info'] + print(f"Debug completado: {info['successful']}/{info['total_queries']} exitosas") + if args.output: + print(f"Resultados en: {args.output}") + else: + base_name = Path(args.input_file).stem + print(f"Resultados en: {base_name}_results.json") + + except KeyboardInterrupt: + print("\nInterrumpido por el usuario") + sys.exit(1) + except Exception as e: + print(f"Error inesperado: {e}") + sys.exit(1) + + +if __name__ == '__main__': + main() \ No newline at end of file