Módulo Analizador (Parser) de VHDL

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.3.0 (rama v1.3.0) Última actualización: 2026-08-03


1. Descripción general 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 las FPGA.

Motivaciones clave

  • Accesibilidad: Superar la barrera económica que representan las costosas tarjetas FPGA y las licencias de software propietario.
  • Mejora pedagógica: Ofrecer una interfaz gráfica intuitiva e integrada que simplifica el proceso de depuración, a diferencia de los flujos de trabajo fragmentados por línea de comandos de herramientas como GHDL y GTKWave.
  • Flexibilidad: Proporcionar una aplicación autónoma 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 analizador

Este módulo específico, el Analizador de VHDL, funciona como el primer paso fundamental del flujo de simulación. Su única responsabilidad es leer el código fuente VHDL en bruto (archivos .vhdl) y transformarlo en una representación estructurada, en memoria, utilizando clases de C#. Estos datos estructurados se pasan luego al motor de simulación para su ejecución y análisis.

El analizador está diseñado para ser robusto, manejando sintaxis VHDL compleja y proporcionando mensajes de error claros para código no válido.

Responsabilidades principales

  • Análisis léxico: Descomponer el código VHDL en bruto en un flujo de tokens individuales (palabras clave, identificadores, operadores, literales, etc.).
  • Análisis sintáctico: Validar la secuencia de tokens contra las reglas gramaticales de VHDL para comprender la estructura del código.
  • Generación del árbol de sintaxis abstracta (AST): Construir una estructura en forma de árbol de objetos de C# que representa el diseño VHDL, incluyendo sus entidades, arquitecturas, señales, procesos y sentencias secuenciales.
  • Gestión de ámbitos (scopes): Rastrear todos los identificadores declarados (señales, variables, puertos, etc.) y el ámbito en el que son válidos (por ejemplo, entidad, arquitectura, proceso).

3. Arquitectura del módulo analizador

El analizador está construido con un diseño modular y orientado a objetos, donde distintas clases son responsables de analizar partes específicas del lenguaje VHDL.

Componentes clave

  • Tokenizer.cs: La primera etapa del flujo. Lee un archivo .vhdl o una cadena en bruto, elimina los comentarios y utiliza expresiones regulares para dividir el código en una lista de tokens de cadena en bruto. Luego evalúa estos tokens para clasificarlos en tipos (por ejemplo, KEYWORD, IDENTIFIER) y crea una lista de objetos Token.

  • TokenStream.cs: Un envoltorio alrededor de la lista de objetos Token generada por el Tokenizer. Proporciona una API tipo flujo (Current, Previous, MoveNext, Expect, Match, SetRollBack, RollBack) que permite a las clases del analizador consumir y validar tokens en secuencia de forma sencilla. También incluye StreamerException para el reporte detallado de errores relacionados con los tokens.

  • Parser.cs: El orquestador principal. Inicializa el TokenStream y delega el análisis de las unidades de diseño VHDL de alto nivel (entity, architecture, package) a sus clases analizadoras especializadas. ParseV2() además maneja las cláusulas library / use en cualquier punto entre unidades de diseño (se toleran múltiples declaraciones de librería) y recorre los bloques configuration en su lugar mediante ParseConfigurationBlock() / ParseConfigurationBody(), incluyendo la forma de enlace for CompLabel : CompType use entity work.X(Arch);.

  • Analizadores estructurales (IParseStructures):

    • EntityParser.cs: Analiza un bloque entity, incluyendo su nombre y las delegaciones para los bloques generic y port.
    • ArchitectureParser.cs: Analiza un bloque architecture, incluyendo su encabezado (declaraciones de señales/constantes) y su cuerpo (procesos, sentencias concurrentes).
    • PackageParser.cs: Analiza las declaraciones package y package body.
  • Analizadores de declaraciones (IParseDeclarations):

    • PortsParser.cs: Maneja el bloque port (...) dentro de una entidad, creando objetos Variable para cada puerto.
    • GenericParser.cs: Maneja el bloque generic (...), analizando las constantes genéricas.
    • ArchitectureAssignationParser.cs: Analiza las declaraciones en el encabezado de una arquitectura, como signal y constant.
  • Analizadores de sentencias y expresiones:

    • DataStructuresParser.cs: Un analizador versátil para las sentencias secuenciales que se encuentran dentro de procesos, funciones y cuerpos de arquitectura. Puede analizar if-then-elsif-else, case-when, for-loop, while-loop, exit, wait, null, return, y asignaciones de señales/variables. Admite contextos de sentencias tanto concurrentes como secuenciales.
    • FunctionParser.cs: Analiza las declaraciones de funciones, incluyendo parámetros, tipos de retorno (con límites complejos), declaraciones de variables locales y cuerpos de función con sentencias secuenciales.
    • FunctionAssignationParser.cs: Analiza las declaraciones de parámetros dentro de la firma de una función.
    • VectorParser.cs y RangeParser.cs: Analizadores auxiliares que manejan la sintaxis específica para tipos de vector (por ejemplo, std_logic_vector(7 downto 0), signed, unsigned) y rangos (por ejemplo, integer range 0 to 255). También manejan atributos de vector como 'length, 'range, 'high, 'low.

Modelos de datos principales

  • Token.cs: Representa una única unidad léxica del código fuente. Contiene su Value (por ejemplo, "entity", "clk"), su Type (por ejemplo, KEYWORDS) y su ubicación (Line, Col).

  • Variable.cs: Una clase crucial que representa cualquier objeto con nombre en VHDL (una señal, puerto, variable o constante). Almacena su Name, su Type de dato, su Value y su Size. También incluye un evento OnValueChange para notificar al motor de simulación las actualizaciones. Adición del 2026-06-22: ElementSize + ArrayLength llevan el ancho por elemento y la cantidad de ranuras para señales de tipo arreglo (por ejemplo, signal mem : rom_t donde rom_t = array(0 to 7) of std_logic_vector(7 downto 0) queda como ElementSize=8, ArrayLength=8). DeferredIndex / DynamicIndex los consultan para segmentar por elemento en las lecturas de mem(N); sin ellos el indexador recurría a lecturas bit a bit y devolvía basura. Cuidado: Variable sobrecarga operator== para comparar nombres (no referencias), así que use siempre is null / is not null para las comprobaciones de referencia.

  • Constants.cs: Una clase estática que centraliza todas las constantes utilizadas por el analizador, incluyendo:

    • enum TYPE: Define todos los posibles tipos de token.
    • enum DATATYPES: Define todos los tipos de dato VHDL admitidos.
    • Expresiones regulares para identificar cada tipo de token.
  • ParserException.cs: Una clase de excepción personalizada usada para reportar errores sintácticos y semánticos detallados durante el análisis, incluyendo:

    • Números de línea y columna (base 1) para la ubicación del error
    • Información de contexto (el valor del token donde ocurrió el error)
    • Códigos de error predefinidos (INITIALIZATION, DUPLICATE_DECLARATION, MISSING_STRUCTURE, DUPLICATE_STRUCTURE, MISSING_DECLARATION, SYNTAX_ERROR)
    • Métodos de fábrica para errores comunes: UnexpectedToken(), UndeclaredVariable(), TypeMismatch()

Construcciones VHDL admitidas

