Módulo Depurador

Proyecto: Kmila-9s

Este módulo es un componente central de Kmila-9s, un proyecto titulado oficialmente "Aplicación Multiplataforma para el Aprendizaje y Depuración de Código VHDL sin Requerimiento de Hardware Físico" (Multiplatform Application for Learning and Debugging VHDL Code without Requiring Physical Hardware).

Autor: Ulrich Tamayo Daniel Correo: [email protected] Correo 2: [email protected] Versión: 1.14.1 Última actualización: 3 de agosto de 2026


1. Panorama del proyecto

El objetivo principal de este proyecto es desarrollar una aplicación de escritorio libre, de código abierto y multiplataforma para la depuración interactiva de código VHDL. La aplicación busca facilitar el aprendizaje del diseño digital al proporcionar un entorno unificado para la simulación y la visualización en tiempo real de señales digitales, ciclos de reloj y estados de puertos, eliminando por completo la necesidad de hardware físico como FPGAs.

Motivaciones clave

  • Accesibilidad: Superar la barrera económica de las costosas tarjetas FPGA y las licencias de software propietario.
  • Mejora pedagógica: Ofrecer una interfaz gráfica integrada e intuitiva que simplifica el proceso de depuración, a diferencia de los flujos de trabajo fragmentados basados en línea de comandos de herramientas como GHDL y GTKWave.
  • Flexibilidad: Proporcionar una aplicación autocontenida que se ejecuta localmente en múltiples sistemas operativos (Windows, macOS, Linux, Android, iOS) sin requerir conexión a internet.

Este proyecto está construido con .NET y MAUI, lo que garantiza una base de código moderna y mantenible con amplio soporte de plataformas.

2. El módulo Depurador

Este módulo específico, el Depurador, es una fachada ligera que enlaza los archivos fuente crudos de VHDL con los modelos de datos del Interpreter. En el estado actual de la base de código realiza dos tareas:

  1. Ejecutar una pasada estructural rápida para extraer los bloques entity / architecture de nivel superior y, a partir de ellos, poblar los objetos Interpreter.Services.Entity / Interpreter.Services.Architecture.
  2. Ejecutar una pasada de lint ligera a nivel de fuente (RunDiagnostics) que emite entradas estructuradas de Parser.Models.Diagnostic (falta de ;, desbalance de begin / end) para que la superficie del editor las renderice como marcadores de Monaco — antes de que se ejecute el análisis estructural, de modo que las advertencias sigan apareciendo en fuentes con errores.

No orquesta la simulación en vivo ni gestiona puntos de interrupción; esas responsabilidades residen en Kmila.Shared.Services (SimulationParameters, SimulationCoordinator, etc.) y en el ejecutor de simulación del Interpreter. Debugger.csproj aún hace ProjectReference a Interpreter y a TimeMachine porque la pasada estructural devuelve los resultados a tipos de Interpreter y los consumidores posteriores (Kmila.Shared) dependen de que las instantáneas de TimeMachine sean alcanzables desde el mismo grafo de ensamblados — pero nada dentro del propio Debugger invoca a TimeMachine. Revisiones anteriores del README describían un rol más amplio de "orquestador central" que nunca se materializó aquí; este módulo se mantuvo exclusivamente como preprocesamiento.

Responsabilidades centrales

  • Procesamiento de archivos VHDL: Lectura de archivos fuente .vhdl crudos desde el sistema de archivos (File.ReadAllText en el constructor).
  • Análisis estructural: Recorrido del archivo línea por línea con una máquina de estados de 3 estados (default → entity → architecture) para identificar los bloques de nivel superior.
  • Poblado de modelos de datos: Construcción de objetos Entity y Architecture (del módulo Interpreter) y asociación de la arquitectura a su entidad declarante mediante entity.SetArchitecture(arch).
  • Diagnósticos a nivel de fuente: Recolección de advertencias no fatales sobre punto y coma faltantes y balance de begin / end en Diagnostics, apto para el mapeo directo a setModelMarkers de Monaco.
  • Análisis resiliente: La pasada de lint se ejecuta antes de la pasada estructural, de modo que aunque la pasada estructural lance una excepción, el editor siga mostrando diagnósticos sobre los cuales el usuario pueda actuar.

3. Arquitectura del módulo Depurador

El módulo Depurador está diseñado como un coordinador de alto nivel que prepara los datos para los demás componentes centrales del proyecto Kmila-9s.

