From c0e638ec79de621c53c286b3c3fe073a666bb19d Mon Sep 17 00:00:00 2001 From: Miguel Date: Fri, 6 Jun 2025 11:40:34 +0200 Subject: [PATCH] Mejorado del Motor algebraico --- .doc/DESCRIPTION.md | 165 +++ .doc/Desarrollo.md | 426 ------- .doc/Documentacion.md | 712 ----------- .doc/Guia_Base.md | 234 ---- .doc/LLM_DEBUG_API_GUIDE.md | 462 ------- .doc/SISTEMA_TOKENIZACION_DISTRIBUIDA.md | 166 --- .doc/refactoring/debug_api_docs.md | 392 ------ .doc/refactoring/remover_corchetes.md | 1021 --------------- .doc/refactoring/tokenizacion_ditribuida.md | 1262 ------------------- .gitignore | 2 + basic_test_results.json | 81 -- custom_types/intbase_type.py | 50 +- custom_types/ip4_type.py | 86 ++ hybrid_calc_history.txt | 27 +- hybrid_calc_settings.json | 2 +- main_evaluation_puro.py | 274 ++-- test_ecuaciones_puras.json | 84 -- test_ecuaciones_puras_results.json | 139 -- test_motor_puro.json | 80 -- test_motor_puro_results.json | 131 -- test_sympy_puro.py | 72 -- 21 files changed, 490 insertions(+), 5378 deletions(-) create mode 100644 .doc/DESCRIPTION.md delete mode 100644 .doc/Desarrollo.md delete mode 100644 .doc/Documentacion.md delete mode 100644 .doc/Guia_Base.md delete mode 100644 .doc/LLM_DEBUG_API_GUIDE.md delete mode 100644 .doc/SISTEMA_TOKENIZACION_DISTRIBUIDA.md delete mode 100644 .doc/refactoring/debug_api_docs.md delete mode 100644 .doc/refactoring/remover_corchetes.md delete mode 100644 .doc/refactoring/tokenizacion_ditribuida.md delete mode 100644 basic_test_results.json delete mode 100644 test_ecuaciones_puras.json delete mode 100644 test_ecuaciones_puras_results.json delete mode 100644 test_motor_puro.json delete mode 100644 test_motor_puro_results.json delete mode 100644 test_sympy_puro.py diff --git a/.doc/DESCRIPTION.md b/.doc/DESCRIPTION.md new file mode 100644 index 0000000..8d85f43 --- /dev/null +++ b/.doc/DESCRIPTION.md @@ -0,0 +1,165 @@ + +# MAV Calculator Application Logic Documentation + +## Overview + +The MAV Calculator is a hybrid Computer Algebra System (CAS) built around a pure algebraic engine that treats all assignments as equations. The application follows a clean pipeline from user input to result display, with sophisticated tokenization and symbolic computation capabilities. + +### The application has this main components: + +* Algebraic components using sympy engine with sympify, solve, evalf. This includes all the mathematical functions and the functions of sympy. +* Special objects called Types or classes under custom_types folder. + * This classes are automatically discovered at startup. + * every class has functions that some are for the interaction with other clases o with sympy. Other functions can be used directly by the user with parameters and returning the same object or other object. + * special tokenization patterns & substitution using data collected from classes under custom_types folder. + * every class has the Helper function to assist the user when on the line it is detected an error and the line starts with some patter + * every class has a descriptor of functions that can be used for this object to permit the autocomplete popup help the user when they press "." + +### Concepts + +* Types : Special classes under custom_types folder. That are automatically discovered at startup. +* Cycle: every time the user change the input panel start a cycle that ends when all lines are evaluated and produced an output +* Input panel: tk text area where the user can write or modify text. +* Output panel: read only tk area text correlated 1:1 to every line on the input panel . Every input line must correspond to only 1 line on the output. The output lines can have colors and binds to click for opening the plots or to show matrix or lists. +* Persistence: The app maintain the state of all configurated setup and dimension of the window and all the text on the input panel. + +## Application Architecture + +### Entry Point and Initialization + +The application starts through `calc.py`, which serves as a launcher with comprehensive logging and error handling. It initializes the main GUI application (`HybridCalculatorApp`) which in turn creates a `PureAlgebraicEngine` instance as its computational core. + +During initialization, the system performs auto-discovery of custom types from the `custom_types/` directory. Each type module can define tokenization patterns, helper functions, and computational behaviors. This creates a registry of available types and their associated transformation rules. + +## Calculation Cycle Flow + +### 1. Input Capture and Triggering + +The calculation cycle begins when the user types in the input panel. The GUI uses a debounced key release handler that triggers evaluation after a 300ms delay, preventing excessive computation during active typing. + +### 2. Line Processing Pipeline + +The engine processes all input lines sequentially, maintaining a clean context for each evaluation cycle: + +**Context Reset**: Before each evaluation cycle, the engine clears its internal context completely. This ensures that each modification to the input re-evaluates everything from scratch, maintaining consistency. + +**Line Classification**: Each line is categorized as: +- Empty or comment (ignored) +- Equation (contains `=` but not as comparison operator) +- Solve shortcut (pattern `variable=?`) +- Expression (everything else) + +### 3. Tokenization System + +The tokenization system operates through a distributed pattern-matching architecture: + +**Pattern Discovery**: During initialization, the system discovers tokenization patterns from all registered type classes. Each type can define multiple patterns with priorities and replacement functions. + +**Pattern Application**: Tokenization applies patterns in priority order (higher priority first). Each pattern consists of: +- A regular expression to match +- A replacement function that transforms the match +- Priority level for ordering +- Description for debugging + +**Example Transformations**: +- `192.168.1.1` becomes `FourBytes("192.168.1.1")` +- `16#FF` becomes `IntBase("FF", 16)` +- `2#1010` becomes `IntBase("1010", 2)` + +**Bracket Parser Integration**: After tokenization, the bracket parser processes any remaining bracket syntax transformations for legacy compatibility. ==Must be removed== + +Also every constructor of every Type must be instantiated using eval. For example if a line has "a = IP4(10.1.1.1)" the tokeniser first will translate to "a = IP4(FourBytes("10.1.1.1"))" then eval needs to instantiate the IP4 object so the a will be equal to an object IP4. +### 4. Algebraic Engine Processing + +The `PureAlgebraicEngine` implements a pure algebraic approach where all assignments become equations: + +**Equation Detection**: The engine distinguishes between: +- Simple assignments (`x = 5`) - creates both a variable assignment and an implicit equation- +- Complex equations (`x**2 + 2*x = 8`) - added directly to the equation system +- Solve shortcuts (`x=?`) - equivalent to `solve(x)` using the current equation system + +**Symbol Management**: Variables are automatically and must maintain the type as long as possible. Also if there are aritmetic use with Types we need to cast the result to the Type involved. For example if we have "IP4(10.1.1.1)+1" the result must be IP4 object. The engine maintains a symbol table and equation list that increase every line that persist until the end of the cycle. + +**Context Building**: For each evaluation, the engine builds a complete context including: +- Base mathematical functions (sin, cos, solve, etc.) +- Dynamically registered custom types +- Current variable assignments +- Helper functions from type registry + +### 5. Expression Evaluation Strategy + +The engine uses a hybrid evaluation strategy: + +**Symbolic Priority**: Expressions are first attempted through SymPy's `sympify` to maintain symbolic forms. This preserves exact mathematical representations. + +**Automatic Substitution**: +* The engine automatically substitutes known numerical values into symbolic expressions when possible, while preserving symbolic forms for unknown variables. +* ==Funtions + + +**Numerical Approximation**: When beneficial, the engine provides numerical approximations alongside symbolic results. The decision is based on whether the numerical form differs meaningfully from the symbolic representation. + +### 6. Result Processing and Display + +**Result Classification**: Each evaluation produces an `EvaluationResult` with: +- The computed result (symbolic or numerical) +- Result type classification +- Success/error status +- Optional numerical approximation +- Metadata about the evaluation + +**Interactive Elements**: The display system identifies results that benefit from interactivity: +- Large matrices become expandable views +- Plot commands create interactive matplotlib windows +- Long lists become scrollable displays +- Complex objects get detailed inspection windows + +**Tag-Based Formatting**: Results are displayed with color-coded tags based on their type and semantic meaning. The GUI applies appropriate formatting for equations, errors, symbolic expressions, and custom type outputs. + +## Type System Integration + +### Dynamic Type Loading + +The type system loads custom classes from the `custom_types/` directory at startup. Each type module can define: +- Class behaviors and arithmetic operations +- Tokenization patterns for automatic recognition +- Helper functions for contextual assistance +- Display and conversion methods + +### Inheritance Patterns + +Types inherit from either: +- `ClassBase` - for basic custom types +- `SympyClassBase` - for types requiring full SymPy integration + +This allows seamless interaction between custom types and the symbolic algebra system. + +### Method Discovery + +The system automatically discovers and exposes methods from custom types for autocompletion and help systems. This includes both instance methods and static helper functions. + +## Error Handling and Recovery + +The application implements comprehensive error handling at each stage: + +**Tokenization Errors**: Failed tokenization patterns are logged but don't halt processing, allowing partial transformations to proceed. + +**Evaluation Errors**: Helper from classes first, then if there is no match SymPy evaluation errors trigger fallback strategies, including symbol creation for undefined variables and alternative parsing approaches. + +**Display Errors**: Rendering failures fall back to string representations, ensuring results are always visible even if formatting fails. + +## Performance Characteristics + +**Lazy Evaluation**: Complex computations are deferred until needed, with caching of intermediate results where appropriate. + +**Incremental Processing**: Only changed content triggers re-evaluation, though the current implementation re-evaluates everything for consistency. + +**Memory Management**: The system manages symbol tables and equation lists efficiently, with cleanup mechanisms for large sessions. + +## Session Persistence + +The application maintains session continuity through: + +**History Persistence**: Input content is automatically saved and restored between sessions. +**Context Preservation**: Variable assignments and equations persist during the scope of every cycle. The context is added on every line and cleared at the begining of ever cycle. This means that every time the user changes the input panel the context is cleared. +**Settings Management**: UI state and preferences are preserved across application restarts. diff --git a/.doc/Desarrollo.md b/.doc/Desarrollo.md deleted file mode 100644 index 9f62b4c..0000000 --- a/.doc/Desarrollo.md +++ /dev/null @@ -1,426 +0,0 @@ -# Guía de Desarrollo - Calculadora MAV CAS Híbrido - -## Visión General - -Este documento describe el estado actual y los objetivos de desarrollo para la **Calculadora MAV**, un **Sistema de Álgebra Computacional (CAS) híbrido**. El proyecto busca combinar la potencia de SymPy como motor algebraico central con capacidades especializadas (IP4, Hex, etc.) integradas de forma nativa. - -## Objetivos Principales - -### 1. **SymPy como Motor Central** -- ✅ Integrar todas las funciones de SymPy directamente -- ✅ SymPy maneja toda la evaluación algebraica y ecuaciones -- ✅ Cada línea de ingreso corresponde a una sola línea de resultados -- ✅ **CONTEXTO LIMPIO POR EVALUACIÓN**: Cada vez que se hace una evaluación se comienza con el contexto completamente limpio y se evalúa desde arriba hacia abajo, línea por línea. Esto garantiza comportamiento predecible sin "memoria" de evaluaciones anteriores. -- ✅ La interfaz de usuario se divide en 2 columnas, a la izquierda el área de ingreso de datos y ecuaciones a ser evaluadas, a la derecha el área de resultados que se colorean según cada tipo de respuesta. - -### 2. **Sistema de Tokenización Automática** ⭐ **ACTUALIZADO** -- ❌ ~~Usar **exclusivamente** `Class[args]` en lugar de `Class("args")`~~ **ELIMINADO** -- ✅ **Tokenización automática de patrones específicos**: - - `16#FF` → `IntBase('FF', 16)` automáticamente - - `192.168.1.1` → `FourBytes('192.168.1.1')` automáticamente -- ✅ **Sistema distribuido**: Cada clase define sus propios patrones de tokenización -- ✅ **Precedencia automática**: Patrones más específicos tienen mayor prioridad -- ✅ **Parser genérico**: No hardcodea lógica específica de tipos - -### 3. **Detección Automática y Manejo de Ecuaciones** -- ✅ Ecuaciones ingresadas directamente (ej. `3+b=5+c`) se detectan y añaden al sistema interno de SymPy. -- ✅ Soporte para comparaciones SymPy (ej. `x > 5`, `a == b`, `x <= 10`) como expresiones evaluables. -- ✅ Sintaxis de atajo para solve: `variable=?` equivale a `solve(variable)` - -### 4. **Evaluación Híbrida con Variables SymPy Puras** -- ✅ Resultado simbólico (siempre disponible) -- ✅ Evaluación numérica con `.evalf()` (cuando sea aplicable y las variables estén definidas). Esta evaluación se muestra a la derecha del resultado simbólico. -- ✅ Las asignaciones son siempre simbólicas, solo los resultados se muestra la evaluación numérica. -- ✅ **Todas las variables son símbolos SymPy**: Sin variables Python tradicionales -- 🔄 **Conversión perezosa a SymPy**: Objetos nativos hasta que se necesite álgebra compleja - -### 5. **Resultados Interactivos** -- ✅ **Tags clickeables** en el widget Text para resultados complejos que necesitan más de una línea de resultado como Plot, Matrices y Listas. -- ✅ **Detección automática** de tipos que requieren visualización especial: - - ✅ Plots de SymPy → "📊 Ver Plot" (abre ventana matplotlib). - - ✅ Matrices → "📋 Ver Matriz" (muestra matriz formateada). - - ✅ Listas largas → "📋 Ver Lista" (muestra contenido expandido). - - ✅ Objetos complejos → "🔍 Ver Detalles" (representación completa). -- ✅ **Implementación con tags**: Usar `text.tag_bind()` en widget existente -- ✅ **Ventanas popup** para contenido que no cabe en línea. - -### 6. **Sistema de Tipos Auto-Descubrible** ⭐ **NUEVO** -- ✅ **Directorio unificado**: Todos los tipos en `custom_types/` -- ✅ **Auto-registro**: Sistema automático de descubrimiento de clases -- ✅ **Función de registro**: Cada módulo tiene `register_classes_in_module()` -- ✅ **Metadatos**: Categorías, opciones y descripciones por tipo -- ✅ **Helpers dinámicos**: Sistema de ayuda contextual distribuido - -### 7. **Clases Base Universales** ⭐ **NUEVO** -- ✅ **IntBase**: Representación universal de números en cualquier base -- ✅ **FourBytes**: Representación universal de patrones x.x.x.x -- ✅ **Manejo interno de símbolos**: Sin conversión prematura a SymPy -- ✅ **Aritmética nativa**: Operaciones que mantienen el tipo original -- ✅ **Conversión explícita**: `.to_sympy()` cuando se necesite álgebra compleja - -## Arquitectura Actual ⭐ **ACTUALIZADA** - -### Estructura de archivos -``` -/ -├── logs/ # Subcarpeta de logs -├── docs/ # Documentación técnica -├── custom_types/ # ⭐ NUEVO: Sistema de tipos unificado -│ ├── intbase_type.py # IntBase + tokenización + registro -│ ├── fourbytes_type.py # FourBytes + tokenización + registro -│ ├── hex_type.py # Hex (usa IntBase) -│ ├── bin_type.py # Bin (usa IntBase) -│ ├── dec_type.py # Dec -│ ├── chr_type.py # Chr -│ ├── ip4_type.py # IP4 + IP4Mask (usa FourBytes) -│ └── latex_type.py # LaTeX -├── calc.py # Launcher principal -├── main_calc_app.py # Aplicación principal -├── main_evaluation.py # Motor de evaluación híbrido -├── type_registry.py # ⭐ NUEVO: Auto-descubrimiento de tipos -├── tl_popup.py # Motor de ventanas emergentes -├── tl_bracket_parser.py # ⭐ ACTUALIZADO: Tokenizador universal -├── class_base.py # Clases base -├── sympy_Base.py # Base para integración SymPy -└── sympy_helper.py # Helpers para SymPy -``` - -### Flujo de Procesamiento ⭐ **ACTUALIZADO** -``` -Entrada Usuario → [Universal Tokenizer] → [Evaluación Híbrida] → { - 1. Resultado Nativo (FourBytes, IntBase, etc.) - 2. Conversión a SymPy (solo si es necesario) - 3. Evaluación numérica (evalf si aplica) - 4. Métodos especializados disponibles -} -``` - -### Transformación de Sintaxis ⭐ **ACTUALIZADA** -```python -# Tokenización automática (NUEVO): -16#FF + 2#1010 → IntBase("FF", 16) + IntBase("1010", 2) -192.168.1.1 + 5 → FourBytes("192.168.1.1") + 5 -IP4(10.1.1.1, 255.255.0.0) → IP4(FourBytes("10.1.1.1"), FourBytes("255.255.0.0")) - -# Detección contextual de ecuaciones (EXISTENTE): -3+b=5+c → Se detecta como ecuación standalone (agregar al sistema SymPy) -"x > 5" → Se procesa como desigualdad SymPy -x**2 + 2*x == 0 → Se detecta como igualdad lógica standalone - -# Sintaxis de atajo para solve (EXISTENTE): -x=? → solve(x) -``` - -## Componentes Técnicos ⭐ **ACTUALIZADOS** - -### 1. **Sistema de Auto-Descubrimiento de Tipos** -```python -# type_registry.py -class TypeRegistry: - def discover_and_register_all(self): - """Descubre y registra todas las clases en custom_types/""" - for type_file in self.types_directory.glob("*_type.py"): - self._process_type_file(type_file) - - def _register_classes_from_info(self, class_info_list): - """Registra clases basándose en metadatos""" - for name, class_obj, category, options in class_info_list: - self._register_single_class(name, class_obj, category, options) -``` - -### 2. **Tokenizador Universal Distribuido** -```python -# tl_bracket_parser.py -class UniversalTokenizer: - def _discover_tokenization_rules(self): - """Auto-descubre reglas desde todas las clases registradas""" - for class_name, class_obj in registered_classes.items(): - if hasattr(class_obj, 'get_tokenization_patterns'): - patterns = class_obj.get_tokenization_patterns() - for pattern_info in patterns: - self.tokenization_rules.append({ - 'class_name': class_name, - 'class_obj': class_obj, - **pattern_info - }) -``` - -### 3. **Clases con Tokenización Propia** -```python -# custom_types/intbase_type.py -class IntBase(ClassBase): - @staticmethod - def get_tokenization_patterns(): - return [ - { - 'pattern': r'(\d+)#([0-9A-Fa-fx]+)', - 'replacement': lambda match: f'IntBase("{match.group(2)}", {match.group(1)})', - 'priority': 5, - 'description': 'Números con base: 16#FF, 2#1010' - } - ] -``` - -### 4. **Conversión Perezosa a SymPy** ⭐ **NUEVO CONCEPTO** -```python -class FourBytes(ClassBase): # No SympyClassBase - def substitute(self, **kwargs): - """Sustitución interna sin involucrar SymPy""" - # Lógica nativa - - def to_sympy(self): - """Conversión EXPLÍCITA cuando se necesite álgebra""" - # Solo convertir cuando se requiera -``` - -### 5. **Sistema de Evaluación Integrado** -```python -# main_evaluation.py -class HybridEvaluationEngine: - def __init__(self, auto_discover_types=True): - # Auto-descubrir tipos dinámicamente - self.registered_types_info = discover_and_register_types() - - # Configurar parser con tokenización distribuida - self.parser = BracketParser(enable_tokenization=True) - - # Configurar contexto con tipos descubiertos - self._setup_base_context() -``` - -## Capacidades Expandidas ⭐ **ACTUALIZADAS** - -### 1. **Funciones SymPy Completas con Objetos Nativos** -```python -# Objetos nativos en álgebra (conversión automática cuando es necesario): -diff(IP4(192.168.x.1) + offset, x) # FourBytes → SymPy automático -integrate(Hex(16#x0) * sin(x), x) # IntBase → SymPy automático -solve([IP4(192.168.x.y) == IP4(192.168.1.10)], [x, y]) - -# Aritmética nativa (mantiene tipos): -base_ip = 192.168.1.1 # → FourBytes automático -next_ip = base_ip + 5 # → FourBytes('192.168.1.6') -hex_val = 16#FF + 16#10 # → IntBase('10F', 16) -``` - -### 2. **Conversiones Automáticas Bidireccionales** -```python -# Conversiones fluidas entre tipos: -ip = 192.168.1.1 # → FourBytes automático -ip_hex = ip.ToHex() # → "16#C0.16#A8.16#1.16#1" -mask = 255.255.255.0 # → FourBytes automático -mask_cidr = IP4Mask(mask) # → /24 automático - -# Constructores inteligentes: -IP4(192.168.1.1, 16#ffffff00) # FourBytes + IntBase → IP4 -Hex(192.168.1.1) # FourBytes → Hex automático -``` - -### 3. **Sistema de Ayuda Dinámico** -```python -# Helpers auto-descubiertos: -help_functions = get_registered_helper_functions() # De todas las clases -help_context = obtener_ayuda("16#FF") # Helper de IntBase -autocompletado = obj.PopupFunctionList() # Métodos disponibles -``` - -## Ejemplos de Uso Objetivo ⭐ **ACTUALIZADOS** - -```python -# Tokenización automática de patrones: -16#FF + 2#1010 # → IntBase('FF', 16) + IntBase('1010', 2) -192.168.1.1 + 5 # → FourBytes('192.168.1.6') -10.x.1.y # → FourBytes('10.x.1.y') simbólico - -# Constructores inteligentes: -IP4(192.168.1.1, 24) # → IP4(FourBytes('192.168.1.1'), 24) -IP4(10.1.1.1, 255.255.0.0) # → Ambos FourBytes automáticamente -Hex(192.168.1.1) # → Conversión FourBytes → Hex - -# Conversiones fluidas: -mask = 255.255.255.0 # → FourBytes automático -mask.ToHex() # → "16#FF.16#FF.16#FF.16#0" -mask.ToBase(2) # → "2#11111111.2#11111111.2#11111111.2#0" - -# Álgebra con objetos nativos: -network = 10.x.0.0 # → FourBytes simbólico -solve(network + 256 == 10.5.0.0, x) # → x = 5 - -# Aritmética que preserva tipos: -base = 192.168.0.0 # → FourBytes -for i in range(4): - subnet = base + (i * 256) # → Cada resultado es FourBytes - print(f"Red {i}: {subnet}") - -# Evaluación directa con SymPy: -solve(x + 10 - 15, x) # → [5] -diff(16#x0, x) # → 16*log(16) (conversión automática) - -# Variables puras SymPy: -x = 5 # → x es Symbol('x') con valor 5 -y = x + 2 # → y es expresión simbólica: Symbol('x') + 2 - -# Funciones avanzadas con resultados interactivos: -plot(sin(2*pi*16#x/256), (x, 0, 255)) # → "📊 Ver Plot" -Matrix([[1, 2, 3], [4, 5, 6]]) # → "📋 Ver Matriz" -``` - -## Beneficios Logrados ⭐ **ACTUALIZADOS** - -### 1. **Usabilidad Mejorada** -- ✅ Sintaxis natural sin artificios (no más `Class[args]`) -- ✅ Tokenización invisible al usuario -- ✅ Acceso completo a capacidades de CAS -- ✅ Evaluación automática simbólica + numérica - -### 2. **Potencia Matemática Expandida** -- ✅ Todas las funciones de SymPy disponibles -- ✅ Objetos especializados participan en álgebra cuando es necesario -- ✅ Aritmética nativa que preserva tipos -- ✅ Conversiones automáticas bidireccionales - -### 3. **Extensibilidad Real** -- ✅ Sistema verdaderamente modular sin hardcoding -- ✅ Fácil agregar nuevos tipos sin modificar código core -- ✅ Tokenización distribuida y auto-descubrible -- ✅ Compatible con ecosystem SymPy completo - -### 4. **Arquitectura Limpia** -- ✅ Responsabilidad única: cada clase maneja su tokenización -- ✅ Parser genérico sin conocimiento específico de tipos -- ✅ Auto-descubrimiento completo del sistema de tipos -- ✅ Manejo unificado de errores - -## Sistema de Autocompletado y Ayuda Contextual ⭐ **IMPLEMENTADO** - -### 1. **Helpers Dinámicos** -- ✅ **Auto-descubrimiento**: Sistema obtiene helpers de todas las clases registradas -- ✅ **Ayuda contextual**: Cada tipo define su propio `Helper(input_str)` -- ✅ **Integración con SymPy**: Helper especializado para funciones SymPy -- ✅ **Manejo centralizado**: Lista dinámica de helpers disponibles - -### 2. **Autocompletado Inteligente** -- ✅ **Popup tras punto**: Evaluación automática del objeto para métodos disponibles -- ✅ **PopupFunctionList()**: Cada clase define sus métodos sugeridos -- ✅ **Filtrado inteligente**: Solo métodos útiles y públicos -- ✅ **No invasivo**: Solo aparece cuando el usuario lo solicita - -### 3. **Ejemplos de Integración** -```python -# Sistema distribuido: -HELPERS = get_registered_helper_functions() # Auto-descubierto - -# Ayuda contextual: -16#FF. # → Popup con métodos de IntBase -192.168.1.1. # → Popup con métodos de FourBytes -IP4(10.1.1.1). # → Popup con métodos de IP4 - -# Helpers específicos: -ayuda = obtener_ayuda("16#FF") # → "Números con base: 16#FF, 2#1010..." -``` - -## Estado de Implementación ⭐ **ACTUALIZADO** - -### ✅ **Completamente Implementado** -- **Sistema de auto-descubrimiento**: `type_registry.py` -- **Tokenizador universal**: `tl_bracket_parser.py` refactorizado -- **Clases base universales**: `intbase_type.py`, `fourbytes_type.py` -- **Motor híbrido integrado**: `main_evaluation.py` con auto-descubrimiento -- **Tipos en custom_types/**: Todos los tipos migrados al sistema unificado -- **Helpers dinámicos**: Sistema de ayuda contextual distribuido -- **Resultados interactivos**: Tags clickeables y ventanas popup -- **Contexto limpio**: Evaluación completa desde cero en cada cambio - -### 🔄 **En Progreso** -- **Conversión perezosa a SymPy**: Algunos objetos aún se convierten prematuramente -- **Aritmética que preserva tipos**: Necesita refinamiento para todos los casos -- **Optimización de precedencia**: Ajuste fino de prioridades de tokenización - -### 📋 **Pendiente** -- **Tipos adicionales**: MAC addresses, timestamps, coordenadas -- **Performance optimization**: Caching y evaluación lazy -- **Documentación de usuario**: Guías y ejemplos actualizados - -## Métricas de Éxito ⭐ **ACTUALIZADAS** - -- ✅ **Sistema de tokenización automática funcional**: - - ✅ Patrones `16#FF`, `2#1010` → IntBase automático - - ✅ Patrones `192.168.1.1` → FourBytes automático - - ✅ Precedencia automática por especificidad - - ✅ Parser genérico sin hardcoding - -- ✅ **Sistema de tipos auto-descubrible**: - - ✅ Todas las clases en `custom_types/` - - ✅ Función `register_classes_in_module()` en cada módulo - - ✅ Auto-registro con metadatos y opciones - - ✅ Helpers dinámicos desde todas las clases - -- ✅ **Clases base universales funcionales**: - - ✅ IntBase con aritmética nativa y tokenización propia - - ✅ FourBytes con aritmética nativa y tokenización propia - - ✅ Conversión explícita `.to_sympy()` cuando sea necesario - - ✅ Operadores que mantienen tipos nativos - -- ✅ **Motor híbrido integrado**: - - ✅ Auto-descubrimiento de tipos al inicializar - - ✅ Tokenización distribuida automática - - ✅ Contexto limpio por evaluación - - ✅ Evaluación simbólica + numérica - -- 🔄 **Conversión perezosa optimizada**: - - ✅ Objetos nativos por defecto - - 🔄 Conversión solo cuando se necesite álgebra compleja - - 🔄 Aritmética preservando tipos en todos los casos - -- ✅ **Detección contextual de ecuaciones**: - - ✅ Ecuaciones standalone detectadas y agregadas al sistema - - ✅ Sintaxis de atajo `variable=?` → `solve(variable)` - - ✅ Protección contra detección en argumentos de función - -- ✅ **Resultados interactivos**: - - ✅ Tags clickeables para plots, matrices, listas - - ✅ Ventanas popup con posicionamiento inteligente - - ✅ Detección automática de tipos complejos - -- ✅ **Sistema de ayuda avanzado**: - - ✅ Helpers auto-descubiertos desde todas las clases - - ✅ Autocompletado tras punto con métodos disponibles - - ✅ Ayuda contextual no invasiva - -- ✅ **Performance aceptable**: Debounce y evaluación optimizada -- ✅ **Manejo robusto de errores**: Logging centralizado y presentación clara -- ✅ **Documentación actualizada**: Guías técnicas y de desarrollo - -## Consideraciones Futuras - -### **Tipos Adicionales Planeados** -```python -custom_types/ -├── mac_type.py # Direcciones MAC: AA:BB:CC:DD:EE:FF -├── time_type.py # Timestamps: HH:MM:SS, ISO format -├── coordinate_type.py # Coordenadas: lat,lon -├── ipv6_type.py # IPv6: 2001:db8::1 -└── range_type.py # Rangos: 1..100, a..z -``` - -### **Optimizaciones de Performance** -- Caching de patrones de tokenización frecuentes -- Evaluación lazy de `evalf()` solo cuando se muestre -- Memoización de conversiones SymPy costosas - -### **Extensiones del Parser** -- Soporte para sintaxis de múltiples líneas -- Macros y templates definidos por usuario -- Validación contextual más sofisticada - -## Conclusión - -La calculadora MAV ha evolucionado hacia un **sistema verdaderamente híbrido y extensible** que combina: - -1. **Potencia algebraica completa** de SymPy -2. **Objetos nativos especializados** con aritmética propia -3. **Tokenización automática invisible** al usuario -4. **Arquitectura modular** sin hardcoding -5. **Auto-descubrimiento completo** del sistema de tipos - -El resultado es una herramienta que es **más simple para el usuario** (sintaxis natural) pero **más poderosa internamente** (álgebra simbólica + tipos especializados), con una **arquitectura limpia y extensible** que facilita agregar nuevas capacidades sin modificar código existente. \ No newline at end of file diff --git a/.doc/Documentacion.md b/.doc/Documentacion.md deleted file mode 100644 index 0ab8f5e..0000000 --- a/.doc/Documentacion.md +++ /dev/null @@ -1,712 +0,0 @@ -# Calculadora MAV - CAS Híbrido - -## Descripción General - -La Calculadora MAV es un **Sistema de Álgebra Computacional (CAS) Híbrido** que combina la potencia de SymPy con clases especializadas para networking, programación y análisis numérico, usando un **sistema de tokenización automática** que convierte patrones específicos en objetos tipados. - -### Características Principales - -- **Motor SymPy completo**: Todas las funciones de cálculo simbólico -- **Tokenización automática invisible**: `16#FF` y `192.168.1.1` se convierten automáticamente en objetos tipados -- **Sistema de tipos dinámico**: Auto-descubrimiento desde `custom_types/` -- **Detección automática de ecuaciones**: Sin necesidad de comillas especiales -- **Resultados interactivos**: Plots, matrices y listas clickeables -- **Clases especializadas**: IntBase, FourBytes, IP4, Hex, Bin, Chr y más -- **Variables SymPy puras**: Todas las variables son símbolos automáticamente -- **Contexto limpio por evaluación**: Comportamiento predecible sin "memoria" de evaluaciones anteriores - -## Instalación - -### Método 1: Instalación Automática -```bash -python calc.py --setup -``` - -### Método 2: Instalación Manual -```bash -# Instalar dependencias -pip install sympy matplotlib numpy - -# En Linux: instalar tkinter -sudo apt-get install python3-tk - -# Ejecutar tests (opcional) -python test_suite.py - -# Iniciar aplicación -python calc.py -``` - -### Dependencias Requeridas -- **Python 3.8+** -- **SymPy ≥ 1.12** (motor algebraico) -- **Matplotlib ≥ 3.7.0** (plotting) -- **NumPy ≥ 1.24.0** (cálculos numéricos) -- **Tkinter** (interfaz gráfica, incluido con Python) - -### Dependencias Opcionales -- **Markdown ≥ 3.4.0** (ayuda mejorada) -- **pytest ≥ 7.0.0** (testing) - -## Guía de Uso - -### Tokenización Automática ⭐ **NUEVO** - -La aplicación reconoce patrones específicos y los convierte automáticamente en objetos tipados: - -#### Números con Base (IntBase) -```python -# Tokenización automática de números con base -16#FF # → IntBase('FF', 16) = 255 -2#1010 # → IntBase('1010', 2) = 10 -8#777 # → IntBase('777', 8) = 511 -16#x0 # → IntBase('x0', 16) = simbólico - -# Aritmética que mantiene la base original -16#FF + 16#10 # → IntBase('10F', 16) = 271 -2#1010 * 3 # → IntBase('11110', 2) = 30 -``` - -#### Patrones x.x.x.x (FourBytes) -```python -# Tokenización automática de patrones dotted -192.168.1.1 # → FourBytes('192.168.1.1') -255.255.255.0 # → FourBytes('255.255.255.0') -10.x.1.y # → FourBytes('10.x.1.y') = simbólico - -# Aritmética de direcciones (32-bit) -192.168.1.1 + 5 # → FourBytes('192.168.1.6') -10.0.0.0 + 256 # → FourBytes('10.0.1.0') -``` - -### Clases Especializadas - -#### Uso Natural con Constructores -```python -# Los constructores reciben objetos ya tokenizados -IP4(192.168.1.100, 24) # FourBytes automático -IP4(10.0.0.1, 255.255.0.0) # Ambos son FourBytes -Hex(16#FF) # IntBase automático -Dec(255) # Entero normal -Chr(65) # Carácter ASCII -``` - -#### Métodos de Clases Especializadas -```python -# Métodos de IP4 -ip = IP4(192.168.1.100, 24) -ip.NetworkAddress() # → IP4(192.168.1.0, 24) -ip.BroadcastAddress() # → IP4(192.168.1.255, 24) -ip.Nodes() # → 254 (hosts disponibles) - -# Conversiones de IntBase -val = 16#FF # → IntBase('FF', 16) -val.to_decimal() # → 255 -val.to_base(2) # → IntBase('11111111', 2) - -# Conversiones de FourBytes -addr = 192.168.1.1 # → FourBytes('192.168.1.1') -addr.ToHex() # → "16#C0.16#A8.16#1.16#1" -addr.ToBinary() # → "2#11000000.2#10101000.2#1.2#1" -``` - -### Conversiones Automáticas Bidireccionales - -#### Flujo Natural Entre Tipos -```python -# IP con máscara hexadecimal -IP4(192.168.1.1, 16#ffffff00) # IntBase → IP4Mask automático - -# Conversión desde IP a Hex -ip_bytes = 192.168.1.1 # → FourBytes -hex_ip = Hex(ip_bytes) # → Hex con valor 32-bit - -# Análisis de máscara en múltiples bases -mask = 255.255.255.0 # → FourBytes -mask_hex = Hex(mask) # → Hex basado en 32-bit -mask_bin = Bin(mask) # → Bin por elementos -mask_cidr = IP4Mask(mask) # → /24 - -# Operaciones mixtas -base_ip = 192.168.0.0 # → FourBytes -offset = 16#100 # → IntBase('100', 16) = 256 -next_ip = base_ip + offset # → FourBytes('192.168.1.0') -``` - -### Ecuaciones y Álgebra - -#### Detección Automática de Ecuaciones -```python -# Ecuaciones simples (detectadas automáticamente) -x + 2 = 5 -3*a + b = 10 -y**2 = 16 - -# Desigualdades -x > 5 -a <= 10 -b != 0 - -# Ecuaciones complejas -sin(x) = 1/2 -log(y) + 3 = 5 - -# Ecuaciones con objetos tipados -10.x.1.1 + 256 = 10.5.1.1 # Resuelve: x = 4 -``` - -#### Resolución de Ecuaciones -```python -# Resolver ecuación específica -solve(x**2 + 2*x - 8, x) # [-4, 2] - -# Atajo para resolver variable -x=? # Equivale a solve(x) - -# Resolver sistema de ecuaciones -x + y = 10 -x - y = 2 -solve([x + y - 10, x - y - 2], [x, y]) # {x: 6, y: 4} - -# Sistemas con objetos tipados -network = 10.a.b.0 -solve(network + 256 == 10.5.1.0, [a, b]) # Encuentra valores de a, b -``` - -### Variables y Símbolos - -#### Variables SymPy Automáticas -```python -# Todas las variables son símbolos SymPy automáticamente -x + 2*y # Expresión simbólica -z = 5 # z es Symbol('z') con valor 5 -w = z**2 + 3 # w es expresión: Symbol('z')**2 + 3 - -# Con objetos tipados -ip_base = 10.x.1.1 # FourBytes simbólico -ip_final = ip_base + 10 # Aritmética simbólica -substitute(ip_final, x=5) # → FourBytes('10.5.1.11') -``` - -#### Contexto Limpio por Evaluación -```python -# Cada modificación reevalúa todo desde cero: -x # → Symbol('x') puro (sin valor) -x = 1 # → Asigna x = 1 -y = x + 2 # → y = 3 (usa x=1 definido arriba) - -# Si modifica cualquier línea, se reevalúa todo nuevamente -# Esto garantiza comportamiento predecible y determinista -``` - -### Funciones Matemáticas - -#### Cálculo Diferencial e Integral -```python -# Derivadas con objetos tipados -diff(16#x0, x) # → 16*log(16) -diff(sin(192.168.x.1), x) # → cos(3232235777 + x) - -# Integrales -integrate(x**2, x) # → x**3/3 -integrate(sin(x), (x, 0, pi)) # → 2 - -# Límites -limit(sin(x)/x, x, 0) # → 1 - -# Series de Taylor -series(exp(x), x, 0, 5) # → 1 + x + x**2/2 + x**3/6 + x**4/24 + O(x**5) -``` - -#### Funciones Trigonométricas -```python -# Funciones básicas -sin(pi/2) # → 1 -cos(0) # → 1 -tan(pi/4) # → 1 - -# Con valores hexadecimales -sin(16#FF / 255 * pi) # → Seno de π (aproximadamente) -``` - -#### Álgebra y Simplificación -```python -# Simplificación -simplify((x**2 - 1)/(x - 1)) # → x + 1 -expand((x + 1)**3) # → x**3 + 3*x**2 + 3*x + 1 -factor(x**2 - 1) # → (x - 1)*(x + 1) - -# Con objetos especializados -simplify(16#FF + 2#1010 - 265) # → 0 (IntBase convertido automáticamente) -``` - -### Álgebra Lineal - -#### Matrices -```python -# Crear matrices -M = Matrix([[1, 2], [3, 4]]) -N = Matrix([[5, 6], [7, 8]]) - -# Operaciones básicas -M + N # Suma de matrices -M * N # Multiplicación -M**2 # Potencia - -# Propiedades (clickeables en interfaz) -det(M) # Determinante: -2 -inv(M) # Matriz inversa -M.transpose() # Transpuesta -``` - -### Plotting Interactivo - -#### Plots 2D -```python -# Plot básico -plot(sin(x), (x, -2*pi, 2*pi)) - -# Múltiples funciones -plot(sin(x), cos(x), (x, 0, 2*pi)) - -# Plot con objetos tipados -plot(16#x/256, (x, 0, 255)) # Hex values -plot(sin(192.168.x.1 / 1000), (x, 0, 255)) # IP arithmetic -``` - -#### Plots 3D -```python -# Superficie 3D -plot3d(x**2 + y**2, (x, -5, 5), (y, -5, 5)) - -# Con funciones trigonométricas -plot3d(sin(x)*cos(y), (x, 0, 2*pi), (y, 0, 2*pi)) - -# Con objetos tipados -plot3d(16#x + 16#y, (x, 0, 255), (y, 0, 255)) -``` - -### Resultados Interactivos - -#### Elementos Clickeables -- **📊 Ver Plot**: Abre ventana matplotlib para plots -- **📋 Ver Matriz**: Muestra matriz formateada con operaciones -- **📋 Ver Lista**: Expande listas largas -- **🔍 Ver Detalles**: Información completa de objetos - -#### Ejemplo de Uso -```python -# Estos resultados serán clickeables en la interfaz -Matrix([[1, 2, 3], [4, 5, 6]]) # → 📋 Ver Matriz 2×3 -plot(x**2, (x, -10, 10)) # → 📊 Ver Plot -solve(x**3 - 6*x**2 + 11*x - 6, x) # → 📋 Ver Soluciones -``` - -## Casos de Uso Avanzados - -### Análisis de Redes con Álgebra -```python -# Definir red con variables -network = 192.168.x.0 # → FourBytes simbólico -subnet_size = 2#100000000 # → IntBase('100000000', 2) = 256 - -# Encontrar valores específicos -solve(network + subnet_size == 192.168.5.0, x) # → x = 4 - -# Análisis de rangos -base_ip = 10.a.b.0 -constraints = [ - base_ip[1] >= 0, # a >= 0 - base_ip[1] <= 255, # a <= 255 - base_ip[2] >= 0, # b >= 0 - base_ip[2] <= 255 # b <= 255 -] -valid_ranges = solve(constraints, [a, b]) -``` - -### Programación y Conversiones Avanzadas -```python -# Análisis de valores hex con variables -hex_pattern = 16#x0 # → IntBase simbólico -solve(hex_pattern + 16#10 == 256, x) # → x = 15 (0xF) - -# Conversiones automáticas en cadena -ip = 192.168.1.1 # → FourBytes -ip_hex = ip.ToHex() # → "16#C0.16#A8.16#1.16#1" -ip_bin = ip.ToBinary() # → "2#11000000.2#10101000.2#1.2#1" - -# Operaciones bit a bit con conversión automática -mask1 = 255.255.255.0 # → FourBytes -mask2 = 255.255.0.0 # → FourBytes -combined = mask1 & mask2 # → Operación bit a bit -``` - -### Análisis Matemático con Tipos Especializados -```python -# Función compleja con valores hex -f = sin(16#x * pi / 256) # IntBase convertido automáticamente - -# Análisis completo -df_dx = diff(f, x) # Derivada -critical_points = solve(df_dx, x) # Puntos críticos -integral = integrate(f, (x, 0, 255)) # Integral en rango hex - -# Visualización -plot(f, df_dx, (x, 0, 255)) # Plot función y derivada -``` - -### Sistemas de Ecuaciones con Redes -```python -# Sistema con direcciones IP -ip1 = 192.168.x.y -ip2 = 10.a.b.c - -# Condiciones de red -ip1 + 256 = 192.168.5.10 -ip2.NetworkAddress() = 10.1.0.0 - -# Resolver para encontrar valores válidos -solve([ - ip1 + 256 - 192.168.5.10, - ip2[1] - 1, # a = 1 - ip2[2] - 0 # b = 0 -], [x, y, a, b, c]) -``` - -## Sistema de Tipos Dinámico ⭐ **NUEVO** - -### Auto-descubrimiento de Tipos -```python -# El sistema automáticamente descubre tipos desde custom_types/ -custom_types/ -├── intbase_type.py # IntBase + tokenización + registro -├── fourbytes_type.py # FourBytes + tokenización + registro -├── hex_type.py # Hex (usa IntBase) -├── bin_type.py # Bin (usa IntBase) -├── ip4_type.py # IP4 + IP4Mask (usa FourBytes) -├── chr_type.py # Chr -└── latex_type.py # LaTeX -``` - -### Tokenización Distribuida -Cada clase define sus propios patrones de reconocimiento: -```python -# En intbase_type.py -@staticmethod -def get_tokenization_patterns(): - return [ - { - 'pattern': r'(\d+)#([0-9A-Fa-fx]+)', - 'replacement': lambda match: f'IntBase("{match.group(2)}", {match.group(1)})', - 'priority': 5, - 'description': 'Números con base: 16#FF, 2#1010' - } - ] -``` - -### Precedencia Automática -- **IntBase**: Prioridad 5 (alta) - patrón muy específico -- **FourBytes**: Prioridad 10 (media) - patrón más general -- Sistema ordena automáticamente por especificidad - -### Información de Tipos -Use **Menú Tipos → Información de tipos** para ver tipos descubiertos automáticamente. - -## Interfaz de Usuario - -### Paneles -- **Panel Izquierdo**: Entrada de código -- **Panel Derecho**: Resultados con colores y elementos interactivos - -### Sistema de Ayuda Dinámico -- **Helpers auto-descubiertos**: Cada tipo define su ayuda contextual -- **Autocompletado inteligente**: Escribir `.` muestra métodos del objeto -- **PopupFunctionList**: Lista de métodos disponibles por tipo - -### Menús Actualizados -- **Archivo**: Nuevo, Cargar, Guardar -- **Editar**: Limpiar entrada/salida, Limpiar historial -- **Configuración**: Modo simbólico, Recargar tipos personalizados -- **Tipos**: Información de tipos, Sintaxis de tipos ⭐ **NUEVO** -- **Ayuda**: Guías, sintaxis, funciones SymPy - -### Autocompletado Avanzado -```python -# Ejemplos de autocompletado tras punto: -192.168.1.1. # → Métodos de FourBytes -16#FF. # → Métodos de IntBase -IP4(10.1.1.1). # → Métodos de IP4 -Matrix([[1,2]]). # → Métodos de SymPy Matrix -``` - -## Configuración y Personalización - -### Archivos de Configuración -- **`hybrid_calc_settings.json`**: Configuración de ventana y UI -- **`hybrid_calc_history.txt`**: Historial de sesión anterior -- **`logs/`**: Archivos de log para debugging - -### Modo Simbólico Configurable -- **Resultado simbólico**: Siempre disponible -- **Aproximación numérica**: Configurable (mostrar/ocultar) -- **Fracciones simbólicas**: Mantener como fracción vs decimal -- **Auto-simplificación**: Activar/desactivar - -## Resolución de Problemas - -### Errores Comunes - -#### Dependencias Faltantes -```bash -# Error: ModuleNotFoundError: No module named 'sympy' -pip install sympy matplotlib numpy - -# Linux: tkinter no disponible -sudo apt-get install python3-tk -``` - -#### Problemas de Tokenización -```python -# Problema: Pattern no reconocido -192.168.1 # No es x.x.x.x completo, no se tokeniza - -# Solución: Usar patrón completo -192.168.1.1 # → FourBytes automático - -# Verificar tipos cargados -# Menú Tipos → Información de tipos -``` - -#### Problemas de Variables -```python -# Las variables son símbolos automáticamente -x = 5 # → x es Symbol('x') con valor 5, no variable Python -y = x + 2 # → y es Symbol('x') + 2, evaluado como 7 - -# Con objetos tipados -ip = 10.x.1.1 # → FourBytes simbólico -ip.substitute(x=5) # → FourBytes('10.5.1.1') -``` - -### Performance - -#### Optimizaciones Implementadas -- **Contexto limpio**: Cada evaluación es independiente y predecible -- **Tokenización cacheada**: Patrones frecuentes se cachean -- **Evaluación lazy**: `evalf()` solo cuando se muestra resultado -- **Auto-descubrimiento optimizado**: Una sola carga al inicio - -#### Límites Conocidos -- Sistemas de ecuaciones muy grandes pueden ser lentos -- Plots 3D complejos requieren tiempo de renderizado -- Matrices muy grandes pueden consumir memoria -- Objetos simbólicos complejos necesitan más tiempo de evaluación - -### Debugging del Sistema de Tipos - -#### Verificación de Auto-descubrimiento -```python -# En código o logs -print(f"Tipos descubiertos: {len(registered_classes)}") -print(f"Reglas de tokenización: {len(tokenization_rules)}") - -# En interfaz -# Menú Tipos → Información de tipos -``` - -#### Recarga de Tipos en Desarrollo -```python -# Para desarrollo en tiempo real -# Menú Configuración → Recargar Tipos Personalizados -# O reiniciar aplicación para cambios en custom_types/ -``` - -## Desarrollo y Extensión - -### Estructura Actual del Proyecto -``` -calculadora-mav-cas/ -├── calc.py # Launcher principal -├── main_calc_app.py # Aplicación principal -├── main_evaluation.py # Motor CAS híbrido -├── type_registry.py # Auto-descubrimiento de tipos ⭐ -├── tl_bracket_parser.py # Tokenizador universal ⭐ -├── tl_popup.py # Resultados interactivos -├── class_base.py # Clases base -├── sympy_Base.py # Base para integración SymPy -├── sympy_helper.py # Helpers para SymPy -├── custom_types/ # ⭐ Sistema de tipos unificado -│ ├── intbase_type.py # IntBase + tokenización -│ ├── fourbytes_type.py # FourBytes + tokenización -│ ├── hex_type.py # Hex (usa IntBase) -│ ├── bin_type.py # Bin (usa IntBase) -│ ├── ip4_type.py # IP4 + IP4Mask (usa FourBytes) -│ ├── chr_type.py # Chr -│ └── latex_type.py # LaTeX -├── logs/ # Logs de debugging -└── docs/ # Documentación técnica -``` - -### Agregar Nuevos Tipos ⭐ **SIMPLIFICADO** - -#### 1. Crear Archivo en `custom_types/` -```python -# custom_types/ejemplo_type.py -from class_base import ClassBase -import re - -class Class_Ejemplo(ClassBase): - def __init__(self, value_input): - # Procesar entrada - super().__init__(processed_value, original_str) - - @staticmethod - def get_tokenization_patterns(): - """Define cómo reconocer y tokenizar este tipo""" - return [ - { - 'pattern': r'@([a-zA-Z0-9_]+)', - 'replacement': lambda match: f'Ejemplo("{match.group(1)}")', - 'priority': 8, - 'description': 'Patrones @ejemplo' - } - ] - - @staticmethod - def Helper(input_str): - if re.match(r"^\s*@\w+", input_str): - return "Ej: @usuario, @sistema. Funciones: .validate(), .expand()" - return None - - @staticmethod - def PopupFunctionList(): - return [ - ("validate", "Valida formato del ejemplo"), - ("expand", "Expande el valor del ejemplo") - ] - -def register_classes_in_module(): - return [ - ("Ejemplo", Class_Ejemplo, "ClassBase", { - "add_lowercase": True, - "supports_brackets": False, - "has_tokenization": True, - "description": "Ejemplos con @patron" - }) - ] -``` - -#### 2. Usar Automáticamente -```python -# Después de crear el archivo, automáticamente funciona: -@usuario # → Ejemplo('usuario') -@sistema.validate() # → Autocompletado disponible -``` - -### Tokenización Personalizada Avanzada - -#### Múltiples Patrones por Tipo -```python -@staticmethod -def get_tokenization_patterns(): - return [ - { - 'pattern': r'([0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2})', - 'replacement': lambda match: f'MAC("{match.group(1)}", format="colon")', - 'priority': 6, - 'description': 'MAC con dos puntos: AA:BB:CC:DD:EE:FF' - }, - { - 'pattern': r'([0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2})', - 'replacement': lambda match: f'MAC("{match.group(1)}", format="dash")', - 'priority': 6, - 'description': 'MAC con guiones: AA-BB-CC-DD-EE-FF' - } - ] -``` - -### Herencia de Tipos Base - -#### Usar IntBase para Nuevas Bases -```python -# custom_types/octal_type.py -from sympy_Base import SympyClassBase - -class Class_Octal(SympyClassBase): - def __init__(self, value_input): - # Obtener IntBase dinámicamente del registro - IntBase = get_intbase_class() - - if isinstance(value_input, IntBase): - if value_input.base == 8: - self.int_base = value_input - else: - # Convertir a octal - octal_value = IntBase._convert_to_base_string(value_input._numeric_value, 8) - self.int_base = IntBase(octal_value, 8) - - super().__init__(self.int_base.value, str(self.int_base)) - -# Automáticamente disponible: 8#777 → IntBase, Octal(8#777) → Class_Octal -``` - -## Changelog - -### Versión 2.1 (Sistema de Tipos Dinámico) ⭐ **ACTUAL** -- ✅ **Tokenización automática**: Patrones `16#FF` y `192.168.1.1` convertidos automáticamente -- ✅ **Sistema de tipos dinámico**: Auto-descubrimiento desde `custom_types/` -- ✅ **Clases base universales**: IntBase y FourBytes como tipos fundamentales -- ✅ **Tokenización distribuida**: Cada clase define sus propios patrones -- ✅ **Parser genérico**: Sin hardcoding de tipos específicos -- ✅ **Conversión perezosa a SymPy**: Objetos nativos hasta que se necesite álgebra -- ✅ **Helpers dinámicos**: Sistema de ayuda contextual auto-descubierto -- ✅ **Aritmética nativa**: Operaciones que preservan tipos originales - -### Versión 2.0 (CAS Híbrido) - **ANTERIOR** -- ✅ Motor SymPy completo como base -- ❌ ~~Sintaxis con corchetes únicamente~~ **ELIMINADO** -- ✅ Detección automática de ecuaciones -- ✅ Variables SymPy puras -- ✅ Resultados interactivos clickeables -- ✅ Sistema de plotting integrado -- ✅ Arquitectura modular extensible - -### Diferencias vs Versión Anterior -| Característica | v2.0 | v2.1 | -|---|---|---| -| Sintaxis | `Class[args]` | Tokenización automática | -| Tipos | Hardcodeados | Auto-descubrimiento | -| Parser | Específico | Genérico distribuido | -| Extensibilidad | Manual | Automática | -| Aritmética | SymPy siempre | Nativa + SymPy cuando se necesite | -| Tokenización | Centralizada | Distribuida por clase | - -## FAQ - -### ¿Cómo funciona la tokenización automática? -El sistema reconoce patrones como `16#FF` y `192.168.1.1` automáticamente y los convierte en objetos `IntBase` y `FourBytes` respectivamente. No necesita sintaxis especial. - -### ¿Puedo usar la sintaxis de corchetes? -No. La v2.1 usa tokenización automática invisible que es más natural e intuitiva. - -### ¿Cómo agregar nuevos tipos? -1. Crear archivo en `custom_types/nuevo_type.py` -2. Implementar clase con `get_tokenization_patterns()` y `register_classes_in_module()` -3. El sistema lo detecta automáticamente - -### ¿Los resultados son exactos? -Sí. Los objetos mantienen precisión nativa hasta que se convierten a SymPy para álgebra compleja. - -### ¿Cómo funcionan las conversiones entre tipos? -Son automáticas y bidireccionales. Por ejemplo: `Hex(192.168.1.1)` convierte FourBytes a Hex automáticamente. - -### ¿Puedo crear tipos que usen múltiples patrones? -Sí. Cada tipo puede definir múltiples patrones con diferentes prioridades en `get_tokenization_patterns()`. - -### ¿Cómo debuggear problemas de tipos? -Use **Menú Tipos → Información de tipos** para ver tipos descubiertos y **Menú Configuración → Recargar Tipos** para desarrollo. - ---- - -*Calculadora MAV - CAS Híbrido v2.1* -*Sistema extensible con tokenización automática para cálculo matemático avanzado* \ No newline at end of file diff --git a/.doc/Guia_Base.md b/.doc/Guia_Base.md deleted file mode 100644 index 8a0356f..0000000 --- a/.doc/Guia_Base.md +++ /dev/null @@ -1,234 +0,0 @@ -# Guía de Principios Arquitectónicos - Calculadora Algebraica Híbrida - -## Filosofía Central: Álgebra Simbólica con Evaluación Numérica - -### Concepto Fundamental - -La calculadora mantiene **forma simbólica como representación primaria** y proporciona **evaluación numérica como información complementaria** cuando es posible y útil. - -**Principio de Evaluación Numérica**: El resultado siempre se intenta calcular numéricamente y se muestra cuando la representación string del resultado numérico difiere del resultado algebraico. - -## Sistema de Asignaciones y Ecuaciones - -### Principio: Separación clara entre asignaciones y ecuaciones - -```python -# Asignación de variable -x = 5 # → Se guarda el valor simbólico (NO se agrega como ecuación) -y = x + a # → Se guarda la expresión simbólica - -# Ecuación pura (detección automática) -x**2 + 2*x = 8 # → Se agrega como ecuación Eq(x**2 + 2*x, 8) -a + b = 10 # → Se agrega como ecuación Eq(a + b, 10) -a > b + 1 # → Se agrega como desigualdad -``` - -### Criterios de Detección de Ecuaciones - -**Se considera ecuación si:** - -- Contiene `=` Y NO es una asignación simple de variable -- Contiene operadores de comparación: `==`, `>`, `<`, `>=`, `<=` -- Tiene estructura algebraica en ambos lados del `=` - -**NO se considera ecuación:** - -- Asignaciones simples: `x = 5`, `y = expresión` -- Líneas con solo números o comentarios - ---- - -## Uso de `solve()` y el Atajo `=?` - -### Principio: `variable=?` es equivalente a `solve(variable)` - -```python -# Ambas formas son idénticas: -solve(a) # Resuelve 'a' usando el sistema de ecuaciones -a=? # Atajo sintáctico para solve(a) -``` - -El comando intenta resolver algebraicamente y numéricamente en paralelo cuando es posible. - ---- - -## Sistema de Tokenización con Prioridades - -### Principio: Las prioridades son números arbitrarios que determinan el orden de aplicación - -``` -Prioridad 5: IntBase → Patrón: 16#FF (muy específico) -Prioridad 6: Hex/Bin → Patrón: 0xFF, 0b1010 (específico) -Prioridad 10: FourBytes → Patrón: x.x.x.x (general) -``` - -**Regla**: Menor número = se aplica primero - -Esto garantiza que `16#10.10.10.10` se tokenice como IntBase antes de considerar FourBytes. - ---- - -## Conversión Perezosa y Tipos Especializados - -### Principio: Mantener el tipo especializado siempre que sea posible - -**Se evita convertir a SymPy al máximo**. Los objetos especializados mantienen su tipo cuando pueden resolver la operación internamente. - -```python -# Operaciones que mantienen el tipo especializado: -Hex(15) + 1 → Hex(16) # Hex maneja la suma -16#15 + 1 → 16#16 # IntBase maneja la suma -10.1.1.1 + 1 → 10.1.1.2 # FourBytes maneja la suma - -# Conversión a SymPy solo cuando es necesario: -sin(16#FF) → sin(255) # sin() requiere SymPy -solve(x + 16#10) → solve() # Álgebra compleja requiere SymPy -``` - -**Regla**: Solo se convierte a SymPy cuando el token no es atómico o la operación requiere capacidades algebraicas avanzadas. - ---- - -## Contexto Limpio por Evaluación - -### Principio: Cada evaluación comienza desde cero - -**Contexto Limpio** significa: - -- Se eliminan todas las ecuaciones previas -- Se eliminan todas las variables definidas -- Se re-evalúa todo el contenido línea por línea -- Comportamiento idéntico a iniciar una nueva sesión - -Esto garantiza resultados predecibles y sin efectos secundarios acumulados. - ---- - -## Resultados Interactivos - -### Principio: Activación mediante clicks en elementos con binding - -Los resultados interactivos se crean cuando el tipo de resultado requiere visualización expandida: - -```python -plot(sin(x)) → "📊 Ver Plot" # Click abre ventana matplotlib -Matrix([[1,2],[3,4]]) → "📋 Ver Matriz" # Click muestra matriz formateada -[1,2,3,4,5,6,7,8] → "📋 Ver Lista" # Click expande contenido -``` - -**Activación**: Click en el texto con binding dispara la ventana emergente correspondiente. - ---- - -## Sistema de Autocompletado - -### Principio: Sistema extensible con prioridades (por implementar) - -Se propone usar un sistema de prioridades numéricas similar al de tokenización: - -``` -Prioridad 1: Métodos del objeto específico -Prioridad 2: Métodos de la clase base -Prioridad 3: Funciones SymPy relevantes -Prioridad 4: Helpers contextuales -``` - ---- - -## Integración con SymPy - -### Principio: En casos de conflicto, siempre prevalece SymPy - -Cuando hay ambigüedad o conflicto entre tipos personalizados y SymPy: - -- Funciones SymPy tienen precedencia -- Símbolos SymPy (como `e`, `pi`) mantienen su significado matemático -- Las conversiones fallidas retornan a comportamiento SymPy estándar - ---- - -## Arquitectura de Tipos Personalizados - -### Clases Base Universales - -**IntBase**: Números en cualquier base - -- Aritmética nativa que preserva la base -- Conversión a SymPy solo cuando necesario -- Soporte para símbolos algebraicos - -**FourBytes**: Patrones x.x.x.x - -- Aritmética de 32-bit para IPs -- Soporte para elementos simbólicos -- Base para tipos como IP4 - -### Principio de Extensibilidad - -Cada tipo en `custom_types/` define: - -1. Su lógica de tokenización -2. Sus operaciones aritméticas -3. Sus métodos de conversión -4. Su ayuda contextual -5. Sus sugerencias de autocompletado - ---- - -## Flujo de Evaluación Completo - -``` -Entrada Usuario - ↓ -[Tokenización Universal] - ↓ -¿Asignación o Ecuación? - ├─ Asignación → Guardar valor simbólico - └─ Ecuación → Agregar al sistema - ↓ -[Evaluación] - ├─ Mantener tipo especializado si es posible - └─ Convertir a SymPy si es necesario - ↓ -[Resultado] - ├─ Forma simbólica (siempre) - └─ Evaluación numérica (si difiere como string) -``` - ---- - -## Ejemplos Integrales - -```python -# Tokenización y tipos especializados -16#FF + 2#1010 → 16#109 # IntBase preservado -192.168.1.1 + 5 → 192.168.1.6 # FourBytes preservado - -# Asignaciones vs ecuaciones -x = 10 # Asignación (solo guarda valor) -x + y = 15 # Ecuación (se agrega al sistema) -y=? # solve(y) → y = 5 - -# Evaluación numérica automática -4/5 → 4/5 ≈ 0.8 # Difiere como string -sqrt(2) → √2 ≈ 1.414 # Difiere como string -2 + 3 → 5 # NO se muestra ≈ (igual string) - -# Resultados interactivos -plot(sin(x)) → "📊 Ver Plot" [clickeable] -Matrix([[1,2]]) → "📋 Ver Matriz" [clickeable] -``` - ---- - -## Principios de Diseño Resumidos - -1. **Álgebra primero**: Mantener forma simbólica siempre -2. **Evaluación numérica inteligente**: Mostrar solo cuando agrega valor -3. **Tipos especializados preservados**: Conversión a SymPy solo cuando necesario -4. **Tokenización ordenada**: Sistema de prioridades simple y predecible -5. **Contexto limpio**: Cada evaluación desde cero -6. **Extensibilidad**: Nuevos tipos se integran automáticamente -7. **SymPy prevalece**: En conflictos, el comportamiento matemático estándar gana - -Este documento ahora refleja con precisión la arquitectura implementada y sirve como guía coherente para el desarrollo futuro. \ No newline at end of file diff --git a/.doc/LLM_DEBUG_API_GUIDE.md b/.doc/LLM_DEBUG_API_GUIDE.md deleted file mode 100644 index 083a39f..0000000 --- a/.doc/LLM_DEBUG_API_GUIDE.md +++ /dev/null @@ -1,462 +0,0 @@ -# Simple Debug API - Guía para LLM - -## Propósito - -Esta API permite debuggear la **Calculadora MAV CAS** sin modificar código fuente ni crear scripts adicionales. - -**Principio**: **texto → texto** tal como se muestra en la aplicación. -**NO duplica lógica**, solo encapsula llamadas directas al motor existente. - -Como LLM, puedes usar esta herramienta para: - -- **Diagnosticar problemas** en el motor de evaluación -- **Verificar comportamiento** de tipos personalizados (FourBytes, IntBase, IP4Mask, etc.) -- **Inspeccionar el estado interno** del motor (variables, contexto, tipos registrados) -- **Testing de regresión** para verificar que cambios no rompan funcionalidad existente - -## Flujo de Trabajo Recomendado - -### 1. Crear archivo JSON con queries -### 2. Ejecutar: `python simple_debug.py archivo.json` -### 3. Analizar resultados en el archivo `*_results.json` - ---- - -## Tipos de Query - -### Query tipo `input` -Evalúa expresiones como si el usuario las escribiera en la calculadora. -**Resultado**: texto tal como se muestra en la aplicación. - -```json -{"index": 0, "type": "input", "content": "10.1.1.1 + 1"} -``` - -### Query tipo `exec` -Ejecuta código Python para inspeccionar el estado interno del motor. -**Resultado**: valor directo de la evaluación Python. - -```json -{"index": 1, "type": "exec", "content": "type(engine.last_result).__name__"} -``` - ---- - -## Plantilla Base - -```json -{ - "queries": [ - {"index": 0, "type": "input", "content": "EXPRESION_A_EVALUAR"}, - {"index": 1, "type": "exec", "content": "CODIGO_PYTHON_INSPECCION"} - ] -} -``` - ---- - -## Casos de Uso Comunes - -### Debugging de Tipos Personalizados - -**Problema**: Verificar si FourBytes maneja correctamente operaciones IP. - -```json -{ - "queries": [ - {"index": 0, "type": "input", "content": "192.168.1.1 + 1"}, - {"index": 1, "type": "exec", "content": "type(engine.last_result).__name__"}, - {"index": 2, "type": "exec", "content": "engine.last_result.original"}, - {"index": 3, "type": "exec", "content": "engine.last_result._numeric_value"}, - {"index": 4, "type": "input", "content": "10.0.0.0/8 + 256"}, - {"index": 5, "type": "exec", "content": "engine.last_result.has_symbols"} - ] -} -``` - -### Debugging de Tokenización - -**Problema**: La expresión `192.168.1.x + 16#FF` no se tokeniza correctamente. - -```json -{ - "queries": [ - {"index": 0, "type": "exec", "content": "engine.parser.process_expression('192.168.1.x + 16#FF')"}, - {"index": 1, "type": "input", "content": "192.168.1.x + 16#FF"}, - {"index": 2, "type": "exec", "content": "engine.parser.get_tokenization_info()"}, - {"index": 3, "type": "exec", "content": "len(engine.parser.tokenizer.tokenization_rules)"} - ] -} -``` - -### Debugging de Errores - -**Problema**: IP4Mask rechaza máscaras que deberían ser válidas. - -```json -{ - "queries": [ - {"index": 0, "type": "input", "content": "mask = 255.240.0.0"}, - {"index": 1, "type": "input", "content": "IP4Mask(mask)"}, - {"index": 2, "type": "exec", "content": "engine.last_result"}, - {"index": 3, "type": "input", "content": "IP4Mask(255.240.0.3)"}, - {"index": 4, "type": "exec", "content": "engine.last_result"} - ] -} -``` - -### Debugging de Estado del Motor - -**Problema**: Las variables no se están guardando correctamente. - -```json -{ - "queries": [ - {"index": 0, "type": "input", "content": "x = 5"}, - {"index": 1, "type": "input", "content": "y = x + 10"}, - {"index": 2, "type": "exec", "content": "engine.symbol_table"}, - {"index": 3, "type": "exec", "content": "len(engine.symbol_table)"}, - {"index": 4, "type": "exec", "content": "list(engine.symbol_table.keys())"}, - {"index": 5, "type": "input", "content": "solve(z**2 + x, z)"}, - {"index": 6, "type": "exec", "content": "len(engine.equations)"} - ] -} -``` - ---- - -## Interpretación de Resultados - -### Estructura del Resultado - -```json -{ - "execution_info": { - "timestamp": "2025-06-05T18:25:22.256442Z", - "total_queries": 5, - "successful": 4, - "failed": 1 - }, - "results": [...] -} -``` - -### Resultado Individual (Query `input`) - -```json -{ - "index": 0, - "input": "10.1.1.1 + 1", - "output": "10.1.1.2", // Texto tal como se muestra en la app - "result_type": "FourBytes", // Tipo del objeto resultado - "success": true, - "error": null -} -``` - -### Resultado Individual (Query `exec`) - -```json -{ - "index": 1, - "input": "type(engine.last_result).__name__", - "output": "FourBytes", // String del resultado - "result_type": "str", // Tipo del resultado de la evaluación - "success": true, - "error": null, - "exec_result": "FourBytes" // Valor directo (serializado si es necesario) -} -``` - -### Resultado con Error - -```json -{ - "index": 2, - "input": "IP4Mask(255.240.0.3)", - "output": "None", - "result_type": "NoneType", - "success": false, - "error": "❌ Máscara inválida: 255.240.0.3..." -} -``` - ---- - -## Funciones de Inspección Útiles - -### Estado del Motor - -```python -"engine.symbol_table" # Variables actuales -"engine.last_result" # Último resultado -"engine.symbolic_mode" # ¿Modo simbólico activo? -"len(engine.equations)" # Cantidad de ecuaciones en el sistema -"engine.debug" # ¿Debug habilitado? -"list(engine.base_context.keys())[:10]" # Funciones disponibles (muestra 10) -``` - -### Información de Tipos - -```python -"engine.get_available_types()" # Info completa de tipos registrados -"list(engine.registered_types_info['registered_classes'].keys())" # Lista de tipos -"engine.registered_types_info['class_count']" # Cantidad de tipos registrados -"type(engine.last_result).__name__" # Tipo del último resultado -"hasattr(engine.last_result, 'has_symbols')" # ¿El resultado tiene símbolos? -``` - -### Tokenización y Parsing - -```python -"engine.parser.get_tokenization_info()" # Info completa de tokenización -"engine.parser.process_expression('test')" # Procesar expresión específica -"len(engine.parser.tokenizer.tokenization_rules)" # Cantidad de reglas -"engine._classify_line('x = 5')" # Clasificar tipo de línea -"engine._extract_variable_names('x + y')" # Extraer nombres de variables -``` - -### Análisis de Objetos Específicos - -```python -# Para FourBytes -"engine.last_result.original" # String original -"engine.last_result._numeric_value" # Valor numérico interno -"engine.last_result.has_symbols" # ¿Tiene símbolos? - -# Para IntBase -"engine.last_result.base" # Base numérica (10, 16, 8, 2) -"engine.last_result.value_str" # String del valor -"engine.last_result._symbols" # Símbolos detectados - -# Para IP4Mask -"engine.last_result.get_prefix_int()" # Prefijo como entero -"engine.last_result.is_valid()" # ¿Es máscara válida? -``` - ---- - -## Patrones de Debugging - -### 1. Debugging de Regresión - -**Cuándo usar**: Verificar que cambios no rompan funcionalidad existente. - -```json -{ - "queries": [ - {"index": 0, "type": "input", "content": "10.1.1.1 + 1"}, - {"index": 1, "type": "exec", "content": "type(engine.last_result).__name__"}, - {"index": 2, "type": "input", "content": "16#FF + 10"}, - {"index": 3, "type": "exec", "content": "engine.last_result.base"}, - {"index": 4, "type": "input", "content": "IP4Mask(255.255.0.0)"}, - {"index": 5, "type": "exec", "content": "engine.last_result.get_prefix_int()"} - ] -} -``` - -### 2. Debugging de Nuevas Funcionalidades - -**Cuándo usar**: Verificar que nueva funcionalidad trabaja correctamente. - -```json -{ - "queries": [ - {"index": 0, "type": "input", "content": "NUEVA_FUNCIONALIDAD_AQUI"}, - {"index": 1, "type": "exec", "content": "type(engine.last_result)"}, - {"index": 2, "type": "exec", "content": "dir(engine.last_result)"}, - {"index": 3, "type": "exec", "content": "str(engine.last_result)"}, - {"index": 4, "type": "exec", "content": "engine.symbol_table"} - ] -} -``` - -### 3. Debugging de Performance - -**Cuándo usar**: Identificar operaciones lentas o problemáticas. - -```json -{ - "queries": [ - {"index": 0, "type": "exec", "content": "import time; start = time.time()"}, - {"index": 1, "type": "input", "content": "OPERACION_LENTA"}, - {"index": 2, "type": "exec", "content": "time.time() - start"}, - {"index": 3, "type": "exec", "content": "len(engine.symbol_table)"} - ] -} -``` - -### 4. Debugging de Comportamiento - -**Cuándo usar**: Verificar comportamiento específico de tipos o funciones. - -```json -{ - "queries": [ - {"index": 0, "type": "input", "content": "EXPRESION"}, - {"index": 1, "type": "exec", "content": "str(engine.last_result)"}, - {"index": 2, "type": "exec", "content": "repr(engine.last_result)"}, - {"index": 3, "type": "exec", "content": "type(engine.last_result).__name__"} - ] -} -``` - ---- - -## Comandos de Ejecución - -```bash -# Ejecución básica -python simple_debug.py mi_debug.json - -# Con archivo de salida específico -python simple_debug.py mi_debug.json --output resultados.json - -# Modo verboso (para ver progreso) -python simple_debug.py mi_debug.json --verbose -``` - ---- - -## Templates Existentes para Reutilizar - -### Casos Básicos -**Archivo**: `debug_templates/basic_test.json` -- Operaciones con FourBytes e IntBase -- Variables y SymPy básico - -### Testing de Errores -**Archivo**: `debug_templates/error_debug.json` -- Máscaras inválidas -- IPs fuera de rango -- Expresiones malformadas - -### Información de Contexto -**Archivo**: `debug_templates/context_debug.json` -- Estado completo del motor -- Tipos registrados -- Sistema de ecuaciones - -### Tokenización -**Archivo**: `debug_templates/tokenization_test.json` -- Debug del sistema de parsing -- Reglas de tokenización - ---- - -## Flujo de Resolución de Problemas - -### 1. Identificar el Problema -- ¿Es un error de evaluación? -- ¿Es un problema de tokenización? -- ¿Es un problema de estado del motor? - -### 2. Crear Query de Diagnóstico -- Usar query `input` para reproducir el problema -- Usar query `exec` para inspeccionar el estado - -### 3. Analizar Resultados -- Verificar `success: true/false` -- Examinar `error` si hay fallo -- Comparar `output` (texto de la app) con `exec_result` (valor interno) - -### 4. Iterar -- Crear nuevas queries basadas en hallazgos -- Profundizar en áreas problemáticas -- Verificar soluciones con queries adicionales - ---- - -## Ejemplo Completo de Debugging - -**Problema**: "La operación `192.168.1.x + 1` no funciona correctamente" - -### Paso 1: Crear archivo de debug - -```json -{ - "queries": [ - {"index": 0, "type": "input", "content": "192.168.1.x + 1"}, - {"index": 1, "type": "exec", "content": "type(engine.last_result).__name__"}, - {"index": 2, "type": "exec", "content": "engine.last_result.has_symbols"}, - {"index": 3, "type": "exec", "content": "engine.parser.process_expression('192.168.1.x + 1')"}, - {"index": 4, "type": "input", "content": "ip = 192.168.1.x"}, - {"index": 5, "type": "exec", "content": "type(engine.symbol_table['ip'])"}, - {"index": 6, "type": "input", "content": "ip.substitute(x=5)"} - ] -} -``` - -### Paso 2: Ejecutar -```bash -python simple_debug.py debug_problema.json -``` - -### Paso 3: Analizar resultados -- Verificar si `192.168.1.x + 1` se evalúa correctamente (campo `output`) -- Comprobar el tipo resultante con `exec` -- Verificar si tiene símbolos -- Examinar cómo se tokeniza la expresión -- Probar operaciones relacionadas - -Este flujo te permite identificar exactamente dónde está el problema y verificar la solución. - ---- - -## Consejos para LLMs - -1. **Siempre usa índices secuenciales** en las queries para facilitar la lectura -2. **Combina queries `input` y `exec`** para obtener contexto completo -3. **El campo `output` es texto tal como se muestra en la aplicación** -4. **El campo `exec_result` es el valor directo de la evaluación Python** -5. **Usa los templates existentes** como punto de partida -6. **Examina `success` y `error`** antes de analizar resultados -7. **Crea queries incrementales** que construyan sobre resultados anteriores -8. **NO intentes duplicar lógica de la aplicación** - usa solo llamadas directas - ---- - -## Referencia Rápida - -### Query Básica -```json -{"index": N, "type": "input|exec", "content": "CONTENIDO"} -``` - -### Comandos Esenciales -```bash -python simple_debug.py archivo.json # Ejecutar debug -python simple_debug.py archivo.json --verbose # Con detalles -python simple_debug.py archivo.json -o resultado.json # Salida específica -``` - -### Inspección Básica del Motor -```python -"engine.last_result" # Último resultado -"type(engine.last_result)" # Tipo del resultado -"engine.symbol_table" # Variables actuales -"engine.get_available_types()" # Tipos registrados -"engine.parser.get_tokenization_info()" # Info de parsing -``` - -### Análisis de Resultado -```python -"success": true/false # ¿Éxito? -"error": "mensaje" # Error si falla -"output": "resultado" # Texto tal como se muestra en la app -"exec_result": valor # Valor directo (exec queries) -``` - -### Templates Disponibles -- `debug_templates/basic_test.json` - Pruebas básicas -- `debug_templates/error_debug.json` - Testing de errores -- `debug_templates/context_debug.json` - Estado del motor -- `debug_templates/tokenization_test.json` - Debug de parsing - -### Workflow Típico -1. **Identificar problema** → Crear query `input` para reproducir -2. **Inspeccionar estado** → Añadir queries `exec` para diagnosticar -3. **Analizar resultados** → Examinar `success`, `error`, `output` -4. **Iterar** → Crear nuevas queries basadas en hallazgos - -**Principio clave**: texto → texto. La API no interpreta ni procesa, solo encapsula llamadas directas al motor existente. \ No newline at end of file diff --git a/.doc/SISTEMA_TOKENIZACION_DISTRIBUIDA.md b/.doc/SISTEMA_TOKENIZACION_DISTRIBUIDA.md deleted file mode 100644 index 6a49088..0000000 --- a/.doc/SISTEMA_TOKENIZACION_DISTRIBUIDA.md +++ /dev/null @@ -1,166 +0,0 @@ -# Sistema de Tokenización Distribuida - IMPLEMENTADO - -## ✅ IMPLEMENTACIÓN COMPLETADA - -Se ha implementado exitosamente el sistema de tokenización distribuida según las especificaciones, reemplazando completamente el sistema de corchetes por un tokenizador universal que auto-descubre reglas. - -## 🏗️ Arquitectura Implementada - -### 1. **UniversalTokenizer** (en `tl_bracket_parser.py`) -- **Auto-descubrimiento**: Descubre automáticamente reglas de tokenización desde todas las clases registradas -- **Prioridad**: Ordena reglas por prioridad (menor número = mayor prioridad) -- **Aplicación secuencial**: Aplica reglas en orden de prioridad -- **Debug integrado**: Sistema completo de debug y estadísticas - -### 2. **TokenizationParser** (en `tl_bracket_parser.py`) -- **Integración completa**: Integra tokenización con el parser existente -- **Compatibilidad**: Mantiene interfaz compatible con BracketParser -- **Estadísticas**: Tracking de conversiones y rendimiento - -### 3. **Clases Base Universales** - -#### **IntBase** (en `custom_types/intbase_type.py`) -- **Patrón**: `(\d+)#([0-9A-Fa-fx]+)` -- **Prioridad**: 5 (ALTA - patrón específico) -- **Ejemplos**: `16#FF` → `IntBase("FF", 16)`, `2#1010` → `IntBase("1010", 2)` -- **Capacidades**: Aritmética nativa, símbolos algebraicos, conversión de bases - -#### **FourBytes** (en `custom_types/fourbytes_type.py`) -- **Patrón**: `(? **"Cada objeto debe mantenerse en su forma nativa hasta que se involucre en una ecuación algebraica compleja"** - -#### **Jerarquía Revisada:** -```python -ClassBase (SIN SymPy - manejo interno de símbolos) -├── FourBytes (símbolos internos, aritmética nativa) -├── IntBase (símbolos internos, aritmética nativa) -├── IP4, Hex, Dec, etc. (usan objetos nativos) -└── IP4Mask (nativo) - -SympyClassBase (solo cuando sea necesario) -├── Para ecuaciones algebraicas complejas -├── Para integración/derivación -└── Para sistemas de ecuaciones -``` - -#### **Comportamiento Deseado:** -```python -mm = 255.240.0.x # → FourBytes('255.240.0.x') [FourBytes] -IP4(mm, 24) # → IP4 con FourBytes interno [IP4] -j.substitute(x=1) # → Lógica interna de FourBytes [IP4] -j.next_ip() # → Aritmética interna nativa [IP4] -hh = 16#ff # → IntBase('ff', 16) [IntBase] -Dec(hh) # → Dec con IntBase interno [Dec] -``` - -## Clases Base Universales (Arquitectura Corregida) - -### IntBase: Universal para Bases Numéricas (ClassBase) - -```python -class IntBase(ClassBase): # ← CAMBIO: ClassBase, NO SympyClassBase - """ - Representación universal de números en cualquier base - con manejo interno de símbolos y conversión perezosa a SymPy - """ - - def __init__(self, value_str, base=10): - self.value_str = value_str - self.base = base - self.has_symbols = bool(re.search(r'[a-zA-Z_]', value_str)) - - if self.has_symbols: - # Modo algebraico: mantener símbolos INTERNAMENTE - self._symbols = self._extract_symbols() - self._expression = self._build_internal_expression() - super().__init__(self._expression, f"{base}#{value_str}") - else: - # Modo numérico: convertir a entero - self._numeric_value = int(value_str, base) - super().__init__(self._numeric_value, f"{base}#{value_str}") - - def _extract_symbols(self): - """Extrae símbolos sin convertir a SymPy aún""" - return re.findall(r'[a-zA-Z_][a-zA-Z0-9_]*', self.value_str) - - def _build_internal_expression(self): - """Construye expresión interna SIN SymPy""" - # Mantener como string procesable internamente - # Solo convertir a SymPy cuando se llame to_sympy() - return f"base{self.base}_expression({self.value_str})" - - def substitute(self, **kwargs): - """Sustitución interna sin involucrar SymPy""" - if not self.has_symbols: - return self - - new_value_str = self.value_str - for symbol, value in kwargs.items(): - new_value_str = new_value_str.replace(symbol, str(value)) - - return IntBase(new_value_str, self.base) - - def to_sympy(self): - """Conversión EXPLÍCITA a SymPy cuando se necesite álgebra""" - if self.has_symbols: - # AHORA sí convertir a expresión SymPy - result = 0 - digits = list(self.value_str) - - for i, digit in enumerate(reversed(digits)): - power = self.base ** i - if digit.isdigit(): - coefficient = int(digit) - elif digit in 'ABCDEF': - coefficient = ord(digit) - ord('A') + 10 - elif digit in 'abcdef': - coefficient = ord(digit) - ord('a') + 10 - else: - # Es un símbolo - coefficient = sympy.Symbol(digit) - - result += coefficient * power - - return result - else: - return sympy.sympify(self._numeric_value) - - # ========== OPERADORES ARITMÉTICOS NATIVOS ========== - - def __add__(self, other): - """Suma: aritmética nativa, mantiene la base""" - if self.has_symbols: - # Para símbolos, mantener como expresión interna - return IntBase(f"({self.value_str}) + ({other})", self.base) - - if isinstance(other, IntBase): - if other.has_symbols: - return IntBase(f"({self.value_str}) + ({other.value_str})", self.base) - result_value = self._numeric_value + other._numeric_value - elif isinstance(other, int): - result_value = self._numeric_value + other - else: - # Para operaciones complejas, convertir a SymPy - return self.to_sympy() + other - - result_str = self._convert_to_base_string(result_value, self.base) - return IntBase(result_str, self.base) - - # ... resto de operadores aritméticos nativos -``` - -### FourBytes: Universal para Patrones x.x.x.x (ClassBase) - -```python -class FourBytes(ClassBase): # ← CAMBIO: ClassBase, NO SympyClassBase - """ - Representación universal de patrones de 4 elementos - con manejo interno de símbolos y conversión perezosa a SymPy - """ - - def __init__(self, dotted_string): - self.original = dotted_string - self.elements = dotted_string.split('.') - - if len(self.elements) != 4: - raise ValueError(f"FourBytes requiere exactamente 4 elementos: {dotted_string}") - - self.has_symbols = any(not elem.isdigit() for elem in self.elements) - - if self.has_symbols: - # Modo algebraico: mantener símbolos INTERNAMENTE - self._symbols = self._extract_symbols() - super().__init__(dotted_string, dotted_string) # Mantener como string - else: - # Modo numérico: validar rangos y convertir - self._numeric_elements = [int(elem) for elem in self.elements] - # Crear valor como entero de 32 bits (para IPs) - self._numeric_value = (self._numeric_elements[0] << 24 | - self._numeric_elements[1] << 16 | - self._numeric_elements[2] << 8 | - self._numeric_elements[3]) - super().__init__(self._numeric_value, dotted_string) - - def _extract_symbols(self): - """Extrae símbolos sin convertir a SymPy aún""" - symbols = [] - for elem in self.elements: - if not elem.isdigit(): - if elem not in symbols: - symbols.append(elem) - return symbols - - def substitute(self, **kwargs): - """Sustitución interna sin involucrar SymPy""" - if not self.has_symbols: - return self - - new_elements = [] - for elem in self.elements: - if elem in kwargs: - new_elements.append(str(kwargs[elem])) - else: - new_elements.append(elem) - - return FourBytes('.'.join(new_elements)) - - def to_sympy(self): - """Conversión EXPLÍCITA a SymPy cuando se necesite álgebra""" - if self.has_symbols: - symbolic_elements = [] - for elem in self.elements: - if elem.isdigit(): - symbolic_elements.append(int(elem)) - else: - symbolic_elements.append(sympy.Symbol(elem)) - - # Retornar como Matrix o expresión según el contexto - return sympy.Matrix(symbolic_elements) - else: - return sympy.sympify(self._numeric_value) - - def __add__(self, other): - """Suma: aritmética nativa de 32-bit""" - if self.has_symbols: - # Para símbolos, mantener como expresión interna - return FourBytes(f"({self.original}) + ({other})") - - if isinstance(other, FourBytes): - if other.has_symbols: - return FourBytes(f"({self.original}) + ({other.original})") - result_int = self._numeric_value + other._numeric_value - elif isinstance(other, int): - result_int = self._numeric_value + other - else: - # Para operaciones complejas, convertir a SymPy - return self.to_sympy() + other - - # Mantener en rango de 32 bits - result_int = result_int & 0xFFFFFFFF - result_str = self._int_to_fourbytes(result_int) - return FourBytes(result_str) - - # ... resto de operadores aritméticos nativos -``` - -## Conversión Perezosa a SymPy - -### **Cuándo Convertir:** -```python -# NUNCA conversión automática - mantener nativo: -mm = FourBytes('255.240.0.x') # → [FourBytes] ✅ -ip = IP4(mm, 24) # → [IP4] ✅ -ip.next_ip() # → [IP4] ✅ - -# Conversión EXPLÍCITA solo cuando se necesite álgebra: -eq = Eq(mm.to_sympy(), other_expr) # → [SymPy Equation] ✅ -solve(eq, [x, y]) # → [SymPy Solutions] ✅ -``` - -### **Beneficios de la Conversión Perezosa:** -1. **Control granular**: Cada clase decide cuándo necesita SymPy -2. **Operaciones nativas**: Sin overhead para operaciones simples -3. **Errores claros**: Comportamiento predecible por tipo -4. **Flexibilidad**: Conversión explícita cuando se requiera álgebra compleja - -### **Integración con Ecuaciones:** -```python -# Para resolver ecuaciones complejas: -x, y = symbols('x y') -fb = FourBytes('10.x.1.y') - -# Crear constraints usando conversión explícita: -constraints = [ - fb.to_sympy()[1] >= 0, # x >= 0 - fb.to_sympy()[1] <= 255, # x <= 255 - fb.to_sympy()[3] >= 0, # y >= 0 - fb.to_sympy()[3] <= 255 # y <= 255 -] - -solutions = solve(constraints, [x, y]) -``` - -## Integración con Clases Especializadas - -### Constructores Mejorados - -```python -class IP4(SympyClassBase): - def __init__(self, address, mask=None): - # address es FourBytes (ya tokenizado) - if not isinstance(address, FourBytes): - raise TypeError("address debe ser FourBytes") - - self.address = address - - if mask is not None: - if isinstance(mask, int): - self.mask = IP4Mask(mask) # CIDR - elif isinstance(mask, FourBytes): - self.mask = IP4Mask(mask) # Dotted decimal - elif isinstance(mask, IntBase): - # Conversión automática desde hex: 16#ffffff00 → máscara - if not mask.has_symbols: - mask_int = mask._numeric_value - # Convertir a FourBytes primero - mask_fourbytes = FourBytes(FourBytes._int_to_fourbytes(mask_int)) - self.mask = IP4Mask(mask_fourbytes) - else: - self.mask = mask # Mantener simbólico - else: - self.mask = mask - -class Hex(SympyClassBase): - def __init__(self, value): - if isinstance(value, IntBase): - # value es IntBase (ya tokenizado) - self.int_base = value - super().__init__(value.value, value.original) - elif isinstance(value, FourBytes): - # Conversión automática desde FourBytes - if not value.has_symbols: - # Convertir a valor hex único (32 bits) - hex_value = hex(value._numeric_value)[2:].upper() - self.int_base = IntBase(hex_value, 16) - super().__init__(value._numeric_value, f"16#{hex_value}") - else: - # Mantener simbólico para análisis algebraico - self.int_base = value - super().__init__(value._symbolic_value, str(value)) - else: - raise TypeError("value debe ser IntBase o FourBytes") - -class Bin(SympyClassBase): - def __init__(self, value): - if isinstance(value, IntBase): - # Conversión automática de cualquier base a binario - if not value.has_symbols: - bin_value = bin(value._numeric_value)[2:] # Remover '0b' - self.int_base = IntBase(bin_value, 2) - super().__init__(value._numeric_value, f"2#{bin_value}") - else: - self.int_base = value - super().__init__(value._symbolic_value, str(value)) - elif isinstance(value, FourBytes): - # Convertir cada elemento a binario - if not value.has_symbols: - # Para FourBytes, crear representación elemento por elemento - bin_elements = [] - for elem in value._numeric_elements: - bin_elements.append(bin(elem)[2:].zfill(8)) # 8 bits por elemento - bin_representation = '.'.join(f"2#{elem}" for elem in bin_elements) - - # Crear IntBase con valor completo - full_bin = ''.join(bin_elements) - self.int_base = IntBase(full_bin, 2) - super().__init__(value._numeric_value, bin_representation) - else: - self.int_base = value - super().__init__(value._symbolic_value, str(value)) - else: - raise TypeError("value debe ser IntBase o FourBytes") - -class IP4Mask(ClassBase): - def __init__(self, mask_input): - if isinstance(mask_input, int): - # CIDR notation - self.prefix = mask_input - self.mask_int = self._prefix_to_mask_int(mask_input) - elif isinstance(mask_input, FourBytes): - # Dotted decimal desde tokenización automática - if not mask_input.has_symbols: - self.mask_int = mask_input._numeric_value - self.prefix = self._mask_int_to_prefix(self.mask_int) - else: - # Mantener simbólico - self.prefix = None - self.mask_int = mask_input._symbolic_value - elif isinstance(mask_input, IntBase): - # Desde hex u otra base - if not mask_input.has_symbols: - self.mask_int = mask_input._numeric_value - self.prefix = self._mask_int_to_prefix(self.mask_int) - else: - # Mantener simbólico - self.prefix = None - self.mask_int = mask_input._symbolic_value - else: - raise TypeError("mask_input debe ser int, FourBytes, o IntBase") - - super().__init__(self.mask_int, str(mask_input)) -``` - -### Conversiones Automáticas Bidireccionales - -```python -# Sistema de conversión fluido entre tipos: - -class UniversalConverter: - """Conversiones automáticas entre todos los tipos""" - - @staticmethod - def auto_convert(source, target_type): - """Convierte automáticamente entre tipos compatibles""" - - if target_type == Hex: - if isinstance(source, FourBytes): - return Hex(source) # Usa constructor mejorado - elif isinstance(source, IntBase): - return Hex(source) - - elif target_type == IP4: - if isinstance(source, FourBytes): - return IP4(source) - elif isinstance(source, str) and '.' in source: - # Tokenización automática - tokenized = preprocess_tokens(source) - return eval(f"IP4({tokenized})") - - elif target_type == Bin: - if isinstance(source, (IntBase, FourBytes)): - return Bin(source) - - # Continuar para otros tipos... - -# Ejemplos de uso fluido: - -# 1. IP con máscara hexadecimal (automático): -ip1 = IP4(192.168.1.1, 16#ffffff00) -# Tokeniza a: IP4(FourBytes('192.168.1.1'), IntBase('ffffff00', 16)) -# Constructor IP4 convierte IntBase → IP4Mask automáticamente - -# 2. Hex desde IP (automático): -ip_bytes = FourBytes('192.168.1.1') -hex_ip = Hex(ip_bytes) -# Constructor Hex convierte FourBytes → representación hex - -# 3. Análisis de máscara en múltiples bases: -mask = FourBytes('255.255.255.0') -mask_hex = Hex(mask) # → Hex basado en 32-bit value -mask_bin = Bin(mask) # → Bin con representación por elementos -mask_cidr = IP4Mask(mask) # → /24 - -# 4. Operaciones mixtas automáticas: -base_ip = FourBytes('10.0.0.0') -offset = 16#100 # Tokeniza a IntBase('100', 16) = 256 -next_ip = base_ip + offset # → FourBytes('10.0.1.0') -``` - -## Casos de Uso Algebraicos - -### 1. **Redes con Variables** -```python -# Usuario escribe: -network = IP4(192.168.x.0, 24) - -# Tokenizado automáticamente a: -network = IP4(FourBytes('192.168.x.0'), 24) - -# Operaciones algebraicas: -network.substitute(x=1) # → IP4(192.168.1.0, 24) -network.address[2] # → Symbol('x') -``` - -### 2. **Aritmética de Bases** -```python -# Usuario escribe: -hex_val = Hex(16#x0) - -# Tokenizado automáticamente a: -hex_val = Hex(IntBase('x0', 16)) - -# Operaciones algebraicas: -hex_val.substitute(x=15) # → Hex(240) -hex_val + 1 # → 16*x + 1 -``` - -### 3. **Operaciones Aritméticas con IntBase** -```python -# Operaciones mantienen la base original: -a = 16#FF # → IntBase('FF', 16) -b = 16#10 # → IntBase('10', 16) -result = a + b # → IntBase('10F', 16) [255 + 16 = 271] - -# Operaciones mixtas con enteros: -c = 16#A0 + 32 # → IntBase('C0', 16) [160 + 32 = 192] -d = 2#1010 * 3 # → IntBase('11110', 2) [10 * 3 = 30] - -# Operaciones entre bases diferentes: -e = 16#FF + 2#1010 # → IntBase('109', 16) [255 + 10 = 265] -``` - -### 4. **Operaciones Aritméticas con FourBytes** -```python -# Aritmética de direcciones: -ip1 = FourBytes('192.168.1.1') # → ip_int = 3232235777 -ip2 = FourBytes('0.0.0.5') # → ip_int = 5 -next_ip = ip1 + ip2 # → FourBytes('192.168.1.6') - -# Incremento simple: -base_ip = FourBytes('10.0.0.0') -next_net = base_ip + 256 # → FourBytes('10.0.1.0') - -# Cálculos de rango: -start = FourBytes('192.168.1.0') -end = start + 255 # → FourBytes('192.168.1.255') -``` - -### 5. **Conversiones Automáticas Bidireccionales** -```python -# FourBytes → Otras bases: -ip = FourBytes('10.1.3.15') -ip.ToHex() # → "16#A.16#1.16#3.16#F" -ip.ToBinary() # → "2#1010.2#1.2#11.2#1111" -ip.ToBase(8) # → "8#12.8#1.8#3.8#17" - -# Conversión directa en constructores: -hex_ip = Hex(FourBytes('10.1.3.15')) # Automático via tokenización -ip_from_hex = IP4(FourBytes('10.1.3.16#ff')) # 16#ff se tokeniza a IntBase - -# Conversiones fluidas: -mask = FourBytes('255.255.255.0') -mask.ToBase(2) # → "2#11111111.2#11111111.2#11111111.2#0" -``` - -### 6. **Análisis de Rangos con Constraints** -```python -# Red con parámetros: -net = IP4(10.a.b.0, c) - -# Encontrar valores válidos: -constraints = [ - net.address[1] >= 0, # a >= 0 - net.address[1] <= 255, # a <= 255 - net.address[2] >= 0, # b >= 0 - net.address[2] <= 255, # b <= 255 - c >= 8, # c >= 8 - c <= 30 # c <= 30 -] - -solutions = solve(constraints, [a, b, c]) -``` - -### 7. **Ejemplos Prácticos de Conversión** -```python -# Ejemplo 1: IP con máscara hexadecimal -ip_hex_mask = IP4(192.168.1.1, 16#ffffff00) # Tokeniza automáticamente -# Equivale a: IP4(FourBytes('192.168.1.1'), IntBase('ffffff00', 16)) - -# Ejemplo 2: Conversión de máscara -mask_fourbytes = FourBytes('255.255.255.0') -mask_hex = mask_fourbytes.ToHex() # → "16#FF.16#FF.16#FF.16#0" -mask_cidr = mask_fourbytes.ToCIDR(24) # → "255.255.255.0/24" - -# Ejemplo 3: Aritmética mixta -base_net = FourBytes('192.168.0.0') -subnet_size = 2#100000000 # 256 en binario -next_subnet = base_net + subnet_size # → FourBytes('192.168.1.0') - -# Ejemplo 4: Análisis de subredes -network = FourBytes('10.0.0.0') -for i in range(4): - subnet = network + (i * 256) - print(f"Subred {i}: {subnet}") - # Subred 0: 10.0.0.0 - # Subred 1: 10.0.1.0 - # Subred 2: 10.0.2.0 - # Subred 3: 10.0.3.0 -``` - -## Ventajas del Nuevo Sistema - -### 1. **Simplicidad** -- ✅ Sin parser complejo -- ✅ Sintaxis estándar (Python puro) -- ✅ Tokenización simple y rápida -- ✅ Integración natural con eval() - -### 2. **Potencia Algebraica** -- ✅ Variables en cualquier posición -- ✅ Sustituciones automáticas -- ✅ Integración completa con SymPy -- ✅ Análisis simbólico de redes/números - -### 3. **Extensibilidad** -- ✅ Fácil agregar nuevos patrones -- ✅ Clases especializadas más simples -- ✅ Reutilización de lógica base -- ✅ Escalabilidad natural - -### 4. **Intuitividad** -- ✅ Usuario escribe código normal -- ✅ Sin sintaxis especial que memorizar -- ✅ Comportamiento predecible -- ✅ Curva de aprendizaje mínima - -## Posibles Problemas y Soluciones - -### 1. **Ambigüedad x.y vs x.y.z.w** -```python -# PROBLEMA: ¿x.y es acceso a atributo o parte de FourBytes? -# SOLUCIÓN: Solo tokenizar si hay exactamente 4 elementos -pattern = r'\b([a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+)\b' -``` - -### 2. **Precedencia de Operadores** -```python -# PROBLEMA: 192.168.1.1 + 1 -# SOLUCIÓN: Tokenización antes de parsing de operadores -# Resultado: FourBytes('192.168.1.1') + 1 -``` - -### 3. **Validación con Símbolos** -```python -# PROBLEMA: ¿Cómo validar 10.x.1.2 como IP? -# SOLUCIÓN: Validación condicional y constraint solving -def is_valid_ip_symbolic(fourbytes): - if not fourbytes.has_symbols: - return fourbytes.is_valid_ip_range() - - # Crear constraints para símbolos - constraints = [] - for i, elem in enumerate(fourbytes.elements): - if not elem.isdigit(): - symbol = Symbol(elem) - constraints.extend([symbol >= 0, symbol <= 255]) - - return constraints -``` - -## Integración Completa en el Sistema de Tipos - -### **Todas las Clases en `custom_types/`** - -#### **Consistencia Arquitectónica:** -- **IntBase** y **FourBytes** son tipos como cualquier otro -- Deben seguir el mismo patrón de registro y auto-descubrimiento -- El parser los importa desde el registro, no desde ubicaciones hardcodeadas - -#### **Estructura de Directorios Revisada:** -``` -custom_types/ -├── intbase_type.py # IntBase + función de registro -├── fourbytes_type.py # FourBytes + función de registro -├── hex_type.py # Hex (usa IntBase del registro) -├── bin_type.py # Bin (usa IntBase del registro) -├── dec_type.py # Dec (usa IntBase del registro) -├── ip4_type.py # IP4 + IP4Mask (usan FourBytes del registro) -├── chr_type.py # Chr -└── latex_type.py # LaTeX -``` - -### **Archivos de Tipos Fundamentales** - -#### **`custom_types/intbase_type.py`** -```python -""" -Clase base universal para números en cualquier base - TIPO REGISTRADO -""" -from class_base import ClassBase -import sympy -import re - -class IntBase(ClassBase): - """ - Representación universal de números en cualquier base - con manejo interno de símbolos y conversión perezosa a SymPy - """ - - def __init__(self, value_str, base=10): - self.value_str = value_str - self.base = base - self.has_symbols = bool(re.search(r'[a-zA-Z_]', value_str)) - - if self.has_symbols: - # Modo algebraico: mantener símbolos INTERNAMENTE - self._symbols = self._extract_symbols() - self._expression = self._build_internal_expression() - super().__init__(self._expression, f"{base}#{value_str}") - else: - # Modo numérico: convertir a entero - self._numeric_value = int(value_str, base) - super().__init__(self._numeric_value, f"{base}#{value_str}") - - # ... implementación completa ... - - @staticmethod - def Helper(input_str): - """Ayuda contextual para IntBase""" - if re.search(r'\d+#[0-9A-Fa-fx]+', input_str): - return '''IntBase - Números en cualquier base con álgebra simbólica - -Sintaxis: base#valor -Ejemplos: - 16#FF → IntBase('FF', 16) = 255 - 2#1010 → IntBase('1010', 2) = 10 - 8#777 → IntBase('777', 8) = 511 - 16#x0 → IntBase('x0', 16) = simbólico - -Operaciones aritméticas mantienen la base original: - 16#FF + 16#10 → IntBase('10F', 16) - 2#1010 * 3 → IntBase('11110', 2) - -Métodos disponibles: - .to_base(nueva_base) - Convierte a otra base - .substitute(x=valor) - Sustituye símbolos - .to_sympy() - Conversión explícita a SymPy''' - return None - - @staticmethod - def PopupFunctionList(): - """Lista de métodos para autocompletado""" - return [ - ("to_base", "Convierte a otra base"), - ("substitute", "Sustituye símbolos por valores"), - ("to_sympy", "Conversión explícita a SymPy para álgebra"), - ("to_decimal", "Obtiene valor decimal"), - ("to_hex", "Convierte a hexadecimal"), - ("to_binary", "Convierte a binario"), - ("to_octal", "Convierte a octal"), - ] - -def register_classes_in_module(): - """Registro de IntBase en el sistema de tipos""" - return [ - ("IntBase", IntBase, "ClassBase", { - "add_lowercase": True, - "supports_brackets": False, # Se maneja por tokenización - "is_fundamental": True, # Clase fundamental del sistema - "description": "Números universales en cualquier base con álgebra simbólica" - }), - ] -``` - -#### **`custom_types/fourbytes_type.py`** -```python -""" -Clase base universal para patrones x.x.x.x - TIPO REGISTRADO -""" -from class_base import ClassBase -import sympy -import re - -class FourBytes(ClassBase): - """ - Representación universal de patrones de 4 elementos - con manejo interno de símbolos y conversión perezosa a SymPy - """ - - def __init__(self, dotted_string): - self.original = dotted_string - self.elements = dotted_string.split('.') - - if len(self.elements) != 4: - raise ValueError(f"FourBytes requiere exactamente 4 elementos: {dotted_string}") - - self.has_symbols = any(not elem.isdigit() for elem in self.elements) - - if self.has_symbols: - # Modo algebraico: mantener símbolos INTERNAMENTE - self._symbols = self._extract_symbols() - super().__init__(dotted_string, dotted_string) # Mantener como string - else: - # Modo numérico: validar rangos y convertir - self._numeric_elements = [int(elem) for elem in self.elements] - # Crear valor como entero de 32 bits (para IPs) - self._numeric_value = (self._numeric_elements[0] << 24 | - self._numeric_elements[1] << 16 | - self._numeric_elements[2] << 8 | - self._numeric_elements[3]) - super().__init__(self._numeric_value, dotted_string) - - # ... implementación completa ... - - @staticmethod - def Helper(input_str): - """Ayuda contextual para FourBytes""" - if re.search(r'\d+\.\d+\.\d+\.\d+', input_str): - return '''FourBytes - Patrones x.x.x.x con álgebra simbólica - -Sintaxis: x.y.z.w (donde cada elemento puede ser número o símbolo) -Ejemplos: - 192.168.1.1 → FourBytes('192.168.1.1') - 255.255.0.0 → FourBytes('255.255.0.0') - 10.x.1.y → FourBytes('10.x.1.y') = simbólico - -Aritmética de 32-bit: - 192.168.1.1 + 5 → FourBytes('192.168.1.6') - 10.0.0.0 + 256 → FourBytes('10.0.1.0') - -Métodos disponibles: - .ToBase(base) - Convierte cada elemento a base específica - .ToHex() - Convierte a hexadecimal - .ToBinary() - Convierte a binario - .substitute(x=val) - Sustituye símbolos - .to_sympy() - Conversión explícita a SymPy''' - return None - - @staticmethod - def PopupFunctionList(): - """Lista de métodos para autocompletado""" - return [ - ("ToBase", "Convierte cada elemento a base específica"), - ("ToHex", "Convierte cada elemento a hexadecimal"), - ("ToBinary", "Convierte cada elemento a binario"), - ("ToOctal", "Convierte cada elemento a octal"), - ("ToCIDR", "Añade notación CIDR"), - ("substitute", "Sustituye símbolos por valores"), - ("to_sympy", "Conversión explícita a SymPy para álgebra"), - ("is_valid_ip_range", "Verifica si es rango IP válido"), - ] - -def register_classes_in_module(): - """Registro de FourBytes en el sistema de tipos""" - return [ - ("FourBytes", FourBytes, "ClassBase", { - "add_lowercase": True, - "supports_brackets": False, # Se maneja por tokenización - "is_fundamental": True, # Clase fundamental del sistema - "description": "Patrones universales x.x.x.x con álgebra simbólica" - }), - ] -``` - -### **Parser Integrado con el Registro** - -#### **`tl_bracket_parser.py` (Actualizado)** -```python -""" -Tokenizador integrado con el sistema de auto-descubrimiento de tipos -""" -import re -from typing import Tuple, Dict, Any - -# Importar desde el registro de tipos (NO hardcodeado) -try: - from type_registry import get_registered_base_context - TYPE_REGISTRY_AVAILABLE = True -except ImportError: - TYPE_REGISTRY_AVAILABLE = False - -class UniversalTokenizer: - """Tokenizador que usa el registro de tipos para conversiones automáticas""" - - def __init__(self): - self.debug = False - self._update_fundamental_classes() - - def _update_fundamental_classes(self): - """Obtiene clases fundamentales desde el registro""" - if not TYPE_REGISTRY_AVAILABLE: - # Fallback básico - self.IntBase = None - self.FourBytes = None - return - - try: - # Obtener clases desde el registro automáticamente - registered_context = get_registered_base_context() - - self.IntBase = registered_context.get('IntBase') - self.FourBytes = registered_context.get('FourBytes') - - if self.debug: - print(f"🔧 Clases fundamentales cargadas desde registro:") - print(f" IntBase: {self.IntBase}") - print(f" FourBytes: {self.FourBytes}") - - except Exception as e: - if self.debug: - print(f"⚠️ Error cargando clases fundamentales: {e}") - self.IntBase = None - self.FourBytes = None - - def reload_fundamental_classes(self): - """Recarga clases fundamentales del registro""" - if self.debug: - print("🔄 Recargando clases fundamentales...") - self._update_fundamental_classes() - - def preprocess_tokens(self, expression): - """Convierte patrones específicos en objetos tipados""" - if not self.IntBase or not self.FourBytes: - if self.debug: - print("⚠️ Clases fundamentales no disponibles") - return expression - - # Fase 1: IntBase (mayor precedencia) - # 16#FF → IntBase('FF', 16) - def replace_intbase(match): - base = match.group(1) - value = match.group(2) - return f'IntBase("{value}", {base})' - - expression = re.sub( - r'(\d+)#([0-9A-Fa-fx]+)', - replace_intbase, - expression - ) - - # Fase 2: FourBytes - # 192.168.1.1 → FourBytes('192.168.1.1') - def replace_fourbytes(match): - dotted_value = match.group(1) - return f'FourBytes("{dotted_value}")' - - expression = re.sub( - r'\b([a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+)\b', - replace_fourbytes, - expression - ) - - if self.debug: - print(f"🔧 Tokenización: '{original}' → '{expression}'") - - return expression -``` - -### **Ventajas de la Integración Completa** - -#### **1. Consistencia Arquitectónica** -- **Todo** el sistema de tipos en un lugar -- Patrón unificado de registro y descubrimiento -- No hay clases "especiales" hardcodeadas - -#### **2. Modularidad Completa** -- Cada tipo es independiente y auto-contenido -- Fácil agregar/remover tipos sin modificar código core -- Sistema verdaderamente extensible - -#### **3. Autocompletado y Ayuda Unificados** -- **IntBase** y **FourBytes** tienen `PopupFunctionList()` automáticamente -- Ayuda contextual mediante `Helper()` -- Integración completa con el sistema de autocompletado - -#### **4. Carga Dinámica** -- Parser obtiene clases desde el registro, no por importación directa -- Permite recargar tipos en tiempo de ejecución -- Sistema totalmente dinámico - -#### **5. Escalabilidad** -```python -# Agregar nuevos tipos fundamentales es trivial: -custom_types/ -├── intbase_type.py # Base universal para números -├── fourbytes_type.py # Base universal para x.x.x.x -├── sixbytes_type.py # NUEVO: Para MACs (xx:xx:xx:xx:xx:xx) -├── time_type.py # NUEVO: Para HH:MM:SS -└── ... -``` - -#### **6. Pureza del Parser** -```python -# ANTES (hardcodeado): -from intbase import IntBase -from fourbytes import FourBytes - -# DESPUÉS (dinámico): -IntBase = registry.get('IntBase') -FourBytes = registry.get('FourBytes') -``` - -## Conclusión - -Esta refactorización elimina complejidad innecesaria mientras añade capacidades algebraicas poderosas. El resultado será: - -- **Más simple** para casos básicos -- **Más poderoso** para casos avanzados -- **Más intuitivo** para el usuario -- **Más extensible** para el futuro - -El tokenizador automático hace que el usuario no tenga que aprender sintaxis especial, mientras que el soporte algebraico completo permite análisis sofisticados cuando se necesitan. \ No newline at end of file diff --git a/.doc/refactoring/tokenizacion_ditribuida.md b/.doc/refactoring/tokenizacion_ditribuida.md deleted file mode 100644 index b15e607..0000000 --- a/.doc/refactoring/tokenizacion_ditribuida.md +++ /dev/null @@ -1,1262 +0,0 @@ -# Refactorización: Sistema de Tokenización Algebraica - -## Resumen Ejecutivo - -Reemplazar el sistema actual de corchetes por un tokenizador que convierte automáticamente patrones específicos en objetos tipados con capacidades algebraicas completas. - -## Problemas del Sistema Actual - -### Sistema de Corchetes -- ❌ Complejidad innecesaria del `BracketParser` -- ❌ Conflictos con `eval()` y SymPy -- ❌ Sintaxis no estándar -- ❌ Beneficios limitados (solo IPs realmente) -- ❌ Dificulta la integración algebraica - -### Impacto -- Código complejo para casos simples -- Curva de aprendizaje innecesaria -- Mantenimiento difícil -- Extensibilidad limitada - -## Nuevo Sistema: Tokenización Algebraica - -### Filosofía Central -**El usuario escribe código Python normal. El parser mejora automáticamente los tipos de datos.** - -### Patrones de Tokenización - -#### 1. **IntBase**: `base#valor` -```python -# Input usuario # Post-tokenización -16#FF → IntBase('FF', 16) -2#1010 → IntBase('1010', 2) -8#777 → IntBase('777', 8) -16#x0 → IntBase('x0', 16) # ← ALGEBRAICO -2#101x → IntBase('101x', 2) # ← ALGEBRAICO -``` - -#### 2. **FourBytes**: `x.x.x.x` -```python -# Input usuario # Post-tokenización -192.168.1.1 → FourBytes('192.168.1.1') -255.255.0.0 → FourBytes('255.255.0.0') -10.1.x.2 → FourBytes('10.1.x.2') # ← ALGEBRAICO -a.b.c.d → FourBytes('a.b.c.d') # ← ALGEBRAICO -``` - -### Precedencia de Tokenización -```python -def preprocess_tokens(expression): - # 1. MAYOR PRECEDENCIA: IntBase (patrón más específico) - expression = re.sub(r'(\d+)#([0-9A-Fa-fx]+)', r'IntBase("\2", \1)', expression) - - # 2. MENOR PRECEDENCIA: FourBytes (patrón más general) - expression = re.sub(r'\b([a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+)\b', - r'FourBytes("\1")', expression) - - return expression -``` - -## Problema Arquitectónico Crítico: Conversión Prematura a SymPy - -### Diagnóstico del Problema Actual - -#### **Resultado Problemático Observado:** -```python -mm = 255.240.0.x # → 255.240.0.x[Sympy] ❌ -IP4(mm, 24) # → Error: MutableDenseMatrix + int ❌ -j.next_ip() # → Error: unsupported operand ❌ -``` - -#### **Causa Raíz:** -- **Conversión prematura**: FourBytes se convierte inmediatamente a SymPy Matrix -- **Pérdida de control**: SymPy toma control antes de que el objeto pueda manejar sus operaciones -- **Incompatibilidad operacional**: Matrices de SymPy no entienden aritmética de direcciones IP - -### Arquitectura Correcta: Objetos Nativos con Conversión Perezosa - -#### **Principio Fundamental:** -> **"Cada objeto debe mantenerse en su forma nativa hasta que se involucre en una ecuación algebraica compleja"** - -#### **Jerarquía Revisada:** -```python -ClassBase (SIN SymPy - manejo interno de símbolos) -├── FourBytes (símbolos internos, aritmética nativa) -├── IntBase (símbolos internos, aritmética nativa) -├── IP4, Hex, Dec, etc. (usan objetos nativos) -└── IP4Mask (nativo) - -SympyClassBase (solo cuando sea necesario) -├── Para ecuaciones algebraicas complejas -├── Para integración/derivación -└── Para sistemas de ecuaciones -``` - -#### **Comportamiento Deseado:** -```python -mm = 255.240.0.x # → FourBytes('255.240.0.x') [FourBytes] -IP4(mm, 24) # → IP4 con FourBytes interno [IP4] -j.substitute(x=1) # → Lógica interna de FourBytes [IP4] -j.next_ip() # → Aritmética interna nativa [IP4] -hh = 16#ff # → IntBase('ff', 16) [IntBase] -Dec(hh) # → Dec con IntBase interno [Dec] -``` - -## Clases Base Universales (Arquitectura Corregida) - -### IntBase: Universal para Bases Numéricas (ClassBase) - -```python -class IntBase(ClassBase): # ← CAMBIO: ClassBase, NO SympyClassBase - """ - Representación universal de números en cualquier base - con manejo interno de símbolos y conversión perezosa a SymPy - """ - - def __init__(self, value_str, base=10): - self.value_str = value_str - self.base = base - self.has_symbols = bool(re.search(r'[a-zA-Z_]', value_str)) - - if self.has_symbols: - # Modo algebraico: mantener símbolos INTERNAMENTE - self._symbols = self._extract_symbols() - self._expression = self._build_internal_expression() - super().__init__(self._expression, f"{base}#{value_str}") - else: - # Modo numérico: convertir a entero - self._numeric_value = int(value_str, base) - super().__init__(self._numeric_value, f"{base}#{value_str}") - - def _extract_symbols(self): - """Extrae símbolos sin convertir a SymPy aún""" - return re.findall(r'[a-zA-Z_][a-zA-Z0-9_]*', self.value_str) - - def _build_internal_expression(self): - """Construye expresión interna SIN SymPy""" - # Mantener como string procesable internamente - # Solo convertir a SymPy cuando se llame to_sympy() - return f"base{self.base}_expression({self.value_str})" - - def substitute(self, **kwargs): - """Sustitución interna sin involucrar SymPy""" - if not self.has_symbols: - return self - - new_value_str = self.value_str - for symbol, value in kwargs.items(): - new_value_str = new_value_str.replace(symbol, str(value)) - - return IntBase(new_value_str, self.base) - - def to_sympy(self): - """Conversión EXPLÍCITA a SymPy cuando se necesite álgebra""" - if self.has_symbols: - # AHORA sí convertir a expresión SymPy - result = 0 - digits = list(self.value_str) - - for i, digit in enumerate(reversed(digits)): - power = self.base ** i - if digit.isdigit(): - coefficient = int(digit) - elif digit in 'ABCDEF': - coefficient = ord(digit) - ord('A') + 10 - elif digit in 'abcdef': - coefficient = ord(digit) - ord('a') + 10 - else: - # Es un símbolo - coefficient = sympy.Symbol(digit) - - result += coefficient * power - - return result - else: - return sympy.sympify(self._numeric_value) - - # ========== OPERADORES ARITMÉTICOS NATIVOS ========== - - def __add__(self, other): - """Suma: aritmética nativa, mantiene la base""" - if self.has_symbols: - # Para símbolos, mantener como expresión interna - return IntBase(f"({self.value_str}) + ({other})", self.base) - - if isinstance(other, IntBase): - if other.has_symbols: - return IntBase(f"({self.value_str}) + ({other.value_str})", self.base) - result_value = self._numeric_value + other._numeric_value - elif isinstance(other, int): - result_value = self._numeric_value + other - else: - # Para operaciones complejas, convertir a SymPy - return self.to_sympy() + other - - result_str = self._convert_to_base_string(result_value, self.base) - return IntBase(result_str, self.base) - - # ... resto de operadores aritméticos nativos -``` - -### FourBytes: Universal para Patrones x.x.x.x (ClassBase) - -```python -class FourBytes(ClassBase): # ← CAMBIO: ClassBase, NO SympyClassBase - """ - Representación universal de patrones de 4 elementos - con manejo interno de símbolos y conversión perezosa a SymPy - """ - - def __init__(self, dotted_string): - self.original = dotted_string - self.elements = dotted_string.split('.') - - if len(self.elements) != 4: - raise ValueError(f"FourBytes requiere exactamente 4 elementos: {dotted_string}") - - self.has_symbols = any(not elem.isdigit() for elem in self.elements) - - if self.has_symbols: - # Modo algebraico: mantener símbolos INTERNAMENTE - self._symbols = self._extract_symbols() - super().__init__(dotted_string, dotted_string) # Mantener como string - else: - # Modo numérico: validar rangos y convertir - self._numeric_elements = [int(elem) for elem in self.elements] - # Crear valor como entero de 32 bits (para IPs) - self._numeric_value = (self._numeric_elements[0] << 24 | - self._numeric_elements[1] << 16 | - self._numeric_elements[2] << 8 | - self._numeric_elements[3]) - super().__init__(self._numeric_value, dotted_string) - - def _extract_symbols(self): - """Extrae símbolos sin convertir a SymPy aún""" - symbols = [] - for elem in self.elements: - if not elem.isdigit(): - if elem not in symbols: - symbols.append(elem) - return symbols - - def substitute(self, **kwargs): - """Sustitución interna sin involucrar SymPy""" - if not self.has_symbols: - return self - - new_elements = [] - for elem in self.elements: - if elem in kwargs: - new_elements.append(str(kwargs[elem])) - else: - new_elements.append(elem) - - return FourBytes('.'.join(new_elements)) - - def to_sympy(self): - """Conversión EXPLÍCITA a SymPy cuando se necesite álgebra""" - if self.has_symbols: - symbolic_elements = [] - for elem in self.elements: - if elem.isdigit(): - symbolic_elements.append(int(elem)) - else: - symbolic_elements.append(sympy.Symbol(elem)) - - # Retornar como Matrix o expresión según el contexto - return sympy.Matrix(symbolic_elements) - else: - return sympy.sympify(self._numeric_value) - - def __add__(self, other): - """Suma: aritmética nativa de 32-bit""" - if self.has_symbols: - # Para símbolos, mantener como expresión interna - return FourBytes(f"({self.original}) + ({other})") - - if isinstance(other, FourBytes): - if other.has_symbols: - return FourBytes(f"({self.original}) + ({other.original})") - result_int = self._numeric_value + other._numeric_value - elif isinstance(other, int): - result_int = self._numeric_value + other - else: - # Para operaciones complejas, convertir a SymPy - return self.to_sympy() + other - - # Mantener en rango de 32 bits - result_int = result_int & 0xFFFFFFFF - result_str = self._int_to_fourbytes(result_int) - return FourBytes(result_str) - - # ... resto de operadores aritméticos nativos -``` - -## Conversión Perezosa a SymPy - -### **Cuándo Convertir:** -```python -# NUNCA conversión automática - mantener nativo: -mm = FourBytes('255.240.0.x') # → [FourBytes] ✅ -ip = IP4(mm, 24) # → [IP4] ✅ -ip.next_ip() # → [IP4] ✅ - -# Conversión EXPLÍCITA solo cuando se necesite álgebra: -eq = Eq(mm.to_sympy(), other_expr) # → [SymPy Equation] ✅ -solve(eq, [x, y]) # → [SymPy Solutions] ✅ -``` - -### **Beneficios de la Conversión Perezosa:** -1. **Control granular**: Cada clase decide cuándo necesita SymPy -2. **Operaciones nativas**: Sin overhead para operaciones simples -3. **Errores claros**: Comportamiento predecible por tipo -4. **Flexibilidad**: Conversión explícita cuando se requiera álgebra compleja - -### **Integración con Ecuaciones:** -```python -# Para resolver ecuaciones complejas: -x, y = symbols('x y') -fb = FourBytes('10.x.1.y') - -# Crear constraints usando conversión explícita: -constraints = [ - fb.to_sympy()[1] >= 0, # x >= 0 - fb.to_sympy()[1] <= 255, # x <= 255 - fb.to_sympy()[3] >= 0, # y >= 0 - fb.to_sympy()[3] <= 255 # y <= 255 -] - -solutions = solve(constraints, [x, y]) -``` - -## Integración con Clases Especializadas - -### Constructores Mejorados - -```python -class IP4(SympyClassBase): - def __init__(self, address, mask=None): - # address es FourBytes (ya tokenizado) - if not isinstance(address, FourBytes): - raise TypeError("address debe ser FourBytes") - - self.address = address - - if mask is not None: - if isinstance(mask, int): - self.mask = IP4Mask(mask) # CIDR - elif isinstance(mask, FourBytes): - self.mask = IP4Mask(mask) # Dotted decimal - elif isinstance(mask, IntBase): - # Conversión automática desde hex: 16#ffffff00 → máscara - if not mask.has_symbols: - mask_int = mask._numeric_value - # Convertir a FourBytes primero - mask_fourbytes = FourBytes(FourBytes._int_to_fourbytes(mask_int)) - self.mask = IP4Mask(mask_fourbytes) - else: - self.mask = mask # Mantener simbólico - else: - self.mask = mask - -class Hex(SympyClassBase): - def __init__(self, value): - if isinstance(value, IntBase): - # value es IntBase (ya tokenizado) - self.int_base = value - super().__init__(value.value, value.original) - elif isinstance(value, FourBytes): - # Conversión automática desde FourBytes - if not value.has_symbols: - # Convertir a valor hex único (32 bits) - hex_value = hex(value._numeric_value)[2:].upper() - self.int_base = IntBase(hex_value, 16) - super().__init__(value._numeric_value, f"16#{hex_value}") - else: - # Mantener simbólico para análisis algebraico - self.int_base = value - super().__init__(value._symbolic_value, str(value)) - else: - raise TypeError("value debe ser IntBase o FourBytes") - -class Bin(SympyClassBase): - def __init__(self, value): - if isinstance(value, IntBase): - # Conversión automática de cualquier base a binario - if not value.has_symbols: - bin_value = bin(value._numeric_value)[2:] # Remover '0b' - self.int_base = IntBase(bin_value, 2) - super().__init__(value._numeric_value, f"2#{bin_value}") - else: - self.int_base = value - super().__init__(value._symbolic_value, str(value)) - elif isinstance(value, FourBytes): - # Convertir cada elemento a binario - if not value.has_symbols: - # Para FourBytes, crear representación elemento por elemento - bin_elements = [] - for elem in value._numeric_elements: - bin_elements.append(bin(elem)[2:].zfill(8)) # 8 bits por elemento - bin_representation = '.'.join(f"2#{elem}" for elem in bin_elements) - - # Crear IntBase con valor completo - full_bin = ''.join(bin_elements) - self.int_base = IntBase(full_bin, 2) - super().__init__(value._numeric_value, bin_representation) - else: - self.int_base = value - super().__init__(value._symbolic_value, str(value)) - else: - raise TypeError("value debe ser IntBase o FourBytes") - -class IP4Mask(ClassBase): - def __init__(self, mask_input): - if isinstance(mask_input, int): - # CIDR notation - self.prefix = mask_input - self.mask_int = self._prefix_to_mask_int(mask_input) - elif isinstance(mask_input, FourBytes): - # Dotted decimal desde tokenización automática - if not mask_input.has_symbols: - self.mask_int = mask_input._numeric_value - self.prefix = self._mask_int_to_prefix(self.mask_int) - else: - # Mantener simbólico - self.prefix = None - self.mask_int = mask_input._symbolic_value - elif isinstance(mask_input, IntBase): - # Desde hex u otra base - if not mask_input.has_symbols: - self.mask_int = mask_input._numeric_value - self.prefix = self._mask_int_to_prefix(self.mask_int) - else: - # Mantener simbólico - self.prefix = None - self.mask_int = mask_input._symbolic_value - else: - raise TypeError("mask_input debe ser int, FourBytes, o IntBase") - - super().__init__(self.mask_int, str(mask_input)) -``` - -### Conversiones Automáticas Bidireccionales - -```python -# Sistema de conversión fluido entre tipos: - -class UniversalConverter: - """Conversiones automáticas entre todos los tipos""" - - @staticmethod - def auto_convert(source, target_type): - """Convierte automáticamente entre tipos compatibles""" - - if target_type == Hex: - if isinstance(source, FourBytes): - return Hex(source) # Usa constructor mejorado - elif isinstance(source, IntBase): - return Hex(source) - - elif target_type == IP4: - if isinstance(source, FourBytes): - return IP4(source) - elif isinstance(source, str) and '.' in source: - # Tokenización automática - tokenized = preprocess_tokens(source) - return eval(f"IP4({tokenized})") - - elif target_type == Bin: - if isinstance(source, (IntBase, FourBytes)): - return Bin(source) - - # Continuar para otros tipos... - -# Ejemplos de uso fluido: - -# 1. IP con máscara hexadecimal (automático): -ip1 = IP4(192.168.1.1, 16#ffffff00) -# Tokeniza a: IP4(FourBytes('192.168.1.1'), IntBase('ffffff00', 16)) -# Constructor IP4 convierte IntBase → IP4Mask automáticamente - -# 2. Hex desde IP (automático): -ip_bytes = FourBytes('192.168.1.1') -hex_ip = Hex(ip_bytes) -# Constructor Hex convierte FourBytes → representación hex - -# 3. Análisis de máscara en múltiples bases: -mask = FourBytes('255.255.255.0') -mask_hex = Hex(mask) # → Hex basado en 32-bit value -mask_bin = Bin(mask) # → Bin con representación por elementos -mask_cidr = IP4Mask(mask) # → /24 - -# 4. Operaciones mixtas automáticas: -base_ip = FourBytes('10.0.0.0') -offset = 16#100 # Tokeniza a IntBase('100', 16) = 256 -next_ip = base_ip + offset # → FourBytes('10.0.1.0') -``` - -## Casos de Uso Algebraicos - -### 1. **Redes con Variables** -```python -# Usuario escribe: -network = IP4(192.168.x.0, 24) - -# Tokenizado automáticamente a: -network = IP4(FourBytes('192.168.x.0'), 24) - -# Operaciones algebraicas: -network.substitute(x=1) # → IP4(192.168.1.0, 24) -network.address[2] # → Symbol('x') -``` - -### 2. **Aritmética de Bases** -```python -# Usuario escribe: -hex_val = Hex(16#x0) - -# Tokenizado automáticamente a: -hex_val = Hex(IntBase('x0', 16)) - -# Operaciones algebraicas: -hex_val.substitute(x=15) # → Hex(240) -hex_val + 1 # → 16*x + 1 -``` - -### 3. **Operaciones Aritméticas con IntBase** -```python -# Operaciones mantienen la base original: -a = 16#FF # → IntBase('FF', 16) -b = 16#10 # → IntBase('10', 16) -result = a + b # → IntBase('10F', 16) [255 + 16 = 271] - -# Operaciones mixtas con enteros: -c = 16#A0 + 32 # → IntBase('C0', 16) [160 + 32 = 192] -d = 2#1010 * 3 # → IntBase('11110', 2) [10 * 3 = 30] - -# Operaciones entre bases diferentes: -e = 16#FF + 2#1010 # → IntBase('109', 16) [255 + 10 = 265] -``` - -### 4. **Operaciones Aritméticas con FourBytes** -```python -# Aritmética de direcciones: -ip1 = FourBytes('192.168.1.1') # → ip_int = 3232235777 -ip2 = FourBytes('0.0.0.5') # → ip_int = 5 -next_ip = ip1 + ip2 # → FourBytes('192.168.1.6') - -# Incremento simple: -base_ip = FourBytes('10.0.0.0') -next_net = base_ip + 256 # → FourBytes('10.0.1.0') - -# Cálculos de rango: -start = FourBytes('192.168.1.0') -end = start + 255 # → FourBytes('192.168.1.255') -``` - -### 5. **Conversiones Automáticas Bidireccionales** -```python -# FourBytes → Otras bases: -ip = FourBytes('10.1.3.15') -ip.ToHex() # → "16#A.16#1.16#3.16#F" -ip.ToBinary() # → "2#1010.2#1.2#11.2#1111" -ip.ToBase(8) # → "8#12.8#1.8#3.8#17" - -# Conversión directa en constructores: -hex_ip = Hex(FourBytes('10.1.3.15')) # Automático via tokenización -ip_from_hex = IP4(FourBytes('10.1.3.16#ff')) # 16#ff se tokeniza a IntBase - -# Conversiones fluidas: -mask = FourBytes('255.255.255.0') -mask.ToBase(2) # → "2#11111111.2#11111111.2#11111111.2#0" -``` - -### 6. **Análisis de Rangos con Constraints** -```python -# Red con parámetros: -net = IP4(10.a.b.0, c) - -# Encontrar valores válidos: -constraints = [ - net.address[1] >= 0, # a >= 0 - net.address[1] <= 255, # a <= 255 - net.address[2] >= 0, # b >= 0 - net.address[2] <= 255, # b <= 255 - c >= 8, # c >= 8 - c <= 30 # c <= 30 -] - -solutions = solve(constraints, [a, b, c]) -``` - -### 7. **Ejemplos Prácticos de Conversión** -```python -# Ejemplo 1: IP con máscara hexadecimal -ip_hex_mask = IP4(192.168.1.1, 16#ffffff00) # Tokeniza automáticamente -# Equivale a: IP4(FourBytes('192.168.1.1'), IntBase('ffffff00', 16)) - -# Ejemplo 2: Conversión de máscara -mask_fourbytes = FourBytes('255.255.255.0') -mask_hex = mask_fourbytes.ToHex() # → "16#FF.16#FF.16#FF.16#0" -mask_cidr = mask_fourbytes.ToCIDR(24) # → "255.255.255.0/24" - -# Ejemplo 3: Aritmética mixta -base_net = FourBytes('192.168.0.0') -subnet_size = 2#100000000 # 256 en binario -next_subnet = base_net + subnet_size # → FourBytes('192.168.1.0') - -# Ejemplo 4: Análisis de subredes -network = FourBytes('10.0.0.0') -for i in range(4): - subnet = network + (i * 256) - print(f"Subred {i}: {subnet}") - # Subred 0: 10.0.0.0 - # Subred 1: 10.0.1.0 - # Subred 2: 10.0.2.0 - # Subred 3: 10.0.3.0 -``` - -## Ventajas del Nuevo Sistema - -### 1. **Simplicidad** -- ✅ Sin parser complejo -- ✅ Sintaxis estándar (Python puro) -- ✅ Tokenización simple y rápida -- ✅ Integración natural con eval() - -### 2. **Potencia Algebraica** -- ✅ Variables en cualquier posición -- ✅ Sustituciones automáticas -- ✅ Integración completa con SymPy -- ✅ Análisis simbólico de redes/números - -### 3. **Extensibilidad** -- ✅ Fácil agregar nuevos patrones -- ✅ Clases especializadas más simples -- ✅ Reutilización de lógica base -- ✅ Escalabilidad natural - -### 4. **Intuitividad** -- ✅ Usuario escribe código normal -- ✅ Sin sintaxis especial que memorizar -- ✅ Comportamiento predecible -- ✅ Curva de aprendizaje mínima - -## Posibles Problemas y Soluciones - -### 1. **Ambigüedad x.y vs x.y.z.w** -```python -# PROBLEMA: ¿x.y es acceso a atributo o parte de FourBytes? -# SOLUCIÓN: Solo tokenizar si hay exactamente 4 elementos -pattern = r'\b([a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+)\b' -``` - -### 2. **Precedencia de Operadores** -```python -# PROBLEMA: 192.168.1.1 + 1 -# SOLUCIÓN: Tokenización antes de parsing de operadores -# Resultado: FourBytes('192.168.1.1') + 1 -``` - -### 3. **Validación con Símbolos** -```python -# PROBLEMA: ¿Cómo validar 10.x.1.2 como IP? -# SOLUCIÓN: Validación condicional y constraint solving -def is_valid_ip_symbolic(fourbytes): - if not fourbytes.has_symbols: - return fourbytes.is_valid_ip_range() - - # Crear constraints para símbolos - constraints = [] - for i, elem in enumerate(fourbytes.elements): - if not elem.isdigit(): - symbol = Symbol(elem) - constraints.extend([symbol >= 0, symbol <= 255]) - - return constraints -``` - -## Integración Completa en el Sistema de Tipos - -### **Todas las Clases en `custom_types/`** - -#### **Consistencia Arquitectónica:** -- **IntBase** y **FourBytes** son tipos como cualquier otro -- Deben seguir el mismo patrón de registro y auto-descubrimiento -- El parser los importa desde el registro, no desde ubicaciones hardcodeadas - -#### **Estructura de Directorios Revisada:** -``` -custom_types/ -├── intbase_type.py # IntBase + función de registro -├── fourbytes_type.py # FourBytes + función de registro -├── hex_type.py # Hex (usa IntBase del registro) -├── bin_type.py # Bin (usa IntBase del registro) -├── dec_type.py # Dec (usa IntBase del registro) -├── ip4_type.py # IP4 + IP4Mask (usan FourBytes del registro) -├── chr_type.py # Chr -└── latex_type.py # LaTeX -``` - -### **Archivos de Tipos Fundamentales** - -#### **`custom_types/intbase_type.py`** -```python -""" -Clase base universal para números en cualquier base - TIPO REGISTRADO -""" -from class_base import ClassBase -import sympy -import re - -class IntBase(ClassBase): - """ - Representación universal de números en cualquier base - con manejo interno de símbolos y conversión perezosa a SymPy - """ - - def __init__(self, value_str, base=10): - self.value_str = value_str - self.base = base - self.has_symbols = bool(re.search(r'[a-zA-Z_]', value_str)) - - if self.has_symbols: - # Modo algebraico: mantener símbolos INTERNAMENTE - self._symbols = self._extract_symbols() - self._expression = self._build_internal_expression() - super().__init__(self._expression, f"{base}#{value_str}") - else: - # Modo numérico: convertir a entero - self._numeric_value = int(value_str, base) - super().__init__(self._numeric_value, f"{base}#{value_str}") - - # ... implementación completa ... - - @staticmethod - def Helper(input_str): - """Ayuda contextual para IntBase""" - if re.search(r'\d+#[0-9A-Fa-fx]+', input_str): - return '''IntBase - Números en cualquier base con álgebra simbólica - -Sintaxis: base#valor -Ejemplos: - 16#FF → IntBase('FF', 16) = 255 - 2#1010 → IntBase('1010', 2) = 10 - 8#777 → IntBase('777', 8) = 511 - 16#x0 → IntBase('x0', 16) = simbólico - -Operaciones aritméticas mantienen la base original: - 16#FF + 16#10 → IntBase('10F', 16) - 2#1010 * 3 → IntBase('11110', 2) - -Métodos disponibles: - .to_base(nueva_base) - Convierte a otra base - .substitute(x=valor) - Sustituye símbolos - .to_sympy() - Conversión explícita a SymPy''' - return None - - @staticmethod - def PopupFunctionList(): - """Lista de métodos para autocompletado""" - return [ - ("to_base", "Convierte a otra base"), - ("substitute", "Sustituye símbolos por valores"), - ("to_sympy", "Conversión explícita a SymPy para álgebra"), - ("to_decimal", "Obtiene valor decimal"), - ("to_hex", "Convierte a hexadecimal"), - ("to_binary", "Convierte a binario"), - ("to_octal", "Convierte a octal"), - ] - -def register_classes_in_module(): - """Registro de IntBase en el sistema de tipos""" - return [ - ("IntBase", IntBase, "ClassBase", { - "add_lowercase": True, - "supports_brackets": False, # Se maneja por tokenización - "is_fundamental": True, # Clase fundamental del sistema - "description": "Números universales en cualquier base con álgebra simbólica" - }), - ] -``` - -#### **`custom_types/fourbytes_type.py`** -```python -""" -Clase base universal para patrones x.x.x.x - TIPO REGISTRADO -""" -from class_base import ClassBase -import sympy -import re - -class FourBytes(ClassBase): - """ - Representación universal de patrones de 4 elementos - con manejo interno de símbolos y conversión perezosa a SymPy - """ - - def __init__(self, dotted_string): - self.original = dotted_string - self.elements = dotted_string.split('.') - - if len(self.elements) != 4: - raise ValueError(f"FourBytes requiere exactamente 4 elementos: {dotted_string}") - - self.has_symbols = any(not elem.isdigit() for elem in self.elements) - - if self.has_symbols: - # Modo algebraico: mantener símbolos INTERNAMENTE - self._symbols = self._extract_symbols() - super().__init__(dotted_string, dotted_string) # Mantener como string - else: - # Modo numérico: validar rangos y convertir - self._numeric_elements = [int(elem) for elem in self.elements] - # Crear valor como entero de 32 bits (para IPs) - self._numeric_value = (self._numeric_elements[0] << 24 | - self._numeric_elements[1] << 16 | - self._numeric_elements[2] << 8 | - self._numeric_elements[3]) - super().__init__(self._numeric_value, dotted_string) - - # ... implementación completa ... - - @staticmethod - def Helper(input_str): - """Ayuda contextual para FourBytes""" - if re.search(r'\d+\.\d+\.\d+\.\d+', input_str): - return '''FourBytes - Patrones x.x.x.x con álgebra simbólica - -Sintaxis: x.y.z.w (donde cada elemento puede ser número o símbolo) -Ejemplos: - 192.168.1.1 → FourBytes('192.168.1.1') - 255.255.0.0 → FourBytes('255.255.0.0') - 10.x.1.y → FourBytes('10.x.1.y') = simbólico - -Aritmética de 32-bit: - 192.168.1.1 + 5 → FourBytes('192.168.1.6') - 10.0.0.0 + 256 → FourBytes('10.0.1.0') - -Métodos disponibles: - .ToBase(base) - Convierte cada elemento a base específica - .ToHex() - Convierte a hexadecimal - .ToBinary() - Convierte a binario - .substitute(x=val) - Sustituye símbolos - .to_sympy() - Conversión explícita a SymPy''' - return None - - @staticmethod - def PopupFunctionList(): - """Lista de métodos para autocompletado""" - return [ - ("ToBase", "Convierte cada elemento a base específica"), - ("ToHex", "Convierte cada elemento a hexadecimal"), - ("ToBinary", "Convierte cada elemento a binario"), - ("ToOctal", "Convierte cada elemento a octal"), - ("ToCIDR", "Añade notación CIDR"), - ("substitute", "Sustituye símbolos por valores"), - ("to_sympy", "Conversión explícita a SymPy para álgebra"), - ("is_valid_ip_range", "Verifica si es rango IP válido"), - ] - -def register_classes_in_module(): - """Registro de FourBytes en el sistema de tipos""" - return [ - ("FourBytes", FourBytes, "ClassBase", { - "add_lowercase": True, - "supports_brackets": False, # Se maneja por tokenización - "is_fundamental": True, # Clase fundamental del sistema - "description": "Patrones universales x.x.x.x con álgebra simbólica" - }), - ] -``` - -### **Parser Completamente Dinámico y Auto-Descubridor** - -#### **Principio: Responsabilidad de Tokenización Distribuida** -> **"Cada clase define cómo debe ser reconocida y tokenizada, el parser simplemente ejecuta las reglas descubiertas"** - -#### **`tl_universal_tokenizer.py` (Completamente Genérico)** -```python -""" -Tokenizador universal que auto-descubre reglas de tokenización -""" -import re -from typing import List, Dict, Any, Callable - -# Importar desde el registro de tipos -try: - from type_registry import get_registered_base_context - TYPE_REGISTRY_AVAILABLE = True -except ImportError: - TYPE_REGISTRY_AVAILABLE = False - -class UniversalTokenizer: - """ - Tokenizador que auto-descubre reglas de tokenización desde las clases registradas - """ - - def __init__(self): - self.debug = False - self.tokenization_rules: List[Dict[str, Any]] = [] - self._discover_tokenization_rules() - - def _discover_tokenization_rules(self): - """Auto-descubre reglas de tokenización desde todas las clases registradas""" - if not TYPE_REGISTRY_AVAILABLE: - if self.debug: - print("⚠️ Sistema de tipos no disponible") - return - - try: - # Obtener todas las clases registradas - registered_classes = get_registered_base_context() - - self.tokenization_rules.clear() - - for class_name, class_obj in registered_classes.items(): - if hasattr(class_obj, 'get_tokenization_patterns'): - try: - patterns = class_obj.get_tokenization_patterns() - for pattern_info in patterns: - self.tokenization_rules.append({ - 'class_name': class_name, - 'class_obj': class_obj, - **pattern_info - }) - - if self.debug: - print(f"📋 Regla tokenización: {class_name} - {pattern_info.get('description', 'Sin descripción')}") - - except Exception as e: - if self.debug: - print(f"⚠️ Error obteniendo patrones de {class_name}: {e}") - - # Ordenar por prioridad (menor número = mayor prioridad) - self.tokenization_rules.sort(key=lambda x: x.get('priority', 100)) - - if self.debug: - print(f"🔧 {len(self.tokenization_rules)} reglas de tokenización cargadas") - for rule in self.tokenization_rules: - print(f" {rule['priority']:2d}: {rule['class_name']} - {rule.get('description', '')}") - - except Exception as e: - if self.debug: - print(f"⚠️ Error en auto-descubrimiento: {e}") - self.tokenization_rules.clear() - - def reload_tokenization_rules(self): - """Recarga reglas de tokenización del registro""" - if self.debug: - print("🔄 Recargando reglas de tokenización...") - self._discover_tokenization_rules() - - def preprocess_tokens(self, expression: str) -> str: - """Aplica todas las reglas de tokenización descubiertas""" - if not self.tokenization_rules: - return expression - - result = expression - original = expression - - for rule in self.tokenization_rules: - try: - pattern = rule['pattern'] - replacement_func = rule['replacement'] - - # Aplicar transformación - if callable(replacement_func): - result = re.sub(pattern, replacement_func, result) - else: - result = re.sub(pattern, replacement_func, result) - - except Exception as e: - if self.debug: - print(f"⚠️ Error aplicando regla {rule['class_name']}: {e}") - continue - - if self.debug and result != original: - print(f"🔧 Tokenización: '{original}' → '{result}'") - - return result - - def get_tokenization_info(self) -> List[Dict[str, Any]]: - """Retorna información sobre las reglas de tokenización activas""" - return [ - { - 'class': rule['class_name'], - 'priority': rule.get('priority', 100), - 'description': rule.get('description', 'Sin descripción'), - 'pattern': rule['pattern'] - } - for rule in self.tokenization_rules - ] -``` - -### **Implementación en Clases de Tipos** - -#### **`custom_types/intbase_type.py` (con Tokenización)** -```python -""" -Clase base universal para números en cualquier base - CON TOKENIZACIÓN PROPIA -""" -from class_base import ClassBase -import sympy -import re - -class IntBase(ClassBase): - """ - Representación universal de números en cualquier base - con manejo interno de símbolos y conversión perezosa a SymPy - """ - - def __init__(self, value_str, base=10): - # ... implementación completa ... - - @staticmethod - def get_tokenization_patterns(): - """ - Define cómo esta clase debe ser reconocida y tokenizada - - Returns: - List[Dict]: Lista de reglas de tokenización - """ - return [ - { - 'pattern': r'(\d+)#([0-9A-Fa-fx]+)', - 'replacement': lambda match: f'IntBase("{match.group(2)}", {match.group(1)})', - 'priority': 5, # ALTA prioridad (patrón muy específico) - 'description': 'Números con base: 16#FF, 2#1010, etc.' - } - ] - - # ... resto de la implementación ... - -def register_classes_in_module(): - """Registro de IntBase en el sistema de tipos""" - return [ - ("IntBase", IntBase, "ClassBase", { - "add_lowercase": True, - "supports_brackets": False, - "is_fundamental": True, - "has_tokenization": True, # NUEVO: Indica que tiene tokenización - "description": "Números universales en cualquier base con álgebra simbólica" - }), - ] -``` - -#### **`custom_types/fourbytes_type.py` (con Tokenización)** -```python -""" -Clase base universal para patrones x.x.x.x - CON TOKENIZACIÓN PROPIA -""" -from class_base import ClassBase -import sympy -import re - -class FourBytes(ClassBase): - """ - Representación universal de patrones de 4 elementos - con manejo interno de símbolos y conversión perezosa a SymPy - """ - - def __init__(self, dotted_string): - # ... implementación completa ... - - @staticmethod - def get_tokenization_patterns(): - """ - Define cómo esta clase debe ser reconocida y tokenizada - - Returns: - List[Dict]: Lista de reglas de tokenización - """ - return [ - { - 'pattern': r'\b([a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+)\b', - 'replacement': lambda match: f'FourBytes("{match.group(1)}")', - 'priority': 10, # MENOR prioridad que IntBase (patrón más general) - 'description': 'Patrones de 4 elementos: 192.168.1.1, 10.x.y.z' - } - ] - - # ... resto de la implementación ... - -def register_classes_in_module(): - """Registro de FourBytes en el sistema de tipos""" - return [ - ("FourBytes", FourBytes, "ClassBase", { - "add_lowercase": True, - "supports_brackets": False, - "is_fundamental": True, - "has_tokenization": True, # NUEVO: Indica que tiene tokenización - "description": "Patrones universales x.x.x.x con álgebra simbólica" - }), - ] -``` - -#### **Ejemplo: Tipo Futuro con Tokenización Compleja** -```python -# custom_types/mac_type.py -class MACAddress(ClassBase): - """Direcciones MAC con tokenización sofisticada""" - - @staticmethod - def get_tokenization_patterns(): - """Múltiples patrones de MAC address""" - return [ - { - 'pattern': r'\b([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})\b', - 'replacement': lambda match: f'MACAddress("{match.group(1)}", format="colon")', - 'priority': 6, - 'description': 'MAC con dos puntos: AA:BB:CC:DD:EE:FF' - }, - { - 'pattern': r'\b([0-9A-Fa-f]{2}-[0-9A-Fa-f]{2}-[0-9A-Fa-f]{2}-[0-9A-Fa-f]{2}-[0-9A-Fa-f]{2}-[0-9A-Fa-f]{2})\b', - 'replacement': lambda match: f'MACAddress("{match.group(1)}", format="dash")', - 'priority': 6, - 'description': 'MAC con guiones: AA-BB-CC-DD-EE-FF' - }, - { - 'pattern': r'\b([0-9A-Fa-f]{12})\b', - 'replacement': lambda match: f'MACAddress("{match.group(1)}", format="compact")', - 'priority': 15, # Baja prioridad (patrón muy general) - 'description': 'MAC compacta: AABBCCDDEEFF' - } - ] - -# custom_types/time_type.py -class TimeStamp(ClassBase): - """Timestamps con múltiples formatos""" - - @staticmethod - def get_tokenization_patterns(): - return [ - { - 'pattern': r'\b(\d{1,2}:\d{2}:\d{2})\b', - 'replacement': lambda match: f'TimeStamp("{match.group(1)}", format="HMS")', - 'priority': 8, - 'description': 'Tiempo HH:MM:SS' - }, - { - 'pattern': r'\b(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})\b', - 'replacement': lambda match: f'TimeStamp("{match.group(1)}", format="ISO")', - 'priority': 7, - 'description': 'ISO timestamp: 2024-01-01T12:30:45' - } - ] -``` - -### **Ventajas de la Tokenización Distribuida** - -#### **1. Responsabilidad Única** -- Cada clase es responsable de su reconocimiento -- Parser genérico sin conocimiento específico de tipos -- Lógica de tokenización encapsulada con la clase - -#### **2. Escalabilidad Perfecta** -```python -# Agregar nuevo tipo con tokenización: -# 1. Crear clase en custom_types/ -# 2. Implementar get_tokenization_patterns() -# 3. ¡AUTOMÁTICAMENTE funciona! - -# NO se requiere: -# - Modificar parser -# - Modificar motor de evaluación -# - Registrar patrones manualmente -``` - -#### **3. Control de Precedencia Granular** -```python -# Precedencias típicas: -IntBase: priority = 5 # MUY específico: 16#FF -IPv6: priority = 6 # Específico: 2001:db8::1 -MAC: priority = 7 # Específico: AA:BB:CC:DD:EE:FF -FourBytes: priority = 10 # General: x.x.x.x -Compact: priority = 15 # MUY general: AABBCCDD -``` - -#### **4. Flexibilidad Extrema** -- Múltiples patrones por clase -- Lógica de reemplazo compleja con funciones lambda -- Validación condicional dentro del patrón -- Soporte para formatos alternativos - -#### **5. Auto-Descubrimiento Completo** -```python -# El sistema automáticamente descubre: -tokenizer.get_tokenization_info() -# [ -# {'class': 'IntBase', 'priority': 5, 'description': 'Números con base'}, -# {'class': 'IPv6', 'priority': 6, 'description': 'Direcciones IPv6'}, -# {'class': 'FourBytes', 'priority': 10, 'description': 'Patrones x.x.x.x'} -# ] -``` - -#### **6. Debugging y Introspección** -```python -# Información completa del proceso: -tokenizer.debug = True -tokenizer.preprocess_tokens("16#FF + 192.168.1.1") - -# Output: -# 📋 Regla tokenización: IntBase - Números con base: 16#FF, 2#1010 -# 📋 Regla tokenización: FourBytes - Patrones de 4 elementos: 192.168.1.1, 10.x.y.z -# 🔧 Tokenización: '16#FF + 192.168.1.1' → 'IntBase("FF", 16) + FourBytes("192.168.1.1")' -``` - -### **Ventajas de la Integración Completa** - -#### **1. Consistencia Arquitectónica** -- **Todo** el sistema de tipos en un lugar -- Patrón unificado de registro y descubrimiento -- No hay clases "especiales" hardcodeadas - -#### **2. Modularidad Completa** -- Cada tipo es independiente y auto-contenido -- Fácil agregar/remover tipos sin modificar código core -- Sistema verdaderamente extensible - -#### **3. Autocompletado y Ayuda Unificados** -- **IntBase** y **FourBytes** tienen `PopupFunctionList()` automáticamente -- Ayuda contextual mediante `Helper()` -- Integración completa con el sistema de autocompletado - -#### **4. Carga Dinámica** -- Parser obtiene clases desde el registro, no por importación directa -- Permite recargar tipos en tiempo de ejecución -- Sistema totalmente dinámico - -#### **5. Escalabilidad** -```python -# Agregar nuevos tipos fundamentales es trivial: -custom_types/ -├── intbase_type.py # Base universal para números -├── fourbytes_type.py # Base universal para x.x.x.x -├── sixbytes_type.py # NUEVO: Para MACs (xx:xx:xx:xx:xx:xx) -├── time_type.py # NUEVO: Para HH:MM:SS -└── ... -``` - -#### **6. Pureza del Parser** -```python -# ANTES (hardcodeado): -from intbase import IntBase -from fourbytes import FourBytes - -# DESPUÉS (dinámico): -IntBase = registry.get('IntBase') -FourBytes = registry.get('FourBytes') -``` - -## Conclusión - -Esta refactorización elimina complejidad innecesaria mientras añade capacidades algebraicas poderosas. El resultado será: - -- **Más simple** para casos básicos -- **Más poderoso** para casos avanzados -- **Más intuitivo** para el usuario -- **Más extensible** para el futuro - -El tokenizador automático hace que el usuario no tenga que aprender sintaxis especial, mientras que el soporte algebraico completo permite análisis sofisticados cuando se necesitan. \ No newline at end of file diff --git a/.gitignore b/.gitignore index bda86c7..65045b3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ __pycache__/ # C extensions *.so +*.json + # Distribution / packaging .Python build/ diff --git a/basic_test_results.json b/basic_test_results.json deleted file mode 100644 index f846a88..0000000 --- a/basic_test_results.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "execution_info": { - "timestamp": "2025-06-05T18:25:22.256442Z", - "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 - }, - { - "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 - }, - { - "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 - }, - { - "index": 5, - "input": "engine.symbol_table", - "output": "{'mask': FourBytes('255.255.0.0')}", - "result_type": "dict", - "success": true, - "error": null, - "exec_result": { - "mask": "255.255.0.0" - } - }, - { - "index": 6, - "input": "solve(x**2 + 1, x)", - "output": "[-I, I]", - "result_type": "list", - "success": true, - "error": null - }, - { - "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/custom_types/intbase_type.py b/custom_types/intbase_type.py index 016e1d1..efbb9eb 100644 --- a/custom_types/intbase_type.py +++ b/custom_types/intbase_type.py @@ -2,15 +2,19 @@ Clase base universal para números en cualquier base - TIPO REGISTRADO """ from class_base import ClassBase +from sympy_Base import SympyClassBase import sympy import re -class IntBase(ClassBase): +class IntBase(SympyClassBase): """ Representación universal de números en cualquier base con manejo interno de símbolos y conversión perezosa a SymPy """ + # Prioridad alta para que SymPy use nuestros operadores aritméticos + _op_priority = 15 + def __init__(self, value_str, base=10): self.value_str = str(value_str) self.base = int(base) @@ -19,18 +23,20 @@ class IntBase(ClassBase): self.has_symbols = self._has_algebraic_symbols() if self.has_symbols: - # Modo algebraico: mantener símbolos INTERNAMENTE, no convertir a SymPy aún + # Modo algebraico: mantener símbolos INTERNAMENTE self._symbols = self._extract_symbols() self._numeric_value = None # No hay valor numérico directo original_str = f"{self.base}#{self.value_str}" - super().__init__(self.value_str, original_str) # ClassBase, NO SympyClassBase + # Para SympyClassBase: (numeric_value, string_representation) + super().__init__(self.value_str, original_str) else: # Modo numérico: convertir a entero try: self._numeric_value = int(self.value_str, self.base) self._symbols = [] original_str = f"{self.base}#{self.value_str}" - super().__init__(self._numeric_value, original_str) # ClassBase + # Para SympyClassBase: (numeric_value, string_representation) + super().__init__(self._numeric_value, original_str) except ValueError: raise ValueError(f"Valor inválido '{self.value_str}' para base {self.base}") @@ -154,6 +160,20 @@ class IntBase(ClassBase): def __add__(self, other): """Suma nativa - mantiene como IntBase cuando es posible""" + # Manejar tipos de SymPy (Integer, One, etc.) + if hasattr(other, '__int__'): + try: + other_int = int(other) + if self.has_symbols: + new_expr = f"({self.value_str}) + {other_int}" + return IntBase(new_expr, self.base) + else: + result_value = self._numeric_value + other_int + result_str = self._convert_to_base_string(int(result_value), self.base) + return IntBase(result_str, self.base) + except (ValueError, TypeError): + pass + if self.has_symbols: # Con símbolos, crear expresión simbólica pero mantener IntBase if isinstance(other, IntBase): @@ -190,6 +210,20 @@ class IntBase(ClassBase): def __sub__(self, other): """Resta nativa""" + # Manejar tipos de SymPy + if hasattr(other, '__int__'): + try: + other_int = int(other) + if self.has_symbols: + new_expr = f"({self.value_str}) - {other_int}" + return IntBase(new_expr, self.base) + else: + result_value = self._numeric_value - other_int + result_str = self._convert_to_base_string(int(result_value), self.base) + return IntBase(result_str, self.base) + except (ValueError, TypeError): + pass + if self.has_symbols: if isinstance(other, IntBase): if other.has_symbols: @@ -281,6 +315,10 @@ class IntBase(ClassBase): """Representación para debug""" return f"IntBase('{self.value_str}', {self.base})" + def _sympystr(self, printer): + """Controla cómo SymPy representa este objeto""" + return f"{self.base}#{self.value_str}" + @staticmethod def Helper(input_str): """Ayuda contextual para IntBase""" @@ -337,10 +375,10 @@ Métodos disponibles: def register_classes_in_module(): """Registro de IntBase en el sistema de tipos""" return [ - ("IntBase", IntBase, "ClassBase", { + ("IntBase", IntBase, "SympyClassBase", { "add_lowercase": True, "supports_brackets": False, # Se maneja por tokenización "is_fundamental": True, # Clase fundamental del sistema - "description": "Números universales en cualquier base con álgebra simbólica" + "description": "Números universales en cualquier base con álgebra simbólica completa" }), ] \ No newline at end of file diff --git a/custom_types/ip4_type.py b/custom_types/ip4_type.py index 5ed01e2..e7e7a05 100644 --- a/custom_types/ip4_type.py +++ b/custom_types/ip4_type.py @@ -357,6 +357,9 @@ class IP4Mask(ClassBase): class Class_IP4(SympyClassBase): """Clase híbrida para direcciones IPv4 - ADAPTADA AL NUEVO SISTEMA""" + # Prioridad alta para que SymPy use nuestros operadores en lugar de conversión a entero + _op_priority = 20 + def __init__(self, address, mask=None): """ Constructor mejorado que acepta FourBytes desde tokenización automática @@ -524,6 +527,89 @@ class Class_IP4(SympyClassBase): def is_symbolic(self): """¿Contiene símbolos?""" return self._has_symbols + + # ========== OPERADORES ARITMÉTICOS PARA SYMPY ========== + + def __add__(self, other): + """Suma aritmética de direcciones IP - mantiene tipo IP4""" + # Manejar tipos de SymPy (Integer, One, etc.) + if hasattr(other, '__int__'): + try: + other_int = int(other) + return self.add_hosts(other_int) + except (ValueError, TypeError): + pass + + if isinstance(other, (int, float)): + # ip + entero = ip + hosts + return self.add_hosts(int(other)) + elif isinstance(other, Class_IP4): + # ip1 + ip2 = suma de enteros convertida a IP + new_int = self.to_integer() + other.to_integer() + return Class_IP4(new_int, self._mask_obj) + else: + # Para otros tipos, dejar que SymPy maneje + return NotImplemented + + def __radd__(self, other): + """Suma reversa""" + return self.__add__(other) + + def __sub__(self, other): + """Resta aritmética de direcciones IP""" + # Manejar tipos de SymPy + if hasattr(other, '__int__'): + try: + other_int = int(other) + return self.subtract_hosts(other_int) + except (ValueError, TypeError): + pass + + if isinstance(other, (int, float)): + # ip - entero = ip - hosts + return self.subtract_hosts(int(other)) + elif isinstance(other, Class_IP4): + # ip1 - ip2 = diferencia en hosts + return self.to_integer() - other.to_integer() + else: + return NotImplemented + + def __rsub__(self, other): + """Resta reversa""" + if isinstance(other, (int, float)): + # entero - ip = nueva IP + new_int = int(other) - self.to_integer() + return Class_IP4(new_int, self._mask_obj) + else: + return NotImplemented + + def __mul__(self, other): + """Multiplicación (útil para cálculos de subred)""" + if isinstance(other, (int, float)): + new_int = self.to_integer() * int(other) + return Class_IP4(new_int, self._mask_obj) + else: + return NotImplemented + + def __rmul__(self, other): + """Multiplicación reversa""" + return self.__mul__(other) + + def __floordiv__(self, other): + """División entera""" + if isinstance(other, (int, float)): + new_int = self.to_integer() // int(other) + return Class_IP4(new_int, self._mask_obj) + else: + return NotImplemented + + def __mod__(self, other): + """Módulo (útil para cálculos en subredes)""" + if isinstance(other, (int, float)): + remainder = self.to_integer() % int(other) + return Class_IP4(remainder, self._mask_obj) + else: + return NotImplemented @staticmethod def Helper(input_str): diff --git a/hybrid_calc_history.txt b/hybrid_calc_history.txt index 60e6407..a52ced7 100644 --- a/hybrid_calc_history.txt +++ b/hybrid_calc_history.txt @@ -1,9 +1,26 @@ -a = x + 5 / z +# x = 5 +y = x + 3 +z = y + x +x=? +solve() + # Instanciación via sympify +ip = IP4(120.11.255.2,30) +ip.Nodes() +ip.to_hex() +ip + 1 +10.1.1.1 +p=Dec(16#FF + 16#FF) / 18 +p +t= 2#1010 + 16#f +t.to_base(8) -z=? -a=2 -x=3 +ip.bit_representation() +ip=ip+20 +ip.bit_representation() +ip.mask() +ip.get_prefix_length() -IP4(10.1.1.2) \ No newline at end of file +a = b + 5 +solve(x) \ No newline at end of file diff --git a/hybrid_calc_settings.json b/hybrid_calc_settings.json index ec8e909..b491249 100644 --- a/hybrid_calc_settings.json +++ b/hybrid_calc_settings.json @@ -1,6 +1,6 @@ { "window_geometry": "1020x700+356+1216", - "sash_pos_x": 359, + "sash_pos_x": 327, "symbolic_mode": true, "show_numeric_approximation": true, "keep_symbolic_fractions": true, diff --git a/main_evaluation_puro.py b/main_evaluation_puro.py index 19741c5..471e1d8 100644 --- a/main_evaluation_puro.py +++ b/main_evaluation_puro.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ Motor de Evaluación Algebraico Puro para Calculadora MAV -ARQUITECTURA SIMPLIFICADA: Todas las líneas con = son ecuaciones +VERSIÓN UNIFICADA: Un solo parser (sympify) con contexto completo """ import re import sympy as sp @@ -16,7 +16,11 @@ try: except ImportError: HAS_SYMPY_HELPER = False -from type_registry import get_registered_base_context, get_registered_tokenization_patterns, discover_and_register_types +from type_registry import ( + get_registered_base_context, + get_registered_tokenization_patterns, + discover_and_register_types +) from tl_bracket_parser import BracketParser @@ -33,21 +37,22 @@ class EvaluationResult: class PureAlgebraicEngine: - """Motor algebraico puro - Todas las asignaciones son ecuaciones""" + """Motor algebraico puro unificado - Un solo parser sympify""" 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.symbol_table = {} # Variables del usuario + self.unified_context = {} # Contexto unificado para sympify 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() + # Construir contexto unificado + self._build_unified_context() self._load_tokenization_patterns() def _load_custom_types(self): @@ -58,64 +63,69 @@ class PureAlgebraicEngine: 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""" + def _build_unified_context(self): + """Construye contexto unificado para sympify con TODOS los componentes""" + + # 1. FUNCIONES SYMPY BÁSICAS + sympy_functions = { + 'sin': sp.sin, 'cos': sp.cos, 'tan': sp.tan, + 'asin': sp.asin, 'acos': sp.acos, 'atan': sp.atan, + 'sinh': sp.sinh, 'cosh': sp.cosh, 'tanh': sp.tanh, + '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, + 'Eq': sp.Eq, 'simplify': sp.simplify, + 'expand': sp.expand, 'factor': sp.factor, + 'diff': sp.diff, 'integrate': sp.integrate, + 'Matrix': sp.Matrix, 'symbols': sp.symbols, + 'Symbol': sp.Symbol, 'Rational': sp.Rational, + 'Float': sp.Float, 'Integer': sp.Integer, + 'limit': sp.limit, 'series': sp.series, + 'summation': sp.summation, 'product': sp.product, + 'binomial': sp.binomial, 'factorial': sp.factorial, + 'gcd': sp.gcd, 'lcm': sp.lcm, + 'ceiling': sp.ceiling, 'floor': sp.floor, + 'Piecewise': sp.Piecewise, + } + + # 2. TIPOS PERSONALIZADOS REGISTRADOS (CLAVE PARA INSTANCIACIÓN) + registered_types = get_registered_base_context() + + # 3. FUNCIONES DE PLOTTING try: - # Contexto de SymPy básico - self.context.update({ - 'sin': sp.sin, 'cos': sp.cos, 'tan': sp.tan, - 'asin': sp.asin, 'acos': sp.acos, 'atan': sp.atan, - 'sinh': sp.sinh, 'cosh': sp.cosh, 'tanh': sp.tanh, - '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, - 'Symbol': sp.Symbol, 'Rational': sp.Rational, - 'Float': sp.Float, 'Integer': sp.Integer, - 'limit': sp.limit, 'series': sp.series, - 'summation': sp.summation, 'product': sp.product, - 'binomial': sp.binomial, 'factorial': sp.factorial, - 'gcd': sp.gcd, 'lcm': sp.lcm, - 'ceiling': sp.ceiling, 'floor': sp.floor, - 'Piecewise': sp.Piecewise, - }) - - # Funciones de plotting - try: - from sympy.plotting import plot, plot3d, plot_parametric, plot3d_parametric_line - self.context.update({ - 'plot': plot, - 'plot3d': plot3d, - 'plot_parametric': plot_parametric, - 'plot3d_parametric_line': plot3d_parametric_line, - }) - self.logger.debug("Funciones de plotting cargadas") - except Exception as e: - self.logger.warning(f"Error cargando funciones de plotting: {e}") - - # Contexto dinámico de tipos personalizados - dynamic_context = get_registered_base_context() - self.context.update(dynamic_context) - - # Verificar que las clases principales estén disponibles - required_classes = ['IP4', 'IP4Mask', 'FourBytes', 'IntBase', 'Hex', 'Bin', 'Dec', 'Chr', 'LaTeX'] - missing_classes = [] - for cls_name in required_classes: - if cls_name not in self.context: - missing_classes.append(cls_name) - - if missing_classes: - self.logger.warning(f"Clases faltantes en contexto: {missing_classes}") - - self.logger.debug(f"Contexto cargado: {len(self.context)} elementos") - + from sympy.plotting import plot, plot3d, plot_parametric, plot3d_parametric_line + plotting_functions = { + 'plot': plot, + 'plot3d': plot3d, + 'plot_parametric': plot_parametric, + 'plot3d_parametric_line': plot3d_parametric_line, + } except Exception as e: - self.logger.error(f"Error cargando contexto: {e}") + self.logger.warning(f"Error cargando funciones de plotting: {e}") + plotting_functions = {} + + # 4. COMBINAR TODO EN CONTEXTO UNIFICADO + self.unified_context = { + **sympy_functions, + **registered_types, # IP4, FourBytes, IntBase, etc. + **plotting_functions + } + + # 5. VERIFICAR CARGA DE TIPOS PRINCIPALES + required_classes = ['IP4', 'IP4Mask', 'FourBytes', 'IntBase', 'Hex', 'Bin', 'Dec', 'Chr', 'LaTeX'] + missing_classes = [cls for cls in required_classes if cls not in self.unified_context] + + if missing_classes: + self.logger.warning(f"Clases faltantes en contexto: {missing_classes}") + + self.logger.debug(f"Contexto unificado construido: {len(self.unified_context)} elementos") + + # Verificar que tipos principales tengan prioridad correcta para álgebra + for name, cls in registered_types.items(): + if hasattr(cls, '_op_priority'): + self.logger.debug(f"{name} tiene prioridad: {cls._op_priority}") def _load_tokenization_patterns(self): """Carga los patrones de tokenización dinámicos""" @@ -143,7 +153,6 @@ class PureAlgebraicEngine: 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}") @@ -154,17 +163,25 @@ class PureAlgebraicEngine: return tokenized_line + def _get_complete_context(self) -> Dict[str, Any]: + """Obtiene contexto completo incluyendo variables del usuario""" + complete_context = self.unified_context.copy() + complete_context.update(self.symbol_table) + return complete_context + def evaluate_line(self, line: str) -> EvaluationResult: - """Evalúa una línea de entrada""" + """Evalúa una línea de entrada usando sympify unificado""" line = line.strip() if not line or line.startswith('#'): return EvaluationResult(line, "", "comment", True) try: - # 1. Aplicar tokenización dinámica PRIMERO + # 1. Aplicar tokenización dinámica tokenized_line = self._apply_tokenization(line) - # 2. Preprocesar con bracket parser + # Tokenización aplicada silenciosamente + + # 2. Preprocesar con bracket parser (legacy) processed_line = self.bracket_parser.process_expression(tokenized_line) self.logger.debug(f"Línea procesada: {processed_line}") @@ -172,7 +189,12 @@ class PureAlgebraicEngine: 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) + # Verificar si es una asignación simple (lado izquierdo es variable) + left_side = processed_line.split('=')[0].strip() + if re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', left_side): + return self._evaluate_assignment(processed_line) + else: + return self._evaluate_equation(processed_line) else: return self._evaluate_expression(processed_line) @@ -200,18 +222,18 @@ class PureAlgebraicEngine: solution = self._solve_for_variable(var_symbol) # Output conciso - if solution != var_symbol: # Si hay solución real + if solution != var_symbol: 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 + output = str(var_symbol) return EvaluationResult(line, output, "symbolic", True, is_solve_query=True) elif line.startswith('solve('): - # Función solve() + # Función solve() - usar sympify unificado result = self._evaluate_expression(line) result.is_solve_query = True return result @@ -220,6 +242,44 @@ class PureAlgebraicEngine: error_msg = f"Error en resolución: {str(e)}" return EvaluationResult(line, error_msg, "error", False, str(e)) + def _evaluate_assignment(self, line: str) -> EvaluationResult: + """Evalúa una asignación manteniendo doble registro""" + try: + # Separar variable = expresión + var_name, expr_str = line.split('=', 1) + var_name = var_name.strip() + expr_str = expr_str.strip() + + # Evaluar la expresión del lado derecho + context = self._get_complete_context() + result = sympify(expr_str, locals=context) + + # 1. ASIGNACIÓN DIRECTA (para uso inmediato) + self.symbol_table[var_name] = result + # Variable asignada correctamente + + # 2. ECUACIÓN IMPLÍCITA (para solve) + var_symbol = sp.Symbol(var_name) + equation = Eq(var_symbol, result) + self.equations.append(equation) + + # Añadir símbolo a variables conocidas + self.variables.add(var_symbol) + + # Output conciso - mostrar el valor asignado + output = str(result) + + # Añadir aproximación numérica si es útil + numeric = self._get_numeric_approximation(result) + if numeric and str(result) != str(numeric): + output += f" ≈ {numeric}" + + return EvaluationResult(line, output, "assignment", True) + + except Exception as e: + error_msg = f"Error en asignació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: @@ -228,9 +288,10 @@ class PureAlgebraicEngine: 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) + # USAR SYMPIFY UNIFICADO para ambos lados + context = self._get_complete_context() + left_expr = sympify(left_str, locals=context) + right_expr = sympify(right_str, locals=context) # Crear ecuación equation = Eq(left_expr, right_expr) @@ -242,10 +303,10 @@ class PureAlgebraicEngine: eq_vars = equation.free_symbols self.variables.update(eq_vars) - # Output conciso de una línea + # Output conciso output = str(equation) - # Evaluación numérica si es posible (solo para lado derecho) + # Evaluación numérica si es posible numeric = self._get_numeric_approximation(equation.rhs) if numeric and str(equation.rhs) != str(numeric): output += f" ≈ {numeric}" @@ -257,15 +318,17 @@ class PureAlgebraicEngine: return EvaluationResult(line, error_msg, "error", False, str(e)) def _evaluate_expression(self, line: str) -> EvaluationResult: - """Evalúa una expresión libre""" + """Evalúa una expresión usando sympify unificado ÚNICAMENTE""" try: - # Evaluar con SymPy - expr = sympify(line, locals=self.context) + # USAR SYMPIFY UNIFICADO - Un solo parser + context = self._get_complete_context() + result = sympify(line, locals=context) - # Evaluar la expresión - result = expr - if hasattr(expr, 'evalf'): # Es expresión SymPy, simplificar - result = simplify(expr) + # Nota: Las asignaciones ahora se manejan en _evaluate_assignment + + # Simplificar si es expresión SymPy + if hasattr(result, 'simplify'): + result = simplify(result) output = str(result) @@ -288,7 +351,6 @@ class PureAlgebraicEngine: 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) @@ -339,7 +401,7 @@ class PureAlgebraicEngine: 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 + if abs(float_val) > 1e-10: return f"{float_val:.6f}".rstrip('0').rstrip('.') except: pass @@ -353,6 +415,7 @@ class PureAlgebraicEngine: """Limpia el contexto de evaluación pero mantiene los tipos base""" self.equations.clear() self.variables.clear() + self.symbol_table.clear() self.logger.info("Contexto limpio") def get_context_info(self) -> Dict[str, Any]: @@ -360,26 +423,20 @@ class PureAlgebraicEngine: return { "equations": len(self.equations), "variables": list(self.variables), - "context_size": len(self.context), + "symbol_table": len(self.symbol_table), + "context_size": len(self.unified_context), "tokenization_patterns": len(self.tokenization_patterns), - "recent_equations": [str(eq) for eq in self.equations[-5:]] # Últimas 5 + "recent_equations": [str(eq) for eq in self.equations[-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 + return self._get_complete_context() def get_available_types(self) -> List[str]: """Obtiene tipos disponibles (compatibilidad)""" available_types = [] - for name, obj in self.context.items(): + for name, obj in self.unified_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) @@ -387,7 +444,8 @@ class PureAlgebraicEngine: def reload_types(self): """Recarga los tipos dinámicos (compatibilidad)""" - self._load_base_context() + self._load_custom_types() + self._build_unified_context() self._load_tokenization_patterns() self.logger.info("Tipos y patrones recargados") @@ -405,24 +463,32 @@ def evaluate_line(line: str, engine: PureAlgebraicEngine = None) -> EvaluationRe # ========== EJEMPLO DE USO ========== if __name__ == "__main__": - # Demo del motor puro + # Demo del motor unificado 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 + "ip = IP4(10.1.1.1)", + "ip + 1", # ✅ Aritmética IP con _op_priority + "ip.to_hex()", + "10.1.1.1", # ✅ Tokenización automática + "16#FF + 1", # ✅ Aritmética con IntBase y _op_priority + "16#FF", # IntBase directo + "16#FF.to_hex()", # Método directo + "n = 16#FF", # Asignación IntBase + "n.to_hex()" # Método del objeto asignado ] - print("=== DEMO MOTOR ALGEBRAICO PURO ===") + print("=== DEMO MOTOR ALGEBRAICO UNIFICADO ===") + print(f"Tipos personalizados cargados: {len([k for k in engine.unified_context.keys() if k in ['IP4', 'FourBytes', 'IntBase']])}") + print(f"Patrones de tokenización: {len(engine.tokenization_patterns)}") + print() + 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()}") \ No newline at end of file + print(f"\nContexto: {engine.get_context_info()}") \ No newline at end of file diff --git a/test_ecuaciones_puras.json b/test_ecuaciones_puras.json deleted file mode 100644 index e723598..0000000 --- a/test_ecuaciones_puras.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "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)" - } - ] -} \ No newline at end of file diff --git a/test_ecuaciones_puras_results.json b/test_ecuaciones_puras_results.json deleted file mode 100644 index 5c600de..0000000 --- a/test_ecuaciones_puras_results.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "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 (, line 1)" - }, - { - "index": 1, - "input": "from sympy import symbols, Eq, solve", - "output": null, - "result_type": null, - "success": false, - "error": "invalid syntax (, line 1)" - }, - { - "index": 2, - "input": "x, y, z = symbols('x y z')", - "output": null, - "result_type": null, - "success": false, - "error": "invalid syntax (, line 1)" - }, - { - "index": 3, - "input": "# Sistema puramente algebraico", - "output": null, - "result_type": null, - "success": false, - "error": "invalid syntax (, line 1)" - }, - { - "index": 4, - "input": "eq1 = Eq(x, 5)", - "output": null, - "result_type": null, - "success": false, - "error": "invalid syntax (, line 1)" - }, - { - "index": 5, - "input": "eq2 = Eq(y, x + 3)", - "output": null, - "result_type": null, - "success": false, - "error": "invalid syntax (, line 1)" - }, - { - "index": 6, - "input": "eq3 = Eq(z, x + y)", - "output": null, - "result_type": null, - "success": false, - "error": "invalid syntax (, line 1)" - }, - { - "index": 7, - "input": "sistema = [eq1, eq2, eq3]", - "output": null, - "result_type": null, - "success": false, - "error": "invalid syntax (, 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 (, line 1)" - }, - { - "index": 10, - "input": "m, t, u = symbols('m t u')", - "output": null, - "result_type": null, - "success": false, - "error": "invalid syntax (, line 1)" - }, - { - "index": 11, - "input": "eq4 = Eq(m, t + u * 5)", - "output": null, - "result_type": null, - "success": false, - "error": "invalid syntax (, line 1)" - }, - { - "index": 12, - "input": "eq5 = Eq(t, 4)", - "output": null, - "result_type": null, - "success": false, - "error": "invalid syntax (, line 1)" - }, - { - "index": 13, - "input": "eq6 = Eq(m, 3)", - "output": null, - "result_type": null, - "success": false, - "error": "invalid syntax (, line 1)" - }, - { - "index": 14, - "input": "sistema2 = [eq4, eq5, eq6]", - "output": null, - "result_type": null, - "success": false, - "error": "invalid syntax (, line 1)" - }, - { - "index": 15, - "input": "solve(sistema2)", - "output": null, - "result_type": null, - "success": false, - "error": "name 'solve' is not defined" - } - ] -} \ No newline at end of file diff --git a/test_motor_puro.json b/test_motor_puro.json deleted file mode 100644 index f37edf4..0000000 --- a/test_motor_puro.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "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" - } - ] -} \ No newline at end of file diff --git a/test_motor_puro_results.json b/test_motor_puro_results.json deleted file mode 100644 index 34044cd..0000000 --- a/test_motor_puro_results.json +++ /dev/null @@ -1,131 +0,0 @@ -{ - "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 - } - ] -} \ No newline at end of file diff --git a/test_sympy_puro.py b/test_sympy_puro.py deleted file mode 100644 index dc3cb31..0000000 --- a/test_sympy_puro.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/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!") \ No newline at end of file