El analizador admite un conjunto completo de características del lenguaje VHDL:

  • Estructuras de alto nivel: entity, architecture, package, package body, configuration
  • Declaraciones: signal, constant, variable, attribute, alias, component, disconnect, type, subtype
  • Tipos personalizados: tipos record (con declaraciones de campos), tipos array (con restricciones de índice), tipos de enumeración, subtipos con restricciones
  • Puertos y genéricos: declaraciones completas de puertos/genéricos con tipos de vector, rangos y valores predeterminados
  • Sentencias secuenciales: if-then-elsif-else, case-when, for-loop, while-loop, exit, wait, null, return
  • Sentencias concurrentes: asignaciones de señales, instanciaciones de componentes con port map y generic map
  • Sentencias generate: for-generate, if-generate, generates anidados con declaraciones locales de señales/constantes
  • Sentencias de bloque: soporte completo de sentencias de bloque con expresiones de guarda, declaraciones locales y bloques anidados
  • Construcciones etiquetadas: procesos etiquetados, sentencias generate etiquetadas, instanciaciones de componentes etiquetadas, bloques etiquetados
  • Funciones: declaraciones de funciones con parámetros, variables locales, límites de tipo de retorno (incluyendo expresiones complejas) y expresiones complejas
  • Expresiones: aritméticas (+, -, *, /, mod, **), lógicas (and, or, xor, nand, nor, xnor, not, sll, srl, sla, sra, rol, ror), de comparación (=, /=, <, >, <=, >=)
  • Operaciones con vectores: acceso indexado (por ejemplo, signal(7 downto 0), signal(i)), atributos de vector (por ejemplo, A'length, A'range, A'high, A'low, A'left, A'right, A'event, A'last_value, A'stable, A'quiet, A'active, A'transaction)
  • Tipos de dato: std_logic, std_logic_vector, integer, boolean, bit, bit_vector, natural, positive, signed, unsigned, std_ulogic, std_ulogic_vector, boolean_vector, integer_vector, character, string, time, real
  • Construcciones especiales: rising_edge, falling_edge, expresiones de agregado (por ejemplo, (others => '0')), llamadas a funciones en límites (por ejemplo, clog2(N)), operador de concatenación (&), especificaciones de temporización after con unidades de tiempo (fs, ps, ns, us, ms, sec, min, hr)

Funciones de la biblioteca estándar

El analizador reconoce las siguientes funciones de la biblioteca estándar IEEE (estas no requieren declaración explícita):

Categoría Funciones
Conversión IEEE.numeric_std to_unsigned, to_signed, to_integer, unsigned, signed
Redimensionado/Desplazamiento IEEE.numeric_std resize, shift_left, shift_right
IEEE.std_logic_1164 std_logic_vector, std_ulogic_vector, to_stdlogicvector, to_stdulogicvector, to_bit, to_bitvector, to_x01, to_x01z, to_ux01
Funciones matemáticas clog2, log2, ceil, floor, abs, minimum, maximum
Conversiones de tipo conv_integer, conv_unsigned, conv_signed, conv_std_logic_vector, integer, natural, positive, real, boolean, bit, character, string
Detección de flancos rising_edge, falling_edge
IEEE.math_real sqrt, sin, cos, tan, exp, log, pow
Otras now, time

Soporte de sentencias de bloque

Las sentencias de bloque proporcionan una forma de agrupar sentencias concurrentes con expresiones de guarda opcionales y declaraciones locales:

my_block : block (guard_signal = '1') is
    signal local_sig : std_logic;
begin
    guarded output <= input;
end block my_block;

Características admitidas:

  • Expresiones de guarda (opcionales)
  • Declaraciones locales de señales/constantes dentro de los bloques
  • Bloques anidados (bloques dentro de bloques)
  • Bloques etiquetados con etiquetas de cierre opcionales
  • Asignaciones de señales con guarda mediante la palabra clave guarded

Soporte de tipos personalizados

El analizador admite tipos definidos por el usuario, incluyendo registros (records), arreglos (arrays) y enumeraciones:

-- Record type declaration
type ComplexNumber is record
    re : signed(15 downto 0);
    im : signed(15 downto 0);
end record;

-- Array type declaration
type MemoryArray is array (0 to 255) of std_logic_vector(7 downto 0);

-- Enumeration type declaration
type State is (IDLE, RUNNING, STOPPED, ERROR);

-- Subtype declaration
subtype SmallInt is integer range 0 to 100;

Características admitidas:

  • Tipos record con múltiples campos de diversos tipos de dato
  • Tipos array con restricciones de índice (usando to o downto)
  • Tipos de enumeración con constantes con nombre (agregadas al ámbito automáticamente)
  • Declaraciones de subtipos con restricciones de rango

Instanciación de componentes

El analizador admite plenamente las instanciaciones de componentes con mapeos de puertos y genéricos:

-- Component declaration in architecture header
component Adder
    generic (WIDTH : integer := 8);
    port (
        A, B : in std_logic_vector(WIDTH-1 downto 0);
        Sum  : out std_logic_vector(WIDTH-1 downto 0)
    );
end component;

-- Component instantiation
U1 : Adder
    generic map (WIDTH => 16)
    port map (A => input_a, B => input_b, Sum => result);

Convención de nombres de ámbitos

Los ámbitos de arquitectura usan la convención de nombres {entityName}_{archName} para admitir múltiples arquitecturas con el mismo nombre para diferentes entidades:

Entity "Adder" with architecture "Behavioral" → scope key: "Adder_Behavioral"
Entity "Counter" with architecture "Behavioral" → scope key: "Counter_Behavioral"

Característica de retroceso (rollback) de TokenStream

La clase TokenStream proporciona capacidad de retroceso (rollback) para dar marcha atrás durante el análisis:

// Mark current position
stream.SetRollBack();

// Try to parse something
try {
    ParseComplexConstruct();
} catch {
    // Restore to marked position if parsing fails
    stream.RollBack();
    ParseAlternativeConstruct();
}

4. Cómo usar este módulo

Para usar el analizador, se instancia la clase principal Parser, proporcionándole la ruta a un archivo VHDL. Luego, se llama al método ParseV2() para iniciar el proceso.

using Parser.Repositories;
using System;

try
{
    // 1. The Tokenizer reads the file and performs lexical analysis.
    Tokenizer tokenizer = new("path/to/your/design.vhdl");

    // 2. The Parser class orchestrates the syntactic analysis.
    Parser vhdlParser = new(tokenizer);
    vhdlParser.ParseV2();

    // 3. After parsing, the results are available in the Scopes dictionary.
    // This dictionary contains all entities, architectures, and their declared variables.
    var scopes = vhdlParser.GetScopes();

    Console.WriteLine("Parsing completed successfully!");

    // You can now inspect the parsed data structures.
    foreach (var scope in scopes)
    {
        Console.WriteLine($"Scope: {scope.Key}");
        foreach (var variable in scope.Value)
        {
            Console.WriteLine($"  - Variable: {variable.Name}, Type: {variable.Type}");
        }
    }
}
catch (Exception ex)
{
    Console.WriteLine($"An error occurred during parsing: {ex.Message}");
}

5. Dependencias

Este módulo depende de las siguientes bibliotecas de .NET:

  • Microsoft.Extensions.Logging: Para registrar información y errores durante el proceso de análisis.