Componentes clave

  • Program.cs: Un pequeño arnés de prueba de consola que explora el directorio Codes/ en busca de archivos .vhdl y analiza cada uno, imprimiendo el número de entidades que encontró por archivo. Útil para pruebas de humo del puente del analizador de forma aislada.

  • Repositories/Parser.cs: La única clase real del módulo. Lee un archivo .vhdl en el constructor y expone:

    • string FileContent { get; private set; } — texto crudo del archivo.
    • List<Entity> Entities { get; private set; } — entidades descubiertas (con sus arquitecturas ya asociadas tras el análisis).
    • List<Parser.Models.Diagnostic> Diagnostics { get; private set; } — advertencias estructuradas producidas por la pasada de lint.
    • void ParseFile() — ejecuta primero la pasada de lint y luego recorre el archivo con la máquina de estados de 3 estados para poblar Entities. Reenvía a ParseFile(CancellationToken.None).
    • void ParseFile(CancellationToken cancellationToken) — sobrecarga cancelable agregada en la sesión 22 pasada 4 (F-G-08). Propaga el token tanto a entity.Syntetize(rawEntity, ct) como a arch.Syntetize(rawEntity, ct) para que el gancho de tiempo límite de pared (KMILA_SIM_WALL_TIMEOUT_MS en HeadlessSimulator) pueda realmente interrumpir un descenso de síntesis atascado en lugar de esperar a que el cliente HTTP se desconecte. Antes de la pasada 4, el analizador no tenía superficie de cancelación.
    • private void RunDiagnostics(string source) — la pasada de lint en sí.
    • private static bool LooksLikeStatement(string lower) — heurística que decide qué líneas deberían requerir un ; final.

Modelos de datos centrales

Este módulo usa modelos de datos definidos en dos módulos anteriores en la cadena:

  • Interpreter.Services.Entity — representa una entidad VHDL, incluidos sus puertos y genéricos.
  • Interpreter.Services.Architecture — representa una arquitectura VHDL; Syntetize(string[] lines) se invoca aquí una vez que se ha recolectado el cuerpo de la arquitectura.
  • Parser.Models.Diagnostic + Parser.Models.DiagnosticSeverity — registro de diagnóstico estructurado usado para exponer los hallazgos del lint a la página del Editor. El Depurador nunca instancia Parser.Models.ParserException directamente; esa es tarea del proyecto anterior Parser, pero el Editor mapea ambos al mismo flujo de diagnósticos.

Diagnósticos de lint emitidos

Código Severidad Disparador
VHD-MISSING-SEMICOLON Advertencia La línea parece una sentencia (<=, :=, =>, o comienza con library/use/signal/variable/constant) pero no termina con ;, ,, is, then, else, loop, begin, generate, ( ni ).
VHD-BLOCK-BALANCE Advertencia El número total de palabras clave begin no coincide con el número total de palabras clave end en todo el archivo. Se reporta una sola vez en la última línea.

Los errores lanzados por el proyecto anterior Parser se propagan como Parser.Models.ParserException; los llamadores (p. ej. la página del Editor) típicamente los convierten con Diagnostic.FromException(...) para que aparezcan en el mismo flujo de marcadores de Monaco.

4. Cómo usar este módulo

Para usar la funcionalidad de análisis del depurador, se proporciona la ruta de un archivo VHDL a Debugger.Repositories.Parser. El método ParseFile() entonces procesa el archivo, ejecuta la pasada de lint y puebla las listas Entities y Diagnostics.

using Debugger.Repositories;
using System;

// This example demonstrates the current file processing capability.
var files = Directory.GetFiles("Codes");
foreach (var file in files)
{
    try
    {
        // 1. Initialize the Debugger's Parser with a file path.
        Parser vhdlFileParser = new(file);

        // 2. Process the file to identify entities and architectures.
        //    The lint pass runs first, even if the structural pass later throws.
        vhdlFileParser.ParseFile();

        // 3. The parsed entities are now available for the Interpreter.
        Console.WriteLine($"File: {file}, Entities found: {vhdlFileParser.Entities.Count}");

        // 4. Surface non-fatal lint warnings (mirrors what the Editor page does
        //    when wiring Monaco's setModelMarkers).
        foreach (var d in vhdlFileParser.Diagnostics)
        {
            Console.WriteLine($"  [{d.Severity}] L{d.Line}:{d.StartColumn} {d.Code} — {d.Message}");
        }
    }
    catch (Exception ex)
    {
        // Structural failures (e.g. "Entity not found") propagate. The editor
        // surface typically catches these and maps them to error-level
        // diagnostics via Diagnostic.FromException(...).
        Console.WriteLine($"An error occurred while parsing {file}: {ex.Message}");
    }
}

