Refactorización del motor de evaluación para utilizar un sistema algebraico puro. Se actualizan las configuraciones de la interfaz y se simplifican las funciones de evaluación. Se añaden patrones de tokenización en el registro de tipos y se mejora la gestión de errores en el motor. Se eliminan configuraciones obsoletas y se optimiza el menú de herramientas.
This commit is contained in:
parent
ece028e837
commit
7d2033c60e
|
@ -1,12 +1,9 @@
|
|||
|
||||
a = x + 5
|
||||
x=?
|
||||
solve(x)
|
||||
a = x + 5 / z
|
||||
|
||||
m=t+u * 5
|
||||
z=?
|
||||
|
||||
t=4
|
||||
m=3
|
||||
u=?
|
||||
a=2
|
||||
x=3
|
||||
|
||||
u
|
||||
IP4(10.1.1.2)
|
293
main_calc_app.py
293
main_calc_app.py
|
@ -45,7 +45,7 @@ if MARKDOWN_AVAILABLE and HTML_VIEWER_TYPE is None:
|
|||
|
||||
# ========== IMPORTS ADAPTADOS AL NUEVO SISTEMA ==========
|
||||
# Importar componentes del CAS híbrido con nuevo sistema de tipos
|
||||
from main_evaluation import HybridEvaluationEngine, EvaluationResult
|
||||
from main_evaluation_puro import PureAlgebraicEngine, EvaluationResult
|
||||
from tl_popup import InteractiveResultManager, PlotResult
|
||||
from type_registry import get_registered_helper_functions, get_registered_base_context
|
||||
import sympy
|
||||
|
@ -69,13 +69,15 @@ class HybridCalculatorApp:
|
|||
self.root.geometry(self.settings.get("window_geometry", "1000x700"))
|
||||
self.root.configure(bg="#2b2b2b")
|
||||
|
||||
# Configurar motor con configuraciones cargadas
|
||||
self.engine = HybridEvaluationEngine(auto_discover_types=True, types_directory="custom_types")
|
||||
self._apply_symbolic_settings() # NUEVO: Aplicar configuraciones simbólicas
|
||||
|
||||
# Debug desde configuración
|
||||
# Debug desde configuración (definir antes del motor)
|
||||
self.debug = self.settings.get("debug", False)
|
||||
self.engine.debug = self.debug
|
||||
|
||||
# Motor de evaluación - SISTEMA ALGEBRAICO PURO
|
||||
self.engine = PureAlgebraicEngine()
|
||||
|
||||
# Configurar motor
|
||||
if hasattr(self.engine, 'logger'):
|
||||
self.engine.logger.setLevel(logging.DEBUG if self.debug else logging.INFO)
|
||||
|
||||
# Autocompletado
|
||||
self.autocomplete_popup = None
|
||||
|
@ -103,7 +105,7 @@ class HybridCalculatorApp:
|
|||
|
||||
self.status_label = tk.Label(
|
||||
self.status_frame,
|
||||
text=self._get_status_text(),
|
||||
text="🔢 Calculadora MAV - Sistema Algebraico Puro",
|
||||
bg="#2b2b2b",
|
||||
fg="#80c7f7",
|
||||
font=("Consolas", 9),
|
||||
|
@ -140,49 +142,43 @@ class HybridCalculatorApp:
|
|||
def reload_types(self):
|
||||
"""Recarga el sistema de tipos (útil para desarrollo)"""
|
||||
try:
|
||||
self.logger.info("Recargando sistema de tipos...") # Original: 🔄
|
||||
|
||||
# Recargar engine
|
||||
self.engine.reload_types()
|
||||
self.logger.info("Recargando sistema de tipos...")
|
||||
|
||||
# Recargar helpers
|
||||
self._setup_dynamic_helpers()
|
||||
# Re-evaluar contenido actual
|
||||
self._evaluate_and_update()
|
||||
|
||||
self.logger.info("Sistema de tipos recargado.") # Original: ✅
|
||||
self.logger.info("Sistema de tipos recargado.")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error recargando tipos: {e}", exc_info=True) # Original: ❌
|
||||
self.logger.error(f"Error recargando tipos: {e}", exc_info=True)
|
||||
messagebox.showerror("Error", f"Error recargando tipos:\n{e}")
|
||||
|
||||
def show_types_info(self):
|
||||
"""Muestra información sobre tipos disponibles"""
|
||||
try:
|
||||
types_info = self.engine.get_available_types()
|
||||
context_info = self.engine.get_context_info()
|
||||
|
||||
info_text = f"""INFORMACIÓN DEL SISTEMA DE TIPOS
|
||||
info_text = f"""INFORMACIÓN DEL SISTEMA ALGEBRAICO PURO
|
||||
|
||||
Clases registradas: {len(types_info.get('registered_classes', {}))}
|
||||
Clases con sintaxis de corchetes: {len(types_info.get('bracket_classes', []))}
|
||||
Entradas en contexto: {types_info.get('total_context_entries', 0)}
|
||||
Helper functions: {types_info.get('helper_functions_count', 0)}
|
||||
Ecuaciones en el sistema: {context_info.get('equations', 0)}
|
||||
Variables definidas: {context_info.get('variables', 0)}
|
||||
Variables activas: {', '.join(context_info.get('variable_names', []))}
|
||||
|
||||
CLASES DISPONIBLES:
|
||||
CARACTERÍSTICAS:
|
||||
• Sistema de ecuaciones puras con SymPy
|
||||
• Todas las asignaciones son ecuaciones
|
||||
• Resolución automática de sistemas
|
||||
• Evaluación numérica inteligente
|
||||
• Atajo x=? equivale a solve(x)
|
||||
"""
|
||||
|
||||
for name, cls in types_info.get('registered_classes', {}).items():
|
||||
info_text += f"• {name}: {cls.__name__}\n"
|
||||
|
||||
info_text += f"\nCLASES CON SINTAXIS DE CORCHETES:\n"
|
||||
for name in types_info.get('bracket_classes', []):
|
||||
info_text += f"• {name}[...]\n"
|
||||
|
||||
# Mostrar en ventana
|
||||
self._show_help_window("Información de Tipos", info_text)
|
||||
self._show_help_window("Información del Sistema", info_text)
|
||||
|
||||
except Exception as e:
|
||||
messagebox.showerror("Error", f"Error obteniendo información de tipos:\n{e}")
|
||||
messagebox.showerror("Error", f"Error obteniendo información del sistema:\n{e}")
|
||||
|
||||
def _setup_icon(self):
|
||||
"""Configura el ícono de la aplicación"""
|
||||
|
@ -228,27 +224,7 @@ CLASES DISPONIBLES:
|
|||
if self.debug:
|
||||
self.logger.error(f"Error guardando configuración: {e}", exc_info=True)
|
||||
|
||||
def update_symbolic_settings(self, symbolic_mode=None, show_numeric=None,
|
||||
keep_fractions=None, auto_simplify=None):
|
||||
"""Actualiza configuraciones simbólicas y las guarda"""
|
||||
if symbolic_mode is not None:
|
||||
self.settings["symbolic_mode"] = symbolic_mode
|
||||
if show_numeric is not None:
|
||||
self.settings["show_numeric_approximation"] = show_numeric
|
||||
if keep_fractions is not None:
|
||||
self.settings["keep_symbolic_fractions"] = keep_fractions
|
||||
if auto_simplify is not None:
|
||||
self.settings["auto_simplify"] = auto_simplify
|
||||
|
||||
# Aplicar al motor
|
||||
self._apply_symbolic_settings()
|
||||
|
||||
# Actualizar barra de estado
|
||||
if hasattr(self, 'status_label'):
|
||||
self.status_label.config(text=self._get_status_text())
|
||||
|
||||
# Guardar configuraciones
|
||||
self._save_settings()
|
||||
|
||||
|
||||
def create_widgets(self):
|
||||
"""Crea la interfaz gráfica"""
|
||||
|
@ -346,34 +322,10 @@ CLASES DISPONIBLES:
|
|||
edit_menu.add_separator()
|
||||
edit_menu.add_command(label="Limpiar historial", command=self.clear_history)
|
||||
|
||||
# Menú Configuración
|
||||
config_menu = Menu(menubar, tearoff=0, bg="#3c3c3c", fg="white")
|
||||
menubar.add_cascade(label="Configuración", menu=config_menu)
|
||||
|
||||
# Variables para checkbuttons
|
||||
self.symbolic_mode_var = tk.BooleanVar(value=self.settings.get("symbolic_mode", True))
|
||||
self.show_numeric_var = tk.BooleanVar(value=self.settings.get("show_numeric_approximation", True))
|
||||
self.keep_fractions_var = tk.BooleanVar(value=self.settings.get("keep_symbolic_fractions", True))
|
||||
|
||||
# Modo simbólico
|
||||
config_menu.add_checkbutton(
|
||||
label="Modo Simbólico",
|
||||
variable=self.symbolic_mode_var,
|
||||
command=self.toggle_symbolic_mode
|
||||
)
|
||||
config_menu.add_checkbutton(
|
||||
label="Mostrar Aproximación Numérica",
|
||||
variable=self.show_numeric_var,
|
||||
command=self.toggle_numeric_approximation
|
||||
)
|
||||
config_menu.add_checkbutton(
|
||||
label="Mantener Fracciones Simbólicas",
|
||||
variable=self.keep_fractions_var,
|
||||
command=self.toggle_symbolic_fractions
|
||||
)
|
||||
config_menu.add_separator()
|
||||
|
||||
config_menu.add_command(label="Recargar Tipos Personalizados", command=self.reload_types)
|
||||
# Menú Herramientas (simplificado)
|
||||
tools_menu = Menu(menubar, tearoff=0, bg="#3c3c3c", fg="white")
|
||||
menubar.add_cascade(label="Herramientas", menu=tools_menu)
|
||||
tools_menu.add_command(label="Recargar Tipos Personalizados", command=self.reload_types)
|
||||
|
||||
# ========== MENÚ TIPOS (NUEVO) ==========
|
||||
types_menu = Menu(menubar, tearoff=0, bg="#3c3c3c", fg="white")
|
||||
|
@ -687,7 +639,7 @@ CLASES DISPONIBLES:
|
|||
|
||||
# NUEVO: Limpiar completamente el contexto antes de cada evaluación
|
||||
# Esto garantiza que cada modificación reevalúe todo desde cero
|
||||
self.engine.clear_all()
|
||||
self.engine.clear_context()
|
||||
|
||||
lines = input_content.splitlines()
|
||||
self._evaluate_lines(lines)
|
||||
|
@ -718,132 +670,73 @@ CLASES DISPONIBLES:
|
|||
self._display_output(output_data)
|
||||
|
||||
def _process_evaluation_result(self, result: EvaluationResult) -> List[tuple]:
|
||||
"""Procesa el resultado de evaluación para display"""
|
||||
"""Procesa el resultado de evaluación para display - ADAPTADO AL MOTOR PURO"""
|
||||
output_parts = []
|
||||
|
||||
if result.is_error:
|
||||
ayuda = self.obtener_ayuda(result.original_line)
|
||||
if not result.success:
|
||||
# Error
|
||||
ayuda = self.obtener_ayuda(result.input_line)
|
||||
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}"))
|
||||
output_parts.append(("error", f"Error: {result.error_message}"))
|
||||
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}"))
|
||||
output_parts.append(("comment", result.input_line))
|
||||
elif result.result_type == "equation":
|
||||
output_parts.append(("equation", result.output))
|
||||
elif result.result_type == "symbolic":
|
||||
output_parts.append(("symbolic", result.output))
|
||||
else:
|
||||
# Resultado normal
|
||||
if result.result is not None:
|
||||
# Determinar tag basado en tipo (DINÁMICO)
|
||||
tag = self._get_result_tag_dynamic(result.result)
|
||||
|
||||
# Verificar si es resultado interactivo
|
||||
if self.interactive_manager and result.is_interactive:
|
||||
interactive_tag, display_text = self.interactive_manager.create_interactive_tag(result.result, self.output_text, "1.0")
|
||||
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
|
||||
primary_result_object = result.result
|
||||
if not isinstance(primary_result_object, PlotResult):
|
||||
class_display_name = self._get_class_display_name_dynamic(primary_result_object)
|
||||
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})"))
|
||||
# Resultado general
|
||||
output_parts.append(("result", result.output))
|
||||
|
||||
return output_parts
|
||||
|
||||
def _get_result_tag_dynamic(self, result: Any) -> str:
|
||||
"""Determina el tag de color para un resultado - VERSIÓN DINÁMICA"""
|
||||
# Obtener clases registradas dinámicamente del sistema de tipos
|
||||
try:
|
||||
registered_classes = self.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):
|
||||
# Usar tags específicos basados en el nombre de la clase
|
||||
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" # Tag genérico para tipos personalizados
|
||||
|
||||
except Exception as e:
|
||||
if self.debug:
|
||||
self.logger.debug(f"Error en get_result_tag_dynamic: {e}")
|
||||
"""Determina el tag de color para un resultado - SIMPLIFICADO"""
|
||||
# Determinar tag basado en tipo
|
||||
if hasattr(result, '__class__'):
|
||||
class_name = result.__class__.__name__.lower()
|
||||
if 'hex' in class_name:
|
||||
return "hex"
|
||||
elif 'bin' in class_name:
|
||||
return "bin"
|
||||
elif 'ip' in class_name:
|
||||
return "ip"
|
||||
elif 'chr' in class_name:
|
||||
return "chr_type"
|
||||
elif 'date' in class_name:
|
||||
return "date"
|
||||
|
||||
# Fallback a tags existentes para tipos no registrados
|
||||
if isinstance(result, sympy.Basic):
|
||||
return "symbolic"
|
||||
else:
|
||||
return "result"
|
||||
# Fallback a tags existentes
|
||||
try:
|
||||
import sympy
|
||||
if isinstance(result, sympy.Basic):
|
||||
return "symbolic"
|
||||
except:
|
||||
pass
|
||||
|
||||
return "result"
|
||||
|
||||
def _get_class_display_name_dynamic(self, obj: Any) -> str:
|
||||
"""Obtiene nombre de clase para display - VERSIÓN DINÁMICA"""
|
||||
"""Obtiene nombre de clase para display - SIMPLIFICADO"""
|
||||
try:
|
||||
# Verificar si es una clase registrada dinámicamente
|
||||
registered_classes = self.engine.get_available_types().get('registered_classes', {})
|
||||
|
||||
for name, cls in registered_classes.items():
|
||||
if isinstance(obj, cls):
|
||||
return name
|
||||
|
||||
except Exception as e:
|
||||
if self.debug:
|
||||
self.logger.debug(f"Error en get_class_display_name_dynamic: {e}")
|
||||
|
||||
# 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:
|
||||
import sympy
|
||||
if isinstance(obj, sympy.Basic):
|
||||
return "Sympy"
|
||||
elif isinstance(obj, bool):
|
||||
return "Boolean"
|
||||
elif isinstance(obj, (int, float, str, list, dict, tuple, type(None))):
|
||||
except:
|
||||
pass
|
||||
|
||||
if isinstance(obj, (int, float, str, list, dict, tuple, bool, type(None))):
|
||||
class_display_name = type(obj).__name__.capitalize()
|
||||
if class_display_name == "Nonetype":
|
||||
class_display_name = "None"
|
||||
return class_display_name
|
||||
|
||||
return ""
|
||||
return type(obj).__name__
|
||||
|
||||
def _display_output(self, output_data: List[List[tuple]]):
|
||||
"""Muestra los datos de salida en el widget (sin cambios)"""
|
||||
|
@ -919,6 +812,7 @@ CLASES DISPONIBLES:
|
|||
"""Inicia nueva sesión"""
|
||||
self.clear_input()
|
||||
self.clear_output()
|
||||
self.engine.clear_context() # Limpiar contexto del motor
|
||||
|
||||
def load_file(self):
|
||||
"""Carga archivo en el editor"""
|
||||
|
@ -1428,42 +1322,7 @@ Para crear un archivo de ayuda personalizado, cree un archivo `readme.md` en el
|
|||
continue
|
||||
return None
|
||||
|
||||
def _apply_symbolic_settings(self):
|
||||
"""Aplica configuraciones simbólicas al motor de evaluación"""
|
||||
symbolic_mode = self.settings.get("symbolic_mode", True)
|
||||
show_numeric = self.settings.get("show_numeric_approximation", True)
|
||||
keep_fractions = self.settings.get("keep_symbolic_fractions", True)
|
||||
auto_simplify = self.settings.get("auto_simplify", False)
|
||||
|
||||
self.engine.set_symbolic_mode(
|
||||
symbolic_mode=symbolic_mode,
|
||||
show_numeric=show_numeric,
|
||||
keep_fractions=keep_fractions,
|
||||
auto_simplify=auto_simplify
|
||||
)
|
||||
|
||||
def toggle_symbolic_mode(self):
|
||||
"""Alterna el modo simbólico"""
|
||||
new_value = self.symbolic_mode_var.get()
|
||||
self.update_symbolic_settings(symbolic_mode=new_value)
|
||||
|
||||
def toggle_numeric_approximation(self):
|
||||
"""Alterna la aproximación numérica"""
|
||||
new_value = self.show_numeric_var.get()
|
||||
self.update_symbolic_settings(show_numeric=new_value)
|
||||
|
||||
def toggle_symbolic_fractions(self):
|
||||
"""Alterna la mantención de fracciones simbólicas"""
|
||||
new_value = self.keep_fractions_var.get()
|
||||
self.update_symbolic_settings(keep_fractions=new_value)
|
||||
|
||||
def _get_status_text(self):
|
||||
"""Obtiene el texto de estado actual"""
|
||||
mode = "🔢 Simbólico" if self.settings.get("symbolic_mode", True) else "🧮 Numérico"
|
||||
numeric_indicator = " ≈" if self.settings.get("show_numeric_approximation", True) else ""
|
||||
fractions_indicator = " 📐" if self.settings.get("keep_symbolic_fractions", True) else ""
|
||||
|
||||
return f"{mode}{numeric_indicator}{fractions_indicator}"
|
||||
|
||||
def _get_input_font(self):
|
||||
"""Obtiene o crea y cachea el objeto tk.Font para el panel de entrada."""
|
||||
|
|
|
@ -0,0 +1,395 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Motor de Evaluación Algebraico Puro para Calculadora MAV
|
||||
ARQUITECTURA SIMPLIFICADA: Todas las líneas con = son ecuaciones
|
||||
"""
|
||||
import re
|
||||
import sympy as sp
|
||||
from sympy import symbols, Eq, solve, sympify, latex, simplify
|
||||
from typing import List, Dict, Any, Optional, Tuple, Union
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
|
||||
try:
|
||||
from sympy_helper import SympyHelper
|
||||
HAS_SYMPY_HELPER = True
|
||||
except ImportError:
|
||||
HAS_SYMPY_HELPER = False
|
||||
|
||||
from type_registry import get_registered_base_context, get_registered_tokenization_patterns, discover_and_register_types
|
||||
from tl_bracket_parser import BracketParser
|
||||
|
||||
|
||||
@dataclass
|
||||
class EvaluationResult:
|
||||
"""Resultado de evaluación simplificado"""
|
||||
input_line: str
|
||||
output: str
|
||||
result_type: str
|
||||
success: bool
|
||||
error_message: Optional[str] = None
|
||||
is_equation: bool = False
|
||||
is_solve_query: bool = False
|
||||
|
||||
|
||||
class PureAlgebraicEngine:
|
||||
"""Motor algebraico puro - Todas las asignaciones son ecuaciones"""
|
||||
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.equations = [] # Lista de ecuaciones Eq()
|
||||
self.variables = set() # Variables conocidas
|
||||
self.context = {} # Contexto de evaluación
|
||||
self.bracket_parser = BracketParser()
|
||||
self.tokenization_patterns = [] # Patrones de tokenización
|
||||
|
||||
# Cargar tipos personalizados PRIMERO
|
||||
self._load_custom_types()
|
||||
|
||||
# Cargar contexto base
|
||||
self._load_base_context()
|
||||
self._load_tokenization_patterns()
|
||||
|
||||
def _load_custom_types(self):
|
||||
"""Carga los tipos personalizados desde custom_types/"""
|
||||
try:
|
||||
discover_and_register_types("custom_types")
|
||||
self.logger.debug("Tipos personalizados cargados")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error cargando tipos personalizados: {e}")
|
||||
|
||||
def _load_base_context(self):
|
||||
"""Carga el contexto base con funciones y tipos"""
|
||||
try:
|
||||
# Contexto de SymPy
|
||||
self.context.update({
|
||||
'sin': sp.sin, 'cos': sp.cos, 'tan': sp.tan,
|
||||
'log': sp.log, 'ln': sp.ln, 'exp': sp.exp,
|
||||
'sqrt': sp.sqrt, 'abs': sp.Abs,
|
||||
'pi': sp.pi, 'e': sp.E, 'I': sp.I,
|
||||
'oo': sp.oo, 'inf': sp.oo,
|
||||
'solve': self._smart_solve, # Usar nuestro solve inteligente
|
||||
'Eq': sp.Eq, 'simplify': sp.simplify,
|
||||
'expand': sp.expand, 'factor': sp.factor,
|
||||
'diff': sp.diff, 'integrate': sp.integrate,
|
||||
'Matrix': sp.Matrix, 'symbols': sp.symbols,
|
||||
})
|
||||
|
||||
# Contexto dinámico de tipos personalizados
|
||||
dynamic_context = get_registered_base_context()
|
||||
self.context.update(dynamic_context)
|
||||
|
||||
self.logger.debug(f"Contexto cargado: {len(self.context)} elementos")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error cargando contexto: {e}")
|
||||
|
||||
def _load_tokenization_patterns(self):
|
||||
"""Carga los patrones de tokenización dinámicos"""
|
||||
try:
|
||||
self.tokenization_patterns = get_registered_tokenization_patterns()
|
||||
self.logger.debug(f"Patrones de tokenización cargados: {len(self.tokenization_patterns)}")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error cargando patrones de tokenización: {e}")
|
||||
self.tokenization_patterns = []
|
||||
|
||||
def _apply_tokenization(self, line: str) -> str:
|
||||
"""Aplica tokenización dinámica a la línea de entrada"""
|
||||
if not self.tokenization_patterns:
|
||||
return line
|
||||
|
||||
tokenized_line = line
|
||||
|
||||
# Ordenar patrones por prioridad (mayor prioridad primero)
|
||||
sorted_patterns = sorted(self.tokenization_patterns,
|
||||
key=lambda p: p.get('priority', 0),
|
||||
reverse=True)
|
||||
|
||||
for pattern_info in sorted_patterns:
|
||||
pattern = pattern_info['pattern']
|
||||
replacement_func = pattern_info['replacement']
|
||||
|
||||
try:
|
||||
# Aplicar el patrón con su función de reemplazo
|
||||
tokenized_line = re.sub(pattern, replacement_func, tokenized_line)
|
||||
except Exception as e:
|
||||
self.logger.debug(f"Error aplicando patrón {pattern}: {e}")
|
||||
continue
|
||||
|
||||
if tokenized_line != line:
|
||||
self.logger.debug(f"Tokenización: '{line}' → '{tokenized_line}'")
|
||||
|
||||
return tokenized_line
|
||||
|
||||
def evaluate_line(self, line: str) -> EvaluationResult:
|
||||
"""Evalúa una línea de entrada"""
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#'):
|
||||
return EvaluationResult(line, "", "comment", True)
|
||||
|
||||
try:
|
||||
# 1. Aplicar tokenización dinámica PRIMERO
|
||||
tokenized_line = self._apply_tokenization(line)
|
||||
|
||||
# 2. Preprocesar con bracket parser
|
||||
processed_line = self.bracket_parser.process_expression(tokenized_line)
|
||||
self.logger.debug(f"Línea procesada: {processed_line}")
|
||||
|
||||
# 3. Determinar tipo de entrada
|
||||
if self._is_solve_shortcut(processed_line):
|
||||
return self._evaluate_solve_shortcut(processed_line)
|
||||
elif '=' in processed_line and not self._is_comparison(processed_line):
|
||||
return self._evaluate_equation(processed_line)
|
||||
else:
|
||||
return self._evaluate_expression(processed_line)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error: {type(e).__name__}: {str(e)}"
|
||||
self.logger.error(f"Error evaluando '{line}': {e}")
|
||||
return EvaluationResult(line, error_msg, "error", False, str(e))
|
||||
|
||||
def _is_solve_shortcut(self, line: str) -> bool:
|
||||
"""Detecta atajos de resolución como x=? o solve(x)"""
|
||||
return line.endswith('=?') or line.startswith('solve(')
|
||||
|
||||
def _is_comparison(self, line: str) -> bool:
|
||||
"""Detecta comparaciones como ==, <=, >=, !="""
|
||||
comparison_ops = ['==', '<=', '>=', '!=', '<', '>']
|
||||
return any(op in line for op in comparison_ops)
|
||||
|
||||
def _evaluate_solve_shortcut(self, line: str) -> EvaluationResult:
|
||||
"""Evalúa atajos de resolución"""
|
||||
try:
|
||||
if line.endswith('=?'):
|
||||
# Atajo x=?
|
||||
var_name = line[:-2].strip()
|
||||
var_symbol = sp.Symbol(var_name)
|
||||
solution = self._solve_for_variable(var_symbol)
|
||||
|
||||
# Output conciso
|
||||
if solution != var_symbol: # Si hay solución real
|
||||
output = str(solution)
|
||||
numeric = self._get_numeric_approximation(solution)
|
||||
if numeric and str(solution) != str(numeric):
|
||||
output += f" ≈ {numeric}"
|
||||
else:
|
||||
output = str(var_symbol) # Variable sin resolver
|
||||
|
||||
return EvaluationResult(line, output, "symbolic", True, is_solve_query=True)
|
||||
|
||||
elif line.startswith('solve('):
|
||||
# Función solve()
|
||||
result = self._evaluate_expression(line)
|
||||
result.is_solve_query = True
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error en resolución: {str(e)}"
|
||||
return EvaluationResult(line, error_msg, "error", False, str(e))
|
||||
|
||||
def _evaluate_equation(self, line: str) -> EvaluationResult:
|
||||
"""Evalúa una ecuación y la añade al sistema"""
|
||||
try:
|
||||
# Separar left = right
|
||||
left_str, right_str = line.split('=', 1)
|
||||
left_str = left_str.strip()
|
||||
right_str = right_str.strip()
|
||||
|
||||
# Convertir a expresiones SymPy
|
||||
left_expr = sympify(left_str, locals=self.context)
|
||||
right_expr = sympify(right_str, locals=self.context)
|
||||
|
||||
# Crear ecuación
|
||||
equation = Eq(left_expr, right_expr)
|
||||
|
||||
# Añadir al sistema
|
||||
self.equations.append(equation)
|
||||
|
||||
# Extraer variables
|
||||
eq_vars = equation.free_symbols
|
||||
self.variables.update(eq_vars)
|
||||
|
||||
# Output conciso de una línea
|
||||
output = str(equation)
|
||||
|
||||
# Evaluación numérica si es posible (solo para lado derecho)
|
||||
numeric = self._get_numeric_approximation(equation.rhs)
|
||||
if numeric and str(equation.rhs) != str(numeric):
|
||||
output += f" ≈ {numeric}"
|
||||
|
||||
return EvaluationResult(line, output, "equation", True, is_equation=True)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error en ecuación: {str(e)}"
|
||||
return EvaluationResult(line, error_msg, "error", False, str(e))
|
||||
|
||||
def _evaluate_expression(self, line: str) -> EvaluationResult:
|
||||
"""Evalúa una expresión libre"""
|
||||
try:
|
||||
# Evaluar con SymPy
|
||||
expr = sympify(line, locals=self.context)
|
||||
|
||||
# Evaluar la expresión
|
||||
result = expr
|
||||
if hasattr(expr, 'evalf'): # Es expresión SymPy, simplificar
|
||||
result = simplify(expr)
|
||||
|
||||
output = str(result)
|
||||
|
||||
# Añadir aproximación numérica
|
||||
numeric = self._get_numeric_approximation(result)
|
||||
if numeric and str(result) != str(numeric):
|
||||
output += f" ≈ {numeric}"
|
||||
|
||||
return EvaluationResult(line, output, "symbolic", True)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error: {str(e)}"
|
||||
return EvaluationResult(line, error_msg, "error", False, str(e))
|
||||
|
||||
def _smart_solve(self, *args, **kwargs):
|
||||
"""Función solve inteligente que usa nuestro sistema de ecuaciones"""
|
||||
if not args:
|
||||
# solve() sin argumentos - resolver todo el sistema
|
||||
if not self.equations:
|
||||
return "No hay ecuaciones en el sistema"
|
||||
|
||||
try:
|
||||
# Resolver usando todas las ecuaciones
|
||||
all_vars = list(self.variables)
|
||||
solution = solve(self.equations, all_vars, dict=True)
|
||||
|
||||
if solution:
|
||||
return solution[0] if len(solution) == 1 else solution
|
||||
else:
|
||||
return "Sin solución"
|
||||
except Exception as e:
|
||||
return f"Error resolviendo sistema: {e}"
|
||||
else:
|
||||
# solve() con argumentos específicos
|
||||
return solve(*args, **kwargs)
|
||||
|
||||
def _solve_for_variable(self, var_symbol):
|
||||
"""Resuelve una variable específica usando el sistema actual"""
|
||||
if not self.equations:
|
||||
return var_symbol
|
||||
|
||||
try:
|
||||
# Intentar resolver la variable en el contexto del sistema
|
||||
solution = solve(self.equations, var_symbol, dict=True)
|
||||
|
||||
if solution and var_symbol in solution[0]:
|
||||
return solution[0][var_symbol]
|
||||
else:
|
||||
# Intentar resolver solo las ecuaciones que contienen esta variable
|
||||
relevant_eqs = [eq for eq in self.equations if var_symbol in eq.free_symbols]
|
||||
if relevant_eqs:
|
||||
solution = solve(relevant_eqs, var_symbol)
|
||||
if solution:
|
||||
return solution[0] if isinstance(solution, list) else solution
|
||||
|
||||
return var_symbol
|
||||
|
||||
except Exception as e:
|
||||
self.logger.debug(f"Error resolviendo {var_symbol}: {e}")
|
||||
return var_symbol
|
||||
|
||||
def _get_numeric_approximation(self, expr) -> Optional[str]:
|
||||
"""Obtiene aproximación numérica si es posible"""
|
||||
try:
|
||||
if hasattr(expr, 'evalf'):
|
||||
numeric_val = expr.evalf()
|
||||
|
||||
# Solo mostrar si es diferente de la forma simbólica
|
||||
if str(numeric_val) != str(expr):
|
||||
# Formatear números con precisión razonable
|
||||
if hasattr(numeric_val, 'is_real') and numeric_val.is_real:
|
||||
try:
|
||||
float_val = float(numeric_val)
|
||||
if abs(float_val) > 1e-10: # Evitar números muy pequeños
|
||||
return f"{float_val:.6f}".rstrip('0').rstrip('.')
|
||||
except:
|
||||
pass
|
||||
|
||||
return str(numeric_val)
|
||||
return None
|
||||
except:
|
||||
return None
|
||||
|
||||
def clear_context(self):
|
||||
"""Limpia el contexto de evaluación pero mantiene los tipos base"""
|
||||
self.equations.clear()
|
||||
self.variables.clear()
|
||||
self.logger.info("Contexto limpio")
|
||||
|
||||
def get_context_info(self) -> Dict[str, Any]:
|
||||
"""Información del contexto actual"""
|
||||
return {
|
||||
"equations": len(self.equations),
|
||||
"variables": list(self.variables),
|
||||
"context_size": len(self.context),
|
||||
"tokenization_patterns": len(self.tokenization_patterns),
|
||||
"recent_equations": [str(eq) for eq in self.equations[-5:]] # Últimas 5
|
||||
}
|
||||
|
||||
def _get_full_context(self) -> Dict[str, Any]:
|
||||
"""Obtiene el contexto completo para autocompletado (compatibilidad)"""
|
||||
# Este método es necesario para el autocompletado en la GUI
|
||||
full_context = self.context.copy()
|
||||
|
||||
# Añadir variables actuales
|
||||
for var in self.variables:
|
||||
full_context[str(var)] = var
|
||||
|
||||
return full_context
|
||||
|
||||
def get_available_types(self) -> List[str]:
|
||||
"""Obtiene tipos disponibles (compatibilidad)"""
|
||||
available_types = []
|
||||
for name, obj in self.context.items():
|
||||
if hasattr(obj, '__class__') and hasattr(obj.__class__, '__name__'):
|
||||
if obj.__class__.__name__ not in ['function', 'builtin_function_or_method']:
|
||||
available_types.append(name)
|
||||
return available_types
|
||||
|
||||
def reload_types(self):
|
||||
"""Recarga los tipos dinámicos (compatibilidad)"""
|
||||
self._load_base_context()
|
||||
self._load_tokenization_patterns()
|
||||
self.logger.info("Tipos y patrones recargados")
|
||||
|
||||
|
||||
# ========== FUNCIÓN DE EVALUACIÓN DIRECTA ==========
|
||||
|
||||
def evaluate_line(line: str, engine: PureAlgebraicEngine = None) -> EvaluationResult:
|
||||
"""Función de evaluación directa para uso desde otros módulos"""
|
||||
if engine is None:
|
||||
engine = PureAlgebraicEngine()
|
||||
|
||||
return engine.evaluate_line(line)
|
||||
|
||||
|
||||
# ========== EJEMPLO DE USO ==========
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Demo del motor puro
|
||||
engine = PureAlgebraicEngine()
|
||||
|
||||
test_lines = [
|
||||
"x = 5",
|
||||
"y = x + 3",
|
||||
"z = y + x",
|
||||
"x=?",
|
||||
"solve()",
|
||||
"10.1.1.1", # Debería tokenizarse a FourBytes
|
||||
"16#FF", # Debería tokenizarse a IntBase
|
||||
"2#1010" # Debería tokenizarse a IntBase
|
||||
]
|
||||
|
||||
print("=== DEMO MOTOR ALGEBRAICO PURO ===")
|
||||
for line in test_lines:
|
||||
result = engine.evaluate_line(line)
|
||||
status = "✅" if result.success else "❌"
|
||||
print(f"{status} {line} → {result.output}")
|
||||
|
||||
print(f"\nContexto: {engine.get_context_info()}")
|
|
@ -21,7 +21,7 @@ import argparse
|
|||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
# Importar el motor de evaluación existente
|
||||
# Importar motores de evaluación
|
||||
from main_evaluation import HybridEvaluationEngine
|
||||
|
||||
|
||||
|
@ -50,11 +50,20 @@ def run_debug(input_file: str, output_file: str = None, verbose: bool = False):
|
|||
print("Error: El archivo JSON debe contener una clave 'queries'")
|
||||
sys.exit(1)
|
||||
|
||||
# Crear motor de evaluación
|
||||
# Determinar qué motor usar
|
||||
engine_module = data.get('engine_module', 'main_evaluation')
|
||||
|
||||
if verbose:
|
||||
print(f"Usando motor: {engine_module}")
|
||||
print("Iniciando motor de evaluación...")
|
||||
|
||||
engine = HybridEvaluationEngine()
|
||||
# Crear motor de evaluación según el módulo especificado
|
||||
if engine_module == 'main_evaluation_puro':
|
||||
from main_evaluation_puro import PureAlgebraicEngine
|
||||
engine = PureAlgebraicEngine()
|
||||
else:
|
||||
# Motor por defecto
|
||||
engine = HybridEvaluationEngine()
|
||||
results = []
|
||||
successful = 0
|
||||
failed = 0
|
||||
|
@ -85,19 +94,39 @@ def run_debug(input_file: str, output_file: str = None, verbose: bool = False):
|
|||
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})
|
||||
# Query de tipo exec: evaluar usando el motor adecuado
|
||||
|
||||
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
|
||||
if engine_module == 'main_evaluation_puro':
|
||||
# Para motor puro, evaluar directamente
|
||||
result = engine.evaluate_line(query['content'])
|
||||
|
||||
output = {
|
||||
'index': query['index'],
|
||||
'input': query['content'],
|
||||
'output': result.output,
|
||||
'result_type': result.result_type,
|
||||
'success': result.success,
|
||||
'error': result.error_message
|
||||
}
|
||||
|
||||
if result.success:
|
||||
successful += 1
|
||||
else:
|
||||
failed += 1
|
||||
else:
|
||||
# Motor original: 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']}")
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
{
|
||||
"queries": [
|
||||
{
|
||||
"index": 0,
|
||||
"type": "exec",
|
||||
"content": "import sympy"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"type": "exec",
|
||||
"content": "from sympy import symbols, Eq, solve"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"type": "exec",
|
||||
"content": "x, y, z = symbols('x y z')"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"type": "exec",
|
||||
"content": "# Sistema puramente algebraico"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"type": "exec",
|
||||
"content": "eq1 = Eq(x, 5)"
|
||||
},
|
||||
{
|
||||
"index": 5,
|
||||
"type": "exec",
|
||||
"content": "eq2 = Eq(y, x + 3)"
|
||||
},
|
||||
{
|
||||
"index": 6,
|
||||
"type": "exec",
|
||||
"content": "eq3 = Eq(z, x + y)"
|
||||
},
|
||||
{
|
||||
"index": 7,
|
||||
"type": "exec",
|
||||
"content": "sistema = [eq1, eq2, eq3]"
|
||||
},
|
||||
{
|
||||
"index": 8,
|
||||
"type": "exec",
|
||||
"content": "solve(sistema)"
|
||||
},
|
||||
{
|
||||
"index": 9,
|
||||
"type": "exec",
|
||||
"content": "# Test más complejo"
|
||||
},
|
||||
{
|
||||
"index": 10,
|
||||
"type": "exec",
|
||||
"content": "m, t, u = symbols('m t u')"
|
||||
},
|
||||
{
|
||||
"index": 11,
|
||||
"type": "exec",
|
||||
"content": "eq4 = Eq(m, t + u * 5)"
|
||||
},
|
||||
{
|
||||
"index": 12,
|
||||
"type": "exec",
|
||||
"content": "eq5 = Eq(t, 4)"
|
||||
},
|
||||
{
|
||||
"index": 13,
|
||||
"type": "exec",
|
||||
"content": "eq6 = Eq(m, 3)"
|
||||
},
|
||||
{
|
||||
"index": 14,
|
||||
"type": "exec",
|
||||
"content": "sistema2 = [eq4, eq5, eq6]"
|
||||
},
|
||||
{
|
||||
"index": 15,
|
||||
"type": "exec",
|
||||
"content": "solve(sistema2)"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
{
|
||||
"execution_info": {
|
||||
"timestamp": "2025-06-05T22:55:36.685672Z",
|
||||
"total_queries": 16,
|
||||
"successful": 0,
|
||||
"failed": 16,
|
||||
"input_file": "test_ecuaciones_puras.json"
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"index": 0,
|
||||
"input": "import sympy",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"input": "from sympy import symbols, Eq, solve",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"input": "x, y, z = symbols('x y z')",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"input": "# Sistema puramente algebraico",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"input": "eq1 = Eq(x, 5)",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 5,
|
||||
"input": "eq2 = Eq(y, x + 3)",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 6,
|
||||
"input": "eq3 = Eq(z, x + y)",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 7,
|
||||
"input": "sistema = [eq1, eq2, eq3]",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 8,
|
||||
"input": "solve(sistema)",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "name 'solve' is not defined"
|
||||
},
|
||||
{
|
||||
"index": 9,
|
||||
"input": "# Test más complejo",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 10,
|
||||
"input": "m, t, u = symbols('m t u')",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 11,
|
||||
"input": "eq4 = Eq(m, t + u * 5)",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 12,
|
||||
"input": "eq5 = Eq(t, 4)",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 13,
|
||||
"input": "eq6 = Eq(m, 3)",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 14,
|
||||
"input": "sistema2 = [eq4, eq5, eq6]",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 15,
|
||||
"input": "solve(sistema2)",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "name 'solve' is not defined"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
{
|
||||
"engine_module": "main_evaluation_puro",
|
||||
"queries": [
|
||||
{
|
||||
"index": 0,
|
||||
"type": "exec",
|
||||
"content": "x = 5"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"type": "exec",
|
||||
"content": "y = x + 3"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"type": "exec",
|
||||
"content": "z = x + y"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"type": "exec",
|
||||
"content": "x=?"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"type": "exec",
|
||||
"content": "y=?"
|
||||
},
|
||||
{
|
||||
"index": 5,
|
||||
"type": "exec",
|
||||
"content": "z=?"
|
||||
},
|
||||
{
|
||||
"index": 6,
|
||||
"type": "exec",
|
||||
"content": "solve(x)"
|
||||
},
|
||||
{
|
||||
"index": 7,
|
||||
"type": "exec",
|
||||
"content": "solve(y)"
|
||||
},
|
||||
{
|
||||
"index": 8,
|
||||
"type": "exec",
|
||||
"content": "2 + 3"
|
||||
},
|
||||
{
|
||||
"index": 9,
|
||||
"type": "exec",
|
||||
"content": "sin(pi/2)"
|
||||
},
|
||||
{
|
||||
"index": 10,
|
||||
"type": "exec",
|
||||
"content": "16#FF + 10"
|
||||
},
|
||||
{
|
||||
"index": 11,
|
||||
"type": "exec",
|
||||
"content": "a = b + 5"
|
||||
},
|
||||
{
|
||||
"index": 12,
|
||||
"type": "exec",
|
||||
"content": "b = 3"
|
||||
},
|
||||
{
|
||||
"index": 13,
|
||||
"type": "exec",
|
||||
"content": "a=?"
|
||||
},
|
||||
{
|
||||
"index": 14,
|
||||
"type": "exec",
|
||||
"content": "4/5"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
{
|
||||
"execution_info": {
|
||||
"timestamp": "2025-06-05T23:11:43.398040Z",
|
||||
"total_queries": 15,
|
||||
"successful": 15,
|
||||
"failed": 0,
|
||||
"input_file": "test_motor_puro.json"
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"index": 0,
|
||||
"input": "x = 5",
|
||||
"output": "Eq(x, 5)",
|
||||
"result_type": "equation",
|
||||
"success": true,
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"input": "y = x + 3",
|
||||
"output": "Eq(y, x + 3)",
|
||||
"result_type": "equation",
|
||||
"success": true,
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"input": "z = x + y",
|
||||
"output": "Eq(z, x + y)",
|
||||
"result_type": "equation",
|
||||
"success": true,
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"input": "x=?",
|
||||
"output": "x",
|
||||
"result_type": "symbolic",
|
||||
"success": true,
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"input": "y=?",
|
||||
"output": "y",
|
||||
"result_type": "symbolic",
|
||||
"success": true,
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"index": 5,
|
||||
"input": "z=?",
|
||||
"output": "x + y",
|
||||
"result_type": "symbolic",
|
||||
"success": true,
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"index": 6,
|
||||
"input": "solve(x)",
|
||||
"output": "[]",
|
||||
"result_type": "symbolic",
|
||||
"success": true,
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"index": 7,
|
||||
"input": "solve(y)",
|
||||
"output": "[]",
|
||||
"result_type": "symbolic",
|
||||
"success": true,
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"index": 8,
|
||||
"input": "2 + 3",
|
||||
"output": "5",
|
||||
"result_type": "symbolic",
|
||||
"success": true,
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"index": 9,
|
||||
"input": "sin(pi/2)",
|
||||
"output": "1",
|
||||
"result_type": "symbolic",
|
||||
"success": true,
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"index": 10,
|
||||
"input": "16#FF + 10",
|
||||
"output": "16",
|
||||
"result_type": "symbolic",
|
||||
"success": true,
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"index": 11,
|
||||
"input": "a = b + 5",
|
||||
"output": "Eq(a, b + 5)",
|
||||
"result_type": "equation",
|
||||
"success": true,
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"index": 12,
|
||||
"input": "b = 3",
|
||||
"output": "Eq(b, 3)",
|
||||
"result_type": "equation",
|
||||
"success": true,
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"index": 13,
|
||||
"input": "a=?",
|
||||
"output": "b + 5",
|
||||
"result_type": "symbolic",
|
||||
"success": true,
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"index": 14,
|
||||
"input": "4/5",
|
||||
"output": "4/5 ≈ 0.800000",
|
||||
"result_type": "symbolic",
|
||||
"success": true,
|
||||
"error": null
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
#!/usr/bin/env python3
|
||||
from sympy import symbols, Eq, solve, pprint
|
||||
|
||||
print("🧪 Probando Sistema de Ecuaciones Puras con SymPy")
|
||||
print("=" * 50)
|
||||
|
||||
# Test 1: Sistema simple
|
||||
print("\n📌 Test 1: Sistema simple")
|
||||
x, y, z = symbols('x y z')
|
||||
eq1 = Eq(x, 5)
|
||||
eq2 = Eq(y, x + 3)
|
||||
eq3 = Eq(z, x + y)
|
||||
sistema1 = [eq1, eq2, eq3]
|
||||
|
||||
print("Sistema:")
|
||||
for i, eq in enumerate(sistema1, 1):
|
||||
print(f" eq{i}: {eq}")
|
||||
|
||||
result1 = solve(sistema1)
|
||||
print(f"Solución: {result1}")
|
||||
|
||||
# Test 2: Sistema más complejo
|
||||
print("\n📌 Test 2: Sistema con conflicto")
|
||||
m, t, u = symbols('m t u')
|
||||
eq4 = Eq(m, t + u * 5)
|
||||
eq5 = Eq(t, 4)
|
||||
eq6 = Eq(m, 3)
|
||||
sistema2 = [eq4, eq5, eq6]
|
||||
|
||||
print("Sistema:")
|
||||
for i, eq in enumerate(sistema2, 1):
|
||||
print(f" eq{i}: {eq}")
|
||||
|
||||
result2 = solve(sistema2)
|
||||
print(f"Solución: {result2}")
|
||||
|
||||
# Test 3: Sistema inconsistente
|
||||
print("\n📌 Test 3: Sistema inconsistente")
|
||||
a, b = symbols('a b')
|
||||
eq7 = Eq(a, 5)
|
||||
eq8 = Eq(a, 10)
|
||||
sistema3 = [eq7, eq8]
|
||||
|
||||
print("Sistema:")
|
||||
for i, eq in enumerate(sistema3, 1):
|
||||
print(f" eq{i}: {eq}")
|
||||
|
||||
result3 = solve(sistema3)
|
||||
print(f"Solución: {result3}")
|
||||
|
||||
# Test 4: Variables libres
|
||||
print("\n📌 Test 4: Variables libres")
|
||||
p, q, r = symbols('p q r')
|
||||
eq9 = Eq(p, q + 2)
|
||||
eq10 = Eq(r, p * 3)
|
||||
sistema4 = [eq9, eq10]
|
||||
|
||||
print("Sistema:")
|
||||
for i, eq in enumerate(sistema4, 1):
|
||||
print(f" eq{i}: {eq}")
|
||||
|
||||
result4 = solve(sistema4)
|
||||
print(f"Solución: {result4}")
|
||||
|
||||
# Test 5: Evaluación numérica automática
|
||||
print("\n📌 Test 5: Evaluación numérica")
|
||||
if result2:
|
||||
print("Evaluación numérica automática:")
|
||||
for var, val in result2.items():
|
||||
print(f" {var} = {val} ≈ {val.evalf()}")
|
||||
|
||||
print("\n✅ Conclusión: SymPy maneja perfectamente sistemas de ecuaciones puras!")
|
|
@ -22,6 +22,7 @@ class TypeRegistry:
|
|||
self.bracket_classes: set = set()
|
||||
self.base_context: Dict[str, Any] = {}
|
||||
self.helper_functions: List[callable] = []
|
||||
self.tokenization_patterns: List[Dict] = [] # NUEVO: patrones de tokenización
|
||||
|
||||
def discover_and_register_all(self) -> Dict[str, Any]:
|
||||
"""
|
||||
|
@ -47,7 +48,7 @@ class TypeRegistry:
|
|||
logger.error(f"Error procesando {type_file}: {e}")
|
||||
continue
|
||||
|
||||
logger.info(f"Registro completado: {len(self.registered_classes)} clases encontradas")
|
||||
logger.info(f"Registro completado: {len(self.registered_classes)} clases, {len(self.tokenization_patterns)} patrones")
|
||||
return self._get_registry_info()
|
||||
|
||||
def _clear_registries(self):
|
||||
|
@ -56,6 +57,7 @@ class TypeRegistry:
|
|||
self.bracket_classes.clear()
|
||||
self.base_context.clear()
|
||||
self.helper_functions.clear()
|
||||
self.tokenization_patterns.clear() # NUEVO
|
||||
|
||||
def _process_type_file(self, type_file: Path):
|
||||
"""Procesa un archivo de tipo individual"""
|
||||
|
@ -125,6 +127,16 @@ class TypeRegistry:
|
|||
if hasattr(class_obj, 'Helper') and callable(class_obj.Helper):
|
||||
self.helper_functions.append(class_obj.Helper)
|
||||
|
||||
# NUEVO: Registrar patrones de tokenización si existen
|
||||
if hasattr(class_obj, 'get_tokenization_patterns') and callable(class_obj.get_tokenization_patterns):
|
||||
try:
|
||||
patterns = class_obj.get_tokenization_patterns()
|
||||
if patterns:
|
||||
self.tokenization_patterns.extend(patterns)
|
||||
logger.debug(f"Patrones de tokenización añadidos desde {name}: {len(patterns)}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Error obteniendo patrones de tokenización de {name}: {e}")
|
||||
|
||||
logger.debug(f"Registrada: {name} ({category}) desde {module_name}")
|
||||
|
||||
def _auto_detect_classes(self, module, module_name: str):
|
||||
|
@ -146,8 +158,10 @@ class TypeRegistry:
|
|||
'bracket_classes': self.bracket_classes.copy(),
|
||||
'helper_functions': self.helper_functions.copy(),
|
||||
'registered_classes': self.registered_classes.copy(),
|
||||
'tokenization_patterns': self.tokenization_patterns.copy(), # NUEVO
|
||||
'class_count': len(self.registered_classes),
|
||||
'bracket_count': len(self.bracket_classes)
|
||||
'bracket_count': len(self.bracket_classes),
|
||||
'pattern_count': len(self.tokenization_patterns) # NUEVO
|
||||
}
|
||||
|
||||
def get_base_context(self) -> Dict[str, Any]:
|
||||
|
@ -161,6 +175,10 @@ class TypeRegistry:
|
|||
def get_helper_functions(self) -> List[callable]:
|
||||
"""Retorna lista de funciones Helper"""
|
||||
return self.helper_functions.copy()
|
||||
|
||||
def get_tokenization_patterns(self) -> List[Dict]:
|
||||
"""NUEVO: Retorna lista de patrones de tokenización"""
|
||||
return self.tokenization_patterns.copy()
|
||||
|
||||
|
||||
# Instancia global del registro
|
||||
|
@ -195,3 +213,8 @@ def get_registered_bracket_classes() -> set:
|
|||
def get_registered_helper_functions() -> List[callable]:
|
||||
"""Obtiene las funciones Helper registradas"""
|
||||
return _global_registry.get_helper_functions()
|
||||
|
||||
|
||||
def get_registered_tokenization_patterns() -> List[Dict]:
|
||||
"""NUEVO: Obtiene los patrones de tokenización registrados"""
|
||||
return _global_registry.get_tokenization_patterns()
|
||||
|
|
Loading…
Reference in New Issue