Todo el resto del código es autónomo dentro de este módulo del proyecto.

6. Flujo del analizador y relaciones entre clases

Para comprender mejor el funcionamiento interno del analizador, los siguientes diagramas ilustran el flujo de datos, las relaciones entre clases y la secuencia de operaciones.

6.1. Flujo de análisis de alto nivel

Este diagrama muestra el proceso general, comenzando desde el archivo VHDL en bruto y terminando con el diccionario estructurado Scopes que el motor de simulación puede utilizar.

graph TD
    A["VHDL File (.vhdl)"] --> B(Tokenizer);
    B --> C{"List<Token>"};
    C --> D[TokenStream];
    D --> E(Parser);
    E --> F(EntityParser);
    E --> G(ArchitectureParser);
    E --> H(PackageParser);
    
    subgraph "Parsing Execution"
        F --> I{Scopes};
        G --> I;
        H --> I;
    end

    I --> J[Simulation Engine];

    style B fill:#f9f,stroke:#333,stroke-width:2px
    style E fill:#ccf,stroke:#333,stroke-width:2px
    style J fill:#bbf,stroke:#333,stroke-width:2px

6.2. Diagrama de clases detallado

Este diagrama detalla las relaciones de composición y herencia entre las clases clave del módulo analizador. Destaca cómo los analizadores especializados se componen para manejar diferentes partes de la sintaxis VHDL.

classDiagram
    direction LR

    class Parser {
        +ParseV2()
        +Parse()
        +GetScopes() : Dictionary~string, HashSet~Variable~~
    }

    class Tokenizer {
        +ReadFile()
        +ReadString(string)
        +TokenizeLine(string)
        +Evaluate(string) : Token
        +GetDictionaryType() : List~Token~
    }

    class TokenStream {
        +Current : Token
        +Previous : Token
        +MoveNext()
        +Expect(TYPE)
        +Match(TYPE) : bool
        +SetRollBack()
        +RollBack()
        +EndStream() : bool
    }

    class EntityParser {
        +Parse()
        +ParseDeclaration()
        +GetName() : string
        +ExistsVariable(string) : bool
    }

    class ArchitectureParser {
        +Parse()
        +ParseDeclaration()
        +ParseHeader()
        +ParseBodyDeclaration()
        +ParseGenerateStatement()
        +ParseBlockStatement()
        +ParseComponentInstantiation()
    }

    class PackageParser {
        +Parse()
        +ParseDeclaration()
        +ParseBodyDeclaration()
        +ParseProcedureBody()
    }

    class DataStructuresParser {
        +Parse()
        +ParseSequentialBody()
        +ParseProcess()
        +ParseIfStructure()
        +ParseCaseStructure()
        +ParseForStructure()
        +ParseWhileStructure()
        +ParseAsignations()
        +ParseOperation()
    }

    class FunctionParser {
        +Parse()
        +ParseDeclaration()
        +ParseBodyDeclaration()
        +ParseReturnTypeBounds()
    }

    class GenericParser {
        +Parse()
        +ParseStructure()
        +ParseAssignations()
        +ParseAssignationsWithComas()
    }

    class PortsParser {
        +Parse()
        +ParseDeclarations()
        +ParseTypeBounds()
    }

    class ArchitectureAssignationParser {
        +Parse()
        +ParseDeclarations()
        +ParseAttributeDeclaration()
        +ParseDisconnectSpecification()
    }

    class FunctionAssignationParser {
        +Parse()
        +ParseDeclarations()
    }

    class VectorParser {
        +Parsed : bool
        +Parse()
        +ParseBody()
        +ParseAttributes()
        +ParseBoundExpression()
    }

    class RangeParser {
        +Parse()
        +ParseBody()
    }

    subgraph Models
        class Token {
            +Value : string
            +Type : TYPE
            +Line : int
            +Col : int
        }
        class Variable {
            +Name : string
            +Type : DATATYPES
            +Value : object
            +Size : int
            +IsPort : bool
            +PortType : PortType
            +IsStub : bool
            +CustomTypeName : string?
            +RecordFields : Dictionary?
            +ElementSize : int
            +ArrayLength : int
            +OnValueChange : event
        }
        class Constants {
            <<static>>
            +TYPE enum
            +DATATYPES enum
            +ParseExceptions enum
            +OperatorType enum
            +PortType enum
        }
        class ParserException {
            +Line : int
            +Column : int
            +ErrorCode : ParseExceptions
            +Context : string
            +UnexpectedToken()$ ParserException
            +UndeclaredVariable()$ ParserException
            +TypeMismatch()$ ParserException
        }
        class Diagnostic {
            +Severity : DiagnosticSeverity
            +Line : int
            +StartColumn : int
            +EndColumn : int
            +Code : string
            +Message : string
            +FromException(ParserException)$ Diagnostic
        }
        class DiagnosticSeverity {
            <<enumeration>>
            Info
            Warning
            Error
        }
    end

    Parser o-- TokenStream : uses
    Parser o-- EntityParser : creates
    Parser o-- ArchitectureParser : creates
    Parser o-- PackageParser : creates
    Tokenizer --> TokenStream : provides tokens for

    EntityParser o-- GenericParser : creates
    EntityParser o-- PortsParser : creates

    ArchitectureParser o-- DataStructuresParser : creates
    ArchitectureParser o-- FunctionParser : creates
    ArchitectureParser o-- ArchitectureAssignationParser : creates

    PackageParser o-- DataStructuresParser : creates
    PackageParser o-- FunctionParser : creates

    FunctionParser o-- DataStructuresParser : creates
    FunctionParser o-- FunctionAssignationParser : creates

    PortsParser o-- VectorParser : uses
    GenericParser o-- VectorParser : uses
    GenericParser o-- RangeParser : uses

    VectorParser <|-- RangeParser : inherits from

6.3. Diagrama de secuencia: análisis de una entidad

Este diagrama de secuencia muestra las interacciones en tiempo de ejecución entre objetos cuando el método ParseV2() encuentra un bloque entity en el código VHDL.

sequenceDiagram
    participant User
    participant Parser
    participant TokenStream
    participant EntityParser
    participant GenericParser
    participant PortsParser

    User->>Parser: ParseV2()
    Parser->>EntityParser: new(stream, scopes)
    Parser->>EntityParser: Parse()
    activate EntityParser

    EntityParser->>TokenStream: Expect(KEYWORD 'entity')
    EntityParser->>TokenStream: Current (get entity name)
    loop until 'is'
        EntityParser->>TokenStream: MoveNext()
    end

    alt 'generic' keyword found
        EntityParser->>GenericParser: new(stream, scopes, name)
        EntityParser->>GenericParser: Parse()
        activate GenericParser
        loop Parse generic declarations
            GenericParser->>TokenStream: Expect/MoveNext
        end
        deactivate GenericParser
    end

    alt 'port' keyword found
        EntityParser->>PortsParser: new(stream, scopes, name)
        EntityParser->>PortsParser: Parse()
        activate PortsParser
        loop Parse port declarations
            PortsParser->>TokenStream: Expect/MoveNext
        end
        deactivate PortsParser
    end

    EntityParser->>TokenStream: Expect(KEYWORD 'end')
    deactivate EntityParser

6.4. Diagrama de módulos