5. Dependencias

Este módulo integra otras partes clave del proyecto Kmila-9s y depende de:

  • Módulo Interpreter: Para sus modelos de datos (Entity, Architecture) y el punto de entrada de síntesis de arquitectura (Architecture.Syntetize).
  • Módulo Parser: Para dos cosas — el Tokenizer (actualmente instanciado pero reservado para futuras reescrituras estructurales) y los tipos Parser.Models.Diagnostic / Parser.Models.DiagnosticSeverity que usa la pasada de lint.
  • Solo la BCL de .NET 9 por lo demás — sin Microsoft.Extensions.Logging, sin dependencia de TimeMachine. El Depurador es una capa de preprocesamiento; la simulación en vivo se dirige desde la pila del Editor de Kmila.Shared, no desde este proyecto.

6. Flujo del Depurador y relaciones entre clases

Los siguientes diagramas ilustran el flujo de datos y las relaciones entre clases reales del módulo tal como está hoy. Revisiones anteriores de este README representaban un subsistema de "State Manager / Time Travel Logic / Simulation Orchestrator" dentro del Depurador que nunca existió en el código fuente — se ha eliminado.

6.1. Flujo de datos de alto nivel

graph TD
    A["VHDL Files (.vhdl)"] --> B["Debugger.Repositories.Parser"]
    B -->|RunDiagnostics first| D["List&lt;Parser.Models.Diagnostic&gt;<br/>(warnings)"]
    B -->|Structural state machine| C["Interpreter.Services.Entity<br/>+ Architecture (with body synthesised)"]
    D --> E["Editor (Monaco markers)<br/>[External — Kmila.Shared]"]
    C --> F["Interpreter Simulation<br/>[External]"]

    style B fill:#ccf,stroke:#333,stroke-width:2px
    style D fill:#fec,stroke:#333,stroke-width:1px
    style F fill:#bbf,stroke:#333,stroke-width:2px

6.2. Diagrama de clases detallado

classDiagram
    direction LR

    class Program {
        +static Main(args : string[])
    }

    class DbgParser["Debugger.Repositories.Parser"] {
        +FileContent : string
        +Entities : List~Entity~
        +Diagnostics : List~Diagnostic~
        -_excludedEnds : List~string~
        +Parser(filePath : string)
        +ParseFile() void
        -RunDiagnostics(source : string) void
        -LooksLikeStatement(lower : string)$ bool
    }

    class IntEntity["Interpreter.Services.Entity"] {
        <<External>>
        +Name : string
        +Ports
        +Generics
        +SetArchitecture(arch)
    }

    class IntArch["Interpreter.Services.Architecture"] {
        <<External>>
        +Syntetize(lines : string[])
    }

    class Tok["Parser.Repositories.Tokenizer"] {
        <<External, reserved>>
    }

    class Diag["Parser.Models.Diagnostic"] {
        <<External>>
        +Severity : DiagnosticSeverity
        +Line : int
        +StartColumn : int
        +EndColumn : int
        +Code : string
        +Message : string
    }

    Program --> DbgParser : creates and uses
    DbgParser --> IntEntity : instantiates
    DbgParser --> IntArch : instantiates and Syntetize()
    DbgParser ..> Tok : instantiates (currently unused)
    DbgParser --> Diag : appends warnings

6.3. Diagrama de secuencia: análisis de un archivo

sequenceDiagram
    participant Main
    participant DbgParser as "Debugger.Repositories.Parser"
    participant FileIO as "File IO"
    participant Arch as "Interpreter.Services.Architecture"

    Main->>DbgParser: new(filePath)
    activate DbgParser
    DbgParser->>FileIO: ReadAllText(filePath)
    FileIO-->>DbgParser: file content
    deactivate DbgParser

    Main->>DbgParser: ParseFile()
    activate DbgParser
    DbgParser->>DbgParser: RunDiagnostics(FileContent)
    Note right of DbgParser: emits VHD-MISSING-SEMICOLON,<br/>VHD-BLOCK-BALANCE warnings

    loop For each non-empty line
        DbgParser->>DbgParser: Detect entity / architecture starts
        DbgParser->>DbgParser: Collect lines until "end ...;"
    end

    DbgParser->>DbgParser: new Entity(rawLines)
    DbgParser->>Arch: new Architecture(ports, generics)
    DbgParser->>Arch: Syntetize(rawLines)
    DbgParser->>DbgParser: entity.SetArchitecture(arch)
    deactivate DbgParser
    DbgParser-->>Main: Entities + Diagnostics populated

