Mejorado del Motor algebraico
This commit is contained in:
parent
556c63ad31
commit
c0e638ec79
|
@ -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.
|
|
@ -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.
|
|
@ -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*
|
|
@ -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.
|
|
@ -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.
|
|
@ -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**: `(?<!FourBytes\(")(?<!")([a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+)(?!")`
|
||||
- **Prioridad**: 10 (MENOR - patrón general)
|
||||
- **Ejemplos**: `192.168.1.1` → `FourBytes("192.168.1.1")`, `10.x.y.z` → `FourBytes("10.x.y.z")`
|
||||
- **Capacidades**: Aritmética de 32-bit, símbolos algebraicos, conversiones
|
||||
|
||||
### 4. **Clases Especializadas Adaptadas**
|
||||
|
||||
#### **Hex** (en `custom_types/hex_type.py`)
|
||||
- **Patrón adicional**: `\b0x([0-9A-Fa-f]+)\b`
|
||||
- **Prioridad**: 6 (MEDIA - específica de hex)
|
||||
- **Ejemplo**: `0xFF` → `Hex("FF")`
|
||||
- **Delegación**: Usa IntBase internamente
|
||||
|
||||
#### **Bin** (en `custom_types/bin_type.py`)
|
||||
- **Patrón adicional**: `\b0b([01]+)\b`
|
||||
- **Prioridad**: 6 (MEDIA - específica de binario)
|
||||
- **Ejemplo**: `0b1010` → `Bin("1010")`
|
||||
- **Delegación**: Usa IntBase internamente
|
||||
|
||||
#### **IP4** (en `custom_types/ip4_type.py`)
|
||||
- **Sin tokenización directa**: Usa FourBytes desde tokenización automática
|
||||
- **Constructor mejorado**: Acepta FourBytes, IntBase para máscaras
|
||||
- **Ejemplos**: `IP4(FourBytes("192.168.1.1"), IntBase("ffffff00", 16))`
|
||||
|
||||
## 🔧 Integración con Motor de Evaluación
|
||||
|
||||
### **HybridEvaluationEngine** (en `main_evaluation.py`)
|
||||
- **Tokenización automática**: Aplica tokenización en cada expresión
|
||||
- **Contexto dinámico**: Clases base cargadas automáticamente desde el registro
|
||||
- **Debug sincronizado**: Sistema de debug coordinado entre motor, parser y tokenizador
|
||||
|
||||
## 🧪 Validación Completa
|
||||
|
||||
### **Pruebas Exitosas**
|
||||
```bash
|
||||
# Todas las pruebas pasaron exitosamente:
|
||||
|
||||
✅ Registro de tipos: 9 clases registradas
|
||||
✅ Reglas de tokenización: 8 reglas activas
|
||||
✅ IntBase: '16#ff' → IntBase("ff", 16)
|
||||
✅ FourBytes: '192.168.1.1' → FourBytes("192.168.1.1")
|
||||
✅ Hex: '0xFF' → Hex("FF")
|
||||
✅ Bin: '0b1010' → Bin("1010")
|
||||
✅ Evaluación directa funcional
|
||||
✅ Contexto del motor completo
|
||||
```
|
||||
|
||||
### **Casos de Uso Validados**
|
||||
```python
|
||||
# Tokenización automática funcionando:
|
||||
a = 16#FF # → IntBase("FF", 16)
|
||||
b = 192.168.1.1 # → FourBytes("192.168.1.1")
|
||||
c = 0xFF # → Hex("FF")
|
||||
d = 0b1010 # → Bin("1010")
|
||||
|
||||
# Operaciones mixtas:
|
||||
resultado = a + 5 # IntBase mantiene base
|
||||
ip_suma = b + 256 # FourBytes mantiene tipo
|
||||
|
||||
# Símbolos algebraicos:
|
||||
symb_ip = 10.x.y.0 # → FourBytes("10.x.y.0")
|
||||
symb_hex = 16#x0 # → IntBase("x0", 16)
|
||||
```
|
||||
|
||||
## 🎯 Beneficios Logrados
|
||||
|
||||
### **1. Simplicidad para el Usuario**
|
||||
- ✅ **Sintaxis natural**: Usuario escribe Python estándar
|
||||
- ✅ **Sin curva de aprendizaje**: No hay sintaxis especial
|
||||
- ✅ **Tokenización invisible**: Funciona automáticamente
|
||||
|
||||
### **2. Potencia del Sistema**
|
||||
- ✅ **Extensibilidad**: Fácil agregar nuevos tipos con tokenización
|
||||
- ✅ **Flexibilidad**: Cada clase define sus propios patrones
|
||||
- ✅ **Modularidad**: Sistema completamente distribuido
|
||||
|
||||
### **3. Robustez Técnica**
|
||||
- ✅ **Auto-descubrimiento**: Sistema dinámico, no hardcodeado
|
||||
- ✅ **Prioridades**: Control preciso de precedencia
|
||||
- ✅ **Compatibilidad**: Mantiene interfaz existente
|
||||
- ✅ **Debug completo**: Trazabilidad total del proceso
|
||||
|
||||
## 🔄 Migración Completada
|
||||
|
||||
### **Antes: Sistema de Corchetes**
|
||||
```python
|
||||
# Sintaxis compleja y específica:
|
||||
ip = [IP4: 192.168.1.1, 24]
|
||||
hex_val = [Hex: ff]
|
||||
```
|
||||
|
||||
### **Después: Tokenización Distribuida**
|
||||
```python
|
||||
# Sintaxis Python natural:
|
||||
ip = IP4(192.168.1.1, 24) # Automáticamente tokenizado
|
||||
hex_val = 16#ff # Automáticamente tokenizado
|
||||
```
|
||||
|
||||
## 🚀 Extensión Futura
|
||||
|
||||
### **Agregar Nuevos Tipos**
|
||||
Para agregar un nuevo tipo con tokenización:
|
||||
|
||||
1. **Crear archivo**: `custom_types/mitype_type.py`
|
||||
2. **Implementar método**:
|
||||
```python
|
||||
@staticmethod
|
||||
def get_tokenization_patterns():
|
||||
return [
|
||||
{
|
||||
'pattern': r'mi_patron_regex',
|
||||
'replacement': lambda match: f'MiClase("{match.group(1)}")',
|
||||
'priority': 7, # Elegir prioridad apropiada
|
||||
'description': 'Descripción del patrón'
|
||||
}
|
||||
]
|
||||
```
|
||||
3. **Registrar**: El sistema auto-descubre automáticamente
|
||||
|
||||
### **Patrones Disponibles para Extensión**
|
||||
- **Fechas/Tiempo**: `HH:MM:SS`, `YYYY-MM-DD`
|
||||
- **MACs**: `xx:xx:xx:xx:xx:xx`
|
||||
- **UUIDs**: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
|
||||
- **Coordenadas**: `lat,lng`
|
||||
- **Cualquier patrón específico del dominio**
|
||||
|
||||
## 📋 Estado Final
|
||||
|
||||
**✅ SISTEMA COMPLETAMENTE IMPLEMENTADO Y FUNCIONAL**
|
||||
|
||||
- Sistema de tokenización distribuida operativo
|
||||
- Auto-descubrimiento funcionando
|
||||
- Todas las clases adaptadas correctamente
|
||||
- Integración con motor de evaluación completa
|
||||
- Pruebas exitosas en todos los componentes
|
||||
- Documentación y ejemplos incluidos
|
||||
|
||||
El sistema está listo para producción y extensión futura según las especificaciones del documento `@tokenizacion_distribuida.md`.
|
|
@ -1,392 +0,0 @@
|
|||
# Simple Debug API - Calculadora MAV CAS
|
||||
|
||||
## Descripción
|
||||
|
||||
API CLI simple que usa el motor de evaluación existente para generar debug traces sin modificar el código base. Permite debuggear "como si usaras la aplicación" mediante archivos JSON.
|
||||
|
||||
---
|
||||
|
||||
## Uso
|
||||
|
||||
```bash
|
||||
# Ejecutar debug
|
||||
python simple_debug.py debug_input.json
|
||||
|
||||
# Con archivo de salida específico
|
||||
python simple_debug.py debug_input.json --output debug_results.json
|
||||
|
||||
# Modo verboso
|
||||
python simple_debug.py debug_input.json --verbose
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Formato de Entrada
|
||||
|
||||
### Estructura Simple
|
||||
|
||||
```json
|
||||
{
|
||||
"queries": [
|
||||
{"index": 0, "type": "input", "content": "10.1.1.1 + 1"},
|
||||
{"index": 1, "type": "input", "content": "16#FF + 10"},
|
||||
{"index": 2, "type": "exec", "content": "engine.symbol_table"},
|
||||
{"index": 3, "type": "input", "content": "x = 5"},
|
||||
{"index": 4, "type": "exec", "content": "len(engine.symbol_table)"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Tipos de Query
|
||||
|
||||
#### Input Query
|
||||
Evalúa expresiones como si las escribieras en la calculadora:
|
||||
|
||||
```json
|
||||
{"index": 0, "type": "input", "content": "10.1.1.1 + 1"}
|
||||
{"index": 1, "type": "input", "content": "mask = 255.255.0.0"}
|
||||
{"index": 2, "type": "input", "content": "solve(x**2 + 1, x)"}
|
||||
```
|
||||
|
||||
#### Exec Query
|
||||
Ejecuta código Python para inspeccionar el estado interno:
|
||||
|
||||
```json
|
||||
{"index": 3, "type": "exec", "content": "engine.symbol_table"}
|
||||
{"index": 4, "type": "exec", "content": "engine.parser.get_tokenization_info()"}
|
||||
{"index": 5, "type": "exec", "content": "list(engine.base_context.keys())[:10]"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Formato de Salida
|
||||
|
||||
### Estructura de Respuesta
|
||||
|
||||
```json
|
||||
{
|
||||
"execution_info": {
|
||||
"timestamp": "2025-01-01T10:30:15Z",
|
||||
"total_queries": 5,
|
||||
"successful": 4,
|
||||
"failed": 1
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"index": 0,
|
||||
"input": "10.1.1.1 + 1",
|
||||
"output": "10.1.1.2",
|
||||
"result_type": "FourBytes",
|
||||
"success": true
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"input": "16#FF + 10",
|
||||
"output": "16#109",
|
||||
"result_type": "IntBase",
|
||||
"success": true
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"input": "engine.symbol_table",
|
||||
"output": "{'x': Symbol('x'), 'mask': FourBytes('255.255.0.0')}",
|
||||
"result_type": "dict",
|
||||
"success": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Resultado Individual
|
||||
|
||||
```json
|
||||
{
|
||||
"index": 0,
|
||||
"input": "10.1.1.1 + 1",
|
||||
"output": "10.1.1.2",
|
||||
"result_type": "FourBytes",
|
||||
"success": true,
|
||||
"error": null,
|
||||
"display_class": "[FourBytes]"
|
||||
}
|
||||
```
|
||||
|
||||
### Resultado con Error
|
||||
|
||||
```json
|
||||
{
|
||||
"index": 3,
|
||||
"input": "IP4Mask(255.240.0.3)",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "ValueError: Máscara inválida: 255.240.0.3"
|
||||
}
|
||||
```
|
||||
|
||||
### Resultado de Exec
|
||||
|
||||
```json
|
||||
{
|
||||
"index": 4,
|
||||
"input": "len(engine.symbol_table)",
|
||||
"output": "3",
|
||||
"result_type": "int",
|
||||
"success": true,
|
||||
"exec_result": 3
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Casos de Uso Comunes
|
||||
|
||||
### 1. Debug de Tokenización
|
||||
|
||||
```json
|
||||
{
|
||||
"queries": [
|
||||
{"index": 0, "type": "input", "content": "192.168.1.1 + 16#FF"},
|
||||
{"index": 1, "type": "exec", "content": "engine.parser.process_expression('192.168.1.1 + 16#FF')"},
|
||||
{"index": 2, "type": "exec", "content": "engine.parser.get_tokenization_info()"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Debug de Contexto
|
||||
|
||||
```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.equations)"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Debug de Tipos
|
||||
|
||||
```json
|
||||
{
|
||||
"queries": [
|
||||
{"index": 0, "type": "input", "content": "ip = 10.1.1.x"},
|
||||
{"index": 1, "type": "exec", "content": "type(engine.symbol_table['ip'])"},
|
||||
{"index": 2, "type": "exec", "content": "engine.symbol_table['ip'].has_symbols"},
|
||||
{"index": 3, "type": "input", "content": "ip.substitute(x=5)"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Debug de Errores
|
||||
|
||||
```json
|
||||
{
|
||||
"queries": [
|
||||
{"index": 0, "type": "input", "content": "bad_mask = 255.240.0.3"},
|
||||
{"index": 1, "type": "input", "content": "IP4Mask(bad_mask)"},
|
||||
{"index": 2, "type": "exec", "content": "engine.last_result"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Testing de Regresión
|
||||
|
||||
```json
|
||||
{
|
||||
"queries": [
|
||||
{"index": 0, "type": "input", "content": "10.1.1.1 + 1"},
|
||||
{"index": 1, "type": "exec", "content": "str(type(engine.last_result))"},
|
||||
{"index": 2, "type": "exec", "content": "engine.last_result.original"},
|
||||
{"index": 3, "type": "input", "content": "16#FF + 10"},
|
||||
{"index": 4, "type": "exec", "content": "engine.last_result.base"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Funciones Útiles para Exec
|
||||
|
||||
### Estado del Motor
|
||||
|
||||
```python
|
||||
# Contexto y variables
|
||||
"engine.symbol_table" # Variables actuales
|
||||
"list(engine.base_context.keys())" # Funciones disponibles
|
||||
"len(engine.equations)" # Ecuaciones en el sistema
|
||||
"engine.last_result" # Último resultado
|
||||
|
||||
# Configuración del motor
|
||||
"engine.symbolic_mode" # ¿Modo simbólico?
|
||||
"engine.debug" # ¿Debug habilitado?
|
||||
```
|
||||
|
||||
### Información de Tipos
|
||||
|
||||
```python
|
||||
# Tipos registrados
|
||||
"engine.get_available_types()" # Info completa de tipos
|
||||
"list(engine.registered_types_info['registered_classes'].keys())" # Tipos disponibles
|
||||
"engine.registered_types_info['class_count']" # Cantidad de tipos
|
||||
|
||||
# Análisis de objetos
|
||||
"type(engine.last_result)" # Tipo del último resultado
|
||||
"engine.last_result.__class__.__name__" # Nombre de la clase
|
||||
"hasattr(engine.last_result, 'has_symbols')" # ¿Tiene símbolos?
|
||||
```
|
||||
|
||||
### Tokenización y Parsing
|
||||
|
||||
```python
|
||||
# Tokenización
|
||||
"engine.parser.get_tokenization_info()" # Info de tokenización
|
||||
"engine.parser.process_expression('test')" # Procesar expresión
|
||||
"len(engine.parser.tokenizer.tokenization_rules)" # Cantidad de reglas
|
||||
|
||||
# Análisis de expresiones
|
||||
"engine._classify_line('x = 5')" # Clasificar línea
|
||||
"engine._extract_variable_names('x + y')" # Extraer variables
|
||||
```
|
||||
|
||||
### Testing de Funciones Específicas
|
||||
|
||||
```python
|
||||
# Testing de FourBytes
|
||||
"engine.last_result._numeric_value" # Valor numérico interno
|
||||
"engine.last_result.has_symbols" # ¿Tiene símbolos?
|
||||
"engine.last_result.original" # String original
|
||||
|
||||
# Testing de IntBase
|
||||
"engine.last_result.base" # Base numérica
|
||||
"engine.last_result.value_str" # String del valor
|
||||
"engine.last_result._symbols" # Símbolos detectados
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementación Simple
|
||||
|
||||
### Estructura Mínima
|
||||
|
||||
```
|
||||
simple_debug.py # CLI principal (~100 líneas)
|
||||
debug_templates/ # Templates de ejemplo
|
||||
├── basic_test.json
|
||||
├── tokenization_test.json
|
||||
└── regression_test.json
|
||||
```
|
||||
|
||||
### CLI Principal (Pseudocódigo)
|
||||
|
||||
```python
|
||||
# simple_debug.py
|
||||
import json
|
||||
from main_evaluation import HybridEvaluationEngine
|
||||
|
||||
def run_debug(input_file, output_file=None):
|
||||
# Cargar queries
|
||||
with open(input_file) as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Crear motor
|
||||
engine = HybridEvaluationEngine()
|
||||
results = []
|
||||
|
||||
# Ejecutar cada query
|
||||
for query in data['queries']:
|
||||
if query['type'] == 'input':
|
||||
result = engine.evaluate_line(query['content'])
|
||||
output = {
|
||||
'index': query['index'],
|
||||
'input': query['content'],
|
||||
'output': str(result.result),
|
||||
'result_type': type(result.result).__name__,
|
||||
'success': not result.is_error,
|
||||
'error': result.error if result.is_error else None
|
||||
}
|
||||
elif query['type'] == 'exec':
|
||||
try:
|
||||
exec_result = eval(query['content'], {'engine': engine})
|
||||
output = {
|
||||
'index': query['index'],
|
||||
'input': query['content'],
|
||||
'output': str(exec_result),
|
||||
'result_type': type(exec_result).__name__,
|
||||
'success': True,
|
||||
'exec_result': exec_result
|
||||
}
|
||||
except Exception as e:
|
||||
output = {
|
||||
'index': query['index'],
|
||||
'input': query['content'],
|
||||
'success': False,
|
||||
'error': str(e)
|
||||
}
|
||||
|
||||
results.append(output)
|
||||
|
||||
# Guardar resultados
|
||||
final_output = {
|
||||
'execution_info': {...},
|
||||
'results': results
|
||||
}
|
||||
|
||||
with open(output_file, 'w') as f:
|
||||
json.dump(final_output, f, indent=2)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Templates de Ejemplo
|
||||
|
||||
### basic_test.json
|
||||
```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"},
|
||||
{"index": 3, "type": "exec", "content": "engine.last_result.base"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### tokenization_test.json
|
||||
```json
|
||||
{
|
||||
"queries": [
|
||||
{"index": 0, "type": "exec", "content": "engine.parser.process_expression('192.168.1.1 + 16#FF')"},
|
||||
{"index": 1, "type": "input", "content": "192.168.1.1 + 16#FF"},
|
||||
{"index": 2, "type": "exec", "content": "engine.parser.get_tokenization_info()['rules'][:3]"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### regression_test.json
|
||||
```json
|
||||
{
|
||||
"queries": [
|
||||
{"index": 0, "type": "input", "content": "mask=255.240.0.0"},
|
||||
{"index": 1, "type": "exec", "content": "type(engine.symbol_table['mask']).__name__"},
|
||||
{"index": 2, "type": "input", "content": "10.1.1.1 + 1"},
|
||||
{"index": 3, "type": "exec", "content": "type(engine.last_result).__name__"},
|
||||
{"index": 4, "type": "input", "content": "IP4Mask(255.255.0.0)"},
|
||||
{"index": 5, "type": "exec", "content": "engine.last_result.get_prefix_int()"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ventajas de este Diseño
|
||||
|
||||
✅ **Simplicidad**: ~100 líneas de código total
|
||||
✅ **Sin modificaciones**: Usa el motor existente tal como está
|
||||
✅ **Flexibilidad**: Cualquier función del engine es accesible via exec
|
||||
✅ **Debugging real**: Exactamente como usar la aplicación
|
||||
✅ **Fácil testing**: JSON simple para casos de prueba
|
||||
✅ **Serialización automática**: Python maneja la conversión a string
|
||||
|
||||
Esta aproximación te permite debuggear efectivamente sin crear una infraestructura compleja, usando el poder del motor existente.
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -6,6 +6,8 @@ __pycache__/
|
|||
# C extensions
|
||||
*.so
|
||||
|
||||
*.json
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
|
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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"
|
||||
}),
|
||||
]
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
a = b + 5
|
||||
solve(x)
|
|
@ -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,
|
||||
|
|
|
@ -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()}")
|
||||
print(f"\nContexto: {engine.get_context_info()}")
|
|
@ -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)"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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 (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"input": "from sympy import symbols, Eq, solve",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"input": "x, y, z = symbols('x y z')",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"input": "# Sistema puramente algebraico",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"input": "eq1 = Eq(x, 5)",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 5,
|
||||
"input": "eq2 = Eq(y, x + 3)",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 6,
|
||||
"input": "eq3 = Eq(z, x + y)",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 7,
|
||||
"input": "sistema = [eq1, eq2, eq3]",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 8,
|
||||
"input": "solve(sistema)",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "name 'solve' is not defined"
|
||||
},
|
||||
{
|
||||
"index": 9,
|
||||
"input": "# Test más complejo",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 10,
|
||||
"input": "m, t, u = symbols('m t u')",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 11,
|
||||
"input": "eq4 = Eq(m, t + u * 5)",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 12,
|
||||
"input": "eq5 = Eq(t, 4)",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 13,
|
||||
"input": "eq6 = Eq(m, 3)",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 14,
|
||||
"input": "sistema2 = [eq4, eq5, eq6]",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "invalid syntax (<string>, line 1)"
|
||||
},
|
||||
{
|
||||
"index": 15,
|
||||
"input": "solve(sistema2)",
|
||||
"output": null,
|
||||
"result_type": null,
|
||||
"success": false,
|
||||
"error": "name 'solve' is not defined"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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!")
|
Loading…
Reference in New Issue