graph TD

    vhdl_files["VHDL Source Files<br>(.vhdl)"]
    subgraph parser_system["VHDL Parser Module"]
        subgraph repositories["Repositories"]
            tokenizer["Tokenizer<br>/Repositories/Tokenizer.cs"]
            token_stream["TokenStream<br>/Repositories/TokenStream.cs"]
            parser_core["Parser<br>/Repositories/Parser.cs"]
            structural_parsers["Structural Parsers<br>EntityParser, ArchitectureParser,<br>PackageParser"]
            declaration_parsers["Declaration Parsers<br>PortsParser, GenericParser,<br>ArchitectureAssignationParser,<br>FunctionAssignationParser"]
            statement_parsers["Statement Parsers<br>DataStructuresParser, FunctionParser"]
            helper_parsers["Helper Parsers<br>VectorParser, RangeParser"]
        end
        subgraph models["Models"]
            token_model["Token"]
            variable_model["Variable"]
            constants_model["Constants"]
            exception_model["ParserException"]
            diagnostic_model["Diagnostic<br>+ DiagnosticSeverity"]
        end
        subgraph interfaces["Interfaces"]
            iparser["IParser, IParserBaseMethods"]
            iparse_structures["IParseStructures"]
            iparse_declarations["IParseDeclarations"]
            iparse_vector["IParseVectorStructure"]
            iparse_generic["IParseGeneric"]
        end
    end

    scopes["Scopes Dictionary<br>Dictionary&lt;string, HashSet&lt;Variable&gt;&gt;"]
    downstream["Downstream Modules<br>(Debugger / Interpreter)"]
    editor_markers["Editor (Monaco markers)<br>Kmila.Shared.Pages.Editor"]

    vhdl_files -->|"reads"| tokenizer
    tokenizer -->|"produces List&lt;Token&gt;"| token_stream
    token_stream -->|"consumed by"| parser_core
    parser_core -->|"delegates to"| structural_parsers
    structural_parsers -->|"delegates to"| declaration_parsers
    structural_parsers -->|"delegates to"| statement_parsers
    declaration_parsers -->|"uses"| helper_parsers

    structural_parsers -.->|"implements"| interfaces
    declaration_parsers -.->|"implements"| interfaces
    statement_parsers -.->|"implements"| interfaces
    helper_parsers -.->|"implements"| interfaces

    repositories -->|"uses"| models
    parser_core -->|"populates"| scopes
    scopes -->|"consumed by"| downstream
    diagnostic_model -->|"FromException(ParserException)"| editor_markers
    exception_model -.->|"adapted via"| diagnostic_model

7. Historial de cambios

Re-sincronización de la documentación (2026-08-03, rama v1.3.0)

  • Se actualizó el sello del encabezado a la rama actual (v1.3.0) y la fecha. Se volvió a cotejar la referencia de clases contra el árbol en disco: se agregaron ParserSkipOverflowException y Diagnostic a la tabla de Modelos, SharedLogging a la tabla de Analizadores y una nueva tabla de Servicios para UserTypeRegistry / ParseProfile. Se actualizó la lista de fixtures negativos de la "Pasada de rigurosidad" para incluir el tercer fixture (test_negative_garbage_in_body.vhdl), que Program.cs ya cablea. Solo documentación; sin cambios en el código fuente del analizador ni en la API pública.

Versión 1.14.1 (2026-05-01)

Verificación de la documentación

  • README.md: Se cotejó el documento completo contra el código fuente actual de Repositories/, Models/ e Interfaces/. Se confirmó que los puntos de entrada del orquestador siguen siendo Parser(Tokenizer) / Parser(string FilePath), con ParseV2() como la ruta recomendada y Parse() conservado como la implementación monolítica heredada. El conjunto completo IParser documentado aquí coincide uno a uno con los archivos *.cs en disco (14 clases analizadoras, 6 interfaces, 5 archivos de modelo incluyendo Diagnostic).
  • §2 Arquitectura / Parser.cs: Se aclaró que ParseV2() también maneja las declaraciones library/use en línea entre unidades de diseño y recorre los bloques configuration (con la forma de enlace for CompLabel : CompType use entity work.X(Arch);) mediante ParseConfigurationBlock / ParseConfigurationBody. Las versiones anteriores de este documento solo mencionaban el cambio entre entity / architecture / package.
  • §6.2 Diagrama de clases: Se agregó el modelo Diagnostic (con el adaptador estático FromException(ParserException)) y la enumeración DiagnosticSeverity al clúster de Modelos.
  • §6.4 Diagrama de módulos: Se agregó el nodo Diagnostic / DiagnosticSeverity al subgrafo de Modelos y una arista explícita hacia la superficie del editor (Kmila.Shared.Pages.Editor) que muestra cómo ParserException se adapta mediante Diagnostic.FromException(...) para alimentar los marcadores de Monaco.
  • Sin cambios en la API pública ni en el comportamiento en tiempo de ejecución en esta versión; la superficie del analizador sigue siendo retrocompatible con la versión v1.14.0 que introdujo el flujo de diagnósticos.

Versión 0.13.1 (2026-02-21)

Mejoras de documentación

  • Variable.cs: Se agregó documentación XML exhaustiva para todos los constructores, operadores, campos de respaldo (_size, _value), IsPort, PortType, ToString(), la conversión implícita y los operadores de igualdad/desigualdad
  • DataStructuresParser.cs: Se agregaron documentos XML para ParseDeclaration() (lanza NotImplementedException) y se documentaron los grupos de campos privados
  • VectorParser.cs: Se agregaron documentos XML para el método protegido CheckVariable() y el método privado IsVector()
  • TokenizerTests.cs: Se agregó documentación XML para la clase de prueba y el método de prueba con descripciones de parámetros
  • Program.cs: Se agregó documentación XML para la clase ejecutora de pruebas y su punto de entrada

Versión 0.13.0 (2025-12-13)

Esta versión incluye mejoras exhaustivas de documentación, correcciones de errores y mejoras menores de código.

Correcciones de errores

  • Corrección de fuga de memoria en TokenStream: Se corrigió una fuga de memoria significativa causada por que cada instancia de TokenStream creaba su propio LoggerFactory. Ahora usa un LoggerFactory compartido estático para evitar la acumulación de memoria durante operaciones de análisis repetidas.

Mejoras de documentación

  • Actualizaciones del README: Se agregó documentación exhaustiva para características antes no documentadas:

    • Funciones de la biblioteca estándar: tabla completa de más de 50 funciones reconocidas de la biblioteca estándar IEEE
    • Soporte de sentencias de bloque: explicación detallada con ejemplos de código VHDL
    • Instanciación de componentes: documentación completa con ejemplos de generic/port map
    • Convención de nombres de ámbitos: explicación del patrón {entityName}_{archName}
    • Característica de retroceso (rollback) de TokenStream: documentación de uso con ejemplos de código
  • Documentación de Constants.cs: Se agregó documentación XML a todos los valores de enum y patrones de regex:

    • Enum TYPE: cada tipo de token ahora tiene una descripción detallada
    • Enum ParseExceptions: cada código de error tiene una explicación con ejemplos
    • Enum DATATYPES: cada tipo de dato tiene una descripción de su semántica VHDL
    • Enum OperatorType: cada categoría de operador está documentada
    • Enum PortType: descripciones de puertos de entrada/salida
    • Todos los patrones de regex: se agregaron ejemplos y explicaciones para cada patrón