6.4. Diagrama de módulos

graph TD
    vhdl_code["VHDL Code Files<br>/Codes/*.vhdl"]
    subgraph debugger_boundary["Debugger Project"]
        program["Program.cs<br>(test harness)"]
        dbg_parser["Repositories/Parser.cs<br>(structural pass + lint pass)"]
        program -->|invokes| dbg_parser
    end

    subgraph parser_project["Parser Project [External]"]
        diagnostic["Parser.Models.Diagnostic<br>+ DiagnosticSeverity"]
        tokenizer["Parser.Repositories.Tokenizer<br>(instantiated, reserved)"]
    end

    subgraph interpreter_project["Interpreter Project [External]"]
        entity["Interpreter.Services.Entity"]
        architecture["Interpreter.Services.Architecture<br>.Syntetize(lines)"]
    end

    subgraph kmila_shared["Kmila.Shared [External — actual UI consumer]"]
        editor_page["Pages/Editor.razor<br>(Monaco markers)"]
        sim_coord["Services/SimulationParameters,<br>SimulationCoordinator"]
    end

    dbg_parser -->|reads| vhdl_code
    dbg_parser -->|emits warnings into| diagnostic
    dbg_parser -.->|references| tokenizer
    dbg_parser -->|hydrates| entity
    dbg_parser -->|hydrates and synthesises| architecture

    editor_page -->|consumes Diagnostics + maps via Diagnostic.FromException| dbg_parser
    sim_coord -->|drives the actual simulation, NOT the Debugger| architecture

7. Limitaciones actuales

Las siguientes limitaciones existen en la implementación actual:

  • El Tokenizer no se usa activamente: La clase Tokenizer del módulo Parser se instancia dentro de ParseFile() pero sus tokens no se consumen. La pasada estructural sigue usando coincidencia de cadenas a nivel de línea. Reservado para una futura reescritura.
  • Comentarios de VHDL: La pasada de lint elimina los comentarios de línea -- antes del análisis, pero las formas de comentarios multilínea / de bloque no se tratan de forma especial (VHDL en sí no tiene /* ... */).
  • Enfoque de análisis simple: La detección de estructura se apoya en comparer.StartsWith("entity") / "architecture" — no se construye un AST real en este proyecto. El análisis estilo AST ocurre en el proyecto anterior Parser.
  • Suposición de una sola pareja entidad-arquitectura: Las arquitecturas se emparejan por nombre con entidades declaradas antes en el mismo archivo. Las declaraciones fuera de orden y las búsquedas entre archivos lanzan Exception("Entity not found").
  • Una sola pasada de diagnóstico: RunDiagnostics solo emite hoy dos códigos (VHD-MISSING-SEMICOLON, VHD-BLOCK-BALANCE). Reglas más ricas (dirección de puertos, discrepancias de tipos, señales sin usar) residen en la canalización de simulación, no aquí.
  • Sin simulación, sin TimeMachine: A pesar del nombre del proyecto, este módulo no ejecuta una simulación ni se comunica con TimeMachine. La depuración en vivo se dirige desde Kmila.Shared.Services contra el Interpreter.
  • test_output.vcd extraviado en la raíz del módulo: Un archivo Debugger/test_output.vcd se encuentra en la raíz del módulo. Es un artefacto residual, no producido por este módulo (que no ejecuta simulación alguna ni emite VCD — véase arriba). Trátelo como seguro de ignorar/eliminar durante la limpieza; no juega ningún papel en la pasada de preprocesamiento del Depurador.

7.bis. Diagramas de auditoría (2026-05-09)

Estos diagramas complementan las secciones 6.1–6.4. Se agregaron durante la auditoría entre módulos porque los diagramas TT1 existentes o bien tergiversaban este módulo (fig_5_2_c4_contenedores afirmaba que el Depurador delega la tokenización a Parser y coordina con TimeMachine — ninguna de las dos cosas es cierta) o bien no representaban en absoluto la canalización con lint primero. Las fuentes también residen en Documentacion/TT1/diagrams/.