Actualizaciones (del 2025-12-09)

  • Operadores de desplazamiento/rotación en LOGIC_OPERATORS: Se agregaron sll, srl, sla, sra, rol, ror al patrón de regex LOGIC_OPERATORS. Estos operadores ya estaban en KEYWORDS pero no estaban clasificados como operadores lógicos, lo que causaba problemas en la evaluación de expresiones del Intérprete.

Versión 0.12.5 (2025-12-09)

Esta versión agrega los operadores de desplazamiento y rotación a LOGIC_OPERATORS para su clasificación adecuada por parte del intérprete aguas abajo.

Actualizaciones

  • Operadores de desplazamiento/rotación en LOGIC_OPERATORS: Se agregaron sll, srl, sla, sra, rol, ror al patrón de regex LOGIC_OPERATORS. Estos operadores ya estaban en KEYWORDS pero no estaban clasificados como operadores lógicos, lo que causaba problemas en la evaluación de expresiones del Intérprete.

Versión 0.12.5 (2025-12-09)

Esta versión corrige las fallas de prueba restantes (pruebas 18, 19, 20) y agrega soporte para construcciones VHDL avanzadas, incluyendo sentencias de bloque, señales con guarda, atributos de señal y generates condicionales complejos.

Nuevas características

  • Soporte de sentencias de bloque: Análisis completo de sentencias de bloque VHDL con:
    • Expresiones de guarda (por ejemplo, block (guard_signal = '1'))
    • Declaraciones locales de señales/constantes dentro de los bloques
    • Bloques anidados (bloques dentro de bloques)
    • Bloques etiquetados con etiquetas de cierre opcionales
  • Asignaciones de señales con guarda: Soporte para asignaciones concurrentes de señales con guarda mediante la palabra clave guarded
  • Atributos de señal con argumentos: Análisis de atributos de señal con argumentos de tiempo:
    • 'stable(time) - comprobación de estabilidad de señal
    • 'delayed(time) - valor de señal retardado
    • 'last_value, 'last_event, 'event en expresiones condicionales
  • Funciones de conversión de tipo en condicionales: Soporte para las funciones de conversión de tipo de IEEE numeric_std en condiciones if/when:
    • unsigned(), signed(), to_integer(), to_unsigned(), to_signed()
    • resize(), shift_left(), shift_right()
    • std_logic_vector(), integer(), natural()
  • Enlace de arquitectura en configuración: Soporte para la especificación de arquitectura en bloques de configuración:
    • sintaxis use entity work.Component(Architecture);
  • Soporte mejorado de operadores: Se agregaron /= (distinto) y >= (mayor o igual) como operadores correctamente tokenizados
  • Constantes de paquete en genéricos: Los valores predeterminados de los genéricos ahora pueden referenciar constantes de paquete (identificadores), no solo literales
  • Tipos personalizados en puertos: Las declaraciones de puertos ahora aceptan tipos definidos por el usuario (identificadores) además de los tipos VHDL estándar

Correcciones de errores

  • Se corrigió un bucle infinito en ParseOperation(): Se corrigió la condición del bucle while que impedía la terminación adecuada al encontrar un punto y coma
  • Se corrigió el regex QUALIFIED_IDENTIFIERS: Ahora permite dígitos en el primer segmento del identificador (por ejemplo, s_axi_s2m.bvalid donde s2m contiene un dígito)
  • Se corrigió el análisis de bloques anidados: ParseBlockBody() ahora reconoce y maneja correctamente las sentencias de bloque anidadas
  • Se corrigió el análisis de bloques de configuración: Se agregó soporte para la sintaxis de enlace de arquitectura con paréntesis
  • Se agregó un mecanismo de reserva para tokens desconocidos: ParseSingleStatement() ahora omite con elegancia los tokens desconocidos hasta el siguiente punto y coma en lugar de provocar bucles infinitos

Correcciones en archivos de prueba

  • test20.vhdl: Se corrigieron conflictos de mayúsculas/minúsculas en identificadores VHDL renombrando las constantes de la máquina de estados:
    • TX_DATATX_ST_DATA (para evitar el conflicto con la señal tx_data)
    • RX_DATARX_ST_DATA (para evitar el conflicto con la señal rx_data)
    • Renombrados similares para TX_IDLE, TX_START, TX_STOP, RX_IDLE, RX_START, RX_STOP

Cobertura de pruebas

  • Los 43 casos de prueba ahora pasan:
    • test1-10: construcciones VHDL básicas a intermedias
    • test11-17: sentencias generate, diseños multientidad, procesos etiquetados
    • test18: atributos de señal avanzados, señales con guarda, sentencias de bloque, configuración
    • test19: generates condicionales complejos con conversiones de tipo
    • test20: diseño completo estilo SoC con paquetes, records, múltiples entidades y configuración

Versión 0.12.4 (2025-12-08)

Esta versión incluye mejoras significativas en el manejo por parte del analizador de construcciones VHDL complejas, en particular en torno al análisis de sentencias concurrentes frente a secuenciales, la gestión de ámbitos y el reporte de errores mejorado.

Nuevas características

  • Soporte de sentencias generate: Análisis completo de sentencias for-generate e if-generate, incluyendo:
    • Sentencias generate anidadas
    • Declaraciones locales de señales/constantes dentro de los bloques generate
    • Ámbito de la variable iteradora (eliminada automáticamente después de que el generate finaliza)
  • Soporte de procesos etiquetados: Análisis de procesos con etiquetas (por ejemplo, my_proc : process(clk) begin ... end process my_proc;)
  • Soporte de bucles while: Se agregó el análisis de while condition loop ... end loop;
  • Sentencias secuenciales adicionales: Soporte para las sentencias exit, wait y null
  • Palabras clave extendidas: Se agregó soporte para muchas palabras clave VHDL adicionales, incluyendo nand, nor, xnor, sll, srl, sla, sra, rol, ror, abs, rem, transport, reject, inertial, unaffected, y más
  • Atributos de vector: Soporte extendido para atributos de señal como 'last_value, 'last_event, 'event, 'stable, 'quiet, 'active, 'transaction, 'delayed, 'now, 'path_name, 'instance_name, 'base, 'high, 'low, 'left, 'right, 'length, 'range, 'reverse_range, 'ascending, etc.
  • Análisis de bloques de configuración: Se agregó soporte para el análisis de bloques configuration de VHDL con sentencias for anidadas
  • Declaraciones de componentes: Análisis completo de declaraciones component dentro de los encabezados de arquitectura, incluyendo bloques generic y port opcionales
  • Instanciación de componentes: Soporte para instanciaciones de componentes con port map y generic map
  • Declaraciones de variables de proceso: Análisis de declaraciones variable y constant dentro de los cuerpos de proceso antes de begin
  • Expresiones de agregado: Soporte para expresiones de agregado como (others => '0') en inicializaciones de señal/constante
  • Análisis de llamadas a funciones: Análisis adecuado de llamadas a funciones en expresiones (por ejemplo, conv_integer(x), clog2(N))
  • Acceso indexado a señales: Análisis del acceso indexado a señales/variables como signal(7 downto 0) o signal(i)
  • Declaraciones de atributos: Soporte para declaraciones y especificaciones attribute
  • Especificaciones de disconnect: Análisis de especificaciones disconnect para señales con guarda

Reporte de errores mejorado

  • ParserException: Completamente reescrita con mensajes de error detallados que incluyen:
    • Números de línea y columna (base 1)
    • Información de contexto (cerca de qué token ocurrió el error)
    • Tipos de error predefinidos: INITIALIZATION, DUPLICATE_DECLARATION, MISSING_STRUCTURE, DUPLICATE_STRUCTURE, MISSING_DECLARATION, SYNTAX_ERROR
    • Métodos de fábrica: UnexpectedToken(), UndeclaredVariable(), TypeMismatch()
  • StreamerException: Mejorada con contexto detallado:
    • Descripciones de tipo legibles por humanos (por ejemplo, "keyword (e.g., 'process', 'signal', 'begin', 'end')")
    • Rastrea el último token válido para una mejor ubicación del error al llegar al fin de la entrada

Cambios arquitectónicos clave

  1. Análisis de sentencias concurrentes frente a secuenciales (DataStructuresParser.cs)

    • Se agregó el método ParseSingleStatement() para analizar exactamente UNA sentencia concurrente (usado en el contexto del cuerpo de la arquitectura)
    • Se agregó el método ParseSequentialBody() para analizar TODAS las sentencias secuenciales hasta la palabra clave end (usado en cuerpos de proceso/función)
    • El método Parse() ahora usa ParseSingleStatement() para evitar el bucle incorrecto a través de las construcciones del cuerpo de la arquitectura
  2. Nombres de ámbito de arquitectura (ArchitectureParser.cs)

    • Los ámbitos de arquitectura ahora usan {entityName}_{archName} como clave
    • Esto permite múltiples arquitecturas con el mismo nombre para diferentes entidades (por ejemplo, tanto Adder como PipelineReg pueden tener arquitecturas Behavioral)
  3. Ámbito de la variable iteradora

    • Las variables iteradoras de for-loop y for-generate ahora tienen un ámbito adecuado
    • Las variables se eliminan del ámbito después de que el bucle/generate finaliza
    • Evita errores de "declaración duplicada" cuando se reutiliza el mismo nombre de iterador
  4. Análisis del cuerpo de función (FunctionParser.cs)

    • Ahora usa ParseSequentialBody() en lugar de Parse() para manejar correctamente múltiples sentencias en los cuerpos de función
    • Soporte para límites de tipo de retorno con expresiones complejas (por ejemplo, std_logic_vector((A'length + B'length) - 1 downto 0))
    • Análisis de declaraciones de variables/constantes locales antes de begin
  5. Mejoras del modelo Variable (Variable.cs)

    • Se extendió GetDatatype() para admitir tipos adicionales: std_ulogic, signed, unsigned, bit_vector, boolean_vector, integer_vector, positive, string, time, real
    • Manejo mejorado de los patrones de agregado (others => 'X')
    • Soporte para expresiones de concatenación (concat:...)
    • Comparación de variables sin distinción de mayúsculas/minúsculas (los identificadores VHDL no distinguen mayúsculas de minúsculas)

Correcciones de errores

  • Se corrigió un problema donde las etiquetas de generate se analizaban incorrectamente como asignaciones de señal
  • Se corrigió un problema donde las etiquetas de instanciación de componentes causaban errores de "instancia no está en el ámbito"
  • Se corrigieron errores de estructura duplicada cuando múltiples arquitecturas compartían el mismo nombre
  • Se corrigió el análisis del cuerpo de función que se detenía después de la primera sentencia
  • Se corrigió el análisis del cuerpo del bucle while que retornaba prematuramente
  • Se corrigió que los límites de tipo de vector no se analizaban en los parámetros de función
  • Se corrigió el análisis de la expresión de retorno en funciones

Cobertura de pruebas

  • Los 20 casos de prueba ahora pasan, cubriendo:
    • Análisis básico de entidad/arquitectura (test1-5)
    • Expresiones y operadores complejos (test6-10)
    • Sentencias generate con declaraciones locales (test11-12)
    • Diseños multientidad con nombres de arquitectura compartidos (test11, test17)
    • Procesos etiquetados y estructuras de control anidadas (test13-16)
    • Jerarquías e instanciaciones de componentes (test17-20)

8. Referencia completa de clases

Modelos

Clase Descripción
Token Representa un token léxico con las propiedades Value, Type, Line y Col
Variable Representa señales, puertos, variables y constantes de VHDL con las propiedades: Name, Type, Value, Size, IsPort, PortType, IsStub, CustomTypeName, RecordFields, y el evento reactivo OnValueChange. Incluye el método estático GetDatatype() para la resolución de la cadena de tipo.
Constants Clase estática que contiene los tipos de token (enum TYPE), tipos de dato (enum DATATYPES), códigos de error (enum ParseExceptions), tipos de operador (enum OperatorType: ASSIGNATION, LOGIC_OPERATION, ARITH_OPERATION, COMPARATION), tipos de puerto (enum PortType: IN, OUT, INOUT, BUFFER), y patrones de regex para la clasificación de tokens
ParserException Excepción personalizada con contexto de error enriquecido que incluye información de línea/columna
ParserSkipOverflowException Subclase de ParserException que se lanza cuando un bucle Skip* / Parse* sobrepasa su límite de iteraciones (lleva el nombre del método ofensor y el límite); permite que la entrada patológica falle rápido en lugar de colgar el analizador
Diagnostic Diagnóstico estructurado (DiagnosticSeverity + línea/columna/código/mensaje); FromException(ParserException) adapta el flujo de excepciones a los marcadores de Monaco

Interfaces

Interfaz Descripción
IParser Interfaz base con el método Parse()
IParserBaseMethods Métodos para la gestión de ámbitos/variables: ExistsVariable(), GetScopes()
IParseStructures Para estructuras de alto nivel (entity, architecture, package): GetName(), ParseDeclaration(), ParseBodyDeclaration()
IParseDeclarations Para bloques de declaración: ParseDeclarations()
IParseVectorStructure Para tipos de vector: ParseBody()
IParseGeneric Para bloques genéricos: ParseStructure(), ParseAssignations(), ParseAssignationsWithComas()

Analizadores

Analizador Responsabilidad
Tokenizer Análisis léxico: convierte el código fuente VHDL en tokens
TokenStream Gestión del flujo de tokens con Current, MoveNext(), Expect(), Match()
Parser Orquestador principal: delega a los analizadores especializados
EntityParser Analiza los bloques entity
ArchitectureParser Analiza los bloques architecture, incluyendo sentencias generate e instanciaciones de componentes
PackageParser Analiza los bloques package y package body
DataStructuresParser Analiza sentencias secuenciales (if, case, for, while, asignaciones)
FunctionParser Analiza declaraciones y cuerpos de function
FunctionAssignationParser Analiza las declaraciones de parámetros de función
GenericParser Analiza los bloques generic
PortsParser Analiza los bloques port
ArchitectureAssignationParser Analiza las declaraciones del encabezado de arquitectura (señales, constantes, atributos)
VectorParser Analiza los tipos de vector (std_logic_vector, etc.)
RangeParser Analiza las declaraciones range (extiende VectorParser)
SharedLogging Auxiliar internal static que expone una única ILoggerFactory compartida (proveedor de consola), reutilizada por cada analizador para evitar la asignación de LoggerFactory por instancia