fig_5_5_12_clases_debugger — Detalle de clases del Depurador

classDiagram
    direction TB

    class DbgParser {
        +string FileContent
        +List~Entity~ Entities
        +List~Diagnostic~ Diagnostics
        -List _excludedEnds
        -int funcProcDepth
        +ParseFile() void
        +RunDiagnostics(content) void
        -LooksLikeStatement(line) bool
    }

    class Tokenizer {
        <<from Parser>>
        +ReadFile(path) void
        +Evaluate(token) void
    }

    class Diagnostic {
        <<from Parser.Models>>
        +Severity Severity
        +int Line
        +int StartColumn
        +int EndColumn
        +string Code
        +string Message
        +FromException(ex)$ Diagnostic
    }

    class Entity {
        <<from Interpreter.Services>>
        +Syntetize(lines: List~tuple~) void
        +SetArchitecture(arch) void
    }

    class Architecture {
        <<from Interpreter.Services>>
        +Syntetize(rawEntity: List~tuple~) void
    }

    DbgParser ..> Tokenizer : instancia (no consume tokens)
    DbgParser "1" *-- "*" Entity : produce
    DbgParser "1" *-- "*" Diagnostic : emite
    Entity "1" *-- "1" Architecture : compone

fig_5_11_12_act_debugger_lint_resilience — Canalización con lint primero

flowchart TD
    A([DbgParser.ParseFile]) --> B[Cargar FileContent desde disco]
    B --> C[**Lint pass**: RunDiagnostics FileContent]
    C --> D[Por cada linea]
    D --> E{LooksLikeStatement?}
    E -->|Si y falta `;`| F[Diagnostic VHD-MISSING-SEMICOLON]
    E -->|No| G[Skip]
    D --> H[Contar begin / end por bloque]
    H --> I{Balance?}
    I -->|No| J[Diagnostic VHD-BLOCK-BALANCE]
    F --> K[Diagnostics.Add]
    J --> K
    G --> L
    K --> L[Continuar siguiente linea]
    L --> M{Quedan lineas?}
    M -->|Si| D
    M -->|No| N[Lint pass completo: Diagnostics persistentes]

    N --> O[**Structural pass**: maquina line-based<br/>default -> in-entity -> in-architecture]
    O --> P{Excepcion en parsing?}
    P -->|Si| Q[Capturar y agregar a Diagnostics]
    Q --> R([Retornar: Entities parciales + Diagnostics completos])
    P -->|No| S[Crear Entity / Architecture<br/>con tuplas List int string]
    S --> T[Entity.Syntetize / Architecture.Syntetize]
    T --> U[Entity.SetArchitecture]
    U --> V([Retornar: Entities completas + Diagnostics])

8. Registro de cambios

Versión 1.14.1 (2026-05-01)

Realineación de la documentación

  • Sección 2 / "Responsabilidades centrales": Se eliminó la antigua afirmación de que el módulo Depurador orquesta Parser + Interpreter + TimeMachine. El código actual es solo de preprocesamiento (extracción estructural + diagnósticos de lint). La simulación en vivo siempre ha sido dirigida por Kmila.Shared.Services.
  • Sección 3 / Componentes clave: Se agregaron la propiedad Diagnostics, la pasada de lint RunDiagnostics(string) y el ayudante LooksLikeStatement(string). Se agregó una subsección explícita "Diagnósticos de lint emitidos" enumerando los dos códigos (VHD-MISSING-SEMICOLON, VHD-BLOCK-BALANCE).
  • Sección 4 / Ejemplo de uso: Se actualizó el fragmento para exponer Diagnostics (la propiedad que el editor realmente consume) y se aclaró que Parser.Models.ParserException se mapea mediante Diagnostic.FromException(...) aguas arriba.
  • Sección 5 / Dependencias: Se eliminaron las dependencias espurias de TimeMachine y Microsoft.Extensions.Logging — el Depurador solo referencia a Interpreter y Parser, sobre .NET 9 puro.
  • Sección 6 / Diagramas — los cuatro regenerados:
    • §6.1 ahora muestra la pasada de lint ejecutándose antes de la pasada estructural y alimentando al editor de forma independiente.
    • §6.2 lista la superficie pública real (Diagnostics, _excludedEnds, los ayudantes RunDiagnostics/LooksLikeStatement) y agrega la clase externa Parser.Models.Diagnostic.
    • §6.3 inserta el paso RunDiagnostics y la llamada a Architecture.Syntetize.
    • §6.4 eliminó las fronteras ficticias "State Manager" y "Time Travel Logic" y las reemplazó con la superficie de consumo real (Kmila.Shared.Pages.Editor y SimulationCoordinator).
  • Sección 7 / Limitaciones: Se refinó la nota sobre el manejo de comentarios (los comentarios de línea se eliminan, y de todas formas no existe una forma de comentario de bloque en VHDL), se agregaron "Una sola pasada de diagnóstico" + "Sin simulación, sin TimeMachine" para fijar expectativas correctas.