Servicios

Servicio Responsabilidad
UserTypeRegistry Catálogo a nivel de proceso de los nombres de tipo VHDL definidos por el usuario ya resueltos; poblado por el host a partir de los símbolos de LibraryCompiler antes del análisis, de modo que el analizador acepta los tipos declarados en paquetes/librerías como identificadores de tipo válidos
ParseProfile Contadores ligeros por ejecución para las rutas calientes del lado del análisis (por ejemplo, Tokenizer.EvaluateToken), emitidos por el host cuando KMILA_PARSE_PROFILE está definido

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

Estos diagramas fueron reescritos o creados durante la auditoría entre módulos porque los diagramas TT1 existentes modelaban un Parse() de máquina de estados única que fue reemplazado por la delegación explícita de ParseV2() (EntityParser / ArchitectureParser / PackageParser). También residen en Documentacion/TT1/diagrams/.

fig_4_4_dominio_analisis_codigo — Modelo de dominio: análisis de código (reescrito)

classDiagram
    direction LR

    class ArchivoHDL {
        +String nombre
        +String contenido
    }
    class Scope {
        +String nombre
        +TipoScope tipo
    }
    class Variable {
        +String nombre
        +Tipo tipo
        +Object valor
        +Int tamanio
        +PortType direccion
    }
    class Diagnostico {
        +Severidad severidad
        +Int linea
        +Int columnaInicio
        +Int columnaFin
        +String codigo
        +String mensaje
    }

    ArchivoHDL "1" --> "0..*" Scope : produce
    Scope "1" --> "0..*" Variable : agrupa
    ArchivoHDL "1" --> "0..*" Diagnostico : genera
    Variable "0..*" ..> "0..*" Variable : referencia (records, vectores)

fig_5_8_1_seq_validacion_vhdl — Secuencia de validación (reescrito)

sequenceDiagram
    actor E as Estudiante
    participant UI as ButtonCollapse
    participant Ed as Editor.razor
    participant PB as ProgramBuilder
    participant Tk as Tokenizer
    participant TS as TokenStream
    participant Pa as Parser

    E->>UI: Presiona "Validar programa"
    UI->>Ed: OnBuildRequested
    Ed->>PB: Build(ProjectFile)
    PB->>PB: Validar extension .vhd/.vhdl
    PB->>Pa: new Parser(filePath)
    Pa->>Tk: ReadFile + TokenizeLine + Evaluate
    Tk-->>Pa: List~Token~
    Pa->>TS: new TokenStream(tokens)
    PB->>Pa: ParseV2()
    loop Mientras quedan tokens
        Pa->>TS: Current / MoveNext
        alt Token = entity
            Pa->>Pa: EntityParser.ParseDeclaration
        else Token = architecture
            Pa->>Pa: ArchitectureParser.ParseDeclaration
        else Token = package
            Pa->>Pa: PackageParser.ParseDeclaration
        else Token = library / use
            Pa->>Pa: ParseLibrariesUsage
        else Token = configuration
            Pa->>Pa: ParseConfigurationBlock
        end
    end
    Pa-->>PB: Scopes (Dictionary<string, HashSet~Variable~>)
    PB-->>UI: OnBuildProgressChanged(100%)
    UI-->>E: Muestra entidades y recursos detectados

fig_5_11_2_1_act_validacion_prep — Actividad de preparación (reescrito)

flowchart TD
    A([Inicio: Validar programa]) --> B{Extension .vhd o .vhdl?}
    B -->|No| C([Fin: archivo rechazado])
    B -->|Si| D[Crear instancia de Parser con ruta del archivo]
    D --> E[Tokenizer: ReadFile y eliminar comentarios]
    E --> F[TokenizeLine: clasificar tokens por regex]
    F --> G[Evaluate: asignar TYPE a cada token]
    G --> H[Construir TokenStream sobre la lista de tokens]
    H --> I([Continua en Figura 5.11.2.2])

fig_5_11_2_2_act_validacion_recorrido — Recorrido guiado por tokens (reescrito)

flowchart TD
    A([TokenStream listo]) --> B[ParseV2: leer Current]
    B --> C{Tipo de token?}
    C -->|entity| D[EntityParser: nombre, generic, ports]
    C -->|architecture| E[ArchitectureParser: nombre, entidad, header]
    C -->|package| F[PackageParser: constantes, funciones, tipos]
    C -->|library / use| G[ParseLibrariesUsage]
    C -->|configuration| H[ParseConfigurationBlock]
    C -->|otro| B
    D --> I[Registrar Variables en Scopes]
    E --> I
    F --> I
    G --> B
    H --> I
    I --> J{EndStream?}
    J -->|No| B
    J -->|Si| K{Hubo entidades / packages?}
    K -->|No| L([Fin: error, sin unidades de diseno])
    K -->|Si| M([Fin: Scopes disponibles + Diagnosticos])

fig_5_11_6_act_tokenizer_pipeline — Pipeline del Tokenizer (nuevo)

flowchart TD
    A([Inicio: Tokenizer.ReadFile]) --> B[Leer archivo VHDL crudo]
    B --> C[Eliminar lineas que comienzan con --]
    C --> D[Por cada linea: TokenizeLine]
    D --> E[Aplicar regex a cada fragmento]
    E --> F{Patron coincide?}
    F -->|KEYWORDS| G[Token.Type = KEYWORDS]
    F -->|OPERATORS| H[Token.Type = OPERATORS]
    F -->|LITERALS| I[Token.Type = LITERALS]
    F -->|IDENTIFIERS| J[Token.Type = IDENTIFIERS]
    F -->|TIME / VECTORS_ATTRIBUTES /<br/>FUNCTIONS / SEPARATORS /<br/>PARENTHESIS| K[Token.Type = otro]
    F -->|Sin coincidencia| L[Token.Type = UNKNOWN<br/>+ Diagnostico critico]
    G --> M[Adjuntar Line, Col]
    H --> M
    I --> M
    J --> M
    K --> M
    L --> M
    M --> N{Quedan lineas?}
    N -->|Si| D
    N -->|No| O([Fin: List~Token~ + Diagnosticos])

fig_5_11_7_act_configuration_block — Análisis de bloque de configuración (nuevo)

flowchart TD
    A([Token = configuration]) --> B[ParseConfigurationBlock: leer nombre y entidad objetivo]
    B --> C[Expect of, leer entityName]
    C --> D[Expect is]
    D --> E[ParseConfigurationBody]
    E --> F{Siguiente token?}
    F -->|for label : compType use| G[Registrar binding<br/>label -> entity work.X arch]
    F -->|end for| H[Cerrar bloque interno]
    F -->|end configuration| I[Validar nombre coincide]
    F -->|otro| E
    G --> E
    H --> E
    I --> J[Crear scope configuration_name]
    J --> K[Registrar bindings como Variables<br/>en HashSet del scope]
    K --> L([Fin: scope listo en Scopes])

fig_5_11_8_act_generate_scope — Ciclo de vida del ámbito de la sentencia generate (nuevo)