Versión 0.6.1 (2026-02-21)

Correcciones de documentación

  • README.md: Se corrigió el diagrama de módulos para referenciar correctamente Parser.Repositories.Tokenizer en lugar de Interpreter.Services.Tokenizer
  • README.md: Se actualizó el nodo VHDL Models para referenciar ambos espacios de nombres Interpreter.Models e Interpreter.Services (Entity está en Services, los modelos en Models)

Versión 0.6.0 (2025-12-13)

Mejoras de documentación

  • Program.cs: Se agregó documentación XML exhaustiva que incluye:

    • Resumen a nivel de clase que explica el propósito del arnés de prueba
    • Documentación de métodos con pasos de procesamiento
    • Diagrama de la estructura de directorios esperada
    • Documentación de excepciones
  • Repositories/Parser.cs: Se agregó documentación XML completa que incluye:

    • Documentación a nivel de clase que explica el algoritmo de la máquina de estados
    • Comentarios detallados sobre las limitaciones del análisis
    • Documentación de propiedades para FileContent y Entities
    • Documentación del campo privado _excludedEnds
    • Documentación del constructor y de los métodos con detalles de procesamiento paso a paso
    • Comentarios en línea que explican las transiciones de estado y la asociación entidad/arquitectura
  • README.md: Se agregó información de versión, fecha de última actualización, sección de limitaciones actuales y este registro de cambios


Versión 1.14.0 (2026-04-19)

  • Repositories/Parser.cs — recolección de diagnósticos. Se agregó una propiedad Diagnostics (tipada como global::Parser.Models.Diagnostic para esquivar la colisión de espacios de nombres entre la clase Parser de este archivo y el proyecto Parser) más una pasada de lint ligera RunDiagnostics(source) que detecta ; faltantes al final de sentencias y desbalance de begin / end antes de pasar al analizador completo. Alimenta directamente a setModelMarkers de Monaco desde el editor.
  • Las instancias de ParserException también se coaccionan mediante Diagnostic.FromException(...) a la misma lista, de modo que su exposición en el editor sea uniforme independientemente de si el error provino de la pasada de lint o de que el analizador lanzara una excepción.
  • Higiene de compilación — se agregó <NoWarn> para el efecto en cadena de la documentación XML, de modo que el proyecto Depurador compile limpiamente junto con el resto de la solución.

Novedades en la v1.15 (2 de mayo de 2026)

  • Diagnósticos precisos a nivel de línea de fuente (#21). ParseFile ahora recolecta cada línea no vacía como (lineIndex, text) y alimenta esas tuplas a las nuevas sobrecargas Entity.Syntetize(IReadOnlyList<(int, string)>) / Architecture.Syntetize(IReadOnlyList<(int, string)>), que llaman a Tokenizer.TokenizeLineAt. Los mensajes de error referencian las líneas reales del archivo del usuario en lugar del contador interno de llamadas del tokenizer.
  • Los diagnósticos de lint se exponen incluso ante un fallo de análisis. Program.Main envuelve ParseFile() en un try/catch e imprime cada Diagnostic (p. ej. VHD-MISSING-SEMICOLON, VHD-BLOCK-BALANCE) antes de reemitir el error estructural. Anteriormente, una excepción ocultaba toda la salida de lint aguas arriba.
  • Corrección del error de seccionamiento de arquitectura. El respaldo por coincidencia de subcadenas comparer.Contains(currentArchName.ToLower()) ya no confunde end maxv; con el fin de la arquitectura de architecture A of …. Solo coincidencia de nombre exacto, más una lista de exclusión de palabras clave de control de flujo (end if/loop/case/for/while/generate/record).
  • GC de estación de trabajo habilitado a nivel de proyecto (#7).

Notas de versión completas: ../Documentacion/CHANGELOG_v1.15.md.


Última actualización: 2026-08-03