flowchart TD
    A([Statement: for label in rango generate]) --> B[ArchitectureParser.ParseGenerateStatement]
    B --> C[Crear scope hijo<br/>arch_label_generate]
    C --> D[Registrar variable iteradora<br/>en el scope hijo]
    D --> E[ParseSequentialBody hasta end generate]
    E --> F{Statement tipo?}
    F -->|signal_assignment| G[Resolver nombres en scope hijo<br/>+ scope padre]
    F -->|process| H[Procesar Process anidado<br/>SyntentizeSensitiveList]
    F -->|component_instantiation| I[Resolver entity ref con<br/>iterador como generic]
    F -->|generate anidado| J[Recursion: nuevo scope hijo]
    G --> E
    H --> E
    I --> E
    J --> E
    F -->|end generate| K[Validar nombre = label]
    K --> L[Cerrar scope hijo,<br/>iterador liberado]
    L --> M([Fin: bindings disponibles para Interpreter])

fig_5_11_9_act_record_types — Resolución de tipos record (nuevo)

flowchart TD
    A([Declaracion: type T is record ...]) --> B[ArchitectureAssignationParser detecta record]
    B --> C[Crear Variable typeDef con<br/>CustomTypeName=T y RecordFields={}]
    C --> D[Por cada campo del record]
    D --> E[Leer nombre del campo + tipo]
    E --> F[RecordFields[name] = default por tipo]
    F --> G{Mas campos?}
    G -->|Si| D
    G -->|No| H[Registrar tipo T en scope]
    H --> I([Tipo disponible para declaraciones])

    J([Asignacion: signal x : T := ...]) --> K[Crear Variable x con<br/>CustomTypeName=T]
    K --> L[Copiar RecordFields del typeDef]
    L --> M([Variable lista en scope])

    N([Acceso: x.field <= value]) --> O[DataStructuresParser: detectar punto]
    O --> P[Resolver x en Scopes]
    P --> Q{Existe x.RecordFields[field]?}
    Q -->|No| R[Diagnostico: campo desconocido]
    Q -->|Si| S[Generar asignacion sobre<br/>RecordFields[field]]
    S --> T([Operacion lista para Interpreter])

Historial de cambios

Versión 1.14.0 (2026-04-19)

  • Diagnósticos estructurados — nuevo tipo Parser.Models.Diagnostic con DiagnosticSeverity (Info / Warning / Error) y línea / columna / código / mensaje. Diagnostic.FromException(ParserException) adapta el flujo existente basado en excepciones a la nueva colección sin romper a los llamadores.
  • Enganche del linter — los llamadores aguas abajo (véase Debugger.Repositories.Parser) ahora pueden ejecutar una pasada no fatal que reporta los ; faltantes al final de las sentencias y el desbalance de bloques begin / end antes de que se ejecute el analizador completo. Aborda la queja de larga data de que el analizador aceptaba silenciosamente fuentes a las que les faltaban terminadores en tiempo de simulación.
  • Integración con el editor — estos diagnósticos se mapean a la API setModelMarkers de Monaco mediante Kmila.Shared/Pages/Editor.razor, de modo que los errores de sintaxis ahora se iluminan en el margen en lugar de aparecer solo en una notificación (toast).
  • Higiene de compilación — se agregó <NoWarn> para la cascada de documentación XML (CS1587 / CS1591 / CS1570) causada por las listas de constantes separadas por comas en Models/Constants.cs. El proyecto compila sin advertencias sin el trabajo pesado de dividir cada constante de regex en su propia declaración.

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

La superficie del analizador ahora acepta:

  • procedure name(...) is ... end name; en el encabezado de la arquitectura (#12).
  • Instanciación directa de entidad label : entity work.Sub[(arch)] port map(...) (#13).
  • Valores predeterminados de genéricos con expresiones aritméticas, por ejemplo TOTAL : integer := BASE * TIMES (#16).
  • Los números de línea en los diagnósticos de error ahora referencian el archivo fuente del usuario (no el contador de llamadas del tokenizer) mediante la nueva sobrecarga Tokenizer.TokenizeLineAt(text, sourceLineZeroBased) (#21).

Memoria: Tokenizer.Evaluate() ahora libera el búfer _tokens en bruto una vez que _typeDictionary está construido. GC de estación de trabajo (Workstation GC) habilitado a nivel de proyecto para dirigirse a dispositivos de poca RAM (#7).

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


Pasada de rigurosidad (2026-05-17)

PortsParser ahora rechaza dos clases de descuidos en la declaración de puertos que antes pasaban silenciosamente. Ambas fueron reportadas por Daniel como parte de la reproducción del contador de rizo (ripple-counter) "Visible Wave" que compilaba sin problemas a pesar de errores tipográficos evidentes — véase el apéndice de Documentacion/CHANGELOG_concepts_module_2026-05-17.md y la entrada de memoria project_vhdl_parser_too_lenient.md.

Tipo de puerto desconocido → error "¿quiso decir …?"

Cuando el identificador de tipo en una declaración de puerto se captura como un IDENTIFIER (no como un KEYWORD reconocido) Y su valor sin distinción de mayúsculas está dentro de una distancia de Levenshtein 2 de cualquier nombre canónico de tipo VHDL en Constants.KNOWN_TYPE_NAMES, el analizador ahora lanza una ParserException con una pista "Did you mean ''?". Detecta los descuidos comunes:

Token de origen Canónico Distancia Comportamiento
STDLOGIC std_logic 1 (falta _) rechazado
STD_LGIC std_logic 1 (falta O) rechazado
coefficient_array_t ninguno cercano > 2 aceptado como tipo definido por el usuario

El auxiliar de Levenshtein reside en Constants.cs:LevenshteinDistance (privado, búfer rotativo de dos filas). Constants.FindTypoSuggestion(string) es el punto de entrada público — pase el identificador de origen y obtenga de vuelta el nombre canónico más cercano o null cuando el identificador parece legítimo.

Falta ; entre puertos

El bucle Parse() ahora detecta el caso en el que una declaración de puerto termina y el siguiente token es otro IDENTIFIER (en lugar de ; o )). Genera una ParserException que dice "Missing ';' before port '' in port declaration list." con la línea + columna del token ofensor — en lugar de dejar que el análisis se desborde en un confuso mensaje "expected )" muy por debajo del problema real.

Suite de pruebas negativas

Program.cs ganó una segunda pasada de pruebas para fixtures "que deben fallar". Cada entrada es un par (filename, mustContainSubstring); un PASS significa que el analizador generó la excepción esperada y el mensaje contenía la subcadena requerida. El código de salida del ejecutor es distinto de cero a menos que pasen tanto los 40 fixtures positivos COMO cada fixture negativo — de modo que las regresiones no pueden reaflojar silenciosamente la rigurosidad.

Cobertura negativa actual (raíz del Parser, junto a los test1..test40.vhdl existentes):

  • test_negative_unknown_type.vhdl — error tipográfico STDLOGIC en un tipo de puerto (debe contener STDLOGIC).
  • test_negative_missing_port_semi.vhdl — al puerto le falta ; antes del siguiente puerto (debe contener Missing ';').
  • test_negative_garbage_in_body.vhdl — token $ extraviado en el cuerpo de la arquitectura (debe contener Unexpected token '$').

Agregar más es una entrada en el arreglo negativeTests + un archivo test_negative_*.vhdl en la raíz del Parser.


Última actualización: 2026-08-03